From da5fda3f4efc1992baaabd3dd627a10986d7f200 Mon Sep 17 00:00:00 2001 From: Ruben Mennes Date: Fri, 9 Feb 2024 12:44:40 +0100 Subject: [PATCH 1/8] Pointer omitempty --- generate/config.go | 4 ++-- generate/convert.go | 7 +++++++ generate/generate_test.go | 8 ++++++++ generate/types.go | 3 ++- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/generate/config.go b/generate/config.go index 94ee12c4..11c148df 100644 --- a/generate/config.go +++ b/generate/config.go @@ -157,8 +157,8 @@ func (c *Config) ValidateAndFillDefaults(baseDir string) error { c.ContextType = "context.Context" } - if c.Optional != "" && c.Optional != "value" && c.Optional != "pointer" && c.Optional != "generic" { - return errorf(nil, "optional must be one of: 'value' (default), 'pointer', or 'generic'") + if c.Optional != "" && c.Optional != "value" && c.Optional != "pointer" && c.Optional != "pointer_omitempty" && c.Optional != "generic" { + return errorf(nil, "optional must be one of: 'value' (default), 'pointer', 'pointer_omitempty' or 'generic'") } if c.Optional == "generic" && c.OptionalGenericType == "" { diff --git a/generate/convert.go b/generate/convert.go index 3b1f750a..79c369c4 100644 --- a/generate/convert.go +++ b/generate/convert.go @@ -262,6 +262,13 @@ func (g *generator) convertType( // options work, recursing here isn't as connvenient.) // Note this does []*T or [][]*T, not e.g. *[][]T. See #16. goTyp = &goPointerType{goTyp} + } else if !options.PointerIsFalse() && (options.GetPointer() || (!typ.NonNull && g.Config.Optional == "pointer_omitempty")) { + goTyp = &goPointerType{Elem: goTyp} + + if options.Omitempty == nil { + oe := true + options.Omitempty = &oe + } } else if !typ.NonNull && g.Config.Optional == "generic" { var genericRef string genericRef, err = g.ref(g.Config.OptionalGenericType) diff --git a/generate/generate_test.go b/generate/generate_test.go index 21dcaae2..af2f2d61 100644 --- a/generate/generate_test.go +++ b/generate/generate_test.go @@ -240,6 +240,14 @@ func TestGenerateWithConfig(t *testing.T) { Enums: map[string]CasingAlgorithm{"Role": CasingRaw}, }, }}, + {"OptionalPointerOmitEmpty", "", []string{ + "ListInput.graphql", + "QueryWithSlices.graphql", + "SimpleQueryWithPointerFalseOverride.graphql", + "SimpleQueryNoOverride.graphql", + }, &Config{ + Optional: "pointer_omitempty", + }}, } sourceFilename := "SimpleQuery.graphql" diff --git a/generate/types.go b/generate/types.go index 01f619f5..6879bc25 100644 --- a/generate/types.go +++ b/generate/types.go @@ -80,7 +80,8 @@ type ( // goSliceType represents the Go type *Elem, used when requested by the // user (perhaps to handle nulls explicitly, or to avoid copying large // structures). - goPointerType struct{ Elem goType } + goPointerType struct{ Elem goType } + goPointerOmitEmptyType struct{ goPointerType } // goGenericType represent the Go type GoGenericRef[Elem], used when requested by the // user to box nullable data without using pointers or sentinel values goGenericType struct { From 56d479dca237bb7266dab41a9c48584ecf7c21c4 Mon Sep 17 00:00:00 2001 From: Ruben Mennes Date: Fri, 9 Feb 2024 12:45:28 +0100 Subject: [PATCH 2/8] Migrate module to raito-io --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index bac27203..db2bb674 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/Khan/genqlient +module github.com/raito-io/genqlient go 1.18 From f397d71b9ebbd2b56c86332cc07102bb8dba15cd Mon Sep 17 00:00:00 2001 From: Ruben Mennes Date: Fri, 9 Feb 2024 13:34:58 +0100 Subject: [PATCH 3/8] Update tests --- docs/genqlient.yaml | 3 + generate/generate_test.go | 13 +- ...terOmitEmpty-testdata-queries-generated.go | 493 ++++++++++++++++++ .../TestInvalidConfigs-InvalidOptional.yaml | 2 +- generate/types.go | 3 +- 5 files changed, 509 insertions(+), 5 deletions(-) create mode 100644 generate/testdata/snapshots/TestGenerateWithConfig-OptionalPointerOmitEmpty-testdata-queries-generated.go diff --git a/docs/genqlient.yaml b/docs/genqlient.yaml index ddfb760b..ba471209 100644 --- a/docs/genqlient.yaml +++ b/docs/genqlient.yaml @@ -108,6 +108,9 @@ use_extensions: boolean # pointers-to-slices, so the GraphQL type `[String]` will map to the Go # type `[]*string`, not `*[]*string`; GraphQL null and empty list simply # map to Go nil- and empty-slice. +# - pointer_omitempty: optional fields are generated as pointers, as described above. +# Additionally, the fields are annotated with omitempty. Fields that are not defined, +# will not be exposed. # - generic: optional fields are generated as type parameters to a generic type # specified by `optional_generic_type`. E.g. fields with GraphQL type `String` # will map to the Go type `generic.Type[string]`. This is useful if you have a diff --git a/generate/generate_test.go b/generate/generate_test.go index af2f2d61..1fe505cc 100644 --- a/generate/generate_test.go +++ b/generate/generate_test.go @@ -9,8 +9,9 @@ import ( "strings" "testing" - "github.com/Khan/genqlient/internal/testutil" "gopkg.in/yaml.v2" + + "github.com/Khan/genqlient/internal/testutil" ) const ( @@ -241,12 +242,20 @@ func TestGenerateWithConfig(t *testing.T) { }, }}, {"OptionalPointerOmitEmpty", "", []string{ + "InputObject.graphql", "ListInput.graphql", "QueryWithSlices.graphql", "SimpleQueryWithPointerFalseOverride.graphql", "SimpleQueryNoOverride.graphql", }, &Config{ - Optional: "pointer_omitempty", + Optional: "pointer_omitempty", + Bindings: map[string]*TypeBinding{ + "Date": { + Type: "time.Time", + Marshaler: "github.com/Khan/genqlient/internal/testutil.MarshalDate", + Unmarshaler: "github.com/Khan/genqlient/internal/testutil.UnmarshalDate", + }, + }, }}, } diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OptionalPointerOmitEmpty-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OptionalPointerOmitEmpty-testdata-queries-generated.go new file mode 100644 index 00000000..e125e987 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OptionalPointerOmitEmpty-testdata-queries-generated.go @@ -0,0 +1,493 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package queries + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/Khan/genqlient/graphql" + "github.com/Khan/genqlient/internal/testutil" +) + +// InputObjectQueryResponse is returned by InputObjectQuery on success. +type InputObjectQueryResponse struct { + // user looks up a user by some stuff. + // + // See UserQueryInput for what stuff is supported. + // If query is null, returns the current user. + User *InputObjectQueryUser `json:"user"` +} + +// GetUser returns InputObjectQueryResponse.User, and is useful for accessing the field via an interface. +func (v *InputObjectQueryResponse) GetUser() *InputObjectQueryUser { return v.User } + +// InputObjectQueryUser includes the requested fields of the GraphQL type User. +// The GraphQL type's documentation follows. +// +// A User is a user! +type InputObjectQueryUser struct { + // id is the user's ID. + // + // It is stable, unique, and opaque, like all good IDs. + Id string `json:"id"` +} + +// GetId returns InputObjectQueryUser.Id, and is useful for accessing the field via an interface. +func (v *InputObjectQueryUser) GetId() string { return v.Id } + +// ListInputQueryResponse is returned by ListInputQuery on success. +type ListInputQueryResponse struct { + // user looks up a user by some stuff. + // + // See UserQueryInput for what stuff is supported. + // If query is null, returns the current user. + User *ListInputQueryUser `json:"user"` +} + +// GetUser returns ListInputQueryResponse.User, and is useful for accessing the field via an interface. +func (v *ListInputQueryResponse) GetUser() *ListInputQueryUser { return v.User } + +// ListInputQueryUser includes the requested fields of the GraphQL type User. +// The GraphQL type's documentation follows. +// +// A User is a user! +type ListInputQueryUser struct { + // id is the user's ID. + // + // It is stable, unique, and opaque, like all good IDs. + Id string `json:"id"` +} + +// GetId returns ListInputQueryUser.Id, and is useful for accessing the field via an interface. +func (v *ListInputQueryUser) GetId() string { return v.Id } + +type PokemonInput struct { + Species string `json:"species"` + Level int `json:"level"` +} + +// GetSpecies returns PokemonInput.Species, and is useful for accessing the field via an interface. +func (v *PokemonInput) GetSpecies() string { return v.Species } + +// GetLevel returns PokemonInput.Level, and is useful for accessing the field via an interface. +func (v *PokemonInput) GetLevel() int { return v.Level } + +// QueryWithSlicesResponse is returned by QueryWithSlices on success. +type QueryWithSlicesResponse struct { + // user looks up a user by some stuff. + // + // See UserQueryInput for what stuff is supported. + // If query is null, returns the current user. + User *QueryWithSlicesUser `json:"user"` +} + +// GetUser returns QueryWithSlicesResponse.User, and is useful for accessing the field via an interface. +func (v *QueryWithSlicesResponse) GetUser() *QueryWithSlicesUser { return v.User } + +// QueryWithSlicesUser includes the requested fields of the GraphQL type User. +// The GraphQL type's documentation follows. +// +// A User is a user! +type QueryWithSlicesUser struct { + Emails []string `json:"emails"` + EmailsOrNull []string `json:"emailsOrNull"` + EmailsWithNulls []*string `json:"emailsWithNulls"` + EmailsWithNullsOrNull []*string `json:"emailsWithNullsOrNull"` +} + +// GetEmails returns QueryWithSlicesUser.Emails, and is useful for accessing the field via an interface. +func (v *QueryWithSlicesUser) GetEmails() []string { return v.Emails } + +// GetEmailsOrNull returns QueryWithSlicesUser.EmailsOrNull, and is useful for accessing the field via an interface. +func (v *QueryWithSlicesUser) GetEmailsOrNull() []string { return v.EmailsOrNull } + +// GetEmailsWithNulls returns QueryWithSlicesUser.EmailsWithNulls, and is useful for accessing the field via an interface. +func (v *QueryWithSlicesUser) GetEmailsWithNulls() []*string { return v.EmailsWithNulls } + +// GetEmailsWithNullsOrNull returns QueryWithSlicesUser.EmailsWithNullsOrNull, and is useful for accessing the field via an interface. +func (v *QueryWithSlicesUser) GetEmailsWithNullsOrNull() []*string { return v.EmailsWithNullsOrNull } + +// Role is a type a user may have. +type Role string + +const ( + // What is a student? + // + // A student is primarily a person enrolled in a school or other educational institution and who is under learning with goals of acquiring knowledge, developing professions and achieving employment at desired field. In the broader sense, a student is anyone who applies themselves to the intensive intellectual engagement with some matter necessary to master it as part of some practical affair in which such mastery is basic or decisive. + // + // (from [Wikipedia](https://en.wikipedia.org/wiki/Student)) + RoleStudent Role = "STUDENT" + // Teacher is a teacher, who teaches the students. + RoleTeacher Role = "TEACHER" +) + +// SimpleQueryNoOverrideResponse is returned by SimpleQueryNoOverride on success. +type SimpleQueryNoOverrideResponse struct { + // user looks up a user by some stuff. + // + // See UserQueryInput for what stuff is supported. + // If query is null, returns the current user. + User *SimpleQueryNoOverrideUser `json:"user"` +} + +// GetUser returns SimpleQueryNoOverrideResponse.User, and is useful for accessing the field via an interface. +func (v *SimpleQueryNoOverrideResponse) GetUser() *SimpleQueryNoOverrideUser { return v.User } + +// SimpleQueryNoOverrideUser includes the requested fields of the GraphQL type User. +// The GraphQL type's documentation follows. +// +// A User is a user! +type SimpleQueryNoOverrideUser struct { + // id is the user's ID. + // + // It is stable, unique, and opaque, like all good IDs. + Id string `json:"id"` + Name *string `json:"name"` +} + +// GetId returns SimpleQueryNoOverrideUser.Id, and is useful for accessing the field via an interface. +func (v *SimpleQueryNoOverrideUser) GetId() string { return v.Id } + +// GetName returns SimpleQueryNoOverrideUser.Name, and is useful for accessing the field via an interface. +func (v *SimpleQueryNoOverrideUser) GetName() *string { return v.Name } + +// SimpleQueryWithPointerFalseOverrideResponse is returned by SimpleQueryWithPointerFalseOverride on success. +type SimpleQueryWithPointerFalseOverrideResponse struct { + // user looks up a user by some stuff. + // + // See UserQueryInput for what stuff is supported. + // If query is null, returns the current user. + User *SimpleQueryWithPointerFalseOverrideUser `json:"user"` +} + +// GetUser returns SimpleQueryWithPointerFalseOverrideResponse.User, and is useful for accessing the field via an interface. +func (v *SimpleQueryWithPointerFalseOverrideResponse) GetUser() *SimpleQueryWithPointerFalseOverrideUser { + return v.User +} + +// SimpleQueryWithPointerFalseOverrideUser includes the requested fields of the GraphQL type User. +// The GraphQL type's documentation follows. +// +// A User is a user! +type SimpleQueryWithPointerFalseOverrideUser struct { + // id is the user's ID. + // + // It is stable, unique, and opaque, like all good IDs. + Id string `json:"id"` + Name string `json:"name"` +} + +// GetId returns SimpleQueryWithPointerFalseOverrideUser.Id, and is useful for accessing the field via an interface. +func (v *SimpleQueryWithPointerFalseOverrideUser) GetId() string { return v.Id } + +// GetName returns SimpleQueryWithPointerFalseOverrideUser.Name, and is useful for accessing the field via an interface. +func (v *SimpleQueryWithPointerFalseOverrideUser) GetName() string { return v.Name } + +// UserQueryInput is the argument to Query.users. +// +// Ideally this would support anything and everything! +// Or maybe ideally it wouldn't. +// Really I'm just talking to make this documentation longer. +type UserQueryInput struct { + Email *string `json:"email,omitempty"` + Name *string `json:"name,omitempty"` + // id looks the user up by ID. It's a great way to look up users. + Id *string `json:"id,omitempty"` + Role *Role `json:"role,omitempty"` + Names []*string `json:"names,omitempty"` + HasPokemon *PokemonInput `json:"hasPokemon,omitempty"` + Birthdate *time.Time `json:"-"` +} + +// GetEmail returns UserQueryInput.Email, and is useful for accessing the field via an interface. +func (v *UserQueryInput) GetEmail() *string { return v.Email } + +// GetName returns UserQueryInput.Name, and is useful for accessing the field via an interface. +func (v *UserQueryInput) GetName() *string { return v.Name } + +// GetId returns UserQueryInput.Id, and is useful for accessing the field via an interface. +func (v *UserQueryInput) GetId() *string { return v.Id } + +// GetRole returns UserQueryInput.Role, and is useful for accessing the field via an interface. +func (v *UserQueryInput) GetRole() *Role { return v.Role } + +// GetNames returns UserQueryInput.Names, and is useful for accessing the field via an interface. +func (v *UserQueryInput) GetNames() []*string { return v.Names } + +// GetHasPokemon returns UserQueryInput.HasPokemon, and is useful for accessing the field via an interface. +func (v *UserQueryInput) GetHasPokemon() *PokemonInput { return v.HasPokemon } + +// GetBirthdate returns UserQueryInput.Birthdate, and is useful for accessing the field via an interface. +func (v *UserQueryInput) GetBirthdate() *time.Time { return v.Birthdate } + +func (v *UserQueryInput) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *UserQueryInput + Birthdate json.RawMessage `json:"birthdate"` + 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,omitempty"` + + Name *string `json:"name,omitempty"` + + Id *string `json:"id,omitempty"` + + Role *Role `json:"role,omitempty"` + + Names []*string `json:"names,omitempty"` + + HasPokemon *PokemonInput `json:"hasPokemon,omitempty"` + + Birthdate json.RawMessage `json:"birthdate,omitempty"` +} + +func (v *UserQueryInput) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + 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 := &retval.Birthdate + src := v.Birthdate + if src != nil { + var err error + *dst, err = testutil.MarshalDate( + src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal UserQueryInput.Birthdate: %w", err) + } + } + } + return &retval, nil +} + +// __InputObjectQueryInput is used internally by genqlient +type __InputObjectQueryInput struct { + Query *UserQueryInput `json:"query,omitempty"` +} + +// GetQuery returns __InputObjectQueryInput.Query, and is useful for accessing the field via an interface. +func (v *__InputObjectQueryInput) GetQuery() *UserQueryInput { return v.Query } + +// __ListInputQueryInput is used internally by genqlient +type __ListInputQueryInput struct { + Names []*string `json:"names,omitempty"` +} + +// GetNames returns __ListInputQueryInput.Names, and is useful for accessing the field via an interface. +func (v *__ListInputQueryInput) GetNames() []*string { return v.Names } + +// The query or mutation executed by InputObjectQuery. +const InputObjectQuery_Operation = ` +query InputObjectQuery ($query: UserQueryInput) { + user(query: $query) { + id + } +} +` + +func InputObjectQuery( + ctx_ context.Context, + client_ graphql.Client, + query *UserQueryInput, +) (*InputObjectQueryResponse, error) { + req_ := &graphql.Request{ + OpName: "InputObjectQuery", + Query: InputObjectQuery_Operation, + Variables: &__InputObjectQueryInput{ + Query: query, + }, + } + var err_ error + + var data_ InputObjectQueryResponse + resp_ := &graphql.Response{Data: &data_} + + err_ = client_.MakeRequest( + ctx_, + req_, + resp_, + ) + + return &data_, err_ +} + +// The query or mutation executed by ListInputQuery. +const ListInputQuery_Operation = ` +query ListInputQuery ($names: [String]) { + user(query: {names:$names}) { + id + } +} +` + +func ListInputQuery( + ctx_ context.Context, + client_ graphql.Client, + names []*string, +) (*ListInputQueryResponse, error) { + req_ := &graphql.Request{ + OpName: "ListInputQuery", + Query: ListInputQuery_Operation, + Variables: &__ListInputQueryInput{ + Names: names, + }, + } + var err_ error + + var data_ ListInputQueryResponse + resp_ := &graphql.Response{Data: &data_} + + err_ = client_.MakeRequest( + ctx_, + req_, + resp_, + ) + + return &data_, err_ +} + +// The query or mutation executed by QueryWithSlices. +const QueryWithSlices_Operation = ` +query QueryWithSlices { + user { + emails + emailsOrNull + emailsWithNulls + emailsWithNullsOrNull + } +} +` + +func QueryWithSlices( + ctx_ context.Context, + client_ graphql.Client, +) (*QueryWithSlicesResponse, error) { + req_ := &graphql.Request{ + OpName: "QueryWithSlices", + Query: QueryWithSlices_Operation, + } + var err_ error + + var data_ QueryWithSlicesResponse + resp_ := &graphql.Response{Data: &data_} + + err_ = client_.MakeRequest( + ctx_, + req_, + resp_, + ) + + return &data_, err_ +} + +// The query or mutation executed by SimpleQueryNoOverride. +const SimpleQueryNoOverride_Operation = ` +query SimpleQueryNoOverride { + user { + id + name + } +} +` + +func SimpleQueryNoOverride( + ctx_ context.Context, + client_ graphql.Client, +) (*SimpleQueryNoOverrideResponse, error) { + req_ := &graphql.Request{ + OpName: "SimpleQueryNoOverride", + Query: SimpleQueryNoOverride_Operation, + } + var err_ error + + var data_ SimpleQueryNoOverrideResponse + resp_ := &graphql.Response{Data: &data_} + + err_ = client_.MakeRequest( + ctx_, + req_, + resp_, + ) + + return &data_, err_ +} + +// The query or mutation executed by SimpleQueryWithPointerFalseOverride. +const SimpleQueryWithPointerFalseOverride_Operation = ` +query SimpleQueryWithPointerFalseOverride { + user { + id + name + } +} +` + +func SimpleQueryWithPointerFalseOverride( + ctx_ context.Context, + client_ graphql.Client, +) (*SimpleQueryWithPointerFalseOverrideResponse, error) { + req_ := &graphql.Request{ + OpName: "SimpleQueryWithPointerFalseOverride", + Query: SimpleQueryWithPointerFalseOverride_Operation, + } + var err_ error + + var data_ SimpleQueryWithPointerFalseOverrideResponse + resp_ := &graphql.Response{Data: &data_} + + err_ = client_.MakeRequest( + ctx_, + req_, + resp_, + ) + + return &data_, err_ +} + diff --git a/generate/testdata/snapshots/TestInvalidConfigs-InvalidOptional.yaml b/generate/testdata/snapshots/TestInvalidConfigs-InvalidOptional.yaml index fc5394bf..d0ece9f0 100644 --- a/generate/testdata/snapshots/TestInvalidConfigs-InvalidOptional.yaml +++ b/generate/testdata/snapshots/TestInvalidConfigs-InvalidOptional.yaml @@ -1 +1 @@ -invalid config file testdata/invalid-config/InvalidOptional.yaml: optional must be one of: 'value' (default), 'pointer', or 'generic' +invalid config file testdata/invalid-config/InvalidOptional.yaml: optional must be one of: 'value' (default), 'pointer', 'pointer_omitempty' or 'generic' diff --git a/generate/types.go b/generate/types.go index 6879bc25..01f619f5 100644 --- a/generate/types.go +++ b/generate/types.go @@ -80,8 +80,7 @@ type ( // goSliceType represents the Go type *Elem, used when requested by the // user (perhaps to handle nulls explicitly, or to avoid copying large // structures). - goPointerType struct{ Elem goType } - goPointerOmitEmptyType struct{ goPointerType } + goPointerType struct{ Elem goType } // goGenericType represent the Go type GoGenericRef[Elem], used when requested by the // user to box nullable data without using pointers or sentinel values goGenericType struct { From 98fde7d00499dee0f6e2b446891b3d46baff37c2 Mon Sep 17 00:00:00 2001 From: Ruben Mennes Date: Fri, 9 Feb 2024 13:35:22 +0100 Subject: [PATCH 4/8] Revert "Migrate module to raito-io" This reverts commit 56d479dca237bb7266dab41a9c48584ecf7c21c4. --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index db2bb674..bac27203 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/raito-io/genqlient +module github.com/Khan/genqlient go 1.18 From d59a1b5c2d56168c52d27bfd89808113bdcfb508 Mon Sep 17 00:00:00 2001 From: Ruben Mennes Date: Fri, 9 Feb 2024 14:01:52 +0100 Subject: [PATCH 5/8] Update change log and test --- docs/CHANGELOG.md | 1 + generate/generate_test.go | 10 +- ...terOmitEmpty-testdata-queries-generated.go | 636 ++++++++++++++---- 3 files changed, 517 insertions(+), 130 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index bec20ef4..8605776b 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -25,6 +25,7 @@ When releasing a new version: ### New features: - The new `optional: generic` allows using a generic type to represent optionality. See the [documentation](genqlient.yaml) for details. +- The new `optional: pointer_omitempty` allows using a pointer that is also annotated with `omitempty`. See the [documentation](genqlient.yaml) for details. - For schemas with enum values that differ only in casing, it's now possible to disable smart-casing in genqlient.yaml; see the [documentation](genqlient.yaml) for `casing` for details. ### Bug fixes: diff --git a/generate/generate_test.go b/generate/generate_test.go index 1fe505cc..e5f0ba04 100644 --- a/generate/generate_test.go +++ b/generate/generate_test.go @@ -243,10 +243,9 @@ func TestGenerateWithConfig(t *testing.T) { }}, {"OptionalPointerOmitEmpty", "", []string{ "InputObject.graphql", + "Pointers.graphql", + "Omitempty.graphql", "ListInput.graphql", - "QueryWithSlices.graphql", - "SimpleQueryWithPointerFalseOverride.graphql", - "SimpleQueryNoOverride.graphql", }, &Config{ Optional: "pointer_omitempty", Bindings: map[string]*TypeBinding{ @@ -255,6 +254,11 @@ func TestGenerateWithConfig(t *testing.T) { Marshaler: "github.com/Khan/genqlient/internal/testutil.MarshalDate", Unmarshaler: "github.com/Khan/genqlient/internal/testutil.UnmarshalDate", }, + "DateTime": { + Type: "time.Time", + Marshaler: "github.com/Khan/genqlient/internal/testutil.MarshalDate", + Unmarshaler: "github.com/Khan/genqlient/internal/testutil.UnmarshalDate", + }, }, }}, } diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OptionalPointerOmitEmpty-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OptionalPointerOmitEmpty-testdata-queries-generated.go index e125e987..73a9142e 100644 --- a/generate/testdata/snapshots/TestGenerateWithConfig-OptionalPointerOmitEmpty-testdata-queries-generated.go +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OptionalPointerOmitEmpty-testdata-queries-generated.go @@ -64,127 +64,325 @@ type ListInputQueryUser struct { // GetId returns ListInputQueryUser.Id, and is useful for accessing the field via an interface. func (v *ListInputQueryUser) GetId() string { return v.Id } -type PokemonInput struct { - Species string `json:"species"` - Level int `json:"level"` -} - -// GetSpecies returns PokemonInput.Species, and is useful for accessing the field via an interface. -func (v *PokemonInput) GetSpecies() string { return v.Species } - -// GetLevel returns PokemonInput.Level, and is useful for accessing the field via an interface. -func (v *PokemonInput) GetLevel() int { return v.Level } - -// QueryWithSlicesResponse is returned by QueryWithSlices on success. -type QueryWithSlicesResponse struct { +// OmitEmptyQueryResponse is returned by OmitEmptyQuery on success. +type OmitEmptyQueryResponse struct { // user looks up a user by some stuff. // // See UserQueryInput for what stuff is supported. // If query is null, returns the current user. - User *QueryWithSlicesUser `json:"user"` + User *OmitEmptyQueryUser `json:"user"` + Users []*OmitEmptyQueryUsersUser `json:"users"` + MaybeConvert *time.Time `json:"-"` + Convert2 *time.Time `json:"-"` } -// GetUser returns QueryWithSlicesResponse.User, and is useful for accessing the field via an interface. -func (v *QueryWithSlicesResponse) GetUser() *QueryWithSlicesUser { return v.User } +// GetUser returns OmitEmptyQueryResponse.User, and is useful for accessing the field via an interface. +func (v *OmitEmptyQueryResponse) GetUser() *OmitEmptyQueryUser { return v.User } -// QueryWithSlicesUser includes the requested fields of the GraphQL type User. -// The GraphQL type's documentation follows. -// -// A User is a user! -type QueryWithSlicesUser struct { - Emails []string `json:"emails"` - EmailsOrNull []string `json:"emailsOrNull"` - EmailsWithNulls []*string `json:"emailsWithNulls"` - EmailsWithNullsOrNull []*string `json:"emailsWithNullsOrNull"` +// GetUsers returns OmitEmptyQueryResponse.Users, and is useful for accessing the field via an interface. +func (v *OmitEmptyQueryResponse) GetUsers() []*OmitEmptyQueryUsersUser { return v.Users } + +// GetMaybeConvert returns OmitEmptyQueryResponse.MaybeConvert, and is useful for accessing the field via an interface. +func (v *OmitEmptyQueryResponse) GetMaybeConvert() *time.Time { return v.MaybeConvert } + +// GetConvert2 returns OmitEmptyQueryResponse.Convert2, and is useful for accessing the field via an interface. +func (v *OmitEmptyQueryResponse) GetConvert2() *time.Time { return v.Convert2 } + +func (v *OmitEmptyQueryResponse) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *OmitEmptyQueryResponse + MaybeConvert json.RawMessage `json:"maybeConvert"` + Convert2 json.RawMessage `json:"convert2"` + graphql.NoUnmarshalJSON + } + firstPass.OmitEmptyQueryResponse = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.MaybeConvert + src := firstPass.MaybeConvert + 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 OmitEmptyQueryResponse.MaybeConvert: %w", err) + } + } + } + + { + dst := &v.Convert2 + src := firstPass.Convert2 + 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 OmitEmptyQueryResponse.Convert2: %w", err) + } + } + } + return nil } -// GetEmails returns QueryWithSlicesUser.Emails, and is useful for accessing the field via an interface. -func (v *QueryWithSlicesUser) GetEmails() []string { return v.Emails } +type __premarshalOmitEmptyQueryResponse struct { + User *OmitEmptyQueryUser `json:"user"` -// GetEmailsOrNull returns QueryWithSlicesUser.EmailsOrNull, and is useful for accessing the field via an interface. -func (v *QueryWithSlicesUser) GetEmailsOrNull() []string { return v.EmailsOrNull } + Users []*OmitEmptyQueryUsersUser `json:"users"` -// GetEmailsWithNulls returns QueryWithSlicesUser.EmailsWithNulls, and is useful for accessing the field via an interface. -func (v *QueryWithSlicesUser) GetEmailsWithNulls() []*string { return v.EmailsWithNulls } + MaybeConvert json.RawMessage `json:"maybeConvert"` -// GetEmailsWithNullsOrNull returns QueryWithSlicesUser.EmailsWithNullsOrNull, and is useful for accessing the field via an interface. -func (v *QueryWithSlicesUser) GetEmailsWithNullsOrNull() []*string { return v.EmailsWithNullsOrNull } + Convert2 json.RawMessage `json:"convert2"` +} -// Role is a type a user may have. -type Role string +func (v *OmitEmptyQueryResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} -const ( - // What is a student? - // - // A student is primarily a person enrolled in a school or other educational institution and who is under learning with goals of acquiring knowledge, developing professions and achieving employment at desired field. In the broader sense, a student is anyone who applies themselves to the intensive intellectual engagement with some matter necessary to master it as part of some practical affair in which such mastery is basic or decisive. - // - // (from [Wikipedia](https://en.wikipedia.org/wiki/Student)) - RoleStudent Role = "STUDENT" - // Teacher is a teacher, who teaches the students. - RoleTeacher Role = "TEACHER" -) +func (v *OmitEmptyQueryResponse) __premarshalJSON() (*__premarshalOmitEmptyQueryResponse, error) { + var retval __premarshalOmitEmptyQueryResponse -// SimpleQueryNoOverrideResponse is returned by SimpleQueryNoOverride on success. -type SimpleQueryNoOverrideResponse struct { - // user looks up a user by some stuff. + retval.User = v.User + retval.Users = v.Users + { + + dst := &retval.MaybeConvert + src := v.MaybeConvert + if src != nil { + var err error + *dst, err = testutil.MarshalDate( + src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal OmitEmptyQueryResponse.MaybeConvert: %w", err) + } + } + } + { + + dst := &retval.Convert2 + src := v.Convert2 + if src != nil { + var err error + *dst, err = testutil.MarshalDate( + src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal OmitEmptyQueryResponse.Convert2: %w", err) + } + } + } + return &retval, nil +} + +// OmitEmptyQueryUser includes the requested fields of the GraphQL type User. +// The GraphQL type's documentation follows. +// +// A User is a user! +type OmitEmptyQueryUser struct { + // id is the user's ID. // - // See UserQueryInput for what stuff is supported. - // If query is null, returns the current user. - User *SimpleQueryNoOverrideUser `json:"user"` + // It is stable, unique, and opaque, like all good IDs. + Id string `json:"id"` } -// GetUser returns SimpleQueryNoOverrideResponse.User, and is useful for accessing the field via an interface. -func (v *SimpleQueryNoOverrideResponse) GetUser() *SimpleQueryNoOverrideUser { return v.User } +// GetId returns OmitEmptyQueryUser.Id, and is useful for accessing the field via an interface. +func (v *OmitEmptyQueryUser) GetId() string { return v.Id } -// SimpleQueryNoOverrideUser includes the requested fields of the GraphQL type User. +// OmitEmptyQueryUsersUser includes the requested fields of the GraphQL type User. // The GraphQL type's documentation follows. // // A User is a user! -type SimpleQueryNoOverrideUser struct { +type OmitEmptyQueryUsersUser struct { // id is the user's ID. // // It is stable, unique, and opaque, like all good IDs. - Id string `json:"id"` - Name *string `json:"name"` + Id string `json:"id"` } -// GetId returns SimpleQueryNoOverrideUser.Id, and is useful for accessing the field via an interface. -func (v *SimpleQueryNoOverrideUser) GetId() string { return v.Id } +// GetId returns OmitEmptyQueryUsersUser.Id, and is useful for accessing the field via an interface. +func (v *OmitEmptyQueryUsersUser) GetId() string { return v.Id } + +// PointersQueryOtherUser includes the requested fields of the GraphQL type User. +// The GraphQL type's documentation follows. +// +// A User is a user! +type PointersQueryOtherUser struct { + // id is the user's ID. + // + // It is stable, unique, and opaque, like all good IDs. + Id string `json:"id"` +} -// GetName returns SimpleQueryNoOverrideUser.Name, and is useful for accessing the field via an interface. -func (v *SimpleQueryNoOverrideUser) GetName() *string { return v.Name } +// GetId returns PointersQueryOtherUser.Id, and is useful for accessing the field via an interface. +func (v *PointersQueryOtherUser) GetId() string { return v.Id } -// SimpleQueryWithPointerFalseOverrideResponse is returned by SimpleQueryWithPointerFalseOverride on success. -type SimpleQueryWithPointerFalseOverrideResponse struct { +// PointersQueryResponse is returned by PointersQuery on success. +type PointersQueryResponse struct { + // user looks up a user by some stuff. + // + // See UserQueryInput for what stuff is supported. + // If query is null, returns the current user. + User *PointersQueryUser `json:"user"` // user looks up a user by some stuff. // // See UserQueryInput for what stuff is supported. // If query is null, returns the current user. - User *SimpleQueryWithPointerFalseOverrideUser `json:"user"` + OtherUser *PointersQueryOtherUser `json:"otherUser"` + MaybeConvert *time.Time `json:"-"` } -// GetUser returns SimpleQueryWithPointerFalseOverrideResponse.User, and is useful for accessing the field via an interface. -func (v *SimpleQueryWithPointerFalseOverrideResponse) GetUser() *SimpleQueryWithPointerFalseOverrideUser { - return v.User +// GetUser returns PointersQueryResponse.User, and is useful for accessing the field via an interface. +func (v *PointersQueryResponse) GetUser() *PointersQueryUser { return v.User } + +// GetOtherUser returns PointersQueryResponse.OtherUser, and is useful for accessing the field via an interface. +func (v *PointersQueryResponse) GetOtherUser() *PointersQueryOtherUser { return v.OtherUser } + +// GetMaybeConvert returns PointersQueryResponse.MaybeConvert, and is useful for accessing the field via an interface. +func (v *PointersQueryResponse) GetMaybeConvert() *time.Time { return v.MaybeConvert } + +func (v *PointersQueryResponse) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *PointersQueryResponse + MaybeConvert json.RawMessage `json:"maybeConvert"` + graphql.NoUnmarshalJSON + } + firstPass.PointersQueryResponse = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.MaybeConvert + src := firstPass.MaybeConvert + 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 PointersQueryResponse.MaybeConvert: %w", err) + } + } + } + return nil +} + +type __premarshalPointersQueryResponse struct { + User *PointersQueryUser `json:"user"` + + OtherUser *PointersQueryOtherUser `json:"otherUser"` + + MaybeConvert json.RawMessage `json:"maybeConvert"` +} + +func (v *PointersQueryResponse) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *PointersQueryResponse) __premarshalJSON() (*__premarshalPointersQueryResponse, error) { + var retval __premarshalPointersQueryResponse + + retval.User = v.User + retval.OtherUser = v.OtherUser + { + + dst := &retval.MaybeConvert + src := v.MaybeConvert + if src != nil { + var err error + *dst, err = testutil.MarshalDate( + src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal PointersQueryResponse.MaybeConvert: %w", err) + } + } + } + return &retval, nil } -// SimpleQueryWithPointerFalseOverrideUser includes the requested fields of the GraphQL type User. +// PointersQueryUser includes the requested fields of the GraphQL type User. // The GraphQL type's documentation follows. // // A User is a user! -type SimpleQueryWithPointerFalseOverrideUser struct { +type PointersQueryUser struct { // id is the user's ID. // // It is stable, unique, and opaque, like all good IDs. - Id string `json:"id"` - Name string `json:"name"` + Id *string `json:"id"` + Roles []*Role `json:"roles"` + Name *string `json:"name"` + Emails []*string `json:"emails"` + EmailsNoPtr []string `json:"emailsNoPtr"` } -// GetId returns SimpleQueryWithPointerFalseOverrideUser.Id, and is useful for accessing the field via an interface. -func (v *SimpleQueryWithPointerFalseOverrideUser) GetId() string { return v.Id } +// GetId returns PointersQueryUser.Id, and is useful for accessing the field via an interface. +func (v *PointersQueryUser) GetId() *string { return v.Id } -// GetName returns SimpleQueryWithPointerFalseOverrideUser.Name, and is useful for accessing the field via an interface. -func (v *SimpleQueryWithPointerFalseOverrideUser) GetName() string { return v.Name } +// GetRoles returns PointersQueryUser.Roles, and is useful for accessing the field via an interface. +func (v *PointersQueryUser) GetRoles() []*Role { return v.Roles } + +// GetName returns PointersQueryUser.Name, and is useful for accessing the field via an interface. +func (v *PointersQueryUser) GetName() *string { return v.Name } + +// GetEmails returns PointersQueryUser.Emails, and is useful for accessing the field via an interface. +func (v *PointersQueryUser) GetEmails() []*string { return v.Emails } + +// GetEmailsNoPtr returns PointersQueryUser.EmailsNoPtr, and is useful for accessing the field via an interface. +func (v *PointersQueryUser) GetEmailsNoPtr() []string { return v.EmailsNoPtr } + +type PokemonInput struct { + Species string `json:"species"` + Level int `json:"level"` +} + +// GetSpecies returns PokemonInput.Species, and is useful for accessing the field via an interface. +func (v *PokemonInput) GetSpecies() string { return v.Species } + +// GetLevel returns PokemonInput.Level, and is useful for accessing the field via an interface. +func (v *PokemonInput) GetLevel() int { return v.Level } + +// Role is a type a user may have. +type Role string + +const ( + // What is a student? + // + // A student is primarily a person enrolled in a school or other educational institution and who is under learning with goals of acquiring knowledge, developing professions and achieving employment at desired field. In the broader sense, a student is anyone who applies themselves to the intensive intellectual engagement with some matter necessary to master it as part of some practical affair in which such mastery is basic or decisive. + // + // (from [Wikipedia](https://en.wikipedia.org/wiki/Student)) + RoleStudent Role = "STUDENT" + // Teacher is a teacher, who teaches the students. + RoleTeacher Role = "TEACHER" +) // UserQueryInput is the argument to Query.users. // @@ -323,6 +521,193 @@ type __ListInputQueryInput struct { // GetNames returns __ListInputQueryInput.Names, and is useful for accessing the field via an interface. func (v *__ListInputQueryInput) GetNames() []*string { return v.Names } +// __OmitEmptyQueryInput is used internally by genqlient +type __OmitEmptyQueryInput struct { + Query *UserQueryInput `json:"query,omitempty"` + Queries []*UserQueryInput `json:"queries,omitempty"` + Dt *time.Time `json:"-"` + Tz *string `json:"tz,omitempty"` + TzNoOmitEmpty *string `json:"tzNoOmitEmpty"` +} + +// GetQuery returns __OmitEmptyQueryInput.Query, and is useful for accessing the field via an interface. +func (v *__OmitEmptyQueryInput) GetQuery() *UserQueryInput { return v.Query } + +// GetQueries returns __OmitEmptyQueryInput.Queries, and is useful for accessing the field via an interface. +func (v *__OmitEmptyQueryInput) GetQueries() []*UserQueryInput { return v.Queries } + +// GetDt returns __OmitEmptyQueryInput.Dt, and is useful for accessing the field via an interface. +func (v *__OmitEmptyQueryInput) GetDt() *time.Time { return v.Dt } + +// GetTz returns __OmitEmptyQueryInput.Tz, and is useful for accessing the field via an interface. +func (v *__OmitEmptyQueryInput) GetTz() *string { return v.Tz } + +// GetTzNoOmitEmpty returns __OmitEmptyQueryInput.TzNoOmitEmpty, and is useful for accessing the field via an interface. +func (v *__OmitEmptyQueryInput) GetTzNoOmitEmpty() *string { return v.TzNoOmitEmpty } + +func (v *__OmitEmptyQueryInput) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *__OmitEmptyQueryInput + Dt json.RawMessage `json:"dt"` + graphql.NoUnmarshalJSON + } + firstPass.__OmitEmptyQueryInput = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.Dt + src := firstPass.Dt + 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 __OmitEmptyQueryInput.Dt: %w", err) + } + } + } + return nil +} + +type __premarshal__OmitEmptyQueryInput struct { + Query *UserQueryInput `json:"query,omitempty"` + + Queries []*UserQueryInput `json:"queries,omitempty"` + + Dt json.RawMessage `json:"dt,omitempty"` + + Tz *string `json:"tz,omitempty"` + + TzNoOmitEmpty *string `json:"tzNoOmitEmpty"` +} + +func (v *__OmitEmptyQueryInput) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *__OmitEmptyQueryInput) __premarshalJSON() (*__premarshal__OmitEmptyQueryInput, error) { + var retval __premarshal__OmitEmptyQueryInput + + retval.Query = v.Query + retval.Queries = v.Queries + { + + dst := &retval.Dt + src := v.Dt + if src != nil { + var err error + *dst, err = testutil.MarshalDate( + src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal __OmitEmptyQueryInput.Dt: %w", err) + } + } + } + retval.Tz = v.Tz + retval.TzNoOmitEmpty = v.TzNoOmitEmpty + return &retval, nil +} + +// __PointersQueryInput is used internally by genqlient +type __PointersQueryInput struct { + Query *UserQueryInput `json:"query"` + Dt time.Time `json:"-"` + Tz *string `json:"tz"` +} + +// GetQuery returns __PointersQueryInput.Query, and is useful for accessing the field via an interface. +func (v *__PointersQueryInput) GetQuery() *UserQueryInput { return v.Query } + +// GetDt returns __PointersQueryInput.Dt, and is useful for accessing the field via an interface. +func (v *__PointersQueryInput) GetDt() time.Time { return v.Dt } + +// GetTz returns __PointersQueryInput.Tz, and is useful for accessing the field via an interface. +func (v *__PointersQueryInput) GetTz() *string { return v.Tz } + +func (v *__PointersQueryInput) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *__PointersQueryInput + Dt json.RawMessage `json:"dt"` + graphql.NoUnmarshalJSON + } + firstPass.__PointersQueryInput = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.Dt + src := firstPass.Dt + if len(src) != 0 && string(src) != "null" { + err = testutil.UnmarshalDate( + src, dst) + if err != nil { + return fmt.Errorf( + "unable to unmarshal __PointersQueryInput.Dt: %w", err) + } + } + } + return nil +} + +type __premarshal__PointersQueryInput struct { + Query *UserQueryInput `json:"query"` + + Dt json.RawMessage `json:"dt"` + + Tz *string `json:"tz"` +} + +func (v *__PointersQueryInput) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *__PointersQueryInput) __premarshalJSON() (*__premarshal__PointersQueryInput, error) { + var retval __premarshal__PointersQueryInput + + retval.Query = v.Query + { + + dst := &retval.Dt + src := v.Dt + var err error + *dst, err = testutil.MarshalDate( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal __PointersQueryInput.Dt: %w", err) + } + } + retval.Tz = v.Tz + return &retval, nil +} + // The query or mutation executed by InputObjectQuery. const InputObjectQuery_Operation = ` query InputObjectQuery ($query: UserQueryInput) { @@ -393,61 +778,43 @@ func ListInputQuery( return &data_, err_ } -// The query or mutation executed by QueryWithSlices. -const QueryWithSlices_Operation = ` -query QueryWithSlices { - user { - emails - emailsOrNull - emailsWithNulls - emailsWithNullsOrNull - } -} -` - -func QueryWithSlices( - ctx_ context.Context, - client_ graphql.Client, -) (*QueryWithSlicesResponse, error) { - req_ := &graphql.Request{ - OpName: "QueryWithSlices", - Query: QueryWithSlices_Operation, +// The query or mutation executed by OmitEmptyQuery. +const OmitEmptyQuery_Operation = ` +query OmitEmptyQuery ($query: UserQueryInput, $queries: [UserQueryInput], $dt: DateTime, $tz: String, $tzNoOmitEmpty: String) { + user(query: $query) { + id } - var err_ error - - var data_ QueryWithSlicesResponse - resp_ := &graphql.Response{Data: &data_} - - err_ = client_.MakeRequest( - ctx_, - req_, - resp_, - ) - - return &data_, err_ -} - -// The query or mutation executed by SimpleQueryNoOverride. -const SimpleQueryNoOverride_Operation = ` -query SimpleQueryNoOverride { - user { + users(query: $queries) { id - name } + maybeConvert(dt: $dt, tz: $tz) + convert2: maybeConvert(dt: $dt, tz: $tzNoOmitEmpty) } ` -func SimpleQueryNoOverride( +func OmitEmptyQuery( ctx_ context.Context, client_ graphql.Client, -) (*SimpleQueryNoOverrideResponse, error) { + query *UserQueryInput, + queries []*UserQueryInput, + dt *time.Time, + tz *string, + tzNoOmitEmpty *string, +) (*OmitEmptyQueryResponse, error) { req_ := &graphql.Request{ - OpName: "SimpleQueryNoOverride", - Query: SimpleQueryNoOverride_Operation, + OpName: "OmitEmptyQuery", + Query: OmitEmptyQuery_Operation, + Variables: &__OmitEmptyQueryInput{ + Query: query, + Queries: queries, + Dt: dt, + Tz: tz, + TzNoOmitEmpty: tzNoOmitEmpty, + }, } var err_ error - var data_ SimpleQueryNoOverrideResponse + var data_ OmitEmptyQueryResponse resp_ := &graphql.Response{Data: &data_} err_ = client_.MakeRequest( @@ -459,27 +826,42 @@ func SimpleQueryNoOverride( return &data_, err_ } -// The query or mutation executed by SimpleQueryWithPointerFalseOverride. -const SimpleQueryWithPointerFalseOverride_Operation = ` -query SimpleQueryWithPointerFalseOverride { - user { +// The query or mutation executed by PointersQuery. +const PointersQuery_Operation = ` +query PointersQuery ($query: UserQueryInput, $dt: DateTime, $tz: String) { + user(query: $query) { id + roles name + emails + emailsNoPtr: emails } + otherUser: user(query: $query) { + id + } + maybeConvert(dt: $dt, tz: $tz) } ` -func SimpleQueryWithPointerFalseOverride( +func PointersQuery( ctx_ context.Context, client_ graphql.Client, -) (*SimpleQueryWithPointerFalseOverrideResponse, error) { + query *UserQueryInput, + dt time.Time, + tz *string, +) (*PointersQueryResponse, error) { req_ := &graphql.Request{ - OpName: "SimpleQueryWithPointerFalseOverride", - Query: SimpleQueryWithPointerFalseOverride_Operation, + OpName: "PointersQuery", + Query: PointersQuery_Operation, + Variables: &__PointersQueryInput{ + Query: query, + Dt: dt, + Tz: tz, + }, } var err_ error - var data_ SimpleQueryWithPointerFalseOverrideResponse + var data_ PointersQueryResponse resp_ := &graphql.Response{Data: &data_} err_ = client_.MakeRequest( From d332ad6e5e9dd979c9db920b0f046d01eb800b28 Mon Sep 17 00:00:00 2001 From: Ruben Mennes Date: Fri, 9 Feb 2024 14:09:30 +0100 Subject: [PATCH 6/8] Update module so it can be used in raito --- go.mod | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index bac27203..1864c0a2 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,12 @@ -module github.com/Khan/genqlient +module github.com/raito-io/genqlient go 1.18 +replace github.com/Khan/genqlient => ./ + require ( github.com/99designs/gqlgen v0.17.35 + github.com/Khan/genqlient v0.0.6 github.com/alexflint/go-arg v1.4.2 github.com/bradleyjkemp/cupaloy/v2 v2.6.0 github.com/stretchr/testify v1.8.2 From 1750a8f92542eb1f3d3bb23590a3a4ad4ef9b222 Mon Sep 17 00:00:00 2001 From: Ruben Mennes Date: Fri, 9 Feb 2024 14:16:54 +0100 Subject: [PATCH 7/8] Revert "Update module so it can be used in raito" This reverts commit d332ad6e5e9dd979c9db920b0f046d01eb800b28. --- go.mod | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 1864c0a2..bac27203 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,9 @@ -module github.com/raito-io/genqlient +module github.com/Khan/genqlient go 1.18 -replace github.com/Khan/genqlient => ./ - require ( github.com/99designs/gqlgen v0.17.35 - github.com/Khan/genqlient v0.0.6 github.com/alexflint/go-arg v1.4.2 github.com/bradleyjkemp/cupaloy/v2 v2.6.0 github.com/stretchr/testify v1.8.2 From b6b568ec1650e9b3460c54f2da482121082d5d52 Mon Sep 17 00:00:00 2001 From: Ruben Mennes Date: Fri, 16 Feb 2024 11:49:42 +0100 Subject: [PATCH 8/8] Feedback on PR --- generate/convert.go | 12 ++++++------ ...nalPointerOmitEmpty-testdata-queries-generated.go | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/generate/convert.go b/generate/convert.go index 79c369c4..6ae91057 100644 --- a/generate/convert.go +++ b/generate/convert.go @@ -257,18 +257,18 @@ func (g *generator) convertType( oe := true options.Omitempty = &oe } - } else if !options.PointerIsFalse() && (options.GetPointer() || (!typ.NonNull && g.Config.Optional == "pointer")) { - // Whatever we get, wrap it in a pointer. (Because of the way the - // options work, recursing here isn't as connvenient.) - // Note this does []*T or [][]*T, not e.g. *[][]T. See #16. - goTyp = &goPointerType{goTyp} - } else if !options.PointerIsFalse() && (options.GetPointer() || (!typ.NonNull && g.Config.Optional == "pointer_omitempty")) { + } else if !options.PointerIsFalse() && (options.GetPointer() || !typ.NonNull) && g.Config.Optional == "pointer_omitempty" { goTyp = &goPointerType{Elem: goTyp} if options.Omitempty == nil { oe := true options.Omitempty = &oe } + } else if !options.PointerIsFalse() && (options.GetPointer() || (!typ.NonNull && g.Config.Optional == "pointer")) { + // Whatever we get, wrap it in a pointer. (Because of the way the + // options work, recursing here isn't as connvenient.) + // Note this does []*T or [][]*T, not e.g. *[][]T. See #16. + goTyp = &goPointerType{goTyp} } else if !typ.NonNull && g.Config.Optional == "generic" { var genericRef string genericRef, err = g.ref(g.Config.OptionalGenericType) diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OptionalPointerOmitEmpty-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OptionalPointerOmitEmpty-testdata-queries-generated.go index 73a9142e..87b8a605 100644 --- a/generate/testdata/snapshots/TestGenerateWithConfig-OptionalPointerOmitEmpty-testdata-queries-generated.go +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OptionalPointerOmitEmpty-testdata-queries-generated.go @@ -625,9 +625,9 @@ func (v *__OmitEmptyQueryInput) __premarshalJSON() (*__premarshal__OmitEmptyQuer // __PointersQueryInput is used internally by genqlient type __PointersQueryInput struct { - Query *UserQueryInput `json:"query"` + Query *UserQueryInput `json:"query,omitempty"` Dt time.Time `json:"-"` - Tz *string `json:"tz"` + Tz *string `json:"tz,omitempty"` } // GetQuery returns __PointersQueryInput.Query, and is useful for accessing the field via an interface. @@ -673,11 +673,11 @@ func (v *__PointersQueryInput) UnmarshalJSON(b []byte) error { } type __premarshal__PointersQueryInput struct { - Query *UserQueryInput `json:"query"` + Query *UserQueryInput `json:"query,omitempty"` Dt json.RawMessage `json:"dt"` - Tz *string `json:"tz"` + Tz *string `json:"tz,omitempty"` } func (v *__PointersQueryInput) MarshalJSON() ([]byte, error) {