diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..d163863 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +build/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index 259148f..04839e8 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,11 @@ *.exe *.out *.app + +# custom build +build/ + +# idea +.idea/ +cmake-build-debug/ +*/cmake-build-debug/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..77ab42a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_minimum_required(VERSION 3.1) + +set(project_name example-consul) ## rename your project here + +project(${project_name}-loader) + +include(ExternalProject) + +############################################################################# +## load all dependencies + +ExternalProject_Add(oatpp + GIT_REPOSITORY "https://github.com/oatpp/oatpp.git" + GIT_TAG origin/master + CMAKE_ARGS -DOATPP_BUILD_TESTS=OFF +) + +ExternalProject_Add(oatpp-consul + GIT_REPOSITORY "https://github.com/oatpp/oatpp-consul.git" + GIT_TAG origin/master +) + +ExternalProject_Add(main + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/main + INSTALL_COMMAND cmake -E echo "SKIP INSTALL" + DEPENDS oatpp oatpp-consul +) + +############################################################################# +## make run command + +ExternalProject_Get_Property(main BINARY_DIR) + +add_custom_target(run + COMMAND ${BINARY_DIR}/${project_name}-exe + DEPENDS main + WORKING_DIRECTORY ${BINARY_DIR} +) + +############################################################################# +## make test command + +enable_testing() +add_test(all-tests ${BINARY_DIR}/${project_name}-test) \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3095e62 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM lganzzzo/alpine-cmake:latest + +ADD . /service + +WORKDIR /service/build + +RUN cmake .. +RUN make + +EXPOSE 8000 8000 + +ENTRYPOINT ["make", "run"] \ No newline at end of file diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..13e1e34 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,27 @@ +# Starter pipeline +# Start with a minimal pipeline that you can customize to build and deploy your code. +# Add steps that build, run tests, deploy, and more: +# https://aka.ms/yaml + +jobs: + - job: ubuntu_16_04 + displayName: 'Build - Ubuntu 16.04' + continueOnError: false + pool: + vmImage: 'Ubuntu 16.04' + container: + image: lganzzzo/ubuntu-cmake:latest + workspace: + clean: all + steps: + - script: | + mkdir build + - script: | + cmake .. + sudo make + displayName: 'CMake' + workingDirectory: build + - script: | + make test ARGS="-V" + displayName: 'Test' + workingDirectory: build \ No newline at end of file diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt new file mode 100644 index 0000000..ebfad77 --- /dev/null +++ b/main/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 3.1) + +set(project_name example-consul) ## rename your project here + +project(${project_name}) + +set(CMAKE_CXX_STANDARD 11) + +add_library(${project_name}-lib + src/AppComponent.hpp + src/Logger.cpp + src/Logger.hpp + src/controller/DemoController.hpp + src/controller/HealthController.hpp + src/dto/HealthDto.hpp +) + +## link libs + +find_package(oatpp 0.19.1 REQUIRED) +find_package(oatpp-consul 0.19.1 REQUIRED) + +target_link_libraries(${project_name}-lib + PUBLIC oatpp::oatpp + PUBLIC oatpp::oatpp-test + PUBLIC oatpp::oatpp-consul +) + +target_include_directories(${project_name}-lib PUBLIC src) + +## add executables + +add_executable(${project_name}-exe + src/App.cpp +) +target_link_libraries(${project_name}-exe ${project_name}-lib) +add_dependencies(${project_name}-exe ${project_name}-lib) + +add_executable(${project_name}-test + test/tests.cpp +) +target_link_libraries(${project_name}-test ${project_name}-lib) +add_dependencies(${project_name}-test ${project_name}-lib) diff --git a/main/src/App.cpp b/main/src/App.cpp new file mode 100644 index 0000000..2597841 --- /dev/null +++ b/main/src/App.cpp @@ -0,0 +1,99 @@ +// +// main.cpp +// web-starter-project +// +// Created by Leonid on 2/12/18. +// Copyright © 2018 oatpp. All rights reserved. +// + +//#define OATPP_USE_TARGET +//#define OATPP_TARGET_TEST + +////////////////////////////////// +// App + +#include "./controller/DemoController.hpp" +#include "./controller/HealthController.hpp" +#include "./AppComponent.hpp" +#include "./Logger.hpp" + +////////////////////////////////// +// Test + +#ifdef OATPP_TARGET_TEST + #include "test/ControllerLevelTest.hpp" + #include "test/RouterLevelTest.hpp" + #include "test/ConnectionHandlerLevelTest.hpp" +#endif + +////////////////////////////////// +// oatpp + +#include "oatpp/network/server/Server.hpp" + +////////////////////////////////// +// std + +#include + +/** + * run() method. + * 1) set Environment components. + * 2) add ApiController's endpoints to router + * 3) run server + */ +void run() { + + AppComponent components; // Create scope Environment components + + /* create ApiControllers and add endpoints to router */ + + auto router = components.httpRouter.getObject(); + + auto DemoController = DemoController::createShared(); + DemoController->addEndpointsToRouter(router); + + auto healthController = HealthController::createShared(); + healthController->addEndpointsToRouter(router); + + /* create server */ + + oatpp::network::server::Server server(components.serverConnectionProvider.getObject(), + components.serverConnectionHandler.getObject()); + + OATPP_LOGD("Server", "Running on port %s...", components.serverConnectionProvider.getObject()->getProperty("port").toString()->c_str()); + + server.run(); + +} + +/** + * main + */ +int main(int argc, const char * argv[]) { + + oatpp::base::Environment::setLogger(new Logger()); + oatpp::base::Environment::init(); + +#if !defined(OATPP_USE_TARGET) | defined(OATPP_TARGET_APP) + run(); +#endif + +#ifdef OATPP_TARGET_TEST + OATPP_RUN_TEST(ControllerLevelTest); + OATPP_RUN_TEST(RouterLevelTest); + OATPP_RUN_TEST(ControllerLevelTest); +#endif + + oatpp::base::Environment::setLogger(nullptr); ///< free Logger + + /* Print how much objects were created during app running, and what have left-probably leaked */ + /* Disable object counting for release builds using '-D OATPP_DISABLE_ENV_OBJECT_COUNTERS' flag for better performance */ + std::cout << "\nEnvironment:\n"; + std::cout << "objectsCount = " << oatpp::base::Environment::getObjectsCount() << "\n"; + std::cout << "objectsCreated = " << oatpp::base::Environment::getObjectsCreated() << "\n\n"; + + oatpp::base::Environment::destroy(); + + return 0; +} diff --git a/main/src/AppComponent.hpp b/main/src/AppComponent.hpp new file mode 100644 index 0000000..4ef3491 --- /dev/null +++ b/main/src/AppComponent.hpp @@ -0,0 +1,90 @@ +// +// AppComponent.hpp +// oatpp-web-starter +// +// Created by Leonid on 3/2/18. +// Copyright © 2018 lganzzzo. All rights reserved. +// + +#ifndef AppComponent_hpp +#define AppComponent_hpp + +#include "oatpp-consul/Client.hpp" + +#include "oatpp/web/client/HttpRequestExecutor.hpp" +#include "oatpp/web/server/HttpConnectionHandler.hpp" +#include "oatpp/web/server/HttpRouter.hpp" +#include "oatpp/network/client/SimpleTCPConnectionProvider.hpp" +#include "oatpp/network/server/SimpleTCPConnectionProvider.hpp" + +#include "oatpp/parser/json/mapping/Serializer.hpp" +#include "oatpp/parser/json/mapping/Deserializer.hpp" + +#include "oatpp/core/macro/component.hpp" + +/** + * Class which creates and holds Application components and registers components in oatpp::base::Environment + * Order of components initialization is from top to bottom + */ +class AppComponent { +public: + + /** + * Create ConnectionProvider component which listens on the port + */ + OATPP_CREATE_COMPONENT(std::shared_ptr, serverConnectionProvider)([] { + return oatpp::network::server::SimpleTCPConnectionProvider::createShared(8000); + }()); + + /** + * Create Router component + */ + OATPP_CREATE_COMPONENT(std::shared_ptr, httpRouter)([] { + return oatpp::web::server::HttpRouter::createShared(); + }()); + + /** + * Create ConnectionHandler component which uses Router component to route requests + */ + OATPP_CREATE_COMPONENT(std::shared_ptr, serverConnectionHandler)([] { + OATPP_COMPONENT(std::shared_ptr, router); // get Router component + return oatpp::web::server::HttpConnectionHandler::createShared(router); + }()); + + /** + * Create ObjectMapper component to serialize/deserialize DTOs in Contoller's API + */ + OATPP_CREATE_COMPONENT(std::shared_ptr, apiObjectMapper)([] { + + auto serializerConfig = oatpp::parser::json::mapping::Serializer::Config::createShared(); + serializerConfig->includeNullFields = true; + + auto deserializerConfig = oatpp::parser::json::mapping::Deserializer::Config::createShared(); + deserializerConfig->allowUnknownFields = false; + + auto objectMapper = oatpp::parser::json::mapping::ObjectMapper::createShared(serializerConfig, deserializerConfig); + return objectMapper; + + }()); + + /** + * Create Demo-oatpp::consul::Client component + */ + OATPP_CREATE_COMPONENT(std::shared_ptr, consulClient)([] { + + OATPP_LOGD("AppComponent", "Assuming Consul is at port 8500"); + + // Create connection provider for Consul + // In case you need secure connection provider so you can connect to Consul via https see oatpp-libressl and tls-libressl example project + auto connectionProvider = oatpp::network::client::SimpleTCPConnectionProvider::createShared("localhost", 8500); + + // Create httpRequestExecutor + auto requestExecutor = oatpp::web::client::HttpRequestExecutor::createShared(connectionProvider); + + // Create and return consul client + return oatpp::consul::Client::createShared(requestExecutor); + }()); + +}; + +#endif /* AppComponent_hpp */ diff --git a/main/src/Logger.cpp b/main/src/Logger.cpp new file mode 100644 index 0000000..c187c09 --- /dev/null +++ b/main/src/Logger.cpp @@ -0,0 +1,16 @@ +// +// Logger.hpp +// oatpp-web-starter +// +// Created by Leonid on 3/2/18. +// Copyright © 2018 lganzzzo. All rights reserved. +// + +#include "Logger.hpp" + +#include + +void Logger::log(v_int32 priority, const std::string& tag, const std::string& message) { + oatpp::concurrency::SpinLock lock(m_atom); + std::cout << tag << ":" << message << "\n"; +} \ No newline at end of file diff --git a/main/src/Logger.hpp b/main/src/Logger.hpp new file mode 100644 index 0000000..f73d003 --- /dev/null +++ b/main/src/Logger.hpp @@ -0,0 +1,33 @@ +// +// Logger.hpp +// oatpp-web-starter +// +// Created by Leonid on 3/2/18. +// Copyright © 2018 lganzzzo. All rights reserved. +// + +#ifndef Logger_hpp +#define Logger_hpp + +#include "oatpp/core/concurrency/SpinLock.hpp" +#include "oatpp/core/base/Environment.hpp" + +/** + * Environment logger. + * All logs from OATPP_LOGV(...), OATPP_LOGD(...), OATPP_LOGE(...) go here + * You may ignore or redirect them here + */ +class Logger : public oatpp::base::Logger { +private: + oatpp::concurrency::SpinLock::Atom m_atom; +public: + + Logger() + : m_atom(false) + {} + + void log(v_int32 priority, const std::string& tag, const std::string& message) override; + +}; + +#endif /* Logger_hpp */ diff --git a/main/src/controller/DemoController.hpp b/main/src/controller/DemoController.hpp new file mode 100644 index 0000000..80959c8 --- /dev/null +++ b/main/src/controller/DemoController.hpp @@ -0,0 +1,75 @@ +// +// DemoController.hpp +// web-starter-project +// +// Created by Leonid on 2/12/18. +// Copyright © 2018 oatpp. All rights reserved. +// + +#ifndef DemoController_hpp +#define DemoController_hpp + +#include "oatpp-consul/Client.hpp" + +#include "oatpp/web/server/api/ApiController.hpp" +#include "oatpp/parser/json/mapping/ObjectMapper.hpp" +#include "oatpp/core/macro/codegen.hpp" +#include "oatpp/core/macro/component.hpp" + +/** + * EXAMPLE ApiController + * Basic examples of howto create ENDPOINTs + * More details on oatpp.io + */ +class DemoController : public oatpp::web::server::api::ApiController { +protected: + DemoController(const std::shared_ptr& objectMapper) + : oatpp::web::server::api::ApiController(objectMapper) + {} +private: + + /** + * Inject Database component + */ + OATPP_COMPONENT(std::shared_ptr, m_consulClient); +public: + + /** + * Inject @objectMapper component here as default parameter + * Do not return bare Controllable* object! use shared_ptr! + */ + static std::shared_ptr createShared(OATPP_COMPONENT(std::shared_ptr, + objectMapper)){ + return std::shared_ptr(new DemoController(objectMapper)); + } + + /** + * Begin ENDPOINTs generation ('ApiController' codegen) + */ +#include OATPP_CODEGEN_BEGIN(ApiController) + + /** + * Insert Your endpoints here !!! + */ + + ENDPOINT("PUT", "demo/consul/kv/{key}", storeValue, + PATH(String, key), + BODY_STRING(String, value)) { + m_consulClient->kvPut(key, value); + return createResponse(Status::CODE_200, "value stored"); + } + + ENDPOINT("GET", "demo/consul/kv/{key}", getValue, + PATH(String, key)) { + return createResponse(Status::CODE_200, m_consulClient->kvGet(key)); + } + + + /** + * Finish ENDPOINTs generation ('ApiController' codegen) + */ +#include OATPP_CODEGEN_END(ApiController) + +}; + +#endif /* DemoController_hpp */ diff --git a/main/src/controller/HealthController.hpp b/main/src/controller/HealthController.hpp new file mode 100644 index 0000000..a76bbd4 --- /dev/null +++ b/main/src/controller/HealthController.hpp @@ -0,0 +1,67 @@ +// +// HealthController.hpp +// consul-integration +// +// Created by Leonid on 7/3/18. +// Copyright © 2018 oatpp. All rights reserved. +// + +#ifndef HealthController_hpp +#define HealthController_hpp + +#include "oatpp-consul/rest/DTOs.hpp" +#include "dto/HealthDto.hpp" + +#include "oatpp/web/server/api/ApiController.hpp" +#include "oatpp/parser/json/mapping/ObjectMapper.hpp" +#include "oatpp/core/macro/codegen.hpp" +#include "oatpp/core/macro/component.hpp" + +/** + * EXAMPLE ApiController + * Basic examples of howto create ENDPOINTs + * More details on oatpp.io + */ +class HealthController : public oatpp::web::server::api::ApiController { +protected: + HealthController(const std::shared_ptr& objectMapper) + : oatpp::web::server::api::ApiController(objectMapper) + {} +public: + + /** + * Inject @objectMapper component here as default parameter + * Do not return bare Controllable* object! use shared_ptr! + */ + static std::shared_ptr createShared(OATPP_COMPONENT(std::shared_ptr, + objectMapper)){ + return std::shared_ptr(new HealthController(objectMapper)); + } + + /** + * Begin ENDPOINTs generation ('ApiController' codegen) + */ +#include OATPP_CODEGEN_BEGIN(ApiController) + + /** + * Insert Your endpoints here !!! + */ + + ENDPOINT("GET", "check/health", healthCheck) { + + auto status = HealthDto::createShared(); + status->status = "healthy"; + + OATPP_LOGD("health", "check"); + + return createDtoResponse(Status::CODE_200, status); + } + + /** + * Finish ENDPOINTs generation ('ApiController' codegen) + */ +#include OATPP_CODEGEN_END(ApiController) + +}; + +#endif /* HealthController_hpp */ diff --git a/main/src/dto/HealthDto.hpp b/main/src/dto/HealthDto.hpp new file mode 100644 index 0000000..7c4323b --- /dev/null +++ b/main/src/dto/HealthDto.hpp @@ -0,0 +1,31 @@ +// +// HealthDto.hpp +// consul-integration +// +// Created by Leonid on 7/3/18. +// Copyright © 2018 oatpp. All rights reserved. +// + +#ifndef HealthDto_hpp +#define HealthDto_hpp + +#include "oatpp/core/data/mapping/type/Object.hpp" +#include "oatpp/core/macro/codegen.hpp" + +#include OATPP_CODEGEN_BEGIN(DTO) + +/** + * Data Transfer Object. Object containing fields only. + * Used in API for serialization/deserialization and validation + */ +class HealthDto : public oatpp::data::mapping::type::Object { + + DTO_INIT(HealthDto, Object) + + DTO_FIELD(String, status); + +}; + +#include OATPP_CODEGEN_END(DTO) + +#endif /* HealthDto_hpp */ diff --git a/main/test/tests.cpp b/main/test/tests.cpp new file mode 100644 index 0000000..ddb0985 --- /dev/null +++ b/main/test/tests.cpp @@ -0,0 +1,46 @@ + +#include "Logger.hpp" + +#include "oatpp-test/UnitTest.hpp" +#include + +namespace { + +class Test : public oatpp::test::UnitTest { +public: + Test() : oatpp::test::UnitTest("[MyTest]") + {} + + bool onRun() override { + OATPP_LOGD(TAG, "Hello Test"); + return true; + } +}; + +void runTests() { + OATPP_RUN_TEST(Test); +} + +} + +int main() { + + oatpp::base::Environment::init(); + oatpp::base::Environment::setLogger(new Logger()); + + runTests(); + + oatpp::base::Environment::setLogger(nullptr); + + /* Print how much objects were created during app running, and what have left-probably leaked */ + /* Disable object counting for release builds using '-D OATPP_DISABLE_ENV_OBJECT_COUNTERS' flag for better performance */ + std::cout << "\nEnvironment:\n"; + std::cout << "objectsCount = " << oatpp::base::Environment::getObjectsCount() << "\n"; + std::cout << "objectsCreated = " << oatpp::base::Environment::getObjectsCreated() << "\n\n"; + + OATPP_ASSERT(oatpp::base::Environment::getObjectsCount() == 0); + + oatpp::base::Environment::destroy(); + + return 0; +}