Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Confusing error message when passing non-interface{} slice type for List variable #157

Open
vergenzt opened this issue Feb 2, 2018 · 4 comments

Comments

@vergenzt
Copy link

vergenzt commented Feb 2, 2018

Demo

See https://gist.github.com/vergenzt/d5685cee2c8f80e9d98bca7ef3845173 for example schema/inputs demonstrating this issue.

Issue

When I pass in a concrete slice value (for instance, a []map[string]interface{}) instead of an []interface{} as the value of a GraphQL variable of type [SomeInputType!]!, I get the following panic:

graphql: graphql: panic occurred: interface conversion: interface {} is []map[string]interface {}, not map[string]interface {}

occurring at:
https://github.com/neelance/graphql-go/blob/b46637030579abd312c5eea21d36845b6e9e7ca4/internal/exec/packer/packer.go#L223

and called from:
https://github.com/neelance/graphql-go/blob/b46637030579abd312c5eea21d36845b6e9e7ca4/internal/exec/packer/packer.go#L254

Cause

The issue is caused by: https://github.com/neelance/graphql-go/blob/b46637030579abd312c5eea21d36845b6e9e7ca4/internal/exec/packer/packer.go#L246-L250

If the value passed in for the variable is not exactly of type []interface{}--for instance if it's a slice of a more specific type--then the code above wraps it into []interface{}{ <original_slice_value> } in order to coerce single-value inputs into lists (#41).

However slice types in Go are not covariant: a []myStructType is not assignable to a []interface{}. So this check ends up wrapping the input slice type into another slice, and the attempt to convert the "first element" of the wrapped slice (i.e. the original full slice) into the corresponding inner GraphQL list element type fails.

Proposed fix

Can we just add a better error message if the type of the value passed is a non-interface{} slice type?

@thrasher-redhat
Copy link

thrasher-redhat commented Apr 30, 2018

I believe I've run into the same thing with a []string not satisfying the interface. It feels really ugly, but you can use a *[]string in the resolver args struct to get around this.

Is the correct approach to force everyone to use interface wrappers for any list (and update the error message to better reflect that)? Or should the packer properly handle lists that are not interfaces?

Changing

func (e *listPacker) Pack(value interface{}) (reflect.Value, error) {
list, ok := value.([]interface{})
if !ok {
list = []interface{}{value}
}
to something like the following would work. It would also probably break backwards compatibility for those who work around it by using *[]type though.

func (e *listPacker) Pack(value interface{}) (reflect.Value, error) {
	list, ok := value.([]interface{})
	if !ok {
		list = make([]interface{}, len(value))
		for i := range value {
			list[i] = value[i]
		}
	}

	v := reflect.MakeSlice(e.sliceType, len(list), len(list))
	for i := range list {
		packed, err := e.elem.Pack(list[i])
		if err != nil {
			return reflect.Value{}, err
		}
		v.Index(i).Set(packed)
	}
	return v, nil
}

@thrasher-redhat
Copy link

^Thoughts @neelance ?

@neelance
Copy link
Collaborator

neelance commented May 2, 2018

@thrasher-redhat Sorry, I'm quite busy with bringing WebAssembly to the Go compiler. @tonyghita is the primary maintainer of this project now.

@thrasher-redhat
Copy link

Thanks for the redirect! I just pinged the name on the code =P

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants