in src/Validation.cpp [1560:1830]
void ValidateExecutableVisitor::visitField(const peg::ast_node& field)
{
peg::on_first_child<peg::directives>(field, [this](const peg::ast_node& child) {
visitDirectives(introspection::DirectiveLocation::FIELD, child);
});
std::string_view name;
peg::on_first_child<peg::field_name>(field, [&name](const peg::ast_node& child) {
name = child.string_view();
});
auto itrType = getScopedTypeFields();
if (itrType == _typeFields.end())
{
// https://spec.graphql.org/October2021/#sec-Leaf-Field-Selections
auto position = field.begin();
std::ostringstream message;
message << "Field on scalar type: " << _scopedType->get().name() << " name: " << name;
_errors.push_back({ message.str(), { position.line, position.column } });
return;
}
ValidateType innerType;
ValidateType wrappedType;
switch (_scopedType->get().kind())
{
case introspection::TypeKind::OBJECT:
case introspection::TypeKind::INTERFACE:
{
// https://spec.graphql.org/October2021/#sec-Field-Selections
innerType = getFieldType(itrType->second, name);
wrappedType = getWrappedFieldType(itrType->second, name);
break;
}
case introspection::TypeKind::UNION:
{
if (name != R"gql(__typename)gql"sv)
{
// https://spec.graphql.org/October2021/#sec-Leaf-Field-Selections
auto position = field.begin();
std::ostringstream message;
message << "Field on union type: " << _scopedType->get().name()
<< " name: " << name;
_errors.push_back({ message.str(), { position.line, position.column } });
return;
}
// https://spec.graphql.org/October2021/#sec-Field-Selections
innerType = getValidateType(_schema->LookupType("String"sv));
wrappedType = getValidateType(
_schema->WrapType(introspection::TypeKind::NON_NULL, getSharedType(innerType)));
break;
}
default:
break;
}
if (!innerType)
{
// https://spec.graphql.org/October2021/#sec-Field-Selections
auto position = field.begin();
std::ostringstream message;
message << "Undefined field type: " << _scopedType->get().name() << " name: " << name;
_errors.push_back({ message.str(), { position.line, position.column } });
return;
}
std::string_view alias;
peg::on_first_child<peg::alias_name>(field, [&alias](const peg::ast_node& child) {
alias = child.string_view();
});
if (alias.empty())
{
alias = name;
}
ValidateFieldArguments validateArguments;
internal::string_view_map<schema_location> argumentLocations;
std::list<std::string_view> argumentNames;
peg::on_first_child<peg::arguments>(field,
[this, name, &validateArguments, &argumentLocations, &argumentNames](
const peg::ast_node& child) {
for (auto& argument : child.children)
{
auto argumentName = argument->children.front()->string_view();
auto position = argument->begin();
if (validateArguments.find(argumentName) != validateArguments.end())
{
// https://spec.graphql.org/October2021/#sec-Argument-Uniqueness
std::ostringstream message;
message << "Conflicting argument type: " << _scopedType->get().name()
<< " field: " << name << " 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);
}
});
ValidateType objectType =
(_scopedType->get().kind() == introspection::TypeKind::OBJECT ? _scopedType
: ValidateType {});
ValidateField validateField(std::move(wrappedType),
std::move(objectType),
name,
std::move(validateArguments));
auto itrValidateField = _selectionFields.find(alias);
if (itrValidateField != _selectionFields.end())
{
if (itrValidateField->second == validateField)
{
// We already validated this field.
return;
}
else
{
// https://spec.graphql.org/October2021/#sec-Field-Selection-Merging
auto position = field.begin();
std::ostringstream message;
message << "Conflicting field type: " << _scopedType->get().name() << " name: " << name;
_errors.push_back({ message.str(), { position.line, position.column } });
}
}
auto itrField = itrType->second.find(name);
if (itrField != itrType->second.end())
{
for (auto argumentName : argumentNames)
{
auto itrArgument = itrField->second.arguments.find(argumentName);
if (itrArgument == itrField->second.arguments.end())
{
// https://spec.graphql.org/October2021/#sec-Argument-Names
std::ostringstream message;
message << "Undefined argument type: " << _scopedType->get().name()
<< " field: " << name << " name: " << argumentName;
_errors.push_back({ message.str(), argumentLocations[argumentName] });
}
}
for (auto& argument : itrField->second.arguments)
{
auto itrArgument = validateField.arguments.find(argument.first);
const bool missing = itrArgument == validateField.arguments.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 type: " << _scopedType->get().name()
<< " field: " << name << " 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 = field.begin();
std::ostringstream message;
message << (missing ? "Missing argument type: "
: "Required non-null argument type: ")
<< _scopedType->get().name() << " field: " << name
<< " name: " << argument.first;
_errors.push_back({ message.str(), { position.line, position.column } });
}
}
}
_selectionFields.emplace(alias, std::move(validateField));
const peg::ast_node* selection = nullptr;
peg::on_first_child<peg::selection_set>(field, [&selection](const peg::ast_node& child) {
selection = &child;
});
size_t subFieldCount = 0;
if (selection != nullptr)
{
auto outerType = std::move(_scopedType);
auto outerFields = std::move(_selectionFields);
auto outerFieldCount = _fieldCount;
auto outerIntrospectionFieldCount = _introspectionFieldCount;
_fieldCount = 0;
_introspectionFieldCount = 0;
_selectionFields.clear();
_scopedType = std::move(innerType);
visitSelection(*selection);
innerType = std::move(_scopedType);
_scopedType = std::move(outerType);
_selectionFields = std::move(outerFields);
subFieldCount = _fieldCount;
_introspectionFieldCount = outerIntrospectionFieldCount;
_fieldCount = outerFieldCount;
}
if (subFieldCount == 0 && !isScalarType(innerType->get().kind()))
{
// https://spec.graphql.org/October2021/#sec-Leaf-Field-Selections
auto position = field.begin();
std::ostringstream message;
message << "Missing fields on non-scalar type: " << innerType->get().name();
_errors.push_back({ message.str(), { position.line, position.column } });
return;
}
++_fieldCount;
constexpr auto c_introspectionFieldPrefix = R"gql(__)gql"sv;
if (name.size() >= c_introspectionFieldPrefix.size()
&& name.substr(0, c_introspectionFieldPrefix.size()) == c_introspectionFieldPrefix)
{
++_introspectionFieldCount;
}
}