I set these tests up in #270, but I realized there's a lot more we could
test unrelated to that PR. In this commit I add some more tests. They're
not really exhaustive yet, but they did catch a few bugs, which I fixed:
- we weren't validating the package-name if you do set it (only if we
guess it)
- If you omitted `generated`, you would try to write generated code to
the directory containing `genqlient.yaml`, which makes no sense; now we
default to `generated.go`. In the real world it's probably good to set
explicitly, but it's actually very convenient in tests that we don't
have to, and maybe in small projects too.
There are a bunch of places in genqlient where we just kind of hope you
don't have ridiculous casing conflicts in your schema. Apparently with
enum values there are actual schemas that have this problem! Now we have
an option to disable. I ended up putting it all under `casing` instead
of in `bindings` so we can clearly document the list of algorithms we
support, and so we can have an `all_enums` value if you're working with
a schema with a lot of this; in the future we may want to add similar
behavior for types/fields, add more possible algorithms (e.g. to make
things unexported), etc.
I added tests for the new feature, although the way the tests are set up
it wasn't convenient to do so for a schema where this is actually
required. I also added a check for case conflicts that points you to
this option. (I don't want to do it automatically for reasons described
in the issue; mainly it just seemed a bit too magical.)
Fixes#265.
This is an implementation for #251, it adds a new `"generic"` option for
the `"optional"` configuration, and a companion type
`"optional_generic_type"` which is a fully qualified type with a
placeholder `%` for the generic parameter.
Co-authored-by: Dylan R. Johnston <dylan.johnston@familyzone.com>
Co-authored-by: Ben Kraft <ben@benkraft.org>
This means we can use generics and various other things. I didn't use
any of them yet, this is just bumping the numbers. I added tests for
1.20, and fixed one small bug, I think caused by `go/packages` changes
therein. And I bumped the golangci-lint version too while I was in the
area.
Fixes#256, fixes#257.
I have:
- [x] Written a clear PR title and description (above)
- [x] Signed the [Khan Academy CLA](https://www.khanacademy.org/r/cla)
- [x] Added tests covering my changes, if applicable
- [x] Included a link to the issue fixed, if applicable
- [x] Included documentation, for new features
- [x] Added an entry to the changelog
This is just a tweak to #239 to update the snapshots.
I have:
- [X] Written a clear PR title and description (above)
- [X] Signed the [Khan Academy CLA](https://www.khanacademy.org/r/cla)
- [X] Added tests covering my changes, if applicable
- [X] Included a link to the issue fixed, if applicable
- [X] Included documentation, for new features
- [ ] Added an entry to the changelog
Signed-off-by: Steve Coffman <steve@khanacademy.org>
Co-authored-by: Sam Wallace <29391916+thecasual@users.noreply.github.com>
The entries under the new `package_bindings` field should be packages,
but it's an easy mistake to put a file path instead (most of the other
fields in `genqlient.yaml` are files). Due to some bizzare behavior from
`go/packages` (described in #220), if you do that you get weird broken
code that gives you no clue what is wrong. Instead, let's guess if what
you gave us looks like a filename, and report a nice error if so.
Test plan: crossed fingers
Lint is failing with some inscrutable panic (on a commit with no code
changes). Let's try bumping the version in case they fixed it.
Additionally, the new version's github action uses Go 1.19, which means
it pulls in gofmt updates to match the new [doc-comment formatting rules][1].
So I added Go 1.19 to our list of versions to test (fixes#216)
and updated some of our doc-comments to format better in the
new world (mostly using the new link syntax).
[1]: https://go.dev/doc/comment
Test plan: make lint
Add new package bindings option
This commit adds list of packages for which genqlient should automatically
generate bindings; it's equivalent to adding all the exported types in the
package to `bindings` explicitly. This can be useful when you're both a client
and a server of the same schema and want to share types. We don't recommend
doing things that way, but the feature isn't too invasive and may be useful for
other purposes.
Co-authored-by: Ben Kraft <ben@benkraft.org>
* Allow absolute paths in schema files.
Thsi is useful for some out-of-tree testing I want to do. It's
unfortunate (imo) that filepath.join doesn't have this behavior by
default.
Test plan:
go test ./...
* run gofmt
* one more change to use pathJoin
This enables accessing the Extensions field, as defined in the response
format: https://spec.graphql.org/October2021/#sec-Response-Format
Extensions can be enabled using configuration option use_extensions.
This will change the return parameters of the generated client
functions. Making it a breaking change if enabled.
Since extensions are untyped as defined in the spec, the Client will
return an interface of type map[string]interface{}.
Now that we're on Go 1.16+ we can do this easily! The main advantage is
it means users can build a genqlient binary and use that portably (or we
could distribute one, or whatever). Plus the code is marginally simpler;
the `embed` API is really quite nice.
Fixes#9.
Test plan:
```
make check
go build .
rm -rf generate # pretend we have no checkout
./genqlient ./internal/integration/genqlient.yaml
./genqlient --init # fails after generating a default config
```
Adds a "struct_references" configuration that will:
* Use a pointer type for struct fields that are a complex type
* The behaviour can be overridden by setting "pointer: false" in the
proceeding comment blocks
* Sets the "omitempty: true" flag on fields matching the criteria.
This can also be overriden by setting "omitempty: false"
Although we'd debated not setting the pointer for array elements, it did turn out to be simpler to set them everywhere and also made the documentation cleaner to have a single rule to explain.
Fixes#149.
Code is mostly borrowed from [gqlgen](https://github.com/99designs/gqlgen).
The idea here is that I want to be able to store `genqlient.yaml` at the top-level, but my client code lives down in `graph/client/`. I put the `//go:generate` line in `graph/client/client.go`.
Previously initConfig always returned an error even if it succeeded.
This resulted in a confusing CLI error message that did not match the
executed behavior.
Now it returns an error only if it fails to init the config.
Multiple schema-files are now supported as of #134, but the support was
a bit different from how we did multiple operation-files. Before anyone
starts to depend on the ways the syntaxes differ, let's just make them
the same. Since it's easy, I also added support for having just a
single operations-file.
I also realized while writing this that the type-change is technically
breaking (if you call from Go), so documented it as such. I think
this is unlikely to affect many people.
Test plan: make check
## Summary:
It's nice to be clear here because it may be your first interaction with
genqlient!
## Test plan:
make check
Author: benjaminjkraft
Reviewers: StevenACoffman, benjaminjkraft, dnerdy
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/117
## 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
## 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
## Summary:
Before open-sourcing, we want to make sure that (a) GoDoc looks
reasonable, and (b) everything in the API is something we want to commit
to. In this commit, I do some miscellaneous cleanup on both fronts;
this does involve a few breaking changes to the programmatic API (better
now than once it has users). In future commits, I'll likely move the
documentation for `genqlient.yaml` and `@genqlient` to clearer places,
and make `GenqlientDirective` private, such that GoDoc is really only
for programmatic users.
Fixes#25.
Issue: https://github.com/Khan/genqlient/issues/25
## Test plan:
make check
Author: benjaminjkraft
Reviewers: dnerdy, benjaminjkraft, jvoll, aberkan, MiguelCastillo, mahtabsabet
Required Reviewers:
Approved By: dnerdy, 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/82
## Summary:
Steve pointed out (#73) that having genqlient with no arguments silently
use a default config file was a bit confusing, and changed it to use
`genqlient.yaml` by default (#74). Mark pointed out (#76) that this
makes it a bit less convenient when you're starting from scratch; you
have to go create a config file. In this commit I add a new init flag
that creates you a config file before using it.
Originally the suggestion was to use subcommands, e.g. we'd have
`genqlient init` and `genqlient generate` and so on. But I couldn't
think of anything else we might want subcommands for in the future, and
it felt a little silly to make you type `generate` each time. So
instead, I made it a flag, which has the nice property that you can do
`genqlient --init` and it will generate and then use a config file. (I
mean, maybe it will immediately crash because you don't have a schema,
but hopefully that's still a useful clue as to what to do next!) The
implmentation was fairly trivial.
Since we now have a nice way to generate a default config, I removed the
default values for most of the options; I've always felt they were
probably more confusing than helpful. (And indeed, all the users I know
of (Khan/webapp, and the much smaller project Steve was working on, are
setting those options explicitly.) This required a slight change to
the syntax to say "don't use context", which is probably also net clearer.
I decided this is also a good time to pull in a proper CLI parser (#31);
see ADR-504 for more on that choice. This also adds some nice help
messages!
Fixes#76, #31.
Issue: https://github.com/Khan/genqlient/issues/76
## Test plan:
```
go run .
go run . --init
go run . --init example/genqlient.yaml # refuses to clobber
go run . --init example/newgenqlient.yaml
```
Author: benjaminjkraft
Reviewers: dnerdy, aberkan, MiguelCastillo, StevenACoffman
Required Reviewers:
Approved By: 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/81
## 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
## Summary:
One sharp edge of the new `bindings` setting (when used for composite
types) is this: the (presumably struct) type to which you're binding
may expect to have particular fields, but it's GraphQL so you could have
requested some other set of fields. Now, if you ask us, we check.
Specifically, I've added a new setting under the `bindings` items, which
says: everywhere we query this must select these fields. (Or use its
own inline `# @genqlient(bind: ...)`.) It must select exactly those
fields, in order, no more, no less. This was fairly easy to implement;
actually comparing the selections was surprisingly much code but it's
all pretty straightforward.
## 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/70
## Summary:
We had this setting called "scalars", which said: bind this GraphQL type
to this Go type, rather than the one you would normally use. It's
called that because it's most useful for custom scalars, where "the one
you would normally use" is "error: unknown scalar". But nothing ever
stopped you from using it for a non-scalar type. I was planning on
removing this functionality, because it's sort of a rough edge, but a
discussion with Craig found some good use cases, so instead, in this
commit, I document it better and add some slightly nicer ways to specify
it.
Specifically, here are a few potential non-scalar use cases:
- bind a GraphQL enum to a nonstandard type (or even `string`)
- bind an input type to some type that has exactly the fields you want;
this acts as a sort of workaround for issues #14 and #44
- bind an object type to your own struct, so as to add methods to it
(this is the use case Craig raised)
- bind an object type to your own struct, so as to share it between
multiple queries (I believe named fragments will address this case
better, but it doesn't hurt to have options)
- bind a GraphQL list type to a non-slice type in Go (presumably one
with an UnmarshalJSON method), or any other different structure
The latter three cases still have the sharp edge I was originally
worried about, which is that nothing guarantees that the fields you
request in the query are the ones the type expects to get. But I think
it's worth having the option, with appropriate disclaimers.
The main change to help support that better is that you can now specify
the type inline in the query, as an alternative to specifying it in the
config file; this means you might map a given object to a given struct,
but only in some cases, and when you do you have a chance to look at the
list of fields you're requesting.
Additionally, I renamed the config field from "scalars" to "bindings"
(but mentioned it in a few places where you might go looking for how to
map scalars, most importantly the error message you get for an unknown
(custom) scalar). While I was making a breaking change, I also changed
it to be a `map[string]<struct>` instead of a `map[string]string`,
because I expect to add more fields soon, e.g. to handle issue #38.
Finally, since the feature is now intended/documented, I added some
tests, although it's honestly quite simple on the genqlient side.
## Test plan:
make tesc
Author: benjaminjkraft
Reviewers: csilvers, aberkan, dnerdy, MiguelCastillo
Required Reviewers:
Approved by: csilvers
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/69
## Summary:
In this commit I begin the journey to add the long-awaited support for
interfaces (part of #8). Well, it's not the beginning: I already had
some half-written broken code around. But it's the first fully
functional support, and especially, the first *tested* support; it's
probably best to review the nontrivially-changed code as if it were new.
Conceptually, the code so far is pretty simple: we generate an interface
type, and the implementations. (That code is in fact mostly unchanged.)
The complexity comes in because encoding/json doesn't know how to
unmarshal that. So we have to add an UnmarshalJSON method, which
actually has to be on the types with interface-type fields, that knows
how. I factored it into two methods, such that that UnmarshalJSON
method is just glue, and then there's a separate function, corresponding
to each interface-type, that actually does all the work. (If only one
could just write it as an actual method!) The method uses the same
trick suggested to me by a few others in another context to deserialize
all but one field, then handle that field specially, which is discussed
in the code.
This still has some limitations, which will be lifted in future commits:
- it doesn't allow for list-of-interface fields
- it requires that you manually ask for `__typename`
- it doesn't support fragments, i.e. you can only query for interface
fields, not concrete-type-specific ones
But it works, even in integration tests, which is progress!
As a part of this, I added a proper config option for the "allow broken
features" flag, since I need to be able to set it from the integration
tests which are in a separate package (and actually shell out via `go
generate`). I also renamed what was to be the first case
(InterfaceNoFragments), and replaced it with a further-simplified
version (avoiding list-of-interface fields.
[1] https://github.com/benjaminjkraft/notes/blob/master/go-json-interfaces.md
Issue: https://github.com/Khan/genqlient/issues/8
## Test plan:
make tesc
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, ✅ 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/52
## Summary:
As Mark pointed out in a review where I realized that the *example* had
a bogus config key (which I hadn't noticed earlier because it was just
using the default value), we should probably do a strict-unmarshal;
there's no reason you should have random extra keys in your config and
if you do it's probably a mistake. (Or maybe you're running a too-old
version of genqlient for your codebase, which you probably also want to
know.) Now we do.
## Test plan:
- `make check`
- in webapp, `go mod edit -replace github.com/Khan/genqlient=../genqlient`
then `make genqlient` produces no diffs (except go.mod/go.sum).
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/55