diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 9797b136..a8451245 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -24,6 +24,8 @@ When releasing a new version: ### New features: +- genqlient's types are now safe to JSON-marshal, which can be useful for putting them in a cache, for example. See the [docs](FAQ.md#-let-me-json-marshal-my-response-objects) for details. + ### Bug fixes: ## v0.2.0 diff --git a/docs/FAQ.md b/docs/FAQ.md index b9181325..7d24a0a0 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -96,6 +96,19 @@ bindings: Or, you can bind it to any other type, perhaps one with size-checked constructors; see the [`genqlient.yaml` documentation](`genqlient.yaml`) for more details. +### … let me json-marshal my response objects + +This is supported by default! All genqlient-generated types support both JSON-marshaling and unmarshaling, which can be useful for putting them in a cache, inspecting them by hand, using them in mocks (although this is [not recommended](#-test-my-graphql-apis)), or anything else you can do with JSON. It's not guaranteed that marshaling a genqlient type will produce the exact GraphQL input -- we try to get as close as we can but there are some limitations around Go zero values -- but unmarshaling again should produce the value genqlient returned. That is: + +```go +resp, err := MyQuery(...) +// not guaranteed to match what the server sent (but close): +b, err := json.Marshal(resp) +// guaranteed to match resp: +var respAgain MyQueryResponse +err := json.Unmarshal(b, &resp) +``` + ## How do I make a query with … ### … a specific name for a field? diff --git a/generate/marshal.go.tmpl b/generate/marshal.go.tmpl index 0f31c3d0..1f905aa0 100644 --- a/generate/marshal.go.tmpl +++ b/generate/marshal.go.tmpl @@ -1,28 +1,60 @@ -{{/* See unmarshal.go.tmpl for more on how this works; this is mostly just - parallel (and simplified -- we don't need to handle embedding). */}} +{{/* We generate MarshalJSON for much the same reasons as UnmarshalJSON -- see + unmarshal.go.tmpl for details. (Note we generate both even if genqlient + itself needs only UnmarshalJSON, for the benefit of callers who want to, + for example, put genqlient responses in a cache.) But our implementation + for marshaling is quite different. + + Specifically, the treatment of field-visibility with embedded fields must + differ from both ordinary encoding/json and unmarshaling: we need to + choose exactly one conflicting field in all cases (whereas Go chooses at + most one and when unmarshaling we choose them all). See + goStructType.FlattenedFields in types.go for more discussion of embedding + and visibility. + + To accomplish that, we essentially flatten out all the embedded fields + when marshaling, following those precedence rules. Then we basically + follow what we do in unmarshaling, but in reverse order: first we marshal + the special fields, then we glue everything together with the ordinary + fields. + + We do one other thing differently, for the benefit of the marshal-helper + in marshal_helper.go.tmpl. While when unmarshaling it's easy to unmarshal + out the __typename field, then unmarshal out everything else, with + marshaling we can't do the same (at least not without some careful + JSON-stitching; the considerations are basically the same as those + discussed in FlattenedFields). So we write out a helper method + __premarshalJSON() which basically does all but the final JSON-marshal. + (Then the real MarshalJSON() just calls that, and then marshals.) + Thus a marshal-helper for this type, if any, can call __premarshalJSON() + directly, and embed its result. */}} + +type __premarshal{{.GoName}} struct{ + {{range .FlattenedFields -}} + {{if .NeedsMarshaling -}} + {{.GoName}} {{repeat .GoType.SliceDepth "[]"}}{{ref "encoding/json.RawMessage"}} `json:"{{.JSONName}}"` + {{else}} + {{.GoName}} {{.GoType.Reference}} `json:"{{.JSONName}}"` + {{end}} + {{end}} +} func (v *{{.GoName}}) MarshalJSON() ([]byte, error) { - {{/* We do the two passes in the opposite order of unmarshal: first, we - marshal the special fields, then we assign those to the wrapper struct - and finish marshaling the whole object. But first we set up the - object for the second part, so we can assign to it as we go. */}} - var fullObject struct{ - *{{.GoName}} - {{range .Fields -}} - {{if .NeedsMarshaler -}} - {{.GoName}} {{repeat .GoType.SliceDepth "[]"}}{{ref "encoding/json.RawMessage"}} `json:"{{.JSONName}}"` - {{end -}} - {{end -}} - {{ref "github.com/Khan/genqlient/graphql.NoMarshalJSON"}} + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err } - fullObject.{{.GoName}} = v + return json.Marshal(premarshaled) +} - {{range $field := .Fields -}} - {{if $field.NeedsMarshaler -}} +func (v *{{.GoName}}) __premarshalJSON() (*__premarshal{{.GoName}}, error) { + var retval __premarshal{{.GoName}} + + {{range $field := .FlattenedFields -}} + {{if $field.NeedsMarshaling -}} { - {{/* Here dst is the json.RawMessage, and src is the Go type */}} - dst := &fullObject.{{$field.GoName}} - src := v.{{$field.GoName}} + {{/* Here dst is the json.RawMessage, and src is the Go type. */}} + dst := &retval.{{$field.GoName}} + src := v.{{$field.Selector}} {{range $i := intRange $field.GoType.SliceDepth -}} *dst = make( {{repeat (sub $field.GoType.SliceDepth $i) "[]"}}{{ref "encoding/json.RawMessage"}}, @@ -45,7 +77,7 @@ func (v *{{.GoName}}) MarshalJSON() ([]byte, error) { {{if not $field.GoType.IsPointer}}&{{end}}src) if err != nil { return nil, fmt.Errorf( - "Unable to marshal {{$.GoName}}.{{$field.GoName}}: %w", err) + "Unable to marshal {{$.GoName}}.{{$field.Selector}}: %w", err) } {{if $field.GoType.IsPointer -}} }{{/* end if src != nil */}} @@ -54,8 +86,10 @@ func (v *{{.GoName}}) MarshalJSON() ([]byte, error) { } {{end -}} } + {{else -}} + retval.{{$field.GoName}} = v.{{$field.Selector}} + {{end -}} {{end -}} - {{end}} - return {{ref "encoding/json.Marshal"}}(&fullObject) + return &retval, nil } diff --git a/generate/marshal_helper.go.tmpl b/generate/marshal_helper.go.tmpl new file mode 100644 index 00000000..85ca3082 --- /dev/null +++ b/generate/marshal_helper.go.tmpl @@ -0,0 +1,44 @@ +{{/* This is somewhat parallel to unmarshal_helper.go.tmpl, but, as usual, in + reverse. Note the helper accepts a pointer-to-interface, for + consistency with unmarshaling and with the API we expect of custom + marshalers. */}} + +func __marshal{{.GoName}}(v *{{.GoName}}) ([]byte, error) { + {{/* Determine the GraphQL typename, which the unmarshaler will need should + it be called on our output. */}} + var typename string + switch v := (*v).(type) { + {{range .Implementations -}} + case *{{.GoName}}: + typename = "{{.GraphQLName}}" + + {{/* Now actually do the marshal, with the concrete type. (Go only + marshals embeds the way we want if they're structs.) Except that + won't work right if the implementation-type has its own + MarshalJSON method (maybe it in turn has an interface-typed + field), so we call the helper __premarshalJSON directly (see + marshal.go.tmpl). */}} + {{if .NeedsMarshaling -}} + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshal{{.GoName}} + }{typename, premarshaled} + {{else -}} + result := struct { + TypeName string `json:"__typename"` + *{{.GoName}} + }{typename, v} + {{end -}} + return json.Marshal(result) + {{end -}} + case nil: + return []byte("null"), nil + default: + return nil, {{ref "fmt.Errorf"}}( + `Unexpected concrete type for {{.GoName}}: "%T"`, v) + } +} diff --git a/generate/testdata/snapshots/TestGenerate-ComplexInlineFragments.graphql-ComplexInlineFragments.graphql.go b/generate/testdata/snapshots/TestGenerate-ComplexInlineFragments.graphql-ComplexInlineFragments.graphql.go index 5e4d035e..79a97ef3 100644 --- a/generate/testdata/snapshots/TestGenerate-ComplexInlineFragments.graphql-ComplexInlineFragments.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-ComplexInlineFragments.graphql-ComplexInlineFragments.graphql.go @@ -87,6 +87,42 @@ func __unmarshalComplexInlineFragmentsConflictingStuffContent(b []byte, v *Compl } } +func __marshalComplexInlineFragmentsConflictingStuffContent(v *ComplexInlineFragmentsConflictingStuffContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *ComplexInlineFragmentsConflictingStuffArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsConflictingStuffArticle + }{typename, v} + return json.Marshal(result) + case *ComplexInlineFragmentsConflictingStuffVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsConflictingStuffVideo + }{typename, v} + return json.Marshal(result) + case *ComplexInlineFragmentsConflictingStuffTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsConflictingStuffTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for ComplexInlineFragmentsConflictingStuffContent: "%T"`, v) + } +} + // ComplexInlineFragmentsConflictingStuffTopic includes the requested fields of the GraphQL type Topic. type ComplexInlineFragmentsConflictingStuffTopic struct { Typename string `json:"__typename"` @@ -174,6 +210,46 @@ func __unmarshalComplexInlineFragmentsNestedStuffContent(b []byte, v *ComplexInl } } +func __marshalComplexInlineFragmentsNestedStuffContent(v *ComplexInlineFragmentsNestedStuffContent) ([]byte, error) { + + 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 *ComplexInlineFragmentsNestedStuffVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsNestedStuffVideo + }{typename, v} + return json.Marshal(result) + case *ComplexInlineFragmentsNestedStuffTopic: + typename = "Topic" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalComplexInlineFragmentsNestedStuffTopic + }{typename, premarshaled} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for ComplexInlineFragmentsNestedStuffContent: "%T"`, v) + } +} + // ComplexInlineFragmentsNestedStuffTopic includes the requested fields of the GraphQL type Topic. type ComplexInlineFragmentsNestedStuffTopic struct { Typename string `json:"__typename"` @@ -219,6 +295,46 @@ func (v *ComplexInlineFragmentsNestedStuffTopic) UnmarshalJSON(b []byte) error { return nil } +type __premarshalComplexInlineFragmentsNestedStuffTopic struct { + Typename string `json:"__typename"` + + Children []json.RawMessage `json:"children"` +} + +func (v *ComplexInlineFragmentsNestedStuffTopic) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *ComplexInlineFragmentsNestedStuffTopic) __premarshalJSON() (*__premarshalComplexInlineFragmentsNestedStuffTopic, error) { + + var retval __premarshalComplexInlineFragmentsNestedStuffTopic + + retval.Typename = v.Typename + { + + dst := &retval.Children + src := v.Children + *dst = make( + []json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + var err error + *dst, err = __marshalComplexInlineFragmentsNestedStuffTopicChildrenContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal ComplexInlineFragmentsNestedStuffTopic.Children: %w", err) + } + } + } + return &retval, nil +} + // ComplexInlineFragmentsNestedStuffTopicChildrenArticle includes the requested fields of the GraphQL type Article. type ComplexInlineFragmentsNestedStuffTopicChildrenArticle struct { Typename string `json:"__typename"` @@ -272,6 +388,43 @@ func (v *ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParen return nil } +type __premarshalComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopic struct { + Children []json.RawMessage `json:"children"` +} + +func (v *ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopic) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopic) __premarshalJSON() (*__premarshalComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopic, error) { + + var retval __premarshalComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopic + + { + + dst := &retval.Children + src := v.Children + *dst = make( + []json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + var err error + *dst, err = __marshalComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopicChildrenContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopic.Children: %w", err) + } + } + } + return &retval, nil +} + // ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopicChildrenArticle includes the requested fields of the GraphQL type Article. type ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopicChildrenArticle struct { Typename string `json:"__typename"` @@ -388,6 +541,42 @@ func __unmarshalComplexInlineFragmentsNestedStuffTopicChildrenArticleParentConte } } +func __marshalComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopicChildrenContent(v *ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopicChildrenContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopicChildrenArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopicChildrenArticle + }{typename, v} + return json.Marshal(result) + case *ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopicChildrenVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopicChildrenVideo + }{typename, v} + return json.Marshal(result) + case *ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopicChildrenTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopicChildrenTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopicChildrenContent: "%T"`, v) + } +} + // ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopicChildrenTopic includes the requested fields of the GraphQL type Topic. type ComplexInlineFragmentsNestedStuffTopicChildrenArticleParentContentParentTopicChildrenTopic struct { Typename string `json:"__typename"` @@ -491,6 +680,42 @@ func __unmarshalComplexInlineFragmentsNestedStuffTopicChildrenContent(b []byte, } } +func __marshalComplexInlineFragmentsNestedStuffTopicChildrenContent(v *ComplexInlineFragmentsNestedStuffTopicChildrenContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *ComplexInlineFragmentsNestedStuffTopicChildrenArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsNestedStuffTopicChildrenArticle + }{typename, v} + return json.Marshal(result) + case *ComplexInlineFragmentsNestedStuffTopicChildrenVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsNestedStuffTopicChildrenVideo + }{typename, v} + return json.Marshal(result) + case *ComplexInlineFragmentsNestedStuffTopicChildrenTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsNestedStuffTopicChildrenTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for ComplexInlineFragmentsNestedStuffTopicChildrenContent: "%T"`, v) + } +} + // ComplexInlineFragmentsNestedStuffTopicChildrenTopic includes the requested fields of the GraphQL type Topic. type ComplexInlineFragmentsNestedStuffTopicChildrenTopic struct { Typename string `json:"__typename"` @@ -609,6 +834,42 @@ func __unmarshalComplexInlineFragmentsRandomItemContent(b []byte, v *ComplexInli } } +func __marshalComplexInlineFragmentsRandomItemContent(v *ComplexInlineFragmentsRandomItemContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *ComplexInlineFragmentsRandomItemArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsRandomItemArticle + }{typename, v} + return json.Marshal(result) + case *ComplexInlineFragmentsRandomItemVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsRandomItemVideo + }{typename, v} + return json.Marshal(result) + case *ComplexInlineFragmentsRandomItemTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsRandomItemTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for ComplexInlineFragmentsRandomItemContent: "%T"`, v) + } +} + // ComplexInlineFragmentsRandomItemTopic includes the requested fields of the GraphQL type Topic. type ComplexInlineFragmentsRandomItemTopic struct { Typename string `json:"__typename"` @@ -765,6 +1026,42 @@ func __unmarshalComplexInlineFragmentsRepeatedStuffContent(b []byte, v *ComplexI } } +func __marshalComplexInlineFragmentsRepeatedStuffContent(v *ComplexInlineFragmentsRepeatedStuffContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *ComplexInlineFragmentsRepeatedStuffArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsRepeatedStuffArticle + }{typename, v} + return json.Marshal(result) + case *ComplexInlineFragmentsRepeatedStuffVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsRepeatedStuffVideo + }{typename, v} + return json.Marshal(result) + case *ComplexInlineFragmentsRepeatedStuffTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *ComplexInlineFragmentsRepeatedStuffTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for ComplexInlineFragmentsRepeatedStuffContent: "%T"`, v) + } +} + // ComplexInlineFragmentsRepeatedStuffTopic includes the requested fields of the GraphQL type Topic. type ComplexInlineFragmentsRepeatedStuffTopic struct { Typename string `json:"__typename"` @@ -874,6 +1171,82 @@ func (v *ComplexInlineFragmentsResponse) UnmarshalJSON(b []byte) error { return nil } +type __premarshalComplexInlineFragmentsResponse struct { + Root ComplexInlineFragmentsRootTopic `json:"root"` + + RandomItem json.RawMessage `json:"randomItem"` + + RepeatedStuff json.RawMessage `json:"repeatedStuff"` + + ConflictingStuff json.RawMessage `json:"conflictingStuff"` + + NestedStuff json.RawMessage `json:"nestedStuff"` +} + +func (v *ComplexInlineFragmentsResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *ComplexInlineFragmentsResponse) __premarshalJSON() (*__premarshalComplexInlineFragmentsResponse, error) { + + var retval __premarshalComplexInlineFragmentsResponse + + retval.Root = v.Root + { + + dst := &retval.RandomItem + src := v.RandomItem + var err error + *dst, err = __marshalComplexInlineFragmentsRandomItemContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal ComplexInlineFragmentsResponse.RandomItem: %w", err) + } + } + { + + dst := &retval.RepeatedStuff + src := v.RepeatedStuff + var err error + *dst, err = __marshalComplexInlineFragmentsRepeatedStuffContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal ComplexInlineFragmentsResponse.RepeatedStuff: %w", err) + } + } + { + + dst := &retval.ConflictingStuff + src := v.ConflictingStuff + var err error + *dst, err = __marshalComplexInlineFragmentsConflictingStuffContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal ComplexInlineFragmentsResponse.ConflictingStuff: %w", err) + } + } + { + + dst := &retval.NestedStuff + src := v.NestedStuff + var err error + *dst, err = __marshalComplexInlineFragmentsNestedStuffContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal ComplexInlineFragmentsResponse.NestedStuff: %w", err) + } + } + return &retval, nil +} + // ComplexInlineFragmentsRootTopic includes the requested fields of the GraphQL type Topic. type ComplexInlineFragmentsRootTopic struct { // ID is documented in the Content interface. diff --git a/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go b/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go index df90dc50..59aa35dc 100644 --- a/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go @@ -40,6 +40,65 @@ func (v *ComplexNamedFragmentsResponse) UnmarshalJSON(b []byte) error { return nil } +type __premarshalComplexNamedFragmentsResponse struct { + RandomItem json.RawMessage `json:"randomItem"` + + RandomLeaf json.RawMessage `json:"randomLeaf"` + + OtherLeaf json.RawMessage `json:"otherLeaf"` +} + +func (v *ComplexNamedFragmentsResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *ComplexNamedFragmentsResponse) __premarshalJSON() (*__premarshalComplexNamedFragmentsResponse, error) { + + var retval __premarshalComplexNamedFragmentsResponse + + { + + dst := &retval.RandomItem + src := v.QueryFragment.InnerQueryFragment.RandomItem + var err error + *dst, err = __marshalInnerQueryFragmentRandomItemContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal ComplexNamedFragmentsResponse.QueryFragment.InnerQueryFragment.RandomItem: %w", err) + } + } + { + + dst := &retval.RandomLeaf + src := v.QueryFragment.InnerQueryFragment.RandomLeaf + var err error + *dst, err = __marshalInnerQueryFragmentRandomLeafLeafContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal ComplexNamedFragmentsResponse.QueryFragment.InnerQueryFragment.RandomLeaf: %w", err) + } + } + { + + dst := &retval.OtherLeaf + src := v.QueryFragment.InnerQueryFragment.OtherLeaf + var err error + *dst, err = __marshalInnerQueryFragmentOtherLeafLeafContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal ComplexNamedFragmentsResponse.QueryFragment.InnerQueryFragment.OtherLeaf: %w", err) + } + } + return &retval, nil +} + // ContentFields includes the GraphQL fields of Content requested by the fragment ContentFields. // The GraphQL type's documentation follows. // @@ -113,6 +172,42 @@ func __unmarshalContentFields(b []byte, v *ContentFields) error { } } +func __marshalContentFields(v *ContentFields) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *ContentFieldsArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *ContentFieldsArticle + }{typename, v} + return json.Marshal(result) + case *ContentFieldsVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *ContentFieldsVideo + }{typename, v} + return json.Marshal(result) + case *ContentFieldsTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *ContentFieldsTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for ContentFields: "%T"`, v) + } +} + // ContentFields includes the GraphQL fields of Article requested by the fragment ContentFields. // The GraphQL type's documentation follows. // @@ -211,6 +306,65 @@ func (v *InnerQueryFragment) UnmarshalJSON(b []byte) error { return nil } +type __premarshalInnerQueryFragment struct { + RandomItem json.RawMessage `json:"randomItem"` + + RandomLeaf json.RawMessage `json:"randomLeaf"` + + OtherLeaf json.RawMessage `json:"otherLeaf"` +} + +func (v *InnerQueryFragment) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *InnerQueryFragment) __premarshalJSON() (*__premarshalInnerQueryFragment, error) { + + var retval __premarshalInnerQueryFragment + + { + + dst := &retval.RandomItem + src := v.RandomItem + var err error + *dst, err = __marshalInnerQueryFragmentRandomItemContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal InnerQueryFragment.RandomItem: %w", err) + } + } + { + + dst := &retval.RandomLeaf + src := v.RandomLeaf + var err error + *dst, err = __marshalInnerQueryFragmentRandomLeafLeafContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal InnerQueryFragment.RandomLeaf: %w", err) + } + } + { + + dst := &retval.OtherLeaf + src := v.OtherLeaf + var err error + *dst, err = __marshalInnerQueryFragmentOtherLeafLeafContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal InnerQueryFragment.OtherLeaf: %w", err) + } + } + return &retval, nil +} + // InnerQueryFragmentOtherLeafArticle includes the requested fields of the GraphQL type Article. type InnerQueryFragmentOtherLeafArticle struct { Typename string `json:"__typename"` @@ -242,6 +396,32 @@ func (v *InnerQueryFragmentOtherLeafArticle) UnmarshalJSON(b []byte) error { return nil } +type __premarshalInnerQueryFragmentOtherLeafArticle struct { + Typename string `json:"__typename"` + + Name string `json:"name"` + + Url string `json:"url"` +} + +func (v *InnerQueryFragmentOtherLeafArticle) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *InnerQueryFragmentOtherLeafArticle) __premarshalJSON() (*__premarshalInnerQueryFragmentOtherLeafArticle, error) { + + var retval __premarshalInnerQueryFragmentOtherLeafArticle + + retval.Typename = v.Typename + retval.Name = v.ContentFieldsArticle.Name + retval.Url = v.ContentFieldsArticle.Url + return &retval, nil +} + // InnerQueryFragmentOtherLeafLeafContent includes the requested fields of the GraphQL interface LeafContent. // // InnerQueryFragmentOtherLeafLeafContent is implemented by the following types: @@ -297,6 +477,42 @@ func __unmarshalInnerQueryFragmentOtherLeafLeafContent(b []byte, v *InnerQueryFr } } +func __marshalInnerQueryFragmentOtherLeafLeafContent(v *InnerQueryFragmentOtherLeafLeafContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *InnerQueryFragmentOtherLeafArticle: + typename = "Article" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalInnerQueryFragmentOtherLeafArticle + }{typename, premarshaled} + return json.Marshal(result) + case *InnerQueryFragmentOtherLeafVideo: + typename = "Video" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalInnerQueryFragmentOtherLeafVideo + }{typename, premarshaled} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for InnerQueryFragmentOtherLeafLeafContent: "%T"`, v) + } +} + // InnerQueryFragmentOtherLeafVideo includes the requested fields of the GraphQL type Video. type InnerQueryFragmentOtherLeafVideo struct { Typename string `json:"__typename"` @@ -334,6 +550,38 @@ func (v *InnerQueryFragmentOtherLeafVideo) UnmarshalJSON(b []byte) error { return nil } +type __premarshalInnerQueryFragmentOtherLeafVideo struct { + Typename string `json:"__typename"` + + Id *testutil.ID `json:"id"` + + Parent *MoreVideoFieldsParentTopic `json:"parent"` + + Name string `json:"name"` + + Url string `json:"url"` +} + +func (v *InnerQueryFragmentOtherLeafVideo) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *InnerQueryFragmentOtherLeafVideo) __premarshalJSON() (*__premarshalInnerQueryFragmentOtherLeafVideo, error) { + + var retval __premarshalInnerQueryFragmentOtherLeafVideo + + retval.Typename = v.Typename + retval.Id = v.MoreVideoFields.Id + retval.Parent = v.MoreVideoFields.Parent + retval.Name = v.ContentFieldsVideo.Name + retval.Url = v.ContentFieldsVideo.Url + return &retval, nil +} + // InnerQueryFragmentRandomItemArticle includes the requested fields of the GraphQL type Article. type InnerQueryFragmentRandomItemArticle struct { Typename string `json:"__typename"` @@ -368,6 +616,35 @@ func (v *InnerQueryFragmentRandomItemArticle) UnmarshalJSON(b []byte) error { return nil } +type __premarshalInnerQueryFragmentRandomItemArticle struct { + Typename string `json:"__typename"` + + Id testutil.ID `json:"id"` + + Name string `json:"name"` + + Url string `json:"url"` +} + +func (v *InnerQueryFragmentRandomItemArticle) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *InnerQueryFragmentRandomItemArticle) __premarshalJSON() (*__premarshalInnerQueryFragmentRandomItemArticle, error) { + + var retval __premarshalInnerQueryFragmentRandomItemArticle + + retval.Typename = v.Typename + retval.Id = v.Id + retval.Name = v.Name + retval.Url = v.ContentFieldsArticle.Url + return &retval, nil +} + // InnerQueryFragmentRandomItemContent includes the requested fields of the GraphQL interface Content. // // InnerQueryFragmentRandomItemContent is implemented by the following types: @@ -459,6 +736,54 @@ func __unmarshalInnerQueryFragmentRandomItemContent(b []byte, v *InnerQueryFragm } } +func __marshalInnerQueryFragmentRandomItemContent(v *InnerQueryFragmentRandomItemContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *InnerQueryFragmentRandomItemArticle: + typename = "Article" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalInnerQueryFragmentRandomItemArticle + }{typename, premarshaled} + return json.Marshal(result) + case *InnerQueryFragmentRandomItemVideo: + typename = "Video" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalInnerQueryFragmentRandomItemVideo + }{typename, premarshaled} + return json.Marshal(result) + case *InnerQueryFragmentRandomItemTopic: + typename = "Topic" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalInnerQueryFragmentRandomItemTopic + }{typename, premarshaled} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for InnerQueryFragmentRandomItemContent: "%T"`, v) + } +} + // InnerQueryFragmentRandomItemTopic includes the requested fields of the GraphQL type Topic. type InnerQueryFragmentRandomItemTopic struct { Typename string `json:"__typename"` @@ -493,6 +818,35 @@ func (v *InnerQueryFragmentRandomItemTopic) UnmarshalJSON(b []byte) error { return nil } +type __premarshalInnerQueryFragmentRandomItemTopic struct { + Typename string `json:"__typename"` + + Id testutil.ID `json:"id"` + + Name string `json:"name"` + + Url string `json:"url"` +} + +func (v *InnerQueryFragmentRandomItemTopic) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *InnerQueryFragmentRandomItemTopic) __premarshalJSON() (*__premarshalInnerQueryFragmentRandomItemTopic, error) { + + var retval __premarshalInnerQueryFragmentRandomItemTopic + + retval.Typename = v.Typename + retval.Id = v.Id + retval.Name = v.Name + retval.Url = v.ContentFieldsTopic.Url + return &retval, nil +} + // InnerQueryFragmentRandomItemVideo includes the requested fields of the GraphQL type Video. type InnerQueryFragmentRandomItemVideo struct { Typename string `json:"__typename"` @@ -533,6 +887,41 @@ func (v *InnerQueryFragmentRandomItemVideo) UnmarshalJSON(b []byte) error { return nil } +type __premarshalInnerQueryFragmentRandomItemVideo struct { + Typename string `json:"__typename"` + + Id testutil.ID `json:"id"` + + Name string `json:"name"` + + Url string `json:"url"` + + Duration int `json:"duration"` + + Thumbnail VideoFieldsThumbnail `json:"thumbnail"` +} + +func (v *InnerQueryFragmentRandomItemVideo) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *InnerQueryFragmentRandomItemVideo) __premarshalJSON() (*__premarshalInnerQueryFragmentRandomItemVideo, error) { + + var retval __premarshalInnerQueryFragmentRandomItemVideo + + 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 +} + // InnerQueryFragmentRandomLeafArticle includes the requested fields of the GraphQL type Article. type InnerQueryFragmentRandomLeafArticle struct { Typename string `json:"__typename"` @@ -564,6 +953,32 @@ func (v *InnerQueryFragmentRandomLeafArticle) UnmarshalJSON(b []byte) error { return nil } +type __premarshalInnerQueryFragmentRandomLeafArticle struct { + Typename string `json:"__typename"` + + Name string `json:"name"` + + Url string `json:"url"` +} + +func (v *InnerQueryFragmentRandomLeafArticle) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *InnerQueryFragmentRandomLeafArticle) __premarshalJSON() (*__premarshalInnerQueryFragmentRandomLeafArticle, error) { + + var retval __premarshalInnerQueryFragmentRandomLeafArticle + + retval.Typename = v.Typename + retval.Name = v.ContentFieldsArticle.Name + retval.Url = v.ContentFieldsArticle.Url + return &retval, nil +} + // InnerQueryFragmentRandomLeafLeafContent includes the requested fields of the GraphQL interface LeafContent. // // InnerQueryFragmentRandomLeafLeafContent is implemented by the following types: @@ -619,6 +1034,42 @@ func __unmarshalInnerQueryFragmentRandomLeafLeafContent(b []byte, v *InnerQueryF } } +func __marshalInnerQueryFragmentRandomLeafLeafContent(v *InnerQueryFragmentRandomLeafLeafContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *InnerQueryFragmentRandomLeafArticle: + typename = "Article" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalInnerQueryFragmentRandomLeafArticle + }{typename, premarshaled} + return json.Marshal(result) + case *InnerQueryFragmentRandomLeafVideo: + typename = "Video" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalInnerQueryFragmentRandomLeafVideo + }{typename, premarshaled} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for InnerQueryFragmentRandomLeafLeafContent: "%T"`, v) + } +} + // InnerQueryFragmentRandomLeafVideo includes the requested fields of the GraphQL type Video. type InnerQueryFragmentRandomLeafVideo struct { Typename string `json:"__typename"` @@ -662,6 +1113,44 @@ func (v *InnerQueryFragmentRandomLeafVideo) UnmarshalJSON(b []byte) error { return nil } +type __premarshalInnerQueryFragmentRandomLeafVideo struct { + Typename string `json:"__typename"` + + Id testutil.ID `json:"id"` + + Name string `json:"name"` + + Url string `json:"url"` + + Duration int `json:"duration"` + + Thumbnail VideoFieldsThumbnail `json:"thumbnail"` + + Parent *MoreVideoFieldsParentTopic `json:"parent"` +} + +func (v *InnerQueryFragmentRandomLeafVideo) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *InnerQueryFragmentRandomLeafVideo) __premarshalJSON() (*__premarshalInnerQueryFragmentRandomLeafVideo, error) { + + var retval __premarshalInnerQueryFragmentRandomLeafVideo + + 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 + retval.Parent = v.MoreVideoFields.Parent + return &retval, nil +} + // MoreVideoFields includes the GraphQL fields of Video requested by the fragment MoreVideoFields. type MoreVideoFields struct { // ID is documented in the Content interface. @@ -722,6 +1211,49 @@ func (v *MoreVideoFieldsParentTopic) UnmarshalJSON(b []byte) error { return nil } +type __premarshalMoreVideoFieldsParentTopic struct { + Name *string `json:"name"` + + Url *string `json:"url"` + + Children []json.RawMessage `json:"children"` +} + +func (v *MoreVideoFieldsParentTopic) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *MoreVideoFieldsParentTopic) __premarshalJSON() (*__premarshalMoreVideoFieldsParentTopic, error) { + + var retval __premarshalMoreVideoFieldsParentTopic + + retval.Name = v.Name + retval.Url = v.Url + { + + dst := &retval.Children + src := v.Children + *dst = make( + []json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + var err error + *dst, err = __marshalMoreVideoFieldsParentTopicChildrenContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal MoreVideoFieldsParentTopic.Children: %w", err) + } + } + } + return &retval, nil +} + // MoreVideoFieldsParentTopicChildrenArticle includes the requested fields of the GraphQL type Article. type MoreVideoFieldsParentTopicChildrenArticle struct { Typename *string `json:"__typename"` @@ -792,6 +1324,46 @@ func __unmarshalMoreVideoFieldsParentTopicChildrenContent(b []byte, v *MoreVideo } } +func __marshalMoreVideoFieldsParentTopicChildrenContent(v *MoreVideoFieldsParentTopicChildrenContent) ([]byte, error) { + + 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 *MoreVideoFieldsParentTopicChildrenVideo: + typename = "Video" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalMoreVideoFieldsParentTopicChildrenVideo + }{typename, premarshaled} + return json.Marshal(result) + case *MoreVideoFieldsParentTopicChildrenTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *MoreVideoFieldsParentTopicChildrenTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for MoreVideoFieldsParentTopicChildrenContent: "%T"`, v) + } +} + // MoreVideoFieldsParentTopicChildrenTopic includes the requested fields of the GraphQL type Topic. type MoreVideoFieldsParentTopicChildrenTopic struct { Typename *string `json:"__typename"` @@ -828,6 +1400,41 @@ func (v *MoreVideoFieldsParentTopicChildrenVideo) UnmarshalJSON(b []byte) error return nil } +type __premarshalMoreVideoFieldsParentTopicChildrenVideo struct { + Typename *string `json:"__typename"` + + Id testutil.ID `json:"id"` + + Name string `json:"name"` + + Url string `json:"url"` + + Duration int `json:"duration"` + + Thumbnail VideoFieldsThumbnail `json:"thumbnail"` +} + +func (v *MoreVideoFieldsParentTopicChildrenVideo) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *MoreVideoFieldsParentTopicChildrenVideo) __premarshalJSON() (*__premarshalMoreVideoFieldsParentTopicChildrenVideo, error) { + + var retval __premarshalMoreVideoFieldsParentTopicChildrenVideo + + 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 +} + // QueryFragment includes the GraphQL fields of Query requested by the fragment QueryFragment. // The GraphQL type's documentation follows. // @@ -861,6 +1468,65 @@ func (v *QueryFragment) UnmarshalJSON(b []byte) error { return nil } +type __premarshalQueryFragment struct { + RandomItem json.RawMessage `json:"randomItem"` + + RandomLeaf json.RawMessage `json:"randomLeaf"` + + OtherLeaf json.RawMessage `json:"otherLeaf"` +} + +func (v *QueryFragment) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *QueryFragment) __premarshalJSON() (*__premarshalQueryFragment, error) { + + var retval __premarshalQueryFragment + + { + + dst := &retval.RandomItem + src := v.InnerQueryFragment.RandomItem + var err error + *dst, err = __marshalInnerQueryFragmentRandomItemContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal QueryFragment.InnerQueryFragment.RandomItem: %w", err) + } + } + { + + dst := &retval.RandomLeaf + src := v.InnerQueryFragment.RandomLeaf + var err error + *dst, err = __marshalInnerQueryFragmentRandomLeafLeafContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal QueryFragment.InnerQueryFragment.RandomLeaf: %w", err) + } + } + { + + dst := &retval.OtherLeaf + src := v.InnerQueryFragment.OtherLeaf + var err error + *dst, err = __marshalInnerQueryFragmentOtherLeafLeafContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal QueryFragment.InnerQueryFragment.OtherLeaf: %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. @@ -897,6 +1563,38 @@ func (v *VideoFields) UnmarshalJSON(b []byte) error { return nil } +type __premarshalVideoFields struct { + Id testutil.ID `json:"id"` + + Name string `json:"name"` + + Url string `json:"url"` + + Duration int `json:"duration"` + + Thumbnail VideoFieldsThumbnail `json:"thumbnail"` +} + +func (v *VideoFields) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *VideoFields) __premarshalJSON() (*__premarshalVideoFields, error) { + + var retval __premarshalVideoFields + + retval.Id = v.Id + retval.Name = v.Name + retval.Url = v.Url + retval.Duration = v.Duration + retval.Thumbnail = v.Thumbnail + return &retval, nil +} + // VideoFieldsThumbnail includes the requested fields of the GraphQL type Thumbnail. type VideoFieldsThumbnail struct { Id testutil.ID `json:"id"` diff --git a/generate/testdata/snapshots/TestGenerate-CustomMarshal.graphql-CustomMarshal.graphql.go b/generate/testdata/snapshots/TestGenerate-CustomMarshal.graphql-CustomMarshal.graphql.go index bd7b1407..0e678610 100644 --- a/generate/testdata/snapshots/TestGenerate-CustomMarshal.graphql-CustomMarshal.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-CustomMarshal.graphql-CustomMarshal.graphql.go @@ -61,23 +61,97 @@ func (v *CustomMarshalUsersBornOnUser) UnmarshalJSON(b []byte) error { return nil } +type __premarshalCustomMarshalUsersBornOnUser struct { + Id testutil.ID `json:"id"` + + Birthdate json.RawMessage `json:"birthdate"` +} + +func (v *CustomMarshalUsersBornOnUser) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *CustomMarshalUsersBornOnUser) __premarshalJSON() (*__premarshalCustomMarshalUsersBornOnUser, error) { + + var retval __premarshalCustomMarshalUsersBornOnUser + + retval.Id = v.Id + { + + dst := &retval.Birthdate + src := v.Birthdate + var err error + *dst, err = testutil.MarshalDate( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal CustomMarshalUsersBornOnUser.Birthdate: %w", err) + } + } + return &retval, nil +} + // __CustomMarshalInput is used internally by genqlient type __CustomMarshalInput struct { Date time.Time `json:"-"` } -func (v *__CustomMarshalInput) MarshalJSON() ([]byte, error) { +func (v *__CustomMarshalInput) UnmarshalJSON(b []byte) error { - var fullObject struct { + if string(b) == "null" { + return nil + } + + var firstPass struct { *__CustomMarshalInput Date json.RawMessage `json:"date"` - graphql.NoMarshalJSON + graphql.NoUnmarshalJSON + } + firstPass.__CustomMarshalInput = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err } - fullObject.__CustomMarshalInput = v { + dst := &v.Date + src := firstPass.Date + if len(src) != 0 && string(src) != "null" { + err = testutil.UnmarshalDate( + src, dst) + if err != nil { + return fmt.Errorf( + "Unable to unmarshal __CustomMarshalInput.Date: %w", err) + } + } + } + return nil +} + +type __premarshal__CustomMarshalInput struct { + Date json.RawMessage `json:"date"` +} - dst := &fullObject.Date +func (v *__CustomMarshalInput) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *__CustomMarshalInput) __premarshalJSON() (*__premarshal__CustomMarshalInput, error) { + + var retval __premarshal__CustomMarshalInput + + { + + dst := &retval.Date src := v.Date var err error *dst, err = testutil.MarshalDate( @@ -87,8 +161,7 @@ func (v *__CustomMarshalInput) MarshalJSON() ([]byte, error) { "Unable to marshal __CustomMarshalInput.Date: %w", err) } } - - return json.Marshal(&fullObject) + return &retval, nil } func CustomMarshal( diff --git a/generate/testdata/snapshots/TestGenerate-CustomMarshalSlice.graphql-CustomMarshalSlice.graphql.go b/generate/testdata/snapshots/TestGenerate-CustomMarshalSlice.graphql-CustomMarshalSlice.graphql.go index dcc2c9e2..00d54af6 100644 --- a/generate/testdata/snapshots/TestGenerate-CustomMarshalSlice.graphql-CustomMarshalSlice.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-CustomMarshalSlice.graphql-CustomMarshalSlice.graphql.go @@ -23,19 +23,111 @@ type __CustomMarshalSliceInput struct { Datesssp [][][]*time.Time `json:"-"` } -func (v *__CustomMarshalSliceInput) MarshalJSON() ([]byte, error) { +func (v *__CustomMarshalSliceInput) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } - var fullObject struct { + var firstPass struct { *__CustomMarshalSliceInput Datesss [][][]json.RawMessage `json:"datesss"` Datesssp [][][]json.RawMessage `json:"datesssp"` - graphql.NoMarshalJSON + graphql.NoUnmarshalJSON + } + firstPass.__CustomMarshalSliceInput = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.Datesss + src := firstPass.Datesss + *dst = make( + [][][]time.Time, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + *dst = make( + [][]time.Time, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + *dst = make( + []time.Time, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + if len(src) != 0 && string(src) != "null" { + err = testutil.UnmarshalDate( + src, dst) + if err != nil { + return fmt.Errorf( + "Unable to unmarshal __CustomMarshalSliceInput.Datesss: %w", err) + } + } + } + } + } + } + + { + dst := &v.Datesssp + src := firstPass.Datesssp + *dst = make( + [][][]*time.Time, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + *dst = make( + [][]*time.Time, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + *dst = make( + []*time.Time, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + if len(src) != 0 && string(src) != "null" { + *dst = new(time.Time) + err = testutil.UnmarshalDate( + src, *dst) + if err != nil { + return fmt.Errorf( + "Unable to unmarshal __CustomMarshalSliceInput.Datesssp: %w", err) + } + } + } + } + } + } + return nil +} + +type __premarshal__CustomMarshalSliceInput struct { + Datesss [][][]json.RawMessage `json:"datesss"` + + Datesssp [][][]json.RawMessage `json:"datesssp"` +} + +func (v *__CustomMarshalSliceInput) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err } - fullObject.__CustomMarshalSliceInput = v + return json.Marshal(premarshaled) +} + +func (v *__CustomMarshalSliceInput) __premarshalJSON() (*__premarshal__CustomMarshalSliceInput, error) { + + var retval __premarshal__CustomMarshalSliceInput { - dst := &fullObject.Datesss + dst := &retval.Datesss src := v.Datesss *dst = make( [][][]json.RawMessage, @@ -65,7 +157,7 @@ func (v *__CustomMarshalSliceInput) MarshalJSON() ([]byte, error) { } { - dst := &fullObject.Datesssp + dst := &retval.Datesssp src := v.Datesssp *dst = make( [][][]json.RawMessage, @@ -95,8 +187,7 @@ func (v *__CustomMarshalSliceInput) MarshalJSON() ([]byte, error) { } } } - - return json.Marshal(&fullObject) + return &retval, nil } func CustomMarshalSlice( diff --git a/generate/testdata/snapshots/TestGenerate-InputObject.graphql-InputObject.graphql.go b/generate/testdata/snapshots/TestGenerate-InputObject.graphql-InputObject.graphql.go index b8bc0bf0..2fb29f5e 100644 --- a/generate/testdata/snapshots/TestGenerate-InputObject.graphql-InputObject.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-InputObject.graphql-InputObject.graphql.go @@ -61,18 +61,76 @@ type UserQueryInput struct { Birthdate time.Time `json:"-"` } -func (v *UserQueryInput) MarshalJSON() ([]byte, error) { +func (v *UserQueryInput) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } - var fullObject struct { + var firstPass struct { *UserQueryInput Birthdate json.RawMessage `json:"birthdate"` - graphql.NoMarshalJSON + graphql.NoUnmarshalJSON + } + firstPass.UserQueryInput = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.Birthdate + src := firstPass.Birthdate + if len(src) != 0 && string(src) != "null" { + err = testutil.UnmarshalDate( + src, dst) + if err != nil { + return fmt.Errorf( + "Unable to unmarshal UserQueryInput.Birthdate: %w", err) + } + } + } + return nil +} + +type __premarshalUserQueryInput struct { + Email string `json:"email"` + + Name string `json:"name"` + + Id testutil.ID `json:"id"` + + Role Role `json:"role"` + + Names []string `json:"names"` + + HasPokemon testutil.Pokemon `json:"hasPokemon"` + + Birthdate json.RawMessage `json:"birthdate"` +} + +func (v *UserQueryInput) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err } - fullObject.UserQueryInput = v + return json.Marshal(premarshaled) +} +func (v *UserQueryInput) __premarshalJSON() (*__premarshalUserQueryInput, error) { + + var retval __premarshalUserQueryInput + + retval.Email = v.Email + retval.Name = v.Name + retval.Id = v.Id + retval.Role = v.Role + retval.Names = v.Names + retval.HasPokemon = v.HasPokemon { - dst := &fullObject.Birthdate + dst := &retval.Birthdate src := v.Birthdate var err error *dst, err = testutil.MarshalDate( @@ -82,8 +140,7 @@ func (v *UserQueryInput) MarshalJSON() ([]byte, error) { "Unable to marshal UserQueryInput.Birthdate: %w", err) } } - - return json.Marshal(&fullObject) + return &retval, nil } // __InputObjectQueryInput is used internally by genqlient diff --git a/generate/testdata/snapshots/TestGenerate-InterfaceListField.graphql-InterfaceListField.graphql.go b/generate/testdata/snapshots/TestGenerate-InterfaceListField.graphql-InterfaceListField.graphql.go index 11f475bb..69f9c18c 100644 --- a/generate/testdata/snapshots/TestGenerate-InterfaceListField.graphql-InterfaceListField.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-InterfaceListField.graphql-InterfaceListField.graphql.go @@ -63,6 +63,49 @@ func (v *InterfaceListFieldRootTopic) UnmarshalJSON(b []byte) error { return nil } +type __premarshalInterfaceListFieldRootTopic struct { + Id testutil.ID `json:"id"` + + Name string `json:"name"` + + Children []json.RawMessage `json:"children"` +} + +func (v *InterfaceListFieldRootTopic) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *InterfaceListFieldRootTopic) __premarshalJSON() (*__premarshalInterfaceListFieldRootTopic, error) { + + var retval __premarshalInterfaceListFieldRootTopic + + retval.Id = v.Id + retval.Name = v.Name + { + + dst := &retval.Children + src := v.Children + *dst = make( + []json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + var err error + *dst, err = __marshalInterfaceListFieldRootTopicChildrenContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal InterfaceListFieldRootTopic.Children: %w", err) + } + } + } + return &retval, nil +} + // InterfaceListFieldRootTopicChildrenArticle includes the requested fields of the GraphQL type Article. type InterfaceListFieldRootTopicChildrenArticle struct { Typename string `json:"__typename"` @@ -161,6 +204,42 @@ func __unmarshalInterfaceListFieldRootTopicChildrenContent(b []byte, v *Interfac } } +func __marshalInterfaceListFieldRootTopicChildrenContent(v *InterfaceListFieldRootTopicChildrenContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *InterfaceListFieldRootTopicChildrenArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceListFieldRootTopicChildrenArticle + }{typename, v} + return json.Marshal(result) + case *InterfaceListFieldRootTopicChildrenVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceListFieldRootTopicChildrenVideo + }{typename, v} + return json.Marshal(result) + case *InterfaceListFieldRootTopicChildrenTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceListFieldRootTopicChildrenTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for InterfaceListFieldRootTopicChildrenContent: "%T"`, v) + } +} + // InterfaceListFieldRootTopicChildrenTopic includes the requested fields of the GraphQL type Topic. type InterfaceListFieldRootTopicChildrenTopic struct { Typename string `json:"__typename"` @@ -224,6 +303,49 @@ func (v *InterfaceListFieldWithPointerTopic) UnmarshalJSON(b []byte) error { return nil } +type __premarshalInterfaceListFieldWithPointerTopic struct { + Id testutil.ID `json:"id"` + + Name string `json:"name"` + + Children []json.RawMessage `json:"children"` +} + +func (v *InterfaceListFieldWithPointerTopic) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *InterfaceListFieldWithPointerTopic) __premarshalJSON() (*__premarshalInterfaceListFieldWithPointerTopic, error) { + + var retval __premarshalInterfaceListFieldWithPointerTopic + + retval.Id = v.Id + retval.Name = v.Name + { + + dst := &retval.Children + src := v.Children + *dst = make( + []json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + var err error + *dst, err = __marshalInterfaceListFieldWithPointerTopicChildrenContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal InterfaceListFieldWithPointerTopic.Children: %w", err) + } + } + } + return &retval, nil +} + // InterfaceListFieldWithPointerTopicChildrenArticle includes the requested fields of the GraphQL type Article. type InterfaceListFieldWithPointerTopicChildrenArticle struct { Typename string `json:"__typename"` @@ -322,6 +444,42 @@ func __unmarshalInterfaceListFieldWithPointerTopicChildrenContent(b []byte, v *I } } +func __marshalInterfaceListFieldWithPointerTopicChildrenContent(v *InterfaceListFieldWithPointerTopicChildrenContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *InterfaceListFieldWithPointerTopicChildrenArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceListFieldWithPointerTopicChildrenArticle + }{typename, v} + return json.Marshal(result) + case *InterfaceListFieldWithPointerTopicChildrenVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceListFieldWithPointerTopicChildrenVideo + }{typename, v} + return json.Marshal(result) + case *InterfaceListFieldWithPointerTopicChildrenTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceListFieldWithPointerTopicChildrenTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for InterfaceListFieldWithPointerTopicChildrenContent: "%T"`, v) + } +} + // InterfaceListFieldWithPointerTopicChildrenTopic includes the requested fields of the GraphQL type Topic. type InterfaceListFieldWithPointerTopicChildrenTopic struct { Typename string `json:"__typename"` diff --git a/generate/testdata/snapshots/TestGenerate-InterfaceListOfListsOfListsField.graphql-InterfaceListOfListsOfListsField.graphql.go b/generate/testdata/snapshots/TestGenerate-InterfaceListOfListsOfListsField.graphql-InterfaceListOfListsOfListsField.graphql.go index f5b5092b..2b7be3ed 100644 --- a/generate/testdata/snapshots/TestGenerate-InterfaceListOfListsOfListsField.graphql-InterfaceListOfListsOfListsField.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-InterfaceListOfListsOfListsField.graphql-InterfaceListOfListsOfListsField.graphql.go @@ -118,6 +118,42 @@ func __unmarshalInterfaceListOfListOfListsFieldListOfListsOfListsOfContent(b []b } } +func __marshalInterfaceListOfListOfListsFieldListOfListsOfListsOfContent(v *InterfaceListOfListOfListsFieldListOfListsOfListsOfContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *InterfaceListOfListOfListsFieldListOfListsOfListsOfContentArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceListOfListOfListsFieldListOfListsOfListsOfContentArticle + }{typename, v} + return json.Marshal(result) + case *InterfaceListOfListOfListsFieldListOfListsOfListsOfContentVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceListOfListOfListsFieldListOfListsOfListsOfContentVideo + }{typename, v} + return json.Marshal(result) + case *InterfaceListOfListOfListsFieldListOfListsOfListsOfContentTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceListOfListOfListsFieldListOfListsOfListsOfContentTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for InterfaceListOfListOfListsFieldListOfListsOfListsOfContent: "%T"`, v) + } +} + // InterfaceListOfListOfListsFieldListOfListsOfListsOfContentArticle includes the requested fields of the GraphQL type Article. type InterfaceListOfListOfListsFieldListOfListsOfListsOfContentArticle struct { Typename string `json:"__typename"` @@ -232,6 +268,89 @@ func (v *InterfaceListOfListOfListsFieldResponse) UnmarshalJSON(b []byte) error return nil } +type __premarshalInterfaceListOfListOfListsFieldResponse struct { + ListOfListsOfListsOfContent [][][]json.RawMessage `json:"listOfListsOfListsOfContent"` + + WithPointer [][][]json.RawMessage `json:"withPointer"` +} + +func (v *InterfaceListOfListOfListsFieldResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *InterfaceListOfListOfListsFieldResponse) __premarshalJSON() (*__premarshalInterfaceListOfListOfListsFieldResponse, error) { + + var retval __premarshalInterfaceListOfListOfListsFieldResponse + + { + + dst := &retval.ListOfListsOfListsOfContent + src := v.ListOfListsOfListsOfContent + *dst = make( + [][][]json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + *dst = make( + [][]json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + *dst = make( + []json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + var err error + *dst, err = __marshalInterfaceListOfListOfListsFieldListOfListsOfListsOfContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal InterfaceListOfListOfListsFieldResponse.ListOfListsOfListsOfContent: %w", err) + } + } + } + } + } + { + + dst := &retval.WithPointer + src := v.WithPointer + *dst = make( + [][][]json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + *dst = make( + [][]json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + *dst = make( + []json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + if src != nil { + var err error + *dst, err = __marshalInterfaceListOfListOfListsFieldWithPointerContent( + src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal InterfaceListOfListOfListsFieldResponse.WithPointer: %w", err) + } + } + } + } + } + } + return &retval, nil +} + // InterfaceListOfListOfListsFieldWithPointerArticle includes the requested fields of the GraphQL type Article. type InterfaceListOfListOfListsFieldWithPointerArticle struct { Typename string `json:"__typename"` @@ -330,6 +449,42 @@ func __unmarshalInterfaceListOfListOfListsFieldWithPointerContent(b []byte, v *I } } +func __marshalInterfaceListOfListOfListsFieldWithPointerContent(v *InterfaceListOfListOfListsFieldWithPointerContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *InterfaceListOfListOfListsFieldWithPointerArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceListOfListOfListsFieldWithPointerArticle + }{typename, v} + return json.Marshal(result) + case *InterfaceListOfListOfListsFieldWithPointerVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceListOfListOfListsFieldWithPointerVideo + }{typename, v} + return json.Marshal(result) + case *InterfaceListOfListOfListsFieldWithPointerTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceListOfListOfListsFieldWithPointerTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for InterfaceListOfListOfListsFieldWithPointerContent: "%T"`, v) + } +} + // InterfaceListOfListOfListsFieldWithPointerTopic includes the requested fields of the GraphQL type Topic. type InterfaceListOfListOfListsFieldWithPointerTopic struct { Typename string `json:"__typename"` diff --git a/generate/testdata/snapshots/TestGenerate-InterfaceNesting.graphql-InterfaceNesting.graphql.go b/generate/testdata/snapshots/TestGenerate-InterfaceNesting.graphql-InterfaceNesting.graphql.go index 5a3f66e9..671e0cb8 100644 --- a/generate/testdata/snapshots/TestGenerate-InterfaceNesting.graphql-InterfaceNesting.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-InterfaceNesting.graphql-InterfaceNesting.graphql.go @@ -61,6 +61,46 @@ func (v *InterfaceNestingRootTopic) UnmarshalJSON(b []byte) error { return nil } +type __premarshalInterfaceNestingRootTopic struct { + Id testutil.ID `json:"id"` + + Children []json.RawMessage `json:"children"` +} + +func (v *InterfaceNestingRootTopic) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *InterfaceNestingRootTopic) __premarshalJSON() (*__premarshalInterfaceNestingRootTopic, error) { + + var retval __premarshalInterfaceNestingRootTopic + + retval.Id = v.Id + { + + dst := &retval.Children + src := v.Children + *dst = make( + []json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + var err error + *dst, err = __marshalInterfaceNestingRootTopicChildrenContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal InterfaceNestingRootTopic.Children: %w", err) + } + } + } + return &retval, nil +} + // InterfaceNestingRootTopicChildrenArticle includes the requested fields of the GraphQL type Article. type InterfaceNestingRootTopicChildrenArticle struct { Typename string `json:"__typename"` @@ -165,6 +205,42 @@ func __unmarshalInterfaceNestingRootTopicChildrenContent(b []byte, v *InterfaceN } } +func __marshalInterfaceNestingRootTopicChildrenContent(v *InterfaceNestingRootTopicChildrenContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *InterfaceNestingRootTopicChildrenArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceNestingRootTopicChildrenArticle + }{typename, v} + return json.Marshal(result) + case *InterfaceNestingRootTopicChildrenVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceNestingRootTopicChildrenVideo + }{typename, v} + return json.Marshal(result) + case *InterfaceNestingRootTopicChildrenTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceNestingRootTopicChildrenTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for InterfaceNestingRootTopicChildrenContent: "%T"`, v) + } +} + // InterfaceNestingRootTopicChildrenContentParentTopic includes the requested fields of the GraphQL type Topic. type InterfaceNestingRootTopicChildrenContentParentTopic struct { // ID is documented in the Content interface. @@ -211,6 +287,46 @@ func (v *InterfaceNestingRootTopicChildrenContentParentTopic) UnmarshalJSON(b [] return nil } +type __premarshalInterfaceNestingRootTopicChildrenContentParentTopic struct { + Id testutil.ID `json:"id"` + + Children []json.RawMessage `json:"children"` +} + +func (v *InterfaceNestingRootTopicChildrenContentParentTopic) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *InterfaceNestingRootTopicChildrenContentParentTopic) __premarshalJSON() (*__premarshalInterfaceNestingRootTopicChildrenContentParentTopic, error) { + + var retval __premarshalInterfaceNestingRootTopicChildrenContentParentTopic + + retval.Id = v.Id + { + + dst := &retval.Children + src := v.Children + *dst = make( + []json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + var err error + *dst, err = __marshalInterfaceNestingRootTopicChildrenContentParentTopicChildrenContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal InterfaceNestingRootTopicChildrenContentParentTopic.Children: %w", err) + } + } + } + return &retval, nil +} + // InterfaceNestingRootTopicChildrenContentParentTopicChildrenArticle includes the requested fields of the GraphQL type Article. type InterfaceNestingRootTopicChildrenContentParentTopicChildrenArticle struct { Typename string `json:"__typename"` @@ -309,6 +425,42 @@ func __unmarshalInterfaceNestingRootTopicChildrenContentParentTopicChildrenConte } } +func __marshalInterfaceNestingRootTopicChildrenContentParentTopicChildrenContent(v *InterfaceNestingRootTopicChildrenContentParentTopicChildrenContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *InterfaceNestingRootTopicChildrenContentParentTopicChildrenArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceNestingRootTopicChildrenContentParentTopicChildrenArticle + }{typename, v} + return json.Marshal(result) + case *InterfaceNestingRootTopicChildrenContentParentTopicChildrenVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceNestingRootTopicChildrenContentParentTopicChildrenVideo + }{typename, v} + return json.Marshal(result) + case *InterfaceNestingRootTopicChildrenContentParentTopicChildrenTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceNestingRootTopicChildrenContentParentTopicChildrenTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for InterfaceNestingRootTopicChildrenContentParentTopicChildrenContent: "%T"`, v) + } +} + // InterfaceNestingRootTopicChildrenContentParentTopicChildrenTopic includes the requested fields of the GraphQL type Topic. type InterfaceNestingRootTopicChildrenContentParentTopicChildrenTopic struct { Typename string `json:"__typename"` diff --git a/generate/testdata/snapshots/TestGenerate-InterfaceNoFragments.graphql-InterfaceNoFragments.graphql.go b/generate/testdata/snapshots/TestGenerate-InterfaceNoFragments.graphql-InterfaceNoFragments.graphql.go index 74b72c5f..9a434a49 100644 --- a/generate/testdata/snapshots/TestGenerate-InterfaceNoFragments.graphql-InterfaceNoFragments.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-InterfaceNoFragments.graphql-InterfaceNoFragments.graphql.go @@ -108,6 +108,42 @@ func __unmarshalInterfaceNoFragmentsQueryRandomItemContent(b []byte, v *Interfac } } +func __marshalInterfaceNoFragmentsQueryRandomItemContent(v *InterfaceNoFragmentsQueryRandomItemContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *InterfaceNoFragmentsQueryRandomItemArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceNoFragmentsQueryRandomItemArticle + }{typename, v} + return json.Marshal(result) + case *InterfaceNoFragmentsQueryRandomItemVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceNoFragmentsQueryRandomItemVideo + }{typename, v} + return json.Marshal(result) + case *InterfaceNoFragmentsQueryRandomItemTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceNoFragmentsQueryRandomItemTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for InterfaceNoFragmentsQueryRandomItemContent: "%T"`, v) + } +} + // InterfaceNoFragmentsQueryRandomItemTopic includes the requested fields of the GraphQL type Topic. type InterfaceNoFragmentsQueryRandomItemTopic struct { Typename string `json:"__typename"` @@ -228,6 +264,42 @@ func __unmarshalInterfaceNoFragmentsQueryRandomItemWithTypeNameContent(b []byte, } } +func __marshalInterfaceNoFragmentsQueryRandomItemWithTypeNameContent(v *InterfaceNoFragmentsQueryRandomItemWithTypeNameContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *InterfaceNoFragmentsQueryRandomItemWithTypeNameArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceNoFragmentsQueryRandomItemWithTypeNameArticle + }{typename, v} + return json.Marshal(result) + case *InterfaceNoFragmentsQueryRandomItemWithTypeNameVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceNoFragmentsQueryRandomItemWithTypeNameVideo + }{typename, v} + return json.Marshal(result) + case *InterfaceNoFragmentsQueryRandomItemWithTypeNameTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceNoFragmentsQueryRandomItemWithTypeNameTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for InterfaceNoFragmentsQueryRandomItemWithTypeNameContent: "%T"`, v) + } +} + // InterfaceNoFragmentsQueryRandomItemWithTypeNameTopic includes the requested fields of the GraphQL type Topic. type InterfaceNoFragmentsQueryRandomItemWithTypeNameTopic struct { Typename string `json:"__typename"` @@ -314,6 +386,70 @@ func (v *InterfaceNoFragmentsQueryResponse) UnmarshalJSON(b []byte) error { 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. @@ -419,6 +555,42 @@ func __unmarshalInterfaceNoFragmentsQueryWithPointerContent(b []byte, v *Interfa } } +func __marshalInterfaceNoFragmentsQueryWithPointerContent(v *InterfaceNoFragmentsQueryWithPointerContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *InterfaceNoFragmentsQueryWithPointerArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceNoFragmentsQueryWithPointerArticle + }{typename, v} + return json.Marshal(result) + case *InterfaceNoFragmentsQueryWithPointerVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceNoFragmentsQueryWithPointerVideo + }{typename, v} + return json.Marshal(result) + case *InterfaceNoFragmentsQueryWithPointerTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *InterfaceNoFragmentsQueryWithPointerTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for InterfaceNoFragmentsQueryWithPointerContent: "%T"`, v) + } +} + // InterfaceNoFragmentsQueryWithPointerTopic includes the requested fields of the GraphQL type Topic. type InterfaceNoFragmentsQueryWithPointerTopic struct { Typename string `json:"__typename"` diff --git a/generate/testdata/snapshots/TestGenerate-MultipleDirectives.graphql-MultipleDirectives.graphql.go b/generate/testdata/snapshots/TestGenerate-MultipleDirectives.graphql-MultipleDirectives.graphql.go index 560cb610..8aeba385 100644 --- a/generate/testdata/snapshots/TestGenerate-MultipleDirectives.graphql-MultipleDirectives.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-MultipleDirectives.graphql-MultipleDirectives.graphql.go @@ -27,18 +27,77 @@ type MyInput struct { Birthdate *time.Time `json:"-"` } -func (v *MyInput) MarshalJSON() ([]byte, error) { +func (v *MyInput) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } - var fullObject struct { + var firstPass struct { *MyInput Birthdate json.RawMessage `json:"birthdate"` - graphql.NoMarshalJSON + graphql.NoUnmarshalJSON + } + firstPass.MyInput = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.Birthdate + src := firstPass.Birthdate + if len(src) != 0 && string(src) != "null" { + *dst = new(time.Time) + err = testutil.UnmarshalDate( + src, *dst) + if err != nil { + return fmt.Errorf( + "Unable to unmarshal MyInput.Birthdate: %w", err) + } + } + } + return nil +} + +type __premarshalMyInput struct { + Email *string `json:"email"` + + Name *string `json:"name"` + + Id *testutil.ID `json:"id"` + + Role *Role `json:"role"` + + Names []*string `json:"names"` + + HasPokemon *testutil.Pokemon `json:"hasPokemon"` + + Birthdate json.RawMessage `json:"birthdate"` +} + +func (v *MyInput) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err } - fullObject.MyInput = v + return json.Marshal(premarshaled) +} +func (v *MyInput) __premarshalJSON() (*__premarshalMyInput, error) { + + var retval __premarshalMyInput + + retval.Email = v.Email + retval.Name = v.Name + retval.Id = v.Id + retval.Role = v.Role + retval.Names = v.Names + retval.HasPokemon = v.HasPokemon { - dst := &fullObject.Birthdate + dst := &retval.Birthdate src := v.Birthdate if src != nil { var err error @@ -50,8 +109,7 @@ func (v *MyInput) MarshalJSON() ([]byte, error) { } } } - - return json.Marshal(&fullObject) + return &retval, nil } // MyMultipleDirectivesResponse is returned by MultipleDirectives on success. @@ -116,18 +174,77 @@ type UserQueryInput struct { Birthdate *time.Time `json:"-"` } -func (v *UserQueryInput) MarshalJSON() ([]byte, error) { +func (v *UserQueryInput) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } - var fullObject struct { + var firstPass struct { *UserQueryInput Birthdate json.RawMessage `json:"birthdate"` - graphql.NoMarshalJSON + graphql.NoUnmarshalJSON + } + firstPass.UserQueryInput = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.Birthdate + src := firstPass.Birthdate + if len(src) != 0 && string(src) != "null" { + *dst = new(time.Time) + err = testutil.UnmarshalDate( + src, *dst) + if err != nil { + return fmt.Errorf( + "Unable to unmarshal UserQueryInput.Birthdate: %w", err) + } + } + } + return nil +} + +type __premarshalUserQueryInput struct { + Email *string `json:"email"` + + Name *string `json:"name"` + + Id *testutil.ID `json:"id"` + + Role *Role `json:"role"` + + Names []*string `json:"names"` + + HasPokemon *testutil.Pokemon `json:"hasPokemon"` + + Birthdate json.RawMessage `json:"birthdate"` +} + +func (v *UserQueryInput) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err } - fullObject.UserQueryInput = v + return json.Marshal(premarshaled) +} +func (v *UserQueryInput) __premarshalJSON() (*__premarshalUserQueryInput, error) { + + var retval __premarshalUserQueryInput + + retval.Email = v.Email + retval.Name = v.Name + retval.Id = v.Id + retval.Role = v.Role + retval.Names = v.Names + retval.HasPokemon = v.HasPokemon { - dst := &fullObject.Birthdate + dst := &retval.Birthdate src := v.Birthdate if src != nil { var err error @@ -139,8 +256,7 @@ func (v *UserQueryInput) MarshalJSON() ([]byte, error) { } } } - - return json.Marshal(&fullObject) + return &retval, nil } // __MultipleDirectivesInput is used internally by genqlient diff --git a/generate/testdata/snapshots/TestGenerate-Omitempty.graphql-Omitempty.graphql.go b/generate/testdata/snapshots/TestGenerate-Omitempty.graphql-Omitempty.graphql.go index 01209584..4ecb7ad4 100644 --- a/generate/testdata/snapshots/TestGenerate-Omitempty.graphql-Omitempty.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-Omitempty.graphql-Omitempty.graphql.go @@ -75,18 +75,76 @@ type UserQueryInput struct { Birthdate time.Time `json:"-"` } -func (v *UserQueryInput) MarshalJSON() ([]byte, error) { +func (v *UserQueryInput) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } - var fullObject struct { + var firstPass struct { *UserQueryInput Birthdate json.RawMessage `json:"birthdate"` - graphql.NoMarshalJSON + graphql.NoUnmarshalJSON + } + firstPass.UserQueryInput = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.Birthdate + src := firstPass.Birthdate + if len(src) != 0 && string(src) != "null" { + err = testutil.UnmarshalDate( + src, dst) + if err != nil { + return fmt.Errorf( + "Unable to unmarshal UserQueryInput.Birthdate: %w", err) + } + } + } + return nil +} + +type __premarshalUserQueryInput struct { + Email string `json:"email"` + + Name string `json:"name"` + + Id testutil.ID `json:"id"` + + Role Role `json:"role"` + + Names []string `json:"names"` + + HasPokemon testutil.Pokemon `json:"hasPokemon"` + + Birthdate json.RawMessage `json:"birthdate"` +} + +func (v *UserQueryInput) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err } - fullObject.UserQueryInput = v + return json.Marshal(premarshaled) +} +func (v *UserQueryInput) __premarshalJSON() (*__premarshalUserQueryInput, error) { + + var retval __premarshalUserQueryInput + + retval.Email = v.Email + retval.Name = v.Name + retval.Id = v.Id + retval.Role = v.Role + retval.Names = v.Names + retval.HasPokemon = v.HasPokemon { - dst := &fullObject.Birthdate + dst := &retval.Birthdate src := v.Birthdate var err error *dst, err = testutil.MarshalDate( @@ -96,8 +154,7 @@ func (v *UserQueryInput) MarshalJSON() ([]byte, error) { "Unable to marshal UserQueryInput.Birthdate: %w", err) } } - - return json.Marshal(&fullObject) + return &retval, nil } // __OmitEmptyQueryInput is used internally by genqlient diff --git a/generate/testdata/snapshots/TestGenerate-Pointers.graphql-Pointers.graphql.go b/generate/testdata/snapshots/TestGenerate-Pointers.graphql-Pointers.graphql.go index 0c0cdaea..8243b2d3 100644 --- a/generate/testdata/snapshots/TestGenerate-Pointers.graphql-Pointers.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-Pointers.graphql-Pointers.graphql.go @@ -82,18 +82,77 @@ type UserQueryInput struct { Birthdate *time.Time `json:"-"` } -func (v *UserQueryInput) MarshalJSON() ([]byte, error) { +func (v *UserQueryInput) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } - var fullObject struct { + var firstPass struct { *UserQueryInput Birthdate json.RawMessage `json:"birthdate"` - graphql.NoMarshalJSON + graphql.NoUnmarshalJSON + } + firstPass.UserQueryInput = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.Birthdate + src := firstPass.Birthdate + if len(src) != 0 && string(src) != "null" { + *dst = new(time.Time) + err = testutil.UnmarshalDate( + src, *dst) + if err != nil { + return fmt.Errorf( + "Unable to unmarshal UserQueryInput.Birthdate: %w", err) + } + } + } + return nil +} + +type __premarshalUserQueryInput struct { + Email *string `json:"email"` + + Name *string `json:"name"` + + Id *testutil.ID `json:"id"` + + Role *Role `json:"role"` + + Names []*string `json:"names"` + + HasPokemon *testutil.Pokemon `json:"hasPokemon"` + + Birthdate json.RawMessage `json:"birthdate"` +} + +func (v *UserQueryInput) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err } - fullObject.UserQueryInput = v + return json.Marshal(premarshaled) +} +func (v *UserQueryInput) __premarshalJSON() (*__premarshalUserQueryInput, error) { + + var retval __premarshalUserQueryInput + + retval.Email = v.Email + retval.Name = v.Name + retval.Id = v.Id + retval.Role = v.Role + retval.Names = v.Names + retval.HasPokemon = v.HasPokemon { - dst := &fullObject.Birthdate + dst := &retval.Birthdate src := v.Birthdate if src != nil { var err error @@ -105,8 +164,7 @@ func (v *UserQueryInput) MarshalJSON() ([]byte, error) { } } } - - return json.Marshal(&fullObject) + return &retval, nil } // __PointersQueryInput is used internally by genqlient diff --git a/generate/testdata/snapshots/TestGenerate-PointersInline.graphql-PointersInline.graphql.go b/generate/testdata/snapshots/TestGenerate-PointersInline.graphql-PointersInline.graphql.go index bca3927c..c27ef83b 100644 --- a/generate/testdata/snapshots/TestGenerate-PointersInline.graphql-PointersInline.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-PointersInline.graphql-PointersInline.graphql.go @@ -82,18 +82,76 @@ type UserQueryInput struct { Birthdate time.Time `json:"-"` } -func (v *UserQueryInput) MarshalJSON() ([]byte, error) { +func (v *UserQueryInput) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } - var fullObject struct { + var firstPass struct { *UserQueryInput Birthdate json.RawMessage `json:"birthdate"` - graphql.NoMarshalJSON + graphql.NoUnmarshalJSON + } + firstPass.UserQueryInput = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.Birthdate + src := firstPass.Birthdate + if len(src) != 0 && string(src) != "null" { + err = testutil.UnmarshalDate( + src, dst) + if err != nil { + return fmt.Errorf( + "Unable to unmarshal UserQueryInput.Birthdate: %w", err) + } + } + } + return nil +} + +type __premarshalUserQueryInput struct { + Email string `json:"email"` + + Name string `json:"name"` + + Id testutil.ID `json:"id"` + + Role Role `json:"role"` + + Names []string `json:"names"` + + HasPokemon testutil.Pokemon `json:"hasPokemon"` + + Birthdate json.RawMessage `json:"birthdate"` +} + +func (v *UserQueryInput) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err } - fullObject.UserQueryInput = v + return json.Marshal(premarshaled) +} +func (v *UserQueryInput) __premarshalJSON() (*__premarshalUserQueryInput, error) { + + var retval __premarshalUserQueryInput + + retval.Email = v.Email + retval.Name = v.Name + retval.Id = v.Id + retval.Role = v.Role + retval.Names = v.Names + retval.HasPokemon = v.HasPokemon { - dst := &fullObject.Birthdate + dst := &retval.Birthdate src := v.Birthdate var err error *dst, err = testutil.MarshalDate( @@ -103,8 +161,7 @@ func (v *UserQueryInput) MarshalJSON() ([]byte, error) { "Unable to marshal UserQueryInput.Birthdate: %w", err) } } - - return json.Marshal(&fullObject) + return &retval, nil } // __PointersQueryInput is used internally by genqlient diff --git a/generate/testdata/snapshots/TestGenerate-SimpleInlineFragment.graphql-SimpleInlineFragment.graphql.go b/generate/testdata/snapshots/TestGenerate-SimpleInlineFragment.graphql-SimpleInlineFragment.graphql.go index 673f55ed..24b4050f 100644 --- a/generate/testdata/snapshots/TestGenerate-SimpleInlineFragment.graphql-SimpleInlineFragment.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-SimpleInlineFragment.graphql-SimpleInlineFragment.graphql.go @@ -109,6 +109,42 @@ func __unmarshalSimpleInlineFragmentRandomItemContent(b []byte, v *SimpleInlineF } } +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 *SimpleInlineFragmentRandomItemTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *SimpleInlineFragmentRandomItemTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for SimpleInlineFragmentRandomItemContent: "%T"`, v) + } +} + // SimpleInlineFragmentRandomItemTopic includes the requested fields of the GraphQL type Topic. type SimpleInlineFragmentRandomItemTopic struct { Typename string `json:"__typename"` @@ -164,6 +200,37 @@ func (v *SimpleInlineFragmentResponse) UnmarshalJSON(b []byte) error { 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 +} + func SimpleInlineFragment( client graphql.Client, ) (*SimpleInlineFragmentResponse, error) { diff --git a/generate/testdata/snapshots/TestGenerate-SimpleNamedFragment.graphql-SimpleNamedFragment.graphql.go b/generate/testdata/snapshots/TestGenerate-SimpleNamedFragment.graphql-SimpleNamedFragment.graphql.go index b8d4de3f..a27406a8 100644 --- a/generate/testdata/snapshots/TestGenerate-SimpleNamedFragment.graphql-SimpleNamedFragment.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-SimpleNamedFragment.graphql-SimpleNamedFragment.graphql.go @@ -108,6 +108,46 @@ func __unmarshalSimpleNamedFragmentRandomItemContent(b []byte, v *SimpleNamedFra } } +func __marshalSimpleNamedFragmentRandomItemContent(v *SimpleNamedFragmentRandomItemContent) ([]byte, error) { + + 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 *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 *SimpleNamedFragmentRandomItemTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *SimpleNamedFragmentRandomItemTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for SimpleNamedFragmentRandomItemContent: "%T"`, v) + } +} + // SimpleNamedFragmentRandomItemTopic includes the requested fields of the GraphQL type Topic. type SimpleNamedFragmentRandomItemTopic struct { Typename string `json:"__typename"` @@ -150,6 +190,41 @@ func (v *SimpleNamedFragmentRandomItemVideo) UnmarshalJSON(b []byte) error { return nil } +type __premarshalSimpleNamedFragmentRandomItemVideo struct { + Typename string `json:"__typename"` + + Id testutil.ID `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 +} + // SimpleNamedFragmentRandomLeafArticle includes the requested fields of the GraphQL type Article. type SimpleNamedFragmentRandomLeafArticle struct { Typename string `json:"__typename"` @@ -210,6 +285,38 @@ func __unmarshalSimpleNamedFragmentRandomLeafLeafContent(b []byte, v *SimpleName } } +func __marshalSimpleNamedFragmentRandomLeafLeafContent(v *SimpleNamedFragmentRandomLeafLeafContent) ([]byte, error) { + + 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" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalSimpleNamedFragmentRandomLeafVideo + }{typename, premarshaled} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for SimpleNamedFragmentRandomLeafLeafContent: "%T"`, v) + } +} + // SimpleNamedFragmentRandomLeafVideo includes the requested fields of the GraphQL type Video. type SimpleNamedFragmentRandomLeafVideo struct { Typename string `json:"__typename"` @@ -241,6 +348,41 @@ func (v *SimpleNamedFragmentRandomLeafVideo) UnmarshalJSON(b []byte) error { return nil } +type __premarshalSimpleNamedFragmentRandomLeafVideo struct { + Typename string `json:"__typename"` + + Id testutil.ID `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:"-"` @@ -294,6 +436,51 @@ func (v *SimpleNamedFragmentResponse) UnmarshalJSON(b []byte) error { 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. diff --git a/generate/testdata/snapshots/TestGenerate-StructOption.graphql-StructOption.graphql.go b/generate/testdata/snapshots/TestGenerate-StructOption.graphql-StructOption.graphql.go index 29d8b47c..1adca9ce 100644 --- a/generate/testdata/snapshots/TestGenerate-StructOption.graphql-StructOption.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-StructOption.graphql-StructOption.graphql.go @@ -99,6 +99,49 @@ func (v *StructOptionRootTopicChildrenContentParentTopic) UnmarshalJSON(b []byte return nil } +type __premarshalStructOptionRootTopicChildrenContentParentTopic struct { + Id testutil.ID `json:"id"` + + Children []StructOptionRootTopicChildrenContentParentTopicChildrenContent `json:"children"` + + InterfaceChildren []json.RawMessage `json:"interfaceChildren"` +} + +func (v *StructOptionRootTopicChildrenContentParentTopic) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *StructOptionRootTopicChildrenContentParentTopic) __premarshalJSON() (*__premarshalStructOptionRootTopicChildrenContentParentTopic, error) { + + var retval __premarshalStructOptionRootTopicChildrenContentParentTopic + + retval.Id = v.Id + retval.Children = v.Children + { + + dst := &retval.InterfaceChildren + src := v.InterfaceChildren + *dst = make( + []json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + var err error + *dst, err = __marshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal StructOptionRootTopicChildrenContentParentTopic.InterfaceChildren: %w", err) + } + } + } + return &retval, nil +} + // StructOptionRootTopicChildrenContentParentTopicChildrenContent includes the requested fields of the GraphQL type Content. // The GraphQL type's documentation follows. // @@ -207,6 +250,46 @@ func __unmarshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildren } } +func __marshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent(v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent) ([]byte, error) { + + 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 *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenVideo: + typename = "Video" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenVideo + }{typename, premarshaled} + return json.Marshal(result) + case *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenContent: "%T"`, v) + } +} + // StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic includes the requested fields of the GraphQL type Topic. type StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenTopic struct { Typename string `json:"__typename"` @@ -247,6 +330,32 @@ func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenVideo) return nil } +type __premarshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenVideo struct { + Typename string `json:"__typename"` + + Id testutil.ID `json:"id"` + + Duration int `json:"duration"` +} + +func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenVideo) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *StructOptionRootTopicChildrenContentParentTopicInterfaceChildrenVideo) __premarshalJSON() (*__premarshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenVideo, error) { + + var retval __premarshalStructOptionRootTopicChildrenContentParentTopicInterfaceChildrenVideo + + retval.Typename = v.Typename + retval.Id = v.Id + retval.Duration = v.VideoFields.Duration + return &retval, nil +} + // StructOptionUser includes the requested fields of the GraphQL type User. // The GraphQL type's documentation follows. // diff --git a/generate/testdata/snapshots/TestGenerate-TypeNames.graphql-TypeNames.graphql.go b/generate/testdata/snapshots/TestGenerate-TypeNames.graphql-TypeNames.graphql.go index cf8687bd..eb696cb9 100644 --- a/generate/testdata/snapshots/TestGenerate-TypeNames.graphql-TypeNames.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-TypeNames.graphql-TypeNames.graphql.go @@ -97,6 +97,42 @@ func __unmarshalItem(b []byte, v *Item) error { } } +func __marshalItem(v *Item) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *ItemArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *ItemArticle + }{typename, v} + return json.Marshal(result) + case *ItemVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *ItemVideo + }{typename, v} + return json.Marshal(result) + case *ItemTopic: + typename = "Topic" + + result := struct { + TypeName string `json:"__typename"` + *ItemTopic + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for Item: "%T"`, v) + } +} + // ItemArticle includes the requested fields of the GraphQL type Article. type ItemArticle struct { Typename string `json:"__typename"` @@ -165,6 +201,43 @@ func (v *Resp) UnmarshalJSON(b []byte) error { return nil } +type __premarshalResp struct { + User User `json:"user"` + + RandomItem json.RawMessage `json:"randomItem"` + + Users []User `json:"users"` +} + +func (v *Resp) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *Resp) __premarshalJSON() (*__premarshalResp, error) { + + var retval __premarshalResp + + retval.User = v.User + { + + dst := &retval.RandomItem + src := v.RandomItem + var err error + *dst, err = __marshalItem( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal Resp.RandomItem: %w", err) + } + } + retval.Users = v.Users + return &retval, nil +} + // User includes the requested fields of the GraphQL type User. // The GraphQL type's documentation follows. // diff --git a/generate/testdata/snapshots/TestGenerate-UnionNoFragments.graphql-UnionNoFragments.graphql.go b/generate/testdata/snapshots/TestGenerate-UnionNoFragments.graphql-UnionNoFragments.graphql.go index 8f6d50ed..bc7dc848 100644 --- a/generate/testdata/snapshots/TestGenerate-UnionNoFragments.graphql-UnionNoFragments.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-UnionNoFragments.graphql-UnionNoFragments.graphql.go @@ -69,6 +69,34 @@ func __unmarshalUnionNoFragmentsQueryRandomLeafLeafContent(b []byte, v *UnionNoF } } +func __marshalUnionNoFragmentsQueryRandomLeafLeafContent(v *UnionNoFragmentsQueryRandomLeafLeafContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *UnionNoFragmentsQueryRandomLeafArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *UnionNoFragmentsQueryRandomLeafArticle + }{typename, v} + return json.Marshal(result) + case *UnionNoFragmentsQueryRandomLeafVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *UnionNoFragmentsQueryRandomLeafVideo + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for UnionNoFragmentsQueryRandomLeafLeafContent: "%T"`, v) + } +} + // UnionNoFragmentsQueryRandomLeafVideo includes the requested fields of the GraphQL type Video. type UnionNoFragmentsQueryRandomLeafVideo struct { Typename string `json:"__typename"` @@ -112,6 +140,37 @@ func (v *UnionNoFragmentsQueryResponse) UnmarshalJSON(b []byte) error { return nil } +type __premarshalUnionNoFragmentsQueryResponse struct { + RandomLeaf json.RawMessage `json:"randomLeaf"` +} + +func (v *UnionNoFragmentsQueryResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *UnionNoFragmentsQueryResponse) __premarshalJSON() (*__premarshalUnionNoFragmentsQueryResponse, error) { + + var retval __premarshalUnionNoFragmentsQueryResponse + + { + + dst := &retval.RandomLeaf + src := v.RandomLeaf + var err error + *dst, err = __marshalUnionNoFragmentsQueryRandomLeafLeafContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal UnionNoFragmentsQueryResponse.RandomLeaf: %w", err) + } + } + return &retval, nil +} + func UnionNoFragmentsQuery( client graphql.Client, ) (*UnionNoFragmentsQueryResponse, error) { diff --git a/generate/testdata/snapshots/TestGenerate-unexported.graphql-unexported.graphql.go b/generate/testdata/snapshots/TestGenerate-unexported.graphql-unexported.graphql.go index e0267f81..0dbb6bc1 100644 --- a/generate/testdata/snapshots/TestGenerate-unexported.graphql-unexported.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-unexported.graphql-unexported.graphql.go @@ -41,18 +41,76 @@ type UserQueryInput struct { Birthdate time.Time `json:"-"` } -func (v *UserQueryInput) MarshalJSON() ([]byte, error) { +func (v *UserQueryInput) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } - var fullObject struct { + var firstPass struct { *UserQueryInput Birthdate json.RawMessage `json:"birthdate"` - graphql.NoMarshalJSON + graphql.NoUnmarshalJSON + } + firstPass.UserQueryInput = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.Birthdate + src := firstPass.Birthdate + if len(src) != 0 && string(src) != "null" { + err = testutil.UnmarshalDate( + src, dst) + if err != nil { + return fmt.Errorf( + "Unable to unmarshal UserQueryInput.Birthdate: %w", err) + } + } + } + return nil +} + +type __premarshalUserQueryInput struct { + Email string `json:"email"` + + Name string `json:"name"` + + Id testutil.ID `json:"id"` + + Role Role `json:"role"` + + Names []string `json:"names"` + + HasPokemon testutil.Pokemon `json:"hasPokemon"` + + Birthdate json.RawMessage `json:"birthdate"` +} + +func (v *UserQueryInput) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err } - fullObject.UserQueryInput = v + return json.Marshal(premarshaled) +} +func (v *UserQueryInput) __premarshalJSON() (*__premarshalUserQueryInput, error) { + + var retval __premarshalUserQueryInput + + retval.Email = v.Email + retval.Name = v.Name + retval.Id = v.Id + retval.Role = v.Role + retval.Names = v.Names + retval.HasPokemon = v.HasPokemon { - dst := &fullObject.Birthdate + dst := &retval.Birthdate src := v.Birthdate var err error *dst, err = testutil.MarshalDate( @@ -62,8 +120,7 @@ func (v *UserQueryInput) MarshalJSON() ([]byte, error) { "Unable to marshal UserQueryInput.Birthdate: %w", err) } } - - return json.Marshal(&fullObject) + return &retval, nil } // __unexportedInput is used internally by genqlient diff --git a/generate/types.go b/generate/types.go index c758fdd0..2c5378ba 100644 --- a/generate/types.go +++ b/generate/types.go @@ -158,6 +158,17 @@ func (field *goStructField) IsEmbedded() bool { return field.GoName == "" } +// Selector returns the field's name, which is unqualified type-name if it's +// embedded. +func (field *goStructField) Selector() string { + if field.GoName != "" { + return field.GoName + } + // TODO(benkraft): This assumes the type is package-local, which is always + // true for embedded types for us, but isn't the most robust assumption. + return field.GoType.Unwrap().Reference() +} + // unmarshaler returns: // - the name of the function to use to unmarshal this field // - true if this is a fully-qualified name (false if it is a package-local @@ -176,14 +187,6 @@ func (field *goStructField) unmarshaler() (qualifiedName string, needsImport boo return "encoding/json.Unmarshal", true, field.IsEmbedded() } -// NeedsUnmarshaler returns true if this field needs special handling when -// unmarshaling, e.g. if it's of interface type, embedded, or has a -// user-specified custom unmarshaler. -func (field *goStructField) NeedsUnmarshaler() bool { - _, _, ok := field.unmarshaler() - return ok -} - // Unmarshaler returns the Go name of the function to use to unmarshal this // field (which may be "json.Unmarshal" if there's not a special one). func (field *goStructField) Unmarshaler(g *generator) (string, error) { @@ -198,34 +201,149 @@ func (field *goStructField) Unmarshaler(g *generator) (string, error) { // - the fully-qualified name of the function to use to marshal this field // - true if we need to generate an marshaler at all, false if the default // behavior will suffice -func (field *goStructField) marshaler() (qualifiedName string, needsMarshaler bool) { - // (there are no interfaces on the input side) - opaque, ok := field.GoType.Unwrap().(*goOpaqueType) - if ok && opaque.Marshaler != "" { - return opaque.Marshaler, true +func (field *goStructField) marshaler() (qualifiedName string, needsImport bool, needsMarshaler bool) { + switch typ := field.GoType.Unwrap().(type) { + case *goOpaqueType: + if typ.Marshaler != "" { + return typ.Marshaler, true, true + } + case *goInterfaceType: + return "__marshal" + typ.Reference(), false, true } - return "encoding/json.Marshal", field.IsEmbedded() -} - -// NeedsMarshaler returns true if this field needs special handling when -// marshaling, e.g. if it has a user-specified custom marshaler. -func (field *goStructField) NeedsMarshaler() bool { - _, ok := field.marshaler() - return ok + return "encoding/json.Marshal", true, field.IsEmbedded() } // Marshaler returns the Go name of the function to use to marshal this // field (which may be "json.Marshal" if there's not a special one). func (field *goStructField) Marshaler(g *generator) (string, error) { - name, _ := field.marshaler() - // Unlike unmarshaler, we never have a local name, and always need g.ref. - return g.ref(name) + name, needsImport, _ := field.marshaler() + if needsImport { + return g.ref(name) + } + return name, nil +} + +// NeedsMarshaling returns true if this field needs special handling when +// marshaling and unmarshaling, e.g. if it has a user-specified custom +// (un)marshaler. Note if it needs one, it needs the other: even if the user +// only specified an unmarshaler, we need to add `json:"-"` to the field, which +// means we need to specially handling it when marshaling. +func (field *goStructField) NeedsMarshaling() bool { + _, _, ok1 := field.marshaler() + _, _, ok2 := field.unmarshaler() + return ok1 || ok2 +} + +// NeedsMarshaler returns true if any fields of this type need special +// handling when (un)marshaling (see goStructField.NeedsMarshaling). +func (typ *goStructType) NeedsMarshaling() bool { + for _, f := range typ.Fields { + if f.NeedsMarshaling() { + return true + } + } + return false +} + +// selector represents a field and the path to get there from the type in +// question, and is used in FlattenedFields, below. +type selector struct { + *goStructField + // e.g. "OuterEmbed.InnerEmbed.LeafField" + Selector string +} + +// FlattenedFields returns the fields of this type and its recursive embeds, +// and the paths to reach them (via those embeds), but with different +// visibility rules for conflicting fields than Go. +// +// (Before you read further, now's a good time to review Go's rules: +// https://golang.org/ref/spec#Selectors. Done? Good.) +// +// To illustrate the need, consider the following query: +// fragment A on T { id } +// fragment B on T { id } +// query Q { t { ...A ...B } } +// We generate types: +// type A struct { Id string `json:"id"` } +// type B struct { Id string `json:"id"` } +// type QT struct { A; B } +// According to Go's embedding rules, QT has no field Id: since QT.A.Id and +// QT.B.Id are at equal depth, neither wins and gets promoted. (Go's JSON +// library uses similar logic to decide which field to write to JSON, except +// with the additional rule that a field with a JSON tag wins over a field +// without; in our case both have such a field.) +// +// Those rules don't work for us. When unmarshaling, we want to fill in all +// the potentially-matching fields (QT.A.Id and QT.B.Id in this case), and when +// marshaling, we want to always marshal exactly one potentially-conflicting +// field; we're happy to use the Go visibility rules when they apply. such field, choosing an +// arbitrary one (in a stable manner) if needed. For unmarshaling, our +// QT.UnmarshalJSON ends up unmarshaling the same JSON object into QT, QT.A, +// and QT.B, which gives us the behavior we want. But for +// marshaling, we need to resolve the conflicts: if we simply marshaled QT, +// QT.A, and QT.B, we'd have to do some JSON-surgery to join them, and we'd +// probably end up with duplicate fields, which leads to unpredictable behavior +// based on the reader. That's no good. +// +// So: instead, we have our own rules, which work like the Go rules, except +// that if there's a tie we choose the first field (in source order). (In +// practice, hopefully, they all match, but validating that is even more work +// for a fairly rare case.) This function returns, for each JSON-name, the Go +// field we want to use. In the example above, it would return: +// []selector{{, "A.Id"}} +func (typ *goStructType) FlattenedFields() ([]*selector, error) { + seenJSONNames := map[string]bool{} + retval := make([]*selector, 0, len(typ.Fields)) + + queue := make([]*selector, len(typ.Fields)) + for i, field := range typ.Fields { + queue[i] = &selector{field, field.Selector()} + } + + // Since our (non-embedded) fields always have JSON tags, the logic we want + // is simply: do a breadth-first search through the recursively embedded + // fields, and take the first one we see with a given JSON tag. + for len(queue) > 0 { + field := queue[0] + queue = queue[1:] + if field.IsEmbedded() { + typ, ok := field.GoType.(*goStructType) + if !ok { + // Should never happen: embeds correspond to named fragments, + // and even if the fragment is of interface type in GraphQL, + // either it's spread into a concrete type, or we are writing + // one of the implementations of the interface into which it's + // spread; either way we embed the corresponding implementation + // of the fragment. + return nil, errorf(nil, + "genqlient internal error: embedded field %s.%s was not a struct", + typ.GoName, field.GoName) + } + + // Enqueue the embedded fields for our BFS. + for _, subField := range typ.Fields { + queue = append(queue, + &selector{subField, field.Selector + "." + subField.Selector()}) + } + continue + } + + if seenJSONNames[field.JSONName] { + // We already chose a selector for this JSON field. Skip it. + continue + } + + // Else, we are the selector we are looking for. + seenJSONNames[field.JSONName] = true + retval = append(retval, field) + } + return retval, nil } func (typ *goStructType) WriteDefinition(w io.Writer, g *generator) error { writeDescription(w, structDescription(typ)) - needUnmarshaler, needMarshaler := false, false fmt.Fprintf(w, "type %s struct {\n", typ.GoName) for _, field := range typ.Fields { writeDescription(w, field.Description) @@ -234,13 +352,8 @@ func (typ *goStructType) WriteDefinition(w io.Writer, g *generator) error { jsonTag += ",omitempty" } jsonTag += `"` - if !typ.IsInput && field.NeedsUnmarshaler() { - // certain types are handled in our UnmarshalJSON (see below) - needUnmarshaler = true - jsonTag = `"-"` - } - if typ.IsInput && field.NeedsMarshaler() { - needMarshaler = true + if field.NeedsMarshaling() { + // certain types are handled in our (Un)MarshalJSON (see below) jsonTag = `"-"` } // Note for embedded types field.GoName is "", which produces the code @@ -274,19 +387,20 @@ func (typ *goStructType) WriteDefinition(w io.Writer, g *generator) error { // same thing as interface-typed fields, except the user has defined the // helper. // - // Note that in all cases we need only write an unmarshaler if this is an - // input type, and a marshaler if it's an output type. + // Note that genqlient itself only uses unmarshalers for output types, and + // marshalers for input types. But we write both in case you want to write + // your data to JSON for some reason (say to put it in a cache). (And we + // need to write both if we need to write either, because in such cases we + // write a `json:"-"` tag on the field.) // // TODO(benkraft): If/when proposal #5901 is implemented (Go 1.18 at the // earliest), we may be able to do some of this a simpler way. - if needUnmarshaler { + if typ.NeedsMarshaling() { err := g.render("unmarshal.go.tmpl", w, typ) if err != nil { return err } - } - if needMarshaler { - err := g.render("marshal.go.tmpl", w, typ) + err = g.render("marshal.go.tmpl", w, typ) if err != nil { return err } @@ -367,9 +481,14 @@ func (typ *goInterfaceType) WriteDefinition(w io.Writer, g *generator) error { fmt.Fprintf(w, "\n") // blank line between each type's implementations } - // Finally, write the unmarshal-helper, which will be called by struct - // fields referencing this type (see goStructType.WriteDefinition). - return g.render("unmarshal_helper.go.tmpl", w, typ) + // Finally, write the marshal- and unmarshal-helpers, which + // will be called by struct fields referencing this type (see + // goStructType.WriteDefinition). + err := g.render("unmarshal_helper.go.tmpl", w, typ) + if err != nil { + return err + } + return g.render("marshal_helper.go.tmpl", w, typ) } func (typ *goInterfaceType) Reference() string { return typ.GoName } diff --git a/generate/unmarshal.go.tmpl b/generate/unmarshal.go.tmpl index db0942b7..532e3245 100644 --- a/generate/unmarshal.go.tmpl +++ b/generate/unmarshal.go.tmpl @@ -1,5 +1,23 @@ -{{/* (the blank lines at the start are intentional, to separate - UnmarshalJSON from the function it follows) */}} +{{/* We need to generate UnmarshalJSON methods for some types that we want to + handle specially. Specifically, we generate an UnmarshalJSON for each + struct with a field meeting any of these criteria: + - a field whose type is configured with a custom unmarshaler + - an embedded (anonymous) field; technically we only need an + UnmarshalJSON if the embedded type needs one, but it's easier to + generate it unconditionally + - a field of interface type + Additionally, since we add `json:"-"` to fields we handle specially, for + any field which requires a MarshalJSON, we also generate an UnmarshalJSON, + and vice versa. + + Given that, we want to specially handle the above-described fields, but + unmarshal everything else normally. To handle fields with custom + unmarshalers, first we unmarshal them into a json.RawMessage, then call + the custom unmarshaler. Interface-typed fields are similar, except + instead of a custom unmarshaler we call the helper we've generated + (see unmarshal_helper.go.tmpl). Embedded fields don't need the + json.RawMessage; we just unmarshal our input again into the embedded + field. */}} func (v *{{.GoName}}) UnmarshalJSON(b []byte) error { {{/* Standard convention for unmarshalers is to no-op on null. */}} @@ -7,16 +25,14 @@ func (v *{{.GoName}}) UnmarshalJSON(b []byte) error { return nil } - {{/* We want to specially handle certain fields, but unmarshal everything - else normally. To handle abstract fields and fields with custom - unmarshalers, first we unmarshal them into a json.RawMessage, and then - handle those further, below. Embedded fields don't need the - json.RawMessage; we just use our input again. Either way, we first - want to call json.Unmarshal on the receiver (v). But if we do that - naively on a value of type `.Type`, it will call this function again, - and recurse infinitely. So we make a wrapper type which embeds both - this type and NoUmnarshalJSON, which prevents either's UnmarshalJSON - method from being promoted. For more on why this is so difficult, see + {{/* For our first pass, we ignore embedded fields, unmarshal all the + custom-unmarshaler or abstract fields into json.RawMessage, and + unmarshal everything else normally. To do this, we want to call + json.Unmarshal on the receiver (v). But if we do that naively on a + value of type <.GoName>, it will call this function again, and recurse + infinitely. So we make a wrapper type which embeds both this type and + NoUmnarshalJSON, which prevents either's UnmarshalJSON method from + being promoted. For more on why this is so difficult, see https://github.com/benjaminjkraft/notes/blob/master/go-json-interfaces.md. (Note there are a few different ways "hide" the method, but this one seems to be the best option that works if this type has embedded types @@ -28,7 +44,7 @@ func (v *{{.GoName}}) UnmarshalJSON(b []byte) error { var firstPass struct{ *{{.GoName}} {{range .Fields -}} - {{if and .NeedsUnmarshaler (not .IsEmbedded) -}} + {{if and .NeedsMarshaling (not .IsEmbedded) -}} {{.GoName}} {{repeat .GoType.SliceDepth "[]"}}{{ref "encoding/json.RawMessage"}} `json:"{{.JSONName}}"` {{end -}} {{end -}} @@ -45,11 +61,16 @@ func (v *{{.GoName}}) UnmarshalJSON(b []byte) error { {{/* Now, handle the fields needing special handling. */}} {{range $field := .Fields -}} - {{if $field.NeedsUnmarshaler -}} + {{if $field.NeedsMarshaling -}} {{if $field.IsEmbedded -}} {{/* Embedded fields are easier: we just unmarshal the same input into them. (They're also easier because they can't be lists, since they - arise from GraphQL fragment spreads.) */ -}} + arise from GraphQL fragment spreads.) + + Note that our behavior if you have two fields of the same name via + different embeds differs from ordinary json-unmarshaling: we unmarshal + into *all* of the fields. See goStructType.FlattenedFields in + types.go for more discussion of embedding and visibility. */ -}} err = {{$field.Unmarshaler $.Generator}}( b, &v.{{$field.GoType.Unwrap.Reference}}) if err != nil { @@ -125,8 +146,8 @@ func (v *{{.GoName}}) UnmarshalJSON(b []byte) error { } {{end -}} } - {{end -}}{{/* end if/else .IsEmbedded */ -}} - {{end -}}{{/* end if .NeedsUnmarshaler */ -}} + {{end}}{{/* end if/else .IsEmbedded */ -}} + {{end}}{{/* end if .NeedsMarshaling */ -}} {{end}}{{/* end range .Fields */ -}} return nil diff --git a/generate/unmarshal_helper.go.tmpl b/generate/unmarshal_helper.go.tmpl index 25d53468..1a65b527 100644 --- a/generate/unmarshal_helper.go.tmpl +++ b/generate/unmarshal_helper.go.tmpl @@ -1,5 +1,8 @@ -{{/* (the blank lines at the start are intentional, to separate - the helper from the function it follows) */}} +{{/* This template generates a helper for each interface type genqlient must + unmarshal. This helper is called by the UnmarshalJSON of each (struct) + type with a field of the interface type, similar to how it calls custom + unmarshalers. The helper itself is fairly simple: it just parses out and + switches on __typename, then unmarshals into the relevant struct. */}} func __unmarshal{{.GoName}}(b []byte, v *{{.GoName}}) error { if string(b) == "null" { diff --git a/internal/integration/generated.go b/internal/integration/generated.go index d904fa7c..ebfefdd3 100644 --- a/internal/integration/generated.go +++ b/internal/integration/generated.go @@ -52,6 +52,43 @@ func (v *AnimalFields) UnmarshalJSON(b []byte) error { return nil } +type __premarshalAnimalFields struct { + Id string `json:"id"` + + Hair AnimalFieldsHairBeingsHair `json:"hair"` + + Owner json.RawMessage `json:"owner"` +} + +func (v *AnimalFields) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *AnimalFields) __premarshalJSON() (*__premarshalAnimalFields, error) { + + var retval __premarshalAnimalFields + + retval.Id = v.Id + retval.Hair = v.Hair + { + + dst := &retval.Owner + src := v.Owner + var err error + *dst, err = __marshalAnimalFieldsOwnerBeing( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal AnimalFields.Owner: %w", err) + } + } + return &retval, nil +} + // AnimalFieldsHairBeingsHair includes the requested fields of the GraphQL type BeingsHair. type AnimalFieldsHairBeingsHair struct { HasHair bool `json:"hasHair"` @@ -121,6 +158,38 @@ func __unmarshalAnimalFieldsOwnerBeing(b []byte, v *AnimalFieldsOwnerBeing) erro } } +func __marshalAnimalFieldsOwnerBeing(v *AnimalFieldsOwnerBeing) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *AnimalFieldsOwnerUser: + typename = "User" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalAnimalFieldsOwnerUser + }{typename, premarshaled} + return json.Marshal(result) + case *AnimalFieldsOwnerAnimal: + typename = "Animal" + + result := struct { + TypeName string `json:"__typename"` + *AnimalFieldsOwnerAnimal + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for AnimalFieldsOwnerBeing: "%T"`, v) + } +} + // AnimalFieldsOwnerUser includes the requested fields of the GraphQL type User. type AnimalFieldsOwnerUser struct { Typename string `json:"__typename"` @@ -159,6 +228,35 @@ func (v *AnimalFieldsOwnerUser) UnmarshalJSON(b []byte) error { return nil } +type __premarshalAnimalFieldsOwnerUser struct { + Typename string `json:"__typename"` + + Id string `json:"id"` + + LuckyNumber int `json:"luckyNumber"` + + Hair MoreUserFieldsHair `json:"hair"` +} + +func (v *AnimalFieldsOwnerUser) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *AnimalFieldsOwnerUser) __premarshalJSON() (*__premarshalAnimalFieldsOwnerUser, error) { + + var retval __premarshalAnimalFieldsOwnerUser + + retval.Typename = v.Typename + retval.Id = v.Id + retval.LuckyNumber = v.LuckyFieldsUser.LuckyNumber + retval.Hair = v.UserFields.MoreUserFields.Hair + return &retval, nil +} + // LuckyFields includes the GraphQL fields of Lucky requested by the fragment LuckyFields. // // LuckyFields is implemented by the following types: @@ -200,6 +298,30 @@ func __unmarshalLuckyFields(b []byte, v *LuckyFields) error { } } +func __marshalLuckyFields(v *LuckyFields) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *LuckyFieldsUser: + typename = "User" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalLuckyFieldsUser + }{typename, premarshaled} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for LuckyFields: "%T"`, v) + } +} + // LuckyFields includes the GraphQL fields of User requested by the fragment LuckyFields. type LuckyFieldsUser struct { MoreUserFields `json:"-"` @@ -231,6 +353,32 @@ func (v *LuckyFieldsUser) UnmarshalJSON(b []byte) error { return nil } +type __premarshalLuckyFieldsUser struct { + LuckyNumber int `json:"luckyNumber"` + + Id string `json:"id"` + + Hair MoreUserFieldsHair `json:"hair"` +} + +func (v *LuckyFieldsUser) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *LuckyFieldsUser) __premarshalJSON() (*__premarshalLuckyFieldsUser, error) { + + var retval __premarshalLuckyFieldsUser + + retval.LuckyNumber = v.LuckyNumber + retval.Id = v.MoreUserFields.Id + retval.Hair = v.MoreUserFields.Hair + return &retval, nil +} + // MoreUserFields includes the GraphQL fields of User requested by the fragment MoreUserFields. type MoreUserFields struct { Id string `json:"id"` @@ -286,23 +434,89 @@ func (v *UserFields) UnmarshalJSON(b []byte) error { return nil } +type __premarshalUserFields struct { + Id string `json:"id"` + + LuckyNumber int `json:"luckyNumber"` + + Hair MoreUserFieldsHair `json:"hair"` +} + +func (v *UserFields) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *UserFields) __premarshalJSON() (*__premarshalUserFields, error) { + + var retval __premarshalUserFields + + retval.Id = v.Id + retval.LuckyNumber = v.LuckyFieldsUser.LuckyNumber + retval.Hair = v.MoreUserFields.Hair + return &retval, nil +} + // __queryWithCustomMarshalInput is used internally by genqlient type __queryWithCustomMarshalInput struct { Date time.Time `json:"-"` } -func (v *__queryWithCustomMarshalInput) MarshalJSON() ([]byte, error) { +func (v *__queryWithCustomMarshalInput) UnmarshalJSON(b []byte) error { - var fullObject struct { + if string(b) == "null" { + return nil + } + + var firstPass struct { *__queryWithCustomMarshalInput Date json.RawMessage `json:"date"` - graphql.NoMarshalJSON + graphql.NoUnmarshalJSON + } + firstPass.__queryWithCustomMarshalInput = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.Date + src := firstPass.Date + if len(src) != 0 && string(src) != "null" { + err = testutil.UnmarshalDate( + src, dst) + if err != nil { + return fmt.Errorf( + "Unable to unmarshal __queryWithCustomMarshalInput.Date: %w", err) + } + } + } + return nil +} + +type __premarshal__queryWithCustomMarshalInput struct { + Date json.RawMessage `json:"date"` +} + +func (v *__queryWithCustomMarshalInput) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err } - fullObject.__queryWithCustomMarshalInput = v + return json.Marshal(premarshaled) +} + +func (v *__queryWithCustomMarshalInput) __premarshalJSON() (*__premarshal__queryWithCustomMarshalInput, error) { + + var retval __premarshal__queryWithCustomMarshalInput { - dst := &fullObject.Date + dst := &retval.Date src := v.Date var err error *dst, err = testutil.MarshalDate( @@ -312,8 +526,7 @@ func (v *__queryWithCustomMarshalInput) MarshalJSON() ([]byte, error) { "Unable to marshal __queryWithCustomMarshalInput.Date: %w", err) } } - - return json.Marshal(&fullObject) + return &retval, nil } // __queryWithCustomMarshalOptionalInput is used internally by genqlient @@ -322,18 +535,61 @@ type __queryWithCustomMarshalOptionalInput struct { Id *string `json:"id"` } -func (v *__queryWithCustomMarshalOptionalInput) MarshalJSON() ([]byte, error) { +func (v *__queryWithCustomMarshalOptionalInput) UnmarshalJSON(b []byte) error { - var fullObject struct { + if string(b) == "null" { + return nil + } + + var firstPass struct { *__queryWithCustomMarshalOptionalInput Date json.RawMessage `json:"date"` - graphql.NoMarshalJSON + graphql.NoUnmarshalJSON + } + firstPass.__queryWithCustomMarshalOptionalInput = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.Date + src := firstPass.Date + if len(src) != 0 && string(src) != "null" { + *dst = new(time.Time) + err = testutil.UnmarshalDate( + src, *dst) + if err != nil { + return fmt.Errorf( + "Unable to unmarshal __queryWithCustomMarshalOptionalInput.Date: %w", err) + } + } } - fullObject.__queryWithCustomMarshalOptionalInput = v + return nil +} + +type __premarshal__queryWithCustomMarshalOptionalInput struct { + Date json.RawMessage `json:"date"` + + Id *string `json:"id"` +} + +func (v *__queryWithCustomMarshalOptionalInput) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *__queryWithCustomMarshalOptionalInput) __premarshalJSON() (*__premarshal__queryWithCustomMarshalOptionalInput, error) { + + var retval __premarshal__queryWithCustomMarshalOptionalInput { - dst := &fullObject.Date + dst := &retval.Date src := v.Date if src != nil { var err error @@ -345,8 +601,8 @@ func (v *__queryWithCustomMarshalOptionalInput) MarshalJSON() ([]byte, error) { } } } - - return json.Marshal(&fullObject) + retval.Id = v.Id + return &retval, nil } // __queryWithCustomMarshalSliceInput is used internally by genqlient @@ -354,18 +610,64 @@ type __queryWithCustomMarshalSliceInput struct { Dates []time.Time `json:"-"` } -func (v *__queryWithCustomMarshalSliceInput) MarshalJSON() ([]byte, error) { +func (v *__queryWithCustomMarshalSliceInput) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } - var fullObject struct { + var firstPass struct { *__queryWithCustomMarshalSliceInput Dates []json.RawMessage `json:"dates"` - graphql.NoMarshalJSON + graphql.NoUnmarshalJSON + } + firstPass.__queryWithCustomMarshalSliceInput = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.Dates + src := firstPass.Dates + *dst = make( + []time.Time, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + if len(src) != 0 && string(src) != "null" { + err = testutil.UnmarshalDate( + src, dst) + if err != nil { + return fmt.Errorf( + "Unable to unmarshal __queryWithCustomMarshalSliceInput.Dates: %w", err) + } + } + } + } + return nil +} + +type __premarshal__queryWithCustomMarshalSliceInput struct { + Dates []json.RawMessage `json:"dates"` +} + +func (v *__queryWithCustomMarshalSliceInput) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err } - fullObject.__queryWithCustomMarshalSliceInput = v + return json.Marshal(premarshaled) +} + +func (v *__queryWithCustomMarshalSliceInput) __premarshalJSON() (*__premarshal__queryWithCustomMarshalSliceInput, error) { + + var retval __premarshal__queryWithCustomMarshalSliceInput { - dst := &fullObject.Dates + dst := &retval.Dates src := v.Dates *dst = make( []json.RawMessage, @@ -381,8 +683,7 @@ func (v *__queryWithCustomMarshalSliceInput) MarshalJSON() ([]byte, error) { } } } - - return json.Marshal(&fullObject) + return &retval, nil } // __queryWithFragmentsInput is used internally by genqlient @@ -476,6 +777,43 @@ func (v *queryWithCustomMarshalOptionalUserSearchUser) UnmarshalJSON(b []byte) e return nil } +type __premarshalqueryWithCustomMarshalOptionalUserSearchUser struct { + Id string `json:"id"` + + Name string `json:"name"` + + Birthdate json.RawMessage `json:"birthdate"` +} + +func (v *queryWithCustomMarshalOptionalUserSearchUser) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *queryWithCustomMarshalOptionalUserSearchUser) __premarshalJSON() (*__premarshalqueryWithCustomMarshalOptionalUserSearchUser, error) { + + var retval __premarshalqueryWithCustomMarshalOptionalUserSearchUser + + retval.Id = v.Id + retval.Name = v.Name + { + + dst := &retval.Birthdate + src := v.Birthdate + var err error + *dst, err = testutil.MarshalDate( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal queryWithCustomMarshalOptionalUserSearchUser.Birthdate: %w", err) + } + } + return &retval, nil +} + // queryWithCustomMarshalResponse is returned by queryWithCustomMarshal on success. type queryWithCustomMarshalResponse struct { UsersBornOn []queryWithCustomMarshalUsersBornOnUser `json:"usersBornOn"` @@ -526,6 +864,43 @@ func (v *queryWithCustomMarshalSliceUsersBornOnDatesUser) UnmarshalJSON(b []byte return nil } +type __premarshalqueryWithCustomMarshalSliceUsersBornOnDatesUser struct { + Id string `json:"id"` + + Name string `json:"name"` + + Birthdate json.RawMessage `json:"birthdate"` +} + +func (v *queryWithCustomMarshalSliceUsersBornOnDatesUser) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *queryWithCustomMarshalSliceUsersBornOnDatesUser) __premarshalJSON() (*__premarshalqueryWithCustomMarshalSliceUsersBornOnDatesUser, error) { + + var retval __premarshalqueryWithCustomMarshalSliceUsersBornOnDatesUser + + retval.Id = v.Id + retval.Name = v.Name + { + + dst := &retval.Birthdate + src := v.Birthdate + var err error + *dst, err = testutil.MarshalDate( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal queryWithCustomMarshalSliceUsersBornOnDatesUser.Birthdate: %w", err) + } + } + return &retval, nil +} + // queryWithCustomMarshalUsersBornOnUser includes the requested fields of the GraphQL type User. type queryWithCustomMarshalUsersBornOnUser struct { Id string `json:"id"` @@ -566,6 +941,43 @@ func (v *queryWithCustomMarshalUsersBornOnUser) UnmarshalJSON(b []byte) error { return nil } +type __premarshalqueryWithCustomMarshalUsersBornOnUser struct { + Id string `json:"id"` + + Name string `json:"name"` + + Birthdate json.RawMessage `json:"birthdate"` +} + +func (v *queryWithCustomMarshalUsersBornOnUser) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *queryWithCustomMarshalUsersBornOnUser) __premarshalJSON() (*__premarshalqueryWithCustomMarshalUsersBornOnUser, error) { + + var retval __premarshalqueryWithCustomMarshalUsersBornOnUser + + retval.Id = v.Id + retval.Name = v.Name + { + + dst := &retval.Birthdate + src := v.Birthdate + var err error + *dst, err = testutil.MarshalDate( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal queryWithCustomMarshalUsersBornOnUser.Birthdate: %w", err) + } + } + return &retval, nil +} + // queryWithFragmentsBeingsAnimal includes the requested fields of the GraphQL type Animal. type queryWithFragmentsBeingsAnimal struct { Typename string `json:"__typename"` @@ -609,6 +1021,52 @@ func (v *queryWithFragmentsBeingsAnimal) UnmarshalJSON(b []byte) error { return nil } +type __premarshalqueryWithFragmentsBeingsAnimal struct { + Typename string `json:"__typename"` + + Id string `json:"id"` + + Name string `json:"name"` + + Hair queryWithFragmentsBeingsAnimalHairBeingsHair `json:"hair"` + + Species Species `json:"species"` + + Owner json.RawMessage `json:"owner"` +} + +func (v *queryWithFragmentsBeingsAnimal) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *queryWithFragmentsBeingsAnimal) __premarshalJSON() (*__premarshalqueryWithFragmentsBeingsAnimal, error) { + + var retval __premarshalqueryWithFragmentsBeingsAnimal + + retval.Typename = v.Typename + retval.Id = v.Id + retval.Name = v.Name + retval.Hair = v.Hair + retval.Species = v.Species + { + + dst := &retval.Owner + src := v.Owner + var err error + *dst, err = __marshalqueryWithFragmentsBeingsAnimalOwnerBeing( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal queryWithFragmentsBeingsAnimal.Owner: %w", err) + } + } + return &retval, nil +} + // queryWithFragmentsBeingsAnimalHairBeingsHair includes the requested fields of the GraphQL type BeingsHair. type queryWithFragmentsBeingsAnimalHairBeingsHair struct { HasHair bool `json:"hasHair"` @@ -689,6 +1147,34 @@ func __unmarshalqueryWithFragmentsBeingsAnimalOwnerBeing(b []byte, v *queryWithF } } +func __marshalqueryWithFragmentsBeingsAnimalOwnerBeing(v *queryWithFragmentsBeingsAnimalOwnerBeing) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *queryWithFragmentsBeingsAnimalOwnerUser: + typename = "User" + + result := struct { + TypeName string `json:"__typename"` + *queryWithFragmentsBeingsAnimalOwnerUser + }{typename, v} + return json.Marshal(result) + case *queryWithFragmentsBeingsAnimalOwnerAnimal: + typename = "Animal" + + result := struct { + TypeName string `json:"__typename"` + *queryWithFragmentsBeingsAnimalOwnerAnimal + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for queryWithFragmentsBeingsAnimalOwnerBeing: "%T"`, v) + } +} + // queryWithFragmentsBeingsAnimalOwnerUser includes the requested fields of the GraphQL type User. type queryWithFragmentsBeingsAnimalOwnerUser struct { Typename string `json:"__typename"` @@ -763,6 +1249,38 @@ func __unmarshalqueryWithFragmentsBeingsBeing(b []byte, v *queryWithFragmentsBei } } +func __marshalqueryWithFragmentsBeingsBeing(v *queryWithFragmentsBeingsBeing) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *queryWithFragmentsBeingsUser: + typename = "User" + + result := struct { + TypeName string `json:"__typename"` + *queryWithFragmentsBeingsUser + }{typename, v} + return json.Marshal(result) + case *queryWithFragmentsBeingsAnimal: + typename = "Animal" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalqueryWithFragmentsBeingsAnimal + }{typename, premarshaled} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for queryWithFragmentsBeingsBeing: "%T"`, v) + } +} + // queryWithFragmentsBeingsUser includes the requested fields of the GraphQL type User. type queryWithFragmentsBeingsUser struct { Typename string `json:"__typename"` @@ -821,6 +1339,43 @@ func (v *queryWithFragmentsResponse) UnmarshalJSON(b []byte) error { return nil } +type __premarshalqueryWithFragmentsResponse struct { + Beings []json.RawMessage `json:"beings"` +} + +func (v *queryWithFragmentsResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *queryWithFragmentsResponse) __premarshalJSON() (*__premarshalqueryWithFragmentsResponse, error) { + + var retval __premarshalqueryWithFragmentsResponse + + { + + dst := &retval.Beings + src := v.Beings + *dst = make( + []json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + var err error + *dst, err = __marshalqueryWithFragmentsBeingsBeing( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal queryWithFragmentsResponse.Beings: %w", err) + } + } + } + return &retval, nil +} + // queryWithInterfaceListFieldBeingsAnimal includes the requested fields of the GraphQL type Animal. type queryWithInterfaceListFieldBeingsAnimal struct { Typename string `json:"__typename"` @@ -896,6 +1451,34 @@ func __unmarshalqueryWithInterfaceListFieldBeingsBeing(b []byte, v *queryWithInt } } +func __marshalqueryWithInterfaceListFieldBeingsBeing(v *queryWithInterfaceListFieldBeingsBeing) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *queryWithInterfaceListFieldBeingsUser: + typename = "User" + + result := struct { + TypeName string `json:"__typename"` + *queryWithInterfaceListFieldBeingsUser + }{typename, v} + return json.Marshal(result) + case *queryWithInterfaceListFieldBeingsAnimal: + typename = "Animal" + + result := struct { + TypeName string `json:"__typename"` + *queryWithInterfaceListFieldBeingsAnimal + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for queryWithInterfaceListFieldBeingsBeing: "%T"`, v) + } +} + // queryWithInterfaceListFieldBeingsUser includes the requested fields of the GraphQL type User. type queryWithInterfaceListFieldBeingsUser struct { Typename string `json:"__typename"` @@ -947,6 +1530,43 @@ func (v *queryWithInterfaceListFieldResponse) UnmarshalJSON(b []byte) error { return nil } +type __premarshalqueryWithInterfaceListFieldResponse struct { + Beings []json.RawMessage `json:"beings"` +} + +func (v *queryWithInterfaceListFieldResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *queryWithInterfaceListFieldResponse) __premarshalJSON() (*__premarshalqueryWithInterfaceListFieldResponse, error) { + + var retval __premarshalqueryWithInterfaceListFieldResponse + + { + + dst := &retval.Beings + src := v.Beings + *dst = make( + []json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + var err error + *dst, err = __marshalqueryWithInterfaceListFieldBeingsBeing( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal queryWithInterfaceListFieldResponse.Beings: %w", err) + } + } + } + return &retval, nil +} + // queryWithInterfaceListPointerFieldBeingsAnimal includes the requested fields of the GraphQL type Animal. type queryWithInterfaceListPointerFieldBeingsAnimal struct { Typename string `json:"__typename"` @@ -1022,6 +1642,34 @@ func __unmarshalqueryWithInterfaceListPointerFieldBeingsBeing(b []byte, v *query } } +func __marshalqueryWithInterfaceListPointerFieldBeingsBeing(v *queryWithInterfaceListPointerFieldBeingsBeing) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *queryWithInterfaceListPointerFieldBeingsUser: + typename = "User" + + result := struct { + TypeName string `json:"__typename"` + *queryWithInterfaceListPointerFieldBeingsUser + }{typename, v} + return json.Marshal(result) + case *queryWithInterfaceListPointerFieldBeingsAnimal: + typename = "Animal" + + result := struct { + TypeName string `json:"__typename"` + *queryWithInterfaceListPointerFieldBeingsAnimal + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for queryWithInterfaceListPointerFieldBeingsBeing: "%T"`, v) + } +} + // queryWithInterfaceListPointerFieldBeingsUser includes the requested fields of the GraphQL type User. type queryWithInterfaceListPointerFieldBeingsUser struct { Typename string `json:"__typename"` @@ -1074,6 +1722,45 @@ func (v *queryWithInterfaceListPointerFieldResponse) UnmarshalJSON(b []byte) err return nil } +type __premarshalqueryWithInterfaceListPointerFieldResponse struct { + Beings []json.RawMessage `json:"beings"` +} + +func (v *queryWithInterfaceListPointerFieldResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *queryWithInterfaceListPointerFieldResponse) __premarshalJSON() (*__premarshalqueryWithInterfaceListPointerFieldResponse, error) { + + var retval __premarshalqueryWithInterfaceListPointerFieldResponse + + { + + dst := &retval.Beings + src := v.Beings + *dst = make( + []json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + if src != nil { + var err error + *dst, err = __marshalqueryWithInterfaceListPointerFieldBeingsBeing( + src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal queryWithInterfaceListPointerFieldResponse.Beings: %w", err) + } + } + } + } + return &retval, nil +} + // queryWithInterfaceNoFragmentsBeing includes the requested fields of the GraphQL interface Being. // // queryWithInterfaceNoFragmentsBeing is implemented by the following types: @@ -1142,6 +1829,34 @@ func __unmarshalqueryWithInterfaceNoFragmentsBeing(b []byte, v *queryWithInterfa } } +func __marshalqueryWithInterfaceNoFragmentsBeing(v *queryWithInterfaceNoFragmentsBeing) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *queryWithInterfaceNoFragmentsBeingUser: + typename = "User" + + result := struct { + TypeName string `json:"__typename"` + *queryWithInterfaceNoFragmentsBeingUser + }{typename, v} + return json.Marshal(result) + case *queryWithInterfaceNoFragmentsBeingAnimal: + typename = "Animal" + + result := struct { + TypeName string `json:"__typename"` + *queryWithInterfaceNoFragmentsBeingAnimal + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for queryWithInterfaceNoFragmentsBeing: "%T"`, v) + } +} + // queryWithInterfaceNoFragmentsBeingAnimal includes the requested fields of the GraphQL type Animal. type queryWithInterfaceNoFragmentsBeingAnimal struct { Typename string `json:"__typename"` @@ -1201,6 +1916,40 @@ func (v *queryWithInterfaceNoFragmentsResponse) UnmarshalJSON(b []byte) error { return nil } +type __premarshalqueryWithInterfaceNoFragmentsResponse struct { + Being json.RawMessage `json:"being"` + + Me queryWithInterfaceNoFragmentsMeUser `json:"me"` +} + +func (v *queryWithInterfaceNoFragmentsResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *queryWithInterfaceNoFragmentsResponse) __premarshalJSON() (*__premarshalqueryWithInterfaceNoFragmentsResponse, error) { + + var retval __premarshalqueryWithInterfaceNoFragmentsResponse + + { + + dst := &retval.Being + src := v.Being + var err error + *dst, err = __marshalqueryWithInterfaceNoFragmentsBeing( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal queryWithInterfaceNoFragmentsResponse.Being: %w", err) + } + } + retval.Me = v.Me + return &retval, nil +} + // queryWithNamedFragmentsBeingsAnimal includes the requested fields of the GraphQL type Animal. type queryWithNamedFragmentsBeingsAnimal struct { Typename string `json:"__typename"` @@ -1233,6 +1982,46 @@ func (v *queryWithNamedFragmentsBeingsAnimal) UnmarshalJSON(b []byte) error { return nil } +type __premarshalqueryWithNamedFragmentsBeingsAnimal struct { + Typename string `json:"__typename"` + + Id string `json:"id"` + + Hair AnimalFieldsHairBeingsHair `json:"hair"` + + Owner json.RawMessage `json:"owner"` +} + +func (v *queryWithNamedFragmentsBeingsAnimal) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *queryWithNamedFragmentsBeingsAnimal) __premarshalJSON() (*__premarshalqueryWithNamedFragmentsBeingsAnimal, error) { + + var retval __premarshalqueryWithNamedFragmentsBeingsAnimal + + retval.Typename = v.Typename + retval.Id = v.Id + retval.Hair = v.AnimalFields.Hair + { + + dst := &retval.Owner + src := v.AnimalFields.Owner + var err error + *dst, err = __marshalAnimalFieldsOwnerBeing( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal queryWithNamedFragmentsBeingsAnimal.AnimalFields.Owner: %w", err) + } + } + return &retval, nil +} + // queryWithNamedFragmentsBeingsBeing includes the requested fields of the GraphQL interface Being. // // queryWithNamedFragmentsBeingsBeing is implemented by the following types: @@ -1293,6 +2082,42 @@ func __unmarshalqueryWithNamedFragmentsBeingsBeing(b []byte, v *queryWithNamedFr } } +func __marshalqueryWithNamedFragmentsBeingsBeing(v *queryWithNamedFragmentsBeingsBeing) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *queryWithNamedFragmentsBeingsUser: + typename = "User" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalqueryWithNamedFragmentsBeingsUser + }{typename, premarshaled} + return json.Marshal(result) + case *queryWithNamedFragmentsBeingsAnimal: + typename = "Animal" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalqueryWithNamedFragmentsBeingsAnimal + }{typename, premarshaled} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `Unexpected concrete type for queryWithNamedFragmentsBeingsBeing: "%T"`, v) + } +} + // queryWithNamedFragmentsBeingsUser includes the requested fields of the GraphQL type User. type queryWithNamedFragmentsBeingsUser struct { Typename string `json:"__typename"` @@ -1325,6 +2150,35 @@ func (v *queryWithNamedFragmentsBeingsUser) UnmarshalJSON(b []byte) error { return nil } +type __premarshalqueryWithNamedFragmentsBeingsUser struct { + Typename string `json:"__typename"` + + Id string `json:"id"` + + LuckyNumber int `json:"luckyNumber"` + + Hair MoreUserFieldsHair `json:"hair"` +} + +func (v *queryWithNamedFragmentsBeingsUser) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *queryWithNamedFragmentsBeingsUser) __premarshalJSON() (*__premarshalqueryWithNamedFragmentsBeingsUser, error) { + + var retval __premarshalqueryWithNamedFragmentsBeingsUser + + retval.Typename = v.Typename + retval.Id = v.Id + retval.LuckyNumber = v.UserFields.LuckyFieldsUser.LuckyNumber + retval.Hair = v.UserFields.MoreUserFields.Hair + return &retval, nil +} + // queryWithNamedFragmentsResponse is returned by queryWithNamedFragments on success. type queryWithNamedFragmentsResponse struct { Beings []queryWithNamedFragmentsBeingsBeing `json:"-"` @@ -1369,6 +2223,43 @@ func (v *queryWithNamedFragmentsResponse) UnmarshalJSON(b []byte) error { return nil } +type __premarshalqueryWithNamedFragmentsResponse struct { + Beings []json.RawMessage `json:"beings"` +} + +func (v *queryWithNamedFragmentsResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *queryWithNamedFragmentsResponse) __premarshalJSON() (*__premarshalqueryWithNamedFragmentsResponse, error) { + + var retval __premarshalqueryWithNamedFragmentsResponse + + { + + dst := &retval.Beings + src := v.Beings + *dst = make( + []json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + var err error + *dst, err = __marshalqueryWithNamedFragmentsBeingsBeing( + &src) + if err != nil { + return nil, fmt.Errorf( + "Unable to marshal queryWithNamedFragmentsResponse.Beings: %w", err) + } + } + } + return &retval, nil +} + // queryWithOmitemptyResponse is returned by queryWithOmitempty on success. type queryWithOmitemptyResponse struct { User queryWithOmitemptyUser `json:"user"` diff --git a/internal/integration/integration_test.go b/internal/integration/integration_test.go index 132582a5..b0fd5283 100644 --- a/internal/integration/integration_test.go +++ b/internal/integration/integration_test.go @@ -25,7 +25,7 @@ func TestSimpleQuery(t *testing.T) { ctx := context.Background() server := server.RunServer() defer server.Close() - client := graphql.NewClient(server.URL, http.DefaultClient) + client := newRoundtripClient(t, server.URL) resp, err := simpleQuery(ctx, client) require.NoError(t, err) @@ -42,7 +42,7 @@ func TestServerError(t *testing.T) { ctx := context.Background() server := server.RunServer() defer server.Close() - client := graphql.NewClient(server.URL, http.DefaultClient) + client := newRoundtripClient(t, server.URL) resp, err := failingQuery(ctx, client) // As long as we get some response back, we should still return a full @@ -55,7 +55,7 @@ func TestServerError(t *testing.T) { func TestNetworkError(t *testing.T) { ctx := context.Background() - client := graphql.NewClient("https://nothing.invalid/graphql", http.DefaultClient) + client := newRoundtripClient(t, "https://nothing.invalid/graphql") resp, err := failingQuery(ctx, client) // As we guarantee in the README, even on network error you always get a @@ -75,6 +75,11 @@ func TestVariables(t *testing.T) { ctx := context.Background() server := server.RunServer() defer server.Close() + // This doesn't roundtrip successfully because the zero user gets marshaled + // as {"id": "", "name": "", ...}, not null. There's really no way to do + // this right in Go (without adding `pointer: true` just for this purpose), + // and unmarshal(marshal(resp)) == resp should still hold, so we don't + // worry about it. client := graphql.NewClient(server.URL, http.DefaultClient) resp, err := queryWithVariables(ctx, client, "2") @@ -99,7 +104,7 @@ func TestOmitempty(t *testing.T) { ctx := context.Background() server := server.RunServer() defer server.Close() - client := graphql.NewClient(server.URL, http.DefaultClient) + client := newRoundtripClient(t, server.URL) resp, err := queryWithOmitempty(ctx, client, "2") require.NoError(t, err) @@ -126,7 +131,7 @@ func TestCustomMarshal(t *testing.T) { ctx := context.Background() server := server.RunServer() defer server.Close() - client := graphql.NewClient(server.URL, http.DefaultClient) + client := newRoundtripClient(t, server.URL) resp, err := queryWithCustomMarshal(ctx, client, time.Date(2025, time.January, 1, 12, 34, 56, 789, time.UTC)) @@ -155,7 +160,7 @@ func TestCustomMarshalSlice(t *testing.T) { ctx := context.Background() server := server.RunServer() defer server.Close() - client := graphql.NewClient(server.URL, http.DefaultClient) + client := newRoundtripClient(t, server.URL) resp, err := queryWithCustomMarshalSlice(ctx, client, []time.Time{time.Date(2025, time.January, 1, 12, 34, 56, 789, time.UTC)}) @@ -189,7 +194,7 @@ func TestCustomMarshalOptional(t *testing.T) { ctx := context.Background() server := server.RunServer() defer server.Close() - client := graphql.NewClient(server.URL, http.DefaultClient) + client := newRoundtripClient(t, server.URL) date := time.Date(2025, time.January, 1, 12, 34, 56, 789, time.UTC) resp, err := queryWithCustomMarshalOptional(ctx, client, &date, nil) @@ -223,7 +228,7 @@ func TestInterfaceNoFragments(t *testing.T) { ctx := context.Background() server := server.RunServer() defer server.Close() - client := graphql.NewClient(server.URL, http.DefaultClient) + client := newRoundtripClient(t, server.URL) resp, err := queryWithInterfaceNoFragments(ctx, client, "1") require.NoError(t, err) @@ -286,7 +291,7 @@ func TestInterfaceListField(t *testing.T) { ctx := context.Background() server := server.RunServer() defer server.Close() - client := graphql.NewClient(server.URL, http.DefaultClient) + client := newRoundtripClient(t, server.URL) resp, err := queryWithInterfaceListField(ctx, client, []string{"1", "3", "12847394823"}) @@ -333,7 +338,7 @@ func TestInterfaceListPointerField(t *testing.T) { ctx := context.Background() server := server.RunServer() defer server.Close() - client := graphql.NewClient(server.URL, http.DefaultClient) + client := newRoundtripClient(t, server.URL) resp, err := queryWithInterfaceListPointerField(ctx, client, []string{"1", "3", "12847394823"}) @@ -387,7 +392,7 @@ func TestFragments(t *testing.T) { ctx := context.Background() server := server.RunServer() defer server.Close() - client := graphql.NewClient(server.URL, http.DefaultClient) + client := newRoundtripClient(t, server.URL) resp, err := queryWithFragments(ctx, client, []string{"1", "3", "12847394823"}) require.NoError(t, err) @@ -480,7 +485,7 @@ func TestNamedFragments(t *testing.T) { ctx := context.Background() server := server.RunServer() defer server.Close() - client := graphql.NewClient(server.URL, http.DefaultClient) + client := newRoundtripClient(t, server.URL) resp, err := queryWithNamedFragments(ctx, client, []string{"1", "3", "12847394823"}) require.NoError(t, err) diff --git a/internal/integration/roundtrip.go b/internal/integration/roundtrip.go new file mode 100644 index 00000000..8b482951 --- /dev/null +++ b/internal/integration/roundtrip.go @@ -0,0 +1,111 @@ +package integration + +// Machinery for integration tests to round-trip check the JSON-marshalers and +// unmarshalers we generate. + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "testing" + + "github.com/Khan/genqlient/graphql" + "github.com/stretchr/testify/assert" +) + +// lastResponseTransport is an HTTP transport that keeps track of the last response +// that passed through it. +type lastResponseTransport struct { + wrapped http.RoundTripper + lastResponseBody []byte +} + +func (t *lastResponseTransport) RoundTrip(req *http.Request) (*http.Response, error) { + resp, err := t.wrapped.RoundTrip(req) + if err != nil { + return resp, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return resp, fmt.Errorf("roundtrip failed: unreadable body: %w", err) + } + t.lastResponseBody = body + // Restore the body for the next reader: + resp.Body = ioutil.NopCloser(bytes.NewBuffer(body)) + return resp, err +} + +// roundtripClient is a graphql.Client that checks that +// unmarshal(marshal(req)) == req && marshal(unmarshal(resp)) == resp +// for each request it processes. +type roundtripClient struct { + wrapped graphql.Client + transport *lastResponseTransport + t *testing.T +} + +// Put JSON in a stable and human-readable format. +func (c *roundtripClient) formatJSON(b []byte) []byte { + // We don't care about key ordering, so do another roundtrip through + // interface{} to drop that. + var parsed interface{} + err := json.Unmarshal(b, &parsed) + if err != nil { + c.t.Fatal(err) + } + + // When marshaling, add indents to make things human-readable. + b, err = json.MarshalIndent(parsed, "", " ") + if err != nil { + c.t.Fatal(err) + } + return b +} + +func (c *roundtripClient) roundtripResponse(resp interface{}) { + var graphqlResponse struct { + Data json.RawMessage `json:"data"` + } + err := json.Unmarshal(c.transport.lastResponseBody, &graphqlResponse) + if err != nil { + c.t.Error(err) + return + } + body := c.formatJSON(graphqlResponse.Data) + + // resp is constructed to be unmarshal(body), so just use it + bodyAgain, err := json.Marshal(resp) + if err != nil { + c.t.Error(err) + return + } + bodyAgain = c.formatJSON(bodyAgain) + + assert.Equal(c.t, string(body), string(bodyAgain)) +} + +func (c *roundtripClient) MakeRequest(ctx context.Context, opName, query string, retval, variables interface{}) error { + // TODO(benkraft): Also check the variables round-trip. This is a bit less + // important since most of the code is the same (and input types are + // strictly simpler), and a bit hard to do because when asserting about + // structs we need to worry about things like equality of time.Time values. + err := c.wrapped.MakeRequest(ctx, opName, query, retval, variables) + if err != nil { + return err + } + c.roundtripResponse(retval) + return nil +} + +func newRoundtripClient(t *testing.T, endpoint string) graphql.Client { + transport := &lastResponseTransport{wrapped: http.DefaultTransport} + return &roundtripClient{ + wrapped: graphql.NewClient(endpoint, &http.Client{Transport: transport}), + transport: transport, + t: t, + } +} diff --git a/internal/testutil/types.go b/internal/testutil/types.go index e160a121..52795abd 100644 --- a/internal/testutil/types.go +++ b/internal/testutil/types.go @@ -31,6 +31,9 @@ func GetClientFromMyContext(ctx MyContext) (graphql.Client, error) { return const dateFormat = "2006-01-02" func MarshalDate(t *time.Time) ([]byte, error) { + if t == nil || t.IsZero() { + return []byte("null"), nil + } return []byte(`"` + t.Format(dateFormat) + `"`), nil }