Skip to content

Commit a1968cb

Browse files
authored
Merge pull request #113 from wravery/master
Make fragment type conditions recognize union types
2 parents f43fbf4 + b254146 commit a1968cb

File tree

9 files changed

+121
-2
lines changed

9 files changed

+121
-2
lines changed

cmake/version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.2.3
1+
3.2.4

include/SchemaGenerator.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ struct ObjectType
200200
std::string type;
201201
std::string cppType;
202202
std::vector<std::string> interfaces;
203+
std::vector<std::string> unions;
203204
OutputFieldList fields;
204205
std::string description;
205206
};

samples/separate/AppointmentObject.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ namespace object {
1717
Appointment::Appointment()
1818
: service::Object({
1919
"Node",
20+
"UnionType",
2021
"Appointment"
2122
}, {
2223
{ "id", [this](service::ResolverParams&& params) { return resolveId(std::move(params)); } },

samples/separate/FolderObject.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ namespace object {
1717
Folder::Folder()
1818
: service::Object({
1919
"Node",
20+
"UnionType",
2021
"Folder"
2122
}, {
2223
{ "id", [this](service::ResolverParams&& params) { return resolveId(std::move(params)); } },

samples/separate/TaskObject.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ namespace object {
1717
Task::Task()
1818
: service::Object({
1919
"Node",
20+
"UnionType",
2021
"Task"
2122
}, {
2223
{ "id", [this](service::ResolverParams&& params) { return resolveId(std::move(params)); } },

samples/unified/TodaySchema.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,7 @@ std::future<response::Value> Subscription::resolve_typename(service::ResolverPar
736736
Appointment::Appointment()
737737
: service::Object({
738738
"Node",
739+
"UnionType",
739740
"Appointment"
740741
}, {
741742
{ "id", [this](service::ResolverParams&& params) { return resolveId(std::move(params)); } },
@@ -811,6 +812,7 @@ std::future<response::Value> Appointment::resolve_typename(service::ResolverPara
811812
Task::Task()
812813
: service::Object({
813814
"Node",
815+
"UnionType",
814816
"Task"
815817
}, {
816818
{ "id", [this](service::ResolverParams&& params) { return resolveId(std::move(params)); } },
@@ -871,6 +873,7 @@ std::future<response::Value> Task::resolve_typename(service::ResolverParams&& pa
871873
Folder::Folder()
872874
: service::Object({
873875
"Node",
876+
"UnionType",
874877
"Folder"
875878
}, {
876879
{ "id", [this](service::ResolverParams&& params) { return resolveId(std::move(params)); } },

samples/validation/ValidationSchema.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ std::future<response::Value> Query::resolve_type(service::ResolverParams&& param
247247
Dog::Dog()
248248
: service::Object({
249249
"Pet",
250+
"CatOrDog",
251+
"DogOrHuman",
250252
"Dog"
251253
}, {
252254
{ "name", [this](service::ResolverParams&& params) { return resolveName(std::move(params)); } },
@@ -354,6 +356,7 @@ std::future<response::Value> Dog::resolve_typename(service::ResolverParams&& par
354356
Alien::Alien()
355357
: service::Object({
356358
"Sentient",
359+
"HumanOrAlien",
357360
"Alien"
358361
}, {
359362
{ "name", [this](service::ResolverParams&& params) { return resolveName(std::move(params)); } },
@@ -399,6 +402,8 @@ std::future<response::Value> Alien::resolve_typename(service::ResolverParams&& p
399402
Human::Human()
400403
: service::Object({
401404
"Sentient",
405+
"DogOrHuman",
406+
"HumanOrAlien",
402407
"Human"
403408
}, {
404409
{ "name", [this](service::ResolverParams&& params) { return resolveName(std::move(params)); } },
@@ -444,6 +449,7 @@ std::future<response::Value> Human::resolve_typename(service::ResolverParams&& p
444449
Cat::Cat()
445450
: service::Object({
446451
"Pet",
452+
"CatOrDog",
447453
"Cat"
448454
}, {
449455
{ "name", [this](service::ResolverParams&& params) { return resolveName(std::move(params)); } },

src/SchemaGenerator.cpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,35 @@ void Generator::validateSchema()
520520
}
521521
}
522522
}
523+
524+
// Validate the objects that are possible types for unions and add the unions to
525+
// the list of matching types for the objects.
526+
for (const auto& entry : _unionTypes)
527+
{
528+
for (const auto& objectName : entry.options)
529+
{
530+
auto itr = _objectNames.find(objectName);
531+
532+
if (itr == _objectNames.cend())
533+
{
534+
std::ostringstream error;
535+
auto itrPosition = _typePositions.find(entry.type);
536+
537+
error << "Unknown type: " << objectName
538+
<< " included by: " << entry.type;
539+
540+
if (itrPosition != _typePositions.cend())
541+
{
542+
error << " line: " << itrPosition->second.line
543+
<< " column: " << itrPosition->second.column;
544+
}
545+
546+
throw std::runtime_error(error.str());
547+
}
548+
549+
_objectTypes[itr->second].unions.push_back(entry.type);
550+
}
551+
}
523552
}
524553

525554
void Generator::fixupOutputFieldList(OutputFieldList& fields, const std::optional<std::unordered_set<std::string>>& interfaceFields, const std::optional<std::string_view>& accessor)
@@ -776,7 +805,8 @@ void Generator::visitObjectTypeDefinition(const peg::ast_node& objectTypeDefinit
776805

777806
auto cppName = getSafeCppName(name);
778807

779-
_objectTypes.push_back({ std::move(name), std::move(cppName), {}, {}, std::move(description) });
808+
_objectTypes.push_back(
809+
{ std::move(name), std::move(cppName), {}, {}, {}, std::move(description) });
780810

781811
visitObjectTypeExtension(objectTypeDefinition);
782812
}
@@ -2750,6 +2780,12 @@ void Generator::outputObjectImplementation(std::ostream& sourceFile, const Objec
27502780
)cpp";
27512781
}
27522782

2783+
for (const auto& unionName : objectType.unions)
2784+
{
2785+
sourceFile << R"cpp( ")cpp" << unionName << R"cpp(",
2786+
)cpp";
2787+
}
2788+
27532789
sourceFile << R"cpp( ")cpp" << objectType.type << R"cpp("
27542790
}, {
27552791
)cpp";

test/TodayTests.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,3 +1343,73 @@ TEST_F(TodayServiceCase, BlockingAsyncExpensive)
13431343
FAIL() << response::toJSON(ex.getErrors());
13441344
}
13451345
}
1346+
1347+
TEST_F(TodayServiceCase, QueryAppointmentsThroughUnionTypeFragment)
1348+
{
1349+
auto query = R"({
1350+
appointments {
1351+
edges {
1352+
node {
1353+
...AppointmentUnionFragment
1354+
}
1355+
}
1356+
}
1357+
}
1358+
1359+
fragment AppointmentUnionFragment on UnionType {
1360+
...on Appointment {
1361+
appointmentId: id
1362+
subject
1363+
when
1364+
isNow
1365+
}
1366+
})"_graphql;
1367+
response::Value variables(response::Type::Map);
1368+
auto state = std::make_shared<today::RequestState>(20);
1369+
auto result = _service->resolve(state, query, "", std::move(variables)).get();
1370+
EXPECT_EQ(size_t(1), _getAppointmentsCount)
1371+
<< "today service lazy loads the appointments and caches the result";
1372+
EXPECT_GE(size_t(1), _getTasksCount)
1373+
<< "today service lazy loads the tasks and caches the result";
1374+
EXPECT_GE(size_t(1), _getUnreadCountsCount)
1375+
<< "today service lazy loads the unreadCounts and caches the result";
1376+
EXPECT_EQ(size_t(20), state->appointmentsRequestId)
1377+
<< "today service passed the same RequestState";
1378+
EXPECT_EQ(size_t(0), state->tasksRequestId) << "today service did not call the loader";
1379+
EXPECT_EQ(size_t(0), state->unreadCountsRequestId) << "today service did not call the loader";
1380+
EXPECT_EQ(size_t(1), state->loadAppointmentsCount) << "today service called the loader once";
1381+
EXPECT_EQ(size_t(0), state->loadTasksCount) << "today service did not call the loader";
1382+
EXPECT_EQ(size_t(0), state->loadUnreadCountsCount) << "today service did not call the loader";
1383+
1384+
try
1385+
{
1386+
ASSERT_TRUE(result.type() == response::Type::Map);
1387+
auto errorsItr = result.find("errors");
1388+
if (errorsItr != result.get<response::MapType>().cend())
1389+
{
1390+
FAIL() << response::toJSON(response::Value(errorsItr->second));
1391+
}
1392+
const auto data = service::ScalarArgument::require("data", result);
1393+
1394+
const auto appointments = service::ScalarArgument::require("appointments", data);
1395+
const auto appointmentEdges =
1396+
service::ScalarArgument::require<service::TypeModifier::List>("edges", appointments);
1397+
ASSERT_EQ(1, appointmentEdges.size()) << "appointments should have 1 entry";
1398+
ASSERT_TRUE(appointmentEdges[0].type() == response::Type::Map)
1399+
<< "appointment should be an object";
1400+
const auto appointmentNode = service::ScalarArgument::require("node", appointmentEdges[0]);
1401+
EXPECT_EQ(_fakeAppointmentId,
1402+
service::IdArgument::require("appointmentId", appointmentNode))
1403+
<< "id should match in base64 encoding";
1404+
EXPECT_EQ("Lunch?", service::StringArgument::require("subject", appointmentNode))
1405+
<< "subject should match";
1406+
EXPECT_EQ("tomorrow", service::StringArgument::require("when", appointmentNode))
1407+
<< "when should match";
1408+
EXPECT_FALSE(service::BooleanArgument::require("isNow", appointmentNode))
1409+
<< "isNow should match";
1410+
}
1411+
catch (service::schema_exception& ex)
1412+
{
1413+
FAIL() << response::toJSON(ex.getErrors());
1414+
}
1415+
}

0 commit comments

Comments
 (0)