Skip to content

Commit c29bcbf

Browse files
committed
Support repeatable directives and preserve fragment directives
1 parent 6ed1209 commit c29bcbf

File tree

10 files changed

+232
-213
lines changed

10 files changed

+232
-213
lines changed

include/Validation.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ using ValidateDirectiveArguments = internal::string_view_map<ValidateArgument>;
3737

3838
struct ValidateDirective
3939
{
40+
bool isRepeatable = false;
4041
internal::sorted_set<introspection::DirectiveLocation> locations;
4142
ValidateDirectiveArguments arguments;
4243
};

include/graphqlservice/GraphQLService.h

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,15 @@ class await_async : public coro::suspend_always
229229
}
230230
};
231231

232+
// Directive order matters, and some of them are repeatable. So rather than passing them in a
233+
// response::Value, pass directives in something like the underlying response::MapType which
234+
// preserves the order of the elements without complete uniqueness.
235+
using Directives = std::vector<std::pair<std::string_view, response::Value>>;
236+
237+
// Traversing a fragment spread adds a new set of directives.
238+
using FragmentDefinitionDirectiveStack = std::list<std::reference_wrapper<const Directives>>;
239+
using FragmentSpreadDirectiveStack = std::list<Directives>;
240+
232241
// Pass a common bundle of parameters to all of the generated Object::getField accessors in a
233242
// SelectionSet
234243
struct SelectionSetParams
@@ -239,14 +248,14 @@ struct SelectionSetParams
239248
// The lifetime of each of these borrowed references is guaranteed until the future returned
240249
// by the accessor is resolved or destroyed. They are owned by the OperationData shared pointer.
241250
const std::shared_ptr<RequestState>& state;
242-
const response::Value& operationDirectives;
243-
const response::Value& fragmentDefinitionDirectives;
251+
const Directives& operationDirectives;
252+
const std::shared_ptr<FragmentDefinitionDirectiveStack> fragmentDefinitionDirectives;
244253

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

251260
// Field error path to this selection set.
252261
std::optional<field_path> errorPath;
@@ -259,12 +268,12 @@ struct SelectionSetParams
259268
struct FieldParams : SelectionSetParams
260269
{
261270
GRAPHQLSERVICE_EXPORT explicit FieldParams(
262-
SelectionSetParams&& selectionSetParams, response::Value directives);
271+
SelectionSetParams&& selectionSetParams, Directives directives);
263272

264273
// Each field owns its own field-specific directives. Once the accessor returns it will be
265274
// destroyed, but you can move it into another instance of response::Value to keep it alive
266275
// longer.
267-
response::Value fieldDirectives;
276+
Directives fieldDirectives;
268277
};
269278

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

380389
std::string_view getType() const;
381390
const peg::ast_node& getSelection() const;
382-
const response::Value& getDirectives() const;
391+
const Directives& getDirectives() const;
383392

384393
private:
385394
std::string_view _type;
386-
response::Value _directives;
395+
Directives _directives;
387396

388397
std::reference_wrapper<const peg::ast_node> _selection;
389398
};
@@ -399,16 +408,16 @@ struct ResolverParams : SelectionSetParams
399408
{
400409
GRAPHQLSERVICE_EXPORT explicit ResolverParams(const SelectionSetParams& selectionSetParams,
401410
const peg::ast_node& field, std::string&& fieldName, response::Value arguments,
402-
response::Value fieldDirectives, const peg::ast_node* selection,
403-
const FragmentMap& fragments, const response::Value& variables);
411+
Directives fieldDirectives, const peg::ast_node* selection, const FragmentMap& fragments,
412+
const response::Value& variables);
404413

405414
GRAPHQLSERVICE_EXPORT schema_location getLocation() const;
406415

407416
// These values are different for each resolver.
408417
const peg::ast_node& field;
409418
std::string fieldName;
410419
response::Value arguments { response::Type::Map };
411-
response::Value fieldDirectives { response::Type::Map };
420+
Directives fieldDirectives;
412421
const peg::ast_node* selection;
413422

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

949958
std::shared_ptr<RequestState> state;
950959
response::Value variables;
951-
response::Value directives;
960+
Directives directives;
952961
FragmentMap fragments;
953962
};
954963

955964
// Subscription callbacks receive the response::Value representing the result of evaluating the
956965
// SelectionSet against the payload.
957966
using SubscriptionCallback = std::function<void(response::Value)>;
958967
using SubscriptionArguments = std::map<std::string_view, response::Value>;
959-
using SubscriptionFilterCallback = std::function<bool(response::MapType::const_reference)>;
968+
using SubscriptionArgumentFilterCallback = std::function<bool(response::MapType::const_reference)>;
969+
using SubscriptionDirectiveFilterCallback = std::function<bool(Directives::const_reference)>;
960970

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

977987
std::shared_ptr<OperationData> data;
978988

