GraphQL JSON to Go struct - json

I have a GraphQL query that looks like this:
{
actor {
entitySearch(query: "name LIKE 'SOME_NAME'") {
results {
entities {
guid
}
}
}
}
}
I can't figure out how to create the Go struct to hold the returned data. The only thing I care about is the guid field that gets returned.
This clearly doesn't work:
type graphQlResponse struct {
guid string
}
Any help? Or is there a way I can simply get the guid and store it in a string without a struct?
Here is the whole code. I don't get an error, but guid is an empty string:
package main
import (
"context"
"fmt"
"log"
"github.com/machinebox/graphql"
)
func main() {
type graphQlResponse struct {
guid string
}
// create a client (safe to share across requests)
client := graphql.NewClient("GraphQL EndPoint")
// make a request
req := graphql.NewRequest(`
{
actor {
entitySearch(query: "name LIKE 'SOME_NAME'") {
results {
entities {
guid
}
}
}
}
}
`)
// set any variables
//req.Var("key", "value")
// set header fields
//req.Header.Set("Cache-Control", "no-cache")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("API-Key", "KEY_HERE")
// define a Context for the request
ctx := context.Background()
// run it and capture the response
var respData graphQlResponse
if err := client.Run(ctx, req, &respData); err != nil {
log.Fatal(err)
}
fmt.Println(respData.guid)
}

In GraphQL, the shape of the returned JSON will match the shape of the GraphQL query: you will have a "data" field, which will have an "actor" child, which will contain "entitySearch", and so on. The library you're calling is in fact pretty minimal. Given the conventional HTTP transport format, it uses ordinary encoding/json decoding to decode the response. Whatever structure you provide needs to be able to unmarshal the "data" field.
This means that you need to create a nested set of structures that mirrors the JSON format, which in turn mirrors your GraphQL query:
type Entity struct {
Guid string `json:"guid"`
}
type Result struct {
Entities Entity `json:"entities"`
}
type EntitySearch struct {
Results Result `json:"results"`
}
type Actor struct {
EntitySearch EntitySearch `json:"entitySearch"`
}
type Response struct {
Actor Actor `json:"actor"`
}
fmt.Println(resp.Actor.EntitySearch.Results.Entities.Guid)
https://play.golang.org/p/ENCIjtfAJif has an example using this structure and an artificial JSON body, though not the library you mention as such.

I suggest using a map and the json package.
I am unfamiliar with graphQL so I'll make a regular HTTP request and hope you can use it to make sense of your own issue:
response, err := http.Get("https://example.com")
// error checking code omitted
defer response.Body.Close()
// now we want to read the body, easiest way is with the ioutil package,
// this should work with the graphQL response body assuming it satisfies
// the io.Reader interface. This gets us the response body as a byte slice
body, err := ioutil.ReadAll(response.Body)
// next make a destination map, interface can be used in place of string or int
// if you need multiple types
jsonResult := map[string]string{"uuid": ""}
// finally, we use json.Unmarshal to write our byte slice into the map
err = json.Unmarshal(body, &jsonResult)
// now you can access your UUID
fmt.Println(jsonResult["uuid"])
I assume that a REST response and a graphQL response are similar, if that's not the case let me know what type the request body is and I can help you figure out a better fitting solution.

Related

How to convert json to string in golang and echo framework?

I have a json that I receive by post
{"endpoint": "assistance"}
I receive this like this
json_map: = make (map[string]interface{})
Now I need to assign it to a variable as a string but I don't know how to do it.
endpoint: = c.String (json_map ["endpoint"])
A type safe way to do this would be creating a struct that represents your request object and unmarshalling it.
This gives you a panic on unexpected requests.
package main
import (
"encoding/json"
"fmt"
)
type response struct {
Endpoint string
}
func main() {
jsonBody := []byte(`{"endpoint": "assistance"}`)
data := response{}
if err := json.Unmarshal(jsonBody, &data); err != nil {
panic(err)
}
fmt.Println(data.Endpoint)
}
// assistance
This program as an example safely decodes the JSON into a struct and prints the value.
What you are trying to achieve is not to convert a JSON to a string but an empty interface interface{} to a string You can achieve this by doing a type assertion:
endpoint, ok := json_map["endpoint"].(string)
if !ok {
// handle the error if the underlying type was not a string
}
Also as #Lex mentionned, it would probably be much safer to have a Go struct defining your JSON data. This way all your fields will be typed and you will no longer need this kind of type assertion.

Parsing JSON with GoLang in AWS Lamda

