Skip to content

Commit c004699

Browse files
committed
Cleanup docs and comments for pending v3.4.0 release
1 parent e7ba9af commit c004699

22 files changed

+335
-183
lines changed

README.md

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ The easiest way to get all of these and to build `cppgraphqlgen` in one step is
4747
[microsoft/vcpkg](https://github.com/microsoft/vcpkg). To install with vcpkg, make sure you've pulled the latest version
4848
and then run `vcpkg install cppgraphqlgen` (or `cppgraphqlgen:x64-windows`, `cppgraphqlgen:x86-windows-static`, etc.
4949
depending on your platform). To install just the dependencies and work in a clone of this repo, you'll need some subset
50-
of `vcpkg install pegtl boost-program-options boost-filesystem rapidjson gtest`. It works for Windows, Linux, and Mac,
50+
of `vcpkg install pegtl boost-program-options rapidjson gtest`. It works for Windows, Linux, and Mac,
5151
but if you want to try building for another platform (e.g. Android or iOS), you'll need to do more of this manually.
5252

5353
Manual installation will work best if you clone the GitHub repos for each of the dependencies and follow the installation
@@ -67,9 +67,9 @@ means you need to include an acknowledgement along with the license text.
6767

6868
### graphqlpeg
6969

70-
- GraphQL parsing: [Parsing Expression Grammar Template Library (PEGTL)](https://github.com/taocpp/PEGTL) release 3.0.0,
70+
- GraphQL parsing: [Parsing Expression Grammar Template Library (PEGTL)](https://github.com/taocpp/PEGTL) release 3.1.1,
7171
which is part of [The Art of C++](https://taocpp.github.io/) library collection. I've added this as a sub-module, so you
72-
do not need to install this separately. If you already have 3.0.0 installed where CMake can find it, it will use that
72+
do not need to install this separately. If you already have 3.1.1 installed where CMake can find it, it will use that
7373
instead of the sub-module and avoid installing another copy of PEGTL.
7474

7575
### graphqlservice
@@ -87,11 +87,6 @@ do that.
8787

8888
I'm using [Boost](https://www.boost.org/doc/libs/1_69_0/more/getting_started/index.html) for `schemagen`:
8989

90-
- C++17 std::filesystem support on Unix:
91-
[Boost.Filesystem](https://www.boost.org/doc/libs/1_69_0/libs/filesystem/doc/index.htm). Most of the default C++
92-
compilers on Linux still have `std::filesystem` from C++17 in an experimental directory and require an extra
93-
library. The standard just adopted the Boost library, so on Unix systems I have an `#ifdef` which redirects back to
94-
it for the time being.
9590
- Command line handling: [Boost.Program_options](https://www.boost.org/doc/libs/1_69_0/doc/html/program_options.html).
9691
Run `schemagen -?` to get a list of options. Many of the files in the [samples](samples/) directory were generated
9792
with `schemagen`, you can look at [samples/CMakeLists.txt](samples/CMakeLists.txt) for a few examples of how to call it:
@@ -108,6 +103,7 @@ Command line options:
108103
--header-dir arg Target path for the <prefix>Schema.h header file
109104
--no-stubs Generate abstract classes without stub implementations
110105
--separate-files Generate separate files for each of the types
106+
--no-introspection Do not generate support for Introspection
111107
```
112108

113109
I've tested this with several versions of Boost going back to 1.65.0. I expect it will work fine with most versions of

cmake/version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.3.0
1+
3.4.0

doc/fieldparams.md

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,36 @@ passes it to every `getField` method as the first parameter.
1111

1212
The `graphql::service::FieldParams` struct is declared in [GraphQLService.h](../include/graphqlservice/GraphQLService.h):
1313
```cpp
14-
// Pass a common bundle of parameters to all of the generated Object::getField accessors in a SelectionSet
14+
// Resolvers may be called in multiple different Operation contexts.
15+
enum class ResolverContext
16+
{
17+
// Resolving a Query operation.
18+
Query,
19+
20+
// Resolving a Mutation operation.
21+
Mutation,
22+
23+
// Adding a Subscription. If you need to prepare to send events for this Subsciption
24+
// (e.g. registering an event sink of your own), this is a chance to do that.
25+
NotifySubscribe,
26+
27+
// Resolving a Subscription event.
28+
Subscription,
29+
30+
// Removing a Subscription. If there are no more Subscriptions registered this is an
31+
// opportunity to release resources which are no longer needed.
32+
NotifyUnsubscribe,
33+
};
34+
35+
// Pass a common bundle of parameters to all of the generated Object::getField accessors in a
36+
// SelectionSet
1537
struct SelectionSetParams
1638
{
39+
// Context for this selection set.
40+
const ResolverContext resolverContext;
41+
1742
// The lifetime of each of these borrowed references is guaranteed until the future returned
18-
// by the accessor is resolved or destroyed. They are owned by the OperationData shared pointer.
43+
// by the accessor is resolved or destroyed. They are owned by the OperationData shared pointer.
1944
const std::shared_ptr<RequestState>& state;
2045
const response::Value& operationDirectives;
2146
const response::Value& fragmentDefinitionDirectives;
@@ -25,19 +50,32 @@ struct SelectionSetParams
2550
// you'll need to explicitly copy them into other instances of response::Value.
2651
const response::Value& fragmentSpreadDirectives;
2752
const response::Value& inlineFragmentDirectives;
53+
54+
// Field error path to this selection set.
55+
std::optional<field_path> errorPath;
56+
57+
// Async launch policy for sub-field resolvers.
58+
const std::launch launch = std::launch::deferred;
2859
};
2960

3061
// Pass a common bundle of parameters to all of the generated Object::getField accessors.
3162
struct FieldParams : SelectionSetParams
3263
{
33-
explicit FieldParams(const SelectionSetParams& selectionSetParams, response::Value&& directives);
64+
GRAPHQLSERVICE_EXPORT explicit FieldParams(
65+
SelectionSetParams&& selectionSetParams, response::Value&& directives);
3466

35-
// Each field owns its own field-specific directives. Once the accessor returns it will be destroyed,
36-
// but you can move it into another instance of response::Value to keep it alive longer.
67+
// Each field owns its own field-specific directives. Once the accessor returns it will be
68+
// destroyed, but you can move it into another instance of response::Value to keep it alive
69+
// longer.
3770
response::Value fieldDirectives;
3871
};
3972
```
4073

74+
### Resolver Context
75+
76+
The `SelectionSetParams::resolverContext` enum member informs the `getField`
77+
accessors about what type of operation is being resolved.
78+
4179
### Request State
4280

4381
The `SelectionSetParams::state` member is a reference to the
@@ -75,7 +113,29 @@ or `fragmentDefinitionDirectives` because those are kept alive until the
75113
passed by `const` reference, the reference should always be valid as long as
76114
there's a pending result from the `getField` call.
77115

116+
### Error Path
117+
118+
The `SelectionSetParams::errorPath` member should be considered an opaque
119+
implementation detail by client code. It automatically propagates through the
120+
field resolvers, and if there is a schema exception or one of the `getField`
121+
accessors throws another exception derived from `std::exception`, the
122+
`graphqlservice` library will automatically add the resulting path to the error
123+
report, accoring to the [spec](http://spec.graphql.org/June2018/#sec-Errors).
124+
125+
### Launch Policy
126+
127+
The `graphqlservice` library uses the `SelectionSetParams::launch` parameter to
128+
determine how it should handle async resolvers in the same selection set or
129+
elements in the same list. It is passed from the top-most `resolve`, `deliver`,
130+
or async `subscribe`/`unsubscribe` call. The `getField` accessors get a copy of
131+
this member in their `FieldParams` argument, and they may change their own
132+
behavior based on that, but they cannot alter the launch policy which
133+
`graphqlservice` uses for the resolvers themselves.
134+
78135
## Related Documents
79136

80137
1. The `getField` methods are discussed in more detail in [resolvers.md](./resolvers.md).
81-
2. Built-in and custom `directives` are discussed in [directives.md](./directives.md).
138+
2. Built-in and custom `directives` are discussed in [directives.md](./directives.md).
139+
3. Subscription resolvers get called up to 3 times depending on which
140+
`subscribe`/`unsubscribe` overrides you call. See [subscriptions.md](./subscriptions.md)
141+
for more details.

doc/json.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ the functions in [JSONResponse.h](../include/JSONResponse.h):
1919
```cpp
2020
namespace graphql::response {
2121

22-
std::string toJSON(Value&& response);
22+
JSONRESPONSE_EXPORT std::string toJSON(Value&& response);
2323

24-
Value parseJSON(const std::string& json);
24+
JSONRESPONSE_EXPORT Value parseJSON(const std::string& json);
2525

2626
} /* namespace graphql::response */
2727
```

doc/parsing.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44

55
As mentioned in the [README](../README.md), `cppgraphqlgen` uses the
66
[Parsing Expression Grammar Template Library (PEGTL)](https://github.com/taocpp/PEGTL)
7-
release 3.0.0, which is part of [The Art of C++](https://taocpp.github.io/)
7+
release 3.1.1, which is part of [The Art of C++](https://taocpp.github.io/)
88
library collection. I've added this as a sub-module, so you do not need to
9-
install this separately. If you already have 3.0.0 installed where CMake can
9+
install this separately. If you already have 3.1.1 installed where CMake can
1010
find it, it will use that instead of the sub-module and avoid installing
11-
another copy of PEGTL. _Note: PEGTL 3.0.0 is currently at pre-release._
11+
another copy of PEGTL.
1212

1313
It uses the [contrib/parse_tree.hpp](../PEGTL/include/tao/pegtl/contrib/parse_tree.hpp)
1414
module to build an AST automatically while parsing the document. The AST and
@@ -33,13 +33,24 @@ for hardcoded documents.
3333

3434
The UDL is used throughout the sample unit tests and in `schemagen` for the
3535
hard-coded introspection schema. It will be useful for additional unit tests
36-
against your own custom schema.
36+
against your own custom schema. The UDL
3737

3838
At runtime, you will probably call `parseString` most often to handle dynamic
3939
queries. If you have persisted queries saved to the file system or you are
4040
using a snapshot/[Approval Testing](https://approvaltests.com/) strategy you
4141
might also use `parseFile` to parse queries saved to text files.
4242

43+
When parsing an executable document with `parseString`, `parseFile`, or the
44+
UDL, the parser will try a subset of the grammar first which does not accept
45+
schema definitions, and if that fails it will try the full grammar as a
46+
fallback so that the validation step can check for documents with an invalid
47+
mix of executable and schema definitions.
48+
49+
There are `parseSchemaString` and `parseSchemaFile` functions which do the
50+
opposite, but unless you are building additional tooling on top of the
51+
`graphqlpeg` library, you will probably not need them. They have only been used
52+
by `schemagen` in this project.
53+
4354
## Encoding
4455

4556
The document must use a UTF-8 encoding. If you need to handle documents in

doc/responses.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ As the comment in
77
responses are not technically JSON-specific, although that is probably the most
88
common way of representing them. These are the primitive types that may be
99
represented in GraphQL, as of the
10-
[June 2018 spec](https://facebook.github.io/graphql/June2018/#sec-Serialization-Format):
10+
[June 2018 spec](http://spec.graphql.org/June2018/#sec-Serialization-Format):
1111

1212
```c++
1313
enum class Type : uint8_t

doc/subscriptions.md

Lines changed: 78 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,13 @@ the subscriptions to those listeners.
1313
Subscriptions are created or removed by calling the `Request::subscribe`
1414
and `Request::unsubscribe` methods in [GraphQLService.h](../include/graphqlservice/GraphQLService.h):
1515
```cpp
16-
SubscriptionKey subscribe(SubscriptionParams&& params, SubscriptionCallback&& callback);
17-
void unsubscribe(SubscriptionKey key);
16+
GRAPHQLSERVICE_EXPORT SubscriptionKey subscribe(
17+
SubscriptionParams&& params, SubscriptionCallback&& callback);
18+
GRAPHQLSERVICE_EXPORT std::future<SubscriptionKey> subscribe(
19+
std::launch launch, SubscriptionParams&& params, SubscriptionCallback&& callback);
20+
21+
GRAPHQLSERVICE_EXPORT void unsubscribe(SubscriptionKey key);
22+
GRAPHQLSERVICE_EXPORT std::future<void> unsubscribe(std::launch launch, SubscriptionKey key);
1823
```
1924
You need to fill in a `SubscriptionParams` struct with the [parsed](./parsing.md)
2025
query and any other relevant operation parameters:
@@ -37,6 +42,23 @@ The `SubscriptionCallback` signature is:
3742
using SubscriptionCallback = std::function<void(std::future<response::Value>)>;
3843
```
3944

45+
## `ResolverContext::NotifySubscribe` and `ResolverContext::NotifyUnsubscribe`
46+
47+
If you use the async version of `subscribe` and `unsubscribe` which take a
48+
`std::launch` parameter, and you provide a default instance of the
49+
`Subscription` object to the `Request`/`Operations` constructor, you will get
50+
additional callbacks with the `ResolverContext::NotifySubscribe` and
51+
`ResolverContext::NotifyUnsubscribe` values for the
52+
`FieldParams::resolverContext` member. These are passed as part of the
53+
`subscribe` and `unsubscribe` calls on the default subscription object, and
54+
they provide an opportunity to acquire or release resources that are required
55+
to implement the subscription. You can provide separate implementations of the
56+
`Subscription` object as the default in the `Operations` constructor and as the
57+
payload of a specific `deliver` call, which will be resolved with
58+
`ResolverContext::Subscription`. If you do not provide a separate
59+
`Subscription` object in the `deliver` call, it will fall back to delivering a
60+
new event resolved against the default
61+
4062
## Delivering Subscription Updates
4163

4264
There are currently three `Request::deliver` overrides you can choose from when
@@ -46,33 +68,67 @@ parameter (which should match the `Subscription` type in the `schema`). It will
4668
unconditionally invoke every subscribed `SubscriptionCallback` callback with
4769
the response to its query:
4870
```cpp
49-
void deliver(const SubscriptionName& name, const std::shared_ptr<Object>& subscriptionObject) const;
71+
GRAPHQLSERVICE_EXPORT void deliver(
72+
const SubscriptionName& name, const std::shared_ptr<Object>& subscriptionObject) const;
5073
```
5174
5275
The second override adds argument filtering. It will look at the field
5376
arguments in the subscription `query`, and if all of the required parameters
54-
in the `arguments` parameter are present and are an exact match it will dispatch the callback to that subscription:
77+
in the `arguments` parameter are present and are an exact match it will
78+
dispatch the callback to that subscription:
79+
```cpp
80+
GRAPHQLSERVICE_EXPORT void deliver(const SubscriptionName& name,
81+
const SubscriptionArguments& arguments,
82+
const std::shared_ptr<Object>& subscriptionObject) const;
83+
```
84+
85+
The third override adds directive filtering. It will look at both the field
86+
arguments and the directives with their own arguments in the subscription
87+
`query`, and if all of them match it will dispatch the callback to that
88+
subscription:
5589
```cpp
56-
void deliver(const SubscriptionName& name, const SubscriptionArguments& arguments, const std::shared_ptr<Object>& subscriptionObject) const;
90+
GRAPHQLSERVICE_EXPORT void deliver(const SubscriptionName& name,
91+
const SubscriptionArguments& arguments, const SubscriptionArguments& directives,
92+
const std::shared_ptr<Object>& subscriptionObject) const;
5793
```
5894
59-
The last override lets you customize the the way that the required arguments
60-
are matched. Instead of an exact match or making all of the arguments required,
61-
it will dispatch the callback if the `apply` function parameter returns true
62-
for every required field in the subscription `query`.
95+
The last two overrides let you customize the the way that the required
96+
arguments and directives are matched. Instead of an exact match or making all
97+
of the arguments required, it will dispatch the callback if the `apply`
98+
function parameters return true for every required field and directive in the
99+
subscription `query`.
63100
```cpp
64-
void deliver(const SubscriptionName& name, const SubscriptionFilterCallback& apply, const std::shared_ptr<Object>& subscriptionObject) const;
101+
GRAPHQLSERVICE_EXPORT void deliver(const SubscriptionName& name,
102+
const SubscriptionFilterCallback& applyArguments,
103+
const std::shared_ptr<Object>& subscriptionObject) const;
104+
GRAPHQLSERVICE_EXPORT void deliver(const SubscriptionName& name,
105+
const SubscriptionFilterCallback& applyArguments,
106+
const SubscriptionFilterCallback& applyDirectives,
107+
const std::shared_ptr<Object>& subscriptionObject) const;
65108
```
66109

67-
By default, `deliver` invokes all of the `SubscriptionCallback` listeners with `std::future`
68-
payloads which are resolved on-demand but synchronously, using `std::launch::deferred` with the
69-
`std::async` function. There's also a version of each overload which lets you substitute the
70-
`std::launch::async` option to begin executing the queries and invoke the callbacks on multiple
71-
threads in parallel:
110+
By default, `deliver` invokes all of the `SubscriptionCallback` listeners with
111+
`std::future` payloads which are resolved on-demand but synchronously, using
112+
`std::launch::deferred` with the `std::async` function. There's also a version
113+
of each overload which lets you substitute the `std::launch::async` option to
114+
begin executing the queries and invoke the callbacks on multiple threads in
115+
parallel:
72116
```cpp
73-
void deliver(std::launch launch, const SubscriptionName& name, const std::shared_ptr<Object>& subscriptionObject) const;
74-
void deliver(std::launch launch, const SubscriptionName& name, const SubscriptionArguments& arguments, const std::shared_ptr<Object>& subscriptionObject) const;
75-
void deliver(std::launch launch, const SubscriptionName& name, const SubscriptionFilterCallback& apply, const std::shared_ptr<Object>& subscriptionObject) const;
117+
GRAPHQLSERVICE_EXPORT void deliver(std::launch launch, const SubscriptionName& name,
118+
const std::shared_ptr<Object>& subscriptionObject) const;
119+
GRAPHQLSERVICE_EXPORT void deliver(std::launch launch, const SubscriptionName& name,
120+
const SubscriptionArguments& arguments,
121+
const std::shared_ptr<Object>& subscriptionObject) const;
122+
GRAPHQLSERVICE_EXPORT void deliver(std::launch launch, const SubscriptionName& name,
123+
const SubscriptionArguments& arguments, const SubscriptionArguments& directives,
124+
const std::shared_ptr<Object>& subscriptionObject) const;
125+
GRAPHQLSERVICE_EXPORT void deliver(std::launch launch, const SubscriptionName& name,
126+
const SubscriptionFilterCallback& applyArguments,
127+
const std::shared_ptr<Object>& subscriptionObject) const;
128+
GRAPHQLSERVICE_EXPORT void deliver(std::launch launch, const SubscriptionName& name,
129+
const SubscriptionFilterCallback& applyArguments,
130+
const SubscriptionFilterCallback& applyDirectives,
131+
const std::shared_ptr<Object>& subscriptionObject) const;
76132
```
77133
78134
## Handling Multiple Operation Types
@@ -83,8 +139,9 @@ tell which operation type it is without parsing the query and searching for
83139
a specific operation name, so it's hard to tell whether you should call
84140
`resolve` or `subscribe` when the request is received that way. To help with
85141
that, there's a public `Request::findOperationDefinition` method which returns
86-
the operation type as a `std::string_view` along with a pointer to the AST node for
87-
the selected operation in the parsed query:
142+
the operation type as a `std::string_view` along with a pointer to the AST node
143+
for the selected operation in the parsed query:
88144
```cpp
89-
std::pair<std::string_view, const peg::ast_node*> findOperationDefinition(peg::ast& root, std::string_view operationName) const;
145+
GRAPHQLSERVICE_EXPORT std::pair<std::string_view, const peg::ast_node*> findOperationDefinition(
146+
peg::ast& query, std::string_view operationName) const;
90147
```

0 commit comments

Comments
 (0)