Add support for interfaces, part 3: automatically add __typename (#56)
## Summary: In #52, I added support for interface types, but with the simplifying restriction (among others) that the user must request the field `__typename`. In this commit, I remove this restriction. The basic idea is simple: we preprocess the query to add `__typename`. The implementation isn't much more complicated! Although it required some new wiring in a few places. Issue: https://github.com/Khan/genqlient/issues/8 ## Test plan: make check Author: benjaminjkraft Reviewers: dnerdy, aberkan, 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/56
This commit is contained in:
+5
-13
@@ -259,19 +259,6 @@ func (g *generator) convertDefinition(
|
|||||||
return nil, errorf(pos, "not implemented: %v", def.Kind)
|
return nil, errorf(pos, "not implemented: %v", def.Kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to request __typename so we know which concrete type to use.
|
|
||||||
hasTypename := false
|
|
||||||
for _, selection := range selectionSet {
|
|
||||||
field, ok := selection.(*ast.Field)
|
|
||||||
if ok && field.Name == "__typename" {
|
|
||||||
hasTypename = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !hasTypename {
|
|
||||||
// TODO(benkraft): Instead, modify the query to add __typename.
|
|
||||||
return nil, errorf(pos, "union/interface type %s must request __typename", def.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
implementationTypes := g.schema.GetPossibleTypes(def)
|
implementationTypes := g.schema.GetPossibleTypes(def)
|
||||||
goType := &goInterfaceType{
|
goType := &goInterfaceType{
|
||||||
GoName: name,
|
GoName: name,
|
||||||
@@ -283,6 +270,11 @@ func (g *generator) convertDefinition(
|
|||||||
|
|
||||||
for i, implDef := range implementationTypes {
|
for i, implDef := range implementationTypes {
|
||||||
implName, implNamePrefix := g.typeName(namePrefix, implDef)
|
implName, implNamePrefix := g.typeName(namePrefix, implDef)
|
||||||
|
// TODO(benkraft): In principle we should skip generating a Go
|
||||||
|
// field for __typename each of these impl-defs if you didn't
|
||||||
|
// request it (and it was automatically added by
|
||||||
|
// preprocessQueryDocument). But in practice it doesn't really
|
||||||
|
// hurt, and would be extra work to avoid, so we just leave it.
|
||||||
implTyp, err := g.convertDefinition(
|
implTyp, err := g.convertDefinition(
|
||||||
implName, implNamePrefix, implDef, pos, selectionSet, queryOptions)
|
implName, implNamePrefix, implDef, pos, selectionSet, queryOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+76
-4
@@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
"github.com/vektah/gqlparser/v2/formatter"
|
"github.com/vektah/gqlparser/v2/formatter"
|
||||||
|
"github.com/vektah/gqlparser/v2/validator"
|
||||||
"golang.org/x/tools/imports"
|
"golang.org/x/tools/imports"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -140,17 +141,88 @@ func (g *generator) getArgument(
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Preprocess each query to make any changes that genqlient needs.
|
||||||
|
//
|
||||||
|
// At present, the only change is that we add __typename, if not already
|
||||||
|
// requested, to each field of interface type, so we can use the right types
|
||||||
|
// when unmarshaling.
|
||||||
|
func (g *generator) preprocessQueryDocument(doc *ast.QueryDocument) {
|
||||||
|
var observers validator.Events
|
||||||
|
// We want to ensure that everywhere you ask for some list of fields (a
|
||||||
|
// selection-set) from an interface (or union) type, you ask for its
|
||||||
|
// __typename field. There are four places we might find a selection-set:
|
||||||
|
// at the toplevel of a query, on a field, or in an inline or named
|
||||||
|
// fragment. The toplevel of a query must be an object type, so we don't
|
||||||
|
// need to consider that.
|
||||||
|
// TODO(benkraft): Once we support fragments, figure out whether we need to
|
||||||
|
// traverse inline/named fragments here too.
|
||||||
|
observers.OnField(func(_ *validator.Walker, field *ast.Field) {
|
||||||
|
// We are interested in a field from the query like
|
||||||
|
// field { subField ... }
|
||||||
|
// where the schema looks like
|
||||||
|
// type ... { # or interface/union
|
||||||
|
// field: FieldType # or [FieldType!]! etc.
|
||||||
|
// }
|
||||||
|
// interface FieldType { # or union
|
||||||
|
// subField: ...
|
||||||
|
// }
|
||||||
|
// If FieldType is an interface/union, and none of the subFields is
|
||||||
|
// __typename, we want to change the query to
|
||||||
|
// field { __typename subField ... }
|
||||||
|
|
||||||
|
fieldType := g.schema.Types[field.Definition.Type.Name()]
|
||||||
|
if fieldType.Kind != ast.Interface && fieldType.Kind != ast.Union {
|
||||||
|
return // a concrete type
|
||||||
|
}
|
||||||
|
|
||||||
|
hasTypename := false
|
||||||
|
for _, selection := range field.SelectionSet {
|
||||||
|
// Check if we already selected __typename. We ignore fragments,
|
||||||
|
// because we want __typename as a toplevel field.
|
||||||
|
subField, ok := selection.(*ast.Field)
|
||||||
|
if ok && subField.Name == "__typename" {
|
||||||
|
hasTypename = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasTypename {
|
||||||
|
// Ok, we need to add the field!
|
||||||
|
field.SelectionSet = append(ast.SelectionSet{
|
||||||
|
&ast.Field{
|
||||||
|
Alias: "__typename", Name: "__typename",
|
||||||
|
// Fake definition for the magic field __typename cribbed
|
||||||
|
// from gqlparser's validator/walk.go, equivalent to
|
||||||
|
// __typename: String
|
||||||
|
// TODO(benkraft): This should in principle be
|
||||||
|
// __typename: String!
|
||||||
|
// But genqlient doesn't care, so we just match gqlparser.
|
||||||
|
Definition: &ast.FieldDefinition{
|
||||||
|
Name: "__typename",
|
||||||
|
Type: ast.NamedType("String", nil /* pos */),
|
||||||
|
},
|
||||||
|
// Definition of the object that contains this field, i.e.
|
||||||
|
// FieldType.
|
||||||
|
ObjectDefinition: fieldType,
|
||||||
|
},
|
||||||
|
}, field.SelectionSet...)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
validator.Walk(g.schema, doc, &observers)
|
||||||
|
}
|
||||||
|
|
||||||
func (g *generator) addOperation(op *ast.OperationDefinition) error {
|
func (g *generator) addOperation(op *ast.OperationDefinition) error {
|
||||||
if op.Name == "" {
|
if op.Name == "" {
|
||||||
return errorf(op.Position, "operations must have operation-names")
|
return errorf(op.Position, "operations must have operation-names")
|
||||||
}
|
}
|
||||||
|
|
||||||
var builder strings.Builder
|
queryDoc := &ast.QueryDocument{
|
||||||
f := formatter.NewFormatter(&builder)
|
|
||||||
f.FormatQueryDocument(&ast.QueryDocument{
|
|
||||||
Operations: ast.OperationList{op},
|
Operations: ast.OperationList{op},
|
||||||
// TODO: handle fragments
|
// TODO: handle fragments
|
||||||
})
|
}
|
||||||
|
g.preprocessQueryDocument(queryDoc)
|
||||||
|
|
||||||
|
var builder strings.Builder
|
||||||
|
f := formatter.NewFormatter(&builder)
|
||||||
|
f.FormatQueryDocument(queryDoc)
|
||||||
|
|
||||||
commentLines, directive, err := g.parsePrecedingComment(op, op.Position)
|
commentLines, directive, err := g.parsePrecedingComment(op, op.Position)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -146,6 +146,10 @@ func (g *generator) parsePrecedingComment(
|
|||||||
pos *ast.Position,
|
pos *ast.Position,
|
||||||
) (comment string, directive *GenqlientDirective, err error) {
|
) (comment string, directive *GenqlientDirective, err error) {
|
||||||
directive = new(GenqlientDirective)
|
directive = new(GenqlientDirective)
|
||||||
|
if pos == nil || pos.Src == nil { // node was added by genqlient itself
|
||||||
|
return "", directive, nil // treated as if there were no comment
|
||||||
|
}
|
||||||
|
|
||||||
var commentLines []string
|
var commentLines []string
|
||||||
sourceLines := strings.Split(pos.Src.Input, "\n")
|
sourceLines := strings.Split(pos.Src.Input, "\n")
|
||||||
for i := pos.Line - 1; i > 0; i-- {
|
for i := pos.Line - 1; i > 0; i-- {
|
||||||
|
|||||||
-5
@@ -1,5 +0,0 @@
|
|||||||
package errors
|
|
||||||
|
|
||||||
const _ = `# @genqlient
|
|
||||||
query MyQuery { i { f } }
|
|
||||||
`
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
query MyQuery { i { f } }
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
type Query {
|
|
||||||
i: I
|
|
||||||
}
|
|
||||||
|
|
||||||
type T implements I {
|
|
||||||
f: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
interface I {
|
|
||||||
f: String!
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,6 @@ query InterfaceListField {
|
|||||||
id
|
id
|
||||||
name
|
name
|
||||||
children {
|
children {
|
||||||
__typename
|
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
@@ -13,7 +12,6 @@ query InterfaceListField {
|
|||||||
id
|
id
|
||||||
name
|
name
|
||||||
children {
|
children {
|
||||||
__typename
|
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
query InterfaceListOfListOfListsField {
|
query InterfaceListOfListOfListsField {
|
||||||
listOfListsOfListsOfContent { __typename id name }
|
listOfListsOfListsOfContent { id name }
|
||||||
# @genqlient(pointer: true)
|
# @genqlient(pointer: true)
|
||||||
withPointer: listOfListsOfListsOfContent { __typename id name }
|
withPointer: listOfListsOfListsOfContent { id name }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,10 @@ query InterfaceNesting {
|
|||||||
root {
|
root {
|
||||||
id
|
id
|
||||||
children {
|
children {
|
||||||
__typename
|
|
||||||
id
|
id
|
||||||
parent {
|
parent {
|
||||||
__typename
|
|
||||||
id
|
id
|
||||||
children {
|
children {
|
||||||
__typename
|
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -1,6 +1,7 @@
|
|||||||
query InterfaceNoFragmentsQuery {
|
query InterfaceNoFragmentsQuery {
|
||||||
root { id name } # (make sure sibling fields work)
|
root { id name } # (make sure sibling fields work)
|
||||||
randomItem { __typename id name }
|
randomItem { id name }
|
||||||
|
randomItemWithTypeName: randomItem { __typename id name }
|
||||||
# @genqlient(pointer: true)
|
# @genqlient(pointer: true)
|
||||||
withPointer: randomItem { __typename id name }
|
withPointer: randomItem { id name }
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -160,7 +160,7 @@ func (v *InterfaceListOfListOfListsFieldResponse) UnmarshalJSON(b []byte) error
|
|||||||
|
|
||||||
// InterfaceListOfListOfListsFieldWithPointerArticle includes the requested fields of the GraphQL type Article.
|
// InterfaceListOfListOfListsFieldWithPointerArticle includes the requested fields of the GraphQL type Article.
|
||||||
type InterfaceListOfListOfListsFieldWithPointerArticle struct {
|
type InterfaceListOfListOfListsFieldWithPointerArticle struct {
|
||||||
Typename *string `json:"__typename"`
|
Typename string `json:"__typename"`
|
||||||
// ID is the identifier of the content.
|
// ID is the identifier of the content.
|
||||||
Id *testutil.ID `json:"id"`
|
Id *testutil.ID `json:"id"`
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
@@ -212,7 +212,7 @@ func __unmarshalInterfaceListOfListOfListsFieldWithPointerContent(v *InterfaceLi
|
|||||||
|
|
||||||
// InterfaceListOfListOfListsFieldWithPointerTopic includes the requested fields of the GraphQL type Topic.
|
// InterfaceListOfListOfListsFieldWithPointerTopic includes the requested fields of the GraphQL type Topic.
|
||||||
type InterfaceListOfListOfListsFieldWithPointerTopic struct {
|
type InterfaceListOfListOfListsFieldWithPointerTopic struct {
|
||||||
Typename *string `json:"__typename"`
|
Typename string `json:"__typename"`
|
||||||
// ID is the identifier of the content.
|
// ID is the identifier of the content.
|
||||||
Id *testutil.ID `json:"id"`
|
Id *testutil.ID `json:"id"`
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
@@ -220,7 +220,7 @@ type InterfaceListOfListOfListsFieldWithPointerTopic struct {
|
|||||||
|
|
||||||
// InterfaceListOfListOfListsFieldWithPointerVideo includes the requested fields of the GraphQL type Video.
|
// InterfaceListOfListOfListsFieldWithPointerVideo includes the requested fields of the GraphQL type Video.
|
||||||
type InterfaceListOfListOfListsFieldWithPointerVideo struct {
|
type InterfaceListOfListOfListsFieldWithPointerVideo struct {
|
||||||
Typename *string `json:"__typename"`
|
Typename string `json:"__typename"`
|
||||||
// ID is the identifier of the content.
|
// ID is the identifier of the content.
|
||||||
Id *testutil.ID `json:"id"`
|
Id *testutil.ID `json:"id"`
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
|
|||||||
Vendored
-4
@@ -65,7 +65,6 @@ type InterfaceNestingRootTopicChildrenArticle struct {
|
|||||||
|
|
||||||
// InterfaceNestingRootTopicChildrenArticleParentTopic includes the requested fields of the GraphQL type Topic.
|
// InterfaceNestingRootTopicChildrenArticleParentTopic includes the requested fields of the GraphQL type Topic.
|
||||||
type InterfaceNestingRootTopicChildrenArticleParentTopic struct {
|
type InterfaceNestingRootTopicChildrenArticleParentTopic struct {
|
||||||
Typename string `json:"__typename"`
|
|
||||||
// ID is documented in the Content interface.
|
// ID is documented in the Content interface.
|
||||||
Id testutil.ID `json:"id"`
|
Id testutil.ID `json:"id"`
|
||||||
Children []InterfaceNestingRootTopicChildrenArticleParentTopicChildrenContent `json:"-"`
|
Children []InterfaceNestingRootTopicChildrenArticleParentTopicChildrenContent `json:"-"`
|
||||||
@@ -223,7 +222,6 @@ type InterfaceNestingRootTopicChildrenTopic struct {
|
|||||||
|
|
||||||
// InterfaceNestingRootTopicChildrenTopicParentTopic includes the requested fields of the GraphQL type Topic.
|
// InterfaceNestingRootTopicChildrenTopicParentTopic includes the requested fields of the GraphQL type Topic.
|
||||||
type InterfaceNestingRootTopicChildrenTopicParentTopic struct {
|
type InterfaceNestingRootTopicChildrenTopicParentTopic struct {
|
||||||
Typename string `json:"__typename"`
|
|
||||||
// ID is documented in the Content interface.
|
// ID is documented in the Content interface.
|
||||||
Id testutil.ID `json:"id"`
|
Id testutil.ID `json:"id"`
|
||||||
Children []InterfaceNestingRootTopicChildrenTopicParentTopicChildrenContent `json:"-"`
|
Children []InterfaceNestingRootTopicChildrenTopicParentTopicChildrenContent `json:"-"`
|
||||||
@@ -337,7 +335,6 @@ type InterfaceNestingRootTopicChildrenVideo struct {
|
|||||||
|
|
||||||
// InterfaceNestingRootTopicChildrenVideoParentTopic includes the requested fields of the GraphQL type Topic.
|
// InterfaceNestingRootTopicChildrenVideoParentTopic includes the requested fields of the GraphQL type Topic.
|
||||||
type InterfaceNestingRootTopicChildrenVideoParentTopic struct {
|
type InterfaceNestingRootTopicChildrenVideoParentTopic struct {
|
||||||
Typename string `json:"__typename"`
|
|
||||||
// ID is documented in the Content interface.
|
// ID is documented in the Content interface.
|
||||||
Id testutil.ID `json:"id"`
|
Id testutil.ID `json:"id"`
|
||||||
Children []InterfaceNestingRootTopicChildrenVideoParentTopicChildrenContent `json:"-"`
|
Children []InterfaceNestingRootTopicChildrenVideoParentTopicChildrenContent `json:"-"`
|
||||||
@@ -456,7 +453,6 @@ query InterfaceNesting {
|
|||||||
__typename
|
__typename
|
||||||
id
|
id
|
||||||
parent {
|
parent {
|
||||||
__typename
|
|
||||||
id
|
id
|
||||||
children {
|
children {
|
||||||
__typename
|
__typename
|
||||||
|
|||||||
Vendored
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
"operations": [
|
"operations": [
|
||||||
{
|
{
|
||||||
"operationName": "InterfaceNesting",
|
"operationName": "InterfaceNesting",
|
||||||
"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",
|
"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\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"
|
"sourceLocation": "testdata/queries/InterfaceNesting.graphql"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
+92
-8
@@ -78,11 +78,80 @@ type InterfaceNoFragmentsQueryRandomItemVideo struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InterfaceNoFragmentsQueryRandomItemWithTypeNameArticle includes the requested fields of the GraphQL type Article.
|
||||||
|
type InterfaceNoFragmentsQueryRandomItemWithTypeNameArticle struct {
|
||||||
|
Typename string `json:"__typename"`
|
||||||
|
// ID is the identifier of the content.
|
||||||
|
Id testutil.ID `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterfaceNoFragmentsQueryRandomItemWithTypeNameContent 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 InterfaceNoFragmentsQueryRandomItemWithTypeNameContent interface {
|
||||||
|
implementsGraphQLInterfaceInterfaceNoFragmentsQueryRandomItemWithTypeNameContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *InterfaceNoFragmentsQueryRandomItemWithTypeNameArticle) implementsGraphQLInterfaceInterfaceNoFragmentsQueryRandomItemWithTypeNameContent() {
|
||||||
|
}
|
||||||
|
func (v *InterfaceNoFragmentsQueryRandomItemWithTypeNameVideo) implementsGraphQLInterfaceInterfaceNoFragmentsQueryRandomItemWithTypeNameContent() {
|
||||||
|
}
|
||||||
|
func (v *InterfaceNoFragmentsQueryRandomItemWithTypeNameTopic) implementsGraphQLInterfaceInterfaceNoFragmentsQueryRandomItemWithTypeNameContent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func __unmarshalInterfaceNoFragmentsQueryRandomItemWithTypeNameContent(v *InterfaceNoFragmentsQueryRandomItemWithTypeNameContent, 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(InterfaceNoFragmentsQueryRandomItemWithTypeNameArticle)
|
||||||
|
return json.Unmarshal(m, *v)
|
||||||
|
case "Video":
|
||||||
|
*v = new(InterfaceNoFragmentsQueryRandomItemWithTypeNameVideo)
|
||||||
|
return json.Unmarshal(m, *v)
|
||||||
|
case "Topic":
|
||||||
|
*v = new(InterfaceNoFragmentsQueryRandomItemWithTypeNameTopic)
|
||||||
|
return json.Unmarshal(m, *v)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf(
|
||||||
|
`Unexpected concrete type for InterfaceNoFragmentsQueryRandomItemWithTypeNameContent: "%v"`, tn.TypeName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterfaceNoFragmentsQueryRandomItemWithTypeNameTopic includes the requested fields of the GraphQL type Topic.
|
||||||
|
type InterfaceNoFragmentsQueryRandomItemWithTypeNameTopic struct {
|
||||||
|
Typename string `json:"__typename"`
|
||||||
|
// ID is the identifier of the content.
|
||||||
|
Id testutil.ID `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterfaceNoFragmentsQueryRandomItemWithTypeNameVideo includes the requested fields of the GraphQL type Video.
|
||||||
|
type InterfaceNoFragmentsQueryRandomItemWithTypeNameVideo 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.
|
// InterfaceNoFragmentsQueryResponse is returned by InterfaceNoFragmentsQuery on success.
|
||||||
type InterfaceNoFragmentsQueryResponse struct {
|
type InterfaceNoFragmentsQueryResponse struct {
|
||||||
Root InterfaceNoFragmentsQueryRootTopic `json:"root"`
|
Root InterfaceNoFragmentsQueryRootTopic `json:"root"`
|
||||||
RandomItem InterfaceNoFragmentsQueryRandomItemContent `json:"-"`
|
RandomItem InterfaceNoFragmentsQueryRandomItemContent `json:"-"`
|
||||||
WithPointer *InterfaceNoFragmentsQueryWithPointerContent `json:"-"`
|
RandomItemWithTypeName InterfaceNoFragmentsQueryRandomItemWithTypeNameContent `json:"-"`
|
||||||
|
WithPointer *InterfaceNoFragmentsQueryWithPointerContent `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *InterfaceNoFragmentsQueryResponse) UnmarshalJSON(b []byte) error {
|
func (v *InterfaceNoFragmentsQueryResponse) UnmarshalJSON(b []byte) error {
|
||||||
@@ -91,8 +160,9 @@ func (v *InterfaceNoFragmentsQueryResponse) UnmarshalJSON(b []byte) error {
|
|||||||
|
|
||||||
var firstPass struct {
|
var firstPass struct {
|
||||||
*InterfaceNoFragmentsQueryResponseWrapper
|
*InterfaceNoFragmentsQueryResponseWrapper
|
||||||
RandomItem json.RawMessage `json:"randomItem"`
|
RandomItem json.RawMessage `json:"randomItem"`
|
||||||
WithPointer json.RawMessage `json:"withPointer"`
|
RandomItemWithTypeName json.RawMessage `json:"randomItemWithTypeName"`
|
||||||
|
WithPointer json.RawMessage `json:"withPointer"`
|
||||||
}
|
}
|
||||||
firstPass.InterfaceNoFragmentsQueryResponseWrapper = (*InterfaceNoFragmentsQueryResponseWrapper)(v)
|
firstPass.InterfaceNoFragmentsQueryResponseWrapper = (*InterfaceNoFragmentsQueryResponseWrapper)(v)
|
||||||
|
|
||||||
@@ -110,6 +180,15 @@ func (v *InterfaceNoFragmentsQueryResponse) UnmarshalJSON(b []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
target := &v.RandomItemWithTypeName
|
||||||
|
raw := firstPass.RandomItemWithTypeName
|
||||||
|
err = __unmarshalInterfaceNoFragmentsQueryRandomItemWithTypeNameContent(
|
||||||
|
target, raw)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
{
|
{
|
||||||
target := &v.WithPointer
|
target := &v.WithPointer
|
||||||
raw := firstPass.WithPointer
|
raw := firstPass.WithPointer
|
||||||
@@ -132,7 +211,7 @@ type InterfaceNoFragmentsQueryRootTopic struct {
|
|||||||
|
|
||||||
// InterfaceNoFragmentsQueryWithPointerArticle includes the requested fields of the GraphQL type Article.
|
// InterfaceNoFragmentsQueryWithPointerArticle includes the requested fields of the GraphQL type Article.
|
||||||
type InterfaceNoFragmentsQueryWithPointerArticle struct {
|
type InterfaceNoFragmentsQueryWithPointerArticle struct {
|
||||||
Typename *string `json:"__typename"`
|
Typename string `json:"__typename"`
|
||||||
// ID is the identifier of the content.
|
// ID is the identifier of the content.
|
||||||
Id *testutil.ID `json:"id"`
|
Id *testutil.ID `json:"id"`
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
@@ -184,7 +263,7 @@ func __unmarshalInterfaceNoFragmentsQueryWithPointerContent(v *InterfaceNoFragme
|
|||||||
|
|
||||||
// InterfaceNoFragmentsQueryWithPointerTopic includes the requested fields of the GraphQL type Topic.
|
// InterfaceNoFragmentsQueryWithPointerTopic includes the requested fields of the GraphQL type Topic.
|
||||||
type InterfaceNoFragmentsQueryWithPointerTopic struct {
|
type InterfaceNoFragmentsQueryWithPointerTopic struct {
|
||||||
Typename *string `json:"__typename"`
|
Typename string `json:"__typename"`
|
||||||
// ID is the identifier of the content.
|
// ID is the identifier of the content.
|
||||||
Id *testutil.ID `json:"id"`
|
Id *testutil.ID `json:"id"`
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
@@ -192,7 +271,7 @@ type InterfaceNoFragmentsQueryWithPointerTopic struct {
|
|||||||
|
|
||||||
// InterfaceNoFragmentsQueryWithPointerVideo includes the requested fields of the GraphQL type Video.
|
// InterfaceNoFragmentsQueryWithPointerVideo includes the requested fields of the GraphQL type Video.
|
||||||
type InterfaceNoFragmentsQueryWithPointerVideo struct {
|
type InterfaceNoFragmentsQueryWithPointerVideo struct {
|
||||||
Typename *string `json:"__typename"`
|
Typename string `json:"__typename"`
|
||||||
// ID is the identifier of the content.
|
// ID is the identifier of the content.
|
||||||
Id *testutil.ID `json:"id"`
|
Id *testutil.ID `json:"id"`
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
@@ -216,6 +295,11 @@ query InterfaceNoFragmentsQuery {
|
|||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
randomItemWithTypeName: randomItem {
|
||||||
|
__typename
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
withPointer: randomItem {
|
withPointer: randomItem {
|
||||||
__typename
|
__typename
|
||||||
id
|
id
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
"operations": [
|
"operations": [
|
||||||
{
|
{
|
||||||
"operationName": "InterfaceNoFragmentsQuery",
|
"operationName": "InterfaceNoFragmentsQuery",
|
||||||
"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\twithPointer: randomItem {\n\t\t__typename\n\t\tid\n\t\tname\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\trandomItemWithTypeName: randomItem {\n\t\t__typename\n\t\tid\n\t\tname\n\t}\n\twithPointer: randomItem {\n\t\t__typename\n\t\tid\n\t\tname\n\t}\n}\n",
|
||||||
"sourceLocation": "testdata/queries/InterfaceNoFragments.graphql"
|
"sourceLocation": "testdata/queries/InterfaceNoFragments.graphql"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ func TestVariables(t *testing.T) {
|
|||||||
func TestInterfaceNoFragments(t *testing.T) {
|
func TestInterfaceNoFragments(t *testing.T) {
|
||||||
_ = `# @genqlient
|
_ = `# @genqlient
|
||||||
query queryWithInterfaceNoFragments($id: ID!) {
|
query queryWithInterfaceNoFragments($id: ID!) {
|
||||||
being(id: $id) { __typename id name }
|
being(id: $id) { id name }
|
||||||
me { id name }
|
me { id name }
|
||||||
}`
|
}`
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ func TestInterfaceNoFragments(t *testing.T) {
|
|||||||
func TestInterfaceListField(t *testing.T) {
|
func TestInterfaceListField(t *testing.T) {
|
||||||
_ = `# @genqlient
|
_ = `# @genqlient
|
||||||
query queryWithInterfaceListField($ids: [ID!]!) {
|
query queryWithInterfaceListField($ids: [ID!]!) {
|
||||||
beings(ids: $ids) { __typename id name }
|
beings(ids: $ids) { id name }
|
||||||
}`
|
}`
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|||||||
Reference in New Issue
Block a user