Skip to content

Commit 673a649

Browse files
committed
Validate implemented interfaces
1 parent 970375f commit 673a649

File tree

2 files changed

+180
-24
lines changed

2 files changed

+180
-24
lines changed

include/SchemaLoader.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,11 @@ class SchemaLoader
293293
const std::optional<std::string_view>& accessor);
294294
void fixupInputFieldList(InputFieldList& fields);
295295
void reorderInputTypeDependencies();
296+
void validateImplementedInterfaces() const;
297+
void validateInterfaceFields(std::string_view typeName, const OutputFieldList& typeFields,
298+
std::string_view interfaceName) const;
299+
void validateTransitiveInterfaces(
300+
std::string_view typeName, const std::vector<std::string_view>& interfaces) const;
296301

297302
static const std::string_view s_introspectionNamespace;
298303
static const BuiltinTypeMap s_builtinTypes;

src/SchemaLoader.cpp

Lines changed: 175 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <fstream>
88
#include <iostream>
99
#include <regex>
10+
#include <set>
1011
#include <sstream>
1112
#include <stdexcept>
1213

@@ -86,6 +87,9 @@ void SchemaLoader::validateSchema()
8687
// Handle nested input types by fully declaring the dependencies first.
8788
reorderInputTypeDependencies();
8889

90+
// Validate the interface dependencies and that all of the interface fields are implemented.
91+
validateImplementedInterfaces();
92+
8993
for (auto& entry : _interfaceTypes)
9094
{
9195
fixupOutputFieldList(entry.fields, std::nullopt, std::nullopt);
@@ -186,30 +190,6 @@ void SchemaLoader::validateSchema()
186190
fixupOutputFieldList(entry.fields, interfaceFields, accessor);
187191
}
188192

189-
// Validate the interfaces implemented by the object types.
190-
for (const auto& entry : _objectTypes)
191-
{
192-
for (const auto& interfaceName : entry.interfaces)
193-
{
194-
if (_interfaceNames.find(interfaceName) == _interfaceNames.cend())
195-
{
196-
std::ostringstream error;
197-
auto itrPosition = _typePositions.find(entry.type);
198-
199-
error << "Unknown interface: " << interfaceName
200-
<< " implemented by: " << entry.type;
201-
202-
if (itrPosition != _typePositions.cend())
203-
{
204-
error << " line: " << itrPosition->second.line
205-
<< " column: " << itrPosition->second.column;
206-
}
207-
208-
throw std::runtime_error(error.str());
209-
}
210-
}
211-
}
212-
213193
// Validate the objects that are possible types for unions and add the unions to
214194
// the list of matching types for the objects.
215195
for (const auto& entry : _unionTypes)
@@ -433,6 +413,29 @@ void SchemaLoader::reorderInputTypeDependencies()
433413
}
434414
}
435415

416+
void SchemaLoader::validateImplementedInterfaces() const
417+
{
418+
for (const auto& interfaceType : _interfaceTypes)
419+
{
420+
validateTransitiveInterfaces(interfaceType.type, interfaceType.interfaces);
421+
422+
for (auto interfaceName : interfaceType.interfaces)
423+
{
424+
validateInterfaceFields(interfaceType.type, interfaceType.fields, interfaceName);
425+
}
426+
}
427+
428+
for (const auto& objectType : _objectTypes)
429+
{
430+
validateTransitiveInterfaces(objectType.type, objectType.interfaces);
431+
432+
for (auto interfaceName : objectType.interfaces)
433+
{
434+
validateInterfaceFields(objectType.type, objectType.fields, interfaceName);
435+
}
436+
}
437+
}
438+
436439
void SchemaLoader::visitDefinition(const peg::ast_node& definition)
437440
{
438441
if (definition.is_type<peg::schema_definition>())
@@ -1148,6 +1151,154 @@ void SchemaLoader::blockReservedName(
11481151
}
11491152
}
11501153