As part of an application we are building, one of the process steps is an AWS Lamda that captures a post request does some work with it, and then moves one. It has an API Gateway Request as a trigger and the body of this request would be a JSON String. I am having trouble parsing the JSON String to GoLang Object. Here is what I have:
The function that catches request:
func HandleRequest(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
log.Print(fmt.Sprintf("body:[%s] ", event.Body))
parseResponseStringToTypedObject(event.Body)
return events.APIGatewayProxyResponse{
StatusCode: http.StatusOK,
Body: "OK",
}, nil
}
Then the parseResponseStringToTypedObject function :
func parseResponseStringToTypedObject(responseString string) {
b := []byte(responseString)
var resp SimpleType
err := json.Unmarshal(b, &resp)
if err == nil {
log.Print(fmt.Sprintf("Account Name: [%s]", resp.accountName))
} else {
log.Print(fmt.Sprintf("Could not unmarshall JSON string: [%s]", err.Error()))
}
}
Here is the SimpleType struct:
type SimpleType struct {
accountName string `json:accountName`
amount int `json:amount`
}
I then, as a test, posted this JSON Body via Postman:
I opened up the CloudWatch Logs (where my lamda logs to) and see that the body is present in the event.Body property, and then logging out a field in the unmarshalled object (resp.accountName) I note that the field is blank. Why is this? Here is log output for the request:
Your SimpleType struct needs 2 things here...
1) The properties need to be "public" or "exported". Meaning they need to start with an upper cased character.
AND
2) The properties need proper tags for the serialization and de serialization of JSON. e.g. each JSON tag surrounded by "
type SimpleType struct {
AccountName string `json:"accountName"`
Amount int `json:"amount"`
}
Hope this helps!

How to mock http.Client that returns a JSON response

