From e4940502324b7b8e3aaeac9f996f2a3daee23fd7 Mon Sep 17 00:00:00 2001 From: magodo Date: Wed, 17 May 2023 16:55:38 +0800 Subject: [PATCH 01/26] Add new method `Offset` to `jsonpointer.Pointer` to return the offset of the pointer in a JSON document Signed-off-by: magodo --- pointer.go | 108 ++++++++++++++++++++++++++++++++++++++++++++++++ pointer_test.go | 56 +++++++++++++++++++++++++ 2 files changed, 164 insertions(+) diff --git a/pointer.go b/pointer.go index 7df9853..614c819 100644 --- a/pointer.go +++ b/pointer.go @@ -26,6 +26,7 @@ package jsonpointer import ( + "encoding/json" "errors" "fmt" "reflect" @@ -40,6 +41,7 @@ const ( pointerSeparator = `/` invalidStart = `JSON pointer must be empty or start with a "` + pointerSeparator + notFound = `Can't find the pointer in the document` ) var jsonPointableType = reflect.TypeOf(new(JSONPointable)).Elem() @@ -363,6 +365,112 @@ func (p *Pointer) String() string { return pointerString } +func (p *Pointer) Offset(document string) (int64, error) { + dec := json.NewDecoder(strings.NewReader(document)) + var offset int64 + for _, ttk := range p.DecodedTokens() { + tk, err := dec.Token() + if err != nil { + return 0, err + } + switch tk := tk.(type) { + case json.Delim: + switch tk { + case '{': + offset, err = offsetSingleObject(dec, ttk) + if err != nil { + return 0, err + } + case '[': + offset, err = offsetSingleArray(dec, ttk) + if err != nil { + return 0, err + } + default: + return 0, fmt.Errorf("invalid token %#v", tk) + } + default: + return 0, fmt.Errorf("invalid token %#v", tk) + } + } + return offset, nil +} + +func offsetSingleObject(dec *json.Decoder, decodedToken string) (int64, error) { + for dec.More() { + tk, err := dec.Token() + if err != nil { + return 0, err + } + switch tk := tk.(type) { + case json.Delim: + switch tk { + case '{': + if err := drainSingle(dec); err != nil { + return 0, err + } + case '[': + if err := drainSingle(dec); err != nil { + return 0, err + } + } + case string: + if tk == decodedToken { + return dec.InputOffset(), nil + } + default: + return 0, fmt.Errorf("invalid token %#v", tk) + } + } + return 0, fmt.Errorf("token reference %q not found", decodedToken) +} + +func offsetSingleArray(dec *json.Decoder, decodedToken string) (int64, error) { + idx, err := strconv.Atoi(decodedToken) + if err != nil { + return 0, fmt.Errorf("token reference %q is not a number: %v", decodedToken, err) + } + var i int + for i = 0; i < idx && dec.More(); i++ { + tk, err := dec.Token() + if err != nil { + return 0, err + } + switch tk := tk.(type) { + case json.Delim: + switch tk { + case '{': + if err := drainSingle(dec); err != nil { + return 0, err + } + case '[': + if err := drainSingle(dec); err != nil { + return 0, err + } + } + default: + // just consume the token + } + } + if !dec.More() { + return 0, fmt.Errorf("token reference %q not found", decodedToken) + } + return dec.InputOffset(), nil +} + +func drainSingle(dec *json.Decoder) error { + for dec.More() { + if _, err := dec.Token(); err != nil { + return err + } + } + // Consumes the ending delim + if _, err := dec.Token(); err != nil { + return err + } + return nil +} + // Specific JSON pointer encoding here // ~0 => ~ // ~1 => / diff --git a/pointer_test.go b/pointer_test.go index 020b19d..771ffaa 100644 --- a/pointer_test.go +++ b/pointer_test.go @@ -595,3 +595,59 @@ func TestSetNode(t *testing.T) { } } } + +func TestOffset(t *testing.T) { + cases := []struct { + name string + ptr string + input string + offset int64 + hasError bool + }{ + { + name: "object key", + ptr: "/foo/bar", + input: `{"foo": {"bar": 21}}`, + offset: 14, + }, + { + name: "array index", + ptr: "/0/1", + input: `[[1,2], [3,4]]`, + offset: 3, + }, + { + name: "mix array index and object key", + ptr: "/0/1/foo/0", + input: `[[1, {"foo": ["a", "b"]}], [3, 4]]`, + offset: 14, + }, + { + name: "nonexist object key", + ptr: "/foo/baz", + input: `{"foo": {"bar": 21}}`, + hasError: true, + }, + { + name: "nonexist array index", + ptr: "/0/2", + input: `[[1,2], [3,4]]`, + hasError: true, + }, + } + + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + ptr, err := New(tt.ptr) + assert.NoError(t, err) + offset, err := ptr.Offset(tt.input) + if tt.hasError { + assert.Error(t, err) + return + } + t.Log(offset, err) + assert.NoError(t, err) + assert.Equal(t, tt.offset, offset) + }) + } +} From cdfe8d8747d5644288c8c535f9c18981a622c734 Mon Sep 17 00:00:00 2001 From: magodo Date: Thu, 18 May 2023 15:58:57 +0800 Subject: [PATCH 02/26] Fix bug Signed-off-by: magodo --- pointer.go | 20 +++++++++++++++++--- pointer_test.go | 6 ++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/pointer.go b/pointer.go index 614c819..4f55fe3 100644 --- a/pointer.go +++ b/pointer.go @@ -448,8 +448,6 @@ func offsetSingleArray(dec *json.Decoder, decodedToken string) (int64, error) { return 0, err } } - default: - // just consume the token } } if !dec.More() { @@ -458,11 +456,27 @@ func offsetSingleArray(dec *json.Decoder, decodedToken string) (int64, error) { return dec.InputOffset(), nil } +// drainSingle drains a single level of object or array. +// The decoder has to guarantee the begining delim (i.e. '{' or '[') has been consumed. func drainSingle(dec *json.Decoder) error { for dec.More() { - if _, err := dec.Token(); err != nil { + tk, err := dec.Token() + if err != nil { return err } + switch tk := tk.(type) { + case json.Delim: + switch tk { + case '{': + if err := drainSingle(dec); err != nil { + return err + } + case '[': + if err := drainSingle(dec); err != nil { + return err + } + } + } } // Consumes the ending delim if _, err := dec.Token(); err != nil { diff --git a/pointer_test.go b/pointer_test.go index 771ffaa..4fe6d82 100644 --- a/pointer_test.go +++ b/pointer_test.go @@ -610,6 +610,12 @@ func TestOffset(t *testing.T) { input: `{"foo": {"bar": 21}}`, offset: 14, }, + { + name: "complex object key", + ptr: "/paths/~1p~1{}/get", + input: `{"paths": {"foo": {"bar": 123, "baz": {}}, "/p/{}": {"get": {}}}}`, + offset: 58, + }, { name: "array index", ptr: "/0/1", From d3f17d34b644879902890f4825f579a1b4707825 Mon Sep 17 00:00:00 2001 From: magodo Date: Fri, 19 May 2023 10:41:05 +0800 Subject: [PATCH 03/26] Correct offset for object key --- pointer.go | 3 ++- pointer_test.go | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pointer.go b/pointer.go index 4f55fe3..a1b6b12 100644 --- a/pointer.go +++ b/pointer.go @@ -398,6 +398,7 @@ func (p *Pointer) Offset(document string) (int64, error) { func offsetSingleObject(dec *json.Decoder, decodedToken string) (int64, error) { for dec.More() { + offset := dec.InputOffset() tk, err := dec.Token() if err != nil { return 0, err @@ -416,7 +417,7 @@ func offsetSingleObject(dec *json.Decoder, decodedToken string) (int64, error) { } case string: if tk == decodedToken { - return dec.InputOffset(), nil + return offset, nil } default: return 0, fmt.Errorf("invalid token %#v", tk) diff --git a/pointer_test.go b/pointer_test.go index 4fe6d82..d19f3c6 100644 --- a/pointer_test.go +++ b/pointer_test.go @@ -608,13 +608,13 @@ func TestOffset(t *testing.T) { name: "object key", ptr: "/foo/bar", input: `{"foo": {"bar": 21}}`, - offset: 14, + offset: 9, }, { name: "complex object key", ptr: "/paths/~1p~1{}/get", input: `{"paths": {"foo": {"bar": 123, "baz": {}}, "/p/{}": {"get": {}}}}`, - offset: 58, + offset: 53, }, { name: "array index", From ff1d15425068d4e8e24eefd2f5b286adca484748 Mon Sep 17 00:00:00 2001 From: guoguangwu Date: Thu, 29 Jun 2023 13:26:38 +0800 Subject: [PATCH 04/26] chore: slice replace loop Signed-off-by: guoguangwu --- pointer.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pointer.go b/pointer.go index 7df9853..9f00cf1 100644 --- a/pointer.go +++ b/pointer.go @@ -81,9 +81,7 @@ func (p *Pointer) parse(jsonPointerString string) error { err = errors.New(invalidStart) } else { referenceTokens := strings.Split(jsonPointerString, pointerSeparator) - for _, referenceToken := range referenceTokens[1:] { - p.referenceTokens = append(p.referenceTokens, referenceToken) - } + p.referenceTokens = append(p.referenceTokens, referenceTokens[1:]...) } } From c7d419921bf2a9b70ac807d4d2f81c396ed21714 Mon Sep 17 00:00:00 2001 From: Gaiaz Iusipov Date: Fri, 14 Jul 2023 01:23:38 +0800 Subject: [PATCH 05/26] Update dependencies Signed-off-by: Gaiaz Iusipov --- go.mod | 15 ++++++++++++--- go.sum | 25 +++++++++---------------- pointer.go | 22 +++++++++++----------- pointer_test.go | 42 +++++++++++++++++++++--------------------- 4 files changed, 53 insertions(+), 51 deletions(-) diff --git a/go.mod b/go.mod index ce0ad24..495f879 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,17 @@ module github.com/go-openapi/jsonpointer +go 1.18 + require ( - github.com/go-openapi/swag v0.22.3 - github.com/stretchr/testify v1.8.1 + github.com/go-openapi/swag v0.22.4 + github.com/stretchr/testify v1.8.4 ) -go 1.13 +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index 10ca578..5666509 100644 --- a/go.sum +++ b/go.sum @@ -1,31 +1,24 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pointer.go b/pointer.go index ba22d4d..de60dc7 100644 --- a/pointer.go +++ b/pointer.go @@ -50,13 +50,13 @@ var jsonSetableType = reflect.TypeOf(new(JSONSetable)).Elem() // JSONPointable is an interface for structs to implement when they need to customize the // json pointer process type JSONPointable interface { - JSONLookup(string) (interface{}, error) + JSONLookup(string) (any, error) } // JSONSetable is an interface for structs to implement when they need to customize the // json pointer process type JSONSetable interface { - JSONSet(string, interface{}) error + JSONSet(string, any) error } // New creates a new json pointer for the given string @@ -83,7 +83,7 @@ func (p *Pointer) parse(jsonPointerString string) error { err = errors.New(invalidStart) } else { referenceTokens := strings.Split(jsonPointerString, pointerSeparator) - p.referenceTokens = append(p.referenceTokens, referenceTokens[1:]...) + p.referenceTokens = append(p.referenceTokens, referenceTokens[1:]...) } } @@ -91,26 +91,26 @@ func (p *Pointer) parse(jsonPointerString string) error { } // Get uses the pointer to retrieve a value from a JSON document -func (p *Pointer) Get(document interface{}) (interface{}, reflect.Kind, error) { +func (p *Pointer) Get(document any) (any, reflect.Kind, error) { return p.get(document, swag.DefaultJSONNameProvider) } // Set uses the pointer to set a value from a JSON document -func (p *Pointer) Set(document interface{}, value interface{}) (interface{}, error) { +func (p *Pointer) Set(document any, value any) (any, error) { return document, p.set(document, value, swag.DefaultJSONNameProvider) } // GetForToken gets a value for a json pointer token 1 level deep -func GetForToken(document interface{}, decodedToken string) (interface{}, reflect.Kind, error) { +func GetForToken(document any, decodedToken string) (any, reflect.Kind, error) { return getSingleImpl(document, decodedToken, swag.DefaultJSONNameProvider) } // SetForToken gets a value for a json pointer token 1 level deep -func SetForToken(document interface{}, decodedToken string, value interface{}) (interface{}, error) { +func SetForToken(document any, decodedToken string, value any) (any, error) { return document, setSingleImpl(document, value, decodedToken, swag.DefaultJSONNameProvider) } -func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) { +func getSingleImpl(node any, decodedToken string, nameProvider *swag.NameProvider) (any, reflect.Kind, error) { rValue := reflect.Indirect(reflect.ValueOf(node)) kind := rValue.Kind() @@ -159,7 +159,7 @@ func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.Nam } -func setSingleImpl(node, data interface{}, decodedToken string, nameProvider *swag.NameProvider) error { +func setSingleImpl(node, data any, decodedToken string, nameProvider *swag.NameProvider) error { rValue := reflect.Indirect(reflect.ValueOf(node)) if ns, ok := node.(JSONSetable); ok { // pointer impl @@ -210,7 +210,7 @@ func setSingleImpl(node, data interface{}, decodedToken string, nameProvider *sw } -func (p *Pointer) get(node interface{}, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) { +func (p *Pointer) get(node any, nameProvider *swag.NameProvider) (any, reflect.Kind, error) { if nameProvider == nil { nameProvider = swag.DefaultJSONNameProvider @@ -241,7 +241,7 @@ func (p *Pointer) get(node interface{}, nameProvider *swag.NameProvider) (interf return node, kind, nil } -func (p *Pointer) set(node, data interface{}, nameProvider *swag.NameProvider) error { +func (p *Pointer) set(node, data any, nameProvider *swag.NameProvider) error { knd := reflect.ValueOf(node).Kind() if knd != reflect.Ptr && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array { diff --git a/pointer_test.go b/pointer_test.go index d19f3c6..4ed2365 100644 --- a/pointer_test.go +++ b/pointer_test.go @@ -52,7 +52,7 @@ const ( }` ) -var testDocumentJSON interface{} +var testDocumentJSON any type testStructJSON struct { Foo []string `json:"foo"` @@ -67,7 +67,7 @@ type testStructJSON struct { } `json:"obj"` } -type aliasedMap map[string]interface{} +type aliasedMap map[string]any var testStructJSONDoc testStructJSON var testStructJSONPtr *testStructJSON @@ -109,7 +109,7 @@ func TestFullDocument(t *testing.T) { t.Errorf("Get(%v) error %v", in, err.Error()) } - if len(result.(map[string]interface{})) != TestDocumentNBItems { + if len(result.(map[string]any)) != TestDocumentNBItems { t.Errorf("Get(%v) = %v, expect full document", in, result) } @@ -118,7 +118,7 @@ func TestFullDocument(t *testing.T) { t.Errorf("Get(%v) error %v", in, err.Error()) } - if len(result.(map[string]interface{})) != TestDocumentNBItems { + if len(result.(map[string]any)) != TestDocumentNBItems { t.Errorf("Get(%v) = %v, expect full document", in, result) } } @@ -160,7 +160,7 @@ type pointableImpl struct { a string } -func (p pointableImpl) JSONLookup(token string) (interface{}, error) { +func (p pointableImpl) JSONLookup(token string) (any, error) { if token == "some" { return p.a, nil } @@ -169,7 +169,7 @@ func (p pointableImpl) JSONLookup(token string) (interface{}, error) { type pointableMap map[string]string -func (p pointableMap) JSONLookup(token string) (interface{}, error) { +func (p pointableMap) JSONLookup(token string) (any, error) { if token == "swap" { return p["swapped"], nil } @@ -213,7 +213,7 @@ func TestGetNode(t *testing.T) { assert.NoError(t, err) assert.Len(t, result, TestNodeObjNBItems) - result, _, err = p.Get(aliasedMap(testDocumentJSON.(map[string]interface{}))) + result, _, err = p.Get(aliasedMap(testDocumentJSON.(map[string]any))) assert.NoError(t, err) assert.Len(t, result, TestNodeObjNBItems) @@ -288,8 +288,8 @@ func TestOtherThings(t *testing.T) { p, err = New("/foo/1") assert.NoError(t, err) expected := "hello" - bbb := testDocumentJSON.(map[string]interface{})["foo"] - bbb.([]interface{})[1] = "hello" + bbb := testDocumentJSON.(map[string]any)["foo"] + bbb.([]any)[1] = "hello" v, _, err := p.Get(testDocumentJSON) assert.NoError(t, err) @@ -371,7 +371,7 @@ func (s *settableDoc) UnmarshalJSON(data []byte) error { } // JSONLookup implements an interface to customize json pointer lookup -func (s settableDoc) JSONLookup(token string) (interface{}, error) { +func (s settableDoc) JSONLookup(token string) (any, error) { switch token { case "a": return &s.Coll, nil @@ -383,7 +383,7 @@ func (s settableDoc) JSONLookup(token string) (interface{}, error) { } // JSONLookup implements an interface to customize json pointer lookup -func (s *settableDoc) JSONSet(token string, data interface{}) error { +func (s *settableDoc) JSONSet(token string, data any) error { switch token { case "a": switch dt := data.(type) { @@ -440,7 +440,7 @@ func (s *settableColl) UnmarshalJSON(data []byte) error { } // JSONLookup implements an interface to customize json pointer lookup -func (s settableColl) JSONLookup(token string) (interface{}, error) { +func (s settableColl) JSONLookup(token string) (any, error) { if tok, err := strconv.Atoi(token); err == nil { return &s.Items[tok], nil } @@ -448,7 +448,7 @@ func (s settableColl) JSONLookup(token string) (interface{}, error) { } // JSONLookup implements an interface to customize json pointer lookup -func (s *settableColl) JSONSet(token string, data interface{}) error { +func (s *settableColl) JSONSet(token string, data any) error { if _, err := strconv.Atoi(token); err == nil { _, err := SetForToken(s.Items, token, data) return err @@ -476,7 +476,7 @@ func TestSetNode(t *testing.T) { jsonText := `{"a":[{"b": 1, "c": 2}], "d": 3}` - var jsonDocument interface{} + var jsonDocument any if assert.NoError(t, json.Unmarshal([]byte(jsonText), &jsonDocument)) { in := "/a/0/c" p, err := New(in) @@ -485,13 +485,13 @@ func TestSetNode(t *testing.T) { _, err = p.Set(jsonDocument, 999) assert.NoError(t, err) - firstNode := jsonDocument.(map[string]interface{}) + firstNode := jsonDocument.(map[string]any) assert.Len(t, firstNode, 2) - sliceNode := firstNode["a"].([]interface{}) + sliceNode := firstNode["a"].([]any) assert.Len(t, sliceNode, 1) - changedNode := sliceNode[0].(map[string]interface{}) + changedNode := sliceNode[0].(map[string]any) chNodeVI := changedNode["c"] if assert.IsType(t, 0, chNodeVI) { changedNodeValue := chNodeVI.(int) @@ -503,14 +503,14 @@ func TestSetNode(t *testing.T) { v, err := New("/a/0") if assert.NoError(t, err) { - _, err = v.Set(jsonDocument, map[string]interface{}{"b": 3, "c": 8}) + _, err = v.Set(jsonDocument, map[string]any{"b": 3, "c": 8}) if assert.NoError(t, err) { - firstNode := jsonDocument.(map[string]interface{}) + firstNode := jsonDocument.(map[string]any) assert.Len(t, firstNode, 2) - sliceNode := firstNode["a"].([]interface{}) + sliceNode := firstNode["a"].([]any) assert.Len(t, sliceNode, 1) - changedNode := sliceNode[0].(map[string]interface{}) + changedNode := sliceNode[0].(map[string]any) assert.Equal(t, 3, changedNode["b"]) assert.Equal(t, 8, changedNode["c"]) } From b59e589e657ca3797cf633ef6472f2bcb1fce15c Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Wed, 6 Dec 2023 15:24:38 +0100 Subject: [PATCH 06/26] chore: updated and relinted * updated required go version to 1.19 * update lint config * relinted code * ci: updated github actions * doc: updated README badges Signed-off-by: Frederic BIDON --- .github/workflows/go-test.yml | 45 ++++- .golangci.yml | 61 ++++++ README.md | 8 +- go.mod | 4 +- pointer.go | 44 ++--- pointer_test.go | 349 ++++++++++++++++++---------------- 6 files changed, 309 insertions(+), 202 deletions(-) create mode 100644 .golangci.yml diff --git a/.github/workflows/go-test.yml b/.github/workflows/go-test.yml index 6a230af..e1a95c7 100644 --- a/.github/workflows/go-test.yml +++ b/.github/workflows/go-test.yml @@ -1,23 +1,50 @@ -name: Go Test +name: go test -on: [push] +on: [push, pull_request] jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: stable + check-latest: true + cache: true + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: latest + only-new-issues: true test: - - name: Test + name: Unit tests runs-on: ${{ matrix.os }} strategy: matrix: os: [ ubuntu-latest, macos-latest, windows-latest ] - steps: + go_version: ['oldstable', 'stable' ] - - uses: actions/setup-go@v2 + steps: + - name: Run unit tests + uses: actions/setup-go@v4 with: - go-version: 1.x + go-version: '${{ matrix.go_version }}' + check-latest: true + cache: true + + - uses: actions/checkout@v3 - - uses: actions/checkout@v2 + - run: go test -v -race -coverprofile="coverage-${{ matrix.os }}.${{ matrix.go_version }}.out" -covermode=atomic ./... - - run: go test \ No newline at end of file + - name: Upload coverage to codecov + uses: codecov/codecov-action@v3 + with: + files: './coverage-${{ matrix.os }}.${{ matrix.go_version }}.out' + flags: '${{ matrix.go_version }}' + os: '${{ matrix.os }}' + fail_ci_if_error: false + verbose: true diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..22f8d21 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,61 @@ +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + gocyclo: + min-complexity: 45 + maligned: + suggest-new: true + dupl: + threshold: 200 + goconst: + min-len: 2 + min-occurrences: 3 + +linters: + enable-all: true + disable: + - maligned + - unparam + - lll + - gochecknoinits + - gochecknoglobals + - funlen + - godox + - gocognit + - whitespace + - wsl + - wrapcheck + - testpackage + - nlreturn + - gomnd + - exhaustivestruct + - goerr113 + - errorlint + - nestif + - godot + - gofumpt + - paralleltest + - tparallel + - thelper + - ifshort + - exhaustruct + - varnamelen + - gci + - depguard + - errchkjson + - inamedparam + - nonamedreturns + - musttag + - ireturn + - forcetypeassert + - cyclop + # deprecated linters + - deadcode + - interfacer + - scopelint + - varcheck + - structcheck + - golint + - nosnakecase diff --git a/README.md b/README.md index 813788a..0108f1d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ -# gojsonpointer [![Build Status](https://travis-ci.org/go-openapi/jsonpointer.svg?branch=master)](https://travis-ci.org/go-openapi/jsonpointer) [![codecov](https://codecov.io/gh/go-openapi/jsonpointer/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonpointer) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) +# gojsonpointer [![Build Status](https://github.com/go-openapi/jsonpointer/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/jsonpointer/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/jsonpointer/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonpointer) + +[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonpointer/master/LICENSE) +[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/jsonpointer.svg)](https://pkg.go.dev/github.com/go-openapi/jsonpointer) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/jsonpointer)](https://goreportcard.com/report/github.com/go-openapi/jsonpointer) -[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonpointer/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/jsonpointer?status.svg)](http://godoc.org/github.com/go-openapi/jsonpointer) An implementation of JSON Pointer - Go language ## Status diff --git a/go.mod b/go.mod index 495f879..55b6db0 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,5 @@ module github.com/go-openapi/jsonpointer -go 1.18 - require ( github.com/go-openapi/swag v0.22.4 github.com/stretchr/testify v1.8.4 @@ -15,3 +13,5 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +go 1.19 diff --git a/pointer.go b/pointer.go index de60dc7..bed6b81 100644 --- a/pointer.go +++ b/pointer.go @@ -122,7 +122,7 @@ func getSingleImpl(node any, decodedToken string, nameProvider *swag.NameProvide return r, kind, nil } - switch kind { + switch kind { //nolint:exhaustive case reflect.Struct: nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) if !ok { @@ -170,7 +170,7 @@ func setSingleImpl(node, data any, decodedToken string, nameProvider *swag.NameP return node.(JSONSetable).JSONSet(decodedToken, data) } - switch rValue.Kind() { + switch rValue.Kind() { //nolint:exhaustive case reflect.Struct: nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) if !ok { @@ -231,8 +231,7 @@ func (p *Pointer) get(node any, nameProvider *swag.NameProvider) (any, reflect.K if err != nil { return nil, knd, err } - node, kind = r, knd - + node = r } rValue := reflect.ValueOf(node) @@ -284,7 +283,7 @@ func (p *Pointer) set(node, data any, nameProvider *swag.NameProvider) error { continue } - switch kind { + switch kind { //nolint:exhaustive case reflect.Struct: nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) if !ok { @@ -405,11 +404,11 @@ func offsetSingleObject(dec *json.Decoder, decodedToken string) (int64, error) { case json.Delim: switch tk { case '{': - if err := drainSingle(dec); err != nil { + if err = drainSingle(dec); err != nil { return 0, err } case '[': - if err := drainSingle(dec); err != nil { + if err = drainSingle(dec); err != nil { return 0, err } } @@ -435,20 +434,21 @@ func offsetSingleArray(dec *json.Decoder, decodedToken string) (int64, error) { if err != nil { return 0, err } - switch tk := tk.(type) { - case json.Delim: - switch tk { + + if delim, isDelim := tk.(json.Delim); isDelim { + switch delim { case '{': - if err := drainSingle(dec); err != nil { + if err = drainSingle(dec); err != nil { return 0, err } case '[': - if err := drainSingle(dec); err != nil { + if err = drainSingle(dec); err != nil { return 0, err } } } } + if !dec.More() { return 0, fmt.Errorf("token reference %q not found", decodedToken) } @@ -456,27 +456,27 @@ func offsetSingleArray(dec *json.Decoder, decodedToken string) (int64, error) { } // drainSingle drains a single level of object or array. -// The decoder has to guarantee the begining delim (i.e. '{' or '[') has been consumed. +// The decoder has to guarantee the beginning delim (i.e. '{' or '[') has been consumed. func drainSingle(dec *json.Decoder) error { for dec.More() { tk, err := dec.Token() if err != nil { return err } - switch tk := tk.(type) { - case json.Delim: - switch tk { + if delim, isDelim := tk.(json.Delim); isDelim { + switch delim { case '{': - if err := drainSingle(dec); err != nil { + if err = drainSingle(dec); err != nil { return err } case '[': - if err := drainSingle(dec); err != nil { + if err = drainSingle(dec); err != nil { return err } } } } + // Consumes the ending delim if _, err := dec.Token(); err != nil { return err @@ -498,14 +498,14 @@ const ( // Unescape unescapes a json pointer reference token string to the original representation func Unescape(token string) string { - step1 := strings.Replace(token, encRefTok1, decRefTok1, -1) - step2 := strings.Replace(step1, encRefTok0, decRefTok0, -1) + step1 := strings.ReplaceAll(token, encRefTok1, decRefTok1) + step2 := strings.ReplaceAll(step1, encRefTok0, decRefTok0) return step2 } // Escape escapes a pointer reference token string func Escape(token string) string { - step1 := strings.Replace(token, decRefTok0, encRefTok0, -1) - step2 := strings.Replace(step1, decRefTok1, encRefTok1, -1) + step1 := strings.ReplaceAll(token, decRefTok0, encRefTok0) + step2 := strings.ReplaceAll(step1, decRefTok1, encRefTok1) return step2 } diff --git a/pointer_test.go b/pointer_test.go index 4ed2365..5e27ab1 100644 --- a/pointer_test.go +++ b/pointer_test.go @@ -32,6 +32,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) const ( @@ -73,86 +74,84 @@ var testStructJSONDoc testStructJSON var testStructJSONPtr *testStructJSON func init() { - json.Unmarshal([]byte(TestDocumentString), &testDocumentJSON) - json.Unmarshal([]byte(TestDocumentString), &testStructJSONDoc) + if err := json.Unmarshal([]byte(TestDocumentString), &testDocumentJSON); err != nil { + panic(err) + } + if err := json.Unmarshal([]byte(TestDocumentString), &testStructJSONDoc); err != nil { + panic(err) + } + testStructJSONPtr = &testStructJSONDoc } func TestEscaping(t *testing.T) { - ins := []string{`/`, `/`, `/a~1b`, `/a~1b`, `/c%d`, `/e^f`, `/g|h`, `/i\j`, `/k"l`, `/ `, `/m~0n`} outs := []float64{0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8} for i := range ins { p, err := New(ins[i]) - if assert.NoError(t, err, "input: %v", ins[i]) { - result, _, err := p.Get(testDocumentJSON) - if assert.NoError(t, err, "input: %v", ins[i]) { - assert.Equal(t, outs[i], result, "input: %v", ins[i]) - } - } + require.NoError(t, err, "input: %v", ins[i]) + result, _, err := p.Get(testDocumentJSON) + + require.NoError(t, err, "input: %v", ins[i]) + assert.Equal(t, outs[i], result, "input: %v", ins[i]) } } func TestFullDocument(t *testing.T) { - - in := `` + const in = `` p, err := New(in) - if err != nil { - t.Errorf("New(%v) error %v", in, err.Error()) - } + require.NoErrorf(t, err, "New(%v) error %v", in, err) result, _, err := p.Get(testDocumentJSON) - if err != nil { - t.Errorf("Get(%v) error %v", in, err.Error()) - } + require.NoErrorf(t, err, "Get(%v) error %v", in, err) - if len(result.(map[string]any)) != TestDocumentNBItems { - t.Errorf("Get(%v) = %v, expect full document", in, result) - } + asMap, ok := result.(map[string]any) + require.True(t, ok) + require.Lenf(t, asMap, TestDocumentNBItems, "Get(%v) = %v, expect full document", in, result) result, _, err = p.get(testDocumentJSON, nil) - if err != nil { - t.Errorf("Get(%v) error %v", in, err.Error()) - } + require.NoErrorf(t, err, "Get(%v) error %v", in, err) - if len(result.(map[string]any)) != TestDocumentNBItems { - t.Errorf("Get(%v) = %v, expect full document", in, result) - } + asMap, ok = result.(map[string]any) + require.True(t, ok) + require.Lenf(t, asMap, TestDocumentNBItems, "Get(%v) = %v, expect full document", in, result) } func TestDecodedTokens(t *testing.T) { p, err := New("/obj/a~1b") - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, []string{"obj", "a/b"}, p.DecodedTokens()) } func TestIsEmpty(t *testing.T) { p, err := New("") - assert.NoError(t, err) + require.NoError(t, err) + assert.True(t, p.IsEmpty()) p, err = New("/obj") - assert.NoError(t, err) + require.NoError(t, err) + assert.False(t, p.IsEmpty()) } func TestGetSingle(t *testing.T) { - in := `/obj` + const in = `/obj` _, err := New(in) - assert.NoError(t, err) + require.NoError(t, err) result, _, err := GetForToken(testDocumentJSON, "obj") - assert.NoError(t, err) + require.NoError(t, err) assert.Len(t, result, TestNodeObjNBItems) result, _, err = GetForToken(testStructJSONDoc, "Obj") - assert.Error(t, err) + require.Error(t, err) assert.Nil(t, result) result, _, err = GetForToken(testStructJSONDoc, "Obj2") - assert.Error(t, err) + require.Error(t, err) assert.Nil(t, result) } @@ -186,78 +185,78 @@ func TestPointableInterface(t *testing.T) { p := &pointableImpl{"hello"} result, _, err := GetForToken(p, "some") - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, p.a, result) result, _, err = GetForToken(p, "something") - assert.Error(t, err) + require.Error(t, err) assert.Nil(t, result) pm := pointableMap{"swapped": "hello", "a": "world"} result, _, err = GetForToken(pm, "swap") - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, pm["swapped"], result) result, _, err = GetForToken(pm, "a") - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, pm["a"], result) } func TestGetNode(t *testing.T) { - - in := `/obj` + const in = `/obj` p, err := New(in) - assert.NoError(t, err) + require.NoError(t, err) + result, _, err := p.Get(testDocumentJSON) - assert.NoError(t, err) + require.NoError(t, err) assert.Len(t, result, TestNodeObjNBItems) result, _, err = p.Get(aliasedMap(testDocumentJSON.(map[string]any))) - assert.NoError(t, err) + require.NoError(t, err) assert.Len(t, result, TestNodeObjNBItems) result, _, err = p.Get(testStructJSONDoc) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, testStructJSONDoc.Obj, result) result, _, err = p.Get(testStructJSONPtr) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, testStructJSONDoc.Obj, result) } func TestArray(t *testing.T) { - ins := []string{`/foo/0`, `/foo/0`, `/foo/1`} outs := []string{"bar", "bar", "baz"} for i := range ins { p, err := New(ins[i]) - assert.NoError(t, err) + require.NoError(t, err) result, _, err := p.Get(testStructJSONDoc) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, outs[i], result) result, _, err = p.Get(testStructJSONPtr) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, outs[i], result) result, _, err = p.Get(testDocumentJSON) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, outs[i], result) } } func TestOtherThings(t *testing.T) { _, err := New("abc") - assert.Error(t, err) + require.Error(t, err) p, err := New("") - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "", p.String()) p, err = New("/obj/a") + require.NoError(t, err) assert.Equal(t, "/obj/a", p.String()) s := Escape("m~n") @@ -266,33 +265,33 @@ func TestOtherThings(t *testing.T) { assert.Equal(t, "m~1n", s) p, err = New("/foo/3") - assert.NoError(t, err) + require.NoError(t, err) _, _, err = p.Get(testDocumentJSON) - assert.Error(t, err) + require.Error(t, err) p, err = New("/foo/a") - assert.NoError(t, err) + require.NoError(t, err) _, _, err = p.Get(testDocumentJSON) - assert.Error(t, err) + require.Error(t, err) p, err = New("/notthere") - assert.NoError(t, err) + require.NoError(t, err) _, _, err = p.Get(testDocumentJSON) - assert.Error(t, err) + require.Error(t, err) p, err = New("/invalid") - assert.NoError(t, err) + require.NoError(t, err) _, _, err = p.Get(1234) - assert.Error(t, err) + require.Error(t, err) p, err = New("/foo/1") - assert.NoError(t, err) + require.NoError(t, err) expected := "hello" bbb := testDocumentJSON.(map[string]any)["foo"] bbb.([]any)[1] = "hello" v, _, err := p.Get(testDocumentJSON) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, expected, v) esc := Escape("a/") @@ -307,34 +306,34 @@ func TestOtherThings(t *testing.T) { } func TestObject(t *testing.T) { - ins := []string{`/obj/a`, `/obj/b`, `/obj/c/0`, `/obj/c/1`, `/obj/c/1`, `/obj/d/1/f/0`} outs := []float64{1, 2, 3, 4, 4, 50} for i := range ins { - p, err := New(ins[i]) - assert.NoError(t, err) + require.NoError(t, err) result, _, err := p.Get(testDocumentJSON) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, outs[i], result) result, _, err = p.Get(testStructJSONDoc) - assert.NoError(t, err) + require.NoError(t, err) assert.EqualValues(t, outs[i], result) result, _, err = p.Get(testStructJSONPtr) - assert.NoError(t, err) + require.NoError(t, err) assert.EqualValues(t, outs[i], result) } } -type setJsonDocEle struct { - B int `json:"b"` - C int `json:"c"` -} -type setJsonDoc struct { +/* + type setJSONDocEle struct { + B int `json:"b"` + C int `json:"c"` + } +*/ +type setJSONDoc struct { A []struct { B int `json:"b"` C int `json:"c"` @@ -473,127 +472,141 @@ func (s *settableInt) UnmarshalJSON(data []byte) error { } func TestSetNode(t *testing.T) { - - jsonText := `{"a":[{"b": 1, "c": 2}], "d": 3}` + const jsonText = `{"a":[{"b": 1, "c": 2}], "d": 3}` var jsonDocument any - if assert.NoError(t, json.Unmarshal([]byte(jsonText), &jsonDocument)) { - in := "/a/0/c" + require.NoError(t, json.Unmarshal([]byte(jsonText), &jsonDocument)) + + t.Run("with set node c", func(t *testing.T) { + const in = "/a/0/c" p, err := New(in) - if assert.NoError(t, err) { + require.NoError(t, err) - _, err = p.Set(jsonDocument, 999) - assert.NoError(t, err) + _, err = p.Set(jsonDocument, 999) + require.NoError(t, err) - firstNode := jsonDocument.(map[string]any) - assert.Len(t, firstNode, 2) + firstNode, ok := jsonDocument.(map[string]any) + require.True(t, ok) + assert.Len(t, firstNode, 2) - sliceNode := firstNode["a"].([]any) - assert.Len(t, sliceNode, 1) + sliceNode, ok := firstNode["a"].([]any) + require.True(t, ok) + assert.Len(t, sliceNode, 1) - changedNode := sliceNode[0].(map[string]any) - chNodeVI := changedNode["c"] - if assert.IsType(t, 0, chNodeVI) { - changedNodeValue := chNodeVI.(int) - if assert.Equal(t, 999, changedNodeValue) { - assert.Len(t, sliceNode, 1) - } - } - } + changedNode, ok := sliceNode[0].(map[string]any) + require.True(t, ok) + chNodeVI := changedNode["c"] + require.IsType(t, 0, chNodeVI) + changedNodeValue := chNodeVI.(int) + require.Equal(t, 999, changedNodeValue) + assert.Len(t, sliceNode, 1) + }) + + t.Run("with set node 0 with map", func(t *testing.T) { v, err := New("/a/0") - if assert.NoError(t, err) { - _, err = v.Set(jsonDocument, map[string]any{"b": 3, "c": 8}) - if assert.NoError(t, err) { - firstNode := jsonDocument.(map[string]any) - assert.Len(t, firstNode, 2) - - sliceNode := firstNode["a"].([]any) - assert.Len(t, sliceNode, 1) - changedNode := sliceNode[0].(map[string]any) - assert.Equal(t, 3, changedNode["b"]) - assert.Equal(t, 8, changedNode["c"]) - } - } - } + require.NoError(t, err) + + _, err = v.Set(jsonDocument, map[string]any{"b": 3, "c": 8}) + require.NoError(t, err) + + firstNode, ok := jsonDocument.(map[string]any) + require.True(t, ok) + assert.Len(t, firstNode, 2) + + sliceNode, ok := firstNode["a"].([]any) + require.True(t, ok) + assert.Len(t, sliceNode, 1) + + changedNode, ok := sliceNode[0].(map[string]any) + require.True(t, ok) + assert.Equal(t, 3, changedNode["b"]) + assert.Equal(t, 8, changedNode["c"]) + }) + + t.Run("with struct", func(t *testing.T) { + var structDoc setJSONDoc + require.NoError(t, json.Unmarshal([]byte(jsonText), &structDoc)) + + t.Run("with set array node", func(t *testing.T) { + g, err := New("/a") + require.NoError(t, err) - var structDoc setJsonDoc - if assert.NoError(t, json.Unmarshal([]byte(jsonText), &structDoc)) { - g, err := New("/a") - if assert.NoError(t, err) { _, err = g.Set(&structDoc, []struct { B int `json:"b"` C int `json:"c"` }{{B: 4, C: 7}}) + require.NoError(t, err) + assert.Len(t, structDoc.A, 1) + changedNode := structDoc.A[0] + assert.Equal(t, 4, changedNode.B) + assert.Equal(t, 7, changedNode.C) + }) - if assert.NoError(t, err) { - assert.Len(t, structDoc.A, 1) - changedNode := structDoc.A[0] - assert.Equal(t, 4, changedNode.B) - assert.Equal(t, 7, changedNode.C) - } - } + t.Run("with set node 0 with struct", func(t *testing.T) { + v, err := New("/a/0") + require.NoError(t, err) - v, err := New("/a/0") - if assert.NoError(t, err) { _, err = v.Set(structDoc, struct { B int `json:"b"` C int `json:"c"` }{B: 3, C: 8}) + require.NoError(t, err) + assert.Len(t, structDoc.A, 1) + changedNode := structDoc.A[0] + assert.Equal(t, 3, changedNode.B) + assert.Equal(t, 8, changedNode.C) + }) - if assert.NoError(t, err) { - assert.Len(t, structDoc.A, 1) - changedNode := structDoc.A[0] - assert.Equal(t, 3, changedNode.B) - assert.Equal(t, 8, changedNode.C) - } - } + t.Run("with set node c with struct", func(t *testing.T) { + p, err := New("/a/0/c") + require.NoError(t, err) - p, err := New("/a/0/c") - if assert.NoError(t, err) { _, err = p.Set(&structDoc, 999) - assert.NoError(t, err) - if assert.Len(t, structDoc.A, 1) { - assert.Equal(t, 999, structDoc.A[0].C) - } - } - } + require.NoError(t, err) + + require.Len(t, structDoc.A, 1) + assert.Equal(t, 999, structDoc.A[0].C) + }) + }) + + t.Run("with Settable", func(t *testing.T) { + var setDoc settableDoc + require.NoError(t, json.Unmarshal([]byte(jsonText), &setDoc)) + + t.Run("with array node a", func(t *testing.T) { + g, err := New("/a") + require.NoError(t, err) - var setDoc settableDoc - if assert.NoError(t, json.Unmarshal([]byte(jsonText), &setDoc)) { - g, err := New("/a") - if assert.NoError(t, err) { _, err = g.Set(&setDoc, []settableCollItem{{B: 4, C: 7}}) + require.NoError(t, err) + assert.Len(t, setDoc.Coll.Items, 1) + changedNode := setDoc.Coll.Items[0] + assert.Equal(t, 4, changedNode.B) + assert.Equal(t, 7, changedNode.C) + }) - if assert.NoError(t, err) { - assert.Len(t, setDoc.Coll.Items, 1) - changedNode := setDoc.Coll.Items[0] - assert.Equal(t, 4, changedNode.B) - assert.Equal(t, 7, changedNode.C) - } - } + t.Run("with node 0", func(t *testing.T) { + v, err := New("/a/0") + require.NoError(t, err) - v, err := New("/a/0") - if assert.NoError(t, err) { _, err = v.Set(setDoc, settableCollItem{B: 3, C: 8}) + require.NoError(t, err) + assert.Len(t, setDoc.Coll.Items, 1) + changedNode := setDoc.Coll.Items[0] + assert.Equal(t, 3, changedNode.B) + assert.Equal(t, 8, changedNode.C) + }) - if assert.NoError(t, err) { - assert.Len(t, setDoc.Coll.Items, 1) - changedNode := setDoc.Coll.Items[0] - assert.Equal(t, 3, changedNode.B) - assert.Equal(t, 8, changedNode.C) - } - } - - p, err := New("/a/0/c") - if assert.NoError(t, err) { + t.Run("with node c", func(t *testing.T) { + p, err := New("/a/0/c") + require.NoError(t, err) _, err = p.Set(setDoc, 999) - assert.NoError(t, err) - if assert.Len(t, setDoc.Coll.Items, 1) { - assert.Equal(t, 999, setDoc.Coll.Items[0].C) - } - } - } + require.NoError(t, err) + require.Len(t, setDoc.Coll.Items, 1) + assert.Equal(t, 999, setDoc.Coll.Items[0].C) + }) + }) } func TestOffset(t *testing.T) { @@ -645,14 +658,16 @@ func TestOffset(t *testing.T) { for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { ptr, err := New(tt.ptr) - assert.NoError(t, err) + require.NoError(t, err) + offset, err := ptr.Offset(tt.input) if tt.hasError { - assert.Error(t, err) + require.Error(t, err) return } + t.Log(offset, err) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, tt.offset, offset) }) } From 6cf0fb884069863aa1d66cc6a783094b09b7d676 Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Sun, 17 Dec 2023 18:34:39 +0100 Subject: [PATCH 07/26] updated to swag v0.22.5 Signed-off-by: Frederic BIDON --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 55b6db0..5d8abdc 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/go-openapi/jsonpointer require ( - github.com/go-openapi/swag v0.22.4 + github.com/go-openapi/swag v0.22.5 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 5666509..d6d4e33 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.5 h1:fVS63IE3M0lsuWRzuom3RLwUMVI2peDH01s6M70ugys= +github.com/go-openapi/swag v0.22.5/go.mod h1:Gl91UqO+btAM0plGGxHqJcQZ1ZTy6jbmridBTsDy8A0= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= From 344388f5b9184a55dea9d36cf96742b69dddeac4 Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Thu, 21 Dec 2023 10:16:35 +0100 Subject: [PATCH 08/26] fix(GetForToken): added support for pointer to interface{} reflect-based switch doesn't work when the value passed is *any: the resulting indirection is typed as reflect.Interface and not the actual underlying type. * added more stringent checking on nil values (i.e. covers interface{}(nil) * contributes: go-swagger/go-swagger#1898 (pointers to content of a x-... swagger extension) Signed-off-by: Frederic BIDON --- pointer.go | 24 ++++++++++++++++++-- pointer_test.go | 59 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/pointer.go b/pointer.go index bed6b81..d975773 100644 --- a/pointer.go +++ b/pointer.go @@ -110,16 +110,36 @@ func SetForToken(document any, decodedToken string, value any) (any, error) { return document, setSingleImpl(document, value, decodedToken, swag.DefaultJSONNameProvider) } +func isNil(input any) bool { + if input == nil { + return true + } + + kind := reflect.TypeOf(input).Kind() + switch kind { //nolint:exhaustive + case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Chan: + return reflect.ValueOf(input).IsNil() + default: + return false + } +} + func getSingleImpl(node any, decodedToken string, nameProvider *swag.NameProvider) (any, reflect.Kind, error) { rValue := reflect.Indirect(reflect.ValueOf(node)) kind := rValue.Kind() + if isNil(node) { + return nil, kind, fmt.Errorf("nil value has not field %q", decodedToken) + } - if rValue.Type().Implements(jsonPointableType) { - r, err := node.(JSONPointable).JSONLookup(decodedToken) + switch typed := node.(type) { + case JSONPointable: + r, err := typed.JSONLookup(decodedToken) if err != nil { return nil, kind, err } return r, kind, nil + case *any: // case of a pointer to interface, that is not resolved by reflect.Indirect + return getSingleImpl(*typed, decodedToken, nameProvider) } switch kind { //nolint:exhaustive diff --git a/pointer_test.go b/pointer_test.go index 5e27ab1..a789154 100644 --- a/pointer_test.go +++ b/pointer_test.go @@ -140,19 +140,56 @@ func TestIsEmpty(t *testing.T) { func TestGetSingle(t *testing.T) { const in = `/obj` - _, err := New(in) - require.NoError(t, err) - result, _, err := GetForToken(testDocumentJSON, "obj") - require.NoError(t, err) - assert.Len(t, result, TestNodeObjNBItems) + t.Run("should create a new JSON pointer", func(t *testing.T) { + _, err := New(in) + require.NoError(t, err) + }) - result, _, err = GetForToken(testStructJSONDoc, "Obj") - require.Error(t, err) - assert.Nil(t, result) + t.Run(`should find token "obj" in JSON`, func(t *testing.T) { + result, _, err := GetForToken(testDocumentJSON, "obj") + require.NoError(t, err) + assert.Len(t, result, TestNodeObjNBItems) + }) - result, _, err = GetForToken(testStructJSONDoc, "Obj2") - require.Error(t, err) - assert.Nil(t, result) + t.Run(`should find token "obj" in type alias interface`, func(t *testing.T) { + type alias interface{} + var in alias = testDocumentJSON + result, _, err := GetForToken(in, "obj") + require.NoError(t, err) + assert.Len(t, result, TestNodeObjNBItems) + }) + + t.Run(`should find token "obj" in pointer to interface`, func(t *testing.T) { + in := &testDocumentJSON + result, _, err := GetForToken(in, "obj") + require.NoError(t, err) + assert.Len(t, result, TestNodeObjNBItems) + }) + + t.Run(`should not find token "Obj" in struct`, func(t *testing.T) { + result, _, err := GetForToken(testStructJSONDoc, "Obj") + require.Error(t, err) + assert.Nil(t, result) + }) + + t.Run(`should not find token "Obj2" in struct`, func(t *testing.T) { + result, _, err := GetForToken(testStructJSONDoc, "Obj2") + require.Error(t, err) + assert.Nil(t, result) + }) + + t.Run(`should not find token in nil`, func(t *testing.T) { + result, _, err := GetForToken(nil, "obj") + require.Error(t, err) + assert.Nil(t, result) + }) + + t.Run(`should not find token in nil interface`, func(t *testing.T) { + var in interface{} + result, _, err := GetForToken(in, "obj") + require.Error(t, err) + assert.Nil(t, result) + }) } type pointableImpl struct { From 8a258df1f0ffd5e356fca0eadd25c51d87e8e9de Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Sun, 24 Dec 2023 19:14:37 +0100 Subject: [PATCH 09/26] fix(ci): muted warnings in CI runs due to cache conflicts Every time a job is posted, I receive false alarm failure notifications because of some cache conflict during the linting job. Reference: https://github.com/golangci/golangci-lint-action/issues/807 Signed-off-by: Frederic BIDON --- .github/workflows/go-test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/go-test.yml b/.github/workflows/go-test.yml index e1a95c7..62682bf 100644 --- a/.github/workflows/go-test.yml +++ b/.github/workflows/go-test.yml @@ -18,6 +18,7 @@ jobs: with: version: latest only-new-issues: true + skip-cache: true test: name: Unit tests @@ -38,7 +39,7 @@ jobs: - uses: actions/checkout@v3 - - run: go test -v -race -coverprofile="coverage-${{ matrix.os }}.${{ matrix.go_version }}.out" -covermode=atomic ./... + - run: go test -v -race -coverprofile="coverage-${{ matrix.os }}.${{ matrix.go_version }}.out" -covermode=atomic -coverpkg=$(go list)/... ./... - name: Upload coverage to codecov uses: codecov/codecov-action@v3 From ca8bdfd65013257385a951094d9a6701f219ce1d Mon Sep 17 00:00:00 2001 From: Neo2308 Date: Tue, 8 Aug 2023 15:37:44 +0530 Subject: [PATCH 10/26] Added dependabot * Added dependabot to keep go dependencies & actions updated. Signed-off-by: Neo2308 --- .github/dependabot.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/dependabot.yaml diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 0000000..adc2943 --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,11 @@ +version: 2 +updates: +- package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "daily" + open-pull-requests-limit: 10 +- package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" From afcd21f902597b96c9e4ec42036e371cf4881aad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 6 Jan 2024 09:33:38 +0000 Subject: [PATCH 11/26] Bump actions/setup-go from 4 to 5 Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4 to 5. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/go-test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/go-test.yml b/.github/workflows/go-test.yml index 62682bf..78c8636 100644 --- a/.github/workflows/go-test.yml +++ b/.github/workflows/go-test.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: go-version: stable check-latest: true @@ -31,7 +31,7 @@ jobs: steps: - name: Run unit tests - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: '${{ matrix.go_version }}' check-latest: true From 70f59d69b9a327667c79adedddcae80799fa5216 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 6 Jan 2024 09:39:39 +0000 Subject: [PATCH 12/26] Bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/go-test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/go-test.yml b/.github/workflows/go-test.yml index 78c8636..728b9e5 100644 --- a/.github/workflows/go-test.yml +++ b/.github/workflows/go-test.yml @@ -7,7 +7,7 @@ jobs: name: Lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: stable @@ -37,7 +37,7 @@ jobs: check-latest: true cache: true - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: go test -v -race -coverprofile="coverage-${{ matrix.os }}.${{ matrix.go_version }}.out" -covermode=atomic -coverpkg=$(go list)/... ./... From 5872b5c2f68d39378f0c977b9d06125045b4e6a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 6 Jan 2024 10:45:42 +0100 Subject: [PATCH 13/26] Bump github.com/go-openapi/swag from 0.22.5 to 0.22.7 (#19) Bumps [github.com/go-openapi/swag](https://github.com/go-openapi/swag) from 0.22.5 to 0.22.7. - [Commits](https://github.com/go-openapi/swag/compare/v0.22.5...v0.22.7) --- updated-dependencies: - dependency-name: github.com/go-openapi/swag dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: fredbi --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5d8abdc..ca099d7 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/go-openapi/jsonpointer require ( - github.com/go-openapi/swag v0.22.5 + github.com/go-openapi/swag v0.22.7 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index d6d4e33..cf658a7 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-openapi/swag v0.22.5 h1:fVS63IE3M0lsuWRzuom3RLwUMVI2peDH01s6M70ugys= -github.com/go-openapi/swag v0.22.5/go.mod h1:Gl91UqO+btAM0plGGxHqJcQZ1ZTy6jbmridBTsDy8A0= +github.com/go-openapi/swag v0.22.7 h1:JWrc1uc/P9cSomxfnsFSVWoE1FW6bNbrVPmpQYpCcR8= +github.com/go-openapi/swag v0.22.7/go.mod h1:Gl91UqO+btAM0plGGxHqJcQZ1ZTy6jbmridBTsDy8A0= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= From 262c7fb9eaeb5402bf3878220d3521cfd3bc12aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 07:13:11 +0100 Subject: [PATCH 14/26] Bump github.com/go-openapi/swag from 0.22.7 to 0.22.8 (#22) Bumps [github.com/go-openapi/swag](https://github.com/go-openapi/swag) from 0.22.7 to 0.22.8. - [Commits](https://github.com/go-openapi/swag/compare/v0.22.7...v0.22.8) --- updated-dependencies: - dependency-name: github.com/go-openapi/swag dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ca099d7..90ef293 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/go-openapi/jsonpointer require ( - github.com/go-openapi/swag v0.22.7 + github.com/go-openapi/swag v0.22.8 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index cf658a7..b6c6df7 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-openapi/swag v0.22.7 h1:JWrc1uc/P9cSomxfnsFSVWoE1FW6bNbrVPmpQYpCcR8= -github.com/go-openapi/swag v0.22.7/go.mod h1:Gl91UqO+btAM0plGGxHqJcQZ1ZTy6jbmridBTsDy8A0= +github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw= +github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= From 6f774b25a4422fb28e6b3c398c1d36df6166666e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 08:14:58 +0100 Subject: [PATCH 15/26] Bump github.com/go-openapi/swag from 0.22.8 to 0.22.9 (#24) Bumps [github.com/go-openapi/swag](https://github.com/go-openapi/swag) from 0.22.8 to 0.22.9. - [Commits](https://github.com/go-openapi/swag/compare/v0.22.8...v0.22.9) --- updated-dependencies: - dependency-name: github.com/go-openapi/swag dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 90ef293..00a90ea 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/go-openapi/jsonpointer require ( - github.com/go-openapi/swag v0.22.8 + github.com/go-openapi/swag v0.22.9 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index b6c6df7..befc31c 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw= -github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI= +github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= +github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= From 60c301f36ecc63622ea360b2347df92da3442786 Mon Sep 17 00:00:00 2001 From: fredbi Date: Sat, 27 Jan 2024 17:57:01 +0100 Subject: [PATCH 16/26] ci: configured auto-approve & auto-merge for dependabot (#23) * All groups are checked once a week and each produce at most 1 PR. * All dependabot PRs are auto-approved Caveats: * this requires auto-merge to be enabled in the repository settings [done] * this requires all desired tests to be required in the branch protection rule [done] - package-ecosystem: "github-actions" # 1. development-dependencies are auto-merged - package-ecosystem: "gomod" # We define 4 groups of dependencies to regroup update pull requests: # - development (e.g. test dependencies) # - go-openapi updates # - golang.org (e.g. golang.org/x/... packages) # - other dependencies (direct or indirect) # # # Auto-merging policy, when requirements are met: # 1. development-dependencies are auto-merged # 2. golang.org-dependencies are auto-merged # 3. go-openapi patch updates are auto-merged. Minor/major version updates require a manual merge. # 4. other dependencies require a manual merge Signed-off-by: Frederic BIDON --- .github/dependabot.yaml | 64 +++++++++++++++++++++++++++----- .github/workflows/auto-merge.yml | 43 +++++++++++++++++++++ 2 files changed, 98 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/auto-merge.yml diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index adc2943..d53b535 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -1,11 +1,57 @@ version: 2 updates: -- package-ecosystem: "gomod" - directory: "/" - schedule: - interval: "daily" - open-pull-requests-limit: 10 -- package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "friday" + open-pull-requests-limit: 2 # <- default is 5 + groups: # <- group all github actions updates in a single PR + # 1. development-dependencies are auto-merged + development-dependencies: + dependency-type: development + patterns: + - '*' + + - package-ecosystem: "gomod" + # We define 4 groups of dependencies to regroup update pull requests: + # - development (e.g. test dependencies) + # - go-openapi updates + # - golang.org (e.g. golang.org/x/... packages) + # - other dependencies (direct or indirect) + # + # * All groups are checked once a week and each produce at most 1 PR. + # * All dependabot PRs are auto-approved + # + # Auto-merging policy, when requirements are met: + # 1. development-dependencies are auto-merged + # 2. golang.org-dependencies are auto-merged + # 3. go-openapi patch updates are auto-merged. Minor/major version updates require a manual merge. + # 4. other dependencies require a manual merge + directory: "/" + schedule: + interval: "weekly" + day: "friday" + open-pull-requests-limit: 4 + groups: + development-dependencies: + dependency-type: development + patterns: + - "github.com/stretchr/testify" + + golang.org-dependencies: + dependency-type: production + patterns: + - "golang.org/*" + + go-openapi-dependencies: + dependency-type: production + patterns: + - "github.com/go-openapi/*" + + other-dependencies: + dependency-type: production + exclude-patterns: + - "github.com/go-openapi/*" + - "github.com/stretchr/testify" + - "golang.org/*" diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml new file mode 100644 index 0000000..7ec23aa --- /dev/null +++ b/.github/workflows/auto-merge.yml @@ -0,0 +1,43 @@ +name: Dependabot auto-merge +on: pull_request + +permissions: + contents: write + pull-requests: write + +jobs: + dependabot: + runs-on: ubuntu-latest + if: github.actor == 'dependabot[bot]' + steps: + - name: Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@v1 + + - name: Auto-approve all dependabot PRs + run: gh pr review --approve "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GH_TOKEN: ${{secrets.GITHUB_TOKEN}} + + - name: Auto-merge dependabot PRs for development dependencies + if: contains(steps.metadata.outputs.dependency-group, 'development-dependencies') + run: gh pr merge --auto --rebase "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GH_TOKEN: ${{secrets.GITHUB_TOKEN}} + + - name: Auto-merge dependabot PRs for go-openapi patches + if: contains(steps.metadata.outputs.dependency-group, 'go-openapi-dependencies') && steps.metadata.outputs.update-type == 'version-update:semver-patch' + run: gh pr merge --auto --rebase "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GH_TOKEN: ${{secrets.GITHUB_TOKEN}} + + - name: Auto-merge dependabot PRs for golang.org updates + if: contains(steps.metadata.outputs.dependency-group, 'golang.org-dependencies') + run: gh pr merge --auto --rebase "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GH_TOKEN: ${{secrets.GITHUB_TOKEN}} + From b635cd3416291a03ccc5e2d572a69522baf84c89 Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Wed, 31 Jan 2024 13:37:20 +0100 Subject: [PATCH 17/26] ci: auto-merge dependabot PRs * enabled auto-merge for go-openapi minor version bumps, not just patches Signed-off-by: Frederic BIDON --- .github/workflows/auto-merge.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index 7ec23aa..b4009db 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -28,7 +28,7 @@ jobs: GH_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Auto-merge dependabot PRs for go-openapi patches - if: contains(steps.metadata.outputs.dependency-group, 'go-openapi-dependencies') && steps.metadata.outputs.update-type == 'version-update:semver-patch' + if: contains(steps.metadata.outputs.dependency-group, 'go-openapi-dependencies') && (steps.metadata.outputs.update-type == 'version-update:semver-minor' || steps.metadata.outputs.update-type == 'version-update:semver-patch') run: gh pr merge --auto --rebase "$PR_URL" env: PR_URL: ${{github.event.pull_request.html_url}} From 5767083822053f5af30ddc7a2db257946bd83b34 Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Wed, 31 Jan 2024 15:11:43 +0100 Subject: [PATCH 18/26] ci: regroup & auto-approve all github actions updates Signed-off-by: Frederic BIDON --- .github/dependabot.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index d53b535..976af1b 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -9,7 +9,6 @@ updates: groups: # <- group all github actions updates in a single PR # 1. development-dependencies are auto-merged development-dependencies: - dependency-type: development patterns: - '*' From 92c7c395ebb740bbbd813ae600dcfa13ed3b7f1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20BIDON?= Date: Thu, 1 Feb 2024 10:57:51 +0100 Subject: [PATCH 19/26] chore(ci): prevents duplicate workflow runs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frédéric BIDON --- .github/workflows/go-test.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/go-test.yml b/.github/workflows/go-test.yml index 728b9e5..e3c7dda 100644 --- a/.github/workflows/go-test.yml +++ b/.github/workflows/go-test.yml @@ -1,6 +1,17 @@ name: go test -on: [push, pull_request] +on: + push: + tags: + - v* + branches: + - master + paths-ignore: + - '**.md' + + pull_request: + paths-ignore: + - '**.md' jobs: lint: From f9d3b1bdbfba91e3186f0639ffcd08bc110e9a25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Feb 2024 02:46:28 +0000 Subject: [PATCH 20/26] Bump the development-dependencies group with 1 update Bumps the development-dependencies group with 1 update: [codecov/codecov-action](https://github.com/codecov/codecov-action). Updates `codecov/codecov-action` from 3 to 4 - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: development-dependencies ... Signed-off-by: dependabot[bot] --- .github/workflows/go-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/go-test.yml b/.github/workflows/go-test.yml index e3c7dda..5910868 100644 --- a/.github/workflows/go-test.yml +++ b/.github/workflows/go-test.yml @@ -53,7 +53,7 @@ jobs: - run: go test -v -race -coverprofile="coverage-${{ matrix.os }}.${{ matrix.go_version }}.out" -covermode=atomic -coverpkg=$(go list)/... ./... - name: Upload coverage to codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: files: './coverage-${{ matrix.os }}.${{ matrix.go_version }}.out' flags: '${{ matrix.go_version }}' From e0c759bbf74bbf8930ae1593d5fb0bb65ad13b8d Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Sat, 3 Feb 2024 18:10:07 +0100 Subject: [PATCH 21/26] ci: remove paths-ignore Using paths-ignore as a short-circuit for doc-only PRs does not work: branch protection rules cannot be passed. Signed-off-by: Frederic BIDON --- .github/workflows/go-test.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/go-test.yml b/.github/workflows/go-test.yml index 5910868..1ffa6c7 100644 --- a/.github/workflows/go-test.yml +++ b/.github/workflows/go-test.yml @@ -6,12 +6,8 @@ on: - v* branches: - master - paths-ignore: - - '**.md' pull_request: - paths-ignore: - - '**.md' jobs: lint: From e344ef7a094c7aa71aa9de88935dcb6f8c9a0652 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Feb 2024 02:54:57 +0000 Subject: [PATCH 22/26] Bump the development-dependencies group with 1 update Bumps the development-dependencies group with 1 update: [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action). Updates `golangci/golangci-lint-action` from 3 to 4 - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](https://github.com/golangci/golangci-lint-action/compare/v3...v4) --- updated-dependencies: - dependency-name: golangci/golangci-lint-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: development-dependencies ... Signed-off-by: dependabot[bot] --- .github/workflows/go-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/go-test.yml b/.github/workflows/go-test.yml index 1ffa6c7..e1c413e 100644 --- a/.github/workflows/go-test.yml +++ b/.github/workflows/go-test.yml @@ -21,7 +21,7 @@ jobs: check-latest: true cache: true - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v4 with: version: latest only-new-issues: true From 9352cad389845ece17bccbd81fe2f467654d2938 Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Mon, 4 Mar 2024 07:54:24 +0100 Subject: [PATCH 23/26] fix(ci): remove dependency-type from dependabot groups dependency-type is irrelevant in the gomod ecosystem. This setting prevented some updates to auto-merge as expected. Signed-off-by: Frederic BIDON --- .github/dependabot.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 976af1b..7dc7009 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -34,22 +34,18 @@ updates: open-pull-requests-limit: 4 groups: development-dependencies: - dependency-type: development patterns: - "github.com/stretchr/testify" golang.org-dependencies: - dependency-type: production patterns: - "golang.org/*" go-openapi-dependencies: - dependency-type: production patterns: - "github.com/go-openapi/*" other-dependencies: - dependency-type: production exclude-patterns: - "github.com/go-openapi/*" - "github.com/stretchr/testify" From 8e6deda34aa4e28e00378e166e9b2c48122752fe Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Mon, 4 Mar 2024 08:24:16 +0100 Subject: [PATCH 24/26] chore(lint): relinted Signed-off-by: Frederic BIDON --- pointer.go | 2 +- pointer_test.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pointer.go b/pointer.go index d975773..d970c7c 100644 --- a/pointer.go +++ b/pointer.go @@ -264,7 +264,7 @@ func (p *Pointer) set(node, data any, nameProvider *swag.NameProvider) error { knd := reflect.ValueOf(node).Kind() if knd != reflect.Ptr && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array { - return fmt.Errorf("only structs, pointers, maps and slices are supported for setting values") + return errors.New("only structs, pointers, maps and slices are supported for setting values") } if nameProvider == nil { diff --git a/pointer_test.go b/pointer_test.go index a789154..6f378e9 100644 --- a/pointer_test.go +++ b/pointer_test.go @@ -94,7 +94,7 @@ func TestEscaping(t *testing.T) { result, _, err := p.Get(testDocumentJSON) require.NoError(t, err, "input: %v", ins[i]) - assert.Equal(t, outs[i], result, "input: %v", ins[i]) + assert.InDeltaf(t, outs[i], result, 1e-6, "input: %v", ins[i]) } } @@ -352,15 +352,15 @@ func TestObject(t *testing.T) { result, _, err := p.Get(testDocumentJSON) require.NoError(t, err) - assert.Equal(t, outs[i], result) + assert.InDelta(t, outs[i], result, 1e-6) result, _, err = p.Get(testStructJSONDoc) require.NoError(t, err) - assert.EqualValues(t, outs[i], result) + assert.InDelta(t, outs[i], result, 1e-6) result, _, err = p.Get(testStructJSONPtr) require.NoError(t, err) - assert.EqualValues(t, outs[i], result) + assert.InDelta(t, outs[i], result, 1e-6) } } From 5d6253e5e1cc5904a3949ce27915e77f14ea8078 Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Mon, 4 Mar 2024 10:51:27 +0100 Subject: [PATCH 25/26] updated dependencies Signed-off-by: Frederic BIDON --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 00a90ea..c6095d0 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module github.com/go-openapi/jsonpointer require ( - github.com/go-openapi/swag v0.22.9 - github.com/stretchr/testify v1.8.4 + github.com/go-openapi/swag v0.22.10 + github.com/stretchr/testify v1.9.0 ) require ( diff --git a/go.sum b/go.sum index befc31c..bc9b73b 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= -github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= +github.com/go-openapi/swag v0.22.10 h1:4y86NVn7Z2yYd6pfS4Z+Nyh3aAUL3Nul+LMbhFKy0gA= +github.com/go-openapi/swag v0.22.10/go.mod h1:Cnn8BYtRlx6BNE3DPN86f/xkapGIcLWzh3CLEb4C1jI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -16,8 +16,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= From 8b546b950409bd7b131488a88613339cd8937b7f Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Sat, 9 Mar 2024 19:16:08 +0100 Subject: [PATCH 26/26] chore(go): go-openapi requires go.1.20 across the board Signed-off-by: Frederic BIDON --- go.mod | 4 ++-- go.sum | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index c6095d0..1563e88 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/go-openapi/jsonpointer require ( - github.com/go-openapi/swag v0.22.10 + github.com/go-openapi/swag v0.23.0 github.com/stretchr/testify v1.9.0 ) @@ -14,4 +14,4 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -go 1.19 +go 1.20 diff --git a/go.sum b/go.sum index bc9b73b..0922c86 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-openapi/swag v0.22.10 h1:4y86NVn7Z2yYd6pfS4Z+Nyh3aAUL3Nul+LMbhFKy0gA= -github.com/go-openapi/swag v0.22.10/go.mod h1:Cnn8BYtRlx6BNE3DPN86f/xkapGIcLWzh3CLEb4C1jI= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=