in src/Validation.cpp [1967:2146]
void ValidateExecutableVisitor::visitDirectives(
introspection::DirectiveLocation location, const peg::ast_node& directives)
{
internal::string_view_set uniqueDirectives;
for (const auto& directive : directives.children)
{
std::string_view directiveName;
peg::on_first_child<peg::directive_name>(*directive,
[&directiveName](const peg::ast_node& child) {
directiveName = child.string_view();
});
const auto itrDirective = _directives.find(directiveName);
if (itrDirective == _directives.end())
{
// https://spec.graphql.org/October2021/#sec-Directives-Are-Defined
auto position = directive->begin();
std::ostringstream message;
message << "Undefined directive name: " << directiveName;
_errors.push_back({ message.str(), { position.line, position.column } });
continue;
}
if (!itrDirective->second.isRepeatable && !uniqueDirectives.emplace(directiveName).second)
{
// https://spec.graphql.org/October2021/#sec-Directives-Are-Unique-Per-Location
auto position = directive->begin();
std::ostringstream message;
message << "Conflicting directive name: " << directiveName;
_errors.push_back({ message.str(), { position.line, position.column } });
continue;
}
if (itrDirective->second.locations.find(location) == itrDirective->second.locations.end())
{
// https://spec.graphql.org/October2021/#sec-Directives-Are-In-Valid-Locations
auto position = directive->begin();
std::ostringstream message;
message << "Unexpected location for directive: " << directiveName;
switch (location)
{
case introspection::DirectiveLocation::QUERY:
message << " name: QUERY";
break;
case introspection::DirectiveLocation::MUTATION:
message << " name: MUTATION";
break;
case introspection::DirectiveLocation::SUBSCRIPTION:
message << " name: SUBSCRIPTION";
break;
case introspection::DirectiveLocation::FIELD:
message << " name: FIELD";
break;
case introspection::DirectiveLocation::FRAGMENT_DEFINITION:
message << " name: FRAGMENT_DEFINITION";
break;
case introspection::DirectiveLocation::FRAGMENT_SPREAD:
message << " name: FRAGMENT_SPREAD";
break;
case introspection::DirectiveLocation::INLINE_FRAGMENT:
message << " name: INLINE_FRAGMENT";
break;
default:
break;
}
_errors.push_back({ message.str(), { position.line, position.column } });
continue;
}
peg::on_first_child<peg::arguments>(*directive,
[this, &directive, &directiveName, itrDirective](const peg::ast_node& child) {
ValidateFieldArguments validateArguments;
internal::string_view_map<schema_location> argumentLocations;
std::list<std::string_view> argumentNames;
for (auto& argument : child.children)
{
auto position = argument->begin();
auto argumentName = argument->children.front()->string_view();
if (validateArguments.find(argumentName) != validateArguments.end())
{
// https://spec.graphql.org/October2021/#sec-Argument-Uniqueness
std::ostringstream message;
message << "Conflicting argument directive: " << directiveName
<< " name: " << argumentName;
_errors.push_back({ message.str(), { position.line, position.column } });
continue;
}
ValidateArgumentValueVisitor visitor(_errors);
visitor.visit(*argument->children.back());
validateArguments[argumentName] = visitor.getArgumentValue();
argumentLocations[argumentName] = { position.line, position.column };
argumentNames.push_back(argumentName);
}
for (auto argumentName : argumentNames)
{
auto itrArgument = itrDirective->second.arguments.find(argumentName);
if (itrArgument == itrDirective->second.arguments.end())
{
// https://spec.graphql.org/October2021/#sec-Argument-Names
std::ostringstream message;
message << "Undefined argument directive: " << directiveName
<< " name: " << argumentName;
_errors.push_back({ message.str(), argumentLocations[argumentName] });
}
}
for (auto& argument : itrDirective->second.arguments)
{
auto itrArgument = validateArguments.find(argument.first);
const bool missing = itrArgument == validateArguments.end();
if (!missing && itrArgument->second.value)
{
// The value was not null.
if (!validateInputValue(argument.second.nonNullDefaultValue,
itrArgument->second,
argument.second.type))
{
// https://spec.graphql.org/October2021/#sec-Values-of-Correct-Type
std::ostringstream message;
message << "Incompatible argument directive: " << directiveName
<< " name: " << argument.first;
_errors.push_back({ message.str(), argumentLocations[argument.first] });
}
continue;
}
else if (argument.second.defaultValue)
{
// The argument has a default value.
continue;
}
// See if the argument is wrapped in NON_NULL
if (argument.second.type
&& introspection::TypeKind::NON_NULL == argument.second.type->get().kind())
{
// https://spec.graphql.org/October2021/#sec-Required-Arguments
auto position = directive->begin();
std::ostringstream message;
message << (missing ? "Missing argument directive: "
: "Required non-null argument directive: ")
<< directiveName << " name: " << argument.first;
_errors.push_back({ message.str(), { position.line, position.column } });
}
}
});
}
}