Add support for concrete-typed named fragments (#75)
## Summary:
In previous commits I added support to genqlient for interfaces and
inline fragments. This means the only query structures that remain are
named fragments and their spreads, e.g.
```
fragment MyFragment on MyType { myField }
query MyQuery { getMyType { ...MyFragment } }
```
Other than mere completionism, these are potentially useful for code
sharing: you can spread the same fragment multiple places; and then
genqlient can notice that and generate the same type for each. (They
can even be shared between different queries in the same package.)
In this commit I add support for named fragments of concrete
(object/struct, not interface) type, spread into either concrete or
abstract scope. For genqlient's purposes, these are a new "root"
type-name, just like each operation, and are then embedded into the
appropriate struct. (Using embeds allows their fields to be referenced
as fields of the containing type, if convenient. Further design
considerations are discussed in DESIGN.md.)
This requires new code in two main places (plus miscellaneous glue),
both nontrivial but neither particularly complex:
- We need to actually traverse both structures and generate the types
(in `convert.go`).
- We need to decide which fragments from this package to send to the
server, both for good hyigene and because GraphQL requires we send
only ones this query uses (in `generate.go`).
- We need a little new wiring for options -- because fragments can be
shared between queries they get their own toplevel options, rather
than inheriting the query's options.
Finally, this required slightly subtler changes to how we do
unmarshaling (in `types.go` and `unmarshal.go.tmpl`). Basically,
because embedded fields' methods, including `UnmarshalJSON`, get
promoted to the parent type, and because the JSON library ignores their
fields when shadowed by those of the parent type, we need a little bit
of special logic in each such parent type to do its own unmarshal and
then delegate to each embed. This is similar (and much simpler) to
what we did for interfaces, although it required some changes to the
"method-hiding" trick (used for both). It's only really necessary in
certain specific cases (namely when an embedded type has an
`UnmarshalJSON` method or a field with the same name as the embedder),
but it's easier to just generate it always. This is all described in
more detail inline.
This does not support fragments of abstract type, which have their own
complexities. I'll address those, which are now the only remaining
piece of #8, in a future commit.
Issue: https://github.com/Khan/genqlient/issues/8
## Test plan:
make check
Author: benjaminjkraft
Reviewers: dnerdy, benjaminjkraft, aberkan, MiguelCastillo
Required Reviewers:
Approved By: dnerdy
Checks: ✅ Lint, ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Test (1.13), ✅ 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/75
This commit is contained in:
+75
-2
@@ -330,7 +330,12 @@ func (g *generator) convertSelectionSet(
|
||||
}
|
||||
fields = append(fields, field)
|
||||
case *ast.FragmentSpread:
|
||||
return nil, errorf(selection.Position, "not implemented: %T", selection)
|
||||
maybeField, err := g.convertFragmentSpread(selection, containingTypedef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if maybeField != nil {
|
||||
fields = append(fields, maybeField)
|
||||
}
|
||||
case *ast.InlineFragment:
|
||||
// (Note this will return nil, nil if the fragment doesn't apply to
|
||||
// this type.)
|
||||
@@ -354,7 +359,9 @@ func (g *generator) convertSelectionSet(
|
||||
// GraphQL (and, effectively, JSON) requires that all fields with the
|
||||
// same alias (JSON-name) must be the same (i.e. refer to the same
|
||||
// field), so that's how we deduplicate.
|
||||
if fieldNames[field.JSONName] {
|
||||
// It's fine to have duplicate embeds (i.e. via named fragments), even
|
||||
// ones with complicated overlaps, since they are separate types to us.
|
||||
if field.JSONName != "" && fieldNames[field.JSONName] {
|
||||
// GraphQL (and, effectively, JSON) forbids you from having two
|
||||
// fields with the same alias (JSON-name) that refer to different
|
||||
// GraphQL fields. But it does allow you to have the same field
|
||||
@@ -444,6 +451,72 @@ func (g *generator) convertInlineFragment(
|
||||
containingTypedef, queryOptions)
|
||||
}
|
||||
|
||||
// convertFragmentSpread converts a single GraphQL fragment-spread
|
||||
// (`...MyFragment`) into a Go struct-field. If the fragment does not apply to
|
||||
// this type, returns nil.
|
||||
//
|
||||
// containingTypedef is as described in convertInlineFragment, above.
|
||||
func (g *generator) convertFragmentSpread(
|
||||
fragmentSpread *ast.FragmentSpread,
|
||||
containingTypedef *ast.Definition,
|
||||
) (*goStructField, error) {
|
||||
if !fragmentMatches(containingTypedef, fragmentSpread.Definition.Definition) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
typ, ok := g.typeMap[fragmentSpread.Name]
|
||||
if !ok {
|
||||
// If we haven't yet, convert the fragment itself. Note that fragments
|
||||
// aren't allowed to have cycles, so this won't recurse forever.
|
||||
var err error
|
||||
typ, err = g.convertNamedFragment(fragmentSpread.Definition)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &goStructField{GoName: "" /* i.e. embedded */, GoType: typ}, nil
|
||||
}
|
||||
|
||||
// convertNamedFragment converts a single GraphQL named fragment-definition
|
||||
// (`fragment MyFragment on MyType { ... }`) into a Go struct.
|
||||
func (g *generator) convertNamedFragment(fragment *ast.FragmentDefinition) (goType, error) {
|
||||
typ := g.schema.Types[fragment.TypeCondition]
|
||||
if !g.Config.AllowBrokenFeatures &&
|
||||
(typ.Kind == ast.Interface || typ.Kind == ast.Union) {
|
||||
return nil, errorf(fragment.Position, "not implemented: abstract-typed fragments")
|
||||
}
|
||||
|
||||
description, directive, err := g.parsePrecedingComment(fragment, fragment.Position)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the user included a comment, use that. Else make up something
|
||||
// generic; there's not much to say though.
|
||||
if description == "" {
|
||||
description = fmt.Sprintf(
|
||||
"%v includes the GraphQL fields of %v requested by the fragment %v.",
|
||||
fragment.Name, fragment.TypeCondition, fragment.Name)
|
||||
}
|
||||
|
||||
fields, err := g.convertSelectionSet(
|
||||
newPrefixList(fragment.Name), fragment.SelectionSet, typ, directive)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
goType := &goStructType{
|
||||
GoName: fragment.Name,
|
||||
Description: description,
|
||||
GraphQLName: fragment.TypeCondition,
|
||||
Fields: fields,
|
||||
Incomplete: false,
|
||||
}
|
||||
g.typeMap[fragment.Name] = goType
|
||||
return goType, nil
|
||||
}
|
||||
|
||||
// convertField converts a single GraphQL operation-field into a Go
|
||||
// struct-field (and its type).
|
||||
//
|
||||
|
||||
+62
-15
@@ -35,6 +35,11 @@ type generator struct {
|
||||
templateCache map[string]*template.Template
|
||||
// Schema we are generating code against
|
||||
schema *ast.Schema
|
||||
// Named fragments (map by name), so we can look them up from spreads.
|
||||
// TODO(benkraft): In theory we shouldn't need this, we can just use
|
||||
// ast.FragmentSpread.Definition, but for some reason it doesn't seem to be
|
||||
// set consistently, even post-validation.
|
||||
fragments map[string]*ast.FragmentDefinition
|
||||
}
|
||||
|
||||
// JSON tags in operation are for ExportOperations (see Config for details).
|
||||
@@ -67,7 +72,11 @@ type argument struct {
|
||||
Options *GenqlientDirective
|
||||
}
|
||||
|
||||
func newGenerator(config *Config, schema *ast.Schema) (*generator, error) {
|
||||
func newGenerator(
|
||||
config *Config,
|
||||
schema *ast.Schema,
|
||||
fragments ast.FragmentDefinitionList,
|
||||
) (*generator, error) {
|
||||
g := generator{
|
||||
Config: config,
|
||||
typeMap: map[string]goType{},
|
||||
@@ -75,6 +84,11 @@ func newGenerator(config *Config, schema *ast.Schema) (*generator, error) {
|
||||
usedAliases: map[string]bool{},
|
||||
templateCache: map[string]*template.Template{},
|
||||
schema: schema,
|
||||
fragments: make(map[string]*ast.FragmentDefinition, len(fragments)),
|
||||
}
|
||||
|
||||
for _, fragment := range fragments {
|
||||
g.fragments[fragment.Name] = fragment
|
||||
}
|
||||
|
||||
_, err := g.addRef("github.com/Khan/genqlient/graphql.Client")
|
||||
@@ -146,6 +160,38 @@ func (g *generator) getArgument(
|
||||
}, nil
|
||||
}
|
||||
|
||||
// usedFragmentNames returns the named-fragments used by (i.e. spread into)
|
||||
// this operation.
|
||||
func (g *generator) usedFragments(op *ast.OperationDefinition) ast.FragmentDefinitionList {
|
||||
var retval, queue ast.FragmentDefinitionList
|
||||
seen := map[string]bool{}
|
||||
|
||||
var observers validator.Events
|
||||
// Fragment-spreads are easy to find; just ask for them!
|
||||
observers.OnFragmentSpread(func(_ *validator.Walker, fragmentSpread *ast.FragmentSpread) {
|
||||
if seen[fragmentSpread.Name] {
|
||||
return
|
||||
}
|
||||
def := g.fragments[fragmentSpread.Name]
|
||||
seen[fragmentSpread.Name] = true
|
||||
retval = append(retval, def)
|
||||
queue = append(queue, def)
|
||||
})
|
||||
|
||||
doc := ast.QueryDocument{Operations: ast.OperationList{op}}
|
||||
validator.Walk(g.schema, &doc, &observers)
|
||||
// Well, easy-ish: we also have to look recursively.
|
||||
// Note GraphQL guarantees there are no cycles among fragments:
|
||||
// https://spec.graphql.org/draft/#sec-Fragment-spreads-must-not-form-cycles
|
||||
for len(queue) > 0 {
|
||||
doc = ast.QueryDocument{Fragments: ast.FragmentDefinitionList{queue[0]}}
|
||||
validator.Walk(g.schema, &doc, &observers) // traversal is the same
|
||||
queue = queue[1:]
|
||||
}
|
||||
|
||||
return retval
|
||||
}
|
||||
|
||||
// Preprocess each query to make any changes that genqlient needs.
|
||||
//
|
||||
// At present, the only change is that we add __typename, if not already
|
||||
@@ -158,9 +204,11 @@ func (g *generator) preprocessQueryDocument(doc *ast.QueryDocument) {
|
||||
// __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.
|
||||
// need to consider that. And fragments must (if used at all) be spread
|
||||
// into some parent selection-set, so we'll add __typename there (if
|
||||
// needed). Note this does mean abstract-typed fragments spread into
|
||||
// object-typed scope will *not* have access to `__typename`, but they
|
||||
// indeed don't need it, since we do know the type in that context.
|
||||
observers.OnField(func(_ *validator.Walker, field *ast.Field) {
|
||||
// We are interested in a field from the query like
|
||||
// field { subField ... }
|
||||
@@ -214,6 +262,11 @@ func (g *generator) preprocessQueryDocument(doc *ast.QueryDocument) {
|
||||
validator.Walk(g.schema, doc, &observers)
|
||||
}
|
||||
|
||||
// addOperation adds to g.Operations the information needed to generate a
|
||||
// genqlient entrypoint function for the given operation. It also adds to
|
||||
// g.typeMap any types referenced by the operation, except for types belonging
|
||||
// to named fragments, which are added separately by Generate via
|
||||
// convertFragment.
|
||||
func (g *generator) addOperation(op *ast.OperationDefinition) error {
|
||||
if op.Name == "" {
|
||||
return errorf(op.Position, "operations must have operation-names")
|
||||
@@ -221,7 +274,7 @@ func (g *generator) addOperation(op *ast.OperationDefinition) error {
|
||||
|
||||
queryDoc := &ast.QueryDocument{
|
||||
Operations: ast.OperationList{op},
|
||||
// TODO: handle fragments
|
||||
Fragments: g.usedFragments(op),
|
||||
}
|
||||
g.preprocessQueryDocument(queryDoc)
|
||||
|
||||
@@ -304,19 +357,13 @@ func Generate(config *Config) (map[string][]byte, error) {
|
||||
strings.Join(config.Operations, ", "))
|
||||
}
|
||||
|
||||
if len(document.Fragments) > 0 && !config.AllowBrokenFeatures {
|
||||
return nil, errorf(document.Fragments[0].Position,
|
||||
"genqlient does not yet support fragments")
|
||||
}
|
||||
|
||||
// Step 2: For each operation, convert it into data structures representing
|
||||
// Go types (defined in types.go). The bulk of this logic is in
|
||||
// convert.go.
|
||||
g, err := newGenerator(config, schema)
|
||||
// Step 2: For each operation and fragment, convert it into data structures
|
||||
// representing Go types (defined in types.go). The bulk of this logic is
|
||||
// in convert.go.
|
||||
g, err := newGenerator(config, schema, document.Fragments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, op := range document.Operations {
|
||||
if err = g.addOperation(op); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -153,6 +153,15 @@ func (dir *GenqlientDirective) validate(node interface{}) error {
|
||||
// Anything else is valid on the entire operation; it will just apply
|
||||
// to whatever it is relevant to.
|
||||
return nil
|
||||
case *ast.FragmentDefinition:
|
||||
if dir.Bind != "" {
|
||||
// TODO(benkraft): Implement this if people find it useful.
|
||||
return errorf(dir.pos, "bind is not implemented for named fragments")
|
||||
}
|
||||
|
||||
// Like operations, anything else will just apply to the entire
|
||||
// fragment.
|
||||
return nil
|
||||
case *ast.VariableDefinition:
|
||||
if dir.Omitempty != nil && node.Type.NonNull {
|
||||
return errorf(dir.pos, "omitempty may only be used on optional arguments")
|
||||
@@ -164,7 +173,7 @@ func (dir *GenqlientDirective) validate(node interface{}) error {
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return errorf(dir.pos, "invalid directive location: %T", node)
|
||||
return errorf(dir.pos, "invalid @genqlient directive location: %T", node)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
fragment QueryFragment on Query {
|
||||
...InnerQueryFragment
|
||||
}
|
||||
|
||||
fragment InnerQueryFragment on Query {
|
||||
randomItem {
|
||||
id name
|
||||
...VideoFields
|
||||
}
|
||||
randomLeaf {
|
||||
...VideoFields
|
||||
...MoreVideoFields
|
||||
}
|
||||
otherLeaf: randomLeaf {
|
||||
... on Video {
|
||||
...MoreVideoFields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment VideoFields on Video {
|
||||
id name url duration thumbnail { id }
|
||||
}
|
||||
|
||||
# @genqlient(pointer: true)
|
||||
fragment MoreVideoFields on Video {
|
||||
id
|
||||
parent {
|
||||
name url
|
||||
# @genqlient(pointer: false)
|
||||
children {
|
||||
...VideoFields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query ComplexNamedFragments {
|
||||
... on Query { ...QueryFragment }
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fragment VideoFields on Video {
|
||||
id name url duration thumbnail { id }
|
||||
}
|
||||
|
||||
query SimpleNamedFragment {
|
||||
randomItem {
|
||||
id name
|
||||
...VideoFields
|
||||
}
|
||||
randomLeaf {
|
||||
...VideoFields
|
||||
}
|
||||
}
|
||||
+15
-12
@@ -184,13 +184,12 @@ type ComplexInlineFragmentsNestedStuffTopic struct {
|
||||
|
||||
func (v *ComplexInlineFragmentsNestedStuffTopic) UnmarshalJSON(b []byte) error {
|
||||
|
||||
type ComplexInlineFragmentsNestedStuffTopicWrapper ComplexInlineFragmentsNestedStuffTopic
|
||||
|
||||
var firstPass struct {
|
||||
*ComplexInlineFragmentsNestedStuffTopicWrapper
|
||||
*ComplexInlineFragmentsNestedStuffTopic
|
||||
Children []json.RawMessage `json:"children"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.ComplexInlineFragmentsNestedStuffTopicWrapper = (*ComplexInlineFragmentsNestedStuffTopicWrapper)(v)
|
||||
firstPass.ComplexInlineFragmentsNestedStuffTopic = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
@@ -213,6 +212,7 @@ func (v *ComplexInlineFragmentsNestedStuffTopic) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -232,13 +232,12 @@ type ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTop
|
||||
|
||||
func (v *ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopic) UnmarshalJSON(b []byte) error {
|
||||
|
||||
type ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopicWrapper ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopic
|
||||
|
||||
var firstPass struct {
|
||||
*ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopicWrapper
|
||||
*ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopic
|
||||
Children []json.RawMessage `json:"children"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopicWrapper = (*ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopicWrapper)(v)
|
||||
firstPass.ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopic = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
@@ -261,6 +260,7 @@ func (v *ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -797,16 +797,15 @@ type ComplexInlineFragmentsResponse struct {
|
||||
|
||||
func (v *ComplexInlineFragmentsResponse) UnmarshalJSON(b []byte) error {
|
||||
|
||||
type ComplexInlineFragmentsResponseWrapper ComplexInlineFragmentsResponse
|
||||
|
||||
var firstPass struct {
|
||||
*ComplexInlineFragmentsResponseWrapper
|
||||
*ComplexInlineFragmentsResponse
|
||||
RandomItem json.RawMessage `json:"randomItem"`
|
||||
RepeatedStuff json.RawMessage `json:"repeatedStuff"`
|
||||
ConflictingStuff json.RawMessage `json:"conflictingStuff"`
|
||||
NestedStuff json.RawMessage `json:"nestedStuff"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.ComplexInlineFragmentsResponseWrapper = (*ComplexInlineFragmentsResponseWrapper)(v)
|
||||
firstPass.ComplexInlineFragmentsResponse = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
@@ -823,6 +822,7 @@ func (v *ComplexInlineFragmentsResponse) UnmarshalJSON(b []byte) error {
|
||||
"Unable to unmarshal ComplexInlineFragmentsResponse.RandomItem: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
target := &v.RepeatedStuff
|
||||
raw := firstPass.RepeatedStuff
|
||||
@@ -833,6 +833,7 @@ func (v *ComplexInlineFragmentsResponse) UnmarshalJSON(b []byte) error {
|
||||
"Unable to unmarshal ComplexInlineFragmentsResponse.RepeatedStuff: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
target := &v.ConflictingStuff
|
||||
raw := firstPass.ConflictingStuff
|
||||
@@ -843,6 +844,7 @@ func (v *ComplexInlineFragmentsResponse) UnmarshalJSON(b []byte) error {
|
||||
"Unable to unmarshal ComplexInlineFragmentsResponse.ConflictingStuff: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
target := &v.NestedStuff
|
||||
raw := firstPass.NestedStuff
|
||||
@@ -853,6 +855,7 @@ func (v *ComplexInlineFragmentsResponse) UnmarshalJSON(b []byte) error {
|
||||
"Unable to unmarshal ComplexInlineFragmentsResponse.NestedStuff: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+666
@@ -0,0 +1,666 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// ComplexNamedFragmentsResponse is returned by ComplexNamedFragments on success.
|
||||
type ComplexNamedFragmentsResponse struct {
|
||||
QueryFragment `json:"-"`
|
||||
}
|
||||
|
||||
func (v *ComplexNamedFragmentsResponse) UnmarshalJSON(b []byte) error {
|
||||
|
||||
var firstPass struct {
|
||||
*ComplexNamedFragmentsResponse
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.ComplexNamedFragmentsResponse = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, &v.QueryFragment)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InnerQueryFragment includes the GraphQL fields of Query requested by the fragment InnerQueryFragment.
|
||||
type InnerQueryFragment struct {
|
||||
RandomItem InnerQueryFragmentRandomItemContent `json:"-"`
|
||||
RandomLeaf InnerQueryFragmentRandomLeafLeafContent `json:"-"`
|
||||
OtherLeaf InnerQueryFragmentOtherLeafLeafContent `json:"-"`
|
||||
}
|
||||
|
||||
func (v *InnerQueryFragment) UnmarshalJSON(b []byte) error {
|
||||
|
||||
var firstPass struct {
|
||||
*InnerQueryFragment
|
||||
RandomItem json.RawMessage `json:"randomItem"`
|
||||
RandomLeaf json.RawMessage `json:"randomLeaf"`
|
||||
OtherLeaf json.RawMessage `json:"otherLeaf"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.InnerQueryFragment = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
{
|
||||
target := &v.RandomItem
|
||||
raw := firstPass.RandomItem
|
||||
err = __unmarshalInnerQueryFragmentRandomItemContent(
|
||||
target, raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"Unable to unmarshal InnerQueryFragment.RandomItem: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
target := &v.RandomLeaf
|
||||
raw := firstPass.RandomLeaf
|
||||
err = __unmarshalInnerQueryFragmentRandomLeafLeafContent(
|
||||
target, raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"Unable to unmarshal InnerQueryFragment.RandomLeaf: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
target := &v.OtherLeaf
|
||||
raw := firstPass.OtherLeaf
|
||||
err = __unmarshalInnerQueryFragmentOtherLeafLeafContent(
|
||||
target, raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"Unable to unmarshal InnerQueryFragment.OtherLeaf: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InnerQueryFragmentOtherLeafArticle includes the requested fields of the GraphQL type Article.
|
||||
type InnerQueryFragmentOtherLeafArticle struct {
|
||||
Typename string `json:"__typename"`
|
||||
}
|
||||
|
||||
// InnerQueryFragmentOtherLeafLeafContent includes the requested fields of the GraphQL interface LeafContent.
|
||||
//
|
||||
// InnerQueryFragmentOtherLeafLeafContent is implemented by the following types:
|
||||
// InnerQueryFragmentOtherLeafArticle
|
||||
// InnerQueryFragmentOtherLeafVideo
|
||||
//
|
||||
// The GraphQL type's documentation follows.
|
||||
//
|
||||
// LeafContent represents content items that can't have child-nodes.
|
||||
type InnerQueryFragmentOtherLeafLeafContent interface {
|
||||
implementsGraphQLInterfaceInnerQueryFragmentOtherLeafLeafContent()
|
||||
// GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values).
|
||||
GetTypename() string
|
||||
}
|
||||
|
||||
func (v *InnerQueryFragmentOtherLeafArticle) implementsGraphQLInterfaceInnerQueryFragmentOtherLeafLeafContent() {
|
||||
}
|
||||
|
||||
// GetTypename is a part of, and documented with, the interface InnerQueryFragmentOtherLeafLeafContent.
|
||||
func (v *InnerQueryFragmentOtherLeafArticle) GetTypename() string { return v.Typename }
|
||||
|
||||
func (v *InnerQueryFragmentOtherLeafVideo) implementsGraphQLInterfaceInnerQueryFragmentOtherLeafLeafContent() {
|
||||
}
|
||||
|
||||
// GetTypename is a part of, and documented with, the interface InnerQueryFragmentOtherLeafLeafContent.
|
||||
func (v *InnerQueryFragmentOtherLeafVideo) GetTypename() string { return v.Typename }
|
||||
|
||||
func __unmarshalInnerQueryFragmentOtherLeafLeafContent(v *InnerQueryFragmentOtherLeafLeafContent, 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(InnerQueryFragmentOtherLeafArticle)
|
||||
return json.Unmarshal(m, *v)
|
||||
case "Video":
|
||||
*v = new(InnerQueryFragmentOtherLeafVideo)
|
||||
return json.Unmarshal(m, *v)
|
||||
case "":
|
||||
return fmt.Errorf(
|
||||
"Response was missing LeafContent.__typename")
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
`Unexpected concrete type for InnerQueryFragmentOtherLeafLeafContent: "%v"`, tn.TypeName)
|
||||
}
|
||||
}
|
||||
|
||||
// InnerQueryFragmentOtherLeafVideo includes the requested fields of the GraphQL type Video.
|
||||
type InnerQueryFragmentOtherLeafVideo struct {
|
||||
Typename string `json:"__typename"`
|
||||
MoreVideoFields `json:"-"`
|
||||
}
|
||||
|
||||
func (v *InnerQueryFragmentOtherLeafVideo) UnmarshalJSON(b []byte) error {
|
||||
|
||||
var firstPass struct {
|
||||
*InnerQueryFragmentOtherLeafVideo
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.InnerQueryFragmentOtherLeafVideo = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, &v.MoreVideoFields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InnerQueryFragmentRandomItemArticle includes the requested fields of the GraphQL type Article.
|
||||
type InnerQueryFragmentRandomItemArticle struct {
|
||||
Typename string `json:"__typename"`
|
||||
// ID is the identifier of the content.
|
||||
Id testutil.ID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// InnerQueryFragmentRandomItemContent includes the requested fields of the GraphQL interface Content.
|
||||
//
|
||||
// InnerQueryFragmentRandomItemContent is implemented by the following types:
|
||||
// InnerQueryFragmentRandomItemArticle
|
||||
// InnerQueryFragmentRandomItemVideo
|
||||
// InnerQueryFragmentRandomItemTopic
|
||||
//
|
||||
// The GraphQL type's documentation follows.
|
||||
//
|
||||
// Content is implemented by various types like Article, Video, and Topic.
|
||||
type InnerQueryFragmentRandomItemContent interface {
|
||||
implementsGraphQLInterfaceInnerQueryFragmentRandomItemContent()
|
||||
// GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values).
|
||||
GetTypename() string
|
||||
// GetId returns the interface-field "id" from its implementation.
|
||||
// The GraphQL interface field's documentation follows.
|
||||
//
|
||||
// ID is the identifier of the content.
|
||||
GetId() testutil.ID
|
||||
// GetName returns the interface-field "name" from its implementation.
|
||||
GetName() string
|
||||
}
|
||||
|
||||
func (v *InnerQueryFragmentRandomItemArticle) implementsGraphQLInterfaceInnerQueryFragmentRandomItemContent() {
|
||||
}
|
||||
|
||||
// GetTypename is a part of, and documented with, the interface InnerQueryFragmentRandomItemContent.
|
||||
func (v *InnerQueryFragmentRandomItemArticle) GetTypename() string { return v.Typename }
|
||||
|
||||
// GetId is a part of, and documented with, the interface InnerQueryFragmentRandomItemContent.
|
||||
func (v *InnerQueryFragmentRandomItemArticle) GetId() testutil.ID { return v.Id }
|
||||
|
||||
// GetName is a part of, and documented with, the interface InnerQueryFragmentRandomItemContent.
|
||||
func (v *InnerQueryFragmentRandomItemArticle) GetName() string { return v.Name }
|
||||
|
||||
func (v *InnerQueryFragmentRandomItemVideo) implementsGraphQLInterfaceInnerQueryFragmentRandomItemContent() {
|
||||
}
|
||||
|
||||
// GetTypename is a part of, and documented with, the interface InnerQueryFragmentRandomItemContent.
|
||||
func (v *InnerQueryFragmentRandomItemVideo) GetTypename() string { return v.Typename }
|
||||
|
||||
// GetId is a part of, and documented with, the interface InnerQueryFragmentRandomItemContent.
|
||||
func (v *InnerQueryFragmentRandomItemVideo) GetId() testutil.ID { return v.Id }
|
||||
|
||||
// GetName is a part of, and documented with, the interface InnerQueryFragmentRandomItemContent.
|
||||
func (v *InnerQueryFragmentRandomItemVideo) GetName() string { return v.Name }
|
||||
|
||||
func (v *InnerQueryFragmentRandomItemTopic) implementsGraphQLInterfaceInnerQueryFragmentRandomItemContent() {
|
||||
}
|
||||
|
||||
// GetTypename is a part of, and documented with, the interface InnerQueryFragmentRandomItemContent.
|
||||
func (v *InnerQueryFragmentRandomItemTopic) GetTypename() string { return v.Typename }
|
||||
|
||||
// GetId is a part of, and documented with, the interface InnerQueryFragmentRandomItemContent.
|
||||
func (v *InnerQueryFragmentRandomItemTopic) GetId() testutil.ID { return v.Id }
|
||||
|
||||
// GetName is a part of, and documented with, the interface InnerQueryFragmentRandomItemContent.
|
||||
func (v *InnerQueryFragmentRandomItemTopic) GetName() string { return v.Name }
|
||||
|
||||
func __unmarshalInnerQueryFragmentRandomItemContent(v *InnerQueryFragmentRandomItemContent, 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(InnerQueryFragmentRandomItemArticle)
|
||||
return json.Unmarshal(m, *v)
|
||||
case "Video":
|
||||
*v = new(InnerQueryFragmentRandomItemVideo)
|
||||
return json.Unmarshal(m, *v)
|
||||
case "Topic":
|
||||
*v = new(InnerQueryFragmentRandomItemTopic)
|
||||
return json.Unmarshal(m, *v)
|
||||
case "":
|
||||
return fmt.Errorf(
|
||||
"Response was missing Content.__typename")
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
`Unexpected concrete type for InnerQueryFragmentRandomItemContent: "%v"`, tn.TypeName)
|
||||
}
|
||||
}
|
||||
|
||||
// InnerQueryFragmentRandomItemTopic includes the requested fields of the GraphQL type Topic.
|
||||
type InnerQueryFragmentRandomItemTopic struct {
|
||||
Typename string `json:"__typename"`
|
||||
// ID is the identifier of the content.
|
||||
Id testutil.ID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// InnerQueryFragmentRandomItemVideo includes the requested fields of the GraphQL type Video.
|
||||
type InnerQueryFragmentRandomItemVideo struct {
|
||||
Typename string `json:"__typename"`
|
||||
// ID is the identifier of the content.
|
||||
Id testutil.ID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
VideoFields `json:"-"`
|
||||
}
|
||||
|
||||
func (v *InnerQueryFragmentRandomItemVideo) UnmarshalJSON(b []byte) error {
|
||||
|
||||
var firstPass struct {
|
||||
*InnerQueryFragmentRandomItemVideo
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.InnerQueryFragmentRandomItemVideo = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, &v.VideoFields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InnerQueryFragmentRandomLeafArticle includes the requested fields of the GraphQL type Article.
|
||||
type InnerQueryFragmentRandomLeafArticle struct {
|
||||
Typename string `json:"__typename"`
|
||||
}
|
||||
|
||||
// InnerQueryFragmentRandomLeafLeafContent includes the requested fields of the GraphQL interface LeafContent.
|
||||
//
|
||||
// InnerQueryFragmentRandomLeafLeafContent is implemented by the following types:
|
||||
// InnerQueryFragmentRandomLeafArticle
|
||||
// InnerQueryFragmentRandomLeafVideo
|
||||
//
|
||||
// The GraphQL type's documentation follows.
|
||||
//
|
||||
// LeafContent represents content items that can't have child-nodes.
|
||||
type InnerQueryFragmentRandomLeafLeafContent interface {
|
||||
implementsGraphQLInterfaceInnerQueryFragmentRandomLeafLeafContent()
|
||||
// GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values).
|
||||
GetTypename() string
|
||||
}
|
||||
|
||||
func (v *InnerQueryFragmentRandomLeafArticle) implementsGraphQLInterfaceInnerQueryFragmentRandomLeafLeafContent() {
|
||||
}
|
||||
|
||||
// GetTypename is a part of, and documented with, the interface InnerQueryFragmentRandomLeafLeafContent.
|
||||
func (v *InnerQueryFragmentRandomLeafArticle) GetTypename() string { return v.Typename }
|
||||
|
||||
func (v *InnerQueryFragmentRandomLeafVideo) implementsGraphQLInterfaceInnerQueryFragmentRandomLeafLeafContent() {
|
||||
}
|
||||
|
||||
// GetTypename is a part of, and documented with, the interface InnerQueryFragmentRandomLeafLeafContent.
|
||||
func (v *InnerQueryFragmentRandomLeafVideo) GetTypename() string { return v.Typename }
|
||||
|
||||
func __unmarshalInnerQueryFragmentRandomLeafLeafContent(v *InnerQueryFragmentRandomLeafLeafContent, 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(InnerQueryFragmentRandomLeafArticle)
|
||||
return json.Unmarshal(m, *v)
|
||||
case "Video":
|
||||
*v = new(InnerQueryFragmentRandomLeafVideo)
|
||||
return json.Unmarshal(m, *v)
|
||||
case "":
|
||||
return fmt.Errorf(
|
||||
"Response was missing LeafContent.__typename")
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
`Unexpected concrete type for InnerQueryFragmentRandomLeafLeafContent: "%v"`, tn.TypeName)
|
||||
}
|
||||
}
|
||||
|
||||
// InnerQueryFragmentRandomLeafVideo includes the requested fields of the GraphQL type Video.
|
||||
type InnerQueryFragmentRandomLeafVideo struct {
|
||||
Typename string `json:"__typename"`
|
||||
VideoFields `json:"-"`
|
||||
MoreVideoFields `json:"-"`
|
||||
}
|
||||
|
||||
func (v *InnerQueryFragmentRandomLeafVideo) UnmarshalJSON(b []byte) error {
|
||||
|
||||
var firstPass struct {
|
||||
*InnerQueryFragmentRandomLeafVideo
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.InnerQueryFragmentRandomLeafVideo = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, &v.VideoFields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, &v.MoreVideoFields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MoreVideoFields includes the GraphQL fields of Video requested by the fragment MoreVideoFields.
|
||||
type MoreVideoFields struct {
|
||||
// ID is documented in the Content interface.
|
||||
Id *testutil.ID `json:"id"`
|
||||
Parent *MoreVideoFieldsParentTopic `json:"parent"`
|
||||
}
|
||||
|
||||
// MoreVideoFieldsParentTopic includes the requested fields of the GraphQL type Topic.
|
||||
type MoreVideoFieldsParentTopic struct {
|
||||
Name *string `json:"name"`
|
||||
Url *string `json:"url"`
|
||||
Children []MoreVideoFieldsParentTopicChildrenContent `json:"-"`
|
||||
}
|
||||
|
||||
func (v *MoreVideoFieldsParentTopic) UnmarshalJSON(b []byte) error {
|
||||
|
||||
var firstPass struct {
|
||||
*MoreVideoFieldsParentTopic
|
||||
Children []json.RawMessage `json:"children"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.MoreVideoFieldsParentTopic = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
{
|
||||
target := &v.Children
|
||||
raw := firstPass.Children
|
||||
*target = make(
|
||||
[]MoreVideoFieldsParentTopicChildrenContent,
|
||||
len(raw))
|
||||
for i, raw := range raw {
|
||||
target := &(*target)[i]
|
||||
err = __unmarshalMoreVideoFieldsParentTopicChildrenContent(
|
||||
target, raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"Unable to unmarshal MoreVideoFieldsParentTopic.Children: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MoreVideoFieldsParentTopicChildrenArticle includes the requested fields of the GraphQL type Article.
|
||||
type MoreVideoFieldsParentTopicChildrenArticle struct {
|
||||
Typename *string `json:"__typename"`
|
||||
}
|
||||
|
||||
// MoreVideoFieldsParentTopicChildrenContent includes the requested fields of the GraphQL interface Content.
|
||||
//
|
||||
// MoreVideoFieldsParentTopicChildrenContent is implemented by the following types:
|
||||
// MoreVideoFieldsParentTopicChildrenArticle
|
||||
// MoreVideoFieldsParentTopicChildrenVideo
|
||||
// MoreVideoFieldsParentTopicChildrenTopic
|
||||
//
|
||||
// The GraphQL type's documentation follows.
|
||||
//
|
||||
// Content is implemented by various types like Article, Video, and Topic.
|
||||
type MoreVideoFieldsParentTopicChildrenContent interface {
|
||||
implementsGraphQLInterfaceMoreVideoFieldsParentTopicChildrenContent()
|
||||
// GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values).
|
||||
GetTypename() *string
|
||||
}
|
||||
|
||||
func (v *MoreVideoFieldsParentTopicChildrenArticle) implementsGraphQLInterfaceMoreVideoFieldsParentTopicChildrenContent() {
|
||||
}
|
||||
|
||||
// GetTypename is a part of, and documented with, the interface MoreVideoFieldsParentTopicChildrenContent.
|
||||
func (v *MoreVideoFieldsParentTopicChildrenArticle) GetTypename() *string { return v.Typename }
|
||||
|
||||
func (v *MoreVideoFieldsParentTopicChildrenVideo) implementsGraphQLInterfaceMoreVideoFieldsParentTopicChildrenContent() {
|
||||
}
|
||||
|
||||
// GetTypename is a part of, and documented with, the interface MoreVideoFieldsParentTopicChildrenContent.
|
||||
func (v *MoreVideoFieldsParentTopicChildrenVideo) GetTypename() *string { return v.Typename }
|
||||
|
||||
func (v *MoreVideoFieldsParentTopicChildrenTopic) implementsGraphQLInterfaceMoreVideoFieldsParentTopicChildrenContent() {
|
||||
}
|
||||
|
||||
// GetTypename is a part of, and documented with, the interface MoreVideoFieldsParentTopicChildrenContent.
|
||||
func (v *MoreVideoFieldsParentTopicChildrenTopic) GetTypename() *string { return v.Typename }
|
||||
|
||||
func __unmarshalMoreVideoFieldsParentTopicChildrenContent(v *MoreVideoFieldsParentTopicChildrenContent, 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(MoreVideoFieldsParentTopicChildrenArticle)
|
||||
return json.Unmarshal(m, *v)
|
||||
case "Video":
|
||||
*v = new(MoreVideoFieldsParentTopicChildrenVideo)
|
||||
return json.Unmarshal(m, *v)
|
||||
case "Topic":
|
||||
*v = new(MoreVideoFieldsParentTopicChildrenTopic)
|
||||
return json.Unmarshal(m, *v)
|
||||
case "":
|
||||
return fmt.Errorf(
|
||||
"Response was missing Content.__typename")
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
`Unexpected concrete type for MoreVideoFieldsParentTopicChildrenContent: "%v"`, tn.TypeName)
|
||||
}
|
||||
}
|
||||
|
||||
// MoreVideoFieldsParentTopicChildrenTopic includes the requested fields of the GraphQL type Topic.
|
||||
type MoreVideoFieldsParentTopicChildrenTopic struct {
|
||||
Typename *string `json:"__typename"`
|
||||
}
|
||||
|
||||
// MoreVideoFieldsParentTopicChildrenVideo includes the requested fields of the GraphQL type Video.
|
||||
type MoreVideoFieldsParentTopicChildrenVideo struct {
|
||||
Typename *string `json:"__typename"`
|
||||
VideoFields `json:"-"`
|
||||
}
|
||||
|
||||
func (v *MoreVideoFieldsParentTopicChildrenVideo) UnmarshalJSON(b []byte) error {
|
||||
|
||||
var firstPass struct {
|
||||
*MoreVideoFieldsParentTopicChildrenVideo
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.MoreVideoFieldsParentTopicChildrenVideo = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, &v.VideoFields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// QueryFragment includes the GraphQL fields of Query requested by the fragment QueryFragment.
|
||||
type QueryFragment struct {
|
||||
InnerQueryFragment `json:"-"`
|
||||
}
|
||||
|
||||
func (v *QueryFragment) UnmarshalJSON(b []byte) error {
|
||||
|
||||
var firstPass struct {
|
||||
*QueryFragment
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.QueryFragment = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, &v.InnerQueryFragment)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VideoFields includes the GraphQL fields of Video requested by the fragment VideoFields.
|
||||
type VideoFields struct {
|
||||
// ID is documented in the Content interface.
|
||||
Id testutil.ID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Url string `json:"url"`
|
||||
Duration int `json:"duration"`
|
||||
Thumbnail VideoFieldsThumbnail `json:"thumbnail"`
|
||||
}
|
||||
|
||||
// VideoFieldsThumbnail includes the requested fields of the GraphQL type Thumbnail.
|
||||
type VideoFieldsThumbnail struct {
|
||||
Id testutil.ID `json:"id"`
|
||||
}
|
||||
|
||||
func ComplexNamedFragments(
|
||||
client graphql.Client,
|
||||
) (*ComplexNamedFragmentsResponse, error) {
|
||||
var err error
|
||||
|
||||
var retval ComplexNamedFragmentsResponse
|
||||
err = client.MakeRequest(
|
||||
nil,
|
||||
"ComplexNamedFragments",
|
||||
`
|
||||
query ComplexNamedFragments {
|
||||
... on Query {
|
||||
... QueryFragment
|
||||
}
|
||||
}
|
||||
fragment QueryFragment on Query {
|
||||
... InnerQueryFragment
|
||||
}
|
||||
fragment InnerQueryFragment on Query {
|
||||
randomItem {
|
||||
__typename
|
||||
id
|
||||
name
|
||||
... VideoFields
|
||||
}
|
||||
randomLeaf {
|
||||
__typename
|
||||
... VideoFields
|
||||
... MoreVideoFields
|
||||
}
|
||||
otherLeaf: randomLeaf {
|
||||
__typename
|
||||
... on Video {
|
||||
... MoreVideoFields
|
||||
}
|
||||
}
|
||||
}
|
||||
fragment VideoFields on Video {
|
||||
id
|
||||
name
|
||||
url
|
||||
duration
|
||||
thumbnail {
|
||||
id
|
||||
}
|
||||
}
|
||||
fragment MoreVideoFields on Video {
|
||||
id
|
||||
parent {
|
||||
name
|
||||
url
|
||||
children {
|
||||
__typename
|
||||
... VideoFields
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
&retval,
|
||||
nil,
|
||||
)
|
||||
return &retval, err
|
||||
}
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"operations": [
|
||||
{
|
||||
"operationName": "ComplexNamedFragments",
|
||||
"query": "\nquery ComplexNamedFragments {\n\t... on Query {\n\t\t... QueryFragment\n\t}\n}\nfragment QueryFragment on Query {\n\t... InnerQueryFragment\n}\nfragment InnerQueryFragment on Query {\n\trandomItem {\n\t\t__typename\n\t\tid\n\t\tname\n\t\t... VideoFields\n\t}\n\trandomLeaf {\n\t\t__typename\n\t\t... VideoFields\n\t\t... MoreVideoFields\n\t}\n\totherLeaf: randomLeaf {\n\t\t__typename\n\t\t... on Video {\n\t\t\t... MoreVideoFields\n\t\t}\n\t}\n}\nfragment VideoFields on Video {\n\tid\n\tname\n\turl\n\tduration\n\tthumbnail {\n\t\tid\n\t}\n}\nfragment MoreVideoFields on Video {\n\tid\n\tparent {\n\t\tname\n\t\turl\n\t\tchildren {\n\t\t\t__typename\n\t\t\t... VideoFields\n\t\t}\n\t}\n}\n",
|
||||
"sourceLocation": "testdata/queries/ComplexNamedFragments.graphql"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+8
-8
@@ -26,13 +26,12 @@ type InterfaceListFieldRootTopic struct {
|
||||
|
||||
func (v *InterfaceListFieldRootTopic) UnmarshalJSON(b []byte) error {
|
||||
|
||||
type InterfaceListFieldRootTopicWrapper InterfaceListFieldRootTopic
|
||||
|
||||
var firstPass struct {
|
||||
*InterfaceListFieldRootTopicWrapper
|
||||
*InterfaceListFieldRootTopic
|
||||
Children []json.RawMessage `json:"children"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.InterfaceListFieldRootTopicWrapper = (*InterfaceListFieldRootTopicWrapper)(v)
|
||||
firstPass.InterfaceListFieldRootTopic = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
@@ -55,6 +54,7 @@ func (v *InterfaceListFieldRootTopic) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -183,13 +183,12 @@ type InterfaceListFieldWithPointerTopic struct {
|
||||
|
||||
func (v *InterfaceListFieldWithPointerTopic) UnmarshalJSON(b []byte) error {
|
||||
|
||||
type InterfaceListFieldWithPointerTopicWrapper InterfaceListFieldWithPointerTopic
|
||||
|
||||
var firstPass struct {
|
||||
*InterfaceListFieldWithPointerTopicWrapper
|
||||
*InterfaceListFieldWithPointerTopic
|
||||
Children []json.RawMessage `json:"children"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.InterfaceListFieldWithPointerTopicWrapper = (*InterfaceListFieldWithPointerTopicWrapper)(v)
|
||||
firstPass.InterfaceListFieldWithPointerTopic = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
@@ -212,6 +211,7 @@ func (v *InterfaceListFieldWithPointerTopic) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+5
-4
@@ -151,14 +151,13 @@ type InterfaceListOfListOfListsFieldResponse struct {
|
||||
|
||||
func (v *InterfaceListOfListOfListsFieldResponse) UnmarshalJSON(b []byte) error {
|
||||
|
||||
type InterfaceListOfListOfListsFieldResponseWrapper InterfaceListOfListOfListsFieldResponse
|
||||
|
||||
var firstPass struct {
|
||||
*InterfaceListOfListOfListsFieldResponseWrapper
|
||||
*InterfaceListOfListOfListsFieldResponse
|
||||
ListOfListsOfListsOfContent [][][]json.RawMessage `json:"listOfListsOfListsOfContent"`
|
||||
WithPointer [][][]json.RawMessage `json:"withPointer"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.InterfaceListOfListOfListsFieldResponseWrapper = (*InterfaceListOfListOfListsFieldResponseWrapper)(v)
|
||||
firstPass.InterfaceListOfListOfListsFieldResponse = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
@@ -193,6 +192,7 @@ func (v *InterfaceListOfListOfListsFieldResponse) UnmarshalJSON(b []byte) error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
target := &v.WithPointer
|
||||
raw := firstPass.WithPointer
|
||||
@@ -222,6 +222,7 @@ func (v *InterfaceListOfListOfListsFieldResponse) UnmarshalJSON(b []byte) error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Vendored
+8
-8
@@ -24,13 +24,12 @@ type InterfaceNestingRootTopic struct {
|
||||
|
||||
func (v *InterfaceNestingRootTopic) UnmarshalJSON(b []byte) error {
|
||||
|
||||
type InterfaceNestingRootTopicWrapper InterfaceNestingRootTopic
|
||||
|
||||
var firstPass struct {
|
||||
*InterfaceNestingRootTopicWrapper
|
||||
*InterfaceNestingRootTopic
|
||||
Children []json.RawMessage `json:"children"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.InterfaceNestingRootTopicWrapper = (*InterfaceNestingRootTopicWrapper)(v)
|
||||
firstPass.InterfaceNestingRootTopic = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
@@ -53,6 +52,7 @@ func (v *InterfaceNestingRootTopic) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -170,13 +170,12 @@ type InterfaceNestingRootTopicChildrenContentParentTopic struct {
|
||||
|
||||
func (v *InterfaceNestingRootTopicChildrenContentParentTopic) UnmarshalJSON(b []byte) error {
|
||||
|
||||
type InterfaceNestingRootTopicChildrenContentParentTopicWrapper InterfaceNestingRootTopicChildrenContentParentTopic
|
||||
|
||||
var firstPass struct {
|
||||
*InterfaceNestingRootTopicChildrenContentParentTopicWrapper
|
||||
*InterfaceNestingRootTopicChildrenContentParentTopic
|
||||
Children []json.RawMessage `json:"children"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.InterfaceNestingRootTopicChildrenContentParentTopicWrapper = (*InterfaceNestingRootTopicChildrenContentParentTopicWrapper)(v)
|
||||
firstPass.InterfaceNestingRootTopicChildrenContentParentTopic = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
@@ -199,6 +198,7 @@ func (v *InterfaceNestingRootTopicChildrenContentParentTopic) UnmarshalJSON(b []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+6
-4
@@ -256,15 +256,14 @@ type InterfaceNoFragmentsQueryResponse struct {
|
||||
|
||||
func (v *InterfaceNoFragmentsQueryResponse) UnmarshalJSON(b []byte) error {
|
||||
|
||||
type InterfaceNoFragmentsQueryResponseWrapper InterfaceNoFragmentsQueryResponse
|
||||
|
||||
var firstPass struct {
|
||||
*InterfaceNoFragmentsQueryResponseWrapper
|
||||
*InterfaceNoFragmentsQueryResponse
|
||||
RandomItem json.RawMessage `json:"randomItem"`
|
||||
RandomItemWithTypeName json.RawMessage `json:"randomItemWithTypeName"`
|
||||
WithPointer json.RawMessage `json:"withPointer"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.InterfaceNoFragmentsQueryResponseWrapper = (*InterfaceNoFragmentsQueryResponseWrapper)(v)
|
||||
firstPass.InterfaceNoFragmentsQueryResponse = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
@@ -281,6 +280,7 @@ func (v *InterfaceNoFragmentsQueryResponse) UnmarshalJSON(b []byte) error {
|
||||
"Unable to unmarshal InterfaceNoFragmentsQueryResponse.RandomItem: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
target := &v.RandomItemWithTypeName
|
||||
raw := firstPass.RandomItemWithTypeName
|
||||
@@ -291,6 +291,7 @@ func (v *InterfaceNoFragmentsQueryResponse) UnmarshalJSON(b []byte) error {
|
||||
"Unable to unmarshal InterfaceNoFragmentsQueryResponse.RandomItemWithTypeName: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
target := &v.WithPointer
|
||||
raw := firstPass.WithPointer
|
||||
@@ -302,6 +303,7 @@ func (v *InterfaceNoFragmentsQueryResponse) UnmarshalJSON(b []byte) error {
|
||||
"Unable to unmarshal InterfaceNoFragmentsQueryResponse.WithPointer: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -134,13 +134,12 @@ type SimpleInlineFragmentResponse struct {
|
||||
|
||||
func (v *SimpleInlineFragmentResponse) UnmarshalJSON(b []byte) error {
|
||||
|
||||
type SimpleInlineFragmentResponseWrapper SimpleInlineFragmentResponse
|
||||
|
||||
var firstPass struct {
|
||||
*SimpleInlineFragmentResponseWrapper
|
||||
*SimpleInlineFragmentResponse
|
||||
RandomItem json.RawMessage `json:"randomItem"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.SimpleInlineFragmentResponseWrapper = (*SimpleInlineFragmentResponseWrapper)(v)
|
||||
firstPass.SimpleInlineFragmentResponse = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
@@ -157,6 +156,7 @@ func (v *SimpleInlineFragmentResponse) UnmarshalJSON(b []byte) error {
|
||||
"Unable to unmarshal SimpleInlineFragmentResponse.RandomItem: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Vendored
+334
@@ -0,0 +1,334 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// SimpleNamedFragmentRandomItemArticle includes the requested fields of the GraphQL type Article.
|
||||
type SimpleNamedFragmentRandomItemArticle struct {
|
||||
Typename string `json:"__typename"`
|
||||
// ID is the identifier of the content.
|
||||
Id testutil.ID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// SimpleNamedFragmentRandomItemContent includes the requested fields of the GraphQL interface Content.
|
||||
//
|
||||
// SimpleNamedFragmentRandomItemContent is implemented by the following types:
|
||||
// SimpleNamedFragmentRandomItemArticle
|
||||
// SimpleNamedFragmentRandomItemVideo
|
||||
// SimpleNamedFragmentRandomItemTopic
|
||||
//
|
||||
// The GraphQL type's documentation follows.
|
||||
//
|
||||
// Content is implemented by various types like Article, Video, and Topic.
|
||||
type SimpleNamedFragmentRandomItemContent interface {
|
||||
implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent()
|
||||
// GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values).
|
||||
GetTypename() string
|
||||
// GetId returns the interface-field "id" from its implementation.
|
||||
// The GraphQL interface field's documentation follows.
|
||||
//
|
||||
// ID is the identifier of the content.
|
||||
GetId() testutil.ID
|
||||
// GetName returns the interface-field "name" from its implementation.
|
||||
GetName() string
|
||||
}
|
||||
|
||||
func (v *SimpleNamedFragmentRandomItemArticle) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() {
|
||||
}
|
||||
|
||||
// GetTypename is a part of, and documented with, the interface SimpleNamedFragmentRandomItemContent.
|
||||
func (v *SimpleNamedFragmentRandomItemArticle) GetTypename() string { return v.Typename }
|
||||
|
||||
// GetId is a part of, and documented with, the interface SimpleNamedFragmentRandomItemContent.
|
||||
func (v *SimpleNamedFragmentRandomItemArticle) GetId() testutil.ID { return v.Id }
|
||||
|
||||
// GetName is a part of, and documented with, the interface SimpleNamedFragmentRandomItemContent.
|
||||
func (v *SimpleNamedFragmentRandomItemArticle) GetName() string { return v.Name }
|
||||
|
||||
func (v *SimpleNamedFragmentRandomItemVideo) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() {
|
||||
}
|
||||
|
||||
// GetTypename is a part of, and documented with, the interface SimpleNamedFragmentRandomItemContent.
|
||||
func (v *SimpleNamedFragmentRandomItemVideo) GetTypename() string { return v.Typename }
|
||||
|
||||
// GetId is a part of, and documented with, the interface SimpleNamedFragmentRandomItemContent.
|
||||
func (v *SimpleNamedFragmentRandomItemVideo) GetId() testutil.ID { return v.Id }
|
||||
|
||||
// GetName is a part of, and documented with, the interface SimpleNamedFragmentRandomItemContent.
|
||||
func (v *SimpleNamedFragmentRandomItemVideo) GetName() string { return v.Name }
|
||||
|
||||
func (v *SimpleNamedFragmentRandomItemTopic) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() {
|
||||
}
|
||||
|
||||
// GetTypename is a part of, and documented with, the interface SimpleNamedFragmentRandomItemContent.
|
||||
func (v *SimpleNamedFragmentRandomItemTopic) GetTypename() string { return v.Typename }
|
||||
|
||||
// GetId is a part of, and documented with, the interface SimpleNamedFragmentRandomItemContent.
|
||||
func (v *SimpleNamedFragmentRandomItemTopic) GetId() testutil.ID { return v.Id }
|
||||
|
||||
// GetName is a part of, and documented with, the interface SimpleNamedFragmentRandomItemContent.
|
||||
func (v *SimpleNamedFragmentRandomItemTopic) GetName() string { return v.Name }
|
||||
|
||||
func __unmarshalSimpleNamedFragmentRandomItemContent(v *SimpleNamedFragmentRandomItemContent, 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(SimpleNamedFragmentRandomItemArticle)
|
||||
return json.Unmarshal(m, *v)
|
||||
case "Video":
|
||||
*v = new(SimpleNamedFragmentRandomItemVideo)
|
||||
return json.Unmarshal(m, *v)
|
||||
case "Topic":
|
||||
*v = new(SimpleNamedFragmentRandomItemTopic)
|
||||
return json.Unmarshal(m, *v)
|
||||
case "":
|
||||
return fmt.Errorf(
|
||||
"Response was missing Content.__typename")
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
`Unexpected concrete type for SimpleNamedFragmentRandomItemContent: "%v"`, tn.TypeName)
|
||||
}
|
||||
}
|
||||
|
||||
// SimpleNamedFragmentRandomItemTopic includes the requested fields of the GraphQL type Topic.
|
||||
type SimpleNamedFragmentRandomItemTopic struct {
|
||||
Typename string `json:"__typename"`
|
||||
// ID is the identifier of the content.
|
||||
Id testutil.ID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// SimpleNamedFragmentRandomItemVideo includes the requested fields of the GraphQL type Video.
|
||||
type SimpleNamedFragmentRandomItemVideo struct {
|
||||
Typename string `json:"__typename"`
|
||||
// ID is the identifier of the content.
|
||||
Id testutil.ID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
VideoFields `json:"-"`
|
||||
}
|
||||
|
||||
func (v *SimpleNamedFragmentRandomItemVideo) UnmarshalJSON(b []byte) error {
|
||||
|
||||
var firstPass struct {
|
||||
*SimpleNamedFragmentRandomItemVideo
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.SimpleNamedFragmentRandomItemVideo = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, &v.VideoFields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SimpleNamedFragmentRandomLeafArticle includes the requested fields of the GraphQL type Article.
|
||||
type SimpleNamedFragmentRandomLeafArticle struct {
|
||||
Typename string `json:"__typename"`
|
||||
}
|
||||
|
||||
// SimpleNamedFragmentRandomLeafLeafContent includes the requested fields of the GraphQL interface LeafContent.
|
||||
//
|
||||
// SimpleNamedFragmentRandomLeafLeafContent is implemented by the following types:
|
||||
// SimpleNamedFragmentRandomLeafArticle
|
||||
// SimpleNamedFragmentRandomLeafVideo
|
||||
//
|
||||
// The GraphQL type's documentation follows.
|
||||
//
|
||||
// LeafContent represents content items that can't have child-nodes.
|
||||
type SimpleNamedFragmentRandomLeafLeafContent interface {
|
||||
implementsGraphQLInterfaceSimpleNamedFragmentRandomLeafLeafContent()
|
||||
// GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values).
|
||||
GetTypename() string
|
||||
}
|
||||
|
||||
func (v *SimpleNamedFragmentRandomLeafArticle) implementsGraphQLInterfaceSimpleNamedFragmentRandomLeafLeafContent() {
|
||||
}
|
||||
|
||||
// GetTypename is a part of, and documented with, the interface SimpleNamedFragmentRandomLeafLeafContent.
|
||||
func (v *SimpleNamedFragmentRandomLeafArticle) GetTypename() string { return v.Typename }
|
||||
|
||||
func (v *SimpleNamedFragmentRandomLeafVideo) implementsGraphQLInterfaceSimpleNamedFragmentRandomLeafLeafContent() {
|
||||
}
|
||||
|
||||
// GetTypename is a part of, and documented with, the interface SimpleNamedFragmentRandomLeafLeafContent.
|
||||
func (v *SimpleNamedFragmentRandomLeafVideo) GetTypename() string { return v.Typename }
|
||||
|
||||
func __unmarshalSimpleNamedFragmentRandomLeafLeafContent(v *SimpleNamedFragmentRandomLeafLeafContent, 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(SimpleNamedFragmentRandomLeafArticle)
|
||||
return json.Unmarshal(m, *v)
|
||||
case "Video":
|
||||
*v = new(SimpleNamedFragmentRandomLeafVideo)
|
||||
return json.Unmarshal(m, *v)
|
||||
case "":
|
||||
return fmt.Errorf(
|
||||
"Response was missing LeafContent.__typename")
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
`Unexpected concrete type for SimpleNamedFragmentRandomLeafLeafContent: "%v"`, tn.TypeName)
|
||||
}
|
||||
}
|
||||
|
||||
// SimpleNamedFragmentRandomLeafVideo includes the requested fields of the GraphQL type Video.
|
||||
type SimpleNamedFragmentRandomLeafVideo struct {
|
||||
Typename string `json:"__typename"`
|
||||
VideoFields `json:"-"`
|
||||
}
|
||||
|
||||
func (v *SimpleNamedFragmentRandomLeafVideo) UnmarshalJSON(b []byte) error {
|
||||
|
||||
var firstPass struct {
|
||||
*SimpleNamedFragmentRandomLeafVideo
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.SimpleNamedFragmentRandomLeafVideo = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, &v.VideoFields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SimpleNamedFragmentResponse is returned by SimpleNamedFragment on success.
|
||||
type SimpleNamedFragmentResponse struct {
|
||||
RandomItem SimpleNamedFragmentRandomItemContent `json:"-"`
|
||||
RandomLeaf SimpleNamedFragmentRandomLeafLeafContent `json:"-"`
|
||||
}
|
||||
|
||||
func (v *SimpleNamedFragmentResponse) UnmarshalJSON(b []byte) error {
|
||||
|
||||
var firstPass struct {
|
||||
*SimpleNamedFragmentResponse
|
||||
RandomItem json.RawMessage `json:"randomItem"`
|
||||
RandomLeaf json.RawMessage `json:"randomLeaf"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.SimpleNamedFragmentResponse = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
{
|
||||
target := &v.RandomItem
|
||||
raw := firstPass.RandomItem
|
||||
err = __unmarshalSimpleNamedFragmentRandomItemContent(
|
||||
target, raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"Unable to unmarshal SimpleNamedFragmentResponse.RandomItem: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
target := &v.RandomLeaf
|
||||
raw := firstPass.RandomLeaf
|
||||
err = __unmarshalSimpleNamedFragmentRandomLeafLeafContent(
|
||||
target, raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"Unable to unmarshal SimpleNamedFragmentResponse.RandomLeaf: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// VideoFields includes the GraphQL fields of Video requested by the fragment VideoFields.
|
||||
type VideoFields struct {
|
||||
// ID is documented in the Content interface.
|
||||
Id testutil.ID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Url string `json:"url"`
|
||||
Duration int `json:"duration"`
|
||||
Thumbnail VideoFieldsThumbnail `json:"thumbnail"`
|
||||
}
|
||||
|
||||
// VideoFieldsThumbnail includes the requested fields of the GraphQL type Thumbnail.
|
||||
type VideoFieldsThumbnail struct {
|
||||
Id testutil.ID `json:"id"`
|
||||
}
|
||||
|
||||
func SimpleNamedFragment(
|
||||
client graphql.Client,
|
||||
) (*SimpleNamedFragmentResponse, error) {
|
||||
var err error
|
||||
|
||||
var retval SimpleNamedFragmentResponse
|
||||
err = client.MakeRequest(
|
||||
nil,
|
||||
"SimpleNamedFragment",
|
||||
`
|
||||
query SimpleNamedFragment {
|
||||
randomItem {
|
||||
__typename
|
||||
id
|
||||
name
|
||||
... VideoFields
|
||||
}
|
||||
randomLeaf {
|
||||
__typename
|
||||
... VideoFields
|
||||
}
|
||||
}
|
||||
fragment VideoFields on Video {
|
||||
id
|
||||
name
|
||||
url
|
||||
duration
|
||||
thumbnail {
|
||||
id
|
||||
}
|
||||
}
|
||||
`,
|
||||
&retval,
|
||||
nil,
|
||||
)
|
||||
return &retval, err
|
||||
}
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"operations": [
|
||||
{
|
||||
"operationName": "SimpleNamedFragment",
|
||||
"query": "\nquery SimpleNamedFragment {\n\trandomItem {\n\t\t__typename\n\t\tid\n\t\tname\n\t\t... VideoFields\n\t}\n\trandomLeaf {\n\t\t__typename\n\t\t... VideoFields\n\t}\n}\nfragment VideoFields on Video {\n\tid\n\tname\n\turl\n\tduration\n\tthumbnail {\n\t\tid\n\t}\n}\n",
|
||||
"sourceLocation": "testdata/queries/SimpleNamedFragment.graphql"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+4
-4
@@ -82,13 +82,12 @@ type UnionNoFragmentsQueryResponse struct {
|
||||
|
||||
func (v *UnionNoFragmentsQueryResponse) UnmarshalJSON(b []byte) error {
|
||||
|
||||
type UnionNoFragmentsQueryResponseWrapper UnionNoFragmentsQueryResponse
|
||||
|
||||
var firstPass struct {
|
||||
*UnionNoFragmentsQueryResponseWrapper
|
||||
*UnionNoFragmentsQueryResponse
|
||||
RandomLeaf json.RawMessage `json:"randomLeaf"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.UnionNoFragmentsQueryResponseWrapper = (*UnionNoFragmentsQueryResponseWrapper)(v)
|
||||
firstPass.UnionNoFragmentsQueryResponse = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
@@ -105,6 +104,7 @@ func (v *UnionNoFragmentsQueryResponse) UnmarshalJSON(b []byte) error {
|
||||
"Unable to unmarshal UnionNoFragmentsQueryResponse.RandomLeaf: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+41
-26
@@ -119,11 +119,19 @@ type goStructField struct {
|
||||
Description string
|
||||
}
|
||||
|
||||
func isAbstract(typ goType) bool {
|
||||
_, ok := typ.Unwrap().(*goInterfaceType)
|
||||
// IsAbstract returns true if this field is of abstract type (i.e. GraphQL
|
||||
// union or interface; equivalently, represented by an interface in Go).
|
||||
func (field *goStructField) IsAbstract() bool {
|
||||
_, ok := field.GoType.Unwrap().(*goInterfaceType)
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsEmbedded returns true if this field is embedded (a.k.a. anonymous), which
|
||||
// is in practice true if it corresponds to a named fragment spread in GraphQL.
|
||||
func (field *goStructField) IsEmbedded() bool {
|
||||
return field.GoName == ""
|
||||
}
|
||||
|
||||
func (typ *goStructType) WriteDefinition(w io.Writer, g *generator) error {
|
||||
description := typ.Description
|
||||
if typ.Incomplete {
|
||||
@@ -144,28 +152,47 @@ func (typ *goStructType) WriteDefinition(w io.Writer, g *generator) error {
|
||||
}
|
||||
writeDescription(w, description)
|
||||
|
||||
needUnmarshaler := false
|
||||
fmt.Fprintf(w, "type %s struct {\n", typ.GoName)
|
||||
for _, field := range typ.Fields {
|
||||
writeDescription(w, field.Description)
|
||||
jsonName := field.JSONName
|
||||
if isAbstract(field.GoType) {
|
||||
// abstract types are handled in our UnmarshalJSON
|
||||
if field.IsAbstract() {
|
||||
// abstract types are handled in our UnmarshalJSON (see below)
|
||||
needUnmarshaler = true
|
||||
jsonName = "-"
|
||||
}
|
||||
fmt.Fprintf(w, "\t%s %s `json:\"%s\"`\n",
|
||||
field.GoName, field.GoType.Reference(), jsonName)
|
||||
if field.IsEmbedded() {
|
||||
// embedded fields also need UnmarshalJSON handling (see below)
|
||||
needUnmarshaler = true
|
||||
fmt.Fprintf(w, "\t%s `json:\"-\"`\n", field.GoType.Unwrap().Reference())
|
||||
} else {
|
||||
fmt.Fprintf(w, "\t%s %s `json:\"%s\"`\n",
|
||||
field.GoName, field.GoType.Reference(), jsonName)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(w, "}\n")
|
||||
|
||||
// Now, if needed, write the unmarshaler.
|
||||
// Now, if needed, write the unmarshaler. We need one if we have any
|
||||
// interface-typed fields, or any embedded fields.
|
||||
//
|
||||
// Specifically, in order to unmarshal interface values, we need to add an
|
||||
// UnmarshalJSON method to each type which has an interface-typed *field*
|
||||
// (not the interface type itself -- we can't add methods to that).
|
||||
// But we put most of the logic in a per-interface-type helper function,
|
||||
// written along with the interface type; the UnmarshalJSON method is just
|
||||
// the boilerplate.
|
||||
if len(typ.AbstractFields()) == 0 {
|
||||
// For interface-typed fields, ideally we'd write an UnmarshalJSON method
|
||||
// on the field, but you can't add a method to an interface. So we write a
|
||||
// per-interface-type helper, but we have to call it (with a little
|
||||
// boilerplate) everywhere the type is referenced.
|
||||
//
|
||||
// For embedded fields (from fragments), mostly the JSON library would just
|
||||
// do what we want, but there are two problems. First, if the embedded
|
||||
// type has its own UnmarshalJSON, naively that would be promoted to
|
||||
// become our UnmarshalJSON, which is no good. But we don't want to just
|
||||
// hide that method and inline its fields, either; we need to call its
|
||||
// UnmarshalJSON (on the same object we unmarshal into this struct).
|
||||
// Second, if the embedded type duplicates any fields of the embedding type
|
||||
// -- maybe both the fragment and the selection into which it's spread
|
||||
// select the same field, or several fragments select the same field -- the
|
||||
// JSON library will only fill one of those (the least-nested one); we want
|
||||
// to fill them all.
|
||||
if !needUnmarshaler {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -181,18 +208,6 @@ func (typ *goStructType) WriteDefinition(w io.Writer, g *generator) error {
|
||||
|
||||
func (typ *goStructType) Reference() string { return typ.GoName }
|
||||
|
||||
// AbstractFields returns all the fields which are abstract types (i.e. GraphQL
|
||||
// unions and interfaces; equivalently, types represented by interfaces in Go).
|
||||
func (typ *goStructType) AbstractFields() []*goStructField {
|
||||
var ret []*goStructField
|
||||
for _, field := range typ.Fields {
|
||||
if isAbstract(field.GoType) {
|
||||
ret = append(ret, field)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// goInterfaceType represents a Go interface type, used to represent a GraphQL
|
||||
// interface or union type.
|
||||
type goInterfaceType struct {
|
||||
|
||||
+37
-15
@@ -2,36 +2,49 @@
|
||||
UnmarshalJSON from the function it follows) */}}
|
||||
|
||||
func (v *{{.GoName}}) UnmarshalJSON(b []byte) error {
|
||||
{{/* We want to specially handle the abstract fields (.AbstractFields),
|
||||
but unmarshal everything else normally. To handle abstract fields,
|
||||
{{/* We want to specially handle the abstract or embedded fields, but
|
||||
unmarshal everything else normally. To handle abstract fields,
|
||||
first we unmarshal them into a json.RawMessage, and then handle those
|
||||
further, below. For the rest, we just want to call json.Unmarshal.
|
||||
further, below. Embedded fields we just unmarshal directly into the
|
||||
embedded value. For the rest, we just want to call json.Unmarshal.
|
||||
But if we do that naively on a value of type `.Type`, it will call
|
||||
this function again, and recurse infinitely. So we make a wrapper
|
||||
type -- with a different name, thus different methods, but the same
|
||||
fields, and unmarshal into that. For more on why this is so
|
||||
difficult, see
|
||||
https://github.com/benjaminjkraft/notes/blob/master/go-json-interfaces.md
|
||||
type which embeds both this type and NoUmnarshalJSON, which prevents
|
||||
either's UnmarshalJSON method from being promoted. For more on why
|
||||
this is so difficult, see
|
||||
https://github.com/benjaminjkraft/notes/blob/master/go-json-interfaces.md.
|
||||
(Note there are a few different ways "hide" the method, but this one
|
||||
seems to be the best option that works if this type has embedded types
|
||||
with UnmarshalJSON methods.)
|
||||
TODO(benkraft)): Ensure `{{.Type}}Wrapper` won't collide with any
|
||||
other type we need. (For the most part it being locally-scoped saves
|
||||
us; it's not clear if this can be a problem in practice.)
|
||||
*/}}
|
||||
type {{.GoName}}Wrapper {{.GoName}}
|
||||
|
||||
{{/* TODO(benkraft): Omit/simplify the first pass if all fields are
|
||||
embedded/abstract. */ -}}
|
||||
var firstPass struct{
|
||||
*{{.GoName}}Wrapper
|
||||
{{range .AbstractFields -}}
|
||||
*{{.GoName}}
|
||||
{{range .Fields -}}
|
||||
{{if .IsAbstract -}}
|
||||
{{.GoName}} {{repeat .GoType.SliceDepth "[]"}}{{ref "encoding/json.RawMessage"}} `json:"{{.JSONName}}"`
|
||||
{{end}}
|
||||
{{end -}}
|
||||
{{end -}}
|
||||
{{/* TODO(benkraft): In principle you might have a field-name that
|
||||
conflicts with this one; avoid that. */ -}}
|
||||
{{ref "github.com/Khan/genqlient/graphql.NoUnmarshalJSON"}}
|
||||
}
|
||||
firstPass.{{.GoName}}Wrapper = (*{{.GoName}}Wrapper)(v)
|
||||
firstPass.{{.GoName}} = v
|
||||
|
||||
err := {{ref "encoding/json.Unmarshal"}}(b, &firstPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
{{/* Now, for each field, call out to the unmarshal-helper.
|
||||
{{/* Now, handle the fields needing special handling. */}}
|
||||
{{range $field := .Fields -}}
|
||||
{{if $field.IsAbstract -}}
|
||||
{{/* First, for abstract fields, call the unmarshal-helper.
|
||||
This gets a little complicated because we may have a slice field.
|
||||
So what we do is basically, for each field of type `[][]...[]MyType`:
|
||||
|
||||
@@ -65,7 +78,6 @@ func (v *{{.GoName}}) UnmarshalJSON(b []byte) error {
|
||||
each field we are handling, which would otherwise conflict. (We could
|
||||
instead suffix the names, but that makes things much harder to read.)
|
||||
*/}}
|
||||
{{range $field := .AbstractFields -}}
|
||||
{
|
||||
target := &v.{{$field.GoName}}
|
||||
raw := firstPass.{{$field.GoName}}
|
||||
@@ -92,7 +104,17 @@ func (v *{{.GoName}}) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
{{end -}}
|
||||
}
|
||||
{{end -}}
|
||||
{{end -}}{{/* end if .IsAbstract */}}
|
||||
{{if $field.IsEmbedded -}}
|
||||
{{/* Embedded fields are easier: we just unmarshal the same input into
|
||||
them. (They're also easier because they can't be lists, since they
|
||||
arise from GraphQL fragment spreads.) */}}
|
||||
err = json.Unmarshal(b, &v.{{$field.GoType.Unwrap.Reference}})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
{{end}}{{/* end if .IsEmbedded */ -}}
|
||||
{{end}}{{/* end range .Fields */ -}}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig
|
||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
||||
github.com/bradleyjkemp/cupaloy/v2 v2.6.0 h1:knToPYa2xtfg42U3I6punFEjaGFKWQRXJwj0JTv4mTs=
|
||||
github.com/bradleyjkemp/cupaloy/v2 v2.6.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@@ -39,6 +40,7 @@ github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047 h1:zCoDWFD5
|
||||
github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package graphql
|
||||
|
||||
// Utility types used by the generated code. In general, these are *not*
|
||||
// intended for end-users.
|
||||
|
||||
// NoUnmarshalJSON is intended for the use of genqlient's generated code only.
|
||||
//
|
||||
// It is used to prevent a struct type from inheriting its embed's
|
||||
// UnmarshalJSON method: given a type
|
||||
// type T struct { E; NoUnmarshalJSON }
|
||||
// where E has an UnmarshalJSON method, T will not inherit it, per the Go
|
||||
// selector rules: https://golang.org/ref/spec#Selectors.
|
||||
type NoUnmarshalJSON struct{}
|
||||
|
||||
// UnmarshalJSON should never be called; it exists only to prevent a sibling
|
||||
// UnmarshalJSON method from being promoted.
|
||||
func (NoUnmarshalJSON) UnmarshalJSON(b []byte) error {
|
||||
panic("NoUnmarshalJSON.UnmarshalJSON should never be called!")
|
||||
}
|
||||
@@ -10,6 +10,152 @@ import (
|
||||
"github.com/Khan/genqlient/graphql"
|
||||
)
|
||||
|
||||
// AnimalFields includes the GraphQL fields of Animal requested by the fragment AnimalFields.
|
||||
type AnimalFields struct {
|
||||
Id string `json:"id"`
|
||||
Hair AnimalFieldsHairBeingsHair `json:"hair"`
|
||||
Owner AnimalFieldsOwnerBeing `json:"-"`
|
||||
}
|
||||
|
||||
func (v *AnimalFields) UnmarshalJSON(b []byte) error {
|
||||
|
||||
var firstPass struct {
|
||||
*AnimalFields
|
||||
Owner json.RawMessage `json:"owner"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.AnimalFields = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
{
|
||||
target := &v.Owner
|
||||
raw := firstPass.Owner
|
||||
err = __unmarshalAnimalFieldsOwnerBeing(
|
||||
target, raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"Unable to unmarshal AnimalFields.Owner: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AnimalFieldsHairBeingsHair includes the requested fields of the GraphQL type BeingsHair.
|
||||
type AnimalFieldsHairBeingsHair struct {
|
||||
HasHair bool `json:"hasHair"`
|
||||
}
|
||||
|
||||
// AnimalFieldsOwnerAnimal includes the requested fields of the GraphQL type Animal.
|
||||
type AnimalFieldsOwnerAnimal struct {
|
||||
Typename string `json:"__typename"`
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
// AnimalFieldsOwnerBeing includes the requested fields of the GraphQL interface Being.
|
||||
//
|
||||
// AnimalFieldsOwnerBeing is implemented by the following types:
|
||||
// AnimalFieldsOwnerUser
|
||||
// AnimalFieldsOwnerAnimal
|
||||
//
|
||||
// The GraphQL type's documentation follows.
|
||||
//
|
||||
//
|
||||
type AnimalFieldsOwnerBeing interface {
|
||||
implementsGraphQLInterfaceAnimalFieldsOwnerBeing()
|
||||
// GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values).
|
||||
GetTypename() string
|
||||
// GetId returns the interface-field "id" from its implementation.
|
||||
GetId() string
|
||||
}
|
||||
|
||||
func (v *AnimalFieldsOwnerUser) implementsGraphQLInterfaceAnimalFieldsOwnerBeing() {}
|
||||
|
||||
// GetTypename is a part of, and documented with, the interface AnimalFieldsOwnerBeing.
|
||||
func (v *AnimalFieldsOwnerUser) GetTypename() string { return v.Typename }
|
||||
|
||||
// GetId is a part of, and documented with, the interface AnimalFieldsOwnerBeing.
|
||||
func (v *AnimalFieldsOwnerUser) GetId() string { return v.Id }
|
||||
|
||||
func (v *AnimalFieldsOwnerAnimal) implementsGraphQLInterfaceAnimalFieldsOwnerBeing() {}
|
||||
|
||||
// GetTypename is a part of, and documented with, the interface AnimalFieldsOwnerBeing.
|
||||
func (v *AnimalFieldsOwnerAnimal) GetTypename() string { return v.Typename }
|
||||
|
||||
// GetId is a part of, and documented with, the interface AnimalFieldsOwnerBeing.
|
||||
func (v *AnimalFieldsOwnerAnimal) GetId() string { return v.Id }
|
||||
|
||||
func __unmarshalAnimalFieldsOwnerBeing(v *AnimalFieldsOwnerBeing, 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 "User":
|
||||
*v = new(AnimalFieldsOwnerUser)
|
||||
return json.Unmarshal(m, *v)
|
||||
case "Animal":
|
||||
*v = new(AnimalFieldsOwnerAnimal)
|
||||
return json.Unmarshal(m, *v)
|
||||
case "":
|
||||
return fmt.Errorf(
|
||||
"Response was missing Being.__typename")
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
`Unexpected concrete type for AnimalFieldsOwnerBeing: "%v"`, tn.TypeName)
|
||||
}
|
||||
}
|
||||
|
||||
// AnimalFieldsOwnerUser includes the requested fields of the GraphQL type User.
|
||||
type AnimalFieldsOwnerUser struct {
|
||||
Typename string `json:"__typename"`
|
||||
Id string `json:"id"`
|
||||
UserFields `json:"-"`
|
||||
}
|
||||
|
||||
func (v *AnimalFieldsOwnerUser) UnmarshalJSON(b []byte) error {
|
||||
|
||||
var firstPass struct {
|
||||
*AnimalFieldsOwnerUser
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.AnimalFieldsOwnerUser = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, &v.UserFields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MoreUserFields includes the GraphQL fields of User requested by the fragment MoreUserFields.
|
||||
type MoreUserFields struct {
|
||||
Id string `json:"id"`
|
||||
Hair MoreUserFieldsHair `json:"hair"`
|
||||
}
|
||||
|
||||
// MoreUserFieldsHair includes the requested fields of the GraphQL type Hair.
|
||||
type MoreUserFieldsHair struct {
|
||||
Color string `json:"color"`
|
||||
}
|
||||
|
||||
type Species string
|
||||
|
||||
const (
|
||||
@@ -17,6 +163,33 @@ const (
|
||||
SpeciesCoelacanth Species = "COELACANTH"
|
||||
)
|
||||
|
||||
// UserFields includes the GraphQL fields of User requested by the fragment UserFields.
|
||||
type UserFields struct {
|
||||
Id string `json:"id"`
|
||||
LuckyNumber int `json:"luckyNumber"`
|
||||
MoreUserFields `json:"-"`
|
||||
}
|
||||
|
||||
func (v *UserFields) UnmarshalJSON(b []byte) error {
|
||||
|
||||
var firstPass struct {
|
||||
*UserFields
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.UserFields = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, &v.MoreUserFields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// queryWithFragmentsBeingsAnimal includes the requested fields of the GraphQL type Animal.
|
||||
type queryWithFragmentsBeingsAnimal struct {
|
||||
Typename string `json:"__typename"`
|
||||
@@ -29,13 +202,12 @@ type queryWithFragmentsBeingsAnimal struct {
|
||||
|
||||
func (v *queryWithFragmentsBeingsAnimal) UnmarshalJSON(b []byte) error {
|
||||
|
||||
type queryWithFragmentsBeingsAnimalWrapper queryWithFragmentsBeingsAnimal
|
||||
|
||||
var firstPass struct {
|
||||
*queryWithFragmentsBeingsAnimalWrapper
|
||||
*queryWithFragmentsBeingsAnimal
|
||||
Owner json.RawMessage `json:"owner"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.queryWithFragmentsBeingsAnimalWrapper = (*queryWithFragmentsBeingsAnimalWrapper)(v)
|
||||
firstPass.queryWithFragmentsBeingsAnimal = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
@@ -52,6 +224,7 @@ func (v *queryWithFragmentsBeingsAnimal) UnmarshalJSON(b []byte) error {
|
||||
"Unable to unmarshal queryWithFragmentsBeingsAnimal.Owner: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -238,13 +411,12 @@ type queryWithFragmentsResponse struct {
|
||||
|
||||
func (v *queryWithFragmentsResponse) UnmarshalJSON(b []byte) error {
|
||||
|
||||
type queryWithFragmentsResponseWrapper queryWithFragmentsResponse
|
||||
|
||||
var firstPass struct {
|
||||
*queryWithFragmentsResponseWrapper
|
||||
*queryWithFragmentsResponse
|
||||
Beings []json.RawMessage `json:"beings"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.queryWithFragmentsResponseWrapper = (*queryWithFragmentsResponseWrapper)(v)
|
||||
firstPass.queryWithFragmentsResponse = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
@@ -267,6 +439,7 @@ func (v *queryWithFragmentsResponse) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -363,13 +536,12 @@ type queryWithInterfaceListFieldResponse struct {
|
||||
|
||||
func (v *queryWithInterfaceListFieldResponse) UnmarshalJSON(b []byte) error {
|
||||
|
||||
type queryWithInterfaceListFieldResponseWrapper queryWithInterfaceListFieldResponse
|
||||
|
||||
var firstPass struct {
|
||||
*queryWithInterfaceListFieldResponseWrapper
|
||||
*queryWithInterfaceListFieldResponse
|
||||
Beings []json.RawMessage `json:"beings"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.queryWithInterfaceListFieldResponseWrapper = (*queryWithInterfaceListFieldResponseWrapper)(v)
|
||||
firstPass.queryWithInterfaceListFieldResponse = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
@@ -392,6 +564,7 @@ func (v *queryWithInterfaceListFieldResponse) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -488,13 +661,12 @@ type queryWithInterfaceListPointerFieldResponse struct {
|
||||
|
||||
func (v *queryWithInterfaceListPointerFieldResponse) UnmarshalJSON(b []byte) error {
|
||||
|
||||
type queryWithInterfaceListPointerFieldResponseWrapper queryWithInterfaceListPointerFieldResponse
|
||||
|
||||
var firstPass struct {
|
||||
*queryWithInterfaceListPointerFieldResponseWrapper
|
||||
*queryWithInterfaceListPointerFieldResponse
|
||||
Beings []json.RawMessage `json:"beings"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.queryWithInterfaceListPointerFieldResponseWrapper = (*queryWithInterfaceListPointerFieldResponseWrapper)(v)
|
||||
firstPass.queryWithInterfaceListPointerFieldResponse = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
@@ -518,6 +690,7 @@ func (v *queryWithInterfaceListPointerFieldResponse) UnmarshalJSON(b []byte) err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -621,13 +794,12 @@ type queryWithInterfaceNoFragmentsResponse struct {
|
||||
|
||||
func (v *queryWithInterfaceNoFragmentsResponse) UnmarshalJSON(b []byte) error {
|
||||
|
||||
type queryWithInterfaceNoFragmentsResponseWrapper queryWithInterfaceNoFragmentsResponse
|
||||
|
||||
var firstPass struct {
|
||||
*queryWithInterfaceNoFragmentsResponseWrapper
|
||||
*queryWithInterfaceNoFragmentsResponse
|
||||
Being json.RawMessage `json:"being"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.queryWithInterfaceNoFragmentsResponseWrapper = (*queryWithInterfaceNoFragmentsResponseWrapper)(v)
|
||||
firstPass.queryWithInterfaceNoFragmentsResponse = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
@@ -644,6 +816,164 @@ func (v *queryWithInterfaceNoFragmentsResponse) UnmarshalJSON(b []byte) error {
|
||||
"Unable to unmarshal queryWithInterfaceNoFragmentsResponse.Being: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// queryWithNamedFragmentsBeingsAnimal includes the requested fields of the GraphQL type Animal.
|
||||
type queryWithNamedFragmentsBeingsAnimal struct {
|
||||
Typename string `json:"__typename"`
|
||||
Id string `json:"id"`
|
||||
AnimalFields `json:"-"`
|
||||
}
|
||||
|
||||
func (v *queryWithNamedFragmentsBeingsAnimal) UnmarshalJSON(b []byte) error {
|
||||
|
||||
var firstPass struct {
|
||||
*queryWithNamedFragmentsBeingsAnimal
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.queryWithNamedFragmentsBeingsAnimal = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, &v.AnimalFields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// queryWithNamedFragmentsBeingsBeing includes the requested fields of the GraphQL interface Being.
|
||||
//
|
||||
// queryWithNamedFragmentsBeingsBeing is implemented by the following types:
|
||||
// queryWithNamedFragmentsBeingsUser
|
||||
// queryWithNamedFragmentsBeingsAnimal
|
||||
//
|
||||
// The GraphQL type's documentation follows.
|
||||
//
|
||||
//
|
||||
type queryWithNamedFragmentsBeingsBeing interface {
|
||||
implementsGraphQLInterfacequeryWithNamedFragmentsBeingsBeing()
|
||||
// GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values).
|
||||
GetTypename() string
|
||||
// GetId returns the interface-field "id" from its implementation.
|
||||
GetId() string
|
||||
}
|
||||
|
||||
func (v *queryWithNamedFragmentsBeingsUser) implementsGraphQLInterfacequeryWithNamedFragmentsBeingsBeing() {
|
||||
}
|
||||
|
||||
// GetTypename is a part of, and documented with, the interface queryWithNamedFragmentsBeingsBeing.
|
||||
func (v *queryWithNamedFragmentsBeingsUser) GetTypename() string { return v.Typename }
|
||||
|
||||
// GetId is a part of, and documented with, the interface queryWithNamedFragmentsBeingsBeing.
|
||||
func (v *queryWithNamedFragmentsBeingsUser) GetId() string { return v.Id }
|
||||
|
||||
func (v *queryWithNamedFragmentsBeingsAnimal) implementsGraphQLInterfacequeryWithNamedFragmentsBeingsBeing() {
|
||||
}
|
||||
|
||||
// GetTypename is a part of, and documented with, the interface queryWithNamedFragmentsBeingsBeing.
|
||||
func (v *queryWithNamedFragmentsBeingsAnimal) GetTypename() string { return v.Typename }
|
||||
|
||||
// GetId is a part of, and documented with, the interface queryWithNamedFragmentsBeingsBeing.
|
||||
func (v *queryWithNamedFragmentsBeingsAnimal) GetId() string { return v.Id }
|
||||
|
||||
func __unmarshalqueryWithNamedFragmentsBeingsBeing(v *queryWithNamedFragmentsBeingsBeing, 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 "User":
|
||||
*v = new(queryWithNamedFragmentsBeingsUser)
|
||||
return json.Unmarshal(m, *v)
|
||||
case "Animal":
|
||||
*v = new(queryWithNamedFragmentsBeingsAnimal)
|
||||
return json.Unmarshal(m, *v)
|
||||
case "":
|
||||
return fmt.Errorf(
|
||||
"Response was missing Being.__typename")
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
`Unexpected concrete type for queryWithNamedFragmentsBeingsBeing: "%v"`, tn.TypeName)
|
||||
}
|
||||
}
|
||||
|
||||
// queryWithNamedFragmentsBeingsUser includes the requested fields of the GraphQL type User.
|
||||
type queryWithNamedFragmentsBeingsUser struct {
|
||||
Typename string `json:"__typename"`
|
||||
Id string `json:"id"`
|
||||
UserFields `json:"-"`
|
||||
}
|
||||
|
||||
func (v *queryWithNamedFragmentsBeingsUser) UnmarshalJSON(b []byte) error {
|
||||
|
||||
var firstPass struct {
|
||||
*queryWithNamedFragmentsBeingsUser
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.queryWithNamedFragmentsBeingsUser = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, &v.UserFields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// queryWithNamedFragmentsResponse is returned by queryWithNamedFragments on success.
|
||||
type queryWithNamedFragmentsResponse struct {
|
||||
Beings []queryWithNamedFragmentsBeingsBeing `json:"-"`
|
||||
}
|
||||
|
||||
func (v *queryWithNamedFragmentsResponse) UnmarshalJSON(b []byte) error {
|
||||
|
||||
var firstPass struct {
|
||||
*queryWithNamedFragmentsResponse
|
||||
Beings []json.RawMessage `json:"beings"`
|
||||
graphql.NoUnmarshalJSON
|
||||
}
|
||||
firstPass.queryWithNamedFragmentsResponse = v
|
||||
|
||||
err := json.Unmarshal(b, &firstPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
{
|
||||
target := &v.Beings
|
||||
raw := firstPass.Beings
|
||||
*target = make(
|
||||
[]queryWithNamedFragmentsBeingsBeing,
|
||||
len(raw))
|
||||
for i, raw := range raw {
|
||||
target := &(*target)[i]
|
||||
err = __unmarshalqueryWithNamedFragmentsBeingsBeing(
|
||||
target, raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"Unable to unmarshal queryWithNamedFragmentsResponse.Beings: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -877,3 +1207,56 @@ query queryWithFragments ($ids: [ID!]!) {
|
||||
)
|
||||
return &retval, err
|
||||
}
|
||||
|
||||
func queryWithNamedFragments(
|
||||
ctx context.Context,
|
||||
client graphql.Client,
|
||||
ids []string,
|
||||
) (*queryWithNamedFragmentsResponse, error) {
|
||||
variables := map[string]interface{}{
|
||||
"ids": ids,
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
var retval queryWithNamedFragmentsResponse
|
||||
err = client.MakeRequest(
|
||||
ctx,
|
||||
"queryWithNamedFragments",
|
||||
`
|
||||
query queryWithNamedFragments ($ids: [ID!]!) {
|
||||
beings(ids: $ids) {
|
||||
__typename
|
||||
id
|
||||
... AnimalFields
|
||||
... UserFields
|
||||
}
|
||||
}
|
||||
fragment AnimalFields on Animal {
|
||||
id
|
||||
hair {
|
||||
hasHair
|
||||
}
|
||||
owner {
|
||||
__typename
|
||||
id
|
||||
... UserFields
|
||||
}
|
||||
}
|
||||
fragment UserFields on User {
|
||||
id
|
||||
luckyNumber
|
||||
... MoreUserFields
|
||||
}
|
||||
fragment MoreUserFields on User {
|
||||
id
|
||||
hair {
|
||||
color
|
||||
}
|
||||
}
|
||||
`,
|
||||
&retval,
|
||||
variables,
|
||||
)
|
||||
return &retval, err
|
||||
}
|
||||
|
||||
@@ -288,6 +288,94 @@ func TestFragments(t *testing.T) {
|
||||
assert.Nil(t, resp.Beings[2])
|
||||
}
|
||||
|
||||
func TestNamedFragments(t *testing.T) {
|
||||
_ = `# @genqlient
|
||||
fragment AnimalFields on Animal {
|
||||
id
|
||||
hair { hasHair }
|
||||
owner { id ...UserFields }
|
||||
}
|
||||
|
||||
fragment MoreUserFields on User {
|
||||
id
|
||||
hair { color }
|
||||
}
|
||||
|
||||
fragment UserFields on User {
|
||||
id luckyNumber
|
||||
...MoreUserFields
|
||||
}
|
||||
|
||||
query queryWithNamedFragments($ids: [ID!]!) {
|
||||
beings(ids: $ids) {
|
||||
__typename id
|
||||
...AnimalFields
|
||||
...UserFields
|
||||
}
|
||||
}`
|
||||
|
||||
ctx := context.Background()
|
||||
server := server.RunServer()
|
||||
defer server.Close()
|
||||
client := graphql.NewClient(server.URL, http.DefaultClient)
|
||||
|
||||
resp, err := queryWithNamedFragments(ctx, client, []string{"1", "3", "12847394823"})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, resp.Beings, 3)
|
||||
|
||||
// We should get the following three beings:
|
||||
// User{Id: 1, Name: "Yours Truly"},
|
||||
// Animal{Id: 3, Name: "Fido"},
|
||||
// null
|
||||
|
||||
// Check fields both via interface and via type-assertion when possible
|
||||
// User has, in total, the fields: __typename id luckyNumber.
|
||||
assert.Equal(t, "User", resp.Beings[0].GetTypename())
|
||||
assert.Equal(t, "1", resp.Beings[0].GetId())
|
||||
// (luckyNumber, hair we need to cast for)
|
||||
|
||||
user, ok := resp.Beings[0].(*queryWithNamedFragmentsBeingsUser)
|
||||
require.Truef(t, ok, "got %T, not User", resp.Beings[0])
|
||||
assert.Equal(t, "1", user.Id)
|
||||
assert.Equal(t, "1", user.UserFields.Id)
|
||||
assert.Equal(t, "1", user.UserFields.MoreUserFields.Id)
|
||||
// on UserFields, but we should be able to access directly via embedding:
|
||||
assert.Equal(t, 17, user.LuckyNumber)
|
||||
assert.Equal(t, "Black", user.Hair.Color)
|
||||
|
||||
// Animal has, in total, the fields:
|
||||
// __typename
|
||||
// id
|
||||
// hair { hasHair }
|
||||
// owner { id luckyNumber }
|
||||
assert.Equal(t, "Animal", resp.Beings[1].GetTypename())
|
||||
assert.Equal(t, "3", resp.Beings[1].GetId())
|
||||
// (hair.* and owner.* we have to cast for)
|
||||
|
||||
animal, ok := resp.Beings[1].(*queryWithNamedFragmentsBeingsAnimal)
|
||||
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
||||
// Check that we filled in *both* ID fields:
|
||||
assert.Equal(t, "3", animal.Id)
|
||||
assert.Equal(t, "3", animal.AnimalFields.Id)
|
||||
// on AnimalFields:
|
||||
assert.True(t, animal.Hair.HasHair)
|
||||
assert.Equal(t, "1", animal.Owner.GetId())
|
||||
// (luckyNumber we have to cast for, again)
|
||||
|
||||
owner, ok := animal.Owner.(*AnimalFieldsOwnerUser)
|
||||
require.Truef(t, ok, "got %T, not User", animal.Owner)
|
||||
// Check that we filled in *both* ID fields:
|
||||
assert.Equal(t, "1", owner.Id)
|
||||
assert.Equal(t, "1", owner.UserFields.Id)
|
||||
assert.Equal(t, "1", owner.UserFields.MoreUserFields.Id)
|
||||
// on UserFields:
|
||||
assert.Equal(t, 17, owner.LuckyNumber)
|
||||
assert.Equal(t, "Black", owner.Hair.Color)
|
||||
|
||||
assert.Nil(t, resp.Beings[2])
|
||||
}
|
||||
|
||||
func TestGeneratedCode(t *testing.T) {
|
||||
// TODO(benkraft): Check that gqlgen is up to date too. In practice that's
|
||||
// less likely to be a problem, since it should only change if you update
|
||||
|
||||
Reference in New Issue
Block a user