Skip to content

Commit 64ae642

Browse files
committed
Return futures and add tests
1 parent 37c7712 commit 64ae642

File tree

4 files changed

+179
-53
lines changed

4 files changed

+179
-53
lines changed

include/graphqlservice/GraphQLService.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -879,10 +879,10 @@ class Request : public std::enable_shared_from_this<Request>
879879
GRAPHQLSERVICE_EXPORT std::future<response::Value> resolve(std::launch launch, const std::shared_ptr<RequestState>& state, peg::ast& query, const std::string& operationName, response::Value&& variables) const;
880880

881881
GRAPHQLSERVICE_EXPORT SubscriptionKey subscribe(SubscriptionParams&& params, SubscriptionCallback&& callback);
882-
GRAPHQLSERVICE_EXPORT SubscriptionKey subscribe(std::launch launch, SubscriptionParams&& params, SubscriptionCallback&& callback);
882+
GRAPHQLSERVICE_EXPORT std::future<SubscriptionKey> subscribe(std::launch launch, SubscriptionParams&& params, SubscriptionCallback&& callback);
883883

884884
GRAPHQLSERVICE_EXPORT void unsubscribe(SubscriptionKey key);
885-
GRAPHQLSERVICE_EXPORT void unsubscribe(std::launch launch, SubscriptionKey key);
885+
GRAPHQLSERVICE_EXPORT std::future<void> unsubscribe(std::launch launch, SubscriptionKey key);
886886

887887
GRAPHQLSERVICE_EXPORT void deliver(const SubscriptionName& name, const std::shared_ptr<Object>& subscriptionObject) const;
888888
GRAPHQLSERVICE_EXPORT void deliver(const SubscriptionName& name, const SubscriptionArguments& arguments, const std::shared_ptr<Object>& subscriptionObject) const;

samples/today/TodayMock.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,8 +434,50 @@ class NextAppointmentChange : public object::Subscription
434434
{
435435
}
436436

437+
static size_t getCount(service::ResolverContext resolverContext)
438+
{
439+
switch (resolverContext)
440+
{
441+
case service::ResolverContext::NotifySubscribe:
442+
return _notifySubscribeCount;
443+
444+
case service::ResolverContext::Subscription:
445+
return _subscriptionCount;
446+
447+
case service::ResolverContext::NotifyUnsubscribe:
448+
return _notifyUnsubscribeCount;
449+
450+
default:
451+
throw std::runtime_error("Unexpected ResolverContext");
452+
}
453+
}
454+
437455
service::FieldResult<std::shared_ptr<object::Appointment>> getNextAppointmentChange(service::FieldParams&& params) const final
438456
{
457+
switch (params.resolverContext)
458+
{
459+
case service::ResolverContext::NotifySubscribe:
460+
{
461+
++_notifySubscribeCount;
462+
break;
463+
}
464+
465+
case service::ResolverContext::Subscription:
466+
{
467+
++_subscriptionCount;
468+
break;
469+
}
470+
471+
case service::ResolverContext::NotifyUnsubscribe:
472+
{
473+
++_notifyUnsubscribeCount;
474+
break;
475+
}
476+
477+
default:
478+
throw std::runtime_error("Unexpected ResolverContext");
479+
}
480+
439481
return std::static_pointer_cast<object::Appointment>(_changeNextAppointment(params.state));
440482
}
441483

@@ -446,6 +488,10 @@ class NextAppointmentChange : public object::Subscription
446488

447489
private:
448490
nextAppointmentChange _changeNextAppointment;
491+
492+
static size_t _notifySubscribeCount;
493+
static size_t _subscriptionCount;
494+
static size_t _notifyUnsubscribeCount;
449495
};
450496

451497
class NodeChange : public object::Subscription

src/GraphQLService.cpp

Lines changed: 49 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2104,37 +2104,36 @@ SubscriptionKey Request::subscribe(SubscriptionParams&& params, SubscriptionCall
21042104
return key;
21052105
}
21062106

