## Summary:
One common use of fragment spreads is as the entirety of a field's
selection, e.g.
```graphql
query MyQuery {
myField {
...MyFragment
}
}
```
In this case, by default, genqlient generates a wrapper type
`MyQueryMyFieldMyType`, which just embeds `MyFragment`. This makes
sense if you later want to add more fields in addition to the fragment
spread. But if you don't -- and you did the fragment because you want
to share types, it's an extra layer of indirection. (Which becomes
especially onerous if `myField` has list type (`[MyType!]`), such that
it's not just an extra attribute-access to get to `MyFragment`.)
The new option `# @genqlient(flatten: true)` simplifies this situation:
if applied to `myField` is skips the wrapper type;
`MyQueryResponse.MyField` will simply have type `MyFragment` (or
`[]MyFragment`, or whatever). This should hopefully make the `typename`
option, which has more limitations, less necessary.
Note that in #30 the initial idea was to support this for fields as
well. This would require significant additional complexity in the
JSON-(un)marshaling code, and has proven less necessary, so I
implemented this option only for fragment-spreads for now. With that
restriction, it was shockingly simple; we have to hook into a bunch of
different places, but they're all quite simple, since the structure of
the Go types still matches the structure in GraphQL.
Issue: https://github.com/Khan/genqlient/issues/30
## Test plan:
make check
Author: benjaminjkraft
Reviewers: csilvers, dnerdy, aberkan, jvoll, mahtabsabet, MiguelCastillo, StevenACoffman
Required Reviewers:
Approved By: csilvers, dnerdy
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/121
## Summary:
When genqlient generates output types, it generates whatever code is
necessary to unmarshal them. Conversely, when it generates input types,
it generates whatever code is necessary to marshal. This is all that's
needed for genqlient itself: it never needs to marshal output types or
unmarshal input types.
But maybe you do! (For example, to put the responses in a cache, which
is the use case that @csilvers hit at Khan, although there are others
one can imagine.) While we can't support every serialization format you
might want (at least not without adding plugins or some such), it's not
unreasonable to expect that since genqlient can read JSON, it can write
it too. Sadly, in the past this was not true for types requiring custom
unmarshaling logic, for several reasons.
In this commit I implement logic to always write both marshalers and
unmarshalers whenever they're needed to be able to correctly round-trip
the types, even though genqlient doesn't do so. I wasn't starting from
scratch, since of course we already write both marshalers and
unmarshalers in some cases. But this ended up requiring surprisingly
large changes on the marshaling side, mostly to correctly support
embedding (which we use for named fragments).
Specifically, as the comments in `types.go` discuss, the most difficult
issue is spreads with duplicate fields, which translate to Go embedded
fields which end up hidden from the json-marshaler. Ultimately, I had
to do things quite differently from unmarshaling, and essentially
flatten the type when we write marshaler. But in the end it's not so
ugly -- indeed arguably it's cleaner! Mainly it's just different.
One thing to note is that we do marshal `__typename` based on
what we know about the types; users need not fill it in (and if they
do we'll ignore it). This seemed to me to be a better UX, and
didn't add much complexity.
In general, I begin to wonder whether using `encoding/json` at all is
really right for genqlient: we're doing a lot of work to appease it,
despite knowing what our types look like. I think it would still be a
significant increase in lines of code to roll our own, but that code
would perhaps be simpler, and would surely be faster (although if we
just want the speed gains we could use another JSON-generator library,
see also #47). Anyway, something to think about in the future.
## Test plan:
make tesc
Author: benjaminjkraft
Reviewers: csilvers, StevenACoffman, benjaminjkraft, dnerdy, aberkan, jvoll, mahtabsabet, MiguelCastillo
Required Reviewers:
Approved By: StevenACoffman, dnerdy
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/120
## 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
## Summary:
The basic idea here is if you only request interface fields (no
fragments) you may not care about the concrete type, and so we could
just generate a struct as if it were an object. I don't think it's a
good idea to do that by default, because then if you later add a
fragment all your code totally changes, but it's quite reasonable as an
option!
Most of the code involved is just wiring and validation; the
core implementation is literally just: treat it like an object.
Issue: https://github.com/Khan/genqlient/issues/85
## Test plan:
make check
Author: benjaminjkraft
Reviewers: csilvers, StevenACoffman, benjaminjkraft, aberkan, dnerdy, jvoll, mahtabsabet, MiguelCastillo
Required Reviewers:
Approved By: StevenACoffman
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/97
## Summary:
In this commit, I add a bunch more documentation of how to use
genqlient, including a getting started guide, and a FAQ that also acts
as a how-to shaped index of common configuration options and extension
points. Much of this is adapted from the internal doc I wrote at Khan,
or reorganized from what was in the README. Speaking of which, now that
there are better places for all the details, I rewrote the README to be
a bit more of an overview and index of other documentation. (For now I
left the "unmaintained" notes, which I'll remove fairly soon once all
our ducks are in a row.)
Fixes#26, #39.
Issue: https://github.com/Khan/genqlient/issues/26
Issue: https://github.com/Khan/genqlient/issues/39
## Test plan:
make check, read the docs
Author: benjaminjkraft
Reviewers: mahtabsabet, benjaminjkraft, aberkan, dnerdy, jvoll, MiguelCastillo
Required Reviewers:
Approved By: mahtabsabet
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/86