diff --git a/CMakeLists.txt b/CMakeLists.txt index 957a73d..cfdd0dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,54 +1,30 @@ cmake_minimum_required(VERSION 3.15) -set(PROJECT_NAME "to-dos-api") -project(${PROJECT_NAME} CXX) - -file(GLOB_RECURSE sources CONFIGURE_DEPENDS - ${CMAKE_SOURCE_DIR}/src/*/*.cpp - ${CMAKE_SOURCE_DIR}/src/data/models/odb-gen/*.cxx - ${CMAKE_SOURCE_DIR}/src/utils/**/*.cpp -) - -set(PROJECT_OBJECTS ${PROJECT_NAME}_lib) - -add_library(${PROJECT_OBJECTS} OBJECT ${sources}) - -file(GLOB_RECURSE database_models - ${CMAKE_CURRENT_SOURCE_DIR}/src/data/models/*.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/data/models/*.hxx -) - -set(database_model_dirs "") -foreach(header ${database_models}) - get_filename_component(dir ${header} DIRECTORY) - list(APPEND database_model_dirs ${dir}) -endforeach() -list(REMOVE_DUPLICATES database_model_dirs) - -target_include_directories(${PROJECT_OBJECTS} PUBLIC - ${CMAKE_SOURCE_DIR}/src - /usr/include - ${database_model_dirs} -) +project(to-dos-api CXX) find_package(Drogon REQUIRED) find_package(jsoncpp REQUIRED) find_package(libodb REQUIRED) find_package(libodb-pgsql REQUIRED) -target_link_libraries(${PROJECT_OBJECTS} PUBLIC - Drogon::Drogon - JsonCpp::JsonCpp - libodb::libodb - libodb-pgsql::libodb-pgsql +set(API_LIB api) +set(APPLICATION_LIB application) +set(CORE_LIB core) + +add_subdirectory(src/core) +add_subdirectory(src/application) +add_subdirectory(src/api) + +add_executable(${PROJECT_NAME} + src/main.cpp + src/utils/app-config/app-config.cpp ) -add_executable(${PROJECT_NAME} - ${CMAKE_SOURCE_DIR}/src/main.cpp - $ +target_include_directories(${PROJECT_NAME} PRIVATE + src/utils ) -target_link_libraries(${PROJECT_NAME} PUBLIC - ${PROJECT_OBJECTS} +target_link_libraries(${PROJECT_NAME} + PRIVATE -Wl,--whole-archive ${API_LIB} -Wl,--no-whole-archive ) # Tests @@ -58,4 +34,4 @@ else() message(STATUS "UNIT TEST ENABLED") enable_testing() add_subdirectory(test) -endif() \ No newline at end of file +endif() diff --git a/src/data/alembic.ini b/alembic/alembic.ini similarity index 100% rename from src/data/alembic.ini rename to alembic/alembic.ini diff --git a/src/data/migrations/README b/alembic/migrations/README similarity index 100% rename from src/data/migrations/README rename to alembic/migrations/README diff --git a/src/data/migrations/env.py b/alembic/migrations/env.py similarity index 100% rename from src/data/migrations/env.py rename to alembic/migrations/env.py diff --git a/src/data/migrations/script.py.mako b/alembic/migrations/script.py.mako similarity index 100% rename from src/data/migrations/script.py.mako rename to alembic/migrations/script.py.mako diff --git a/src/data/migrations/versions/4305af8cc1e9_initial_migration.py b/alembic/migrations/versions/4305af8cc1e9_initial_migration.py similarity index 100% rename from src/data/migrations/versions/4305af8cc1e9_initial_migration.py rename to alembic/migrations/versions/4305af8cc1e9_initial_migration.py diff --git a/src/data/models/to_do.py b/alembic/models/to_do.py similarity index 100% rename from src/data/models/to_do.py rename to alembic/models/to_do.py diff --git a/src/api/CMakeLists.txt b/src/api/CMakeLists.txt new file mode 100644 index 0000000..694ad75 --- /dev/null +++ b/src/api/CMakeLists.txt @@ -0,0 +1,14 @@ +file(GLOB_RECURSE api_sources + ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp +) + +add_library(${API_LIB} STATIC ${api_sources}) + +target_include_directories(${API_LIB} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_link_libraries(${API_LIB} + PUBLIC ${APPLICATION_LIB} + Drogon::Drogon +) \ No newline at end of file diff --git a/src/controllers/app-controller.cpp b/src/api/features/todos/todos-controller.cpp similarity index 52% rename from src/controllers/app-controller.cpp rename to src/api/features/todos/todos-controller.cpp index b926954..c326b25 100644 --- a/src/controllers/app-controller.cpp +++ b/src/api/features/todos/todos-controller.cpp @@ -1,8 +1,8 @@ -#include "app-controller.h" -#include "data/db_connection.h" +#include "todos-controller.h" +#include "db_connection.h" // TODO(https://github.com/TourmalineCore/to-dos-api-cpp/issues/25): Create http exception handler or generic class for handling that type of errors -HttpResponsePtr AppController::createInternalServerErrorResponse(const std::string& error) const +HttpResponsePtr ToDosController::createInternalServerErrorResponse(const std::string& error) const { Json::Value jsonResponse; jsonResponse["status"] = "Error"; @@ -14,21 +14,37 @@ HttpResponsePtr AppController::createInternalServerErrorResponse(const std::stri return resp; } -AppController::AppController() +ToDosController::ToDosController() { db_ = std::move(DbConnection::get()); - queries_ = std::make_unique(*db_); - commands_ = std::make_unique(*db_); - todo_service_ = std::make_unique(*queries_, *commands_); + + createToDoCommand_ = std::make_unique(*db_); + getAllToDosQuery_ = std::make_unique(*db_); + getToDoByIdQuery_ = std::make_unique(*db_); + hardDeleteToDoCommand_ = std::make_unique(*db_); + softDeleteCommand_ = std::make_unique(*db_); + + createToDoHandler_ = std::make_unique(*createToDoCommand_); + getAllToDosHandler_ = std::make_unique(*getAllToDosQuery_); + getToDoByIdHandler_ = std::make_unique(*getToDoByIdQuery_); + hardDeleteToDoHandler_ = std::make_unique(*hardDeleteToDoCommand_); + softDeleteToDoHandler_ = std::make_unique(*softDeleteCommand_); } -void AppController::getToDos(const HttpRequestPtr& req, std::function&& callback) +void ToDosController::getToDos(const HttpRequestPtr& req, std::function&& callback) { - Json::Value jsonResponse; - try { - auto resp = HttpResponse::newHttpJsonResponse(todo_service_->getToDos()); + auto result = getAllToDosHandler_->handle(); + + Json::Value toDosArray(Json::arrayValue); + for (const auto& dto : result.todos) + toDosArray.append(dto.toJson()); + + Json::Value json; + json["toDos"] = toDosArray; + + auto resp = HttpResponse::newHttpJsonResponse(json); resp->setStatusCode(k200OK); callback(resp); } @@ -38,10 +54,8 @@ void AppController::getToDos(const HttpRequestPtr& req, std::function&& callback) +void ToDosController::addToDo(const HttpRequestPtr& req, std::function&& callback) { - Json::Value jsonResponse; - try { auto json = req->getJsonObject(); @@ -55,11 +69,11 @@ void AppController::addToDo(const HttpRequestPtr& req, std::functionsetStatusCode(k400BadRequest); callback(resp); - return; } - todo_service_->addToDo(json->get("name", "").asString()); + CreateToDoRequest request { json->get("name", "").asString() }; + (void) createToDoHandler_->handle(request); auto resp = HttpResponse::newHttpResponse(); resp->setStatusCode(k201Created); @@ -71,10 +85,8 @@ void AppController::addToDo(const HttpRequestPtr& req, std::function&& callback) +void ToDosController::completeToDos(const HttpRequestPtr& req, std::function&& callback) { - Json::Value jsonResponse; - try { auto json = req->getJsonObject(); @@ -88,15 +100,14 @@ void AppController::completeToDos(const HttpRequestPtr& req, std::functionsetStatusCode(k400BadRequest); callback(resp); - return; } auto toDosIds = json->get("toDoIds", Json::Value(Json::arrayValue)); - for (const auto& id : toDosIds) { - todo_service_->completeToDo(id.asInt()); + // TODO(https://github.com/TourmalineCore/to-dos-api-cpp/issues/38): add here a check for not found todos + (void) softDeleteToDoHandler_->handle(static_cast(id.asInt())); } auto resp = HttpResponse::newHttpResponse(); @@ -109,16 +120,15 @@ void AppController::completeToDos(const HttpRequestPtr& req, std::function&& callback) +void ToDosController::deleteToDo(const HttpRequestPtr& req, std::function&& callback) { - Json::Value jsonResponse; - try { auto toDoIdStr = req->getParameter("toDoId"); - int toDoId = std::stoi(toDoIdStr); + const uint64_t toDoId = static_cast(std::stoi(toDoIdStr)); - todo_service_->deleteToDo(toDoId); + // TODO(https://github.com/TourmalineCore/to-dos-api-cpp/issues/38): add here a check for not found todo + (void) hardDeleteToDoHandler_->handle(toDoId); auto resp = HttpResponse::newHttpResponse(); resp->setStatusCode(k200OK); @@ -128,4 +138,4 @@ void AppController::deleteToDo(const HttpRequestPtr& req, std::function + +#include "features/create-to-do/create-to-do-handler.h" +#include "features/get-all-to-dos/get-all-to-dos-handler.h" +#include "features/get-to-do-by-id/get-to-do-by-id-handler.h" +#include "features/hard-delete-to-do/hard-delete-to-do-handler.h" +#include "features/soft-delete-to-do/soft-delete-to-do-handler.h" + +using namespace drogon; + +class ToDosController : public drogon::HttpController +{ +public: + explicit ToDosController(); + + METHOD_LIST_BEGIN + ADD_METHOD_TO(ToDosController::getToDos, "/api/to-dos", Get); // Getting a list of tasks + ADD_METHOD_TO(ToDosController::addToDo, "/api/to-dos", Post); // Adding a new task + ADD_METHOD_TO(ToDosController::completeToDos, "/api/to-dos/complete", Post); // Executing (deleting) a task list + ADD_METHOD_TO(ToDosController::deleteToDo, "/api/to-dos", Delete); // Deleting a specific task + METHOD_LIST_END + + HttpResponsePtr createInternalServerErrorResponse(const std::string& error) const; + + void getToDos(const HttpRequestPtr& req, std::function&& callback); + + void addToDo(const HttpRequestPtr& req, std::function&& callback); + + void completeToDos(const HttpRequestPtr& req, std::function&& callback); + + void deleteToDo(const HttpRequestPtr& req, std::function&& callback); + +private: + std::shared_ptr db_; + + std::unique_ptr createToDoCommand_; + std::unique_ptr getAllToDosQuery_; + std::unique_ptr getToDoByIdQuery_; + std::unique_ptr hardDeleteToDoCommand_; + std::unique_ptr softDeleteCommand_; + + std::unique_ptr createToDoHandler_; + std::unique_ptr getAllToDosHandler_; + std::unique_ptr getToDoByIdHandler_; + std::unique_ptr hardDeleteToDoHandler_; + std::unique_ptr softDeleteToDoHandler_; +}; \ No newline at end of file diff --git a/src/application/CMakeLists.txt b/src/application/CMakeLists.txt new file mode 100644 index 0000000..7bc1f12 --- /dev/null +++ b/src/application/CMakeLists.txt @@ -0,0 +1,19 @@ +file(GLOB_RECURSE application_sources + ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/odb-gen/*.cxx +) + +add_library(${APPLICATION_LIB} STATIC ${application_sources}) + +target_include_directories(${APPLICATION_LIB} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/odb-gen +) + +target_link_libraries(${APPLICATION_LIB} + PUBLIC ${CORE_LIB} + JsonCpp::JsonCpp + PRIVATE libodb::libodb + libodb-pgsql::libodb-pgsql + Drogon::Drogon +) \ No newline at end of file diff --git a/src/data/db_connection.cpp b/src/application/db_connection.cpp similarity index 100% rename from src/data/db_connection.cpp rename to src/application/db_connection.cpp diff --git a/src/data/db_connection.h b/src/application/db_connection.h similarity index 100% rename from src/data/db_connection.h rename to src/application/db_connection.h diff --git a/src/application/features/create-to-do/create-to-do-command.cpp b/src/application/features/create-to-do/create-to-do-command.cpp new file mode 100644 index 0000000..20137c3 --- /dev/null +++ b/src/application/features/create-to-do/create-to-do-command.cpp @@ -0,0 +1,14 @@ +#include "create-to-do-command.h" +#include "odb-gen/to-do-odb.hxx" +#include + +uint64_t CreateToDoCommand::execute(const std::string& name, std::time_t createdAtUtc) +{ + ToDo todo(name, createdAtUtc); + + odb::transaction t(db_.begin()); + db_.persist(todo); + t.commit(); + + return todo.id(); +} \ No newline at end of file diff --git a/src/application/features/create-to-do/create-to-do-command.h b/src/application/features/create-to-do/create-to-do-command.h new file mode 100644 index 0000000..2cbef86 --- /dev/null +++ b/src/application/features/create-to-do/create-to-do-command.h @@ -0,0 +1,18 @@ +#pragma once + +#include "to-do.h" +#include +#include + +class CreateToDoCommand +{ +public: + CreateToDoCommand(odb::database& db) + : db_(db) + {} + + uint64_t execute(const std::string& name, std::time_t createdAtUtc); + +private: + odb::database& db_; +}; \ No newline at end of file diff --git a/src/application/features/create-to-do/create-to-do-handler.cpp b/src/application/features/create-to-do/create-to-do-handler.cpp new file mode 100644 index 0000000..cce9291 --- /dev/null +++ b/src/application/features/create-to-do/create-to-do-handler.cpp @@ -0,0 +1,13 @@ +#include "create-to-do-handler.h" +#include +#include + +using std::string; + +CreateToDoResponse CreateToDoHandler::handle(const CreateToDoRequest& request) +{ + const std::time_t now_utc = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + + const uint64_t id = _createToDoCommand.execute(request.name, now_utc); + return { id }; +} \ No newline at end of file diff --git a/src/application/features/create-to-do/create-to-do-handler.h b/src/application/features/create-to-do/create-to-do-handler.h new file mode 100644 index 0000000..0bd5a3f --- /dev/null +++ b/src/application/features/create-to-do/create-to-do-handler.h @@ -0,0 +1,17 @@ +#pragma once +#include "create-to-do-command.h" +#include "create-to-do-request.h" +#include "create-to-do-response.h" + +class CreateToDoHandler +{ +private: + CreateToDoCommand& _createToDoCommand; + +public: + explicit CreateToDoHandler(CreateToDoCommand& createToDoCommand) + : _createToDoCommand(createToDoCommand) + {} + + CreateToDoResponse handle(const CreateToDoRequest& request); +}; \ No newline at end of file diff --git a/src/application/features/create-to-do/create-to-do-request.h b/src/application/features/create-to-do/create-to-do-request.h new file mode 100644 index 0000000..21aec14 --- /dev/null +++ b/src/application/features/create-to-do/create-to-do-request.h @@ -0,0 +1,7 @@ +#pragma once +#include + +struct CreateToDoRequest +{ + std::string name; +}; \ No newline at end of file diff --git a/src/application/features/create-to-do/create-to-do-response.h b/src/application/features/create-to-do/create-to-do-response.h new file mode 100644 index 0000000..75078b6 --- /dev/null +++ b/src/application/features/create-to-do/create-to-do-response.h @@ -0,0 +1,7 @@ +#pragma once +#include + +struct CreateToDoResponse +{ + uint64_t id; +}; \ No newline at end of file diff --git a/src/application/features/get-all-to-dos/get-all-to-dos-handler.cpp b/src/application/features/get-all-to-dos/get-all-to-dos-handler.cpp new file mode 100644 index 0000000..ab2bed2 --- /dev/null +++ b/src/application/features/get-all-to-dos/get-all-to-dos-handler.cpp @@ -0,0 +1,26 @@ +#include "get-all-to-dos-handler.h" + +using std::vector; + +static vector mapToDTOs(const std::shared_ptr>& todos) +{ + vector out; + out.reserve(todos ? todos->size() : 0); + if (todos) + { + for (const auto& t : *todos) + { + ToDoDTO dto; + dto.id = static_cast(t.id()); + dto.name = t.name(); + out.push_back(std::move(dto)); + } + } + return out; +} + +GetAllToDosResponse GetAllToDosHandler::handle() +{ + auto todos = _getAllToDosQuery.get(); + return { mapToDTOs(todos) }; +} \ No newline at end of file diff --git a/src/application/features/get-all-to-dos/get-all-to-dos-handler.h b/src/application/features/get-all-to-dos/get-all-to-dos-handler.h new file mode 100644 index 0000000..cb89db3 --- /dev/null +++ b/src/application/features/get-all-to-dos/get-all-to-dos-handler.h @@ -0,0 +1,16 @@ +#pragma once +#include "get-all-to-dos-query.h" +#include "get-all-to-dos-response.h" + +class GetAllToDosHandler +{ +private: + GetAllToDosQuery& _getAllToDosQuery; + +public: + explicit GetAllToDosHandler(GetAllToDosQuery& getAllToDosQuery) + : _getAllToDosQuery(getAllToDosQuery) + {} + + GetAllToDosResponse handle(); +}; \ No newline at end of file diff --git a/src/application/features/get-all-to-dos/get-all-to-dos-query.cpp b/src/application/features/get-all-to-dos/get-all-to-dos-query.cpp new file mode 100644 index 0000000..4dffc3c --- /dev/null +++ b/src/application/features/get-all-to-dos/get-all-to-dos-query.cpp @@ -0,0 +1,17 @@ +#include "get-all-to-dos-query.h" +#include "odb-gen/to-do-odb.hxx" +#include + +std::shared_ptr> GetAllToDosQuery::get() +{ + odb::transaction t(db_.begin()); + + odb::result r = db_.query(odb::query()); + + auto todos = std::make_shared>(); + for (auto i = r.begin(); i != r.end(); ++i) + todos->push_back(*i); + + t.commit(); + return todos; +} \ No newline at end of file diff --git a/src/application/features/get-all-to-dos/get-all-to-dos-query.h b/src/application/features/get-all-to-dos/get-all-to-dos-query.h new file mode 100644 index 0000000..f4a0faa --- /dev/null +++ b/src/application/features/get-all-to-dos/get-all-to-dos-query.h @@ -0,0 +1,18 @@ +#pragma once + +#include "to-do.h" +#include +#include +#include + +class GetAllToDosQuery +{ +public: + GetAllToDosQuery(odb::database& db) + : db_(db) + {} + std::shared_ptr> get(); + +private: + odb::database& db_; +}; \ No newline at end of file diff --git a/src/application/features/get-all-to-dos/get-all-to-dos-response.h b/src/application/features/get-all-to-dos/get-all-to-dos-response.h new file mode 100644 index 0000000..db264fa --- /dev/null +++ b/src/application/features/get-all-to-dos/get-all-to-dos-response.h @@ -0,0 +1,8 @@ +#pragma once +#include "../../shared-dtos/to-do-dtos.h" +#include + +struct GetAllToDosResponse +{ + std::vector todos; +}; \ No newline at end of file diff --git a/src/application/features/get-to-do-by-id/get-to-do-by-id-handler.cpp b/src/application/features/get-to-do-by-id/get-to-do-by-id-handler.cpp new file mode 100644 index 0000000..d4fbe21 --- /dev/null +++ b/src/application/features/get-to-do-by-id/get-to-do-by-id-handler.cpp @@ -0,0 +1,19 @@ +#include "get-to-do-by-id-handler.h" + +static ToDoDTO mapToDTO(const ToDo& todo) +{ + ToDoDTO dto; + dto.id = static_cast(todo.id()); + dto.name = todo.name(); + return dto; +} + +GetToDoByIdResponse GetToDoByIdHandler::handle(uint64_t id) +{ + auto todo = _getToDoByIdQuery.get(static_cast(id)); + + if (!todo) + return { std::nullopt }; + + return { mapToDTO(*todo) }; +} \ No newline at end of file diff --git a/src/application/features/get-to-do-by-id/get-to-do-by-id-handler.h b/src/application/features/get-to-do-by-id/get-to-do-by-id-handler.h new file mode 100644 index 0000000..2ae3994 --- /dev/null +++ b/src/application/features/get-to-do-by-id/get-to-do-by-id-handler.h @@ -0,0 +1,17 @@ +#pragma once +#include "get-to-do-by-id-query.h" +#include "get-to-do-by-id-response.h" +#include + +class GetToDoByIdHandler +{ +private: + GetToDoById& _getToDoByIdQuery; + +public: + explicit GetToDoByIdHandler(GetToDoById& getToDoByIdQuery) + : _getToDoByIdQuery(getToDoByIdQuery) + {} + + GetToDoByIdResponse handle(uint64_t id); +}; \ No newline at end of file diff --git a/src/application/features/get-to-do-by-id/get-to-do-by-id-query.cpp b/src/application/features/get-to-do-by-id/get-to-do-by-id-query.cpp new file mode 100644 index 0000000..c3b2582 --- /dev/null +++ b/src/application/features/get-to-do-by-id/get-to-do-by-id-query.cpp @@ -0,0 +1,18 @@ +#include "get-to-do-by-id-query.h" +#include "odb-gen/to-do-odb.hxx" +#include +#include + +std::shared_ptr GetToDoById::get(int id) +{ + odb::transaction t(db_.begin()); + + odb::result r = db_.query(odb::query::id == id); + + std::shared_ptr todo; + if (!r.empty()) + todo = std::make_shared(*r.begin()); + + t.commit(); + return todo; +} \ No newline at end of file diff --git a/src/application/features/get-to-do-by-id/get-to-do-by-id-query.h b/src/application/features/get-to-do-by-id/get-to-do-by-id-query.h new file mode 100644 index 0000000..000e436 --- /dev/null +++ b/src/application/features/get-to-do-by-id/get-to-do-by-id-query.h @@ -0,0 +1,18 @@ +#pragma once + +#include "to-do.h" +#include +#include +#include + +class GetToDoById +{ +public: + GetToDoById(odb::database& db) + : db_(db) + {} + std::shared_ptr get(int id); + +private: + odb::database& db_; +}; \ No newline at end of file diff --git a/src/application/features/get-to-do-by-id/get-to-do-by-id-response.h b/src/application/features/get-to-do-by-id/get-to-do-by-id-response.h new file mode 100644 index 0000000..753d0da --- /dev/null +++ b/src/application/features/get-to-do-by-id/get-to-do-by-id-response.h @@ -0,0 +1,8 @@ +#pragma once +#include "../../shared-dtos/to-do-dtos.h" +#include + +struct GetToDoByIdResponse +{ + std::optional todo; +}; \ No newline at end of file diff --git a/src/application/features/hard-delete-to-do/hard-delete-to-do-command.cpp b/src/application/features/hard-delete-to-do/hard-delete-to-do-command.cpp new file mode 100644 index 0000000..baa7c44 --- /dev/null +++ b/src/application/features/hard-delete-to-do/hard-delete-to-do-command.cpp @@ -0,0 +1,13 @@ +#include "hard-delete-to-do-command.h" +#include "odb-gen/to-do-odb.hxx" +#include + +uint64_t HardDeleteToDoCommand::execute(int id) +{ + odb::transaction t(db_.begin()); + const unsigned long long todo_id = db_.erase_query(odb::query::id == id); + t.commit(); + + // TODO: return exactly deleted todo id + return id; +} \ No newline at end of file diff --git a/src/application/features/hard-delete-to-do/hard-delete-to-do-command.h b/src/application/features/hard-delete-to-do/hard-delete-to-do-command.h new file mode 100644 index 0000000..ab08201 --- /dev/null +++ b/src/application/features/hard-delete-to-do/hard-delete-to-do-command.h @@ -0,0 +1,18 @@ +#pragma once + +#include "to-do.h" +#include +#include + +class HardDeleteToDoCommand +{ +public: + HardDeleteToDoCommand(odb::database& db) + : db_(db) + {} + + uint64_t execute(int id); + +private: + odb::database& db_; +}; \ No newline at end of file diff --git a/src/application/features/hard-delete-to-do/hard-delete-to-do-handler.cpp b/src/application/features/hard-delete-to-do/hard-delete-to-do-handler.cpp new file mode 100644 index 0000000..4caaf88 --- /dev/null +++ b/src/application/features/hard-delete-to-do/hard-delete-to-do-handler.cpp @@ -0,0 +1,8 @@ +#include "hard-delete-to-do-handler.h" + +// TODO(https://github.com/TourmalineCore/to-dos-api-cpp/issues/38): add here a check for not found todo +bool HardDeleteToDoHandler::handle(uint64_t id) +{ + (void) _hardDeleteToDoCommand.execute(static_cast(id)); + return true; +} \ No newline at end of file diff --git a/src/application/features/hard-delete-to-do/hard-delete-to-do-handler.h b/src/application/features/hard-delete-to-do/hard-delete-to-do-handler.h new file mode 100644 index 0000000..af94b9d --- /dev/null +++ b/src/application/features/hard-delete-to-do/hard-delete-to-do-handler.h @@ -0,0 +1,16 @@ +#pragma once +#include "hard-delete-to-do-command.h" +#include + +class HardDeleteToDoHandler +{ +private: + HardDeleteToDoCommand& _hardDeleteToDoCommand; + +public: + explicit HardDeleteToDoHandler(HardDeleteToDoCommand& hardDeleteToDoCommand) + : _hardDeleteToDoCommand(hardDeleteToDoCommand) + {} + + bool handle(uint64_t id); +}; \ No newline at end of file diff --git a/src/application/features/soft-delete-to-do/soft-delete-to-do-command.cpp b/src/application/features/soft-delete-to-do/soft-delete-to-do-command.cpp new file mode 100644 index 0000000..2404de4 --- /dev/null +++ b/src/application/features/soft-delete-to-do/soft-delete-to-do-command.cpp @@ -0,0 +1,18 @@ +#include "soft-delete-to-do-command.h" +#include "odb-gen/to-do-odb.hxx" +#include +#include +#include + +uint64_t SoftDeleteCommand::execute(int id) +{ + odb::transaction t(db_.begin()); + + std::unique_ptr todo(db_.load(id)); + const std::time_t now_utc = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + todo->deletedAtUtc(now_utc); + db_.update(*todo); + + t.commit(); + return todo->id(); +} \ No newline at end of file diff --git a/src/application/features/soft-delete-to-do/soft-delete-to-do-command.h b/src/application/features/soft-delete-to-do/soft-delete-to-do-command.h new file mode 100644 index 0000000..af572b5 --- /dev/null +++ b/src/application/features/soft-delete-to-do/soft-delete-to-do-command.h @@ -0,0 +1,18 @@ +#pragma once + +#include "to-do.h" +#include +#include + +class SoftDeleteCommand +{ +public: + SoftDeleteCommand(odb::database& db) + : db_(db) + {} + + uint64_t execute(int id); + +private: + odb::database& db_; +}; \ No newline at end of file diff --git a/src/application/features/soft-delete-to-do/soft-delete-to-do-handler.cpp b/src/application/features/soft-delete-to-do/soft-delete-to-do-handler.cpp new file mode 100644 index 0000000..56c9d6a --- /dev/null +++ b/src/application/features/soft-delete-to-do/soft-delete-to-do-handler.cpp @@ -0,0 +1,8 @@ +#include "soft-delete-to-do-handler.h" + +// TODO(https://github.com/TourmalineCore/to-dos-api-cpp/issues/38): add here a check for not found todo +SoftDeleteToDoResponse SoftDeleteToDoHandler::handle(uint64_t id) +{ + const uint64_t deletedId = _softDeleteToDoCommand.execute(static_cast(id)); + return { deletedId }; +} \ No newline at end of file diff --git a/src/application/features/soft-delete-to-do/soft-delete-to-do-handler.h b/src/application/features/soft-delete-to-do/soft-delete-to-do-handler.h new file mode 100644 index 0000000..c64a096 --- /dev/null +++ b/src/application/features/soft-delete-to-do/soft-delete-to-do-handler.h @@ -0,0 +1,17 @@ +#pragma once +#include "soft-delete-to-do-command.h" +#include "soft-delete-to-do-response.h" +#include + +class SoftDeleteToDoHandler +{ +private: + SoftDeleteCommand& _softDeleteToDoCommand; + +public: + explicit SoftDeleteToDoHandler(SoftDeleteCommand& softDeleteToDoCommand) + : _softDeleteToDoCommand(softDeleteToDoCommand) + {} + + SoftDeleteToDoResponse handle(uint64_t id); +}; \ No newline at end of file diff --git a/src/application/features/soft-delete-to-do/soft-delete-to-do-response.h b/src/application/features/soft-delete-to-do/soft-delete-to-do-response.h new file mode 100644 index 0000000..a76940b --- /dev/null +++ b/src/application/features/soft-delete-to-do/soft-delete-to-do-response.h @@ -0,0 +1,7 @@ +#pragma once +#include + +struct SoftDeleteToDoResponse +{ + uint64_t id; +}; \ No newline at end of file diff --git a/src/data/models/odb-gen/to-do-odb.cxx b/src/application/odb-gen/to-do-odb.cxx similarity index 100% rename from src/data/models/odb-gen/to-do-odb.cxx rename to src/application/odb-gen/to-do-odb.cxx diff --git a/src/data/models/odb-gen/to-do-odb.hxx b/src/application/odb-gen/to-do-odb.hxx similarity index 100% rename from src/data/models/odb-gen/to-do-odb.hxx rename to src/application/odb-gen/to-do-odb.hxx diff --git a/src/data/models/odb-gen/to-do-odb.ixx b/src/application/odb-gen/to-do-odb.ixx similarity index 100% rename from src/data/models/odb-gen/to-do-odb.ixx rename to src/application/odb-gen/to-do-odb.ixx diff --git a/src/services/dtos/to-dos-dto.cpp b/src/application/shared-dtos/to-do-dtos.cpp similarity index 83% rename from src/services/dtos/to-dos-dto.cpp rename to src/application/shared-dtos/to-do-dtos.cpp index 3c1b7e3..d1384ac 100644 --- a/src/services/dtos/to-dos-dto.cpp +++ b/src/application/shared-dtos/to-do-dtos.cpp @@ -1,10 +1,9 @@ -#include "to-dos-dto.h" +#include "to-do-dtos.h" Json::Value ToDoDTO::toJson() const { Json::Value json; json["id"] = id; json["name"] = name; - return json; } \ No newline at end of file diff --git a/src/services/dtos/to-dos-dto.h b/src/application/shared-dtos/to-do-dtos.h similarity index 99% rename from src/services/dtos/to-dos-dto.h rename to src/application/shared-dtos/to-do-dtos.h index b8c3837..b56c07e 100644 --- a/src/services/dtos/to-dos-dto.h +++ b/src/application/shared-dtos/to-do-dtos.h @@ -1,5 +1,4 @@ #pragma once - #include #include diff --git a/src/controllers/app-controller.h b/src/controllers/app-controller.h deleted file mode 100644 index c9dc4a0..0000000 --- a/src/controllers/app-controller.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include - -#include "data/commands/todo-commands.h" -#include "data/queries/todo-queries.h" -#include "services/to-dos.service.h" - -using namespace drogon; - -class AppController : public drogon::HttpController -{ -public: - explicit AppController(); - - METHOD_LIST_BEGIN - ADD_METHOD_TO(AppController::getToDos, "/api/to-dos", Get); // Getting a list of tasks - ADD_METHOD_TO(AppController::addToDo, "/api/to-dos", Post); // Adding a new task - ADD_METHOD_TO(AppController::completeToDos, "/api/to-dos/complete", Post); // Executing (deleting) a task list - ADD_METHOD_TO(AppController::deleteToDo, "/api/to-dos", Delete); // Deleting a specific task - METHOD_LIST_END - - HttpResponsePtr createInternalServerErrorResponse(const std::string& error) const; - - void getToDos(const HttpRequestPtr& req, std::function&& callback); - - void addToDo(const HttpRequestPtr& req, std::function&& callback); - - void completeToDos(const HttpRequestPtr& req, std::function&& callback); - - void deleteToDo(const HttpRequestPtr& req, std::function&& callback); - -private: - std::shared_ptr db_; - std::unique_ptr queries_; - std::unique_ptr commands_; - std::unique_ptr todo_service_; -}; \ No newline at end of file diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt new file mode 100644 index 0000000..cba6b5c --- /dev/null +++ b/src/core/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(${CORE_LIB} INTERFACE) +target_include_directories(${CORE_LIB} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +target_link_libraries(${CORE_LIB} + INTERFACE libodb::libodb +) \ No newline at end of file diff --git a/src/data/models/to-do.h b/src/core/to-do.h similarity index 100% rename from src/data/models/to-do.h rename to src/core/to-do.h diff --git a/src/data/commands/todo-commands.cpp b/src/data/commands/todo-commands.cpp deleted file mode 100644 index edf2423..0000000 --- a/src/data/commands/todo-commands.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "todo-commands.h" -#include "data/models/odb-gen/to-do-odb.hxx" -#include -#include - -uint64_t ToDoCommands::create_todo(const std::string& name, std::time_t createdAtUtc) -{ - ToDo todo(name, createdAtUtc); - - odb::transaction t(db_.begin()); - db_.persist(todo); - t.commit(); - - return todo.id(); -} - -uint64_t ToDoCommands::delete_todo(int id) -{ - odb::transaction t(db_.begin()); - - const unsigned long long todo_id = db_.erase_query(odb::query::id == id); - - t.commit(); - // TODO: return exactly deleted todo id - return id; -} - -uint64_t ToDoCommands::soft_delete_todo(int id) -{ - odb::transaction t(db_.begin()); - - std::unique_ptr todo(db_.load(id)); - - const std::time_t now_utc = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); - - todo->deletedAtUtc(now_utc); - db_.update(*todo); - - t.commit(); - return todo->id(); -} diff --git a/src/data/commands/todo-commands.h b/src/data/commands/todo-commands.h deleted file mode 100644 index 1953476..0000000 --- a/src/data/commands/todo-commands.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "data/models/to-do.h" -#include -#include - -class ToDoCommands -{ -public: - ToDoCommands(odb::database& db) - : db_(db) - {} - - uint64_t create_todo(const std::string& name, std::time_t createdAtUtc); - uint64_t delete_todo(int id); - uint64_t soft_delete_todo(int id); - -private: - odb::database& db_; -}; \ No newline at end of file diff --git a/src/data/queries/todo-queries.cpp b/src/data/queries/todo-queries.cpp deleted file mode 100644 index aecb7ed..0000000 --- a/src/data/queries/todo-queries.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "todo-queries.h" -#include "data/models/odb-gen/to-do-odb.hxx" -#include -#include - -std::shared_ptr> ToDoQueries::get_all_todos() -{ - odb::transaction t(db_.begin()); - - odb::result r = db_.query(odb::query()); - - auto todos = std::make_shared>(); - for (auto i = r.begin(); i != r.end(); ++i) - todos->push_back(*i); - - t.commit(); - return todos; -} - -std::shared_ptr ToDoQueries::get_todo_by_id(int id) -{ - odb::transaction t(db_.begin()); - - odb::result r = db_.query(odb::query::id == id); - - std::shared_ptr todo; - if (!r.empty()) - todo = std::make_shared(*r.begin()); - - t.commit(); - return todo; -} diff --git a/src/data/queries/todo-queries.h b/src/data/queries/todo-queries.h deleted file mode 100644 index 504e571..0000000 --- a/src/data/queries/todo-queries.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "data/models/to-do.h" -#include -#include -#include - -class ToDoQueries -{ -public: - ToDoQueries(odb::database& db) - : db_(db) - {} - std::shared_ptr> get_all_todos(); - std::shared_ptr get_todo_by_id(int id); - -private: - odb::database& db_; -}; \ No newline at end of file diff --git a/src/services/to-dos.service.cpp b/src/services/to-dos.service.cpp deleted file mode 100644 index fb292ec..0000000 --- a/src/services/to-dos.service.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "to-dos.service.h" -#include -#include - -using std::string; -using std::vector; - -static vector mapToDTOs(const std::shared_ptr>& todos) -{ - vector out; - out.reserve(todos ? todos->size() : 0); - if (todos) - { - for (const auto& t : *todos) - { - ToDoDTO dto; - dto.id = static_cast(t.id()); - dto.name = t.name(); - out.push_back(std::move(dto)); - } - } - return out; -} - -void ToDoService::addToDo(const string name) -{ - const std::time_t now_utc = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); - (void) _commands.create_todo(name, now_utc); -} - -// TODO(https://github.com/TourmalineCore/to-dos-api-cpp/issues/38): add here a check for not found todos -bool ToDoService::completeToDo(int id) -{ - (void) _commands.soft_delete_todo(id); - return true; -} - -// TODO(https://github.com/TourmalineCore/to-dos-api-cpp/issues/38): add here a check for not found todos -bool ToDoService::deleteToDo(int id) -{ - (void) _commands.delete_todo(id); - return true; -} - -const Json::Value ToDoService::getToDos() const -{ - Json::Value json; - Json::Value toDosArray(Json::arrayValue); - - auto todos = _queries.get_all_todos(); - auto dtos = mapToDTOs(todos); - for (const auto& dto : dtos) - { - toDosArray.append(dto.toJson()); - } - json["toDos"] = toDosArray; - return json; -} \ No newline at end of file diff --git a/src/services/to-dos.service.h b/src/services/to-dos.service.h deleted file mode 100644 index 1f836e0..0000000 --- a/src/services/to-dos.service.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "data/commands/todo-commands.h" -#include "data/queries/todo-queries.h" -#include "dtos/to-dos-dto.h" - -#include -#include -#include - -class ToDoService -{ -private: - ToDoQueries& _queries; - ToDoCommands& _commands; - -public: - explicit ToDoService(ToDoQueries& queries, ToDoCommands& commands) - : _queries(queries), - _commands(commands) - {} - - void addToDo(const std::string description); - bool completeToDo(int id); - bool deleteToDo(int id); - const Json::Value getToDos() const; -}; \ No newline at end of file diff --git a/src/utils/app-config/app-config.cpp b/src/utils/app-config/app-config.cpp index f8c48b0..3699684 100644 --- a/src/utils/app-config/app-config.cpp +++ b/src/utils/app-config/app-config.cpp @@ -134,28 +134,46 @@ void AppConfig::setDatabasePassword(std::string databasePassword) }; const std::string& AppConfig::getApiHost() const -{ return apiHost_; } +{ + return apiHost_; +} const uint32_t& AppConfig::getApiPort() const -{ return apiPort_; } +{ + return apiPort_; +} const uint32_t& AppConfig::getApiNumThreads() const -{ return apiNumThreads_; } +{ + return apiNumThreads_; +} const trantor::Logger::LogLevel AppConfig::getApiLogLevel() -{ return parseLogLevel(getEnv("API_LOG_LEVEL", "INFO")); } +{ + return parseLogLevel(getEnv("API_LOG_LEVEL", "INFO")); +} const std::string& AppConfig::getDatabaseHost() const -{ return databaseHost_; } +{ + return databaseHost_; +} const std::string& AppConfig::getDatabasePort() const -{ return databasePort_; } +{ + return databasePort_; +} const std::string& AppConfig::getDatabaseName() const -{ return databaseName_; } +{ + return databaseName_; +} const std::string& AppConfig::getDatabaseUser() const -{ return databaseUser_; } +{ + return databaseUser_; +} const std::string& AppConfig::getDatabasePassword() const -{ return databasePassword_; } \ No newline at end of file +{ + return databasePassword_; +} \ No newline at end of file diff --git a/src/utils/app-config/app-config.h b/src/utils/app-config/app-config.h index 86fa4b3..d54d4c7 100644 --- a/src/utils/app-config/app-config.h +++ b/src/utils/app-config/app-config.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include diff --git a/test/addition-operation.cpp b/test/addition-operation.cpp index 50489ed..05ba360 100644 --- a/test/addition-operation.cpp +++ b/test/addition-operation.cpp @@ -1,6 +1,5 @@ +// WARN: This included as example. In future please place unit tests in folder where the tested functionality is located. #include "addition-operation.h" int AdditionOperation(int a, int b) -{ - return a + b; -} \ No newline at end of file +{ return a + b; } \ No newline at end of file diff --git a/test/addition-operation.h b/test/addition-operation.h index 844dd78..cfff120 100644 --- a/test/addition-operation.h +++ b/test/addition-operation.h @@ -1,3 +1,4 @@ +// WARN: This included as example. In future please place unit tests in folder where the tested functionality is located. #pragma once int AdditionOperation(int a, int b); \ No newline at end of file