diff --git a/.github/workflows/release-build.yaml b/.github/workflows/release-build.yaml index 013e9c2..7c978f9 100644 --- a/.github/workflows/release-build.yaml +++ b/.github/workflows/release-build.yaml @@ -1,7 +1,9 @@ name: Release Build on: release: - types: [created] + types: [published] + workflow_dispatch: + jobs: releases-matrix: name: Release Go Binary diff --git a/test/README.md b/test/README.md index dc74b15..c0fc643 100644 --- a/test/README.md +++ b/test/README.md @@ -271,11 +271,11 @@ This implementation would be closer to the `Scalar consensus` as it does not alw |`$['a','a']`|`{"a":1}`|`[1 1]`|`[1 1]`|:white_check_mark:| |`$[?(@.key<3),?(@.key>6)]`|`[{"key": 1}, {"key": 8}, {"key": 3}, {"key": 10}, {"key": 7}, {"key": 2}, {"key": 6}, {"key": 4}]`|none|`[]`|:question:| |`$['key','another']`|`{ "key": "value", "another": "entry" }`|`[value entry]`|`[value entry]`|:white_check_mark:| -|`$['missing','key']`|`{ "key": "value", "another": "entry" }`|`[value]`|`nil`|:no_entry:| +|`$['missing','key']`|`{ "key": "value", "another": "entry" }`|`[value]`|`[value]`|:white_check_mark:| |`$[:]['c','d']`|`[{"c":"cc1","d":"dd1","e":"ee1"},{"c":"cc2","d":"dd2","e":"ee2"}]`|`[cc1 dd1 cc2 dd2]`|`[[cc1 dd1] [cc2 dd2]]`|:no_entry:| |`$[0]['c','d']`|`[{"c":"cc1","d":"dd1","e":"ee1"},{"c":"cc2","d":"dd2","e":"ee2"}]`|`[cc1 dd1]`|`[cc1 dd1]`|:white_check_mark:| |`$.*['c','d']`|`[{"c":"cc1","d":"dd1","e":"ee1"},{"c":"cc2","d":"dd2","e":"ee2"}]`|`[cc1 dd1 cc2 dd2]`|`[[cc1 dd1] [cc2 dd2]]`|:no_entry:| -|`$..['c','d']`|`[{"c":"cc1","d":"dd1","e":"ee1"}, {"c": "cc2", "child": {"d": "dd2"}}, {"c": "cc3"}, {"d": "dd4"}, {"child": {"c": "cc5"}}]`|none|`[cc1 dd1]`|:question:| +|`$..['c','d']`|`[{"c":"cc1","d":"dd1","e":"ee1"}, {"c": "cc2", "child": {"d": "dd2"}}, {"c": "cc3"}, {"d": "dd4"}, {"child": {"c": "cc5"}}]`|none|`[cc1 dd1 cc2 dd2 cc3 dd4 cc5]`|:question:| |`$[4,1]`|`[1,2,3,4,5]`|`[5 2]`|`[5 2]`|:white_check_mark:| |`$.*[0,:5]`|`{ "a": [ "string", null, true ], "b": [ false, "string", 5.4 ] }`|none|`nil`|:question:| |`$[1:3,4]`|`[1,2,3,4,5]`|none|`nil`|:question:| diff --git a/test/union_test.go b/test/union_test.go index ef52e17..e894540 100644 --- a/test/union_test.go +++ b/test/union_test.go @@ -41,11 +41,11 @@ func Test_Union(t *testing.T) { expectedError: "", }, { - query: `$['missing','key']`, // TODO : should we error with invald keys? + query: `$['missing','key']`, data: `{ "key": "value", "another": "entry" }`, - expected: nil, + expected: []interface{}{"value"}, consensus: []interface{}{"value"}, - expectedError: "union: invalid token key 'missing' not found", + expectedError: "", }, { query: `$[:]['c','d']`, @@ -71,7 +71,7 @@ func Test_Union(t *testing.T) { { query: `$..['c','d']`, data: `[{"c":"cc1","d":"dd1","e":"ee1"}, {"c": "cc2", "child": {"d": "dd2"}}, {"c": "cc3"}, {"d": "dd4"}, {"child": {"c": "cc5"}}]`, - expected: []interface{}{"cc1", "dd1"}, + expected: []interface{}{"cc1", "dd1", "cc2", "dd2", "cc3", "dd4", "cc5"}, consensus: consensusNone, expectedError: "", }, @@ -113,5 +113,5 @@ func Test_Union(t *testing.T) { } batchTest(t, tests) - // printConsensusMatrix(tests) + //printConsensusMatrix(tests) } diff --git a/token/options.go b/token/options.go index 2cd9dfb..3fdae5e 100644 --- a/token/options.go +++ b/token/options.go @@ -23,4 +23,7 @@ type Options struct { AllowMapReferenceByIndexInSubscript bool // AllowStringReferenceByIndexInSubscript allow string characters to be referenced by index in subscript tokens. AllowStringReferenceByIndexInSubscript bool + + // FailUnionOnInvalidIdentifier force union tokens to fail on missing or invalid keys or invalid index. + FailUnionOnInvalidIdentifier bool } diff --git a/token/union.go b/token/union.go index 73389ea..e088d3d 100644 --- a/token/union.go +++ b/token/union.go @@ -10,23 +10,28 @@ import ( func newUnionToken(arguments []interface{}, options *Options) *unionToken { allowMap := false allowString := false + failUnionOnInvalidIdentifier := false if options != nil { allowMap = options.AllowMapReferenceByIndex || options.AllowMapReferenceByIndexInUnion allowString = options.AllowStringReferenceByIndex || options.AllowStringReferenceByIndexInUnion + + failUnionOnInvalidIdentifier = options.FailUnionOnInvalidIdentifier } return &unionToken{ - arguments: arguments, - allowMap: allowMap, - allowString: allowString, + arguments: arguments, + allowMap: allowMap, + allowString: allowString, + failUnionOnInvalidIdentifier: failUnionOnInvalidIdentifier, } } type unionToken struct { - arguments []interface{} - allowMap bool - allowString bool + arguments []interface{} + allowMap bool + allowString bool + failUnionOnInvalidIdentifier bool } func (token *unionToken) String() string { @@ -163,7 +168,7 @@ func (token *unionToken) getUnionByKey(obj interface{}, keys []string) ([]interf } } - if len(missingKeys) > 0 { + if token.failUnionOnInvalidIdentifier && len(missingKeys) > 0 { sort.Strings(missingKeys) return nil, getInvalidTokenKeyNotFoundError(token.Type(), strings.Join(missingKeys, ",")) } @@ -183,7 +188,7 @@ func (token *unionToken) getUnionByKey(obj interface{}, keys []string) ([]interf } } - if len(missingKeys) > 0 { + if token.failUnionOnInvalidIdentifier && len(missingKeys) > 0 { sort.Strings(missingKeys) return nil, getInvalidTokenKeyNotFoundError(token.Type(), strings.Join(missingKeys, ",")) } @@ -267,7 +272,10 @@ func (token *unionToken) getUnionByIndex(obj interface{}, indices []int64) (inte idx = length + idx } if idx < 0 || idx >= length { - return nil, getInvalidTokenOutOfRangeError(token.Type()) + if token.failUnionOnInvalidIdentifier { + return nil, getInvalidTokenOutOfRangeError(token.Type()) + } + continue } if mapKeys != nil { diff --git a/token/union_test.go b/token/union_test.go index 6a300af..f461da5 100644 --- a/token/union_test.go +++ b/token/union_test.go @@ -42,6 +42,18 @@ func Test_newUnionToken(t *testing.T) { allowString: false, }, }, + { + input: input{ + options: &Options{ + FailUnionOnInvalidIdentifier: true, + }, + }, + expected: &unionToken{ + allowMap: false, + allowString: false, + failUnionOnInvalidIdentifier: true, + }, + }, { input: input{ options: &Options{ @@ -270,7 +282,7 @@ func Test_UnionToken_Apply(t *testing.T) { }, }, expected: expected{ - err: "union: invalid token out of range", + value: []interface{}{"one", "four"}, }, }, { @@ -336,7 +348,7 @@ func Test_UnionToken_Apply(t *testing.T) { }, }, expected: expected{ - err: "union: invalid token key 'e' not found", + value: []interface{}{"one", "four"}, }, }, { @@ -491,12 +503,36 @@ func Test_UnionToken_getUnionByIndex(t *testing.T) { err: "union: invalid token target. expected [array slice] got [int]", }, }, + { + input: input{ + token: &unionToken{ + failUnionOnInvalidIdentifier: true, + }, + obj: []string{"one", "two", "three"}, + keys: []int64{4}, + }, + expected: expected{ + err: "union: invalid token out of range", + }, + }, { input: input{ token: &unionToken{}, obj: []string{"one", "two", "three"}, keys: []int64{4}, }, + expected: expected{ + obj: []interface{}{}, + }, + }, + { + input: input{ + token: &unionToken{ + failUnionOnInvalidIdentifier: true, + }, + obj: []string{"one", "two", "three"}, + keys: []int64{-10}, + }, expected: expected{ err: "union: invalid token out of range", }, @@ -508,7 +544,7 @@ func Test_UnionToken_getUnionByIndex(t *testing.T) { keys: []int64{-10}, }, expected: expected{ - err: "union: invalid token out of range", + obj: []interface{}{}, }, }, { @@ -703,7 +739,9 @@ func Test_UnionToken_getUnionByKey(t *testing.T) { }, { input: input{ - token: &unionToken{}, + token: &unionToken{ + failUnionOnInvalidIdentifier: true, + }, obj: map[string]interface{}{ "a": "one", "b": "two", @@ -727,12 +765,46 @@ func Test_UnionToken_getUnionByKey(t *testing.T) { "d": "four", "e": "five", }, + keys: []string{"a", "b", "c", "f"}, + }, + expected: expected{ + obj: []interface{}{"one", "two", "three"}, + }, + }, + { + input: input{ + token: &unionToken{ + failUnionOnInvalidIdentifier: true, + }, + obj: map[string]interface{}{ + "a": "one", + "b": "two", + "c": "three", + "d": "four", + "e": "five", + }, keys: []string{"a", "b", "c", "f", "one", "blah"}, }, expected: expected{ err: "union: invalid token key 'blah,f,one' not found", }, }, + { + input: input{ + token: &unionToken{}, + obj: map[string]interface{}{ + "a": "one", + "b": "two", + "c": "three", + "d": "four", + "e": "five", + }, + keys: []string{"a", "b", "c", "f", "one", "blah"}, + }, + expected: expected{ + obj: []interface{}{"one", "two", "three"}, + }, + }, { input: input{ token: &unionToken{}, @@ -764,6 +836,18 @@ func Test_UnionToken_getUnionByKey(t *testing.T) { }, }, }, + { + input: input{ + token: &unionToken{ + failUnionOnInvalidIdentifier: true, + }, + obj: sampleStruct{}, + keys: []string{"missing", "gone"}, + }, + expected: expected{ + err: "union: invalid token key 'gone,missing' not found", + }, + }, { input: input{ token: &unionToken{}, @@ -771,7 +855,7 @@ func Test_UnionToken_getUnionByKey(t *testing.T) { keys: []string{"missing", "gone"}, }, expected: expected{ - err: "union: invalid token key 'gone,missing' not found", + obj: []interface{}{}, }, }, {