hasura / go-graphql-client

Package graphql provides a GraphQL client implementation.
MIT License
395 stars 91 forks source link

jsonb type declaration #112

Closed MohamedKHALILRouissi closed 9 months ago

MohamedKHALILRouissi commented 9 months ago

{ "uuid": "efgh-5678-abcd-1234", "network": "avalanche", "address": "0x9876543210987654321098765432109876543210", "rulechain": false, "topics": [ "0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d", "0x5e4f3d2c1b0a99887766554433221100" ] }

i have data to be send like this func QuerySchema(sc Variables_schema) (int, error) { / query MyQuery($ca: String, $nt: String , $tp: jsonb) { schema_event(where: {topics: {_contained_in: $tp}, contractaddress: {_eq: $ca}, network: {_eq: $nt}}) { id } } /

jsondata, err := json.Marshal(sc.Topics)
if err != nil {
    return 0, err
}

client := startgraphqlconnection()
var query struct {
    SchemaEvent []struct {
        ID int `json:"id"`
    } `graphql:"schema_event(where: {contractaddress: {_eq: $ca}, network: {_eq: $nt}})"`
}
variables := map[string]interface{}{
    "ca": sc.ContractAddress,
    "nt": sc.Network,
    "tp": string(jsondata),
}

fmt.Print(variables)
if err := client.Query(context.Background(), &query, variables); err != nil {
    fmt.Print(err)
    return 0, err
} else {
    if len(query.SchemaEvent) == 0 {
        // not found logic
        return 0, nil
    } else {
        // found logic
        fmt.Print(query)
        return query.SchemaEvent[0].ID, nil
    }
}

}

am always getting this return map[ca:0x9876543210987654321098765432109876543210 nt:avalanche tp:["0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6","0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d","0x5e4f3d2c1b0a99887766554433221100"]]Message: unexpected variables in variableValues: tp, Locations: [], Extensions: map[code:validation-failed path:$]inside the qyeryschema[GIN] 2023/11/21 - 13:41:21 | 500 | 558.581868ms | ::1 | POST "/api/v1/subscribe"

hgiasac commented 9 months ago
var query struct {
    SchemaEvent []struct {
        ID int `json:"id"`
    } `graphql:"schema_event(where: {contractaddress: {_eq: $ca}, network: {_eq: $nt}})"`
}
variables := map[string]interface{}{
    "ca": sc.ContractAddress,
    "nt": sc.Network,
    "tp": string(jsondata), // this variable is redundant 
}

The query struct only has 2 variables $ca and $nt. the $tp variable should be removed

MohamedKHALILRouissi commented 9 months ago

thanks for response , but am trying to search in the graphql based on those topics to narrow the research and find duplicated event with the same topics

MohamedKHALILRouissi commented 9 months ago

sorry for the mistake the graph should be like this `graphql:"schema_event(where: { topics: {_contained_in: $tp}, contractaddress: {_eq: $ca}, network: {_eq: $nt}})" `where $tp is jsonb

MohamedKHALILRouissi commented 9 months ago

{efgh-5678-abcd-1234 avalanche 0x1234567890123456789012345678901234567890 false [0x1111111111111111111111111111111111111111111111111111111111111111 0x2222222222222222222222222222222222222222222222222222222222222222 0x5e4f3d2c1b0a9988776d6554433221100]} map[ca:0x1234567890123456789012345678901234567890 nt:avalanche tp:["0x1111111111111111111111111111111111111111111111111111111111111111","0x2222222222222222222222222222222222222222222222222222222222222222","0x5e4f3d2c1b0a9988776d6554433221100"]]Message: variable 'tp' is declared as 'String!', but used where 'jsonb' is expected, Locations: [], Extensions: map[code:validation-failed path:$.selectionSet.schema_event.args.where.topics._contained_in]inside the qyeryschema[GIN] 2023/11/21 - 16:07:54 | 500 | 649.984312ms | ::1 | POST "/api/v1/subscribe"

MohamedKHALILRouissi commented 9 months ago