I'm trying to test a method that uses net/http to make requests. Specifically what I'm trying to achieve is to inject a mock http.Client that responds with a certain JSON body
type clientMock struct{}
func (c *clientMock) Do(req *http.Request) (*http.Response, error) {
json := struct {
AccessToken string `json:"access_token`
Scope string `json:"scope"`
}{
AccessToken: "123457678",
Scope: "read, write",
}
body := json.Marshal(json)
res := &http.Response {
StatusCode: http.StatusOK,
Body: // I haven't got a clue what to put here
}
return res
}
func TestRequest(t *testing.T) { //tests here }
I do know that the Body is of a type io.ReadCloser interface. Trouble is I can't for the life of me find a way to implement it in the mock body response.
Examples as found here so far only demonstrates returning a blank &http.Response{}
While it's probably more useful to mock the full request cycle with httptest.Server, you can use ioutil.NopCloser to create the closer around any reader:
Body: ioutil.NopCloser(bytes.NewReader(body))
and if you want an empty body, just provider a reader with no content.
Body: ioutil.NopCloser(bytes.NewReader(nil))
In your test file (my_test.go):
type MyJSON struct {
Id string
Age int
}
// Interface which is the same as httpClient because it implements "Do" method.
type ClientMock struct {}
func (c *ClientMock) Do(req *http.Request) (*http.Response, error) {
mockedRes := MyJSON {"1", 3}
// Marshal a JSON-encoded version of mockedRes
b, err := json.Marshal(mockedRes)
if err != nil {
log.Panic("Error reading a mockedRes from mocked client", err)
}
return &http.Response{Body: ioutil.NopCloser(bytes.NewBuffer(b))}, nil
}
// your test which will use the mocked response
func TestMyFunction(t *testing.T) {
mock := &ClientMock{}
actualResult := myFunction(mock)
assert.NotEmpty(t, actualResult, "myFunction should have at least 1 result")
}
In your implementation (main.go):
package main
import (
"net/http"
)
func main() {
myFunction(&http.Client{})
}
I know it's been a little while but I just wrote something to help with this recently.
Like JimB I recommend starting up a real HTTP server locally, since in Go this is easy to do with https://golang.org/pkg/net/http/httptest/.
However having done a lot of HTTP mocking I wanted something that does a little more, like a good mock library would: returning specific data, easy setting of expectations, validation that all requests were made, etc. I have generally used https://godoc.org/github.com/stretchr/testify/mock for mocking and wanted features like that.
So I wrote https://github.com/dankinder/httpmock, which basically combines the two. If you just want a mock that accepts JSON and spits out JSON, it may be an easier way to go.

Unmarshalling a JSON that may or may not return an array?

I'm retrieving JSON from a third party website (home electricity usage), and depending on what I've requested from the site, the JSON returned may or may not be an array. For example, if I request a list of my smart meters, I get this (results truncated, due to large size):
{"gwrcmds":{"gwrcmd":{"gcmd":"SPA_UserGetSmartMeterList","gdata":{"gip":{"version":"1"...
Where gwrcmd is a single element.
But if I request electricity usage for the last half hour, I get this:
{"gwrcmds":{"gwrcmd":[{"gcmd":"DeviceGetChart","gdata":{"gip":{"version":"1" ...
See how gwrcmd is now an array?
Within my Go app, I have a struct that looks like this (again, truncated, as it goes on for a while. There's more sub-structs and properties beneath "Version":
type Response struct {
Gwrcmds struct {
Gwrcmd struct {
Gcmd string
Gdata struct {
Gip struct {
Version string
If gwrcmd is an array, Gwrcmd needs to be a []struct { }, but if it's not, it's just a regular old struct { }
The problem is that json.Unmarshal just returns an error if the JSON has an array and the struct does not have a slice (or vice versa).
Would I need to create a second struct that duplicates the first one (except with a []struct { } instead), or is there a better way to do it? I thought of something with interfaces, but I haven't really touched those yet, so I'm not 100% sure on them.
Usually, whenever you have a JSON value of unknown type, you will use json.RawMessage to get it, peek into it, and unmarshal it correctly into the corresponding type. A simplified example:
// The A here can be either an object, or a JSON array of objects.
type Response struct {
RawAWrapper struct {
RawA json.RawMessage `json:"a"`
}
A A `json:"-"`
As []A `json:"-"`
}
type A struct {
B string
}
func (r *Response) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &r.RawAWrapper); err != nil {
return err
}
if r.RawAWrapper.RawA[0] == '[' {
return json.Unmarshal(r.RawAWrapper.RawA, &r.As)
}
return json.Unmarshal(r.RawAWrapper.RawA, &r.A)
}
Playground: http://play.golang.org/p/2d_OrGltDu.
Guessing the content based on the first byte doesn't seem too robust to me though. Usually you'll have some sort of a clue in your JSON (like a length or type field on the same level as the dynamic one) that tells you whether you have an object or an array.
See also:
How can you decode multiple message types with golang websockets?
Partly JSON unmarshal into a map in Go
You can try to make custom json unmarshal method, like
func (a *GwrcmCustom) UnmarshalJSON(b []byte) (err error) {
g, ga := Gwrcmd{}, []Gwrcmd{}
if err = json.Unmarshal(b, &g); err == nil {
*a = make([]Gwrcmd, 1)
[]Gwrcmd(*a)[0] = Gwrcmd(g)
return
}
if err = json.Unmarshal(b, &ga); err == nil {
*a = GwrcmCustom(ga)
return
}
return
}
GwrcmCustom is a custom type, slice of Gwrcm
type GwrcmCustom []Gwrcmd
So we will get slice always
Try this on Go playground
I hope this will help

How do I Unmarshal JSON?

I am trying to unmarshal JSON into a struct. However, the struct has a field with a tag. Using reflection, and I try to see if the tag has the string "json" in it. If it does, then the json to unmarshal should simply be unmarshaled into the field as a string.
Example:
const data = `{"I":3, "S":{"phone": {"sales": "2223334444"}}}`
type A struct {
I int64
S string `sql:"type:json"`
}
Problem is simple - unmarshal "S" in the json as a string into the struct A.
This is how far I have come. But I am stuck here.
http://play.golang.org/p/YzrhjuXxGN
This is the go way of doing it - no reflection requred. Create a new type RawString and create MarshalJSON and UnmarshalJSON methods for it. (playground)
// RawString is a raw encoded JSON object.
// It implements Marshaler and Unmarshaler and can
// be used to delay JSON decoding or precompute a JSON encoding.
type RawString string
// MarshalJSON returns *m as the JSON encoding of m.
func (m *RawString) MarshalJSON() ([]byte, error) {
return []byte(*m), nil
}
// UnmarshalJSON sets *m to a copy of data.
func (m *RawString) UnmarshalJSON(data []byte) error {
if m == nil {
return errors.New("RawString: UnmarshalJSON on nil pointer")
}
*m += RawString(data)
return nil
}
const data = `{"i":3, "S":{"phone": {"sales": "2223334444"}}}`
type A struct {
I int64
S RawString `sql:"type:json"`
}
func main() {
a := A{}
err := json.Unmarshal([]byte(data), &a)
if err != nil {
log.Fatal("Unmarshal failed", err)
}
fmt.Println("Done", a)
}
I modified the implementation of RawMessage to create the above.
The problem here is that you are using one encoding format for two protocols and probably there is something wrong in your model.
That is a valid Json payload and should be handled as such. One trick that you can use is to create
another field to handle the "string" json and one to handle the "json struct".
See this modified example: I first unmarshal json and then marshal back to json to create the final string to upload to the database. One field is used for unmarshaling and the other to communicate with the DB.
package main
import(
"fmt"
"encoding/json"
)
const data = `{"i":3, "S":{"phone": {"sales": "2223334444"}}}`
type A struct {
I int64
Sjson struct {
Phone struct {
Sales string `json:"sales"`
} `json:"phone"`
} `json:"S", sql:"-"`
S string `sql:"type:json",json:"-"`
}
func main() {
msg := A{}
_ = json.Unmarshal([]byte(data), &msg)
data, _ := json.Marshal(msg.Sjson)
msg.S = string(data)
fmt.Println("Done", msg)
}
Looks like the problem is that s interface{} in your code was not addressable. For Value.SetString the Value has to be addressable and with Kind String. you can check the documentation for it - http://golang.org/pkg/reflect/#Value.SetString
How i understand it SetString would not change the value in a, since you are only working with interface s. in Laws of Reflection you can find "reflect.ValueOf is a copy of x, not x itself"(3rd Law).
To make your code work I made some type assertions, and I used reflect.ValueOf on a pointer to asserted struct.
To check if Value is settable or addressable you can use Value.CanSet ad Value.CanAddr
working code: http://play.golang.org/p/DTriENkzA8
No idea whether its correct way to do this