Commit Graph

198 Commits

Author SHA1 Message Date
Ben Kraft 50b2f56a4e [🔥AUDIT🔥] Typo fix (#100)
🖍 _This is an audit!_ 🖍

Auditors: StevenACoffman

Author: benjaminjkraft

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/100
2021-09-16 09:11:00 -07:00
Ben Kraft c399be2792 Autofix lint when we can (#99)
## Summary:
This is mostly useful for gofumpt, although a few other linters may
support it.  Anyway, we may as well.

## Test plan:
make lint


Author: benjaminjkraft

Reviewers: dnerdy, StevenACoffman

Required Reviewers: 

Approved By: dnerdy, 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/99
2021-09-16 09:06:14 -07:00
Ben Kraft 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
2021-09-15 18:06:43 -07:00
Ben Kraft 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
2021-09-15 18:00:22 -07:00
Ben Kraft 463cffdede Lint against fmt.Println except in main.go (#98)
## Summary:
Usually this is an artifact of debugging, not intentional!  And it's
come up a few times, luckily all noticed by reviewers.

## Test plan:
make check


Author: benjaminjkraft

Reviewers: dnerdy, jvoll

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/98
2021-09-15 17:57:52 -07:00
Ben Kraft ce079a7e16 Clean up example a little (#89)
## Summary:
I realized:
- it's simpler to avoid the usual `cmd` and just put it all in the same
  directory
- we don't need schema.json since GitHub now posts a schema in SDL
  format

## Test plan:
make lint example


Author: benjaminjkraft

Reviewers: dnerdy, StevenACoffman, aberkan, jvoll, mahtabsabet, MiguelCastillo

Required Reviewers: 

Approved By: dnerdy, 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/89
2021-09-15 10:48:50 -07:00
Ben Kraft 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
2021-09-14 11:05:11 -07:00
Steve Coffman 673840e495 Picking some nits that my IDE complained about (#91)
* Picking some nits that my IDE complained about

Signed-off-by: Steve Coffman <steve@khanacademy.org>

* Update graphql/util.go

Co-authored-by: Ben Kraft <benkraft@khanacademy.org>

* revert to original for comment

Signed-off-by: Steve Coffman <steve@khanacademy.org>

Co-authored-by: Ben Kraft <benkraft@khanacademy.org>
2021-09-14 13:42:47 -04:00
Ben Kraft 639b46df32 Avoid putting incorrect extensions on error snapshot files (#92)
## Summary:
They were accidentally ending up as `.go` or `.graphql`, but are
actually just text.

Issue: https://github.com/Khan/genqlient/issues/90

## Test plan:
make check


Author: benjaminjkraft

Reviewers: StevenACoffman, 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/92
2021-09-14 10:30:33 -07:00
Ben Kraft 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
2021-09-10 17:33:01 -07:00
Ben Kraft 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
2021-09-10 16:03:30 -07:00
Ben Kraft d41cf636af Clean up godoc (#82)
## 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
2021-09-10 15:54:34 -07:00
Ben Kraft dc38360d9d Add a flag --init to write a default config (#81)
## 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
2021-09-10 15:49:54 -07:00
Ben Kraft 9e1c98488e Add tests for behavior on error (#83)
## Summary:
We guarantee that we never return a nil response, so you can safely do
```
resp, err := myQuery(...)
return resp.Field.SubField, err
```
And furthermore, if the error was a GraphQL error, `resp` may even be
nonzero; other, non-failing fields may be set.  (This depends on the
server, of course.) But we weren't testing either of those.  Now we do.

## Test plan:
make check


Author: benjaminjkraft

Reviewers: jvoll, aberkan, dnerdy, MiguelCastillo, mahtabsabet

Required Reviewers: 

Approved By: 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/83
2021-09-10 15:45:34 -07:00
Ben Kraft 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
2021-09-09 09:48:18 -07:00
Ben Kraft 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
2021-09-09 09:39:30 -07:00
Ben Kraft 1c061b153a Create a separate module for our golangci-lint version (#80)
## Summary:
In principle, it's not a problem to have test-only deps in your go.mod,
because they won't end up in your importers' builds (and in newer Go
versions may not even be downloaded.  (Which is why there's no
annotation to do so.)  In practice, that doesn't really work for
golangci-lint, because it doesn't really use semver (reasonably, in that
any updated linter may break lint in your codebase).  And we don't
really need it other than to run the binary at a particular version.

So now, I put it in its own go module.  This requires a bit more
throat-clearing to run it (we can't just `go run`), but it's not so bad
and avoids anyone getting annoyed at us because we upgraded their
golangci-lint for them.

We could do the same for `internal/integration`, which adds quite a lot,
including gqlgen, to our dependency tree, but it's not clear there's a
need.

Fixes #62.

Issue: https://github.com/Khan/genqlient/issues/62

## Test plan:
make check


Author: benjaminjkraft

Reviewers: dnerdy, StevenACoffman, benjaminjkraft, aberkan, MiguelCastillo

Required Reviewers: 

Approved By: dnerdy, StevenACoffman

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/80
2021-09-09 09:23:00 -07:00
Ben Kraft 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
2021-09-07 09:58:49 -07:00
Steve Coffman f3e601353c Add IDEs to gitignore and default config file name (#74)
Signed-off-by: Steve Coffman <steve@khanacademy.org>
2021-09-02 21:00:29 -04:00
Ben Kraft 6fdb170b99 Fix type-naming in the presence of interfaces, and refactor it a lot (#71)
## Summary:
When adding support for interfaces, I did not do the type-names as I
intended: they came out to be `MyFieldMyType`, not
`MyInterfaceMyFieldMyType`, which is inconsistent, but not strictly
wrong.  But once supporting fragments, this is also now incorrect.
(Exactly why is described in the comments inline.)  In this commit, in
any case, I fix it.

To do that, I finally did the last of the refactors I've been hoping to
do but unable to successfully implement, which is to make the type-name
and type-name-prefix management clearer.  In the past it was kind of
spread out, and each caller would have to pass the right name into
`convertDefinition`, which go quite unwieldy.  Now, the case that really
wanted that -- the operation toplevel -- just does it own thing; and the
main name-generation code  is factored out into a separate file with
tests, and with a long comment that goes into all the details of the
algorithm that the design-doc didn't cover.  (I even had some fun using
a linked list to implement the prefix-stack!)

This allowed me to fix the above bug fairly easily -- actually the fix
was pretty much automatic once I understood how to organize things.
There is one change which is that if your query name is unexported, we
no longer do the same with the input-type names; it's unclear to me if
anyone will actually care about this behavior (Khan always makes the
queries exported) but if they did it was very inconsistent (only at the
query toplevel, and only for input-objects, not enums), so we can
reimplement it properly if that comes up.  As a bonus fix, we now better
handle the case where your type-names are lowercase, which is legal if
nonstandard GraphQL.

Issue: https://github.com/Khan/genqlient/issues/8

## Test plan:
make tesc


Author: benjaminjkraft

Reviewers: dnerdy, benjaminjkraft, 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/71
2021-08-30 10:50:11 -07:00
Ben Kraft 95609f77a1 If requested, validate binding-types get the right fields (#70)
## 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
2021-08-30 10:18:52 -07:00
Ben Kraft fc4aa084ae [🔥AUDIT🔥] Fix snapshots from changes to __typename error (#72)
🖍 _This is an audit!_ 🖍

## Summary:
These got broken by the merge.

## Test plan:
make check


Author: benjaminjkraft

Auditors: aberkan, csilvers, dnerdy, MiguelCastillo

Required Reviewers: 

Approved by: 

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/72
2021-08-27 18:20:17 -07:00
Ben Kraft 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
2021-08-27 18:12:35 -07:00
Ben Kraft e6b1984d44 Add support for inline fragments (#65)
## Summary:
In this commit I add support for inline fragments
(`... on MyType { fields }`) to genqlient.  This will make interfaces a
lot more useful!  In future commits I'll add named fragments, for which
we'll generate slightly different types, as discussed in DESIGN.md.

In general, implementing the flattening approach described in DESIGN.md
was... surprisingly easy.  All we have to do is recurse on applicable
fragments when generating our selection-set.  The refactor to
selection-set handling this encouraged was, I think, quite beneficial.
It did reveal two tricky pre-existing issues.

One issue is that GraphQL allows for duplicate selections, as long as
they match.  (In practice, this is only useful in the context of
fragments, although GraphQL allows it even without.) I decided to handle
the simple case (duplicate leaf fields; we just deduplicate) but leave
to the future the complex cases where we need to merge different
sub-selections (now #64).  For now we just forbid that; we can see how
much it comes up.

The other issue is that we are generating type-names incorrectly for
interface types; I had intended to do `MyInterfaceMyFieldMyType` for
shared fields and `MyImplMyFieldMyType` for non-shared ones, but instead
I did `MyFieldMyType`, which is inconsistent already and can result in
conflicts in the presence of fragments.  I'm going to fix this in a
separate commit, though, because it's going to require some refactoring
and is irrelevant to the main logic of this commit; I left some TODOs in
the tests related to this.

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/65
2021-08-27 18:06:30 -07:00
Ben Kraft e99dced757 Add support for fragments, part 0: design sketch (#59)
## Summary:
Having implemented support for interfaces, it's time to implement
support for fragments.  And it turns out there's actually another design
decision I hadn't really thought about when thinking about interfaces!
In this commit I add a sketch of the design -- two proposed designs
really.  This one I think will be easier to change later (via a flag),
but opinions are still welcome.

Issue: https://github.com/Khan/genqlient/issues/8

## Test plan:
read it


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/59
2021-08-27 18:04:38 -07:00
Ben Kraft 222d6f191a Document and improve support for binding non-scalars to a specific type (#69)
## 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
2021-08-27 15:57:10 -07:00
Ben Kraft bf266d874d [🔥AUDIT🔥] Mark interfaces as enabled! (#67)
🖍 _This is an audit!_ 🖍

## Summary:
We've completed support for interfaces!  Fragments are still pending,
but interfaces by themselves should be usable.  But I forgot to actually
remove the flag they were behind.  In this commit I do!

Issue: https://khanacademy.slack.com/archives/C01120CNCS0/p1630012808004100

## Test plan:
make check


Author: benjaminjkraft

Auditors: aberkan, csilvers, dnerdy, MiguelCastillo

Required Reviewers: 

Approved by: 

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/67
2021-08-26 14:35:54 -07:00
Ben Kraft 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
2021-08-25 12:02:18 -07:00
Ben Kraft 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
2021-08-25 11:59:45 -07:00
Ben Kraft 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
2021-08-25 11:57:24 -07:00
Ben Kraft 4c38cb7759 Add support for interfaces, part 1: the simplest cases (#52)
## 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
2021-08-25 11:51:26 -07:00
Ben Kraft 8815d0991c Big refactor to separate operation-traversal from code-generation (#51)
## Summary:
I've felt for a while that types.go is way too confusing, and as I
started to implement some of the more complex cases of generating
interface-types, the cracks were really starting to show.  Luckily, I
also finally realized how to fix it: we need to separate the process of
traversing the GraphQL operation and schema to decide what types to
generate from the process of actually generating those types.  This
requires an extra set of intermediate data structures, but I think it
makes things quite a lot easier to understand -- and, importantly, it
means that the code-generation doesn't need to go in the order we
traverse the query/schema.

So in this commit, I did that huge refactor.  It's probably best to just
review types.go and traverse.go as if they were new; the old code was
quite hard to understand and the new code will hopefully make a lot more
sense.  (And to that end, review comments about what could be organized
better or needs more documentation are very much in order, even for code
that is mostly unchanged.)

This does introduce one bug, sort of, which is that rather than
generating broken code for list-of-interface fields, we generate no code
at all.  (A TODO in unmarshal.go describes why.)  I'll fix this when I
add support for those fields.  (It's all behind the AllowBrokenFeatures
flag, anyway.)  Otherwise, the only changes to generated code are that a
few methods are ordered differently, because we now generate the
implements-interface methods with the interface, rather than the
implementations, as it's much simpler that way.  (In GraphQL, unlike Go,
we know the list of all possible implementations of each interface, so
this is possible.)

## Test plan:
golangci-lint run ./... && go test ./...

Author: benjaminjkraft

Reviewers: dnerdy, benjaminjkraft, aberkan, csilvers, 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

Pull request URL: https://github.com/Khan/genqlient/pull/51
2021-08-25 11:49:30 -07:00
Ben Kraft 4a06e3f28e Fix typo in README (#58)
## Test plan
read it

Author: benjaminjkraft

Reviewers: aberkan, dnerdy, MiguelCastillo

Required Reviewers: 

Approved by: aberkan

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/58
2021-08-24 10:20:59 -07:00
Ben Kraft 6689d5a009 Use yaml.UnmarshalStrict (#55)
## 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
2021-08-23 15:52:08 -07:00
Ben Kraft 3746ecd063 Add integration tests against a gqlgen server (#50)
## Summary:
We have lots of tests covering codegen, but not a lot that actually run
the code.  For things where all we do is generate types, that's (mostly)
fine (especially now that we actually build the code), but as we
generate more nontrivial non-type code we need to actually run it.

So I wrote some integration tests that spin up a little gqlgen
server, and make calls to it; we can add more over time especially as
the JSON marshalling logic gets complex (to support fragments).
They're more work to write than the snapshot tests, but of course they
can test a lot more.

In addition to gqlgen, I pulled in testify assert/require, because I
really wanted to be able to use assert.Equal and such for these.  I
didn't bother converting existing tests, although I assume they will
become useful elsewhere in time.  Both gqlgen and testify are of course
only used in tests.

Fixes #21 and #24.
Issue: https://github.com/Khan/genqlient/issues/21
Issue: https://github.com/Khan/genqlient/issues/24

## Test plan:
make check

Author: benjaminjkraft

Reviewers: aberkan, dnerdy, benjaminjkraft, csilvers, MiguelCastillo

Required Reviewers: 

Approved by: aberkan, 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/50
2021-08-20 10:54:41 -07:00
Ben Kraft 700392315a Enable golangci-lint (#49)
## Summary:
It would be nice to have some linting beyond `go vet`!  Now we do.  I
started by copying the config from Khan/webapp.  I did remove a couple
of staticcheck checks that I didn't feel were useful.  (Note also that
exportloopref is the replacement for scopelint in newer golangci-lint.)

Included are all the needed lint fixes; most are stylistic but the
changes in the example are a (minor) bugfix.

Fixes #22.
Issue: https://github.com/Khan/genqlient/issues/22

## Test plan:
make check

Author: benjaminjkraft

Reviewers: aberkan, dnerdy, benjaminjkraft, csilvers, MiguelCastillo

Required Reviewers: 

Approved by: aberkan, 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/49
2021-08-20 10:39:12 -07:00
Mark Sandstrom 832c0bf855 Correct string zero value in design doc (#53)
The design doc talks about the zero value for the `string` and `*string` types and then references `0` and `null`. This PR changes these values to `""` and `nil`.
2021-08-19 18:03:35 -07:00
Ben Kraft dfc9f02efc Actually build the generated code in tests
This revealed one bug already (a redux of #43)!  And will hopefully help
more as unmarshalers get more complicated (thanks to
interfaces/fragments).
2021-06-03 15:48:05 -07:00
Ben Kraft 1f442da041 Switch to cupaloy for snapshots
Slightly uglier filenames, but less code and free diffing!  Approved in
ADR-466 for Khan use.  Fixes #23.
2021-06-03 12:08:56 -07:00
Ben Kraft 3c11f07e62 Make test ID type actually exist
This way I can start to test that the code compiles.
2021-06-03 11:36:45 -07:00
Ben Kraft c2e7dc4e5b Handle omitempty correctly for slices
We were generating broken code; fixes #43.  Also fixes a bug where
applying omitempty to the entire query was broken by e597cac74c.
2021-06-01 15:49:42 -07:00
Ben Kraft 589680f323 Notes from meeting about content-library
In general it seems like they are happy with the potential future fragments plan!
2021-05-11 14:32:11 -07:00
Ben Kraft caa72dae6a Remove line numbers from export-operations file, again
I messed it up in 6adfe3b2377!  This shows it's really time to turn on
more linters, which would have caught this.
2021-05-03 11:16:13 -07:00
Ben Kraft 870284271a Some more ideas on how to allow fragments for code-sharing
And clarified some existing ideas and tradeoffs.  This is enough to
make me think we may not need to implement both options, at
least to start.
2021-04-30 16:55:11 -07:00
Ben Kraft c34c5ff505 Turn on test coverage in Makefile/actions 2021-04-23 18:18:07 -07:00
Ben Kraft 6adfe3b237 Remove line numbers from export-operations file
This was sort of accidental from putting them in line numbers, and it
turns out it's more annoying than useful.  Easy enough to remove.

Fixes #40.
2021-04-23 18:14:09 -07:00
Ben Kraft d449acdda3 Clarify object type documentation
Craig pointed out this is a bit confusing when you don't have all the
fields.  Now we say so, but still include the type's description in case
it's useful.

Fixes #37.
2021-04-23 18:13:32 -07:00
Ben Kraft f1914cd9ef log response body, not request body, on non-200 2021-04-22 10:14:26 -07:00
Ben Kraft b399e0d740 move TODOs into issues 2021-04-21 19:05:38 -07:00
Ben Kraft 748f2cf072 Add documentation to types and fields
Fixes #3.
2021-04-21 18:07:36 -07:00