@@ -1816,10 +1816,6 @@ class ValidateExecutableVisitor
18161816ValidateExecutableVisitor::ValidateExecutableVisitor (const Request& service)
18171817 : _service(service)
18181818{
1819- // This is taking advantage of the fact that during validation we can choose to execute
1820- // unvalidated queries. Normally you can't have fragment cycles, so tools like GraphiQL work
1821- // around that limitation by nesting the ofType selection set many layers deep to eventually
1822- // read the underlying type when it's wrapped in List or NotNull types.
18231819 auto data = executeQuery (R"gql( query {
18241820 __schema {
18251821 queryType {
@@ -1993,12 +1989,18 @@ ValidateExecutableVisitor::ValidateExecutableVisitor(const Request& service)
19931989
19941990response::Value ValidateExecutableVisitor::executeQuery (std::string_view query) const
19951991{
1992+ auto ast = peg::parseString (query);
1993+
1994+ // This is taking advantage of the fact that during validation we can choose to execute
1995+ // unvalidated queries against the Introspection schema. This way we can use fragment
1996+ // cycles to expand an arbitrary number of wrapper types.
1997+ ast.validated = true ;
1998+
19961999 response::Value data (response::Type::Map);
19972000 std::shared_ptr<RequestState> state;
1998- auto ast = peg::parseString (query);
19992001 const std::string operationName;
20002002 response::Value variables (response::Type::Map);
2001- auto result = _service.resolve (state, * ast. root , operationName, std::move (variables)).get ();
2003+ auto result = _service.resolve (state, ast, operationName, std::move (variables)).get ();
20022004 auto members = result.release <response::MapType>();
20032005 auto itrResponse = std::find_if (members.begin (), members.end (),
20042006 [](const std::pair<std::string, response::Value>& entry) noexcept
@@ -2370,10 +2372,6 @@ ValidateExecutableVisitor::TypeFields::const_iterator ValidateExecutableVisitor:
23702372 {
23712373 std::ostringstream oss;
23722374
2373- // This is taking advantage of the fact that during validation we can choose to execute
2374- // unvalidated queries. Normally you can't have fragment cycles, so tools like GraphiQL work
2375- // around that limitation by nesting the ofType selection set many layers deep to eventually
2376- // read the underlying type when it's wrapped in List or NotNull types.
23772375 oss << R"gql( query {
23782376 __type(name: ")gql" << _scopedType << R"gql( ") {
23792377 fields(includeDeprecated: true) {
@@ -3560,13 +3558,21 @@ Request::Request(TypeMap&& operationTypes)
35603558{
35613559}
35623560
3563- std::vector<schema_error> Request::validate (const peg::ast_node& root ) const
3561+ std::vector<schema_error> Request::validate (peg::ast& query ) const
35643562{
3565- ValidateExecutableVisitor visitor (*this );
3563+ std::vector<schema_error> errors;
3564+
3565+ if (!query.validated )
3566+ {
3567+ ValidateExecutableVisitor visitor (*this );
3568+
3569+ visitor.visit (*query.root );
35663570
3567- visitor.visit (root);
3571+ errors = visitor.getStructuredErrors ();
3572+ query.validated = errors.empty ();
3573+ }
35683574
3569- return visitor. getStructuredErrors () ;
3575+ return errors ;
35703576}
35713577
35723578std::pair<std::string, const peg::ast_node*> Request::findOperationDefinition (const peg::ast_node& root, const std::string& operationName) const
@@ -3667,10 +3673,40 @@ std::pair<std::string, const peg::ast_node*> Request::findOperationDefinition(co
36673673
36683674std::future<response::Value> Request::resolve (const std::shared_ptr<RequestState>& state, const peg::ast_node& root, const std::string& operationName, response::Value&& variables) const
36693675{
3670- return resolve (std::launch::deferred, state, root, operationName, std::move (variables));
3676+ return resolveValidated (std::launch::deferred, state, root, operationName, std::move (variables));
36713677}
36723678
36733679std::future<response::Value> Request::resolve (std::launch launch, const std::shared_ptr<RequestState>& state, const peg::ast_node& root, const std::string& operationName, response::Value&& variables) const
3680+ {
3681+ return resolveValidated (launch, state, root, operationName, std::move (variables));
3682+ }
3683+
3684+ std::future<response::Value> Request::resolve (const std::shared_ptr<RequestState>& state, peg::ast& query, const std::string& operationName, response::Value&& variables) const
3685+ {
3686+ return resolve (std::launch::deferred, state, query, operationName, std::move (variables));
3687+ }
3688+
3689+ std::future<response::Value> Request::resolve (std::launch launch, const std::shared_ptr<RequestState>& state, peg::ast& query, const std::string& operationName, response::Value&& variables) const
3690+ {
3691+ auto errors = validate (query);
3692+
3693+ if (!errors.empty ())
3694+ {
3695+ schema_exception ex { std::move (errors) };
3696+ std::promise<response::Value> promise;
3697+ response::Value document (response::Type::Map);
3698+
3699+ document.emplace_back (std::string { strData }, response::Value ());
3700+ document.emplace_back (std::string { strErrors }, ex.getErrors ());
3701+ promise.set_value (std::move (document));
3702+
3703+ return promise.get_future ();
3704+ }
3705+
3706+ return resolveValidated (launch, state, *query.root , operationName, std::move (variables));
3707+ }
3708+
3709+ std::future<response::Value> Request::resolveValidated (std::launch launch, const std::shared_ptr<RequestState>& state, const peg::ast_node& root, const std::string& operationName, response::Value&& variables) const
36743710{
36753711 try
36763712 {
@@ -3756,6 +3792,13 @@ std::future<response::Value> Request::resolve(std::launch launch, const std::sha
37563792
37573793SubscriptionKey Request::subscribe (SubscriptionParams&& params, SubscriptionCallback&& callback)
37583794{
3795+ auto errors = validate (params.query );
3796+
3797+ if (!errors.empty ())
3798+ {
3799+ throw schema_exception { std::move (errors) };
3800+ }
3801+
37593802 FragmentDefinitionVisitor fragmentVisitor (params.variables );
37603803
37613804 peg::for_each_child<peg::fragment_definition>(*params.query .root ,
0 commit comments