Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for mapping all nullable types as pointers #198

Merged
merged 5 commits into from
May 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ When releasing a new version:
- You can now enable `use_extensions` in the configuration file, to receive extensions returned by the GraphQL API server. Generated functions will return extensions as `map[string]interface{}`, if enabled.
- You can now use `graphql.NewClientUsingGet` to create a client that uses query parameters to pass the query to the GraphQL API server.
- In config files, `schema`, `operations`, and `generated` can now be absolute paths.
- You can now configure how nullable types are mapped to Go types in the configuration file. Specifically, you can set `optional: pointer` to have all nullable GraphQL arguments, input fields, and output fields map to pointers.

### Bug fixes:

Expand Down
17 changes: 17 additions & 0 deletions docs/genqlient.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,23 @@ use_struct_references: boolean
use_extensions: boolean


# Customize how optional fields are handled.
optional:
# Customize how models are generated for optional fields. This can currently
# be set to one of the following values:
# - value (default): optional fields are generated as values, the same as
# non-optional fields. E.g. fields with GraphQL types `String` or `String!`
# will both map to the Go type `string`. When values are absent in
# responses the zero value will be used.
# - pointer: optional fields are generated as pointers. E.g. fields with
# GraphQL type `String` will map to the Go type `*string`. When values are
# absent in responses `nil` will be used. Optional list fields do not use
# 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.
output: value


# A map from GraphQL type name to Go fully-qualified type name to override
# the Go type genqlient will use for this GraphQL type.
#
Expand Down
1 change: 1 addition & 0 deletions generate/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type Config struct {
ContextType string `yaml:"context_type"`
ClientGetter string `yaml:"client_getter"`
Bindings map[string]*TypeBinding `yaml:"bindings"`
Optional string `yaml:"optional"`
StructReferences bool `yaml:"use_struct_references"`
Extensions bool `yaml:"use_extensions"`

Expand Down
2 changes: 1 addition & 1 deletion generate/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ func (g *generator) convertType(
oe := true
options.Omitempty = &oe
}
} else if options.GetPointer() {
} else if 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.
Expand Down
48 changes: 32 additions & 16 deletions generate/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,55 +147,64 @@ func getDefaultConfig(t *testing.T) *Config {
// configurations. It uses snapshots, just like TestGenerate.
func TestGenerateWithConfig(t *testing.T) {
tests := []struct {
name string
baseDir string // relative to dataDir
config *Config // omits Schema and Operations, set below.
name string
baseDir string // relative to dataDir
operations []string // overrides the default set below
config *Config // omits Schema and Operations, set below.
}{
{"DefaultConfig", "", getDefaultConfig(t)},
{"Subpackage", "", &Config{
{"DefaultConfig", "", nil, getDefaultConfig(t)},
{"Subpackage", "", nil, &Config{
Generated: "mypkg/myfile.go",
}},
{"SubpackageConfig", "mypkg", &Config{
{"SubpackageConfig", "mypkg", nil, &Config{
Generated: "myfile.go", // (relative to genqlient.yaml)
}},
{"PackageName", "", &Config{
{"PackageName", "", nil, &Config{
Generated: "myfile.go",
Package: "mypkg",
}},
{"ExportOperations", "", &Config{
{"ExportOperations", "", nil, &Config{
Generated: "generated.go",
ExportOperations: "operations.json",
}},
{"CustomContext", "", &Config{
{"CustomContext", "", nil, &Config{
Generated: "generated.go",
ContextType: "github.com/Khan/genqlient/internal/testutil.MyContext",
}},
{"StructReferences", "", &Config{
{"StructReferences", "", nil, &Config{
StructReferences: true,
Generated: "generated-structrefs.go",
}},
{"NoContext", "", &Config{
{"NoContext", "", nil, &Config{
Generated: "generated.go",
ContextType: "-",
}},
{"ClientGetter", "", &Config{
{"ClientGetter", "", nil, &Config{
Generated: "generated.go",
ClientGetter: "github.com/Khan/genqlient/internal/testutil.GetClientFromContext",
}},
{"ClientGetterCustomContext", "", &Config{
{"ClientGetterCustomContext", "", nil, &Config{
Generated: "generated.go",
ClientGetter: "github.com/Khan/genqlient/internal/testutil.GetClientFromMyContext",
ContextType: "github.com/Khan/genqlient/internal/testutil.MyContext",
}},
{"ClientGetterNoContext", "", &Config{
{"ClientGetterNoContext", "", nil, &Config{
Generated: "generated.go",
ClientGetter: "github.com/Khan/genqlient/internal/testutil.GetClientFromNowhere",
ContextType: "-",
}},
{"Extensions", "", &Config{
{"Extensions", "", nil, &Config{
Generated: "generated.go",
Extensions: true,
}},
{"OptionalValue", "", []string{"ListInput.graphql", "QueryWithSlices.graphql"}, &Config{
Generated: "generated.go",
Optional: "value",
}},
{"OptionalPointer", "", []string{"ListInput.graphql", "QueryWithSlices.graphql"}, &Config{
Generated: "generated.go",
Optional: "pointer",
}},
}

sourceFilename := "SimpleQuery.graphql"
Expand All @@ -206,7 +215,14 @@ func TestGenerateWithConfig(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
err := config.ValidateAndFillDefaults(baseDir)
config.Schema = []string{filepath.Join(dataDir, "schema.graphql")}
config.Operations = []string{filepath.Join(dataDir, sourceFilename)}
if test.operations == nil {
config.Operations = []string{filepath.Join(dataDir, sourceFilename)}
} else {
config.Operations = make([]string, len(test.operations))
for i := range test.operations {
config.Operations[i] = filepath.Join(dataDir, test.operations[i])
}
}
if err != nil {
t.Fatal(err)
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading