Skip to content

Commit

Permalink
Add XStationClient interface
Browse files Browse the repository at this point in the history
  • Loading branch information
MPogotsky committed Oct 4, 2024
1 parent 2cf9ae2 commit f97ed40
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 0 deletions.
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ set( SOURCES
TestConnection.cpp
TestSocket.cpp
TestStream.cpp
TestXStationClient.cpp
)

add_executable( tests
Expand Down
124 changes: 124 additions & 0 deletions test/TestXStationClient.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#include "xapi/Exceptions.hpp"
#include "xapi/XStationClient.hpp"
#include <gtest/gtest.h>
#include <string>

using namespace xapi;

class XStationClientTest : public testing::Test
{
protected:
void SetUp() override
{
}

void TearDown() override
{
}

public:
boost::asio::io_context &getIoContext()
{
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;
boost::asio::co_spawn(
m_context,
[&]() -> boost::asio::awaitable<void> {
try
{
co_await std::forward<Awaitable>(awaitable);
}
catch (...)
{
eptr = std::current_exception();
}
},
boost::asio::detached);

m_context.run();

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

private:
boost::asio::io_context m_context;
};

TEST_F(XStationClientTest, XStationClient_string_constructor)
{
EXPECT_NO_THROW(auto client = std::make_unique<XStationClient>(getIoContext(), "test", "test", "demo"));
}

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

EXPECT_NO_THROW(auto client = std::make_unique<XStationClient>(getIoContext(), accountCredentials));
}

TEST_F(XStationClientTest, XStationClient_getSocket_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(runAwaitable(client->getSocket()), exception::LoginFailed);
}

TEST_F(XStationClientTest, XStationClient_getStream_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(runAwaitable(client->getStream()), exception::LoginFailed);
}
2 changes: 2 additions & 0 deletions xapi/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ set(XAPI_PUBLIC_H
Connection.hpp
Socket.hpp
Stream.hpp
XStationClient.hpp
Xapi.hpp
)

Expand All @@ -12,6 +13,7 @@ set(XAPI_SOURCES
Connection.cpp
Socket.cpp
Stream.cpp
XStationClient.cpp
)

add_library(Xapi SHARED ${XAPI_SOURCES})
Expand Down
40 changes: 40 additions & 0 deletions xapi/XStationClient.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include "XStationClient.hpp"
#include "Exceptions.hpp"

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("")
{
}

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("")
{
}

boost::asio::awaitable<std::shared_ptr<xapi::Socket>> XStationClient::getSocket()
{
auto socket = std::make_shared<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
{
if (m_streamSessionId.empty())
{
throw xapi::exception::LoginFailed("No stream session ID, get Socket to establish a session first");
}
auto stream = std::make_shared<xapi::Stream>(m_ioContext);
co_await stream->initSession(m_accountType, m_streamSessionId);
co_return stream;
}

} // namespace xapi
84 changes: 84 additions & 0 deletions xapi/XStationClient.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#pragma once

/**
* @file XStationClient.hpp
* @brief Defines the XStationClient class for managing xStation client.
*
* This file contains the definition of the XStationClient class, which provides
* an interface for managing xStation client and retreiving Socket and Stream objects.
*/

#include "Socket.hpp"
#include "Stream.hpp"

namespace xapi
{
/**
* @brief Provides an interface for managing xStation client and retreiving Socket and Stream objects.
*
* The XStationClient class provides a high-level interface for managing xStation client and retreiving xapi::Socket and
* xapi::Stream objects that can be used to interact with xAPI.
*/
class XStationClient
{
public:
XStationClient() = delete;

/**
* @brief Constructs a new XStationClient object.
* @param ioContext The IO context for asynchronous operations.
* @param accountId The account ID to use for the client.
* @param password The password to use for the client.
* @param accountType The type of account to use for the client.
* Possible values are:
*
* - `"demo"` for a demo account.
*
* - `"real"` for a real money account.
*/
XStationClient(boost::asio::io_context &ioContext, const std::string &accountId,
const std::string &password, const std::string &accountType = "demo");

/**
* @brief Constructs a new XStationClient object.
*
* Initializes the client with the provided IO context and account credentials.
*
* @param ioContext The IO context for managing asynchronous operations.
* @param accountCredentials A boost::json::object containing the account credentials required for the client.
* The map should include the following keys:
*
* - `accountId`: The account ID as a string.
*
* - `password`: The account password as a string.
*
* - `accountType`: The type of account. Possible values are: `"demo"` or `"real"`
*
*/
XStationClient(boost::asio::io_context &ioContext, const boost::json::object &accountCredentials);

~XStationClient() = default;

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

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

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

const std::string m_accountId;
const std::string m_password;
const std::string m_accountType;

std::string m_streamSessionId;
};

} // namespace xapi
1 change: 1 addition & 0 deletions xapi/Xapi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
#include "Connection.hpp"
#include "Socket.hpp"
#include "Stream.hpp"
#include "XStationClient.hpp"

0 comments on commit f97ed40

Please sign in to comment.