Closed dmitshur closed 7 years ago
An update. I mentioned 3 days ago in https://github.com/google/go-github/issues/646#issuecomment-312952417 that:
Next, I plan to work on resolving shurcooL/githubql#10 (improving support for unions), because that's the biggest usability issue right now. I have a plan for how to tackle it.
I've been working on that plan, and I can say today that it's looking quite good. It was a lot of work, but it was worth it. It will resolve this issue in a nice way (no more need to implement UnmarshalJSON
method!), but also lead to benefits in other areas that I knew could be improved.
I'll be cleaning up the code, and pushing it out over the next few days.
The gist of the solution is that I implemented custom JSON unmarshaling logic specifically for the GraphQL query input structure. It's aware of the graphql
struct tags, and doesn't get thrown off by json
tags.
Instead of forking encoding/json
or creating an entire JSON parser from scratch, I built it on top of the JSON tokenizer that was added in Go 1.5 (see json.Decoder.Token
).
User query ends up looking like this (and nothing more), and it works as one would expect:
var q struct {
Repository struct {
Issue struct {
Timeline struct {
Nodes []struct {
Typename string `graphql:"__typename"`
ClosedEvent struct {
Actor struct{ Login githubql.String }
CreatedAt githubql.DateTime
} `graphql:"... on ClosedEvent"`
ReopenedEvent struct {
Actor struct{ Login githubql.String }
CreatedAt githubql.DateTime
} `graphql:"... on ReopenedEvent"`
}
} `graphql:"timeline(first: 10)"`
} `graphql:"issue(number: $issueNumber)"`
} `graphql:"repository(owner: $repositoryOwner, name: $repositoryName)"`
}
I've created #15 which will resolve this issue.
I've documented the support for inline fragments in the README in commit 48704d75118eb275751b1b9d84d4658c2bece9b3, see:
It looks like supporting unions will be an interesting challenge.
From https://developer.github.com/v4/reference/union/:
For example, imagine you want to fetch a timeline of an issue. The type of
timeline
field ofIssue
object isIssueTimelineConnection
, which is a connection type forIssueTimelineItem
, a union:https://developer.github.com/v4/reference/union/issuetimelineitem/
Suppose you want to find all closed/reopened events within an issue. For each event, you want to know who was the actor, and when it happened. You might write a query like this:
Which might give you a response like this:
A Go type that can result in that query is something like this:
Unfortunately, because
IssueTimelineItem
embeds bothClosedEvent
andReopenedEvent
, according to the currentencoding/json
unmarshaling rules:The multiple fields are ignored. So, the following JSON value:
When unmarshaled into
IssueTimelineItem
, becomes:Only the value of
Typename
has correct value. Accessingitem.ClosedEvent.CreatedAt
anditem.ClosedEvent.Actor.Login
gives zero values.This is not a problem if the multiple potential event types don't have common fields.
I came up with the following solution on the user-side, to implement a custom
UnmarshalJSON
method on theIssueTimelineItem
type:But this is very verbose, inconvenient, error prone. It also means the custom
IssueTimelineItem
type must be declared at package level rather than inside a function. I want to find a better solution.One of the big guns here is making a copy of
encoding/json
internal togithubql
and changing its behavior, but that's something I'd really like to avoid.