Skip to content

Commit

Permalink
Update XStationClient interface
Browse files Browse the repository at this point in the history
  • Loading branch information
MPogotsky committed Oct 5, 2024
1 parent f97ed40 commit 5ff16b7
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 52 deletions.
79 changes: 43 additions & 36 deletions test/TestXStationClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,36 +22,6 @@ class XStationClientTest : public testing::Test
return m_context;
}

template <typename Awaitable> auto runAwaitable(Awaitable &&awaitable)
{
std::exception_ptr eptr;
using result_type = typename Awaitable::value_type;
result_type result;

boost::asio::co_spawn(
m_context,
[&]() -> boost::asio::awaitable<void> {
try
{
result = co_await std::forward<Awaitable>(awaitable);
}
catch (...)
{
eptr = std::current_exception();
}
},
boost::asio::detached);

m_context.run();

if (eptr)
{
std::rethrow_exception(eptr);
}

return result;
}

template <typename Awaitable> auto runAwaitableVoid(Awaitable &&awaitable)
{
std::exception_ptr eptr;
Expand Down Expand Up @@ -83,7 +53,10 @@ class XStationClientTest : public testing::Test

TEST_F(XStationClientTest, XStationClient_string_constructor)
{
EXPECT_NO_THROW(auto client = std::make_unique<XStationClient>(getIoContext(), "test", "test", "demo"));
std::shared_ptr<XStationClient> client;
EXPECT_NO_THROW(client = std::make_unique<XStationClient>(getIoContext(), "test", "test", "demo"));
EXPECT_TRUE(client->socket == nullptr);
EXPECT_TRUE(client->stream == nullptr);
}

TEST_F(XStationClientTest, XStationClient_json_constructor)
Expand All @@ -94,10 +67,44 @@ TEST_F(XStationClientTest, XStationClient_json_constructor)
{"accountType", "demo"}
};

EXPECT_NO_THROW(auto client = std::make_unique<XStationClient>(getIoContext(), accountCredentials));
std::shared_ptr<XStationClient> client;
EXPECT_NO_THROW(client = std::make_unique<XStationClient>(getIoContext(), accountCredentials));
EXPECT_TRUE(client->socket == nullptr);
EXPECT_TRUE(client->stream == nullptr);
}

TEST_F(XStationClientTest, XStationClient_setupSocketConnection_exception)
{
const boost::json::object accountCredentials = {
{"accountId", "test"},
{"password", "test"},
{"accountType", "demo"}
};

std::unique_ptr<XStationClient> client;
EXPECT_NO_THROW(client = std::make_unique<XStationClient>(getIoContext(), accountCredentials));
EXPECT_THROW(runAwaitableVoid(client->setupSocketConnection()), exception::LoginFailed);
EXPECT_TRUE(client->socket != nullptr);
EXPECT_TRUE(client->stream == nullptr);
}

TEST_F(XStationClientTest, XStationClient_setupStreamConnection_exception)
{
const boost::json::object accountCredentials = {
{"accountId", "test"},
{"password", "test"},
{"accountType", "demo"}
};

std::unique_ptr<XStationClient> client;
EXPECT_NO_THROW(client = std::make_unique<XStationClient>(getIoContext(), accountCredentials));
EXPECT_THROW(runAwaitableVoid(client->setupStreamConnection()), exception::LoginFailed);
EXPECT_TRUE(client->socket == nullptr);
// Socket is initialized, but not logged in, so no stream session ID is available, that is why no stream is created.
EXPECT_TRUE(client->stream == nullptr);
}

