void SchemaLoader::validateSchema()

in src/SchemaLoader.cpp [72:233]


void SchemaLoader::validateSchema()
{
	// Verify that none of the custom types conflict with a built-in type.
	for (const auto& entry : _schemaTypes)
	{
		if (s_builtinTypes.find(entry.first) != s_builtinTypes.cend())
		{
			std::ostringstream error;
			auto itrPosition = _typePositions.find(entry.first);

			error << "Builtin type overridden: " << entry.first;

			if (itrPosition != _typePositions.cend())
			{
				error << " line: " << itrPosition->second.line
					  << " column: " << itrPosition->second.column;
			}

			throw std::runtime_error(error.str());
		}
	}

	// Fixup all of the fieldType members.
	for (auto& entry : _inputTypes)
	{
		fixupInputFieldList(entry.fields);
	}

	// Handle nested input types by fully declaring the dependencies first.
	reorderInputTypeDependencies();

	// Validate the interface dependencies and that all of the interface fields are implemented.
	validateImplementedInterfaces();

	for (auto& entry : _interfaceTypes)
	{
		fixupOutputFieldList(entry.fields, std::nullopt, std::nullopt);
	}

	if (!_isIntrospection)
	{
		bool queryDefined = false;

		if (_operationTypes.empty())
		{
			// Fill in the operations with default type names if present.
			constexpr auto strDefaultQuery = "Query"sv;
			constexpr auto strDefaultMutation = "Mutation"sv;
			constexpr auto strDefaultSubscription = "Subscription"sv;

			if (_objectNames.find(strDefaultQuery) != _objectNames.cend())
			{
				_operationTypes.push_back(
					{ strDefaultQuery, getSafeCppName(strDefaultQuery), service::strQuery });
				queryDefined = true;
			}

			if (_objectNames.find(strDefaultMutation) != _objectNames.cend())
			{
				_operationTypes.push_back({ strDefaultMutation,
					getSafeCppName(strDefaultMutation),
					service::strMutation });
			}

			if (_objectNames.find(strDefaultSubscription) != _objectNames.cend())
			{
				_operationTypes.push_back({ strDefaultSubscription,
					getSafeCppName(strDefaultSubscription),
					service::strSubscription });
			}
		}
		else
		{
			// Validate that all of the operation types exist and that query is defined.
			for (const auto& operation : _operationTypes)
			{
				if (_objectNames.find(operation.type) == _objectNames.cend())
				{
					std::ostringstream error;

					error << "Unknown operation type: " << operation.type
						  << " operation: " << operation.operation;

					throw std::runtime_error(error.str());
				}

				queryDefined = queryDefined || (operation.operation == service::strQuery);
			}
		}

		if (!queryDefined)
		{
			throw std::runtime_error("Query operation type undefined");
		}
	}
	else if (!_operationTypes.empty())
	{
		throw std::runtime_error("Introspection should not define any operation types");
	}

	std::string_view mutationType;

	for (const auto& operation : _operationTypes)
	{
		if (operation.operation == service::strMutation)
		{
			mutationType = operation.type;
			break;
		}
	}

	for (auto& entry : _objectTypes)
	{
		auto interfaceFields = std::make_optional<std::unordered_set<std::string_view>>();
		auto accessor = (mutationType == entry.type)
			? std::make_optional<std::string_view>(strApply)
			: std::nullopt;

		for (const auto& interfaceName : entry.interfaces)
		{
			auto itr = _interfaceNames.find(interfaceName);

			if (itr != _interfaceNames.cend())
			{
				for (const auto& field : _interfaceTypes[itr->second].fields)
				{
					interfaceFields->insert(field.name);
				}
			}
		}

		fixupOutputFieldList(entry.fields, interfaceFields, accessor);
	}

	// Validate the objects that are possible types for unions and add the unions to
	// the list of matching types for the objects.
	for (const auto& entry : _unionTypes)
	{
		for (const auto& objectName : entry.options)
		{
			auto itr = _objectNames.find(objectName);

			if (itr == _objectNames.cend())
			{
				std::ostringstream error;
				auto itrPosition = _typePositions.find(entry.type);

				error << "Unknown type: " << objectName << " included by: " << entry.type;

				if (itrPosition != _typePositions.cend())
				{
					error << " line: " << itrPosition->second.line
						  << " column: " << itrPosition->second.column;
				}

				throw std::runtime_error(error.str());
			}

			_objectTypes[itr->second].unions.push_back(entry.type);
		}
	}
}