979989
SubscriptionName field;
980990
response::Value arguments;
981-
response::Value fieldDirectives;
991+
Directives fieldDirectives;
982992
peg::ast query;
983993
std::string operationName;
984994
SubscriptionCallback callback;
@@ -1023,29 +1033,29 @@ class Request : public std::enable_shared_from_this<Request>
10231033
GRAPHQLSERVICE_EXPORT void deliver(const SubscriptionName& name,
10241034
const SubscriptionArguments& arguments, std::shared_ptr<Object> subscriptionObject) const;
10251035
GRAPHQLSERVICE_EXPORT void deliver(const SubscriptionName& name,
1026-
const SubscriptionArguments& arguments, const SubscriptionArguments& directives,
1036+
const SubscriptionArguments& arguments, const Directives& directives,
10271037
std::shared_ptr<Object> subscriptionObject) const;
10281038
GRAPHQLSERVICE_EXPORT void deliver(const SubscriptionName& name,
1029-
const SubscriptionFilterCallback& applyArguments,
1039+
const SubscriptionArgumentFilterCallback& applyArguments,
10301040
std::shared_ptr<Object> subscriptionObject) const;
10311041
GRAPHQLSERVICE_EXPORT void deliver(const SubscriptionName& name,
1032-
const SubscriptionFilterCallback& applyArguments,
1033-
const SubscriptionFilterCallback& applyDirectives,
1042+
const SubscriptionArgumentFilterCallback& applyArguments,
1043+
const SubscriptionDirectiveFilterCallback& applyDirectives,
10341044
std::shared_ptr<Object> subscriptionObject) const;
10351045

10361046
GRAPHQLSERVICE_EXPORT AwaitableDeliver deliver(await_async launch, const SubscriptionName& name,
10371047
std::shared_ptr<Object> subscriptionObject) const;
10381048
GRAPHQLSERVICE_EXPORT AwaitableDeliver deliver(await_async launch, const SubscriptionName& name,
10391049
const SubscriptionArguments& arguments, std::shared_ptr<Object> subscriptionObject) const;
10401050
GRAPHQLSERVICE_EXPORT AwaitableDeliver deliver(await_async launch, const SubscriptionName& name,
1041-
const SubscriptionArguments& arguments, const SubscriptionArguments& directives,
1051+
const SubscriptionArguments& arguments, const Directives& directives,
10421052
std::shared_ptr<Object> subscriptionObject) const;
10431053
GRAPHQLSERVICE_EXPORT AwaitableDeliver deliver(await_async launch, const SubscriptionName& name,
1044-
const SubscriptionFilterCallback& applyArguments,
1054+
const SubscriptionArgumentFilterCallback& applyArguments,
10451055
std::shared_ptr<Object> subscriptionObject) const;
10461056
GRAPHQLSERVICE_EXPORT AwaitableDeliver deliver(await_async launch, const SubscriptionName& name,
1047-
const SubscriptionFilterCallback& applyArguments,
1048-
const SubscriptionFilterCallback& applyDirectives,
1057+
const SubscriptionArgumentFilterCallback& applyArguments,
1058+
const SubscriptionDirectiveFilterCallback& applyDirectives,
10491059
std::shared_ptr<Object> subscriptionObject) const;
10501060

10511061
private:

samples/today/TodayMock.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
#include "TodayMock.h"
55

66
#include "AppointmentConnectionObject.h"
7-
#include "TaskConnectionObject.h"
7+
#include "CompleteTaskPayloadObject.h"
8+
#include "ExpensiveObject.h"
89
#include "FolderConnectionObject.h"
9-
#include "UnionTypeObject.h"
1010
#include "NestedTypeObject.h"
11-
#include "ExpensiveObject.h"
12-
#include "CompleteTaskPayloadObject.h"
11+
#include "TaskConnectionObject.h"
12+
#include "UnionTypeObject.h"
1313

