## Summary: This is useful if you want to bind to a type you don't control (or use for other things) but need different serialization than its default. This is a feature gqlgen has and we've found it very useful. For example, in webapp we want to bind `DateTime` to `time.Time`, but its default serialization is not compatible with Python, so currently we have to bind to a wrapper type and cast all over the place, which is exactly the sort of boilerplate genqlient is supposed to avoid. For unmarshaling, the implementation basically just follows the existing support for abstract types; instead of calling our own generated helper, we now call your specified function. This required some refactoring to abstract the handling of custom unmarshalers generally from abstract types specifically, and to wire in not only the unmarshaler-name but also the `generator` (in order to compute the right import alias). For marshaling, I had to implement all that stuff over again; it's mostly parallel to unmarshaling (and I made a few minor changes to unmarshaling to make the two more parallel). Luckily, after #103 I at least only had to do it once, rather than implementing the same functionality for arguments and for input-type fields. It was still quite a bit of code; I didn't try to be quite as completionist about the tests as with unmarshal but still had to add a few. Issue: https://github.com/Khan/genqlient/issues/38 ## Test plan: make check Author: benjaminjkraft Reviewers: StevenACoffman, dnerdy, benjaminjkraft, aberkan, jvoll, mahtabsabet, MiguelCastillo Required Reviewers: Approved By: StevenACoffman, dnerdy Checks: ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Lint, ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Lint Pull Request URL: https://github.com/Khan/genqlient/pull/104
137 lines
5.3 KiB
GraphQL
137 lines
5.3 KiB
GraphQL
# 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 has an empty value, defined
|
|
# (the same as in encoding/json) as false, 0, a nil pointer, a nil interface
|
|
# value, and any empty array, slice, map, or string.
|
|
#
|
|
# 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. Ignored for types with
|
|
# custom marshalers (see their documentation in genqlient.yaml for details).
|
|
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 field will use a struct type in Go, even if it's an interface.
|
|
#
|
|
# This is useful when you have a query like
|
|
# query MyQuery {
|
|
# myInterface { myField }
|
|
# }
|
|
# where you are requesting only shared fields of an interface. By default,
|
|
# genqlient still generates an interface type, for consistency. But this
|
|
# isn't necessary: a struct would do just fine since there are no
|
|
# type-specific fields. Setting `struct: true` tells genqlient to do that.
|
|
#
|
|
# Note that this is only allowed when there are no fragments in play, such
|
|
# that all fields are on the interface type. Note that if you later add a
|
|
# fragment, you'll have to remove this option, and the types will change.
|
|
struct: 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
|
|
|
|
# If set, the type of this field will have the given name in Go.
|
|
#
|
|
# For example, given the following query:
|
|
# # @genqlient(typename: "MyResp")
|
|
# query MyQuery {
|
|
# # @genqlient(typename: "User")
|
|
# user {
|
|
# id
|
|
# }
|
|
# }
|
|
# genqlient will generate
|
|
# type Resp struct {
|
|
# User User
|
|
# }
|
|
# type User struct {
|
|
# Id string
|
|
# }
|
|
# instead of its usual, more verbose type names.
|
|
#
|
|
# With great power comes great responsibility: when using typename you'll
|
|
# need to avoid comments; genqlient will complain if you use the same
|
|
# type-name in multiple places unless they request the exact same fields, or
|
|
# if your type-name conflicts with an autogenerated one (again, unless they
|
|
# request the exact same fields). They must even have the fields in the
|
|
# same order. Fragments are often easier to use (see the discussion of
|
|
# code-sharing in FAQ.md).
|
|
#
|
|
# Note that unlike most directives, if applied to the entire operation,
|
|
# typename affects the overall response type, rather than being propagated
|
|
# down to all child fields (which would cause conflicts).
|
|
typename: 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
|