Skip to content

Miscellaneous cleanup getting ready for 3.4.0 release #144

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Dec 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

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

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

### graphqlpeg

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

### graphqlservice
Expand All @@ -87,11 +87,6 @@ do that.

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

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

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
Expand Down
2 changes: 1 addition & 1 deletion cmake/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.3.0
3.4.0
72 changes: 66 additions & 6 deletions doc/fieldparams.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,36 @@ passes it to every `getField` method as the first parameter.

The `graphql::service::FieldParams` struct is declared in [GraphQLService.h](../include/graphqlservice/GraphQLService.h):
```cpp
// Pass a common bundle of parameters to all of the generated Object::getField accessors in a SelectionSet
// Resolvers may be called in multiple different Operation contexts.
enum class ResolverContext
{
// Resolving a Query operation.
Query,

// Resolving a Mutation operation.
Mutation,

// Adding a Subscription. If you need to prepare to send events for this Subsciption
// (e.g. registering an event sink of your own), this is a chance to do that.
NotifySubscribe,

// Resolving a Subscription event.
Subscription,

// Removing a Subscription. If there are no more Subscriptions registered this is an
// opportunity to release resources which are no longer needed.
NotifyUnsubscribe,
};

// Pass a common bundle of parameters to all of the generated Object::getField accessors in a
// SelectionSet
struct SelectionSetParams
{
// Context for this selection set.
const ResolverContext resolverContext;

// The lifetime of each of these borrowed references is guaranteed until the future returned
// by the accessor is resolved or destroyed. They are owned by the OperationData shared pointer.
// by the accessor is resolved or destroyed. They are owned by the OperationData shared pointer.
const std::shared_ptr<RequestState>& state;
const response::Value& operationDirectives;
const response::Value& fragmentDefinitionDirectives;
Expand All @@ -25,19 +50,32 @@ struct SelectionSetParams
// you'll need to explicitly copy them into other instances of response::Value.
const response::Value& fragmentSpreadDirectives;
const response::Value& inlineFragmentDirectives;

// Field error path to this selection set.
std::optional<field_path> errorPath;

// Async launch policy for sub-field resolvers.
const std::launch launch = std::launch::deferred;
};

// Pass a common bundle of parameters to all of the generated Object::getField accessors.
struct FieldParams : SelectionSetParams
{
explicit FieldParams(const SelectionSetParams& selectionSetParams, response::Value&& directives);
GRAPHQLSERVICE_EXPORT explicit FieldParams(
SelectionSetParams&& selectionSetParams, response::Value&& directives);

// Each field owns its own field-specific directives. Once the accessor returns it will be destroyed,
// but you can move it into another instance of response::Value to keep it alive longer.
// Each field owns its own field-specific directives. Once the accessor returns it will be
// destroyed, but you can move it into another instance of response::Value to keep it alive
// longer.
response::Value fieldDirectives;
};
```

### Resolver Context

The `SelectionSetParams::resolverContext` enum member informs the `getField`
accessors about what type of operation is being resolved.

### Request State

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

### Error Path

