Skip to content
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
2 changes: 1 addition & 1 deletion doc/fieldparams.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ 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).
report, accoring to the [spec](https://spec.graphql.org/October2021/#sec-Errors).

### Launch Policy

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](http://spec.graphql.org/June2018/#sec-Serialization-Format):
[October 2021 spec](https://spec.graphql.org/October2021/#sec-Serialization-Format):

```c++
enum class Type : uint8_t
Expand Down
2 changes: 2 additions & 0 deletions include/SchemaGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ class Generator
void outputObjectImplementation(
std::ostream& sourceFile, const ObjectType& objectType, bool isQueryType) const;
void outputObjectIntrospection(std::ostream& sourceFile, const ObjectType& objectType) const;
void outputIntrospectionInterfaces(std::ostream& sourceFile, std::string_view cppType,
const std::vector<std::string_view>& interfaces) const;
void outputIntrospectionFields(
std::ostream& sourceFile, std::string_view cppType, const OutputFieldList& fields) const;
std::string getArgumentDefaultValue(
Expand Down
15 changes: 15 additions & 0 deletions include/SchemaLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ struct ScalarType
{
std::string_view type;
std::string_view description;
std::string_view specifiedByURL;
};

using ScalarTypeList = std::vector<ScalarType>;
Expand Down Expand Up @@ -113,6 +114,7 @@ using InputTypeList = std::vector<InputType>;
struct Directive
{
std::string_view name;
bool isRepeatable = false;
std::vector<std::string_view> locations;
InputFieldList arguments;
std::string_view description;
Expand Down Expand Up @@ -174,6 +176,7 @@ struct InterfaceType
{
std::string_view type;
std::string_view cppType;
std::vector<std::string_view> interfaces;
OutputFieldList fields;
std::string_view description;
};
Expand Down Expand Up @@ -219,6 +222,7 @@ class SchemaLoader
explicit SchemaLoader(SchemaOptions&& schemaOptions);

bool isIntrospection() const noexcept;
std::string_view getSchemaDescription() const noexcept;
std::string_view getFilenamePrefix() const noexcept;
std::string_view getSchemaNamespace() const noexcept;

Expand Down Expand Up @@ -265,6 +269,7 @@ class SchemaLoader
void visitSchemaDefinition(const peg::ast_node& schemaDefinition);
void visitSchemaExtension(const peg::ast_node& schemaExtension);
void visitScalarTypeDefinition(const peg::ast_node& scalarTypeDefinition);
void visitScalarTypeExtension(const peg::ast_node& scalarTypeExtension);
void visitEnumTypeDefinition(const peg::ast_node& enumTypeDefinition);
void visitEnumTypeExtension(const peg::ast_node& enumTypeExtension);
void visitInputObjectTypeDefinition(const peg::ast_node& inputObjectTypeDefinition);
Expand All @@ -277,6 +282,8 @@ class SchemaLoader
void visitObjectTypeExtension(const peg::ast_node& objectTypeExtension);
void visitDirectiveDefinition(const peg::ast_node& directiveDefinition);

static void blockReservedName(
std::string_view name, std::optional<tao::graphqlpeg::position> position = std::nullopt);
static OutputFieldList getOutputFields(const peg::ast_node::children_t& fields);
static InputFieldList getInputFields(const peg::ast_node::children_t& fields);

Expand All @@ -286,6 +293,13 @@ class SchemaLoader
const std::optional<std::string_view>& accessor);
void fixupInputFieldList(InputFieldList& fields);
void reorderInputTypeDependencies();
void validateImplementedInterfaces() const;
const InterfaceType& findInterfaceType(
std::string_view typeName, std::string_view interfaceName) const;
void validateInterfaceFields(std::string_view typeName,
std::string_view interfaceName, const OutputFieldList& typeFields) const;
void validateTransitiveInterfaces(
std::string_view typeName, const std::vector<std::string_view>& interfaces) const;

static const std::string_view s_introspectionNamespace;
static const BuiltinTypeMap s_builtinTypes;
Expand All @@ -294,6 +308,7 @@ class SchemaLoader

const SchemaOptions _schemaOptions;
const bool _isIntrospection;
std::string_view _schemaDescription;
std::string_view _schemaNamespace;
peg::ast _ast;

Expand Down
2 changes: 2 additions & 0 deletions include/Validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ using ValidateDirectiveArguments = internal::string_view_map<ValidateArgument>;

struct ValidateDirective
{
bool isRepeatable = false;
internal::sorted_set<introspection::DirectiveLocation> locations;
ValidateDirectiveArguments arguments;
};
Expand Down Expand Up @@ -250,6 +251,7 @@ class ValidateExecutableVisitor
VariableSet _referencedVariables;
FragmentSet _fragmentStack;
size_t _fieldCount = 0;
size_t _introspectionFieldCount = 0;
TypeFields _typeFields;
InputTypeFields _inputTypeFields;
ValidateType _scopedType;
Expand Down
2 changes: 1 addition & 1 deletion include/graphqlservice/GraphQLResponse.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ namespace graphql::response {

// GraphQL 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](http://spec.graphql.org/June2018/#sec-Serialization-Format).
// of the [October 2021 spec](https://spec.graphql.org/October2021/#sec-Serialization-Format).
enum class Type : uint8_t
{
Map, // JSON Object
Expand Down
60 changes: 35 additions & 25 deletions include/graphqlservice/GraphQLService.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,15 @@ class await_async : public coro::suspend_always
}
};

// Directive order matters, and some of them are repeatable. So rather than passing them in a
// response::Value, pass directives in something like the underlying response::MapType which
// preserves the order of the elements without complete uniqueness.
using Directives = std::vector<std::pair<std::string_view, response::Value>>;

// Traversing a fragment spread adds a new set of directives.
using FragmentDefinitionDirectiveStack = std::list<std::reference_wrapper<const Directives>>;
using FragmentSpreadDirectiveStack = std::list<Directives>;

// Pass a common bundle of parameters to all of the generated Object::getField accessors in a
// SelectionSet
struct SelectionSetParams
Expand All @@ -239,14 +248,14 @@ struct SelectionSetParams
// 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.
const std::shared_ptr<RequestState>& state;
const response::Value& operationDirectives;
const response::Value& fragmentDefinitionDirectives;
const Directives& operationDirectives;
const std::shared_ptr<FragmentDefinitionDirectiveStack> fragmentDefinitionDirectives;

// Fragment directives are shared for all fields in that fragment, but they aren't kept alive
// after the call to the last accessor in the fragment. If you need to keep them alive longer,
// you'll need to explicitly copy them into other instances of response::Value.
const response::Value& fragmentSpreadDirectives;
const response::Value& inlineFragmentDirectives;
// you'll need to explicitly copy them into other instances of Directives.
const std::shared_ptr<FragmentSpreadDirectiveStack> fragmentSpreadDirectives;
const std::shared_ptr<FragmentSpreadDirectiveStack> inlineFragmentDirectives;

// Field error path to this selection set.
std::optional<field_path> errorPath;
Expand All @@ -259,12 +268,12 @@ struct SelectionSetParams
struct FieldParams : SelectionSetParams
{
GRAPHQLSERVICE_EXPORT explicit FieldParams(
SelectionSetParams&& selectionSetParams, response::Value directives);
SelectionSetParams&& selectionSetParams, Directives 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.
response::Value fieldDirectives;
Directives fieldDirectives;
};

// Field accessors may return either a result of T, an awaitable of T, or a std::future<T>, so at
Expand Down Expand Up @@ -379,11 +388,11 @@ class Fragment

std::string_view getType() const;
const peg::ast_node& getSelection() const;
const response::Value& getDirectives() const;
const Directives& getDirectives() const;

private:
std::string_view _type;
response::Value _directives;
Directives _directives;

std::reference_wrapper<const peg::ast_node> _selection;
};
Expand All @@ -399,16 +408,16 @@ struct ResolverParams : SelectionSetParams
{
GRAPHQLSERVICE_EXPORT explicit ResolverParams(const SelectionSetParams& selectionSetParams,
const peg::ast_node& field, std::string&& fieldName, response::Value arguments,
response::Value fieldDirectives, const peg::ast_node* selection,
const FragmentMap& fragments, const response::Value& variables);
Directives fieldDirectives, const peg::ast_node* selection, const FragmentMap& fragments,
const response::Value& variables);

GRAPHQLSERVICE_EXPORT schema_location getLocation() const;

// These values are different for each resolver.
const peg::ast_node& field;
std::string fieldName;
response::Value arguments { response::Type::Map };
response::Value fieldDirectives { response::Type::Map };
Directives fieldDirectives;
const peg::ast_node* selection;

// These values remain unchanged for the entire operation, but they're passed to each of the
Expand Down Expand Up @@ -944,19 +953,20 @@ struct SubscriptionParams
struct OperationData : std::enable_shared_from_this<OperationData>
{
explicit OperationData(std::shared_ptr<RequestState> state, response::Value variables,
response::Value directives, FragmentMap fragments);
Directives directives, FragmentMap fragments);

std::shared_ptr<RequestState> state;
response::Value variables;
response::Value directives;
Directives directives;
FragmentMap fragments;
};

// Subscription callbacks receive the response::Value representing the result of evaluating the
// SelectionSet against the payload.
using SubscriptionCallback = std::function<void(response::Value)>;
using SubscriptionArguments = std::map<std::string_view, response::Value>;
using SubscriptionFilterCallback = std::function<bool(response::MapType::const_reference)>;
using SubscriptionArgumentFilterCallback = std::function<bool(response::MapType::const_reference)>;
using SubscriptionDirectiveFilterCallback = std::function<bool(Directives::const_reference)>;

// Subscriptions are stored in maps using these keys.
using SubscriptionKey = size_t;
Expand All @@ -970,15 +980,15 @@ using AwaitableDeliver = internal::Awaitable<void>;
struct SubscriptionData : std::enable_shared_from_this<SubscriptionData>
{
explicit SubscriptionData(std::shared_ptr<OperationData> data, SubscriptionName&& field,
response::Value arguments, response::Value fieldDirectives, peg::ast&& query,
response::Value arguments, Directives fieldDirectives, peg::ast&& query,
std::string&& operationName, SubscriptionCallback&& callback,
const peg::ast_node& selection);

std::shared_ptr<OperationData> data;

SubscriptionName field;
response::Value arguments;
response::Value fieldDirectives;
Directives fieldDirectives;
peg::ast query;
std::string operationName;
SubscriptionCallback callback;
Expand Down Expand Up @@ -1023,29 +1033,29 @@ class Request : public std::enable_shared_from_this<Request>
GRAPHQLSERVICE_EXPORT void deliver(const SubscriptionName& name,
const SubscriptionArguments& arguments, std::shared_ptr<Object> subscriptionObject) const;
GRAPHQLSERVICE_EXPORT void deliver(const SubscriptionName& name,
const SubscriptionArguments& arguments, const SubscriptionArguments& directives,
const SubscriptionArguments& arguments, const Directives& directives,
std::shared_ptr<Object> subscriptionObject) const;
GRAPHQLSERVICE_EXPORT void deliver(const SubscriptionName& name,
const SubscriptionFilterCallback& applyArguments,
const SubscriptionArgumentFilterCallback& applyArguments,
std::shared_ptr<Object> subscriptionObject) const;
GRAPHQLSERVICE_EXPORT void deliver(const SubscriptionName& name,
const SubscriptionFilterCallback& applyArguments,
const SubscriptionFilterCallback& applyDirectives,
const SubscriptionArgumentFilterCallback& applyArguments,
const SubscriptionDirectiveFilterCallback& applyDirectives,
std::shared_ptr<Object> subscriptionObject) const;

GRAPHQLSERVICE_EXPORT AwaitableDeliver deliver(await_async launch, const SubscriptionName& name,
std::shared_ptr<Object> subscriptionObject) const;
GRAPHQLSERVICE_EXPORT AwaitableDeliver deliver(await_async launch, const SubscriptionName& name,
const SubscriptionArguments& arguments, std::shared_ptr<Object> subscriptionObject) const;
GRAPHQLSERVICE_EXPORT AwaitableDeliver deliver(await_async launch, const SubscriptionName& name,
const SubscriptionArguments& arguments, const SubscriptionArguments& directives,
const SubscriptionArguments& arguments, const Directives& directives,
std::shared_ptr<Object> subscriptionObject) const;
GRAPHQLSERVICE_EXPORT AwaitableDeliver deliver(await_async launch, const SubscriptionName& name,
const SubscriptionFilterCallback& applyArguments,
const SubscriptionArgumentFilterCallback& applyArguments,
std::shared_ptr<Object> subscriptionObject) const;
GRAPHQLSERVICE_EXPORT AwaitableDeliver deliver(await_async launch, const SubscriptionName& name,
const SubscriptionFilterCallback& applyArguments,
const SubscriptionFilterCallback& applyDirectives,
const SubscriptionArgumentFilterCallback& applyArguments,
const SubscriptionDirectiveFilterCallback& applyDirectives,
std::shared_ptr<Object> subscriptionObject) const;

private:
Expand Down
Loading