6e2b1a581102e46d88c23e274b7912a1df55795e
11
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6e2b1a5811 |
Fix broken behavior for several options on the same node (#105)
## Summary: Previously, we actually allowed you to put several genqlient directives on the same node, but the semantics were undocumented (and somewhat confusing, when it comes to `typename`). In order to support directives on input options, we're actually going to be encouraging this usage (see notes in #14), so it's time to fix it. To avoid confusion, I just had conflicting directives be an error, rather than defining which one "wins". The same applies to specifying the same option several times in one directive. I also fixed two small bugs: - `typename` on an operation would incorrectly cascade down to all input types in a query (causing conflicts). - directive parse errors had useless positions, now they're correct ## Test plan: make check Author: benjaminjkraft Reviewers: StevenACoffman, 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/105 |
||
|
|
8de55d352e |
Add support for binding with a custom marshal/unmarshal function (#104)
## Summary: This is useful if you want to bind to a type you don't control (or use for other things) but need different serialization than its default. This is a feature gqlgen has and we've found it very useful. For example, in webapp we want to bind `DateTime` to `time.Time`, but its default serialization is not compatible with Python, so currently we have to bind to a wrapper type and cast all over the place, which is exactly the sort of boilerplate genqlient is supposed to avoid. For unmarshaling, the implementation basically just follows the existing support for abstract types; instead of calling our own generated helper, we now call your specified function. This required some refactoring to abstract the handling of custom unmarshalers generally from abstract types specifically, and to wire in not only the unmarshaler-name but also the `generator` (in order to compute the right import alias). For marshaling, I had to implement all that stuff over again; it's mostly parallel to unmarshaling (and I made a few minor changes to unmarshaling to make the two more parallel). Luckily, after #103 I at least only had to do it once, rather than implementing the same functionality for arguments and for input-type fields. It was still quite a bit of code; I didn't try to be quite as completionist about the tests as with unmarshal but still had to add a few. Issue: https://github.com/Khan/genqlient/issues/38 ## Test plan: make check Author: benjaminjkraft Reviewers: StevenACoffman, dnerdy, benjaminjkraft, 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/104 |
||
|
|
5995653583 |
Refactor argument-handling to use a struct (#103)
## Summary: In this commit I refactor the argument-generation logic to move most of the code out of the template and into the type-generator. This logic predates #51, and I didn't think to update it there, but I think it benefits from similar treatment, for similar reasons. Specifically, the main change is to treat variables as another struct type we can generate, rather than handling them inline as a `map[string]interface{}`. Users still pass them the same way, but instead of putting them into a `map[string]interface{}` and JSONifying that, we generate a struct and put them there. This turns out to simplify things quite a lot, because we already have a lot of code to generate types. Notably, the omitempty code goes from a dozen lines to basically two, and fixes a bug (#43) in the process, because now that we have a struct, `json.Marshal` will do our work for us! (And, once we have syntax for it (#14), we'll be able to handle field-level omitempty basically for free.) More importantly, it will simplify custom marshalers (#38, forthcoming) significantly, since we do all that logic at the containing-struct level, but will need to apply it to arguments. It does require two breaking changes: 1. For folks implementing the `graphql.Client` API (rather than just calling `NewClient`): we now pass them variables as an `interface{}` rather than a `map[string]interface{}`. For most callers, including Khan/webapp, this is basically a one-line change to the signature of their `MakeRequest`, and it should be a lot more future-proof. 2. genqlient's handling of the `omitempty` option has changed to match that of `encoding/json`, in particular it now never considers structs "empty". The difference was never intentional (I just didn't realize that behavior of `encoding/json`); arguably our behavior was more useful but I think that's outweighed by the value of consistency with `encoding/json` as well as the simpler and more correct implementation (fixing #43 is actually quite nontrivial otherwise). Once we have custom unmarshaler support (#38), users will be able to map a zero value to JSON null if they wish, which is mostly if not entirely equivalent for GraphQL's purposes. Issue: https://github.com/Khan/genqlient/issues/38 Issue: https://github.com/Khan/genqlient/issues/43 ## Test plan: make check Author: benjaminjkraft Reviewers: StevenACoffman, 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, ✅ Lint, ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14) Pull Request URL: https://github.com/Khan/genqlient/pull/103 |
||
|
|
ab1aaed845 |
Fix a bug in type-name collapsing introduced by #71 (#110)
## Summary: We typically name our types `OperationFieldTypeFieldType`, but if a type's name matches the preceding field-name, we omit the type-name. In #71 I changed the behavior such that we no longer do that in the case where the type's name matches some suffix of the name-so-far that's longer than just the leaf field-name. This was semi-intentional; I assumed it didn't matter and would be more predictable this way. But it turns out that was a feature, both in the sense that almost any change to the type-name-generator is breaking, and in the sense that it made the names uglier. Plus, now that we have better conflict-detection (#94), the possibility that some tricksy type-names could cause problems is no longer as much of an issue, so we can be a little less careful here. (Although I think this is no less safe than before; the field-names are the important part.) So in this commit I revert the change. Specifically, this comes up a lot at Khan where we do ``` mutation ForcePhantom { forcePhantom { # type: ForcePhantom error { ... } # type: ForcePhantomError } } ``` Before #71, and again after this change, we'll generate `ForcePhantomForcePhantomError` for `error`; before we'd generate `ForcePhantomForcePhantomErrorForcePhantomError`. Issue: https://github.com/Khan/genqlient/issues/109 ## Test plan: make tesc Author: benjaminjkraft Reviewers: csilvers, aberkan, dnerdy, jvoll, mahtabsabet, MiguelCastillo, StevenACoffman Required Reviewers: Approved By: csilvers 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/110 |
||
|
|
f72933fa0e |
Refactor import-loading to simplify the type-generation code (#101)
## Summary:
genqlient has some code (`imports.go`) dedicated to tracking which
imports we need and avoiding conflicts, as well as converting a
(restricted) Go expression like `map[string]github.com/me/mypkg.MyType`
to an import (`github.com/me/mypkg`) and a type-reference
(`map[string]mypkg.MyType`) to be used in the context of that import,
and at least making some attempt to track conflicts. (Right now the
conflict-avoidance is not very smart, and not very well tested, but it
comes up rarely anyway.) Sadly, that code was a bit cumbersome to use,
because you had to first register the imports (typically from
`convert.go`), then use them (often from the template).
In this commit I refactor the order we write things in order to allow a
significant simplification of how we import; in particular we no longer
have to guess in advance what imports which template will need; it can
just do `{{ref <expr>}}` as before, and it just works. To do this, I:
- changed the importer to have only one API, which adds the import if
needed, and returns the reference either way
- added a check that we don't add imports after they're written
- reorganized the toplevel templates a bit to make sure that check never
fires; we now generate all the types and operations, then write the
imports and glue it all together
This removes a bunch of silly code, and should simplify the process of
adding custom (un)marshalers (#38).
While I was at it, I put the documentation of what expressions we
support in a more visible place, and added a type-assertion that your
custom context type implements context.Context (if applicable).
## Test plan:
make check
Author: benjaminjkraft
Reviewers: StevenACoffman, 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/101
|
||
|
|
1d6e728181 |
Add open-source boilerplate (#96)
## Summary: There's a lot these days! I got most of it from the GitHub docs [1], as well as looking at other projects. I'll probably hold off on landing this until a couple more things land, then land this and tag v0.1.0. [1] https://docs.github.com/en/communities Issue: https://github.com/Khan/genqlient/issues/35 ## Test plan: view in github UI Author: benjaminjkraft Reviewers: jvoll, StevenACoffman, aberkan, dnerdy, mahtabsabet, MiguelCastillo Required Reviewers: Approved By: jvoll, 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/96 |
||
|
|
fcae8dd1d7 |
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 |
||
|
|
5211442843 |
Add a new option to treat an interface like an object (#97)
## 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 |
||
|
|
4d2058c617 |
Add a bunch more documentation (and rewrite README) (#86)
## 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 |
||
|
|
9804d42e2f |
Make README logo work in dark mode (#87)
## Summary: Dark mode is, they say, the new thing, and the logo doesn't work super well with it, because it's black on dark grey. This is surprisingly hard to fix. There are a few options: - make the logo colored; sorry, but I like black - add a white border to the logo; this is what Apollo does but I think it's pretty ugly in dark mode - add svg with media queries; this works well but only if the github dark mode setting matches the browser(/OS) dark mode setting, since that's what the media queries look at I opted for option 3. This required converting the text in the SVG to paths, since it really matters that you have the exact same font. (I tested on Android Firefox, which empirically doesn't have the same fonts I have on desktop.) Fixes #17. Issue: https://github.com/Khan/genqlient/issues/17 ## Test plan: tested that it looks good on both mobile firefox in dark mode, and desktop firefox+chrome in light mode. Author: benjaminjkraft Reviewers: dangoor, dbraley, somewhatabstract, dnerdy, kevinbarabash Required Reviewers: Approved By: dangoor, dbraley, somewhatabstract Checks: ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Lint Pull Request URL: https://github.com/Khan/genqlient/pull/87 |
||
|
|
2eba9a2c30 |
Reorganize documentation to make room to grow (#84)
## Summary: In this commit I reorganize much of our documentation into a new `docs` directory, where there will hopefully be more room to grow and to organize things in a user-friendly way. There's almost no net-new documentation, although of course it's a great time to review it anyway. In particular: - I moved the documentation for the `genqlient.yaml` config file into an example file instead of GoDoc (which now just points to the example file); I think this will be a lot clearer for casual users. - I moved the documentation for the `@genqlient` directive out of GoDoc and into a GraphQL schema file (since while it's a comment it's all real syntax), likewise, and made the `GenqlientDirective` type private (since there's now nothing useful to do with it). - I moved `DESIGN.md` and the logo into `docs/` (just to keep the toplevel a bit cleaner), and separated the Contributing section of the README into `docs/CONTRIBUTING.md` (which github will automatically link on various issue and PR pages). This leaves it so that: - README.md is the only documentation at the toplevel (and will become just the high-level introduction as I add more user docs to `docs/`) - GoDoc is only documentation for if you want to call genqlient programmatically (which is fairly limited as the API surface is quite small: it's now just Main, Generate, and Config, plus a constructor, a single method, and a bunch of fields on the latter) In future commits, I'll add some more new documentation to the `docs` directory. Issue: https://github.com/Khan/genqlient/issues/26 ## Test plan: make check (and read the docs) Author: benjaminjkraft Reviewers: jvoll, benjaminjkraft, aberkan, dnerdy, MiguelCastillo, mahtabsabet Required Reviewers: Approved By: jvoll 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/84 |