The `SelectionSetParams::errorPath` member should be considered an opaque
implementation detail by client code. It automatically propagates through the
field resolvers, and if there is a schema exception or one of the `getField`
accessors throws another exception derived from `std::exception`, the
`graphqlservice` library will automatically add the resulting path to the error
report, accoring to the [spec](http://spec.graphql.org/June2018/#sec-Errors).

### Launch Policy

The `graphqlservice` library uses the `SelectionSetParams::launch` parameter to
determine how it should handle async resolvers in the same selection set or
elements in the same list. It is passed from the top-most `resolve`, `deliver`,
or async `subscribe`/`unsubscribe` call. The `getField` accessors get a copy of
this member in their `FieldParams` argument, and they may change their own
behavior based on that, but they cannot alter the launch policy which
`graphqlservice` uses for the resolvers themselves.

## Related Documents

1. The `getField` methods are discussed in more detail in [resolvers.md](./resolvers.md).
2. Built-in and custom `directives` are discussed in [directives.md](./directives.md).
2. Built-in and custom `directives` are discussed in [directives.md](./directives.md).
3. Subscription resolvers get called up to 3 times depending on which
`subscribe`/`unsubscribe` overrides you call. See [subscriptions.md](./subscriptions.md)
for more details.
4 changes: 2 additions & 2 deletions doc/json.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ the functions in [JSONResponse.h](../include/JSONResponse.h):
```cpp
namespace graphql::response {

std::string toJSON(Value&& response);
JSONRESPONSE_EXPORT std::string toJSON(Value&& response);

Value parseJSON(const std::string& json);
JSONRESPONSE_EXPORT Value parseJSON(const std::string& json);

} /* namespace graphql::response */
```
Expand Down
17 changes: 14 additions & 3 deletions doc/parsing.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

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

It uses the [contrib/parse_tree.hpp](../PEGTL/include/tao/pegtl/contrib/parse_tree.hpp)
module to build an AST automatically while parsing the document. The AST and
Expand Down Expand Up @@ -40,6 +40,17 @@ queries. If you have persisted queries saved to the file system or you are
using a snapshot/[Approval Testing](https://approvaltests.com/) strategy you
might also use `parseFile` to parse queries saved to text files.

When parsing an executable document with `parseString`, `parseFile`, or the
UDL, the parser will try a subset of the grammar first which does not accept
schema definitions, and if that fails it will try the full grammar as a
fallback so that the validation step can check for documents with an invalid
mix of executable and schema definitions.

There are `parseSchemaString` and `parseSchemaFile` functions which do the
opposite, but unless you are building additional tooling on top of the
`graphqlpeg` library, you will probably not need them. They have only been used
by `schemagen` in this project.

## Encoding

The document must use a UTF-8 encoding. If you need to handle documents in
Expand Down
10 changes: 7 additions & 3 deletions doc/resolvers.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,19 @@ schema {

Executing a query or mutation starts by calling `Request::resolve` from [GraphQLService.h](../include/graphqlservice/GraphQLService.h):
```cpp
std::future<response::Value> resolve(const std::shared_ptr<RequestState>& state, peg::ast& query, const std::string& operationName, response::Value&& variables) const;
GRAPHQLSERVICE_EXPORT std::future<response::Value> resolve(
const std::shared_ptr<RequestState>& state, peg::ast& query,
const std::string& operationName, response::Value&& variables) const;
```
By default, the `std::future` results are resolved on-demand but synchronously,
using `std::launch::deferred` with the `std::async` function. You can also use
an override of `Request::resolve` which lets you substitute the
`std::launch::async` option to begin executing the query on multiple threads
in parallel:
```cpp
std::future<response::Value> resolve(std::launch launch, const std::shared_ptr<RequestState>& state, peg::ast& query, const std::string& operationName, response::Value&& variables) const;
GRAPHQLSERVICE_EXPORT std::future<response::Value> resolve(std::launch launch,
const std::shared_ptr<RequestState>& state, peg::ast& query,
const std::string& operationName, response::Value&& variables) const;
```

### `graphql::service::Request` and `graphql::<schema>::Operations`
Expand All @@ -66,7 +70,7 @@ recursively call the `resolvers` for each of the `fields` in the nested
`graphql::today::object::Appointment` object from the `today` sample in
[AppointmentObject.h](../samples/separate/AppointmentObject.h).
```cpp
std::future<response::Value> resolveId(service::ResolverParams&& params);
std::future<service::ResolverResult> resolveId(service::ResolverParams&& params);
```
In this example, the `resolveId` method invokes `getId`:
```cpp
Expand Down
2 changes: 1 addition & 1 deletion doc/responses.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ As the comment in
responses are not technically JSON-specific, although that is probably the most
common way of representing them. These are the primitive types that may be
represented in GraphQL, as of the
[June 2018 spec](https://facebook.github.io/graphql/June2018/#sec-Serialization-Format):
[June 2018 spec](http://spec.graphql.org/June2018/#sec-Serialization-Format):

```c++
enum class Type : uint8_t
Expand Down
Loading