2107-
SubscriptionKey Request::subscribe(std::launch launch, SubscriptionParams&& params, SubscriptionCallback&& callback)
2107+
std::future<SubscriptionKey> Request::subscribe(std::launch launch, SubscriptionParams&& params, SubscriptionCallback&& callback)
21082108
{
2109-
const auto key = subscribe(std::move(params), std::move(callback));
2110-
const auto itrOperation = _operations.find(std::string { strSubscription });
2111-
const auto itrSubscription = _subscriptions.find(key);
2112-
2113-
if (itrOperation != _operations.cend()
2114-
&& itrSubscription != _subscriptions.cend())
2109+
return std::async(launch, [spThis = shared_from_this(), launch](SubscriptionParams&& paramsFuture, SubscriptionCallback&& callbackFuture)
21152110
{
2116-
const auto& operation = itrOperation->second;
2117-
const auto& registration = itrSubscription->second;
2118-
response::Value emptyFragmentDirectives(response::Type::Map);
2119-
const SelectionSetParams selectionSetParams {
2120-
ResolverContext::NotifySubscribe,
2121-
registration->data->state,
2122-
registration->data->directives,
2123-
emptyFragmentDirectives,
2124-
emptyFragmentDirectives,
2125-
emptyFragmentDirectives,
2126-
{},
2127-
launch,
2128-
};
2111+
const auto key = spThis->subscribe(std::move(paramsFuture), std::move(callbackFuture));
2112+
const auto itrOperation = spThis->_operations.find(std::string { strSubscription });
2113+
const auto itrSubscription = spThis->_subscriptions.find(key);
21292114

2130-
std::async(launch,
2131-
[](std::future<response::Value> document)
2115+
if (itrOperation != spThis->_operations.cend()
2116+
&& itrSubscription != spThis->_subscriptions.cend())
21322117
{
2133-
return document.get();
2134-
}, operation->resolve(selectionSetParams, registration->selection, registration->data->fragments, registration->data->variables)).get();
2135-
}
2118+
const auto& operation = itrOperation->second;
2119+
const auto& registration = itrSubscription->second;
2120+
response::Value emptyFragmentDirectives(response::Type::Map);
2121+
const SelectionSetParams selectionSetParams {
2122+
ResolverContext::NotifySubscribe,
2123+
registration->data->state,
2124+
registration->data->directives,
2125+
emptyFragmentDirectives,
2126+
emptyFragmentDirectives,
2127+
emptyFragmentDirectives,
2128+
{},
2129+
launch,
2130+
};
2131+
2132+
operation->resolve(selectionSetParams, registration->selection, registration->data->fragments, registration->data->variables).get();
2133+
}
21362134

2137-
return key;
2135+
return key;
2136+
}, std::move(params), std::move(callback));
21382137
}
21392138

21402139
void Request::unsubscribe(SubscriptionKey key)
@@ -2166,36 +2165,35 @@ void Request::unsubscribe(SubscriptionKey key)
21662165
}
21672166
}
21682167

2169-
void Request::unsubscribe(std::launch launch, SubscriptionKey key)
2168+
std::future<void> Request::unsubscribe(std::launch launch, SubscriptionKey key)
21702169
{
2171-
const auto itrOperation = _operations.find(std::string { strSubscription });
2172-
const auto itrSubscription = _subscriptions.find(key);
2173-
2174-
if (itrOperation != _operations.cend()
2175-
&& itrSubscription != _subscriptions.cend())
2170+
return std::async(launch, [spThis = shared_from_this(), launch, key]()
21762171
{
2177-
const auto& operation = itrOperation->second;
2178-
const auto& registration = itrSubscription->second;
2179-
response::Value emptyFragmentDirectives(response::Type::Map);
2180-
const SelectionSetParams selectionSetParams {
2181-
ResolverContext::NotifyUnsubscribe,
2182-
registration->data->state,
2183-
registration->data->directives,
2184-
emptyFragmentDirectives,
2185-
emptyFragmentDirectives,
2186-
emptyFragmentDirectives,
2187-
{},
2188-
launch,
2189-
};
2172+
const auto itrOperation = spThis->_operations.find(std::string { strSubscription });
2173+
const auto itrSubscription = spThis->_subscriptions.find(key);
21902174

2191-
std::async(launch,
2192-
[](std::future<response::Value> document)
2175+
if (itrOperation != spThis->_operations.cend()
2176+
&& itrSubscription != spThis->_subscriptions.cend())
21932177
{
2194-
return document.get();
2195-
}, operation->resolve(selectionSetParams, registration->selection, registration->data->fragments, registration->data->variables)).get();
2196-
}
2178+
const auto& operation = itrOperation->second;
2179+
const auto& registration = itrSubscription->second;
2180+
response::Value emptyFragmentDirectives(response::Type::Map);
2181+
const SelectionSetParams selectionSetParams {
2182+
ResolverContext::NotifyUnsubscribe,
2183+
registration->data->state,
2184+
registration->data->directives,
2185+
emptyFragmentDirectives,
2186+
emptyFragmentDirectives,
2187+
emptyFragmentDirectives,
2188+
{},
2189+
launch,
2190+
};
2191+
2192+
operation->resolve(selectionSetParams, registration->selection, registration->data->fragments, registration->data->variables).get();
2193+
}
21972194

2198-
unsubscribe(key);
2195+
spThis->unsubscribe(key);
2196+
});
21992197
}
22002198

22012199
void Request::deliver(const SubscriptionName& name, const std::shared_ptr<Object>& subscriptionObject) const

