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
This commit is contained in:
@@ -10,7 +10,7 @@ check: lint
|
|||||||
go test -cover ./...
|
go test -cover ./...
|
||||||
go mod tidy
|
go mod tidy
|
||||||
|
|
||||||
genqlient.png: genqlient.svg
|
docs/images/genqlient.png: docs/images/genqlient.svg
|
||||||
convert -density 600 -background transparent "$<" "$@"
|
convert -density 600 -background transparent "$<" "$@"
|
||||||
|
|
||||||
.PHONY: example
|
.PHONY: example
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||

|

|
||||||
|
|
||||||
# genqlient: a truly type-safe Go GraphQL client
|
# genqlient: a truly type-safe Go GraphQL client
|
||||||
|
|
||||||
This is a proof-of-concept of using code-generation to create a truly type-safe GraphQL client in Go. It is certainly not ready for production use nor for contributions (see [below](#development)).
|
This is a proof-of-concept of using code-generation to create a truly type-safe GraphQL client in Go. It is certainly not ready for production use nor for contributions (see [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md)).
|
||||||
|
|
||||||
## Why another GraphQL client?
|
## Why another GraphQL client?
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ fmt.Println(query.Me.Name)
|
|||||||
```
|
```
|
||||||
While this code may seem type-safe, and at the Go level it is, there's nothing to check that the schema looks like you expect it to. In fact, perhaps here we're querying the GitHub API, in which the field is called `viewer`, not `me`, so this query will fail. More common than misusing the name of the field is mis-capitalizing it, since Go and GraphQL have somewhat different conventions there. And even if you get it right, it adds up to a lot of handwritten boilerplate! And that's the best case; other clients, such as [machinebox/graphql](https://github.com/machinebox/graphql), have even fewer guardrails to help you make the right query and use the result correctly. This isn't a big deal in a small application, but for serious production-grade tools it's not ideal.
|
While this code may seem type-safe, and at the Go level it is, there's nothing to check that the schema looks like you expect it to. In fact, perhaps here we're querying the GitHub API, in which the field is called `viewer`, not `me`, so this query will fail. More common than misusing the name of the field is mis-capitalizing it, since Go and GraphQL have somewhat different conventions there. And even if you get it right, it adds up to a lot of handwritten boilerplate! And that's the best case; other clients, such as [machinebox/graphql](https://github.com/machinebox/graphql), have even fewer guardrails to help you make the right query and use the result correctly. This isn't a big deal in a small application, but for serious production-grade tools it's not ideal.
|
||||||
|
|
||||||
These problems should be entirely avoidable: GraphQL and Go are both typed languages; and GraphQL servers expose their schema in a standard, machine-readable format. We should be able to simply write a query `{ viewer { name } }`, have that automatically validated against the schema and turned into a Go struct which we can use in our code. In fact, there's already good prior art to do this sort of thing: [99designs/gqlgen](https://github.com/99designs/gqlgen) is a popular server library that generates types, and Apollo has a [codegen tool](https://www.apollographql.com/docs/devtools/cli/#supported-commands) to generate similar client-types for several other languages. (See [DESIGN.md](DESIGN.md) for more prior art.)
|
These problems should be entirely avoidable: GraphQL and Go are both typed languages; and GraphQL servers expose their schema in a standard, machine-readable format. We should be able to simply write a query `{ viewer { name } }`, have that automatically validated against the schema and turned into a Go struct which we can use in our code. In fact, there's already good prior art to do this sort of thing: [99designs/gqlgen](https://github.com/99designs/gqlgen) is a popular server library that generates types, and Apollo has a [codegen tool](https://www.apollographql.com/docs/devtools/cli/#supported-commands) to generate similar client-types for several other languages. (See [docs/DESIGN.md](docs/DESIGN.md) for more prior art.)
|
||||||
|
|
||||||
This is a GraphQL client that does the same sort of thing: you specify the query, and it generates type-safe helpers that make your query.
|
This is a GraphQL client that does the same sort of thing: you specify the query, and it generates type-safe helpers that make your query.
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ fmt.Println("you are", viewerResp.Viewer.MyName)
|
|||||||
//go:generate go run github.com/Khan/genqlient
|
//go:generate go run github.com/Khan/genqlient
|
||||||
```
|
```
|
||||||
|
|
||||||
For a complete working example, see [`example/`](example). For configuration options, see [`go doc github.com/Khan/genqlient/generate.Config`](https://pkg.go.dev/github.com/Khan/genqlient/generate#Config).
|
For a complete working example, see [`example/`](example). For configuration options, see [docs/genqlient.yaml](https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml).
|
||||||
|
|
||||||
### Documentation for generated code
|
### Documentation for generated code
|
||||||
|
|
||||||
@@ -70,25 +70,6 @@ For each GraphQL operation (query or mutation), genqlient generates a Go functio
|
|||||||
|
|
||||||
It returns a pointer to a struct representing the query-result, and an `error`. The struct will always be initialized (never nil), even on error. The error may be a `github.com/vektah/gqlparser/v2/gqlerror.List`, if it was a GraphQL-level error (in this case the returned struct may still contain useful data, if the API returns data even on error), or may be another error if, for example, the whole HTTP request failed (in which case the struct is unlikely to contain useful data). If the GraphQL operation has a comment immediately above it, that comment text will be used as the GoDoc for the generated function.
|
It returns a pointer to a struct representing the query-result, and an `error`. The struct will always be initialized (never nil), even on error. The error may be a `github.com/vektah/gqlparser/v2/gqlerror.List`, if it was a GraphQL-level error (in this case the returned struct may still contain useful data, if the API returns data even on error), or may be another error if, for example, the whole HTTP request failed (in which case the struct is unlikely to contain useful data). If the GraphQL operation has a comment immediately above it, that comment text will be used as the GoDoc for the generated function.
|
||||||
|
|
||||||
The generated code may be customized using a directive-like syntax, `# @genqlient(...)`. For full documentation of options, see [`go doc github.com/Khan/genqlient/generate.GenqlientDirective`](https://pkg.go.dev/github.com/Khan/genqlient/generate#GenqlientDirective).
|
The generated code may be customized using a directive-like syntax, `# @genqlient(...)`. For full documentation of options, see [docs/genqlient_directive.graphql](docs/genqlient_directive.graphql).
|
||||||
|
|
||||||
TODO: consider inlining the direct stuff; and document generated types further.
|
TODO: consider inlining the direct stuff; and document generated types further.
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
genqlient is not yet accepting contributions. The library is still in a very rough state, so while we intend to accept contributions in the future, we're not ready for them just yet. Please contact us (via an issue or email) if you are interested in helping out, but please be aware the answer may be that we aren't ready for your help yet, even if such help will be greatly useful once we are! We'll be ready for the rest of the world soon.
|
|
||||||
|
|
||||||
Khan Academy is a non-profit organization with a mission to provide a free, world-class education to anyone, anywhere. If you're looking for other ways to help us, You can help us in that mission by [donating](https://khanacademy.org/donate) or looking at [career opportunities](https://khanacademy.org/careers).
|
|
||||||
|
|
||||||
### Tests
|
|
||||||
|
|
||||||
To run tests and lint, `make check`. (GitHub Actions also runs them.)
|
|
||||||
|
|
||||||
Notes for contributors:
|
|
||||||
- Most of the tests are snapshot-based; see `generate/generate_test.go`. All new code-generation logic should be snapshot-tested. Some code additionally has standalone unit tests, when convenient.
|
|
||||||
- Integration tests run against a gqlgen server in `internal/integration/integration_test.go`, and should cover everything that snapshot tests can't, including the GraphQL client code and JSON marshaling.
|
|
||||||
- If `GITHUB_TOKEN` is available in the environment, it also checks that the example returns the expected output when run against the real API. This is configured automatically in GitHub Actions, but you can also use a [personal access token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with no scopes. There's no need for this to cover anything in particular; it's just to make sure the example in fact works.
|
|
||||||
|
|
||||||
### Design
|
|
||||||
|
|
||||||
See [DESIGN.md](DESIGN.md) for documentation of major design decisions in this library.
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Contributing to genqlient
|
||||||
|
|
||||||
|
genqlient is not yet accepting contributions. The library is still in a very rough state, so while we intend to accept contributions in the future, we're not ready for them just yet. Please contact us (via an issue or email) if you are interested in helping out, but please be aware the answer may be that we aren't ready for your help yet, even if such help will be greatly useful once we are! We'll be ready for the rest of the world soon.
|
||||||
|
|
||||||
|
Khan Academy is a non-profit organization with a mission to provide a free, world-class education to anyone, anywhere. If you're looking for other ways to help us, You can help us in that mission by [donating](https://khanacademy.org/donate) or looking at [career opportunities](https://khanacademy.org/careers).
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
To run tests and lint, `make check`. (GitHub Actions also runs them.)
|
||||||
|
|
||||||
|
Notes for contributors:
|
||||||
|
- Most of the tests are snapshot-based; see `generate/generate_test.go`. All new code-generation logic should be snapshot-tested. Some code additionally has standalone unit tests, when convenient.
|
||||||
|
- Integration tests run against a gqlgen server in `internal/integration/integration_test.go`, and should cover everything that snapshot tests can't, including the GraphQL client code and JSON marshaling.
|
||||||
|
- If `GITHUB_TOKEN` is available in the environment, it also checks that the example returns the expected output when run against the real API. This is configured automatically in GitHub Actions, but you can also use a [personal access token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with no scopes. There's no need for this to cover anything in particular; it's just to make sure the example in fact works.
|
||||||
|
|
||||||
|
## Design
|
||||||
|
|
||||||
|
See [DESIGN.md](DESIGN.md) for documentation of major design decisions in this library.
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
# genqlient.yaml is genqlient's configuration file. This genqlient.yaml is an
|
||||||
|
# example; use `go run github.com/Khan/genqlient --init` to generate a simple
|
||||||
|
# starting point.
|
||||||
|
|
||||||
|
# The filename with the GraphQL schema (in SDL format), relative to
|
||||||
|
# genqlient.yaml.
|
||||||
|
schema: schema.graphql
|
||||||
|
|
||||||
|
# Filenames or globs with the operations for which to generate code, relative
|
||||||
|
# to genqlient.yaml.
|
||||||
|
#
|
||||||
|
# These may be .graphql files, containing the queries in SDL format, or
|
||||||
|
# Go files, in which case any string-literal starting with (optional
|
||||||
|
# whitespace and) the string "# @genqlient" will be extracted as a query.
|
||||||
|
operations:
|
||||||
|
- genqlient.graphql
|
||||||
|
- "pkg/*.go"
|
||||||
|
|
||||||
|
# The filename to which to write the generated code, relative to
|
||||||
|
# genqlient.yaml.
|
||||||
|
generated: generated/genqlient.go
|
||||||
|
|
||||||
|
# The package name for the output code; defaults to the directory name of
|
||||||
|
# the generated-code file.
|
||||||
|
package: mygenerated
|
||||||
|
|
||||||
|
# If set, a file at this path (relative to genqlient.yaml) will be generated
|
||||||
|
# containing the exact operations that genqlient will send to the server.
|
||||||
|
#
|
||||||
|
# This is useful for systems which require queries to be explicitly
|
||||||
|
# safelisted (e.g. [1]), especially for cases like queries involving fragments
|
||||||
|
# where it may not exactly match the input queries, or for other static
|
||||||
|
# analysis. The JSON is an object of the form
|
||||||
|
# {"operations": [{
|
||||||
|
# "operationName": "operationname",
|
||||||
|
# "query": "query operationName { ... }",
|
||||||
|
# "sourceLocation": "myqueriesfile.graphql",
|
||||||
|
# }]}
|
||||||
|
# Keys may be added in the future.
|
||||||
|
#
|
||||||
|
# By default, no such file is written.
|
||||||
|
#
|
||||||
|
# [1] https://www.apollographql.com/docs/studio/operation-registry/
|
||||||
|
export_operations: operations.json
|
||||||
|
|
||||||
|
# Set to the fully-qualified name of a Go type which generated helpers
|
||||||
|
# should accept and use as the context.Context for HTTP requests.
|
||||||
|
#
|
||||||
|
# Defaults to context.Context; set to "-" to omit context entirely (i.e.
|
||||||
|
# use context.Background()). Must be a type which implements
|
||||||
|
# context.Context.
|
||||||
|
context_type: context.Context
|
||||||
|
|
||||||
|
# If set, a function to get a graphql.Client, perhaps from the context.
|
||||||
|
# By default, the client must be passed explicitly to each genqlient
|
||||||
|
# generated query-helper.
|
||||||
|
#
|
||||||
|
# This is useful if you have a shared client, either a global, or
|
||||||
|
# available from context, and don't want to pass it explicitly. In this
|
||||||
|
# case the signature of the genqlient-generated helpers will omit the
|
||||||
|
# `graphql.Context` and they will call this function instead.
|
||||||
|
#
|
||||||
|
# Must be the fully-qualified name of a function which accepts a context
|
||||||
|
# (of the type configured as ContextType (above), which defaults to
|
||||||
|
# `context.Context`, or a function of no arguments if ContextType is set
|
||||||
|
# to the empty string) and returns (graphql.Client, error). If the
|
||||||
|
# client-getter returns an error, the helper will return the error
|
||||||
|
# without making a query.
|
||||||
|
client_getter: "github.com/you/yourpkg.GetClient"
|
||||||
|
|
||||||
|
# A map from GraphQL type name to Go fully-qualified type name to override
|
||||||
|
# the Go type genqlient will use for this GraphQL type.
|
||||||
|
#
|
||||||
|
# This is primarily used for custom scalars, or to map builtin scalars to
|
||||||
|
# a nonstandard type. By default, builtin scalars are mapped to the
|
||||||
|
# obvious Go types (String and ID to string, Int to int, Float to float64,
|
||||||
|
# and Boolean to bool), but this setting will extend or override those
|
||||||
|
# mappings.
|
||||||
|
#
|
||||||
|
# genqlient does not validate these types in any way; they must define
|
||||||
|
# whatever logic is needed (MarshalJSON/UnmarshalJSON or JSON tags) to
|
||||||
|
# convert to/from JSON. For this reason, it's not recommended to use this
|
||||||
|
# setting to map object, interface, or union types, because nothing
|
||||||
|
# guarantees that the fields requested in the query match those present in
|
||||||
|
# the Go type.
|
||||||
|
#
|
||||||
|
# To get equivalent behavior in just one query, use @genqlient(bind: ...);
|
||||||
|
# see genqlient_directive.graphql for more details.
|
||||||
|
bindings:
|
||||||
|
# To bind a scalar:
|
||||||
|
DateTime:
|
||||||
|
# The fully-qualified name of the Go type to which to bind. For example:
|
||||||
|
# time.Time
|
||||||
|
# map[string]interface{}
|
||||||
|
# github.com/you/yourpkg/subpkg.MyType
|
||||||
|
type: time.Time
|
||||||
|
|
||||||
|
# To bind an object type:
|
||||||
|
MyType:
|
||||||
|
type: github.com/you/yourpkg.GoType
|
||||||
|
# If set, a GraphQL selection which must exactly match the fields
|
||||||
|
# requested whenever this type is used. Only applies if the GraphQL type
|
||||||
|
# is a composite output type (object, interface, or union).
|
||||||
|
#
|
||||||
|
# This is useful if Type is a struct whose UnmarshalJSON or other methods
|
||||||
|
# expect that you requested certain fields. For example, given the below
|
||||||
|
# config, genqlient will reject if you make a query
|
||||||
|
# { fieldOfMytype { id title } }
|
||||||
|
# The fields must match exactly, including the ordering: "{ name id }"
|
||||||
|
# will be rejected. But the arguments and directives, if any, need not
|
||||||
|
# match.
|
||||||
|
#
|
||||||
|
# TODO(benkraft): Also add ExpectIncludesFields and ExpectSubsetOfFields,
|
||||||
|
# or something, if you want to say, for example, that you have to request
|
||||||
|
# certain fields but others are optional.
|
||||||
|
expect_exact_fields: "{ id name }"
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
# The quasi-directive @genqlient is used to configure genqlient on a
|
||||||
|
# query-by-query basis.
|
||||||
|
#
|
||||||
|
# The syntax of the directive is just like a GraphQL directive (as defined
|
||||||
|
# below), except it goes in a comment on the line immediately preceding the
|
||||||
|
# field. (This is because GraphQL expects directives in queries to be defined
|
||||||
|
# by the server, not by the client, so it would reject a real @genqlient
|
||||||
|
# directive as nonexistent.)
|
||||||
|
#
|
||||||
|
# Directives may be applied to fields, arguments, or the entire query.
|
||||||
|
# Directives on the line preceding the query apply to all relevant nodes in
|
||||||
|
# the query; other directives apply to all nodes on the following line. (In
|
||||||
|
# all cases it's fine for there to be other comments in between the directive
|
||||||
|
# and the node(s) to which it applies.) For example, in the following query:
|
||||||
|
# # @genqlient(n: "a")
|
||||||
|
#
|
||||||
|
# # @genqlient(n: "b")
|
||||||
|
# #
|
||||||
|
# # Comment describing the query
|
||||||
|
# #
|
||||||
|
# # @genqlient(n: "c")
|
||||||
|
# query MyQuery(arg1: String,
|
||||||
|
# # @genqlient(n: "d")
|
||||||
|
# arg2: String, arg3: String,
|
||||||
|
# arg4: String,
|
||||||
|
# ) {
|
||||||
|
# # @genqlient(n: "e")
|
||||||
|
# field1, field2
|
||||||
|
# field3
|
||||||
|
# }
|
||||||
|
# the directive "a" is ignored, "b" and "c" apply to all relevant nodes in the
|
||||||
|
# query, "d" applies to arg2 and arg3, and "e" applies to field1 and field2.
|
||||||
|
directive genqlient(
|
||||||
|
|
||||||
|
# If set, this argument will be omitted if it's equal to its Go zero
|
||||||
|
# value, or is an empty slice.
|
||||||
|
#
|
||||||
|
# For example, given the following query:
|
||||||
|
# # @genqlient(omitempty: true)
|
||||||
|
# query MyQuery(arg: String) { ... }
|
||||||
|
# genqlient will generate a function
|
||||||
|
# MyQuery(ctx context.Context, client graphql.Client, arg string) ...
|
||||||
|
# which will pass {"arg": null} to GraphQL if arg is "", and the actual
|
||||||
|
# value otherwise.
|
||||||
|
#
|
||||||
|
# Only applicable to arguments of nullable types.
|
||||||
|
omitempty: Boolean
|
||||||
|
|
||||||
|
# If set, this argument or field will use a pointer type in Go. Response
|
||||||
|
# types always use pointers, but otherwise we typically do not.
|
||||||
|
#
|
||||||
|
# This can be useful if it's a type you'll need to pass around (and want a
|
||||||
|
# pointer to save copies) or if you wish to distinguish between the Go
|
||||||
|
# zero value and null (for nullable fields).
|
||||||
|
pointer: Boolean
|
||||||
|
|
||||||
|
# If set, this argument or field will use the given Go type instead of a
|
||||||
|
# genqlient-generated type.
|
||||||
|
#
|
||||||
|
# The value should be the fully-qualified type name to use for the field,
|
||||||
|
# for example:
|
||||||
|
# time.Time
|
||||||
|
# map[string]interface{}
|
||||||
|
# []github.com/you/yourpkg/subpkg.MyType
|
||||||
|
# Note that the type is the type of the whole field, e.g. if your field in
|
||||||
|
# GraphQL has type `[DateTime]`, you'd do
|
||||||
|
# # @genqlient(bind: "[]time.Time")
|
||||||
|
# (But you're not required to; if you want to map to some type DateList,
|
||||||
|
# you can do that, as long as its UnmarshalJSON method can accept a list
|
||||||
|
# of datetimes.)
|
||||||
|
#
|
||||||
|
# See bindings in genqlient.yaml for more details; this is effectively to a
|
||||||
|
# local version of that global setting and should be used with similar care.
|
||||||
|
# If set to "-", overrides any such global setting and uses a
|
||||||
|
# genqlient-generated type.
|
||||||
|
bind: String
|
||||||
|
|
||||||
|
) on
|
||||||
|
# genqlient directives can go almost anywhere, although some options are only
|
||||||
|
# applicable in certain locations as described above.
|
||||||
|
| QUERY
|
||||||
|
| MUTATION
|
||||||
|
| SUBSCRIPTION
|
||||||
|
| FIELD
|
||||||
|
| FRAGMENT_DEFINITION
|
||||||
|
| VARIABLE_DEFINITION
|
||||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
+14
-107
@@ -15,92 +15,21 @@ import (
|
|||||||
//
|
//
|
||||||
// Callers must call ValidateAndFillDefaults before using the config.
|
// Callers must call ValidateAndFillDefaults before using the config.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// The filename with the GraphQL schema (in SDL format); defaults to
|
// The following fields are documented at:
|
||||||
// schema.graphql
|
// https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml
|
||||||
Schema string `yaml:"schema"`
|
Schema string `yaml:"schema"`
|
||||||
|
Operations []string `yaml:"operations"`
|
||||||
// Filenames or globs with the operations for which to generate code;
|
Generated string `yaml:"generated"`
|
||||||
// defaults to genqlient.graphql.
|
Package string `yaml:"package"`
|
||||||
//
|
ExportOperations string `yaml:"export_operations"`
|
||||||
// These may be .graphql files, containing the queries in SDL format, or
|
ContextType string `yaml:"context_type"`
|
||||||
// Go files, in which case any string-literal starting with (optional
|
ClientGetter string `yaml:"client_getter"`
|
||||||
// whitespace and) the string "# @genqlient" will be extracted as a query.
|
Bindings map[string]*TypeBinding `yaml:"bindings"`
|
||||||
Operations []string `yaml:"operations"`
|
|
||||||
|
|
||||||
// If set, a file at this path will be generated containing the exact
|
|
||||||
// operations that genqlient will send to the server.
|
|
||||||
//
|
|
||||||
// This is useful for systems which require queries to be explicitly
|
|
||||||
// safelisted, especially for cases like queries involving fragments where
|
|
||||||
// it may not exactly match the input queries. The JSON is an object of
|
|
||||||
// the form
|
|
||||||
// {"operations": [{
|
|
||||||
// "operationName": "operationname",
|
|
||||||
// "query": "query operationName { ... }",
|
|
||||||
// "sourceLocation": "myqueriesfile.graphql",
|
|
||||||
// }]}
|
|
||||||
// Keys may be added in the future.
|
|
||||||
//
|
|
||||||
// By default, no such file is written.
|
|
||||||
ExportOperations string `yaml:"export_operations"`
|
|
||||||
|
|
||||||
// The filename to which to write the generated code; defaults to
|
|
||||||
// generated.go
|
|
||||||
Generated string `yaml:"generated"`
|
|
||||||
|
|
||||||
// The package name for the output code; defaults to the directory name of
|
|
||||||
// Generated
|
|
||||||
Package string `yaml:"package"`
|
|
||||||
|
|
||||||
// Set to the fully-qualified name of a Go type which generated helpers
|
|
||||||
// should accept and use as the context.Context for HTTP requests.
|
|
||||||
//
|
|
||||||
// Defaults to context.Context; set to "-" to omit context entirely (i.e.
|
|
||||||
// use context.Background()). Must be a type which implements
|
|
||||||
// context.Context.
|
|
||||||
ContextType string `yaml:"context_type"`
|
|
||||||
|
|
||||||
// If set, a function to get a graphql.Client, perhaps from the context.
|
|
||||||
// By default, the client must be passed explicitly to each genqlient
|
|
||||||
// generated query-helper.
|
|
||||||
//
|
|
||||||
// This is useful if you have a shared client, either a global, or
|
|
||||||
// available from context, and don't want to pass it explicitly. In this
|
|
||||||
// case the signature of the genqlient-generated helpers will omit the
|
|
||||||
// `graphql.Context` and they will call this function instead.
|
|
||||||
//
|
|
||||||
// Must be the fully-qualified name of a function which accepts a context
|
|
||||||
// (of the type configured as ContextType (above), which defaults to
|
|
||||||
// `context.Context`, or a function of no arguments if ContextType is set
|
|
||||||
// to the empty string) and returns (graphql.Client, error). If the
|
|
||||||
// client-getter returns an error, the helper will return the error
|
|
||||||
// without making a query.
|
|
||||||
ClientGetter string `yaml:"client_getter"`
|
|
||||||
|
|
||||||
// A map from GraphQL type name to Go fully-qualified type name to override
|
|
||||||
// the Go type genqlient will use for this GraphQL type.
|
|
||||||
//
|
|
||||||
// This is primarily used for custom scalars, or to map builtin scalars to
|
|
||||||
// a nonstandard type. By default, builtin scalars are mapped to the
|
|
||||||
// obvious Go types (String and ID to string, Int to int, Float to float64,
|
|
||||||
// and Boolean to bool), but this setting will extend or override those
|
|
||||||
// mappings.
|
|
||||||
//
|
|
||||||
// genqlient does not validate these types in any way; they must define
|
|
||||||
// whatever logic is needed (MarshalJSON/UnmarshalJSON or JSON tags) to
|
|
||||||
// convert to/from JSON. For this reason, it's not recommended to use this
|
|
||||||
// setting to map object, interface, or union types, because nothing
|
|
||||||
// guarantees that the fields requested in the query match those present in
|
|
||||||
// the Go type.
|
|
||||||
//
|
|
||||||
// To get equivalent behavior in just one query, use @genqlient(bind: ...);
|
|
||||||
// see GenqlientDirective.Bind for more details.
|
|
||||||
Bindings map[string]*TypeBinding `yaml:"bindings"`
|
|
||||||
|
|
||||||
// Set to true to use features that aren't fully ready to use.
|
// Set to true to use features that aren't fully ready to use.
|
||||||
//
|
//
|
||||||
// This is primarily intended for genqlient's own tests. These features
|
// This is primarily intended for genqlient's own tests. These features
|
||||||
// are likely BROKEN and come with NO EXPECTATION OF COMPATIBBILITY. Use
|
// are likely BROKEN and come with NO EXPECTATION OF COMPATIBILITY. Use
|
||||||
// them at your own risk!
|
// them at your own risk!
|
||||||
AllowBrokenFeatures bool `yaml:"allow_broken_features"`
|
AllowBrokenFeatures bool `yaml:"allow_broken_features"`
|
||||||
|
|
||||||
@@ -110,32 +39,10 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A TypeBinding represents a Go type to which genqlient will bind a particular
|
// A TypeBinding represents a Go type to which genqlient will bind a particular
|
||||||
// GraphQL type. See Config.Bind, above, for more details.
|
// GraphQL type, and is documented further at:
|
||||||
|
// https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml
|
||||||
type TypeBinding struct {
|
type TypeBinding struct {
|
||||||
// The fully-qualified name of the Go type to which to bind. For example:
|
Type string `yaml:"type"`
|
||||||
// time.Time
|
|
||||||
// map[string]interface{}
|
|
||||||
// github.com/you/yourpkg/subpkg.MyType
|
|
||||||
Type string `yaml:"type"`
|
|
||||||
// If set, a GraphQL selection which must exactly match the fields
|
|
||||||
// requested whenever this type is used. Only applies if the GraphQL type
|
|
||||||
// is a composite output type (object, interface, or union).
|
|
||||||
//
|
|
||||||
// This is useful if Type is a struct whose UnmarshalJSON or other methods
|
|
||||||
// expect that you requested certain fields. You can specify those fields
|
|
||||||
// like
|
|
||||||
// MyType:
|
|
||||||
// type: path/to/my.GoType
|
|
||||||
// expect_exact_fields: "{ id name }"
|
|
||||||
// and then genqlient will reject if you make a query
|
|
||||||
// { fieldOfMytype { id title } }
|
|
||||||
// The fields must match exactly, including the ordering: "{ name id }"
|
|
||||||
// will be rejected. But the arguments and directives, if any, need not
|
|
||||||
// match.
|
|
||||||
//
|
|
||||||
// TODO(benkraft): Also add ExpectIncludesFields and ExpectSubsetOfFields,
|
|
||||||
// or something, if you want to say, for example, that you have to request
|
|
||||||
// certain fields but others are optional.
|
|
||||||
ExpectExactFields string `yaml:"expect_exact_fields"`
|
ExpectExactFields string `yaml:"expect_exact_fields"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-9
@@ -37,7 +37,7 @@ func (g *generator) baseTypeForOperation(operation ast.Operation) (*ast.Definiti
|
|||||||
// result will be unmarshaled.
|
// result will be unmarshaled.
|
||||||
func (g *generator) convertOperation(
|
func (g *generator) convertOperation(
|
||||||
operation *ast.OperationDefinition,
|
operation *ast.OperationDefinition,
|
||||||
queryOptions *GenqlientDirective,
|
queryOptions *genqlientDirective,
|
||||||
) (goType, error) {
|
) (goType, error) {
|
||||||
name := operation.Name + "Response"
|
name := operation.Name + "Response"
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ var builtinTypes = map[string]string{
|
|||||||
// argument to a GraphQL operation.
|
// argument to a GraphQL operation.
|
||||||
func (g *generator) convertInputType(
|
func (g *generator) convertInputType(
|
||||||
typ *ast.Type,
|
typ *ast.Type,
|
||||||
options, queryOptions *GenqlientDirective,
|
options, queryOptions *genqlientDirective,
|
||||||
) (goType, error) {
|
) (goType, error) {
|
||||||
// note prefix is ignored here (see generator.typeName), as is selectionSet
|
// note prefix is ignored here (see generator.typeName), as is selectionSet
|
||||||
// (for input types we use the whole thing)).
|
// (for input types we use the whole thing)).
|
||||||
@@ -102,7 +102,7 @@ func (g *generator) convertType(
|
|||||||
namePrefix *prefixList,
|
namePrefix *prefixList,
|
||||||
typ *ast.Type,
|
typ *ast.Type,
|
||||||
selectionSet ast.SelectionSet,
|
selectionSet ast.SelectionSet,
|
||||||
options, queryOptions *GenqlientDirective,
|
options, queryOptions *genqlientDirective,
|
||||||
) (goType, error) {
|
) (goType, error) {
|
||||||
// We check for local bindings here, so that you can bind, say, a
|
// We check for local bindings here, so that you can bind, say, a
|
||||||
// `[String!]` to a struct instead of a slice. Global bindings can only
|
// `[String!]` to a struct instead of a slice. Global bindings can only
|
||||||
@@ -145,7 +145,7 @@ func (g *generator) convertDefinition(
|
|||||||
def *ast.Definition,
|
def *ast.Definition,
|
||||||
pos *ast.Position,
|
pos *ast.Position,
|
||||||
selectionSet ast.SelectionSet,
|
selectionSet ast.SelectionSet,
|
||||||
options, queryOptions *GenqlientDirective,
|
options, queryOptions *genqlientDirective,
|
||||||
) (goType, error) {
|
) (goType, error) {
|
||||||
// Check if we should use an existing type. (This is usually true for
|
// Check if we should use an existing type. (This is usually true for
|
||||||
// GraphQL scalars, but we allow you to bind non-scalar types too, if you
|
// GraphQL scalars, but we allow you to bind non-scalar types too, if you
|
||||||
@@ -315,7 +315,7 @@ func (g *generator) convertSelectionSet(
|
|||||||
namePrefix *prefixList,
|
namePrefix *prefixList,
|
||||||
selectionSet ast.SelectionSet,
|
selectionSet ast.SelectionSet,
|
||||||
containingTypedef *ast.Definition,
|
containingTypedef *ast.Definition,
|
||||||
queryOptions *GenqlientDirective,
|
queryOptions *genqlientDirective,
|
||||||
) ([]*goStructField, error) {
|
) ([]*goStructField, error) {
|
||||||
fields := make([]*goStructField, 0, len(selectionSet))
|
fields := make([]*goStructField, 0, len(selectionSet))
|
||||||
for _, selection := range selectionSet {
|
for _, selection := range selectionSet {
|
||||||
@@ -422,7 +422,7 @@ func (g *generator) convertSelectionSet(
|
|||||||
// the fragment's type. This is distinct from the rules for when a fragment
|
// the fragment's type. This is distinct from the rules for when a fragment
|
||||||
// spread is legal, which is true when the fragment would be active for *any*
|
// spread is legal, which is true when the fragment would be active for *any*
|
||||||
// of the concrete types the spread-context could have (see
|
// of the concrete types the spread-context could have (see
|
||||||
// https://spec.graphql.org/draft/#sec-Fragment-Spreads or DESIGN.md).
|
// https://spec.graphql.org/draft/#sec-Fragment-Spreads or docs/DESIGN.md).
|
||||||
//
|
//
|
||||||
// containingTypedef is as described in convertInlineFragment, below.
|
// containingTypedef is as described in convertInlineFragment, below.
|
||||||
// fragmentTypedef is the definition of the fragment's type-condition, i.e. the
|
// fragmentTypedef is the definition of the fragment's type-condition, i.e. the
|
||||||
@@ -456,12 +456,12 @@ func fragmentMatches(containingTypedef, fragmentTypedef *ast.Definition) bool {
|
|||||||
//
|
//
|
||||||
// In general, we treat such fragments' fields as if they were fields of the
|
// In general, we treat such fragments' fields as if they were fields of the
|
||||||
// parent selection-set (except of course they are only included in types the
|
// parent selection-set (except of course they are only included in types the
|
||||||
// fragment matches); see DESIGN.md for more.
|
// fragment matches); see docs/DESIGN.md for more.
|
||||||
func (g *generator) convertInlineFragment(
|
func (g *generator) convertInlineFragment(
|
||||||
namePrefix *prefixList,
|
namePrefix *prefixList,
|
||||||
fragment *ast.InlineFragment,
|
fragment *ast.InlineFragment,
|
||||||
containingTypedef *ast.Definition,
|
containingTypedef *ast.Definition,
|
||||||
queryOptions *GenqlientDirective,
|
queryOptions *genqlientDirective,
|
||||||
) ([]*goStructField, error) {
|
) ([]*goStructField, error) {
|
||||||
// You might think fragmentTypedef would be fragment.ObjectDefinition, but
|
// You might think fragmentTypedef would be fragment.ObjectDefinition, but
|
||||||
// actually that's the type into which the fragment is spread.
|
// actually that's the type into which the fragment is spread.
|
||||||
@@ -601,7 +601,7 @@ func (g *generator) convertNamedFragment(fragment *ast.FragmentDefinition) (goTy
|
|||||||
func (g *generator) convertField(
|
func (g *generator) convertField(
|
||||||
namePrefix *prefixList,
|
namePrefix *prefixList,
|
||||||
field *ast.Field,
|
field *ast.Field,
|
||||||
fieldOptions, queryOptions *GenqlientDirective,
|
fieldOptions, queryOptions *genqlientDirective,
|
||||||
) (*goStructField, error) {
|
) (*goStructField, error) {
|
||||||
if field.Definition == nil {
|
if field.Definition == nil {
|
||||||
// Unclear why gqlparser hasn't already rejected this,
|
// Unclear why gqlparser hasn't already rejected this,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
# Default genqlient config, see
|
# Default genqlient config; for full documentation see:
|
||||||
# go doc github.com/Khan/genqlient/generate.Config
|
# https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml
|
||||||
# for more options.
|
|
||||||
schema: schema.graphql
|
schema: schema.graphql
|
||||||
operations:
|
operations:
|
||||||
- genqlient.graphql
|
- genqlient.graphql
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ type argument struct {
|
|||||||
GoType string
|
GoType string
|
||||||
GraphQLName string
|
GraphQLName string
|
||||||
IsSlice bool
|
IsSlice bool
|
||||||
Options *GenqlientDirective
|
Options *genqlientDirective
|
||||||
}
|
}
|
||||||
|
|
||||||
func newGenerator(
|
func newGenerator(
|
||||||
@@ -139,7 +139,7 @@ func (g *generator) Types() (string, error) {
|
|||||||
|
|
||||||
func (g *generator) getArgument(
|
func (g *generator) getArgument(
|
||||||
arg *ast.VariableDefinition,
|
arg *ast.VariableDefinition,
|
||||||
operationDirective *GenqlientDirective,
|
operationDirective *genqlientDirective,
|
||||||
) (argument, error) {
|
) (argument, error) {
|
||||||
_, directive, err := g.parsePrecedingComment(arg, arg.Position)
|
_, directive, err := g.parsePrecedingComment(arg, arg.Position)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -8,86 +8,17 @@ import (
|
|||||||
"github.com/vektah/gqlparser/v2/parser"
|
"github.com/vektah/gqlparser/v2/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GenqlientDirective represents the @genqlient quasi-directive, used to
|
// Represents the genqlient directive, described in detail in
|
||||||
// configure genqlient on a query-by-query basis.
|
// docs/genqlient_directive.graphql.
|
||||||
//
|
type genqlientDirective struct {
|
||||||
// The syntax of the directive is just like a GraphQL directive, except it goes
|
pos *ast.Position
|
||||||
// in a comment on the line immediately preceding the field. (This is because
|
|
||||||
// GraphQL expects directives in queries to be defined by the server, not by
|
|
||||||
// the client, so it would reject a real @genqlient directive as nonexistent.)
|
|
||||||
//
|
|
||||||
// Directives may be applied to fields, arguments, or the entire query.
|
|
||||||
// Directives on the line preceding the query apply to all relevant nodes in
|
|
||||||
// the query; other directives apply to all nodes on the following line. (In
|
|
||||||
// all cases it's fine for there to be other comments in between the directive
|
|
||||||
// and the node(s) to which it applies.) For example, in the following query:
|
|
||||||
// # @genqlient(n: "a")
|
|
||||||
//
|
|
||||||
// # @genqlient(n: "b")
|
|
||||||
// #
|
|
||||||
// # Comment describing the query
|
|
||||||
// #
|
|
||||||
// # @genqlient(n: "c")
|
|
||||||
// query MyQuery(arg1: String,
|
|
||||||
// # @genqlient(n: "d")
|
|
||||||
// arg2: String, arg3: String,
|
|
||||||
// arg4: String,
|
|
||||||
// ) {
|
|
||||||
// # @genqlient(n: "e")
|
|
||||||
// field1, field2
|
|
||||||
// field3
|
|
||||||
// }
|
|
||||||
// the directive "a" is ignored, "b" and "c" apply to all relevant nodes in the
|
|
||||||
// query, "d" applies to arg2 and arg3, and "e" applies to field1 and field2.
|
|
||||||
type GenqlientDirective struct {
|
|
||||||
pos *ast.Position
|
|
||||||
|
|
||||||
// If set, this argument will be omitted if it's equal to its Go zero
|
|
||||||
// value, or is an empty slice.
|
|
||||||
//
|
|
||||||
// For example, given the following query:
|
|
||||||
// # @genqlient(omitempty: true)
|
|
||||||
// query MyQuery(arg: String) { ... }
|
|
||||||
// genqlient will generate a function
|
|
||||||
// MyQuery(ctx context.Context, client graphql.Client, arg string) ...
|
|
||||||
// which will pass {"arg": null} to GraphQL if arg is "", and the actual
|
|
||||||
// value otherwise.
|
|
||||||
//
|
|
||||||
// Only applicable to arguments of nullable types.
|
|
||||||
Omitempty *bool
|
Omitempty *bool
|
||||||
|
Pointer *bool
|
||||||
// If set, this argument or field will use a pointer type in Go. Response
|
Bind string
|
||||||
// types always use pointers, but otherwise we typically do not.
|
|
||||||
//
|
|
||||||
// This can be useful if it's a type you'll need to pass around (and want a
|
|
||||||
// pointer to save copies) or if you wish to distinguish between the Go
|
|
||||||
// zero value and null (for nullable fields).
|
|
||||||
Pointer *bool
|
|
||||||
|
|
||||||
// If set, this argument or field will use the given Go type instead of a
|
|
||||||
// genqlient-generated type.
|
|
||||||
//
|
|
||||||
// The value should be the fully-qualified type name to use for the field,
|
|
||||||
// for example:
|
|
||||||
// time.Time
|
|
||||||
// map[string]interface{}
|
|
||||||
// []github.com/you/yourpkg/subpkg.MyType
|
|
||||||
// Note that the type is the type of the whole field, e.g. if your field in
|
|
||||||
// GraphQL has type `[DateTime]`, you'd do
|
|
||||||
// # @genqlient(bind: "[]time.Time")
|
|
||||||
// (But you're not required to; if you want to map to some type DateList,
|
|
||||||
// you can do that, as long as its UnmarshalJSON method can accept a list
|
|
||||||
// of datetimes.)
|
|
||||||
//
|
|
||||||
// See Config.Bindings for more details; this is effectively to a local
|
|
||||||
// version of that global setting and should be used with similar care.
|
|
||||||
// If set to "-", overrides any such global setting and uses a
|
|
||||||
// genqlient-generated type.
|
|
||||||
Bind string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dir *GenqlientDirective) GetOmitempty() bool { return dir.Omitempty != nil && *dir.Omitempty }
|
func (dir *genqlientDirective) GetOmitempty() bool { return dir.Omitempty != nil && *dir.Omitempty }
|
||||||
func (dir *GenqlientDirective) GetPointer() bool { return dir.Pointer != nil && *dir.Pointer }
|
func (dir *genqlientDirective) GetPointer() bool { return dir.Pointer != nil && *dir.Pointer }
|
||||||
|
|
||||||
func setBool(dst **bool, v *ast.Value) error {
|
func setBool(dst **bool, v *ast.Value) error {
|
||||||
ei, err := v.Value(nil) // no vars allowed
|
ei, err := v.Value(nil) // no vars allowed
|
||||||
@@ -113,14 +44,14 @@ func setString(dst *string, v *ast.Value) error {
|
|||||||
return errorf(v.Position, "expected string, got non-string value %T(%v)", ei, ei)
|
return errorf(v.Position, "expected string, got non-string value %T(%v)", ei, ei)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fromGraphQL(dir *ast.Directive) (*GenqlientDirective, error) {
|
func fromGraphQL(dir *ast.Directive) (*genqlientDirective, error) {
|
||||||
if dir.Name != "genqlient" {
|
if dir.Name != "genqlient" {
|
||||||
// Actually we just won't get here; we only get here if the line starts
|
// Actually we just won't get here; we only get here if the line starts
|
||||||
// with "# @genqlient", unless there's some sort of bug.
|
// with "# @genqlient", unless there's some sort of bug.
|
||||||
return nil, errorf(dir.Position, "the only valid comment-directive is @genqlient, got %v", dir.Name)
|
return nil, errorf(dir.Position, "the only valid comment-directive is @genqlient, got %v", dir.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
var retval GenqlientDirective
|
var retval genqlientDirective
|
||||||
retval.pos = dir.Position
|
retval.pos = dir.Position
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@@ -143,7 +74,7 @@ func fromGraphQL(dir *ast.Directive) (*GenqlientDirective, error) {
|
|||||||
return &retval, nil
|
return &retval, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dir *GenqlientDirective) validate(node interface{}) error {
|
func (dir *genqlientDirective) validate(node interface{}) error {
|
||||||
switch node := node.(type) {
|
switch node := node.(type) {
|
||||||
case *ast.OperationDefinition:
|
case *ast.OperationDefinition:
|
||||||
if dir.Bind != "" {
|
if dir.Bind != "" {
|
||||||
@@ -177,7 +108,7 @@ func (dir *GenqlientDirective) validate(node interface{}) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dir *GenqlientDirective) merge(other *GenqlientDirective) *GenqlientDirective {
|
func (dir *genqlientDirective) merge(other *genqlientDirective) *genqlientDirective {
|
||||||
retval := *dir
|
retval := *dir
|
||||||
if other.Omitempty != nil {
|
if other.Omitempty != nil {
|
||||||
retval.Omitempty = other.Omitempty
|
retval.Omitempty = other.Omitempty
|
||||||
@@ -194,8 +125,8 @@ func (dir *GenqlientDirective) merge(other *GenqlientDirective) *GenqlientDirect
|
|||||||
func (g *generator) parsePrecedingComment(
|
func (g *generator) parsePrecedingComment(
|
||||||
node interface{},
|
node interface{},
|
||||||
pos *ast.Position,
|
pos *ast.Position,
|
||||||
) (comment string, directive *GenqlientDirective, err error) {
|
) (comment string, directive *genqlientDirective, err error) {
|
||||||
directive = new(GenqlientDirective)
|
directive = new(genqlientDirective)
|
||||||
if pos == nil || pos.Src == nil { // node was added by genqlient itself
|
if pos == nil || pos.Src == nil { // node was added by genqlient itself
|
||||||
return "", directive, nil // treated as if there were no comment
|
return "", directive, nil // treated as if there were no comment
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -2,7 +2,7 @@ package generate
|
|||||||
|
|
||||||
// This file generates the names for genqlient's generated types. This is
|
// This file generates the names for genqlient's generated types. This is
|
||||||
// somewhat tricky because the names need to be unique, stable, and, to the
|
// somewhat tricky because the names need to be unique, stable, and, to the
|
||||||
// extent possible, human-readable and -writable. See DESIGN.md for an
|
// extent possible, human-readable and -writable. See docs/DESIGN.md for an
|
||||||
// overview of the considerations; in short, we need long names.
|
// overview of the considerations; in short, we need long names.
|
||||||
//
|
//
|
||||||
// Specifically, the names we generate are of the form:
|
// Specifically, the names we generate are of the form:
|
||||||
@@ -33,9 +33,9 @@ package generate
|
|||||||
// One subtlety in the above description is: is the "MyType" the interface or
|
// One subtlety in the above description is: is the "MyType" the interface or
|
||||||
// the impelmentation? When it's a suffix, the answer is both: we generate
|
// the impelmentation? When it's a suffix, the answer is both: we generate
|
||||||
// both MyFieldMyInterface and MyFieldMyImplementation, and the latter, in Go,
|
// both MyFieldMyInterface and MyFieldMyImplementation, and the latter, in Go,
|
||||||
// implements the former. (See DESIGN.md for more.) But as an infix, we use
|
// implements the former. (See docs/DESIGN.md for more.) But as an infix, we
|
||||||
// the type on which the field is requested. Concretely, the following schema
|
// use the type on which the field is requested. Concretely, the following
|
||||||
// and query:
|
// schema and query:
|
||||||
// type Query { f: I }
|
// type Query { f: I }
|
||||||
// interface I { g: G }
|
// interface I { g: G }
|
||||||
// type T implements I { g: G, h: H }
|
// type T implements I { g: G, h: H }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# We test all the spread cases from DESIGN.md, see there for more context on
|
# We test all the spread cases from docs/DESIGN.md, see there for more context
|
||||||
# each, as well as various other nonsense. But for abstract-in-abstract
|
# on each, as well as various other nonsense. But for abstract-in-abstract
|
||||||
# spreads, we can't test cases (4b) and (4c), where I implements J or vice
|
# spreads, we can't test cases (4b) and (4c), where I implements J or vice
|
||||||
# versa, because gqlparser doesn't support interfaces that implement other
|
# versa, because gqlparser doesn't support interfaces that implement other
|
||||||
# interfaces yet.
|
# interfaces yet.
|
||||||
|
|||||||
+2
-2
@@ -858,8 +858,8 @@ type ComplexInlineFragmentsRootTopic struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// We test all the spread cases from DESIGN.md, see there for more context on
|
// We test all the spread cases from docs/DESIGN.md, see there for more context
|
||||||
// each, as well as various other nonsense. But for abstract-in-abstract
|
// on each, as well as various other nonsense. But for abstract-in-abstract
|
||||||
// spreads, we can't test cases (4b) and (4c), where I implements J or vice
|
// spreads, we can't test cases (4b) and (4c), where I implements J or vice
|
||||||
// versa, because gqlparser doesn't support interfaces that implement other
|
// versa, because gqlparser doesn't support interfaces that implement other
|
||||||
// interfaces yet.
|
// interfaces yet.
|
||||||
|
|||||||
Reference in New Issue
Block a user