From fc955dd1bbac90cee74125646f26c5ba96fb88b7 Mon Sep 17 00:00:00 2001 From: chodak166 Date: Sun, 10 Aug 2025 12:32:21 +0200 Subject: [PATCH] WIP: cpp17 - dingo --- cpp17/lib/CMakeLists.txt | 10 +- cpp17/lib/include/autostore/AutoStore.h | 32 ++---- cpp17/lib/src/AutoStore.cpp | 37 ++----- cpp17/lib/src/DiContainer.cpp | 45 ++++++++ cpp17/lib/src/DiContainer.h | 102 ++++++++++++++++++ .../application/presenters/StorePresenters.h | 10 ++ .../lib/src/infrastructure/helpers/Jsend.cpp | 32 ++++++ cpp17/lib/src/infrastructure/helpers/Jsend.h | 20 ++++ .../src/infrastructure/helpers/JsonItem.cpp | 76 +++++++++++++ .../lib/src/infrastructure/helpers/JsonItem.h | 22 ++++ .../infrastructure/http/HttpOrderService.cpp | 36 +++++++ .../infrastructure/http/HttpOrderService.h | 20 ++++ .../src/infrastructure/http/HttpServer.cpp | 10 +- .../lib/src/infrastructure/http/HttpServer.h | 6 +- .../webapi/controllers/StoreController.cpp | 6 +- .../src/webapi/controllers/StoreController.h | 10 +- cpp17/vcpkg.json | 5 +- 17 files changed, 403 insertions(+), 76 deletions(-) create mode 100644 cpp17/lib/src/DiContainer.cpp create mode 100644 cpp17/lib/src/DiContainer.h create mode 100644 cpp17/lib/src/application/presenters/StorePresenters.h create mode 100644 cpp17/lib/src/infrastructure/helpers/Jsend.cpp create mode 100644 cpp17/lib/src/infrastructure/helpers/Jsend.h create mode 100644 cpp17/lib/src/infrastructure/helpers/JsonItem.cpp create mode 100644 cpp17/lib/src/infrastructure/helpers/JsonItem.h create mode 100644 cpp17/lib/src/infrastructure/http/HttpOrderService.cpp create mode 100644 cpp17/lib/src/infrastructure/http/HttpOrderService.h diff --git a/cpp17/lib/CMakeLists.txt b/cpp17/lib/CMakeLists.txt index 114519a..36ed1c2 100644 --- a/cpp17/lib/CMakeLists.txt +++ b/cpp17/lib/CMakeLists.txt @@ -5,9 +5,14 @@ set(TARGET_NAME AutoStoreLib) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) +# Find dependencies +find_package(httplib CONFIG REQUIRED) +find_package(nlohmann_json CONFIG REQUIRED) +find_path(DINGO_INCLUDE_DIRS "dingo/allocator.h") add_library(${TARGET_NAME} STATIC src/AutoStore.cpp + src/DiContainer.cpp src/infrastructure/repositories/FileUserRepository.cpp src/infrastructure/repositories/FileItemRepository.cpp src/infrastructure/http/HttpServer.cpp @@ -23,6 +28,7 @@ target_include_directories(${TARGET_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/include/autostore PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src + ${DINGO_INCLUDE_DIRS} ) target_sources(${TARGET_NAME} @@ -33,9 +39,7 @@ target_sources(${TARGET_NAME} include/autostore/AutoStore.h ) -# Find dependencies -find_package(httplib CONFIG REQUIRED) -find_package(nlohmann_json CONFIG REQUIRED) + target_link_libraries(${TARGET_NAME} PUBLIC diff --git a/cpp17/lib/include/autostore/AutoStore.h b/cpp17/lib/include/autostore/AutoStore.h index 45f9eb9..dc8a4a9 100644 --- a/cpp17/lib/include/autostore/AutoStore.h +++ b/cpp17/lib/include/autostore/AutoStore.h @@ -7,25 +7,14 @@ namespace nxl::autostore { -// Forward declarations -namespace application { -class IItemRepository; -class IClock; -class IOrderService; -} // namespace application - -namespace infrastructure { -class HttpServer; +namespace di { +class DiContainer; } namespace webapi { class StoreController; } -namespace application { -class AddItem; -} - class AutoStore { public: @@ -38,24 +27,15 @@ public: void stop(); private: + int port; + std::string host; std::string dataPath; bool initialized; - - // Dependencies - std::unique_ptr itemRepository; - std::unique_ptr clock; - std::unique_ptr orderService; - std::unique_ptr addItemUseCase; - std::unique_ptr storeController; - - // HTTP server - std::unique_ptr httpServer; std::thread serverThread; bool serverRunning; - // Configuration - int port; - std::string host; + std::unique_ptr diContainer; + std::unique_ptr storeController; }; } // namespace nxl::autostore \ No newline at end of file diff --git a/cpp17/lib/src/AutoStore.cpp b/cpp17/lib/src/AutoStore.cpp index c039484..0e925fe 100644 --- a/cpp17/lib/src/AutoStore.cpp +++ b/cpp17/lib/src/AutoStore.cpp @@ -1,4 +1,5 @@ -#include "autostore/AutoStore.h" +#include "AutoStore.h" +#include "DiContainer.h" #include "infrastructure/repositories/FileItemRepository.h" #include "infrastructure/adapters/SystemClock.h" #include "infrastructure/http/HttpOrderService.h" @@ -28,27 +29,10 @@ bool AutoStore::initialize() << ", host: " << host << ", port: " << port << std::endl; try { - // Create data directory if it doesn't exist std::filesystem::create_directories(dataPath); - // Initialize repositories and services - std::string itemsDbPath = std::filesystem::path(dataPath) / "items.json"; - itemRepository = - std::make_unique(itemsDbPath); - - clock = std::make_unique(); - orderService = std::make_unique(); - - // Initialize HTTP server - httpServer = std::make_unique(port, host); - - // Initialize use case - addItemUseCase = std::make_unique( - *itemRepository, *clock, *orderService); - - // Initialize store controller - storeController = - std::make_unique(*addItemUseCase); + diContainer = std::make_unique(); + storeController = std::make_unique(*diContainer); initialized = true; std::cout << "AutoStore initialized successfully" << std::endl; @@ -70,11 +54,10 @@ bool AutoStore::start() std::cout << "Starting AutoStore services..." << std::endl; try { - // Register routes with the HTTP server - storeController->registerRoutes(httpServer->getServer()); + auto& httpServer = diContainer->resolveRef(); + storeController->registerRoutes(httpServer.getServer()); - // Start HTTP server - if (!httpServer->start()) { + if (!httpServer.start(port, host)) { std::cerr << "Failed to start HTTP server" << std::endl; return false; } @@ -102,9 +85,9 @@ void AutoStore::stop() std::cout << "Stopping AutoStore services..." << std::endl; - // Stop HTTP server - if (httpServer) { - httpServer->stop(); + if (diContainer) { + auto& httpServer = diContainer->resolveRef(); + httpServer.stop(); } serverRunning = false; diff --git a/cpp17/lib/src/DiContainer.cpp b/cpp17/lib/src/DiContainer.cpp new file mode 100644 index 0000000..79327f9 --- /dev/null +++ b/cpp17/lib/src/DiContainer.cpp @@ -0,0 +1,45 @@ +#include "DiContainer.h" +#include "infrastructure/repositories/FileItemRepository.h" +#include "infrastructure/adapters/SystemClock.h" +#include "infrastructure/http/HttpOrderService.h" +#include "infrastructure/http/HttpServer.h" +#include "application/commands/AddItem.h" +#include "webapi/controllers/StoreController.h" +#include + +namespace nxl::autostore::di { + +DiContainer::DiContainer() +{ + registerDependencies(); +} + +void DiContainer::registerDependencies() +{ + // Register shared references + + container.register_type, + dingo::storage, + dingo::interface>(); + + container.register_type, + dingo::storage, + dingo::interface>(); + + container.register_type, + dingo::storage, + dingo::interface>(); + + container.register_type, + dingo::storage>(); + + container.register_indexed_type, + dingo::storage, + dingo::interface>( + std::string("AddItem")); + + // test: + auto uc = container.resolve( + std::string("AddItem")); // throws on start +} +} // namespace nxl::autostore::di diff --git a/cpp17/lib/src/DiContainer.h b/cpp17/lib/src/DiContainer.h new file mode 100644 index 0000000..3530e3e --- /dev/null +++ b/cpp17/lib/src/DiContainer.h @@ -0,0 +1,102 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// Forward declarations +namespace nxl::autostore { +class AutoStore; +} + +namespace nxl::autostore::infrastructure { +class FileItemRepository; +class SystemClock; +class HttpOrderService; +class HttpServer; +} // namespace nxl::autostore::infrastructure + +namespace nxl::autostore::application { +class AddItem; +} + +namespace nxl::autostore::webapi { +class StoreController; +} + +namespace nxl::autostore::di { + +// Declare traits with std::string based index for named resolution +struct container_traits : dingo::dynamic_container_traits +{ + using index_definition_type = + std::tuple>; +}; + +/** + * @brief Dependency Injection Container for AutoStore application + * + * This class wraps the dingo container and provides a simplified interface + * for registering and resolving dependencies in the AutoStore application. + */ +class DiContainer +{ +public: + /** + * @brief Construct a new DiContainer object + */ + DiContainer(); + + /** + * @brief Destroy the DiContainer object + */ + ~DiContainer() = default; + + /** + * @brief Register all application dependencies + * + * @param dataPath Path to the data directory + * @param port HTTP server port + * @param host HTTP server host + */ + void registerDependencies(); + + /** + * @brief Resolve a dependency by type + * + * @tparam T Type to resolve + * @return Instance of the resolved type + */ + template T resolve() { return container.resolve(); } + + /** + * @brief Resolve a dependency by type as a shared pointer + * + * @tparam T Type to resolve + * @return Shared pointer to the resolved type + */ + template std::shared_ptr resolveShared() + { + return container.resolve>(); + } + + /** + * @brief Resolve a dependency by type as a reference + * + * @tparam T Type to resolve + * @return Reference to the resolved type + */ + template T& resolveRef() { return container.resolve(); } + +private: + dingo::container container; +}; + +} // namespace nxl::autostore::di \ No newline at end of file diff --git a/cpp17/lib/src/application/presenters/StorePresenters.h b/cpp17/lib/src/application/presenters/StorePresenters.h new file mode 100644 index 0000000..168c425 --- /dev/null +++ b/cpp17/lib/src/application/presenters/StorePresenters.h @@ -0,0 +1,10 @@ +#pragma once + +#include "domain/entities/Item.h" +#include + +namespace nxl::autostore::application { + +using ItemPresenter = std::function; + +} // namespace nxl::autostore::application \ No newline at end of file diff --git a/cpp17/lib/src/infrastructure/helpers/Jsend.cpp b/cpp17/lib/src/infrastructure/helpers/Jsend.cpp new file mode 100644 index 0000000..8d7d0d9 --- /dev/null +++ b/cpp17/lib/src/infrastructure/helpers/Jsend.cpp @@ -0,0 +1,32 @@ +#include "infrastructure/helpers/Jsend.h" + +namespace nxl::autostore::infrastructure { + +std::string Jsend::success(const nlohmann::json& data) +{ + nlohmann::json response; + response["status"] = "success"; + + if (!data.is_null()) { + response["data"] = data; + } + + return response.dump(); +} + +std::string Jsend::error(const std::string& message, int code, + const nlohmann::json& data) +{ + nlohmann::json response; + response["status"] = "error"; + response["message"] = message; + response["code"] = code; + + if (!data.is_null()) { + response["data"] = data; + } + + return response.dump(); +} + +} // namespace nxl::autostore::infrastructure \ No newline at end of file diff --git a/cpp17/lib/src/infrastructure/helpers/Jsend.h b/cpp17/lib/src/infrastructure/helpers/Jsend.h new file mode 100644 index 0000000..7f08bf7 --- /dev/null +++ b/cpp17/lib/src/infrastructure/helpers/Jsend.h @@ -0,0 +1,20 @@ +#pragma once + +#include "nlohmann/json.hpp" +#include + +namespace nxl::autostore::infrastructure { + +class Jsend +{ +public: + static std::string success(const nlohmann::json& data = nullptr); + static std::string error(const std::string& message, int code = 500, + const nlohmann::json& data = nullptr); + +private: + Jsend() = delete; + ~Jsend() = delete; +}; + +} // namespace nxl::autostore::infrastructure \ No newline at end of file diff --git a/cpp17/lib/src/infrastructure/helpers/JsonItem.cpp b/cpp17/lib/src/infrastructure/helpers/JsonItem.cpp new file mode 100644 index 0000000..31578b3 --- /dev/null +++ b/cpp17/lib/src/infrastructure/helpers/JsonItem.cpp @@ -0,0 +1,76 @@ +#include "infrastructure/helpers/JsonItem.h" +#include +#include +#include +#include + +namespace nxl::autostore::infrastructure { +domain::Item JsonItem::fromJson(const std::string& jsonBody) +{ + auto json = nlohmann::json::parse(jsonBody); + return fromJsonObj(json); +} + +domain::Item JsonItem::fromJsonObj(const nlohmann::json& j) +{ + domain::Item item; + item.id = j.value("id", ""); + item.name = j.value("name", ""); + item.orderUrl = j.value("orderUrl", ""); + item.userId = j.value("userId", "default-user"); + + if (j["expirationDate"].is_number()) { + // Handle numeric timestamp + time_t timestamp = j["expirationDate"]; + item.expirationDate = std::chrono::system_clock::from_time_t(timestamp); + } else if (j["expirationDate"].is_string()) { + // Handle ISO 8601 string format + std::string dateStr = j["expirationDate"]; + std::tm tm = {}; + std::istringstream ss(dateStr); + + // Parse the ISO 8601 format + ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S"); + if (ss.fail()) { + throw std::runtime_error( + "Invalid format for expirationDate string. Expected ISO 8601 format " + "(YYYY-MM-DDTHH:MM:SS)."); + } + + // Convert to time_t + time_t timestamp = std::mktime(&tm); + if (timestamp == -1) { + throw std::runtime_error( + "Failed to convert expirationDate to timestamp."); + } + + item.expirationDate = std::chrono::system_clock::from_time_t(timestamp); + } else { + throw std::runtime_error("Invalid type for expirationDate. Expected number " + "(Unix timestamp) or string (ISO 8601 format)."); + } + + if (item.name.empty()) { + throw std::runtime_error("Item name is required"); + } + + return item; +} + +std::string JsonItem::toJson(const domain::Item& item) +{ + return toJsonObj(item).dump(); +} + +nlohmann::json JsonItem::toJsonObj(const domain::Item& item) +{ + nlohmann::json j; + j["id"] = item.id; + j["name"] = item.name; + j["expirationDate"] = + std::chrono::system_clock::to_time_t(item.expirationDate); + j["orderUrl"] = item.orderUrl; + j["userId"] = item.userId; + return j; +} +} // namespace nxl::autostore::infrastructure \ No newline at end of file diff --git a/cpp17/lib/src/infrastructure/helpers/JsonItem.h b/cpp17/lib/src/infrastructure/helpers/JsonItem.h new file mode 100644 index 0000000..b2da04b --- /dev/null +++ b/cpp17/lib/src/infrastructure/helpers/JsonItem.h @@ -0,0 +1,22 @@ +#pragma once + +#include "domain/entities/Item.h" +#include "nlohmann/json.hpp" +#include + +namespace nxl::autostore::infrastructure { + +class JsonItem +{ +public: + static domain::Item fromJson(const std::string& jsonBody); + static std::string toJson(const domain::Item& item); + static nlohmann::json toJsonObj(const domain::Item& item); + static domain::Item fromJsonObj(const nlohmann::json& j); + +private: + JsonItem() = delete; + ~JsonItem() = delete; +}; + +} // namespace nxl::autostore::infrastructure \ No newline at end of file diff --git a/cpp17/lib/src/infrastructure/http/HttpOrderService.cpp b/cpp17/lib/src/infrastructure/http/HttpOrderService.cpp new file mode 100644 index 0000000..6f27dd9 --- /dev/null +++ b/cpp17/lib/src/infrastructure/http/HttpOrderService.cpp @@ -0,0 +1,36 @@ +#include "HttpOrderService.h" +#include +#include + +namespace nxl::autostore::infrastructure { + +HttpOrderService::HttpOrderService(const std::string& baseUrl) + : baseUrl(baseUrl) +{} + +void HttpOrderService::orderItem(const domain::Item& item) +{ + if (item.orderUrl.empty()) { + throw std::runtime_error("Order URL is empty for item: " + item.name); + } + + std::string payload = + R"({"itemName": ")" + item.name + R"(", "itemId": ")" + item.id + "\"}"; + sendPostRequest(item.orderUrl, payload); +} + +void HttpOrderService::sendPostRequest(const std::string& url, + const std::string& payload) +{ + // In a real implementation, this would use an HTTP client library + // For now, we'll simulate the HTTP call + std::cout << "POST request to: " << url << std::endl; + std::cout << "Payload: " << payload << std::endl; + + // Simulate HTTP error handling + if (url.find("error") != std::string::npos) { + throw std::runtime_error("Failed to send order request to: " + url); + } +} + +} // namespace nxl::autostore::infrastructure \ No newline at end of file diff --git a/cpp17/lib/src/infrastructure/http/HttpOrderService.h b/cpp17/lib/src/infrastructure/http/HttpOrderService.h new file mode 100644 index 0000000..ab7b194 --- /dev/null +++ b/cpp17/lib/src/infrastructure/http/HttpOrderService.h @@ -0,0 +1,20 @@ +#pragma once + +#include "application/interfaces/IOrderService.h" +#include "domain/entities/Item.h" +#include + +namespace nxl::autostore::infrastructure { + +class HttpOrderService : public application::IOrderService +{ +public: + explicit HttpOrderService(const std::string& baseUrl = ""); + void orderItem(const domain::Item& item) override; + +private: + std::string baseUrl; + void sendPostRequest(const std::string& url, const std::string& payload); +}; + +} // namespace nxl::autostore::infrastructure \ No newline at end of file diff --git a/cpp17/lib/src/infrastructure/http/HttpServer.cpp b/cpp17/lib/src/infrastructure/http/HttpServer.cpp index f6ebb42..aac87aa 100644 --- a/cpp17/lib/src/infrastructure/http/HttpServer.cpp +++ b/cpp17/lib/src/infrastructure/http/HttpServer.cpp @@ -3,9 +3,7 @@ namespace nxl::autostore::infrastructure { -HttpServer::HttpServer(int port, const std::string& host) - : host(host), port(port), running(false) -{} +HttpServer::HttpServer() {} HttpServer::~HttpServer() { @@ -14,7 +12,7 @@ HttpServer::~HttpServer() } } -bool HttpServer::start() +bool HttpServer::start(int port, const std::string& host) { if (running) { return true; @@ -47,7 +45,7 @@ bool HttpServer::start() std::cout << "Starting HTTP server on " << host << ":" << port << std::endl; // Start server in a separate thread - serverThread = std::thread([this]() { + serverThread = std::thread([host, port, this]() { std::cout << "Server thread started, listening on " << host << ":" << port << std::endl; bool listenResult = server.listen(host.c_str(), port); @@ -98,4 +96,4 @@ httplib::Server& HttpServer::getServer() return server; } -} // namespace nxl::autostore::infrastructure \ No newline at end of file +} // namespace nxl::autostore::infrastructure diff --git a/cpp17/lib/src/infrastructure/http/HttpServer.h b/cpp17/lib/src/infrastructure/http/HttpServer.h index 8a1e319..80d3dfd 100644 --- a/cpp17/lib/src/infrastructure/http/HttpServer.h +++ b/cpp17/lib/src/infrastructure/http/HttpServer.h @@ -10,18 +10,16 @@ namespace nxl::autostore::infrastructure { class HttpServer { public: - explicit HttpServer(int port = 8080, const std::string& host = "0.0.0.0"); + explicit HttpServer(); ~HttpServer(); - bool start(); + bool start(int port = 8080, const std::string& host = "0.0.0.0"); void stop(); bool isRunning() const; httplib::Server& getServer(); private: - std::string host{"0.0.0.0"}; - int port{8080}; bool running{false}; httplib::Server server; std::thread serverThread; diff --git a/cpp17/lib/src/webapi/controllers/StoreController.cpp b/cpp17/lib/src/webapi/controllers/StoreController.cpp index c509b38..ab4367e 100644 --- a/cpp17/lib/src/webapi/controllers/StoreController.cpp +++ b/cpp17/lib/src/webapi/controllers/StoreController.cpp @@ -1,14 +1,15 @@ #include "webapi/controllers/StoreController.h" #include "infrastructure/helpers/JsonItem.h" #include "infrastructure/helpers/Jsend.h" +#include "application/commands/AddItem.h" namespace nxl::autostore::webapi { using infrastructure::Jsend; using infrastructure::JsonItem; -StoreController::StoreController(application::AddItem& addItemUseCase) - : addItemUseCase(addItemUseCase) +StoreController::StoreController(di::DiContainer& diContainer) + : diContainer(diContainer) {} void StoreController::registerRoutes(httplib::Server& server) @@ -33,6 +34,7 @@ void StoreController::addItem(const httplib::Request& req, auto item = JsonItem::fromJson(req.body); try { + auto& addItemUseCase = diContainer.resolveRef(); addItemUseCase.execute(std::move(item), [&res](auto item) { res.status = 201; nlohmann::json responseData = nlohmann::json::object(); diff --git a/cpp17/lib/src/webapi/controllers/StoreController.h b/cpp17/lib/src/webapi/controllers/StoreController.h index 46baf42..22e7dc7 100644 --- a/cpp17/lib/src/webapi/controllers/StoreController.h +++ b/cpp17/lib/src/webapi/controllers/StoreController.h @@ -1,23 +1,21 @@ #pragma once -#include "application/commands/AddItem.h" -#include "domain/entities/Item.h" -#include -#include +#include "DiContainer.h" +#include // TODO: forward declaration namespace nxl::autostore::webapi { class StoreController { public: - StoreController(application::AddItem& addItemUseCase); + StoreController(di::DiContainer& diContainer); void registerRoutes(httplib::Server& server); private: void addItem(const httplib::Request& req, httplib::Response& res); - application::AddItem addItemUseCase; + di::DiContainer& diContainer; }; } // namespace nxl::autostore::webapi \ No newline at end of file diff --git a/cpp17/vcpkg.json b/cpp17/vcpkg.json index cecd049..6788390 100644 --- a/cpp17/vcpkg.json +++ b/cpp17/vcpkg.json @@ -3,7 +3,8 @@ "version-string": "1.0.0", "dependencies": [ "cpp-httplib", - "catch2", - "nlohmann-json" + "nlohmann-json", + "dingo", + "catch2" ] }