test/TodayTests.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,3 +1413,85 @@ TEST_F(TodayServiceCase, QueryAppointmentsThroughUnionTypeFragment)
14131413
FAIL() << response::toJSON(ex.getErrors());
14141414
}
14151415
}
1416+
1417+
size_t today::NextAppointmentChange::_notifySubscribeCount = 0;
1418+
size_t today::NextAppointmentChange::_subscriptionCount = 0;
1419+
size_t today::NextAppointmentChange::_notifyUnsubscribeCount = 0;
1420+
1421+
TEST_F(TodayServiceCase, SubscribeUnsubscribeNotificationsAsync)
1422+
{
1423+
auto query = peg::parseString(R"(subscription TestSubscription {
1424+
nextAppointment: nextAppointmentChange {
1425+
nextAppointmentId: id
1426+
when
1427+
subject
1428+
isNow
1429+
}
1430+
})");
1431+
response::Value variables(response::Type::Map);
1432+
auto state = std::make_shared<today::RequestState>(21);
1433+
bool calledCallback = false;
1434+
const auto notifySubscribeBegin = today::NextAppointmentChange::getCount(service::ResolverContext::NotifySubscribe);
1435+
const auto subscriptionBegin = today::NextAppointmentChange::getCount(service::ResolverContext::Subscription);
1436+
const auto notifyUnsubscribeBegin = today::NextAppointmentChange::getCount(service::ResolverContext::NotifyUnsubscribe);
1437+
auto key = _service->subscribe(std::launch::async, service::SubscriptionParams { state, std::move(query), "TestSubscription", std::move(std::move(variables)) },
1438+
[&calledCallback](std::future<response::Value> response)
1439+
{
1440+
calledCallback = true;
1441+
});
1442+
_service->unsubscribe(std::launch::async, key.get()).get();
1443+
const auto notifySubscribeEnd = today::NextAppointmentChange::getCount(service::ResolverContext::NotifySubscribe);
1444+
const auto subscriptionEnd = today::NextAppointmentChange::getCount(service::ResolverContext::Subscription);
1445+
const auto notifyUnsubscribeEnd = today::NextAppointmentChange::getCount(service::ResolverContext::NotifyUnsubscribe);
1446+
1447+
try
1448+
{
1449+
EXPECT_FALSE(calledCallback);
1450+
EXPECT_EQ(notifySubscribeBegin + 1, notifySubscribeEnd) << "should pass NotifySubscribe once";
1451+
EXPECT_EQ(subscriptionBegin, subscriptionEnd) << "should not pass Subscription";
1452+
EXPECT_EQ(notifyUnsubscribeBegin + 1, notifyUnsubscribeEnd) << "should pass NotifyUnsubscribe once";
1453+
}
1454+
catch (service::schema_exception & ex)
1455+
{
1456+
FAIL() << response::toJSON(ex.getErrors());
1457+
}
1458+
}
1459+
1460+
TEST_F(TodayServiceCase, SubscribeUnsubscribeNotificationsDeferred)
1461+
{
1462+
auto query = peg::parseString(R"(subscription TestSubscription {
1463+
nextAppointment: nextAppointmentChange {
1464+
nextAppointmentId: id
1465+
when
1466+
subject
1467+
isNow
1468+
}
1469+
})");
1470+
response::Value variables(response::Type::Map);
1471+
auto state = std::make_shared<today::RequestState>(21);
1472+
bool calledCallback = false;
1473+
const auto notifySubscribeBegin = today::NextAppointmentChange::getCount(service::ResolverContext::NotifySubscribe);
1474+
const auto subscriptionBegin = today::NextAppointmentChange::getCount(service::ResolverContext::Subscription);
1475+
const auto notifyUnsubscribeBegin = today::NextAppointmentChange::getCount(service::ResolverContext::NotifyUnsubscribe);
1476+
auto key = _service->subscribe(std::launch::deferred, service::SubscriptionParams { state, std::move(query), "TestSubscription", std::move(std::move(variables)) },
1477+
[&calledCallback](std::future<response::Value> response)
1478+
{
1479+
calledCallback = true;
1480+
});
1481+
_service->unsubscribe(std::launch::deferred, key.get()).get();
1482+
const auto notifySubscribeEnd = today::NextAppointmentChange::getCount(service::ResolverContext::NotifySubscribe);
1483+
const auto subscriptionEnd = today::NextAppointmentChange::getCount(service::ResolverContext::Subscription);
1484+
const auto notifyUnsubscribeEnd = today::NextAppointmentChange::getCount(service::ResolverContext::NotifyUnsubscribe);
1485+
1486+
try
1487+
{
1488+
EXPECT_FALSE(calledCallback);
1489+
EXPECT_EQ(notifySubscribeBegin + 1, notifySubscribeEnd) << "should pass NotifySubscribe once";
1490+
EXPECT_EQ(subscriptionBegin, subscriptionEnd) << "should not pass Subscription";
1491+
EXPECT_EQ(notifyUnsubscribeBegin + 1, notifyUnsubscribeEnd) << "should pass NotifyUnsubscribe once";
1492+
}
1493+
catch (service::schema_exception & ex)
1494+
{
1495+
FAIL() << response::toJSON(ex.getErrors());
1496+
}
1497+
}

0 commit comments

Comments
 (0)