From 2bd4ab11abfc9264b6c0914c56337fb6b2f4b2d7 Mon Sep 17 00:00:00 2001 From: Leonid Stryzhevskyi Date: Sun, 28 Aug 2022 01:09:52 +0300 Subject: [PATCH] Configure controller paths dynamically - improves (#58) --- CMakeLists.txt | 7 ---- README.md | 27 +++++++++---- res/index.html | 2 +- src/CMakeLists.txt | 4 +- src/oatpp-swagger/AsyncController.hpp | 45 +++++++++++++++++---- src/oatpp-swagger/Controller.hpp | 42 ++++++++++++++------ src/oatpp-swagger/ControllerPaths.hpp | 56 +++++++++++++++++++++++++++ 7 files changed, 146 insertions(+), 37 deletions(-) create mode 100644 src/oatpp-swagger/ControllerPaths.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e60e49..679ed25 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,13 +98,6 @@ message("\n##################################################################### ################################################################################################### ## define targets -set(SWAGGER_ROOT_PATH "/swagger" CACHE STRING "Default root path to the Swagger") -set(SWAGGER_UI_PATH "/ui" CACHE STRING "Default path suffix to the Swagger UI") -add_compile_definitions( - SWAGGER_ROOT_PATH="${SWAGGER_ROOT_PATH}" - SWAGGER_UI_PATH="${SWAGGER_UI_PATH}" -) - include(cmake/module-utils.cmake) include(cmake/msvc-runtime.cmake) diff --git a/README.md b/README.md index 52cdb83..273b24f 100644 --- a/README.md +++ b/README.md @@ -113,14 +113,6 @@ endif() include_directories(${oatpp_INCLUDE_DIRS}) include_directories(${oatpp-swagger_INCLUDE_DIRS}) -set(SWAGGER_ROOT_PATH "/swagger" CACHE STRING "Default root path to the Swagger") -set(SWAGGER_UI_PATH "/ui" CACHE STRING "Default path suffix to the Swagger UI") - -add_compile_definitions( - SWAGGER_ROOT_PATH="${SWAGGER_ROOT_PATH}" - SWAGGER_UI_PATH="${SWAGGER_UI_PATH}" -) - add_definitions( -DOATPP_SWAGGER_RES_PATH="${OATPP_BASE_DIR}/bin/oatpp-swagger/res" ) @@ -132,4 +124,23 @@ target_link_libraries (project PUBLIC ) ``` +### Customise Swagger UI Paths + +To customise swagger UI endpoints paths add the following component: + +```c++ + /** + * Swagger Controller Paths + */ + OATPP_CREATE_COMPONENT(std::shared_ptr, controllerPaths)([] { + auto paths = std::make_shared(); + paths->apiJson = "custom/path/for/api.json"; // default is "api-docs/oas-3.0.0.json" + paths->ui = "my/custom/path/swagger-ui"; // default is "swagger/ui" + paths->uiResources = "my/custom/path/{filename}"; // default is "swagger/{filename}" + return paths; + }()); +``` + +**NOTE:** `paths->ui` and `paths->uiResources` MUST have the same base path - as shown above. + **Done!** diff --git a/res/index.html b/res/index.html index cbcf0f4..3d8a3a2 100644 --- a/res/index.html +++ b/res/index.html @@ -39,7 +39,7 @@ window.onload = function() { // Begin Swagger UI call region const ui = SwaggerUIBundle({ - url: "/api-docs/oas-3.0.0.json", + url: "/%%API.JSON%%", dom_id: '#swagger-ui', deepLinking: true, presets: [ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 92f8d56..4388f95 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,7 @@ add_library(${OATPP_THIS_MODULE_NAME} oatpp-swagger/AsyncController.hpp oatpp-swagger/Controller.hpp + oatpp-swagger/ControllerPaths.hpp oatpp-swagger/Generator.cpp oatpp-swagger/Generator.hpp oatpp-swagger/Model.hpp @@ -9,8 +10,7 @@ add_library(${OATPP_THIS_MODULE_NAME} oatpp-swagger/Resources.hpp oatpp-swagger/Types.cpp oatpp-swagger/Types.hpp - oatpp-swagger/oas3/Model.hpp -) + oatpp-swagger/oas3/Model.hpp) set_target_properties(${OATPP_THIS_MODULE_NAME} PROPERTIES CXX_STANDARD 11 diff --git a/src/oatpp-swagger/AsyncController.hpp b/src/oatpp-swagger/AsyncController.hpp index 9aca7dd..f1e5140 100644 --- a/src/oatpp-swagger/AsyncController.hpp +++ b/src/oatpp-swagger/AsyncController.hpp @@ -25,9 +25,11 @@ #ifndef oatpp_swagger_AsyncController_hpp #define oatpp_swagger_AsyncController_hpp +#include "oatpp-swagger/ControllerPaths.hpp" #include "oatpp-swagger/Resources.hpp" #include "oatpp-swagger/Generator.hpp" +#include "oatpp/web/protocol/http/outgoing/StreamingBody.hpp" #include "oatpp/web/server/api/ApiController.hpp" #include "oatpp/parser/json/mapping/ObjectMapper.hpp" @@ -51,16 +53,20 @@ namespace oatpp { namespace swagger { class AsyncController : public oatpp::web::server::api::ApiController { public: typedef AsyncController __ControllerType; -public: +private: oatpp::Object m_document; std::shared_ptr m_resources; +private: + ControllerPaths m_paths; public: AsyncController(const std::shared_ptr& objectMapper, const oatpp::Object& document, - const std::shared_ptr& resources) + const std::shared_ptr& resources, + const ControllerPaths& paths) : oatpp::web::server::api::ApiController(objectMapper) , m_document(document) , m_resources(resources) + , m_paths(paths) {} public: @@ -92,13 +98,21 @@ public: Generator generator(generatorConfig); auto document = generator.generateDocument(documentInfo, endpointsList); + + ControllerPaths paths; + try { + auto ps = OATPP_GET_COMPONENT(std::shared_ptr); + if(ps) paths = *ps; + } catch (std::runtime_error&) { + // DO nothing. + } - return std::make_shared(objectMapper, document, resources); + return std::make_shared(objectMapper, document, resources, paths); } #include OATPP_CODEGEN_BEGIN(ApiController) - ENDPOINT_ASYNC("GET", "/api-docs/oas-3.0.0.json", Api) { + ENDPOINT_ASYNC("GET", m_paths.apiJson, Api) { ENDPOINT_ASYNC_INIT(Api) @@ -108,23 +122,40 @@ public: }; - ENDPOINT_ASYNC("GET", SWAGGER_ROOT_PATH SWAGGER_UI_PATH, GetUIRoot) { + ENDPOINT_ASYNC("GET", m_paths.ui, GetUIRoot) { ENDPOINT_ASYNC_INIT(GetUIRoot) Action act() override { - return _return(controller->createResponse(Status::CODE_200, controller->m_resources->getResource("index.html"))); + std::string ui; + if(controller->m_resources->isStreaming()) { + v_char8 buffer[1024]; + auto fileStream = controller->m_resources->getResourceStream("index.html"); + oatpp::data::stream::BufferOutputStream s(1024); + oatpp::data::stream::transfer(fileStream, &s, 0, buffer, 1024); + ui = s.toString(); + } else { + ui = * controller->m_resources->getResource("index.html"); // * - copy of the index.html + } + ui.replace(ui.find("%%API.JSON%%"), 12, controller->m_paths.apiJson); + return _return(controller->createResponse(Status::CODE_200, ui)); } }; - ENDPOINT_ASYNC("GET", SWAGGER_ROOT_PATH "/{filename}", GetUIResource) { + ENDPOINT_ASYNC("GET", m_paths.uiResources, GetUIResource) { ENDPOINT_ASYNC_INIT(GetUIResource) Action act() override { auto filename = request->getPathVariable("filename"); OATPP_ASSERT_HTTP(filename, Status::CODE_400, "filename should not be null") + if(controller->m_resources->isStreaming()) { + auto body = std::make_shared( + controller->m_resources->getResourceStream(filename->c_str()) + ); + return _return(OutgoingResponse::createShared(Status::CODE_200, body)); + } return _return(controller->createResponse(Status::CODE_200, controller->m_resources->getResource(filename->c_str()))); } diff --git a/src/oatpp-swagger/Controller.hpp b/src/oatpp-swagger/Controller.hpp index 7ae425b..b26c3d5 100644 --- a/src/oatpp-swagger/Controller.hpp +++ b/src/oatpp-swagger/Controller.hpp @@ -25,6 +25,7 @@ #ifndef oatpp_swagger_Controller_hpp #define oatpp_swagger_Controller_hpp +#include "oatpp-swagger/ControllerPaths.hpp" #include "oatpp-swagger/Resources.hpp" #include "oatpp-swagger/Generator.hpp" @@ -53,13 +54,17 @@ class Controller : public oatpp::web::server::api::ApiController { private: oatpp::Object m_document; std::shared_ptr m_resources; +private: + ControllerPaths m_paths; public: Controller(const std::shared_ptr& objectMapper, const oatpp::Object& document, - const std::shared_ptr& resources) + const std::shared_ptr& resources, + const ControllerPaths& paths) : oatpp::web::server::api::ApiController(objectMapper) , m_document(document) , m_resources(resources) + , m_paths(paths) {} public: @@ -85,33 +90,46 @@ public: std::shared_ptr generatorConfig; try { generatorConfig = OATPP_GET_COMPONENT(std::shared_ptr); - } catch (std::runtime_error e) { + } catch (std::runtime_error&) { generatorConfig = std::make_shared(); } Generator generator(generatorConfig); auto document = generator.generateDocument(documentInfo, endpointsList); - - return std::make_shared(objectMapper, document, resources); + + ControllerPaths paths; + try { + auto ps = OATPP_GET_COMPONENT(std::shared_ptr); + if(ps) paths = *ps; + } catch (std::runtime_error&) { + // DO nothing. + } + + return std::make_shared(objectMapper, document, resources, paths); } #include OATPP_CODEGEN_BEGIN(ApiController) - ENDPOINT("GET", "/api-docs/oas-3.0.0.json", api) { + ENDPOINT("GET", m_paths.apiJson, api) { return createDtoResponse(Status::CODE_200, m_document); } - ENDPOINT("GET", SWAGGER_ROOT_PATH SWAGGER_UI_PATH, getUIRoot) { + ENDPOINT("GET", m_paths.ui, getUIRoot) { + std::string ui; if(m_resources->isStreaming()) { - auto body = std::make_shared( - m_resources->getResourceStream("index.html") - ); - return OutgoingResponse::createShared(Status::CODE_200, body); + v_char8 buffer[1024]; + auto fileStream = m_resources->getResourceStream("index.html"); + oatpp::data::stream::BufferOutputStream s(1024); + oatpp::data::stream::transfer(fileStream, &s, 0, buffer, 1024); + ui = s.toString(); + } else { + ui = *m_resources->getResource("index.html"); // * - copy of the index.html } - return createResponse(Status::CODE_200, m_resources->getResource("index.html")); + ui.replace(ui.find("%%API.JSON%%"), 12, m_paths.apiJson); + return createResponse(Status::CODE_200, ui); } - ENDPOINT("GET", SWAGGER_ROOT_PATH "/{filename}", getUIResource, PATH(String, filename)) { + ENDPOINT("GET", m_paths.uiResources, getUIResource, PATH(String, filename)) { if(m_resources->isStreaming()) { auto body = std::make_shared( m_resources->getResourceStream(filename->c_str()) diff --git a/src/oatpp-swagger/ControllerPaths.hpp b/src/oatpp-swagger/ControllerPaths.hpp new file mode 100644 index 0000000..d94dcff --- /dev/null +++ b/src/oatpp-swagger/ControllerPaths.hpp @@ -0,0 +1,56 @@ +/*************************************************************************** + * + * Project _____ __ ____ _ _ + * ( _ ) /__\ (_ _)_| |_ _| |_ + * )(_)( /(__)\ )( (_ _)(_ _) + * (_____)(__)(__)(__) |_| |_| + * + * + * Copyright 2018-present, Leonid Stryzhevskyi, + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************/ + +#ifndef oatpp_swagger_ControllerPaths_hpp +#define oatpp_swagger_ControllerPaths_hpp + +#include "oatpp/core/Types.hpp" + +namespace oatpp { namespace swagger { + +/** + * Swagger Controller endpoints paths. + */ +struct ControllerPaths { + + /** + * Path to generated API JSON. + */ + oatpp::String apiJson = "api-docs/oas-3.0.0.json"; + + /** + * Path to swagger UI (index.html). + */ + oatpp::String ui = "swagger/ui"; + + /** + * Path to other ui resources. MUST contain `/{filename}` at the end. + */ + oatpp::String uiResources = "swagger/{filename}"; + +}; + +}} + +#endif //oatpp_swagger_ControllerPaths_hpp