hasura test query MyQuery($ca: String, $nt: String, $tp: jsonb) { schema_event(where: {topics: {_contained_in: $tp}, contractaddress: {_eq: $ca}, network: {_eq: $nt}}) { id } } ------- data {"ca": "0x1234567890123456789012345678901234567890", "nt": "eth", "tp": [ "0x1111111111111111111111111111111111111111111111111111111111111113","0x3333333333333333333333333333333333333333333333333333333333333333","0x2222222222222222222222222222222222222222222222222222222222222222" ] }

------ return { "data": { "schema_event": [ { "id": 3 } ] } }

hgiasac commented 9 months ago

Please format the code to be easy to debug. You need to define a type alias so the library can convert it to GraphQL type.

type jsonb interface{}

variables := map[string]interface{}{
    "ca": sc.ContractAddress,
    "nt": sc.Network,
    "tp": jsonb(sc.Topics),
}

No need to marshal the slice to bytes. The library will encode it later

MohamedKHALILRouissi commented 9 months ago

`package graphql

import ( "context" "fmt" "net/http"

"github.com/hasura/go-graphql-client"

)

// first we need to query the schema of the event

// if found add the user uuid to a schema mapping

// if not found add the schema

type Variables_schema struct { ID int json:"id" // Assuming you're setting this on insert or update ContractAddress string json:"contractaddress" Topics []string json:"topics" Network string json:"network" } type Variables_subscription struct { User string json:"user" Event_id int json:"event_id" }

const ( serverEndpoint = "xxxxxxxx" adminSecret = "xxxxx" xHasuraAdminSecret = "x-hasura-admin-secret" )

type headerRoundTripper struct { setHeaders func(req *http.Request) rt http.RoundTripper }

func (h headerRoundTripper) RoundTrip(req http.Request) (http.Response, error) { h.setHeaders(req) return h.rt.RoundTrip(req) }

func startgraphqlconnection() *graphql.Client {

client := graphql.NewClient(serverEndpoint, &http.Client{
    Transport: headerRoundTripper{
        setHeaders: func(req *http.Request) {
            req.Header.Set(xHasuraAdminSecret, adminSecret)
        },
        rt: http.DefaultTransport,
    },
})

return client

}

func InsertSchema(sc Variables_schema) (int, error) { / mutation MyMutation($ca: String , $nt: String , $tp: json) { insert_schema_event(objects: {contractaddress: $ca, network: $nt, topics: $tp}) { returning { id } } } /

// start graphql connection
client := startgraphqlconnection()

// create the returning mutation schema
var mutation struct {
    InsertSchemaEvent struct {
        Returning []struct {
            ID int `json:"id"`
        } `json:"returning"`
    } `graphql:"insert_schema_event(objects: {id: $id, contractaddress: $contractaddress, topics: $topics, network: $network})"`
}
// Define your variables
variables := map[string]interface{}{
    "id":              sc.ID,
    "contractaddress": sc.ContractAddress,
    "topics":          sc.Topics,
    "network":         sc.Network,
}
// Run the mutation

err := client.Mutate(context.Background(), &mutation, variables)
if err != nil {
    // error inserting
    return 0, err
} else {
    if len(mutation.InsertSchemaEvent.Returning) == 0 {
        return 0, fmt.Errorf("no data returned")
    }
    // success insert and return id
    id := mutation.InsertSchemaEvent.Returning[0].ID
    return id, nil
}

}

func QuerySchema(sc Variables_schema) (int, error) { / query MyQuery($ca: String, $nt: String , $tp: jsonb) { schema_event(where: {topics: {_contained_in: $tp}, contractaddress: {_eq: $ca}, network: {_eq: $nt}}) { id } } /

client := startgraphqlconnection()
var query struct {
    SchemaEvent []struct {
        ID int `json:"id"`
    } `graphql:"schema_event(where: {topics: {_contained_in: $tp},contractaddress: {_eq: $ca}, network: {_eq: $nt}})"`
}
type jsonb interface{}

variables := map[string]interface{}{
    "ca": sc.ContractAddress,
    "nt": sc.Network,
    "tp": jsonb(sc.Topics), // this variable is redundant
}

fmt.Print(variables)
if err := client.Query(context.Background(), &query, variables); err != nil {
    fmt.Print(err)
    return 0, err
} else {
    if len(query.SchemaEvent) == 0 {
        fmt.Print("am here")
        // not found logic
        return 0, nil
    } else {
        fmt.Print("am here 2")
        fmt.Print(query)
        return query.SchemaEvent[0].ID, nil
    }
}

}

func InsertUserEventSubscription(sc Variables_subscription) (string, int, error) { client := startgraphqlconnection()

var mutation struct {
    InsertUserEventSubscription struct {
        Returning []struct {
            EventUUID string `json:"event_uuid"`
            ID        int    `json:"id"`
        } `json:"returning"`
    } `graphql:"insert_user_event_subscription(objects: {user: $user, event_id: $event_id})"`
}

variables := map[string]interface{}{
    "user":     sc.User,
    "event_id": sc.Event_id,
}

if err := client.Mutate(context.Background(), &mutation, variables); err != nil {
    return "", 0, err
}

// Check if the mutation has any returning results
if len(mutation.InsertUserEventSubscription.Returning) == 0 {
    return "", 0, fmt.Errorf("no data returned")
}

// Access the fields from the first element of the returning slice
eventUUID := mutation.InsertUserEventSubscription.Returning[0].EventUUID
id := mutation.InsertUserEventSubscription.Returning[0].ID

return eventUUID, id, nil

}

`