1154+
void SchemaLoader::validateInterfaceFields(std::string_view typeName,
1155+
const OutputFieldList& typeFields, std::string_view interfaceName) const
1156+
{
1157+
const auto itrType = _interfaceNames.find(interfaceName);
1158+
1159+
if (itrType == _interfaceNames.cend())
1160+
{
1161+
std::ostringstream error;
1162+
const auto itrPosition = _typePositions.find(typeName);
1163+
1164+
error << "Unknown interface: " << interfaceName << " implemented by: " << typeName;
1165+
1166+
if (itrPosition != _typePositions.cend())
1167+
{
1168+
error << " line: " << itrPosition->second.line
1169+
<< " column: " << itrPosition->second.column;
1170+
}
1171+
1172+
throw std::runtime_error(error.str());
1173+
}
1174+
1175+
const auto& interfaceType = _interfaceTypes[itrType->second];
1176+
std::set<std::string_view> unimplemented;
1177+
1178+
for (const auto& entry : interfaceType.fields)
1179+
{
1180+
unimplemented.insert(entry.name);
1181+
}
1182+
1183+
for (const auto& entry : typeFields)
1184+
{
1185+
unimplemented.erase(entry.name);
1186+
}
1187+
1188+
if (!unimplemented.empty())
1189+
{
1190+
std::ostringstream error;
1191+
const auto itrPosition = _typePositions.find(typeName);
1192+
1193+
error << "Missing interface fields type: " << typeName
1194+
<< " interface: " << interfaceType.type;
1195+
1196+
if (itrPosition != _typePositions.cend())
1197+
{
1198+
error << " line: " << itrPosition->second.line
1199+
<< " column: " << itrPosition->second.column;
1200+
}
1201+
1202+
for (auto fieldName : unimplemented)
1203+
{
1204+
error << " field: " << fieldName;
1205+
}
1206+
1207+
throw std::runtime_error(error.str());
1208+
}
1209+
}
1210+
1211+
void SchemaLoader::validateTransitiveInterfaces(
1212+
std::string_view typeName, const std::vector<std::string_view>& interfaces) const
1213+
{
1214+
std::set<std::string_view> unimplemented;
1215+
1216+
for (auto entry : interfaces)
1217+
{
1218+
const auto itrType = _interfaceNames.find(entry);
1219+
1220+
if (itrType == _interfaceNames.cend())
1221+
{
1222+
std::ostringstream error;
1223+
const auto itrPosition = _typePositions.find(typeName);
1224+
1225+
error << "Unknown interface: " << entry << " implemented by: " << typeName;
1226+
1227+
if (itrPosition != _typePositions.cend())
1228+
{
1229+
error << " line: " << itrPosition->second.line
1230+
<< " column: " << itrPosition->second.column;
1231+
}
1232+
1233+
throw std::runtime_error(error.str());
1234+
}
1235+
1236+
if (typeName == entry || !unimplemented.insert(entry).second)
1237+
{
1238+
std::ostringstream error;
1239+
const auto itrPosition = _typePositions.find(typeName);
1240+
1241+
error << "Interface cycle interface: " << entry << " implemented by: " << typeName;
1242+
1243+
if (itrPosition != _typePositions.cend())
1244+
{
1245+
error << " line: " << itrPosition->second.line
1246+
<< " column: " << itrPosition->second.column;
1247+
}
1248+
1249+
throw std::runtime_error(error.str());
1250+
}
1251+
1252+
const auto& interfaceType = _interfaceTypes[itrType->second];
1253+
1254+
for (auto interfaceName : interfaceType.interfaces)
1255+
{
1256+
if (!unimplemented.insert(interfaceName).second)
1257+
{
1258+
std::ostringstream error;
1259+
const auto itrPosition = _typePositions.find(typeName);
1260+
1261+
error << "Interface cycle interface: " << interfaceName
1262+
<< " implemented by: " << typeName;
1263+
1264+
if (itrPosition != _typePositions.cend())
1265+
{
1266+
error << " line: " << itrPosition->second.line
1267+
<< " column: " << itrPosition->second.column;
1268+
}
1269+
1270+
throw std::runtime_error(error.str());
1271+
}
1272+
}
1273+
}
1274+
1275+
for (auto entry : interfaces)
1276+
{
1277+
unimplemented.erase(entry);
1278+
}
1279+
1280+
if (!unimplemented.empty())
1281+
{
1282+
std::ostringstream error;
1283+
const auto itrPosition = _typePositions.find(typeName);
1284+
1285+
error << "Missing transitive interface type: " << typeName;
1286+
1287+
if (itrPosition != _typePositions.cend())
1288+
{
1289+
error << " line: " << itrPosition->second.line
1290+
<< " column: " << itrPosition->second.column;
1291+
}
1292+
1293+
for (auto interfaceName : unimplemented)
1294+
{
1295+
error << " interface: " << interfaceName;
1296+
}
1297+
1298+
throw std::runtime_error(error.str());
1299+
}
1300+
}
1301+
11511302
OutputFieldList SchemaLoader::getOutputFields(const peg::ast_node::children_t& fields)
11521303
{
11531304
OutputFieldList outputFields;

0 commit comments

Comments
 (0)