Skip to content

Commit d48a9a7

Browse files
shuheiktgwdmitshur
authored andcommitted
internal/jsonutil: Skip unexported fields.
This change makes jsonutil.UnmarshalGraphQL not consider unexported fields when looking for a matching field. This is done because such fields cannot be unmarshaled into, and it's more consistent with behavior of package encoding/json. Document unmarshalValue precondition that v must be addressing and not obtained by the use of unexported fields. That would make it settable, which is a requirement for the needs of unmarshalValue. We arrange the internal jsonutil code so that unmarshalValue is never called on an unsettable reflect.Value. Fixes hasura#36.
1 parent 16b8864 commit d48a9a7

File tree

2 files changed

+22
-6
lines changed

2 files changed

+22
-6
lines changed

internal/jsonutil/graphql.go

+9-6
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func (d *decoder) decode() error {
102102
d.vs[i] = append(d.vs[i], f)
103103
}
104104
if !someFieldExist {
105-
return fmt.Errorf("struct field for %s doesn't exist in any of %v places to unmarshal", key, len(d.vs))
105+
return fmt.Errorf("struct field for %q doesn't exist in any of %v places to unmarshal", key, len(d.vs))
106106
}
107107

108108
// We've just consumed the current token, which was the key.
@@ -252,10 +252,14 @@ func (d *decoder) popAllVs() {
252252
d.vs = nonEmpty
253253
}
254254

255-
// fieldByGraphQLName returns a struct field of struct v that matches GraphQL name,
256-
// or invalid reflect.Value if none found.
255+
// fieldByGraphQLName returns an exported struct field of struct v
256+
// that matches GraphQL name, or invalid reflect.Value if none found.
257257
func fieldByGraphQLName(v reflect.Value, name string) reflect.Value {
258258
for i := 0; i < v.NumField(); i++ {
259+
if v.Type().Field(i).PkgPath != "" {
260+
// Skip unexported field.
261+
continue
262+
}
259263
if hasGraphQLName(v.Type().Field(i), name) {
260264
return v.Field(i)
261265
}
@@ -296,13 +300,12 @@ func isGraphQLFragment(f reflect.StructField) bool {
296300
}
297301

298302
// unmarshalValue unmarshals JSON value into v.
303+
// v must be addressable and not obtained by the use of unexported
304+
// struct fields, otherwise unmarshalValue will panic.
299305
func unmarshalValue(value json.Token, v reflect.Value) error {
300306
b, err := json.Marshal(value) // TODO: Short-circuit (if profiling says it's worth it).
301307
if err != nil {
302308
return err
303309
}
304-
if !v.CanAddr() {
305-
return fmt.Errorf("value %v is not addressable", v)
306-
}
307310
return json.Unmarshal(b, v.Addr().Interface())
308311
}

internal/jsonutil/graphql_test.go

+13
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,19 @@ func TestUnmarshalGraphQL_pointerWithInlineFragment(t *testing.T) {
241241
}
242242
}
243243

244+
func TestUnmarshalGraphQL_unexportedField(t *testing.T) {
245+
type query struct {
246+
foo graphql.String
247+
}
248+
err := jsonutil.UnmarshalGraphQL([]byte(`{"foo": "bar"}`), new(query))
249+
if err == nil {
250+
t.Fatal("got error: nil, want: non-nil")
251+
}
252+
if got, want := err.Error(), "struct field for \"foo\" doesn't exist in any of 1 places to unmarshal"; got != want {
253+
t.Errorf("got error: %v, want: %v", got, want)
254+
}
255+
}
256+
244257
func TestUnmarshalGraphQL_multipleValues(t *testing.T) {
245258
type query struct {
246259
Foo graphql.String

0 commit comments

Comments
 (0)