am still getting the error {efgh-5678-abcd-1234 avalanche 0x1234567890123456789012345678901234567890 false [0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6 0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d 0x5e4f3d2c1b0a9988776d6554433221100]} map[ca:0x1234567890123456789012345678901234567890 nt:avalanche tp:[0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6 0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d 0x5e4f3d2c1b0a9988776d6554433221100]]Message: variable 'tp' is declared as '[String!]!', but used where 'jsonb' is expected, Locations: [], Extensions: map[code:validation-failed path:$.selectionSet.schema_event.args.where.topics._contained_in]inside the qyeryschema[GIN] 2023/11/21 - 16:32:11 | 500 | 546.184897ms | ::1 | POST "/api/v1/subscribe"

am using the same data as always { "uuid": "efgh-5678-abcd-1234", "network": "avalanche", "address": "0x1234567890123456789012345678901234567890", "rulechain": false, "topics": [ "0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d", "0x5e4f3d2c1b0a9988776d6554433221100" ] }

MohamedKHALILRouissi commented 9 months ago

sorry about the formatting am not use to it and thanks for you time again sir

hgiasac commented 9 months ago

You may be wrong about the _contained_in operator https://hasura.io/docs/latest/queries/postgres/filters/jsonb-operators/#_contained_in

To avoid confusion about inner types, you can use the type of where object that is usually <table>_bool_exp

var query struct {
    SchemaEvent []struct {
        ID int `json:"id"`
    } `graphql:"schema_event(where: $where)"`
}

type schema_event_bool_exp map[string]interface{}

variables := map[string]interface{} {
    "where": schema_event_bool_exp{
        "topics": map[string]interface{} {
            "_contained_in": map[string]interface{} {
                "value": sc.Topics,
            },
        },
        "contractaddress": map[string]interface{}{
            "_eq": sc.ContractAddress
        }, 
        "network": {
            "_eq": sc.Network
        }
    }
}
MohamedKHALILRouissi commented 9 months ago
variables := map[string]interface{}{
    "where": schema_event_bool_exp{
        "topics": map[string]interface{}{
            "_contained_in": map[string]interface{}{
                "value": sc.Topics,
            },
        },
        "contractaddress": map[string]interface{}{
            "_eq": sc.ContractAddress,
        },
        "network": map[string]interface{}{
            "_eq": sc.Network,
        },
    },
}

is this the right declaration of variables? 
hgiasac commented 9 months ago

Yes, this is. It is equivalent to the below query

query MyQuery($where: schema_event_bool_exp) {
  schema_event(where: $where) {
    id
  }
}

You can test the query on the Hasura console

MohamedKHALILRouissi commented 9 months ago

