Add support for interfaces, part 1: the simplest cases (#52)

## Summary:
In this commit I begin the journey to add the long-awaited support for
interfaces (part of #8).  Well, it's not the beginning: I already had
some half-written broken code around.  But it's the first fully
functional support, and especially, the first *tested* support; it's
probably best to review the nontrivially-changed code as if it were new.

Conceptually, the code so far is pretty simple: we generate an interface
type, and the implementations.  (That code is in fact mostly unchanged.)
The complexity comes in because encoding/json doesn't know how to
unmarshal that.  So we have to add an UnmarshalJSON method, which
actually has to be on the types with interface-type fields, that knows
how.  I factored it into two methods, such that that UnmarshalJSON
method is just glue, and then there's a separate function, corresponding
to each interface-type, that actually does all the work.  (If only one
could just write it as an actual method!)  The method uses the same
trick suggested to me by a few others in another context to deserialize
all but one field, then handle that field specially, which is discussed
in the code.

This still has some limitations, which will be lifted in future commits:
- it doesn't allow for list-of-interface fields
- it requires that you manually ask for `__typename`
- it doesn't support fragments, i.e. you can only query for interface
  fields, not concrete-type-specific ones
But it works, even in integration tests, which is progress!

As a part of this, I added a proper config option for the "allow broken
features" flag, since I need to be able to set it from the integration
tests which are in a separate package (and actually shell out via `go
generate`).  I also renamed what was to be the first case
(InterfaceNoFragments), and replaced it with a further-simplified
version (avoiding list-of-interface fields.

[1] https://github.com/benjaminjkraft/notes/blob/master/go-json-interfaces.md

Issue: https://github.com/Khan/genqlient/issues/8

## Test plan:
make tesc

Author: benjaminjkraft

Reviewers: dnerdy, benjaminjkraft, aberkan, csilvers, MiguelCastillo

Required Reviewers: 

Approved by: dnerdy

Checks:  Test (1.17),  Test (1.16),  Test (1.15),  Test (1.14),  Test (1.13),  Lint,  Test (1.17),  Test (1.16),  Test (1.15),  Test (1.14),  Test (1.13),  Lint

Pull request URL: https://github.com/Khan/genqlient/pull/52
This commit is contained in:
Ben Kraft
2021-08-25 11:51:26 -07:00
committed by GitHub
parent 8815d0991c
commit 4c38cb7759
31 changed files with 1125 additions and 199 deletions
+5
View File
@@ -0,0 +1,5 @@
package errors
const _ = `# @genqlient
query MyQuery { i { f } }
`
+1
View File
@@ -0,0 +1 @@
query MyQuery { i { f } }
+11
View File
@@ -0,0 +1,11 @@
type Query {
i: I
}
type T implements I {
f: String!
}
interface I {
f: String!
}
+11
View File
@@ -0,0 +1,11 @@
query InterfaceNoFragmentsQuery {
root {
id
name
children {
__typename
id
name
}
}
}
+3
View File
@@ -2,10 +2,13 @@ query InterfaceNesting {
root {
id
children {
__typename
id
parent {
__typename
id
children {
__typename
id
}
}
+2 -8
View File
@@ -1,10 +1,4 @@
query InterfaceNoFragmentsQuery {
root {
id
name
children {
id
name
}
}
root { id name } # (make sure sibling fields work)
randomItem { __typename id name }
}
+1
View File
@@ -104,6 +104,7 @@ type Query {
"""usersWithRole looks a user up by role."""
usersWithRole(role: Role!): [User!]!
root: Topic!
randomItem: Content!
randomLeaf: LeafContent!
convert(dt: DateTime!, tz: String): DateTime!
maybeConvert(dt: DateTime, tz: String): DateTime
@@ -3,6 +3,9 @@ package test
// Code generated by github.com/Khan/genqlient, DO NOT EDIT.
import (
"encoding/json"
"fmt"
"github.com/Khan/genqlient/graphql"
"github.com/Khan/genqlient/internal/testutil"
)
@@ -43,6 +46,35 @@ func (v *InterfaceNoFragmentsQueryRootTopicChildrenVideo) implementsGraphQLInter
func (v *InterfaceNoFragmentsQueryRootTopicChildrenTopic) implementsGraphQLInterfaceInterfaceNoFragmentsQueryRootTopicChildrenContent() {
}
func __unmarshalInterfaceNoFragmentsQueryRootTopicChildrenContent(v *InterfaceNoFragmentsQueryRootTopicChildrenContent, m json.RawMessage) error {
if string(m) == "null" {
return nil
}
var tn struct {
TypeName string `json:"__typename"`
}
err := json.Unmarshal(m, &tn)
if err != nil {
return err
}
switch tn.TypeName {
case "Article":
*v = new(InterfaceNoFragmentsQueryRootTopicChildrenArticle)
return json.Unmarshal(m, *v)
case "Video":
*v = new(InterfaceNoFragmentsQueryRootTopicChildrenVideo)
return json.Unmarshal(m, *v)
case "Topic":
*v = new(InterfaceNoFragmentsQueryRootTopicChildrenTopic)
return json.Unmarshal(m, *v)
default:
return fmt.Errorf(
`Unexpected concrete type for InterfaceNoFragmentsQueryRootTopicChildrenContent: "%v"`, tn.TypeName)
}
}
// InterfaceNoFragmentsQueryRootTopicChildrenTopic includes the requested fields of the GraphQL type Topic.
type InterfaceNoFragmentsQueryRootTopicChildrenTopic struct {
Typename string `json:"__typename"`
@@ -0,0 +1,9 @@
{
"operations": [
{
"operationName": "InterfaceNoFragmentsQuery",
"query": "\nquery InterfaceNoFragmentsQuery {\n\troot {\n\t\tid\n\t\tname\n\t\tchildren {\n\t\t\t__typename\n\t\t\tid\n\t\t\tname\n\t\t}\n\t}\n}\n",
"sourceLocation": "testdata/queries/InterfaceListField.graphql"
}
]
}
@@ -3,6 +3,9 @@ package test
// Code generated by github.com/Khan/genqlient, DO NOT EDIT.
import (
"encoding/json"
"fmt"
"github.com/Khan/genqlient/graphql"
"github.com/Khan/genqlient/internal/testutil"
)
@@ -21,6 +24,7 @@ type InterfaceNestingRootTopic struct {
// InterfaceNestingRootTopicChildrenArticle includes the requested fields of the GraphQL type Article.
type InterfaceNestingRootTopicChildrenArticle struct {
Typename string `json:"__typename"`
// ID is the identifier of the content.
Id testutil.ID `json:"id"`
Parent InterfaceNestingRootTopicChildrenArticleParentTopic `json:"parent"`
@@ -28,6 +32,7 @@ type InterfaceNestingRootTopicChildrenArticle struct {
// InterfaceNestingRootTopicChildrenArticleParentTopic includes the requested fields of the GraphQL type Topic.
type InterfaceNestingRootTopicChildrenArticleParentTopic struct {
Typename string `json:"__typename"`
// ID is documented in the Content interface.
Id testutil.ID `json:"id"`
Children []InterfaceNestingRootTopicChildrenArticleParentTopicChildrenContent `json:"children"`
@@ -35,6 +40,7 @@ type InterfaceNestingRootTopicChildrenArticleParentTopic struct {
// InterfaceNestingRootTopicChildrenArticleParentTopicChildrenArticle includes the requested fields of the GraphQL type Article.
type InterfaceNestingRootTopicChildrenArticleParentTopicChildrenArticle struct {
Typename string `json:"__typename"`
// ID is the identifier of the content.
Id testutil.ID `json:"id"`
}
@@ -47,21 +53,52 @@ type InterfaceNestingRootTopicChildrenArticleParentTopicChildrenContent interfac
implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenArticleParentTopicChildrenContent()
}
func (v InterfaceNestingRootTopicChildrenArticleParentTopicChildrenArticle) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenArticleParentTopicChildrenContent() {
func (v *InterfaceNestingRootTopicChildrenArticleParentTopicChildrenArticle) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenArticleParentTopicChildrenContent() {
}
func (v InterfaceNestingRootTopicChildrenArticleParentTopicChildrenVideo) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenArticleParentTopicChildrenContent() {
func (v *InterfaceNestingRootTopicChildrenArticleParentTopicChildrenVideo) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenArticleParentTopicChildrenContent() {
}
func (v InterfaceNestingRootTopicChildrenArticleParentTopicChildrenTopic) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenArticleParentTopicChildrenContent() {
func (v *InterfaceNestingRootTopicChildrenArticleParentTopicChildrenTopic) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenArticleParentTopicChildrenContent() {
}
func __unmarshalInterfaceNestingRootTopicChildrenArticleParentTopicChildrenContent(v *InterfaceNestingRootTopicChildrenArticleParentTopicChildrenContent, m json.RawMessage) error {
if string(m) == "null" {
return nil
}
var tn struct {
TypeName string `json:"__typename"`
}
err := json.Unmarshal(m, &tn)
if err != nil {
return err
}
switch tn.TypeName {
case "Article":
*v = new(InterfaceNestingRootTopicChildrenArticleParentTopicChildrenArticle)
return json.Unmarshal(m, *v)
case "Video":
*v = new(InterfaceNestingRootTopicChildrenArticleParentTopicChildrenVideo)
return json.Unmarshal(m, *v)
case "Topic":
*v = new(InterfaceNestingRootTopicChildrenArticleParentTopicChildrenTopic)
return json.Unmarshal(m, *v)
default:
return fmt.Errorf(
`Unexpected concrete type for InterfaceNestingRootTopicChildrenArticleParentTopicChildrenContent: "%v"`, tn.TypeName)
}
}
// InterfaceNestingRootTopicChildrenArticleParentTopicChildrenTopic includes the requested fields of the GraphQL type Topic.
type InterfaceNestingRootTopicChildrenArticleParentTopicChildrenTopic struct {
Typename string `json:"__typename"`
// ID is the identifier of the content.
Id testutil.ID `json:"id"`
}
// InterfaceNestingRootTopicChildrenArticleParentTopicChildrenVideo includes the requested fields of the GraphQL type Video.
type InterfaceNestingRootTopicChildrenArticleParentTopicChildrenVideo struct {
Typename string `json:"__typename"`
// ID is the identifier of the content.
Id testutil.ID `json:"id"`
}
@@ -74,15 +111,45 @@ type InterfaceNestingRootTopicChildrenContent interface {
implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenContent()
}
func (v InterfaceNestingRootTopicChildrenArticle) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenContent() {
func (v *InterfaceNestingRootTopicChildrenArticle) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenContent() {
}
func (v InterfaceNestingRootTopicChildrenVideo) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenContent() {
func (v *InterfaceNestingRootTopicChildrenVideo) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenContent() {
}
func (v InterfaceNestingRootTopicChildrenTopic) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenContent() {
func (v *InterfaceNestingRootTopicChildrenTopic) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenContent() {
}
func __unmarshalInterfaceNestingRootTopicChildrenContent(v *InterfaceNestingRootTopicChildrenContent, m json.RawMessage) error {
if string(m) == "null" {
return nil
}
var tn struct {
TypeName string `json:"__typename"`
}
err := json.Unmarshal(m, &tn)
if err != nil {
return err
}
switch tn.TypeName {
case "Article":
*v = new(InterfaceNestingRootTopicChildrenArticle)
return json.Unmarshal(m, *v)
case "Video":
*v = new(InterfaceNestingRootTopicChildrenVideo)
return json.Unmarshal(m, *v)
case "Topic":
*v = new(InterfaceNestingRootTopicChildrenTopic)
return json.Unmarshal(m, *v)
default:
return fmt.Errorf(
`Unexpected concrete type for InterfaceNestingRootTopicChildrenContent: "%v"`, tn.TypeName)
}
}
// InterfaceNestingRootTopicChildrenTopic includes the requested fields of the GraphQL type Topic.
type InterfaceNestingRootTopicChildrenTopic struct {
Typename string `json:"__typename"`
// ID is the identifier of the content.
Id testutil.ID `json:"id"`
Parent InterfaceNestingRootTopicChildrenTopicParentTopic `json:"parent"`
@@ -90,6 +157,7 @@ type InterfaceNestingRootTopicChildrenTopic struct {
// InterfaceNestingRootTopicChildrenTopicParentTopic includes the requested fields of the GraphQL type Topic.
type InterfaceNestingRootTopicChildrenTopicParentTopic struct {
Typename string `json:"__typename"`
// ID is documented in the Content interface.
Id testutil.ID `json:"id"`
Children []InterfaceNestingRootTopicChildrenTopicParentTopicChildrenContent `json:"children"`
@@ -97,6 +165,7 @@ type InterfaceNestingRootTopicChildrenTopicParentTopic struct {
// InterfaceNestingRootTopicChildrenTopicParentTopicChildrenArticle includes the requested fields of the GraphQL type Article.
type InterfaceNestingRootTopicChildrenTopicParentTopicChildrenArticle struct {
Typename string `json:"__typename"`
// ID is the identifier of the content.
Id testutil.ID `json:"id"`
}
@@ -109,27 +178,59 @@ type InterfaceNestingRootTopicChildrenTopicParentTopicChildrenContent interface
implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenTopicParentTopicChildrenContent()
}
func (v InterfaceNestingRootTopicChildrenTopicParentTopicChildrenArticle) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenTopicParentTopicChildrenContent() {
func (v *InterfaceNestingRootTopicChildrenTopicParentTopicChildrenArticle) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenTopicParentTopicChildrenContent() {
}
func (v InterfaceNestingRootTopicChildrenTopicParentTopicChildrenVideo) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenTopicParentTopicChildrenContent() {
func (v *InterfaceNestingRootTopicChildrenTopicParentTopicChildrenVideo) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenTopicParentTopicChildrenContent() {
}
func (v InterfaceNestingRootTopicChildrenTopicParentTopicChildrenTopic) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenTopicParentTopicChildrenContent() {
func (v *InterfaceNestingRootTopicChildrenTopicParentTopicChildrenTopic) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenTopicParentTopicChildrenContent() {
}
func __unmarshalInterfaceNestingRootTopicChildrenTopicParentTopicChildrenContent(v *InterfaceNestingRootTopicChildrenTopicParentTopicChildrenContent, m json.RawMessage) error {
if string(m) == "null" {
return nil
}
var tn struct {
TypeName string `json:"__typename"`
}
err := json.Unmarshal(m, &tn)
if err != nil {
return err
}
switch tn.TypeName {
case "Article":
*v = new(InterfaceNestingRootTopicChildrenTopicParentTopicChildrenArticle)
return json.Unmarshal(m, *v)
case "Video":
*v = new(InterfaceNestingRootTopicChildrenTopicParentTopicChildrenVideo)
return json.Unmarshal(m, *v)
case "Topic":
*v = new(InterfaceNestingRootTopicChildrenTopicParentTopicChildrenTopic)
return json.Unmarshal(m, *v)
default:
return fmt.Errorf(
`Unexpected concrete type for InterfaceNestingRootTopicChildrenTopicParentTopicChildrenContent: "%v"`, tn.TypeName)
}
}
// InterfaceNestingRootTopicChildrenTopicParentTopicChildrenTopic includes the requested fields of the GraphQL type Topic.
type InterfaceNestingRootTopicChildrenTopicParentTopicChildrenTopic struct {
Typename string `json:"__typename"`
// ID is the identifier of the content.
Id testutil.ID `json:"id"`
}
// InterfaceNestingRootTopicChildrenTopicParentTopicChildrenVideo includes the requested fields of the GraphQL type Video.
type InterfaceNestingRootTopicChildrenTopicParentTopicChildrenVideo struct {
Typename string `json:"__typename"`
// ID is the identifier of the content.
Id testutil.ID `json:"id"`
}
// InterfaceNestingRootTopicChildrenVideo includes the requested fields of the GraphQL type Video.
type InterfaceNestingRootTopicChildrenVideo struct {
Typename string `json:"__typename"`
// ID is the identifier of the content.
Id testutil.ID `json:"id"`
Parent InterfaceNestingRootTopicChildrenVideoParentTopic `json:"parent"`
@@ -137,6 +238,7 @@ type InterfaceNestingRootTopicChildrenVideo struct {
// InterfaceNestingRootTopicChildrenVideoParentTopic includes the requested fields of the GraphQL type Topic.
type InterfaceNestingRootTopicChildrenVideoParentTopic struct {
Typename string `json:"__typename"`
// ID is documented in the Content interface.
Id testutil.ID `json:"id"`
Children []InterfaceNestingRootTopicChildrenVideoParentTopicChildrenContent `json:"children"`
@@ -144,6 +246,7 @@ type InterfaceNestingRootTopicChildrenVideoParentTopic struct {
// InterfaceNestingRootTopicChildrenVideoParentTopicChildrenArticle includes the requested fields of the GraphQL type Article.
type InterfaceNestingRootTopicChildrenVideoParentTopicChildrenArticle struct {
Typename string `json:"__typename"`
// ID is the identifier of the content.
Id testutil.ID `json:"id"`
}
@@ -156,21 +259,52 @@ type InterfaceNestingRootTopicChildrenVideoParentTopicChildrenContent interface
implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenVideoParentTopicChildrenContent()
}
func (v InterfaceNestingRootTopicChildrenVideoParentTopicChildrenArticle) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenVideoParentTopicChildrenContent() {
func (v *InterfaceNestingRootTopicChildrenVideoParentTopicChildrenArticle) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenVideoParentTopicChildrenContent() {
}
func (v InterfaceNestingRootTopicChildrenVideoParentTopicChildrenVideo) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenVideoParentTopicChildrenContent() {
func (v *InterfaceNestingRootTopicChildrenVideoParentTopicChildrenVideo) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenVideoParentTopicChildrenContent() {
}
func (v InterfaceNestingRootTopicChildrenVideoParentTopicChildrenTopic) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenVideoParentTopicChildrenContent() {
func (v *InterfaceNestingRootTopicChildrenVideoParentTopicChildrenTopic) implementsGraphQLInterfaceInterfaceNestingRootTopicChildrenVideoParentTopicChildrenContent() {
}
func __unmarshalInterfaceNestingRootTopicChildrenVideoParentTopicChildrenContent(v *InterfaceNestingRootTopicChildrenVideoParentTopicChildrenContent, m json.RawMessage) error {
if string(m) == "null" {
return nil
}
var tn struct {
TypeName string `json:"__typename"`
}
err := json.Unmarshal(m, &tn)
if err != nil {
return err
}
switch tn.TypeName {
case "Article":
*v = new(InterfaceNestingRootTopicChildrenVideoParentTopicChildrenArticle)
return json.Unmarshal(m, *v)
case "Video":
*v = new(InterfaceNestingRootTopicChildrenVideoParentTopicChildrenVideo)
return json.Unmarshal(m, *v)
case "Topic":
*v = new(InterfaceNestingRootTopicChildrenVideoParentTopicChildrenTopic)
return json.Unmarshal(m, *v)
default:
return fmt.Errorf(
`Unexpected concrete type for InterfaceNestingRootTopicChildrenVideoParentTopicChildrenContent: "%v"`, tn.TypeName)
}
}
// InterfaceNestingRootTopicChildrenVideoParentTopicChildrenTopic includes the requested fields of the GraphQL type Topic.
type InterfaceNestingRootTopicChildrenVideoParentTopicChildrenTopic struct {
Typename string `json:"__typename"`
// ID is the identifier of the content.
Id testutil.ID `json:"id"`
}
// InterfaceNestingRootTopicChildrenVideoParentTopicChildrenVideo includes the requested fields of the GraphQL type Video.
type InterfaceNestingRootTopicChildrenVideoParentTopicChildrenVideo struct {
Typename string `json:"__typename"`
// ID is the identifier of the content.
Id testutil.ID `json:"id"`
}
@@ -187,10 +321,13 @@ query InterfaceNesting {
root {
id
children {
__typename
id
parent {
__typename
id
children {
__typename
id
}
}
@@ -2,7 +2,7 @@
"operations": [
{
"operationName": "InterfaceNesting",
"query": "\nquery InterfaceNesting {\n\troot {\n\t\tid\n\t\tchildren {\n\t\t\tid\n\t\t\tparent {\n\t\t\t\tid\n\t\t\t\tchildren {\n\t\t\t\t\tid\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n",
"query": "\nquery InterfaceNesting {\n\troot {\n\t\tid\n\t\tchildren {\n\t\t\t__typename\n\t\t\tid\n\t\t\tparent {\n\t\t\t\t__typename\n\t\t\t\tid\n\t\t\t\tchildren {\n\t\t\t\t\t__typename\n\t\t\t\t\tid\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n",
"sourceLocation": "testdata/queries/InterfaceNesting.graphql"
}
]
@@ -3,55 +3,114 @@ package test
// Code generated by github.com/Khan/genqlient, DO NOT EDIT.
import (
"encoding/json"
"fmt"
"github.com/Khan/genqlient/graphql"
"github.com/Khan/genqlient/internal/testutil"
)
// InterfaceNoFragmentsQueryRandomItemArticle includes the requested fields of the GraphQL type Article.
type InterfaceNoFragmentsQueryRandomItemArticle struct {
Typename string `json:"__typename"`
// ID is the identifier of the content.
Id testutil.ID `json:"id"`
Name string `json:"name"`
}
// InterfaceNoFragmentsQueryRandomItemContent includes the requested fields of the GraphQL type Content.
// The GraphQL type's documentation follows.
//
// Content is implemented by various types like Article, Video, and Topic.
type InterfaceNoFragmentsQueryRandomItemContent interface {
implementsGraphQLInterfaceInterfaceNoFragmentsQueryRandomItemContent()
}
func (v *InterfaceNoFragmentsQueryRandomItemArticle) implementsGraphQLInterfaceInterfaceNoFragmentsQueryRandomItemContent() {
}
func (v *InterfaceNoFragmentsQueryRandomItemVideo) implementsGraphQLInterfaceInterfaceNoFragmentsQueryRandomItemContent() {
}
func (v *InterfaceNoFragmentsQueryRandomItemTopic) implementsGraphQLInterfaceInterfaceNoFragmentsQueryRandomItemContent() {
}
func __unmarshalInterfaceNoFragmentsQueryRandomItemContent(v *InterfaceNoFragmentsQueryRandomItemContent, m json.RawMessage) error {
if string(m) == "null" {
return nil
}
var tn struct {
TypeName string `json:"__typename"`
}
err := json.Unmarshal(m, &tn)
if err != nil {
return err
}
switch tn.TypeName {
case "Article":
*v = new(InterfaceNoFragmentsQueryRandomItemArticle)
return json.Unmarshal(m, *v)
case "Video":
*v = new(InterfaceNoFragmentsQueryRandomItemVideo)
return json.Unmarshal(m, *v)
case "Topic":
*v = new(InterfaceNoFragmentsQueryRandomItemTopic)
return json.Unmarshal(m, *v)
default:
return fmt.Errorf(
`Unexpected concrete type for InterfaceNoFragmentsQueryRandomItemContent: "%v"`, tn.TypeName)
}
}
// InterfaceNoFragmentsQueryRandomItemTopic includes the requested fields of the GraphQL type Topic.
type InterfaceNoFragmentsQueryRandomItemTopic struct {
Typename string `json:"__typename"`
// ID is the identifier of the content.
Id testutil.ID `json:"id"`
Name string `json:"name"`
}
// InterfaceNoFragmentsQueryRandomItemVideo includes the requested fields of the GraphQL type Video.
type InterfaceNoFragmentsQueryRandomItemVideo struct {
Typename string `json:"__typename"`
// ID is the identifier of the content.
Id testutil.ID `json:"id"`
Name string `json:"name"`
}
// InterfaceNoFragmentsQueryResponse is returned by InterfaceNoFragmentsQuery on success.
type InterfaceNoFragmentsQueryResponse struct {
Root InterfaceNoFragmentsQueryRootTopic `json:"root"`
Root InterfaceNoFragmentsQueryRootTopic `json:"root"`
RandomItem InterfaceNoFragmentsQueryRandomItemContent `json:"-"`
}
func (v *InterfaceNoFragmentsQueryResponse) UnmarshalJSON(b []byte) error {
type InterfaceNoFragmentsQueryResponseWrapper InterfaceNoFragmentsQueryResponse
var firstPass struct {
*InterfaceNoFragmentsQueryResponseWrapper
RandomItem json.RawMessage `json:"randomItem"`
}
firstPass.InterfaceNoFragmentsQueryResponseWrapper = (*InterfaceNoFragmentsQueryResponseWrapper)(v)
err := json.Unmarshal(b, &firstPass)
if err != nil {
return err
}
err = __unmarshalInterfaceNoFragmentsQueryRandomItemContent(
&v.RandomItem, firstPass.RandomItem)
if err != nil {
return err
}
return nil
}
// InterfaceNoFragmentsQueryRootTopic includes the requested fields of the GraphQL type Topic.
type InterfaceNoFragmentsQueryRootTopic struct {
// ID is documented in the Content interface.
Id testutil.ID `json:"id"`
Name string `json:"name"`
Children []InterfaceNoFragmentsQueryRootTopicChildrenContent `json:"children"`
}
// InterfaceNoFragmentsQueryRootTopicChildrenArticle includes the requested fields of the GraphQL type Article.
type InterfaceNoFragmentsQueryRootTopicChildrenArticle struct {
// ID is the identifier of the content.
Id testutil.ID `json:"id"`
Name string `json:"name"`
}
// InterfaceNoFragmentsQueryRootTopicChildrenContent includes the requested fields of the GraphQL type Content.
// The GraphQL type's documentation follows.
//
// Content is implemented by various types like Article, Video, and Topic.
type InterfaceNoFragmentsQueryRootTopicChildrenContent interface {
implementsGraphQLInterfaceInterfaceNoFragmentsQueryRootTopicChildrenContent()
}
func (v InterfaceNoFragmentsQueryRootTopicChildrenArticle) implementsGraphQLInterfaceInterfaceNoFragmentsQueryRootTopicChildrenContent() {
}
func (v InterfaceNoFragmentsQueryRootTopicChildrenVideo) implementsGraphQLInterfaceInterfaceNoFragmentsQueryRootTopicChildrenContent() {
}
func (v InterfaceNoFragmentsQueryRootTopicChildrenTopic) implementsGraphQLInterfaceInterfaceNoFragmentsQueryRootTopicChildrenContent() {
}
// InterfaceNoFragmentsQueryRootTopicChildrenTopic includes the requested fields of the GraphQL type Topic.
type InterfaceNoFragmentsQueryRootTopicChildrenTopic struct {
// ID is the identifier of the content.
Id testutil.ID `json:"id"`
Name string `json:"name"`
}
// InterfaceNoFragmentsQueryRootTopicChildrenVideo includes the requested fields of the GraphQL type Video.
type InterfaceNoFragmentsQueryRootTopicChildrenVideo struct {
// ID is the identifier of the content.
Id testutil.ID `json:"id"`
Name string `json:"name"`
}
@@ -68,10 +127,11 @@ query InterfaceNoFragmentsQuery {
root {
id
name
children {
id
name
}
}
randomItem {
__typename
id
name
}
}
`,
@@ -2,7 +2,7 @@
"operations": [
{
"operationName": "InterfaceNoFragmentsQuery",
"query": "\nquery InterfaceNoFragmentsQuery {\n\troot {\n\t\tid\n\t\tname\n\t\tchildren {\n\t\t\tid\n\t\t\tname\n\t\t}\n\t}\n}\n",
"query": "\nquery InterfaceNoFragmentsQuery {\n\troot {\n\t\tid\n\t\tname\n\t}\n\trandomItem {\n\t\t__typename\n\t\tid\n\t\tname\n\t}\n}\n",
"sourceLocation": "testdata/queries/InterfaceNoFragments.graphql"
}
]
@@ -4,6 +4,7 @@ package test
import (
"encoding/json"
"fmt"
"github.com/Khan/genqlient/graphql"
)
@@ -21,9 +22,35 @@ type UnionNoFragmentsQueryRandomLeafLeafContent interface {
implementsGraphQLInterfaceUnionNoFragmentsQueryRandomLeafLeafContent()
}
func (v UnionNoFragmentsQueryRandomLeafArticle) implementsGraphQLInterfaceUnionNoFragmentsQueryRandomLeafLeafContent() {
func (v *UnionNoFragmentsQueryRandomLeafArticle) implementsGraphQLInterfaceUnionNoFragmentsQueryRandomLeafLeafContent() {
}
func (v UnionNoFragmentsQueryRandomLeafVideo) implementsGraphQLInterfaceUnionNoFragmentsQueryRandomLeafLeafContent() {
func (v *UnionNoFragmentsQueryRandomLeafVideo) implementsGraphQLInterfaceUnionNoFragmentsQueryRandomLeafLeafContent() {
}
func __unmarshalUnionNoFragmentsQueryRandomLeafLeafContent(v *UnionNoFragmentsQueryRandomLeafLeafContent, m json.RawMessage) error {
if string(m) == "null" {
return nil
}
var tn struct {
TypeName string `json:"__typename"`
}
err := json.Unmarshal(m, &tn)
if err != nil {
return err
}
switch tn.TypeName {
case "Article":
*v = new(UnionNoFragmentsQueryRandomLeafArticle)
return json.Unmarshal(m, *v)
case "Video":
*v = new(UnionNoFragmentsQueryRandomLeafVideo)
return json.Unmarshal(m, *v)
default:
return fmt.Errorf(
`Unexpected concrete type for UnionNoFragmentsQueryRandomLeafLeafContent: "%v"`, tn.TypeName)
}
}
// UnionNoFragmentsQueryRandomLeafVideo includes the requested fields of the GraphQL type Video.
@@ -37,39 +64,22 @@ type UnionNoFragmentsQueryResponse struct {
}
func (v *UnionNoFragmentsQueryResponse) UnmarshalJSON(b []byte) error {
type UnionNoFragmentsQueryResponseWrapper UnionNoFragmentsQueryResponse
var firstPass struct {
*UnionNoFragmentsQueryResponse
*UnionNoFragmentsQueryResponseWrapper
RandomLeaf json.RawMessage `json:"randomLeaf"`
}
firstPass.UnionNoFragmentsQueryResponse = v
firstPass.UnionNoFragmentsQueryResponseWrapper = (*UnionNoFragmentsQueryResponseWrapper)(v)
err := json.Unmarshal(b, &firstPass)
if err != nil {
return err
}
var tn struct {
TypeName string `json:"__typename"`
}
err = json.Unmarshal(firstPass.RandomLeaf, &tn)
if err != nil {
return err
}
switch tn.TypeName {
case "Article":
v.RandomLeaf = UnionNoFragmentsQueryRandomLeafArticle{}
err = json.Unmarshal(
firstPass.RandomLeaf, &v.RandomLeaf)
case "Video":
v.RandomLeaf = UnionNoFragmentsQueryRandomLeafVideo{}
err = json.Unmarshal(
firstPass.RandomLeaf, &v.RandomLeaf)
}
err = __unmarshalUnionNoFragmentsQueryRandomLeafLeafContent(
&v.RandomLeaf, firstPass.RandomLeaf)
if err != nil {
return err
}
@@ -0,0 +1 @@
testdata/errors/MissingTypeName.schema.graphql:2: union/interface type I must request __typename
@@ -0,0 +1 @@
testdata/errors/MissingTypeName.schema.graphql:2: union/interface type I must request __typename