Add support for specifying type-names, and conflict-detection (#94)
## Summary: In this commit I add two related features to genqlient: conflict-detection to avoid generating two distinct types with the same name, and an option to specify the type-name genqlient should use for some type. The conflict-detection was pretty simple once I realized I had already written all the code to do it in #70. There was a bunch of wiring, since we now need to keep track of the GraphQL type/selection-set that each type corresponds to, but it was pretty straightforward. This allows us to: - detect and reject if you have really sneaky type-names (there are some examples documented in `names.go`) - more clearly crash if genqlient accidentally generates two conflicting types, and - avoid stack-overflow when handing recursive (input) types (although sadly the poor support for options on input types (#14) makes them difficult to use in many cases; you really need to be able to set `pointer: true`) And with that all set up, the type-naming was also easy! (It doesn't have to get into the core of the type-generator, just plug in where we choose names. The desire for conflict detection was the main reason I hadn't set it up already.) Note that the existing limitation of #70 that the fields have to be in exactly the same order remains (and is now documented as #93); it's not deeply hard to fix but it's surprisingly much work. Issue: https://github.com/Khan/genqlient/issues/60 Issue: https://github.com/Khan/genqlient/issues/12 ## Test plan: make check Author: benjaminjkraft Reviewers: StevenACoffman, jvoll, benjaminjkraft, aberkan, csilvers, dnerdy, mahtabsabet, MiguelCastillo Required Reviewers: Approved By: StevenACoffman, jvoll Checks: ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Lint, ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Lint Pull Request URL: https://github.com/Khan/genqlient/pull/94
This commit is contained in:
+39
-7
@@ -9,6 +9,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
)
|
||||
|
||||
// goType represents a type for which we'll generate code.
|
||||
@@ -23,6 +25,16 @@ type goType interface {
|
||||
// used to refer to it in Go code.
|
||||
Reference() string
|
||||
|
||||
// GraphQLTypeName returns the name of the GraphQL type to which this Go type
|
||||
// corresponds.
|
||||
GraphQLTypeName() string
|
||||
|
||||
// SelectionSet returns the selection-set of the GraphQL field from which
|
||||
// this type was generated, or nil if none is applicable (for GraphQL
|
||||
// scalar, enum, and input types, as well as any opaque
|
||||
// (non-genqlient-generated) type since those are validated upon creation).
|
||||
SelectionSet() ast.SelectionSet
|
||||
|
||||
// Remove slice/pointer wrappers, and return the underlying (named (or
|
||||
// builtin)) type. For example, given []*MyStruct, return MyStruct.
|
||||
Unwrap() goType
|
||||
@@ -48,7 +60,10 @@ var (
|
||||
type (
|
||||
// goOpaqueType represents a user-defined or builtin type, often used to
|
||||
// represent a GraphQL scalar. (See Config.Bindings for more context.)
|
||||
goOpaqueType struct{ GoRef string }
|
||||
goOpaqueType struct {
|
||||
GoRef string
|
||||
GraphQLName string
|
||||
}
|
||||
// goSliceType represents the Go type []Elem, used to represent GraphQL
|
||||
// list types.
|
||||
goSliceType struct{ Elem goType }
|
||||
@@ -67,11 +82,20 @@ func (typ *goOpaqueType) Reference() string { return typ.GoRef }
|
||||
func (typ *goSliceType) Reference() string { return "[]" + typ.Elem.Reference() }
|
||||
func (typ *goPointerType) Reference() string { return "*" + typ.Elem.Reference() }
|
||||
|
||||
func (typ *goOpaqueType) SelectionSet() ast.SelectionSet { return nil }
|
||||
func (typ *goSliceType) SelectionSet() ast.SelectionSet { return typ.Elem.SelectionSet() }
|
||||
func (typ *goPointerType) SelectionSet() ast.SelectionSet { return typ.Elem.SelectionSet() }
|
||||
|
||||
func (typ *goOpaqueType) GraphQLTypeName() string { return typ.GraphQLName }
|
||||
func (typ *goSliceType) GraphQLTypeName() string { return typ.Elem.GraphQLTypeName() }
|
||||
func (typ *goPointerType) GraphQLTypeName() string { return typ.Elem.GraphQLTypeName() }
|
||||
|
||||
// goEnumType represents a Go named-string type used to represent a GraphQL
|
||||
// enum. In this case, we generate both the type (`type T string`) and also a
|
||||
// list of consts representing the values.
|
||||
type goEnumType struct {
|
||||
GoName string
|
||||
GraphQLName string
|
||||
Description string
|
||||
Values []goEnumValue
|
||||
}
|
||||
@@ -96,14 +120,17 @@ func (typ *goEnumType) WriteDefinition(w io.Writer, g *generator) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (typ *goEnumType) Reference() string { return typ.GoName }
|
||||
func (typ *goEnumType) Reference() string { return typ.GoName }
|
||||
func (typ *goEnumType) SelectionSet() ast.SelectionSet { return nil }
|
||||
func (typ *goEnumType) GraphQLTypeName() string { return typ.GraphQLName }
|
||||
|
||||
// goStructType represents a Go struct type used to represent a GraphQL object
|
||||
// or input-object type.
|
||||
type goStructType struct {
|
||||
GoName string
|
||||
Fields []*goStructField
|
||||
IsInput bool
|
||||
GoName string
|
||||
Fields []*goStructField
|
||||
IsInput bool
|
||||
Selection ast.SelectionSet
|
||||
descriptionInfo
|
||||
}
|
||||
|
||||
@@ -185,7 +212,9 @@ func (typ *goStructType) WriteDefinition(w io.Writer, g *generator) error {
|
||||
return g.execute("unmarshal.go.tmpl", w, typ)
|
||||
}
|
||||
|
||||
func (typ *goStructType) Reference() string { return typ.GoName }
|
||||
func (typ *goStructType) Reference() string { return typ.GoName }
|
||||
func (typ *goStructType) SelectionSet() ast.SelectionSet { return typ.Selection }
|
||||
func (typ *goStructType) GraphQLTypeName() string { return typ.GraphQLName }
|
||||
|
||||
// goInterfaceType represents a Go interface type, used to represent a GraphQL
|
||||
// interface or union type.
|
||||
@@ -195,6 +224,7 @@ type goInterfaceType struct {
|
||||
// we'll generate getter methods for each.
|
||||
SharedFields []*goStructField
|
||||
Implementations []*goStructType
|
||||
Selection ast.SelectionSet
|
||||
descriptionInfo
|
||||
}
|
||||
|
||||
@@ -272,7 +302,9 @@ func (typ *goInterfaceType) WriteDefinition(w io.Writer, g *generator) error {
|
||||
return g.execute("unmarshal_helper.go.tmpl", w, typ)
|
||||
}
|
||||
|
||||
func (typ *goInterfaceType) Reference() string { return typ.GoName }
|
||||
func (typ *goInterfaceType) Reference() string { return typ.GoName }
|
||||
func (typ *goInterfaceType) SelectionSet() ast.SelectionSet { return typ.Selection }
|
||||
func (typ *goInterfaceType) GraphQLTypeName() string { return typ.GraphQLName }
|
||||
|
||||
func (typ *goOpaqueType) Unwrap() goType { return typ }
|
||||
func (typ *goSliceType) Unwrap() goType { return typ.Elem.Unwrap() }
|
||||
|
||||
Reference in New Issue
Block a user