in src/GraphQLService.cpp [966:1104]
void SelectionVisitor::visitField(const peg::ast_node& field)
{
std::string_view name;
peg::on_first_child<peg::field_name>(field, [&name](const peg::ast_node& child) {
name = child.string_view();
});
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;
}
if (!_names.emplace(alias).second)
{
// Skip resolving fields which map to the same response name as a field we've already
// resolved. Validation should handle merging multiple references to the same field or
// to compatible fields.
return;
}
const auto itrResolver = _resolvers.find(name);
if (itrResolver == _resolvers.end())
{
std::promise<ResolverResult> promise;
auto position = field.begin();
std::ostringstream error;
error << "Unknown field name: " << name;
promise.set_exception(
std::make_exception_ptr(schema_exception { { schema_error { error.str(),
{ position.line, position.column },
buildErrorPath(_path ? std::make_optional(_path->get()) : std::nullopt) } } }));
_values.push_back({ alias, promise.get_future() });
return;
}
DirectiveVisitor directiveVisitor(_variables);
peg::on_first_child<peg::directives>(field, [&directiveVisitor](const peg::ast_node& child) {
directiveVisitor.visit(child);
});
if (directiveVisitor.shouldSkip())
{
return;
}
response::Value arguments(response::Type::Map);
peg::on_first_child<peg::arguments>(field, [this, &arguments](const peg::ast_node& child) {
ValueVisitor visitor(_variables);
for (auto& argument : child.children)
{
visitor.visit(*argument->children.back());
arguments.emplace_back(argument->children.front()->string(), visitor.getValue());
}
});
const peg::ast_node* selection = nullptr;
peg::on_first_child<peg::selection_set>(field, [&selection](const peg::ast_node& child) {
selection = &child;
});
const SelectionSetParams selectionSetParams {
_resolverContext,
_state,
_operationDirectives,
_fragmentDefinitionDirectives,
_fragmentSpreadDirectives,
_inlineFragmentDirectives,
std::make_optional(field_path { _path, path_segment { alias } }),
_launch,
};
try
{
auto result = itrResolver->second(ResolverParams(selectionSetParams,
field,
std::string(alias),
std::move(arguments),
directiveVisitor.getDirectives(),
selection,
_fragments,
_variables));
_values.push_back({ alias, std::move(result) });
}
catch (schema_exception& scx)
{
std::promise<ResolverResult> promise;
auto position = field.begin();
auto messages = scx.getStructuredErrors();
for (auto& message : messages)
{
if (message.location.line == 0)
{
message.location = { position.line, position.column };
}
if (message.path.empty())
{
message.path = buildErrorPath(selectionSetParams.errorPath);
}
}
promise.set_exception(std::make_exception_ptr(schema_exception { std::move(messages) }));
_values.push_back({ alias, promise.get_future() });
}
catch (const std::exception& ex)
{
std::promise<ResolverResult> promise;
auto position = field.begin();
std::ostringstream message;
message << "Field error name: " << alias << " unknown error: " << ex.what();
promise.set_exception(
std::make_exception_ptr(schema_exception { { schema_error { message.str(),
{ position.line, position.column },
buildErrorPath(selectionSetParams.errorPath) } } }));
_values.push_back({ alias, promise.get_future() });
}
}