fix: premarshal structs get generated with omitempty tag (#267)

This PR addresses a bug described in #263

Basically, whenever a struct involves a custom genqlient binding, a
secondary "premarshal" struct gets generated.

The bug was that this "premarshal" struct was not propagating the
`omitempty` JSON tags, which was resulting in unexpected behavior.

The fix involved a few changed lines in the Go template, and a few
changes in the unit tests.

I have:
- [x] Written a clear PR title and description (above)
- [x] Signed the [Khan Academy CLA](https://www.khanacademy.org/r/cla)
- [x] Added tests covering my changes, if applicable
- [x] Included a link to the issue fixed, if applicable
- [x] Included documentation, for new features (n/a)
- [x] Added an entry to the changelog
This commit is contained in:
Kevin Chen
2023-05-05 09:05:20 -07:00
committed by GitHub
parent 4073a49614
commit 15f507b8de
8 changed files with 177 additions and 22 deletions
+1
View File
@@ -34,6 +34,7 @@ When releasing a new version:
- Fixed non-deterministic generated code when querying graphql interfaces.
- Fixed generated code when last component of package name is not a valid identifier (e.g. `"path/to/my-package"`).
- Fixed incorrect documentation of `for` directive.
- Fixed bug where `omitempty` JSON tags were not being correctly applied to `__premarshal` structs.
## v0.5.0
+2 -2
View File
@@ -31,9 +31,9 @@
type __premarshal{{.GoName}} struct{
{{range .FlattenedFields -}}
{{if .NeedsMarshaling -}}
{{.GoName}} {{repeat .GoType.SliceDepth "[]"}}{{ref "encoding/json.RawMessage"}} `json:"{{.JSONName}}"`
{{.GoName}} {{repeat .GoType.SliceDepth "[]"}}{{ref "encoding/json.RawMessage"}} `json:"{{.JSONName}}{{if .Omitempty -}},omitempty{{end}}"`
{{else}}
{{.GoName}} {{.GoType.Reference}} `json:"{{.JSONName}}"`
{{.GoName}} {{.GoType.Reference}} `json:"{{.JSONName}}{{if .Omitempty -}},omitempty{{end}}"`
{{end}}
{{end}}
}
+7
View File
@@ -0,0 +1,7 @@
# @genqlient(pointer: true)
query GetPokemon($where: getPokemonBoolExp!) {
getPokemon(where: $where) {
species
level
}
}
+20
View File
@@ -180,8 +180,28 @@ type Query {
listOfListsOfListsOfContent: [[[Content!]!]!]!
recur(input: RecursiveInput!): Recursive
acceptsListOfListOfListsOfDates(datesss: [[[Date!]!]!]!): Boolean
getPokemon(where: getPokemonBoolExp): [Pokemon!]!
}
type Mutation {
createUser(name: String!, email: String): User
}
input getPokemonBoolExp {
_and: [getPokemonBoolExp!]
_not: getPokemonBoolExp
_or: [getPokemonBoolExp!]
level: IntComparisonExp
}
input IntComparisonExp {
_eq: Int
_gt: Int
_gte: Int
_in: [Int!]
_isNull: Boolean
_lt: Int
_lte: Int
_neq: Int
_nin: [Int!]
}
@@ -0,0 +1,118 @@
// Code generated by github.com/Khan/genqlient, DO NOT EDIT.
package test
import (
"github.com/Khan/genqlient/graphql"
"github.com/Khan/genqlient/internal/testutil"
)
type GetPokemonBoolExp struct {
And []*GetPokemonBoolExp `json:"_and"`
Not *GetPokemonBoolExp `json:"_not"`
Or []*GetPokemonBoolExp `json:"_or"`
Level *IntComparisonExp `json:"level"`
}
// GetAnd returns GetPokemonBoolExp.And, and is useful for accessing the field via an interface.
func (v *GetPokemonBoolExp) GetAnd() []*GetPokemonBoolExp { return v.And }
// GetNot returns GetPokemonBoolExp.Not, and is useful for accessing the field via an interface.
func (v *GetPokemonBoolExp) GetNot() *GetPokemonBoolExp { return v.Not }
// GetOr returns GetPokemonBoolExp.Or, and is useful for accessing the field via an interface.
func (v *GetPokemonBoolExp) GetOr() []*GetPokemonBoolExp { return v.Or }
// GetLevel returns GetPokemonBoolExp.Level, and is useful for accessing the field via an interface.
func (v *GetPokemonBoolExp) GetLevel() *IntComparisonExp { return v.Level }
// GetPokemonResponse is returned by GetPokemon on success.
type GetPokemonResponse struct {
GetPokemon []*testutil.Pokemon `json:"getPokemon"`
}
// GetGetPokemon returns GetPokemonResponse.GetPokemon, and is useful for accessing the field via an interface.
func (v *GetPokemonResponse) GetGetPokemon() []*testutil.Pokemon { return v.GetPokemon }
type IntComparisonExp struct {
Eq *int `json:"_eq"`
Gt *int `json:"_gt"`
Gte *int `json:"_gte"`
In []*int `json:"_in"`
IsNull *bool `json:"_isNull"`
Lt *int `json:"_lt"`
Lte *int `json:"_lte"`
Neq *int `json:"_neq"`
Nin []*int `json:"_nin"`
}
// GetEq returns IntComparisonExp.Eq, and is useful for accessing the field via an interface.
func (v *IntComparisonExp) GetEq() *int { return v.Eq }
// GetGt returns IntComparisonExp.Gt, and is useful for accessing the field via an interface.
func (v *IntComparisonExp) GetGt() *int { return v.Gt }
// GetGte returns IntComparisonExp.Gte, and is useful for accessing the field via an interface.
func (v *IntComparisonExp) GetGte() *int { return v.Gte }
// GetIn returns IntComparisonExp.In, and is useful for accessing the field via an interface.
func (v *IntComparisonExp) GetIn() []*int { return v.In }
// GetIsNull returns IntComparisonExp.IsNull, and is useful for accessing the field via an interface.
func (v *IntComparisonExp) GetIsNull() *bool { return v.IsNull }
// GetLt returns IntComparisonExp.Lt, and is useful for accessing the field via an interface.
func (v *IntComparisonExp) GetLt() *int { return v.Lt }
// GetLte returns IntComparisonExp.Lte, and is useful for accessing the field via an interface.
func (v *IntComparisonExp) GetLte() *int { return v.Lte }
// GetNeq returns IntComparisonExp.Neq, and is useful for accessing the field via an interface.
func (v *IntComparisonExp) GetNeq() *int { return v.Neq }
// GetNin returns IntComparisonExp.Nin, and is useful for accessing the field via an interface.
func (v *IntComparisonExp) GetNin() []*int { return v.Nin }
// __GetPokemonInput is used internally by genqlient
type __GetPokemonInput struct {
Where *GetPokemonBoolExp `json:"where"`
}
// GetWhere returns __GetPokemonInput.Where, and is useful for accessing the field via an interface.
func (v *__GetPokemonInput) GetWhere() *GetPokemonBoolExp { return v.Where }
// The query or mutation executed by GetPokemon.
const GetPokemon_Operation = `
query GetPokemon ($where: getPokemonBoolExp!) {
getPokemon(where: $where) {
species
level
}
}
`
func GetPokemon(
client graphql.Client,
where *GetPokemonBoolExp,
) (*GetPokemonResponse, error) {
req := &graphql.Request{
OpName: "GetPokemon",
Query: GetPokemon_Operation,
Variables: &__GetPokemonInput{
Where: where,
},
}
var err error
var data GetPokemonResponse
resp := &graphql.Response{Data: &data}
err = client.MakeRequest(
nil,
req,
resp,
)
return &data, err
}
@@ -0,0 +1,9 @@
{
"operations": [
{
"operationName": "GetPokemon",
"query": "\nquery GetPokemon ($where: getPokemonBoolExp!) {\n\tgetPokemon(where: $where) {\n\t\tspecies\n\t\tlevel\n\t}\n}\n",
"sourceLocation": "testdata/queries/Hasura.graphql"
}
]
}
@@ -83,19 +83,19 @@ func (v *MyInput) UnmarshalJSON(b []byte) error {
}
type __premarshalMyInput struct {
Email *string `json:"email"`
Email *string `json:"email,omitempty"`
Name *string `json:"name"`
Name *string `json:"name,omitempty"`
Id *testutil.ID `json:"id"`
Id *testutil.ID `json:"id,omitempty"`
Role *Role `json:"role"`
Role *Role `json:"role,omitempty"`
Names []*string `json:"names"`
Names []*string `json:"names,omitempty"`
HasPokemon *testutil.Pokemon `json:"hasPokemon"`
HasPokemon *testutil.Pokemon `json:"hasPokemon,omitempty"`
Birthdate json.RawMessage `json:"birthdate"`
Birthdate json.RawMessage `json:"birthdate,omitempty"`
}
func (v *MyInput) MarshalJSON() ([]byte, error) {
@@ -264,19 +264,19 @@ func (v *UserQueryInput) UnmarshalJSON(b []byte) error {
}
type __premarshalUserQueryInput struct {
Email *string `json:"email"`
Email *string `json:"email,omitempty"`
Name *string `json:"name"`
Name *string `json:"name,omitempty"`
Id *testutil.ID `json:"id"`
Id *testutil.ID `json:"id,omitempty"`
Role *Role `json:"role"`
Role *Role `json:"role,omitempty"`
Names []*string `json:"names"`
Names []*string `json:"names,omitempty"`
HasPokemon *testutil.Pokemon `json:"hasPokemon"`
HasPokemon *testutil.Pokemon `json:"hasPokemon,omitempty"`
Birthdate json.RawMessage `json:"birthdate"`
Birthdate json.RawMessage `json:"birthdate,omitempty"`
}
func (v *UserQueryInput) MarshalJSON() ([]byte, error) {
@@ -148,19 +148,19 @@ func (v *UserQueryInput) UnmarshalJSON(b []byte) error {
}
type __premarshalUserQueryInput struct {
Email string `json:"email"`
Email string `json:"email,omitempty"`
Name string `json:"name"`
Name string `json:"name,omitempty"`
Id testutil.ID `json:"id"`
Role Role `json:"role"`
Role Role `json:"role,omitempty"`
Names []string `json:"names"`
Names []string `json:"names,omitempty"`
HasPokemon testutil.Pokemon `json:"hasPokemon"`
HasPokemon testutil.Pokemon `json:"hasPokemon,omitempty"`
Birthdate json.RawMessage `json:"birthdate"`
Birthdate json.RawMessage `json:"birthdate,omitempty"`
}
func (v *UserQueryInput) MarshalJSON() ([]byte, error) {