diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 8521041b..7e6fe965 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -32,6 +32,7 @@ Note that genqlient now requires Go 1.23 or higher, and is tested through Go 1.2 ### Bug fixes: +- fixed `pointer_omitempty` not being applied to list types when `use_struct_references` is enabled. List fields like `[String!]` now correctly get the `omitempty` JSON tag. - fixed minor typos and grammatical issues across the project ## v0.8.1 diff --git a/generate/convert.go b/generate/convert.go index 4f79fda7..b1a0c062 100644 --- a/generate/convert.go +++ b/generate/convert.go @@ -239,7 +239,20 @@ func (g *generator) convertType( // Type is a list. elem, err := g.convertType( namePrefix, typ.Elem, selectionSet, options, queryOptions) - return &goSliceType{elem}, err + // For list types, we need to handle pointer_omitempty here because + // the code below won't be executed for slices. + // If the list itself is nullable (e.g., [String!] vs [String!]!), + // and pointer_omitempty is configured, we should set omitempty. + if err != nil { + return nil, err + } + if g.Config.Optional == "pointer_omitempty" && !typ.NonNull { + if options.Omitempty == nil { + oe := true + options.Omitempty = &oe + } + } + return &goSliceType{elem}, nil } // If this is a builtin type or custom scalar, just refer to it. diff --git a/generate/generate_test.go b/generate/generate_test.go index 8b987512..6db74343 100644 --- a/generate/generate_test.go +++ b/generate/generate_test.go @@ -245,6 +245,7 @@ func TestGenerateWithConfig(t *testing.T) { "PointersOmitEmpty.graphql", "Omitempty.graphql", "ListInput.graphql", + "ListInputOmitempty.graphql", }, &Config{ Optional: "pointer_omitempty", Bindings: map[string]*TypeBinding{ diff --git a/generate/testdata/queries/ListInputOmitempty.graphql b/generate/testdata/queries/ListInputOmitempty.graphql new file mode 100644 index 00000000..1b9deddb --- /dev/null +++ b/generate/testdata/queries/ListInputOmitempty.graphql @@ -0,0 +1,5 @@ +query ListInputOmitemptyQuery($names: [String!]) { + user(query: {names: $names}) { + id + } +} \ No newline at end of file diff --git a/generate/testdata/snapshots/TestGenerate-ListInputOmitempty.graphql-ListInputOmitempty.graphql.go b/generate/testdata/snapshots/TestGenerate-ListInputOmitempty.graphql-ListInputOmitempty.graphql.go new file mode 100644 index 00000000..bcd44e39 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-ListInputOmitempty.graphql-ListInputOmitempty.graphql.go @@ -0,0 +1,76 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package test + +import ( + "github.com/Khan/genqlient/graphql" + "github.com/Khan/genqlient/internal/testutil" +) + +// ListInputOmitemptyQueryResponse is returned by ListInputOmitemptyQuery on success. +type ListInputOmitemptyQueryResponse 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 ListInputOmitemptyQueryUser `json:"user"` +} + +// GetUser returns ListInputOmitemptyQueryResponse.User, and is useful for accessing the field via an interface. +func (v *ListInputOmitemptyQueryResponse) GetUser() ListInputOmitemptyQueryUser { return v.User } + +// ListInputOmitemptyQueryUser includes the requested fields of the GraphQL type User. +// The GraphQL type's documentation follows. +// +// A User is a user! +type ListInputOmitemptyQueryUser struct { + // id is the user's ID. + // + // It is stable, unique, and opaque, like all good IDs. + Id testutil.ID `json:"id"` +} + +// GetId returns ListInputOmitemptyQueryUser.Id, and is useful for accessing the field via an interface. +func (v *ListInputOmitemptyQueryUser) GetId() testutil.ID { return v.Id } + +// __ListInputOmitemptyQueryInput is used internally by genqlient +type __ListInputOmitemptyQueryInput struct { + Names []string `json:"names"` +} + +// GetNames returns __ListInputOmitemptyQueryInput.Names, and is useful for accessing the field via an interface. +func (v *__ListInputOmitemptyQueryInput) GetNames() []string { return v.Names } + +// The query executed by ListInputOmitemptyQuery. +const ListInputOmitemptyQuery_Operation = ` +query ListInputOmitemptyQuery ($names: [String!]) { + user(query: {names:$names}) { + id + } +} +` + +func ListInputOmitemptyQuery( + client_ graphql.Client, + names []string, +) (data_ *ListInputOmitemptyQueryResponse, err_ error) { + req_ := &graphql.Request{ + OpName: "ListInputOmitemptyQuery", + Query: ListInputOmitemptyQuery_Operation, + Variables: &__ListInputOmitemptyQueryInput{ + Names: names, + }, + } + + data_ = &ListInputOmitemptyQueryResponse{} + resp_ := &graphql.Response{Data: data_} + + err_ = client_.MakeRequest( + nil, + req_, + resp_, + ) + + return data_, err_ +} + diff --git a/generate/testdata/snapshots/TestGenerate-ListInputOmitempty.graphql-ListInputOmitempty.graphql.json b/generate/testdata/snapshots/TestGenerate-ListInputOmitempty.graphql-ListInputOmitempty.graphql.json new file mode 100644 index 00000000..641739aa --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-ListInputOmitempty.graphql-ListInputOmitempty.graphql.json @@ -0,0 +1,9 @@ +{ + "operations": [ + { + "operationName": "ListInputOmitemptyQuery", + "query": "\nquery ListInputOmitemptyQuery ($names: [String!]) {\n\tuser(query: {names:$names}) {\n\t\tid\n\t}\n}\n", + "sourceLocation": "testdata/queries/ListInputOmitempty.graphql" + } + ] +} diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-OptionalPointerOmitEmpty-ListInputOmitempty.graphql-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-OptionalPointerOmitEmpty-ListInputOmitempty.graphql-testdata-queries-generated.go new file mode 100644 index 00000000..432c6b40 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerateWithConfig-OptionalPointerOmitEmpty-ListInputOmitempty.graphql-testdata-queries-generated.go @@ -0,0 +1,78 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package queries + +import ( + "context" + + "github.com/Khan/genqlient/graphql" +) + +// ListInputOmitemptyQueryResponse is returned by ListInputOmitemptyQuery on success. +type ListInputOmitemptyQueryResponse 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 *ListInputOmitemptyQueryUser `json:"user"` +} + +// GetUser returns ListInputOmitemptyQueryResponse.User, and is useful for accessing the field via an interface. +func (v *ListInputOmitemptyQueryResponse) GetUser() *ListInputOmitemptyQueryUser { return v.User } + +// ListInputOmitemptyQueryUser includes the requested fields of the GraphQL type User. +// The GraphQL type's documentation follows. +// +// A User is a user! +type ListInputOmitemptyQueryUser struct { + // id is the user's ID. + // + // It is stable, unique, and opaque, like all good IDs. + Id string `json:"id"` +} + +// GetId returns ListInputOmitemptyQueryUser.Id, and is useful for accessing the field via an interface. +func (v *ListInputOmitemptyQueryUser) GetId() string { return v.Id } + +// __ListInputOmitemptyQueryInput is used internally by genqlient +type __ListInputOmitemptyQueryInput struct { + Names []string `json:"names,omitempty"` +} + +// GetNames returns __ListInputOmitemptyQueryInput.Names, and is useful for accessing the field via an interface. +func (v *__ListInputOmitemptyQueryInput) GetNames() []string { return v.Names } + +// The query executed by ListInputOmitemptyQuery. +const ListInputOmitemptyQuery_Operation = ` +query ListInputOmitemptyQuery ($names: [String!]) { + user(query: {names:$names}) { + id + } +} +` + +func ListInputOmitemptyQuery( + ctx_ context.Context, + client_ graphql.Client, + names []string, +) (data_ *ListInputOmitemptyQueryResponse, err_ error) { + req_ := &graphql.Request{ + OpName: "ListInputOmitemptyQuery", + Query: ListInputOmitemptyQuery_Operation, + Variables: &__ListInputOmitemptyQueryInput{ + Names: names, + }, + } + + data_ = &ListInputOmitemptyQueryResponse{} + resp_ := &graphql.Response{Data: data_} + + err_ = client_.MakeRequest( + ctx_, + req_, + resp_, + ) + + return data_, err_ +} +