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:
Ben Kraft
2021-09-15 18:06:43 -07:00
committed by GitHub
parent 5211442843
commit fcae8dd1d7
20 changed files with 713 additions and 47 deletions
+11
View File
@@ -16,6 +16,7 @@ type genqlientDirective struct {
Pointer *bool
Struct *bool
Bind string
TypeName string
}
func (dir *genqlientDirective) GetOmitempty() bool { return dir.Omitempty != nil && *dir.Omitempty }
@@ -68,6 +69,8 @@ func fromGraphQL(dir *ast.Directive, pos *ast.Position) (*genqlientDirective, er
err = setBool(&retval.Struct, arg.Value)
case "bind":
err = setString(&retval.Bind, arg.Value)
case "typename":
err = setString(&retval.TypeName, arg.Value)
default:
return nil, errorf(pos, "unknown argument %v for @genqlient", arg.Name)
}
@@ -157,6 +160,10 @@ func validateStructOption(
return nil
}
// merge joins the directive applied to this node (the argument) and the one
// applied to the entire operation (the receiver) and returns a new
// directive-object representing the options to apply to this node (where in
// general we take the node's option, then the operation's, then the default).
func (dir *genqlientDirective) merge(other *genqlientDirective) *genqlientDirective {
retval := *dir
if other.Omitempty != nil {
@@ -171,6 +178,10 @@ func (dir *genqlientDirective) merge(other *genqlientDirective) *genqlientDirect
if other.Bind != "" {
retval.Bind = other.Bind
}
// For typename, the local directive always wins: when specified on the query
// options typename applies to the response-struct, not to all parts of the
// query.
retval.TypeName = other.TypeName
return &retval
}