Skip to content

Commit 8d30e10

Browse files
Merge pull request #4 from evilmonkeyinc/fix/union-missing-error
feat: support union with missing identifiers
2 parents 9f162ad + ccdeb38 commit 8d30e10

File tree

6 files changed

+119
-22
lines changed

6 files changed

+119
-22
lines changed

.github/workflows/release-build.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
name: Release Build
22
on:
33
release:
4-
types: [created]
4+
types: [published]
5+
workflow_dispatch:
6+
57
jobs:
68
releases-matrix:
79
name: Release Go Binary

test/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,11 +271,11 @@ This implementation would be closer to the `Scalar consensus` as it does not alw
271271
|`$['a','a']`|`{"a":1}`|`[1 1]`|`[1 1]`|:white_check_mark:|
272272
|`$[?(@.key<3),?(@.key>6)]`|`[{"key": 1}, {"key": 8}, {"key": 3}, {"key": 10}, {"key": 7}, {"key": 2}, {"key": 6}, {"key": 4}]`|none|`[]`|:question:|
273273
|`$['key','another']`|`{ "key": "value", "another": "entry" }`|`[value entry]`|`[value entry]`|:white_check_mark:|
274-
|`$['missing','key']`|`{ "key": "value", "another": "entry" }`|`[value]`|`nil`|:no_entry:|
274+
|`$['missing','key']`|`{ "key": "value", "another": "entry" }`|`[value]`|`[value]`|:white_check_mark:|
275275
|`$[:]['c','d']`|`[{"c":"cc1","d":"dd1","e":"ee1"},{"c":"cc2","d":"dd2","e":"ee2"}]`|`[cc1 dd1 cc2 dd2]`|`[[cc1 dd1] [cc2 dd2]]`|:no_entry:|
276276
|`$[0]['c','d']`|`[{"c":"cc1","d":"dd1","e":"ee1"},{"c":"cc2","d":"dd2","e":"ee2"}]`|`[cc1 dd1]`|`[cc1 dd1]`|:white_check_mark:|
277277
|`$.*['c','d']`|`[{"c":"cc1","d":"dd1","e":"ee1"},{"c":"cc2","d":"dd2","e":"ee2"}]`|`[cc1 dd1 cc2 dd2]`|`[[cc1 dd1] [cc2 dd2]]`|:no_entry:|
278-
|`$..['c','d']`|`[{"c":"cc1","d":"dd1","e":"ee1"}, {"c": "cc2", "child": {"d": "dd2"}}, {"c": "cc3"}, {"d": "dd4"}, {"child": {"c": "cc5"}}]`|none|`[cc1 dd1]`|:question:|
278+
|`$..['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:|
279279
|`$[4,1]`|`[1,2,3,4,5]`|`[5 2]`|`[5 2]`|:white_check_mark:|
280280
|`$.*[0,:5]`|`{ "a": [ "string", null, true ], "b": [ false, "string", 5.4 ] }`|none|`nil`|:question:|
281281
|`$[1:3,4]`|`[1,2,3,4,5]`|none|`nil`|:question:|

test/union_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@ func Test_Union(t *testing.T) {
4141
expectedError: "",
4242
},
4343
{
44-
query: `$['missing','key']`, // TODO : should we error with invald keys?
44+
query: `$['missing','key']`,
4545
data: `{ "key": "value", "another": "entry" }`,
46-
expected: nil,
46+
expected: []interface{}{"value"},
4747
consensus: []interface{}{"value"},
48-
expectedError: "union: invalid token key 'missing' not found",
48+
expectedError: "",
4949
},
5050
{
5151
query: `$[:]['c','d']`,
@@ -71,7 +71,7 @@ func Test_Union(t *testing.T) {
7171
{
7272
query: `$..['c','d']`,
7373
data: `[{"c":"cc1","d":"dd1","e":"ee1"}, {"c": "cc2", "child": {"d": "dd2"}}, {"c": "cc3"}, {"d": "dd4"}, {"child": {"c": "cc5"}}]`,
74-
expected: []interface{}{"cc1", "dd1"},
74+
expected: []interface{}{"cc1", "dd1", "cc2", "dd2", "cc3", "dd4", "cc5"},
7575
consensus: consensusNone,
7676
expectedError: "",
7777
},
@@ -113,5 +113,5 @@ func Test_Union(t *testing.T) {
113113
}
114114

115115
batchTest(t, tests)
116-
// printConsensusMatrix(tests)
116+
//printConsensusMatrix(tests)
117117
}

token/options.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,7 @@ type Options struct {
2323
AllowMapReferenceByIndexInSubscript bool
2424
// AllowStringReferenceByIndexInSubscript allow string characters to be referenced by index in subscript tokens.
2525
AllowStringReferenceByIndexInSubscript bool
26+
27+
// FailUnionOnInvalidIdentifier force union tokens to fail on missing or invalid keys or invalid index.
28+
FailUnionOnInvalidIdentifier bool
2629
}

token/union.go

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,28 @@ import (
1010
func newUnionToken(arguments []interface{}, options *Options) *unionToken {
1111
allowMap := false
1212
allowString := false
13+
failUnionOnInvalidIdentifier := false
1314

1415
if options != nil {
1516
allowMap = options.AllowMapReferenceByIndex || options.AllowMapReferenceByIndexInUnion
1617
allowString = options.AllowStringReferenceByIndex || options.AllowStringReferenceByIndexInUnion
18+
19+
failUnionOnInvalidIdentifier = options.FailUnionOnInvalidIdentifier
1720
}
1821

1922
return &unionToken{
20-
arguments: arguments,
21-
allowMap: allowMap,
22-
allowString: allowString,
23+
arguments: arguments,
24+
allowMap: allowMap,
25+
allowString: allowString,
26+
failUnionOnInvalidIdentifier: failUnionOnInvalidIdentifier,
2327
}
2428
}
2529

2630
type unionToken struct {
27-
arguments []interface{}
28-
allowMap bool
29-
allowString bool
31+
arguments []interface{}
32+
allowMap bool
33+
allowString bool
34+
failUnionOnInvalidIdentifier bool
3035
}
3136

3237
func (token *unionToken) String() string {
@@ -163,7 +168,7 @@ func (token *unionToken) getUnionByKey(obj interface{}, keys []string) ([]interf
163168
}
164169
}
165170

166-
if len(missingKeys) > 0 {
171+
if token.failUnionOnInvalidIdentifier && len(missingKeys) > 0 {
167172
sort.Strings(missingKeys)
168173
return nil, getInvalidTokenKeyNotFoundError(token.Type(), strings.Join(missingKeys, ","))
169174
}
@@ -183,7 +188,7 @@ func (token *unionToken) getUnionByKey(obj interface{}, keys []string) ([]interf
183188
}
184189
}
185190

186-
if len(missingKeys) > 0 {
191+
if token.failUnionOnInvalidIdentifier && len(missingKeys) > 0 {
187192
sort.Strings(missingKeys)
188193
return nil, getInvalidTokenKeyNotFoundError(token.Type(), strings.Join(missingKeys, ","))
189194
}
@@ -267,7 +272,10 @@ func (token *unionToken) getUnionByIndex(obj interface{}, indices []int64) (inte
267272
idx = length + idx
268273
}
269274
if idx < 0 || idx >= length {
270-
return nil, getInvalidTokenOutOfRangeError(token.Type())
275+
if token.failUnionOnInvalidIdentifier {
276+
return nil, getInvalidTokenOutOfRangeError(token.Type())
277+
}
278+
continue
271279
}
272280

273281
if mapKeys != nil {

token/union_test.go

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@ func Test_newUnionToken(t *testing.T) {
4242
allowString: false,
4343
},
4444
},
45+
{
46+
input: input{
47+
options: &Options{
48+
FailUnionOnInvalidIdentifier: true,
49+
},
50+
},
51+
expected: &unionToken{
52+
allowMap: false,
53+
allowString: false,
54+
failUnionOnInvalidIdentifier: true,
55+
},
56+
},
4557
{
4658
input: input{
4759
options: &Options{
@@ -270,7 +282,7 @@ func Test_UnionToken_Apply(t *testing.T) {
270282
},
271283
},
272284
expected: expected{
273-
err: "union: invalid token out of range",
285+
value: []interface{}{"one", "four"},
274286
},
275287
},
276288
{
@@ -336,7 +348,7 @@ func Test_UnionToken_Apply(t *testing.T) {
336348
},
337349
},
338350
expected: expected{
339-
err: "union: invalid token key 'e' not found",
351+
value: []interface{}{"one", "four"},
340352
},
341353
},
342354
{
@@ -491,12 +503,36 @@ func Test_UnionToken_getUnionByIndex(t *testing.T) {
491503
err: "union: invalid token target. expected [array slice] got [int]",
492504
},
493505
},
506+
{
507+
input: input{
508+
token: &unionToken{
509+
failUnionOnInvalidIdentifier: true,
510+
},
511+
obj: []string{"one", "two", "three"},
512+
keys: []int64{4},
513+
},
514+
expected: expected{
515+
err: "union: invalid token out of range",
516+
},
517+
},
494518
{
495519
input: input{
496520
token: &unionToken{},
497521
obj: []string{"one", "two", "three"},
498522
keys: []int64{4},
499523
},
524+
expected: expected{
525+
obj: []interface{}{},
526+
},
527+
},
528+
{
529+
input: input{
530+
token: &unionToken{
531+
failUnionOnInvalidIdentifier: true,
532+
},
533+
obj: []string{"one", "two", "three"},
534+
keys: []int64{-10},
535+
},
500536
expected: expected{
501537
err: "union: invalid token out of range",
502538
},
@@ -508,7 +544,7 @@ func Test_UnionToken_getUnionByIndex(t *testing.T) {
508544
keys: []int64{-10},
509545
},
510546
expected: expected{
511-
err: "union: invalid token out of range",
547+
obj: []interface{}{},
512548
},
513549
},
514550
{
@@ -703,7 +739,9 @@ func Test_UnionToken_getUnionByKey(t *testing.T) {
703739
},
704740
{
705741
input: input{
706-
token: &unionToken{},
742+
token: &unionToken{
743+
failUnionOnInvalidIdentifier: true,
744+
},
707745
obj: map[string]interface{}{
708746
"a": "one",
709747
"b": "two",
@@ -727,12 +765,46 @@ func Test_UnionToken_getUnionByKey(t *testing.T) {
727765
"d": "four",
728766
"e": "five",
729767
},
768+
keys: []string{"a", "b", "c", "f"},
769+
},
770+
expected: expected{
771+
obj: []interface{}{"one", "two", "three"},
772+
},
773+
},
774+
{
775+
input: input{
776+
token: &unionToken{
777+
failUnionOnInvalidIdentifier: true,
778+
},
779+
obj: map[string]interface{}{
780+
"a": "one",
781+
"b": "two",
782+
"c": "three",
783+
"d": "four",
784+
"e": "five",
785+
},
730786
keys: []string{"a", "b", "c", "f", "one", "blah"},
731787
},
732788
expected: expected{
733789
err: "union: invalid token key 'blah,f,one' not found",
734790
},
735791
},
792+
{
793+
input: input{
794+
token: &unionToken{},
795+
obj: map[string]interface{}{
796+
"a": "one",
797+
"b": "two",
798+
"c": "three",
799+
"d": "four",
800+
"e": "five",
801+
},
802+
keys: []string{"a", "b", "c", "f", "one", "blah"},
803+
},
804+
expected: expected{
805+
obj: []interface{}{"one", "two", "three"},
806+
},
807+
},
736808
{
737809
input: input{
738810
token: &unionToken{},
@@ -764,14 +836,26 @@ func Test_UnionToken_getUnionByKey(t *testing.T) {
764836
},
765837
},
766838
},
839+
{
840+
input: input{
841+
token: &unionToken{
842+
failUnionOnInvalidIdentifier: true,
843+
},
844+
obj: sampleStruct{},
845+
keys: []string{"missing", "gone"},
846+
},
847+
expected: expected{
848+
err: "union: invalid token key 'gone,missing' not found",
849+
},
850+
},
767851
{
768852
input: input{
769853
token: &unionToken{},
770854
obj: sampleStruct{},
771855
keys: []string{"missing", "gone"},
772856
},
773857
expected: expected{
774-
err: "union: invalid token key 'gone,missing' not found",
858+
obj: []interface{}{},
775859
},
776860
},
777861
{

0 commit comments

Comments
 (0)