TEST_F(XStationClientTest, XStationClient_getSocket_exception)
TEST_F(XStationClientTest, XStationClient_closeSocketConnection)
{
const boost::json::object accountCredentials = {
{"accountId", "test"},
Expand All @@ -107,10 +114,10 @@ TEST_F(XStationClientTest, XStationClient_getSocket_exception)

std::unique_ptr<XStationClient> client;
EXPECT_NO_THROW(client = std::make_unique<XStationClient>(getIoContext(), accountCredentials));
EXPECT_THROW(runAwaitable(client->getSocket()), exception::LoginFailed);
EXPECT_NO_THROW(runAwaitableVoid(client->closeSocketConnection()));
}

TEST_F(XStationClientTest, XStationClient_getStream_exception)
TEST_F(XStationClientTest, XStationClient_closeStreamConnection)
{
const boost::json::object accountCredentials = {
{"accountId", "test"},
Expand All @@ -120,5 +127,5 @@ TEST_F(XStationClientTest, XStationClient_getStream_exception)

std::unique_ptr<XStationClient> client;
EXPECT_NO_THROW(client = std::make_unique<XStationClient>(getIoContext(), accountCredentials));
EXPECT_THROW(runAwaitable(client->getStream()), exception::LoginFailed);
EXPECT_NO_THROW(runAwaitableVoid(client->closeStreamConnection()));
}
46 changes: 34 additions & 12 deletions xapi/XStationClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,57 @@ namespace xapi

XStationClient::XStationClient(boost::asio::io_context &ioContext, const std::string &accountId,
const std::string &password, const std::string &accountType)
: m_ioContext(ioContext), m_accountId(accountId), m_password(password), m_accountType(accountType),
m_streamSessionId("")
: socket(nullptr), stream(nullptr), m_ioContext(ioContext), m_accountId(accountId), m_password(password),
m_accountType(accountType), m_streamSessionId("")
{
}

XStationClient::XStationClient(boost::asio::io_context &ioContext, const boost::json::object &accountCredentials)
: m_ioContext(ioContext), m_accountId(accountCredentials.at("accountId").as_string()),
m_password(accountCredentials.at("password").as_string()),
m_accountType(accountCredentials.at("accountType").as_string()), m_streamSessionId("")
: XStationClient(ioContext,
std::string(accountCredentials.at("accountId").as_string()),
std::string(accountCredentials.at("password").as_string()),
std::string(accountCredentials.at("accountType").as_string()))
{
}

boost::asio::awaitable<std::shared_ptr<xapi::Socket>> XStationClient::getSocket()
boost::asio::awaitable<void> XStationClient::setupSocketConnection()
{
auto socket = std::make_shared<xapi::Socket>(m_ioContext);
socket = std::make_unique<xapi::Socket>(m_ioContext);
co_await socket->initSession(m_accountType);
m_streamSessionId = co_await socket->login(m_accountId, m_password);
co_return socket;
}

boost::asio::awaitable<std::shared_ptr<xapi::Stream>> XStationClient::getStream() const
boost::asio::awaitable<void> XStationClient::setupStreamConnection()
{
if (m_streamSessionId.empty())
{
throw xapi::exception::LoginFailed("No stream session ID, get Socket to establish a session first");
throw exception::LoginFailed("No stream session ID, get Socket to establish a session first");
}
auto stream = std::make_shared<xapi::Stream>(m_ioContext);
stream = std::make_unique<xapi::Stream>(m_ioContext);
co_await stream->initSession(m_accountType, m_streamSessionId);
co_return stream;
}

boost::asio::awaitable<void> XStationClient::closeSocketConnection()
{
if (socket == nullptr)
{
co_return;
}
auto result = co_await socket->logout();
if (result["status"].as_bool() != true)
{
// If logout fails and server is not closed the connection gracefully, close it from client side.
co_await socket->closeSession();
}
}

boost::asio::awaitable<void> XStationClient::closeStreamConnection()
{
if (stream == nullptr)
{
co_return;
}
co_await stream->closeSession();
}

} // namespace xapi
27 changes: 23 additions & 4 deletions xapi/XStationClient.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,35 @@ class XStationClient
~XStationClient() = default;

/**
* @brief Retrieves a configured Socket object for the client.
* @brief Performes setup of the socket connection.
* @return An awaitable std::shared_ptr<xapi::Socket> object.
*/
boost::asio::awaitable<std::shared_ptr<xapi::Socket>> getSocket();
boost::asio::awaitable<void> setupSocketConnection();

/**
* @brief Retrieves a configured Stream object for the client.
* @brief Performes setup of the stream connection.
* @return An awaitable std::shared_ptr<xapi::Stream> object.
*/
boost::asio::awaitable<std::shared_ptr<xapi::Stream>> getStream() const;
boost::asio::awaitable<void> setupStreamConnection();

/**
* @brief Closes the socket connection.
*
* Tries to logout from the server and close the connection gracefully. If the server responds with negative status, the connection
* is closed from the client side.
*
* @return An awaitable void.
*/
boost::asio::awaitable<void> closeSocketConnection();

/**
* @brief Closes the stream connection.
* @return An awaitable void.
*/
boost::asio::awaitable<void> closeStreamConnection();

std::unique_ptr<xapi::Socket> socket;
std::unique_ptr<xapi::Stream> stream;

private:
boost::asio::io_context &m_ioContext;
Expand Down

0 comments on commit 5ff16b7

Please sign in to comment.