machinebox / graphql

Simple low-level GraphQL HTTP client for Go
https://blog.machinebox.io/a-graphql-client-library-for-go-5bffd0455878
Apache License 2.0
933 stars 217 forks source link

Empty response object #32

Closed marticrespi closed 5 years ago

marticrespi commented 5 years ago

Hi, after a few hours investigating what is happening, I can't find the problem.

Here is my full code, I have used that website to convert a response in struct from a web interface response (playground).

https://mholt.github.io/json-to-go/

import (
        "fmt"
    "github.com/machinebox/graphql"
)

// StatsResponse structure to parse a stats response.
type StatsResponse struct {
    Data struct {
        Stats struct {
            Report struct {
                Edges []struct {
                    Node struct {
                        StatsData struct {
                            Access struct {
                                AccessData struct {
                                    Code     string `json:"code"`
                                    Name     string `json:"name"`
                                    Supplier struct {
                                        SupplierData struct {
                                            Code string `json:"code"`
                                            Name string `json:"name"`
                                        } `json:"supplierData"`
                                    } `json:"supplier"`
                                } `json:"accessData"`
                            } `json:"access"`
                            Client struct {
                                ClientData struct {
                                    Code string `json:"code"`
                                    Name string `json:"name"`
                                } `json:"clientData"`
                            } `json:"client"`
                            Operations []struct {
                                Operation struct {
                                    OperationData struct {
                                        Code  string   `json:"code"`
                                        Types []string `json:"types"`
                                        API   struct {
                                            Code string `json:"code"`
                                        } `json:"api"`
                                    } `json:"operationData"`
                                } `json:"operation"`
                                TotalHits    int    `json:"totalHits"`
                                TrafficType  string `json:"trafficType"`
                                DetailedHits []struct {
                                    Code string `json:"code"`
                                    Type string `json:"type"`
                                    Hits int    `json:"hits"`
                                    Time int    `json:"time"`
                                } `json:"detailedHits"`
                            } `json:"operations"`
                        } `json:"statsData"`
                        AdviseMessage []struct {
                            Code        int    `json:"code"`
                            Description string `json:"description"`
                            Level       string `json:"level"`
                            External    struct {
                                Code    string `json:"code"`
                                Message string `json:"message"`
                            } `json:"external"`
                        } `json:"adviseMessage"`
                    } `json:"node"`
                } `json:"edges"`
            } `json:"report"`
        } `json:"stats"`
    } `json:"data"`
}

type filterInput struct {
    From        time.Time `json:"from"`
    To          time.Time `json:"to"`
    Mode        string    `json:"mode"`
    Owners      []string  `json:"owners"`
    Clients     []string  `json:"clients"`
    Suppliers   []string  `json:"suppliers"`
    ServiceApis []string  `json:"serviceApis"`
    Operation   []string  `json:"operation"`
    Groups      []string  `json:"groups"`
}

client := graphql.NewClient("https://api.xxxxxxx.com")

    req := graphql.NewRequest(`
    query QueryStatsDetails($filterInput: StatsFilterInput!) {
        stats {
            report(filter: $filterInput) {
                edges {
                    node {
                        statsData {
                            access {
                                accessData {
                                    code
                                    name
                                    supplier {
                                        supplierData {
                                            code
                                            name
                                        }
                                    }
                                }
                            }
                            client {
                                clientData {
                                    code
                                    name
                                }
                            }
                            operations {
                                operation {
                                    operationData {
                                        code
                                        types
                                        api {
                                            code
                                        }
                                    }
                                }
                                totalHits
                                trafficType
                                detailedHits {
                                    code
                                    type
                                    hits
                                    time
                                }
                            }
                        }
                        adviseMessage {
                            code
                            description
                            level
                            external {
                                code
                                message
                            }
                        }
                    }
                }
            }
        }
    }   
    `)

    var filterInputBuyer = filterInput{
        From:   time.Date(2019, time.April, 30, 0, 0, 0, 0, time.UTC),
        To: time.Date(2019, time.April, 30, 23, 59, 59, 0, time.UTC),
        Mode:        "BUYER",
        Clients:     make([]string, 0, 1),
        ServiceApis: make([]string, 0, 8),
        Operation:   make([]string, 0, 4),
        Groups:      make([]string, 0, 1),
    }

    filterInputBuyer.Clients = append(filterInputBuyer.Clients, "cli8")
    filterInputBuyer.Groups = append(filterInputBuyer.Groups, "XXXXXX")
    filterInputBuyer.Operation = append(filterInputBuyer.Operation, "SEARCH", "QUOTE", "BOOKING", "OTHER")
    filterInputBuyer.ServiceApis = append(filterInputBuyer.ServiceApis, "ACTIVITIES", "CAR", "HOTEL", "PACKAGE", "PAYMENT", "TRANSFERS", "TRANSPORTATION", "VIRTUALACCOUNT")

    // Set authorization bearer
    req.Header.Add("Authorization", header)

    req.Var("filterInput", filterInputBuyer)

    var res StatsResponse
    ctx := context.Background()
    if err := client.Run(ctx, req, &res); err != nil {
        log.Fatal(err)
    }
    fmt.Println("response", res)

I'm always receiving an empty res object, and if I send it through playground works perfectly. What am I doing wrong? Someone can help me?

Many thanks!

johnknelsonintific commented 4 years ago

Did you ever figure this out? I am also unable to unmarshall my response into my res object, despite getting the correct HTTP response. The runWithJSON method in graphql.go just straight up doesn't deserialize the buffer into my response object correctly and I have no idea why.

JimS-wex commented 4 years ago

I am having a similar issue, deserializing into a map[string]interface{} shows the field names correctly, but there are no values.

dmuntean commented 4 years ago

This library has Data structure defined in the response in itself: https://github.com/machinebox/graphql/blob/3a92531802258604bd12793465c2e28bc4b2fc85/graphql.go#L260-L263

which it assigns the "data" object from received json: https://github.com/machinebox/graphql/blob/3a92531802258604bd12793465c2e28bc4b2fc85/graphql.go#L113-L115

So to be able to unmarshal the response to a structure, you need to pass it the Data struct and not the outer, enclosing struct.

In @marticrespi example, remove the enclosing StatsResponse struct and pass Data struct to client.Run:

    var res Data
    ctx := context.Background()
    if err := client.Run(ctx, req, &res); err != nil {
        log.Fatal(err)
    }