void SelectionVisitor::visitField()

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() });
	}
}