diff --git a/cpp17/CMakePresets.json b/cpp17/CMakePresets.json index 910ec96..d1fe37f 100644 --- a/cpp17/CMakePresets.json +++ b/cpp17/CMakePresets.json @@ -5,8 +5,16 @@ "name": "default", "toolchainFile": "${env:VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", "CMAKE_EXPORT_COMPILE_COMMANDS": "TRUE" } } + ], + "buildPresets": [ + { + "name": "default", + "configurePreset": "default", + "jobs": 8 + } ] -} +} \ No newline at end of file diff --git a/cpp17/lib/CMakeLists.txt b/cpp17/lib/CMakeLists.txt index 1fdc5b0..114519a 100644 --- a/cpp17/lib/CMakeLists.txt +++ b/cpp17/lib/CMakeLists.txt @@ -10,8 +10,10 @@ add_library(${TARGET_NAME} STATIC src/AutoStore.cpp src/infrastructure/repositories/FileUserRepository.cpp src/infrastructure/repositories/FileItemRepository.cpp - src/infrastructure/adapters/HttpOrderService.cpp src/infrastructure/http/HttpServer.cpp + src/infrastructure/http/HttpOrderService.cpp + src/infrastructure/helpers/Jsend.cpp + src/infrastructure/helpers/JsonItem.cpp src/webapi/controllers/StoreController.cpp src/application/commands/AddItem.cpp ) diff --git a/cpp17/lib/include/autostore/AutoStore.h b/cpp17/lib/include/autostore/AutoStore.h index d552c14..45f9eb9 100644 --- a/cpp17/lib/include/autostore/AutoStore.h +++ b/cpp17/lib/include/autostore/AutoStore.h @@ -22,19 +22,19 @@ namespace webapi { class StoreController; } +namespace application { +class AddItem; +} + class AutoStore { public: - explicit AutoStore(std::string_view dataPath); + AutoStore(std::string_view dataPath, int port = 8080, + std::string_view host = "0.0.0.0"); ~AutoStore(); - // Initialize the application bool initialize(); - - // Start the application services bool start(); - - // Stop the application services void stop(); private: @@ -45,12 +45,17 @@ private: 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; }; } // namespace nxl::autostore \ No newline at end of file diff --git a/cpp17/lib/src/AutoStore.cpp b/cpp17/lib/src/AutoStore.cpp index c8242d8..c039484 100644 --- a/cpp17/lib/src/AutoStore.cpp +++ b/cpp17/lib/src/AutoStore.cpp @@ -1,7 +1,7 @@ #include "autostore/AutoStore.h" #include "infrastructure/repositories/FileItemRepository.h" #include "infrastructure/adapters/SystemClock.h" -#include "infrastructure/adapters/HttpOrderService.h" +#include "infrastructure/http/HttpOrderService.h" #include "webapi/controllers/StoreController.h" #include "infrastructure/http/HttpServer.h" #include @@ -10,8 +10,9 @@ namespace nxl::autostore { -AutoStore::AutoStore(std::string_view dataPath) - : dataPath(dataPath), initialized(false), serverRunning(false) +AutoStore::AutoStore(std::string_view dataPath, int port, std::string_view host) + : dataPath(dataPath), port(port), host(host), initialized(false), + serverRunning(false) {} AutoStore::~AutoStore() @@ -24,7 +25,7 @@ AutoStore::~AutoStore() bool AutoStore::initialize() { std::cout << "Initializing AutoStore with data path: " << dataPath - << std::endl; + << ", host: " << host << ", port: " << port << std::endl; try { // Create data directory if it doesn't exist @@ -39,12 +40,16 @@ bool AutoStore::initialize() orderService = std::make_unique(); // Initialize HTTP server - httpServer = std::make_unique(8080, "0.0.0.0"); + httpServer = std::make_unique(port, host); - // Initialize store controller - storeController = std::make_unique( + // Initialize use case + addItemUseCase = std::make_unique( *itemRepository, *clock, *orderService); + // Initialize store controller + storeController = + std::make_unique(*addItemUseCase); + initialized = true; std::cout << "AutoStore initialized successfully" << std::endl; return true; @@ -76,9 +81,10 @@ bool AutoStore::start() serverRunning = true; std::cout << "AutoStore services started successfully" << std::endl; - std::cout << "HTTP server listening on http://0.0.0.0:8080" << std::endl; - std::cout << "API endpoint: POST http://0.0.0.0:8080/api/items" + std::cout << "HTTP server listening on http://" << host << ":" << port << std::endl; + std::cout << "API endpoint: POST http://" << host << ":" << port + << "/api/items" << std::endl; return true; } catch (const std::exception& e) { diff --git a/cpp17/lib/src/application/commands/AddItem.cpp b/cpp17/lib/src/application/commands/AddItem.cpp index dfbac25..f7a4def 100644 --- a/cpp17/lib/src/application/commands/AddItem.cpp +++ b/cpp17/lib/src/application/commands/AddItem.cpp @@ -8,7 +8,7 @@ AddItem::AddItem(IItemRepository& itemRepository, IClock& clock, : itemRepository(itemRepository), clock(clock), orderService(orderService) {} -void AddItem::execute(domain::Item&& item, const IntPresenter& presenter) +void AddItem::execute(domain::Item&& item, const ItemPresenter& presenter) { try { const auto currentTime = clock.getCurrentTime(); @@ -17,10 +17,10 @@ void AddItem::execute(domain::Item&& item, const IntPresenter& presenter) orderService.orderItem(item); } - itemRepository.save(item); - presenter(1); // Success + item.id = itemRepository.save(item); + presenter(item); // Success } catch (const std::exception& e) { - presenter(0); // Failure + presenter(item); // Failure } } diff --git a/cpp17/lib/src/application/commands/AddItem.h b/cpp17/lib/src/application/commands/AddItem.h index 6ccad2b..384ea5a 100644 --- a/cpp17/lib/src/application/commands/AddItem.h +++ b/cpp17/lib/src/application/commands/AddItem.h @@ -5,11 +5,9 @@ #include "application/interfaces/IItemRepository.h" #include "application/interfaces/IClock.h" #include "application/interfaces/IOrderService.h" -#include "application/presenters/GenericPresenters.h" +#include "application/presenters/StorePresenters.h" -namespace nxl { -namespace autostore { -namespace application { +namespace nxl::autostore::application { class AddItem { @@ -18,7 +16,7 @@ public: AddItem(IItemRepository& itemRepository, IClock& clock, IOrderService& orderService); - void execute(domain::Item&& item, const IntPresenter& presenter); + void execute(domain::Item&& item, const ItemPresenter& presenter); private: IItemRepository& itemRepository; @@ -27,6 +25,4 @@ private: domain::ItemExpirationPolicy expirationPolicy; }; -} // namespace application -} // namespace autostore -} // namespace nxl \ No newline at end of file +} // namespace nxl::autostore::application \ No newline at end of file diff --git a/cpp17/lib/src/application/interfaces/IItemRepository.h b/cpp17/lib/src/application/interfaces/IItemRepository.h index d117eb7..cbb312b 100644 --- a/cpp17/lib/src/application/interfaces/IItemRepository.h +++ b/cpp17/lib/src/application/interfaces/IItemRepository.h @@ -12,7 +12,7 @@ class IItemRepository { public: virtual ~IItemRepository() = default; - virtual void save(const domain::Item& item) = 0; + virtual domain::Item::Id_t save(const domain::Item& item) = 0; virtual std::optional findById(std::string_view id) = 0; virtual std::vector findByUser(std::string_view userId) = 0; virtual std::vector findAll() = 0; diff --git a/cpp17/lib/src/domain/entities/Item.h b/cpp17/lib/src/domain/entities/Item.h index 021eafe..b992962 100644 --- a/cpp17/lib/src/domain/entities/Item.h +++ b/cpp17/lib/src/domain/entities/Item.h @@ -9,6 +9,7 @@ namespace nxl::autostore::domain { struct Item { using Id_t = std::string; + inline const static Id_t NULL_ID{""}; Id_t id; std::string name; std::chrono::system_clock::time_point expirationDate; diff --git a/cpp17/lib/src/infrastructure/adapters/HttpOrderService.cpp b/cpp17/lib/src/infrastructure/adapters/HttpOrderService.cpp deleted file mode 100644 index 6f27dd9..0000000 --- a/cpp17/lib/src/infrastructure/adapters/HttpOrderService.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#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/adapters/HttpOrderService.h b/cpp17/lib/src/infrastructure/adapters/HttpOrderService.h deleted file mode 100644 index ab7b194..0000000 --- a/cpp17/lib/src/infrastructure/adapters/HttpOrderService.h +++ /dev/null @@ -1,20 +0,0 @@ -#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.h b/cpp17/lib/src/infrastructure/http/HttpServer.h index cef5a2d..8a1e319 100644 --- a/cpp17/lib/src/infrastructure/http/HttpServer.h +++ b/cpp17/lib/src/infrastructure/http/HttpServer.h @@ -20,9 +20,9 @@ public: httplib::Server& getServer(); private: - std::string host; - int port; - bool running; + 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/infrastructure/repositories/FileItemRepository.cpp b/cpp17/lib/src/infrastructure/repositories/FileItemRepository.cpp index 351c3f3..b52891f 100644 --- a/cpp17/lib/src/infrastructure/repositories/FileItemRepository.cpp +++ b/cpp17/lib/src/infrastructure/repositories/FileItemRepository.cpp @@ -1,5 +1,5 @@ #include "infrastructure/repositories/FileItemRepository.h" -#include "nlohmann/json.hpp" +#include "infrastructure/helpers/JsonItem.h" #include #include #include @@ -10,37 +10,12 @@ namespace nxl::autostore::infrastructure { namespace { -// Helper functions for JSON serialization -inline void itemToJson(nlohmann::json& j, const domain::Item& item) -{ - j = - nlohmann::json{{"id", item.id}, - {"name", item.name}, - {"expirationDate", - std::chrono::system_clock::to_time_t(item.expirationDate)}, - {"orderUrl", item.orderUrl}, - {"userId", item.userId}}; -} - -inline void jsonToItem(const nlohmann::json& j, domain::Item& item) -{ - j.at("id").get_to(item.id); - j.at("name").get_to(item.name); - std::time_t expirationTime; - j.at("expirationDate").get_to(expirationTime); - item.expirationDate = std::chrono::system_clock::from_time_t(expirationTime); - j.at("orderUrl").get_to(item.orderUrl); - j.at("userId").get_to(item.userId); -} - // Helper functions for vector serialization inline nlohmann::json itemsToJson(const std::vector& items) { nlohmann::json j = nlohmann::json::array(); for (const auto& item : items) { - nlohmann::json itemJson; - itemToJson(itemJson, item); - j.push_back(itemJson); + j.push_back(infrastructure::JsonItem::toJsonObj(item)); } return j; } @@ -49,9 +24,7 @@ inline std::vector jsonToItems(const nlohmann::json& j) { std::vector items; for (const auto& itemJson : j) { - domain::Item item; - jsonToItem(itemJson, item); - items.push_back(item); + items.push_back(infrastructure::JsonItem::fromJsonObj(itemJson)); } return items; } @@ -63,9 +36,10 @@ FileItemRepository::FileItemRepository(std::string_view dbPath) : dbPath(dbPath) load(); } -void FileItemRepository::save(const domain::Item& item) +domain::Item::Id_t FileItemRepository::save(const domain::Item& item) { std::lock_guard lock(mtx); + domain::Item::Id_t id = item.id; auto it = std::find_if(items.begin(), items.end(), [&](const domain::Item& i) { return i.id == item.id; }); @@ -73,9 +47,16 @@ void FileItemRepository::save(const domain::Item& item) if (it != items.end()) { *it = item; } else { - items.push_back(item); + domain::Item newItem{item}; + newItem.id = "item-" + + std::to_string( + std::chrono::system_clock::now().time_since_epoch().count()); + items.push_back(newItem); + id = newItem.id; } persist(); + + return id; } std::optional FileItemRepository::findById(std::string_view id) diff --git a/cpp17/lib/src/infrastructure/repositories/FileItemRepository.h b/cpp17/lib/src/infrastructure/repositories/FileItemRepository.h index c091856..9f27aaa 100644 --- a/cpp17/lib/src/infrastructure/repositories/FileItemRepository.h +++ b/cpp17/lib/src/infrastructure/repositories/FileItemRepository.h @@ -11,7 +11,7 @@ class FileItemRepository : public application::IItemRepository { public: explicit FileItemRepository(std::string_view dbPath); - void save(const domain::Item& item) override; + domain::Item::Id_t save(const domain::Item& item) override; std::optional findById(std::string_view id) override; std::vector findByUser(std::string_view userId) override; std::vector findAll() override; diff --git a/cpp17/lib/src/webapi/controllers/StoreController.cpp b/cpp17/lib/src/webapi/controllers/StoreController.cpp index 817a39c..c509b38 100644 --- a/cpp17/lib/src/webapi/controllers/StoreController.cpp +++ b/cpp17/lib/src/webapi/controllers/StoreController.cpp @@ -1,15 +1,14 @@ #include "webapi/controllers/StoreController.h" -#include -#include -#include -#include +#include "infrastructure/helpers/JsonItem.h" +#include "infrastructure/helpers/Jsend.h" namespace nxl::autostore::webapi { -StoreController::StoreController(application::IItemRepository& itemRepository, - application::IClock& clock, - application::IOrderService& orderService) - : addItemUseCase(itemRepository, clock, orderService) +using infrastructure::Jsend; +using infrastructure::JsonItem; + +StoreController::StoreController(application::AddItem& addItemUseCase) + : addItemUseCase(addItemUseCase) {} void StoreController::registerRoutes(httplib::Server& server) @@ -26,96 +25,32 @@ void StoreController::addItem(const httplib::Request& req, try { if (req.body.empty()) { res.status = 400; - res.set_content(createErrorResponse("Request body is empty"), + res.set_content(Jsend::error("Request body is empty", 400), "application/json"); return; } - auto item = parseItemFromJson(req.body); + auto item = JsonItem::fromJson(req.body); - // Execute the use case with a simple presenter - bool success = false; - addItemUseCase.execute(std::move(item), - [&success](int result) { success = (result == 1); }); + try { + addItemUseCase.execute(std::move(item), [&res](auto item) { + res.status = 201; + nlohmann::json responseData = nlohmann::json::object(); + responseData["id"] = item.id; + res.set_content(Jsend::success(responseData), "application/json"); + }); - if (success) { - res.status = 201; - res.set_content(createSuccessResponse(item), "application/json"); - } else { + } catch (const std::exception& e) { res.status = 500; - res.set_content(createErrorResponse("Failed to add item"), - "application/json"); + res.set_content( + Jsend::error("Failed to add item: " + std::string(e.what()), + res.status), + "application/json"); } } catch (const std::exception& e) { res.status = 400; - res.set_content(createErrorResponse(e.what()), "application/json"); - } -} - -domain::Item StoreController::parseItemFromJson(const std::string& jsonBody) -{ - auto json = nlohmann::json::parse(jsonBody); - - domain::Item item; - item.id = json.value("id", ""); - item.name = json.value("name", ""); - item.orderUrl = json.value("orderUrl", ""); - item.userId = json.value("userId", "default-user"); - - // Parse expiration date - if (json.contains("expirationDate")) { - std::string dateStr = json["expirationDate"]; - std::tm tm = {}; - std::istringstream ss(dateStr); - ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S"); - if (ss.fail()) { - throw std::runtime_error("Invalid expiration date format. Use ISO 8601 " - "format: YYYY-MM-DDTHH:MM:SS"); - } - item.expirationDate = - std::chrono::system_clock::from_time_t(std::mktime(&tm)); - } else { - // Default to 24 hours from now - item.expirationDate = - std::chrono::system_clock::now() + std::chrono::hours(24); - } - - if (item.id.empty()) { - // Generate a simple ID if not provided - item.id = "item-" - + std::to_string( - std::chrono::system_clock::now().time_since_epoch().count()); + res.set_content(Jsend::error(e.what(), res.status), "application/json"); } - - if (item.name.empty()) { - throw std::runtime_error("Item name is required"); - } - - return item; -} - -std::string StoreController::createSuccessResponse(const domain::Item& item) -{ - nlohmann::json response; - response["success"] = true; - response["message"] = "Item added successfully"; - response["item"]["id"] = item.id; - response["item"]["name"] = item.name; - response["item"]["expirationDate"] = - std::chrono::system_clock::to_time_t(item.expirationDate); - response["item"]["orderUrl"] = item.orderUrl; - response["item"]["userId"] = item.userId; - - return response.dump(); -} - -std::string StoreController::createErrorResponse(const std::string& message) -{ - nlohmann::json response; - response["success"] = false; - response["message"] = message; - - return response.dump(); } } // namespace nxl::autostore::webapi \ No newline at end of file diff --git a/cpp17/lib/src/webapi/controllers/StoreController.h b/cpp17/lib/src/webapi/controllers/StoreController.h index ad3ccc1..46baf42 100644 --- a/cpp17/lib/src/webapi/controllers/StoreController.h +++ b/cpp17/lib/src/webapi/controllers/StoreController.h @@ -1,9 +1,6 @@ #pragma once #include "application/commands/AddItem.h" -#include "application/interfaces/IItemRepository.h" -#include "application/interfaces/IClock.h" -#include "application/interfaces/IOrderService.h" #include "domain/entities/Item.h" #include #include @@ -13,19 +10,13 @@ namespace nxl::autostore::webapi { class StoreController { public: - StoreController(application::IItemRepository& itemRepository, - application::IClock& clock, - application::IOrderService& orderService); + StoreController(application::AddItem& addItemUseCase); void registerRoutes(httplib::Server& server); private: void addItem(const httplib::Request& req, httplib::Response& res); - domain::Item parseItemFromJson(const std::string& jsonBody); - std::string createSuccessResponse(const domain::Item& item); - std::string createErrorResponse(const std::string& message); - application::AddItem addItemUseCase; };