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