Skip to content

Commit d23aff8

Browse files
authored
Merge pull request #67 from sullivtr/#64-nested-res-data
#64 fix: enhance the query response map to find values nested deep inside…
2 parents 8686e27 + 300a52f commit d23aff8

File tree

3 files changed

+124
-25
lines changed

3 files changed

+124
-25
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Subproject commit 3dcb5028045e697de357f58a69158a14d558e2a3
1+
Subproject commit 4318cae624b3e1737b1b2bb4138b4b1979c70473

graphql/keys.go

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ func getResourceKey(m map[string]interface{}, ks ...string) (val interface{}, er
7070
var obj map[string]interface{}
7171
for i := range items {
7272
if index == int64(i) {
73+
if strValue, ok := items[i].(string); ok {
74+
return strValue, nil
75+
}
7376
if obj, ok = items[i].(map[string]interface{}); !ok {
7477
return nil, fmt.Errorf("malformed structure at provided index: %d", i)
7578
}
@@ -92,34 +95,72 @@ func getResourceKey(m map[string]interface{}, ks ...string) (val interface{}, er
9295
}
9396
}
9497

95-
func mapQueryResponseInputKey(m map[string]interface{}, value, prev string, parentKeys []string) (key string, ok bool) {
96-
for k, v := range m {
97-
var jsonV string
98-
if str, isString := v.(string); !isString {
99-
bytes, err := json.Marshal(v)
100-
if err != nil {
101-
return
98+
func mapQueryResponseInputKey(m interface{}, value, prev string, parentKeys []string) (key string, ok bool) {
99+
if mapObj, isMap := m.(map[string]interface{}); isMap {
100+
for k, v := range mapObj {
101+
var jsonV string
102+
// Check if v is a string, and if not, marshal it as a json string for comparison
103+
if str, isString := v.(string); !isString {
104+
bytes, err := json.Marshal(v)
105+
if err != nil {
106+
return
107+
}
108+
jsonV = string(bytes)
109+
} else {
110+
jsonV = str
102111
}
103-
jsonV = string(bytes)
104-
} else {
105-
jsonV = str
106-
}
107112

108-
if jsonV == value {
109-
if len(parentKeys) != 0 {
110-
newPrev := parentKeys[len(parentKeys)-1]
111-
if newPrev != prev {
112-
parentKeys = parentKeys[:len(parentKeys)-1]
113+
// If jsonV and input value are the same, we have found our match.
114+
if strings.Compare(jsonV, value) == 0 || jsonV == value {
115+
if len(parentKeys) != 0 && !strings.Contains(parentKeys[len(parentKeys)-1], "[") {
116+
newPrev := parentKeys[len(parentKeys)-1]
117+
if newPrev != prev {
118+
parentKeys = parentKeys[:len(parentKeys)-1]
119+
}
113120
}
114-
}
115121

116-
parentKeys = append(parentKeys, k)
117-
key = strings.Join(parentKeys, ".")
118-
ok = true
119-
return
120-
} else if nv, isMap := v.(map[string]interface{}); isMap {
121-
parentKeys = append(parentKeys, k)
122-
key, ok = mapQueryResponseInputKey(nv, value, k, parentKeys)
122+
parentKeys = append(parentKeys, k)
123+
key = strings.Join(parentKeys, ".")
124+
ok = true
125+
return
126+
} else if slice, isSlice := v.([]interface{}); isSlice { // If v is a slice, we need to loop over items in the slice
127+
// key, ok, parentKeys = handleSlice(slice, value, parentKeys, prev)
128+
129+
for i, iv := range slice {
130+
// if the items in the slice are strings, we can simply compare its values
131+
if innerStr, valueIsString := iv.(string); valueIsString {
132+
if innerStr == value {
133+
if len(parentKeys) != 0 && !strings.Contains(parentKeys[len(parentKeys)-1], "[") {
134+
newPrev := parentKeys[len(parentKeys)-1]
135+
if newPrev != prev {
136+
parentKeys = parentKeys[:len(parentKeys)-1]
137+
}
138+
}
139+
parentKeys = append(parentKeys, fmt.Sprintf("%s[%d]", k, i))
140+
key = strings.Join(parentKeys, ".")
141+
ok = true
142+
return
143+
}
144+
} else if innerObj, isObject := iv.(map[string]interface{}); isObject { // If v is an object, traverse the object futher
145+
parentKeys = append(parentKeys, fmt.Sprintf("%s[%d]", k, i))
146+
key, ok = mapQueryResponseInputKey(innerObj, value, k, parentKeys)
147+
if !ok {
148+
parentKeys = parentKeys[:len(parentKeys)-1]
149+
continue
150+
}
151+
return
152+
} else {
153+
parentKeys = parentKeys[:len(parentKeys)-1]
154+
continue
155+
}
156+
}
157+
} else if nv, isMap := v.(map[string]interface{}); isMap {
158+
parentKeys = append(parentKeys, k)
159+
key, ok = mapQueryResponseInputKey(nv, value, k, parentKeys)
160+
if !ok {
161+
continue
162+
}
163+
}
123164
}
124165
}
125166
return

graphql/keys_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ package graphql
33
import (
44
"encoding/json"
55
"fmt"
6+
"strings"
67
"testing"
78

89
"github.com/stretchr/testify/assert"
910
)
1011

12+
var datablob = `{"data": {"someField": "someValue", "items": ["itemValueOne", "itemValueTwo"], "otherItems": [{"field1": "value1", "field2": "value2"}, {"field1": "value3", "field2": "value4"}]}}`
13+
1114
func TestComputeMutationVariableKeys(t *testing.T) {
1215
cases := []struct {
1316
body string
@@ -80,3 +83,58 @@ func TestComputeMutationVariableKeys(t *testing.T) {
8083
}
8184
}
8285
}
86+
87+
func TestMapQueryResponse(t *testing.T) {
88+
var foo map[string]interface{}
89+
err := json.Unmarshal([]byte(datablob), &foo)
90+
if err != nil {
91+
fmt.Println("error:", err)
92+
}
93+
94+
cases := []struct {
95+
value string
96+
expectKey string
97+
}{
98+
{
99+
value: "value1",
100+
expectKey: "data.otherItems[0].field1",
101+
},
102+
{
103+
value: "itemValueOne",
104+
expectKey: "data.items[0]",
105+
},
106+
{
107+
value: "someValue",
108+
expectKey: "data.someField",
109+
},
110+
{
111+
value: "anotherListValue1",
112+
expectKey: "data.funItems[0].anotherList[0]",
113+
},
114+
}
115+
116+
for i, c := range cases {
117+
keyOut, _ := mapQueryResponseInputKey(foo, c.value, "", nil)
118+
assert.Equal(t, c.expectKey, keyOut, "test case %d", i)
119+
ks := strings.Split(keyOut, ".")
120+
_, err = getResourceKey(foo, ks...)
121+
assert.NoError(t, err, "test case %d", i)
122+
}
123+
124+
}
125+
126+
func TestMapQueryResponseJSONString(t *testing.T) {
127+
var foo map[string]interface{}
128+
err := json.Unmarshal([]byte(datablob), &foo)
129+
if err != nil {
130+
fmt.Println("error:", err)
131+
}
132+
133+
items := []string{"itemValueOne", "itemValueTwo"}
134+
jsonV, _ := json.Marshal(items)
135+
itemsValueKey, _ := mapQueryResponseInputKey(foo, string(jsonV), "", nil)
136+
assert.Equal(t, "data.items", itemsValueKey)
137+
ks := strings.Split(itemsValueKey, ".")
138+
_, err = getResourceKey(foo, ks...)
139+
assert.NoError(t, err)
140+
}

0 commit comments

Comments
 (0)