From 666f2b032c17208ab92b8a12d107b4720fdaf357 Mon Sep 17 00:00:00 2001 From: Will Roden Date: Mon, 30 Mar 2026 16:44:57 -0500 Subject: [PATCH 01/12] collapse unreferenced interface impls into a catch-all struct --- generate/convert.go | 82 +++++++++- generate/description.go | 9 +- generate/marshal_helper.go.tmpl | 6 + ....graphql-ComplexInlineFragments.graphql.go | 150 +++++------------- ...s.graphql-ComplexNamedFragments.graphql.go | 55 ++----- ...nt.graphql-SimpleInlineFragment.graphql.go | 41 ++--- ...ent.graphql-SimpleNamedFragment.graphql.go | 112 ++++--------- ...ructOption.graphql-StructOption.graphql.go | 68 ++------ generate/types.go | 11 +- generate/unmarshal_helper.go.tmpl | 5 + internal/integration/generated.go | 84 ++++------ 11 files changed, 249 insertions(+), 374 deletions(-) diff --git a/generate/convert.go b/generate/convert.go index b1a0c062..c6b100d7 100644 --- a/generate/convert.go +++ b/generate/convert.go @@ -522,15 +522,24 @@ func (g *generator) convertDefinition( implementationTypes := g.schema.GetPossibleTypes(def) // Make sure we generate stable output by sorting the types by name when we get them sort.Slice(implementationTypes, func(i, j int) bool { return implementationTypes[i].Name < implementationTypes[j].Name }) + + // Determine which concrete types are explicitly referenced by + // fragments in the selection set. If any are, we only generate + // full implementation structs for those types, and a single + // catch-all "other" struct for everything else. + referencedTypes := g.collectReferencedTypes(selectionSet) + goType := &goInterfaceType{ GoName: name, SharedFields: sharedFields, - Implementations: make([]*goStructType, len(implementationTypes)), Selection: selectionSet, descriptionInfo: desc, } - for i, implDef := range implementationTypes { + for _, implDef := range implementationTypes { + if len(referencedTypes) > 0 && !referencedTypes[implDef.Name] { + continue + } // TODO(benkraft): In principle we should skip generating a Go // field for __typename each of these impl-defs if you didn't // request it (and it was automatically added by @@ -548,8 +557,24 @@ func (g *generator) convertDefinition( pos, "interface %s had non-object implementation %s", def.Name, implDef.Name) } - goType.Implementations[i] = implStructTyp + goType.Implementations = append(goType.Implementations, implStructTyp) + } + + if len(referencedTypes) > 0 && len(goType.Implementations) < len(implementationTypes) { + otherName := makeTypeName(namePrefix, "other", g.Config.GetDefaultCasingAlgorithm()) + otherType := &goStructType{ + GoName: otherName, + Fields: sharedFields, + Selection: selectionSet, + descriptionInfo: descriptionInfo{ + GraphQLName: def.Name, + }, + Generator: g, + } + goType.OtherImplementation = otherType + g.typeMap[otherName] = otherType } + return g.addType(goType, goType.GoName, pos) case ast.Enum: @@ -713,6 +738,30 @@ func (g *generator) convertSelectionSet( return uniqFields, nil } +// collectReferencedTypes returns the set of concrete type names that are +// explicitly referenced by inline fragments or named fragment spreads in the +// given selection set. This is used to determine which implementation types +// need full structs generated, vs. which can share a single catch-all "other" +// struct. +func (g *generator) collectReferencedTypes(selectionSet ast.SelectionSet) map[string]bool { + referenced := make(map[string]bool) + for _, selection := range selectionSet { + switch sel := selection.(type) { + case *ast.InlineFragment: + fragmentTypeDef := g.schema.Types[sel.TypeCondition] + for _, t := range g.schema.GetPossibleTypes(fragmentTypeDef) { + referenced[t.Name] = true + } + case *ast.FragmentSpread: + fragmentTypeDef := sel.Definition.Definition + for _, t := range g.schema.GetPossibleTypes(fragmentTypeDef) { + referenced[t.Name] = true + } + } + } + return referenced +} + // fragmentMatches returns true if the given fragment is "active" when applied // to the given type. // @@ -886,16 +935,22 @@ func (g *generator) convertNamedFragment(fragment *ast.FragmentDefinition) (goTy implementationTypes := g.schema.GetPossibleTypes(typ) // Make sure we generate stable output by sorting the types by name when we get them sort.Slice(implementationTypes, func(i, j int) bool { return implementationTypes[i].Name < implementationTypes[j].Name }) + + referencedTypes := g.collectReferencedTypes(fragment.SelectionSet) + goType := &goInterfaceType{ GoName: fragment.Name, SharedFields: fields, - Implementations: make([]*goStructType, len(implementationTypes)), Selection: fragment.SelectionSet, descriptionInfo: desc, } g.typeMap[fragment.Name] = goType - for i, implDef := range implementationTypes { + for _, implDef := range implementationTypes { + if len(referencedTypes) > 0 && !referencedTypes[implDef.Name] { + continue + } + implFields, err := g.convertSelectionSet( newPrefixList(fragment.Name), fragment.SelectionSet, implDef, directive) if err != nil { @@ -912,10 +967,25 @@ func (g *generator) convertNamedFragment(fragment *ast.FragmentDefinition) (goTy descriptionInfo: implDesc, Generator: g, } - goType.Implementations[i] = implTyp + goType.Implementations = append(goType.Implementations, implTyp) g.typeMap[implTyp.GoName] = implTyp } + if len(referencedTypes) > 0 && len(goType.Implementations) < len(implementationTypes) { + otherName := fragment.Name + "Other" + otherType := &goStructType{ + GoName: otherName, + Fields: fields, + Selection: fragment.SelectionSet, + descriptionInfo: descriptionInfo{ + GraphQLName: typ.Name, + }, + Generator: g, + } + goType.OtherImplementation = otherType + g.typeMap[otherName] = otherType + } + return goType, nil default: return nil, errorf(fragment.Position, "invalid type for fragment: %v is a %v", diff --git a/generate/description.go b/generate/description.go index 13581771..723b7d08 100644 --- a/generate/description.go +++ b/generate/description.go @@ -60,9 +60,12 @@ func structDescription(typ *goStructType) string { } func interfaceDescription(typ *goInterfaceType) string { - goImplNames := make([]string, len(typ.Implementations)) - for i, impl := range typ.Implementations { - goImplNames[i] = impl.Reference() + goImplNames := make([]string, 0, len(typ.Implementations)+1) + for _, impl := range typ.Implementations { + goImplNames = append(goImplNames, impl.Reference()) + } + if typ.OtherImplementation != nil { + goImplNames = append(goImplNames, typ.OtherImplementation.Reference()) } implementationList := fmt.Sprintf( "\n\n%v is implemented by the following types:\n\t%v", diff --git a/generate/marshal_helper.go.tmpl b/generate/marshal_helper.go.tmpl index ec4e7239..e5540520 100644 --- a/generate/marshal_helper.go.tmpl +++ b/generate/marshal_helper.go.tmpl @@ -35,6 +35,12 @@ func __marshal{{.GoName}}(v *{{.GoName}}) ([]byte, error) { {{end -}} return json.Marshal(result) {{end -}} + {{if .OtherImplementation -}} + case *{{.OtherImplementation.GoName}}: + {{/* For the catch-all "other" type, marshal directly; the struct + already has __typename populated from unmarshaling. */}} + return {{ref "encoding/json.Marshal"}}(v) + {{end -}} case nil: return []byte("null"), nil default: diff --git a/generate/testdata/snapshots/TestGenerate-ComplexInlineFragments.graphql-ComplexInlineFragments.graphql.go b/generate/testdata/snapshots/TestGenerate-ComplexInlineFragments.graphql-ComplexInlineFragments.graphql.go index 2a3fb160..a3352737 100644 --- a/generate/testdata/snapshots/TestGenerate-ComplexInlineFragments.graphql-ComplexInlineFragments.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-ComplexInlineFragments.graphql-ComplexInlineFragments.graphql.go @@ -44,8 +44,8 @@ func (v *ComplexInlineFragmentsConflictingStuffArticleThumbnailStuffThumbnail) G // // ComplexInlineFragmentsConflictingStuffContent is implemented by the following types: // ComplexInlineFragmentsConflictingStuffArticle -// ComplexInlineFragmentsConflictingStuffTopic // ComplexInlineFragmentsConflictingStuffVideo +// ComplexInlineFragmentsConflictingStuffOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -57,10 +57,10 @@ type ComplexInlineFragmentsConflictingStuffContent interface { func (v *ComplexInlineFragmentsConflictingStuffArticle) implementsGraphQLInterfaceComplexInlineFragmentsConflictingStuffContent() { } -func (v *ComplexInlineFragmentsConflictingStuffTopic) implementsGraphQLInterfaceComplexInlineFragmentsConflictingStuffContent() { -} func (v *ComplexInlineFragmentsConflictingStuffVideo) implementsGraphQLInterfaceComplexInlineFragmentsConflictingStuffContent() { } +func (v *ComplexInlineFragmentsConflictingStuffOther) implementsGraphQLInterfaceComplexInlineFragmentsConflictingStuffContent() { +} func __unmarshalComplexInlineFragmentsConflictingStuffContent(b []byte, v *ComplexInlineFragmentsConflictingStuffContent) error { if string(b) == "null" { @@ -79,9 +79,6 @@ func __unmarshalComplexInlineFragmentsConflictingStuffContent(b []byte, v *Compl case "Article": *v = new(ComplexInlineFragmentsConflictingStuffArticle) return json.Unmarshal(b, *v) - case "Topic": - *v = new(ComplexInlineFragmentsConflictingStuffTopic) - return json.Unmarshal(b, *v) case "Video": *v = new(ComplexInlineFragmentsConflictingStuffVideo) return json.Unmarshal(b, *v) @@ -89,8 +86,8 @@ func __unmarshalComplexInlineFragmentsConflictingStuffContent(b []byte, v *Compl return fmt.Errorf( "response was missing Content.__typename") default: - return fmt.Errorf( - `unexpected concrete type for ComplexInlineFragmentsConflictingStuffContent: "%v"`, tn.TypeName) + *v = new(ComplexInlineFragmentsConflictingStuffOther) + return json.Unmarshal(b, *v) } } @@ -106,14 +103,6 @@ func __marshalComplexInlineFragmentsConflictingStuffContent(v *ComplexInlineFrag *ComplexInlineFragmentsConflictingStuffArticle }{typename, v} return json.Marshal(result) - case *ComplexInlineFragmentsConflictingStuffTopic: - typename = "Topic" - - result := struct { - TypeName string `json:"__typename"` - *ComplexInlineFragmentsConflictingStuffTopic - }{typename, v} - return json.Marshal(result) case *ComplexInlineFragmentsConflictingStuffVideo: typename = "Video" @@ -122,6 +111,9 @@ func __marshalComplexInlineFragmentsConflictingStuffContent(v *ComplexInlineFrag *ComplexInlineFragmentsConflictingStuffVideo }{typename, v} return json.Marshal(result) + case *ComplexInlineFragmentsConflictingStuffOther: + + return json.Marshal(v) case nil: return []byte("null"), nil default: @@ -130,13 +122,13 @@ func __marshalComplexInlineFragmentsConflictingStuffContent(v *ComplexInlineFrag } } -// ComplexInlineFragmentsConflictingStuffTopic includes the requested fields of the GraphQL type Topic. -type ComplexInlineFragmentsConflictingStuffTopic struct { +// ComplexInlineFragmentsConflictingStuffOther includes the requested fields of the GraphQL type Content. +type ComplexInlineFragmentsConflictingStuffOther struct { Typename string `json:"__typename"` } -// GetTypename returns ComplexInlineFragmentsConflictingStuffTopic.Typename, and is useful for accessing the field via an interface. -func (v *ComplexInlineFragmentsConflictingStuffTopic) GetTypename() string { return v.Typename } +// GetTypename returns ComplexInlineFragmentsConflictingStuffOther.Typename, and is useful for accessing the field via an interface. +func (v *ComplexInlineFragmentsConflictingStuffOther) GetTypename() string { return v.Typename } // ComplexInlineFragmentsConflictingStuffVideo includes the requested fields of the GraphQL type Video. type ComplexInlineFragmentsConflictingStuffVideo struct { @@ -166,20 +158,11 @@ func (v *ComplexInlineFragmentsConflictingStuffVideoThumbnail) GetTimestampSec() return v.TimestampSec } -// ComplexInlineFragmentsNestedStuffArticle includes the requested fields of the GraphQL type Article. -type ComplexInlineFragmentsNestedStuffArticle struct { - Typename string `json:"__typename"` -} - -// GetTypename returns ComplexInlineFragmentsNestedStuffArticle.Typename, and is useful for accessing the field via an interface. -func (v *ComplexInlineFragmentsNestedStuffArticle) GetTypename() string { return v.Typename } - // ComplexInlineFragmentsNestedStuffContent includes the requested fields of the GraphQL interface Content. // // ComplexInlineFragmentsNestedStuffContent is implemented by the following types: -// ComplexInlineFragmentsNestedStuffArticle // ComplexInlineFragmentsNestedStuffTopic -// ComplexInlineFragmentsNestedStuffVideo +// ComplexInlineFragmentsNestedStuffOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -189,11 +172,9 @@ type ComplexInlineFragmentsNestedStuffContent interface { GetTypename() string } -func (v *ComplexInlineFragmentsNestedStuffArticle) implementsGraphQLInterfaceComplexInlineFragmentsNestedStuffContent() { -} func (v *ComplexInlineFragmentsNestedStuffTopic) implementsGraphQLInterfaceComplexInlineFragmentsNestedStuffContent() { } -func (v *ComplexInlineFragmentsNestedStuffVideo) implementsGraphQLInterfaceComplexInlineFragmentsNestedStuffContent() { +func (v *ComplexInlineFragmentsNestedStuffOther) implementsGraphQLInterfaceComplexInlineFragmentsNestedStuffContent() { } func __unmarshalComplexInlineFragmentsNestedStuffContent(b []byte, v *ComplexInlineFragmentsNestedStuffContent) error { @@ -210,21 +191,15 @@ func __unmarshalComplexInlineFragmentsNestedStuffContent(b []byte, v *ComplexInl } switch tn.TypeName { - case "Article": - *v = new(ComplexInlineFragmentsNestedStuffArticle) - return json.Unmarshal(b, *v) case "Topic": *v = new(ComplexInlineFragmentsNestedStuffTopic) return json.Unmarshal(b, *v) - case "Video": - *v = new(ComplexInlineFragmentsNestedStuffVideo) - return json.Unmarshal(b, *v) case "": return fmt.Errorf( "response was missing Content.__typename") default: - return fmt.Errorf( - `unexpected concrete type for ComplexInlineFragmentsNestedStuffContent: "%v"`, tn.TypeName) + *v = new(ComplexInlineFragmentsNestedStuffOther) + return json.Unmarshal(b, *v) } } @@ -232,14 +207,6 @@ func __marshalComplexInlineFragmentsNestedStuffContent(v *ComplexInlineFragments var typename string switch v := (*v).(type) { - case *ComplexInlineFragmentsNestedStuffArticle: - typename = "Article" - - result := struct { - TypeName string `json:"__typename"` - *ComplexInlineFragmentsNestedStuffArticle - }{typename, v} - return json.Marshal(result) case *ComplexInlineFragmentsNestedStuffTopic: typename = "Topic" @@ -252,14 +219,9 @@ func __marshalComplexInlineFragmentsNestedStuffContent(v *ComplexInlineFragments *__premarshalComplexInlineFragmentsNestedStuffTopic }{typename, premarshaled} return json.Marshal(result) - case *ComplexInlineFragmentsNestedStuffVideo: - typename = "Video" + case *ComplexInlineFragmentsNestedStuffOther: - result := struct { - TypeName string `json:"__typename"` - *ComplexInlineFragmentsNestedStuffVideo - }{typename, v} - return json.Marshal(result) + return json.Marshal(v) case nil: return []byte("null"), nil default: @@ -268,6 +230,14 @@ func __marshalComplexInlineFragmentsNestedStuffContent(v *ComplexInlineFragments } } +// ComplexInlineFragmentsNestedStuffOther includes the requested fields of the GraphQL type Content. +type ComplexInlineFragmentsNestedStuffOther struct { + Typename string `json:"__typename"` +} + +// GetTypename returns ComplexInlineFragmentsNestedStuffOther.Typename, and is useful for accessing the field via an interface. +func (v *ComplexInlineFragmentsNestedStuffOther) GetTypename() string { return v.Typename } + // ComplexInlineFragmentsNestedStuffTopic includes the requested fields of the GraphQL type Topic. type ComplexInlineFragmentsNestedStuffTopic struct { Typename string `json:"__typename"` @@ -656,8 +626,7 @@ func (v *ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentTopic) GetPa // // ComplexInlineFragmentsNestedStuffTopicChildrenContent is implemented by the following types: // ComplexInlineFragmentsNestedStuffTopicChildrenArticle -// ComplexInlineFragmentsNestedStuffTopicChildrenTopic -// ComplexInlineFragmentsNestedStuffTopicChildrenVideo +// ComplexInlineFragmentsNestedStuffTopicChildrenOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -674,9 +643,7 @@ type ComplexInlineFragmentsNestedStuffTopicChildrenContent interface { func (v *ComplexInlineFragmentsNestedStuffTopicChildrenArticle) implementsGraphQLInterfaceComplexInlineFragmentsNestedStuffTopicChildrenContent() { } -func (v *ComplexInlineFragmentsNestedStuffTopicChildrenTopic) implementsGraphQLInterfaceComplexInlineFragmentsNestedStuffTopicChildrenContent() { -} -func (v *ComplexInlineFragmentsNestedStuffTopicChildrenVideo) implementsGraphQLInterfaceComplexInlineFragmentsNestedStuffTopicChildrenContent() { +func (v *ComplexInlineFragmentsNestedStuffTopicChildrenOther) implementsGraphQLInterfaceComplexInlineFragmentsNestedStuffTopicChildrenContent() { } func __unmarshalComplexInlineFragmentsNestedStuffTopicChildrenContent(b []byte, v *ComplexInlineFragmentsNestedStuffTopicChildrenContent) error { @@ -696,18 +663,12 @@ func __unmarshalComplexInlineFragmentsNestedStuffTopicChildrenContent(b []byte, case "Article": *v = new(ComplexInlineFragmentsNestedStuffTopicChildrenArticle) return json.Unmarshal(b, *v) - case "Topic": - *v = new(ComplexInlineFragmentsNestedStuffTopicChildrenTopic) - return json.Unmarshal(b, *v) - case "Video": - *v = new(ComplexInlineFragmentsNestedStuffTopicChildrenVideo) - return json.Unmarshal(b, *v) case "": return fmt.Errorf( "response was missing Content.__typename") default: - return fmt.Errorf( - `unexpected concrete type for ComplexInlineFragmentsNestedStuffTopicChildrenContent: "%v"`, tn.TypeName) + *v = new(ComplexInlineFragmentsNestedStuffTopicChildrenOther) + return json.Unmarshal(b, *v) } } @@ -723,22 +684,9 @@ func __marshalComplexInlineFragmentsNestedStuffTopicChildrenContent(v *ComplexIn *ComplexInlineFragmentsNestedStuffTopicChildrenArticle }{typename, v} return json.Marshal(result) - case *ComplexInlineFragmentsNestedStuffTopicChildrenTopic: - typename = "Topic" - - result := struct { - TypeName string `json:"__typename"` - *ComplexInlineFragmentsNestedStuffTopicChildrenTopic - }{typename, v} - return json.Marshal(result) - case *ComplexInlineFragmentsNestedStuffTopicChildrenVideo: - typename = "Video" + case *ComplexInlineFragmentsNestedStuffTopicChildrenOther: - result := struct { - TypeName string `json:"__typename"` - *ComplexInlineFragmentsNestedStuffTopicChildrenVideo - }{typename, v} - return json.Marshal(result) + return json.Marshal(v) case nil: return []byte("null"), nil default: @@ -747,39 +695,18 @@ func __marshalComplexInlineFragmentsNestedStuffTopicChildrenContent(v *ComplexIn } } -// ComplexInlineFragmentsNestedStuffTopicChildrenTopic includes the requested fields of the GraphQL type Topic. -type ComplexInlineFragmentsNestedStuffTopicChildrenTopic struct { +// ComplexInlineFragmentsNestedStuffTopicChildrenOther includes the requested fields of the GraphQL type Content. +type ComplexInlineFragmentsNestedStuffTopicChildrenOther struct { Typename string `json:"__typename"` // ID is the identifier of the content. Id testutil.ID `json:"id"` } -// GetTypename returns ComplexInlineFragmentsNestedStuffTopicChildrenTopic.Typename, and is useful for accessing the field via an interface. -func (v *ComplexInlineFragmentsNestedStuffTopicChildrenTopic) GetTypename() string { return v.Typename } +// GetTypename returns ComplexInlineFragmentsNestedStuffTopicChildrenOther.Typename, and is useful for accessing the field via an interface. +func (v *ComplexInlineFragmentsNestedStuffTopicChildrenOther) GetTypename() string { return v.Typename } -// GetId returns ComplexInlineFragmentsNestedStuffTopicChildrenTopic.Id, and is useful for accessing the field via an interface. -func (v *ComplexInlineFragmentsNestedStuffTopicChildrenTopic) GetId() testutil.ID { return v.Id } - -// ComplexInlineFragmentsNestedStuffTopicChildrenVideo includes the requested fields of the GraphQL type Video. -type ComplexInlineFragmentsNestedStuffTopicChildrenVideo struct { - Typename string `json:"__typename"` - // ID is the identifier of the content. - Id testutil.ID `json:"id"` -} - -// GetTypename returns ComplexInlineFragmentsNestedStuffTopicChildrenVideo.Typename, and is useful for accessing the field via an interface. -func (v *ComplexInlineFragmentsNestedStuffTopicChildrenVideo) GetTypename() string { return v.Typename } - -// GetId returns ComplexInlineFragmentsNestedStuffTopicChildrenVideo.Id, and is useful for accessing the field via an interface. -func (v *ComplexInlineFragmentsNestedStuffTopicChildrenVideo) GetId() testutil.ID { return v.Id } - -// ComplexInlineFragmentsNestedStuffVideo includes the requested fields of the GraphQL type Video. -type ComplexInlineFragmentsNestedStuffVideo struct { - Typename string `json:"__typename"` -} - -// GetTypename returns ComplexInlineFragmentsNestedStuffVideo.Typename, and is useful for accessing the field via an interface. -func (v *ComplexInlineFragmentsNestedStuffVideo) GetTypename() string { return v.Typename } +// GetId returns ComplexInlineFragmentsNestedStuffTopicChildrenOther.Id, and is useful for accessing the field via an interface. +func (v *ComplexInlineFragmentsNestedStuffTopicChildrenOther) GetId() testutil.ID { return v.Id } // ComplexInlineFragmentsRandomItemArticle includes the requested fields of the GraphQL type Article. type ComplexInlineFragmentsRandomItemArticle struct { @@ -1453,4 +1380,3 @@ func ComplexInlineFragments( return data_, err_ } - diff --git a/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go b/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go index 1ea1a5aa..9805e989 100644 --- a/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go @@ -1501,20 +1501,11 @@ func (v *MoreVideoFieldsParentTopic) __premarshalJSON() (*__premarshalMoreVideoF return &retval, nil } -// MoreVideoFieldsParentTopicChildrenArticle includes the requested fields of the GraphQL type Article. -type MoreVideoFieldsParentTopicChildrenArticle struct { - Typename *string `json:"__typename"` -} - -// GetTypename returns MoreVideoFieldsParentTopicChildrenArticle.Typename, and is useful for accessing the field via an interface. -func (v *MoreVideoFieldsParentTopicChildrenArticle) GetTypename() *string { return v.Typename } - // MoreVideoFieldsParentTopicChildrenContent includes the requested fields of the GraphQL interface Content. // // MoreVideoFieldsParentTopicChildrenContent is implemented by the following types: -// MoreVideoFieldsParentTopicChildrenArticle -// MoreVideoFieldsParentTopicChildrenTopic // MoreVideoFieldsParentTopicChildrenVideo +// MoreVideoFieldsParentTopicChildrenOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -1524,12 +1515,10 @@ type MoreVideoFieldsParentTopicChildrenContent interface { GetTypename() *string } -func (v *MoreVideoFieldsParentTopicChildrenArticle) implementsGraphQLInterfaceMoreVideoFieldsParentTopicChildrenContent() { -} -func (v *MoreVideoFieldsParentTopicChildrenTopic) implementsGraphQLInterfaceMoreVideoFieldsParentTopicChildrenContent() { -} func (v *MoreVideoFieldsParentTopicChildrenVideo) implementsGraphQLInterfaceMoreVideoFieldsParentTopicChildrenContent() { } +func (v *MoreVideoFieldsParentTopicChildrenOther) implementsGraphQLInterfaceMoreVideoFieldsParentTopicChildrenContent() { +} func __unmarshalMoreVideoFieldsParentTopicChildrenContent(b []byte, v *MoreVideoFieldsParentTopicChildrenContent) error { if string(b) == "null" { @@ -1545,12 +1534,6 @@ func __unmarshalMoreVideoFieldsParentTopicChildrenContent(b []byte, v *MoreVideo } switch tn.TypeName { - case "Article": - *v = new(MoreVideoFieldsParentTopicChildrenArticle) - return json.Unmarshal(b, *v) - case "Topic": - *v = new(MoreVideoFieldsParentTopicChildrenTopic) - return json.Unmarshal(b, *v) case "Video": *v = new(MoreVideoFieldsParentTopicChildrenVideo) return json.Unmarshal(b, *v) @@ -1558,8 +1541,8 @@ func __unmarshalMoreVideoFieldsParentTopicChildrenContent(b []byte, v *MoreVideo return fmt.Errorf( "response was missing Content.__typename") default: - return fmt.Errorf( - `unexpected concrete type for MoreVideoFieldsParentTopicChildrenContent: "%v"`, tn.TypeName) + *v = new(MoreVideoFieldsParentTopicChildrenOther) + return json.Unmarshal(b, *v) } } @@ -1567,22 +1550,6 @@ func __marshalMoreVideoFieldsParentTopicChildrenContent(v *MoreVideoFieldsParent var typename string switch v := (*v).(type) { - case *MoreVideoFieldsParentTopicChildrenArticle: - typename = "Article" - - result := struct { - TypeName string `json:"__typename"` - *MoreVideoFieldsParentTopicChildrenArticle - }{typename, v} - return json.Marshal(result) - case *MoreVideoFieldsParentTopicChildrenTopic: - typename = "Topic" - - result := struct { - TypeName string `json:"__typename"` - *MoreVideoFieldsParentTopicChildrenTopic - }{typename, v} - return json.Marshal(result) case *MoreVideoFieldsParentTopicChildrenVideo: typename = "Video" @@ -1595,6 +1562,9 @@ func __marshalMoreVideoFieldsParentTopicChildrenContent(v *MoreVideoFieldsParent *__premarshalMoreVideoFieldsParentTopicChildrenVideo }{typename, premarshaled} return json.Marshal(result) + case *MoreVideoFieldsParentTopicChildrenOther: + + return json.Marshal(v) case nil: return []byte("null"), nil default: @@ -1603,13 +1573,13 @@ func __marshalMoreVideoFieldsParentTopicChildrenContent(v *MoreVideoFieldsParent } } -// MoreVideoFieldsParentTopicChildrenTopic includes the requested fields of the GraphQL type Topic. -type MoreVideoFieldsParentTopicChildrenTopic struct { +// MoreVideoFieldsParentTopicChildrenOther includes the requested fields of the GraphQL type Content. +type MoreVideoFieldsParentTopicChildrenOther struct { Typename *string `json:"__typename"` } -// GetTypename returns MoreVideoFieldsParentTopicChildrenTopic.Typename, and is useful for accessing the field via an interface. -func (v *MoreVideoFieldsParentTopicChildrenTopic) GetTypename() *string { return v.Typename } +// GetTypename returns MoreVideoFieldsParentTopicChildrenOther.Typename, and is useful for accessing the field via an interface. +func (v *MoreVideoFieldsParentTopicChildrenOther) GetTypename() *string { return v.Typename } // MoreVideoFieldsParentTopicChildrenVideo includes the requested fields of the GraphQL type Video. type MoreVideoFieldsParentTopicChildrenVideo struct { @@ -2670,4 +2640,3 @@ func ComplexNamedFragmentsWithInlineUnion( return data_, err_ } - diff --git a/generate/testdata/snapshots/TestGenerate-SimpleInlineFragment.graphql-SimpleInlineFragment.graphql.go b/generate/testdata/snapshots/TestGenerate-SimpleInlineFragment.graphql-SimpleInlineFragment.graphql.go index ba391639..e572c06a 100644 --- a/generate/testdata/snapshots/TestGenerate-SimpleInlineFragment.graphql-SimpleInlineFragment.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-SimpleInlineFragment.graphql-SimpleInlineFragment.graphql.go @@ -35,8 +35,8 @@ func (v *SimpleInlineFragmentRandomItemArticle) GetText() string { return v.Text // // SimpleInlineFragmentRandomItemContent is implemented by the following types: // SimpleInlineFragmentRandomItemArticle -// SimpleInlineFragmentRandomItemTopic // SimpleInlineFragmentRandomItemVideo +// SimpleInlineFragmentRandomItemOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -55,10 +55,10 @@ type SimpleInlineFragmentRandomItemContent interface { func (v *SimpleInlineFragmentRandomItemArticle) implementsGraphQLInterfaceSimpleInlineFragmentRandomItemContent() { } -func (v *SimpleInlineFragmentRandomItemTopic) implementsGraphQLInterfaceSimpleInlineFragmentRandomItemContent() { -} func (v *SimpleInlineFragmentRandomItemVideo) implementsGraphQLInterfaceSimpleInlineFragmentRandomItemContent() { } +func (v *SimpleInlineFragmentRandomItemOther) implementsGraphQLInterfaceSimpleInlineFragmentRandomItemContent() { +} func __unmarshalSimpleInlineFragmentRandomItemContent(b []byte, v *SimpleInlineFragmentRandomItemContent) error { if string(b) == "null" { @@ -77,9 +77,6 @@ func __unmarshalSimpleInlineFragmentRandomItemContent(b []byte, v *SimpleInlineF case "Article": *v = new(SimpleInlineFragmentRandomItemArticle) return json.Unmarshal(b, *v) - case "Topic": - *v = new(SimpleInlineFragmentRandomItemTopic) - return json.Unmarshal(b, *v) case "Video": *v = new(SimpleInlineFragmentRandomItemVideo) return json.Unmarshal(b, *v) @@ -87,8 +84,8 @@ func __unmarshalSimpleInlineFragmentRandomItemContent(b []byte, v *SimpleInlineF return fmt.Errorf( "response was missing Content.__typename") default: - return fmt.Errorf( - `unexpected concrete type for SimpleInlineFragmentRandomItemContent: "%v"`, tn.TypeName) + *v = new(SimpleInlineFragmentRandomItemOther) + return json.Unmarshal(b, *v) } } @@ -104,14 +101,6 @@ func __marshalSimpleInlineFragmentRandomItemContent(v *SimpleInlineFragmentRando *SimpleInlineFragmentRandomItemArticle }{typename, v} return json.Marshal(result) - case *SimpleInlineFragmentRandomItemTopic: - typename = "Topic" - - result := struct { - TypeName string `json:"__typename"` - *SimpleInlineFragmentRandomItemTopic - }{typename, v} - return json.Marshal(result) case *SimpleInlineFragmentRandomItemVideo: typename = "Video" @@ -120,6 +109,9 @@ func __marshalSimpleInlineFragmentRandomItemContent(v *SimpleInlineFragmentRando *SimpleInlineFragmentRandomItemVideo }{typename, v} return json.Marshal(result) + case *SimpleInlineFragmentRandomItemOther: + + return json.Marshal(v) case nil: return []byte("null"), nil default: @@ -128,22 +120,22 @@ func __marshalSimpleInlineFragmentRandomItemContent(v *SimpleInlineFragmentRando } } -// SimpleInlineFragmentRandomItemTopic includes the requested fields of the GraphQL type Topic. -type SimpleInlineFragmentRandomItemTopic struct { +// SimpleInlineFragmentRandomItemOther includes the requested fields of the GraphQL type Content. +type SimpleInlineFragmentRandomItemOther struct { Typename string `json:"__typename"` // ID is the identifier of the content. Id testutil.ID `json:"id"` Name string `json:"name"` } -// GetTypename returns SimpleInlineFragmentRandomItemTopic.Typename, and is useful for accessing the field via an interface. -func (v *SimpleInlineFragmentRandomItemTopic) GetTypename() string { return v.Typename } +// GetTypename returns SimpleInlineFragmentRandomItemOther.Typename, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemOther) GetTypename() string { return v.Typename } -// GetId returns SimpleInlineFragmentRandomItemTopic.Id, and is useful for accessing the field via an interface. -func (v *SimpleInlineFragmentRandomItemTopic) GetId() testutil.ID { return v.Id } +// GetId returns SimpleInlineFragmentRandomItemOther.Id, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemOther) GetId() testutil.ID { return v.Id } -// GetName returns SimpleInlineFragmentRandomItemTopic.Name, and is useful for accessing the field via an interface. -func (v *SimpleInlineFragmentRandomItemTopic) GetName() string { return v.Name } +// GetName returns SimpleInlineFragmentRandomItemOther.Name, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemOther) GetName() string { return v.Name } // SimpleInlineFragmentRandomItemVideo includes the requested fields of the GraphQL type Video. type SimpleInlineFragmentRandomItemVideo struct { @@ -275,4 +267,3 @@ func SimpleInlineFragment( return data_, err_ } - diff --git a/generate/testdata/snapshots/TestGenerate-SimpleNamedFragment.graphql-SimpleNamedFragment.graphql.go b/generate/testdata/snapshots/TestGenerate-SimpleNamedFragment.graphql-SimpleNamedFragment.graphql.go index c9fd12a2..3a2a20f3 100644 --- a/generate/testdata/snapshots/TestGenerate-SimpleNamedFragment.graphql-SimpleNamedFragment.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-SimpleNamedFragment.graphql-SimpleNamedFragment.graphql.go @@ -10,29 +10,11 @@ import ( "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"` -} - -// GetTypename returns SimpleNamedFragmentRandomItemArticle.Typename, and is useful for accessing the field via an interface. -func (v *SimpleNamedFragmentRandomItemArticle) GetTypename() string { return v.Typename } - -// GetId returns SimpleNamedFragmentRandomItemArticle.Id, and is useful for accessing the field via an interface. -func (v *SimpleNamedFragmentRandomItemArticle) GetId() testutil.ID { return v.Id } - -// GetName returns SimpleNamedFragmentRandomItemArticle.Name, and is useful for accessing the field via an interface. -func (v *SimpleNamedFragmentRandomItemArticle) GetName() string { return v.Name } - // SimpleNamedFragmentRandomItemContent includes the requested fields of the GraphQL interface Content. // // SimpleNamedFragmentRandomItemContent is implemented by the following types: -// SimpleNamedFragmentRandomItemArticle -// SimpleNamedFragmentRandomItemTopic // SimpleNamedFragmentRandomItemVideo +// SimpleNamedFragmentRandomItemOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -49,12 +31,10 @@ type SimpleNamedFragmentRandomItemContent interface { GetName() string } -func (v *SimpleNamedFragmentRandomItemArticle) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() { -} -func (v *SimpleNamedFragmentRandomItemTopic) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() { -} func (v *SimpleNamedFragmentRandomItemVideo) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() { } +func (v *SimpleNamedFragmentRandomItemOther) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() { +} func __unmarshalSimpleNamedFragmentRandomItemContent(b []byte, v *SimpleNamedFragmentRandomItemContent) error { if string(b) == "null" { @@ -70,12 +50,6 @@ func __unmarshalSimpleNamedFragmentRandomItemContent(b []byte, v *SimpleNamedFra } switch tn.TypeName { - case "Article": - *v = new(SimpleNamedFragmentRandomItemArticle) - return json.Unmarshal(b, *v) - case "Topic": - *v = new(SimpleNamedFragmentRandomItemTopic) - return json.Unmarshal(b, *v) case "Video": *v = new(SimpleNamedFragmentRandomItemVideo) return json.Unmarshal(b, *v) @@ -83,8 +57,8 @@ func __unmarshalSimpleNamedFragmentRandomItemContent(b []byte, v *SimpleNamedFra return fmt.Errorf( "response was missing Content.__typename") default: - return fmt.Errorf( - `unexpected concrete type for SimpleNamedFragmentRandomItemContent: "%v"`, tn.TypeName) + *v = new(SimpleNamedFragmentRandomItemOther) + return json.Unmarshal(b, *v) } } @@ -92,22 +66,6 @@ func __marshalSimpleNamedFragmentRandomItemContent(v *SimpleNamedFragmentRandomI var typename string switch v := (*v).(type) { - case *SimpleNamedFragmentRandomItemArticle: - typename = "Article" - - result := struct { - TypeName string `json:"__typename"` - *SimpleNamedFragmentRandomItemArticle - }{typename, v} - return json.Marshal(result) - case *SimpleNamedFragmentRandomItemTopic: - typename = "Topic" - - result := struct { - TypeName string `json:"__typename"` - *SimpleNamedFragmentRandomItemTopic - }{typename, v} - return json.Marshal(result) case *SimpleNamedFragmentRandomItemVideo: typename = "Video" @@ -120,6 +78,9 @@ func __marshalSimpleNamedFragmentRandomItemContent(v *SimpleNamedFragmentRandomI *__premarshalSimpleNamedFragmentRandomItemVideo }{typename, premarshaled} return json.Marshal(result) + case *SimpleNamedFragmentRandomItemOther: + + return json.Marshal(v) case nil: return []byte("null"), nil default: @@ -128,22 +89,22 @@ func __marshalSimpleNamedFragmentRandomItemContent(v *SimpleNamedFragmentRandomI } } -// SimpleNamedFragmentRandomItemTopic includes the requested fields of the GraphQL type Topic. -type SimpleNamedFragmentRandomItemTopic struct { +// SimpleNamedFragmentRandomItemOther includes the requested fields of the GraphQL type Content. +type SimpleNamedFragmentRandomItemOther struct { Typename string `json:"__typename"` // ID is the identifier of the content. Id testutil.ID `json:"id"` Name string `json:"name"` } -// GetTypename returns SimpleNamedFragmentRandomItemTopic.Typename, and is useful for accessing the field via an interface. -func (v *SimpleNamedFragmentRandomItemTopic) GetTypename() string { return v.Typename } +// GetTypename returns SimpleNamedFragmentRandomItemOther.Typename, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemOther) GetTypename() string { return v.Typename } -// GetId returns SimpleNamedFragmentRandomItemTopic.Id, and is useful for accessing the field via an interface. -func (v *SimpleNamedFragmentRandomItemTopic) GetId() testutil.ID { return v.Id } +// GetId returns SimpleNamedFragmentRandomItemOther.Id, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemOther) GetId() testutil.ID { return v.Id } -// GetName returns SimpleNamedFragmentRandomItemTopic.Name, and is useful for accessing the field via an interface. -func (v *SimpleNamedFragmentRandomItemTopic) GetName() string { return v.Name } +// GetName returns SimpleNamedFragmentRandomItemOther.Name, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemOther) GetName() string { return v.Name } // SimpleNamedFragmentRandomItemVideo includes the requested fields of the GraphQL type Video. type SimpleNamedFragmentRandomItemVideo struct { @@ -233,19 +194,11 @@ func (v *SimpleNamedFragmentRandomItemVideo) __premarshalJSON() (*__premarshalSi return &retval, nil } -// SimpleNamedFragmentRandomLeafArticle includes the requested fields of the GraphQL type Article. -type SimpleNamedFragmentRandomLeafArticle struct { - Typename string `json:"__typename"` -} - -// GetTypename returns SimpleNamedFragmentRandomLeafArticle.Typename, and is useful for accessing the field via an interface. -func (v *SimpleNamedFragmentRandomLeafArticle) GetTypename() string { return v.Typename } - // SimpleNamedFragmentRandomLeafLeafContent includes the requested fields of the GraphQL interface LeafContent. // // SimpleNamedFragmentRandomLeafLeafContent is implemented by the following types: -// SimpleNamedFragmentRandomLeafArticle // SimpleNamedFragmentRandomLeafVideo +// SimpleNamedFragmentRandomLeafOther // The GraphQL type's documentation follows. // // LeafContent represents content items that can't have child-nodes. @@ -255,10 +208,10 @@ type SimpleNamedFragmentRandomLeafLeafContent interface { GetTypename() string } -func (v *SimpleNamedFragmentRandomLeafArticle) implementsGraphQLInterfaceSimpleNamedFragmentRandomLeafLeafContent() { -} func (v *SimpleNamedFragmentRandomLeafVideo) implementsGraphQLInterfaceSimpleNamedFragmentRandomLeafLeafContent() { } +func (v *SimpleNamedFragmentRandomLeafOther) implementsGraphQLInterfaceSimpleNamedFragmentRandomLeafLeafContent() { +} func __unmarshalSimpleNamedFragmentRandomLeafLeafContent(b []byte, v *SimpleNamedFragmentRandomLeafLeafContent) error { if string(b) == "null" { @@ -274,9 +227,6 @@ func __unmarshalSimpleNamedFragmentRandomLeafLeafContent(b []byte, v *SimpleName } switch tn.TypeName { - case "Article": - *v = new(SimpleNamedFragmentRandomLeafArticle) - return json.Unmarshal(b, *v) case "Video": *v = new(SimpleNamedFragmentRandomLeafVideo) return json.Unmarshal(b, *v) @@ -284,8 +234,8 @@ func __unmarshalSimpleNamedFragmentRandomLeafLeafContent(b []byte, v *SimpleName return fmt.Errorf( "response was missing LeafContent.__typename") default: - return fmt.Errorf( - `unexpected concrete type for SimpleNamedFragmentRandomLeafLeafContent: "%v"`, tn.TypeName) + *v = new(SimpleNamedFragmentRandomLeafOther) + return json.Unmarshal(b, *v) } } @@ -293,14 +243,6 @@ func __marshalSimpleNamedFragmentRandomLeafLeafContent(v *SimpleNamedFragmentRan var typename string switch v := (*v).(type) { - case *SimpleNamedFragmentRandomLeafArticle: - typename = "Article" - - result := struct { - TypeName string `json:"__typename"` - *SimpleNamedFragmentRandomLeafArticle - }{typename, v} - return json.Marshal(result) case *SimpleNamedFragmentRandomLeafVideo: typename = "Video" @@ -313,6 +255,9 @@ func __marshalSimpleNamedFragmentRandomLeafLeafContent(v *SimpleNamedFragmentRan *__premarshalSimpleNamedFragmentRandomLeafVideo }{typename, premarshaled} return json.Marshal(result) + case *SimpleNamedFragmentRandomLeafOther: + + return json.Marshal(v) case nil: return []byte("null"), nil default: @@ -321,6 +266,14 @@ func __marshalSimpleNamedFragmentRandomLeafLeafContent(v *SimpleNamedFragmentRan } } +// SimpleNamedFragmentRandomLeafOther includes the requested fields of the GraphQL type LeafContent. +type SimpleNamedFragmentRandomLeafOther struct { + Typename string `json:"__typename"` +} + +// GetTypename returns SimpleNamedFragmentRandomLeafOther.Typename, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomLeafOther) GetTypename() string { return v.Typename } + // SimpleNamedFragmentRandomLeafVideo includes the requested fields of the GraphQL type Video. type SimpleNamedFragmentRandomLeafVideo struct { Typename string `json:"__typename"` @@ -590,4 +543,3 @@ func SimpleNamedFragment( return data_, err_ } - diff --git a/generate/testdata/snapshots/TestGenerate-StructOption.graphql-StructOption.graphql.go b/generate/testdata/snapshots/TestGenerate-StructOption.graphql-StructOption.graphql.go index 7c01e243..42f93d9f 100644 --- a/generate/testdata/snapshots/TestGenerate-StructOption.graphql-StructOption.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-StructOption.graphql-StructOption.graphql.go @@ -204,29 +204,11 @@ func (v *StructOptionRootTopicChildrenContentParentTopicChildrenContent) GetId() return v.Id } -// StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle includes the requested fields of the GraphQL type Article. -type StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle struct { - Typename string `json:"__typename"` - // ID is the identifier of the content. - Id testutil.ID `json:"id"` -} - -// GetTypename returns StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle.Typename, and is useful for accessing the field via an interface. -func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle) GetTypename() string { - return v.Typename -} - -// GetId returns StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle.Id, and is useful for accessing the field via an interface. -func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle) GetId() testutil.ID { - return v.Id -} - // StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent includes the requested fields of the GraphQL interface Content. // // StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent is implemented by the following types: -// StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle -// StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic // StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenVideo +// StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -241,12 +223,10 @@ type StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent int GetId() testutil.ID } -func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle) implementsGraphQLInterfaceStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent() { -} -func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic) implementsGraphQLInterfaceStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent() { -} func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenVideo) implementsGraphQLInterfaceStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent() { } +func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther) implementsGraphQLInterfaceStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent() { +} func __unmarshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent(b []byte, v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent) error { if string(b) == "null" { @@ -262,12 +242,6 @@ func __unmarshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildren } switch tn.TypeName { - case "Article": - *v = new(StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle) - return json.Unmarshal(b, *v) - case "Topic": - *v = new(StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic) - return json.Unmarshal(b, *v) case "Video": *v = new(StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenVideo) return json.Unmarshal(b, *v) @@ -275,8 +249,8 @@ func __unmarshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildren return fmt.Errorf( "response was missing Content.__typename") default: - return fmt.Errorf( - `unexpected concrete type for StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent: "%v"`, tn.TypeName) + *v = new(StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther) + return json.Unmarshal(b, *v) } } @@ -284,22 +258,6 @@ func __marshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenCo var typename string switch v := (*v).(type) { - case *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle: - typename = "Article" - - result := struct { - TypeName string `json:"__typename"` - *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle - }{typename, v} - return json.Marshal(result) - case *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic: - typename = "Topic" - - result := struct { - TypeName string `json:"__typename"` - *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic - }{typename, v} - return json.Marshal(result) case *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenVideo: typename = "Video" @@ -312,6 +270,9 @@ func __marshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenCo *__premarshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenVideo }{typename, premarshaled} return json.Marshal(result) + case *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther: + + return json.Marshal(v) case nil: return []byte("null"), nil default: @@ -320,20 +281,20 @@ func __marshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenCo } } -// StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic includes the requested fields of the GraphQL type Topic. -type StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic struct { +// StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther includes the requested fields of the GraphQL type Content. +type StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther struct { Typename string `json:"__typename"` // ID is the identifier of the content. Id testutil.ID `json:"id"` } -// GetTypename returns StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic.Typename, and is useful for accessing the field via an interface. -func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic) GetTypename() string { +// GetTypename returns StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther.Typename, and is useful for accessing the field via an interface. +func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther) GetTypename() string { return v.Typename } -// GetId returns StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic.Id, and is useful for accessing the field via an interface. -func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic) GetId() testutil.ID { +// GetId returns StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther.Id, and is useful for accessing the field via an interface. +func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther) GetId() testutil.ID { return v.Id } @@ -479,4 +440,3 @@ func StructOption( return data_, err_ } - diff --git a/generate/types.go b/generate/types.go index 5a31b07c..89173c8a 100644 --- a/generate/types.go +++ b/generate/types.go @@ -486,7 +486,12 @@ type goInterfaceType struct { // we'll generate getter methods for each. SharedFields []*goStructField Implementations []*goStructType - Selection ast.SelectionSet + // OtherImplementation is a catch-all struct for concrete types not + // explicitly referenced by any fragment in the query. It contains + // only the shared fields. If nil, all implementations are explicitly + // listed in Implementations. + OtherImplementation *goStructType + Selection ast.SelectionSet descriptionInfo } @@ -530,6 +535,10 @@ func (typ *goInterfaceType) WriteDefinition(w io.Writer, g *generator) error { fmt.Fprintf(w, "func (v *%s) %s() {}\n", impl.Reference(), implementsMethodName) } + if typ.OtherImplementation != nil { + fmt.Fprintf(w, "func (v *%s) %s() {}\n", + typ.OtherImplementation.Reference(), implementsMethodName) + } // Finally, write the marshal- and unmarshal-helpers, which // will be called by struct fields referencing this type (see diff --git a/generate/unmarshal_helper.go.tmpl b/generate/unmarshal_helper.go.tmpl index bab2710e..16eabe97 100644 --- a/generate/unmarshal_helper.go.tmpl +++ b/generate/unmarshal_helper.go.tmpl @@ -30,7 +30,12 @@ func __unmarshal{{.GoName}}(b []byte, v *{{.GoName}}) error { return {{ref "fmt.Errorf"}}( "response was missing {{.GraphQLName}}.__typename") default: + {{if .OtherImplementation -}} + *v = new({{.OtherImplementation.GoName}}) + return {{ref "encoding/json.Unmarshal"}}(b, *v) + {{else -}} return {{ref "fmt.Errorf"}}( `unexpected concrete type for {{.GoName}}: "%v"`, tn.TypeName) + {{end -}} } } diff --git a/internal/integration/generated.go b/internal/integration/generated.go index fd034833..87e5c36b 100644 --- a/internal/integration/generated.go +++ b/internal/integration/generated.go @@ -106,23 +106,11 @@ type AnimalFieldsHairBeingsHair struct { // GetHasHair returns AnimalFieldsHairBeingsHair.HasHair, and is useful for accessing the field via an interface. func (v *AnimalFieldsHairBeingsHair) GetHasHair() bool { return v.HasHair } -// AnimalFieldsOwnerAnimal includes the requested fields of the GraphQL type Animal. -type AnimalFieldsOwnerAnimal struct { - Typename string `json:"__typename"` - Id string `json:"id"` -} - -// GetTypename returns AnimalFieldsOwnerAnimal.Typename, and is useful for accessing the field via an interface. -func (v *AnimalFieldsOwnerAnimal) GetTypename() string { return v.Typename } - -// GetId returns AnimalFieldsOwnerAnimal.Id, and is useful for accessing the field via an interface. -func (v *AnimalFieldsOwnerAnimal) GetId() string { return v.Id } - // AnimalFieldsOwnerBeing includes the requested fields of the GraphQL interface Being. // // AnimalFieldsOwnerBeing is implemented by the following types: -// AnimalFieldsOwnerAnimal // AnimalFieldsOwnerUser +// AnimalFieldsOwnerOther type AnimalFieldsOwnerBeing interface { implementsGraphQLInterfaceAnimalFieldsOwnerBeing() // GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values). @@ -131,8 +119,8 @@ type AnimalFieldsOwnerBeing interface { GetId() string } -func (v *AnimalFieldsOwnerAnimal) implementsGraphQLInterfaceAnimalFieldsOwnerBeing() {} -func (v *AnimalFieldsOwnerUser) implementsGraphQLInterfaceAnimalFieldsOwnerBeing() {} +func (v *AnimalFieldsOwnerUser) implementsGraphQLInterfaceAnimalFieldsOwnerBeing() {} +func (v *AnimalFieldsOwnerOther) implementsGraphQLInterfaceAnimalFieldsOwnerBeing() {} func __unmarshalAnimalFieldsOwnerBeing(b []byte, v *AnimalFieldsOwnerBeing) error { if string(b) == "null" { @@ -148,9 +136,6 @@ func __unmarshalAnimalFieldsOwnerBeing(b []byte, v *AnimalFieldsOwnerBeing) erro } switch tn.TypeName { - case "Animal": - *v = new(AnimalFieldsOwnerAnimal) - return json.Unmarshal(b, *v) case "User": *v = new(AnimalFieldsOwnerUser) return json.Unmarshal(b, *v) @@ -158,8 +143,8 @@ func __unmarshalAnimalFieldsOwnerBeing(b []byte, v *AnimalFieldsOwnerBeing) erro return fmt.Errorf( "response was missing Being.__typename") default: - return fmt.Errorf( - `unexpected concrete type for AnimalFieldsOwnerBeing: "%v"`, tn.TypeName) + *v = new(AnimalFieldsOwnerOther) + return json.Unmarshal(b, *v) } } @@ -167,14 +152,6 @@ func __marshalAnimalFieldsOwnerBeing(v *AnimalFieldsOwnerBeing) ([]byte, error) var typename string switch v := (*v).(type) { - case *AnimalFieldsOwnerAnimal: - typename = "Animal" - - result := struct { - TypeName string `json:"__typename"` - *AnimalFieldsOwnerAnimal - }{typename, v} - return json.Marshal(result) case *AnimalFieldsOwnerUser: typename = "User" @@ -187,6 +164,9 @@ func __marshalAnimalFieldsOwnerBeing(v *AnimalFieldsOwnerBeing) ([]byte, error) *__premarshalAnimalFieldsOwnerUser }{typename, premarshaled} return json.Marshal(result) + case *AnimalFieldsOwnerOther: + + return json.Marshal(v) case nil: return []byte("null"), nil default: @@ -195,6 +175,18 @@ func __marshalAnimalFieldsOwnerBeing(v *AnimalFieldsOwnerBeing) ([]byte, error) } } +// AnimalFieldsOwnerOther includes the requested fields of the GraphQL type Being. +type AnimalFieldsOwnerOther struct { + Typename string `json:"__typename"` + Id string `json:"id"` +} + +// GetTypename returns AnimalFieldsOwnerOther.Typename, and is useful for accessing the field via an interface. +func (v *AnimalFieldsOwnerOther) GetTypename() string { return v.Typename } + +// GetId returns AnimalFieldsOwnerOther.Id, and is useful for accessing the field via an interface. +func (v *AnimalFieldsOwnerOther) GetId() string { return v.Id } + // AnimalFieldsOwnerUser includes the requested fields of the GraphQL type User. type AnimalFieldsOwnerUser struct { Typename string `json:"__typename"` @@ -288,8 +280,8 @@ func (v *FriendsFields) GetName() string { return v.Name } // InnerBeingFields includes the GraphQL fields of Being requested by the fragment InnerBeingFields. // // InnerBeingFields is implemented by the following types: -// InnerBeingFieldsAnimal // InnerBeingFieldsUser +// InnerBeingFieldsOther type InnerBeingFields interface { implementsGraphQLInterfaceInnerBeingFields() // GetId returns the interface-field "id" from its implementation. @@ -298,8 +290,8 @@ type InnerBeingFields interface { GetName() string } -func (v *InnerBeingFieldsAnimal) implementsGraphQLInterfaceInnerBeingFields() {} -func (v *InnerBeingFieldsUser) implementsGraphQLInterfaceInnerBeingFields() {} +func (v *InnerBeingFieldsUser) implementsGraphQLInterfaceInnerBeingFields() {} +func (v *InnerBeingFieldsOther) implementsGraphQLInterfaceInnerBeingFields() {} func __unmarshalInnerBeingFields(b []byte, v *InnerBeingFields) error { if string(b) == "null" { @@ -315,9 +307,6 @@ func __unmarshalInnerBeingFields(b []byte, v *InnerBeingFields) error { } switch tn.TypeName { - case "Animal": - *v = new(InnerBeingFieldsAnimal) - return json.Unmarshal(b, *v) case "User": *v = new(InnerBeingFieldsUser) return json.Unmarshal(b, *v) @@ -325,8 +314,8 @@ func __unmarshalInnerBeingFields(b []byte, v *InnerBeingFields) error { return fmt.Errorf( "response was missing Being.__typename") default: - return fmt.Errorf( - `unexpected concrete type for InnerBeingFields: "%v"`, tn.TypeName) + *v = new(InnerBeingFieldsOther) + return json.Unmarshal(b, *v) } } @@ -334,14 +323,6 @@ func __marshalInnerBeingFields(v *InnerBeingFields) ([]byte, error) { var typename string switch v := (*v).(type) { - case *InnerBeingFieldsAnimal: - typename = "Animal" - - result := struct { - TypeName string `json:"__typename"` - *InnerBeingFieldsAnimal - }{typename, v} - return json.Marshal(result) case *InnerBeingFieldsUser: typename = "User" @@ -350,6 +331,9 @@ func __marshalInnerBeingFields(v *InnerBeingFields) ([]byte, error) { *InnerBeingFieldsUser }{typename, v} return json.Marshal(result) + case *InnerBeingFieldsOther: + + return json.Marshal(v) case nil: return []byte("null"), nil default: @@ -358,17 +342,17 @@ func __marshalInnerBeingFields(v *InnerBeingFields) ([]byte, error) { } } -// InnerBeingFields includes the GraphQL fields of Animal requested by the fragment InnerBeingFields. -type InnerBeingFieldsAnimal struct { +// InnerBeingFieldsOther includes the requested fields of the GraphQL type Being. +type InnerBeingFieldsOther struct { Id string `json:"id"` Name string `json:"name"` } -// GetId returns InnerBeingFieldsAnimal.Id, and is useful for accessing the field via an interface. -func (v *InnerBeingFieldsAnimal) GetId() string { return v.Id } +// GetId returns InnerBeingFieldsOther.Id, and is useful for accessing the field via an interface. +func (v *InnerBeingFieldsOther) GetId() string { return v.Id } -// GetName returns InnerBeingFieldsAnimal.Name, and is useful for accessing the field via an interface. -func (v *InnerBeingFieldsAnimal) GetName() string { return v.Name } +// GetName returns InnerBeingFieldsOther.Name, and is useful for accessing the field via an interface. +func (v *InnerBeingFieldsOther) GetName() string { return v.Name } // InnerBeingFields includes the GraphQL fields of User requested by the fragment InnerBeingFields. type InnerBeingFieldsUser struct { From 9fc4baab7a43b6c2ff3fd424baa0906e94d45398 Mon Sep 17 00:00:00 2001 From: Will Roden Date: Mon, 30 Mar 2026 16:57:21 -0500 Subject: [PATCH 02/12] wire up omit_unreferenced_implementations config option --- docs/genqlient.yaml | 15 + generate/config.go | 5 +- generate/convert.go | 19 +- generate/generate_test.go | 8 + ....graphql-ComplexInlineFragments.graphql.go | 150 +++-- ...s.graphql-ComplexNamedFragments.graphql.go | 55 +- ...nt.graphql-SimpleInlineFragment.graphql.go | 41 +- ...ent.graphql-SimpleNamedFragment.graphql.go | 112 +++- ...ructOption.graphql-StructOption.graphql.go | 68 ++- ...ment.graphql-testdata-queries-generated.go | 271 +++++++++ ...ment.graphql-testdata-queries-generated.go | 547 ++++++++++++++++++ .../snapshots/TestValidConfigs-Empty.yml | 1 + .../snapshots/TestValidConfigs-Lists.yaml | 1 + .../snapshots/TestValidConfigs-Strings.yaml | 1 + internal/integration/generated.go | 84 +-- 15 files changed, 1224 insertions(+), 154 deletions(-) create mode 100644 generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleInlineFragment.graphql-testdata-queries-generated.go create mode 100644 generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleNamedFragment.graphql-testdata-queries-generated.go diff --git a/docs/genqlient.yaml b/docs/genqlient.yaml index f4f01bb6..95974e14 100644 --- a/docs/genqlient.yaml +++ b/docs/genqlient.yaml @@ -104,6 +104,21 @@ use_struct_references: boolean # Defaults to false. use_extensions: boolean +# If set, when a query uses fragments on an interface or union type, +# genqlient will only generate full Go struct types for the concrete +# types explicitly referenced in those fragments. All other possible +# implementation types are collapsed into a single catch-all "Other" +# struct that contains only the shared fields. Unknown __typename +# values are unmarshaled into this Other type instead of returning an +# error. +# +# When false (the default), genqlient generates a separate Go struct +# for every possible implementation type, and returns an error if the +# server sends an unrecognized __typename. +# +# Defaults to false. +omit_unreferenced_implementations: false + # Customize how models are generated for optional fields. This can currently # be set to one of the following values: # - value (default): optional fields are generated as values, the same as diff --git a/generate/config.go b/generate/config.go index 9b37421c..7ad53208 100644 --- a/generate/config.go +++ b/generate/config.go @@ -34,8 +34,9 @@ type Config struct { Casing Casing `yaml:"casing"` Optional string `yaml:"optional"` OptionalGenericType string `yaml:"optional_generic_type"` - StructReferences bool `yaml:"use_struct_references"` - Extensions bool `yaml:"use_extensions"` + StructReferences bool `yaml:"use_struct_references"` + Extensions bool `yaml:"use_extensions"` + OmitUnreferencedImplementations bool `yaml:"omit_unreferenced_implementations"` // The directory of the config-file (relative to which all the other paths // are resolved). Set by ValidateAndFillDefaults. diff --git a/generate/convert.go b/generate/convert.go index c6b100d7..a8d219dd 100644 --- a/generate/convert.go +++ b/generate/convert.go @@ -523,11 +523,15 @@ func (g *generator) convertDefinition( // Make sure we generate stable output by sorting the types by name when we get them sort.Slice(implementationTypes, func(i, j int) bool { return implementationTypes[i].Name < implementationTypes[j].Name }) - // Determine which concrete types are explicitly referenced by - // fragments in the selection set. If any are, we only generate - // full implementation structs for those types, and a single - // catch-all "other" struct for everything else. - referencedTypes := g.collectReferencedTypes(selectionSet) + // If omit_unreferenced_implementations is enabled, determine which + // concrete types are explicitly referenced by fragments in the + // selection set. If any are, we only generate full implementation + // structs for those types, and a single catch-all "other" struct + // for everything else. + var referencedTypes map[string]bool + if g.Config.OmitUnreferencedImplementations { + referencedTypes = g.collectReferencedTypes(selectionSet) + } goType := &goInterfaceType{ GoName: name, @@ -936,7 +940,10 @@ func (g *generator) convertNamedFragment(fragment *ast.FragmentDefinition) (goTy // Make sure we generate stable output by sorting the types by name when we get them sort.Slice(implementationTypes, func(i, j int) bool { return implementationTypes[i].Name < implementationTypes[j].Name }) - referencedTypes := g.collectReferencedTypes(fragment.SelectionSet) + var referencedTypes map[string]bool + if g.Config.OmitUnreferencedImplementations { + referencedTypes = g.collectReferencedTypes(fragment.SelectionSet) + } goType := &goInterfaceType{ GoName: fragment.Name, diff --git a/generate/generate_test.go b/generate/generate_test.go index 6db74343..2ba3c978 100644 --- a/generate/generate_test.go +++ b/generate/generate_test.go @@ -274,6 +274,14 @@ func TestGenerateWithConfig(t *testing.T) { }, }, }, + { + "OmitUnreferencedImplementations", "", []string{ + "SimpleInlineFragment.graphql", + "SimpleNamedFragment.graphql", + }, &Config{ + OmitUnreferencedImplementations: true, + }, + }, } for _, test := range tests { diff --git a/generate/testdata/snapshots/TestGenerate-ComplexInlineFragments.graphql-ComplexInlineFragments.graphql.go b/generate/testdata/snapshots/TestGenerate-ComplexInlineFragments.graphql-ComplexInlineFragments.graphql.go index a3352737..2a3fb160 100644 --- a/generate/testdata/snapshots/TestGenerate-ComplexInlineFragments.graphql-ComplexInlineFragments.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-ComplexInlineFragments.graphql-ComplexInlineFragments.graphql.go @@ -44,8 +44,8 @@ func (v *ComplexInlineFragmentsConflictingStuffArticleThumbnailStuffThumbnail) G // // ComplexInlineFragmentsConflictingStuffContent is implemented by the following types: // ComplexInlineFragmentsConflictingStuffArticle +// ComplexInlineFragmentsConflictingStuffTopic // ComplexInlineFragmentsConflictingStuffVideo -// ComplexInlineFragmentsConflictingStuffOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -57,9 +57,9 @@ type ComplexInlineFragmentsConflictingStuffContent interface { func (v *ComplexInlineFragmentsConflictingStuffArticle) implementsGraphQLInterfaceComplexInlineFragmentsConflictingStuffContent() { } -func (v *ComplexInlineFragmentsConflictingStuffVideo) implementsGraphQLInterfaceComplexInlineFragmentsConflictingStuffContent() { +func (v *ComplexInlineFragmentsConflictingStuffTopic) implementsGraphQLInterfaceComplexInlineFragmentsConflictingStuffContent() { } -func (v *ComplexInlineFragmentsConflictingStuffOther) implementsGraphQLInterfaceComplexInlineFragmentsConflictingStuffContent() { +func (v *ComplexInlineFragmentsConflictingStuffVideo) implementsGraphQLInterfaceComplexInlineFragmentsConflictingStuffContent() { } func __unmarshalComplexInlineFragmentsConflictingStuffContent(b []byte, v *ComplexInlineFragmentsConflictingStuffContent) error { @@ -79,6 +79,9 @@ func __unmarshalComplexInlineFragmentsConflictingStuffContent(b []byte, v *Compl case "Article": *v = new(ComplexInlineFragmentsConflictingStuffArticle) return json.Unmarshal(b, *v) + case "Topic": + *v = new(ComplexInlineFragmentsConflictingStuffTopic) + return json.Unmarshal(b, *v) case "Video": *v = new(ComplexInlineFragmentsConflictingStuffVideo) return json.Unmarshal(b, *v) @@ -86,8 +89,8 @@ func __unmarshalComplexInlineFragmentsConflictingStuffContent(b []byte, v *Compl return fmt.Errorf( "response was missing Content.__typename") default: - *v = new(ComplexInlineFragmentsConflictingStuffOther) - return json.Unmarshal(b, *v) + return fmt.Errorf( + `unexpected concrete type for ComplexInlineFragmentsConflictingStuffContent: "%v"`, tn.TypeName) } } @@ -103,6 +106,14 @@ func __marshalComplexInlineFragmentsConflictingStuffContent(v *ComplexInlineFrag *ComplexInlineFragmentsConflictingStuffArticle }{typename, v} return json.Marshal(result) + case *ComplexInlineFragmentsConflictingStuffTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsConflictingStuffTopic + }{typename, v} + return json.Marshal(result) case *ComplexInlineFragmentsConflictingStuffVideo: typename = "Video" @@ -111,9 +122,6 @@ func __marshalComplexInlineFragmentsConflictingStuffContent(v *ComplexInlineFrag *ComplexInlineFragmentsConflictingStuffVideo }{typename, v} return json.Marshal(result) - case *ComplexInlineFragmentsConflictingStuffOther: - - return json.Marshal(v) case nil: return []byte("null"), nil default: @@ -122,13 +130,13 @@ func __marshalComplexInlineFragmentsConflictingStuffContent(v *ComplexInlineFrag } } -// ComplexInlineFragmentsConflictingStuffOther includes the requested fields of the GraphQL type Content. -type ComplexInlineFragmentsConflictingStuffOther struct { +// ComplexInlineFragmentsConflictingStuffTopic includes the requested fields of the GraphQL type Topic. +type ComplexInlineFragmentsConflictingStuffTopic struct { Typename string `json:"__typename"` } -// GetTypename returns ComplexInlineFragmentsConflictingStuffOther.Typename, and is useful for accessing the field via an interface. -func (v *ComplexInlineFragmentsConflictingStuffOther) GetTypename() string { return v.Typename } +// GetTypename returns ComplexInlineFragmentsConflictingStuffTopic.Typename, and is useful for accessing the field via an interface. +func (v *ComplexInlineFragmentsConflictingStuffTopic) GetTypename() string { return v.Typename } // ComplexInlineFragmentsConflictingStuffVideo includes the requested fields of the GraphQL type Video. type ComplexInlineFragmentsConflictingStuffVideo struct { @@ -158,11 +166,20 @@ func (v *ComplexInlineFragmentsConflictingStuffVideoThumbnail) GetTimestampSec() return v.TimestampSec } +// ComplexInlineFragmentsNestedStuffArticle includes the requested fields of the GraphQL type Article. +type ComplexInlineFragmentsNestedStuffArticle struct { + Typename string `json:"__typename"` +} + +// GetTypename returns ComplexInlineFragmentsNestedStuffArticle.Typename, and is useful for accessing the field via an interface. +func (v *ComplexInlineFragmentsNestedStuffArticle) GetTypename() string { return v.Typename } + // ComplexInlineFragmentsNestedStuffContent includes the requested fields of the GraphQL interface Content. // // ComplexInlineFragmentsNestedStuffContent is implemented by the following types: +// ComplexInlineFragmentsNestedStuffArticle // ComplexInlineFragmentsNestedStuffTopic -// ComplexInlineFragmentsNestedStuffOther +// ComplexInlineFragmentsNestedStuffVideo // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -172,9 +189,11 @@ type ComplexInlineFragmentsNestedStuffContent interface { GetTypename() string } +func (v *ComplexInlineFragmentsNestedStuffArticle) implementsGraphQLInterfaceComplexInlineFragmentsNestedStuffContent() { +} func (v *ComplexInlineFragmentsNestedStuffTopic) implementsGraphQLInterfaceComplexInlineFragmentsNestedStuffContent() { } -func (v *ComplexInlineFragmentsNestedStuffOther) implementsGraphQLInterfaceComplexInlineFragmentsNestedStuffContent() { +func (v *ComplexInlineFragmentsNestedStuffVideo) implementsGraphQLInterfaceComplexInlineFragmentsNestedStuffContent() { } func __unmarshalComplexInlineFragmentsNestedStuffContent(b []byte, v *ComplexInlineFragmentsNestedStuffContent) error { @@ -191,15 +210,21 @@ func __unmarshalComplexInlineFragmentsNestedStuffContent(b []byte, v *ComplexInl } switch tn.TypeName { + case "Article": + *v = new(ComplexInlineFragmentsNestedStuffArticle) + return json.Unmarshal(b, *v) case "Topic": *v = new(ComplexInlineFragmentsNestedStuffTopic) return json.Unmarshal(b, *v) + case "Video": + *v = new(ComplexInlineFragmentsNestedStuffVideo) + return json.Unmarshal(b, *v) case "": return fmt.Errorf( "response was missing Content.__typename") default: - *v = new(ComplexInlineFragmentsNestedStuffOther) - return json.Unmarshal(b, *v) + return fmt.Errorf( + `unexpected concrete type for ComplexInlineFragmentsNestedStuffContent: "%v"`, tn.TypeName) } } @@ -207,6 +232,14 @@ func __marshalComplexInlineFragmentsNestedStuffContent(v *ComplexInlineFragments var typename string switch v := (*v).(type) { + case *ComplexInlineFragmentsNestedStuffArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsNestedStuffArticle + }{typename, v} + return json.Marshal(result) case *ComplexInlineFragmentsNestedStuffTopic: typename = "Topic" @@ -219,9 +252,14 @@ func __marshalComplexInlineFragmentsNestedStuffContent(v *ComplexInlineFragments *__premarshalComplexInlineFragmentsNestedStuffTopic }{typename, premarshaled} return json.Marshal(result) - case *ComplexInlineFragmentsNestedStuffOther: + case *ComplexInlineFragmentsNestedStuffVideo: + typename = "Video" - return json.Marshal(v) + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsNestedStuffVideo + }{typename, v} + return json.Marshal(result) case nil: return []byte("null"), nil default: @@ -230,14 +268,6 @@ func __marshalComplexInlineFragmentsNestedStuffContent(v *ComplexInlineFragments } } -// ComplexInlineFragmentsNestedStuffOther includes the requested fields of the GraphQL type Content. -type ComplexInlineFragmentsNestedStuffOther struct { - Typename string `json:"__typename"` -} - -// GetTypename returns ComplexInlineFragmentsNestedStuffOther.Typename, and is useful for accessing the field via an interface. -func (v *ComplexInlineFragmentsNestedStuffOther) GetTypename() string { return v.Typename } - // ComplexInlineFragmentsNestedStuffTopic includes the requested fields of the GraphQL type Topic. type ComplexInlineFragmentsNestedStuffTopic struct { Typename string `json:"__typename"` @@ -626,7 +656,8 @@ func (v *ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentTopic) GetPa // // ComplexInlineFragmentsNestedStuffTopicChildrenContent is implemented by the following types: // ComplexInlineFragmentsNestedStuffTopicChildrenArticle -// ComplexInlineFragmentsNestedStuffTopicChildrenOther +// ComplexInlineFragmentsNestedStuffTopicChildrenTopic +// ComplexInlineFragmentsNestedStuffTopicChildrenVideo // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -643,7 +674,9 @@ type ComplexInlineFragmentsNestedStuffTopicChildrenContent interface { func (v *ComplexInlineFragmentsNestedStuffTopicChildrenArticle) implementsGraphQLInterfaceComplexInlineFragmentsNestedStuffTopicChildrenContent() { } -func (v *ComplexInlineFragmentsNestedStuffTopicChildrenOther) implementsGraphQLInterfaceComplexInlineFragmentsNestedStuffTopicChildrenContent() { +func (v *ComplexInlineFragmentsNestedStuffTopicChildrenTopic) implementsGraphQLInterfaceComplexInlineFragmentsNestedStuffTopicChildrenContent() { +} +func (v *ComplexInlineFragmentsNestedStuffTopicChildrenVideo) implementsGraphQLInterfaceComplexInlineFragmentsNestedStuffTopicChildrenContent() { } func __unmarshalComplexInlineFragmentsNestedStuffTopicChildrenContent(b []byte, v *ComplexInlineFragmentsNestedStuffTopicChildrenContent) error { @@ -663,12 +696,18 @@ func __unmarshalComplexInlineFragmentsNestedStuffTopicChildrenContent(b []byte, case "Article": *v = new(ComplexInlineFragmentsNestedStuffTopicChildrenArticle) return json.Unmarshal(b, *v) + case "Topic": + *v = new(ComplexInlineFragmentsNestedStuffTopicChildrenTopic) + return json.Unmarshal(b, *v) + case "Video": + *v = new(ComplexInlineFragmentsNestedStuffTopicChildrenVideo) + return json.Unmarshal(b, *v) case "": return fmt.Errorf( "response was missing Content.__typename") default: - *v = new(ComplexInlineFragmentsNestedStuffTopicChildrenOther) - return json.Unmarshal(b, *v) + return fmt.Errorf( + `unexpected concrete type for ComplexInlineFragmentsNestedStuffTopicChildrenContent: "%v"`, tn.TypeName) } } @@ -684,9 +723,22 @@ func __marshalComplexInlineFragmentsNestedStuffTopicChildrenContent(v *ComplexIn *ComplexInlineFragmentsNestedStuffTopicChildrenArticle }{typename, v} return json.Marshal(result) - case *ComplexInlineFragmentsNestedStuffTopicChildrenOther: + case *ComplexInlineFragmentsNestedStuffTopicChildrenTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsNestedStuffTopicChildrenTopic + }{typename, v} + return json.Marshal(result) + case *ComplexInlineFragmentsNestedStuffTopicChildrenVideo: + typename = "Video" - return json.Marshal(v) + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsNestedStuffTopicChildrenVideo + }{typename, v} + return json.Marshal(result) case nil: return []byte("null"), nil default: @@ -695,18 +747,39 @@ func __marshalComplexInlineFragmentsNestedStuffTopicChildrenContent(v *ComplexIn } } -// ComplexInlineFragmentsNestedStuffTopicChildrenOther includes the requested fields of the GraphQL type Content. -type ComplexInlineFragmentsNestedStuffTopicChildrenOther struct { +// ComplexInlineFragmentsNestedStuffTopicChildrenTopic includes the requested fields of the GraphQL type Topic. +type ComplexInlineFragmentsNestedStuffTopicChildrenTopic struct { Typename string `json:"__typename"` // ID is the identifier of the content. Id testutil.ID `json:"id"` } -// GetTypename returns ComplexInlineFragmentsNestedStuffTopicChildrenOther.Typename, and is useful for accessing the field via an interface. -func (v *ComplexInlineFragmentsNestedStuffTopicChildrenOther) GetTypename() string { return v.Typename } +// GetTypename returns ComplexInlineFragmentsNestedStuffTopicChildrenTopic.Typename, and is useful for accessing the field via an interface. +func (v *ComplexInlineFragmentsNestedStuffTopicChildrenTopic) GetTypename() string { return v.Typename } -// GetId returns ComplexInlineFragmentsNestedStuffTopicChildrenOther.Id, and is useful for accessing the field via an interface. -func (v *ComplexInlineFragmentsNestedStuffTopicChildrenOther) GetId() testutil.ID { return v.Id } +// GetId returns ComplexInlineFragmentsNestedStuffTopicChildrenTopic.Id, and is useful for accessing the field via an interface. +func (v *ComplexInlineFragmentsNestedStuffTopicChildrenTopic) GetId() testutil.ID { return v.Id } + +// ComplexInlineFragmentsNestedStuffTopicChildrenVideo includes the requested fields of the GraphQL type Video. +type ComplexInlineFragmentsNestedStuffTopicChildrenVideo struct { + Typename string `json:"__typename"` + // ID is the identifier of the content. + Id testutil.ID `json:"id"` +} + +// GetTypename returns ComplexInlineFragmentsNestedStuffTopicChildrenVideo.Typename, and is useful for accessing the field via an interface. +func (v *ComplexInlineFragmentsNestedStuffTopicChildrenVideo) GetTypename() string { return v.Typename } + +// GetId returns ComplexInlineFragmentsNestedStuffTopicChildrenVideo.Id, and is useful for accessing the field via an interface. +func (v *ComplexInlineFragmentsNestedStuffTopicChildrenVideo) GetId() testutil.ID { return v.Id } + +// ComplexInlineFragmentsNestedStuffVideo includes the requested fields of the GraphQL type Video. +type ComplexInlineFragmentsNestedStuffVideo struct { + Typename string `json:"__typename"` +} + +// GetTypename returns ComplexInlineFragmentsNestedStuffVideo.Typename, and is useful for accessing the field via an interface. +func (v *ComplexInlineFragmentsNestedStuffVideo) GetTypename() string { return v.Typename } // ComplexInlineFragmentsRandomItemArticle includes the requested fields of the GraphQL type Article. type ComplexInlineFragmentsRandomItemArticle struct { @@ -1380,3 +1453,4 @@ func ComplexInlineFragments( return data_, err_ } + diff --git a/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go b/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go index 9805e989..1ea1a5aa 100644 --- a/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go @@ -1501,11 +1501,20 @@ func (v *MoreVideoFieldsParentTopic) __premarshalJSON() (*__premarshalMoreVideoF return &retval, nil } +// MoreVideoFieldsParentTopicChildrenArticle includes the requested fields of the GraphQL type Article. +type MoreVideoFieldsParentTopicChildrenArticle struct { + Typename *string `json:"__typename"` +} + +// GetTypename returns MoreVideoFieldsParentTopicChildrenArticle.Typename, and is useful for accessing the field via an interface. +func (v *MoreVideoFieldsParentTopicChildrenArticle) GetTypename() *string { return v.Typename } + // MoreVideoFieldsParentTopicChildrenContent includes the requested fields of the GraphQL interface Content. // // MoreVideoFieldsParentTopicChildrenContent is implemented by the following types: +// MoreVideoFieldsParentTopicChildrenArticle +// MoreVideoFieldsParentTopicChildrenTopic // MoreVideoFieldsParentTopicChildrenVideo -// MoreVideoFieldsParentTopicChildrenOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -1515,9 +1524,11 @@ type MoreVideoFieldsParentTopicChildrenContent interface { GetTypename() *string } -func (v *MoreVideoFieldsParentTopicChildrenVideo) implementsGraphQLInterfaceMoreVideoFieldsParentTopicChildrenContent() { +func (v *MoreVideoFieldsParentTopicChildrenArticle) implementsGraphQLInterfaceMoreVideoFieldsParentTopicChildrenContent() { +} +func (v *MoreVideoFieldsParentTopicChildrenTopic) implementsGraphQLInterfaceMoreVideoFieldsParentTopicChildrenContent() { } -func (v *MoreVideoFieldsParentTopicChildrenOther) implementsGraphQLInterfaceMoreVideoFieldsParentTopicChildrenContent() { +func (v *MoreVideoFieldsParentTopicChildrenVideo) implementsGraphQLInterfaceMoreVideoFieldsParentTopicChildrenContent() { } func __unmarshalMoreVideoFieldsParentTopicChildrenContent(b []byte, v *MoreVideoFieldsParentTopicChildrenContent) error { @@ -1534,6 +1545,12 @@ func __unmarshalMoreVideoFieldsParentTopicChildrenContent(b []byte, v *MoreVideo } switch tn.TypeName { + case "Article": + *v = new(MoreVideoFieldsParentTopicChildrenArticle) + return json.Unmarshal(b, *v) + case "Topic": + *v = new(MoreVideoFieldsParentTopicChildrenTopic) + return json.Unmarshal(b, *v) case "Video": *v = new(MoreVideoFieldsParentTopicChildrenVideo) return json.Unmarshal(b, *v) @@ -1541,8 +1558,8 @@ func __unmarshalMoreVideoFieldsParentTopicChildrenContent(b []byte, v *MoreVideo return fmt.Errorf( "response was missing Content.__typename") default: - *v = new(MoreVideoFieldsParentTopicChildrenOther) - return json.Unmarshal(b, *v) + return fmt.Errorf( + `unexpected concrete type for MoreVideoFieldsParentTopicChildrenContent: "%v"`, tn.TypeName) } } @@ -1550,6 +1567,22 @@ func __marshalMoreVideoFieldsParentTopicChildrenContent(v *MoreVideoFieldsParent var typename string switch v := (*v).(type) { + case *MoreVideoFieldsParentTopicChildrenArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *MoreVideoFieldsParentTopicChildrenArticle + }{typename, v} + return json.Marshal(result) + case *MoreVideoFieldsParentTopicChildrenTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *MoreVideoFieldsParentTopicChildrenTopic + }{typename, v} + return json.Marshal(result) case *MoreVideoFieldsParentTopicChildrenVideo: typename = "Video" @@ -1562,9 +1595,6 @@ func __marshalMoreVideoFieldsParentTopicChildrenContent(v *MoreVideoFieldsParent *__premarshalMoreVideoFieldsParentTopicChildrenVideo }{typename, premarshaled} return json.Marshal(result) - case *MoreVideoFieldsParentTopicChildrenOther: - - return json.Marshal(v) case nil: return []byte("null"), nil default: @@ -1573,13 +1603,13 @@ func __marshalMoreVideoFieldsParentTopicChildrenContent(v *MoreVideoFieldsParent } } -// MoreVideoFieldsParentTopicChildrenOther includes the requested fields of the GraphQL type Content. -type MoreVideoFieldsParentTopicChildrenOther struct { +// MoreVideoFieldsParentTopicChildrenTopic includes the requested fields of the GraphQL type Topic. +type MoreVideoFieldsParentTopicChildrenTopic struct { Typename *string `json:"__typename"` } -// GetTypename returns MoreVideoFieldsParentTopicChildrenOther.Typename, and is useful for accessing the field via an interface. -func (v *MoreVideoFieldsParentTopicChildrenOther) GetTypename() *string { return v.Typename } +// GetTypename returns MoreVideoFieldsParentTopicChildrenTopic.Typename, and is useful for accessing the field via an interface. +func (v *MoreVideoFieldsParentTopicChildrenTopic) GetTypename() *string { return v.Typename } // MoreVideoFieldsParentTopicChildrenVideo includes the requested fields of the GraphQL type Video. type MoreVideoFieldsParentTopicChildrenVideo struct { @@ -2640,3 +2670,4 @@ func ComplexNamedFragmentsWithInlineUnion( return data_, err_ } + diff --git a/generate/testdata/snapshots/TestGenerate-SimpleInlineFragment.graphql-SimpleInlineFragment.graphql.go b/generate/testdata/snapshots/TestGenerate-SimpleInlineFragment.graphql-SimpleInlineFragment.graphql.go index e572c06a..ba391639 100644 --- a/generate/testdata/snapshots/TestGenerate-SimpleInlineFragment.graphql-SimpleInlineFragment.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-SimpleInlineFragment.graphql-SimpleInlineFragment.graphql.go @@ -35,8 +35,8 @@ func (v *SimpleInlineFragmentRandomItemArticle) GetText() string { return v.Text // // SimpleInlineFragmentRandomItemContent is implemented by the following types: // SimpleInlineFragmentRandomItemArticle +// SimpleInlineFragmentRandomItemTopic // SimpleInlineFragmentRandomItemVideo -// SimpleInlineFragmentRandomItemOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -55,9 +55,9 @@ type SimpleInlineFragmentRandomItemContent interface { func (v *SimpleInlineFragmentRandomItemArticle) implementsGraphQLInterfaceSimpleInlineFragmentRandomItemContent() { } -func (v *SimpleInlineFragmentRandomItemVideo) implementsGraphQLInterfaceSimpleInlineFragmentRandomItemContent() { +func (v *SimpleInlineFragmentRandomItemTopic) implementsGraphQLInterfaceSimpleInlineFragmentRandomItemContent() { } -func (v *SimpleInlineFragmentRandomItemOther) implementsGraphQLInterfaceSimpleInlineFragmentRandomItemContent() { +func (v *SimpleInlineFragmentRandomItemVideo) implementsGraphQLInterfaceSimpleInlineFragmentRandomItemContent() { } func __unmarshalSimpleInlineFragmentRandomItemContent(b []byte, v *SimpleInlineFragmentRandomItemContent) error { @@ -77,6 +77,9 @@ func __unmarshalSimpleInlineFragmentRandomItemContent(b []byte, v *SimpleInlineF case "Article": *v = new(SimpleInlineFragmentRandomItemArticle) return json.Unmarshal(b, *v) + case "Topic": + *v = new(SimpleInlineFragmentRandomItemTopic) + return json.Unmarshal(b, *v) case "Video": *v = new(SimpleInlineFragmentRandomItemVideo) return json.Unmarshal(b, *v) @@ -84,8 +87,8 @@ func __unmarshalSimpleInlineFragmentRandomItemContent(b []byte, v *SimpleInlineF return fmt.Errorf( "response was missing Content.__typename") default: - *v = new(SimpleInlineFragmentRandomItemOther) - return json.Unmarshal(b, *v) + return fmt.Errorf( + `unexpected concrete type for SimpleInlineFragmentRandomItemContent: "%v"`, tn.TypeName) } } @@ -101,6 +104,14 @@ func __marshalSimpleInlineFragmentRandomItemContent(v *SimpleInlineFragmentRando *SimpleInlineFragmentRandomItemArticle }{typename, v} return json.Marshal(result) + case *SimpleInlineFragmentRandomItemTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *SimpleInlineFragmentRandomItemTopic + }{typename, v} + return json.Marshal(result) case *SimpleInlineFragmentRandomItemVideo: typename = "Video" @@ -109,9 +120,6 @@ func __marshalSimpleInlineFragmentRandomItemContent(v *SimpleInlineFragmentRando *SimpleInlineFragmentRandomItemVideo }{typename, v} return json.Marshal(result) - case *SimpleInlineFragmentRandomItemOther: - - return json.Marshal(v) case nil: return []byte("null"), nil default: @@ -120,22 +128,22 @@ func __marshalSimpleInlineFragmentRandomItemContent(v *SimpleInlineFragmentRando } } -// SimpleInlineFragmentRandomItemOther includes the requested fields of the GraphQL type Content. -type SimpleInlineFragmentRandomItemOther struct { +// SimpleInlineFragmentRandomItemTopic includes the requested fields of the GraphQL type Topic. +type SimpleInlineFragmentRandomItemTopic struct { Typename string `json:"__typename"` // ID is the identifier of the content. Id testutil.ID `json:"id"` Name string `json:"name"` } -// GetTypename returns SimpleInlineFragmentRandomItemOther.Typename, and is useful for accessing the field via an interface. -func (v *SimpleInlineFragmentRandomItemOther) GetTypename() string { return v.Typename } +// GetTypename returns SimpleInlineFragmentRandomItemTopic.Typename, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemTopic) GetTypename() string { return v.Typename } -// GetId returns SimpleInlineFragmentRandomItemOther.Id, and is useful for accessing the field via an interface. -func (v *SimpleInlineFragmentRandomItemOther) GetId() testutil.ID { return v.Id } +// GetId returns SimpleInlineFragmentRandomItemTopic.Id, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemTopic) GetId() testutil.ID { return v.Id } -// GetName returns SimpleInlineFragmentRandomItemOther.Name, and is useful for accessing the field via an interface. -func (v *SimpleInlineFragmentRandomItemOther) GetName() string { return v.Name } +// GetName returns SimpleInlineFragmentRandomItemTopic.Name, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemTopic) GetName() string { return v.Name } // SimpleInlineFragmentRandomItemVideo includes the requested fields of the GraphQL type Video. type SimpleInlineFragmentRandomItemVideo struct { @@ -267,3 +275,4 @@ func SimpleInlineFragment( return data_, err_ } + diff --git a/generate/testdata/snapshots/TestGenerate-SimpleNamedFragment.graphql-SimpleNamedFragment.graphql.go b/generate/testdata/snapshots/TestGenerate-SimpleNamedFragment.graphql-SimpleNamedFragment.graphql.go index 3a2a20f3..c9fd12a2 100644 --- a/generate/testdata/snapshots/TestGenerate-SimpleNamedFragment.graphql-SimpleNamedFragment.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-SimpleNamedFragment.graphql-SimpleNamedFragment.graphql.go @@ -10,11 +10,29 @@ import ( "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"` +} + +// GetTypename returns SimpleNamedFragmentRandomItemArticle.Typename, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemArticle) GetTypename() string { return v.Typename } + +// GetId returns SimpleNamedFragmentRandomItemArticle.Id, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemArticle) GetId() testutil.ID { return v.Id } + +// GetName returns SimpleNamedFragmentRandomItemArticle.Name, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemArticle) GetName() string { return v.Name } + // SimpleNamedFragmentRandomItemContent includes the requested fields of the GraphQL interface Content. // // SimpleNamedFragmentRandomItemContent is implemented by the following types: +// SimpleNamedFragmentRandomItemArticle +// SimpleNamedFragmentRandomItemTopic // SimpleNamedFragmentRandomItemVideo -// SimpleNamedFragmentRandomItemOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -31,9 +49,11 @@ type SimpleNamedFragmentRandomItemContent interface { GetName() string } -func (v *SimpleNamedFragmentRandomItemVideo) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() { +func (v *SimpleNamedFragmentRandomItemArticle) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() { +} +func (v *SimpleNamedFragmentRandomItemTopic) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() { } -func (v *SimpleNamedFragmentRandomItemOther) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() { +func (v *SimpleNamedFragmentRandomItemVideo) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() { } func __unmarshalSimpleNamedFragmentRandomItemContent(b []byte, v *SimpleNamedFragmentRandomItemContent) error { @@ -50,6 +70,12 @@ func __unmarshalSimpleNamedFragmentRandomItemContent(b []byte, v *SimpleNamedFra } switch tn.TypeName { + case "Article": + *v = new(SimpleNamedFragmentRandomItemArticle) + return json.Unmarshal(b, *v) + case "Topic": + *v = new(SimpleNamedFragmentRandomItemTopic) + return json.Unmarshal(b, *v) case "Video": *v = new(SimpleNamedFragmentRandomItemVideo) return json.Unmarshal(b, *v) @@ -57,8 +83,8 @@ func __unmarshalSimpleNamedFragmentRandomItemContent(b []byte, v *SimpleNamedFra return fmt.Errorf( "response was missing Content.__typename") default: - *v = new(SimpleNamedFragmentRandomItemOther) - return json.Unmarshal(b, *v) + return fmt.Errorf( + `unexpected concrete type for SimpleNamedFragmentRandomItemContent: "%v"`, tn.TypeName) } } @@ -66,6 +92,22 @@ func __marshalSimpleNamedFragmentRandomItemContent(v *SimpleNamedFragmentRandomI var typename string switch v := (*v).(type) { + case *SimpleNamedFragmentRandomItemArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *SimpleNamedFragmentRandomItemArticle + }{typename, v} + return json.Marshal(result) + case *SimpleNamedFragmentRandomItemTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *SimpleNamedFragmentRandomItemTopic + }{typename, v} + return json.Marshal(result) case *SimpleNamedFragmentRandomItemVideo: typename = "Video" @@ -78,9 +120,6 @@ func __marshalSimpleNamedFragmentRandomItemContent(v *SimpleNamedFragmentRandomI *__premarshalSimpleNamedFragmentRandomItemVideo }{typename, premarshaled} return json.Marshal(result) - case *SimpleNamedFragmentRandomItemOther: - - return json.Marshal(v) case nil: return []byte("null"), nil default: @@ -89,22 +128,22 @@ func __marshalSimpleNamedFragmentRandomItemContent(v *SimpleNamedFragmentRandomI } } -// SimpleNamedFragmentRandomItemOther includes the requested fields of the GraphQL type Content. -type SimpleNamedFragmentRandomItemOther struct { +// 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"` } -// GetTypename returns SimpleNamedFragmentRandomItemOther.Typename, and is useful for accessing the field via an interface. -func (v *SimpleNamedFragmentRandomItemOther) GetTypename() string { return v.Typename } +// GetTypename returns SimpleNamedFragmentRandomItemTopic.Typename, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemTopic) GetTypename() string { return v.Typename } -// GetId returns SimpleNamedFragmentRandomItemOther.Id, and is useful for accessing the field via an interface. -func (v *SimpleNamedFragmentRandomItemOther) GetId() testutil.ID { return v.Id } +// GetId returns SimpleNamedFragmentRandomItemTopic.Id, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemTopic) GetId() testutil.ID { return v.Id } -// GetName returns SimpleNamedFragmentRandomItemOther.Name, and is useful for accessing the field via an interface. -func (v *SimpleNamedFragmentRandomItemOther) GetName() string { return v.Name } +// GetName returns SimpleNamedFragmentRandomItemTopic.Name, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemTopic) GetName() string { return v.Name } // SimpleNamedFragmentRandomItemVideo includes the requested fields of the GraphQL type Video. type SimpleNamedFragmentRandomItemVideo struct { @@ -194,11 +233,19 @@ func (v *SimpleNamedFragmentRandomItemVideo) __premarshalJSON() (*__premarshalSi return &retval, nil } +// SimpleNamedFragmentRandomLeafArticle includes the requested fields of the GraphQL type Article. +type SimpleNamedFragmentRandomLeafArticle struct { + Typename string `json:"__typename"` +} + +// GetTypename returns SimpleNamedFragmentRandomLeafArticle.Typename, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomLeafArticle) GetTypename() string { return v.Typename } + // SimpleNamedFragmentRandomLeafLeafContent includes the requested fields of the GraphQL interface LeafContent. // // SimpleNamedFragmentRandomLeafLeafContent is implemented by the following types: +// SimpleNamedFragmentRandomLeafArticle // SimpleNamedFragmentRandomLeafVideo -// SimpleNamedFragmentRandomLeafOther // The GraphQL type's documentation follows. // // LeafContent represents content items that can't have child-nodes. @@ -208,9 +255,9 @@ type SimpleNamedFragmentRandomLeafLeafContent interface { GetTypename() string } -func (v *SimpleNamedFragmentRandomLeafVideo) implementsGraphQLInterfaceSimpleNamedFragmentRandomLeafLeafContent() { +func (v *SimpleNamedFragmentRandomLeafArticle) implementsGraphQLInterfaceSimpleNamedFragmentRandomLeafLeafContent() { } -func (v *SimpleNamedFragmentRandomLeafOther) implementsGraphQLInterfaceSimpleNamedFragmentRandomLeafLeafContent() { +func (v *SimpleNamedFragmentRandomLeafVideo) implementsGraphQLInterfaceSimpleNamedFragmentRandomLeafLeafContent() { } func __unmarshalSimpleNamedFragmentRandomLeafLeafContent(b []byte, v *SimpleNamedFragmentRandomLeafLeafContent) error { @@ -227,6 +274,9 @@ func __unmarshalSimpleNamedFragmentRandomLeafLeafContent(b []byte, v *SimpleName } switch tn.TypeName { + case "Article": + *v = new(SimpleNamedFragmentRandomLeafArticle) + return json.Unmarshal(b, *v) case "Video": *v = new(SimpleNamedFragmentRandomLeafVideo) return json.Unmarshal(b, *v) @@ -234,8 +284,8 @@ func __unmarshalSimpleNamedFragmentRandomLeafLeafContent(b []byte, v *SimpleName return fmt.Errorf( "response was missing LeafContent.__typename") default: - *v = new(SimpleNamedFragmentRandomLeafOther) - return json.Unmarshal(b, *v) + return fmt.Errorf( + `unexpected concrete type for SimpleNamedFragmentRandomLeafLeafContent: "%v"`, tn.TypeName) } } @@ -243,6 +293,14 @@ func __marshalSimpleNamedFragmentRandomLeafLeafContent(v *SimpleNamedFragmentRan var typename string switch v := (*v).(type) { + case *SimpleNamedFragmentRandomLeafArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *SimpleNamedFragmentRandomLeafArticle + }{typename, v} + return json.Marshal(result) case *SimpleNamedFragmentRandomLeafVideo: typename = "Video" @@ -255,9 +313,6 @@ func __marshalSimpleNamedFragmentRandomLeafLeafContent(v *SimpleNamedFragmentRan *__premarshalSimpleNamedFragmentRandomLeafVideo }{typename, premarshaled} return json.Marshal(result) - case *SimpleNamedFragmentRandomLeafOther: - - return json.Marshal(v) case nil: return []byte("null"), nil default: @@ -266,14 +321,6 @@ func __marshalSimpleNamedFragmentRandomLeafLeafContent(v *SimpleNamedFragmentRan } } -// SimpleNamedFragmentRandomLeafOther includes the requested fields of the GraphQL type LeafContent. -type SimpleNamedFragmentRandomLeafOther struct { - Typename string `json:"__typename"` -} - -// GetTypename returns SimpleNamedFragmentRandomLeafOther.Typename, and is useful for accessing the field via an interface. -func (v *SimpleNamedFragmentRandomLeafOther) GetTypename() string { return v.Typename } - // SimpleNamedFragmentRandomLeafVideo includes the requested fields of the GraphQL type Video. type SimpleNamedFragmentRandomLeafVideo struct { Typename string `json:"__typename"` @@ -543,3 +590,4 @@ func SimpleNamedFragment( return data_, err_ } + diff --git a/generate/testdata/snapshots/TestGenerate-StructOption.graphql-StructOption.graphql.go b/generate/testdata/snapshots/TestGenerate-StructOption.graphql-StructOption.graphql.go index 42f93d9f..7c01e243 100644 --- a/generate/testdata/snapshots/TestGenerate-StructOption.graphql-StructOption.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-StructOption.graphql-StructOption.graphql.go @@ -204,11 +204,29 @@ func (v *StructOptionRootTopicChildrenContentParentTopicChildrenContent) GetId() return v.Id } +// StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle includes the requested fields of the GraphQL type Article. +type StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle struct { + Typename string `json:"__typename"` + // ID is the identifier of the content. + Id testutil.ID `json:"id"` +} + +// GetTypename returns StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle.Typename, and is useful for accessing the field via an interface. +func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle) GetTypename() string { + return v.Typename +} + +// GetId returns StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle.Id, and is useful for accessing the field via an interface. +func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle) GetId() testutil.ID { + return v.Id +} + // StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent includes the requested fields of the GraphQL interface Content. // // StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent is implemented by the following types: +// StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle +// StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic // StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenVideo -// StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -223,9 +241,11 @@ type StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent int GetId() testutil.ID } -func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenVideo) implementsGraphQLInterfaceStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent() { +func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle) implementsGraphQLInterfaceStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent() { +} +func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic) implementsGraphQLInterfaceStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent() { } -func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther) implementsGraphQLInterfaceStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent() { +func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenVideo) implementsGraphQLInterfaceStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent() { } func __unmarshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent(b []byte, v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent) error { @@ -242,6 +262,12 @@ func __unmarshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildren } switch tn.TypeName { + case "Article": + *v = new(StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle) + return json.Unmarshal(b, *v) + case "Topic": + *v = new(StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic) + return json.Unmarshal(b, *v) case "Video": *v = new(StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenVideo) return json.Unmarshal(b, *v) @@ -249,8 +275,8 @@ func __unmarshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildren return fmt.Errorf( "response was missing Content.__typename") default: - *v = new(StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther) - return json.Unmarshal(b, *v) + return fmt.Errorf( + `unexpected concrete type for StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent: "%v"`, tn.TypeName) } } @@ -258,6 +284,22 @@ func __marshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenCo var typename string switch v := (*v).(type) { + case *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenArticle + }{typename, v} + return json.Marshal(result) + case *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic + }{typename, v} + return json.Marshal(result) case *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenVideo: typename = "Video" @@ -270,9 +312,6 @@ func __marshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenCo *__premarshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenVideo }{typename, premarshaled} return json.Marshal(result) - case *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther: - - return json.Marshal(v) case nil: return []byte("null"), nil default: @@ -281,20 +320,20 @@ func __marshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenCo } } -// StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther includes the requested fields of the GraphQL type Content. -type StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther struct { +// StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic includes the requested fields of the GraphQL type Topic. +type StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic struct { Typename string `json:"__typename"` // ID is the identifier of the content. Id testutil.ID `json:"id"` } -// GetTypename returns StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther.Typename, and is useful for accessing the field via an interface. -func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther) GetTypename() string { +// GetTypename returns StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic.Typename, and is useful for accessing the field via an interface. +func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic) GetTypename() string { return v.Typename } -// GetId returns StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther.Id, and is useful for accessing the field via an interface. -func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenOther) GetId() testutil.ID { +// GetId returns StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic.Id, and is useful for accessing the field via an interface. +func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic) GetId() testutil.ID { return v.Id } @@ -440,3 +479,4 @@ func StructOption( return data_, err_ } + diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleInlineFragment.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleInlineFragment.graphql-testdata-queries-generated.go new file mode 100644 index 00000000..3ed39804 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleInlineFragment.graphql-testdata-queries-generated.go @@ -0,0 +1,271 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package queries + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/Khan/genqlient/graphql" +) + +// SimpleInlineFragmentRandomItemArticle includes the requested fields of the GraphQL type Article. +type SimpleInlineFragmentRandomItemArticle struct { + Typename string `json:"__typename"` + // ID is the identifier of the content. + Id string `json:"id"` + Name string `json:"name"` + Text string `json:"text"` +} + +// GetTypename returns SimpleInlineFragmentRandomItemArticle.Typename, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemArticle) GetTypename() string { return v.Typename } + +// GetId returns SimpleInlineFragmentRandomItemArticle.Id, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemArticle) GetId() string { return v.Id } + +// GetName returns SimpleInlineFragmentRandomItemArticle.Name, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemArticle) GetName() string { return v.Name } + +// GetText returns SimpleInlineFragmentRandomItemArticle.Text, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemArticle) GetText() string { return v.Text } + +// SimpleInlineFragmentRandomItemContent includes the requested fields of the GraphQL interface Content. +// +// SimpleInlineFragmentRandomItemContent is implemented by the following types: +// SimpleInlineFragmentRandomItemArticle +// SimpleInlineFragmentRandomItemVideo +// SimpleInlineFragmentRandomItemOther +// The GraphQL type's documentation follows. +// +// Content is implemented by various types like Article, Video, and Topic. +type SimpleInlineFragmentRandomItemContent interface { + implementsGraphQLInterfaceSimpleInlineFragmentRandomItemContent() + // 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() string + // GetName returns the interface-field "name" from its implementation. + GetName() string +} + +func (v *SimpleInlineFragmentRandomItemArticle) implementsGraphQLInterfaceSimpleInlineFragmentRandomItemContent() { +} +func (v *SimpleInlineFragmentRandomItemVideo) implementsGraphQLInterfaceSimpleInlineFragmentRandomItemContent() { +} +func (v *SimpleInlineFragmentRandomItemOther) implementsGraphQLInterfaceSimpleInlineFragmentRandomItemContent() { +} + +func __unmarshalSimpleInlineFragmentRandomItemContent(b []byte, v *SimpleInlineFragmentRandomItemContent) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "Article": + *v = new(SimpleInlineFragmentRandomItemArticle) + return json.Unmarshal(b, *v) + case "Video": + *v = new(SimpleInlineFragmentRandomItemVideo) + return json.Unmarshal(b, *v) + case "": + return fmt.Errorf( + "response was missing Content.__typename") + default: + *v = new(SimpleInlineFragmentRandomItemOther) + return json.Unmarshal(b, *v) + } +} + +func __marshalSimpleInlineFragmentRandomItemContent(v *SimpleInlineFragmentRandomItemContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *SimpleInlineFragmentRandomItemArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *SimpleInlineFragmentRandomItemArticle + }{typename, v} + return json.Marshal(result) + case *SimpleInlineFragmentRandomItemVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *SimpleInlineFragmentRandomItemVideo + }{typename, v} + return json.Marshal(result) + case *SimpleInlineFragmentRandomItemOther: + + return json.Marshal(v) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for SimpleInlineFragmentRandomItemContent: "%T"`, v) + } +} + +// SimpleInlineFragmentRandomItemOther includes the requested fields of the GraphQL type Content. +type SimpleInlineFragmentRandomItemOther struct { + Typename string `json:"__typename"` + // ID is the identifier of the content. + Id string `json:"id"` + Name string `json:"name"` +} + +// GetTypename returns SimpleInlineFragmentRandomItemOther.Typename, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemOther) GetTypename() string { return v.Typename } + +// GetId returns SimpleInlineFragmentRandomItemOther.Id, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemOther) GetId() string { return v.Id } + +// GetName returns SimpleInlineFragmentRandomItemOther.Name, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemOther) GetName() string { return v.Name } + +// SimpleInlineFragmentRandomItemVideo includes the requested fields of the GraphQL type Video. +type SimpleInlineFragmentRandomItemVideo struct { + Typename string `json:"__typename"` + // ID is the identifier of the content. + Id string `json:"id"` + Name string `json:"name"` + Duration int `json:"duration"` +} + +// GetTypename returns SimpleInlineFragmentRandomItemVideo.Typename, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemVideo) GetTypename() string { return v.Typename } + +// GetId returns SimpleInlineFragmentRandomItemVideo.Id, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemVideo) GetId() string { return v.Id } + +// GetName returns SimpleInlineFragmentRandomItemVideo.Name, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemVideo) GetName() string { return v.Name } + +// GetDuration returns SimpleInlineFragmentRandomItemVideo.Duration, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemVideo) GetDuration() int { return v.Duration } + +// SimpleInlineFragmentResponse is returned by SimpleInlineFragment on success. +type SimpleInlineFragmentResponse struct { + RandomItem SimpleInlineFragmentRandomItemContent `json:"-"` +} + +// GetRandomItem returns SimpleInlineFragmentResponse.RandomItem, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentResponse) GetRandomItem() SimpleInlineFragmentRandomItemContent { + return v.RandomItem +} + +func (v *SimpleInlineFragmentResponse) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *SimpleInlineFragmentResponse + RandomItem json.RawMessage `json:"randomItem"` + graphql.NoUnmarshalJSON + } + firstPass.SimpleInlineFragmentResponse = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.RandomItem + src := firstPass.RandomItem + if len(src) != 0 && string(src) != "null" { + err = __unmarshalSimpleInlineFragmentRandomItemContent( + src, dst) + if err != nil { + return fmt.Errorf( + "unable to unmarshal SimpleInlineFragmentResponse.RandomItem: %w", err) + } + } + } + return nil +} + +type __premarshalSimpleInlineFragmentResponse struct { + RandomItem json.RawMessage `json:"randomItem"` +} + +func (v *SimpleInlineFragmentResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *SimpleInlineFragmentResponse) __premarshalJSON() (*__premarshalSimpleInlineFragmentResponse, error) { + var retval __premarshalSimpleInlineFragmentResponse + + { + + dst := &retval.RandomItem + src := v.RandomItem + var err error + *dst, err = __marshalSimpleInlineFragmentRandomItemContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal SimpleInlineFragmentResponse.RandomItem: %w", err) + } + } + return &retval, nil +} + +// The query executed by SimpleInlineFragment. +const SimpleInlineFragment_Operation = ` +query SimpleInlineFragment { + randomItem { + __typename + id + name + ... on Article { + text + } + ... on Video { + duration + } + } +} +` + +func SimpleInlineFragment( + ctx_ context.Context, + client_ graphql.Client, +) (data_ *SimpleInlineFragmentResponse, err_ error) { + req_ := &graphql.Request{ + OpName: "SimpleInlineFragment", + Query: SimpleInlineFragment_Operation, + } + + data_ = &SimpleInlineFragmentResponse{} + resp_ := &graphql.Response{Data: data_} + + err_ = client_.MakeRequest( + ctx_, + req_, + resp_, + ) + + return data_, err_ +} + diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleNamedFragment.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleNamedFragment.graphql-testdata-queries-generated.go new file mode 100644 index 00000000..5a72be89 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleNamedFragment.graphql-testdata-queries-generated.go @@ -0,0 +1,547 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package queries + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/Khan/genqlient/graphql" +) + +// SimpleNamedFragmentRandomItemContent includes the requested fields of the GraphQL interface Content. +// +// SimpleNamedFragmentRandomItemContent is implemented by the following types: +// SimpleNamedFragmentRandomItemVideo +// SimpleNamedFragmentRandomItemOther +// 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() string + // GetName returns the interface-field "name" from its implementation. + GetName() string +} + +func (v *SimpleNamedFragmentRandomItemVideo) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() { +} +func (v *SimpleNamedFragmentRandomItemOther) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() { +} + +func __unmarshalSimpleNamedFragmentRandomItemContent(b []byte, v *SimpleNamedFragmentRandomItemContent) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "Video": + *v = new(SimpleNamedFragmentRandomItemVideo) + return json.Unmarshal(b, *v) + case "": + return fmt.Errorf( + "response was missing Content.__typename") + default: + *v = new(SimpleNamedFragmentRandomItemOther) + return json.Unmarshal(b, *v) + } +} + +func __marshalSimpleNamedFragmentRandomItemContent(v *SimpleNamedFragmentRandomItemContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *SimpleNamedFragmentRandomItemVideo: + typename = "Video" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalSimpleNamedFragmentRandomItemVideo + }{typename, premarshaled} + return json.Marshal(result) + case *SimpleNamedFragmentRandomItemOther: + + return json.Marshal(v) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for SimpleNamedFragmentRandomItemContent: "%T"`, v) + } +} + +// SimpleNamedFragmentRandomItemOther includes the requested fields of the GraphQL type Content. +type SimpleNamedFragmentRandomItemOther struct { + Typename string `json:"__typename"` + // ID is the identifier of the content. + Id string `json:"id"` + Name string `json:"name"` +} + +// GetTypename returns SimpleNamedFragmentRandomItemOther.Typename, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemOther) GetTypename() string { return v.Typename } + +// GetId returns SimpleNamedFragmentRandomItemOther.Id, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemOther) GetId() string { return v.Id } + +// GetName returns SimpleNamedFragmentRandomItemOther.Name, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemOther) GetName() string { return v.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 string `json:"id"` + Name string `json:"name"` + VideoFields `json:"-"` +} + +// GetTypename returns SimpleNamedFragmentRandomItemVideo.Typename, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemVideo) GetTypename() string { return v.Typename } + +// GetId returns SimpleNamedFragmentRandomItemVideo.Id, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemVideo) GetId() string { return v.Id } + +// GetName returns SimpleNamedFragmentRandomItemVideo.Name, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemVideo) GetName() string { return v.Name } + +// GetUrl returns SimpleNamedFragmentRandomItemVideo.Url, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemVideo) GetUrl() string { return v.VideoFields.Url } + +// GetDuration returns SimpleNamedFragmentRandomItemVideo.Duration, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemVideo) GetDuration() int { return v.VideoFields.Duration } + +// GetThumbnail returns SimpleNamedFragmentRandomItemVideo.Thumbnail, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemVideo) GetThumbnail() VideoFieldsThumbnail { + return v.VideoFields.Thumbnail +} + +func (v *SimpleNamedFragmentRandomItemVideo) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + 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 +} + +type __premarshalSimpleNamedFragmentRandomItemVideo struct { + Typename string `json:"__typename"` + + Id string `json:"id"` + + Name string `json:"name"` + + Url string `json:"url"` + + Duration int `json:"duration"` + + Thumbnail VideoFieldsThumbnail `json:"thumbnail"` +} + +func (v *SimpleNamedFragmentRandomItemVideo) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *SimpleNamedFragmentRandomItemVideo) __premarshalJSON() (*__premarshalSimpleNamedFragmentRandomItemVideo, error) { + var retval __premarshalSimpleNamedFragmentRandomItemVideo + + retval.Typename = v.Typename + retval.Id = v.Id + retval.Name = v.Name + retval.Url = v.VideoFields.Url + retval.Duration = v.VideoFields.Duration + retval.Thumbnail = v.VideoFields.Thumbnail + return &retval, nil +} + +// SimpleNamedFragmentRandomLeafLeafContent includes the requested fields of the GraphQL interface LeafContent. +// +// SimpleNamedFragmentRandomLeafLeafContent is implemented by the following types: +// SimpleNamedFragmentRandomLeafVideo +// SimpleNamedFragmentRandomLeafOther +// 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 *SimpleNamedFragmentRandomLeafVideo) implementsGraphQLInterfaceSimpleNamedFragmentRandomLeafLeafContent() { +} +func (v *SimpleNamedFragmentRandomLeafOther) implementsGraphQLInterfaceSimpleNamedFragmentRandomLeafLeafContent() { +} + +func __unmarshalSimpleNamedFragmentRandomLeafLeafContent(b []byte, v *SimpleNamedFragmentRandomLeafLeafContent) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "Video": + *v = new(SimpleNamedFragmentRandomLeafVideo) + return json.Unmarshal(b, *v) + case "": + return fmt.Errorf( + "response was missing LeafContent.__typename") + default: + *v = new(SimpleNamedFragmentRandomLeafOther) + return json.Unmarshal(b, *v) + } +} + +func __marshalSimpleNamedFragmentRandomLeafLeafContent(v *SimpleNamedFragmentRandomLeafLeafContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *SimpleNamedFragmentRandomLeafVideo: + typename = "Video" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalSimpleNamedFragmentRandomLeafVideo + }{typename, premarshaled} + return json.Marshal(result) + case *SimpleNamedFragmentRandomLeafOther: + + return json.Marshal(v) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for SimpleNamedFragmentRandomLeafLeafContent: "%T"`, v) + } +} + +// SimpleNamedFragmentRandomLeafOther includes the requested fields of the GraphQL type LeafContent. +type SimpleNamedFragmentRandomLeafOther struct { + Typename string `json:"__typename"` +} + +// GetTypename returns SimpleNamedFragmentRandomLeafOther.Typename, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomLeafOther) GetTypename() string { return v.Typename } + +// SimpleNamedFragmentRandomLeafVideo includes the requested fields of the GraphQL type Video. +type SimpleNamedFragmentRandomLeafVideo struct { + Typename string `json:"__typename"` + VideoFields `json:"-"` +} + +// GetTypename returns SimpleNamedFragmentRandomLeafVideo.Typename, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomLeafVideo) GetTypename() string { return v.Typename } + +// GetId returns SimpleNamedFragmentRandomLeafVideo.Id, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomLeafVideo) GetId() string { return v.VideoFields.Id } + +// GetName returns SimpleNamedFragmentRandomLeafVideo.Name, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomLeafVideo) GetName() string { return v.VideoFields.Name } + +// GetUrl returns SimpleNamedFragmentRandomLeafVideo.Url, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomLeafVideo) GetUrl() string { return v.VideoFields.Url } + +// GetDuration returns SimpleNamedFragmentRandomLeafVideo.Duration, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomLeafVideo) GetDuration() int { return v.VideoFields.Duration } + +// GetThumbnail returns SimpleNamedFragmentRandomLeafVideo.Thumbnail, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomLeafVideo) GetThumbnail() VideoFieldsThumbnail { + return v.VideoFields.Thumbnail +} + +func (v *SimpleNamedFragmentRandomLeafVideo) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + 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 +} + +type __premarshalSimpleNamedFragmentRandomLeafVideo struct { + Typename string `json:"__typename"` + + Id string `json:"id"` + + Name string `json:"name"` + + Url string `json:"url"` + + Duration int `json:"duration"` + + Thumbnail VideoFieldsThumbnail `json:"thumbnail"` +} + +func (v *SimpleNamedFragmentRandomLeafVideo) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *SimpleNamedFragmentRandomLeafVideo) __premarshalJSON() (*__premarshalSimpleNamedFragmentRandomLeafVideo, error) { + var retval __premarshalSimpleNamedFragmentRandomLeafVideo + + retval.Typename = v.Typename + retval.Id = v.VideoFields.Id + retval.Name = v.VideoFields.Name + retval.Url = v.VideoFields.Url + retval.Duration = v.VideoFields.Duration + retval.Thumbnail = v.VideoFields.Thumbnail + return &retval, nil +} + +// SimpleNamedFragmentResponse is returned by SimpleNamedFragment on success. +type SimpleNamedFragmentResponse struct { + RandomItem SimpleNamedFragmentRandomItemContent `json:"-"` + RandomLeaf SimpleNamedFragmentRandomLeafLeafContent `json:"-"` +} + +// GetRandomItem returns SimpleNamedFragmentResponse.RandomItem, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentResponse) GetRandomItem() SimpleNamedFragmentRandomItemContent { + return v.RandomItem +} + +// GetRandomLeaf returns SimpleNamedFragmentResponse.RandomLeaf, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentResponse) GetRandomLeaf() SimpleNamedFragmentRandomLeafLeafContent { + return v.RandomLeaf +} + +func (v *SimpleNamedFragmentResponse) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + 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 + } + + { + dst := &v.RandomItem + src := firstPass.RandomItem + if len(src) != 0 && string(src) != "null" { + err = __unmarshalSimpleNamedFragmentRandomItemContent( + src, dst) + if err != nil { + return fmt.Errorf( + "unable to unmarshal SimpleNamedFragmentResponse.RandomItem: %w", err) + } + } + } + + { + dst := &v.RandomLeaf + src := firstPass.RandomLeaf + if len(src) != 0 && string(src) != "null" { + err = __unmarshalSimpleNamedFragmentRandomLeafLeafContent( + src, dst) + if err != nil { + return fmt.Errorf( + "unable to unmarshal SimpleNamedFragmentResponse.RandomLeaf: %w", err) + } + } + } + return nil +} + +type __premarshalSimpleNamedFragmentResponse struct { + RandomItem json.RawMessage `json:"randomItem"` + + RandomLeaf json.RawMessage `json:"randomLeaf"` +} + +func (v *SimpleNamedFragmentResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *SimpleNamedFragmentResponse) __premarshalJSON() (*__premarshalSimpleNamedFragmentResponse, error) { + var retval __premarshalSimpleNamedFragmentResponse + + { + + dst := &retval.RandomItem + src := v.RandomItem + var err error + *dst, err = __marshalSimpleNamedFragmentRandomItemContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal SimpleNamedFragmentResponse.RandomItem: %w", err) + } + } + { + + dst := &retval.RandomLeaf + src := v.RandomLeaf + var err error + *dst, err = __marshalSimpleNamedFragmentRandomLeafLeafContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal SimpleNamedFragmentResponse.RandomLeaf: %w", err) + } + } + return &retval, nil +} + +// VideoFields includes the GraphQL fields of Video requested by the fragment VideoFields. +type VideoFields struct { + // ID is documented in the Content interface. + Id string `json:"id"` + Name string `json:"name"` + Url string `json:"url"` + Duration int `json:"duration"` + Thumbnail VideoFieldsThumbnail `json:"thumbnail"` +} + +// GetId returns VideoFields.Id, and is useful for accessing the field via an interface. +func (v *VideoFields) GetId() string { return v.Id } + +// GetName returns VideoFields.Name, and is useful for accessing the field via an interface. +func (v *VideoFields) GetName() string { return v.Name } + +// GetUrl returns VideoFields.Url, and is useful for accessing the field via an interface. +func (v *VideoFields) GetUrl() string { return v.Url } + +// GetDuration returns VideoFields.Duration, and is useful for accessing the field via an interface. +func (v *VideoFields) GetDuration() int { return v.Duration } + +// GetThumbnail returns VideoFields.Thumbnail, and is useful for accessing the field via an interface. +func (v *VideoFields) GetThumbnail() VideoFieldsThumbnail { return v.Thumbnail } + +// VideoFieldsThumbnail includes the requested fields of the GraphQL type Thumbnail. +type VideoFieldsThumbnail struct { + Id string `json:"id"` +} + +// GetId returns VideoFieldsThumbnail.Id, and is useful for accessing the field via an interface. +func (v *VideoFieldsThumbnail) GetId() string { return v.Id } + +// The query executed by SimpleNamedFragment. +const SimpleNamedFragment_Operation = ` +query SimpleNamedFragment { + randomItem { + __typename + id + name + ... VideoFields + } + randomLeaf { + __typename + ... VideoFields + } +} +fragment VideoFields on Video { + id + name + url + duration + thumbnail { + id + } +} +` + +func SimpleNamedFragment( + ctx_ context.Context, + client_ graphql.Client, +) (data_ *SimpleNamedFragmentResponse, err_ error) { + req_ := &graphql.Request{ + OpName: "SimpleNamedFragment", + Query: SimpleNamedFragment_Operation, + } + + data_ = &SimpleNamedFragmentResponse{} + resp_ := &graphql.Response{Data: data_} + + err_ = client_.MakeRequest( + ctx_, + req_, + resp_, + ) + + return data_, err_ +} + diff --git a/generate/testdata/snapshots/TestValidConfigs-Empty.yml b/generate/testdata/snapshots/TestValidConfigs-Empty.yml index ee5e78ca..37fd8e28 100644 --- a/generate/testdata/snapshots/TestValidConfigs-Empty.yml +++ b/generate/testdata/snapshots/TestValidConfigs-Empty.yml @@ -17,6 +17,7 @@ OptionalGenericType: (string) "", StructReferences: (bool) false, Extensions: (bool) false, + OmitUnreferencedImplementations: (bool) false, baseDir: (string) (len=20) "testdata/validConfig", pkgPath: (string) (len=55) "github.com/Khan/genqlient/generate/testdata/validConfig" }) diff --git a/generate/testdata/snapshots/TestValidConfigs-Lists.yaml b/generate/testdata/snapshots/TestValidConfigs-Lists.yaml index 866448d6..20a41219 100644 --- a/generate/testdata/snapshots/TestValidConfigs-Lists.yaml +++ b/generate/testdata/snapshots/TestValidConfigs-Lists.yaml @@ -23,6 +23,7 @@ OptionalGenericType: (string) "", StructReferences: (bool) false, Extensions: (bool) false, + OmitUnreferencedImplementations: (bool) false, baseDir: (string) (len=20) "testdata/validConfig", pkgPath: (string) (len=55) "github.com/Khan/genqlient/generate/testdata/validConfig" }) diff --git a/generate/testdata/snapshots/TestValidConfigs-Strings.yaml b/generate/testdata/snapshots/TestValidConfigs-Strings.yaml index 30a578c2..15e0f447 100644 --- a/generate/testdata/snapshots/TestValidConfigs-Strings.yaml +++ b/generate/testdata/snapshots/TestValidConfigs-Strings.yaml @@ -21,6 +21,7 @@ OptionalGenericType: (string) "", StructReferences: (bool) false, Extensions: (bool) false, + OmitUnreferencedImplementations: (bool) false, baseDir: (string) (len=20) "testdata/validConfig", pkgPath: (string) (len=55) "github.com/Khan/genqlient/generate/testdata/validConfig" }) diff --git a/internal/integration/generated.go b/internal/integration/generated.go index 87e5c36b..fd034833 100644 --- a/internal/integration/generated.go +++ b/internal/integration/generated.go @@ -106,11 +106,23 @@ type AnimalFieldsHairBeingsHair struct { // GetHasHair returns AnimalFieldsHairBeingsHair.HasHair, and is useful for accessing the field via an interface. func (v *AnimalFieldsHairBeingsHair) GetHasHair() bool { return v.HasHair } +// AnimalFieldsOwnerAnimal includes the requested fields of the GraphQL type Animal. +type AnimalFieldsOwnerAnimal struct { + Typename string `json:"__typename"` + Id string `json:"id"` +} + +// GetTypename returns AnimalFieldsOwnerAnimal.Typename, and is useful for accessing the field via an interface. +func (v *AnimalFieldsOwnerAnimal) GetTypename() string { return v.Typename } + +// GetId returns AnimalFieldsOwnerAnimal.Id, and is useful for accessing the field via an interface. +func (v *AnimalFieldsOwnerAnimal) GetId() string { return v.Id } + // AnimalFieldsOwnerBeing includes the requested fields of the GraphQL interface Being. // // AnimalFieldsOwnerBeing is implemented by the following types: +// AnimalFieldsOwnerAnimal // AnimalFieldsOwnerUser -// AnimalFieldsOwnerOther type AnimalFieldsOwnerBeing interface { implementsGraphQLInterfaceAnimalFieldsOwnerBeing() // GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values). @@ -119,8 +131,8 @@ type AnimalFieldsOwnerBeing interface { GetId() string } -func (v *AnimalFieldsOwnerUser) implementsGraphQLInterfaceAnimalFieldsOwnerBeing() {} -func (v *AnimalFieldsOwnerOther) implementsGraphQLInterfaceAnimalFieldsOwnerBeing() {} +func (v *AnimalFieldsOwnerAnimal) implementsGraphQLInterfaceAnimalFieldsOwnerBeing() {} +func (v *AnimalFieldsOwnerUser) implementsGraphQLInterfaceAnimalFieldsOwnerBeing() {} func __unmarshalAnimalFieldsOwnerBeing(b []byte, v *AnimalFieldsOwnerBeing) error { if string(b) == "null" { @@ -136,6 +148,9 @@ func __unmarshalAnimalFieldsOwnerBeing(b []byte, v *AnimalFieldsOwnerBeing) erro } switch tn.TypeName { + case "Animal": + *v = new(AnimalFieldsOwnerAnimal) + return json.Unmarshal(b, *v) case "User": *v = new(AnimalFieldsOwnerUser) return json.Unmarshal(b, *v) @@ -143,8 +158,8 @@ func __unmarshalAnimalFieldsOwnerBeing(b []byte, v *AnimalFieldsOwnerBeing) erro return fmt.Errorf( "response was missing Being.__typename") default: - *v = new(AnimalFieldsOwnerOther) - return json.Unmarshal(b, *v) + return fmt.Errorf( + `unexpected concrete type for AnimalFieldsOwnerBeing: "%v"`, tn.TypeName) } } @@ -152,6 +167,14 @@ func __marshalAnimalFieldsOwnerBeing(v *AnimalFieldsOwnerBeing) ([]byte, error) var typename string switch v := (*v).(type) { + case *AnimalFieldsOwnerAnimal: + typename = "Animal" + + result := struct { + TypeName string `json:"__typename"` + *AnimalFieldsOwnerAnimal + }{typename, v} + return json.Marshal(result) case *AnimalFieldsOwnerUser: typename = "User" @@ -164,9 +187,6 @@ func __marshalAnimalFieldsOwnerBeing(v *AnimalFieldsOwnerBeing) ([]byte, error) *__premarshalAnimalFieldsOwnerUser }{typename, premarshaled} return json.Marshal(result) - case *AnimalFieldsOwnerOther: - - return json.Marshal(v) case nil: return []byte("null"), nil default: @@ -175,18 +195,6 @@ func __marshalAnimalFieldsOwnerBeing(v *AnimalFieldsOwnerBeing) ([]byte, error) } } -// AnimalFieldsOwnerOther includes the requested fields of the GraphQL type Being. -type AnimalFieldsOwnerOther struct { - Typename string `json:"__typename"` - Id string `json:"id"` -} - -// GetTypename returns AnimalFieldsOwnerOther.Typename, and is useful for accessing the field via an interface. -func (v *AnimalFieldsOwnerOther) GetTypename() string { return v.Typename } - -// GetId returns AnimalFieldsOwnerOther.Id, and is useful for accessing the field via an interface. -func (v *AnimalFieldsOwnerOther) GetId() string { return v.Id } - // AnimalFieldsOwnerUser includes the requested fields of the GraphQL type User. type AnimalFieldsOwnerUser struct { Typename string `json:"__typename"` @@ -280,8 +288,8 @@ func (v *FriendsFields) GetName() string { return v.Name } // InnerBeingFields includes the GraphQL fields of Being requested by the fragment InnerBeingFields. // // InnerBeingFields is implemented by the following types: +// InnerBeingFieldsAnimal // InnerBeingFieldsUser -// InnerBeingFieldsOther type InnerBeingFields interface { implementsGraphQLInterfaceInnerBeingFields() // GetId returns the interface-field "id" from its implementation. @@ -290,8 +298,8 @@ type InnerBeingFields interface { GetName() string } -func (v *InnerBeingFieldsUser) implementsGraphQLInterfaceInnerBeingFields() {} -func (v *InnerBeingFieldsOther) implementsGraphQLInterfaceInnerBeingFields() {} +func (v *InnerBeingFieldsAnimal) implementsGraphQLInterfaceInnerBeingFields() {} +func (v *InnerBeingFieldsUser) implementsGraphQLInterfaceInnerBeingFields() {} func __unmarshalInnerBeingFields(b []byte, v *InnerBeingFields) error { if string(b) == "null" { @@ -307,6 +315,9 @@ func __unmarshalInnerBeingFields(b []byte, v *InnerBeingFields) error { } switch tn.TypeName { + case "Animal": + *v = new(InnerBeingFieldsAnimal) + return json.Unmarshal(b, *v) case "User": *v = new(InnerBeingFieldsUser) return json.Unmarshal(b, *v) @@ -314,8 +325,8 @@ func __unmarshalInnerBeingFields(b []byte, v *InnerBeingFields) error { return fmt.Errorf( "response was missing Being.__typename") default: - *v = new(InnerBeingFieldsOther) - return json.Unmarshal(b, *v) + return fmt.Errorf( + `unexpected concrete type for InnerBeingFields: "%v"`, tn.TypeName) } } @@ -323,6 +334,14 @@ func __marshalInnerBeingFields(v *InnerBeingFields) ([]byte, error) { var typename string switch v := (*v).(type) { + case *InnerBeingFieldsAnimal: + typename = "Animal" + + result := struct { + TypeName string `json:"__typename"` + *InnerBeingFieldsAnimal + }{typename, v} + return json.Marshal(result) case *InnerBeingFieldsUser: typename = "User" @@ -331,9 +350,6 @@ func __marshalInnerBeingFields(v *InnerBeingFields) ([]byte, error) { *InnerBeingFieldsUser }{typename, v} return json.Marshal(result) - case *InnerBeingFieldsOther: - - return json.Marshal(v) case nil: return []byte("null"), nil default: @@ -342,17 +358,17 @@ func __marshalInnerBeingFields(v *InnerBeingFields) ([]byte, error) { } } -// InnerBeingFieldsOther includes the requested fields of the GraphQL type Being. -type InnerBeingFieldsOther struct { +// InnerBeingFields includes the GraphQL fields of Animal requested by the fragment InnerBeingFields. +type InnerBeingFieldsAnimal struct { Id string `json:"id"` Name string `json:"name"` } -// GetId returns InnerBeingFieldsOther.Id, and is useful for accessing the field via an interface. -func (v *InnerBeingFieldsOther) GetId() string { return v.Id } +// GetId returns InnerBeingFieldsAnimal.Id, and is useful for accessing the field via an interface. +func (v *InnerBeingFieldsAnimal) GetId() string { return v.Id } -// GetName returns InnerBeingFieldsOther.Name, and is useful for accessing the field via an interface. -func (v *InnerBeingFieldsOther) GetName() string { return v.Name } +// GetName returns InnerBeingFieldsAnimal.Name, and is useful for accessing the field via an interface. +func (v *InnerBeingFieldsAnimal) GetName() string { return v.Name } // InnerBeingFields includes the GraphQL fields of User requested by the fragment InnerBeingFields. type InnerBeingFieldsUser struct { From 115d5392d262b3c1e3ed0329834b1b20b2f827d2 Mon Sep 17 00:00:00 2001 From: Will Roden Date: Mon, 25 May 2026 14:17:12 -0500 Subject: [PATCH 03/12] harden omit_unreferenced_implementations after review --- docs/CHANGELOG.md | 9 + docs/genqlient.yaml | 28 +- generate/config.go | 14 +- generate/convert.go | 200 ++++-- generate/generate_test.go | 3 + generate/marshal_helper.go.tmpl | 21 +- .../OmitImplsNamedFragmentOnInterface.graphql | 13 + .../testdata/queries/OmitImplsUnion.graphql | 9 + ...itImplsNamedFragmentOnInterface.graphql.go | 584 ++++++++++++++++++ ...ImplsNamedFragmentOnInterface.graphql.json | 9 + ...plsUnion.graphql-OmitImplsUnion.graphql.go | 219 +++++++ ...sUnion.graphql-OmitImplsUnion.graphql.json | 9 + ...ents.graphql-testdata-queries-generated.go | 466 ++++++++++++++ ...face.graphql-testdata-queries-generated.go | 458 ++++++++++++++ ...nion.graphql-testdata-queries-generated.go | 215 +++++++ ...ment.graphql-testdata-queries-generated.go | 29 +- ...ment.graphql-testdata-queries-generated.go | 50 +- generate/unmarshal_helper.go.tmpl | 3 + internal/integration/omitimpls/generated.go | 235 +++++++ internal/integration/omitimpls/genqlient.yaml | 11 + .../integration/omitimpls/omitimpls_test.go | 72 +++ 21 files changed, 2548 insertions(+), 109 deletions(-) create mode 100644 generate/testdata/queries/OmitImplsNamedFragmentOnInterface.graphql create mode 100644 generate/testdata/queries/OmitImplsUnion.graphql create mode 100644 generate/testdata/snapshots/TestGenerate-OmitImplsNamedFragmentOnInterface.graphql-OmitImplsNamedFragmentOnInterface.graphql.go create mode 100644 generate/testdata/snapshots/TestGenerate-OmitImplsNamedFragmentOnInterface.graphql-OmitImplsNamedFragmentOnInterface.graphql.json create mode 100644 generate/testdata/snapshots/TestGenerate-OmitImplsUnion.graphql-OmitImplsUnion.graphql.go create mode 100644 generate/testdata/snapshots/TestGenerate-OmitImplsUnion.graphql-OmitImplsUnion.graphql.json create mode 100644 generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-InterfaceNoFragments.graphql-testdata-queries-generated.go create mode 100644 generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsNamedFragmentOnInterface.graphql-testdata-queries-generated.go create mode 100644 generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsUnion.graphql-testdata-queries-generated.go create mode 100644 internal/integration/omitimpls/generated.go create mode 100644 internal/integration/omitimpls/genqlient.yaml create mode 100644 internal/integration/omitimpls/omitimpls_test.go diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 7e6fe965..78a6befc 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -29,6 +29,15 @@ Note that genqlient now requires Go 1.23 or higher, and is tested through Go 1.2 ### New features: - Added `--version` flag to print version information including commit hash and build date +- Added `omit_unreferenced_implementations` config option. When enabled, genqlient + skips generating per-type structs for interface and union implementations + that aren't referenced by an inline or named fragment in the selection set; + a single catch-all struct per interface selection carries the shared + fields and absorbs any `__typename` the server returns that wasn't + explicitly fragment-conditioned. This dramatically reduces generated + code size for interfaces with many implementations (e.g. Relay-style + `Node`) and gracefully decodes unknown concrete types instead of + returning an error. See [#416](https://github.com/Khan/genqlient/issues/416). ### Bug fixes: diff --git a/docs/genqlient.yaml b/docs/genqlient.yaml index 95974e14..5d3f12fe 100644 --- a/docs/genqlient.yaml +++ b/docs/genqlient.yaml @@ -104,20 +104,26 @@ use_struct_references: boolean # Defaults to false. use_extensions: boolean -# If set, when a query uses fragments on an interface or union type, -# genqlient will only generate full Go struct types for the concrete -# types explicitly referenced in those fragments. All other possible -# implementation types are collapsed into a single catch-all "Other" -# struct that contains only the shared fields. Unknown __typename -# values are unmarshaled into this Other type instead of returning an -# error. +# If set, genqlient will skip generating per-type structs for interface +# and union implementations that aren't referenced via an inline fragment +# or named fragment spread in the selection set. Instead, a single +# catch-all struct is generated per interface selection point, carrying +# only the interface's shared fields (including __typename). At runtime, +# any __typename returned by the server that doesn't match one of the +# explicitly-referenced types is decoded into the catch-all -- preserving +# the graceful fallback behavior that existed before this option was +# added, while dramatically reducing the size of generated code for +# interfaces with many implementations (e.g. Relay-style Node). # -# When false (the default), genqlient generates a separate Go struct -# for every possible implementation type, and returns an error if the -# server sends an unrecognized __typename. +# Type conditions that name an interface or union (rather than a concrete +# Object type) are NOT treated as referencing any concrete implementation: +# the optimization is only defeated by inline fragments or named fragments +# whose type condition is itself an Object type. This means a fragment on +# Node spread at a Node-typed field still collapses to the catch-all +# unless one of its (possibly nested) fragments targets a concrete type. # # Defaults to false. -omit_unreferenced_implementations: false +omit_unreferenced_implementations: boolean # Customize how models are generated for optional fields. This can currently # be set to one of the following values: diff --git a/generate/config.go b/generate/config.go index 7ad53208..663f2530 100644 --- a/generate/config.go +++ b/generate/config.go @@ -34,9 +34,17 @@ type Config struct { Casing Casing `yaml:"casing"` Optional string `yaml:"optional"` OptionalGenericType string `yaml:"optional_generic_type"` - StructReferences bool `yaml:"use_struct_references"` - Extensions bool `yaml:"use_extensions"` - OmitUnreferencedImplementations bool `yaml:"omit_unreferenced_implementations"` + StructReferences bool `yaml:"use_struct_references"` + Extensions bool `yaml:"use_extensions"` + + // OmitUnreferencedImplementations, when true, makes genqlient skip + // generating per-type structs for interface/union implementations that + // are not referenced by any inline or named fragment in the selection + // set. A single catch-all struct is generated per interface selection + // to carry the shared fields and preserve the previous fallback + // behavior for any __typename returned by the server that we don't + // have a typed representation for. + OmitUnreferencedImplementations bool `yaml:"omit_unreferenced_implementations"` // The directory of the config-file (relative to which all the other paths // are resolved). Set by ValidateAndFillDefaults. diff --git a/generate/convert.go b/generate/convert.go index a8d219dd..cc5656b8 100644 --- a/generate/convert.go +++ b/generate/convert.go @@ -523,14 +523,24 @@ func (g *generator) convertDefinition( // Make sure we generate stable output by sorting the types by name when we get them sort.Slice(implementationTypes, func(i, j int) bool { return implementationTypes[i].Name < implementationTypes[j].Name }) - // If omit_unreferenced_implementations is enabled, determine which + // When omit_unreferenced_implementations is enabled, determine which // concrete types are explicitly referenced by fragments in the - // selection set. If any are, we only generate full implementation - // structs for those types, and a single catch-all "other" struct - // for everything else. - var referencedTypes map[string]bool + // selection set, and skip generating per-type structs for the rest. + // Anything we skip is handled at runtime by a single catch-all + // struct carrying only the interface's shared fields. + filteredImpls := implementationTypes + var omittedAny bool if g.Config.OmitUnreferencedImplementations { - referencedTypes = g.collectReferencedTypes(selectionSet) + referenced := collectReferencedConcreteTypes(selectionSet, g.schema) + kept := make([]*ast.Definition, 0, len(implementationTypes)) + for _, implDef := range implementationTypes { + if referenced[implDef.Name] { + kept = append(kept, implDef) + } else { + omittedAny = true + } + } + filteredImpls = kept } goType := &goInterfaceType{ @@ -540,10 +550,7 @@ func (g *generator) convertDefinition( descriptionInfo: desc, } - for _, implDef := range implementationTypes { - if len(referencedTypes) > 0 && !referencedTypes[implDef.Name] { - continue - } + for _, implDef := range filteredImpls { // TODO(benkraft): In principle we should skip generating a Go // field for __typename each of these impl-defs if you didn't // request it (and it was automatically added by @@ -564,19 +571,12 @@ func (g *generator) convertDefinition( goType.Implementations = append(goType.Implementations, implStructTyp) } - if len(referencedTypes) > 0 && len(goType.Implementations) < len(implementationTypes) { - otherName := makeTypeName(namePrefix, "other", g.Config.GetDefaultCasingAlgorithm()) - otherType := &goStructType{ - GoName: otherName, - Fields: sharedFields, - Selection: selectionSet, - descriptionInfo: descriptionInfo{ - GraphQLName: def.Name, - }, - Generator: g, + if omittedAny { + otherType, err := g.makeCatchAllStruct(name, def.Name, sharedFields, selectionSet, pos) + if err != nil { + return nil, err } goType.OtherImplementation = otherType - g.typeMap[otherName] = otherType } return g.addType(goType, goType.GoName, pos) @@ -742,30 +742,117 @@ func (g *generator) convertSelectionSet( return uniqFields, nil } -// collectReferencedTypes returns the set of concrete type names that are -// explicitly referenced by inline fragments or named fragment spreads in the -// given selection set. This is used to determine which implementation types -// need full structs generated, vs. which can share a single catch-all "other" -// struct. -func (g *generator) collectReferencedTypes(selectionSet ast.SelectionSet) map[string]bool { - referenced := make(map[string]bool) - for _, selection := range selectionSet { - switch sel := selection.(type) { - case *ast.InlineFragment: - fragmentTypeDef := g.schema.Types[sel.TypeCondition] - for _, t := range g.schema.GetPossibleTypes(fragmentTypeDef) { - referenced[t.Name] = true - } - case *ast.FragmentSpread: - fragmentTypeDef := sel.Definition.Definition - for _, t := range g.schema.GetPossibleTypes(fragmentTypeDef) { - referenced[t.Name] = true +// collectReferencedConcreteTypes walks an interface/union selection set and +// returns the set of concrete (Object) type names referenced via inline or +// named fragments, including transitively through nested fragments. Type +// conditions that name an interface or union are NOT expanded to all their +// possible concrete types — we only recurse into the fragment body. This +// makes OmitUnreferencedImplementations meaningful when fragments are spread +// on abstract types (e.g. a fragment on Node spread at a Node-typed field). +func collectReferencedConcreteTypes(sel ast.SelectionSet, schema *ast.Schema) map[string]bool { + referenced := map[string]bool{} + addCondition := func(condName string) { + if condName == "" { + return + } + if condDef := schema.Types[condName]; condDef != nil && condDef.Kind == ast.Object { + referenced[condName] = true + } + } + var walk func(sel ast.SelectionSet) + walk = func(sel ast.SelectionSet) { + for _, s := range sel { + switch s := s.(type) { + case *ast.InlineFragment: + addCondition(s.TypeCondition) + walk(s.SelectionSet) + case *ast.FragmentSpread: + if s.Definition != nil { + addCondition(s.Definition.TypeCondition) + walk(s.Definition.SelectionSet) + } } + // Plain field selections don't introduce new type conditions + // at this level; nested type conditions are processed when we + // convert those fields' own selection sets. } } + walk(sel) return referenced } +// makeCatchAllStruct builds the catch-all goStructType used by +// OmitUnreferencedImplementations and registers it via g.addType. The +// catch-all carries only the interface's SharedFields (which always include +// __typename, injected by preprocessQueryDocument). At runtime the +// unmarshal-helper instantiates this struct whenever the server returns a +// __typename that doesn't match an explicitly-referenced implementation. +// +// When SharedFields contains an embedded fragment-spread whose GoType is +// itself a *goInterfaceType (a fragment on an abstract type), we substitute +// the fragment-interface's own catch-all so the resulting Go type is a +// valid struct embedding only other structs. This requires the +// option-aware path to have produced a catch-all for that fragment; in +// practice that's always true when this code runs, because the same +// OmitUnreferencedImplementations option drove its construction. +func (g *generator) makeCatchAllStruct( + interfaceGoName, graphQLName string, + sharedFields []*goStructField, + selectionSet ast.SelectionSet, + pos *ast.Position, +) (*goStructType, error) { + catchAllFields := make([]*goStructField, 0, len(sharedFields)) + for _, f := range sharedFields { + if f.GoName == "" { + iface, ok := f.GoType.(*goInterfaceType) + if ok { + if iface.OtherImplementation == nil { + return nil, errorf(pos, + "genqlient internal error: cannot build catch-all for %s: "+ + "embedded fragment %s has no catch-all of its own "+ + "(this is expected to be impossible when "+ + "omit_unreferenced_implementations is enabled)", + interfaceGoName, iface.GoName) + } + swapped := *f + swapped.GoType = iface.OtherImplementation + catchAllFields = append(catchAllFields, &swapped) + continue + } + } + catchAllFields = append(catchAllFields, f) + } + + catchAllName := interfaceGoName + "Other" + catchAll := &goStructType{ + GoName: catchAllName, + Fields: catchAllFields, + Selection: selectionSet, + descriptionInfo: descriptionInfo{ + GraphQLName: graphQLName, + CommentOverride: fmt.Sprintf( + "%s is the catch-all type used by %s for any concrete\n"+ + "GraphQL type returned by the server that doesn't have its own\n"+ + "generated struct (because no fragment selected it). It carries\n"+ + "only the interface's shared fields; the concrete GraphQL type\n"+ + "name is available via the __typename field.", + catchAllName, interfaceGoName), + }, + Generator: g, + } + registered, err := g.addType(catchAll, catchAll.GoName, pos) + if err != nil { + return nil, err + } + catchAllTyp, ok := registered.(*goStructType) + if !ok { + return nil, errorf(pos, + "genqlient internal error: catch-all for %s registered as %T, not *goStructType", + interfaceGoName, registered) + } + return catchAllTyp, nil +} + // fragmentMatches returns true if the given fragment is "active" when applied // to the given type. // @@ -940,9 +1027,19 @@ func (g *generator) convertNamedFragment(fragment *ast.FragmentDefinition) (goTy // Make sure we generate stable output by sorting the types by name when we get them sort.Slice(implementationTypes, func(i, j int) bool { return implementationTypes[i].Name < implementationTypes[j].Name }) - var referencedTypes map[string]bool + filteredImpls := implementationTypes + var omittedAny bool if g.Config.OmitUnreferencedImplementations { - referencedTypes = g.collectReferencedTypes(fragment.SelectionSet) + referenced := collectReferencedConcreteTypes(fragment.SelectionSet, g.schema) + kept := make([]*ast.Definition, 0, len(implementationTypes)) + for _, implDef := range implementationTypes { + if referenced[implDef.Name] { + kept = append(kept, implDef) + } else { + omittedAny = true + } + } + filteredImpls = kept } goType := &goInterfaceType{ @@ -953,11 +1050,7 @@ func (g *generator) convertNamedFragment(fragment *ast.FragmentDefinition) (goTy } g.typeMap[fragment.Name] = goType - for _, implDef := range implementationTypes { - if len(referencedTypes) > 0 && !referencedTypes[implDef.Name] { - continue - } - + for _, implDef := range filteredImpls { implFields, err := g.convertSelectionSet( newPrefixList(fragment.Name), fragment.SelectionSet, implDef, directive) if err != nil { @@ -978,19 +1071,12 @@ func (g *generator) convertNamedFragment(fragment *ast.FragmentDefinition) (goTy g.typeMap[implTyp.GoName] = implTyp } - if len(referencedTypes) > 0 && len(goType.Implementations) < len(implementationTypes) { - otherName := fragment.Name + "Other" - otherType := &goStructType{ - GoName: otherName, - Fields: fields, - Selection: fragment.SelectionSet, - descriptionInfo: descriptionInfo{ - GraphQLName: typ.Name, - }, - Generator: g, + if omittedAny { + otherType, err := g.makeCatchAllStruct(fragment.Name, typ.Name, fields, fragment.SelectionSet, fragment.Position) + if err != nil { + return nil, err } goType.OtherImplementation = otherType - g.typeMap[otherName] = otherType } return goType, nil diff --git a/generate/generate_test.go b/generate/generate_test.go index 2ba3c978..3239f6c2 100644 --- a/generate/generate_test.go +++ b/generate/generate_test.go @@ -278,6 +278,9 @@ func TestGenerateWithConfig(t *testing.T) { "OmitUnreferencedImplementations", "", []string{ "SimpleInlineFragment.graphql", "SimpleNamedFragment.graphql", + "InterfaceNoFragments.graphql", + "OmitImplsNamedFragmentOnInterface.graphql", + "OmitImplsUnion.graphql", }, &Config{ OmitUnreferencedImplementations: true, }, diff --git a/generate/marshal_helper.go.tmpl b/generate/marshal_helper.go.tmpl index e5540520..8057e3b3 100644 --- a/generate/marshal_helper.go.tmpl +++ b/generate/marshal_helper.go.tmpl @@ -6,7 +6,9 @@ func __marshal{{.GoName}}(v *{{.GoName}}) ([]byte, error) { {{/* Determine the GraphQL typename, which the unmarshaler will need should it be called on our output. */}} + {{if .Implementations -}} var typename string + {{end -}} switch v := (*v).(type) { {{range .Implementations -}} case *{{.GoName}}: @@ -37,9 +39,22 @@ func __marshal{{.GoName}}(v *{{.GoName}}) ([]byte, error) { {{end -}} {{if .OtherImplementation -}} case *{{.OtherImplementation.GoName}}: - {{/* For the catch-all "other" type, marshal directly; the struct - already has __typename populated from unmarshaling. */}} - return {{ref "encoding/json.Marshal"}}(v) + {{/* Catch-all from OmitUnreferencedImplementations. Its own + __typename field carries the concrete GraphQL type-name, so we + don't need to splice it in with a wrapper struct like we do for + the typed implementations above. This assumes the caller + populated __typename (which is always true for values produced + by our unmarshal-helper; callers constructing a value + programmatically should set it themselves). */ -}} + {{if .OtherImplementation.NeedsMarshaling -}} + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) + {{else -}} + return json.Marshal(v) + {{end -}} {{end -}} case nil: return []byte("null"), nil diff --git a/generate/testdata/queries/OmitImplsNamedFragmentOnInterface.graphql b/generate/testdata/queries/OmitImplsNamedFragmentOnInterface.graphql new file mode 100644 index 00000000..0a704f18 --- /dev/null +++ b/generate/testdata/queries/OmitImplsNamedFragmentOnInterface.graphql @@ -0,0 +1,13 @@ +fragment NamedContentFields on Content { + id + name + ... on Article { + text + } +} + +query OmitImplsNamedFragmentOnInterface { + randomItem { + ...NamedContentFields + } +} diff --git a/generate/testdata/queries/OmitImplsUnion.graphql b/generate/testdata/queries/OmitImplsUnion.graphql new file mode 100644 index 00000000..6d107bf0 --- /dev/null +++ b/generate/testdata/queries/OmitImplsUnion.graphql @@ -0,0 +1,9 @@ +query OmitImplsUnion { + randomLeaf { + __typename + ... on Article { + id + text + } + } +} diff --git a/generate/testdata/snapshots/TestGenerate-OmitImplsNamedFragmentOnInterface.graphql-OmitImplsNamedFragmentOnInterface.graphql.go b/generate/testdata/snapshots/TestGenerate-OmitImplsNamedFragmentOnInterface.graphql-OmitImplsNamedFragmentOnInterface.graphql.go new file mode 100644 index 00000000..c685036a --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-OmitImplsNamedFragmentOnInterface.graphql-OmitImplsNamedFragmentOnInterface.graphql.go @@ -0,0 +1,584 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package test + +import ( + "encoding/json" + "fmt" + + "github.com/Khan/genqlient/graphql" + "github.com/Khan/genqlient/internal/testutil" +) + +// NamedContentFields includes the GraphQL fields of Content requested by the fragment NamedContentFields. +// The GraphQL type's documentation follows. +// +// Content is implemented by various types like Article, Video, and Topic. +// +// NamedContentFields is implemented by the following types: +// NamedContentFieldsArticle +// NamedContentFieldsTopic +// NamedContentFieldsVideo +type NamedContentFields interface { + implementsGraphQLInterfaceNamedContentFields() + // 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 *NamedContentFieldsArticle) implementsGraphQLInterfaceNamedContentFields() {} +func (v *NamedContentFieldsTopic) implementsGraphQLInterfaceNamedContentFields() {} +func (v *NamedContentFieldsVideo) implementsGraphQLInterfaceNamedContentFields() {} + +func __unmarshalNamedContentFields(b []byte, v *NamedContentFields) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "Article": + *v = new(NamedContentFieldsArticle) + return json.Unmarshal(b, *v) + case "Topic": + *v = new(NamedContentFieldsTopic) + return json.Unmarshal(b, *v) + case "Video": + *v = new(NamedContentFieldsVideo) + return json.Unmarshal(b, *v) + case "": + return fmt.Errorf( + "response was missing Content.__typename") + default: + return fmt.Errorf( + `unexpected concrete type for NamedContentFields: "%v"`, tn.TypeName) + } +} + +func __marshalNamedContentFields(v *NamedContentFields) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *NamedContentFieldsArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *NamedContentFieldsArticle + }{typename, v} + return json.Marshal(result) + case *NamedContentFieldsTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *NamedContentFieldsTopic + }{typename, v} + return json.Marshal(result) + case *NamedContentFieldsVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *NamedContentFieldsVideo + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for NamedContentFields: "%T"`, v) + } +} + +// NamedContentFields includes the GraphQL fields of Article requested by the fragment NamedContentFields. +// The GraphQL type's documentation follows. +// +// Content is implemented by various types like Article, Video, and Topic. +type NamedContentFieldsArticle struct { + // ID is the identifier of the content. + Id testutil.ID `json:"id"` + Name string `json:"name"` + Text string `json:"text"` +} + +// GetId returns NamedContentFieldsArticle.Id, and is useful for accessing the field via an interface. +func (v *NamedContentFieldsArticle) GetId() testutil.ID { return v.Id } + +// GetName returns NamedContentFieldsArticle.Name, and is useful for accessing the field via an interface. +func (v *NamedContentFieldsArticle) GetName() string { return v.Name } + +// GetText returns NamedContentFieldsArticle.Text, and is useful for accessing the field via an interface. +func (v *NamedContentFieldsArticle) GetText() string { return v.Text } + +// NamedContentFields includes the GraphQL fields of Topic requested by the fragment NamedContentFields. +// The GraphQL type's documentation follows. +// +// Content is implemented by various types like Article, Video, and Topic. +type NamedContentFieldsTopic struct { + // ID is the identifier of the content. + Id testutil.ID `json:"id"` + Name string `json:"name"` +} + +// GetId returns NamedContentFieldsTopic.Id, and is useful for accessing the field via an interface. +func (v *NamedContentFieldsTopic) GetId() testutil.ID { return v.Id } + +// GetName returns NamedContentFieldsTopic.Name, and is useful for accessing the field via an interface. +func (v *NamedContentFieldsTopic) GetName() string { return v.Name } + +// NamedContentFields includes the GraphQL fields of Video requested by the fragment NamedContentFields. +// The GraphQL type's documentation follows. +// +// Content is implemented by various types like Article, Video, and Topic. +type NamedContentFieldsVideo struct { + // ID is the identifier of the content. + Id testutil.ID `json:"id"` + Name string `json:"name"` +} + +// GetId returns NamedContentFieldsVideo.Id, and is useful for accessing the field via an interface. +func (v *NamedContentFieldsVideo) GetId() testutil.ID { return v.Id } + +// GetName returns NamedContentFieldsVideo.Name, and is useful for accessing the field via an interface. +func (v *NamedContentFieldsVideo) GetName() string { return v.Name } + +// OmitImplsNamedFragmentOnInterfaceRandomItemArticle includes the requested fields of the GraphQL type Article. +type OmitImplsNamedFragmentOnInterfaceRandomItemArticle struct { + Typename string `json:"__typename"` + NamedContentFieldsArticle `json:"-"` +} + +// GetTypename returns OmitImplsNamedFragmentOnInterfaceRandomItemArticle.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemArticle) GetTypename() string { return v.Typename } + +// GetId returns OmitImplsNamedFragmentOnInterfaceRandomItemArticle.Id, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemArticle) GetId() testutil.ID { + return v.NamedContentFieldsArticle.Id +} + +// GetName returns OmitImplsNamedFragmentOnInterfaceRandomItemArticle.Name, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemArticle) GetName() string { + return v.NamedContentFieldsArticle.Name +} + +// GetText returns OmitImplsNamedFragmentOnInterfaceRandomItemArticle.Text, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemArticle) GetText() string { + return v.NamedContentFieldsArticle.Text +} + +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemArticle) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *OmitImplsNamedFragmentOnInterfaceRandomItemArticle + graphql.NoUnmarshalJSON + } + firstPass.OmitImplsNamedFragmentOnInterfaceRandomItemArticle = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.NamedContentFieldsArticle) + if err != nil { + return err + } + return nil +} + +type __premarshalOmitImplsNamedFragmentOnInterfaceRandomItemArticle struct { + Typename string `json:"__typename"` + + Id testutil.ID `json:"id"` + + Name string `json:"name"` + + Text string `json:"text"` +} + +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemArticle) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemArticle) __premarshalJSON() (*__premarshalOmitImplsNamedFragmentOnInterfaceRandomItemArticle, error) { + var retval __premarshalOmitImplsNamedFragmentOnInterfaceRandomItemArticle + + retval.Typename = v.Typename + retval.Id = v.NamedContentFieldsArticle.Id + retval.Name = v.NamedContentFieldsArticle.Name + retval.Text = v.NamedContentFieldsArticle.Text + return &retval, nil +} + +// OmitImplsNamedFragmentOnInterfaceRandomItemContent includes the requested fields of the GraphQL interface Content. +// +// OmitImplsNamedFragmentOnInterfaceRandomItemContent is implemented by the following types: +// OmitImplsNamedFragmentOnInterfaceRandomItemArticle +// OmitImplsNamedFragmentOnInterfaceRandomItemTopic +// OmitImplsNamedFragmentOnInterfaceRandomItemVideo +// The GraphQL type's documentation follows. +// +// Content is implemented by various types like Article, Video, and Topic. +type OmitImplsNamedFragmentOnInterfaceRandomItemContent interface { + implementsGraphQLInterfaceOmitImplsNamedFragmentOnInterfaceRandomItemContent() + // GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values). + GetTypename() string + NamedContentFields +} + +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemArticle) implementsGraphQLInterfaceOmitImplsNamedFragmentOnInterfaceRandomItemContent() { +} +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemTopic) implementsGraphQLInterfaceOmitImplsNamedFragmentOnInterfaceRandomItemContent() { +} +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemVideo) implementsGraphQLInterfaceOmitImplsNamedFragmentOnInterfaceRandomItemContent() { +} + +func __unmarshalOmitImplsNamedFragmentOnInterfaceRandomItemContent(b []byte, v *OmitImplsNamedFragmentOnInterfaceRandomItemContent) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "Article": + *v = new(OmitImplsNamedFragmentOnInterfaceRandomItemArticle) + return json.Unmarshal(b, *v) + case "Topic": + *v = new(OmitImplsNamedFragmentOnInterfaceRandomItemTopic) + return json.Unmarshal(b, *v) + case "Video": + *v = new(OmitImplsNamedFragmentOnInterfaceRandomItemVideo) + return json.Unmarshal(b, *v) + case "": + return fmt.Errorf( + "response was missing Content.__typename") + default: + return fmt.Errorf( + `unexpected concrete type for OmitImplsNamedFragmentOnInterfaceRandomItemContent: "%v"`, tn.TypeName) + } +} + +func __marshalOmitImplsNamedFragmentOnInterfaceRandomItemContent(v *OmitImplsNamedFragmentOnInterfaceRandomItemContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *OmitImplsNamedFragmentOnInterfaceRandomItemArticle: + typename = "Article" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalOmitImplsNamedFragmentOnInterfaceRandomItemArticle + }{typename, premarshaled} + return json.Marshal(result) + case *OmitImplsNamedFragmentOnInterfaceRandomItemTopic: + typename = "Topic" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalOmitImplsNamedFragmentOnInterfaceRandomItemTopic + }{typename, premarshaled} + return json.Marshal(result) + case *OmitImplsNamedFragmentOnInterfaceRandomItemVideo: + typename = "Video" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalOmitImplsNamedFragmentOnInterfaceRandomItemVideo + }{typename, premarshaled} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for OmitImplsNamedFragmentOnInterfaceRandomItemContent: "%T"`, v) + } +} + +// OmitImplsNamedFragmentOnInterfaceRandomItemTopic includes the requested fields of the GraphQL type Topic. +type OmitImplsNamedFragmentOnInterfaceRandomItemTopic struct { + Typename string `json:"__typename"` + NamedContentFieldsTopic `json:"-"` +} + +// GetTypename returns OmitImplsNamedFragmentOnInterfaceRandomItemTopic.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemTopic) GetTypename() string { return v.Typename } + +// GetId returns OmitImplsNamedFragmentOnInterfaceRandomItemTopic.Id, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemTopic) GetId() testutil.ID { + return v.NamedContentFieldsTopic.Id +} + +// GetName returns OmitImplsNamedFragmentOnInterfaceRandomItemTopic.Name, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemTopic) GetName() string { + return v.NamedContentFieldsTopic.Name +} + +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemTopic) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *OmitImplsNamedFragmentOnInterfaceRandomItemTopic + graphql.NoUnmarshalJSON + } + firstPass.OmitImplsNamedFragmentOnInterfaceRandomItemTopic = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.NamedContentFieldsTopic) + if err != nil { + return err + } + return nil +} + +type __premarshalOmitImplsNamedFragmentOnInterfaceRandomItemTopic struct { + Typename string `json:"__typename"` + + Id testutil.ID `json:"id"` + + Name string `json:"name"` +} + +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemTopic) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemTopic) __premarshalJSON() (*__premarshalOmitImplsNamedFragmentOnInterfaceRandomItemTopic, error) { + var retval __premarshalOmitImplsNamedFragmentOnInterfaceRandomItemTopic + + retval.Typename = v.Typename + retval.Id = v.NamedContentFieldsTopic.Id + retval.Name = v.NamedContentFieldsTopic.Name + return &retval, nil +} + +// OmitImplsNamedFragmentOnInterfaceRandomItemVideo includes the requested fields of the GraphQL type Video. +type OmitImplsNamedFragmentOnInterfaceRandomItemVideo struct { + Typename string `json:"__typename"` + NamedContentFieldsVideo `json:"-"` +} + +// GetTypename returns OmitImplsNamedFragmentOnInterfaceRandomItemVideo.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemVideo) GetTypename() string { return v.Typename } + +// GetId returns OmitImplsNamedFragmentOnInterfaceRandomItemVideo.Id, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemVideo) GetId() testutil.ID { + return v.NamedContentFieldsVideo.Id +} + +// GetName returns OmitImplsNamedFragmentOnInterfaceRandomItemVideo.Name, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemVideo) GetName() string { + return v.NamedContentFieldsVideo.Name +} + +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemVideo) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *OmitImplsNamedFragmentOnInterfaceRandomItemVideo + graphql.NoUnmarshalJSON + } + firstPass.OmitImplsNamedFragmentOnInterfaceRandomItemVideo = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.NamedContentFieldsVideo) + if err != nil { + return err + } + return nil +} + +type __premarshalOmitImplsNamedFragmentOnInterfaceRandomItemVideo struct { + Typename string `json:"__typename"` + + Id testutil.ID `json:"id"` + + Name string `json:"name"` +} + +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemVideo) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemVideo) __premarshalJSON() (*__premarshalOmitImplsNamedFragmentOnInterfaceRandomItemVideo, error) { + var retval __premarshalOmitImplsNamedFragmentOnInterfaceRandomItemVideo + + retval.Typename = v.Typename + retval.Id = v.NamedContentFieldsVideo.Id + retval.Name = v.NamedContentFieldsVideo.Name + return &retval, nil +} + +// OmitImplsNamedFragmentOnInterfaceResponse is returned by OmitImplsNamedFragmentOnInterface on success. +type OmitImplsNamedFragmentOnInterfaceResponse struct { + RandomItem OmitImplsNamedFragmentOnInterfaceRandomItemContent `json:"-"` +} + +// GetRandomItem returns OmitImplsNamedFragmentOnInterfaceResponse.RandomItem, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceResponse) GetRandomItem() OmitImplsNamedFragmentOnInterfaceRandomItemContent { + return v.RandomItem +} + +func (v *OmitImplsNamedFragmentOnInterfaceResponse) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *OmitImplsNamedFragmentOnInterfaceResponse + RandomItem json.RawMessage `json:"randomItem"` + graphql.NoUnmarshalJSON + } + firstPass.OmitImplsNamedFragmentOnInterfaceResponse = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.RandomItem + src := firstPass.RandomItem + if len(src) != 0 && string(src) != "null" { + err = __unmarshalOmitImplsNamedFragmentOnInterfaceRandomItemContent( + src, dst) + if err != nil { + return fmt.Errorf( + "unable to unmarshal OmitImplsNamedFragmentOnInterfaceResponse.RandomItem: %w", err) + } + } + } + return nil +} + +type __premarshalOmitImplsNamedFragmentOnInterfaceResponse struct { + RandomItem json.RawMessage `json:"randomItem"` +} + +func (v *OmitImplsNamedFragmentOnInterfaceResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *OmitImplsNamedFragmentOnInterfaceResponse) __premarshalJSON() (*__premarshalOmitImplsNamedFragmentOnInterfaceResponse, error) { + var retval __premarshalOmitImplsNamedFragmentOnInterfaceResponse + + { + + dst := &retval.RandomItem + src := v.RandomItem + var err error + *dst, err = __marshalOmitImplsNamedFragmentOnInterfaceRandomItemContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal OmitImplsNamedFragmentOnInterfaceResponse.RandomItem: %w", err) + } + } + return &retval, nil +} + +// The query executed by OmitImplsNamedFragmentOnInterface. +const OmitImplsNamedFragmentOnInterface_Operation = ` +query OmitImplsNamedFragmentOnInterface { + randomItem { + __typename + ... NamedContentFields + } +} +fragment NamedContentFields on Content { + id + name + ... on Article { + text + } +} +` + +func OmitImplsNamedFragmentOnInterface( + client_ graphql.Client, +) (data_ *OmitImplsNamedFragmentOnInterfaceResponse, err_ error) { + req_ := &graphql.Request{ + OpName: "OmitImplsNamedFragmentOnInterface", + Query: OmitImplsNamedFragmentOnInterface_Operation, + } + + data_ = &OmitImplsNamedFragmentOnInterfaceResponse{} + resp_ := &graphql.Response{Data: data_} + + err_ = client_.MakeRequest( + nil, + req_, + resp_, + ) + + return data_, err_ +} + diff --git a/generate/testdata/snapshots/TestGenerate-OmitImplsNamedFragmentOnInterface.graphql-OmitImplsNamedFragmentOnInterface.graphql.json b/generate/testdata/snapshots/TestGenerate-OmitImplsNamedFragmentOnInterface.graphql-OmitImplsNamedFragmentOnInterface.graphql.json new file mode 100644 index 00000000..bb730e36 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-OmitImplsNamedFragmentOnInterface.graphql-OmitImplsNamedFragmentOnInterface.graphql.json @@ -0,0 +1,9 @@ +{ + "operations": [ + { + "operationName": "OmitImplsNamedFragmentOnInterface", + "query": "\nquery OmitImplsNamedFragmentOnInterface {\n\trandomItem {\n\t\t__typename\n\t\t... NamedContentFields\n\t}\n}\nfragment NamedContentFields on Content {\n\tid\n\tname\n\t... on Article {\n\t\ttext\n\t}\n}\n", + "sourceLocation": "testdata/queries/OmitImplsNamedFragmentOnInterface.graphql" + } + ] +} diff --git a/generate/testdata/snapshots/TestGenerate-OmitImplsUnion.graphql-OmitImplsUnion.graphql.go b/generate/testdata/snapshots/TestGenerate-OmitImplsUnion.graphql-OmitImplsUnion.graphql.go new file mode 100644 index 00000000..62d9c14b --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-OmitImplsUnion.graphql-OmitImplsUnion.graphql.go @@ -0,0 +1,219 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package test + +import ( + "encoding/json" + "fmt" + + "github.com/Khan/genqlient/graphql" + "github.com/Khan/genqlient/internal/testutil" +) + +// OmitImplsUnionRandomLeafArticle includes the requested fields of the GraphQL type Article. +type OmitImplsUnionRandomLeafArticle struct { + Typename string `json:"__typename"` + // ID is documented in the Content interface. + Id testutil.ID `json:"id"` + Text string `json:"text"` +} + +// GetTypename returns OmitImplsUnionRandomLeafArticle.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsUnionRandomLeafArticle) GetTypename() string { return v.Typename } + +// GetId returns OmitImplsUnionRandomLeafArticle.Id, and is useful for accessing the field via an interface. +func (v *OmitImplsUnionRandomLeafArticle) GetId() testutil.ID { return v.Id } + +// GetText returns OmitImplsUnionRandomLeafArticle.Text, and is useful for accessing the field via an interface. +func (v *OmitImplsUnionRandomLeafArticle) GetText() string { return v.Text } + +// OmitImplsUnionRandomLeafLeafContent includes the requested fields of the GraphQL interface LeafContent. +// +// OmitImplsUnionRandomLeafLeafContent is implemented by the following types: +// OmitImplsUnionRandomLeafArticle +// OmitImplsUnionRandomLeafVideo +// The GraphQL type's documentation follows. +// +// LeafContent represents content items that can't have child-nodes. +type OmitImplsUnionRandomLeafLeafContent interface { + implementsGraphQLInterfaceOmitImplsUnionRandomLeafLeafContent() + // GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values). + GetTypename() string +} + +func (v *OmitImplsUnionRandomLeafArticle) implementsGraphQLInterfaceOmitImplsUnionRandomLeafLeafContent() { +} +func (v *OmitImplsUnionRandomLeafVideo) implementsGraphQLInterfaceOmitImplsUnionRandomLeafLeafContent() { +} + +func __unmarshalOmitImplsUnionRandomLeafLeafContent(b []byte, v *OmitImplsUnionRandomLeafLeafContent) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "Article": + *v = new(OmitImplsUnionRandomLeafArticle) + return json.Unmarshal(b, *v) + case "Video": + *v = new(OmitImplsUnionRandomLeafVideo) + return json.Unmarshal(b, *v) + case "": + return fmt.Errorf( + "response was missing LeafContent.__typename") + default: + return fmt.Errorf( + `unexpected concrete type for OmitImplsUnionRandomLeafLeafContent: "%v"`, tn.TypeName) + } +} + +func __marshalOmitImplsUnionRandomLeafLeafContent(v *OmitImplsUnionRandomLeafLeafContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *OmitImplsUnionRandomLeafArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *OmitImplsUnionRandomLeafArticle + }{typename, v} + return json.Marshal(result) + case *OmitImplsUnionRandomLeafVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *OmitImplsUnionRandomLeafVideo + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for OmitImplsUnionRandomLeafLeafContent: "%T"`, v) + } +} + +// OmitImplsUnionRandomLeafVideo includes the requested fields of the GraphQL type Video. +type OmitImplsUnionRandomLeafVideo struct { + Typename string `json:"__typename"` +} + +// GetTypename returns OmitImplsUnionRandomLeafVideo.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsUnionRandomLeafVideo) GetTypename() string { return v.Typename } + +// OmitImplsUnionResponse is returned by OmitImplsUnion on success. +type OmitImplsUnionResponse struct { + RandomLeaf OmitImplsUnionRandomLeafLeafContent `json:"-"` +} + +// GetRandomLeaf returns OmitImplsUnionResponse.RandomLeaf, and is useful for accessing the field via an interface. +func (v *OmitImplsUnionResponse) GetRandomLeaf() OmitImplsUnionRandomLeafLeafContent { + return v.RandomLeaf +} + +func (v *OmitImplsUnionResponse) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *OmitImplsUnionResponse + RandomLeaf json.RawMessage `json:"randomLeaf"` + graphql.NoUnmarshalJSON + } + firstPass.OmitImplsUnionResponse = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.RandomLeaf + src := firstPass.RandomLeaf + if len(src) != 0 && string(src) != "null" { + err = __unmarshalOmitImplsUnionRandomLeafLeafContent( + src, dst) + if err != nil { + return fmt.Errorf( + "unable to unmarshal OmitImplsUnionResponse.RandomLeaf: %w", err) + } + } + } + return nil +} + +type __premarshalOmitImplsUnionResponse struct { + RandomLeaf json.RawMessage `json:"randomLeaf"` +} + +func (v *OmitImplsUnionResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *OmitImplsUnionResponse) __premarshalJSON() (*__premarshalOmitImplsUnionResponse, error) { + var retval __premarshalOmitImplsUnionResponse + + { + + dst := &retval.RandomLeaf + src := v.RandomLeaf + var err error + *dst, err = __marshalOmitImplsUnionRandomLeafLeafContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal OmitImplsUnionResponse.RandomLeaf: %w", err) + } + } + return &retval, nil +} + +// The query executed by OmitImplsUnion. +const OmitImplsUnion_Operation = ` +query OmitImplsUnion { + randomLeaf { + __typename + ... on Article { + id + text + } + } +} +` + +func OmitImplsUnion( + client_ graphql.Client, +) (data_ *OmitImplsUnionResponse, err_ error) { + req_ := &graphql.Request{ + OpName: "OmitImplsUnion", + Query: OmitImplsUnion_Operation, + } + + data_ = &OmitImplsUnionResponse{} + resp_ := &graphql.Response{Data: data_} + + err_ = client_.MakeRequest( + nil, + req_, + resp_, + ) + + return data_, err_ +} + diff --git a/generate/testdata/snapshots/TestGenerate-OmitImplsUnion.graphql-OmitImplsUnion.graphql.json b/generate/testdata/snapshots/TestGenerate-OmitImplsUnion.graphql-OmitImplsUnion.graphql.json new file mode 100644 index 00000000..54c854cf --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-OmitImplsUnion.graphql-OmitImplsUnion.graphql.json @@ -0,0 +1,9 @@ +{ + "operations": [ + { + "operationName": "OmitImplsUnion", + "query": "\nquery OmitImplsUnion {\n\trandomLeaf {\n\t\t__typename\n\t\t... on Article {\n\t\t\tid\n\t\t\ttext\n\t\t}\n\t}\n}\n", + "sourceLocation": "testdata/queries/OmitImplsUnion.graphql" + } + ] +} diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-InterfaceNoFragments.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-InterfaceNoFragments.graphql-testdata-queries-generated.go new file mode 100644 index 00000000..092cc66b --- /dev/null +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-InterfaceNoFragments.graphql-testdata-queries-generated.go @@ -0,0 +1,466 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package queries + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/Khan/genqlient/graphql" +) + +// InterfaceNoFragmentsQueryRandomItemContent includes the requested fields of the GraphQL interface Content. +// +// InterfaceNoFragmentsQueryRandomItemContent is implemented by the following types: +// InterfaceNoFragmentsQueryRandomItemContentOther +// The GraphQL type's documentation follows. +// +// Content is implemented by various types like Article, Video, and Topic. +type InterfaceNoFragmentsQueryRandomItemContent interface { + implementsGraphQLInterfaceInterfaceNoFragmentsQueryRandomItemContent() + // 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() string + // GetName returns the interface-field "name" from its implementation. + GetName() string +} + +func (v *InterfaceNoFragmentsQueryRandomItemContentOther) implementsGraphQLInterfaceInterfaceNoFragmentsQueryRandomItemContent() { +} + +func __unmarshalInterfaceNoFragmentsQueryRandomItemContent(b []byte, v *InterfaceNoFragmentsQueryRandomItemContent) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "": + return fmt.Errorf( + "response was missing Content.__typename") + default: + *v = new(InterfaceNoFragmentsQueryRandomItemContentOther) + return json.Unmarshal(b, *v) + } +} + +func __marshalInterfaceNoFragmentsQueryRandomItemContent(v *InterfaceNoFragmentsQueryRandomItemContent) ([]byte, error) { + + switch v := (*v).(type) { + case *InterfaceNoFragmentsQueryRandomItemContentOther: + return json.Marshal(v) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for InterfaceNoFragmentsQueryRandomItemContent: "%T"`, v) + } +} + +// InterfaceNoFragmentsQueryRandomItemContentOther is the catch-all type used by InterfaceNoFragmentsQueryRandomItemContent for any concrete +// GraphQL type returned by the server that doesn't have its own +// generated struct (because no fragment selected it). It carries +// only the interface's shared fields; the concrete GraphQL type +// name is available via the __typename field. +type InterfaceNoFragmentsQueryRandomItemContentOther struct { + Typename string `json:"__typename"` + // ID is the identifier of the content. + Id string `json:"id"` + Name string `json:"name"` +} + +// GetTypename returns InterfaceNoFragmentsQueryRandomItemContentOther.Typename, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryRandomItemContentOther) GetTypename() string { return v.Typename } + +// GetId returns InterfaceNoFragmentsQueryRandomItemContentOther.Id, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryRandomItemContentOther) GetId() string { return v.Id } + +// GetName returns InterfaceNoFragmentsQueryRandomItemContentOther.Name, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryRandomItemContentOther) GetName() string { return v.Name } + +// InterfaceNoFragmentsQueryRandomItemWithTypeNameContent includes the requested fields of the GraphQL interface Content. +// +// InterfaceNoFragmentsQueryRandomItemWithTypeNameContent is implemented by the following types: +// InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther +// The GraphQL type's documentation follows. +// +// Content is implemented by various types like Article, Video, and Topic. +type InterfaceNoFragmentsQueryRandomItemWithTypeNameContent interface { + implementsGraphQLInterfaceInterfaceNoFragmentsQueryRandomItemWithTypeNameContent() + // 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() string + // GetName returns the interface-field "name" from its implementation. + GetName() string +} + +func (v *InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther) implementsGraphQLInterfaceInterfaceNoFragmentsQueryRandomItemWithTypeNameContent() { +} + +func __unmarshalInterfaceNoFragmentsQueryRandomItemWithTypeNameContent(b []byte, v *InterfaceNoFragmentsQueryRandomItemWithTypeNameContent) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "": + return fmt.Errorf( + "response was missing Content.__typename") + default: + *v = new(InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther) + return json.Unmarshal(b, *v) + } +} + +func __marshalInterfaceNoFragmentsQueryRandomItemWithTypeNameContent(v *InterfaceNoFragmentsQueryRandomItemWithTypeNameContent) ([]byte, error) { + + switch v := (*v).(type) { + case *InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther: + return json.Marshal(v) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for InterfaceNoFragmentsQueryRandomItemWithTypeNameContent: "%T"`, v) + } +} + +// InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther is the catch-all type used by InterfaceNoFragmentsQueryRandomItemWithTypeNameContent for any concrete +// GraphQL type returned by the server that doesn't have its own +// generated struct (because no fragment selected it). It carries +// only the interface's shared fields; the concrete GraphQL type +// name is available via the __typename field. +type InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther struct { + Typename string `json:"__typename"` + // ID is the identifier of the content. + Id string `json:"id"` + Name string `json:"name"` +} + +// GetTypename returns InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther.Typename, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther) GetTypename() string { + return v.Typename +} + +// GetId returns InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther.Id, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther) GetId() string { return v.Id } + +// GetName returns InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther.Name, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther) GetName() string { return v.Name } + +// InterfaceNoFragmentsQueryResponse is returned by InterfaceNoFragmentsQuery on success. +type InterfaceNoFragmentsQueryResponse struct { + Root InterfaceNoFragmentsQueryRootTopic `json:"root"` + RandomItem InterfaceNoFragmentsQueryRandomItemContent `json:"-"` + RandomItemWithTypeName InterfaceNoFragmentsQueryRandomItemWithTypeNameContent `json:"-"` + WithPointer *InterfaceNoFragmentsQueryWithPointerContent `json:"-"` +} + +// GetRoot returns InterfaceNoFragmentsQueryResponse.Root, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryResponse) GetRoot() InterfaceNoFragmentsQueryRootTopic { + return v.Root +} + +// GetRandomItem returns InterfaceNoFragmentsQueryResponse.RandomItem, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryResponse) GetRandomItem() InterfaceNoFragmentsQueryRandomItemContent { + return v.RandomItem +} + +// GetRandomItemWithTypeName returns InterfaceNoFragmentsQueryResponse.RandomItemWithTypeName, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryResponse) GetRandomItemWithTypeName() InterfaceNoFragmentsQueryRandomItemWithTypeNameContent { + return v.RandomItemWithTypeName +} + +// GetWithPointer returns InterfaceNoFragmentsQueryResponse.WithPointer, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryResponse) GetWithPointer() *InterfaceNoFragmentsQueryWithPointerContent { + return v.WithPointer +} + +func (v *InterfaceNoFragmentsQueryResponse) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *InterfaceNoFragmentsQueryResponse + RandomItem json.RawMessage `json:"randomItem"` + RandomItemWithTypeName json.RawMessage `json:"randomItemWithTypeName"` + WithPointer json.RawMessage `json:"withPointer"` + graphql.NoUnmarshalJSON + } + firstPass.InterfaceNoFragmentsQueryResponse = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.RandomItem + src := firstPass.RandomItem + if len(src) != 0 && string(src) != "null" { + err = __unmarshalInterfaceNoFragmentsQueryRandomItemContent( + src, dst) + if err != nil { + return fmt.Errorf( + "unable to unmarshal InterfaceNoFragmentsQueryResponse.RandomItem: %w", err) + } + } + } + + { + dst := &v.RandomItemWithTypeName + src := firstPass.RandomItemWithTypeName + if len(src) != 0 && string(src) != "null" { + err = __unmarshalInterfaceNoFragmentsQueryRandomItemWithTypeNameContent( + src, dst) + if err != nil { + return fmt.Errorf( + "unable to unmarshal InterfaceNoFragmentsQueryResponse.RandomItemWithTypeName: %w", err) + } + } + } + + { + dst := &v.WithPointer + src := firstPass.WithPointer + if len(src) != 0 && string(src) != "null" { + *dst = new(InterfaceNoFragmentsQueryWithPointerContent) + err = __unmarshalInterfaceNoFragmentsQueryWithPointerContent( + src, *dst) + if err != nil { + return fmt.Errorf( + "unable to unmarshal InterfaceNoFragmentsQueryResponse.WithPointer: %w", err) + } + } + } + return nil +} + +type __premarshalInterfaceNoFragmentsQueryResponse struct { + Root InterfaceNoFragmentsQueryRootTopic `json:"root"` + + RandomItem json.RawMessage `json:"randomItem"` + + RandomItemWithTypeName json.RawMessage `json:"randomItemWithTypeName"` + + WithPointer json.RawMessage `json:"withPointer"` +} + +func (v *InterfaceNoFragmentsQueryResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *InterfaceNoFragmentsQueryResponse) __premarshalJSON() (*__premarshalInterfaceNoFragmentsQueryResponse, error) { + var retval __premarshalInterfaceNoFragmentsQueryResponse + + retval.Root = v.Root + { + + dst := &retval.RandomItem + src := v.RandomItem + var err error + *dst, err = __marshalInterfaceNoFragmentsQueryRandomItemContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal InterfaceNoFragmentsQueryResponse.RandomItem: %w", err) + } + } + { + + dst := &retval.RandomItemWithTypeName + src := v.RandomItemWithTypeName + var err error + *dst, err = __marshalInterfaceNoFragmentsQueryRandomItemWithTypeNameContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal InterfaceNoFragmentsQueryResponse.RandomItemWithTypeName: %w", err) + } + } + { + + dst := &retval.WithPointer + src := v.WithPointer + if src != nil { + var err error + *dst, err = __marshalInterfaceNoFragmentsQueryWithPointerContent( + src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal InterfaceNoFragmentsQueryResponse.WithPointer: %w", err) + } + } + } + return &retval, nil +} + +// InterfaceNoFragmentsQueryRootTopic includes the requested fields of the GraphQL type Topic. +type InterfaceNoFragmentsQueryRootTopic struct { + // ID is documented in the Content interface. + Id string `json:"id"` + Name string `json:"name"` +} + +// GetId returns InterfaceNoFragmentsQueryRootTopic.Id, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryRootTopic) GetId() string { return v.Id } + +// GetName returns InterfaceNoFragmentsQueryRootTopic.Name, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryRootTopic) GetName() string { return v.Name } + +// InterfaceNoFragmentsQueryWithPointerContent includes the requested fields of the GraphQL interface Content. +// +// InterfaceNoFragmentsQueryWithPointerContent is implemented by the following types: +// InterfaceNoFragmentsQueryWithPointerContentOther +// The GraphQL type's documentation follows. +// +// Content is implemented by various types like Article, Video, and Topic. +type InterfaceNoFragmentsQueryWithPointerContent interface { + implementsGraphQLInterfaceInterfaceNoFragmentsQueryWithPointerContent() + // 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() *string + // GetName returns the interface-field "name" from its implementation. + GetName() *string +} + +func (v *InterfaceNoFragmentsQueryWithPointerContentOther) implementsGraphQLInterfaceInterfaceNoFragmentsQueryWithPointerContent() { +} + +func __unmarshalInterfaceNoFragmentsQueryWithPointerContent(b []byte, v *InterfaceNoFragmentsQueryWithPointerContent) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "": + return fmt.Errorf( + "response was missing Content.__typename") + default: + *v = new(InterfaceNoFragmentsQueryWithPointerContentOther) + return json.Unmarshal(b, *v) + } +} + +func __marshalInterfaceNoFragmentsQueryWithPointerContent(v *InterfaceNoFragmentsQueryWithPointerContent) ([]byte, error) { + + switch v := (*v).(type) { + case *InterfaceNoFragmentsQueryWithPointerContentOther: + return json.Marshal(v) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for InterfaceNoFragmentsQueryWithPointerContent: "%T"`, v) + } +} + +// InterfaceNoFragmentsQueryWithPointerContentOther is the catch-all type used by InterfaceNoFragmentsQueryWithPointerContent for any concrete +// GraphQL type returned by the server that doesn't have its own +// generated struct (because no fragment selected it). It carries +// only the interface's shared fields; the concrete GraphQL type +// name is available via the __typename field. +type InterfaceNoFragmentsQueryWithPointerContentOther struct { + Typename string `json:"__typename"` + // ID is the identifier of the content. + Id *string `json:"id"` + Name *string `json:"name"` +} + +// GetTypename returns InterfaceNoFragmentsQueryWithPointerContentOther.Typename, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryWithPointerContentOther) GetTypename() string { return v.Typename } + +// GetId returns InterfaceNoFragmentsQueryWithPointerContentOther.Id, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryWithPointerContentOther) GetId() *string { return v.Id } + +// GetName returns InterfaceNoFragmentsQueryWithPointerContentOther.Name, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryWithPointerContentOther) GetName() *string { return v.Name } + +// The query executed by InterfaceNoFragmentsQuery. +const InterfaceNoFragmentsQuery_Operation = ` +query InterfaceNoFragmentsQuery { + root { + id + name + } + randomItem { + __typename + id + name + } + randomItemWithTypeName: randomItem { + __typename + id + name + } + withPointer: randomItem { + __typename + id + name + } +} +` + +func InterfaceNoFragmentsQuery( + ctx_ context.Context, + client_ graphql.Client, +) (data_ *InterfaceNoFragmentsQueryResponse, err_ error) { + req_ := &graphql.Request{ + OpName: "InterfaceNoFragmentsQuery", + Query: InterfaceNoFragmentsQuery_Operation, + } + + data_ = &InterfaceNoFragmentsQueryResponse{} + resp_ := &graphql.Response{Data: data_} + + err_ = client_.MakeRequest( + ctx_, + req_, + resp_, + ) + + return data_, err_ +} + diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsNamedFragmentOnInterface.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsNamedFragmentOnInterface.graphql-testdata-queries-generated.go new file mode 100644 index 00000000..6c616fd9 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsNamedFragmentOnInterface.graphql-testdata-queries-generated.go @@ -0,0 +1,458 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package queries + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/Khan/genqlient/graphql" +) + +// NamedContentFields includes the GraphQL fields of Content requested by the fragment NamedContentFields. +// The GraphQL type's documentation follows. +// +// Content is implemented by various types like Article, Video, and Topic. +// +// NamedContentFields is implemented by the following types: +// NamedContentFieldsArticle +// NamedContentFieldsOther +type NamedContentFields interface { + implementsGraphQLInterfaceNamedContentFields() + // GetId returns the interface-field "id" from its implementation. + // The GraphQL interface field's documentation follows. + // + // ID is the identifier of the content. + GetId() string + // GetName returns the interface-field "name" from its implementation. + GetName() string +} + +func (v *NamedContentFieldsArticle) implementsGraphQLInterfaceNamedContentFields() {} +func (v *NamedContentFieldsOther) implementsGraphQLInterfaceNamedContentFields() {} + +func __unmarshalNamedContentFields(b []byte, v *NamedContentFields) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "Article": + *v = new(NamedContentFieldsArticle) + return json.Unmarshal(b, *v) + case "": + return fmt.Errorf( + "response was missing Content.__typename") + default: + *v = new(NamedContentFieldsOther) + return json.Unmarshal(b, *v) + } +} + +func __marshalNamedContentFields(v *NamedContentFields) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *NamedContentFieldsArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *NamedContentFieldsArticle + }{typename, v} + return json.Marshal(result) + case *NamedContentFieldsOther: + return json.Marshal(v) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for NamedContentFields: "%T"`, v) + } +} + +// NamedContentFields includes the GraphQL fields of Article requested by the fragment NamedContentFields. +// The GraphQL type's documentation follows. +// +// Content is implemented by various types like Article, Video, and Topic. +type NamedContentFieldsArticle struct { + // ID is the identifier of the content. + Id string `json:"id"` + Name string `json:"name"` + Text string `json:"text"` +} + +// GetId returns NamedContentFieldsArticle.Id, and is useful for accessing the field via an interface. +func (v *NamedContentFieldsArticle) GetId() string { return v.Id } + +// GetName returns NamedContentFieldsArticle.Name, and is useful for accessing the field via an interface. +func (v *NamedContentFieldsArticle) GetName() string { return v.Name } + +// GetText returns NamedContentFieldsArticle.Text, and is useful for accessing the field via an interface. +func (v *NamedContentFieldsArticle) GetText() string { return v.Text } + +// NamedContentFieldsOther is the catch-all type used by NamedContentFields for any concrete +// GraphQL type returned by the server that doesn't have its own +// generated struct (because no fragment selected it). It carries +// only the interface's shared fields; the concrete GraphQL type +// name is available via the __typename field. +type NamedContentFieldsOther struct { + // ID is the identifier of the content. + Id string `json:"id"` + Name string `json:"name"` +} + +// GetId returns NamedContentFieldsOther.Id, and is useful for accessing the field via an interface. +func (v *NamedContentFieldsOther) GetId() string { return v.Id } + +// GetName returns NamedContentFieldsOther.Name, and is useful for accessing the field via an interface. +func (v *NamedContentFieldsOther) GetName() string { return v.Name } + +// OmitImplsNamedFragmentOnInterfaceRandomItemArticle includes the requested fields of the GraphQL type Article. +type OmitImplsNamedFragmentOnInterfaceRandomItemArticle struct { + Typename string `json:"__typename"` + NamedContentFieldsArticle `json:"-"` +} + +// GetTypename returns OmitImplsNamedFragmentOnInterfaceRandomItemArticle.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemArticle) GetTypename() string { return v.Typename } + +// GetId returns OmitImplsNamedFragmentOnInterfaceRandomItemArticle.Id, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemArticle) GetId() string { + return v.NamedContentFieldsArticle.Id +} + +// GetName returns OmitImplsNamedFragmentOnInterfaceRandomItemArticle.Name, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemArticle) GetName() string { + return v.NamedContentFieldsArticle.Name +} + +// GetText returns OmitImplsNamedFragmentOnInterfaceRandomItemArticle.Text, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemArticle) GetText() string { + return v.NamedContentFieldsArticle.Text +} + +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemArticle) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *OmitImplsNamedFragmentOnInterfaceRandomItemArticle + graphql.NoUnmarshalJSON + } + firstPass.OmitImplsNamedFragmentOnInterfaceRandomItemArticle = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.NamedContentFieldsArticle) + if err != nil { + return err + } + return nil +} + +type __premarshalOmitImplsNamedFragmentOnInterfaceRandomItemArticle struct { + Typename string `json:"__typename"` + + Id string `json:"id"` + + Name string `json:"name"` + + Text string `json:"text"` +} + +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemArticle) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemArticle) __premarshalJSON() (*__premarshalOmitImplsNamedFragmentOnInterfaceRandomItemArticle, error) { + var retval __premarshalOmitImplsNamedFragmentOnInterfaceRandomItemArticle + + retval.Typename = v.Typename + retval.Id = v.NamedContentFieldsArticle.Id + retval.Name = v.NamedContentFieldsArticle.Name + retval.Text = v.NamedContentFieldsArticle.Text + return &retval, nil +} + +// OmitImplsNamedFragmentOnInterfaceRandomItemContent includes the requested fields of the GraphQL interface Content. +// +// OmitImplsNamedFragmentOnInterfaceRandomItemContent is implemented by the following types: +// OmitImplsNamedFragmentOnInterfaceRandomItemArticle +// OmitImplsNamedFragmentOnInterfaceRandomItemContentOther +// The GraphQL type's documentation follows. +// +// Content is implemented by various types like Article, Video, and Topic. +type OmitImplsNamedFragmentOnInterfaceRandomItemContent interface { + implementsGraphQLInterfaceOmitImplsNamedFragmentOnInterfaceRandomItemContent() + // GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values). + GetTypename() string + NamedContentFields +} + +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemArticle) implementsGraphQLInterfaceOmitImplsNamedFragmentOnInterfaceRandomItemContent() { +} +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther) implementsGraphQLInterfaceOmitImplsNamedFragmentOnInterfaceRandomItemContent() { +} + +func __unmarshalOmitImplsNamedFragmentOnInterfaceRandomItemContent(b []byte, v *OmitImplsNamedFragmentOnInterfaceRandomItemContent) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "Article": + *v = new(OmitImplsNamedFragmentOnInterfaceRandomItemArticle) + return json.Unmarshal(b, *v) + case "": + return fmt.Errorf( + "response was missing Content.__typename") + default: + *v = new(OmitImplsNamedFragmentOnInterfaceRandomItemContentOther) + return json.Unmarshal(b, *v) + } +} + +func __marshalOmitImplsNamedFragmentOnInterfaceRandomItemContent(v *OmitImplsNamedFragmentOnInterfaceRandomItemContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *OmitImplsNamedFragmentOnInterfaceRandomItemArticle: + typename = "Article" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalOmitImplsNamedFragmentOnInterfaceRandomItemArticle + }{typename, premarshaled} + return json.Marshal(result) + case *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther: + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for OmitImplsNamedFragmentOnInterfaceRandomItemContent: "%T"`, v) + } +} + +// OmitImplsNamedFragmentOnInterfaceRandomItemContentOther is the catch-all type used by OmitImplsNamedFragmentOnInterfaceRandomItemContent for any concrete +// GraphQL type returned by the server that doesn't have its own +// generated struct (because no fragment selected it). It carries +// only the interface's shared fields; the concrete GraphQL type +// name is available via the __typename field. +type OmitImplsNamedFragmentOnInterfaceRandomItemContentOther struct { + Typename string `json:"__typename"` + NamedContentFieldsOther `json:"-"` +} + +// GetTypename returns OmitImplsNamedFragmentOnInterfaceRandomItemContentOther.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther) GetTypename() string { + return v.Typename +} + +// GetId returns OmitImplsNamedFragmentOnInterfaceRandomItemContentOther.Id, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther) GetId() string { + return v.NamedContentFieldsOther.Id +} + +// GetName returns OmitImplsNamedFragmentOnInterfaceRandomItemContentOther.Name, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther) GetName() string { + return v.NamedContentFieldsOther.Name +} + +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther + graphql.NoUnmarshalJSON + } + firstPass.OmitImplsNamedFragmentOnInterfaceRandomItemContentOther = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.NamedContentFieldsOther) + if err != nil { + return err + } + return nil +} + +type __premarshalOmitImplsNamedFragmentOnInterfaceRandomItemContentOther struct { + Typename string `json:"__typename"` + + Id string `json:"id"` + + Name string `json:"name"` +} + +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther) __premarshalJSON() (*__premarshalOmitImplsNamedFragmentOnInterfaceRandomItemContentOther, error) { + var retval __premarshalOmitImplsNamedFragmentOnInterfaceRandomItemContentOther + + retval.Typename = v.Typename + retval.Id = v.NamedContentFieldsOther.Id + retval.Name = v.NamedContentFieldsOther.Name + return &retval, nil +} + +// OmitImplsNamedFragmentOnInterfaceResponse is returned by OmitImplsNamedFragmentOnInterface on success. +type OmitImplsNamedFragmentOnInterfaceResponse struct { + RandomItem OmitImplsNamedFragmentOnInterfaceRandomItemContent `json:"-"` +} + +// GetRandomItem returns OmitImplsNamedFragmentOnInterfaceResponse.RandomItem, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceResponse) GetRandomItem() OmitImplsNamedFragmentOnInterfaceRandomItemContent { + return v.RandomItem +} + +func (v *OmitImplsNamedFragmentOnInterfaceResponse) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *OmitImplsNamedFragmentOnInterfaceResponse + RandomItem json.RawMessage `json:"randomItem"` + graphql.NoUnmarshalJSON + } + firstPass.OmitImplsNamedFragmentOnInterfaceResponse = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.RandomItem + src := firstPass.RandomItem + if len(src) != 0 && string(src) != "null" { + err = __unmarshalOmitImplsNamedFragmentOnInterfaceRandomItemContent( + src, dst) + if err != nil { + return fmt.Errorf( + "unable to unmarshal OmitImplsNamedFragmentOnInterfaceResponse.RandomItem: %w", err) + } + } + } + return nil +} + +type __premarshalOmitImplsNamedFragmentOnInterfaceResponse struct { + RandomItem json.RawMessage `json:"randomItem"` +} + +func (v *OmitImplsNamedFragmentOnInterfaceResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *OmitImplsNamedFragmentOnInterfaceResponse) __premarshalJSON() (*__premarshalOmitImplsNamedFragmentOnInterfaceResponse, error) { + var retval __premarshalOmitImplsNamedFragmentOnInterfaceResponse + + { + + dst := &retval.RandomItem + src := v.RandomItem + var err error + *dst, err = __marshalOmitImplsNamedFragmentOnInterfaceRandomItemContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal OmitImplsNamedFragmentOnInterfaceResponse.RandomItem: %w", err) + } + } + return &retval, nil +} + +// The query executed by OmitImplsNamedFragmentOnInterface. +const OmitImplsNamedFragmentOnInterface_Operation = ` +query OmitImplsNamedFragmentOnInterface { + randomItem { + __typename + ... NamedContentFields + } +} +fragment NamedContentFields on Content { + id + name + ... on Article { + text + } +} +` + +func OmitImplsNamedFragmentOnInterface( + ctx_ context.Context, + client_ graphql.Client, +) (data_ *OmitImplsNamedFragmentOnInterfaceResponse, err_ error) { + req_ := &graphql.Request{ + OpName: "OmitImplsNamedFragmentOnInterface", + Query: OmitImplsNamedFragmentOnInterface_Operation, + } + + data_ = &OmitImplsNamedFragmentOnInterfaceResponse{} + resp_ := &graphql.Response{Data: data_} + + err_ = client_.MakeRequest( + ctx_, + req_, + resp_, + ) + + return data_, err_ +} + diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsUnion.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsUnion.graphql-testdata-queries-generated.go new file mode 100644 index 00000000..5f0ba84b --- /dev/null +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsUnion.graphql-testdata-queries-generated.go @@ -0,0 +1,215 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package queries + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/Khan/genqlient/graphql" +) + +// OmitImplsUnionRandomLeafArticle includes the requested fields of the GraphQL type Article. +type OmitImplsUnionRandomLeafArticle struct { + Typename string `json:"__typename"` + // ID is documented in the Content interface. + Id string `json:"id"` + Text string `json:"text"` +} + +// GetTypename returns OmitImplsUnionRandomLeafArticle.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsUnionRandomLeafArticle) GetTypename() string { return v.Typename } + +// GetId returns OmitImplsUnionRandomLeafArticle.Id, and is useful for accessing the field via an interface. +func (v *OmitImplsUnionRandomLeafArticle) GetId() string { return v.Id } + +// GetText returns OmitImplsUnionRandomLeafArticle.Text, and is useful for accessing the field via an interface. +func (v *OmitImplsUnionRandomLeafArticle) GetText() string { return v.Text } + +// OmitImplsUnionRandomLeafLeafContent includes the requested fields of the GraphQL interface LeafContent. +// +// OmitImplsUnionRandomLeafLeafContent is implemented by the following types: +// OmitImplsUnionRandomLeafArticle +// OmitImplsUnionRandomLeafLeafContentOther +// The GraphQL type's documentation follows. +// +// LeafContent represents content items that can't have child-nodes. +type OmitImplsUnionRandomLeafLeafContent interface { + implementsGraphQLInterfaceOmitImplsUnionRandomLeafLeafContent() + // GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values). + GetTypename() string +} + +func (v *OmitImplsUnionRandomLeafArticle) implementsGraphQLInterfaceOmitImplsUnionRandomLeafLeafContent() { +} +func (v *OmitImplsUnionRandomLeafLeafContentOther) implementsGraphQLInterfaceOmitImplsUnionRandomLeafLeafContent() { +} + +func __unmarshalOmitImplsUnionRandomLeafLeafContent(b []byte, v *OmitImplsUnionRandomLeafLeafContent) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "Article": + *v = new(OmitImplsUnionRandomLeafArticle) + return json.Unmarshal(b, *v) + case "": + return fmt.Errorf( + "response was missing LeafContent.__typename") + default: + *v = new(OmitImplsUnionRandomLeafLeafContentOther) + return json.Unmarshal(b, *v) + } +} + +func __marshalOmitImplsUnionRandomLeafLeafContent(v *OmitImplsUnionRandomLeafLeafContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *OmitImplsUnionRandomLeafArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *OmitImplsUnionRandomLeafArticle + }{typename, v} + return json.Marshal(result) + case *OmitImplsUnionRandomLeafLeafContentOther: + return json.Marshal(v) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for OmitImplsUnionRandomLeafLeafContent: "%T"`, v) + } +} + +// OmitImplsUnionRandomLeafLeafContentOther is the catch-all type used by OmitImplsUnionRandomLeafLeafContent for any concrete +// GraphQL type returned by the server that doesn't have its own +// generated struct (because no fragment selected it). It carries +// only the interface's shared fields; the concrete GraphQL type +// name is available via the __typename field. +type OmitImplsUnionRandomLeafLeafContentOther struct { + Typename string `json:"__typename"` +} + +// GetTypename returns OmitImplsUnionRandomLeafLeafContentOther.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsUnionRandomLeafLeafContentOther) GetTypename() string { return v.Typename } + +// OmitImplsUnionResponse is returned by OmitImplsUnion on success. +type OmitImplsUnionResponse struct { + RandomLeaf OmitImplsUnionRandomLeafLeafContent `json:"-"` +} + +// GetRandomLeaf returns OmitImplsUnionResponse.RandomLeaf, and is useful for accessing the field via an interface. +func (v *OmitImplsUnionResponse) GetRandomLeaf() OmitImplsUnionRandomLeafLeafContent { + return v.RandomLeaf +} + +func (v *OmitImplsUnionResponse) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *OmitImplsUnionResponse + RandomLeaf json.RawMessage `json:"randomLeaf"` + graphql.NoUnmarshalJSON + } + firstPass.OmitImplsUnionResponse = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.RandomLeaf + src := firstPass.RandomLeaf + if len(src) != 0 && string(src) != "null" { + err = __unmarshalOmitImplsUnionRandomLeafLeafContent( + src, dst) + if err != nil { + return fmt.Errorf( + "unable to unmarshal OmitImplsUnionResponse.RandomLeaf: %w", err) + } + } + } + return nil +} + +type __premarshalOmitImplsUnionResponse struct { + RandomLeaf json.RawMessage `json:"randomLeaf"` +} + +func (v *OmitImplsUnionResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *OmitImplsUnionResponse) __premarshalJSON() (*__premarshalOmitImplsUnionResponse, error) { + var retval __premarshalOmitImplsUnionResponse + + { + + dst := &retval.RandomLeaf + src := v.RandomLeaf + var err error + *dst, err = __marshalOmitImplsUnionRandomLeafLeafContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal OmitImplsUnionResponse.RandomLeaf: %w", err) + } + } + return &retval, nil +} + +// The query executed by OmitImplsUnion. +const OmitImplsUnion_Operation = ` +query OmitImplsUnion { + randomLeaf { + __typename + ... on Article { + id + text + } + } +} +` + +func OmitImplsUnion( + ctx_ context.Context, + client_ graphql.Client, +) (data_ *OmitImplsUnionResponse, err_ error) { + req_ := &graphql.Request{ + OpName: "OmitImplsUnion", + Query: OmitImplsUnion_Operation, + } + + data_ = &OmitImplsUnionResponse{} + resp_ := &graphql.Response{Data: data_} + + err_ = client_.MakeRequest( + ctx_, + req_, + resp_, + ) + + return data_, err_ +} + diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleInlineFragment.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleInlineFragment.graphql-testdata-queries-generated.go index 3ed39804..77e9357d 100644 --- a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleInlineFragment.graphql-testdata-queries-generated.go +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleInlineFragment.graphql-testdata-queries-generated.go @@ -36,7 +36,7 @@ func (v *SimpleInlineFragmentRandomItemArticle) GetText() string { return v.Text // SimpleInlineFragmentRandomItemContent is implemented by the following types: // SimpleInlineFragmentRandomItemArticle // SimpleInlineFragmentRandomItemVideo -// SimpleInlineFragmentRandomItemOther +// SimpleInlineFragmentRandomItemContentOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -57,7 +57,7 @@ func (v *SimpleInlineFragmentRandomItemArticle) implementsGraphQLInterfaceSimple } func (v *SimpleInlineFragmentRandomItemVideo) implementsGraphQLInterfaceSimpleInlineFragmentRandomItemContent() { } -func (v *SimpleInlineFragmentRandomItemOther) implementsGraphQLInterfaceSimpleInlineFragmentRandomItemContent() { +func (v *SimpleInlineFragmentRandomItemContentOther) implementsGraphQLInterfaceSimpleInlineFragmentRandomItemContent() { } func __unmarshalSimpleInlineFragmentRandomItemContent(b []byte, v *SimpleInlineFragmentRandomItemContent) error { @@ -84,7 +84,7 @@ func __unmarshalSimpleInlineFragmentRandomItemContent(b []byte, v *SimpleInlineF return fmt.Errorf( "response was missing Content.__typename") default: - *v = new(SimpleInlineFragmentRandomItemOther) + *v = new(SimpleInlineFragmentRandomItemContentOther) return json.Unmarshal(b, *v) } } @@ -109,8 +109,7 @@ func __marshalSimpleInlineFragmentRandomItemContent(v *SimpleInlineFragmentRando *SimpleInlineFragmentRandomItemVideo }{typename, v} return json.Marshal(result) - case *SimpleInlineFragmentRandomItemOther: - + case *SimpleInlineFragmentRandomItemContentOther: return json.Marshal(v) case nil: return []byte("null"), nil @@ -120,22 +119,26 @@ func __marshalSimpleInlineFragmentRandomItemContent(v *SimpleInlineFragmentRando } } -// SimpleInlineFragmentRandomItemOther includes the requested fields of the GraphQL type Content. -type SimpleInlineFragmentRandomItemOther struct { +// SimpleInlineFragmentRandomItemContentOther is the catch-all type used by SimpleInlineFragmentRandomItemContent for any concrete +// GraphQL type returned by the server that doesn't have its own +// generated struct (because no fragment selected it). It carries +// only the interface's shared fields; the concrete GraphQL type +// name is available via the __typename field. +type SimpleInlineFragmentRandomItemContentOther struct { Typename string `json:"__typename"` // ID is the identifier of the content. Id string `json:"id"` Name string `json:"name"` } -// GetTypename returns SimpleInlineFragmentRandomItemOther.Typename, and is useful for accessing the field via an interface. -func (v *SimpleInlineFragmentRandomItemOther) GetTypename() string { return v.Typename } +// GetTypename returns SimpleInlineFragmentRandomItemContentOther.Typename, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemContentOther) GetTypename() string { return v.Typename } -// GetId returns SimpleInlineFragmentRandomItemOther.Id, and is useful for accessing the field via an interface. -func (v *SimpleInlineFragmentRandomItemOther) GetId() string { return v.Id } +// GetId returns SimpleInlineFragmentRandomItemContentOther.Id, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemContentOther) GetId() string { return v.Id } -// GetName returns SimpleInlineFragmentRandomItemOther.Name, and is useful for accessing the field via an interface. -func (v *SimpleInlineFragmentRandomItemOther) GetName() string { return v.Name } +// GetName returns SimpleInlineFragmentRandomItemContentOther.Name, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemContentOther) GetName() string { return v.Name } // SimpleInlineFragmentRandomItemVideo includes the requested fields of the GraphQL type Video. type SimpleInlineFragmentRandomItemVideo struct { diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleNamedFragment.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleNamedFragment.graphql-testdata-queries-generated.go index 5a72be89..78bec303 100644 --- a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleNamedFragment.graphql-testdata-queries-generated.go +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleNamedFragment.graphql-testdata-queries-generated.go @@ -14,7 +14,7 @@ import ( // // SimpleNamedFragmentRandomItemContent is implemented by the following types: // SimpleNamedFragmentRandomItemVideo -// SimpleNamedFragmentRandomItemOther +// SimpleNamedFragmentRandomItemContentOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -33,7 +33,7 @@ type SimpleNamedFragmentRandomItemContent interface { func (v *SimpleNamedFragmentRandomItemVideo) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() { } -func (v *SimpleNamedFragmentRandomItemOther) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() { +func (v *SimpleNamedFragmentRandomItemContentOther) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() { } func __unmarshalSimpleNamedFragmentRandomItemContent(b []byte, v *SimpleNamedFragmentRandomItemContent) error { @@ -57,7 +57,7 @@ func __unmarshalSimpleNamedFragmentRandomItemContent(b []byte, v *SimpleNamedFra return fmt.Errorf( "response was missing Content.__typename") default: - *v = new(SimpleNamedFragmentRandomItemOther) + *v = new(SimpleNamedFragmentRandomItemContentOther) return json.Unmarshal(b, *v) } } @@ -78,8 +78,7 @@ func __marshalSimpleNamedFragmentRandomItemContent(v *SimpleNamedFragmentRandomI *__premarshalSimpleNamedFragmentRandomItemVideo }{typename, premarshaled} return json.Marshal(result) - case *SimpleNamedFragmentRandomItemOther: - + case *SimpleNamedFragmentRandomItemContentOther: return json.Marshal(v) case nil: return []byte("null"), nil @@ -89,22 +88,26 @@ func __marshalSimpleNamedFragmentRandomItemContent(v *SimpleNamedFragmentRandomI } } -// SimpleNamedFragmentRandomItemOther includes the requested fields of the GraphQL type Content. -type SimpleNamedFragmentRandomItemOther struct { +// SimpleNamedFragmentRandomItemContentOther is the catch-all type used by SimpleNamedFragmentRandomItemContent for any concrete +// GraphQL type returned by the server that doesn't have its own +// generated struct (because no fragment selected it). It carries +// only the interface's shared fields; the concrete GraphQL type +// name is available via the __typename field. +type SimpleNamedFragmentRandomItemContentOther struct { Typename string `json:"__typename"` // ID is the identifier of the content. Id string `json:"id"` Name string `json:"name"` } -// GetTypename returns SimpleNamedFragmentRandomItemOther.Typename, and is useful for accessing the field via an interface. -func (v *SimpleNamedFragmentRandomItemOther) GetTypename() string { return v.Typename } +// GetTypename returns SimpleNamedFragmentRandomItemContentOther.Typename, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemContentOther) GetTypename() string { return v.Typename } -// GetId returns SimpleNamedFragmentRandomItemOther.Id, and is useful for accessing the field via an interface. -func (v *SimpleNamedFragmentRandomItemOther) GetId() string { return v.Id } +// GetId returns SimpleNamedFragmentRandomItemContentOther.Id, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemContentOther) GetId() string { return v.Id } -// GetName returns SimpleNamedFragmentRandomItemOther.Name, and is useful for accessing the field via an interface. -func (v *SimpleNamedFragmentRandomItemOther) GetName() string { return v.Name } +// GetName returns SimpleNamedFragmentRandomItemContentOther.Name, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemContentOther) GetName() string { return v.Name } // SimpleNamedFragmentRandomItemVideo includes the requested fields of the GraphQL type Video. type SimpleNamedFragmentRandomItemVideo struct { @@ -198,7 +201,7 @@ func (v *SimpleNamedFragmentRandomItemVideo) __premarshalJSON() (*__premarshalSi // // SimpleNamedFragmentRandomLeafLeafContent is implemented by the following types: // SimpleNamedFragmentRandomLeafVideo -// SimpleNamedFragmentRandomLeafOther +// SimpleNamedFragmentRandomLeafLeafContentOther // The GraphQL type's documentation follows. // // LeafContent represents content items that can't have child-nodes. @@ -210,7 +213,7 @@ type SimpleNamedFragmentRandomLeafLeafContent interface { func (v *SimpleNamedFragmentRandomLeafVideo) implementsGraphQLInterfaceSimpleNamedFragmentRandomLeafLeafContent() { } -func (v *SimpleNamedFragmentRandomLeafOther) implementsGraphQLInterfaceSimpleNamedFragmentRandomLeafLeafContent() { +func (v *SimpleNamedFragmentRandomLeafLeafContentOther) implementsGraphQLInterfaceSimpleNamedFragmentRandomLeafLeafContent() { } func __unmarshalSimpleNamedFragmentRandomLeafLeafContent(b []byte, v *SimpleNamedFragmentRandomLeafLeafContent) error { @@ -234,7 +237,7 @@ func __unmarshalSimpleNamedFragmentRandomLeafLeafContent(b []byte, v *SimpleName return fmt.Errorf( "response was missing LeafContent.__typename") default: - *v = new(SimpleNamedFragmentRandomLeafOther) + *v = new(SimpleNamedFragmentRandomLeafLeafContentOther) return json.Unmarshal(b, *v) } } @@ -255,8 +258,7 @@ func __marshalSimpleNamedFragmentRandomLeafLeafContent(v *SimpleNamedFragmentRan *__premarshalSimpleNamedFragmentRandomLeafVideo }{typename, premarshaled} return json.Marshal(result) - case *SimpleNamedFragmentRandomLeafOther: - + case *SimpleNamedFragmentRandomLeafLeafContentOther: return json.Marshal(v) case nil: return []byte("null"), nil @@ -266,13 +268,17 @@ func __marshalSimpleNamedFragmentRandomLeafLeafContent(v *SimpleNamedFragmentRan } } -// SimpleNamedFragmentRandomLeafOther includes the requested fields of the GraphQL type LeafContent. -type SimpleNamedFragmentRandomLeafOther struct { +// SimpleNamedFragmentRandomLeafLeafContentOther is the catch-all type used by SimpleNamedFragmentRandomLeafLeafContent for any concrete +// GraphQL type returned by the server that doesn't have its own +// generated struct (because no fragment selected it). It carries +// only the interface's shared fields; the concrete GraphQL type +// name is available via the __typename field. +type SimpleNamedFragmentRandomLeafLeafContentOther struct { Typename string `json:"__typename"` } -// GetTypename returns SimpleNamedFragmentRandomLeafOther.Typename, and is useful for accessing the field via an interface. -func (v *SimpleNamedFragmentRandomLeafOther) GetTypename() string { return v.Typename } +// GetTypename returns SimpleNamedFragmentRandomLeafLeafContentOther.Typename, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomLeafLeafContentOther) GetTypename() string { return v.Typename } // SimpleNamedFragmentRandomLeafVideo includes the requested fields of the GraphQL type Video. type SimpleNamedFragmentRandomLeafVideo struct { diff --git a/generate/unmarshal_helper.go.tmpl b/generate/unmarshal_helper.go.tmpl index 16eabe97..e48a43b8 100644 --- a/generate/unmarshal_helper.go.tmpl +++ b/generate/unmarshal_helper.go.tmpl @@ -31,6 +31,9 @@ func __unmarshal{{.GoName}}(b []byte, v *{{.GoName}}) error { "response was missing {{.GraphQLName}}.__typename") default: {{if .OtherImplementation -}} + {{/* Unknown __typename: fall back to the catch-all struct so the + caller still gets the interface's shared fields (and the raw + concrete type-name via __typename). */ -}} *v = new({{.OtherImplementation.GoName}}) return {{ref "encoding/json.Unmarshal"}}(b, *v) {{else -}} diff --git a/internal/integration/omitimpls/generated.go b/internal/integration/omitimpls/generated.go new file mode 100644 index 00000000..cfb6a32e --- /dev/null +++ b/internal/integration/omitimpls/generated.go @@ -0,0 +1,235 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package omitimpls + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/Khan/genqlient/graphql" +) + +// __queryOmitImplsInput is used internally by genqlient +type __queryOmitImplsInput struct { + Id string `json:"id"` +} + +// GetId returns __queryOmitImplsInput.Id, and is useful for accessing the field via an interface. +func (v *__queryOmitImplsInput) GetId() string { return v.Id } + +// queryOmitImplsBeing includes the requested fields of the GraphQL interface Being. +// +// queryOmitImplsBeing is implemented by the following types: +// queryOmitImplsBeingUser +// queryOmitImplsBeingOther +type queryOmitImplsBeing interface { + implementsGraphQLInterfacequeryOmitImplsBeing() + // 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 + // GetName returns the interface-field "name" from its implementation. + GetName() string +} + +func (v *queryOmitImplsBeingUser) implementsGraphQLInterfacequeryOmitImplsBeing() {} +func (v *queryOmitImplsBeingOther) implementsGraphQLInterfacequeryOmitImplsBeing() {} + +func __unmarshalqueryOmitImplsBeing(b []byte, v *queryOmitImplsBeing) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "User": + *v = new(queryOmitImplsBeingUser) + return json.Unmarshal(b, *v) + case "": + return fmt.Errorf( + "response was missing Being.__typename") + default: + *v = new(queryOmitImplsBeingOther) + return json.Unmarshal(b, *v) + } +} + +func __marshalqueryOmitImplsBeing(v *queryOmitImplsBeing) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *queryOmitImplsBeingUser: + typename = "User" + + result := struct { + TypeName string `json:"__typename"` + *queryOmitImplsBeingUser + }{typename, v} + return json.Marshal(result) + case *queryOmitImplsBeingOther: + return json.Marshal(v) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for queryOmitImplsBeing: "%T"`, v) + } +} + +// queryOmitImplsBeingOther is the catch-all type used by queryOmitImplsBeing for any concrete +// GraphQL type returned by the server that doesn't have its own +// generated struct (because no fragment selected it). It carries +// only the interface's shared fields; the concrete GraphQL type +// name is available via the __typename field. +type queryOmitImplsBeingOther struct { + Typename string `json:"__typename"` + Id string `json:"id"` + Name string `json:"name"` +} + +// GetTypename returns queryOmitImplsBeingOther.Typename, and is useful for accessing the field via an interface. +func (v *queryOmitImplsBeingOther) GetTypename() string { return v.Typename } + +// GetId returns queryOmitImplsBeingOther.Id, and is useful for accessing the field via an interface. +func (v *queryOmitImplsBeingOther) GetId() string { return v.Id } + +// GetName returns queryOmitImplsBeingOther.Name, and is useful for accessing the field via an interface. +func (v *queryOmitImplsBeingOther) GetName() string { return v.Name } + +// queryOmitImplsBeingUser includes the requested fields of the GraphQL type User. +type queryOmitImplsBeingUser struct { + Typename string `json:"__typename"` + Id string `json:"id"` + Name string `json:"name"` + LuckyNumber int `json:"luckyNumber"` +} + +// GetTypename returns queryOmitImplsBeingUser.Typename, and is useful for accessing the field via an interface. +func (v *queryOmitImplsBeingUser) GetTypename() string { return v.Typename } + +// GetId returns queryOmitImplsBeingUser.Id, and is useful for accessing the field via an interface. +func (v *queryOmitImplsBeingUser) GetId() string { return v.Id } + +// GetName returns queryOmitImplsBeingUser.Name, and is useful for accessing the field via an interface. +func (v *queryOmitImplsBeingUser) GetName() string { return v.Name } + +// GetLuckyNumber returns queryOmitImplsBeingUser.LuckyNumber, and is useful for accessing the field via an interface. +func (v *queryOmitImplsBeingUser) GetLuckyNumber() int { return v.LuckyNumber } + +// queryOmitImplsResponse is returned by queryOmitImpls on success. +type queryOmitImplsResponse struct { + Being queryOmitImplsBeing `json:"-"` +} + +// GetBeing returns queryOmitImplsResponse.Being, and is useful for accessing the field via an interface. +func (v *queryOmitImplsResponse) GetBeing() queryOmitImplsBeing { return v.Being } + +func (v *queryOmitImplsResponse) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *queryOmitImplsResponse + Being json.RawMessage `json:"being"` + graphql.NoUnmarshalJSON + } + firstPass.queryOmitImplsResponse = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.Being + src := firstPass.Being + if len(src) != 0 && string(src) != "null" { + err = __unmarshalqueryOmitImplsBeing( + src, dst) + if err != nil { + return fmt.Errorf( + "unable to unmarshal queryOmitImplsResponse.Being: %w", err) + } + } + } + return nil +} + +type __premarshalqueryOmitImplsResponse struct { + Being json.RawMessage `json:"being"` +} + +func (v *queryOmitImplsResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *queryOmitImplsResponse) __premarshalJSON() (*__premarshalqueryOmitImplsResponse, error) { + var retval __premarshalqueryOmitImplsResponse + + { + + dst := &retval.Being + src := v.Being + var err error + *dst, err = __marshalqueryOmitImplsBeing( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal queryOmitImplsResponse.Being: %w", err) + } + } + return &retval, nil +} + +// The query executed by queryOmitImpls. +const queryOmitImpls_Operation = ` +query queryOmitImpls ($id: ID!) { + being(id: $id) { + __typename + id + name + ... on User { + luckyNumber + } + } +} +` + +func queryOmitImpls( + ctx_ context.Context, + client_ graphql.Client, + id string, +) (data_ *queryOmitImplsResponse, err_ error) { + req_ := &graphql.Request{ + OpName: "queryOmitImpls", + Query: queryOmitImpls_Operation, + Variables: &__queryOmitImplsInput{ + Id: id, + }, + } + + data_ = &queryOmitImplsResponse{} + resp_ := &graphql.Response{Data: data_} + + err_ = client_.MakeRequest( + ctx_, + req_, + resp_, + ) + + return data_, err_ +} diff --git a/internal/integration/omitimpls/genqlient.yaml b/internal/integration/omitimpls/genqlient.yaml new file mode 100644 index 00000000..b09cc339 --- /dev/null +++ b/internal/integration/omitimpls/genqlient.yaml @@ -0,0 +1,11 @@ +schema: ../schema.graphql +operations: "*_test.go" +generated: generated.go +omit_unreferenced_implementations: true +bindings: + Date: + type: time.Time + marshaler: "github.com/Khan/genqlient/internal/testutil.MarshalDate" + unmarshaler: "github.com/Khan/genqlient/internal/testutil.UnmarshalDate" + MyGreatScalar: + type: github.com/Khan/genqlient/internal/integration.MyGreatScalar diff --git a/internal/integration/omitimpls/omitimpls_test.go b/internal/integration/omitimpls/omitimpls_test.go new file mode 100644 index 00000000..cdb96193 --- /dev/null +++ b/internal/integration/omitimpls/omitimpls_test.go @@ -0,0 +1,72 @@ +// Package omitimpls contains integration tests for the +// omit_unreferenced_implementations config option, run against the shared +// gqlgen server in ../server. +package omitimpls + +//go:generate go run github.com/Khan/genqlient genqlient.yaml + +import ( + "context" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/Khan/genqlient/graphql" + "github.com/Khan/genqlient/internal/integration/server" +) + +func TestCatchAllForUnreferencedImplementation(t *testing.T) { + _ = `# @genqlient + query queryOmitImpls($id: ID!) { + being(id: $id) { + id + name + ... on User { + luckyNumber + } + } + }` + + ctx := context.Background() + srv := server.RunServer() + defer srv.Close() + client := graphql.NewClient(srv.URL, http.DefaultClient) + + // id=1 → server returns a User, which is the only fragment-referenced + // concrete implementation: we should get the typed-impl path with a + // non-zero LuckyNumber. + resp, err := queryOmitImpls(ctx, client, "1") + require.NoError(t, err) + + assert.Equal(t, "User", resp.Being.GetTypename()) + assert.Equal(t, "1", resp.Being.GetId()) + assert.Equal(t, "Yours Truly", resp.Being.GetName()) + + user, ok := resp.Being.(*queryOmitImplsBeingUser) + require.Truef(t, ok, "got %T, not User", resp.Being) + assert.Equal(t, 17, user.LuckyNumber) + + // id=3 → server returns an Animal. Animal is NOT fragment-referenced, + // so it must fall through to the generated catch-all struct, which + // still exposes the interface's shared fields via getters. + resp, err = queryOmitImpls(ctx, client, "3") + require.NoError(t, err) + + assert.Equal(t, "Animal", resp.Being.GetTypename()) + assert.Equal(t, "3", resp.Being.GetId()) + assert.Equal(t, "Fido", resp.Being.GetName()) + + other, ok := resp.Being.(*queryOmitImplsBeingOther) + require.Truef(t, ok, "got %T, expected catch-all queryOmitImplsBeingOther", resp.Being) + assert.Equal(t, "Animal", other.Typename) + assert.Equal(t, "3", other.Id) + assert.Equal(t, "Fido", other.Name) + + // id=missing → server returns null. Both branches return a nil + // interface; the catch-all must not be instantiated for null. + resp, err = queryOmitImpls(ctx, client, "9999999") + require.NoError(t, err) + assert.Nil(t, resp.Being) +} From 897b6bec803cea968eaf0d3c3c55d5293ee10357 Mon Sep 17 00:00:00 2001 From: Will Roden Date: Mon, 25 May 2026 14:34:23 -0500 Subject: [PATCH 04/12] handle abstract fragment spread inside concrete inline fragment --- generate/convert.go | 15 + generate/generate_test.go | 1 + ...mitImplsAbstractFragmentInConcrete.graphql | 14 + ...ImplsAbstractFragmentInConcrete.graphql.go | 466 ++++++++++++++++++ ...plsAbstractFragmentInConcrete.graphql.json | 9 + ...rete.graphql-testdata-queries-generated.go | 370 ++++++++++++++ 6 files changed, 875 insertions(+) create mode 100644 generate/testdata/queries/OmitImplsAbstractFragmentInConcrete.graphql create mode 100644 generate/testdata/snapshots/TestGenerate-OmitImplsAbstractFragmentInConcrete.graphql-OmitImplsAbstractFragmentInConcrete.graphql.go create mode 100644 generate/testdata/snapshots/TestGenerate-OmitImplsAbstractFragmentInConcrete.graphql-OmitImplsAbstractFragmentInConcrete.graphql.json create mode 100644 generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAbstractFragmentInConcrete.graphql-testdata-queries-generated.go diff --git a/generate/convert.go b/generate/convert.go index cc5656b8..e00601c8 100644 --- a/generate/convert.go +++ b/generate/convert.go @@ -964,11 +964,26 @@ func (g *generator) convertFragmentSpread( // type FA struct { ... } // // (other implementations) // when you spread F into a context of type A, we embed FA, not F. + matched := false for _, impl := range iface.Implementations { if impl.GraphQLName == containingTypedef.Name { typ = impl + matched = true + break } } + if !matched && iface.OtherImplementation != nil { + // With OmitUnreferencedImplementations enabled, the fragment's + // per-implementation struct for this concrete type may have been + // omitted (e.g. a fragment on an interface that has no concrete + // type conditions of its own). Fall back to the fragment's + // catch-all struct: it carries the same shared fields and is a + // valid Go struct, so embedding it inside the concrete-type + // struct generates correct code rather than failing later in + // FlattenedFields with an "embedded field was not a struct" + // error. + typ = iface.OtherImplementation + } } // TODO(benkraft): Set directive here if we ever allow @genqlient diff --git a/generate/generate_test.go b/generate/generate_test.go index 3239f6c2..851d9ca7 100644 --- a/generate/generate_test.go +++ b/generate/generate_test.go @@ -281,6 +281,7 @@ func TestGenerateWithConfig(t *testing.T) { "InterfaceNoFragments.graphql", "OmitImplsNamedFragmentOnInterface.graphql", "OmitImplsUnion.graphql", + "OmitImplsAbstractFragmentInConcrete.graphql", }, &Config{ OmitUnreferencedImplementations: true, }, diff --git a/generate/testdata/queries/OmitImplsAbstractFragmentInConcrete.graphql b/generate/testdata/queries/OmitImplsAbstractFragmentInConcrete.graphql new file mode 100644 index 00000000..7f1e87ca --- /dev/null +++ b/generate/testdata/queries/OmitImplsAbstractFragmentInConcrete.graphql @@ -0,0 +1,14 @@ +fragment ContentBasics on Content { + id + name +} + +query OmitImplsAbstractFragmentInConcrete { + randomItem { + id + ... on Article { + text + ...ContentBasics + } + } +} diff --git a/generate/testdata/snapshots/TestGenerate-OmitImplsAbstractFragmentInConcrete.graphql-OmitImplsAbstractFragmentInConcrete.graphql.go b/generate/testdata/snapshots/TestGenerate-OmitImplsAbstractFragmentInConcrete.graphql-OmitImplsAbstractFragmentInConcrete.graphql.go new file mode 100644 index 00000000..25ef32de --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-OmitImplsAbstractFragmentInConcrete.graphql-OmitImplsAbstractFragmentInConcrete.graphql.go @@ -0,0 +1,466 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package test + +import ( + "encoding/json" + "fmt" + + "github.com/Khan/genqlient/graphql" + "github.com/Khan/genqlient/internal/testutil" +) + +// ContentBasics includes the GraphQL fields of Content requested by the fragment ContentBasics. +// The GraphQL type's documentation follows. +// +// Content is implemented by various types like Article, Video, and Topic. +// +// ContentBasics is implemented by the following types: +// ContentBasicsArticle +// ContentBasicsTopic +// ContentBasicsVideo +type ContentBasics interface { + implementsGraphQLInterfaceContentBasics() + // 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 *ContentBasicsArticle) implementsGraphQLInterfaceContentBasics() {} +func (v *ContentBasicsTopic) implementsGraphQLInterfaceContentBasics() {} +func (v *ContentBasicsVideo) implementsGraphQLInterfaceContentBasics() {} + +func __unmarshalContentBasics(b []byte, v *ContentBasics) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "Article": + *v = new(ContentBasicsArticle) + return json.Unmarshal(b, *v) + case "Topic": + *v = new(ContentBasicsTopic) + return json.Unmarshal(b, *v) + case "Video": + *v = new(ContentBasicsVideo) + return json.Unmarshal(b, *v) + case "": + return fmt.Errorf( + "response was missing Content.__typename") + default: + return fmt.Errorf( + `unexpected concrete type for ContentBasics: "%v"`, tn.TypeName) + } +} + +func __marshalContentBasics(v *ContentBasics) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *ContentBasicsArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *ContentBasicsArticle + }{typename, v} + return json.Marshal(result) + case *ContentBasicsTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *ContentBasicsTopic + }{typename, v} + return json.Marshal(result) + case *ContentBasicsVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *ContentBasicsVideo + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for ContentBasics: "%T"`, v) + } +} + +// ContentBasics includes the GraphQL fields of Article requested by the fragment ContentBasics. +// The GraphQL type's documentation follows. +// +// Content is implemented by various types like Article, Video, and Topic. +type ContentBasicsArticle struct { + // ID is the identifier of the content. + Id testutil.ID `json:"id"` + Name string `json:"name"` +} + +// GetId returns ContentBasicsArticle.Id, and is useful for accessing the field via an interface. +func (v *ContentBasicsArticle) GetId() testutil.ID { return v.Id } + +// GetName returns ContentBasicsArticle.Name, and is useful for accessing the field via an interface. +func (v *ContentBasicsArticle) GetName() string { return v.Name } + +// ContentBasics includes the GraphQL fields of Topic requested by the fragment ContentBasics. +// The GraphQL type's documentation follows. +// +// Content is implemented by various types like Article, Video, and Topic. +type ContentBasicsTopic struct { + // ID is the identifier of the content. + Id testutil.ID `json:"id"` + Name string `json:"name"` +} + +// GetId returns ContentBasicsTopic.Id, and is useful for accessing the field via an interface. +func (v *ContentBasicsTopic) GetId() testutil.ID { return v.Id } + +// GetName returns ContentBasicsTopic.Name, and is useful for accessing the field via an interface. +func (v *ContentBasicsTopic) GetName() string { return v.Name } + +// ContentBasics includes the GraphQL fields of Video requested by the fragment ContentBasics. +// The GraphQL type's documentation follows. +// +// Content is implemented by various types like Article, Video, and Topic. +type ContentBasicsVideo struct { + // ID is the identifier of the content. + Id testutil.ID `json:"id"` + Name string `json:"name"` +} + +// GetId returns ContentBasicsVideo.Id, and is useful for accessing the field via an interface. +func (v *ContentBasicsVideo) GetId() testutil.ID { return v.Id } + +// GetName returns ContentBasicsVideo.Name, and is useful for accessing the field via an interface. +func (v *ContentBasicsVideo) GetName() string { return v.Name } + +// OmitImplsAbstractFragmentInConcreteRandomItemArticle includes the requested fields of the GraphQL type Article. +type OmitImplsAbstractFragmentInConcreteRandomItemArticle struct { + Typename string `json:"__typename"` + // ID is the identifier of the content. + Id testutil.ID `json:"id"` + Text string `json:"text"` + ContentBasicsArticle `json:"-"` +} + +// GetTypename returns OmitImplsAbstractFragmentInConcreteRandomItemArticle.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) GetTypename() string { + return v.Typename +} + +// GetId returns OmitImplsAbstractFragmentInConcreteRandomItemArticle.Id, and is useful for accessing the field via an interface. +func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) GetId() testutil.ID { return v.Id } + +// GetText returns OmitImplsAbstractFragmentInConcreteRandomItemArticle.Text, and is useful for accessing the field via an interface. +func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) GetText() string { return v.Text } + +// GetName returns OmitImplsAbstractFragmentInConcreteRandomItemArticle.Name, and is useful for accessing the field via an interface. +func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) GetName() string { + return v.ContentBasicsArticle.Name +} + +func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *OmitImplsAbstractFragmentInConcreteRandomItemArticle + graphql.NoUnmarshalJSON + } + firstPass.OmitImplsAbstractFragmentInConcreteRandomItemArticle = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.ContentBasicsArticle) + if err != nil { + return err + } + return nil +} + +type __premarshalOmitImplsAbstractFragmentInConcreteRandomItemArticle struct { + Typename string `json:"__typename"` + + Id testutil.ID `json:"id"` + + Text string `json:"text"` + + Name string `json:"name"` +} + +func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) __premarshalJSON() (*__premarshalOmitImplsAbstractFragmentInConcreteRandomItemArticle, error) { + var retval __premarshalOmitImplsAbstractFragmentInConcreteRandomItemArticle + + retval.Typename = v.Typename + retval.Id = v.Id + retval.Text = v.Text + retval.Name = v.ContentBasicsArticle.Name + return &retval, nil +} + +// OmitImplsAbstractFragmentInConcreteRandomItemContent includes the requested fields of the GraphQL interface Content. +// +// OmitImplsAbstractFragmentInConcreteRandomItemContent is implemented by the following types: +// OmitImplsAbstractFragmentInConcreteRandomItemArticle +// OmitImplsAbstractFragmentInConcreteRandomItemTopic +// OmitImplsAbstractFragmentInConcreteRandomItemVideo +// The GraphQL type's documentation follows. +// +// Content is implemented by various types like Article, Video, and Topic. +type OmitImplsAbstractFragmentInConcreteRandomItemContent interface { + implementsGraphQLInterfaceOmitImplsAbstractFragmentInConcreteRandomItemContent() + // 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 +} + +func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) implementsGraphQLInterfaceOmitImplsAbstractFragmentInConcreteRandomItemContent() { +} +func (v *OmitImplsAbstractFragmentInConcreteRandomItemTopic) implementsGraphQLInterfaceOmitImplsAbstractFragmentInConcreteRandomItemContent() { +} +func (v *OmitImplsAbstractFragmentInConcreteRandomItemVideo) implementsGraphQLInterfaceOmitImplsAbstractFragmentInConcreteRandomItemContent() { +} + +func __unmarshalOmitImplsAbstractFragmentInConcreteRandomItemContent(b []byte, v *OmitImplsAbstractFragmentInConcreteRandomItemContent) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "Article": + *v = new(OmitImplsAbstractFragmentInConcreteRandomItemArticle) + return json.Unmarshal(b, *v) + case "Topic": + *v = new(OmitImplsAbstractFragmentInConcreteRandomItemTopic) + return json.Unmarshal(b, *v) + case "Video": + *v = new(OmitImplsAbstractFragmentInConcreteRandomItemVideo) + return json.Unmarshal(b, *v) + case "": + return fmt.Errorf( + "response was missing Content.__typename") + default: + return fmt.Errorf( + `unexpected concrete type for OmitImplsAbstractFragmentInConcreteRandomItemContent: "%v"`, tn.TypeName) + } +} + +func __marshalOmitImplsAbstractFragmentInConcreteRandomItemContent(v *OmitImplsAbstractFragmentInConcreteRandomItemContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *OmitImplsAbstractFragmentInConcreteRandomItemArticle: + typename = "Article" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalOmitImplsAbstractFragmentInConcreteRandomItemArticle + }{typename, premarshaled} + return json.Marshal(result) + case *OmitImplsAbstractFragmentInConcreteRandomItemTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *OmitImplsAbstractFragmentInConcreteRandomItemTopic + }{typename, v} + return json.Marshal(result) + case *OmitImplsAbstractFragmentInConcreteRandomItemVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *OmitImplsAbstractFragmentInConcreteRandomItemVideo + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for OmitImplsAbstractFragmentInConcreteRandomItemContent: "%T"`, v) + } +} + +// OmitImplsAbstractFragmentInConcreteRandomItemTopic includes the requested fields of the GraphQL type Topic. +type OmitImplsAbstractFragmentInConcreteRandomItemTopic struct { + Typename string `json:"__typename"` + // ID is the identifier of the content. + Id testutil.ID `json:"id"` +} + +// GetTypename returns OmitImplsAbstractFragmentInConcreteRandomItemTopic.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsAbstractFragmentInConcreteRandomItemTopic) GetTypename() string { return v.Typename } + +// GetId returns OmitImplsAbstractFragmentInConcreteRandomItemTopic.Id, and is useful for accessing the field via an interface. +func (v *OmitImplsAbstractFragmentInConcreteRandomItemTopic) GetId() testutil.ID { return v.Id } + +// OmitImplsAbstractFragmentInConcreteRandomItemVideo includes the requested fields of the GraphQL type Video. +type OmitImplsAbstractFragmentInConcreteRandomItemVideo struct { + Typename string `json:"__typename"` + // ID is the identifier of the content. + Id testutil.ID `json:"id"` +} + +// GetTypename returns OmitImplsAbstractFragmentInConcreteRandomItemVideo.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsAbstractFragmentInConcreteRandomItemVideo) GetTypename() string { return v.Typename } + +// GetId returns OmitImplsAbstractFragmentInConcreteRandomItemVideo.Id, and is useful for accessing the field via an interface. +func (v *OmitImplsAbstractFragmentInConcreteRandomItemVideo) GetId() testutil.ID { return v.Id } + +// OmitImplsAbstractFragmentInConcreteResponse is returned by OmitImplsAbstractFragmentInConcrete on success. +type OmitImplsAbstractFragmentInConcreteResponse struct { + RandomItem OmitImplsAbstractFragmentInConcreteRandomItemContent `json:"-"` +} + +// GetRandomItem returns OmitImplsAbstractFragmentInConcreteResponse.RandomItem, and is useful for accessing the field via an interface. +func (v *OmitImplsAbstractFragmentInConcreteResponse) GetRandomItem() OmitImplsAbstractFragmentInConcreteRandomItemContent { + return v.RandomItem +} + +func (v *OmitImplsAbstractFragmentInConcreteResponse) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *OmitImplsAbstractFragmentInConcreteResponse + RandomItem json.RawMessage `json:"randomItem"` + graphql.NoUnmarshalJSON + } + firstPass.OmitImplsAbstractFragmentInConcreteResponse = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.RandomItem + src := firstPass.RandomItem + if len(src) != 0 && string(src) != "null" { + err = __unmarshalOmitImplsAbstractFragmentInConcreteRandomItemContent( + src, dst) + if err != nil { + return fmt.Errorf( + "unable to unmarshal OmitImplsAbstractFragmentInConcreteResponse.RandomItem: %w", err) + } + } + } + return nil +} + +type __premarshalOmitImplsAbstractFragmentInConcreteResponse struct { + RandomItem json.RawMessage `json:"randomItem"` +} + +func (v *OmitImplsAbstractFragmentInConcreteResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *OmitImplsAbstractFragmentInConcreteResponse) __premarshalJSON() (*__premarshalOmitImplsAbstractFragmentInConcreteResponse, error) { + var retval __premarshalOmitImplsAbstractFragmentInConcreteResponse + + { + + dst := &retval.RandomItem + src := v.RandomItem + var err error + *dst, err = __marshalOmitImplsAbstractFragmentInConcreteRandomItemContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal OmitImplsAbstractFragmentInConcreteResponse.RandomItem: %w", err) + } + } + return &retval, nil +} + +// The query executed by OmitImplsAbstractFragmentInConcrete. +const OmitImplsAbstractFragmentInConcrete_Operation = ` +query OmitImplsAbstractFragmentInConcrete { + randomItem { + __typename + id + ... on Article { + text + ... ContentBasics + } + } +} +fragment ContentBasics on Content { + id + name +} +` + +func OmitImplsAbstractFragmentInConcrete( + client_ graphql.Client, +) (data_ *OmitImplsAbstractFragmentInConcreteResponse, err_ error) { + req_ := &graphql.Request{ + OpName: "OmitImplsAbstractFragmentInConcrete", + Query: OmitImplsAbstractFragmentInConcrete_Operation, + } + + data_ = &OmitImplsAbstractFragmentInConcreteResponse{} + resp_ := &graphql.Response{Data: data_} + + err_ = client_.MakeRequest( + nil, + req_, + resp_, + ) + + return data_, err_ +} + diff --git a/generate/testdata/snapshots/TestGenerate-OmitImplsAbstractFragmentInConcrete.graphql-OmitImplsAbstractFragmentInConcrete.graphql.json b/generate/testdata/snapshots/TestGenerate-OmitImplsAbstractFragmentInConcrete.graphql-OmitImplsAbstractFragmentInConcrete.graphql.json new file mode 100644 index 00000000..21ae543f --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-OmitImplsAbstractFragmentInConcrete.graphql-OmitImplsAbstractFragmentInConcrete.graphql.json @@ -0,0 +1,9 @@ +{ + "operations": [ + { + "operationName": "OmitImplsAbstractFragmentInConcrete", + "query": "\nquery OmitImplsAbstractFragmentInConcrete {\n\trandomItem {\n\t\t__typename\n\t\tid\n\t\t... on Article {\n\t\t\ttext\n\t\t\t... ContentBasics\n\t\t}\n\t}\n}\nfragment ContentBasics on Content {\n\tid\n\tname\n}\n", + "sourceLocation": "testdata/queries/OmitImplsAbstractFragmentInConcrete.graphql" + } + ] +} diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAbstractFragmentInConcrete.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAbstractFragmentInConcrete.graphql-testdata-queries-generated.go new file mode 100644 index 00000000..721c82ad --- /dev/null +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAbstractFragmentInConcrete.graphql-testdata-queries-generated.go @@ -0,0 +1,370 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package queries + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/Khan/genqlient/graphql" +) + +// ContentBasics includes the GraphQL fields of Content requested by the fragment ContentBasics. +// The GraphQL type's documentation follows. +// +// Content is implemented by various types like Article, Video, and Topic. +// +// ContentBasics is implemented by the following types: +// ContentBasicsOther +type ContentBasics interface { + implementsGraphQLInterfaceContentBasics() + // GetId returns the interface-field "id" from its implementation. + // The GraphQL interface field's documentation follows. + // + // ID is the identifier of the content. + GetId() string + // GetName returns the interface-field "name" from its implementation. + GetName() string +} + +func (v *ContentBasicsOther) implementsGraphQLInterfaceContentBasics() {} + +func __unmarshalContentBasics(b []byte, v *ContentBasics) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "": + return fmt.Errorf( + "response was missing Content.__typename") + default: + *v = new(ContentBasicsOther) + return json.Unmarshal(b, *v) + } +} + +func __marshalContentBasics(v *ContentBasics) ([]byte, error) { + + switch v := (*v).(type) { + case *ContentBasicsOther: + return json.Marshal(v) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for ContentBasics: "%T"`, v) + } +} + +// ContentBasicsOther is the catch-all type used by ContentBasics for any concrete +// GraphQL type returned by the server that doesn't have its own +// generated struct (because no fragment selected it). It carries +// only the interface's shared fields; the concrete GraphQL type +// name is available via the __typename field. +type ContentBasicsOther struct { + // ID is the identifier of the content. + Id string `json:"id"` + Name string `json:"name"` +} + +// GetId returns ContentBasicsOther.Id, and is useful for accessing the field via an interface. +func (v *ContentBasicsOther) GetId() string { return v.Id } + +// GetName returns ContentBasicsOther.Name, and is useful for accessing the field via an interface. +func (v *ContentBasicsOther) GetName() string { return v.Name } + +// OmitImplsAbstractFragmentInConcreteRandomItemArticle includes the requested fields of the GraphQL type Article. +type OmitImplsAbstractFragmentInConcreteRandomItemArticle struct { + Typename string `json:"__typename"` + // ID is the identifier of the content. + Id string `json:"id"` + Text string `json:"text"` + ContentBasicsOther `json:"-"` +} + +// GetTypename returns OmitImplsAbstractFragmentInConcreteRandomItemArticle.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) GetTypename() string { + return v.Typename +} + +// GetId returns OmitImplsAbstractFragmentInConcreteRandomItemArticle.Id, and is useful for accessing the field via an interface. +func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) GetId() string { return v.Id } + +// GetText returns OmitImplsAbstractFragmentInConcreteRandomItemArticle.Text, and is useful for accessing the field via an interface. +func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) GetText() string { return v.Text } + +// GetName returns OmitImplsAbstractFragmentInConcreteRandomItemArticle.Name, and is useful for accessing the field via an interface. +func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) GetName() string { + return v.ContentBasicsOther.Name +} + +func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *OmitImplsAbstractFragmentInConcreteRandomItemArticle + graphql.NoUnmarshalJSON + } + firstPass.OmitImplsAbstractFragmentInConcreteRandomItemArticle = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.ContentBasicsOther) + if err != nil { + return err + } + return nil +} + +type __premarshalOmitImplsAbstractFragmentInConcreteRandomItemArticle struct { + Typename string `json:"__typename"` + + Id string `json:"id"` + + Text string `json:"text"` + + Name string `json:"name"` +} + +func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) __premarshalJSON() (*__premarshalOmitImplsAbstractFragmentInConcreteRandomItemArticle, error) { + var retval __premarshalOmitImplsAbstractFragmentInConcreteRandomItemArticle + + retval.Typename = v.Typename + retval.Id = v.Id + retval.Text = v.Text + retval.Name = v.ContentBasicsOther.Name + return &retval, nil +} + +// OmitImplsAbstractFragmentInConcreteRandomItemContent includes the requested fields of the GraphQL interface Content. +// +// OmitImplsAbstractFragmentInConcreteRandomItemContent is implemented by the following types: +// OmitImplsAbstractFragmentInConcreteRandomItemArticle +// OmitImplsAbstractFragmentInConcreteRandomItemContentOther +// The GraphQL type's documentation follows. +// +// Content is implemented by various types like Article, Video, and Topic. +type OmitImplsAbstractFragmentInConcreteRandomItemContent interface { + implementsGraphQLInterfaceOmitImplsAbstractFragmentInConcreteRandomItemContent() + // 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() string +} + +func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) implementsGraphQLInterfaceOmitImplsAbstractFragmentInConcreteRandomItemContent() { +} +func (v *OmitImplsAbstractFragmentInConcreteRandomItemContentOther) implementsGraphQLInterfaceOmitImplsAbstractFragmentInConcreteRandomItemContent() { +} + +func __unmarshalOmitImplsAbstractFragmentInConcreteRandomItemContent(b []byte, v *OmitImplsAbstractFragmentInConcreteRandomItemContent) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "Article": + *v = new(OmitImplsAbstractFragmentInConcreteRandomItemArticle) + return json.Unmarshal(b, *v) + case "": + return fmt.Errorf( + "response was missing Content.__typename") + default: + *v = new(OmitImplsAbstractFragmentInConcreteRandomItemContentOther) + return json.Unmarshal(b, *v) + } +} + +func __marshalOmitImplsAbstractFragmentInConcreteRandomItemContent(v *OmitImplsAbstractFragmentInConcreteRandomItemContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *OmitImplsAbstractFragmentInConcreteRandomItemArticle: + typename = "Article" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalOmitImplsAbstractFragmentInConcreteRandomItemArticle + }{typename, premarshaled} + return json.Marshal(result) + case *OmitImplsAbstractFragmentInConcreteRandomItemContentOther: + return json.Marshal(v) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for OmitImplsAbstractFragmentInConcreteRandomItemContent: "%T"`, v) + } +} + +// OmitImplsAbstractFragmentInConcreteRandomItemContentOther is the catch-all type used by OmitImplsAbstractFragmentInConcreteRandomItemContent for any concrete +// GraphQL type returned by the server that doesn't have its own +// generated struct (because no fragment selected it). It carries +// only the interface's shared fields; the concrete GraphQL type +// name is available via the __typename field. +type OmitImplsAbstractFragmentInConcreteRandomItemContentOther struct { + Typename string `json:"__typename"` + // ID is the identifier of the content. + Id string `json:"id"` +} + +// GetTypename returns OmitImplsAbstractFragmentInConcreteRandomItemContentOther.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsAbstractFragmentInConcreteRandomItemContentOther) GetTypename() string { + return v.Typename +} + +// GetId returns OmitImplsAbstractFragmentInConcreteRandomItemContentOther.Id, and is useful for accessing the field via an interface. +func (v *OmitImplsAbstractFragmentInConcreteRandomItemContentOther) GetId() string { return v.Id } + +// OmitImplsAbstractFragmentInConcreteResponse is returned by OmitImplsAbstractFragmentInConcrete on success. +type OmitImplsAbstractFragmentInConcreteResponse struct { + RandomItem OmitImplsAbstractFragmentInConcreteRandomItemContent `json:"-"` +} + +// GetRandomItem returns OmitImplsAbstractFragmentInConcreteResponse.RandomItem, and is useful for accessing the field via an interface. +func (v *OmitImplsAbstractFragmentInConcreteResponse) GetRandomItem() OmitImplsAbstractFragmentInConcreteRandomItemContent { + return v.RandomItem +} + +func (v *OmitImplsAbstractFragmentInConcreteResponse) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *OmitImplsAbstractFragmentInConcreteResponse + RandomItem json.RawMessage `json:"randomItem"` + graphql.NoUnmarshalJSON + } + firstPass.OmitImplsAbstractFragmentInConcreteResponse = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.RandomItem + src := firstPass.RandomItem + if len(src) != 0 && string(src) != "null" { + err = __unmarshalOmitImplsAbstractFragmentInConcreteRandomItemContent( + src, dst) + if err != nil { + return fmt.Errorf( + "unable to unmarshal OmitImplsAbstractFragmentInConcreteResponse.RandomItem: %w", err) + } + } + } + return nil +} + +type __premarshalOmitImplsAbstractFragmentInConcreteResponse struct { + RandomItem json.RawMessage `json:"randomItem"` +} + +func (v *OmitImplsAbstractFragmentInConcreteResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *OmitImplsAbstractFragmentInConcreteResponse) __premarshalJSON() (*__premarshalOmitImplsAbstractFragmentInConcreteResponse, error) { + var retval __premarshalOmitImplsAbstractFragmentInConcreteResponse + + { + + dst := &retval.RandomItem + src := v.RandomItem + var err error + *dst, err = __marshalOmitImplsAbstractFragmentInConcreteRandomItemContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal OmitImplsAbstractFragmentInConcreteResponse.RandomItem: %w", err) + } + } + return &retval, nil +} + +// The query executed by OmitImplsAbstractFragmentInConcrete. +const OmitImplsAbstractFragmentInConcrete_Operation = ` +query OmitImplsAbstractFragmentInConcrete { + randomItem { + __typename + id + ... on Article { + text + ... ContentBasics + } + } +} +fragment ContentBasics on Content { + id + name +} +` + +func OmitImplsAbstractFragmentInConcrete( + ctx_ context.Context, + client_ graphql.Client, +) (data_ *OmitImplsAbstractFragmentInConcreteResponse, err_ error) { + req_ := &graphql.Request{ + OpName: "OmitImplsAbstractFragmentInConcrete", + Query: OmitImplsAbstractFragmentInConcrete_Operation, + } + + data_ = &OmitImplsAbstractFragmentInConcreteResponse{} + resp_ := &graphql.Response{Data: data_} + + err_ = client_.MakeRequest( + ctx_, + req_, + resp_, + ) + + return data_, err_ +} + From fa5e8c0e9281a3e9c7e5c8554452ac09179dbe32 Mon Sep 17 00:00:00 2001 From: Will Roden Date: Mon, 25 May 2026 14:42:53 -0500 Subject: [PATCH 05/12] avoid catch-all name collisions with concrete impls --- generate/convert.go | 28 ++++++++++++- generate/generate_test.go | 82 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) diff --git a/generate/convert.go b/generate/convert.go index e00601c8..a878e490 100644 --- a/generate/convert.go +++ b/generate/convert.go @@ -823,7 +823,7 @@ func (g *generator) makeCatchAllStruct( catchAllFields = append(catchAllFields, f) } - catchAllName := interfaceGoName + "Other" + catchAllName := pickCatchAllName(g.typeMap, interfaceGoName) catchAll := &goStructType{ GoName: catchAllName, Fields: catchAllFields, @@ -853,6 +853,32 @@ func (g *generator) makeCatchAllStruct( return catchAllTyp, nil } +// pickCatchAllName returns a Go type name for a catch-all struct that does +// not collide with any name already in typeMap. The first choice is +// "Other", matching the user-visible naming convention. If +// the schema contains a concrete implementation whose generated Go name is +// already that string (for example, an implementation literally named "Other" +// under a named fragment, or one named "Other" under an +// interface field selection), we fall back to a disambiguated form rather +// than letting addType surface a misleading "conflicting definition / genqlient +// internal error" diagnostic to the user. +func pickCatchAllName(typeMap map[string]goType, interfaceGoName string) string { + primary := interfaceGoName + "Other" + if _, taken := typeMap[primary]; !taken { + return primary + } + fallback := interfaceGoName + "OtherCatchAll" + if _, taken := typeMap[fallback]; !taken { + return fallback + } + for i := 2; ; i++ { + candidate := fmt.Sprintf("%s%d", fallback, i) + if _, taken := typeMap[candidate]; !taken { + return candidate + } + } +} + // fragmentMatches returns true if the given fragment is "active" when applied // to the given type. // diff --git a/generate/generate_test.go b/generate/generate_test.go index 851d9ca7..cfb52fe6 100644 --- a/generate/generate_test.go +++ b/generate/generate_test.go @@ -397,3 +397,85 @@ func TestGenerateErrors(t *testing.T) { }) } } + +// TestOmitImplsCatchAllNameCollision verifies that +// omit_unreferenced_implementations gracefully handles schemas containing a +// concrete implementation whose Go type name would otherwise collide with the +// catch-all struct's own name (e.g. an interface "Content" with an +// implementation literally named "ContentOther"). Generation must succeed and +// the resulting Go code must compile, rather than failing with a misleading +// "genqlient internal error: conflicting definition" diagnostic. +func TestOmitImplsCatchAllNameCollision(t *testing.T) { + tmpDir := t.TempDir() + schemaPath := filepath.Join(tmpDir, "schema.graphql") + queryPath := filepath.Join(tmpDir, "Repro.graphql") + + schema := ` +type Query { + randomItem: Content +} + +interface Content { + id: ID! +} + +type Article implements Content { + id: ID! + text: String! +} + +type ContentOther implements Content { + id: ID! + flag: Boolean! +} +` + query := ` +query Repro { + randomItem { + id + ... on ContentOther { + flag + } + } +} +` + if err := os.WriteFile(schemaPath, []byte(schema), 0o644); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(queryPath, []byte(query), 0o644); err != nil { + t.Fatal(err) + } + + cfg := &Config{ + Schema: []string{schemaPath}, + Operations: []string{queryPath}, + Package: "test", + Generated: "out.go", + ContextType: "-", + OmitUnreferencedImplementations: true, + } + if err := cfg.ValidateAndFillDefaults("."); err != nil { + t.Fatal(err) + } + + generated, err := Generate(cfg) + if err != nil { + t.Fatalf("Generate failed: %v", err) + } + + // The real concrete impl ContentOther must take the "Other" name; the + // catch-all must fall back to "OtherCatchAll" rather than collide. + out := string(generated["out.go"]) + if !strings.Contains(out, "type ReproRandomItemContentOther struct") { + t.Errorf("expected concrete impl ReproRandomItemContentOther struct in generated code") + } + if !strings.Contains(out, "type ReproRandomItemContentOtherCatchAll struct") { + t.Errorf("expected catch-all ReproRandomItemContentOtherCatchAll struct in generated code") + } + + if !testing.Short() { + if err := buildGoFile("OmitImplsCollision", generated["out.go"]); err != nil { + t.Errorf("generated code does not compile: %v", err) + } + } +} From 3f014db3321a6a73fc19fe68967b52fe3df745ff Mon Sep 17 00:00:00 2001 From: Will Roden Date: Mon, 25 May 2026 14:49:46 -0500 Subject: [PATCH 06/12] always emit catch-all when omit option is enabled --- generate/convert.go | 26 +- generate/generate_test.go | 1 + .../OmitImplsAllImplsReferenced.graphql | 13 + ...hql-OmitImplsAllImplsReferenced.graphql.go | 232 ++++++++++++++++ ...l-OmitImplsAllImplsReferenced.graphql.json | 9 + ...nced.graphql-testdata-queries-generated.go | 252 ++++++++++++++++++ 6 files changed, 523 insertions(+), 10 deletions(-) create mode 100644 generate/testdata/queries/OmitImplsAllImplsReferenced.graphql create mode 100644 generate/testdata/snapshots/TestGenerate-OmitImplsAllImplsReferenced.graphql-OmitImplsAllImplsReferenced.graphql.go create mode 100644 generate/testdata/snapshots/TestGenerate-OmitImplsAllImplsReferenced.graphql-OmitImplsAllImplsReferenced.graphql.json create mode 100644 generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAllImplsReferenced.graphql-testdata-queries-generated.go diff --git a/generate/convert.go b/generate/convert.go index a878e490..55bf8865 100644 --- a/generate/convert.go +++ b/generate/convert.go @@ -526,18 +526,16 @@ func (g *generator) convertDefinition( // When omit_unreferenced_implementations is enabled, determine which // concrete types are explicitly referenced by fragments in the // selection set, and skip generating per-type structs for the rest. - // Anything we skip is handled at runtime by a single catch-all - // struct carrying only the interface's shared fields. + // Anything we skip — plus any future server-side implementation + // added after generation — is handled at runtime by a single + // catch-all struct carrying only the interface's shared fields. filteredImpls := implementationTypes - var omittedAny bool if g.Config.OmitUnreferencedImplementations { referenced := collectReferencedConcreteTypes(selectionSet, g.schema) kept := make([]*ast.Definition, 0, len(implementationTypes)) for _, implDef := range implementationTypes { if referenced[implDef.Name] { kept = append(kept, implDef) - } else { - omittedAny = true } } filteredImpls = kept @@ -571,7 +569,14 @@ func (g *generator) convertDefinition( goType.Implementations = append(goType.Implementations, implStructTyp) } - if omittedAny { + if g.Config.OmitUnreferencedImplementations { + // Always generate the catch-all when the option is enabled, even + // if every currently-known implementation is explicitly + // referenced. This is the contract documented in + // docs/genqlient.yaml: "any __typename returned by the server + // that doesn't match one of the explicitly-referenced types is + // decoded into the catch-all" — including __typenames for + // implementations added to the server after generation. otherType, err := g.makeCatchAllStruct(name, def.Name, sharedFields, selectionSet, pos) if err != nil { return nil, err @@ -1069,15 +1074,12 @@ func (g *generator) convertNamedFragment(fragment *ast.FragmentDefinition) (goTy sort.Slice(implementationTypes, func(i, j int) bool { return implementationTypes[i].Name < implementationTypes[j].Name }) filteredImpls := implementationTypes - var omittedAny bool if g.Config.OmitUnreferencedImplementations { referenced := collectReferencedConcreteTypes(fragment.SelectionSet, g.schema) kept := make([]*ast.Definition, 0, len(implementationTypes)) for _, implDef := range implementationTypes { if referenced[implDef.Name] { kept = append(kept, implDef) - } else { - omittedAny = true } } filteredImpls = kept @@ -1112,7 +1114,11 @@ func (g *generator) convertNamedFragment(fragment *ast.FragmentDefinition) (goTy g.typeMap[implTyp.GoName] = implTyp } - if omittedAny { + if g.Config.OmitUnreferencedImplementations { + // Always generate the catch-all when the option is enabled, so + // __typenames for implementations added to the server after + // generation still decode gracefully (matching the contract in + // docs/genqlient.yaml). otherType, err := g.makeCatchAllStruct(fragment.Name, typ.Name, fields, fragment.SelectionSet, fragment.Position) if err != nil { return nil, err diff --git a/generate/generate_test.go b/generate/generate_test.go index cfb52fe6..a4b88df4 100644 --- a/generate/generate_test.go +++ b/generate/generate_test.go @@ -282,6 +282,7 @@ func TestGenerateWithConfig(t *testing.T) { "OmitImplsNamedFragmentOnInterface.graphql", "OmitImplsUnion.graphql", "OmitImplsAbstractFragmentInConcrete.graphql", + "OmitImplsAllImplsReferenced.graphql", }, &Config{ OmitUnreferencedImplementations: true, }, diff --git a/generate/testdata/queries/OmitImplsAllImplsReferenced.graphql b/generate/testdata/queries/OmitImplsAllImplsReferenced.graphql new file mode 100644 index 00000000..4e723bea --- /dev/null +++ b/generate/testdata/queries/OmitImplsAllImplsReferenced.graphql @@ -0,0 +1,13 @@ +query OmitImplsAllImplsReferenced { + randomLeaf { + __typename + ... on Article { + id + text + } + ... on Video { + id + duration + } + } +} diff --git a/generate/testdata/snapshots/TestGenerate-OmitImplsAllImplsReferenced.graphql-OmitImplsAllImplsReferenced.graphql.go b/generate/testdata/snapshots/TestGenerate-OmitImplsAllImplsReferenced.graphql-OmitImplsAllImplsReferenced.graphql.go new file mode 100644 index 00000000..96ca4e46 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-OmitImplsAllImplsReferenced.graphql-OmitImplsAllImplsReferenced.graphql.go @@ -0,0 +1,232 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package test + +import ( + "encoding/json" + "fmt" + + "github.com/Khan/genqlient/graphql" + "github.com/Khan/genqlient/internal/testutil" +) + +// OmitImplsAllImplsReferencedRandomLeafArticle includes the requested fields of the GraphQL type Article. +type OmitImplsAllImplsReferencedRandomLeafArticle struct { + Typename string `json:"__typename"` + // ID is documented in the Content interface. + Id testutil.ID `json:"id"` + Text string `json:"text"` +} + +// GetTypename returns OmitImplsAllImplsReferencedRandomLeafArticle.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsAllImplsReferencedRandomLeafArticle) GetTypename() string { return v.Typename } + +// GetId returns OmitImplsAllImplsReferencedRandomLeafArticle.Id, and is useful for accessing the field via an interface. +func (v *OmitImplsAllImplsReferencedRandomLeafArticle) GetId() testutil.ID { return v.Id } + +// GetText returns OmitImplsAllImplsReferencedRandomLeafArticle.Text, and is useful for accessing the field via an interface. +func (v *OmitImplsAllImplsReferencedRandomLeafArticle) GetText() string { return v.Text } + +// OmitImplsAllImplsReferencedRandomLeafLeafContent includes the requested fields of the GraphQL interface LeafContent. +// +// OmitImplsAllImplsReferencedRandomLeafLeafContent is implemented by the following types: +// OmitImplsAllImplsReferencedRandomLeafArticle +// OmitImplsAllImplsReferencedRandomLeafVideo +// The GraphQL type's documentation follows. +// +// LeafContent represents content items that can't have child-nodes. +type OmitImplsAllImplsReferencedRandomLeafLeafContent interface { + implementsGraphQLInterfaceOmitImplsAllImplsReferencedRandomLeafLeafContent() + // GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values). + GetTypename() string +} + +func (v *OmitImplsAllImplsReferencedRandomLeafArticle) implementsGraphQLInterfaceOmitImplsAllImplsReferencedRandomLeafLeafContent() { +} +func (v *OmitImplsAllImplsReferencedRandomLeafVideo) implementsGraphQLInterfaceOmitImplsAllImplsReferencedRandomLeafLeafContent() { +} + +func __unmarshalOmitImplsAllImplsReferencedRandomLeafLeafContent(b []byte, v *OmitImplsAllImplsReferencedRandomLeafLeafContent) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "Article": + *v = new(OmitImplsAllImplsReferencedRandomLeafArticle) + return json.Unmarshal(b, *v) + case "Video": + *v = new(OmitImplsAllImplsReferencedRandomLeafVideo) + return json.Unmarshal(b, *v) + case "": + return fmt.Errorf( + "response was missing LeafContent.__typename") + default: + return fmt.Errorf( + `unexpected concrete type for OmitImplsAllImplsReferencedRandomLeafLeafContent: "%v"`, tn.TypeName) + } +} + +func __marshalOmitImplsAllImplsReferencedRandomLeafLeafContent(v *OmitImplsAllImplsReferencedRandomLeafLeafContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *OmitImplsAllImplsReferencedRandomLeafArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *OmitImplsAllImplsReferencedRandomLeafArticle + }{typename, v} + return json.Marshal(result) + case *OmitImplsAllImplsReferencedRandomLeafVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *OmitImplsAllImplsReferencedRandomLeafVideo + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for OmitImplsAllImplsReferencedRandomLeafLeafContent: "%T"`, v) + } +} + +// OmitImplsAllImplsReferencedRandomLeafVideo includes the requested fields of the GraphQL type Video. +type OmitImplsAllImplsReferencedRandomLeafVideo struct { + Typename string `json:"__typename"` + // ID is documented in the Content interface. + Id testutil.ID `json:"id"` + Duration int `json:"duration"` +} + +// GetTypename returns OmitImplsAllImplsReferencedRandomLeafVideo.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsAllImplsReferencedRandomLeafVideo) GetTypename() string { return v.Typename } + +// GetId returns OmitImplsAllImplsReferencedRandomLeafVideo.Id, and is useful for accessing the field via an interface. +func (v *OmitImplsAllImplsReferencedRandomLeafVideo) GetId() testutil.ID { return v.Id } + +// GetDuration returns OmitImplsAllImplsReferencedRandomLeafVideo.Duration, and is useful for accessing the field via an interface. +func (v *OmitImplsAllImplsReferencedRandomLeafVideo) GetDuration() int { return v.Duration } + +// OmitImplsAllImplsReferencedResponse is returned by OmitImplsAllImplsReferenced on success. +type OmitImplsAllImplsReferencedResponse struct { + RandomLeaf OmitImplsAllImplsReferencedRandomLeafLeafContent `json:"-"` +} + +// GetRandomLeaf returns OmitImplsAllImplsReferencedResponse.RandomLeaf, and is useful for accessing the field via an interface. +func (v *OmitImplsAllImplsReferencedResponse) GetRandomLeaf() OmitImplsAllImplsReferencedRandomLeafLeafContent { + return v.RandomLeaf +} + +func (v *OmitImplsAllImplsReferencedResponse) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *OmitImplsAllImplsReferencedResponse + RandomLeaf json.RawMessage `json:"randomLeaf"` + graphql.NoUnmarshalJSON + } + firstPass.OmitImplsAllImplsReferencedResponse = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.RandomLeaf + src := firstPass.RandomLeaf + if len(src) != 0 && string(src) != "null" { + err = __unmarshalOmitImplsAllImplsReferencedRandomLeafLeafContent( + src, dst) + if err != nil { + return fmt.Errorf( + "unable to unmarshal OmitImplsAllImplsReferencedResponse.RandomLeaf: %w", err) + } + } + } + return nil +} + +type __premarshalOmitImplsAllImplsReferencedResponse struct { + RandomLeaf json.RawMessage `json:"randomLeaf"` +} + +func (v *OmitImplsAllImplsReferencedResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *OmitImplsAllImplsReferencedResponse) __premarshalJSON() (*__premarshalOmitImplsAllImplsReferencedResponse, error) { + var retval __premarshalOmitImplsAllImplsReferencedResponse + + { + + dst := &retval.RandomLeaf + src := v.RandomLeaf + var err error + *dst, err = __marshalOmitImplsAllImplsReferencedRandomLeafLeafContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal OmitImplsAllImplsReferencedResponse.RandomLeaf: %w", err) + } + } + return &retval, nil +} + +// The query executed by OmitImplsAllImplsReferenced. +const OmitImplsAllImplsReferenced_Operation = ` +query OmitImplsAllImplsReferenced { + randomLeaf { + __typename + ... on Article { + id + text + } + ... on Video { + id + duration + } + } +} +` + +func OmitImplsAllImplsReferenced( + client_ graphql.Client, +) (data_ *OmitImplsAllImplsReferencedResponse, err_ error) { + req_ := &graphql.Request{ + OpName: "OmitImplsAllImplsReferenced", + Query: OmitImplsAllImplsReferenced_Operation, + } + + data_ = &OmitImplsAllImplsReferencedResponse{} + resp_ := &graphql.Response{Data: data_} + + err_ = client_.MakeRequest( + nil, + req_, + resp_, + ) + + return data_, err_ +} + diff --git a/generate/testdata/snapshots/TestGenerate-OmitImplsAllImplsReferenced.graphql-OmitImplsAllImplsReferenced.graphql.json b/generate/testdata/snapshots/TestGenerate-OmitImplsAllImplsReferenced.graphql-OmitImplsAllImplsReferenced.graphql.json new file mode 100644 index 00000000..25772efc --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-OmitImplsAllImplsReferenced.graphql-OmitImplsAllImplsReferenced.graphql.json @@ -0,0 +1,9 @@ +{ + "operations": [ + { + "operationName": "OmitImplsAllImplsReferenced", + "query": "\nquery OmitImplsAllImplsReferenced {\n\trandomLeaf {\n\t\t__typename\n\t\t... on Article {\n\t\t\tid\n\t\t\ttext\n\t\t}\n\t\t... on Video {\n\t\t\tid\n\t\t\tduration\n\t\t}\n\t}\n}\n", + "sourceLocation": "testdata/queries/OmitImplsAllImplsReferenced.graphql" + } + ] +} diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAllImplsReferenced.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAllImplsReferenced.graphql-testdata-queries-generated.go new file mode 100644 index 00000000..315f5a19 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAllImplsReferenced.graphql-testdata-queries-generated.go @@ -0,0 +1,252 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package queries + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/Khan/genqlient/graphql" +) + +// OmitImplsAllImplsReferencedRandomLeafArticle includes the requested fields of the GraphQL type Article. +type OmitImplsAllImplsReferencedRandomLeafArticle struct { + Typename string `json:"__typename"` + // ID is documented in the Content interface. + Id string `json:"id"` + Text string `json:"text"` +} + +// GetTypename returns OmitImplsAllImplsReferencedRandomLeafArticle.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsAllImplsReferencedRandomLeafArticle) GetTypename() string { return v.Typename } + +// GetId returns OmitImplsAllImplsReferencedRandomLeafArticle.Id, and is useful for accessing the field via an interface. +func (v *OmitImplsAllImplsReferencedRandomLeafArticle) GetId() string { return v.Id } + +// GetText returns OmitImplsAllImplsReferencedRandomLeafArticle.Text, and is useful for accessing the field via an interface. +func (v *OmitImplsAllImplsReferencedRandomLeafArticle) GetText() string { return v.Text } + +// OmitImplsAllImplsReferencedRandomLeafLeafContent includes the requested fields of the GraphQL interface LeafContent. +// +// OmitImplsAllImplsReferencedRandomLeafLeafContent is implemented by the following types: +// OmitImplsAllImplsReferencedRandomLeafArticle +// OmitImplsAllImplsReferencedRandomLeafVideo +// OmitImplsAllImplsReferencedRandomLeafLeafContentOther +// The GraphQL type's documentation follows. +// +// LeafContent represents content items that can't have child-nodes. +type OmitImplsAllImplsReferencedRandomLeafLeafContent interface { + implementsGraphQLInterfaceOmitImplsAllImplsReferencedRandomLeafLeafContent() + // GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values). + GetTypename() string +} + +func (v *OmitImplsAllImplsReferencedRandomLeafArticle) implementsGraphQLInterfaceOmitImplsAllImplsReferencedRandomLeafLeafContent() { +} +func (v *OmitImplsAllImplsReferencedRandomLeafVideo) implementsGraphQLInterfaceOmitImplsAllImplsReferencedRandomLeafLeafContent() { +} +func (v *OmitImplsAllImplsReferencedRandomLeafLeafContentOther) implementsGraphQLInterfaceOmitImplsAllImplsReferencedRandomLeafLeafContent() { +} + +func __unmarshalOmitImplsAllImplsReferencedRandomLeafLeafContent(b []byte, v *OmitImplsAllImplsReferencedRandomLeafLeafContent) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "Article": + *v = new(OmitImplsAllImplsReferencedRandomLeafArticle) + return json.Unmarshal(b, *v) + case "Video": + *v = new(OmitImplsAllImplsReferencedRandomLeafVideo) + return json.Unmarshal(b, *v) + case "": + return fmt.Errorf( + "response was missing LeafContent.__typename") + default: + *v = new(OmitImplsAllImplsReferencedRandomLeafLeafContentOther) + return json.Unmarshal(b, *v) + } +} + +func __marshalOmitImplsAllImplsReferencedRandomLeafLeafContent(v *OmitImplsAllImplsReferencedRandomLeafLeafContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *OmitImplsAllImplsReferencedRandomLeafArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *OmitImplsAllImplsReferencedRandomLeafArticle + }{typename, v} + return json.Marshal(result) + case *OmitImplsAllImplsReferencedRandomLeafVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *OmitImplsAllImplsReferencedRandomLeafVideo + }{typename, v} + return json.Marshal(result) + case *OmitImplsAllImplsReferencedRandomLeafLeafContentOther: + return json.Marshal(v) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for OmitImplsAllImplsReferencedRandomLeafLeafContent: "%T"`, v) + } +} + +// OmitImplsAllImplsReferencedRandomLeafLeafContentOther is the catch-all type used by OmitImplsAllImplsReferencedRandomLeafLeafContent for any concrete +// GraphQL type returned by the server that doesn't have its own +// generated struct (because no fragment selected it). It carries +// only the interface's shared fields; the concrete GraphQL type +// name is available via the __typename field. +type OmitImplsAllImplsReferencedRandomLeafLeafContentOther struct { + Typename string `json:"__typename"` +} + +// GetTypename returns OmitImplsAllImplsReferencedRandomLeafLeafContentOther.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsAllImplsReferencedRandomLeafLeafContentOther) GetTypename() string { + return v.Typename +} + +// OmitImplsAllImplsReferencedRandomLeafVideo includes the requested fields of the GraphQL type Video. +type OmitImplsAllImplsReferencedRandomLeafVideo struct { + Typename string `json:"__typename"` + // ID is documented in the Content interface. + Id string `json:"id"` + Duration int `json:"duration"` +} + +// GetTypename returns OmitImplsAllImplsReferencedRandomLeafVideo.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsAllImplsReferencedRandomLeafVideo) GetTypename() string { return v.Typename } + +// GetId returns OmitImplsAllImplsReferencedRandomLeafVideo.Id, and is useful for accessing the field via an interface. +func (v *OmitImplsAllImplsReferencedRandomLeafVideo) GetId() string { return v.Id } + +// GetDuration returns OmitImplsAllImplsReferencedRandomLeafVideo.Duration, and is useful for accessing the field via an interface. +func (v *OmitImplsAllImplsReferencedRandomLeafVideo) GetDuration() int { return v.Duration } + +// OmitImplsAllImplsReferencedResponse is returned by OmitImplsAllImplsReferenced on success. +type OmitImplsAllImplsReferencedResponse struct { + RandomLeaf OmitImplsAllImplsReferencedRandomLeafLeafContent `json:"-"` +} + +// GetRandomLeaf returns OmitImplsAllImplsReferencedResponse.RandomLeaf, and is useful for accessing the field via an interface. +func (v *OmitImplsAllImplsReferencedResponse) GetRandomLeaf() OmitImplsAllImplsReferencedRandomLeafLeafContent { + return v.RandomLeaf +} + +func (v *OmitImplsAllImplsReferencedResponse) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *OmitImplsAllImplsReferencedResponse + RandomLeaf json.RawMessage `json:"randomLeaf"` + graphql.NoUnmarshalJSON + } + firstPass.OmitImplsAllImplsReferencedResponse = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.RandomLeaf + src := firstPass.RandomLeaf + if len(src) != 0 && string(src) != "null" { + err = __unmarshalOmitImplsAllImplsReferencedRandomLeafLeafContent( + src, dst) + if err != nil { + return fmt.Errorf( + "unable to unmarshal OmitImplsAllImplsReferencedResponse.RandomLeaf: %w", err) + } + } + } + return nil +} + +type __premarshalOmitImplsAllImplsReferencedResponse struct { + RandomLeaf json.RawMessage `json:"randomLeaf"` +} + +func (v *OmitImplsAllImplsReferencedResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *OmitImplsAllImplsReferencedResponse) __premarshalJSON() (*__premarshalOmitImplsAllImplsReferencedResponse, error) { + var retval __premarshalOmitImplsAllImplsReferencedResponse + + { + + dst := &retval.RandomLeaf + src := v.RandomLeaf + var err error + *dst, err = __marshalOmitImplsAllImplsReferencedRandomLeafLeafContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal OmitImplsAllImplsReferencedResponse.RandomLeaf: %w", err) + } + } + return &retval, nil +} + +// The query executed by OmitImplsAllImplsReferenced. +const OmitImplsAllImplsReferenced_Operation = ` +query OmitImplsAllImplsReferenced { + randomLeaf { + __typename + ... on Article { + id + text + } + ... on Video { + id + duration + } + } +} +` + +func OmitImplsAllImplsReferenced( + ctx_ context.Context, + client_ graphql.Client, +) (data_ *OmitImplsAllImplsReferencedResponse, err_ error) { + req_ := &graphql.Request{ + OpName: "OmitImplsAllImplsReferenced", + Query: OmitImplsAllImplsReferenced_Operation, + } + + data_ = &OmitImplsAllImplsReferencedResponse{} + resp_ := &graphql.Response{Data: data_} + + err_ = client_.MakeRequest( + ctx_, + req_, + resp_, + ) + + return data_, err_ +} + From 3643578f36170bf6988b19d824a40dd352009403 Mon Sep 17 00:00:00 2001 From: Will Roden Date: Mon, 25 May 2026 15:52:27 -0500 Subject: [PATCH 07/12] trim CHANGELOG entry --- docs/CHANGELOG.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 78a6befc..b25f5fcf 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -29,15 +29,7 @@ Note that genqlient now requires Go 1.23 or higher, and is tested through Go 1.2 ### New features: - Added `--version` flag to print version information including commit hash and build date -- Added `omit_unreferenced_implementations` config option. When enabled, genqlient - skips generating per-type structs for interface and union implementations - that aren't referenced by an inline or named fragment in the selection set; - a single catch-all struct per interface selection carries the shared - fields and absorbs any `__typename` the server returns that wasn't - explicitly fragment-conditioned. This dramatically reduces generated - code size for interfaces with many implementations (e.g. Relay-style - `Node`) and gracefully decodes unknown concrete types instead of - returning an error. See [#416](https://github.com/Khan/genqlient/issues/416). +- Added `omit_unreferenced_implementations` config option to collapse unfragmented interface/union implementations into a single catch-all struct (fixes [#416](https://github.com/Khan/genqlient/issues/416)). ### Bug fixes: From 18d508f82f66933012404a840173f19fc9d835f9 Mon Sep 17 00:00:00 2001 From: Will Roden Date: Mon, 25 May 2026 15:59:36 -0500 Subject: [PATCH 08/12] trim code comments to match repo style --- generate/config.go | 38 +++----- generate/convert.go | 97 ++++++------------- generate/marshal_helper.go.tmpl | 8 +- ...ents.graphql-testdata-queries-generated.go | 18 +--- ...rete.graphql-testdata-queries-generated.go | 12 +-- ...nced.graphql-testdata-queries-generated.go | 6 +- ...face.graphql-testdata-queries-generated.go | 12 +-- ...nion.graphql-testdata-queries-generated.go | 6 +- ...ment.graphql-testdata-queries-generated.go | 6 +- ...ment.graphql-testdata-queries-generated.go | 12 +-- generate/types.go | 6 +- generate/unmarshal_helper.go.tmpl | 4 +- internal/integration/omitimpls/generated.go | 6 +- 13 files changed, 61 insertions(+), 170 deletions(-) diff --git a/generate/config.go b/generate/config.go index 663f2530..b62eb521 100644 --- a/generate/config.go +++ b/generate/config.go @@ -22,29 +22,21 @@ type Config struct { // The following fields are documented in the [genqlient.yaml docs]. // // [genqlient.yaml docs]: https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml - Schema StringList `yaml:"schema"` - Operations StringList `yaml:"operations"` - Generated string `yaml:"generated"` - Package string `yaml:"package"` - ExportOperations string `yaml:"export_operations"` - ContextType string `yaml:"context_type"` - ClientGetter string `yaml:"client_getter"` - Bindings map[string]*TypeBinding `yaml:"bindings"` - PackageBindings []*PackageBinding `yaml:"package_bindings"` - Casing Casing `yaml:"casing"` - Optional string `yaml:"optional"` - OptionalGenericType string `yaml:"optional_generic_type"` - StructReferences bool `yaml:"use_struct_references"` - Extensions bool `yaml:"use_extensions"` - - // OmitUnreferencedImplementations, when true, makes genqlient skip - // generating per-type structs for interface/union implementations that - // are not referenced by any inline or named fragment in the selection - // set. A single catch-all struct is generated per interface selection - // to carry the shared fields and preserve the previous fallback - // behavior for any __typename returned by the server that we don't - // have a typed representation for. - OmitUnreferencedImplementations bool `yaml:"omit_unreferenced_implementations"` + Schema StringList `yaml:"schema"` + Operations StringList `yaml:"operations"` + Generated string `yaml:"generated"` + Package string `yaml:"package"` + ExportOperations string `yaml:"export_operations"` + ContextType string `yaml:"context_type"` + ClientGetter string `yaml:"client_getter"` + Bindings map[string]*TypeBinding `yaml:"bindings"` + PackageBindings []*PackageBinding `yaml:"package_bindings"` + Casing Casing `yaml:"casing"` + Optional string `yaml:"optional"` + OptionalGenericType string `yaml:"optional_generic_type"` + StructReferences bool `yaml:"use_struct_references"` + Extensions bool `yaml:"use_extensions"` + OmitUnreferencedImplementations bool `yaml:"omit_unreferenced_implementations"` // The directory of the config-file (relative to which all the other paths // are resolved). Set by ValidateAndFillDefaults. diff --git a/generate/convert.go b/generate/convert.go index 55bf8865..4aebc207 100644 --- a/generate/convert.go +++ b/generate/convert.go @@ -523,12 +523,9 @@ func (g *generator) convertDefinition( // Make sure we generate stable output by sorting the types by name when we get them sort.Slice(implementationTypes, func(i, j int) bool { return implementationTypes[i].Name < implementationTypes[j].Name }) - // When omit_unreferenced_implementations is enabled, determine which - // concrete types are explicitly referenced by fragments in the - // selection set, and skip generating per-type structs for the rest. - // Anything we skip — plus any future server-side implementation - // added after generation — is handled at runtime by a single - // catch-all struct carrying only the interface's shared fields. + // If omit_unreferenced_implementations is enabled, only generate + // per-type structs for implementations a fragment actually + // references; the rest are absorbed by the catch-all below. filteredImpls := implementationTypes if g.Config.OmitUnreferencedImplementations { referenced := collectReferencedConcreteTypes(selectionSet, g.schema) @@ -570,13 +567,8 @@ func (g *generator) convertDefinition( } if g.Config.OmitUnreferencedImplementations { - // Always generate the catch-all when the option is enabled, even - // if every currently-known implementation is explicitly - // referenced. This is the contract documented in - // docs/genqlient.yaml: "any __typename returned by the server - // that doesn't match one of the explicitly-referenced types is - // decoded into the catch-all" — including __typenames for - // implementations added to the server after generation. + // Always emit the catch-all so unknown __typenames (e.g. from + // server-side schema additions) decode gracefully. otherType, err := g.makeCatchAllStruct(name, def.Name, sharedFields, selectionSet, pos) if err != nil { return nil, err @@ -747,13 +739,10 @@ func (g *generator) convertSelectionSet( return uniqFields, nil } -// collectReferencedConcreteTypes walks an interface/union selection set and -// returns the set of concrete (Object) type names referenced via inline or -// named fragments, including transitively through nested fragments. Type -// conditions that name an interface or union are NOT expanded to all their -// possible concrete types — we only recurse into the fragment body. This -// makes OmitUnreferencedImplementations meaningful when fragments are spread -// on abstract types (e.g. a fragment on Node spread at a Node-typed field). +// collectReferencedConcreteTypes returns the set of concrete (Object) type +// names reachable via inline or named fragments in sel, recursing through +// nested fragments. Type conditions naming an interface or union do NOT +// expand to their possible concrete types; the catch-all handles those. func collectReferencedConcreteTypes(sel ast.SelectionSet, schema *ast.Schema) map[string]bool { referenced := map[string]bool{} addCondition := func(condName string) { @@ -777,29 +766,21 @@ func collectReferencedConcreteTypes(sel ast.SelectionSet, schema *ast.Schema) ma walk(s.Definition.SelectionSet) } } - // Plain field selections don't introduce new type conditions - // at this level; nested type conditions are processed when we - // convert those fields' own selection sets. } } walk(sel) return referenced } -// makeCatchAllStruct builds the catch-all goStructType used by -// OmitUnreferencedImplementations and registers it via g.addType. The -// catch-all carries only the interface's SharedFields (which always include -// __typename, injected by preprocessQueryDocument). At runtime the -// unmarshal-helper instantiates this struct whenever the server returns a -// __typename that doesn't match an explicitly-referenced implementation. +// makeCatchAllStruct builds and registers the catch-all goStructType used +// by OmitUnreferencedImplementations. The struct carries the interface's +// shared fields (always including __typename); the unmarshal-helper +// instantiates it whenever the server returns a __typename without an +// explicitly-referenced implementation. // -// When SharedFields contains an embedded fragment-spread whose GoType is -// itself a *goInterfaceType (a fragment on an abstract type), we substitute -// the fragment-interface's own catch-all so the resulting Go type is a -// valid struct embedding only other structs. This requires the -// option-aware path to have produced a catch-all for that fragment; in -// practice that's always true when this code runs, because the same -// OmitUnreferencedImplementations option drove its construction. +// Embedded fragment-spreads whose GoType is itself a *goInterfaceType are +// swapped for that fragment's catch-all, so the result is a valid Go +// struct embedding only structs. func (g *generator) makeCatchAllStruct( interfaceGoName, graphQLName string, sharedFields []*goStructField, @@ -813,11 +794,8 @@ func (g *generator) makeCatchAllStruct( if ok { if iface.OtherImplementation == nil { return nil, errorf(pos, - "genqlient internal error: cannot build catch-all for %s: "+ - "embedded fragment %s has no catch-all of its own "+ - "(this is expected to be impossible when "+ - "omit_unreferenced_implementations is enabled)", - interfaceGoName, iface.GoName) + "genqlient internal error: embedded fragment %s has no catch-all", + iface.GoName) } swapped := *f swapped.GoType = iface.OtherImplementation @@ -836,11 +814,8 @@ func (g *generator) makeCatchAllStruct( descriptionInfo: descriptionInfo{ GraphQLName: graphQLName, CommentOverride: fmt.Sprintf( - "%s is the catch-all type used by %s for any concrete\n"+ - "GraphQL type returned by the server that doesn't have its own\n"+ - "generated struct (because no fragment selected it). It carries\n"+ - "only the interface's shared fields; the concrete GraphQL type\n"+ - "name is available via the __typename field.", + "%s is the catch-all for %s implementations that aren't "+ + "explicitly fragmented; the concrete type-name is in __typename.", catchAllName, interfaceGoName), }, Generator: g, @@ -852,21 +827,15 @@ func (g *generator) makeCatchAllStruct( catchAllTyp, ok := registered.(*goStructType) if !ok { return nil, errorf(pos, - "genqlient internal error: catch-all for %s registered as %T, not *goStructType", + "genqlient internal error: catch-all for %s registered as %T", interfaceGoName, registered) } return catchAllTyp, nil } -// pickCatchAllName returns a Go type name for a catch-all struct that does -// not collide with any name already in typeMap. The first choice is -// "Other", matching the user-visible naming convention. If -// the schema contains a concrete implementation whose generated Go name is -// already that string (for example, an implementation literally named "Other" -// under a named fragment, or one named "Other" under an -// interface field selection), we fall back to a disambiguated form rather -// than letting addType surface a misleading "conflicting definition / genqlient -// internal error" diagnostic to the user. +// pickCatchAllName returns a Go name for the catch-all that does not collide +// with anything already in typeMap. Tries "<…>Other" first, then +// "<…>OtherCatchAll", then numeric suffixes. func pickCatchAllName(typeMap map[string]goType, interfaceGoName string) string { primary := interfaceGoName + "Other" if _, taken := typeMap[primary]; !taken { @@ -1004,15 +973,9 @@ func (g *generator) convertFragmentSpread( } } if !matched && iface.OtherImplementation != nil { - // With OmitUnreferencedImplementations enabled, the fragment's - // per-implementation struct for this concrete type may have been - // omitted (e.g. a fragment on an interface that has no concrete - // type conditions of its own). Fall back to the fragment's - // catch-all struct: it carries the same shared fields and is a - // valid Go struct, so embedding it inside the concrete-type - // struct generates correct code rather than failing later in - // FlattenedFields with an "embedded field was not a struct" - // error. + // The per-implementation struct may have been omitted by + // OmitUnreferencedImplementations; fall back to the catch-all, + // which is a real struct with the same shared fields. typ = iface.OtherImplementation } } @@ -1115,10 +1078,6 @@ func (g *generator) convertNamedFragment(fragment *ast.FragmentDefinition) (goTy } if g.Config.OmitUnreferencedImplementations { - // Always generate the catch-all when the option is enabled, so - // __typenames for implementations added to the server after - // generation still decode gracefully (matching the contract in - // docs/genqlient.yaml). otherType, err := g.makeCatchAllStruct(fragment.Name, typ.Name, fields, fragment.SelectionSet, fragment.Position) if err != nil { return nil, err diff --git a/generate/marshal_helper.go.tmpl b/generate/marshal_helper.go.tmpl index 8057e3b3..9977050f 100644 --- a/generate/marshal_helper.go.tmpl +++ b/generate/marshal_helper.go.tmpl @@ -40,12 +40,8 @@ func __marshal{{.GoName}}(v *{{.GoName}}) ([]byte, error) { {{if .OtherImplementation -}} case *{{.OtherImplementation.GoName}}: {{/* Catch-all from OmitUnreferencedImplementations. Its own - __typename field carries the concrete GraphQL type-name, so we - don't need to splice it in with a wrapper struct like we do for - the typed implementations above. This assumes the caller - populated __typename (which is always true for values produced - by our unmarshal-helper; callers constructing a value - programmatically should set it themselves). */ -}} + __typename field carries the GraphQL type-name, so no + wrapper struct is needed. */ -}} {{if .OtherImplementation.NeedsMarshaling -}} premarshaled, err := v.__premarshalJSON() if err != nil { diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-InterfaceNoFragments.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-InterfaceNoFragments.graphql-testdata-queries-generated.go index 092cc66b..935ca4b6 100644 --- a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-InterfaceNoFragments.graphql-testdata-queries-generated.go +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-InterfaceNoFragments.graphql-testdata-queries-generated.go @@ -69,11 +69,7 @@ func __marshalInterfaceNoFragmentsQueryRandomItemContent(v *InterfaceNoFragments } } -// InterfaceNoFragmentsQueryRandomItemContentOther is the catch-all type used by InterfaceNoFragmentsQueryRandomItemContent for any concrete -// GraphQL type returned by the server that doesn't have its own -// generated struct (because no fragment selected it). It carries -// only the interface's shared fields; the concrete GraphQL type -// name is available via the __typename field. +// InterfaceNoFragmentsQueryRandomItemContentOther is the catch-all for InterfaceNoFragmentsQueryRandomItemContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. type InterfaceNoFragmentsQueryRandomItemContentOther struct { Typename string `json:"__typename"` // ID is the identifier of the content. @@ -149,11 +145,7 @@ func __marshalInterfaceNoFragmentsQueryRandomItemWithTypeNameContent(v *Interfac } } -// InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther is the catch-all type used by InterfaceNoFragmentsQueryRandomItemWithTypeNameContent for any concrete -// GraphQL type returned by the server that doesn't have its own -// generated struct (because no fragment selected it). It carries -// only the interface's shared fields; the concrete GraphQL type -// name is available via the __typename field. +// InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther is the catch-all for InterfaceNoFragmentsQueryRandomItemWithTypeNameContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. type InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther struct { Typename string `json:"__typename"` // ID is the identifier of the content. @@ -397,11 +389,7 @@ func __marshalInterfaceNoFragmentsQueryWithPointerContent(v *InterfaceNoFragment } } -// InterfaceNoFragmentsQueryWithPointerContentOther is the catch-all type used by InterfaceNoFragmentsQueryWithPointerContent for any concrete -// GraphQL type returned by the server that doesn't have its own -// generated struct (because no fragment selected it). It carries -// only the interface's shared fields; the concrete GraphQL type -// name is available via the __typename field. +// InterfaceNoFragmentsQueryWithPointerContentOther is the catch-all for InterfaceNoFragmentsQueryWithPointerContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. type InterfaceNoFragmentsQueryWithPointerContentOther struct { Typename string `json:"__typename"` // ID is the identifier of the content. diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAbstractFragmentInConcrete.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAbstractFragmentInConcrete.graphql-testdata-queries-generated.go index 721c82ad..6fed806d 100644 --- a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAbstractFragmentInConcrete.graphql-testdata-queries-generated.go +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAbstractFragmentInConcrete.graphql-testdata-queries-generated.go @@ -66,11 +66,7 @@ func __marshalContentBasics(v *ContentBasics) ([]byte, error) { } } -// ContentBasicsOther is the catch-all type used by ContentBasics for any concrete -// GraphQL type returned by the server that doesn't have its own -// generated struct (because no fragment selected it). It carries -// only the interface's shared fields; the concrete GraphQL type -// name is available via the __typename field. +// ContentBasicsOther is the catch-all for ContentBasics implementations that aren't explicitly fragmented; the concrete type-name is in __typename. type ContentBasicsOther struct { // ID is the identifier of the content. Id string `json:"id"` @@ -237,11 +233,7 @@ func __marshalOmitImplsAbstractFragmentInConcreteRandomItemContent(v *OmitImplsA } } -// OmitImplsAbstractFragmentInConcreteRandomItemContentOther is the catch-all type used by OmitImplsAbstractFragmentInConcreteRandomItemContent for any concrete -// GraphQL type returned by the server that doesn't have its own -// generated struct (because no fragment selected it). It carries -// only the interface's shared fields; the concrete GraphQL type -// name is available via the __typename field. +// OmitImplsAbstractFragmentInConcreteRandomItemContentOther is the catch-all for OmitImplsAbstractFragmentInConcreteRandomItemContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. type OmitImplsAbstractFragmentInConcreteRandomItemContentOther struct { Typename string `json:"__typename"` // ID is the identifier of the content. diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAllImplsReferenced.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAllImplsReferenced.graphql-testdata-queries-generated.go index 315f5a19..b4034701 100644 --- a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAllImplsReferenced.graphql-testdata-queries-generated.go +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAllImplsReferenced.graphql-testdata-queries-generated.go @@ -108,11 +108,7 @@ func __marshalOmitImplsAllImplsReferencedRandomLeafLeafContent(v *OmitImplsAllIm } } -// OmitImplsAllImplsReferencedRandomLeafLeafContentOther is the catch-all type used by OmitImplsAllImplsReferencedRandomLeafLeafContent for any concrete -// GraphQL type returned by the server that doesn't have its own -// generated struct (because no fragment selected it). It carries -// only the interface's shared fields; the concrete GraphQL type -// name is available via the __typename field. +// OmitImplsAllImplsReferencedRandomLeafLeafContentOther is the catch-all for OmitImplsAllImplsReferencedRandomLeafLeafContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. type OmitImplsAllImplsReferencedRandomLeafLeafContentOther struct { Typename string `json:"__typename"` } diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsNamedFragmentOnInterface.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsNamedFragmentOnInterface.graphql-testdata-queries-generated.go index 6c616fd9..8802f260 100644 --- a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsNamedFragmentOnInterface.graphql-testdata-queries-generated.go +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsNamedFragmentOnInterface.graphql-testdata-queries-generated.go @@ -100,11 +100,7 @@ func (v *NamedContentFieldsArticle) GetName() string { return v.Name } // GetText returns NamedContentFieldsArticle.Text, and is useful for accessing the field via an interface. func (v *NamedContentFieldsArticle) GetText() string { return v.Text } -// NamedContentFieldsOther is the catch-all type used by NamedContentFields for any concrete -// GraphQL type returned by the server that doesn't have its own -// generated struct (because no fragment selected it). It carries -// only the interface's shared fields; the concrete GraphQL type -// name is available via the __typename field. +// NamedContentFieldsOther is the catch-all for NamedContentFields implementations that aren't explicitly fragmented; the concrete type-name is in __typename. type NamedContentFieldsOther struct { // ID is the identifier of the content. Id string `json:"id"` @@ -270,11 +266,7 @@ func __marshalOmitImplsNamedFragmentOnInterfaceRandomItemContent(v *OmitImplsNam } } -// OmitImplsNamedFragmentOnInterfaceRandomItemContentOther is the catch-all type used by OmitImplsNamedFragmentOnInterfaceRandomItemContent for any concrete -// GraphQL type returned by the server that doesn't have its own -// generated struct (because no fragment selected it). It carries -// only the interface's shared fields; the concrete GraphQL type -// name is available via the __typename field. +// OmitImplsNamedFragmentOnInterfaceRandomItemContentOther is the catch-all for OmitImplsNamedFragmentOnInterfaceRandomItemContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. type OmitImplsNamedFragmentOnInterfaceRandomItemContentOther struct { Typename string `json:"__typename"` NamedContentFieldsOther `json:"-"` diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsUnion.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsUnion.graphql-testdata-queries-generated.go index 5f0ba84b..e4cd59f5 100644 --- a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsUnion.graphql-testdata-queries-generated.go +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsUnion.graphql-testdata-queries-generated.go @@ -94,11 +94,7 @@ func __marshalOmitImplsUnionRandomLeafLeafContent(v *OmitImplsUnionRandomLeafLea } } -// OmitImplsUnionRandomLeafLeafContentOther is the catch-all type used by OmitImplsUnionRandomLeafLeafContent for any concrete -// GraphQL type returned by the server that doesn't have its own -// generated struct (because no fragment selected it). It carries -// only the interface's shared fields; the concrete GraphQL type -// name is available via the __typename field. +// OmitImplsUnionRandomLeafLeafContentOther is the catch-all for OmitImplsUnionRandomLeafLeafContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. type OmitImplsUnionRandomLeafLeafContentOther struct { Typename string `json:"__typename"` } diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleInlineFragment.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleInlineFragment.graphql-testdata-queries-generated.go index 77e9357d..0056c790 100644 --- a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleInlineFragment.graphql-testdata-queries-generated.go +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleInlineFragment.graphql-testdata-queries-generated.go @@ -119,11 +119,7 @@ func __marshalSimpleInlineFragmentRandomItemContent(v *SimpleInlineFragmentRando } } -// SimpleInlineFragmentRandomItemContentOther is the catch-all type used by SimpleInlineFragmentRandomItemContent for any concrete -// GraphQL type returned by the server that doesn't have its own -// generated struct (because no fragment selected it). It carries -// only the interface's shared fields; the concrete GraphQL type -// name is available via the __typename field. +// SimpleInlineFragmentRandomItemContentOther is the catch-all for SimpleInlineFragmentRandomItemContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. type SimpleInlineFragmentRandomItemContentOther struct { Typename string `json:"__typename"` // ID is the identifier of the content. diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleNamedFragment.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleNamedFragment.graphql-testdata-queries-generated.go index 78bec303..e6810822 100644 --- a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleNamedFragment.graphql-testdata-queries-generated.go +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleNamedFragment.graphql-testdata-queries-generated.go @@ -88,11 +88,7 @@ func __marshalSimpleNamedFragmentRandomItemContent(v *SimpleNamedFragmentRandomI } } -// SimpleNamedFragmentRandomItemContentOther is the catch-all type used by SimpleNamedFragmentRandomItemContent for any concrete -// GraphQL type returned by the server that doesn't have its own -// generated struct (because no fragment selected it). It carries -// only the interface's shared fields; the concrete GraphQL type -// name is available via the __typename field. +// SimpleNamedFragmentRandomItemContentOther is the catch-all for SimpleNamedFragmentRandomItemContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. type SimpleNamedFragmentRandomItemContentOther struct { Typename string `json:"__typename"` // ID is the identifier of the content. @@ -268,11 +264,7 @@ func __marshalSimpleNamedFragmentRandomLeafLeafContent(v *SimpleNamedFragmentRan } } -// SimpleNamedFragmentRandomLeafLeafContentOther is the catch-all type used by SimpleNamedFragmentRandomLeafLeafContent for any concrete -// GraphQL type returned by the server that doesn't have its own -// generated struct (because no fragment selected it). It carries -// only the interface's shared fields; the concrete GraphQL type -// name is available via the __typename field. +// SimpleNamedFragmentRandomLeafLeafContentOther is the catch-all for SimpleNamedFragmentRandomLeafLeafContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. type SimpleNamedFragmentRandomLeafLeafContentOther struct { Typename string `json:"__typename"` } diff --git a/generate/types.go b/generate/types.go index 89173c8a..e978c369 100644 --- a/generate/types.go +++ b/generate/types.go @@ -486,10 +486,8 @@ type goInterfaceType struct { // we'll generate getter methods for each. SharedFields []*goStructField Implementations []*goStructType - // OtherImplementation is a catch-all struct for concrete types not - // explicitly referenced by any fragment in the query. It contains - // only the shared fields. If nil, all implementations are explicitly - // listed in Implementations. + // OtherImplementation, if non-nil, is a catch-all struct used at + // runtime for any concrete type not present in Implementations. OtherImplementation *goStructType Selection ast.SelectionSet descriptionInfo diff --git a/generate/unmarshal_helper.go.tmpl b/generate/unmarshal_helper.go.tmpl index e48a43b8..b7841c8a 100644 --- a/generate/unmarshal_helper.go.tmpl +++ b/generate/unmarshal_helper.go.tmpl @@ -31,9 +31,7 @@ func __unmarshal{{.GoName}}(b []byte, v *{{.GoName}}) error { "response was missing {{.GraphQLName}}.__typename") default: {{if .OtherImplementation -}} - {{/* Unknown __typename: fall back to the catch-all struct so the - caller still gets the interface's shared fields (and the raw - concrete type-name via __typename). */ -}} + {{/* Unknown __typename: fall back to the catch-all. */ -}} *v = new({{.OtherImplementation.GoName}}) return {{ref "encoding/json.Unmarshal"}}(b, *v) {{else -}} diff --git a/internal/integration/omitimpls/generated.go b/internal/integration/omitimpls/generated.go index cfb6a32e..15dbb087 100644 --- a/internal/integration/omitimpls/generated.go +++ b/internal/integration/omitimpls/generated.go @@ -84,11 +84,7 @@ func __marshalqueryOmitImplsBeing(v *queryOmitImplsBeing) ([]byte, error) { } } -// queryOmitImplsBeingOther is the catch-all type used by queryOmitImplsBeing for any concrete -// GraphQL type returned by the server that doesn't have its own -// generated struct (because no fragment selected it). It carries -// only the interface's shared fields; the concrete GraphQL type -// name is available via the __typename field. +// queryOmitImplsBeingOther is the catch-all for queryOmitImplsBeing implementations that aren't explicitly fragmented; the concrete type-name is in __typename. type queryOmitImplsBeingOther struct { Typename string `json:"__typename"` Id string `json:"id"` From 8029b822b09154a79ad5138d005423b2e7b410ac Mon Sep 17 00:00:00 2001 From: Will Roden Date: Mon, 25 May 2026 16:51:52 -0500 Subject: [PATCH 09/12] rename catch-all to GenqlientOther and drop collision-loop --- generate/convert.go | 22 +---- generate/generate_test.go | 82 ------------------- ...ents.graphql-testdata-queries-generated.go | 80 ++++++++++-------- ...rete.graphql-testdata-queries-generated.go | 54 ++++++------ ...nced.graphql-testdata-queries-generated.go | 16 ++-- ...face.graphql-testdata-queries-generated.go | 74 ++++++++--------- ...nion.graphql-testdata-queries-generated.go | 16 ++-- ...ment.graphql-testdata-queries-generated.go | 24 +++--- ...ment.graphql-testdata-queries-generated.go | 42 +++++----- internal/integration/omitimpls/generated.go | 26 +++--- .../integration/omitimpls/omitimpls_test.go | 4 +- 11 files changed, 175 insertions(+), 265 deletions(-) diff --git a/generate/convert.go b/generate/convert.go index 4aebc207..e5ed563f 100644 --- a/generate/convert.go +++ b/generate/convert.go @@ -806,7 +806,7 @@ func (g *generator) makeCatchAllStruct( catchAllFields = append(catchAllFields, f) } - catchAllName := pickCatchAllName(g.typeMap, interfaceGoName) + catchAllName := interfaceGoName + "GenqlientOther" catchAll := &goStructType{ GoName: catchAllName, Fields: catchAllFields, @@ -833,26 +833,6 @@ func (g *generator) makeCatchAllStruct( return catchAllTyp, nil } -// pickCatchAllName returns a Go name for the catch-all that does not collide -// with anything already in typeMap. Tries "<…>Other" first, then -// "<…>OtherCatchAll", then numeric suffixes. -func pickCatchAllName(typeMap map[string]goType, interfaceGoName string) string { - primary := interfaceGoName + "Other" - if _, taken := typeMap[primary]; !taken { - return primary - } - fallback := interfaceGoName + "OtherCatchAll" - if _, taken := typeMap[fallback]; !taken { - return fallback - } - for i := 2; ; i++ { - candidate := fmt.Sprintf("%s%d", fallback, i) - if _, taken := typeMap[candidate]; !taken { - return candidate - } - } -} - // fragmentMatches returns true if the given fragment is "active" when applied // to the given type. // diff --git a/generate/generate_test.go b/generate/generate_test.go index a4b88df4..d73a9652 100644 --- a/generate/generate_test.go +++ b/generate/generate_test.go @@ -398,85 +398,3 @@ func TestGenerateErrors(t *testing.T) { }) } } - -// TestOmitImplsCatchAllNameCollision verifies that -// omit_unreferenced_implementations gracefully handles schemas containing a -// concrete implementation whose Go type name would otherwise collide with the -// catch-all struct's own name (e.g. an interface "Content" with an -// implementation literally named "ContentOther"). Generation must succeed and -// the resulting Go code must compile, rather than failing with a misleading -// "genqlient internal error: conflicting definition" diagnostic. -func TestOmitImplsCatchAllNameCollision(t *testing.T) { - tmpDir := t.TempDir() - schemaPath := filepath.Join(tmpDir, "schema.graphql") - queryPath := filepath.Join(tmpDir, "Repro.graphql") - - schema := ` -type Query { - randomItem: Content -} - -interface Content { - id: ID! -} - -type Article implements Content { - id: ID! - text: String! -} - -type ContentOther implements Content { - id: ID! - flag: Boolean! -} -` - query := ` -query Repro { - randomItem { - id - ... on ContentOther { - flag - } - } -} -` - if err := os.WriteFile(schemaPath, []byte(schema), 0o644); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(queryPath, []byte(query), 0o644); err != nil { - t.Fatal(err) - } - - cfg := &Config{ - Schema: []string{schemaPath}, - Operations: []string{queryPath}, - Package: "test", - Generated: "out.go", - ContextType: "-", - OmitUnreferencedImplementations: true, - } - if err := cfg.ValidateAndFillDefaults("."); err != nil { - t.Fatal(err) - } - - generated, err := Generate(cfg) - if err != nil { - t.Fatalf("Generate failed: %v", err) - } - - // The real concrete impl ContentOther must take the "Other" name; the - // catch-all must fall back to "OtherCatchAll" rather than collide. - out := string(generated["out.go"]) - if !strings.Contains(out, "type ReproRandomItemContentOther struct") { - t.Errorf("expected concrete impl ReproRandomItemContentOther struct in generated code") - } - if !strings.Contains(out, "type ReproRandomItemContentOtherCatchAll struct") { - t.Errorf("expected catch-all ReproRandomItemContentOtherCatchAll struct in generated code") - } - - if !testing.Short() { - if err := buildGoFile("OmitImplsCollision", generated["out.go"]); err != nil { - t.Errorf("generated code does not compile: %v", err) - } - } -} diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-InterfaceNoFragments.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-InterfaceNoFragments.graphql-testdata-queries-generated.go index 935ca4b6..0d41ebcb 100644 --- a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-InterfaceNoFragments.graphql-testdata-queries-generated.go +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-InterfaceNoFragments.graphql-testdata-queries-generated.go @@ -13,7 +13,7 @@ import ( // InterfaceNoFragmentsQueryRandomItemContent includes the requested fields of the GraphQL interface Content. // // InterfaceNoFragmentsQueryRandomItemContent is implemented by the following types: -// InterfaceNoFragmentsQueryRandomItemContentOther +// InterfaceNoFragmentsQueryRandomItemContentGenqlientOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -30,7 +30,7 @@ type InterfaceNoFragmentsQueryRandomItemContent interface { GetName() string } -func (v *InterfaceNoFragmentsQueryRandomItemContentOther) implementsGraphQLInterfaceInterfaceNoFragmentsQueryRandomItemContent() { +func (v *InterfaceNoFragmentsQueryRandomItemContentGenqlientOther) implementsGraphQLInterfaceInterfaceNoFragmentsQueryRandomItemContent() { } func __unmarshalInterfaceNoFragmentsQueryRandomItemContent(b []byte, v *InterfaceNoFragmentsQueryRandomItemContent) error { @@ -51,7 +51,7 @@ func __unmarshalInterfaceNoFragmentsQueryRandomItemContent(b []byte, v *Interfac return fmt.Errorf( "response was missing Content.__typename") default: - *v = new(InterfaceNoFragmentsQueryRandomItemContentOther) + *v = new(InterfaceNoFragmentsQueryRandomItemContentGenqlientOther) return json.Unmarshal(b, *v) } } @@ -59,7 +59,7 @@ func __unmarshalInterfaceNoFragmentsQueryRandomItemContent(b []byte, v *Interfac func __marshalInterfaceNoFragmentsQueryRandomItemContent(v *InterfaceNoFragmentsQueryRandomItemContent) ([]byte, error) { switch v := (*v).(type) { - case *InterfaceNoFragmentsQueryRandomItemContentOther: + case *InterfaceNoFragmentsQueryRandomItemContentGenqlientOther: return json.Marshal(v) case nil: return []byte("null"), nil @@ -69,27 +69,29 @@ func __marshalInterfaceNoFragmentsQueryRandomItemContent(v *InterfaceNoFragments } } -// InterfaceNoFragmentsQueryRandomItemContentOther is the catch-all for InterfaceNoFragmentsQueryRandomItemContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. -type InterfaceNoFragmentsQueryRandomItemContentOther struct { +// InterfaceNoFragmentsQueryRandomItemContentGenqlientOther is the catch-all for InterfaceNoFragmentsQueryRandomItemContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. +type InterfaceNoFragmentsQueryRandomItemContentGenqlientOther struct { Typename string `json:"__typename"` // ID is the identifier of the content. Id string `json:"id"` Name string `json:"name"` } -// GetTypename returns InterfaceNoFragmentsQueryRandomItemContentOther.Typename, and is useful for accessing the field via an interface. -func (v *InterfaceNoFragmentsQueryRandomItemContentOther) GetTypename() string { return v.Typename } +// GetTypename returns InterfaceNoFragmentsQueryRandomItemContentGenqlientOther.Typename, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryRandomItemContentGenqlientOther) GetTypename() string { + return v.Typename +} -// GetId returns InterfaceNoFragmentsQueryRandomItemContentOther.Id, and is useful for accessing the field via an interface. -func (v *InterfaceNoFragmentsQueryRandomItemContentOther) GetId() string { return v.Id } +// GetId returns InterfaceNoFragmentsQueryRandomItemContentGenqlientOther.Id, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryRandomItemContentGenqlientOther) GetId() string { return v.Id } -// GetName returns InterfaceNoFragmentsQueryRandomItemContentOther.Name, and is useful for accessing the field via an interface. -func (v *InterfaceNoFragmentsQueryRandomItemContentOther) GetName() string { return v.Name } +// GetName returns InterfaceNoFragmentsQueryRandomItemContentGenqlientOther.Name, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryRandomItemContentGenqlientOther) GetName() string { return v.Name } // InterfaceNoFragmentsQueryRandomItemWithTypeNameContent includes the requested fields of the GraphQL interface Content. // // InterfaceNoFragmentsQueryRandomItemWithTypeNameContent is implemented by the following types: -// InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther +// InterfaceNoFragmentsQueryRandomItemWithTypeNameContentGenqlientOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -106,7 +108,7 @@ type InterfaceNoFragmentsQueryRandomItemWithTypeNameContent interface { GetName() string } -func (v *InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther) implementsGraphQLInterfaceInterfaceNoFragmentsQueryRandomItemWithTypeNameContent() { +func (v *InterfaceNoFragmentsQueryRandomItemWithTypeNameContentGenqlientOther) implementsGraphQLInterfaceInterfaceNoFragmentsQueryRandomItemWithTypeNameContent() { } func __unmarshalInterfaceNoFragmentsQueryRandomItemWithTypeNameContent(b []byte, v *InterfaceNoFragmentsQueryRandomItemWithTypeNameContent) error { @@ -127,7 +129,7 @@ func __unmarshalInterfaceNoFragmentsQueryRandomItemWithTypeNameContent(b []byte, return fmt.Errorf( "response was missing Content.__typename") default: - *v = new(InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther) + *v = new(InterfaceNoFragmentsQueryRandomItemWithTypeNameContentGenqlientOther) return json.Unmarshal(b, *v) } } @@ -135,7 +137,7 @@ func __unmarshalInterfaceNoFragmentsQueryRandomItemWithTypeNameContent(b []byte, func __marshalInterfaceNoFragmentsQueryRandomItemWithTypeNameContent(v *InterfaceNoFragmentsQueryRandomItemWithTypeNameContent) ([]byte, error) { switch v := (*v).(type) { - case *InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther: + case *InterfaceNoFragmentsQueryRandomItemWithTypeNameContentGenqlientOther: return json.Marshal(v) case nil: return []byte("null"), nil @@ -145,24 +147,28 @@ func __marshalInterfaceNoFragmentsQueryRandomItemWithTypeNameContent(v *Interfac } } -// InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther is the catch-all for InterfaceNoFragmentsQueryRandomItemWithTypeNameContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. -type InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther struct { +// InterfaceNoFragmentsQueryRandomItemWithTypeNameContentGenqlientOther is the catch-all for InterfaceNoFragmentsQueryRandomItemWithTypeNameContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. +type InterfaceNoFragmentsQueryRandomItemWithTypeNameContentGenqlientOther struct { Typename string `json:"__typename"` // ID is the identifier of the content. Id string `json:"id"` Name string `json:"name"` } -// GetTypename returns InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther.Typename, and is useful for accessing the field via an interface. -func (v *InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther) GetTypename() string { +// GetTypename returns InterfaceNoFragmentsQueryRandomItemWithTypeNameContentGenqlientOther.Typename, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryRandomItemWithTypeNameContentGenqlientOther) GetTypename() string { return v.Typename } -// GetId returns InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther.Id, and is useful for accessing the field via an interface. -func (v *InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther) GetId() string { return v.Id } +// GetId returns InterfaceNoFragmentsQueryRandomItemWithTypeNameContentGenqlientOther.Id, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryRandomItemWithTypeNameContentGenqlientOther) GetId() string { + return v.Id +} -// GetName returns InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther.Name, and is useful for accessing the field via an interface. -func (v *InterfaceNoFragmentsQueryRandomItemWithTypeNameContentOther) GetName() string { return v.Name } +// GetName returns InterfaceNoFragmentsQueryRandomItemWithTypeNameContentGenqlientOther.Name, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryRandomItemWithTypeNameContentGenqlientOther) GetName() string { + return v.Name +} // InterfaceNoFragmentsQueryResponse is returned by InterfaceNoFragmentsQuery on success. type InterfaceNoFragmentsQueryResponse struct { @@ -333,7 +339,7 @@ func (v *InterfaceNoFragmentsQueryRootTopic) GetName() string { return v.Name } // InterfaceNoFragmentsQueryWithPointerContent includes the requested fields of the GraphQL interface Content. // // InterfaceNoFragmentsQueryWithPointerContent is implemented by the following types: -// InterfaceNoFragmentsQueryWithPointerContentOther +// InterfaceNoFragmentsQueryWithPointerContentGenqlientOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -350,7 +356,7 @@ type InterfaceNoFragmentsQueryWithPointerContent interface { GetName() *string } -func (v *InterfaceNoFragmentsQueryWithPointerContentOther) implementsGraphQLInterfaceInterfaceNoFragmentsQueryWithPointerContent() { +func (v *InterfaceNoFragmentsQueryWithPointerContentGenqlientOther) implementsGraphQLInterfaceInterfaceNoFragmentsQueryWithPointerContent() { } func __unmarshalInterfaceNoFragmentsQueryWithPointerContent(b []byte, v *InterfaceNoFragmentsQueryWithPointerContent) error { @@ -371,7 +377,7 @@ func __unmarshalInterfaceNoFragmentsQueryWithPointerContent(b []byte, v *Interfa return fmt.Errorf( "response was missing Content.__typename") default: - *v = new(InterfaceNoFragmentsQueryWithPointerContentOther) + *v = new(InterfaceNoFragmentsQueryWithPointerContentGenqlientOther) return json.Unmarshal(b, *v) } } @@ -379,7 +385,7 @@ func __unmarshalInterfaceNoFragmentsQueryWithPointerContent(b []byte, v *Interfa func __marshalInterfaceNoFragmentsQueryWithPointerContent(v *InterfaceNoFragmentsQueryWithPointerContent) ([]byte, error) { switch v := (*v).(type) { - case *InterfaceNoFragmentsQueryWithPointerContentOther: + case *InterfaceNoFragmentsQueryWithPointerContentGenqlientOther: return json.Marshal(v) case nil: return []byte("null"), nil @@ -389,22 +395,24 @@ func __marshalInterfaceNoFragmentsQueryWithPointerContent(v *InterfaceNoFragment } } -// InterfaceNoFragmentsQueryWithPointerContentOther is the catch-all for InterfaceNoFragmentsQueryWithPointerContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. -type InterfaceNoFragmentsQueryWithPointerContentOther struct { +// InterfaceNoFragmentsQueryWithPointerContentGenqlientOther is the catch-all for InterfaceNoFragmentsQueryWithPointerContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. +type InterfaceNoFragmentsQueryWithPointerContentGenqlientOther struct { Typename string `json:"__typename"` // ID is the identifier of the content. Id *string `json:"id"` Name *string `json:"name"` } -// GetTypename returns InterfaceNoFragmentsQueryWithPointerContentOther.Typename, and is useful for accessing the field via an interface. -func (v *InterfaceNoFragmentsQueryWithPointerContentOther) GetTypename() string { return v.Typename } +// GetTypename returns InterfaceNoFragmentsQueryWithPointerContentGenqlientOther.Typename, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryWithPointerContentGenqlientOther) GetTypename() string { + return v.Typename +} -// GetId returns InterfaceNoFragmentsQueryWithPointerContentOther.Id, and is useful for accessing the field via an interface. -func (v *InterfaceNoFragmentsQueryWithPointerContentOther) GetId() *string { return v.Id } +// GetId returns InterfaceNoFragmentsQueryWithPointerContentGenqlientOther.Id, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryWithPointerContentGenqlientOther) GetId() *string { return v.Id } -// GetName returns InterfaceNoFragmentsQueryWithPointerContentOther.Name, and is useful for accessing the field via an interface. -func (v *InterfaceNoFragmentsQueryWithPointerContentOther) GetName() *string { return v.Name } +// GetName returns InterfaceNoFragmentsQueryWithPointerContentGenqlientOther.Name, and is useful for accessing the field via an interface. +func (v *InterfaceNoFragmentsQueryWithPointerContentGenqlientOther) GetName() *string { return v.Name } // The query executed by InterfaceNoFragmentsQuery. const InterfaceNoFragmentsQuery_Operation = ` diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAbstractFragmentInConcrete.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAbstractFragmentInConcrete.graphql-testdata-queries-generated.go index 6fed806d..47070ba3 100644 --- a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAbstractFragmentInConcrete.graphql-testdata-queries-generated.go +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAbstractFragmentInConcrete.graphql-testdata-queries-generated.go @@ -16,7 +16,7 @@ import ( // Content is implemented by various types like Article, Video, and Topic. // // ContentBasics is implemented by the following types: -// ContentBasicsOther +// ContentBasicsGenqlientOther type ContentBasics interface { implementsGraphQLInterfaceContentBasics() // GetId returns the interface-field "id" from its implementation. @@ -28,7 +28,7 @@ type ContentBasics interface { GetName() string } -func (v *ContentBasicsOther) implementsGraphQLInterfaceContentBasics() {} +func (v *ContentBasicsGenqlientOther) implementsGraphQLInterfaceContentBasics() {} func __unmarshalContentBasics(b []byte, v *ContentBasics) error { if string(b) == "null" { @@ -48,7 +48,7 @@ func __unmarshalContentBasics(b []byte, v *ContentBasics) error { return fmt.Errorf( "response was missing Content.__typename") default: - *v = new(ContentBasicsOther) + *v = new(ContentBasicsGenqlientOther) return json.Unmarshal(b, *v) } } @@ -56,7 +56,7 @@ func __unmarshalContentBasics(b []byte, v *ContentBasics) error { func __marshalContentBasics(v *ContentBasics) ([]byte, error) { switch v := (*v).(type) { - case *ContentBasicsOther: + case *ContentBasicsGenqlientOther: return json.Marshal(v) case nil: return []byte("null"), nil @@ -66,26 +66,26 @@ func __marshalContentBasics(v *ContentBasics) ([]byte, error) { } } -// ContentBasicsOther is the catch-all for ContentBasics implementations that aren't explicitly fragmented; the concrete type-name is in __typename. -type ContentBasicsOther struct { +// ContentBasicsGenqlientOther is the catch-all for ContentBasics implementations that aren't explicitly fragmented; the concrete type-name is in __typename. +type ContentBasicsGenqlientOther struct { // ID is the identifier of the content. Id string `json:"id"` Name string `json:"name"` } -// GetId returns ContentBasicsOther.Id, and is useful for accessing the field via an interface. -func (v *ContentBasicsOther) GetId() string { return v.Id } +// GetId returns ContentBasicsGenqlientOther.Id, and is useful for accessing the field via an interface. +func (v *ContentBasicsGenqlientOther) GetId() string { return v.Id } -// GetName returns ContentBasicsOther.Name, and is useful for accessing the field via an interface. -func (v *ContentBasicsOther) GetName() string { return v.Name } +// GetName returns ContentBasicsGenqlientOther.Name, and is useful for accessing the field via an interface. +func (v *ContentBasicsGenqlientOther) GetName() string { return v.Name } // OmitImplsAbstractFragmentInConcreteRandomItemArticle includes the requested fields of the GraphQL type Article. type OmitImplsAbstractFragmentInConcreteRandomItemArticle struct { Typename string `json:"__typename"` // ID is the identifier of the content. - Id string `json:"id"` - Text string `json:"text"` - ContentBasicsOther `json:"-"` + Id string `json:"id"` + Text string `json:"text"` + ContentBasicsGenqlientOther `json:"-"` } // GetTypename returns OmitImplsAbstractFragmentInConcreteRandomItemArticle.Typename, and is useful for accessing the field via an interface. @@ -101,7 +101,7 @@ func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) GetText() string // GetName returns OmitImplsAbstractFragmentInConcreteRandomItemArticle.Name, and is useful for accessing the field via an interface. func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) GetName() string { - return v.ContentBasicsOther.Name + return v.ContentBasicsGenqlientOther.Name } func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) UnmarshalJSON(b []byte) error { @@ -122,7 +122,7 @@ func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) UnmarshalJSON(b [ } err = json.Unmarshal( - b, &v.ContentBasicsOther) + b, &v.ContentBasicsGenqlientOther) if err != nil { return err } @@ -153,7 +153,7 @@ func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) __premarshalJSON( retval.Typename = v.Typename retval.Id = v.Id retval.Text = v.Text - retval.Name = v.ContentBasicsOther.Name + retval.Name = v.ContentBasicsGenqlientOther.Name return &retval, nil } @@ -161,7 +161,7 @@ func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) __premarshalJSON( // // OmitImplsAbstractFragmentInConcreteRandomItemContent is implemented by the following types: // OmitImplsAbstractFragmentInConcreteRandomItemArticle -// OmitImplsAbstractFragmentInConcreteRandomItemContentOther +// OmitImplsAbstractFragmentInConcreteRandomItemContentGenqlientOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -178,7 +178,7 @@ type OmitImplsAbstractFragmentInConcreteRandomItemContent interface { func (v *OmitImplsAbstractFragmentInConcreteRandomItemArticle) implementsGraphQLInterfaceOmitImplsAbstractFragmentInConcreteRandomItemContent() { } -func (v *OmitImplsAbstractFragmentInConcreteRandomItemContentOther) implementsGraphQLInterfaceOmitImplsAbstractFragmentInConcreteRandomItemContent() { +func (v *OmitImplsAbstractFragmentInConcreteRandomItemContentGenqlientOther) implementsGraphQLInterfaceOmitImplsAbstractFragmentInConcreteRandomItemContent() { } func __unmarshalOmitImplsAbstractFragmentInConcreteRandomItemContent(b []byte, v *OmitImplsAbstractFragmentInConcreteRandomItemContent) error { @@ -202,7 +202,7 @@ func __unmarshalOmitImplsAbstractFragmentInConcreteRandomItemContent(b []byte, v return fmt.Errorf( "response was missing Content.__typename") default: - *v = new(OmitImplsAbstractFragmentInConcreteRandomItemContentOther) + *v = new(OmitImplsAbstractFragmentInConcreteRandomItemContentGenqlientOther) return json.Unmarshal(b, *v) } } @@ -223,7 +223,7 @@ func __marshalOmitImplsAbstractFragmentInConcreteRandomItemContent(v *OmitImplsA *__premarshalOmitImplsAbstractFragmentInConcreteRandomItemArticle }{typename, premarshaled} return json.Marshal(result) - case *OmitImplsAbstractFragmentInConcreteRandomItemContentOther: + case *OmitImplsAbstractFragmentInConcreteRandomItemContentGenqlientOther: return json.Marshal(v) case nil: return []byte("null"), nil @@ -233,20 +233,22 @@ func __marshalOmitImplsAbstractFragmentInConcreteRandomItemContent(v *OmitImplsA } } -// OmitImplsAbstractFragmentInConcreteRandomItemContentOther is the catch-all for OmitImplsAbstractFragmentInConcreteRandomItemContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. -type OmitImplsAbstractFragmentInConcreteRandomItemContentOther struct { +// OmitImplsAbstractFragmentInConcreteRandomItemContentGenqlientOther is the catch-all for OmitImplsAbstractFragmentInConcreteRandomItemContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. +type OmitImplsAbstractFragmentInConcreteRandomItemContentGenqlientOther struct { Typename string `json:"__typename"` // ID is the identifier of the content. Id string `json:"id"` } -// GetTypename returns OmitImplsAbstractFragmentInConcreteRandomItemContentOther.Typename, and is useful for accessing the field via an interface. -func (v *OmitImplsAbstractFragmentInConcreteRandomItemContentOther) GetTypename() string { +// GetTypename returns OmitImplsAbstractFragmentInConcreteRandomItemContentGenqlientOther.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsAbstractFragmentInConcreteRandomItemContentGenqlientOther) GetTypename() string { return v.Typename } -// GetId returns OmitImplsAbstractFragmentInConcreteRandomItemContentOther.Id, and is useful for accessing the field via an interface. -func (v *OmitImplsAbstractFragmentInConcreteRandomItemContentOther) GetId() string { return v.Id } +// GetId returns OmitImplsAbstractFragmentInConcreteRandomItemContentGenqlientOther.Id, and is useful for accessing the field via an interface. +func (v *OmitImplsAbstractFragmentInConcreteRandomItemContentGenqlientOther) GetId() string { + return v.Id +} // OmitImplsAbstractFragmentInConcreteResponse is returned by OmitImplsAbstractFragmentInConcrete on success. type OmitImplsAbstractFragmentInConcreteResponse struct { diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAllImplsReferenced.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAllImplsReferenced.graphql-testdata-queries-generated.go index b4034701..2dbb5550 100644 --- a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAllImplsReferenced.graphql-testdata-queries-generated.go +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsAllImplsReferenced.graphql-testdata-queries-generated.go @@ -32,7 +32,7 @@ func (v *OmitImplsAllImplsReferencedRandomLeafArticle) GetText() string { return // OmitImplsAllImplsReferencedRandomLeafLeafContent is implemented by the following types: // OmitImplsAllImplsReferencedRandomLeafArticle // OmitImplsAllImplsReferencedRandomLeafVideo -// OmitImplsAllImplsReferencedRandomLeafLeafContentOther +// OmitImplsAllImplsReferencedRandomLeafLeafContentGenqlientOther // The GraphQL type's documentation follows. // // LeafContent represents content items that can't have child-nodes. @@ -46,7 +46,7 @@ func (v *OmitImplsAllImplsReferencedRandomLeafArticle) implementsGraphQLInterfac } func (v *OmitImplsAllImplsReferencedRandomLeafVideo) implementsGraphQLInterfaceOmitImplsAllImplsReferencedRandomLeafLeafContent() { } -func (v *OmitImplsAllImplsReferencedRandomLeafLeafContentOther) implementsGraphQLInterfaceOmitImplsAllImplsReferencedRandomLeafLeafContent() { +func (v *OmitImplsAllImplsReferencedRandomLeafLeafContentGenqlientOther) implementsGraphQLInterfaceOmitImplsAllImplsReferencedRandomLeafLeafContent() { } func __unmarshalOmitImplsAllImplsReferencedRandomLeafLeafContent(b []byte, v *OmitImplsAllImplsReferencedRandomLeafLeafContent) error { @@ -73,7 +73,7 @@ func __unmarshalOmitImplsAllImplsReferencedRandomLeafLeafContent(b []byte, v *Om return fmt.Errorf( "response was missing LeafContent.__typename") default: - *v = new(OmitImplsAllImplsReferencedRandomLeafLeafContentOther) + *v = new(OmitImplsAllImplsReferencedRandomLeafLeafContentGenqlientOther) return json.Unmarshal(b, *v) } } @@ -98,7 +98,7 @@ func __marshalOmitImplsAllImplsReferencedRandomLeafLeafContent(v *OmitImplsAllIm *OmitImplsAllImplsReferencedRandomLeafVideo }{typename, v} return json.Marshal(result) - case *OmitImplsAllImplsReferencedRandomLeafLeafContentOther: + case *OmitImplsAllImplsReferencedRandomLeafLeafContentGenqlientOther: return json.Marshal(v) case nil: return []byte("null"), nil @@ -108,13 +108,13 @@ func __marshalOmitImplsAllImplsReferencedRandomLeafLeafContent(v *OmitImplsAllIm } } -// OmitImplsAllImplsReferencedRandomLeafLeafContentOther is the catch-all for OmitImplsAllImplsReferencedRandomLeafLeafContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. -type OmitImplsAllImplsReferencedRandomLeafLeafContentOther struct { +// OmitImplsAllImplsReferencedRandomLeafLeafContentGenqlientOther is the catch-all for OmitImplsAllImplsReferencedRandomLeafLeafContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. +type OmitImplsAllImplsReferencedRandomLeafLeafContentGenqlientOther struct { Typename string `json:"__typename"` } -// GetTypename returns OmitImplsAllImplsReferencedRandomLeafLeafContentOther.Typename, and is useful for accessing the field via an interface. -func (v *OmitImplsAllImplsReferencedRandomLeafLeafContentOther) GetTypename() string { +// GetTypename returns OmitImplsAllImplsReferencedRandomLeafLeafContentGenqlientOther.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsAllImplsReferencedRandomLeafLeafContentGenqlientOther) GetTypename() string { return v.Typename } diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsNamedFragmentOnInterface.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsNamedFragmentOnInterface.graphql-testdata-queries-generated.go index 8802f260..8a98ce95 100644 --- a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsNamedFragmentOnInterface.graphql-testdata-queries-generated.go +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsNamedFragmentOnInterface.graphql-testdata-queries-generated.go @@ -17,7 +17,7 @@ import ( // // NamedContentFields is implemented by the following types: // NamedContentFieldsArticle -// NamedContentFieldsOther +// NamedContentFieldsGenqlientOther type NamedContentFields interface { implementsGraphQLInterfaceNamedContentFields() // GetId returns the interface-field "id" from its implementation. @@ -29,8 +29,8 @@ type NamedContentFields interface { GetName() string } -func (v *NamedContentFieldsArticle) implementsGraphQLInterfaceNamedContentFields() {} -func (v *NamedContentFieldsOther) implementsGraphQLInterfaceNamedContentFields() {} +func (v *NamedContentFieldsArticle) implementsGraphQLInterfaceNamedContentFields() {} +func (v *NamedContentFieldsGenqlientOther) implementsGraphQLInterfaceNamedContentFields() {} func __unmarshalNamedContentFields(b []byte, v *NamedContentFields) error { if string(b) == "null" { @@ -53,7 +53,7 @@ func __unmarshalNamedContentFields(b []byte, v *NamedContentFields) error { return fmt.Errorf( "response was missing Content.__typename") default: - *v = new(NamedContentFieldsOther) + *v = new(NamedContentFieldsGenqlientOther) return json.Unmarshal(b, *v) } } @@ -70,7 +70,7 @@ func __marshalNamedContentFields(v *NamedContentFields) ([]byte, error) { *NamedContentFieldsArticle }{typename, v} return json.Marshal(result) - case *NamedContentFieldsOther: + case *NamedContentFieldsGenqlientOther: return json.Marshal(v) case nil: return []byte("null"), nil @@ -100,18 +100,18 @@ func (v *NamedContentFieldsArticle) GetName() string { return v.Name } // GetText returns NamedContentFieldsArticle.Text, and is useful for accessing the field via an interface. func (v *NamedContentFieldsArticle) GetText() string { return v.Text } -// NamedContentFieldsOther is the catch-all for NamedContentFields implementations that aren't explicitly fragmented; the concrete type-name is in __typename. -type NamedContentFieldsOther struct { +// NamedContentFieldsGenqlientOther is the catch-all for NamedContentFields implementations that aren't explicitly fragmented; the concrete type-name is in __typename. +type NamedContentFieldsGenqlientOther struct { // ID is the identifier of the content. Id string `json:"id"` Name string `json:"name"` } -// GetId returns NamedContentFieldsOther.Id, and is useful for accessing the field via an interface. -func (v *NamedContentFieldsOther) GetId() string { return v.Id } +// GetId returns NamedContentFieldsGenqlientOther.Id, and is useful for accessing the field via an interface. +func (v *NamedContentFieldsGenqlientOther) GetId() string { return v.Id } -// GetName returns NamedContentFieldsOther.Name, and is useful for accessing the field via an interface. -func (v *NamedContentFieldsOther) GetName() string { return v.Name } +// GetName returns NamedContentFieldsGenqlientOther.Name, and is useful for accessing the field via an interface. +func (v *NamedContentFieldsGenqlientOther) GetName() string { return v.Name } // OmitImplsNamedFragmentOnInterfaceRandomItemArticle includes the requested fields of the GraphQL type Article. type OmitImplsNamedFragmentOnInterfaceRandomItemArticle struct { @@ -194,7 +194,7 @@ func (v *OmitImplsNamedFragmentOnInterfaceRandomItemArticle) __premarshalJSON() // // OmitImplsNamedFragmentOnInterfaceRandomItemContent is implemented by the following types: // OmitImplsNamedFragmentOnInterfaceRandomItemArticle -// OmitImplsNamedFragmentOnInterfaceRandomItemContentOther +// OmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -207,7 +207,7 @@ type OmitImplsNamedFragmentOnInterfaceRandomItemContent interface { func (v *OmitImplsNamedFragmentOnInterfaceRandomItemArticle) implementsGraphQLInterfaceOmitImplsNamedFragmentOnInterfaceRandomItemContent() { } -func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther) implementsGraphQLInterfaceOmitImplsNamedFragmentOnInterfaceRandomItemContent() { +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther) implementsGraphQLInterfaceOmitImplsNamedFragmentOnInterfaceRandomItemContent() { } func __unmarshalOmitImplsNamedFragmentOnInterfaceRandomItemContent(b []byte, v *OmitImplsNamedFragmentOnInterfaceRandomItemContent) error { @@ -231,7 +231,7 @@ func __unmarshalOmitImplsNamedFragmentOnInterfaceRandomItemContent(b []byte, v * return fmt.Errorf( "response was missing Content.__typename") default: - *v = new(OmitImplsNamedFragmentOnInterfaceRandomItemContentOther) + *v = new(OmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther) return json.Unmarshal(b, *v) } } @@ -252,7 +252,7 @@ func __marshalOmitImplsNamedFragmentOnInterfaceRandomItemContent(v *OmitImplsNam *__premarshalOmitImplsNamedFragmentOnInterfaceRandomItemArticle }{typename, premarshaled} return json.Marshal(result) - case *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther: + case *OmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther: premarshaled, err := v.__premarshalJSON() if err != nil { return nil, err @@ -266,38 +266,38 @@ func __marshalOmitImplsNamedFragmentOnInterfaceRandomItemContent(v *OmitImplsNam } } -// OmitImplsNamedFragmentOnInterfaceRandomItemContentOther is the catch-all for OmitImplsNamedFragmentOnInterfaceRandomItemContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. -type OmitImplsNamedFragmentOnInterfaceRandomItemContentOther struct { - Typename string `json:"__typename"` - NamedContentFieldsOther `json:"-"` +// OmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther is the catch-all for OmitImplsNamedFragmentOnInterfaceRandomItemContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. +type OmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther struct { + Typename string `json:"__typename"` + NamedContentFieldsGenqlientOther `json:"-"` } -// GetTypename returns OmitImplsNamedFragmentOnInterfaceRandomItemContentOther.Typename, and is useful for accessing the field via an interface. -func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther) GetTypename() string { +// GetTypename returns OmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther) GetTypename() string { return v.Typename } -// GetId returns OmitImplsNamedFragmentOnInterfaceRandomItemContentOther.Id, and is useful for accessing the field via an interface. -func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther) GetId() string { - return v.NamedContentFieldsOther.Id +// GetId returns OmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther.Id, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther) GetId() string { + return v.NamedContentFieldsGenqlientOther.Id } -// GetName returns OmitImplsNamedFragmentOnInterfaceRandomItemContentOther.Name, and is useful for accessing the field via an interface. -func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther) GetName() string { - return v.NamedContentFieldsOther.Name +// GetName returns OmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther.Name, and is useful for accessing the field via an interface. +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther) GetName() string { + return v.NamedContentFieldsGenqlientOther.Name } -func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther) UnmarshalJSON(b []byte) error { +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther) UnmarshalJSON(b []byte) error { if string(b) == "null" { return nil } var firstPass struct { - *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther + *OmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther graphql.NoUnmarshalJSON } - firstPass.OmitImplsNamedFragmentOnInterfaceRandomItemContentOther = v + firstPass.OmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther = v err := json.Unmarshal(b, &firstPass) if err != nil { @@ -305,14 +305,14 @@ func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther) UnmarshalJSON( } err = json.Unmarshal( - b, &v.NamedContentFieldsOther) + b, &v.NamedContentFieldsGenqlientOther) if err != nil { return err } return nil } -type __premarshalOmitImplsNamedFragmentOnInterfaceRandomItemContentOther struct { +type __premarshalOmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther struct { Typename string `json:"__typename"` Id string `json:"id"` @@ -320,7 +320,7 @@ type __premarshalOmitImplsNamedFragmentOnInterfaceRandomItemContentOther struct Name string `json:"name"` } -func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther) MarshalJSON() ([]byte, error) { +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther) MarshalJSON() ([]byte, error) { premarshaled, err := v.__premarshalJSON() if err != nil { return nil, err @@ -328,12 +328,12 @@ func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther) MarshalJSON() return json.Marshal(premarshaled) } -func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentOther) __premarshalJSON() (*__premarshalOmitImplsNamedFragmentOnInterfaceRandomItemContentOther, error) { - var retval __premarshalOmitImplsNamedFragmentOnInterfaceRandomItemContentOther +func (v *OmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther) __premarshalJSON() (*__premarshalOmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther, error) { + var retval __premarshalOmitImplsNamedFragmentOnInterfaceRandomItemContentGenqlientOther retval.Typename = v.Typename - retval.Id = v.NamedContentFieldsOther.Id - retval.Name = v.NamedContentFieldsOther.Name + retval.Id = v.NamedContentFieldsGenqlientOther.Id + retval.Name = v.NamedContentFieldsGenqlientOther.Name return &retval, nil } diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsUnion.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsUnion.graphql-testdata-queries-generated.go index e4cd59f5..4a3df84f 100644 --- a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsUnion.graphql-testdata-queries-generated.go +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-OmitImplsUnion.graphql-testdata-queries-generated.go @@ -31,7 +31,7 @@ func (v *OmitImplsUnionRandomLeafArticle) GetText() string { return v.Text } // // OmitImplsUnionRandomLeafLeafContent is implemented by the following types: // OmitImplsUnionRandomLeafArticle -// OmitImplsUnionRandomLeafLeafContentOther +// OmitImplsUnionRandomLeafLeafContentGenqlientOther // The GraphQL type's documentation follows. // // LeafContent represents content items that can't have child-nodes. @@ -43,7 +43,7 @@ type OmitImplsUnionRandomLeafLeafContent interface { func (v *OmitImplsUnionRandomLeafArticle) implementsGraphQLInterfaceOmitImplsUnionRandomLeafLeafContent() { } -func (v *OmitImplsUnionRandomLeafLeafContentOther) implementsGraphQLInterfaceOmitImplsUnionRandomLeafLeafContent() { +func (v *OmitImplsUnionRandomLeafLeafContentGenqlientOther) implementsGraphQLInterfaceOmitImplsUnionRandomLeafLeafContent() { } func __unmarshalOmitImplsUnionRandomLeafLeafContent(b []byte, v *OmitImplsUnionRandomLeafLeafContent) error { @@ -67,7 +67,7 @@ func __unmarshalOmitImplsUnionRandomLeafLeafContent(b []byte, v *OmitImplsUnionR return fmt.Errorf( "response was missing LeafContent.__typename") default: - *v = new(OmitImplsUnionRandomLeafLeafContentOther) + *v = new(OmitImplsUnionRandomLeafLeafContentGenqlientOther) return json.Unmarshal(b, *v) } } @@ -84,7 +84,7 @@ func __marshalOmitImplsUnionRandomLeafLeafContent(v *OmitImplsUnionRandomLeafLea *OmitImplsUnionRandomLeafArticle }{typename, v} return json.Marshal(result) - case *OmitImplsUnionRandomLeafLeafContentOther: + case *OmitImplsUnionRandomLeafLeafContentGenqlientOther: return json.Marshal(v) case nil: return []byte("null"), nil @@ -94,13 +94,13 @@ func __marshalOmitImplsUnionRandomLeafLeafContent(v *OmitImplsUnionRandomLeafLea } } -// OmitImplsUnionRandomLeafLeafContentOther is the catch-all for OmitImplsUnionRandomLeafLeafContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. -type OmitImplsUnionRandomLeafLeafContentOther struct { +// OmitImplsUnionRandomLeafLeafContentGenqlientOther is the catch-all for OmitImplsUnionRandomLeafLeafContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. +type OmitImplsUnionRandomLeafLeafContentGenqlientOther struct { Typename string `json:"__typename"` } -// GetTypename returns OmitImplsUnionRandomLeafLeafContentOther.Typename, and is useful for accessing the field via an interface. -func (v *OmitImplsUnionRandomLeafLeafContentOther) GetTypename() string { return v.Typename } +// GetTypename returns OmitImplsUnionRandomLeafLeafContentGenqlientOther.Typename, and is useful for accessing the field via an interface. +func (v *OmitImplsUnionRandomLeafLeafContentGenqlientOther) GetTypename() string { return v.Typename } // OmitImplsUnionResponse is returned by OmitImplsUnion on success. type OmitImplsUnionResponse struct { diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleInlineFragment.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleInlineFragment.graphql-testdata-queries-generated.go index 0056c790..0fe33d75 100644 --- a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleInlineFragment.graphql-testdata-queries-generated.go +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleInlineFragment.graphql-testdata-queries-generated.go @@ -36,7 +36,7 @@ func (v *SimpleInlineFragmentRandomItemArticle) GetText() string { return v.Text // SimpleInlineFragmentRandomItemContent is implemented by the following types: // SimpleInlineFragmentRandomItemArticle // SimpleInlineFragmentRandomItemVideo -// SimpleInlineFragmentRandomItemContentOther +// SimpleInlineFragmentRandomItemContentGenqlientOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -57,7 +57,7 @@ func (v *SimpleInlineFragmentRandomItemArticle) implementsGraphQLInterfaceSimple } func (v *SimpleInlineFragmentRandomItemVideo) implementsGraphQLInterfaceSimpleInlineFragmentRandomItemContent() { } -func (v *SimpleInlineFragmentRandomItemContentOther) implementsGraphQLInterfaceSimpleInlineFragmentRandomItemContent() { +func (v *SimpleInlineFragmentRandomItemContentGenqlientOther) implementsGraphQLInterfaceSimpleInlineFragmentRandomItemContent() { } func __unmarshalSimpleInlineFragmentRandomItemContent(b []byte, v *SimpleInlineFragmentRandomItemContent) error { @@ -84,7 +84,7 @@ func __unmarshalSimpleInlineFragmentRandomItemContent(b []byte, v *SimpleInlineF return fmt.Errorf( "response was missing Content.__typename") default: - *v = new(SimpleInlineFragmentRandomItemContentOther) + *v = new(SimpleInlineFragmentRandomItemContentGenqlientOther) return json.Unmarshal(b, *v) } } @@ -109,7 +109,7 @@ func __marshalSimpleInlineFragmentRandomItemContent(v *SimpleInlineFragmentRando *SimpleInlineFragmentRandomItemVideo }{typename, v} return json.Marshal(result) - case *SimpleInlineFragmentRandomItemContentOther: + case *SimpleInlineFragmentRandomItemContentGenqlientOther: return json.Marshal(v) case nil: return []byte("null"), nil @@ -119,22 +119,22 @@ func __marshalSimpleInlineFragmentRandomItemContent(v *SimpleInlineFragmentRando } } -// SimpleInlineFragmentRandomItemContentOther is the catch-all for SimpleInlineFragmentRandomItemContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. -type SimpleInlineFragmentRandomItemContentOther struct { +// SimpleInlineFragmentRandomItemContentGenqlientOther is the catch-all for SimpleInlineFragmentRandomItemContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. +type SimpleInlineFragmentRandomItemContentGenqlientOther struct { Typename string `json:"__typename"` // ID is the identifier of the content. Id string `json:"id"` Name string `json:"name"` } -// GetTypename returns SimpleInlineFragmentRandomItemContentOther.Typename, and is useful for accessing the field via an interface. -func (v *SimpleInlineFragmentRandomItemContentOther) GetTypename() string { return v.Typename } +// GetTypename returns SimpleInlineFragmentRandomItemContentGenqlientOther.Typename, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemContentGenqlientOther) GetTypename() string { return v.Typename } -// GetId returns SimpleInlineFragmentRandomItemContentOther.Id, and is useful for accessing the field via an interface. -func (v *SimpleInlineFragmentRandomItemContentOther) GetId() string { return v.Id } +// GetId returns SimpleInlineFragmentRandomItemContentGenqlientOther.Id, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemContentGenqlientOther) GetId() string { return v.Id } -// GetName returns SimpleInlineFragmentRandomItemContentOther.Name, and is useful for accessing the field via an interface. -func (v *SimpleInlineFragmentRandomItemContentOther) GetName() string { return v.Name } +// GetName returns SimpleInlineFragmentRandomItemContentGenqlientOther.Name, and is useful for accessing the field via an interface. +func (v *SimpleInlineFragmentRandomItemContentGenqlientOther) GetName() string { return v.Name } // SimpleInlineFragmentRandomItemVideo includes the requested fields of the GraphQL type Video. type SimpleInlineFragmentRandomItemVideo struct { diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleNamedFragment.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleNamedFragment.graphql-testdata-queries-generated.go index e6810822..0d2425c8 100644 --- a/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleNamedFragment.graphql-testdata-queries-generated.go +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OmitUnreferencedImplementations-SimpleNamedFragment.graphql-testdata-queries-generated.go @@ -14,7 +14,7 @@ import ( // // SimpleNamedFragmentRandomItemContent is implemented by the following types: // SimpleNamedFragmentRandomItemVideo -// SimpleNamedFragmentRandomItemContentOther +// SimpleNamedFragmentRandomItemContentGenqlientOther // The GraphQL type's documentation follows. // // Content is implemented by various types like Article, Video, and Topic. @@ -33,7 +33,7 @@ type SimpleNamedFragmentRandomItemContent interface { func (v *SimpleNamedFragmentRandomItemVideo) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() { } -func (v *SimpleNamedFragmentRandomItemContentOther) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() { +func (v *SimpleNamedFragmentRandomItemContentGenqlientOther) implementsGraphQLInterfaceSimpleNamedFragmentRandomItemContent() { } func __unmarshalSimpleNamedFragmentRandomItemContent(b []byte, v *SimpleNamedFragmentRandomItemContent) error { @@ -57,7 +57,7 @@ func __unmarshalSimpleNamedFragmentRandomItemContent(b []byte, v *SimpleNamedFra return fmt.Errorf( "response was missing Content.__typename") default: - *v = new(SimpleNamedFragmentRandomItemContentOther) + *v = new(SimpleNamedFragmentRandomItemContentGenqlientOther) return json.Unmarshal(b, *v) } } @@ -78,7 +78,7 @@ func __marshalSimpleNamedFragmentRandomItemContent(v *SimpleNamedFragmentRandomI *__premarshalSimpleNamedFragmentRandomItemVideo }{typename, premarshaled} return json.Marshal(result) - case *SimpleNamedFragmentRandomItemContentOther: + case *SimpleNamedFragmentRandomItemContentGenqlientOther: return json.Marshal(v) case nil: return []byte("null"), nil @@ -88,22 +88,22 @@ func __marshalSimpleNamedFragmentRandomItemContent(v *SimpleNamedFragmentRandomI } } -// SimpleNamedFragmentRandomItemContentOther is the catch-all for SimpleNamedFragmentRandomItemContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. -type SimpleNamedFragmentRandomItemContentOther struct { +// SimpleNamedFragmentRandomItemContentGenqlientOther is the catch-all for SimpleNamedFragmentRandomItemContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. +type SimpleNamedFragmentRandomItemContentGenqlientOther struct { Typename string `json:"__typename"` // ID is the identifier of the content. Id string `json:"id"` Name string `json:"name"` } -// GetTypename returns SimpleNamedFragmentRandomItemContentOther.Typename, and is useful for accessing the field via an interface. -func (v *SimpleNamedFragmentRandomItemContentOther) GetTypename() string { return v.Typename } +// GetTypename returns SimpleNamedFragmentRandomItemContentGenqlientOther.Typename, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemContentGenqlientOther) GetTypename() string { return v.Typename } -// GetId returns SimpleNamedFragmentRandomItemContentOther.Id, and is useful for accessing the field via an interface. -func (v *SimpleNamedFragmentRandomItemContentOther) GetId() string { return v.Id } +// GetId returns SimpleNamedFragmentRandomItemContentGenqlientOther.Id, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemContentGenqlientOther) GetId() string { return v.Id } -// GetName returns SimpleNamedFragmentRandomItemContentOther.Name, and is useful for accessing the field via an interface. -func (v *SimpleNamedFragmentRandomItemContentOther) GetName() string { return v.Name } +// GetName returns SimpleNamedFragmentRandomItemContentGenqlientOther.Name, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomItemContentGenqlientOther) GetName() string { return v.Name } // SimpleNamedFragmentRandomItemVideo includes the requested fields of the GraphQL type Video. type SimpleNamedFragmentRandomItemVideo struct { @@ -197,7 +197,7 @@ func (v *SimpleNamedFragmentRandomItemVideo) __premarshalJSON() (*__premarshalSi // // SimpleNamedFragmentRandomLeafLeafContent is implemented by the following types: // SimpleNamedFragmentRandomLeafVideo -// SimpleNamedFragmentRandomLeafLeafContentOther +// SimpleNamedFragmentRandomLeafLeafContentGenqlientOther // The GraphQL type's documentation follows. // // LeafContent represents content items that can't have child-nodes. @@ -209,7 +209,7 @@ type SimpleNamedFragmentRandomLeafLeafContent interface { func (v *SimpleNamedFragmentRandomLeafVideo) implementsGraphQLInterfaceSimpleNamedFragmentRandomLeafLeafContent() { } -func (v *SimpleNamedFragmentRandomLeafLeafContentOther) implementsGraphQLInterfaceSimpleNamedFragmentRandomLeafLeafContent() { +func (v *SimpleNamedFragmentRandomLeafLeafContentGenqlientOther) implementsGraphQLInterfaceSimpleNamedFragmentRandomLeafLeafContent() { } func __unmarshalSimpleNamedFragmentRandomLeafLeafContent(b []byte, v *SimpleNamedFragmentRandomLeafLeafContent) error { @@ -233,7 +233,7 @@ func __unmarshalSimpleNamedFragmentRandomLeafLeafContent(b []byte, v *SimpleName return fmt.Errorf( "response was missing LeafContent.__typename") default: - *v = new(SimpleNamedFragmentRandomLeafLeafContentOther) + *v = new(SimpleNamedFragmentRandomLeafLeafContentGenqlientOther) return json.Unmarshal(b, *v) } } @@ -254,7 +254,7 @@ func __marshalSimpleNamedFragmentRandomLeafLeafContent(v *SimpleNamedFragmentRan *__premarshalSimpleNamedFragmentRandomLeafVideo }{typename, premarshaled} return json.Marshal(result) - case *SimpleNamedFragmentRandomLeafLeafContentOther: + case *SimpleNamedFragmentRandomLeafLeafContentGenqlientOther: return json.Marshal(v) case nil: return []byte("null"), nil @@ -264,13 +264,15 @@ func __marshalSimpleNamedFragmentRandomLeafLeafContent(v *SimpleNamedFragmentRan } } -// SimpleNamedFragmentRandomLeafLeafContentOther is the catch-all for SimpleNamedFragmentRandomLeafLeafContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. -type SimpleNamedFragmentRandomLeafLeafContentOther struct { +// SimpleNamedFragmentRandomLeafLeafContentGenqlientOther is the catch-all for SimpleNamedFragmentRandomLeafLeafContent implementations that aren't explicitly fragmented; the concrete type-name is in __typename. +type SimpleNamedFragmentRandomLeafLeafContentGenqlientOther struct { Typename string `json:"__typename"` } -// GetTypename returns SimpleNamedFragmentRandomLeafLeafContentOther.Typename, and is useful for accessing the field via an interface. -func (v *SimpleNamedFragmentRandomLeafLeafContentOther) GetTypename() string { return v.Typename } +// GetTypename returns SimpleNamedFragmentRandomLeafLeafContentGenqlientOther.Typename, and is useful for accessing the field via an interface. +func (v *SimpleNamedFragmentRandomLeafLeafContentGenqlientOther) GetTypename() string { + return v.Typename +} // SimpleNamedFragmentRandomLeafVideo includes the requested fields of the GraphQL type Video. type SimpleNamedFragmentRandomLeafVideo struct { diff --git a/internal/integration/omitimpls/generated.go b/internal/integration/omitimpls/generated.go index 15dbb087..2f217936 100644 --- a/internal/integration/omitimpls/generated.go +++ b/internal/integration/omitimpls/generated.go @@ -22,7 +22,7 @@ func (v *__queryOmitImplsInput) GetId() string { return v.Id } // // queryOmitImplsBeing is implemented by the following types: // queryOmitImplsBeingUser -// queryOmitImplsBeingOther +// queryOmitImplsBeingGenqlientOther type queryOmitImplsBeing interface { implementsGraphQLInterfacequeryOmitImplsBeing() // GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values). @@ -33,8 +33,8 @@ type queryOmitImplsBeing interface { GetName() string } -func (v *queryOmitImplsBeingUser) implementsGraphQLInterfacequeryOmitImplsBeing() {} -func (v *queryOmitImplsBeingOther) implementsGraphQLInterfacequeryOmitImplsBeing() {} +func (v *queryOmitImplsBeingUser) implementsGraphQLInterfacequeryOmitImplsBeing() {} +func (v *queryOmitImplsBeingGenqlientOther) implementsGraphQLInterfacequeryOmitImplsBeing() {} func __unmarshalqueryOmitImplsBeing(b []byte, v *queryOmitImplsBeing) error { if string(b) == "null" { @@ -57,7 +57,7 @@ func __unmarshalqueryOmitImplsBeing(b []byte, v *queryOmitImplsBeing) error { return fmt.Errorf( "response was missing Being.__typename") default: - *v = new(queryOmitImplsBeingOther) + *v = new(queryOmitImplsBeingGenqlientOther) return json.Unmarshal(b, *v) } } @@ -74,7 +74,7 @@ func __marshalqueryOmitImplsBeing(v *queryOmitImplsBeing) ([]byte, error) { *queryOmitImplsBeingUser }{typename, v} return json.Marshal(result) - case *queryOmitImplsBeingOther: + case *queryOmitImplsBeingGenqlientOther: return json.Marshal(v) case nil: return []byte("null"), nil @@ -84,21 +84,21 @@ func __marshalqueryOmitImplsBeing(v *queryOmitImplsBeing) ([]byte, error) { } } -// queryOmitImplsBeingOther is the catch-all for queryOmitImplsBeing implementations that aren't explicitly fragmented; the concrete type-name is in __typename. -type queryOmitImplsBeingOther struct { +// queryOmitImplsBeingGenqlientOther is the catch-all for queryOmitImplsBeing implementations that aren't explicitly fragmented; the concrete type-name is in __typename. +type queryOmitImplsBeingGenqlientOther struct { Typename string `json:"__typename"` Id string `json:"id"` Name string `json:"name"` } -// GetTypename returns queryOmitImplsBeingOther.Typename, and is useful for accessing the field via an interface. -func (v *queryOmitImplsBeingOther) GetTypename() string { return v.Typename } +// GetTypename returns queryOmitImplsBeingGenqlientOther.Typename, and is useful for accessing the field via an interface. +func (v *queryOmitImplsBeingGenqlientOther) GetTypename() string { return v.Typename } -// GetId returns queryOmitImplsBeingOther.Id, and is useful for accessing the field via an interface. -func (v *queryOmitImplsBeingOther) GetId() string { return v.Id } +// GetId returns queryOmitImplsBeingGenqlientOther.Id, and is useful for accessing the field via an interface. +func (v *queryOmitImplsBeingGenqlientOther) GetId() string { return v.Id } -// GetName returns queryOmitImplsBeingOther.Name, and is useful for accessing the field via an interface. -func (v *queryOmitImplsBeingOther) GetName() string { return v.Name } +// GetName returns queryOmitImplsBeingGenqlientOther.Name, and is useful for accessing the field via an interface. +func (v *queryOmitImplsBeingGenqlientOther) GetName() string { return v.Name } // queryOmitImplsBeingUser includes the requested fields of the GraphQL type User. type queryOmitImplsBeingUser struct { diff --git a/internal/integration/omitimpls/omitimpls_test.go b/internal/integration/omitimpls/omitimpls_test.go index cdb96193..cabfa9ba 100644 --- a/internal/integration/omitimpls/omitimpls_test.go +++ b/internal/integration/omitimpls/omitimpls_test.go @@ -58,8 +58,8 @@ func TestCatchAllForUnreferencedImplementation(t *testing.T) { assert.Equal(t, "3", resp.Being.GetId()) assert.Equal(t, "Fido", resp.Being.GetName()) - other, ok := resp.Being.(*queryOmitImplsBeingOther) - require.Truef(t, ok, "got %T, expected catch-all queryOmitImplsBeingOther", resp.Being) + other, ok := resp.Being.(*queryOmitImplsBeingGenqlientOther) + require.Truef(t, ok, "got %T, expected catch-all queryOmitImplsBeingGenqlientOther", resp.Being) assert.Equal(t, "Animal", other.Typename) assert.Equal(t, "3", other.Id) assert.Equal(t, "Fido", other.Name) From 59363e4e0e234a03a95af0202a6e8d2923844d62 Mon Sep 17 00:00:00 2001 From: Will Roden Date: Mon, 25 May 2026 17:43:58 -0500 Subject: [PATCH 10/12] flatten collectReferencedConcreteTypes and catch-all field copy --- generate/convert.go | 93 ++++++++++++++++++++++++--------------------- generate/types.go | 6 +-- 2 files changed, 52 insertions(+), 47 deletions(-) diff --git a/generate/convert.go b/generate/convert.go index e5ed563f..8f44ad15 100644 --- a/generate/convert.go +++ b/generate/convert.go @@ -528,7 +528,8 @@ func (g *generator) convertDefinition( // references; the rest are absorbed by the catch-all below. filteredImpls := implementationTypes if g.Config.OmitUnreferencedImplementations { - referenced := collectReferencedConcreteTypes(selectionSet, g.schema) + referenced := map[string]bool{} + collectReferencedConcreteTypes(selectionSet, g.schema, referenced) kept := make([]*ast.Definition, 0, len(implementationTypes)) for _, implDef := range implementationTypes { if referenced[implDef.Name] { @@ -739,37 +740,38 @@ func (g *generator) convertSelectionSet( return uniqFields, nil } -// collectReferencedConcreteTypes returns the set of concrete (Object) type -// names reachable via inline or named fragments in sel, recursing through -// nested fragments. Type conditions naming an interface or union do NOT -// expand to their possible concrete types; the catch-all handles those. -func collectReferencedConcreteTypes(sel ast.SelectionSet, schema *ast.Schema) map[string]bool { - referenced := map[string]bool{} - addCondition := func(condName string) { - if condName == "" { - return +// collectReferencedConcreteTypes updates referenced with the concrete (Object) +// type names reachable via inline or named fragments in selSet, recursing +// through nested fragments. Type conditions naming an interface or union +// don't expand to their possible concrete types; the catch-all handles those. +func collectReferencedConcreteTypes(selSet ast.SelectionSet, schema *ast.Schema, referenced map[string]bool) { + for i := range selSet { + var condName string + var fragSet ast.SelectionSet + + switch sel := selSet[i].(type) { + case *ast.InlineFragment: + condName = sel.TypeCondition + fragSet = sel.SelectionSet + case *ast.FragmentSpread: + if sel.Definition == nil { + continue + } + condName = sel.Definition.TypeCondition + fragSet = sel.Definition.SelectionSet + default: + continue } - if condDef := schema.Types[condName]; condDef != nil && condDef.Kind == ast.Object { - referenced[condName] = true + + condDef := schema.Types[condName] + if condDef == nil { + continue } - } - var walk func(sel ast.SelectionSet) - walk = func(sel ast.SelectionSet) { - for _, s := range sel { - switch s := s.(type) { - case *ast.InlineFragment: - addCondition(s.TypeCondition) - walk(s.SelectionSet) - case *ast.FragmentSpread: - if s.Definition != nil { - addCondition(s.Definition.TypeCondition) - walk(s.Definition.SelectionSet) - } - } + if condDef.Kind == ast.Object { + referenced[condName] = true } + collectReferencedConcreteTypes(fragSet, schema, referenced) } - walk(sel) - return referenced } // makeCatchAllStruct builds and registers the catch-all goStructType used @@ -789,21 +791,25 @@ func (g *generator) makeCatchAllStruct( ) (*goStructType, error) { catchAllFields := make([]*goStructField, 0, len(sharedFields)) for _, f := range sharedFields { - if f.GoName == "" { - iface, ok := f.GoType.(*goInterfaceType) - if ok { - if iface.OtherImplementation == nil { - return nil, errorf(pos, - "genqlient internal error: embedded fragment %s has no catch-all", - iface.GoName) - } - swapped := *f - swapped.GoType = iface.OtherImplementation - catchAllFields = append(catchAllFields, &swapped) - continue - } + if f.GoName != "" { + catchAllFields = append(catchAllFields, f) + continue + } + iface, ok := f.GoType.(*goInterfaceType) + if !ok { + catchAllFields = append(catchAllFields, f) + continue + } + if iface.OtherImplementation == nil { + return nil, errorf( + pos, + "genqlient internal error: embedded fragment %s has no catch-all", + iface.GoName, + ) } - catchAllFields = append(catchAllFields, f) + swapped := *f + swapped.GoType = iface.OtherImplementation + catchAllFields = append(catchAllFields, &swapped) } catchAllName := interfaceGoName + "GenqlientOther" @@ -1018,7 +1024,8 @@ func (g *generator) convertNamedFragment(fragment *ast.FragmentDefinition) (goTy filteredImpls := implementationTypes if g.Config.OmitUnreferencedImplementations { - referenced := collectReferencedConcreteTypes(fragment.SelectionSet, g.schema) + referenced := map[string]bool{} + collectReferencedConcreteTypes(fragment.SelectionSet, g.schema, referenced) kept := make([]*ast.Definition, 0, len(implementationTypes)) for _, implDef := range implementationTypes { if referenced[implDef.Name] { diff --git a/generate/types.go b/generate/types.go index e978c369..c34fa48d 100644 --- a/generate/types.go +++ b/generate/types.go @@ -484,10 +484,8 @@ type goInterfaceType struct { GoName string // Fields shared by all the interface's implementations; // we'll generate getter methods for each. - SharedFields []*goStructField - Implementations []*goStructType - // OtherImplementation, if non-nil, is a catch-all struct used at - // runtime for any concrete type not present in Implementations. + SharedFields []*goStructField + Implementations []*goStructType OtherImplementation *goStructType Selection ast.SelectionSet descriptionInfo From 350dda91488e2f8ad20dd7dac1d6befed93dbc68 Mon Sep 17 00:00:00 2001 From: Will Roden Date: Mon, 25 May 2026 18:08:51 -0500 Subject: [PATCH 11/12] factor and inline catch-all helpers --- generate/convert.go | 176 ++++++++++++++++++-------------------------- 1 file changed, 71 insertions(+), 105 deletions(-) diff --git a/generate/convert.go b/generate/convert.go index 8f44ad15..670bfff2 100644 --- a/generate/convert.go +++ b/generate/convert.go @@ -11,6 +11,7 @@ package generate import ( "fmt" + "slices" "sort" "github.com/vektah/gqlparser/v2/ast" @@ -523,21 +524,7 @@ func (g *generator) convertDefinition( // Make sure we generate stable output by sorting the types by name when we get them sort.Slice(implementationTypes, func(i, j int) bool { return implementationTypes[i].Name < implementationTypes[j].Name }) - // If omit_unreferenced_implementations is enabled, only generate - // per-type structs for implementations a fragment actually - // references; the rest are absorbed by the catch-all below. - filteredImpls := implementationTypes - if g.Config.OmitUnreferencedImplementations { - referenced := map[string]bool{} - collectReferencedConcreteTypes(selectionSet, g.schema, referenced) - kept := make([]*ast.Definition, 0, len(implementationTypes)) - for _, implDef := range implementationTypes { - if referenced[implDef.Name] { - kept = append(kept, implDef) - } - } - filteredImpls = kept - } + filteredImpls := g.filterReferencedImpls(implementationTypes, selectionSet) goType := &goInterfaceType{ GoName: name, @@ -567,14 +554,8 @@ func (g *generator) convertDefinition( goType.Implementations = append(goType.Implementations, implStructTyp) } - if g.Config.OmitUnreferencedImplementations { - // Always emit the catch-all so unknown __typenames (e.g. from - // server-side schema additions) decode gracefully. - otherType, err := g.makeCatchAllStruct(name, def.Name, sharedFields, selectionSet, pos) - if err != nil { - return nil, err - } - goType.OtherImplementation = otherType + if err := g.attachCatchAll(goType, def.Name, pos); err != nil { + return nil, err } return g.addType(goType, goType.GoName, pos) @@ -740,103 +721,103 @@ func (g *generator) convertSelectionSet( return uniqFields, nil } -// collectReferencedConcreteTypes updates referenced with the concrete (Object) -// type names reachable via inline or named fragments in selSet, recursing -// through nested fragments. Type conditions naming an interface or union -// don't expand to their possible concrete types; the catch-all handles those. -func collectReferencedConcreteTypes(selSet ast.SelectionSet, schema *ast.Schema, referenced map[string]bool) { - for i := range selSet { - var condName string - var fragSet ast.SelectionSet - - switch sel := selSet[i].(type) { - case *ast.InlineFragment: - condName = sel.TypeCondition - fragSet = sel.SelectionSet - case *ast.FragmentSpread: - if sel.Definition == nil { +// filterReferencedImpls returns the implementations actually referenced by a +// fragment in selSet. When OmitUnreferencedImplementations is disabled, impls +// is returned unchanged. +// +// A type condition that names an interface or union doesn't reference any +// concrete type directly — only its (transitive) fragment body can. +func (g *generator) filterReferencedImpls(impls []*ast.Definition, selSet ast.SelectionSet) []*ast.Definition { + if !g.Config.OmitUnreferencedImplementations { + return impls + } + referenced := map[string]bool{} + queue := []ast.SelectionSet{selSet} + for len(queue) > 0 { + ss := queue[0] + queue = queue[1:] + for _, s := range ss { + var condName string + var fragSet ast.SelectionSet + switch s := s.(type) { + case *ast.InlineFragment: + condName = s.TypeCondition + fragSet = s.SelectionSet + case *ast.FragmentSpread: + if s.Definition == nil { + continue + } + condName = s.Definition.TypeCondition + fragSet = s.Definition.SelectionSet + default: continue } - condName = sel.Definition.TypeCondition - fragSet = sel.Definition.SelectionSet - default: - continue - } - - condDef := schema.Types[condName] - if condDef == nil { - continue - } - if condDef.Kind == ast.Object { - referenced[condName] = true + if d := g.schema.Types[condName]; d != nil && d.Kind == ast.Object { + referenced[condName] = true + } + queue = append(queue, fragSet) } - collectReferencedConcreteTypes(fragSet, schema, referenced) } + + return slices.DeleteFunc(slices.Clone(impls), func(impl *ast.Definition) bool { + return !referenced[impl.Name] + }) } -// makeCatchAllStruct builds and registers the catch-all goStructType used -// by OmitUnreferencedImplementations. The struct carries the interface's -// shared fields (always including __typename); the unmarshal-helper +// attachCatchAll, when OmitUnreferencedImplementations is enabled, +// builds a catch-all struct for iface (registering it via g.addType) and +// assigns it to iface.OtherImplementation. The catch-all carries iface's +// SharedFields (which always include __typename); the unmarshal-helper // instantiates it whenever the server returns a __typename without an // explicitly-referenced implementation. // // Embedded fragment-spreads whose GoType is itself a *goInterfaceType are // swapped for that fragment's catch-all, so the result is a valid Go // struct embedding only structs. -func (g *generator) makeCatchAllStruct( - interfaceGoName, graphQLName string, - sharedFields []*goStructField, - selectionSet ast.SelectionSet, - pos *ast.Position, -) (*goStructType, error) { - catchAllFields := make([]*goStructField, 0, len(sharedFields)) - for _, f := range sharedFields { +func (g *generator) attachCatchAll(iface *goInterfaceType, graphQLName string, pos *ast.Position) error { + if !g.Config.OmitUnreferencedImplementations { + return nil + } + + catchAllFields := make([]*goStructField, 0, len(iface.SharedFields)) + for _, f := range iface.SharedFields { if f.GoName != "" { catchAllFields = append(catchAllFields, f) continue } - iface, ok := f.GoType.(*goInterfaceType) + embedded, ok := f.GoType.(*goInterfaceType) if !ok { catchAllFields = append(catchAllFields, f) continue } - if iface.OtherImplementation == nil { - return nil, errorf( - pos, - "genqlient internal error: embedded fragment %s has no catch-all", - iface.GoName, - ) - } + // embedded.OtherImplementation is guaranteed non-nil here: + // maybeAttachCatchAll only runs under + // OmitUnreferencedImplementations, and the same option ensures + // every embedded fragment-interface has its own catch-all. swapped := *f - swapped.GoType = iface.OtherImplementation + swapped.GoType = embedded.OtherImplementation catchAllFields = append(catchAllFields, &swapped) } - catchAllName := interfaceGoName + "GenqlientOther" catchAll := &goStructType{ - GoName: catchAllName, + GoName: iface.GoName + "GenqlientOther", Fields: catchAllFields, - Selection: selectionSet, + Selection: iface.Selection, descriptionInfo: descriptionInfo{ GraphQLName: graphQLName, CommentOverride: fmt.Sprintf( - "%s is the catch-all for %s implementations that aren't "+ - "explicitly fragmented; the concrete type-name is in __typename.", - catchAllName, interfaceGoName), + "%sGenqlientOther is the catch-all for %s implementations "+ + "that aren't explicitly fragmented; the concrete "+ + "type-name is in __typename.", + iface.GoName, iface.GoName), }, Generator: g, } - registered, err := g.addType(catchAll, catchAll.GoName, pos) - if err != nil { - return nil, err + if _, err := g.addType(catchAll, catchAll.GoName, pos); err != nil { + return err } - catchAllTyp, ok := registered.(*goStructType) - if !ok { - return nil, errorf(pos, - "genqlient internal error: catch-all for %s registered as %T", - interfaceGoName, registered) - } - return catchAllTyp, nil + iface.OtherImplementation = catchAll + return nil } // fragmentMatches returns true if the given fragment is "active" when applied @@ -1022,18 +1003,7 @@ func (g *generator) convertNamedFragment(fragment *ast.FragmentDefinition) (goTy // Make sure we generate stable output by sorting the types by name when we get them sort.Slice(implementationTypes, func(i, j int) bool { return implementationTypes[i].Name < implementationTypes[j].Name }) - filteredImpls := implementationTypes - if g.Config.OmitUnreferencedImplementations { - referenced := map[string]bool{} - collectReferencedConcreteTypes(fragment.SelectionSet, g.schema, referenced) - kept := make([]*ast.Definition, 0, len(implementationTypes)) - for _, implDef := range implementationTypes { - if referenced[implDef.Name] { - kept = append(kept, implDef) - } - } - filteredImpls = kept - } + filteredImpls := g.filterReferencedImpls(implementationTypes, fragment.SelectionSet) goType := &goInterfaceType{ GoName: fragment.Name, @@ -1064,12 +1034,8 @@ func (g *generator) convertNamedFragment(fragment *ast.FragmentDefinition) (goTy g.typeMap[implTyp.GoName] = implTyp } - if g.Config.OmitUnreferencedImplementations { - otherType, err := g.makeCatchAllStruct(fragment.Name, typ.Name, fields, fragment.SelectionSet, fragment.Position) - if err != nil { - return nil, err - } - goType.OtherImplementation = otherType + if err := g.attachCatchAll(goType, typ.Name, fragment.Position); err != nil { + return nil, err } return goType, nil From 65cb67e7ae9715ba52987a18157acce670fdc95b Mon Sep 17 00:00:00 2001 From: Will Roden Date: Mon, 25 May 2026 18:29:18 -0500 Subject: [PATCH 12/12] update operations.md --- docs/operations.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/operations.md b/docs/operations.md index d1a88459..93ac6ade 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -173,6 +173,8 @@ type GetBooksFavoriteBook struct { Keep in mind that if you later want to add fragments to your selection, you won't be able to use `struct` anymore; when you remove it you may need to update your code to replace `.Title` with `.GetTitle()` and so on. +If your interface or union has many implementations and your query only fragments a few, set [`omit_unreferenced_implementations`](genqlient.yaml) in `genqlient.yaml`. genqlient will then generate per-type structs only for the implementations actually referenced by a fragment; everything else decodes into a single catch-all struct (`<…>GenqlientOther`) carrying just the interface's shared fields. This dramatically reduces generated code size for Relay-style `Node` interfaces, and unknown `__typename` values from the server (e.g. implementations added after the client was generated) decode into the catch-all rather than failing. + ## Sharing types By default, genqlient generates a different type for each part of each query, [even those which are structurally the same](faq.md#why-does-genqlient-generate-such-complicated-type-names-). Sometimes, however, you want to have some common code that can accept data from several queries or parts of queries.