20#ifndef ORES_QT_CLIENT_MANAGER_HPP
21#define ORES_QT_CLIENT_MANAGER_HPP
31#include <boost/uuid/uuid.hpp>
32#include <boost/uuid/uuid_io.hpp>
33#include <boost/uuid/random_generator.hpp>
34#include <rfl/json.hpp>
35#include <rfl/AddTagsToVariants.hpp>
38#include "ores.utility/rfl/reflectors.hpp"
40#include "ores.nats/service/nats_client.hpp"
41#include "ores.nats/service/nats_connect_error.hpp"
42#include "ores.nats/service/session_expired_error.hpp"
43#include "ores.nats/service/jetstream_admin.hpp"
44#include "ores.nats/service/subscription.hpp"
45#include "ores.eventing/service/event_bus.hpp"
46#include "ores.logging/make_logger.hpp"
47#include "ores.iam.api/domain/session.hpp"
48#include "ores.iam.api/messaging/session_samples_protocol.hpp"
49#include "ores.trading.api/messaging/trade_protocol.hpp"
50#include "ores.qt/export.hpp"
61 { T::nats_subject } -> std::convertible_to<std::string_view>;
62 typename T::response_type;
69 boost::uuids::uuid id;
71 QString party_category;
72 QString business_center_code;
74 bool is_system()
const {
return party_category ==
"System"; }
82 QString error_message;
83 bool password_reset_required =
false;
84 bool bootstrap_mode =
false;
85 bool tenant_bootstrap_mode =
false;
86 bool party_setup_required =
false;
87 boost::uuids::uuid selected_party_id;
88 std::vector<PartyInfo> available_parties;
96 QString error_message;
104 std::vector<iam::domain::session> sessions;
105 std::uint32_t total_count = 0;
112 std::vector<trading::domain::trade> trades;
113 std::uint32_t total_count = 0;
127 inline static std::string_view logger_name =
128 "ores.qt.client_manager";
130 [[nodiscard]]
static auto& lg() {
132 static auto instance = make_logger(logger_name);
138 static constexpr std::chrono::seconds fast_timeout{30};
140 static constexpr std::chrono::seconds slow_timeout{120};
142 explicit ClientManager(std::shared_ptr<eventing::service::event_bus> event_bus,
143 QObject* parent =
nullptr);
162 LoginResult connect(
const std::string& host, std::uint16_t port);
167 LoginResult login(
const std::string& username,
const std::string& password);
173 const std::string& host,
175 const std::string& username,
176 const std::string& password);
182 const std::string& host,
184 const std::string& username,
185 const std::string& password);
191 const std::string& host,
193 const std::string& username,
194 const std::string& email,
195 const std::string& password);
210 bool isConnected()
const;
215 [[deprecated(
"Permission checks are now server-side via RBAC")]]
234 if (!session_.is_logged_in())
return {};
235 return session_.auth().username;
251 std::optional<boost::uuids::uuid>
accountId()
const {
return current_account_id_; }
257 if (!isConnected())
return "";
258 return connected_host_ +
":" + std::to_string(connected_port_);
265 if (!isConnected())
return "";
266 return connected_host_;
273 if (!isConnected())
return 0;
274 return connected_port_;
323 bool selectParty(
const boost::uuids::uuid& party_id,
const QString& party_name);
335 template <nats_request RequestType>
337 -> std::expected<typename RequestType::response_type, std::string> {
338 using ResponseType =
typename RequestType::response_type;
340 const auto json_body = rfl::json::write(request);
341 auto msg = session_.request(RequestType::nats_subject, json_body);
342 const std::string_view data(
343 reinterpret_cast<const char*
>(msg.data.data()),
345 auto result = rfl::json::read<ResponseType, rfl::AddTagsToVariants>(data);
347 return std::unexpected(
348 std::string(
"Failed to deserialize response: ") +
349 result.error().what());
351 return std::move(*result);
354 }
catch (
const std::exception& e) {
355 return std::unexpected(std::string(e.what()));
369 template <nats_request RequestType>
371 std::chrono::milliseconds timeout = std::chrono::seconds(30))
372 -> std::expected<typename RequestType::response_type, std::string> {
373 using ResponseType =
typename RequestType::response_type;
374 if (!session_.is_logged_in()) {
375 return std::unexpected(std::string(
"Not logged in"));
378 const auto cid = boost::uuids::to_string(
379 boost::uuids::random_generator()());
380 const auto json_body = rfl::json::write(request);
381 auto scoped = session_
382 .with_correlation_id(cid)
383 .with_session_id(session_id_);
384 auto msg = scoped.authenticated_request(
385 RequestType::nats_subject, json_body, timeout);
386 const std::string_view data(
387 reinterpret_cast<const char*
>(msg.data.data()),
389 auto result = rfl::json::read<ResponseType, rfl::AddTagsToVariants>(data);
391 return std::unexpected(
392 std::string(
"Failed to deserialize response: ") +
393 result.error().what());
395 return std::move(*result);
400 BOOST_LOG_SEV(lg(), warn) <<
"Session expired: " << e.what();
401 QMetaObject::invokeMethod(
this,
"sessionExpired", Qt::QueuedConnection);
402 return std::unexpected(std::string(e.what()));
403 }
catch (
const std::exception& e) {
404 return std::unexpected(std::string(e.what()));
411 std::optional<SessionListResult> listSessions(
412 const boost::uuids::uuid& accountId,
413 std::uint32_t limit = 100,
414 std::uint32_t offset = 0);
423 std::optional<TradeListResult> listTrades(
424 std::optional<boost::uuids::uuid> node_id = std::nullopt,
425 std::uint32_t offset = 0,
426 std::uint32_t limit = 100);
434 std::optional<trading::messaging::trade_export_item>
435 getTradeDetail(
const std::string& trade_id);
440 std::optional<std::vector<iam::domain::session>> getActiveSessions();
445 std::optional<std::vector<iam::messaging::session_sample_dto>>
446 getSessionSamples(
const boost::uuids::uuid& sessionId);
452 std::uint64_t bytesSent()
const {
return 0; }
453 std::uint64_t bytesReceived()
const {
return 0; }
454 std::uint64_t lastRttMs()
const {
return 0; }
456 std::optional<std::chrono::steady_clock::time_point> disconnectedSince()
const {
457 return disconnected_since_;
464 bool enableRecording(
const std::filesystem::path&) {
return false; }
465 void disableRecording() {}
466 bool isRecording()
const {
return false; }
467 std::filesystem::path recordingFilePath()
const {
return {}; }
468 void setRecordingDirectory(
const std::filesystem::path& dir) {
469 recording_directory_ = dir;
471 std::filesystem::path recordingDirectory()
const {
472 return recording_directory_;
479 void subscribeToEvent(
const std::string& subject);
480 void unsubscribeFromEvent(
const std::string& subject);
488 void connectionError(
const QString& message);
499 void notificationReceived(
const QString& eventType,
const QDateTime& timestamp,
500 const QStringList& entityIds,
const QString& tenantId,
501 int payloadType,
const QByteArray& payload);
503 void recordingStarted(
const QString& filePath);
504 void recordingStopped();
505 void streamingStarted();
506 void streamingStopped();
510 static constexpr double refresh_lifetime_ratio = 0.8;
519 void arm_refresh_timer(
int lifetime_s);
527 void onRefreshTimer();
533 std::string subject_prefix_;
536 std::shared_ptr<eventing::service::event_bus> event_bus_;
539 std::string connected_host_;
540 std::uint16_t connected_port_{0};
543 std::filesystem::path recording_directory_;
546 std::optional<std::chrono::steady_clock::time_point> disconnected_since_;
549 std::string stored_username_;
550 std::string stored_password_;
553 std::optional<boost::uuids::uuid> current_account_id_;
554 std::string current_email_;
558 std::string session_id_;
561 boost::uuids::uuid current_party_id_;
562 QString current_party_name_;
563 QString current_party_category_;
564 bool last_party_setup_required_ =
false;
567 std::string http_base_url_;
570 std::unordered_map<std::string, nats::service::subscription> nats_subscriptions_;
573 QTimer* refresh_timer_ =
nullptr;
Implements logging infrastructure for ORE Studio.
Definition boost_severity.hpp:28
Qt-based graphical user interface for ORE Studio.
Definition AccountController.hpp:32
JetStream management API.
Definition jetstream_admin.hpp:53
Authenticated NATS client for both interactive and service-to-service use.
Definition nats_client.hpp:72
Thrown when a NATS connection or initial service check fails.
Definition nats_connect_error.hpp:56
Thrown when a session reaches its maximum allowed duration.
Definition session_expired_error.hpp:34
Summary of a party the user can select during login.
Definition ClientManager.hpp:68
Result of a login attempt.
Definition ClientManager.hpp:80
Result of a signup attempt.
Definition ClientManager.hpp:94
Result of a session list request.
Definition ClientManager.hpp:103
Result of a trade list request (metadata only, no instruments).
Definition ClientManager.hpp:111
Manages the lifecycle of the NATS client and login state.
Definition ClientManager.hpp:123
QString currentPartyName() const
Get the name of the currently selected party.
Definition ClientManager.hpp:295
std::string serverAddress() const
Get the server address string.
Definition ClientManager.hpp:256
std::string currentUsername() const
Get the current logged-in user's username.
Definition ClientManager.hpp:233
void sessionExpired()
Emitted when the session can no longer be refreshed.
bool isSystemParty() const
Returns true if the currently selected party is the system party.
Definition ClientManager.hpp:305
boost::uuids::uuid currentPartyId() const
Get the UUID of the currently selected party.
Definition ClientManager.hpp:290
std::uint16_t connectedPort() const
Get the connected server port.
Definition ClientManager.hpp:272
const std::string & httpBaseUrl() const
HTTP base URL discovered during login via NATS service discovery.
Definition ClientManager.hpp:318
bool lastPartySetupRequired() const
Whether the last selectParty call indicated the party needs setup.
Definition ClientManager.hpp:310
std::string storedUsername() const
Get the stored username used for the current session.
Definition ClientManager.hpp:280
std::string connectedHost() const
Get the connected server hostname.
Definition ClientManager.hpp:264
bool isLoggedIn() const
Check if currently logged in.
Definition ClientManager.hpp:221
std::string currentEmail() const
Get the current logged-in user's email.
Definition ClientManager.hpp:241
void setCurrentEmail(const std::string &email)
Set the current logged-in user's email.
Definition ClientManager.hpp:246
auto process_request(RequestType request) -> std::expected< typename RequestType::response_type, std::string >
Process a request that does not require authentication.
Definition ClientManager.hpp:336
void setSubjectPrefix(const std::string &prefix)
Set the NATS subject prefix used for all outbound messages.
Definition ClientManager.hpp:152
std::optional< boost::uuids::uuid > accountId() const
Get the account ID if logged in.
Definition ClientManager.hpp:251
std::string storedPassword() const
Get the stored password used for the current session.
Definition ClientManager.hpp:285
bool isAdmin() const
Definition ClientManager.hpp:216
QString currentPartyCategory() const
Get the category of the currently selected party.
Definition ClientManager.hpp:300
const std::string & subjectPrefix() const
Get the current NATS subject prefix.
Definition ClientManager.hpp:157
auto process_authenticated_request(RequestType request, std::chrono::milliseconds timeout=std::chrono::seconds(30)) -> std::expected< typename RequestType::response_type, std::string >
Process a request that requires authentication.
Definition ClientManager.hpp:370
Concept for NATS-aware request types.
Definition ClientManager.hpp:60