the code is functional but it always returning empty data , tried it with correct data set and still return nothing , but in the hasura console return the correct id {efgh-5678-abcd-1234 eth 0x1234567890123456789012345678901234567890 false [0x1111111111111111111111111111111111111111111111111111111111111111 0x2222222222222222222222222222222222222222222222222222222222222222 0x3333333333333333333333333333333333333333333333333333333333333333]} map[where:map[contractaddress:map[_eq:0x1234567890123456789012345678901234567890] network:map[_eq:eth] topics:map[_contained_in:map[value:[0x1111111111111111111111111111111111111111111111111111111111111111 0x2222222222222222222222222222222222222222222222222222222222222222 0x3333333333333333333333333333333333333333333333333333333333333333]]]]] {[]} [GIN] 2023/11/21 - 17:40:58 | 500 | 317.416892ms | ::1 | POST "/api/v1/subscribe"

MohamedKHALILRouissi commented 9 months ago

thanks again sir hgiasac for all the help and am sorry

MohamedKHALILRouissi commented 9 months ago

tested on the console it work fine but in this script it doesn't ` func QuerySchema(sc Variables_schema) (int, error) { client := startgraphqlconnection()

var query struct {
    SchemaEvent []struct {
        ID int `json:"id"`
    } `graphql:"schema_event(where: $where)"`
}

type schema_event_bool_exp map[string]interface{}

variables := map[string]interface{}{
    "where": schema_event_bool_exp{
        "topics": map[string]interface{}{
            "_contained_in": map[string]interface{}{
                "value": sc.Topics,
            },
        },
        "contractaddress": map[string]interface{}{
            "_eq": sc.ContractAddress,
        },
        "network": map[string]interface{}{
            "_eq": sc.Network,
        },
    },
}

fmt.Println(variables)

if err := client.Query(context.Background(), &query, variables); err != nil {
    fmt.Println(err)
    return 0, err
} else {
    fmt.Println(query)
    return 0, err
}

} `

on hasura {"where": { "contractaddress": {"_eq": "0x1234567890123456789012345678901234567890"} , "network": {"_eq": "eth"} , "topics": { "_contained_in": ["0x1111111111111111111111111111111111111111111111111111111111111111", "0x2222222222222222222222222222222222222222222222222222222222222222" , "0x3333333333333333333333333333333333333333333333333333333333333333"] } } }

return

{
  "data": {
    "schema_event": [
      {
        "id": 1
      }
    ]
  }
}
hgiasac commented 9 months ago

You can try to remove value in _contained_in to see if it works

    "_contained_in": sc.Topics,
MohamedKHALILRouissi commented 9 months ago

`

variables := map[string]interface{}{
    "where": schema_event_bool_exp{
        "contractaddress": map[string]interface{}{
            "_eq": sc.ContractAddress,
        },
        "network": map[string]interface{}{
            "_eq": sc.Network,
        },
    },
}

return a valid value

{efgh-5678-abcd-1234 eth 0x1234567890123456789012345678901234567890 false [0x1111111111111111111111111111111111111111111111111111111111111111 0x2222222222222222222222222222222222222222222222222222222222222222 0x3333333333333333333333333333333333333333333333333333333333333333]}
map[where:map[contractaddress:map[_eq:0x1234567890123456789012345678901234567890] network:map[_eq:eth]]]
{[{1} {2} {3} {6}]}

but if i remove the value in the _contained_in nothing is returned

    variables := map[string]interface{}{
        "where": schema_event_bool_exp{
            "topics": map[string]interface{}{
                "_contained_in": map[string]interface{}{},
            },
            "contractaddress": map[string]interface{}{
                "_eq": sc.ContractAddress,
            },
            "network": map[string]interface{}{
                "_eq": sc.Network,
            },
        },
    }

returned data

2 0x3333333333333333333333333333333333333333333333333333333333333333]}
map[where:map[contractaddress:map[_eq:0x1234567890123456789012345678901234567890] network:map[_eq:eth] topics:map[_contained_in:map[]]]]
{[]}
MohamedKHALILRouissi commented 9 months ago

correct code is

    variables := map[string]interface{}{
        "where": schema_event_bool_exp{
            "topics": map[string]interface{}{
                "_contained_in": sc.Topics,
            },
            "contractaddress": map[string]interface{}{
                "_eq": sc.ContractAddress,
            },
            "network": map[string]interface{}{
                "_eq": sc.Network,
            },
        },
    }

again thanks you sir for you time and help , if this could be added to the documentation