mirror of
https://github.com/Bornholm/formidable.git
synced 2025-07-05 13:14:35 +02:00
Initial commit
This commit is contained in:
73
internal/jsonpointer/delete.go
Normal file
73
internal/jsonpointer/delete.go
Normal file
@ -0,0 +1,73 @@
|
||||
package jsonpointer
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func del(doc interface{}, tokens []string) (interface{}, error) {
|
||||
currentToken := tokens[0]
|
||||
|
||||
switch typedDoc := doc.(type) {
|
||||
|
||||
case map[string]interface{}:
|
||||
nestedDoc, exists := typedDoc[currentToken]
|
||||
if !exists {
|
||||
return doc, nil
|
||||
}
|
||||
|
||||
if len(tokens) == 1 {
|
||||
delete(typedDoc, currentToken)
|
||||
|
||||
return typedDoc, nil
|
||||
}
|
||||
|
||||
nestedDoc, err := del(nestedDoc, tokens[1:])
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
typedDoc[currentToken] = nestedDoc
|
||||
|
||||
return typedDoc, nil
|
||||
|
||||
case []interface{}:
|
||||
var (
|
||||
index uint64
|
||||
nestedDoc interface{}
|
||||
err error
|
||||
)
|
||||
|
||||
if currentToken == NonExistentMemberToken {
|
||||
index = uint64(len(typedDoc) - 1)
|
||||
} else {
|
||||
index, err = strconv.ParseUint(currentToken, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
if len(typedDoc) <= int(index) {
|
||||
return typedDoc, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(tokens) == 1 {
|
||||
typedDoc = append(typedDoc[:index], typedDoc[index+1:]...)
|
||||
|
||||
return typedDoc, nil
|
||||
}
|
||||
|
||||
nestedDoc, err = del(nestedDoc, tokens[1:])
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
typedDoc[index] = nestedDoc
|
||||
|
||||
return typedDoc, nil
|
||||
|
||||
default:
|
||||
return typedDoc, nil
|
||||
}
|
||||
}
|
95
internal/jsonpointer/delete_test.go
Normal file
95
internal/jsonpointer/delete_test.go
Normal file
@ -0,0 +1,95 @@
|
||||
package jsonpointer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type pointerDeleteTestCase struct {
|
||||
DocPath string
|
||||
Pointer string
|
||||
ExpectedRawDocument string
|
||||
}
|
||||
|
||||
func TestPointerDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []pointerDeleteTestCase{
|
||||
{
|
||||
DocPath: "./testdata/set/basic.json",
|
||||
Pointer: "/foo",
|
||||
ExpectedRawDocument: `{}`,
|
||||
},
|
||||
{
|
||||
DocPath: "./testdata/set/nested.json",
|
||||
Pointer: "/nestedObject/foo/1",
|
||||
ExpectedRawDocument: `
|
||||
{
|
||||
"nestedObject": {
|
||||
"foo": [
|
||||
"bar"
|
||||
]
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
DocPath: "./testdata/set/nested.json",
|
||||
Pointer: "/nestedObject/foo/-",
|
||||
ExpectedRawDocument: `
|
||||
{
|
||||
"nestedObject": {
|
||||
"foo": [
|
||||
"bar"
|
||||
]
|
||||
}
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
func(index int, tc pointerDeleteTestCase) {
|
||||
t.Run(fmt.Sprintf("#%d: '%s'", i, tc.Pointer), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
baseRawDocument, err := ioutil.ReadFile(tc.DocPath)
|
||||
if err != nil {
|
||||
t.Fatal(errors.WithStack(err))
|
||||
}
|
||||
|
||||
var baseDoc interface{}
|
||||
|
||||
if err := json.Unmarshal([]byte(baseRawDocument), &baseDoc); err != nil {
|
||||
t.Fatal(errors.WithStack(err))
|
||||
}
|
||||
|
||||
pointer := New(tc.Pointer)
|
||||
|
||||
updatedDoc, err := pointer.Delete(baseDoc)
|
||||
if err != nil {
|
||||
t.Fatal(errors.WithStack(err))
|
||||
}
|
||||
|
||||
rawDoc, err := json.MarshalIndent(updatedDoc, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal(errors.WithStack(err))
|
||||
}
|
||||
|
||||
var expectedDoc interface{}
|
||||
|
||||
if err := json.Unmarshal([]byte(tc.ExpectedRawDocument), &expectedDoc); err != nil {
|
||||
t.Fatal(errors.WithStack(err))
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expectedDoc, updatedDoc) {
|
||||
t.Errorf("Delete pointer '%s': expected document \n'%s', got \n'%s'", tc.Pointer, strings.TrimSpace(tc.ExpectedRawDocument), rawDoc)
|
||||
}
|
||||
})
|
||||
}(i, tc)
|
||||
}
|
||||
}
|
9
internal/jsonpointer/error.go
Normal file
9
internal/jsonpointer/error.go
Normal file
@ -0,0 +1,9 @@
|
||||
package jsonpointer
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrNotFound = errors.New("not found")
|
||||
ErrUnexpectedType = errors.New("unexpected type")
|
||||
ErrOutOfBounds = errors.New("out of bounds")
|
||||
)
|
111
internal/jsonpointer/force.go
Normal file
111
internal/jsonpointer/force.go
Normal file
@ -0,0 +1,111 @@
|
||||
package jsonpointer
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func force(doc interface{}, tokens []string, value interface{}) (interface{}, error) {
|
||||
if len(tokens) == 0 {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
currentToken := tokens[0]
|
||||
|
||||
switch typedDoc := doc.(type) {
|
||||
|
||||
case map[string]interface{}:
|
||||
nestedDoc, exists := typedDoc[currentToken]
|
||||
if !exists {
|
||||
if len(tokens) == 1 {
|
||||
typedDoc[currentToken] = value
|
||||
|
||||
return typedDoc, nil
|
||||
}
|
||||
|
||||
nextToken := tokens[1]
|
||||
if isArrayIndexToken(nextToken) {
|
||||
nestedDoc = make([]interface{}, 0)
|
||||
} else {
|
||||
nestedDoc = make(map[string]interface{})
|
||||
}
|
||||
}
|
||||
|
||||
nestedDoc, err := force(nestedDoc, tokens[1:], value)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
typedDoc[currentToken] = nestedDoc
|
||||
|
||||
return typedDoc, nil
|
||||
|
||||
case []interface{}:
|
||||
var (
|
||||
index uint64
|
||||
nestedDoc interface{}
|
||||
err error
|
||||
)
|
||||
|
||||
if currentToken == NonExistentMemberToken {
|
||||
typedDoc = append(typedDoc, value)
|
||||
index = uint64(len(typedDoc) - 1)
|
||||
} else {
|
||||
index, err = strconv.ParseUint(currentToken, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
if len(typedDoc) <= int(index) {
|
||||
for i := len(typedDoc); i <= int(index); i++ {
|
||||
typedDoc = append(typedDoc, nil)
|
||||
}
|
||||
}
|
||||
|
||||
nestedDoc = typedDoc[index]
|
||||
}
|
||||
|
||||
nestedDoc, err = force(nestedDoc, tokens[1:], value)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
typedDoc[index] = nestedDoc
|
||||
|
||||
return typedDoc, nil
|
||||
|
||||
default:
|
||||
overrideDoc := map[string]interface{}{}
|
||||
overrideDoc[currentToken] = value
|
||||
|
||||
var nestedDoc interface{}
|
||||
|
||||
if len(tokens) > 1 && isArrayIndexToken(tokens[1]) {
|
||||
nestedDoc = make([]interface{}, 0)
|
||||
} else {
|
||||
nestedDoc = make(map[string]interface{})
|
||||
}
|
||||
|
||||
nestedDoc, err := force(nestedDoc, tokens[1:], value)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
overrideDoc[currentToken] = nestedDoc
|
||||
|
||||
return overrideDoc, nil
|
||||
}
|
||||
}
|
||||
|
||||
func isArrayIndexToken(token string) bool {
|
||||
if token == NonExistentMemberToken {
|
||||
return true
|
||||
}
|
||||
|
||||
if _, err := strconv.ParseUint(token, 10, 64); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
60
internal/jsonpointer/get.go
Normal file
60
internal/jsonpointer/get.go
Normal file
@ -0,0 +1,60 @@
|
||||
package jsonpointer
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func get(doc interface{}, tokens []string) (interface{}, error) {
|
||||
if len(tokens) == 0 {
|
||||
return doc, nil
|
||||
}
|
||||
|
||||
currentToken := tokens[0]
|
||||
|
||||
if doc == nil {
|
||||
return nil, errors.Wrapf(ErrNotFound, "pointer '%s' not found on document", tokensToString(tokens))
|
||||
}
|
||||
|
||||
switch typedDoc := doc.(type) {
|
||||
case map[string]interface{}:
|
||||
value, exists := typedDoc[currentToken]
|
||||
if !exists {
|
||||
return nil, errors.Wrapf(ErrNotFound, "pointer '%s' not found on document", tokensToString(tokens))
|
||||
}
|
||||
|
||||
value, err := get(value, tokens[1:])
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return value, nil
|
||||
|
||||
case []interface{}:
|
||||
if currentToken == NonExistentMemberToken {
|
||||
return nil, errors.WithStack(ErrOutOfBounds)
|
||||
}
|
||||
|
||||
index, err := strconv.ParseUint(currentToken, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
if len(typedDoc) <= int(index) {
|
||||
return nil, errors.WithStack(ErrOutOfBounds)
|
||||
}
|
||||
|
||||
value := typedDoc[index]
|
||||
|
||||
value, err = get(value, tokens[1:])
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return value, nil
|
||||
|
||||
default:
|
||||
return nil, errors.Wrapf(ErrUnexpectedType, "unexpected type '%T'", typedDoc)
|
||||
}
|
||||
}
|
140
internal/jsonpointer/get_test.go
Normal file
140
internal/jsonpointer/get_test.go
Normal file
@ -0,0 +1,140 @@
|
||||
package jsonpointer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type pointerGetTestCase struct {
|
||||
Document interface{}
|
||||
Pointer string
|
||||
ExpectedRawValue string
|
||||
}
|
||||
|
||||
func TestPointerGet(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ietfRawDocument, err := ioutil.ReadFile("./testdata/ietf.json")
|
||||
if err != nil {
|
||||
t.Fatal(errors.WithStack(err))
|
||||
}
|
||||
|
||||
var ietfDoc interface{}
|
||||
|
||||
if err := json.Unmarshal([]byte(ietfRawDocument), &ietfDoc); err != nil {
|
||||
t.Fatal(errors.WithStack(err))
|
||||
}
|
||||
|
||||
// IETF tests cases
|
||||
// From https://datatracker.ietf.org/doc/html/rfc6901
|
||||
//
|
||||
// "" // the whole document
|
||||
// "/foo" ["bar", "baz"]
|
||||
// "/foo/0" "bar"
|
||||
// "/" 0
|
||||
// "/a~1b" 1
|
||||
// "/c%d" 2
|
||||
// "/e^f" 3
|
||||
// "/g|h" 4
|
||||
// "/i\\j" 5
|
||||
// "/k\"l" 6
|
||||
// "/ " 7
|
||||
// "/m~0n" 8
|
||||
testCases := []pointerGetTestCase{
|
||||
{
|
||||
Document: ietfDoc,
|
||||
Pointer: "",
|
||||
ExpectedRawValue: string(ietfRawDocument),
|
||||
},
|
||||
{
|
||||
Document: ietfDoc,
|
||||
Pointer: "/foo",
|
||||
ExpectedRawValue: "[\"bar\", \"baz\"]",
|
||||
},
|
||||
{
|
||||
Document: ietfDoc,
|
||||
Pointer: "/foo/0",
|
||||
ExpectedRawValue: "\"bar\"",
|
||||
},
|
||||
{
|
||||
Document: ietfDoc,
|
||||
Pointer: `/`,
|
||||
ExpectedRawValue: `0`,
|
||||
},
|
||||
{
|
||||
Document: ietfDoc,
|
||||
Pointer: "/a~1b",
|
||||
ExpectedRawValue: "1",
|
||||
},
|
||||
{
|
||||
Document: ietfDoc,
|
||||
Pointer: "/c%d",
|
||||
ExpectedRawValue: "2",
|
||||
},
|
||||
{
|
||||
Document: ietfDoc,
|
||||
Pointer: "/e^f",
|
||||
ExpectedRawValue: "3",
|
||||
},
|
||||
{
|
||||
Document: ietfDoc,
|
||||
Pointer: "/g|h",
|
||||
ExpectedRawValue: "4",
|
||||
},
|
||||
{
|
||||
Document: ietfDoc,
|
||||
Pointer: "/i\\j",
|
||||
ExpectedRawValue: "5",
|
||||
},
|
||||
{
|
||||
Document: ietfDoc,
|
||||
Pointer: "/k\"l",
|
||||
ExpectedRawValue: "6",
|
||||
},
|
||||
{
|
||||
Document: ietfDoc,
|
||||
Pointer: "/ ",
|
||||
ExpectedRawValue: "7",
|
||||
},
|
||||
{
|
||||
Document: ietfDoc,
|
||||
Pointer: "/m~0n",
|
||||
ExpectedRawValue: "8",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
func(index int, tc pointerGetTestCase) {
|
||||
t.Run(fmt.Sprintf("#%d: '%s'", i, tc.Pointer), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
pointer := New(tc.Pointer)
|
||||
|
||||
value, err := pointer.Get(tc.Document)
|
||||
if err != nil {
|
||||
t.Fatal(errors.WithStack(err))
|
||||
}
|
||||
|
||||
rawValue, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
t.Fatal(errors.WithStack(err))
|
||||
}
|
||||
|
||||
var expectedValue interface{}
|
||||
|
||||
if err := json.Unmarshal([]byte(tc.ExpectedRawValue), &expectedValue); err != nil {
|
||||
t.Fatal(errors.WithStack(err))
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expectedValue, value) {
|
||||
t.Errorf("Pointer '%s': expected value '%s', got '%s'", tc.Pointer, tc.ExpectedRawValue, rawValue)
|
||||
}
|
||||
})
|
||||
}(i, tc)
|
||||
}
|
||||
}
|
92
internal/jsonpointer/pointer.go
Normal file
92
internal/jsonpointer/pointer.go
Normal file
@ -0,0 +1,92 @@
|
||||
package jsonpointer
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
TokenSeparator = "/"
|
||||
NonExistentMemberToken = "-"
|
||||
)
|
||||
|
||||
type Pointer struct {
|
||||
tokens []string
|
||||
}
|
||||
|
||||
func (p *Pointer) Get(doc interface{}) (interface{}, error) {
|
||||
value, err := get(doc, p.tokens)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (p *Pointer) Set(doc interface{}, value interface{}) (interface{}, error) {
|
||||
doc, err := set(doc, p.tokens, value)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return doc, nil
|
||||
}
|
||||
|
||||
func (p *Pointer) Force(doc interface{}, value interface{}) (interface{}, error) {
|
||||
doc, err := force(doc, p.tokens, value)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return doc, nil
|
||||
}
|
||||
|
||||
func (p *Pointer) Delete(doc interface{}) (interface{}, error) {
|
||||
doc, err := del(doc, p.tokens)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return doc, nil
|
||||
}
|
||||
|
||||
func New(raw string) *Pointer {
|
||||
tokens := decodeTokens(raw)
|
||||
|
||||
return &Pointer{tokens}
|
||||
}
|
||||
|
||||
func tokensToString(tokens []string) string {
|
||||
escapedTokens := make([]string, 0)
|
||||
|
||||
for _, t := range tokens {
|
||||
escapedTokens = append(escapedTokens, escapeToken(t))
|
||||
}
|
||||
|
||||
return TokenSeparator + strings.Join(escapedTokens, TokenSeparator)
|
||||
}
|
||||
|
||||
func escapeToken(token string) string {
|
||||
token = strings.ReplaceAll(token, "/", "~1")
|
||||
token = strings.ReplaceAll(token, "~", "~0")
|
||||
|
||||
return token
|
||||
}
|
||||
|
||||
func unescapeToken(token string) string {
|
||||
token = strings.ReplaceAll(token, "~1", "/")
|
||||
token = strings.ReplaceAll(token, "~0", "~")
|
||||
|
||||
return token
|
||||
}
|
||||
|
||||
func decodeTokens(raw string) []string {
|
||||
tokens := strings.Split(raw, TokenSeparator)
|
||||
|
||||
for i, t := range tokens {
|
||||
tokens[i] = unescapeToken(t)
|
||||
}
|
||||
|
||||
return tokens[1:]
|
||||
}
|
67
internal/jsonpointer/set.go
Normal file
67
internal/jsonpointer/set.go
Normal file
@ -0,0 +1,67 @@
|
||||
package jsonpointer
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func set(doc interface{}, tokens []string, value interface{}) (interface{}, error) {
|
||||
if len(tokens) == 0 {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
currentToken := tokens[0]
|
||||
|
||||
switch typedDoc := doc.(type) {
|
||||
case map[string]interface{}:
|
||||
nestedDoc, exists := typedDoc[currentToken]
|
||||
if !exists {
|
||||
return nil, errors.Wrapf(ErrNotFound, "pointer '%s' not found on document", tokensToString(tokens))
|
||||
}
|
||||
|
||||
nestedDoc, err := set(nestedDoc, tokens[1:], value)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
typedDoc[currentToken] = nestedDoc
|
||||
|
||||
return typedDoc, nil
|
||||
|
||||
case []interface{}:
|
||||
var (
|
||||
index uint64
|
||||
nestedDoc interface{}
|
||||
err error
|
||||
)
|
||||
|
||||
if currentToken == NonExistentMemberToken {
|
||||
typedDoc = append(typedDoc, value)
|
||||
index = uint64(len(typedDoc) - 1)
|
||||
} else {
|
||||
index, err = strconv.ParseUint(currentToken, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
if len(typedDoc) <= int(index) {
|
||||
return nil, errors.WithStack(ErrOutOfBounds)
|
||||
}
|
||||
|
||||
nestedDoc = typedDoc[index]
|
||||
}
|
||||
|
||||
nestedDoc, err = set(nestedDoc, tokens[1:], value)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
typedDoc[index] = nestedDoc
|
||||
|
||||
return typedDoc, nil
|
||||
|
||||
default:
|
||||
return nil, errors.Wrapf(ErrUnexpectedType, "unexpected type '%T'", typedDoc)
|
||||
}
|
||||
}
|
102
internal/jsonpointer/set_test.go
Normal file
102
internal/jsonpointer/set_test.go
Normal file
@ -0,0 +1,102 @@
|
||||
package jsonpointer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type pointerSetTestCase struct {
|
||||
DocPath string
|
||||
Pointer string
|
||||
Value interface{}
|
||||
ExpectedRawDocument string
|
||||
}
|
||||
|
||||
func TestPointerSet(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []pointerSetTestCase{
|
||||
{
|
||||
DocPath: "./testdata/set/basic.json",
|
||||
Pointer: "/foo",
|
||||
Value: "bar",
|
||||
ExpectedRawDocument: `{"foo":"bar"}`,
|
||||
},
|
||||
{
|
||||
DocPath: "./testdata/set/nested.json",
|
||||
Pointer: "/nestedObject/foo/1",
|
||||
Value: "test",
|
||||
ExpectedRawDocument: `
|
||||
{
|
||||
"nestedObject": {
|
||||
"foo": [
|
||||
"bar",
|
||||
"test"
|
||||
]
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
DocPath: "./testdata/set/nested.json",
|
||||
Pointer: "/nestedObject/foo/-",
|
||||
Value: "baz",
|
||||
ExpectedRawDocument: `
|
||||
{
|
||||
"nestedObject": {
|
||||
"foo": [
|
||||
"bar",
|
||||
0,
|
||||
"baz"
|
||||
]
|
||||
}
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
func(index int, tc pointerSetTestCase) {
|
||||
t.Run(fmt.Sprintf("#%d: '%s'", i, tc.Pointer), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
baseRawDocument, err := ioutil.ReadFile(tc.DocPath)
|
||||
if err != nil {
|
||||
t.Fatal(errors.WithStack(err))
|
||||
}
|
||||
|
||||
var baseDoc interface{}
|
||||
|
||||
if err := json.Unmarshal([]byte(baseRawDocument), &baseDoc); err != nil {
|
||||
t.Fatal(errors.WithStack(err))
|
||||
}
|
||||
|
||||
pointer := New(tc.Pointer)
|
||||
|
||||
updatedDoc, err := pointer.Set(baseDoc, tc.Value)
|
||||
if err != nil {
|
||||
t.Fatal(errors.WithStack(err))
|
||||
}
|
||||
|
||||
rawDoc, err := json.MarshalIndent(updatedDoc, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal(errors.WithStack(err))
|
||||
}
|
||||
|
||||
var expectedDoc interface{}
|
||||
|
||||
if err := json.Unmarshal([]byte(tc.ExpectedRawDocument), &expectedDoc); err != nil {
|
||||
t.Fatal(errors.WithStack(err))
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expectedDoc, updatedDoc) {
|
||||
t.Errorf("Set pointer '%s' -> '%v': expected document '%s', got '%s'", tc.Pointer, tc.Value, strings.TrimSpace(tc.ExpectedRawDocument), rawDoc)
|
||||
}
|
||||
})
|
||||
}(i, tc)
|
||||
}
|
||||
}
|
12
internal/jsonpointer/testdata/ietf.json
vendored
Normal file
12
internal/jsonpointer/testdata/ietf.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"foo": ["bar", "baz"],
|
||||
"": 0,
|
||||
"a/b": 1,
|
||||
"c%d": 2,
|
||||
"e^f": 3,
|
||||
"g|h": 4,
|
||||
"i\\j": 5,
|
||||
"k\"l": 6,
|
||||
" ": 7,
|
||||
"m~n": 8
|
||||
}
|
1
internal/jsonpointer/testdata/set/basic.json
vendored
Normal file
1
internal/jsonpointer/testdata/set/basic.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"foo":null}
|
8
internal/jsonpointer/testdata/set/nested.json
vendored
Normal file
8
internal/jsonpointer/testdata/set/nested.json
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"nestedObject": {
|
||||
"foo": [
|
||||
"bar",
|
||||
0
|
||||
]
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user