shurcooL / graphql

Package graphql provides a GraphQL client implementation.
MIT License
708 stars 282 forks source link

Fragments Cannot Be Unmarshaled #58

Closed marcocali closed 4 years ago

marcocali commented 4 years ago

Currently having some issues with fragments.

I am able to correctly send a query with fragments, but the response get lost while unmarshalling. From my understanding, the reason is because queries with fragments and responses have different structures. The response cannot be unmarshalled into the original query.

For example, this query with a fragment:

    type query struct {
        TagSearch struct {
            Error struct {
                Message graphql.String
                Type struct {
                    Class graphql.String
                    ErrorTypeInvalidRequest struct {
                        Fields []struct {
                            Field graphql.String
                            Message graphql.String
                        }
                    }  `graphql:"... on ErrorTypeInvalidRequest"`
                }
            }
            Tags []Tag
        } `graphql:"tagSearch(contextIds: $contextIds, limit: $limit, primary: $primary, public: $public, query: $query)"`
    }

Can only be marshaled to the following struct:

    type query struct {
        TagSearch struct {
            Error struct {
                Message graphql.String
                Type struct {
                    Class graphql.String
                    Fields []struct {
                        Field graphql.String
                        Message graphql.String
                    }
                }
            }
            Tags []Tag
        } `graphql:"tagSearch(contextIds: $contextIds, limit: $limit, primary: $primary, public: $public, query: $query)"`
    }

Where the ErrorTypeInvalidRequest struct was removed.

It is strange because in the README two different structs are actually used. One for the query and one for the response, but this is not mentioned and not actually possible from the library.

fmt.Println(q.Hero.PrimaryFunction) would give you a compilation error. fmt.Println(q.Hero.DroidFragment.PrimaryFunction) is available instead.

marcocali commented 4 years ago

Never mind... this actually works as expected:

func TestFragment(t *testing.T) {
    type myerror struct {
        Message graphql.String
        Type struct {
            Class graphql.String
            ErrorTypeInvalidRequest struct {
                Fields []struct {
                    Field graphql.String
                    Message graphql.String
                }
            }  `graphql:"... on ErrorTypeInvalidRequest"`
        }
    }
    type query struct {
        Error myerror
    }
    var got query
    err := jsonutil.UnmarshalGraphQL([]byte(`{
        "error": {
            "message": "Invalid limit",
            "type": {
                "class": "INVALID_REQUEST",
                "fields": [
                    {
                        "field": "limit",
                        "message": "0 is not between the allowed limit of 1 to 50"
                    }
                ]
            }
        }
    }`), &got)
    if err != nil {
        t.Fatal(err)
    }
    var want query
    want.Error = myerror{
        Message: "Invalid limit",
        Type: struct{Class graphql.String; ErrorTypeInvalidRequest struct{Fields []struct{Field graphql.String; Message graphql.String}} "graphql:\"... on ErrorTypeInvalidRequest\""}{
            Class: "INVALID_REQUEST",
            ErrorTypeInvalidRequest: struct{Fields []struct{Field graphql.String; Message graphql.String}}{
                Fields: []struct{Field graphql.String; Message graphql.String}{
                    {"limit", "0 is not between the allowed limit of 1 to 50"},
                },
            },
        },
    }
    if !reflect.DeepEqual(got, want) {
        t.Error("not equal")
    }
}

This test will run ok.

Delve did not let me inspect Type struct, so I thought it would not unmarshall...