Thanks to `git merge` being clever, this got put in `v0.5.0` even
though it was added after `v0.5.0` was released. Now it's in `vNext`
(perhaps soon to become `v0.5.1`).
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
The generated files for integration tests aren't strictly snapshots and so
`UPDATE_SNAPSHOTS=1` won't work (maybe we should make it work?).
Instead you also need to `go generate ./...`. This came up in #209.
We noticed that the output from genqlient can be non-deterministic
when a graphql query queries a field that's an interface. This PR
fixes that by sorting the types as soon as we extract them from
the schema.
Frequent contributors and those adding significant new functionality
will want to read all the comments in `generate_test.go`, but people
making a small fix just want to update the snapshots. So it makes
sense to put the formula for doing so directly in the contributor docs.
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>
It feels like just yesterday, but it's been over four months since our
last release! So it's as good a time as any; while there are quite a few
changes they're individually mostly small. As usual, this updates the
changelog, and I'll tag it with the release once it lands.
Test plan: no relevant bug reports lately
GraphQL schemas have some builtin types, like `String`. The spec says
your SDL must not include those, but in practice some schemas do. (This
is probably because introspection must include them, and some tools that
create SDL from introspection don't know they're supposed to filter them
out.) Anyway, we've since #145 had logic to handle this; we just parse
with and without the prelude that defines them and see which works.
The problem is that this makes for very confusing error messages if you
have an invalid schema. (Or if you have a schema that you think is valid
but gqlparser doesn't, which is the more common case in the wild; see
for example #200.) Right now if both ways error we take the
without-prelude error, which if you didn't define the builtins is just
`undefined type String`; if we took the with-prelude error then if you
did define the builtins you'd just get `type String defined twice`. So
we actually have to be smart if we want good error messages for
everyone.
So in this commit we are smart: we check if your schema defines
`String`, and include the prelude only if it does not. To do this I
basically inlined `gqlparser.LoadSchema` (twice), so that in between
parsing and validation we can check if you have `String` and if not add
the prelude. This should in theory be both more efficient (we don't
have to do everything twice) and give better error messages,
although it's a bit more code.
Fixes#175.
Test plan: make check
I didn't realize until today that implementations of GraphQL interfaces
are actually allowed to be covariant: if the interface has a field
`f: T`, then the implementations may have fields `f: U` where `U` is a
subtype of `T` (for example `U` may be an implementation of the
interface `T`, or `U` may be `T!` if `T` is non-nullable. (I thought it
had to be `f: T` exactly.) So I figured I'd add a test and see what
breaks.
Surprisingly, and despite the fact that Go interfaces do *not* allow
covariance, everything... worked? There's at least one place where it's
possible we could ideally use a more specific type [1], but for now I
just wanted to make sure we at least write something that builds and is
vaguely reasonable. Of course I'm not sure if there's anything I've
missed (some day I need to find a fuzzing engine that can fuzz GraphQL).
[1] Specifically, the field
`CovariantInterfaceImplementationRandomItemTopic.Next` might ideally
have type `...NextContentTopic`, not `...NextContent`; we know it's a
topic. This doesn't directly cause covariance problems in Go: the method
`GetNext` still returns `...NextContent` so the interface matches. But
that trick doesn't work for the sibling field `.Related` which is
slice-typed: or rather, we'd need the method to copy the slice to the
correct type. (Not to mention the implemention of any change here would
require a bunch of plumbing because the AST doesn't quite have what we
want.) So it's probably best to just keep this as-is for simplicity and
consistency.
Test plan: make check
While writing tests at some point I came across an invalid query that
gqlparser doesn't catch, and which causes a panic for us. Now we
validate for it and return a nice error instead of panicing.
Fixes#176.
Test plan: make check
Some of the errors tests need to have their own schema, so the schema
can do something weird (or even be entirely invalid!). But most can
still share a schema. In this commit I have those indeed share a
schema, to avoid having to have a bunch of copies of mostly the same
schema. While doing so I noticed one error whose location wasn't very
useful, and fixed it.
Test plan: make check
GraphQL is pretty restrictive about its identifiers, so for the most
part we can and do safely use GraphQL identifiers in the Go we generate
with attention only to conflicts with other such identifiers. But we do
need to check one thing, which is that the identifier isn't a Go
keyword. (If it is, the generated code will almost certainly fail to
compile, but often with a confusing error message.) In this commit I add
such checks.
The most likely place to run into trouble here is argument names, which
are often one word and are used as-is. Operation names, if unexported,
can also be keywords, although in practice they're usually multiword.
Field names are always exported, thus safe. Generated type names are
always prefixed, camel-cased, with the operation name, so they always
contain an uppercase letter (even if the operation name is lowercase),
but type-names specified by `typename` may collide, so we check those.
In theory we could check type-names specified by `bind`, but these must
be defined by the user, so their code will already fail to compile, so I
didn't bother. I think that's all the places to consider, although it's
hard to be sure. In summary, we check argument names, operation names,
and user-specified type names.
Test plan: make check
* 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
Current implementation always uses POST as the transport mechanism. Adding GET support enables usage of GET queries for caching simply via URL.
Some notes:
- I left the existing API for creating a new client as is, but the implementation could be much cleaner by introducing some sort of configuration struct when creating a new client
- The construction of the query parameters follows the logic from Apollo's client implementation, which can be found here https://github.com/apollographql/apollo-client/blob/8beb4820edc6352996e08f7f73bde3573f1eb666/src/link/http/rewriteURIForGET.ts
- Updated integration tests to use both sets of clients. Updating the tests to use a test suite would be cleaner
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{}.
1.42 seems to be having some problems with Go 1.18 (see #2649), so let's
just upgrade to latest. There was one new error, suggesting to use `%q`
rather than `"%s"`, since the former does escapes properly. It doesn't
really matter for us -- the string should be an identifier -- but it
doesn't hurt. (Plus it wanted us to upgrade from ioutil, which I did
in #181.)
Test plan: make check
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
```
Go 1.18 is out! So we should run tests on it.
Additionally, gqlgen had some issues with it (see 99designs/gqlgen#1961
and golang/go#45584) so I updated that too, which updated some other
things.
Finally, latest gqlgen requires 1.16+, and it's time for us to do the
same anyway, so we can use `embed` and other newer goodies. So I
dropped running tests for 1.14 and 1.15, and bumped the module language
version.
Test plan: make check
It's been a while, time for a release! This commit updates the
changelog including a few missing entries, and I'll tag it with
the release once it lands.
Fixes#163
Test plan: Craig tested a fairly recent main branch in webapp.
The most important case here is if your glob isn't even a glob, it's
just a filename. But even if it was a glob, it's probably a mistake;
you'll probably end up with a confusing error due to an empty schema, or
a slightly less confusing error due to not having any operations.
Instead, let's just say outright that your glob didn't match any files.
Fixes#146.
Test plan:
This was a bit annoying to test via snapshot, so I just tested it
manually by modifying the example to use a glob that didn't match any
files, first for the schema then for the operations, and got errors like
```
bogus*.graphql did not match any files
exit status 1
example/main.go:68: running "go": exit status 1
```
In #133, Craig added support for a new use of typename, where it applies
to a scalar and means that genqlient should generate a named type, e.g.
`# @genqlient(typename: "MyString")` on a node of type string will
generate and use `type MyString string`. But this gets a bit confusing
if you mix it with `bind`; should
`typename: "MyString", bind: "int32"` generate `type MyString int32`, or
should one override the other, or what? Of course in practice you're
not likely to write that all in one place, but you could via a global
binding, or a `for` directive, and in that case probably it was a
mistake. In #138, we looked at making them work together correctly, but
it added complexity and got even more confusing.
So instead, here, we just ban it; we can always add it back if it proves
useful. (Or, you can make the `typename` win over a global binding by
locally unbinding it via `bind: "-"`.) This required changes in
surprisingly many places; I already knew the directive-validation code
was due for a refactor but that will happen some other day. The tests
show that it works, in any case.
Interestingly, this problem actually could have arisen for a struct
binding already, before #133. But all the same reasons it's confusing
seem to apply, so I just banned it there too. This is technically a
breaking change although I doubt anyone will hit it.
Test plan: make check
This is just what's already in the contributing docs, and is just to help both reviewers and authors remember to add changelog entries, check for tests, etc.
`Code generated by` disclaimer should be at the top of the file, as it is demonstrated in [this article](https://go.dev/blog/generate) on The Go Blog.
The current placing breaks integration with some code formatters and linters which do no skip files generated by `genqlient`.
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:
This lets you write code like:
```
query x {
# @genqlient(typename: "MyString")
someStringField
}
```
and genqlient will do
```
typename MyString string
type x struct {
someStringField MyString
}
```
This was not difficult to implement, though it required introducing a
new identifier type. The main difficulty I had was weird test
failures, that it turns out was due to the tests putting a bunch of
fields on the same line, so that the genqlient directive on the
previous line applied to all of them, accidentally. This became a
problem when `typename` suddenly started being respected for builtin
types! I fixed it by just spreading out the queries a bit.
Fixes#130
## Test plan:
make check
Author: csilvers
Reviewers: dnerdy, StevenACoffman, benjaminjkraft
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/133
## Summary:
If your type implements an interface, we add getter methods for the
shared fields, so that those may be accessed via the interface. But it
turns out occasionally it's useful to have these getter methods when
they don't implement a GraphQL interface, so you can use two
genqlient-generated types in the same function if they have the same
fields. (This comes up most often when you have a GraphQL union that
maybe should really be an interface, or if you don't yet support
interfaces implementing other interfaces (indeed our parser doesn't
either). But one can imagine other use cases.)
We can't predict how you want to do that, so we can't generate the
interface, but we can generate the methods, so you can define the
interface and do a type assertion from there. Since these methods are
pretty simple to generate, we just do it always. (As with #120, if
binary size becomes an issue we could later add an option to only
generate methods that are truly needed but including them seems like the
better default.)
This also fixes a subtle and rare bug, which would have become much more
common (indeed existing tests caught it). Specifically, if you have a
query like
```graphql
fragment FragmentOne on T { id }
fragment FragmentTwo on T { id }
query Q {
f { # interface type T
...FragmentOne
...FragmentTwo
}
}
```
since both `FragmentOne` and `FragmentTwo` request some common field,
say `id`, we generate a method `GetId` on each one. But since
`FragmentOne` and `FragmentTwo` are both on `T`, we also include their
interfaces in the interface we generate for the type of `f`, `QFT`. So
`QFT` includes a method `GetId`. But on the implementations, the two
methods conflict, and neither gets promoted; this causes various code to
fail to compile. With this change, this would have happened much more
frequently -- even if only one of the two fragments is on `T`, as long
as both request the field. Anyway, we now generate explicit methods on
each struct for all of its recursively emebedded fields -- using the
logic from #120 to compute them -- so that we don't need to rely on
method-promotion.
## Test plan:
make tesc
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/126
🖍 _This is an audit!_ 🖍
## Summary:
We've had a few more new features in the few days since 0.2.0! Craig
has run the webapp tests on this too. This commit updates the
changelog, and I'll tag it with the release once it lands.
## Test plan:
crossed fingers
Author: benjaminjkraft
Auditors: csilvers, dnerdy, StevenACoffman
Required Reviewers:
Approved By:
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/125
## Summary:
This has been a bit of a thorn since we started using genqlient in
production: just as you might want to specify, say, `omitempty` on an
argument, you might equally want to specify it on an input-type field.
But there's no obvious syntax to do that, because the input-type field
does not appear in the query (only the schema) so there's nowhere to put
the `# @genqlient` directive.
This commit, at last, fixes that problem, via a new option, `for`, which
you use in an option applied to the entire operation (or fragment), and
says, "actually, apply this directive to the given field, not the entire
operation". (It's mainly useful for input types, but I allowed it for
output types too; I could imagine it being convenient if you want to say
you always use a certain type or type-name for a certain field.) It
works basically like you expect: the inline options take precedence over
`for` take precedence over query-global options.
The implementation was fairly straightforward once I did a little
refactoring, mostly in the directive-parsing and directive-merging
(which are now combined, since merging is now a bit more complicated).
With that in place, and extended to support `for`, we need only add the
same wiring to input-fields that we have for other places you can put
directives. I did not attempt to solve the issue I've now documented
as #123, wherein conflicting options can lead to confusing behavior;
the new `for` is a new and perhaps more attractive avenue to cause it
but the issue remains the same and requires nontrivial refactoring
(described in the issue) to solve. (The breakage isn't horrible for the
most part; the option will just apply, or not apply, where you don't
expect it to.)
But while applying that logic, I noticed a problem, which is that we
were inconsistently cascading operation-level options down to
input-object fields. (I think this came out of the fact that initially
I thought to cascade them, then realized that this could cause problems
like #123 and intended to walk them back, but then accidentally only
"fixed" it for `omitempty`. I guess until this change, operation-level
options were rare enough, and input-field options messy enough, that no
one noticed.) So in this commit I bring things back into consistency,
by saying that they do cascade: with at least a sketch of a path forward
to solve #123 via better validation, I think that's by far the clearest
behavior.
Issue: https://github.com/Khan/genqlient/issues/14
## Test plan:
make check
Author: benjaminjkraft
Reviewers: csilvers, StevenACoffman, benjaminjkraft, aberkan, dnerdy, jvoll, mahtabsabet, MiguelCastillo
Required Reviewers:
Approved By: csilvers, 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/124
🖍 _This is an audit!_ 🖍
## Summary:
This has the same dark mode problem (if your Twitter client, say, is in
dark mode), but we can't be so fancy with CSS, I uess. So I just
switched to a white background, which looks fine in context. The actual
source of truth for the social preview image is in the github settings
page, but I checked in the image for reference.
## Test plan:
looks fine in the compose window on Facebook:

Author: benjaminjkraft
Auditors: dnerdy
Required Reviewers:
Approved By:
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/122
## 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
🖍 _This is an audit!_ 🖍
## Summary:
With the blog post going up soon, and a few new features, it's time for
another release! (Plus, Craig has run the webapp tests on this commit,
so it's now extra-tested.) This commit updates the changelog, and I'll
tag it with the release once it lands.
## Test plan:
crossed fingers
Author: benjaminjkraft
Auditors: csilvers, dnerdy, StevenACoffman
Required Reviewers:
Approved By:
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/119