gqlgenc
gqlgenc copied to clipboard
use standard json.Unmarshal instead of custom
I have a very complex response from the API and the graphqljson.UnmarshalData throws errors. After changing to standard json.Unmarshal everything works fine.
@Yamashou PTAL. Another PR from my side.
@zreigz This way you probably can't bind the structure inlineFragment(ex: ... on Foo) If we make this change, the example GetNode will not work correctly.
@Yamashou I've just run the example GetNode and it works fine. I also tested it with our graphQL API and everything works perfectly fine but with your custom unmarshallee I have errors
@zreigz It works at least in my environment. I have written tests and the following cases fail
func TestUnmarshalGraphQL_pointerWithInlineFragment(t *testing.T) {
t.Parallel()
type actor struct {
User struct {
DatabaseID uint64
} `graphql:"... on User"`
Login string
}
type query struct {
Author actor
Editor *actor
}
var got query
err := json.Unmarshal([]byte(`{
"author": {
"databaseId": 1,
"login": "test1"
},
"editor": {
"databaseId": 2,
"login": "test2"
}
}`), &got)
if err != nil {
t.Fatal(err)
}
var want query
want.Author = actor{
User: struct{ DatabaseID uint64 }{1},
Login: "test1",
}
want.Editor = &actor{
User: struct{ DatabaseID uint64 }{2},
Login: "test2",
}
if diff := cmp.Diff(got, want); diff != "" {
t.Error(diff)
}
}
func TestUnmarshalGraphQL_unexportedField(t *testing.T) {
t.Parallel()
type query struct {
foo string
}
err := json.Unmarshal([]byte(`{"foo": "bar"}`), new(query))
if err == nil {
t.Fatal("got error: nil, want: non-nil")
}
got := err.Error()
want := ": : struct field for \"foo\" doesn't exist in any of 1 places to unmarshal"
if diff := cmp.Diff(got, want); diff != "" {
t.Error(diff)
}
}
func TestUnmarshalGraphQL_multipleValues(t *testing.T) {
t.Parallel()
type query struct {
Foo string
}
err := json.Unmarshal([]byte(`{"foo": "bar"}{"foo": "baz"}`), new(query))
if err == nil {
t.Fatal("got error: nil, want: non-nil")
}
if got, want := err.Error(), "invalid token '{' after top-level value"; got != want {
t.Errorf("got error: %v, want: %v", got, want)
}
}
func TestUnmarshalGraphQL_union(t *testing.T) {
t.Parallel()
/*
{
__typename
... on ClosedEvent {
createdAt
actor {login}
}
... on ReopenedEvent {
createdAt
actor {login}
}
}
*/
type actor struct{ Login string }
type reopenedEvent struct {
Actor actor
CreatedAt time.Time
}
type issueTimelineItem struct {
Typename string `graphql:"__typename"`
ClosedEvent struct {
Actor actor
UpdatedAt time.Time
} `graphql:"... on ClosedEvent"`
ReopenedEvent reopenedEvent `graphql:"... on ReopenedEvent"`
}
var got issueTimelineItem
err := json.Unmarshal([]byte(`{
"__typename": "ClosedEvent",
"createdAt": "2017-06-29T04:12:01Z",
"updatedAt": "2017-06-29T04:12:01Z",
"actor": {
"login": "shurcooL-test"
}
}`), &got)
if err != nil {
t.Fatal(err)
}
want := issueTimelineItem{
Typename: "ClosedEvent",
ClosedEvent: struct {
Actor actor
UpdatedAt time.Time
}{
Actor: actor{
Login: "shurcooL-test",
},
UpdatedAt: time.Unix(1498709521, 0).UTC(),
},
ReopenedEvent: reopenedEvent{
Actor: actor{
Login: "shurcooL-test",
},
CreatedAt: time.Unix(1498709521, 0).UTC(),
},
}
if diff := cmp.Diff(got, want); diff != "" {
t.Error(diff)
}
}
func TestUnmarshalGraphQL_union2(t *testing.T) {
t.Parallel()
type SubscriptionItemFragment struct {
ID string
}
type PurchaseItemFragment struct {
ID string
}
type OrderFragment struct {
SubscriptionItemOrder struct {
SubscriptionItem SubscriptionItemFragment
} `graphql:"... on SubscriptionItemOrder"`
PurchaseItemFragment struct {
PurchaseItem PurchaseItemFragment
} `graphql:"... on PurchaseItemOrder"`
}
type BuyDashItemPayload struct {
Order OrderFragment
}
var got BuyDashItemPayload
resp := `
{
"order": {
"subscriptionItem": {
"id": "subscriptionItemOrderID"
}
}
}
`
err := json.Unmarshal([]byte(resp), &got)
if err != nil {
t.Fatalf("%+v", err)
}
want := BuyDashItemPayload{Order: OrderFragment{
SubscriptionItemOrder: struct {
SubscriptionItem SubscriptionItemFragment
}{
SubscriptionItem: SubscriptionItemFragment{ID: "subscriptionItemOrderID"},
},
PurchaseItemFragment: struct {
PurchaseItem PurchaseItemFragment
}{},
}}
if diff := cmp.Diff(got, want); diff != "" {
t.Error(diff)
}
}
// Issue https://github.com/shurcooL/githubv4/issues/18.
func TestUnmarshalGraphQL_arrayInsideInlineFragment(t *testing.T) {
t.Parallel()
/*
query {
search(type: ISSUE, first: 1, query: "type:pr repo:owner/name") {
nodes {
... on PullRequest {
commits(last: 1) {
nodes {
url
}
}
}
}
}
}
*/
type query struct {
Search struct {
Nodes []struct {
PullRequest struct {
Commits struct {
Nodes []struct {
URL string `graphql:"url"`
}
} `graphql:"commits(last: 1)"`
} `graphql:"... on PullRequest"`
}
} `graphql:"search(type: ISSUE, first: 1, query: \"type:pr repo:owner/name\")"`
}
var got query
err := json.Unmarshal([]byte(`{
"search": {
"nodes": [
{
"commits": {
"nodes": [
{
"url": "https://example.org/commit/49e1"
}
]
}
}
]
}
}`), &got)
if err != nil {
t.Fatal(err)
}
var want query
want.Search.Nodes = make([]struct {
PullRequest struct {
Commits struct {
Nodes []struct {
URL string `graphql:"url"`
}
} `graphql:"commits(last: 1)"`
} `graphql:"... on PullRequest"`
}, 1)
want.Search.Nodes[0].PullRequest.Commits.Nodes = make([]struct {
URL string `graphql:"url"`
}, 1)
want.Search.Nodes[0].PullRequest.Commits.Nodes[0].URL = "https://example.org/commit/49e1"
if diff := cmp.Diff(got, want); diff != "" {
t.Error(diff)
}
}
func TestUnmarshalGraphQL_jsonRawMessage(t *testing.T) {
t.Parallel()
type query struct {
JSONBlob json.RawMessage `json:"jsonBlob"`
JSONArray json.RawMessage `json:"jsonArray"`
JSONNumber json.RawMessage `json:"jsonNumber"`
JSONString json.RawMessage `json:"jsonString"`
JSONOmmited json.RawMessage `json:"jsonOmmited"`
Number int `json:"number"`
String string `json:"string"`
}
var got query
err := json.Unmarshal([]byte(`{
"jsonBlob": {
"foo": "bar"
},
"jsonArray": [1, "two", 3],
"jsonNumber": 2,
"jsonString": "json string",
"number": 1,
"string": "normal string"
}`), &got)
if err != nil {
t.Fatal(err)
}
want := query{
JSONBlob: []byte(`{"foo":"bar"}`),
JSONArray: []byte(`[1,"two",3]`),
JSONNumber: []byte(`2`),
JSONString: []byte(`"json string"`),
JSONOmmited: nil,
Number: 1,
String: "normal string",
}
if diff := cmp.Diff(got, want); diff != "" {
t.Error(diff)
}
}
func TestUnmarshalGraphQL_jsonRawMessageInFragment(t *testing.T) {
t.Parallel()
type Object struct {
Properties struct {
ID string
Metadata json.RawMessage
} `graphql:"... on Properties"`
Value string
}
type query struct {
Object Object
OptionalObject *Object
}
var got query
err := json.Unmarshal([]byte(`{
"object": {
"id": "81beda46-02c1-4641-aa7b-09cc6634c783",
"metadata": {
"created": "2021-05-03T21:27:28+00:00"
},
"value": "object value 1"
},
"optionalObject": {
"id": "6f8af214-f307-4d4d-89d3-965d8b79e3bf",
"metadata": {
"created": "2021-05-03T21:27:28+00:00",
"deleted": "2021-05-04T21:27:28+00:00"
},
"value": "object value 2"
}
}`), &got)
if err != nil {
t.Fatal(err)
}
var want query
want.Object = Object{
Properties: struct {
ID string
Metadata json.RawMessage
}{
ID: "81beda46-02c1-4641-aa7b-09cc6634c783",
Metadata: []byte(`{"created":"2021-05-03T21:27:28+00:00"}`),
},
Value: "object value 1",
}
want.OptionalObject = &Object{
Properties: struct {
ID string
Metadata json.RawMessage
}{
ID: "6f8af214-f307-4d4d-89d3-965d8b79e3bf",
Metadata: []byte(`{"created":"2021-05-03T21:27:28+00:00","deleted":"2021-05-04T21:27:28+00:00"}`),
},
Value: "object value 2",
}
if diff := cmp.Diff(got, want); diff != "" {
t.Error(diff)
}
}
thanks for the update