1414
#include <algorithm>
1515
#include <chrono>
@@ -501,10 +501,16 @@ std::stack<CapturedParams> NestedType::_capturedParams;
501501
NestedType::NestedType(service::FieldParams&& params, int depth)
502502
: depth(depth)
503503
{
504-
_capturedParams.push({ response::Value(params.operationDirectives),
505-
response::Value(params.fragmentDefinitionDirectives),
506-
response::Value(params.fragmentSpreadDirectives),
507-
response::Value(params.inlineFragmentDirectives),
504+
_capturedParams.push({ { params.operationDirectives },
505+
params.fragmentDefinitionDirectives->empty()
506+
? service::Directives {}
507+
: service::Directives { params.fragmentDefinitionDirectives->front().get() },
508+
params.fragmentSpreadDirectives->empty()
509+
? service::Directives {}
510+
: service::Directives { params.fragmentSpreadDirectives->front() },
511+
params.inlineFragmentDirectives->empty()
512+
? service::Directives {}
513+
: service::Directives { params.inlineFragmentDirectives->front() },
508514
std::move(params.fieldDirectives) });
509515
}
510516

samples/today/TodayMock.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -571,13 +571,13 @@ class NodeChange
571571
struct CapturedParams
572572
{
573573
// Copied in the constructor
574-
const response::Value operationDirectives;
575-
const response::Value fragmentDefinitionDirectives;
576-
const response::Value fragmentSpreadDirectives;
577-
const response::Value inlineFragmentDirectives;
574+
const service::Directives operationDirectives;
575+
const service::Directives fragmentDefinitionDirectives;
576+
const service::Directives fragmentSpreadDirectives;
577+
const service::Directives inlineFragmentDirectives;
578578

579579
// Moved in the constructor
580-
const response::Value fieldDirectives;
580+
const service::Directives fieldDirectives;
581581
};
582582

583583
class NestedType

samples/today/nointrospection/TodaySchema.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -262,11 +262,6 @@ void AddTypesToSchema(const std::shared_ptr<schema::Schema>& schema)
262262
schema->AddDirective(schema::Directive::Make(R"gql(id)gql"sv, R"md()md"sv, {
263263
introspection::DirectiveLocation::FIELD_DEFINITION
264264
}, {}, false));
265-
schema->AddDirective(schema::Directive::Make(R"gql(subscriptionTag)gql"sv, R"md()md"sv, {
266-
introspection::DirectiveLocation::SUBSCRIPTION
267-
}, {
268-
schema::InputValue::Make(R"gql(field)gql"sv, R"md()md"sv, schema->LookupType(R"gql(String)gql"sv), R"gql()gql"sv)
269-
}, true));
270265
schema->AddDirective(schema::Directive::Make(R"gql(queryTag)gql"sv, R"md()md"sv, {
271266
introspection::DirectiveLocation::QUERY
272267
}, {
@@ -292,6 +287,9 @@ void AddTypesToSchema(const std::shared_ptr<schema::Schema>& schema)
292287
}, {
293288
schema::InputValue::Make(R"gql(inlineFragment)gql"sv, R"md()md"sv, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(String)gql"sv)), R"gql()gql"sv)
294289
}, false));
290+
schema->AddDirective(schema::Directive::Make(R"gql(repeatableOnField)gql"sv, R"md()md"sv, {
291+
introspection::DirectiveLocation::FIELD
292+
}, {}, true));
295293

296294
schema->AddQueryType(typeQuery);
297295
schema->AddMutationType(typeMutation);

samples/today/schema.today.graphql

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,6 @@ type Subscription {
111111
nodeChange(id: ID!): Node!
112112
}
113113

114-
directive @subscriptionTag(field: String) repeatable on SUBSCRIPTION
115-
116114
scalar DateTime @specifiedBy(url: "https://en.wikipedia.org/wiki/ISO_8601")
117115

118116
enum TaskState {
@@ -151,6 +149,7 @@ directive @fieldTag(field: String!) on FIELD
151149
directive @fragmentDefinitionTag(fragmentDefinition: String!) on FRAGMENT_DEFINITION
152150
directive @fragmentSpreadTag(fragmentSpread: String!) on FRAGMENT_SPREAD
153151
directive @inlineFragmentTag(inlineFragment: String!) on INLINE_FRAGMENT
152+
directive @repeatableOnField repeatable on FIELD
154153

155154
"Infinitely nestable type which can be used with nested fragments to test directive handling"
156155
type NestedType {

samples/today/schema/TodaySchema.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -265,11 +265,6 @@ void AddTypesToSchema(const std::shared_ptr<schema::Schema>& schema)
265265
schema->AddDirective(schema::Directive::Make(R"gql(id)gql"sv, R"md()md"sv, {
266266
introspection::DirectiveLocation::FIELD_DEFINITION
267267
}, {}, false));
268-
schema->AddDirective(schema::Directive::Make(R"gql(subscriptionTag)gql"sv, R"md()md"sv, {
269-
introspection::DirectiveLocation::SUBSCRIPTION
270-
}, {
271-
schema::InputValue::Make(R"gql(field)gql"sv, R"md()md"sv, schema->LookupType(R"gql(String)gql"sv), R"gql()gql"sv)
272-
}, true));
273268
schema->AddDirective(schema::Directive::Make(R"gql(queryTag)gql"sv, R"md()md"sv, {
274269
introspection::DirectiveLocation::QUERY
275270
}, {
@@ -295,6 +290,9 @@ void AddTypesToSchema(const std::shared_ptr<schema::Schema>& schema)
295290
}, {
296291
schema::InputValue::Make(R"gql(inlineFragment)gql"sv, R"md()md"sv, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(String)gql"sv)), R"gql()gql"sv)
297292
}, false));
293+
schema->AddDirective(schema::Directive::Make(R"gql(repeatableOnField)gql"sv, R"md()md"sv, {
294+
introspection::DirectiveLocation::FIELD
295+
}, {}, true));
298296

299297
schema->AddQueryType(typeQuery);
300298
schema->AddMutationType(typeMutation);

0 commit comments

Comments
 (0)