9804d42e2fcf94fedd0c2d60dbc67711637dc5d5
7
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e88305ecbd |
Add support for abstract-typed named fragments (#79)
## Summary: In previous commits I added support to genqlient for interfaces, inline fragments, and, most recently, named fragments of concrete (object) type. This leaves only named fragments of interface type! Like other named fragments, these are useful for code-sharing, especially if you want some code that can handle the same fields of several different types. As seems to be inevitable with genqlient, this was mostly pretty straightforward, although there turned out to be surprisingly many places we needed to add some handling; almost anywhere that touches interfaces *or* named fragments needed some updates. But it's all hopefully fairly clear code. As a part of this change I made three semi-related improvements: 1. I refactored the handling of descriptions (i.e. GoDoc), because it was getting more and more confusing and duplicative. I'm still not sure how much of it it makes sense to inline vs. separate, but I think this is better than it was. This resulted in some minor changes to descriptions, generally in the direction of making things more consistent. 2. I bumped the minimum Go version to 1.14 so we can guarantee support for duplicate interface methods. These are useful for abstract-in-absstract spreads; we generate an interface for the fragment, and (if the fragment-type implements the scope-type) we embed it into the interface we generate for its spread-context, and if the two have a duplicated field we thus duplicate the method. It wouldn't be impossible to support this on 1.13 (maybe just by omitting said embed) but it didn't seem worth it. This also removes a few special-cases in tests. 3. I added a bunch of code to better format syntax errors in the generated code (which we see from `gofmt`). This is mostly just an internal improvement; I wrote it because I got annoyed while hunting down a few such errors.. Fixes, at last, #8. Issue: https://github.com/Khan/genqlient/issues/8 ## Test plan: make check Author: benjaminjkraft Reviewers: dnerdy, benjaminjkraft, aberkan, MiguelCastillo Required Reviewers: Approved By: dnerdy Checks: ✅ Lint, ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Lint Pull Request URL: https://github.com/Khan/genqlient/pull/79 |
||
|
|
f99c10d6fd |
Add support for concrete-typed named fragments (#75)
## Summary:
In previous commits I added support to genqlient for interfaces and
inline fragments. This means the only query structures that remain are
named fragments and their spreads, e.g.
```
fragment MyFragment on MyType { myField }
query MyQuery { getMyType { ...MyFragment } }
```
Other than mere completionism, these are potentially useful for code
sharing: you can spread the same fragment multiple places; and then
genqlient can notice that and generate the same type for each. (They
can even be shared between different queries in the same package.)
In this commit I add support for named fragments of concrete
(object/struct, not interface) type, spread into either concrete or
abstract scope. For genqlient's purposes, these are a new "root"
type-name, just like each operation, and are then embedded into the
appropriate struct. (Using embeds allows their fields to be referenced
as fields of the containing type, if convenient. Further design
considerations are discussed in DESIGN.md.)
This requires new code in two main places (plus miscellaneous glue),
both nontrivial but neither particularly complex:
- We need to actually traverse both structures and generate the types
(in `convert.go`).
- We need to decide which fragments from this package to send to the
server, both for good hyigene and because GraphQL requires we send
only ones this query uses (in `generate.go`).
- We need a little new wiring for options -- because fragments can be
shared between queries they get their own toplevel options, rather
than inheriting the query's options.
Finally, this required slightly subtler changes to how we do
unmarshaling (in `types.go` and `unmarshal.go.tmpl`). Basically,
because embedded fields' methods, including `UnmarshalJSON`, get
promoted to the parent type, and because the JSON library ignores their
fields when shadowed by those of the parent type, we need a little bit
of special logic in each such parent type to do its own unmarshal and
then delegate to each embed. This is similar (and much simpler) to
what we did for interfaces, although it required some changes to the
"method-hiding" trick (used for both). It's only really necessary in
certain specific cases (namely when an embedded type has an
`UnmarshalJSON` method or a field with the same name as the embedder),
but it's easier to just generate it always. This is all described in
more detail inline.
This does not support fragments of abstract type, which have their own
complexities. I'll address those, which are now the only remaining
piece of #8, in a future commit.
Issue: https://github.com/Khan/genqlient/issues/8
## Test plan:
make check
Author: benjaminjkraft
Reviewers: dnerdy, benjaminjkraft, aberkan, MiguelCastillo
Required Reviewers:
Approved By: dnerdy
Checks: ✅ Lint, ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Test (1.13), ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Test (1.13), ✅ Lint, ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Test (1.13), ✅ Lint
Pull Request URL: https://github.com/Khan/genqlient/pull/75
|
||
|
|
6c86eed770 |
Clean up, test, and document ContextType and ClientGetter options (#77)
## Summary: ContextType is in use at Khan as a part of our ka-context system; it basically just lets you configure the type to pass as the `ctx` argument to genqlient helpers (or say to omit such an argument). ClientGetter I wrote thinking we might use it; then we didn't (because we have a few different clients we may use) but it's not much code and may be helpful to others. In this commit I clean up, document, and add tests for both options. The cleanup is mainly for ClientGetter, which was kind of broken before because it was a Go snippet but couldn't specify imports. I was thinking maybe you want to be able to write `ctx.Something()`, but I just don't see how to make it work, so I made it a function of context, which is probably the better idea anyway. Additionally, I improved the documentation for both, and added tests for those and several other config options that weren't completely tested. Fixes #5. Issue: https://github.com/Khan/genqlient/issues/5 ## Test plan: make check Author: benjaminjkraft Reviewers: dnerdy, aberkan, MiguelCastillo Required Reviewers: Approved By: dnerdy Checks: ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Test (1.13), ✅ Lint, ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Test (1.13), ✅ Lint Pull Request URL: https://github.com/Khan/genqlient/pull/77 |
||
|
|
46898ca52c |
Return clearer errors when __typename is missing (#68)
## Summary: In practice, at Khan at least, this is easy to mess up when writing mocks, because you write the mock by looking at the query, and the query doesn't say it's asking for `__typename` (because genqlient automatically adds that). A sufficiently-smart mocking library might be able to fix that, or detect it at least, but in any case, we can give a clearer error. I also removed an unrelated TODO that was done. Issue: https://khanacademy.slack.com/archives/C01120CNCS0/p1630019788014000 ## Test plan: make check Author: benjaminjkraft Reviewers: dnerdy, aberkan, csilvers, MiguelCastillo Required Reviewers: Approved by: dnerdy Checks: ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Test (1.13), ✅ Lint, ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Test (1.13), ✅ Lint Pull request URL: https://github.com/Khan/genqlient/pull/68 |
||
|
|
8f9d1cf792 |
Add support for interfaces, part 4: getter methods (#57)
## Summary:
Right now, if you make a query like `{ myInterface { field } }`, you
have to type-switch on all the possible implementations of `myInterface`
to get at `field`. Now, we generate getter-methods (e.g. `GetField`),
to make that access easier. Of course this only applies to shared
fields (which for now are the only ones, but once we support fragments
will no longer be).
This also includes a small change to the way we generate type-names for
interfaces: we no longer include the name of the concrete type in the
interface we propagate forward, so we generate
`MyInterfaceMyFieldMyType`, not `MyInterfaceMyImplMyFieldMyType`, in the
case where you have an interface `MyInterface` implemented by `MyImpl`
(and maybe other types) with field `myField: MyType`. This is necessary
so the getter method returns a well-defined type, and also probably
convenient for calling code. It will have to get a little bit more
complicated once we support fragments, where you could have two
implementing types with identically-named fields of different types, but
I think it'll be easiest to figure out how to deal with that when
implementing fragments.
While I was in the area, I added to the interface doc-comment a list of
the implementations. (In GraphQL, we're guaranteed to know them all
assuming our schema is up to date.)
Issue: https://github.com/Khan/genqlient/issues/8
## Test plan:
make check
Author: benjaminjkraft
Reviewers: benjaminjkraft, dnerdy, aberkan, MiguelCastillo
Required Reviewers:
Approved by: dnerdy
Checks: ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Test (1.13), ✅ Lint, ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Test (1.13), ✅ Lint
Pull request URL: https://github.com/Khan/genqlient/pull/57
|
||
|
|
65f9e90f2b |
Add support for interfaces, part 3: automatically add __typename (#56)
## Summary: In #52, I added support for interface types, but with the simplifying restriction (among others) that the user must request the field `__typename`. In this commit, I remove this restriction. The basic idea is simple: we preprocess the query to add `__typename`. The implementation isn't much more complicated! Although it required some new wiring in a few places. Issue: https://github.com/Khan/genqlient/issues/8 ## Test plan: make check Author: benjaminjkraft Reviewers: dnerdy, aberkan, MiguelCastillo Required Reviewers: Approved by: dnerdy Checks: ⌛ Test (1.17), ⌛ Test (1.16), ⌛ Test (1.15), ⌛ Test (1.14), ⌛ Test (1.13), ⌛ Lint, ⌛ Test (1.17), ⌛ Test (1.16), ⌛ Test (1.15), ⌛ Test (1.14), ⌛ Test (1.13), ⌛ Lint Pull request URL: https://github.com/Khan/genqlient/pull/56 |
||
|
|
1e87553788 |
Add support for interfaces, part 2: list-of-interface (#54)
## Summary: In this commit I remove one of the limitations of our support for interfaces, from #52, by adding support for list-of-interface fields. This was surprisingly complex! The issue is that, as before, it's the containing type that has to do all the glue work -- and it's that glue work that is complicated by list-of-interface fields. All in all, it's not that much new code, and by far the hard part is just 20 lines in the UnmarshalJSON template (which come with almost twice as many lines of comments to explain them). It may be easiest to start by reading some of the generated code, and then read the template. I also added support for such fields with `pointer: true` specified, such that the type is `[][]...[]*MyInterface`, although I don't know why you would want that. This does *not* allow e.g. `*[]*[][]*MyInterface`; that would require a way to specify it (see #16) but also add some extra complexity (as we'd have to actually walk the type-unwrap chain properly, instead of just counting the number of slices and whether there's a pointer). Issue: https://github.com/Khan/genqlient/issues/8 ## Test plan: make check Author: benjaminjkraft Reviewers: dnerdy, benjaminjkraft, aberkan, csilvers, MiguelCastillo Required Reviewers: Approved by: dnerdy Checks: ⌛ Test (1.17), ⌛ Test (1.16), ⌛ Test (1.15), ⌛ Test (1.14), ⌛ Test (1.13), ⌛ Lint, ⌛ Lint, ⌛ Test (1.17), ⌛ Test (1.16), ⌛ Test (1.15), ⌛ Test (1.14), ⌛ Test (1.13) Pull request URL: https://github.com/Khan/genqlient/pull/54 |