17 changed files with 403 additions and 76 deletions
@ -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 <filesystem> |
||||||
|
|
||||||
|
namespace nxl::autostore::di { |
||||||
|
|
||||||
|
DiContainer::DiContainer() |
||||||
|
{ |
||||||
|
registerDependencies(); |
||||||
|
} |
||||||
|
|
||||||
|
void DiContainer::registerDependencies() |
||||||
|
{ |
||||||
|
// Register shared references
|
||||||
|
|
||||||
|
container.register_type<dingo::scope<dingo::shared>, |
||||||
|
dingo::storage<infrastructure::FileItemRepository>, |
||||||
|
dingo::interface<application::IItemRepository>>(); |
||||||
|
|
||||||
|
container.register_type<dingo::scope<dingo::shared>, |
||||||
|
dingo::storage<infrastructure::SystemClock>, |
||||||
|
dingo::interface<application::IClock>>(); |
||||||
|
|
||||||
|
container.register_type<dingo::scope<dingo::shared>, |
||||||
|
dingo::storage<infrastructure::HttpOrderService>, |
||||||
|
dingo::interface<application::IOrderService>>(); |
||||||
|
|
||||||
|
container.register_type<dingo::scope<dingo::shared>, |
||||||
|
dingo::storage<infrastructure::HttpServer>>(); |
||||||
|
|
||||||
|
container.register_indexed_type<dingo::scope<dingo::shared>, |
||||||
|
dingo::storage<application::AddItem>, |
||||||
|
dingo::interface<application::AddItem>>( |
||||||
|
std::string("AddItem")); |
||||||
|
|
||||||
|
// test:
|
||||||
|
auto uc = container.resolve<application::AddItem>( |
||||||
|
std::string("AddItem")); // throws on start
|
||||||
|
} |
||||||
|
} // namespace nxl::autostore::di
|
||||||
@ -0,0 +1,102 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <dingo/container.h> |
||||||
|
#include <dingo/factory/constructor.h> |
||||||
|
#include <dingo/storage/external.h> |
||||||
|
#include <dingo/storage/shared.h> |
||||||
|
#include <dingo/index/unordered_map.h> |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#include <string> |
||||||
|
#include <filesystem> |
||||||
|
#include <tuple> |
||||||
|
|
||||||
|
// 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<std::tuple<std::string, dingo::index_type::unordered_map>>; |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 <typename T> T resolve() { return container.resolve<T>(); } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Resolve a dependency by type as a shared pointer |
||||||
|
* |
||||||
|
* @tparam T Type to resolve |
||||||
|
* @return Shared pointer to the resolved type |
||||||
|
*/ |
||||||
|
template <typename T> std::shared_ptr<T> resolveShared() |
||||||
|
{ |
||||||
|
return container.resolve<std::shared_ptr<T>>(); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Resolve a dependency by type as a reference |
||||||
|
* |
||||||
|
* @tparam T Type to resolve |
||||||
|
* @return Reference to the resolved type |
||||||
|
*/ |
||||||
|
template <typename T> T& resolveRef() { return container.resolve<T&>(); } |
||||||
|
|
||||||
|
private: |
||||||
|
dingo::container<container_traits> container; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace nxl::autostore::di
|
||||||
@ -0,0 +1,10 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "domain/entities/Item.h" |
||||||
|
#include <functional> |
||||||
|
|
||||||
|
namespace nxl::autostore::application { |
||||||
|
|
||||||
|
using ItemPresenter = std::function<void(const domain::Item& item)>; |
||||||
|
|
||||||
|
} // namespace nxl::autostore::application
|
||||||
@ -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
|
||||||
@ -0,0 +1,20 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "nlohmann/json.hpp" |
||||||
|
#include <string> |
||||||
|
|
||||||
|
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
|
||||||
@ -0,0 +1,76 @@ |
|||||||
|
#include "infrastructure/helpers/JsonItem.h" |
||||||
|
#include <chrono> |
||||||
|
#include <ctime> |
||||||
|
#include <stdexcept> |
||||||
|
#include <type_traits> |
||||||
|
|
||||||
|
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
|
||||||
@ -0,0 +1,22 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "domain/entities/Item.h" |
||||||
|
#include "nlohmann/json.hpp" |
||||||
|
#include <string> |
||||||
|
|
||||||
|
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
|
||||||
@ -0,0 +1,36 @@ |
|||||||
|
#include "HttpOrderService.h" |
||||||
|
#include <stdexcept> |
||||||
|
#include <iostream> |
||||||
|
|
||||||
|
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
|
||||||
@ -0,0 +1,20 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "application/interfaces/IOrderService.h" |
||||||
|
#include "domain/entities/Item.h" |
||||||
|
#include <string> |
||||||
|
|
||||||
|
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
|
||||||
@ -1,23 +1,21 @@ |
|||||||
#pragma once |
#pragma once |
||||||
|
|
||||||
#include "application/commands/AddItem.h" |
#include "DiContainer.h" |
||||||
#include "domain/entities/Item.h" |
#include <httplib.h> // TODO: forward declaration |
||||||
#include <httplib.h> |
|
||||||
#include <nlohmann/json.hpp> |
|
||||||
|
|
||||||
namespace nxl::autostore::webapi { |
namespace nxl::autostore::webapi { |
||||||
|
|
||||||
class StoreController |
class StoreController |
||||||
{ |
{ |
||||||
public: |
public: |
||||||
StoreController(application::AddItem& addItemUseCase); |
StoreController(di::DiContainer& diContainer); |
||||||
|
|
||||||
void registerRoutes(httplib::Server& server); |
void registerRoutes(httplib::Server& server); |
||||||
|
|
||||||
private: |
private: |
||||||
void addItem(const httplib::Request& req, httplib::Response& res); |
void addItem(const httplib::Request& req, httplib::Response& res); |
||||||
|
|
||||||
application::AddItem addItemUseCase; |
di::DiContainer& diContainer; |
||||||
}; |
}; |
||||||
|
|
||||||
} // namespace nxl::autostore::webapi
|
} // namespace nxl::autostore::webapi
|
||||||
Loading…
Reference in new issue