From ba4dfec459688ecab09845275fb4500cce51d2ba Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 5 Dec 2025 20:28:03 +0300 Subject: [PATCH 1/9] refactor(tgbot): change the location of the CMakeLists.txt --- modules/bot/.gitignore | 2 ++ modules/bot/{front => }/CMakeLists.txt | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 modules/bot/.gitignore rename modules/bot/{front => }/CMakeLists.txt (77%) diff --git a/modules/bot/.gitignore b/modules/bot/.gitignore new file mode 100644 index 0000000..bf53375 --- /dev/null +++ b/modules/bot/.gitignore @@ -0,0 +1,2 @@ +generated-client/ +out/ \ No newline at end of file diff --git a/modules/bot/front/CMakeLists.txt b/modules/bot/CMakeLists.txt similarity index 77% rename from modules/bot/front/CMakeLists.txt rename to modules/bot/CMakeLists.txt index f155517..7a8948d 100644 --- a/modules/bot/front/CMakeLists.txt +++ b/modules/bot/CMakeLists.txt @@ -2,13 +2,13 @@ cmake_minimum_required(VERSION 3.10.2) project(AnimeBot) set(SOURCES "") -file(GLOB_RECURSE SRC_FRONT "src/*.cpp") +file(GLOB_RECURSE SRC_FRONT "front/src/*.cpp") list(APPEND SOURCES ${SRC_FRONT}) -file(GLOB_RECURSE SRC_BACK "../back/src/*.cpp") +file(GLOB_RECURSE SRC_BACK "back/src/*.cpp") list(APPEND SOURCES ${SRC_BACK}) -file(GLOB_RECURSE SRC_API "../generated-client/src/*.cpp") +file(GLOB_RECURSE SRC_API "generated-client/src/*.cpp") list(APPEND SOURCES ${SRC_API}) set(CMAKE_CXX_STANDARD 20) @@ -23,9 +23,9 @@ find_package(Boost COMPONENTS system REQUIRED) find_package(CURL) find_library(CPPREST_LIB cpprest REQUIRED) include_directories(/usr/local/include ${OPENSSL_INCLUDE_DIR} ${Boost_INCLUDE_DIR}) -include_directories(include/) -include_directories(../back/include) -include_directories(../generated-client/include) +include_directories(front/include/) +include_directories(back/include) +include_directories(generated-client/include) if (CURL_FOUND) include_directories(${CURL_INCLUDE_DIRS}) add_definitions(-DHAVE_CURL) From 20cf8b1fc21d7b3c45080f08a432303ed32c9262 Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 5 Dec 2025 22:49:14 +0300 Subject: [PATCH 2/9] build(tgbot-gen): Changes and additions required to generate the API client with auth (cookie) I use a standard set of templates, changes are made in api-header.mustache and api-source.mustache. Their _old versions contain the originals. Additions are necessary for the header argument in the generated functions so that you can authenticate using cookies. --- .../cpp-restsdk/README.mustache | 59 ++ .../cpp-restsdk/anytype-header.mustache | 46 ++ .../cpp-restsdk/anytype-source.mustache | 40 ++ .../cpp-restsdk/api-gmock.mustache | 41 ++ .../cpp-restsdk/api-header.mustache | 86 +++ .../cpp-restsdk/api-header.mustache_old | 84 +++ .../cpp-restsdk/api-source.mustache | 375 ++++++++++ .../cpp-restsdk/api-source.mustache_old | 368 ++++++++++ .../cpp-restsdk/apiclient-header.mustache | 107 +++ .../cpp-restsdk/apiclient-source.mustache | 203 ++++++ .../apiconfiguration-header.mustache | 54 ++ .../apiconfiguration-source.mustache | 73 ++ .../cpp-restsdk/apiexception-header.mustache | 48 ++ .../cpp-restsdk/apiexception-source.mustache | 41 ++ .../cpp-restsdk/cmake-config.mustache | 5 + .../cpp-restsdk/cmake-lists.mustache | 91 +++ .../cpp-restsdk/git_push.sh.mustache | 57 ++ .../cpp-restsdk/gitignore.mustache | 29 + .../cpp-restsdk/httpcontent-header.mustache | 57 ++ .../cpp-restsdk/httpcontent-source.mustache | 74 ++ .../cpp-restsdk/ihttpbody-header.mustache | 30 + .../cpp-restsdk/jsonbody-header.mustache | 37 + .../cpp-restsdk/jsonbody-source.mustache | 24 + .../cpp-restsdk/licenseInfo.mustache | 15 + .../cpp-restsdk/model-header.mustache | 288 ++++++++ .../cpp-restsdk/model-source.mustache | 457 ++++++++++++ .../cpp-restsdk/modelbase-header.mustache | 493 +++++++++++++ .../cpp-restsdk/modelbase-source.mustache | 653 ++++++++++++++++++ .../cpp-restsdk/multipart-header.mustache | 49 ++ .../cpp-restsdk/multipart-source.mustache | 100 +++ .../cpp-restsdk/object-header.mustache | 50 ++ .../cpp-restsdk/object-source.mustache | 79 +++ 32 files changed, 4213 insertions(+) create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/README.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/anytype-header.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/anytype-source.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/api-gmock.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/api-header.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/api-header.mustache_old create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/api-source.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/api-source.mustache_old create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/apiclient-header.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/apiclient-source.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/apiconfiguration-header.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/apiconfiguration-source.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/apiexception-header.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/apiexception-source.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/cmake-config.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/cmake-lists.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/git_push.sh.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/gitignore.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/httpcontent-header.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/httpcontent-source.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/ihttpbody-header.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/jsonbody-header.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/jsonbody-source.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/licenseInfo.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/model-header.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/model-source.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/modelbase-header.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/modelbase-source.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/multipart-header.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/multipart-source.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/object-header.mustache create mode 100644 api/_build/my-cpp-templates/cpp-restsdk/object-source.mustache diff --git a/api/_build/my-cpp-templates/cpp-restsdk/README.mustache b/api/_build/my-cpp-templates/cpp-restsdk/README.mustache new file mode 100644 index 0000000..6a59d00 --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/README.mustache @@ -0,0 +1,59 @@ +# C++ API client + +{{#appDescriptionWithNewLines}} +{{{.}}} +{{/appDescriptionWithNewLines}} + +## Overview +This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [OpenAPI spec](https://openapis.org) from a remote server, you can easily generate an API client. + +- API version: {{appVersion}} +- Package version: {{packageVersion}} +{{^hideGenerationTimestamp}} +- Build date: {{generatedDate}} +{{/hideGenerationTimestamp}} +- Generator version: {{generatorVersion}} +- Build package: {{generatorClass}} +{{#infoUrl}} +For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) +{{/infoUrl}} + +- API namespace: {{{apiPackage}}} +- Model namespace: {{{modelPackage}}} + +## Installation + +### Prerequisites + +Install [cpprestsdk](https://github.com/Microsoft/cpprestsdk). + +- Windows: `vcpkg install cpprestsdk cpprestsdk:x64-windows boost-uuid boost-uuid:x64-windows` +- Mac: `brew install cpprestsdk` +- Linux: `sudo apt-get install libcpprest-dev` + +### Build + +```sh +cmake -DCPPREST_ROOT=/usr -DCMAKE_CXX_FLAGS="-I/usr/local/opt/openssl/include" -DCMAKE_MODULE_LINKER_FLAGS="-L/usr/local/opt/openssl/lib" +make +``` + +### Build on Windows with Visual Studio (VS2017) + +- Right click on folder containing source code +- Select 'Open in visual studio' +- Once visual studio opens, CMake should show up in top menu bar. +- Select CMake > Build All. + +*Note: If the CMake menu item doesn't show up in Visual Studio, CMake +for Visual Studio must be installed. In this case, open the 'Visual Studio +Installer' application. Select 'modify' Visual Studio 2017. Make sure +'Desktop Development with C++' is installed, and specifically that 'Visual +C++ tools for CMake' is selected in the 'Installation Details' section. + +Also be sure to review the CMakeLists.txt file. Edits are likely required.* + +## Author + +{{#apiInfo}}{{#apis}}{{#-last}}{{infoEmail}} +{{/-last}}{{/apis}}{{/apiInfo}} diff --git a/api/_build/my-cpp-templates/cpp-restsdk/anytype-header.mustache b/api/_build/my-cpp-templates/cpp-restsdk/anytype-header.mustache new file mode 100644 index 0000000..16206ff --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/anytype-header.mustache @@ -0,0 +1,46 @@ +{{>licenseInfo}} +/* + * AnyType.h + * + * This is the implementation of an any JSON type. + */ + +#ifndef {{modelHeaderGuardPrefix}}_AnyType_H_ +#define {{modelHeaderGuardPrefix}}_AnyType_H_ + +{{{defaultInclude}}} +#include "{{packageName}}/Object.h" + +#include +#include + +{{#modelNamespaceDeclarations}} +namespace {{this}} { +{{/modelNamespaceDeclarations}} + +class {{declspec}} AnyType : public Object { +public: + AnyType(); + virtual ~AnyType(); + + ///////////////////////////////////////////// + /// ModelBase overrides + void validate() override; + + web::json::value toJson() const override; + bool fromJson(const web::json::value &json) override; + + void toMultipart(std::shared_ptr multipart, + const utility::string_t &namePrefix) const override; + bool fromMultiPart(std::shared_ptr multipart, + const utility::string_t &namePrefix) override; + +private: + web::json::value m_value; +}; + +{{#modelNamespaceDeclarations}} +} +{{/modelNamespaceDeclarations}} + +#endif /* {{modelHeaderGuardPrefix}}_AnyType_H_ */ diff --git a/api/_build/my-cpp-templates/cpp-restsdk/anytype-source.mustache b/api/_build/my-cpp-templates/cpp-restsdk/anytype-source.mustache new file mode 100644 index 0000000..8f9421a --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/anytype-source.mustache @@ -0,0 +1,40 @@ +{{>licenseInfo}} +#include "{{packageName}}/AnyType.h" + +{{#modelNamespaceDeclarations}} +namespace {{this}} { +{{/modelNamespaceDeclarations}} + +AnyType::AnyType() { m_value = web::json::value::null(); } + +AnyType::~AnyType() {} + +void AnyType::validate() {} + +web::json::value AnyType::toJson() const { return m_value; } + +bool AnyType::fromJson(const web::json::value &val) { + m_value = val; + m_IsSet = true; + return isSet(); +} + +void AnyType::toMultipart(std::shared_ptr multipart, + const utility::string_t &prefix) const { + if (m_value.is_object()) { + return Object::toMultipart(multipart, prefix); + } + throw std::runtime_error("AnyType::toMultipart: unsupported type"); +} + +bool AnyType::fromMultiPart(std::shared_ptr multipart, + const utility::string_t &prefix) { + if (m_value.is_object()) { + return Object::fromMultiPart(multipart, prefix); + } + return false; +} + +{{#modelNamespaceDeclarations}} +} +{{/modelNamespaceDeclarations}} diff --git a/api/_build/my-cpp-templates/cpp-restsdk/api-gmock.mustache b/api/_build/my-cpp-templates/cpp-restsdk/api-gmock.mustache new file mode 100644 index 0000000..d144bf6 --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/api-gmock.mustache @@ -0,0 +1,41 @@ +{{>licenseInfo}} +{{#operations}} +#ifndef {{apiHeaderGuardPrefix}}_{{classname}}GMock_H_ +#define {{apiHeaderGuardPrefix}}_{{classname}}GMock_H_ + +#include + +#include "{{classname}}.h" + +{{#apiNamespaceDeclarations}} +namespace {{this}} { +{{/apiNamespaceDeclarations}} + +using namespace {{modelNamespace}}; + + +class {{declspec}} {{classname}}Mock : public I{{classname}} +{ +public: + using Base = I{{classname}}; + + {{classname}}Mock() = default; + explicit {{classname}}Mock( std::shared_ptr apiClient ) { }; + ~{{classname}}Mock() override = default; + + {{#operation}} + MOCK_METHOD{{allParams.size}}( {{operationId}}, pplx::task<{{{returnType}}}{{^returnType}}void{{/returnType}}> ( + {{#allParams}} + {{^required}}boost::optional<{{/required}}{{#isFile}}std::shared_ptr<{{/isFile}}{{{dataType}}}{{#isFile}}>{{/isFile}}{{^required}}>{{/required}} {{paramName}}{{^-last}},{{/-last}} + {{/allParams}} + ) ); + {{/operation}} +}; + +{{#apiNamespaceDeclarations}} +} +{{/apiNamespaceDeclarations}} + +#endif /* {{apiHeaderGuardPrefix}}_{{classname}}GMock_H_ */ + +{{/operations}} diff --git a/api/_build/my-cpp-templates/cpp-restsdk/api-header.mustache b/api/_build/my-cpp-templates/cpp-restsdk/api-header.mustache new file mode 100644 index 0000000..a4e60e1 --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/api-header.mustache @@ -0,0 +1,86 @@ +{{>licenseInfo}} +{{#operations}}/* + * {{classname}}.h + * + * {{description}} + */ + +#ifndef {{apiHeaderGuardPrefix}}_{{classname}}_H_ +#define {{apiHeaderGuardPrefix}}_{{classname}}_H_ + +{{{defaultInclude}}} + +#include "{{packageName}}/ApiClient.h" +{{^hasModelImport}}#include "{{packageName}}/ModelBase.h"{{/hasModelImport}} +{{#imports}}{{{import}}} +{{/imports}} +#include +#include // <-- добавлено для std::map + +{{#apiNamespaceDeclarations}} +namespace {{this}} { +{{/apiNamespaceDeclarations}} + +using namespace {{modelNamespace}}; + +{{#gmockApis}} +class {{declspec}} I{{classname}} +{ +public: + I{{classname}}() = default; + virtual ~I{{classname}}() = default; + + {{#operation}} + virtual pplx::task<{{{returnType}}}{{^returnType}}void{{/returnType}}> {{operationId}}( + {{#allParams}} + {{^required}}boost::optional<{{/required}}{{{dataType}}}{{^required}}>{{/required}} {{paramName}},{{/allParams}} + const std::map& customHeaders = {} // <-- добавлено + ) const = 0; + {{/operation}} +};{{/gmockApis}} + +class {{declspec}} {{classname}} {{#gmockApis}} : public I{{classname}} {{/gmockApis}} +{ +public: + {{#gmockApis}} + using Base = I{{classname}}; + {{/gmockApis}} + + explicit {{classname}}( std::shared_ptr apiClient ); + + {{#gmockApis}} + ~{{classname}}() override; + {{/gmockApis}} + {{^gmockApis}} + virtual ~{{classname}}(); + {{/gmockApis}} + + {{#operation}} + /// + /// {{summary}} + /// + /// + /// {{notes}} + /// + {{#allParams}} + /// {{#lambda.multiline_comment_4}}{{description}}{{/lambda.multiline_comment_4}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}} + /// Additional HTTP headers to send with the request (optional) + pplx::task<{{{returnType}}}{{^returnType}}void{{/returnType}}> {{operationId}}( + {{#allParams}} + {{^required}}boost::optional<{{/required}}{{{dataType}}}{{^required}}>{{/required}} {{paramName}},{{/allParams}} + const std::map& customHeaders = {} // <-- добавлено + ) const{{#gmockApis}} override{{/gmockApis}}; + {{/operation}} + +protected: + std::shared_ptr m_ApiClient; +}; + +{{#apiNamespaceDeclarations}} +} +{{/apiNamespaceDeclarations}} + +#endif /* {{apiHeaderGuardPrefix}}_{{classname}}_H_ */ + +{{/operations}} diff --git a/api/_build/my-cpp-templates/cpp-restsdk/api-header.mustache_old b/api/_build/my-cpp-templates/cpp-restsdk/api-header.mustache_old new file mode 100644 index 0000000..c2c6458 --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/api-header.mustache_old @@ -0,0 +1,84 @@ +{{>licenseInfo}} +{{#operations}}/* + * {{classname}}.h + * + * {{description}} + */ + +#ifndef {{apiHeaderGuardPrefix}}_{{classname}}_H_ +#define {{apiHeaderGuardPrefix}}_{{classname}}_H_ + +{{{defaultInclude}}} + +#include "{{packageName}}/ApiClient.h" +{{^hasModelImport}}#include "{{packageName}}/ModelBase.h"{{/hasModelImport}} +{{#imports}}{{{import}}} +{{/imports}} +#include + +{{#apiNamespaceDeclarations}} +namespace {{this}} { +{{/apiNamespaceDeclarations}} + +using namespace {{modelNamespace}}; + +{{#gmockApis}} +class {{declspec}} I{{classname}} +{ +public: + I{{classname}}() = default; + virtual ~I{{classname}}() = default; + + {{#operation}} + virtual pplx::task<{{{returnType}}}{{^returnType}}void{{/returnType}}> {{operationId}}( + {{#allParams}} + {{^required}}boost::optional<{{/required}}{{{dataType}}}{{^required}}>{{/required}} {{paramName}}{{^-last}},{{/-last}} + {{/allParams}} + ) const = 0; + {{/operation}} +};{{/gmockApis}} + +class {{declspec}} {{classname}} {{#gmockApis}} : public I{{classname}} {{/gmockApis}} +{ +public: + {{#gmockApis}} + using Base = I{{classname}}; + {{/gmockApis}} + + explicit {{classname}}( std::shared_ptr apiClient ); + + {{#gmockApis}} + ~{{classname}}() override; + {{/gmockApis}} + {{^gmockApis}} + virtual ~{{classname}}(); + {{/gmockApis}} + + {{#operation}} + /// + /// {{summary}} + /// + /// + /// {{notes}} + /// + {{#allParams}} + /// {{#lambda.multiline_comment_4}}{{description}}{{/lambda.multiline_comment_4}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}} + pplx::task<{{{returnType}}}{{^returnType}}void{{/returnType}}> {{operationId}}( + {{#allParams}} + {{^required}}boost::optional<{{/required}}{{{dataType}}}{{^required}}>{{/required}} {{paramName}}{{^-last}},{{/-last}} + {{/allParams}} + ) const{{#gmockApis}} override{{/gmockApis}}; + {{/operation}} + +protected: + std::shared_ptr m_ApiClient; +}; + +{{#apiNamespaceDeclarations}} +} +{{/apiNamespaceDeclarations}} + +#endif /* {{apiHeaderGuardPrefix}}_{{classname}}_H_ */ + +{{/operations}} diff --git a/api/_build/my-cpp-templates/cpp-restsdk/api-source.mustache b/api/_build/my-cpp-templates/cpp-restsdk/api-source.mustache new file mode 100644 index 0000000..aa6385d --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/api-source.mustache @@ -0,0 +1,375 @@ +{{>licenseInfo}} +{{#operations}} + +#include "{{packageName}}/api/{{classname}}.h" +#include "{{packageName}}/IHttpBody.h" +#include "{{packageName}}/JsonBody.h" +#include "{{packageName}}/MultipartFormData.h" + +#include + +#include + +{{#apiNamespaceDeclarations}} +namespace {{this}} { +{{/apiNamespaceDeclarations}} + +using namespace {{modelNamespace}}; + +{{classname}}::{{classname}}( std::shared_ptr apiClient ) + : m_ApiClient(apiClient) +{ +} + +{{classname}}::~{{classname}}() +{ +} + +{{#operation}} +pplx::task<{{{returnType}}}{{^returnType}}void{{/returnType}}> {{classname}}::{{operationId}}({{#allParams}}{{^required}}boost::optional<{{/required}}{{{dataType}}}{{^required}}>{{/required}} {{paramName}}, {{/allParams}}const std::map& customHeaders) const // <-- изменено: все параметры с запятой, + customHeaders +{ +{{#allParams}}{{#required}}{{^isPrimitiveType}}{{^isContainer}} + // verify the required parameter '{{paramName}}' is set + if ({{paramName}} == nullptr) + { + throw ApiException(400, utility::conversions::to_string_t("Missing required parameter '{{paramName}}' when calling {{classname}}->{{operationId}}")); + } +{{/isContainer}}{{/isPrimitiveType}}{{/required}}{{/allParams}} + + std::shared_ptr localVarApiConfiguration( m_ApiClient->getConfiguration() ); + utility::string_t localVarPath = utility::conversions::to_string_t("{{{path}}}"); + {{#pathParams}} + boost::replace_all(localVarPath, utility::conversions::to_string_t("{") + utility::conversions::to_string_t("{{baseName}}") + utility::conversions::to_string_t("}"), web::uri::encode_uri(ApiClient::parameterToString({{{paramName}}}))); + {{/pathParams}} + + std::map localVarQueryParams; + std::map localVarHeaderParams( localVarApiConfiguration->getDefaultHeaders() ); // <-- уже содержит defaultHeaders + std::map localVarFormParams; + std::map> localVarFileParams; + + std::unordered_set localVarResponseHttpContentTypes; + {{#produces}} + localVarResponseHttpContentTypes.insert( utility::conversions::to_string_t("{{{mediaType}}}") ); + {{/produces}} + + utility::string_t localVarResponseHttpContentType; + + // use JSON if possible + if ( localVarResponseHttpContentTypes.size() == 0 ) + { + {{#vendorExtensions.x-codegen-response.isString}} + localVarResponseHttpContentType = utility::conversions::to_string_t("text/plain"); + {{/vendorExtensions.x-codegen-response.isString}} + {{^vendorExtensions.x-codegen-response.isString}} + localVarResponseHttpContentType = utility::conversions::to_string_t("application/json"); + {{/vendorExtensions.x-codegen-response.isString}} + } + // JSON + else if ( localVarResponseHttpContentTypes.find(utility::conversions::to_string_t("application/json")) != localVarResponseHttpContentTypes.end() ) + { + localVarResponseHttpContentType = utility::conversions::to_string_t("application/json"); + } + // multipart formdata + else if( localVarResponseHttpContentTypes.find(utility::conversions::to_string_t("multipart/form-data")) != localVarResponseHttpContentTypes.end() ) + { + localVarResponseHttpContentType = utility::conversions::to_string_t("multipart/form-data"); + } + {{#vendorExtensions.x-codegen-response.isString}} + // plain text + else if( localVarResponseHttpContentTypes.find(utility::conversions::to_string_t("text/plain")) != localVarResponseHttpContentTypes.end() ) + { + localVarResponseHttpContentType = utility::conversions::to_string_t("text/plain"); + } + {{/vendorExtensions.x-codegen-response.isString}} + {{#vendorExtensions.x-codegen-response-ishttpcontent}} + else + { + //It's going to be binary, so just use the first one. + localVarResponseHttpContentType = *localVarResponseHttpContentTypes.begin(); + } + {{/vendorExtensions.x-codegen-response-ishttpcontent}} + {{^vendorExtensions.x-codegen-response-ishttpcontent}} + else + { + throw ApiException(400, utility::conversions::to_string_t("{{classname}}->{{operationId}} does not produce any supported media type")); + } + {{/vendorExtensions.x-codegen-response-ishttpcontent}} + + localVarHeaderParams[utility::conversions::to_string_t("Accept")] = localVarResponseHttpContentType; + + std::unordered_set localVarConsumeHttpContentTypes; + {{#consumes}} + localVarConsumeHttpContentTypes.insert( utility::conversions::to_string_t("{{{mediaType}}}") ); + {{/consumes}} + + {{#allParams}} + {{^isBodyParam}} + {{^isPathParam}} + {{#required}} + {{^isPrimitiveType}} + {{^isContainer}} + if ({{paramName}} != nullptr) + {{/isContainer}} + {{/isPrimitiveType}} + {{/required}} + {{^required}} + {{^isPrimitiveType}} + {{^isContainer}} + if ({{paramName}} && *{{paramName}} != nullptr) + {{/isContainer}} + {{/isPrimitiveType}} + {{#isPrimitiveType}} + {{#isFile}} + if ({{paramName}} && *{{paramName}} != nullptr) + {{/isFile}} + {{^isFile}} + if ({{paramName}}) + {{/isFile}} + {{/isPrimitiveType}} + {{#isContainer}} + if ({{paramName}}) + {{/isContainer}} + {{/required}} + { + {{#isQueryParam}} + localVarQueryParams[utility::conversions::to_string_t("{{baseName}}")] = ApiClient::parameterToString({{^required}}*{{/required}}{{paramName}}); + {{/isQueryParam}} + {{#isHeaderParam}} + localVarHeaderParams[utility::conversions::to_string_t("{{baseName}}")] = ApiClient::parameterToString({{^required}}*{{/required}}{{paramName}}); + {{/isHeaderParam}} + {{#isFormParam}} + {{#isFile}} + localVarFileParams[ utility::conversions::to_string_t("{{baseName}}") ] = {{^required}}*{{/required}}{{paramName}}; + {{/isFile}} + {{^isFile}} + localVarFormParams[ utility::conversions::to_string_t("{{baseName}}") ] = ApiClient::parameterToString({{^required}}*{{/required}}{{paramName}}); + {{/isFile}} + {{/isFormParam}} + } + {{/isPathParam}} + {{/isBodyParam}} + {{/allParams}} + + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + // Apply customHeaders AFTER all other headers (so they can override) + for (const auto& header : customHeaders) { + localVarHeaderParams[header.first] = header.second; + } + // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + std::shared_ptr localVarHttpBody; + utility::string_t localVarRequestHttpContentType; + + // use JSON if possible + if ( localVarConsumeHttpContentTypes.size() == 0 || localVarConsumeHttpContentTypes.find(utility::conversions::to_string_t("application/json")) != localVarConsumeHttpContentTypes.end() ) + { + localVarRequestHttpContentType = utility::conversions::to_string_t("application/json"); + {{#bodyParam}} + web::json::value localVarJson; + + {{#isPrimitiveType}} + localVarJson = ModelBase::toJson({{paramName}}{{^required}}.get(){{/required}}); + {{/isPrimitiveType}} + {{^isPrimitiveType}} + {{#isArray}} + { + std::vector localVarJsonArray; + for( auto& localVarItem : {{paramName}}{{^required}}.get(){{/required}} ) + { + {{#items.isPrimitiveType}}localVarJsonArray.push_back(ModelBase::toJson(localVarItem)); + {{/items.isPrimitiveType}}{{^items.isPrimitiveType}}{{#items.isString}}localVarJsonArray.push_back(ModelBase::toJson(localVarItem)); + {{/items.isString}}{{^items.isString}}{{#items.isDateTime}}localVarJsonArray.push_back(ModelBase::toJson(localVarItem)); + {{/items.isDateTime}}{{^items.isDateTime}}localVarJsonArray.push_back( localVarItem.get() ? localVarItem->toJson() : web::json::value::null() ); + {{/items.isDateTime}}{{/items.isString}}{{/items.isPrimitiveType}} + } + localVarJson = web::json::value::array(localVarJsonArray); + } + {{/isArray}} + {{^isArray}}{{#required}}localVarJson = ModelBase::toJson({{paramName}}); + {{/required}}{{^required}}if ({{paramName}}) + localVarJson = ModelBase::toJson(*{{paramName}});{{/required}} + {{/isArray}} + {{/isPrimitiveType}} + + localVarHttpBody = std::shared_ptr( new JsonBody( localVarJson ) ); + {{/bodyParam}} + } + // multipart formdata + else if( localVarConsumeHttpContentTypes.find(utility::conversions::to_string_t("multipart/form-data")) != localVarConsumeHttpContentTypes.end() ) + { + localVarRequestHttpContentType = utility::conversions::to_string_t("multipart/form-data"); + {{#bodyParam}} + std::shared_ptr localVarMultipart(new MultipartFormData); + {{#isPrimitiveType}} + localVarMultipart->add(ModelBase::toHttpContent(utility::conversions::to_string_t("{{paramName}}"), {{paramName}}{{^required}}.get(){{/required}})); + {{/isPrimitiveType}}{{^isPrimitiveType}}{{#isArray}} + { + std::vector localVarJsonArray; + for( auto& localVarItem : {{paramName}}{{^required}}.get(){{/required}} ) + { + localVarJsonArray.push_back(ModelBase::toJson(localVarItem)); + } + localVarMultipart->add(ModelBase::toHttpContent(utility::conversions::to_string_t("{{paramName}}"), localVarJsonArray, utility::conversions::to_string_t("application/json"))); + }{{/isArray}}{{#isMap}} + { + std::map localVarJsonMap; + for( auto& localVarItem : {{paramName}}{{^required}}.get(){{/required}} ) + { + web::json::value jval; + localVarJsonMap.insert( std::pair(localVarItem.first, ModelBase::toJson(localVarItem.second) )); + } + localVarMultipart->add(ModelBase::toHttpContent(utility::conversions::to_string_t("{{paramName}}"), localVarJsonMap, utility::conversions::to_string_t("application/json"))); + }{{/isMap}} + {{^isArray}}{{^isMap}}{{#isString}}localVarMultipart->add(ModelBase::toHttpContent(utility::conversions::to_string_t("{{paramName}}"), {{paramName}})); + {{/isString}}{{^isString}}if({{^required}}{{paramName}} && (*{{paramName}}){{/required}}{{#required}}{{paramName}}{{/required}}.get()) + { + {{^required}}(*{{/required}}{{paramName}}{{^required}}){{/required}}->toMultipart(localVarMultipart, utility::conversions::to_string_t("{{paramName}}")); + } + {{/isString}} + {{/isMap}}{{/isArray}}{{/isPrimitiveType}} + + localVarHttpBody = localVarMultipart; + localVarRequestHttpContentType += utility::conversions::to_string_t("; boundary=") + localVarMultipart->getBoundary(); + {{/bodyParam}} + } + else if (localVarConsumeHttpContentTypes.find(utility::conversions::to_string_t("application/x-www-form-urlencoded")) != localVarConsumeHttpContentTypes.end()) + { + localVarRequestHttpContentType = utility::conversions::to_string_t("application/x-www-form-urlencoded"); + } + else + { + throw ApiException(415, utility::conversions::to_string_t("{{classname}}->{{operationId}} does not consume any supported media type")); + } + + {{#authMethods}} + // authentication ({{name}}) required + {{#isApiKey}} + {{#isKeyInHeader}} + { + utility::string_t localVarApiKey = localVarApiConfiguration->getApiKey(utility::conversions::to_string_t("{{keyParamName}}")); + if ( localVarApiKey.size() > 0 ) + { + localVarHeaderParams[utility::conversions::to_string_t("{{keyParamName}}")] = localVarApiKey; + } + } + {{/isKeyInHeader}} + {{#isKeyInQuery}} + { + utility::string_t localVarApiKey = localVarApiConfiguration->getApiKey(utility::conversions::to_string_t("{{keyParamName}}")); + if ( localVarApiKey.size() > 0 ) + { + localVarQueryParams[utility::conversions::to_string_t("{{keyParamName}}")] = localVarApiKey; + } + } + {{/isKeyInQuery}} + {{/isApiKey}} + {{#isBasicBasic}} + // Basic authentication is added automatically as part of the http_client_config + {{/isBasicBasic}} + {{#isOAuth}} + // oauth2 authentication is added automatically as part of the http_client_config + {{/isOAuth}} + {{/authMethods}} + + return m_ApiClient->callApi(localVarPath, utility::conversions::to_string_t("{{httpMethod}}"), localVarQueryParams, localVarHttpBody, localVarHeaderParams, localVarFormParams, localVarFileParams, localVarRequestHttpContentType) + .then([=, this](web::http::http_response localVarResponse) + { + if (m_ApiClient->getResponseHandler()) + { + m_ApiClient->getResponseHandler()(localVarResponse.status_code(), localVarResponse.headers()); + } + + // 1xx - informational : OK + // 2xx - successful : OK + // 3xx - redirection : OK + // 4xx - client error : not OK + // 5xx - client error : not OK + if (localVarResponse.status_code() >= 400) + { + throw ApiException(localVarResponse.status_code() + , utility::conversions::to_string_t("error calling {{operationId}}: ") + localVarResponse.reason_phrase() + , std::make_shared(localVarResponse.extract_utf8string(true).get())); + } + + // check response content type + if(localVarResponse.headers().has(utility::conversions::to_string_t("Content-Type"))) + { + utility::string_t localVarContentType = localVarResponse.headers()[utility::conversions::to_string_t("Content-Type")]; + if( localVarContentType.find(localVarResponseHttpContentType) == std::string::npos ) + { + throw ApiException(500 + , utility::conversions::to_string_t("error calling {{operationId}}: unexpected response type: ") + localVarContentType + , std::make_shared(localVarResponse.extract_utf8string(true).get())); + } + } + + {{#vendorExtensions.x-codegen-response-ishttpcontent}} + return localVarResponse.extract_vector(); + }) + .then([=, this](std::vector localVarResponse) + { + {{{returnType}}} localVarResult = std::make_shared(); + std::shared_ptr stream = std::make_shared(std::string(localVarResponse.begin(), localVarResponse.end())); + localVarResult->setData(stream); + return localVarResult; + {{/vendorExtensions.x-codegen-response-ishttpcontent}} + {{^vendorExtensions.x-codegen-response-ishttpcontent}} + return localVarResponse.extract_string(); + }) + .then([=, this](utility::string_t localVarResponse) + { + {{^returnType}} + return void(); + {{/returnType}} + {{#returnType}} + {{#returnContainer}} + {{{returnType}}} localVarResult; + {{/returnContainer}} + {{^returnContainer}} + {{{returnType}}} localVarResult({{{defaultResponse}}}); + {{/returnContainer}} + + if(localVarResponseHttpContentType == utility::conversions::to_string_t("application/json")) + { + web::json::value localVarJson = web::json::value::parse(localVarResponse); + {{#isArray}} + for( auto& localVarItem : localVarJson.as_array() ) + { + {{{vendorExtensions.x-codegen-response.items.datatype}}} localVarItemObj; + ModelBase::fromJson(localVarItem, localVarItemObj); + localVarResult.push_back(localVarItemObj); + }{{/isArray}}{{#isMap}} + for( auto& localVarItem : localVarJson.as_object() ) + { + {{{vendorExtensions.x-codegen-response.items.datatype}}} localVarItemObj; + ModelBase::fromJson(localVarItem.second, localVarItemObj); + localVarResult[localVarItem.first] = localVarItemObj; + }{{/isMap}}{{^isArray}}{{^isMap}} + ModelBase::fromJson(localVarJson, localVarResult);{{/isMap}}{{/isArray}} + }{{#vendorExtensions.x-codegen-response.isString}} + else if(localVarResponseHttpContentType == utility::conversions::to_string_t("text/plain")) + { + localVarResult = localVarResponse; + }{{/vendorExtensions.x-codegen-response.isString}} + // else if(localVarResponseHttpContentType == utility::conversions::to_string_t("multipart/form-data")) + // { + // TODO multipart response parsing + // } + else + { + throw ApiException(500 + , utility::conversions::to_string_t("error calling {{operationId}}: unsupported response type")); + } + + return localVarResult; + {{/returnType}} + {{/vendorExtensions.x-codegen-response-ishttpcontent}} + }); +} +{{/operation}} + +{{#apiNamespaceDeclarations}} +} +{{/apiNamespaceDeclarations}} + +{{/operations}} diff --git a/api/_build/my-cpp-templates/cpp-restsdk/api-source.mustache_old b/api/_build/my-cpp-templates/cpp-restsdk/api-source.mustache_old new file mode 100644 index 0000000..f4275fd --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/api-source.mustache_old @@ -0,0 +1,368 @@ +{{>licenseInfo}} +{{#operations}} + +#include "{{packageName}}/api/{{classname}}.h" +#include "{{packageName}}/IHttpBody.h" +#include "{{packageName}}/JsonBody.h" +#include "{{packageName}}/MultipartFormData.h" + +#include + +#include + +{{#apiNamespaceDeclarations}} +namespace {{this}} { +{{/apiNamespaceDeclarations}} + +using namespace {{modelNamespace}}; + +{{classname}}::{{classname}}( std::shared_ptr apiClient ) + : m_ApiClient(apiClient) +{ +} + +{{classname}}::~{{classname}}() +{ +} + +{{#operation}} +pplx::task<{{{returnType}}}{{^returnType}}void{{/returnType}}> {{classname}}::{{operationId}}({{#allParams}}{{^required}}boost::optional<{{/required}}{{{dataType}}}{{^required}}>{{/required}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) const +{ +{{#allParams}}{{#required}}{{^isPrimitiveType}}{{^isContainer}} + // verify the required parameter '{{paramName}}' is set + if ({{paramName}} == nullptr) + { + throw ApiException(400, utility::conversions::to_string_t("Missing required parameter '{{paramName}}' when calling {{classname}}->{{operationId}}")); + } +{{/isContainer}}{{/isPrimitiveType}}{{/required}}{{/allParams}} + + std::shared_ptr localVarApiConfiguration( m_ApiClient->getConfiguration() ); + utility::string_t localVarPath = utility::conversions::to_string_t("{{{path}}}"); + {{#pathParams}} + boost::replace_all(localVarPath, utility::conversions::to_string_t("{") + utility::conversions::to_string_t("{{baseName}}") + utility::conversions::to_string_t("}"), web::uri::encode_uri(ApiClient::parameterToString({{{paramName}}}))); + {{/pathParams}} + + std::map localVarQueryParams; + std::map localVarHeaderParams( localVarApiConfiguration->getDefaultHeaders() ); + std::map localVarFormParams; + std::map> localVarFileParams; + + std::unordered_set localVarResponseHttpContentTypes; + {{#produces}} + localVarResponseHttpContentTypes.insert( utility::conversions::to_string_t("{{{mediaType}}}") ); + {{/produces}} + + utility::string_t localVarResponseHttpContentType; + + // use JSON if possible + if ( localVarResponseHttpContentTypes.size() == 0 ) + { + {{#vendorExtensions.x-codegen-response.isString}} + localVarResponseHttpContentType = utility::conversions::to_string_t("text/plain"); + {{/vendorExtensions.x-codegen-response.isString}} + {{^vendorExtensions.x-codegen-response.isString}} + localVarResponseHttpContentType = utility::conversions::to_string_t("application/json"); + {{/vendorExtensions.x-codegen-response.isString}} + } + // JSON + else if ( localVarResponseHttpContentTypes.find(utility::conversions::to_string_t("application/json")) != localVarResponseHttpContentTypes.end() ) + { + localVarResponseHttpContentType = utility::conversions::to_string_t("application/json"); + } + // multipart formdata + else if( localVarResponseHttpContentTypes.find(utility::conversions::to_string_t("multipart/form-data")) != localVarResponseHttpContentTypes.end() ) + { + localVarResponseHttpContentType = utility::conversions::to_string_t("multipart/form-data"); + } + {{#vendorExtensions.x-codegen-response.isString}} + // plain text + else if( localVarResponseHttpContentTypes.find(utility::conversions::to_string_t("text/plain")) != localVarResponseHttpContentTypes.end() ) + { + localVarResponseHttpContentType = utility::conversions::to_string_t("text/plain"); + } + {{/vendorExtensions.x-codegen-response.isString}} + {{#vendorExtensions.x-codegen-response-ishttpcontent}} + else + { + //It's going to be binary, so just use the first one. + localVarResponseHttpContentType = *localVarResponseHttpContentTypes.begin(); + } + {{/vendorExtensions.x-codegen-response-ishttpcontent}} + {{^vendorExtensions.x-codegen-response-ishttpcontent}} + else + { + throw ApiException(400, utility::conversions::to_string_t("{{classname}}->{{operationId}} does not produce any supported media type")); + } + {{/vendorExtensions.x-codegen-response-ishttpcontent}} + + localVarHeaderParams[utility::conversions::to_string_t("Accept")] = localVarResponseHttpContentType; + + std::unordered_set localVarConsumeHttpContentTypes; + {{#consumes}} + localVarConsumeHttpContentTypes.insert( utility::conversions::to_string_t("{{{mediaType}}}") ); + {{/consumes}} + + {{#allParams}} + {{^isBodyParam}} + {{^isPathParam}} + {{#required}} + {{^isPrimitiveType}} + {{^isContainer}} + if ({{paramName}} != nullptr) + {{/isContainer}} + {{/isPrimitiveType}} + {{/required}} + {{^required}} + {{^isPrimitiveType}} + {{^isContainer}} + if ({{paramName}} && *{{paramName}} != nullptr) + {{/isContainer}} + {{/isPrimitiveType}} + {{#isPrimitiveType}} + {{#isFile}} + if ({{paramName}} && *{{paramName}} != nullptr) + {{/isFile}} + {{^isFile}} + if ({{paramName}}) + {{/isFile}} + {{/isPrimitiveType}} + {{#isContainer}} + if ({{paramName}}) + {{/isContainer}} + {{/required}} + { + {{#isQueryParam}} + localVarQueryParams[utility::conversions::to_string_t("{{baseName}}")] = ApiClient::parameterToString({{^required}}*{{/required}}{{paramName}}); + {{/isQueryParam}} + {{#isHeaderParam}} + localVarHeaderParams[utility::conversions::to_string_t("{{baseName}}")] = ApiClient::parameterToString({{^required}}*{{/required}}{{paramName}}); + {{/isHeaderParam}} + {{#isFormParam}} + {{#isFile}} + localVarFileParams[ utility::conversions::to_string_t("{{baseName}}") ] = {{^required}}*{{/required}}{{paramName}}; + {{/isFile}} + {{^isFile}} + localVarFormParams[ utility::conversions::to_string_t("{{baseName}}") ] = ApiClient::parameterToString({{^required}}*{{/required}}{{paramName}}); + {{/isFile}} + {{/isFormParam}} + } + {{/isPathParam}} + {{/isBodyParam}} + {{/allParams}} + + std::shared_ptr localVarHttpBody; + utility::string_t localVarRequestHttpContentType; + + // use JSON if possible + if ( localVarConsumeHttpContentTypes.size() == 0 || localVarConsumeHttpContentTypes.find(utility::conversions::to_string_t("application/json")) != localVarConsumeHttpContentTypes.end() ) + { + localVarRequestHttpContentType = utility::conversions::to_string_t("application/json"); + {{#bodyParam}} + web::json::value localVarJson; + + {{#isPrimitiveType}} + localVarJson = ModelBase::toJson({{paramName}}{{^required}}.get(){{/required}}); + {{/isPrimitiveType}} + {{^isPrimitiveType}} + {{#isArray}} + { + std::vector localVarJsonArray; + for( auto& localVarItem : {{paramName}}{{^required}}.get(){{/required}} ) + { + {{#items.isPrimitiveType}}localVarJsonArray.push_back(ModelBase::toJson(localVarItem)); + {{/items.isPrimitiveType}}{{^items.isPrimitiveType}}{{#items.isString}}localVarJsonArray.push_back(ModelBase::toJson(localVarItem)); + {{/items.isString}}{{^items.isString}}{{#items.isDateTime}}localVarJsonArray.push_back(ModelBase::toJson(localVarItem)); + {{/items.isDateTime}}{{^items.isDateTime}}localVarJsonArray.push_back( localVarItem.get() ? localVarItem->toJson() : web::json::value::null() ); + {{/items.isDateTime}}{{/items.isString}}{{/items.isPrimitiveType}} + } + localVarJson = web::json::value::array(localVarJsonArray); + } + {{/isArray}} + {{^isArray}}{{#required}}localVarJson = ModelBase::toJson({{paramName}}); + {{/required}}{{^required}}if ({{paramName}}) + localVarJson = ModelBase::toJson(*{{paramName}});{{/required}} + {{/isArray}} + {{/isPrimitiveType}} + + localVarHttpBody = std::shared_ptr( new JsonBody( localVarJson ) ); + {{/bodyParam}} + } + // multipart formdata + else if( localVarConsumeHttpContentTypes.find(utility::conversions::to_string_t("multipart/form-data")) != localVarConsumeHttpContentTypes.end() ) + { + localVarRequestHttpContentType = utility::conversions::to_string_t("multipart/form-data"); + {{#bodyParam}} + std::shared_ptr localVarMultipart(new MultipartFormData); + {{#isPrimitiveType}} + localVarMultipart->add(ModelBase::toHttpContent(utility::conversions::to_string_t("{{paramName}}"), {{paramName}}{{^required}}.get(){{/required}})); + {{/isPrimitiveType}}{{^isPrimitiveType}}{{#isArray}} + { + std::vector localVarJsonArray; + for( auto& localVarItem : {{paramName}}{{^required}}.get(){{/required}} ) + { + localVarJsonArray.push_back(ModelBase::toJson(localVarItem)); + } + localVarMultipart->add(ModelBase::toHttpContent(utility::conversions::to_string_t("{{paramName}}"), localVarJsonArray, utility::conversions::to_string_t("application/json"))); + }{{/isArray}}{{#isMap}} + { + std::map localVarJsonMap; + for( auto& localVarItem : {{paramName}}{{^required}}.get(){{/required}} ) + { + web::json::value jval; + localVarJsonMap.insert( std::pair(localVarItem.first, ModelBase::toJson(localVarItem.second) )); + } + localVarMultipart->add(ModelBase::toHttpContent(utility::conversions::to_string_t("{{paramName}}"), localVarJsonMap, utility::conversions::to_string_t("application/json"))); + }{{/isMap}} + {{^isArray}}{{^isMap}}{{#isString}}localVarMultipart->add(ModelBase::toHttpContent(utility::conversions::to_string_t("{{paramName}}"), {{paramName}})); + {{/isString}}{{^isString}}if({{^required}}{{paramName}} && (*{{paramName}}){{/required}}{{#required}}{{paramName}}{{/required}}.get()) + { + {{^required}}(*{{/required}}{{paramName}}{{^required}}){{/required}}->toMultipart(localVarMultipart, utility::conversions::to_string_t("{{paramName}}")); + } + {{/isString}} + {{/isMap}}{{/isArray}}{{/isPrimitiveType}} + + localVarHttpBody = localVarMultipart; + localVarRequestHttpContentType += utility::conversions::to_string_t("; boundary=") + localVarMultipart->getBoundary(); + {{/bodyParam}} + } + else if (localVarConsumeHttpContentTypes.find(utility::conversions::to_string_t("application/x-www-form-urlencoded")) != localVarConsumeHttpContentTypes.end()) + { + localVarRequestHttpContentType = utility::conversions::to_string_t("application/x-www-form-urlencoded"); + } + else + { + throw ApiException(415, utility::conversions::to_string_t("{{classname}}->{{operationId}} does not consume any supported media type")); + } + + {{#authMethods}} + // authentication ({{name}}) required + {{#isApiKey}} + {{#isKeyInHeader}} + { + utility::string_t localVarApiKey = localVarApiConfiguration->getApiKey(utility::conversions::to_string_t("{{keyParamName}}")); + if ( localVarApiKey.size() > 0 ) + { + localVarHeaderParams[utility::conversions::to_string_t("{{keyParamName}}")] = localVarApiKey; + } + } + {{/isKeyInHeader}} + {{#isKeyInQuery}} + { + utility::string_t localVarApiKey = localVarApiConfiguration->getApiKey(utility::conversions::to_string_t("{{keyParamName}}")); + if ( localVarApiKey.size() > 0 ) + { + localVarQueryParams[utility::conversions::to_string_t("{{keyParamName}}")] = localVarApiKey; + } + } + {{/isKeyInQuery}} + {{/isApiKey}} + {{#isBasicBasic}} + // Basic authentication is added automatically as part of the http_client_config + {{/isBasicBasic}} + {{#isOAuth}} + // oauth2 authentication is added automatically as part of the http_client_config + {{/isOAuth}} + {{/authMethods}} + + return m_ApiClient->callApi(localVarPath, utility::conversions::to_string_t("{{httpMethod}}"), localVarQueryParams, localVarHttpBody, localVarHeaderParams, localVarFormParams, localVarFileParams, localVarRequestHttpContentType) + .then([=, this](web::http::http_response localVarResponse) + { + if (m_ApiClient->getResponseHandler()) + { + m_ApiClient->getResponseHandler()(localVarResponse.status_code(), localVarResponse.headers()); + } + + // 1xx - informational : OK + // 2xx - successful : OK + // 3xx - redirection : OK + // 4xx - client error : not OK + // 5xx - client error : not OK + if (localVarResponse.status_code() >= 400) + { + throw ApiException(localVarResponse.status_code() + , utility::conversions::to_string_t("error calling {{operationId}}: ") + localVarResponse.reason_phrase() + , std::make_shared(localVarResponse.extract_utf8string(true).get())); + } + + // check response content type + if(localVarResponse.headers().has(utility::conversions::to_string_t("Content-Type"))) + { + utility::string_t localVarContentType = localVarResponse.headers()[utility::conversions::to_string_t("Content-Type")]; + if( localVarContentType.find(localVarResponseHttpContentType) == std::string::npos ) + { + throw ApiException(500 + , utility::conversions::to_string_t("error calling {{operationId}}: unexpected response type: ") + localVarContentType + , std::make_shared(localVarResponse.extract_utf8string(true).get())); + } + } + + {{#vendorExtensions.x-codegen-response-ishttpcontent}} + return localVarResponse.extract_vector(); + }) + .then([=, this](std::vector localVarResponse) + { + {{{returnType}}} localVarResult = std::make_shared(); + std::shared_ptr stream = std::make_shared(std::string(localVarResponse.begin(), localVarResponse.end())); + localVarResult->setData(stream); + return localVarResult; + {{/vendorExtensions.x-codegen-response-ishttpcontent}} + {{^vendorExtensions.x-codegen-response-ishttpcontent}} + return localVarResponse.extract_string(); + }) + .then([=, this](utility::string_t localVarResponse) + { + {{^returnType}} + return void(); + {{/returnType}} + {{#returnType}} + {{#returnContainer}} + {{{returnType}}} localVarResult; + {{/returnContainer}} + {{^returnContainer}} + {{{returnType}}} localVarResult({{{defaultResponse}}}); + {{/returnContainer}} + + if(localVarResponseHttpContentType == utility::conversions::to_string_t("application/json")) + { + web::json::value localVarJson = web::json::value::parse(localVarResponse); + {{#isArray}} + for( auto& localVarItem : localVarJson.as_array() ) + { + {{{vendorExtensions.x-codegen-response.items.datatype}}} localVarItemObj; + ModelBase::fromJson(localVarItem, localVarItemObj); + localVarResult.push_back(localVarItemObj); + }{{/isArray}}{{#isMap}} + for( auto& localVarItem : localVarJson.as_object() ) + { + {{{vendorExtensions.x-codegen-response.items.datatype}}} localVarItemObj; + ModelBase::fromJson(localVarItem.second, localVarItemObj); + localVarResult[localVarItem.first] = localVarItemObj; + }{{/isMap}}{{^isArray}}{{^isMap}} + ModelBase::fromJson(localVarJson, localVarResult);{{/isMap}}{{/isArray}} + }{{#vendorExtensions.x-codegen-response.isString}} + else if(localVarResponseHttpContentType == utility::conversions::to_string_t("text/plain")) + { + localVarResult = localVarResponse; + }{{/vendorExtensions.x-codegen-response.isString}} + // else if(localVarResponseHttpContentType == utility::conversions::to_string_t("multipart/form-data")) + // { + // TODO multipart response parsing + // } + else + { + throw ApiException(500 + , utility::conversions::to_string_t("error calling {{operationId}}: unsupported response type")); + } + + return localVarResult; + {{/returnType}} + {{/vendorExtensions.x-codegen-response-ishttpcontent}} + }); +} +{{/operation}} + +{{#apiNamespaceDeclarations}} +} +{{/apiNamespaceDeclarations}} + +{{/operations}} diff --git a/api/_build/my-cpp-templates/cpp-restsdk/apiclient-header.mustache b/api/_build/my-cpp-templates/cpp-restsdk/apiclient-header.mustache new file mode 100644 index 0000000..73eee1d --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/apiclient-header.mustache @@ -0,0 +1,107 @@ +{{>licenseInfo}} +/* + * ApiClient.h + * + * This is an API client responsible for stating the HTTP calls + */ + +#ifndef {{apiHeaderGuardPrefix}}_ApiClient_H_ +#define {{apiHeaderGuardPrefix}}_ApiClient_H_ + +{{{defaultInclude}}} +#include "{{packageName}}/ApiConfiguration.h" +#include "{{packageName}}/ApiException.h" +#include "{{packageName}}/IHttpBody.h" +#include "{{packageName}}/HttpContent.h" +{{^hasModelImport}} +#include "{{packageName}}/ModelBase.h" +{{/hasModelImport}} +#if defined (_WIN32) || defined (_WIN64) +#undef U +#endif + +#include +#include + +#include +#include +#include + +{{#apiNamespaceDeclarations}} +namespace {{this}} { +{{/apiNamespaceDeclarations}} + +using namespace {{modelNamespace}}; + +class {{declspec}} ApiClient +{ +public: + ApiClient( std::shared_ptr configuration = nullptr ); + virtual ~ApiClient(); + + typedef std::function ResponseHandlerType; + + const ResponseHandlerType& getResponseHandler() const; + void setResponseHandler(const ResponseHandlerType& responseHandler); + + std::shared_ptr getConfiguration() const; + void setConfiguration(std::shared_ptr configuration); + + static utility::string_t parameterToString(utility::string_t value); + static utility::string_t parameterToString(int32_t value); + static utility::string_t parameterToString(int64_t value); + static utility::string_t parameterToString(float value); + static utility::string_t parameterToString(double value); + static utility::string_t parameterToString(const utility::datetime &value); + static utility::string_t parameterToString(bool value); + {{^hasModelImport}} + static utility::string_t parameterToString(const ModelBase& value); + {{/hasModelImport}} + template + static utility::string_t parameterToString(const std::vector& value); + template + static utility::string_t parameterToString(const std::shared_ptr& value); + + pplx::task callApi( + const utility::string_t& path, + const utility::string_t& method, + const std::map& queryParams, + const std::shared_ptr postBody, + const std::map& headerParams, + const std::map& formParams, + const std::map>& fileParams, + const utility::string_t& contentType + ) const; + +protected: + + ResponseHandlerType m_ResponseHandler; + std::shared_ptr m_Configuration; +}; + +template +utility::string_t ApiClient::parameterToString(const std::vector& value) +{ + utility::stringstream_t ss; + + for( size_t i = 0; i < value.size(); i++) + { + if( i > 0) ss << utility::conversions::to_string_t(", "); + ss << ApiClient::parameterToString(value[i]); + } + + return ss.str(); +} + +template +utility::string_t ApiClient::parameterToString(const std::shared_ptr& value) +{ + return parameterToString(*value.get()); +} + + +{{#apiNamespaceDeclarations}} +} +{{/apiNamespaceDeclarations}} + +#endif /* {{apiHeaderGuardPrefix}}_ApiClient_H_ */ diff --git a/api/_build/my-cpp-templates/cpp-restsdk/apiclient-source.mustache b/api/_build/my-cpp-templates/cpp-restsdk/apiclient-source.mustache new file mode 100644 index 0000000..c69b388 --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/apiclient-source.mustache @@ -0,0 +1,203 @@ +{{>licenseInfo}} +#include "{{packageName}}/ApiClient.h" +#include "{{packageName}}/MultipartFormData.h" +#include "{{packageName}}/ModelBase.h" + +#include +#include +#include + +template +utility::string_t toString(const T value) +{ + utility::ostringstream_t out; + out << std::setprecision(std::numeric_limits::digits10) << std::fixed << value; + return out.str(); +} + +{{#apiNamespaceDeclarations}} +namespace {{this}} { +{{/apiNamespaceDeclarations}} + +using namespace {{modelNamespace}}; + +ApiClient::ApiClient(std::shared_ptr configuration ) + : m_Configuration(configuration) +{ +} +ApiClient::~ApiClient() +{ +} + +const ApiClient::ResponseHandlerType& ApiClient::getResponseHandler() const { + return m_ResponseHandler; +} + +void ApiClient::setResponseHandler(const ResponseHandlerType& responseHandler) { + m_ResponseHandler = responseHandler; +} + +std::shared_ptr ApiClient::getConfiguration() const +{ + return m_Configuration; +} +void ApiClient::setConfiguration(std::shared_ptr configuration) +{ + m_Configuration = configuration; +} + + +utility::string_t ApiClient::parameterToString(utility::string_t value) +{ + return value; +} +utility::string_t ApiClient::parameterToString(int64_t value) +{ + std::stringstream valueAsStringStream; + valueAsStringStream << value; + return utility::conversions::to_string_t(valueAsStringStream.str()); +} +utility::string_t ApiClient::parameterToString(int32_t value) +{ + std::stringstream valueAsStringStream; + valueAsStringStream << value; + return utility::conversions::to_string_t(valueAsStringStream.str()); +} + +utility::string_t ApiClient::parameterToString(float value) +{ + return utility::conversions::to_string_t(toString(value)); +} + +utility::string_t ApiClient::parameterToString(double value) +{ + return utility::conversions::to_string_t(toString(value)); +} + +utility::string_t ApiClient::parameterToString(const utility::datetime &value) +{ + return utility::conversions::to_string_t(value.to_string(utility::datetime::ISO_8601)); +} + +{{^hasModelImport}} +utility::string_t ApiClient::parameterToString(const ModelBase& value) +{ + return value.toJson().serialize(); +} +{{/hasModelImport}} + +utility::string_t ApiClient::parameterToString(bool value) +{ + std::stringstream valueAsStringStream; + valueAsStringStream << std::boolalpha << value; + return utility::conversions::to_string_t(valueAsStringStream.str()); +} + +pplx::task ApiClient::callApi( + const utility::string_t& path, + const utility::string_t& method, + const std::map& queryParams, + const std::shared_ptr postBody, + const std::map& headerParams, + const std::map& formParams, + const std::map>& fileParams, + const utility::string_t& contentType +) const +{ + if (postBody != nullptr && formParams.size() != 0) + { + throw ApiException(400, utility::conversions::to_string_t("Cannot have body and form params")); + } + + if (postBody != nullptr && fileParams.size() != 0) + { + throw ApiException(400, utility::conversions::to_string_t("Cannot have body and file params")); + } + + if (fileParams.size() > 0 && contentType != utility::conversions::to_string_t("multipart/form-data")) + { + throw ApiException(400, utility::conversions::to_string_t("Operations with file parameters must be called with multipart/form-data")); + } + + web::http::client::http_client client(m_Configuration->getBaseUrl(), m_Configuration->getHttpConfig()); + + web::http::http_request request; + for (const auto& kvp : headerParams) + { + request.headers().add(kvp.first, kvp.second); + } + + if (fileParams.size() > 0) + { + MultipartFormData uploadData; + for (const auto& kvp : formParams) + { + uploadData.add(ModelBase::toHttpContent(kvp.first, kvp.second)); + } + for (const auto& kvp : fileParams) + { + uploadData.add(ModelBase::toHttpContent(kvp.first, kvp.second)); + } + std::stringstream data; + uploadData.writeTo(data); + auto bodyString = data.str(); + const auto length = bodyString.size(); + request.set_body(concurrency::streams::bytestream::open_istream(std::move(bodyString)), length, utility::conversions::to_string_t("multipart/form-data; boundary=") + uploadData.getBoundary()); + } + else + { + if (postBody != nullptr) + { + std::stringstream data; + postBody->writeTo(data); + auto bodyString = data.str(); + const auto length = bodyString.size(); + request.set_body(concurrency::streams::bytestream::open_istream(std::move(bodyString)), length, contentType); + } + else + { + if (contentType == utility::conversions::to_string_t("application/json")) + { + web::json::value body_data = web::json::value::object(); + for (auto& kvp : formParams) + { + body_data[kvp.first] = ModelBase::toJson(kvp.second); + } + if (!formParams.empty()) + { + request.set_body(body_data); + } + } + else + { + web::http::uri_builder formData; + for (const auto& kvp : formParams) + { + formData.append_query(kvp.first, kvp.second); + } + if (!formParams.empty()) + { + request.set_body(formData.query(), utility::conversions::to_string_t("application/x-www-form-urlencoded")); + } + } + } + } + + web::http::uri_builder builder(path); + for (const auto& kvp : queryParams) + { + builder.append_query(kvp.first, kvp.second); + } + request.set_request_uri(builder.to_uri()); + request.set_method(method); + if ( !request.headers().has( web::http::header_names::user_agent ) ) + { + request.headers().add( web::http::header_names::user_agent, m_Configuration->getUserAgent() ); + } + + return client.request(request); +} + +{{#apiNamespaceDeclarations}} +} +{{/apiNamespaceDeclarations}} diff --git a/api/_build/my-cpp-templates/cpp-restsdk/apiconfiguration-header.mustache b/api/_build/my-cpp-templates/cpp-restsdk/apiconfiguration-header.mustache new file mode 100644 index 0000000..1408831 --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/apiconfiguration-header.mustache @@ -0,0 +1,54 @@ +{{>licenseInfo}} +/* + * ApiConfiguration.h + * + * This class represents a single item of a multipart-formdata request. + */ + +#ifndef {{apiHeaderGuardPrefix}}_ApiConfiguration_H_ +#define {{apiHeaderGuardPrefix}}_ApiConfiguration_H_ + +{{{defaultInclude}}} + +#include +#include + +#include + +{{#apiNamespaceDeclarations}} +namespace {{this}} { +{{/apiNamespaceDeclarations}} + +class {{declspec}} ApiConfiguration +{ +public: + ApiConfiguration(); + virtual ~ApiConfiguration(); + + const web::http::client::http_client_config& getHttpConfig() const; + void setHttpConfig( web::http::client::http_client_config& value ); + + utility::string_t getBaseUrl() const; + void setBaseUrl( const utility::string_t value ); + + utility::string_t getUserAgent() const; + void setUserAgent( const utility::string_t value ); + + std::map& getDefaultHeaders(); + const std::map& getDefaultHeaders() const; + + utility::string_t getApiKey( const utility::string_t& prefix) const; + void setApiKey( const utility::string_t& prefix, const utility::string_t& apiKey ); + +protected: + utility::string_t m_BaseUrl; + std::map m_DefaultHeaders; + std::map m_ApiKeys; + web::http::client::http_client_config m_HttpConfig; + utility::string_t m_UserAgent; +}; + +{{#apiNamespaceDeclarations}} +} +{{/apiNamespaceDeclarations}} +#endif /* {{apiHeaderGuardPrefix}}_ApiConfiguration_H_ */ diff --git a/api/_build/my-cpp-templates/cpp-restsdk/apiconfiguration-source.mustache b/api/_build/my-cpp-templates/cpp-restsdk/apiconfiguration-source.mustache new file mode 100644 index 0000000..365c55c --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/apiconfiguration-source.mustache @@ -0,0 +1,73 @@ +{{>licenseInfo}} +#include "{{packageName}}/ApiConfiguration.h" + +{{#apiNamespaceDeclarations}} +namespace {{this}} { +{{/apiNamespaceDeclarations}} + +ApiConfiguration::ApiConfiguration() +{ +} + +ApiConfiguration::~ApiConfiguration() +{ +} + +const web::http::client::http_client_config& ApiConfiguration::getHttpConfig() const +{ + return m_HttpConfig; +} + +void ApiConfiguration::setHttpConfig( web::http::client::http_client_config& value ) +{ + m_HttpConfig = value; +} + +utility::string_t ApiConfiguration::getBaseUrl() const +{ + return m_BaseUrl; +} + +void ApiConfiguration::setBaseUrl( const utility::string_t value ) +{ + m_BaseUrl = value; +} + +utility::string_t ApiConfiguration::getUserAgent() const +{ + return m_UserAgent; +} + +void ApiConfiguration::setUserAgent( const utility::string_t value ) +{ + m_UserAgent = value; +} + +std::map& ApiConfiguration::getDefaultHeaders() +{ + return m_DefaultHeaders; +} + +const std::map& ApiConfiguration::getDefaultHeaders() const +{ + return m_DefaultHeaders; +} + +utility::string_t ApiConfiguration::getApiKey( const utility::string_t& prefix) const +{ + auto result = m_ApiKeys.find(prefix); + if( result != m_ApiKeys.end() ) + { + return result->second; + } + return utility::conversions::to_string_t(""); +} + +void ApiConfiguration::setApiKey( const utility::string_t& prefix, const utility::string_t& apiKey ) +{ + m_ApiKeys[prefix] = apiKey; +} + +{{#apiNamespaceDeclarations}} +} +{{/apiNamespaceDeclarations}} diff --git a/api/_build/my-cpp-templates/cpp-restsdk/apiexception-header.mustache b/api/_build/my-cpp-templates/cpp-restsdk/apiexception-header.mustache new file mode 100644 index 0000000..5383a2d --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/apiexception-header.mustache @@ -0,0 +1,48 @@ +{{>licenseInfo}} +/* + * ApiException.h + * + * This is the exception being thrown in case the api call was not successful + */ + +#ifndef {{apiHeaderGuardPrefix}}_ApiException_H_ +#define {{apiHeaderGuardPrefix}}_ApiException_H_ + +{{{defaultInclude}}} + +#include +#include + +#include +#include + +{{#apiNamespaceDeclarations}} +namespace {{this}} { +{{/apiNamespaceDeclarations}} + +class {{declspec}} ApiException + : public web::http::http_exception +{ +public: + ApiException( int errorCode + , const utility::string_t& message + , std::shared_ptr content = nullptr ); + ApiException( int errorCode + , const utility::string_t& message + , std::map& headers + , std::shared_ptr content = nullptr ); + virtual ~ApiException(); + + std::map& getHeaders(); + std::shared_ptr getContent() const; + +protected: + std::shared_ptr m_Content; + std::map m_Headers; +}; + +{{#apiNamespaceDeclarations}} +} +{{/apiNamespaceDeclarations}} + +#endif /* {{apiHeaderGuardPrefix}}_ApiBase_H_ */ diff --git a/api/_build/my-cpp-templates/cpp-restsdk/apiexception-source.mustache b/api/_build/my-cpp-templates/cpp-restsdk/apiexception-source.mustache new file mode 100644 index 0000000..3ca444e --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/apiexception-source.mustache @@ -0,0 +1,41 @@ +{{>licenseInfo}} +#include "{{packageName}}/ApiException.h" + +{{#apiNamespaceDeclarations}} +namespace {{this}} { +{{/apiNamespaceDeclarations}} + +ApiException::ApiException( int errorCode + , const utility::string_t& message + , std::shared_ptr content /*= nullptr*/ ) + : web::http::http_exception( errorCode, message ) + , m_Content(content) +{ +} +ApiException::ApiException( int errorCode + , const utility::string_t& message + , std::map& headers + , std::shared_ptr content /*= nullptr*/ ) + : web::http::http_exception( errorCode, message ) + , m_Content(content) + , m_Headers(headers) +{ +} + +ApiException::~ApiException() +{ +} + +std::shared_ptr ApiException::getContent() const +{ + return m_Content; +} + +std::map& ApiException::getHeaders() +{ + return m_Headers; +} + +{{#apiNamespaceDeclarations}} +} +{{/apiNamespaceDeclarations}} diff --git a/api/_build/my-cpp-templates/cpp-restsdk/cmake-config.mustache b/api/_build/my-cpp-templates/cpp-restsdk/cmake-config.mustache new file mode 100644 index 0000000..9015c2b --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/cmake-config.mustache @@ -0,0 +1,5 @@ +@PACKAGE_INIT@ + +include(${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake) + +check_required_components("@PROJECT_NAME@") diff --git a/api/_build/my-cpp-templates/cpp-restsdk/cmake-lists.mustache b/api/_build/my-cpp-templates/cpp-restsdk/cmake-lists.mustache new file mode 100644 index 0000000..7b7225c --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/cmake-lists.mustache @@ -0,0 +1,91 @@ +# +# {{{appName}}} +# {{{appDescription}}} +# +# The version of the OpenAPI document: 1.0.0 +# +# https://openapi-generator.tech +# +# NOTE: Auto generated by OpenAPI Generator (https://openapi-generator.tech). + +cmake_minimum_required (VERSION 3.10) + +project({{{packageName}}} CXX) + +# Force -fPIC even if the project is configured for building a static library. +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +set(CXX_STANDARD_REQUIRED ON) +if(NOT CMAKE_CXX_STANDARD) + if(DEFINED CMAKE_CXX20_STANDARD_COMPILE_OPTION OR + DEFINED CMAKE_CXX20_EXTENSION_COMPILE_OPTION) + set(CMAKE_CXX_STANDARD 20) + elseif(DEFINED CMAKE_CXX17_STANDARD_COMPILE_OPTION OR + DEFINED CMAKE_CXX17_EXTENSION_COMPILE_OPTION) + set(CMAKE_CXX_STANDARD 17) + elseif(DEFINED CMAKE_CXX14_STANDARD_COMPILE_OPTION OR + DEFINED CMAKE_CXX14_EXTENSION_COMPILE_OPTION) + set(CMAKE_CXX_STANDARD 14) + else() + set(CMAKE_CXX_STANDARD 11) + endif() +endif() + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +find_package(cpprestsdk REQUIRED) +target_compile_definitions(cpprestsdk::cpprest INTERFACE _TURN_OFF_PLATFORM_STRING) +find_package(Boost REQUIRED) + +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +file(GLOB_RECURSE HEADER_FILES "include/*.h") +file(GLOB_RECURSE SOURCE_FILES "src/*.cpp") + +add_library(${PROJECT_NAME} ${HEADER_FILES} ${SOURCE_FILES}) + +target_compile_options(${PROJECT_NAME} + PRIVATE + $<$,$,$>: + -Wall -Wno-unused-variable -Wno-unused-lambda-capture> +) + +target_include_directories(${PROJECT_NAME} + PUBLIC + $ + $ +) + +target_link_libraries(${PROJECT_NAME} PUBLIC Boost::headers cpprestsdk::cpprest) + +configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" +) + +install( + TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Targets + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" +) + +install( + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/${PROJECT_NAME} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) + +install( + FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} +) + +install( + EXPORT ${PROJECT_NAME}Targets + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} +) diff --git a/api/_build/my-cpp-templates/cpp-restsdk/git_push.sh.mustache b/api/_build/my-cpp-templates/cpp-restsdk/git_push.sh.mustache new file mode 100644 index 0000000..0e3776a --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/git_push.sh.mustache @@ -0,0 +1,57 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 +git_host=$4 + +if [ "$git_host" = "" ]; then + git_host="{{{gitHost}}}" + echo "[INFO] No command line input provided. Set \$git_host to $git_host" +fi + +if [ "$git_user_id" = "" ]; then + git_user_id="{{{gitUserId}}}" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="{{{gitRepoId}}}" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="{{{releaseNote}}}" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=$(git remote) +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' diff --git a/api/_build/my-cpp-templates/cpp-restsdk/gitignore.mustache b/api/_build/my-cpp-templates/cpp-restsdk/gitignore.mustache new file mode 100644 index 0000000..4581ef2 --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/gitignore.mustache @@ -0,0 +1,29 @@ +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app diff --git a/api/_build/my-cpp-templates/cpp-restsdk/httpcontent-header.mustache b/api/_build/my-cpp-templates/cpp-restsdk/httpcontent-header.mustache new file mode 100644 index 0000000..cae6a0e --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/httpcontent-header.mustache @@ -0,0 +1,57 @@ +{{>licenseInfo}} +/* + * HttpContent.h + * + * This class represents a single item of a multipart-formdata request. + */ + +#ifndef {{modelHeaderGuardPrefix}}_HttpContent_H_ +#define {{modelHeaderGuardPrefix}}_HttpContent_H_ + +{{{defaultInclude}}} + +#include + +#include + +{{#modelNamespaceDeclarations}} +namespace {{this}} { +{{/modelNamespaceDeclarations}} + +class {{declspec}} HttpContent +{ +public: + HttpContent(); + virtual ~HttpContent(); + + virtual utility::string_t getContentDisposition() const; + virtual void setContentDisposition( const utility::string_t& value ); + + virtual utility::string_t getName() const; + virtual void setName( const utility::string_t& value ); + + virtual utility::string_t getFileName() const; + virtual void setFileName( const utility::string_t& value ); + + virtual utility::string_t getContentType() const; + virtual void setContentType( const utility::string_t& value ); + + virtual std::shared_ptr getData() const; + virtual void setData( std::shared_ptr value ); + + virtual void writeTo( std::ostream& stream ); + +protected: + // NOTE: no utility::string_t here because those strings can only contain ascii + utility::string_t m_ContentDisposition; + utility::string_t m_Name; + utility::string_t m_FileName; + utility::string_t m_ContentType; + std::shared_ptr m_Data; +}; + +{{#modelNamespaceDeclarations}} +} +{{/modelNamespaceDeclarations}} + +#endif /* {{modelHeaderGuardPrefix}}_HttpContent_H_ */ diff --git a/api/_build/my-cpp-templates/cpp-restsdk/httpcontent-source.mustache b/api/_build/my-cpp-templates/cpp-restsdk/httpcontent-source.mustache new file mode 100644 index 0000000..f9739c5 --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/httpcontent-source.mustache @@ -0,0 +1,74 @@ +{{>licenseInfo}} +#include "{{packageName}}/HttpContent.h" + +{{#modelNamespaceDeclarations}} +namespace {{this}} { +{{/modelNamespaceDeclarations}} + +HttpContent::HttpContent() +{ +} + +HttpContent::~HttpContent() +{ +} + +utility::string_t HttpContent::getContentDisposition() const +{ + return m_ContentDisposition; +} + +void HttpContent::setContentDisposition( const utility::string_t & value ) +{ + m_ContentDisposition = value; +} + +utility::string_t HttpContent::getName() const +{ + return m_Name; +} + +void HttpContent::setName( const utility::string_t & value ) +{ + m_Name = value; +} + +utility::string_t HttpContent::getFileName() const +{ + return m_FileName; +} + +void HttpContent::setFileName( const utility::string_t & value ) +{ + m_FileName = value; +} + +utility::string_t HttpContent::getContentType() const +{ + return m_ContentType; +} + +void HttpContent::setContentType( const utility::string_t & value ) +{ + m_ContentType = value; +} + +std::shared_ptr HttpContent::getData() const +{ + return m_Data; +} + +void HttpContent::setData( std::shared_ptr value ) +{ + m_Data = value; +} + +void HttpContent::writeTo( std::ostream& stream ) +{ + m_Data->seekg( 0, m_Data->beg ); + stream << m_Data->rdbuf(); +} + +{{#modelNamespaceDeclarations}} +} +{{/modelNamespaceDeclarations}} diff --git a/api/_build/my-cpp-templates/cpp-restsdk/ihttpbody-header.mustache b/api/_build/my-cpp-templates/cpp-restsdk/ihttpbody-header.mustache new file mode 100644 index 0000000..e9c77b2 --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/ihttpbody-header.mustache @@ -0,0 +1,30 @@ +{{>licenseInfo}} +/* + * IHttpBody.h + * + * This is the interface for contents that can be sent to a remote HTTP server. + */ + +#ifndef {{modelHeaderGuardPrefix}}_IHttpBody_H_ +#define {{modelHeaderGuardPrefix}}_IHttpBody_H_ + +{{{defaultInclude}}} +#include + +{{#modelNamespaceDeclarations}} +namespace {{this}} { +{{/modelNamespaceDeclarations}} + +class {{declspec}} IHttpBody +{ +public: + virtual ~IHttpBody() { } + + virtual void writeTo( std::ostream& stream ) = 0; +}; + +{{#modelNamespaceDeclarations}} +} +{{/modelNamespaceDeclarations}} + +#endif /* {{modelHeaderGuardPrefix}}_IHttpBody_H_ */ diff --git a/api/_build/my-cpp-templates/cpp-restsdk/jsonbody-header.mustache b/api/_build/my-cpp-templates/cpp-restsdk/jsonbody-header.mustache new file mode 100644 index 0000000..7c6ac16 --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/jsonbody-header.mustache @@ -0,0 +1,37 @@ +{{>licenseInfo}} +/* + * JsonBody.h + * + * This is a JSON http body which can be submitted via http + */ + +#ifndef {{modelHeaderGuardPrefix}}_JsonBody_H_ +#define {{modelHeaderGuardPrefix}}_JsonBody_H_ + +{{{defaultInclude}}} +#include "{{packageName}}/IHttpBody.h" + +#include + +{{#modelNamespaceDeclarations}} +namespace {{this}} { +{{/modelNamespaceDeclarations}} + +class {{declspec}} JsonBody + : public IHttpBody +{ +public: + JsonBody( const web::json::value& value ); + virtual ~JsonBody(); + + void writeTo( std::ostream& target ) override; + +protected: + web::json::value m_Json; +}; + +{{#modelNamespaceDeclarations}} +} +{{/modelNamespaceDeclarations}} + +#endif /* {{modelHeaderGuardPrefix}}_JsonBody_H_ */ diff --git a/api/_build/my-cpp-templates/cpp-restsdk/jsonbody-source.mustache b/api/_build/my-cpp-templates/cpp-restsdk/jsonbody-source.mustache new file mode 100644 index 0000000..4129111 --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/jsonbody-source.mustache @@ -0,0 +1,24 @@ +{{>licenseInfo}} +#include "{{packageName}}/JsonBody.h" + +{{#modelNamespaceDeclarations}} +namespace {{this}} { +{{/modelNamespaceDeclarations}} + +JsonBody::JsonBody( const web::json::value& json) + : m_Json(json) +{ +} + +JsonBody::~JsonBody() +{ +} + +void JsonBody::writeTo( std::ostream& target ) +{ + m_Json.serialize(target); +} + +{{#modelNamespaceDeclarations}} +} +{{/modelNamespaceDeclarations}} diff --git a/api/_build/my-cpp-templates/cpp-restsdk/licenseInfo.mustache b/api/_build/my-cpp-templates/cpp-restsdk/licenseInfo.mustache new file mode 100644 index 0000000..4b9c44b --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/licenseInfo.mustache @@ -0,0 +1,15 @@ +/** + * {{{appName}}} + * {{{appDescription}}} + * + {{#version}} + * The version of the OpenAPI document: {{{.}}} + {{/version}} + {{#infoEmail}} + * Contact: {{{.}}} + {{/infoEmail}} + * + * NOTE: This class is auto generated by OpenAPI-Generator {{{generatorVersion}}}. + * https://openapi-generator.tech + * Do not edit the class manually. + */ diff --git a/api/_build/my-cpp-templates/cpp-restsdk/model-header.mustache b/api/_build/my-cpp-templates/cpp-restsdk/model-header.mustache new file mode 100644 index 0000000..825b73a --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/model-header.mustache @@ -0,0 +1,288 @@ +{{>licenseInfo}} +{{#models}}{{#model}}/* + * {{classname}}.h + * + * {{description}} + */ + +#ifndef {{modelHeaderGuardPrefix}}_{{classname}}_H_ +#define {{modelHeaderGuardPrefix}}_{{classname}}_H_ + +{{#hasEnums}} +#include +{{/hasEnums}} +{{#oneOf}} +{{#-first}} +#include +{{/-first}} +{{/oneOf}} +{{^parent}} +{{{defaultInclude}}} +#include "{{packageName}}/ModelBase.h" +{{/parent}} + +{{#imports}}{{{this}}} +{{/imports}} + +{{#modelNamespaceDeclarations}} +namespace {{this}} { +{{/modelNamespaceDeclarations}} + +{{#vendorExtensions.x-has-forward-declarations}} +{{#vendorExtensions.x-forward-declarations}}{{.}} +{{/vendorExtensions.x-forward-declarations}} +{{/vendorExtensions.x-has-forward-declarations}} +{{#oneOf}}{{#-first}} + +class {{declspec}} {{classname}} +{ +public: + {{classname}}() = default; + ~{{classname}}() = default; + + ///////////////////////////////////////////// + + void validate(); + + web::json::value toJson() const; + + template + bool fromJson(const web::json::value& json) { + // convert json to Target type + Target target; + if (!target.fromJson(json)) { + return false; + } + + m_variantValue = target; + return true; + } + + void toMultipart(std::shared_ptr multipart, const utility::string_t& namePrefix) const; + + template + bool fromMultiPart(std::shared_ptr multipart, const utility::string_t& namePrefix) { + // convert multipart to Target type + Target target; + if (!target.fromMultiPart(multipart, namePrefix)) { + return false; + } + + m_variantValue = target; + return true; + } + + ///////////////////////////////////////////// + /// {{classname}} members + + using VariantType = std::variant<{{#oneOf}}{{^-first}}, {{/-first}}{{{.}}}{{/oneOf}}>; + + const VariantType& getVariant() const; + void setVariant(VariantType value); + +protected: + VariantType m_variantValue; +}; + +{{/-first}}{{/oneOf}} +{{^oneOf}} +{{#isEnum}} +class {{declspec}} {{classname}} + : public {{{parent}}}{{^parent}}ModelBase{{/parent}} +{ +public: + {{classname}}(); + {{classname}}(utility::string_t str); + operator utility::string_t() const { + return enumToStrMap.at(getValue()); + } + + {{! operator std::string() const { + return enumToStrMap.at(getValue()); + } }} + + virtual ~{{classname}}(); + + ///////////////////////////////////////////// + /// ModelBase overrides + + void validate() override; + + web::json::value toJson() const override; + bool fromJson(const web::json::value& json) override; + + void toMultipart(std::shared_ptr multipart, const utility::string_t& namePrefix) const override; + bool fromMultiPart(std::shared_ptr multipart, const utility::string_t& namePrefix) override; + + enum class e{{classname}} + { + {{#allowableValues}} + {{#enumVars}} + {{#enumDescription}} + /// + /// {{.}} + /// + {{/enumDescription}} + {{{name}}}{{^last}},{{/last}} + {{/enumVars}} + {{/allowableValues}} + }; + + e{{classname}} getValue() const; + void setValue(e{{classname}} const value); + + protected: + e{{classname}} m_value; + std::map enumToStrMap = { + {{#allowableValues}} + {{#enumVars}} + { e{{classname}}::{{{name}}}, _XPLATSTR("{{{name}}}") }{{^-last}},{{/-last}} + {{/enumVars}} + {{/allowableValues}} +}; + std::map strToEnumMap = { + {{#allowableValues}} + {{#enumVars}} + { _XPLATSTR("{{{name}}}"), e{{classname}}::{{{name}}} }{{^-last}},{{/-last}} + {{/enumVars}} + {{/allowableValues}} +}; + +}; +{{/isEnum}} +{{^isEnum}} + +{{#description}} +/// +/// {{description}} +/// +{{/description}} +class {{declspec}} {{classname}} + : public {{{parent}}}{{^parent}}ModelBase{{/parent}} +{ +public: + {{classname}}(); + virtual ~{{classname}}(); + + ///////////////////////////////////////////// + /// ModelBase overrides + + void validate() override; + + web::json::value toJson() const override; + bool fromJson(const web::json::value& json) override; + + void toMultipart(std::shared_ptr multipart, const utility::string_t& namePrefix) const override; + bool fromMultiPart(std::shared_ptr multipart, const utility::string_t& namePrefix) override; + + + ///////////////////////////////////////////// + /// {{classname}} members + + {{! ENUM DEFINITIONS }} + {{#vars}} + {{^isInherited}} + {{#isEnum}} + enum class {{#isContainer}}{{{enumName}}}{{/isContainer}}{{^isContainer}}{{{datatypeWithEnum}}}{{/isContainer}} + { + {{#allowableValues}} + {{#enumVars}} + {{{name}}}{{^last}},{{/last}} + {{/enumVars}} + {{/allowableValues}} + }; + {{#description}} + /// + /// {{description}} + /// + {{/description}} + {{/isEnum}} + {{/isInherited}} + {{/vars}} + {{#vars}} + {{^isInherited}} + {{#isEnum}} + {{#isContainer}} + {{! ENUM CONVERSIONS }} + {{{enumName}}} to{{{enumName}}}(const utility::string_t& value) const; + const utility::string_t from{{{enumName}}}(const {{{enumName}}} value) const; + {{#isArray}} + {{{datatypeWithEnum}}} to{{{enumName}}}(const {{{dataType}}}& value) const; + {{{dataType}}} from{{{enumName}}}(const {{{datatypeWithEnum}}}& value) const; + {{/isArray}}{{/isContainer}}{{^isContainer}} + {{{datatypeWithEnum}}} to{{{datatypeWithEnum}}}(const utility::string_t& value) const; + const utility::string_t from{{{datatypeWithEnum}}}(const {{{datatypeWithEnum}}} value) const; + {{/isContainer}} + + {{/isEnum}} + {{/isInherited}} + {{/vars}} + + {{! SETTER AND GETTERS }} + {{#vars}} + {{^isInherited}} + {{#description}} + /// + /// {{description}} + /// + {{/description}} + {{#isContainer}} + {{^isEnum}} + {{{dataType}}} {{getter}}() const; + {{/isEnum}} + {{/isContainer}} + {{^isContainer}} + {{^isEnum}} + {{{dataType}}} {{getter}}() const; + {{/isEnum}} + {{/isContainer}} + {{#isEnum}} + {{^isMap}} + {{{datatypeWithEnum}}} {{getter}}() const; + {{/isMap}} + {{#isMap}} + {{{dataType}}} {{getter}}() const; + {{/isMap}} + {{/isEnum}} + bool {{nameInCamelCase}}IsSet() const; + void unset{{name}}(); + {{#isPrimitiveType}} + void {{setter}}({{{dataType}}} value); + {{/isPrimitiveType}} + {{^isPrimitiveType}} + {{^isEnum}} + void {{setter}}(const {{{dataType}}}& value); + {{/isEnum}} + {{/isPrimitiveType}} + {{#isEnum}} + void {{setter}}(const {{^isMap}}{{{datatypeWithEnum}}}{{/isMap}}{{#isMap}}{{{dataType}}}{{/isMap}} value); + {{/isEnum}} + {{/isInherited}} + + {{/vars}} + +protected: + {{#vars}} + {{^isInherited}} + {{^isEnum}} + {{{dataType}}} m_{{name}}; + {{/isEnum}} + {{#isEnum}} + {{^isMap}}{{{datatypeWithEnum}}}{{/isMap}}{{#isMap}}{{{dataType}}}{{/isMap}} m_{{name}}; + {{/isEnum}} + bool m_{{name}}IsSet; + {{/isInherited}} + + {{/vars}} +}; + +{{/isEnum}} +{{/oneOf}} + +{{#modelNamespaceDeclarations}} +} +{{/modelNamespaceDeclarations}} + +#endif /* {{modelHeaderGuardPrefix}}_{{classname}}_H_ */ +{{/model}} +{{/models}} diff --git a/api/_build/my-cpp-templates/cpp-restsdk/model-source.mustache b/api/_build/my-cpp-templates/cpp-restsdk/model-source.mustache new file mode 100644 index 0000000..635cf43 --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/model-source.mustache @@ -0,0 +1,457 @@ +{{>licenseInfo}} +{{#models}}{{#model}} + +#include "{{packageName}}/model/{{classFilename}}.h" + +{{#modelNamespaceDeclarations}} +namespace {{this}} { +{{/modelNamespaceDeclarations}} +{{#oneOf}} +{{#-first}} + +void {{classname}}::validate() +{ + // TODO: implement validation +} + +const {{classname}}::VariantType& {{classname}}::getVariant() const +{ + return m_variantValue; +} + +void {{classname}}::setVariant({{classname}}::VariantType value) +{ + m_variantValue = value; +} + +web::json::value {{classname}}::toJson() const +{ + web::json::value val = web::json::value::object(); + + std::visit([&](auto&& arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + val = web::json::value::null(); + } else { + val = arg.toJson(); + } + }, m_variantValue); + + return val; +} + +void {{classname}}::toMultipart(std::shared_ptr multipart, const utility::string_t& prefix) const +{ + std::visit([&](auto&& arg) { + using T = std::decay_t; + if constexpr (!std::is_same_v) { + arg.toMultipart(multipart, prefix); + } + }, m_variantValue); +} + +{{#oneOf}} +template bool {{classname}}::fromJson<{{.}}>(const web::json::value& json); +template bool {{classname}}::fromMultiPart<{{.}}>(std::shared_ptr multipart, const utility::string_t& namePrefix); +{{/oneOf}} + +{{/-first}} +{{/oneOf}} +{{^oneOf}} +{{#isEnum}} + +namespace +{ +using EnumUnderlyingType = {{#isNumeric}}int64_t{{/isNumeric}}{{^isNumeric}}utility::string_t{{/isNumeric}}; + +{{classname}}::e{{classname}} toEnum(const EnumUnderlyingType& val) +{ +{{#allowableValues}} +{{#isNumeric}} + switch (val) + { + {{#enumVars}} + case {{value}}: + return {{classname}}::e{{classname}}::{{name}}; + {{#-last}} + default: + break; + {{/-last}} + {{/enumVars}} + } +{{/isNumeric}} +{{^isNumeric}} + {{#enumVars}} + if (val == utility::conversions::to_string_t(_XPLATSTR("{{{value}}}"))) + return {{classname}}::e{{classname}}::{{name}}; + {{/enumVars}} +{{/isNumeric}} +{{/allowableValues}} + return {}; +} + +EnumUnderlyingType fromEnum({{classname}}::e{{classname}} e) +{ +{{#allowableValues}} + switch (e) + { +{{#enumVars}} + case {{classname}}::e{{classname}}::{{name}}: + return {{#isNumeric}}{{value}}{{/isNumeric}}{{^isNumeric}}_XPLATSTR("{{value}}"){{/isNumeric}}; +{{#-last}} + default: + break; +{{/-last}} +{{/enumVars}} + } +{{/allowableValues}} + return {}; +} +} + +{{classname}}::{{classname}}() +{ +} + +{{classname}}::~{{classname}}() +{ +} + +void {{classname}}::validate() +{ + // TODO: implement validation +} + +web::json::value {{classname}}::toJson() const +{ + auto val = fromEnum(m_value); + return web::json::value(val); +} + +bool {{classname}}::fromJson(const web::json::value& val) +{ + m_value = toEnum({{#isNumeric}}val.as_number().to_int64(){{/isNumeric}}{{^isNumeric}}val.as_string(){{/isNumeric}}); + return true; +} + +void {{classname}}::toMultipart(std::shared_ptr multipart, const utility::string_t& prefix) const +{ + utility::string_t namePrefix = prefix; + if (!namePrefix.empty() && namePrefix.back() != _XPLATSTR('.')) + { + namePrefix.push_back(_XPLATSTR('.')); + } + + auto e = fromEnum(m_value); + multipart->add(ModelBase::toHttpContent(namePrefix, e)); +} + +bool {{classname}}::fromMultiPart(std::shared_ptr multipart, const utility::string_t& prefix) +{ + bool ok = true; + utility::string_t namePrefix = prefix; + if (!namePrefix.empty() && namePrefix.back() != _XPLATSTR('.')) + { + namePrefix.push_back(_XPLATSTR('.')); + } + { + EnumUnderlyingType e; + ok = ModelBase::fromHttpContent(multipart->getContent(namePrefix), e); + if (ok) + { + auto v = toEnum(e); + setValue(v); + } + } + return ok; +} + +{{classname}}::e{{classname}} {{classname}}::getValue() const +{ + return m_value; +} + +void {{classname}}::setValue({{classname}}::e{{classname}} const value) +{ + m_value = value; +} + +{{classname}}::{{classname}}(utility::string_t str){ + setValue( strToEnumMap[str] ); +} + +{{/isEnum}} +{{^isEnum}} + +{{classname}}::{{classname}}() +{ + {{#vars}} + {{^isInherited}} + {{^isContainer}} + {{^isEnum}} + {{#isPrimitiveType}} + m_{{name}} = {{{defaultValue}}}; + {{/isPrimitiveType}} + {{^isPrimitiveType}} + {{#isString}} + m_{{name}} = {{{defaultValue}}}; + {{/isString}} + {{#isDateTime}} + m_{{name}} = {{{defaultValue}}}; + {{/isDateTime}} + {{/isPrimitiveType}} + {{/isEnum}} + {{/isContainer}} + m_{{name}}IsSet = false; + {{/isInherited}} + {{/vars}} +} + +{{classname}}::~{{classname}}() +{ +} + +void {{classname}}::validate() +{ + // TODO: implement validation +} + +web::json::value {{classname}}::toJson() const +{ + {{#parent}} + web::json::value val = this->{{{.}}}::toJson(); + {{/parent}} + {{^parent}} + web::json::value val = web::json::value::object(); + {{/parent}} + {{#vars}} + {{^isInherited}} + if(m_{{name}}IsSet) + { + {{#isEnum}}{{#isContainer}}{{#isArray}} + {{{dataType}}} refVal = from{{{enumName}}}(m_{{name}}); + {{/isArray}}{{#isMap}} + val[utility::conversions::to_string_t(_XPLATSTR("{{baseName}}"))] = ModelBase::toJson(m_{{name}}); + {{/isMap}}{{/isContainer}}{{^isContainer}} + utility::string_t refVal = from{{{datatypeWithEnum}}}(m_{{name}}); + {{/isContainer}}{{^isMap}}val[utility::conversions::to_string_t(_XPLATSTR("{{baseName}}"))] = ModelBase::toJson(refVal); + {{/isMap}}{{/isEnum}} + {{^isEnum}} + val[utility::conversions::to_string_t(_XPLATSTR("{{baseName}}"))] = ModelBase::toJson(m_{{name}}); + {{/isEnum}} + } + {{/isInherited}} + {{/vars}} + + return val; +} + +bool {{classname}}::fromJson(const web::json::value& val) +{ + bool ok = true; + {{#parent}} + ok &= this->{{{.}}}::fromJson(val); + {{/parent}} + {{#vars}} + {{^isInherited}} + if(val.has_field(utility::conversions::to_string_t(_XPLATSTR("{{baseName}}")))) + { + const web::json::value& fieldValue = val.at(utility::conversions::to_string_t(_XPLATSTR("{{baseName}}"))); + if(!fieldValue.is_null()) + { + {{{dataType}}} refVal_{{setter}}; + ok &= ModelBase::fromJson(fieldValue, refVal_{{setter}}); + {{^isEnum}} + {{setter}}(refVal_{{setter}}); + {{/isEnum}} + {{#isEnum}}{{#isContainer}}{{#isArray}} + {{setter}}(to{{{enumName}}}(refVal_{{setter}})); + {{/isArray}}{{#isMap}} + {{setter}}(refVal_{{setter}}); + {{/isMap}}{{/isContainer}}{{^isContainer}} + {{setter}}(to{{{datatypeWithEnum}}}(refVal_{{setter}})); + {{/isContainer}}{{/isEnum}} + } + } + {{/isInherited}} + {{/vars}} + return ok; +} + +void {{classname}}::toMultipart(std::shared_ptr multipart, const utility::string_t& prefix) const +{ + utility::string_t namePrefix = prefix; + if(namePrefix.size() > 0 && namePrefix.substr(namePrefix.size() - 1) != utility::conversions::to_string_t(_XPLATSTR("."))) + { + namePrefix += utility::conversions::to_string_t(_XPLATSTR(".")); + } + {{#vars}} + if(m_{{name}}IsSet) + { + {{^isEnum}} + multipart->add(ModelBase::toHttpContent(namePrefix + utility::conversions::to_string_t(_XPLATSTR("{{baseName}}")), m_{{name}})); + {{/isEnum}} + {{#isEnum}} + {{#isContainer}} + {{#isArray}} + multipart->add(ModelBase::toHttpContent(namePrefix + utility::conversions::to_string_t(_XPLATSTR("{{baseName}}")), from{{{enumName}}}(m_{{name}}))); + {{/isArray}}{{#isMap}} + multipart->add(ModelBase::toHttpContent(namePrefix + utility::conversions::to_string_t(_XPLATSTR("{{baseName}}")), m_{{name}})); + {{/isMap}} + {{/isContainer}} + {{^isContainer}} + multipart->add(ModelBase::toHttpContent(namePrefix + utility::conversions::to_string_t(_XPLATSTR("{{baseName}}")), from{{{datatypeWithEnum}}}(m_{{name}}))); + {{/isContainer}} + {{/isEnum}} + } + {{/vars}} +} + +bool {{classname}}::fromMultiPart(std::shared_ptr multipart, const utility::string_t& prefix) +{ + bool ok = true; + utility::string_t namePrefix = prefix; + if(namePrefix.size() > 0 && namePrefix.substr(namePrefix.size() - 1) != utility::conversions::to_string_t(_XPLATSTR("."))) + { + namePrefix += utility::conversions::to_string_t(_XPLATSTR(".")); + } + + {{#vars}} + if(multipart->hasContent(utility::conversions::to_string_t(_XPLATSTR("{{baseName}}")))) + { + {{{dataType}}} refVal_{{setter}}; + ok &= ModelBase::fromHttpContent(multipart->getContent(utility::conversions::to_string_t(_XPLATSTR("{{baseName}}"))), refVal_{{setter}} ); + {{^isEnum}} + {{setter}}(refVal_{{setter}}); + {{/isEnum}} + {{#isEnum}} + {{#isContainer}} + {{#isArray}} + {{setter}}(to{{{enumName}}}(refVal_{{setter}})); + {{/isArray}} + {{#isMap}} + {{setter}}(refVal_{{setter}}); + {{/isMap}} + {{/isContainer}} + {{^isContainer}} + {{setter}}(to{{{datatypeWithEnum}}}(refVal_{{setter}})); + {{/isContainer}} + {{/isEnum}} + } + {{/vars}} + return ok; +} + +{{#vars}} +{{^isInherited}} +{{#isEnum}} +{{#isContainer}} +{{classname}}::{{{enumName}}} {{classname}}::to{{{enumName}}}(const utility::string_t& value) const +{{/isContainer}} +{{^isContainer}} +{{classname}}::{{{datatypeWithEnum}}} {{classname}}::to{{{datatypeWithEnum}}}(const {{dataType}}& value) const +{{/isContainer}} +{ + {{#allowableValues}}{{#enumVars}} + if (value == utility::conversions::to_string_t("{{value}}")) { + return {{#isContainer}}{{{enumName}}}{{/isContainer}}{{^isContainer}}{{{datatypeWithEnum}}}{{/isContainer}}::{{name}}; + } + {{/enumVars}}{{/allowableValues}} + throw std::invalid_argument("Invalid value for conversion to {{{datatypeWithEnum}}}"); +} + +{{#isContainer}} +const utility::string_t {{classname}}::from{{{enumName}}}(const {{{enumName}}} value) const +{{/isContainer}}{{^isContainer}} +const {{dataType}} {{classname}}::from{{{datatypeWithEnum}}}(const {{{datatypeWithEnum}}} value) const +{{/isContainer}} +{ + switch(value) + { + {{#allowableValues}}{{#enumVars}} + case {{#isContainer}}{{{enumName}}}{{/isContainer}}{{^isContainer}}{{{datatypeWithEnum}}}{{/isContainer}}::{{name}}: return utility::conversions::to_string_t("{{value}}"); + {{/enumVars}}{{/allowableValues}} + } +} + +{{#isContainer}} +{{#isArray}} +{{{dataType}}} {{{classname}}}::from{{{enumName}}}(const {{{datatypeWithEnum}}}& value) const +{ + {{{dataType}}} ret; + for (auto it = value.begin(); it != value.end(); it++) { + ret.push_back(from{{{enumName}}}(*it)); + } + return ret; +} + +{{{baseType}}}<{{classname}}::{{{enumName}}}> {{{classname}}}::to{{{enumName}}}(const {{{dataType}}}& value) const +{ + {{{datatypeWithEnum}}} ret; + for (auto it = value.begin(); it != value.end(); it++) { + ret.push_back(to{{{enumName}}}(*it)); + } + return ret; +} +{{/isArray}} +{{/isContainer}} +{{/isEnum}} +{{/isInherited}} +{{/vars}} + +{{#vars}} +{{^isInherited}} +{{#isContainer}} +{{^isEnum}} +{{{dataType}}} {{classname}}::{{getter}}() const +{ + return m_{{name}}; +} +{{/isEnum}} +{{/isContainer}} +{{^isContainer}} +{{^isEnum}} +{{{dataType}}} {{classname}}::{{getter}}() const +{ + return m_{{name}}; +} +{{/isEnum}} +{{/isContainer}} +{{#isEnum}} +{{^isMap}}{{#isArray}}{{{baseType}}}<{{/isArray}}{{{classname}}}::{{{enumName}}}{{#isArray}}>{{/isArray}}{{/isMap}}{{#isMap}}{{{dataType}}}{{/isMap}} {{classname}}::{{getter}}() const +{ + return m_{{name}}; +} +{{/isEnum}} + +{{#isPrimitiveType}} +void {{classname}}::{{setter}}({{{dataType}}} value) +{{/isPrimitiveType}}{{^isPrimitiveType}}{{^isEnum}} +void {{classname}}::{{setter}}(const {{{dataType}}}& value) +{{/isEnum}}{{/isPrimitiveType}}{{#isEnum}} +void {{classname}}::{{setter}}(const {{^isMap}}{{{datatypeWithEnum}}}{{/isMap}}{{#isMap}}{{{dataType}}}{{/isMap}} value) +{{/isEnum}} +{ + m_{{name}} = value; + m_{{name}}IsSet = true; +} + +bool {{classname}}::{{nameInCamelCase}}IsSet() const +{ + return m_{{name}}IsSet; +} + +void {{classname}}::unset{{name}}() +{ + m_{{name}}IsSet = false; +} +{{/isInherited}}{{/vars}} +{{/isEnum}} +{{/oneOf}} +{{#modelNamespaceDeclarations}} +} +{{/modelNamespaceDeclarations}} + + +{{/model}} +{{/models}} diff --git a/api/_build/my-cpp-templates/cpp-restsdk/modelbase-header.mustache b/api/_build/my-cpp-templates/cpp-restsdk/modelbase-header.mustache new file mode 100644 index 0000000..b97a7ad --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/modelbase-header.mustache @@ -0,0 +1,493 @@ +{{>licenseInfo}} +/* + * ModelBase.h + * + * This is the base class for all model classes + */ + +#ifndef {{modelHeaderGuardPrefix}}_ModelBase_H_ +#define {{modelHeaderGuardPrefix}}_ModelBase_H_ + +{{{defaultInclude}}} + +#include "{{packageName}}/HttpContent.h" +#include "{{packageName}}/MultipartFormData.h" + +#include +#include + +#include +#include +#include + +{{#modelNamespaceDeclarations}} +namespace {{this}} { +{{/modelNamespaceDeclarations}} + +class {{declspec}} ModelBase +{ +public: + ModelBase(); + virtual ~ModelBase(); + + virtual void validate() = 0; + + virtual web::json::value toJson() const = 0; + virtual bool fromJson( const web::json::value& json ) = 0; + + virtual void toMultipart( std::shared_ptr multipart, const utility::string_t& namePrefix ) const = 0; + virtual bool fromMultiPart( std::shared_ptr multipart, const utility::string_t& namePrefix ) = 0; + + virtual bool isSet() const; + + static utility::string_t toString( const bool val ); + static utility::string_t toString( const float val ); + static utility::string_t toString( const double val ); + static utility::string_t toString( const int32_t val ); + static utility::string_t toString( const int64_t val ); + static utility::string_t toString( const utility::string_t &val ); + static utility::string_t toString( const utility::datetime &val ); + static utility::string_t toString( const web::json::value &val ); + static utility::string_t toString( const std::shared_ptr& val ); + template + static utility::string_t toString( const std::shared_ptr& val ); + template + static utility::string_t toString( const std::vector & val ); + template + static utility::string_t toString( const std::set & val ); + + static web::json::value toJson( bool val ); + static web::json::value toJson( float val ); + static web::json::value toJson( double val ); + static web::json::value toJson( int32_t val ); + static web::json::value toJson( int64_t val ); + static web::json::value toJson( const utility::string_t& val ); + static web::json::value toJson( const utility::datetime& val ); + static web::json::value toJson( const web::json::value& val ); + static web::json::value toJson( const std::shared_ptr& val ); + template + static web::json::value toJson( const std::shared_ptr& val ); + static web::json::value toJson( const std::shared_ptr& val ); + template + static web::json::value toJson( const std::vector& val ); + template + static web::json::value toJson( const std::set& val ); + template + static web::json::value toJson( const std::map& val ); + + static bool fromString( const utility::string_t& val, bool & ); + static bool fromString( const utility::string_t& val, float & ); + static bool fromString( const utility::string_t& val, double & ); + static bool fromString( const utility::string_t& val, int32_t & ); + static bool fromString( const utility::string_t& val, int64_t & ); + static bool fromString( const utility::string_t& val, utility::string_t & ); + static bool fromString( const utility::string_t& val, utility::datetime & ); + static bool fromString( const utility::string_t& val, web::json::value & ); + static bool fromString( const utility::string_t& val, std::shared_ptr & ); + template + static bool fromString( const utility::string_t& val, std::shared_ptr& ); + static bool fromString( const utility::string_t& val, std::shared_ptr& outVal ); + template + static bool fromString( const utility::string_t& val, std::vector & ); + template + static bool fromString( const utility::string_t& val, std::set & ); + template + static bool fromString( const utility::string_t& val, std::map & ); + + static bool fromJson( const web::json::value& val, bool & ); + static bool fromJson( const web::json::value& val, float & ); + static bool fromJson( const web::json::value& val, double & ); + static bool fromJson( const web::json::value& val, int32_t & ); + static bool fromJson( const web::json::value& val, int64_t & ); + static bool fromJson( const web::json::value& val, utility::string_t & ); + static bool fromJson( const web::json::value& val, utility::datetime & ); + static bool fromJson( const web::json::value& val, web::json::value & ); + static bool fromJson( const web::json::value& val, std::shared_ptr & ); + template + static bool fromJson( const web::json::value& val, std::shared_ptr& ); + static bool fromJson( const web::json::value& val, std::shared_ptr &outVal ); + template + static bool fromJson( const web::json::value& val, std::vector & ); + template + static bool fromJson( const web::json::value& val, std::set & ); + template + static bool fromJson( const web::json::value& val, std::map & ); + + + static std::shared_ptr toHttpContent( const utility::string_t& name, bool value, const utility::string_t& contentType = utility::conversions::to_string_t("") ); + static std::shared_ptr toHttpContent( const utility::string_t& name, float value, const utility::string_t& contentType = utility::conversions::to_string_t("") ); + static std::shared_ptr toHttpContent( const utility::string_t& name, double value, const utility::string_t& contentType = utility::conversions::to_string_t("") ); + static std::shared_ptr toHttpContent( const utility::string_t& name, int32_t value, const utility::string_t& contentType = utility::conversions::to_string_t("") ); + static std::shared_ptr toHttpContent( const utility::string_t& name, int64_t value, const utility::string_t& contentType = utility::conversions::to_string_t("") ); + static std::shared_ptr toHttpContent( const utility::string_t& name, const utility::string_t& value, const utility::string_t& contentType = utility::conversions::to_string_t("")); + static std::shared_ptr toHttpContent( const utility::string_t& name, const utility::datetime& value, const utility::string_t& contentType = utility::conversions::to_string_t("")); + static std::shared_ptr toHttpContent( const utility::string_t& name, const web::json::value& value, const utility::string_t& contentType = utility::conversions::to_string_t("application/json") ); + static std::shared_ptr toHttpContent( const utility::string_t& name, const std::shared_ptr& ); + template + static std::shared_ptr toHttpContent( const utility::string_t& name, const std::shared_ptr& , const utility::string_t& contentType = utility::conversions::to_string_t("application/json") ); + static std::shared_ptr toHttpContent(const utility::string_t& name, const std::shared_ptr& value , const utility::string_t& contentType = utility::conversions::to_string_t("application/json") ); + template + static std::shared_ptr toHttpContent( const utility::string_t& name, const std::vector& value, const utility::string_t& contentType = utility::conversions::to_string_t("") ); + template + static std::shared_ptr toHttpContent( const utility::string_t& name, const std::set& value, const utility::string_t& contentType = utility::conversions::to_string_t("") ); + template + static std::shared_ptr toHttpContent( const utility::string_t& name, const std::map& value, const utility::string_t& contentType = utility::conversions::to_string_t("") ); + + static bool fromHttpContent( std::shared_ptr val, bool & ); + static bool fromHttpContent( std::shared_ptr val, float & ); + static bool fromHttpContent( std::shared_ptr val, double & ); + static bool fromHttpContent( std::shared_ptr val, int64_t & ); + static bool fromHttpContent( std::shared_ptr val, int32_t & ); + static bool fromHttpContent( std::shared_ptr val, utility::string_t & ); + static bool fromHttpContent( std::shared_ptr val, utility::datetime & ); + static bool fromHttpContent( std::shared_ptr val, web::json::value & ); + static bool fromHttpContent( std::shared_ptr val, std::shared_ptr& ); + template + static bool fromHttpContent( std::shared_ptr val, std::shared_ptr& ); + template + static bool fromHttpContent( std::shared_ptr val, std::vector & ); + template + static bool fromHttpContent( std::shared_ptr val, std::set & ); + template + static bool fromHttpContent( std::shared_ptr val, std::map & ); + + static utility::string_t toBase64( utility::string_t value ); + static utility::string_t toBase64( std::shared_ptr value ); + static std::shared_ptr fromBase64( const utility::string_t& encoded ); +protected: + bool m_IsSet; +}; + +template +utility::string_t ModelBase::toString( const std::shared_ptr& val ) +{ + utility::stringstream_t ss; + if( val != nullptr ) + { + val->toJson().serialize(ss); + } + return utility::string_t(ss.str()); +} + +// std::vector to string +template +utility::string_t ModelBase::toString( const std::vector & val ) +{ + utility::string_t strArray; + for ( const auto &item : val ) + { + strArray.append( toString(item) + "," ); + } + if (val.count() > 0) + { + strArray.pop_back(); + } + return strArray; +} + +// std::set to string +template +utility::string_t ModelBase::toString( const std::set & val ) +{ + utility::string_t strArray; + for ( const auto &item : val ) + { + strArray.append( toString(item) + "," ); + } + if (val.count() > 0) + { + strArray.pop_back(); + } + return strArray; +} + + +template +web::json::value ModelBase::toJson( const std::shared_ptr& val ) +{ + web::json::value retVal; + if(val != nullptr) + { + retVal = val->toJson(); + } + return retVal; +} + +// std::vector to json +template +web::json::value ModelBase::toJson( const std::vector& value ) +{ + std::vector ret; + for ( const auto& x : value ) + { + ret.push_back( toJson(x) ); + } + return web::json::value::array(ret); +} + +// std::set to json +template +web::json::value ModelBase::toJson( const std::set& value ) +{ + // There's no prototype web::json::value::array(...) taking a std::set parameter. Converting to std::vector to get an array. + std::vector ret; + for ( const auto& x : value ) + { + ret.push_back( toJson(x) ); + } + return web::json::value::array(ret); +} + + +template +web::json::value ModelBase::toJson( const std::map& val ) +{ + web::json::value obj; + for ( const auto &itemkey : val ) + { + obj[itemkey.first] = toJson( itemkey.second ); + } + return obj; +} +template +bool ModelBase::fromString( const utility::string_t& val, std::shared_ptr& outVal ) +{ + bool ok = false; + if(outVal == nullptr) + { + outVal = std::make_shared(); + } + if( outVal != nullptr ) + { + ok = outVal->fromJson(web::json::value::parse(val)); + } + return ok; +} +template +bool ModelBase::fromString(const utility::string_t& val, std::vector& outVal ) +{ + bool ok = true; + web::json::value jsonValue = web::json::value::parse(val); + if (jsonValue.is_array()) + { + for (const web::json::value& jitem : jsonValue.as_array()) + { + T item; + ok &= fromJson(jitem, item); + outVal.push_back(item); + } + } + else + { + T item; + ok = fromJson(jsonValue, item); + outVal.push_back(item); + } + return ok; +} +template +bool ModelBase::fromString(const utility::string_t& val, std::set& outVal ) +{ + bool ok = true; + web::json::value jsonValue = web::json::value::parse(val); + if (jsonValue.is_array()) + { + for (const web::json::value& jitem : jsonValue.as_array()) + { + T item; + ok &= fromJson(jitem, item); + outVal.insert(item); + } + } + else + { + T item; + ok = fromJson(jsonValue, item); + outVal.insert(item); + } + return ok; +} +template +bool ModelBase::fromString(const utility::string_t& val, std::map& outVal ) +{ + bool ok = false; + web::json::value jsonValue = web::json::value::parse(val); + if (jsonValue.is_array()) + { + for (const web::json::value& jitem : jsonValue.as_array()) + { + T item; + ok &= fromJson(jitem, item); + outVal.insert({ val, item }); + } + } + else + { + T item; + ok = fromJson(jsonValue, item); + outVal.insert({ val, item }); + } + return ok; +} +template +bool ModelBase::fromJson( const web::json::value& val, std::shared_ptr &outVal ) +{ + bool ok = false; + if(outVal == nullptr) + { + outVal = std::make_shared(); + } + if( outVal != nullptr ) + { + ok = outVal->fromJson(val); + } + return ok; +} +template +bool ModelBase::fromJson( const web::json::value& val, std::vector &outVal ) +{ + bool ok = true; + if (val.is_array()) + { + for (const web::json::value & jitem : val.as_array()) + { + T item; + ok &= fromJson(jitem, item); + outVal.push_back(item); + } + } + else + { + ok = false; + } + return ok; +} +template +bool ModelBase::fromJson(const web::json::value& val, std::set& outVal ) +{ + bool ok = true; + if (val.is_array()) + { + for (const web::json::value& jitem : val.as_array()) + { + T item; + ok &= fromJson(jitem, item); + outVal.insert(item); + } + } + else + { + T item; + ok = fromJson(val, item); + outVal.insert(item); + } + return ok; +} +template +bool ModelBase::fromJson( const web::json::value& jval, std::map &outVal ) +{ + bool ok = true; + if ( jval.is_object() ) + { + auto obj = jval.as_object(); + for( auto objItr = obj.begin() ; objItr != obj.end() ; objItr++ ) + { + T itemVal; + ok &= fromJson(objItr->second, itemVal); + outVal.insert(std::pair(objItr->first, itemVal)); + } + } + else + { + ok = false; + } + return ok; +} +template +std::shared_ptr ModelBase::toHttpContent(const utility::string_t& name, const std::shared_ptr& value , const utility::string_t& contentType ) +{ + std::shared_ptr content = std::make_shared(); + if (value != nullptr ) + { + content->setName( name ); + content->setContentDisposition( utility::conversions::to_string_t("form-data") ); + content->setContentType( contentType ); + content->setData( std::make_shared( utility::conversions::to_utf8string(value->toJson().serialize()) ) ); + } + return content; +} + +template +std::shared_ptr ModelBase::toHttpContent( const utility::string_t& name, const std::vector& value, const utility::string_t& contentType ) +{ + web::json::value json_array = ModelBase::toJson(value); + std::shared_ptr content = std::make_shared(); + content->setName( name ); + content->setContentDisposition( utility::conversions::to_string_t("form-data") ); + content->setContentType( contentType ); + content->setData( std::make_shared( utility::conversions::to_utf8string(json_array.serialize()) ) ); + return content; +} +template +std::shared_ptr ModelBase::toHttpContent( const utility::string_t& name, const std::set& value, const utility::string_t& contentType ) +{ + web::json::value json_array = ModelBase::toJson(value); + std::shared_ptr content = std::make_shared(); + content->setName(name); + content->setContentDisposition(utility::conversions::to_string_t("form-data")); + content->setContentType(contentType); + content->setData( std::make_shared( utility::conversions::to_utf8string(json_array.serialize()) ) ); + return content; +} +template +std::shared_ptr ModelBase::toHttpContent( const utility::string_t& name, const std::map& value, const utility::string_t& contentType ) +{ + web::json::value jobj = ModelBase::toJson(value); + std::shared_ptr content = std::make_shared(); + content->setName( name ); + content->setContentDisposition( utility::conversions::to_string_t("form-data") ); + content->setContentType( contentType ); + content->setData( std::make_shared( utility::conversions::to_utf8string(jobj.serialize()) ) ); + return content; +} +template +bool ModelBase::fromHttpContent( std::shared_ptr val, std::shared_ptr& outVal ) +{ + utility::string_t str; + if(val == nullptr) return false; + if( outVal == nullptr ) + { + outVal = std::make_shared(); + } + ModelBase::fromHttpContent(val, str); + return fromString(str, outVal); +} +template +bool ModelBase::fromHttpContent( std::shared_ptr val, std::vector & outVal ) +{ + utility::string_t str; + if (val == nullptr) return false; + ModelBase::fromHttpContent(val, str); + return fromString(str, outVal); +} +template +bool ModelBase::fromHttpContent(std::shared_ptr val, std::set& outVal ) +{ + utility::string_t str; + if (val == nullptr) return false; + ModelBase::fromHttpContent(val, str); + return fromString(str, outVal); +} +template +bool ModelBase::fromHttpContent( std::shared_ptr val, std::map & outVal ) +{ + utility::string_t str; + if (val == nullptr) return false; + ModelBase::fromHttpContent(val, str); + return fromString(str, outVal); +} +{{#modelNamespaceDeclarations}} +} +{{/modelNamespaceDeclarations}} + +#endif /* {{modelHeaderGuardPrefix}}_ModelBase_H_ */ diff --git a/api/_build/my-cpp-templates/cpp-restsdk/modelbase-source.mustache b/api/_build/my-cpp-templates/cpp-restsdk/modelbase-source.mustache new file mode 100644 index 0000000..7528343 --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/modelbase-source.mustache @@ -0,0 +1,653 @@ +{{>licenseInfo}} +#include "{{packageName}}/ModelBase.h" + +{{#modelNamespaceDeclarations}} +namespace {{this}} { +{{/modelNamespaceDeclarations}} + +ModelBase::ModelBase(): m_IsSet(false) +{ +} +ModelBase::~ModelBase() +{ +} +bool ModelBase::isSet() const +{ + return m_IsSet; +} +utility::string_t ModelBase::toString( const bool val ) +{ + utility::stringstream_t ss; + ss << val; + return utility::string_t(ss.str()); +} +utility::string_t ModelBase::toString( const float val ) +{ + utility::stringstream_t ss; + ss << val; + return utility::string_t(ss.str()); +} +utility::string_t ModelBase::toString( const double val ) +{ + utility::stringstream_t ss; + ss << val; + return utility::string_t(ss.str()); +} +utility::string_t ModelBase::toString( const int32_t val ) +{ + utility::stringstream_t ss; + ss << val; + return utility::string_t(ss.str()); +} +utility::string_t ModelBase::toString( const int64_t val ) +{ + utility::stringstream_t ss; + ss << val; + return utility::string_t(ss.str()); +} +utility::string_t ModelBase::toString (const utility::string_t &val ) +{ + utility::stringstream_t ss; + ss << val; + return utility::string_t(ss.str()); +} +utility::string_t ModelBase::toString( const utility::datetime &val ) +{ + return val.to_string(utility::datetime::ISO_8601); +} +utility::string_t ModelBase::toString( const web::json::value &val ) +{ + return val.serialize(); +} +utility::string_t ModelBase::toString( const std::shared_ptr& val ) +{ + utility::stringstream_t ss; + if( val != nullptr ) + { + ss << val->getData(); + } + return utility::string_t(ss.str()); +} +web::json::value ModelBase::toJson(bool value) +{ + return web::json::value::boolean(value); +} +web::json::value ModelBase::toJson( float value ) +{ + return web::json::value::number(value); +} +web::json::value ModelBase::toJson( double value ) +{ + return web::json::value::number(value); +} +web::json::value ModelBase::toJson( int32_t value ) +{ + return web::json::value::number(value); +} +web::json::value ModelBase::toJson( int64_t value ) +{ + return web::json::value::number(value); +} +web::json::value ModelBase::toJson( const utility::string_t& value ) +{ + return web::json::value::string(value); +} +web::json::value ModelBase::toJson( const utility::datetime& value ) +{ + return web::json::value::string(value.to_string(utility::datetime::ISO_8601)); +} +web::json::value ModelBase::toJson( const web::json::value& value ) +{ + return value; +} +web::json::value ModelBase::toJson( const std::shared_ptr& content ) +{ + web::json::value value; + if(content != nullptr) + { + value[utility::conversions::to_string_t("ContentDisposition")] = ModelBase::toJson(content->getContentDisposition()); + value[utility::conversions::to_string_t("ContentType")] = ModelBase::toJson(content->getContentType()); + value[utility::conversions::to_string_t("FileName")] = ModelBase::toJson(content->getFileName()); + value[utility::conversions::to_string_t("InputStream")] = web::json::value::string( ModelBase::toBase64(content->getData()) ); + } + return value; +} +web::json::value ModelBase::toJson( const std::shared_ptr& val ) +{ + web::json::value retVal; + if(val != nullptr) + { + retVal = toJson(*val); + } + return retVal; +} +bool ModelBase::fromString( const utility::string_t& val, bool &outVal ) +{ + utility::stringstream_t ss(val); + bool success = true; + try + { + ss >> outVal; + } + catch (...) + { + success = false; + } + return success; +} +bool ModelBase::fromString( const utility::string_t& val, float &outVal ) +{ + utility::stringstream_t ss(val); + bool success = true; + try + { + ss >> outVal; + } + catch (...) + { + int64_t intVal = 0; + success = ModelBase::fromString(val, intVal); + if(success) + { + outVal = static_cast(intVal); + } + } + return success; +} +bool ModelBase::fromString( const utility::string_t& val, double &outVal ) +{ + utility::stringstream_t ss(val); + bool success = true; + try + { + ss >> outVal; + } + catch (...) + { + int64_t intVal = 0; + success = ModelBase::fromString(val, intVal); + if(success) + { + outVal = static_cast(intVal); + } + } + return success; +} +bool ModelBase::fromString( const utility::string_t& val, int32_t &outVal ) +{ + utility::stringstream_t ss(val); + bool success = true; + try + { + ss >> outVal; + } + catch (...) + { + success = false; + } + return success; +} +bool ModelBase::fromString( const utility::string_t& val, int64_t &outVal ) +{ + utility::stringstream_t ss(val); + bool success = true; + try + { + ss >> outVal; + } + catch (...) + { + success = false; + } + return success; +} +bool ModelBase::fromString( const utility::string_t& val, utility::string_t &outVal ) +{ + utility::stringstream_t ss(val); + bool success = true; + try + { + ss >> outVal; + } + catch (...) + { + success = false; + } + return success; +} +bool ModelBase::fromString( const utility::string_t& val, utility::datetime &outVal ) +{ + bool success = true; + auto dt = utility::datetime::from_string(val, utility::datetime::ISO_8601); + if( dt.is_initialized() ) + { + outVal = dt; + } + else + { + success = false; + } + return success; +} +bool ModelBase::fromString( const utility::string_t& val, web::json::value &outVal ) +{ + outVal = web::json::value::parse(val); + return !outVal.is_null(); +} +bool ModelBase::fromString( const utility::string_t& val, std::shared_ptr& outVal ) +{ + bool ok = true; + if(outVal == nullptr) + { + outVal = std::shared_ptr(new HttpContent()); + } + if(outVal != nullptr) + { + outVal->setData(std::shared_ptr(new std::stringstream(utility::conversions::to_utf8string(val)))); + } + else + { + ok = false; + } + return ok; +} +bool ModelBase::fromString( const utility::string_t& val, std::shared_ptr& outVal ) +{ + bool ok = false; + if(outVal == nullptr) + { + outVal = std::shared_ptr(new utility::datetime()); + } + if( outVal != nullptr ) + { + ok = fromJson(web::json::value::parse(val), *outVal); + } + return ok; +} +bool ModelBase::fromJson( const web::json::value& val, bool & outVal ) +{ + outVal = !val.is_boolean() ? false : val.as_bool(); + return val.is_boolean(); +} +bool ModelBase::fromJson( const web::json::value& val, float & outVal ) +{ + outVal = (!val.is_double() && !val.is_integer()) ? std::numeric_limits::quiet_NaN(): static_cast(val.as_double()); + return val.is_double() || val.is_integer(); +} +bool ModelBase::fromJson( const web::json::value& val, double & outVal ) +{ + outVal = (!val.is_double() && !val.is_integer()) ? std::numeric_limits::quiet_NaN(): val.as_double(); + return val.is_double() || val.is_integer(); +} +bool ModelBase::fromJson( const web::json::value& val, int32_t & outVal ) +{ + outVal = !val.is_integer() ? std::numeric_limits::quiet_NaN() : val.as_integer(); + return val.is_integer(); +} +bool ModelBase::fromJson( const web::json::value& val, int64_t & outVal ) +{ + outVal = !val.is_number() ? std::numeric_limits::quiet_NaN() : val.as_number().to_int64(); + return val.is_number(); +} +bool ModelBase::fromJson( const web::json::value& val, utility::string_t & outVal ) +{ + outVal = val.is_string() ? val.as_string() : utility::conversions::to_string_t(""); + return val.is_string(); +} +bool ModelBase::fromJson( const web::json::value& val, utility::datetime & outVal ) +{ + outVal = val.is_null() ? utility::datetime::from_string(utility::conversions::to_string_t("NULL"), utility::datetime::ISO_8601) : utility::datetime::from_string(val.as_string(), utility::datetime::ISO_8601); + return outVal.is_initialized(); +} +bool ModelBase::fromJson( const web::json::value& val, web::json::value & outVal ) +{ + outVal = val; + return !val.is_null(); +} +bool ModelBase::fromJson( const web::json::value& val, std::shared_ptr& content ) +{ + bool result = false; + if( content != nullptr) + { + result = true; + if(content == nullptr) + { + content = std::shared_ptr(new HttpContent()); + } + if(val.has_field(utility::conversions::to_string_t("ContentDisposition"))) + { + utility::string_t value; + result = result && ModelBase::fromJson(val.at(utility::conversions::to_string_t("ContentDisposition")), value); + content->setContentDisposition( value ); + } + if(val.has_field(utility::conversions::to_string_t("ContentType"))) + { + utility::string_t value; + result = result && ModelBase::fromJson(val.at(utility::conversions::to_string_t("ContentType")), value); + content->setContentType( value ); + } + if(val.has_field(utility::conversions::to_string_t("FileName"))) + { + utility::string_t value; + result = result && ModelBase::fromJson(val.at(utility::conversions::to_string_t("FileName")), value); + content->setFileName( value ); + } + if(val.has_field(utility::conversions::to_string_t("InputStream"))) + { + utility::string_t value; + result = result && ModelBase::fromJson(val.at(utility::conversions::to_string_t("InputStream")), value); + content->setData( ModelBase::fromBase64( value ) ); + } + } + return result; +} +bool ModelBase::fromJson( const web::json::value& val, std::shared_ptr &outVal ) +{ + bool ok = false; + if(outVal == nullptr) + { + outVal = std::shared_ptr(new utility::datetime()); + } + if( outVal != nullptr ) + { + ok = fromJson(val, *outVal); + } + return ok; +} +std::shared_ptr ModelBase::toHttpContent( const utility::string_t& name, bool value, const utility::string_t& contentType ) +{ + std::shared_ptr content( new HttpContent ); + content->setName( name ); + content->setContentDisposition( utility::conversions::to_string_t("form-data") ); + content->setContentType( contentType ); + std::stringstream* valueAsStringStream = new std::stringstream(); + (*valueAsStringStream) << value; + content->setData( std::shared_ptr( valueAsStringStream ) ); + return content; +} +std::shared_ptr ModelBase::toHttpContent( const utility::string_t& name, float value, const utility::string_t& contentType ) +{ + std::shared_ptr content( new HttpContent ); + content->setName( name ); + content->setContentDisposition( utility::conversions::to_string_t("form-data") ); + content->setContentType( contentType ); + std::stringstream* valueAsStringStream = new std::stringstream(); + (*valueAsStringStream) << value; + content->setData( std::shared_ptr( valueAsStringStream ) ); + return content; +} +std::shared_ptr ModelBase::toHttpContent( const utility::string_t& name, double value, const utility::string_t& contentType ) +{ + std::shared_ptr content( new HttpContent ); + content->setName( name ); + content->setContentDisposition( utility::conversions::to_string_t("form-data") ); + content->setContentType( contentType ); + std::stringstream* valueAsStringStream = new std::stringstream(); + (*valueAsStringStream) << value; + content->setData( std::shared_ptr( valueAsStringStream ) ); + return content; +} +std::shared_ptr ModelBase::toHttpContent( const utility::string_t& name, int32_t value, const utility::string_t& contentType ) +{ + std::shared_ptr content( new HttpContent ); + content->setName( name ); + content->setContentDisposition( utility::conversions::to_string_t("form-data") ); + content->setContentType( contentType ); + std::stringstream* valueAsStringStream = new std::stringstream(); + (*valueAsStringStream) << value; + content->setData( std::shared_ptr( valueAsStringStream ) ); + return content; +} +std::shared_ptr ModelBase::toHttpContent( const utility::string_t& name, int64_t value, const utility::string_t& contentType ) +{ + std::shared_ptr content( new HttpContent ); + content->setName( name ); + content->setContentDisposition( utility::conversions::to_string_t("form-data") ); + content->setContentType( contentType ); + std::stringstream* valueAsStringStream = new std::stringstream(); + (*valueAsStringStream) << value; + content->setData( std::shared_ptr( valueAsStringStream) ) ; + return content; +} +std::shared_ptr ModelBase::toHttpContent( const utility::string_t& name, const utility::string_t& value, const utility::string_t& contentType) +{ + std::shared_ptr content(new HttpContent); + content->setName( name ); + content->setContentDisposition( utility::conversions::to_string_t("form-data") ); + content->setContentType( contentType ); + content->setData( std::shared_ptr( new std::stringstream( utility::conversions::to_utf8string(value) ) ) ); + return content; +} +std::shared_ptr ModelBase::toHttpContent( const utility::string_t& name, const utility::datetime& value, const utility::string_t& contentType ) +{ + std::shared_ptr content( new HttpContent ); + content->setName( name ); + content->setContentDisposition( utility::conversions::to_string_t("form-data") ); + content->setContentType( contentType ); + content->setData( std::shared_ptr( new std::stringstream( utility::conversions::to_utf8string(value.to_string(utility::datetime::ISO_8601) ) ) ) ); + return content; +} +std::shared_ptr ModelBase::toHttpContent( const utility::string_t& name, const web::json::value& value, const utility::string_t& contentType ) +{ + std::shared_ptr content( new HttpContent ); + content->setName( name ); + content->setContentDisposition( utility::conversions::to_string_t("form-data") ); + content->setContentType( contentType ); + content->setData( std::shared_ptr( new std::stringstream( utility::conversions::to_utf8string(value.serialize()) ) ) ); + return content; +} +std::shared_ptr ModelBase::toHttpContent( const utility::string_t& name, const std::shared_ptr& value ) +{ + std::shared_ptr content( new HttpContent ); + if( value != nullptr ) + { + content->setName( name ); + content->setContentDisposition( value->getContentDisposition() ); + content->setContentType( value->getContentType() ); + content->setData( value->getData() ); + content->setFileName( value->getFileName() ); + } + return content; +} +std::shared_ptr ModelBase::toHttpContent(const utility::string_t& name, const std::shared_ptr& value , const utility::string_t& contentType ) +{ + std::shared_ptr content( new HttpContent ); + if (value != nullptr ) + { + content->setName( name ); + content->setContentDisposition( utility::conversions::to_string_t("form-data") ); + content->setContentType( contentType ); + content->setData( std::shared_ptr( new std::stringstream( utility::conversions::to_utf8string( toJson(*value).serialize() ) ) ) ); + } + return content; +} +bool ModelBase::fromHttpContent(std::shared_ptr val, bool & outVal ) +{ + utility::string_t str; + if( val == nullptr ) return false; + ModelBase::fromHttpContent(val, str); + return fromString(str, outVal); +} +bool ModelBase::fromHttpContent(std::shared_ptr val, float & outVal ) +{ + utility::string_t str; + if( val == nullptr ) return false; + ModelBase::fromHttpContent(val, str); + return fromString(str, outVal); +} +bool ModelBase::fromHttpContent(std::shared_ptr val, double & outVal ) +{ + utility::string_t str; + if( val == nullptr ) return false; + ModelBase::fromHttpContent(val, str); + return fromString(str, outVal); +} +bool ModelBase::fromHttpContent(std::shared_ptr val, int32_t & outVal ) +{ + utility::string_t str; + if( val == nullptr ) return false; + ModelBase::fromHttpContent(val, str); + return fromString(str, outVal); +} +bool ModelBase::fromHttpContent(std::shared_ptr val, int64_t & outVal ) +{ + utility::string_t str; + if( val == nullptr ) return false; + ModelBase::fromHttpContent(val, str); + return fromString(str, outVal); +} +bool ModelBase::fromHttpContent(std::shared_ptr val, utility::string_t & outVal ) +{ + if( val == nullptr ) return false; + std::shared_ptr data = val->getData(); + data->seekg( 0, data->beg ); + + std::string str((std::istreambuf_iterator(*data.get())), + std::istreambuf_iterator()); + outVal = utility::conversions::to_string_t(str); + return true; +} +bool ModelBase::fromHttpContent(std::shared_ptr val, utility::datetime & outVal ) +{ + utility::string_t str; + if( val == nullptr ) return false; + ModelBase::fromHttpContent(val, str); + outVal = utility::datetime::from_string(str, utility::datetime::ISO_8601); + return true; +} +bool ModelBase::fromHttpContent(std::shared_ptr val, web::json::value & outVal ) +{ + utility::string_t str; + if( val == nullptr ) return false; + ModelBase::fromHttpContent(val, str); + return fromString(str, outVal); +} +bool ModelBase::fromHttpContent(std::shared_ptr val, std::shared_ptr& outVal ) +{ + utility::string_t str; + if( val == nullptr ) return false; + if( outVal == nullptr ) + { + outVal = std::shared_ptr(new HttpContent()); + } + ModelBase::fromHttpContent(val, str); + return fromString(str, outVal); +} +// base64 encoding/decoding based on : https://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64#C.2B.2B +const static char Base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +const static char Base64PadChar = '='; +utility::string_t ModelBase::toBase64( utility::string_t value ) +{ + std::shared_ptr source( new std::stringstream( utility::conversions::to_utf8string(value) ) ); + return ModelBase::toBase64(source); +} +utility::string_t ModelBase::toBase64( std::shared_ptr value ) +{ + value->seekg( 0, value->end ); + size_t length = value->tellg(); + value->seekg( 0, value->beg ); + utility::string_t base64; + base64.reserve( ((length / 3) + (length % 3 > 0)) * 4 ); + char read[3] = { 0 }; + uint32_t temp; + for ( size_t idx = 0; idx < length / 3; idx++ ) + { + value->read( read, 3 ); + temp = (read[0]) << 16; + temp += (read[1]) << 8; + temp += (read[2]); + base64.append( 1, Base64Chars[(temp & 0x00FC0000) >> 18] ); + base64.append( 1, Base64Chars[(temp & 0x0003F000) >> 12] ); + base64.append( 1, Base64Chars[(temp & 0x00000FC0) >> 6] ); + base64.append( 1, Base64Chars[(temp & 0x0000003F)] ); + } + switch ( length % 3 ) + { + case 1: + value->read( read, 1 ); + temp = read[0] << 16; + base64.append( 1, Base64Chars[(temp & 0x00FC0000) >> 18] ); + base64.append( 1, Base64Chars[(temp & 0x0003F000) >> 12] ); + base64.append( 2, Base64PadChar ); + break; + case 2: + value->read( read, 2 ); + temp = read[0] << 16; + temp += read[1] << 8; + base64.append( 1, Base64Chars[(temp & 0x00FC0000) >> 18] ); + base64.append( 1, Base64Chars[(temp & 0x0003F000) >> 12] ); + base64.append( 1, Base64Chars[(temp & 0x00000FC0) >> 6] ); + base64.append( 1, Base64PadChar ); + break; + } + return base64; +} +std::shared_ptr ModelBase::fromBase64( const utility::string_t& encoded ) +{ + std::shared_ptr result(new std::stringstream); + + char outBuf[3] = { 0 }; + uint32_t temp = 0; + + utility::string_t::const_iterator cursor = encoded.begin(); + while ( cursor < encoded.end() ) + { + for ( size_t quantumPosition = 0; quantumPosition < 4; quantumPosition++ ) + { + temp <<= 6; + if ( *cursor >= 0x41 && *cursor <= 0x5A ) + { + temp |= *cursor - 0x41; + } + else if ( *cursor >= 0x61 && *cursor <= 0x7A ) + { + temp |= *cursor - 0x47; + } + else if ( *cursor >= 0x30 && *cursor <= 0x39 ) + { + temp |= *cursor + 0x04; + } + else if ( *cursor == 0x2B ) + { + temp |= 0x3E; //change to 0x2D for URL alphabet + } + else if ( *cursor == 0x2F ) + { + temp |= 0x3F; //change to 0x5F for URL alphabet + } + else if ( *cursor == Base64PadChar ) //pad + { + switch ( encoded.end() - cursor ) + { + case 1: //One pad character + outBuf[0] = (temp >> 16) & 0x000000FF; + outBuf[1] = (temp >> 8) & 0x000000FF; + result->write( outBuf, 2 ); + return result; + case 2: //Two pad characters + outBuf[0] = (temp >> 10) & 0x000000FF; + result->write( outBuf, 1 ); + return result; + default: + throw web::json::json_exception( utility::conversions::to_string_t( "Invalid Padding in Base 64!" ).c_str() ); + } + } + else + { + throw web::json::json_exception( utility::conversions::to_string_t( "Non-Valid Character in Base 64!" ).c_str() ); + } + ++cursor; + } + + outBuf[0] = (temp >> 16) & 0x000000FF; + outBuf[1] = (temp >> 8) & 0x000000FF; + outBuf[2] = (temp) & 0x000000FF; + result->write( outBuf, 3 ); + } + + return result; +} + +{{#modelNamespaceDeclarations}} +} +{{/modelNamespaceDeclarations}} diff --git a/api/_build/my-cpp-templates/cpp-restsdk/multipart-header.mustache b/api/_build/my-cpp-templates/cpp-restsdk/multipart-header.mustache new file mode 100644 index 0000000..7430f4e --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/multipart-header.mustache @@ -0,0 +1,49 @@ +{{>licenseInfo}} +/* + * MultipartFormData.h + * + * This class represents a container for building application/x-multipart-formdata requests. + */ + +#ifndef {{modelHeaderGuardPrefix}}_MultipartFormData_H_ +#define {{modelHeaderGuardPrefix}}_MultipartFormData_H_ + +{{{defaultInclude}}} +#include "{{packageName}}/IHttpBody.h" +#include "{{packageName}}/HttpContent.h" + +#include + +#include +#include +#include + +{{#modelNamespaceDeclarations}} +namespace {{this}} { +{{/modelNamespaceDeclarations}} + +class {{declspec}} MultipartFormData + : public IHttpBody +{ +public: + MultipartFormData(); + MultipartFormData(const utility::string_t& boundary); + virtual ~MultipartFormData(); + + virtual void add( std::shared_ptr content ); + virtual utility::string_t getBoundary(); + virtual std::shared_ptr getContent(const utility::string_t& name) const; + virtual bool hasContent(const utility::string_t& name) const; + virtual void writeTo( std::ostream& target ); + +protected: + std::vector> m_Contents; + utility::string_t m_Boundary; + std::map> m_ContentLookup; +}; + +{{#modelNamespaceDeclarations}} +} +{{/modelNamespaceDeclarations}} + +#endif /* {{modelHeaderGuardPrefix}}_MultipartFormData_H_ */ diff --git a/api/_build/my-cpp-templates/cpp-restsdk/multipart-source.mustache b/api/_build/my-cpp-templates/cpp-restsdk/multipart-source.mustache new file mode 100644 index 0000000..1b5c083 --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/multipart-source.mustache @@ -0,0 +1,100 @@ +{{>licenseInfo}} +#include "{{packageName}}/MultipartFormData.h" +#include "{{packageName}}/ModelBase.h" + +#include +#include + +{{#modelNamespaceDeclarations}} +namespace {{this}} { +{{/modelNamespaceDeclarations}} + +MultipartFormData::MultipartFormData() +{ + utility::stringstream_t uuidString; + uuidString << boost::uuids::random_generator()(); + m_Boundary = uuidString.str(); +} + +MultipartFormData::MultipartFormData(const utility::string_t& boundary) + : m_Boundary(boundary) +{ + +} + +MultipartFormData::~MultipartFormData() +{ +} + +utility::string_t MultipartFormData::getBoundary() +{ + return m_Boundary; +} + +void MultipartFormData::add( std::shared_ptr content ) +{ + m_Contents.push_back( content ); + m_ContentLookup[content->getName()] = content; +} + +bool MultipartFormData::hasContent(const utility::string_t& name) const +{ + return m_ContentLookup.find(name) != m_ContentLookup.end(); +} + +std::shared_ptr MultipartFormData::getContent(const utility::string_t& name) const +{ + auto result = m_ContentLookup.find(name); + if(result == m_ContentLookup.end()) + { + return std::shared_ptr(nullptr); + } + return result->second; +} + +void MultipartFormData::writeTo( std::ostream& target ) +{ + for ( size_t i = 0; i < m_Contents.size(); i++ ) + { + std::shared_ptr content = m_Contents[i]; + + // boundary + target << "\r\n" << "--" << utility::conversions::to_utf8string( m_Boundary ) << "\r\n"; + + // headers + target << "Content-Disposition: " << utility::conversions::to_utf8string( content->getContentDisposition() ); + if ( content->getName().size() > 0 ) + { + target << "; name=\"" << utility::conversions::to_utf8string( content->getName() ) << "\""; + } + if ( content->getFileName().size() > 0 ) + { + target << "; filename=\"" << utility::conversions::to_utf8string( content->getFileName() ) << "\""; + } + target << "\r\n"; + + if ( content->getContentType().size() > 0 ) + { + target << "Content-Type: " << utility::conversions::to_utf8string( content->getContentType() ) << "\r\n"; + } + + target << "\r\n"; + + // body + std::shared_ptr data = content->getData(); + + data->seekg( 0, data->end ); + std::vector dataBytes( data->tellg() ); + + data->seekg( 0, data->beg ); + data->read( &dataBytes[0], dataBytes.size() ); + + std::copy( dataBytes.begin(), dataBytes.end(), std::ostreambuf_iterator( target ) ); + } + + target << "\r\n--" << utility::conversions::to_utf8string( m_Boundary ) << "--\r\n"; +} + +{{#modelNamespaceDeclarations}} +} +{{/modelNamespaceDeclarations}} diff --git a/api/_build/my-cpp-templates/cpp-restsdk/object-header.mustache b/api/_build/my-cpp-templates/cpp-restsdk/object-header.mustache new file mode 100644 index 0000000..3597e1b --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/object-header.mustache @@ -0,0 +1,50 @@ +{{>licenseInfo}} +/* + * Object.h + * + * This is the implementation of a JSON object. + */ + +#ifndef {{modelHeaderGuardPrefix}}_Object_H_ +#define {{modelHeaderGuardPrefix}}_Object_H_ + +{{{defaultInclude}}} +#include "{{packageName}}/ModelBase.h" + +#include +#include + +{{#modelNamespaceDeclarations}} +namespace {{this}} { +{{/modelNamespaceDeclarations}} + +class {{declspec}} Object : public ModelBase +{ +public: + Object(); + virtual ~Object(); + + ///////////////////////////////////////////// + /// ModelBase overrides + void validate() override; + + web::json::value toJson() const override; + bool fromJson(const web::json::value& json) override; + + void toMultipart(std::shared_ptr multipart, const utility::string_t& namePrefix) const override; + bool fromMultiPart(std::shared_ptr multipart, const utility::string_t& namePrefix) override; + + ///////////////////////////////////////////// + /// Object manipulation + web::json::value getValue(const utility::string_t& key) const; + void setValue(const utility::string_t& key, const web::json::value& value); + +private: + web::json::value m_object; +}; + +{{#modelNamespaceDeclarations}} +} +{{/modelNamespaceDeclarations}} + +#endif /* {{modelHeaderGuardPrefix}}_Object_H_ */ diff --git a/api/_build/my-cpp-templates/cpp-restsdk/object-source.mustache b/api/_build/my-cpp-templates/cpp-restsdk/object-source.mustache new file mode 100644 index 0000000..fe9c93b --- /dev/null +++ b/api/_build/my-cpp-templates/cpp-restsdk/object-source.mustache @@ -0,0 +1,79 @@ +{{>licenseInfo}} +#include "{{packageName}}/Object.h" + +{{#modelNamespaceDeclarations}} +namespace {{this}} { +{{/modelNamespaceDeclarations}} + +Object::Object() +{ + m_object = web::json::value::object(); +} + +Object::~Object() +{ +} + +void Object::validate() +{ + +} + +web::json::value Object::toJson() const +{ + return m_object; +} + +bool Object::fromJson(const web::json::value& val) +{ + if (val.is_object()) + { + m_object = val; + m_IsSet = true; + } + return isSet(); +} + +void Object::toMultipart(std::shared_ptr multipart, const utility::string_t& prefix) const +{ + utility::string_t namePrefix = prefix; + if(namePrefix.size() > 0 && namePrefix.substr(namePrefix.size() - 1) != utility::conversions::to_string_t(".")) + { + namePrefix += utility::conversions::to_string_t("."); + } + multipart->add(ModelBase::toHttpContent(namePrefix + utility::conversions::to_string_t("object"), m_object)); +} + +bool Object::fromMultiPart(std::shared_ptr multipart, const utility::string_t& prefix) +{ + utility::string_t namePrefix = prefix; + if(namePrefix.size() > 0 && namePrefix.substr(namePrefix.size() - 1) != utility::conversions::to_string_t(".")) + { + namePrefix += utility::conversions::to_string_t("."); + } + + if( ModelBase::fromHttpContent(multipart->getContent(namePrefix + utility::conversions::to_string_t("object")), m_object ) ) + { + m_IsSet = true; + } + return isSet(); +} + +web::json::value Object::getValue(const utility::string_t& key) const +{ + return m_object.at(key); +} + + +void Object::setValue(const utility::string_t& key, const web::json::value& value) +{ + if( !value.is_null() ) + { + m_object[key] = value; + m_IsSet = true; + } +} + +{{#modelNamespaceDeclarations}} +} +{{/modelNamespaceDeclarations}} From a22c96e7a021d10b80bf7d5f08a4823025e2e4a2 Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 5 Dec 2025 23:27:02 +0300 Subject: [PATCH 3/9] build(tgbot-gen): fixing a generator error with a tag The generator makes an error when generating tags with a link to the tag. At the moment, this is fixed by direct insertion. This is not the best solution. If a better option is found, we will fix it. --- api/_build/openapi.yaml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/api/_build/openapi.yaml b/api/_build/openapi.yaml index e096beb..d7a6fac 100644 --- a/api/_build/openapi.yaml +++ b/api/_build/openapi.yaml @@ -550,20 +550,13 @@ components: required: - id - name - Tag: - description: 'A localized tag: keys are language codes (ISO 639-1), values are tag names' - type: object - example: - en: Shojo - ru: Сёдзё - ja: 少女 - additionalProperties: - type: string Tags: description: Array of localized tags type: array items: - $ref: '#/components/schemas/Tag' + type: object + additionalProperties: + type: string example: - en: Shojo ru: Сёдзё From 19164b8d9dc9e72585250df09a2bd51e44723ee4 Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 5 Dec 2025 23:42:30 +0300 Subject: [PATCH 4/9] refactor(tgbot-front): moved the navigation handler to a separate file --- modules/bot/front/include/handlers.hpp | 2 +- modules/bot/front/src/handleNavigation.cpp | 147 +++++++++++++++++++++ modules/bot/front/src/handlers.cpp | 146 +------------------- 3 files changed, 150 insertions(+), 145 deletions(-) create mode 100644 modules/bot/front/src/handleNavigation.cpp diff --git a/modules/bot/front/include/handlers.hpp b/modules/bot/front/include/handlers.hpp index cb462cd..873d74c 100644 --- a/modules/bot/front/include/handlers.hpp +++ b/modules/bot/front/include/handlers.hpp @@ -71,7 +71,7 @@ private: /// @param userId Идентификатор пользователя /// @param payload Полезная нагрузка /// @return HandlerResult - static HandlerResult returnMyTitles(int64_t userId, int64_t payload); + /// static HandlerResult returnMyTitles(int64_t userId, int64_t payload); /// @brief Вход в новое состояние /// @param ctx текущий контекст diff --git a/modules/bot/front/src/handleNavigation.cpp b/modules/bot/front/src/handleNavigation.cpp new file mode 100644 index 0000000..0bd02a3 --- /dev/null +++ b/modules/bot/front/src/handleNavigation.cpp @@ -0,0 +1,147 @@ +#include "handlers.hpp" +#include "KeyboardFactory.hpp" +#include "structs.hpp" +#include "constants.hpp" + +void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query, UserContext& ctx) { + const auto& current = ctx.history.back(); // текущий экран + const std::string& data = query->data; + + // Пагинация (в списках) + if ((data == BotConstants::Callback::LIST_PREV || data == BotConstants::Callback::LIST_NEXT) + && (current.state == UserState::VIEWING_MY_TITLES || current.state == UserState::VIEWING_REVIEW_LIST || + current.state == UserState::VIEWING_FOUND_TITLES)) { + + int64_t newPayload = current.payload; + if (data == BotConstants::Callback::LIST_PREV && newPayload > 0) { + reducePayload(newPayload, current.state); + } else if (data == BotConstants::Callback::LIST_NEXT) { + increasePayload(newPayload, current.state); + } else { + if (data == BotConstants::Callback::LIST_PREV) { + std::cout << "Error: navigation:prev callback for 1st page" << std::endl; + return; + } + // TODO: log + std::cout << "Error: navigation:prev unknown error" << std::endl; + } + + ctx.history.back().payload = newPayload; + + auto result = renderCurrent(query, ctx); + if(result.message == "meow") return; // TODO: убрать + editMessage(query, result); + return; + } + + // Обработка back по интерфейсу + if (data == BotConstants::Callback::NAV_BACK) { + if (!popState(ctx)) { + sendError(query, BotConstants::Text::SAD_ERROR); + return; + } + auto result = renderCurrent(query, ctx); + if(result.message == "meow") return; // TODO: убрать + editMessage(query, result); + return; + } + + // Переходы вперёд (push) + auto newStepOpt = computeNextStep(query, current); + if (!newStepOpt.has_value()) { + sendError(query, BotConstants::Text::SAD_ERROR); + return; + } + + ctx.history.push_back(*newStepOpt); + auto result = renderCurrent(query, ctx); + if(result.message == "meow") return; // TODO: убрать + editMessage(query, result); +} + +HandlerResult BotHandlers::renderCurrent(TgBot::CallbackQuery::Ptr query, const UserContext& ctx) { + const auto& step = ctx.history.back(); + int64_t userId = query->from->id; + switch (step.state) { + case UserState::MAIN_MENU: + return showMainMenu(); + case UserState::VIEWING_MY_TITLES: + server_.fetchUserTitlesAsync(std::to_string(2)) // ALARM: тестовое значение вместо userId + .then([this, query](pplx::task> t) { + try { + auto titles = t.get(); + + std::string message = formatTitlesList(titles); + auto keyboard = KeyboardFactory::createMyTitles(titles); + + editMessage(query, {message, keyboard}); + + } catch (const std::exception& e) { + sendError(query, BotConstants::Text::SERVER_ERROR); + // Логирование ошибки (например, в cerr) + } + }); + + return {"meow", nullptr}; + /* + case UserState::VIEWING_TITLE_PAGE: + return returnTitlePage(step.payload); // payload = titleId + case UserState::VIEWING_REVIEW: + return returnReview(step.payload); // payload = reviewId + case UserState::AWAITING_REVIEW: + return HandlerResult{"Пришлите текст отзыва:", nullptr}; + // ... + */ + default: + return HandlerResult{BotConstants::Text::SAD_ERROR, nullptr}; + } +} + +std::optional BotHandlers::computeNextStep( + const TgBot::CallbackQuery::Ptr& query, + const NavigationStep& current +) { + const std::string& data = query->data; + + switch (current.state) { + case UserState::MAIN_MENU: + if (data == BotConstants::Callback::MY_TITLES) { + return NavigationStep{UserState::VIEWING_MY_TITLES, 0}; + } + break; + /* + case UserState::VIEWING_MY_TITLES: + if (data.starts_with("title_")) { + int64_t titleId = parseId(data); + return NavigationStep{UserState::VIEWING_TITLE_PAGE, titleId}; + } + break; + + case UserState::VIEWING_TITLE_PAGE: + if (data == BotConstants::Callback::ACTION_ADD_REVIEW) { + return NavigationStep{UserState::AWAITING_REVIEW, current.payload}; + } + if (data.starts_with("review_")) { + int64_t reviewId = parseId(data); + return NavigationStep{UserState::VIEWING_REVIEW, reviewId}; + } + break; + */ + default: + break; + } + return std::nullopt; +} + +std::string BotHandlers::formatTitlesList(const std::vector& titles) { + if (titles.empty()) { + return "У вас пока нет тайтлов."; + } + + std::string msg; + for (size_t i = 0; i < titles.size(); ++i) { + // num — 0-based, но в сообщении показываем 1-based + msg += std::to_string(i + 1) + ". " + titles[i].name + "\n"; + } + return msg; +} \ No newline at end of file diff --git a/modules/bot/front/src/handlers.cpp b/modules/bot/front/src/handlers.cpp index 88e026e..d19166a 100644 --- a/modules/bot/front/src/handlers.cpp +++ b/modules/bot/front/src/handlers.cpp @@ -20,6 +20,7 @@ void BotHandlers::handleCallback(TgBot::CallbackQuery::Ptr query) { processCallbackImpl(query); } +/* deprecated. will be deleted soon. HandlerResult BotHandlers::returnMyTitles(int64_t userId, int64_t payload) { // Здесь должен происходить запрос на сервер std::vector titles = {{123, "Школа мертвяков", "", 1}, {321, "KissXsis", "", 2}}; @@ -29,7 +30,7 @@ HandlerResult BotHandlers::returnMyTitles(int64_t userId, int64_t payload) { result.message = "1. Школа мертвяков\n2. KissXsis\n"; return result; -} +}*/ void BotHandlers::handleMessage(TgBot::Message::Ptr message) { //TODO: просмотр состояния пользователя @@ -60,62 +61,6 @@ void BotHandlers::processCallbackImpl(TgBot::CallbackQuery::Ptr query) { } } -void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query, UserContext& ctx) { - const auto& current = ctx.history.back(); // текущий экран - const std::string& data = query->data; - - // Пагинация (в списках) - if ((data == BotConstants::Callback::LIST_PREV || data == BotConstants::Callback::LIST_NEXT) - && (current.state == UserState::VIEWING_MY_TITLES || current.state == UserState::VIEWING_REVIEW_LIST || - current.state == UserState::VIEWING_FOUND_TITLES)) { - - int64_t newPayload = current.payload; - if (data == BotConstants::Callback::LIST_PREV && newPayload > 0) { - reducePayload(newPayload, current.state); - } else if (data == BotConstants::Callback::LIST_NEXT) { - increasePayload(newPayload, current.state); - } else { - if (data == BotConstants::Callback::LIST_PREV) { - std::cout << "Error: navigation:prev callback for 1st page" << std::endl; - return; - } - // TODO: log - std::cout << "Error: navigation:prev unknown error" << std::endl; - } - - ctx.history.back().payload = newPayload; - - auto result = renderCurrent(query, ctx); - if(result.message == "meow") return; // TODO: убрать - editMessage(query, result); - return; - } - - // Обработка back по интерфейсу - if (data == BotConstants::Callback::NAV_BACK) { - if (!popState(ctx)) { - sendError(query, BotConstants::Text::SAD_ERROR); - return; - } - auto result = renderCurrent(query, ctx); - if(result.message == "meow") return; // TODO: убрать - editMessage(query, result); - return; - } - - // Переходы вперёд (push) - auto newStepOpt = computeNextStep(query, current); - if (!newStepOpt.has_value()) { - sendError(query, BotConstants::Text::SAD_ERROR); - return; - } - - ctx.history.push_back(*newStepOpt); - auto result = renderCurrent(query, ctx); - if(result.message == "meow") return; // TODO: убрать - editMessage(query, result); -} - void BotHandlers::pushState(UserContext& ctx, UserState newState, int64_t payload) { ctx.history.push_back({newState, payload}); } @@ -164,44 +109,6 @@ void BotHandlers::editMessage(TgBot::CallbackQuery::Ptr query, HandlerResult res ); } -HandlerResult BotHandlers::renderCurrent(TgBot::CallbackQuery::Ptr query, const UserContext& ctx) { - const auto& step = ctx.history.back(); - int64_t userId = query->from->id; - switch (step.state) { - case UserState::MAIN_MENU: - return showMainMenu(); - case UserState::VIEWING_MY_TITLES: - server_.fetchUserTitlesAsync(std::to_string(2)) // ALARM: тестовое значение вместо userId - .then([this, query](pplx::task> t) { - try { - auto titles = t.get(); - - std::string message = formatTitlesList(titles); - auto keyboard = KeyboardFactory::createMyTitles(titles); - - editMessage(query, {message, keyboard}); - - } catch (const std::exception& e) { - sendError(query, BotConstants::Text::SERVER_ERROR); - // Логирование ошибки (например, в cerr) - } - }); - - return {"meow", nullptr}; - /* - case UserState::VIEWING_TITLE_PAGE: - return returnTitlePage(step.payload); // payload = titleId - case UserState::VIEWING_REVIEW: - return returnReview(step.payload); // payload = reviewId - case UserState::AWAITING_REVIEW: - return HandlerResult{"Пришлите текст отзыва:", nullptr}; - // ... - */ - default: - return HandlerResult{BotConstants::Text::SAD_ERROR, nullptr}; - } -} - HandlerResult BotHandlers::showMainMenu() { auto keyboard = KeyboardFactory::createMainMenu(); @@ -221,42 +128,6 @@ void BotHandlers::sendError(TgBot::CallbackQuery::Ptr query, const std::string& editMessage(query, {errText, keyboard}); } -std::optional BotHandlers::computeNextStep( - const TgBot::CallbackQuery::Ptr& query, - const NavigationStep& current -) { - const std::string& data = query->data; - - switch (current.state) { - case UserState::MAIN_MENU: - if (data == BotConstants::Callback::MY_TITLES) { - return NavigationStep{UserState::VIEWING_MY_TITLES, 0}; - } - break; - /* - case UserState::VIEWING_MY_TITLES: - if (data.starts_with("title_")) { - int64_t titleId = parseId(data); - return NavigationStep{UserState::VIEWING_TITLE_PAGE, titleId}; - } - break; - - case UserState::VIEWING_TITLE_PAGE: - if (data == BotConstants::Callback::ACTION_ADD_REVIEW) { - return NavigationStep{UserState::AWAITING_REVIEW, current.payload}; - } - if (data.starts_with("review_")) { - int64_t reviewId = parseId(data); - return NavigationStep{UserState::VIEWING_REVIEW, reviewId}; - } - break; - */ - default: - break; - } - return std::nullopt; -} - void BotHandlers::createInitContext(int64_t chatId) { NavigationStep init = {UserState::MAIN_MENU, BotConstants::NULL_PAYLOAD}; userContexts[chatId] = {chatId, {init}}; @@ -276,17 +147,4 @@ void BotHandlers::handleError(TgBot::CallbackQuery::Ptr query, UserContext& ctx) HandlerResult result = {BotConstants::Text::AUTH_ERROR, nullptr}; editMessage(query, result); } -} - -std::string BotHandlers::formatTitlesList(const std::vector& titles) { - if (titles.empty()) { - return "У вас пока нет тайтлов."; - } - - std::string msg; - for (size_t i = 0; i < titles.size(); ++i) { - // num — 0-based, но в сообщении показываем 1-based - msg += std::to_string(i + 1) + ". " + titles[i].name + "\n"; - } - return msg; } \ No newline at end of file From 7e0222d6f1c4c1a18a546501f36ca59eacbb5180 Mon Sep 17 00:00:00 2001 From: Kirill Date: Sat, 6 Dec 2025 01:11:34 +0300 Subject: [PATCH 5/9] refactor(tgbot-front): change editMessage args --- modules/bot/front/include/handlers.hpp | 4 ++-- modules/bot/front/src/KeyboardFactory.cpp | 10 ++++----- modules/bot/front/src/handleNavigation.cpp | 24 +++++++++++++--------- modules/bot/front/src/handlers.cpp | 20 ++++++++++-------- 4 files changed, 33 insertions(+), 25 deletions(-) diff --git a/modules/bot/front/include/handlers.hpp b/modules/bot/front/include/handlers.hpp index 873d74c..e1197fc 100644 --- a/modules/bot/front/include/handlers.hpp +++ b/modules/bot/front/include/handlers.hpp @@ -101,7 +101,7 @@ private: /// кнопки в интерфейсе /// @param query Callback запрос /// @param response Параметры ответа: клавиатура и текст - void editMessage(TgBot::CallbackQuery::Ptr query, HandlerResult response); + void editMessage(int64_t chatId, int64_t messageId, HandlerResult response); /// @brief Отрисовка текущего экрана (соотв. контексту) /// @param ctx - текущий контекст @@ -121,7 +121,7 @@ private: /// @brief Посылает интерфейс обработки ошибки на callback запрос /// @param query запрос - void sendError(TgBot::CallbackQuery::Ptr query, const std::string& errText); + void sendError(int64_t chatId, int64_t messageId, const std::string& errText); // Форматирование для отображения в сообщении std::string formatTitlesList(const std::vector& titles); diff --git a/modules/bot/front/src/KeyboardFactory.cpp b/modules/bot/front/src/KeyboardFactory.cpp index af6c1d8..b8bbd06 100644 --- a/modules/bot/front/src/KeyboardFactory.cpp +++ b/modules/bot/front/src/KeyboardFactory.cpp @@ -22,12 +22,12 @@ TgBot::InlineKeyboardMarkup::Ptr KeyboardFactory::createMyTitles(std::vector= 6) { + if(counter >= BotConstants::DISP_TITLES_NUM) { break; } auto button = std::make_shared(); - button->text = std::to_string(title.num) + " " + title.name; - button->callbackData = "title:" + std::to_string(title.num); + button->text = std::to_string(title.num + 1) + " " + title.name; + button->callbackData = BotConstants::Callback::CHOICE + std::to_string(title.num); row.push_back(button); counter++; if(counter % 2 == 0) { @@ -40,7 +40,7 @@ TgBot::InlineKeyboardMarkup::Ptr KeyboardFactory::createMyTitles(std::vector(); button->text = BotConstants::Button::PREV; - if(titles[0].num == 1) { + if(titles[0].num == 0) { button->callbackData = BotConstants::Callback::NAV_BACK; } else { @@ -51,7 +51,7 @@ TgBot::InlineKeyboardMarkup::Ptr KeyboardFactory::createMyTitles(std::vector(); button_prev->text = BotConstants::Button::PREV; - if(titles[0].num == 1) { + if(titles[0].num == 0) { button_prev->callbackData = BotConstants::Callback::NAV_BACK; } else { diff --git a/modules/bot/front/src/handleNavigation.cpp b/modules/bot/front/src/handleNavigation.cpp index 0bd02a3..c934885 100644 --- a/modules/bot/front/src/handleNavigation.cpp +++ b/modules/bot/front/src/handleNavigation.cpp @@ -6,6 +6,8 @@ void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query, UserContext& ctx) { const auto& current = ctx.history.back(); // текущий экран const std::string& data = query->data; + int64_t chatId = query->message->chat->id; + int64_t messageId = query->message->messageId; // Пагинация (в списках) if ((data == BotConstants::Callback::LIST_PREV || data == BotConstants::Callback::LIST_NEXT) @@ -30,54 +32,56 @@ void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query, UserContext& auto result = renderCurrent(query, ctx); if(result.message == "meow") return; // TODO: убрать - editMessage(query, result); + editMessage(chatId, messageId, result); return; } // Обработка back по интерфейсу if (data == BotConstants::Callback::NAV_BACK) { if (!popState(ctx)) { - sendError(query, BotConstants::Text::SAD_ERROR); + sendError(chatId, messageId, BotConstants::Text::SAD_ERROR); return; } auto result = renderCurrent(query, ctx); if(result.message == "meow") return; // TODO: убрать - editMessage(query, result); + editMessage(chatId, messageId, result); return; } - // Переходы вперёд (push) + // Переходы вперёд (pyush) auto newStepOpt = computeNextStep(query, current); if (!newStepOpt.has_value()) { - sendError(query, BotConstants::Text::SAD_ERROR); + sendError(chatId, messageId, BotConstants::Text::SAD_ERROR); return; } ctx.history.push_back(*newStepOpt); auto result = renderCurrent(query, ctx); if(result.message == "meow") return; // TODO: убрать - editMessage(query, result); + editMessage(chatId, messageId, result); } HandlerResult BotHandlers::renderCurrent(TgBot::CallbackQuery::Ptr query, const UserContext& ctx) { const auto& step = ctx.history.back(); - int64_t userId = query->from->id; + //int64_t userId = query->from->id; + int64_t chatId = query->message->chat->id; + int64_t messageId = query->message->messageId; switch (step.state) { case UserState::MAIN_MENU: return showMainMenu(); case UserState::VIEWING_MY_TITLES: server_.fetchUserTitlesAsync(std::to_string(2)) // ALARM: тестовое значение вместо userId - .then([this, query](pplx::task> t) { + .then([this, chatId, messageId](pplx::task> t) { try { auto titles = t.get(); std::string message = formatTitlesList(titles); auto keyboard = KeyboardFactory::createMyTitles(titles); - editMessage(query, {message, keyboard}); + editMessage(chatId, messageId, {message, keyboard}); } catch (const std::exception& e) { - sendError(query, BotConstants::Text::SERVER_ERROR); + sendError(chatId, messageId, BotConstants::Text::SERVER_ERROR); // Логирование ошибки (например, в cerr) } }); diff --git a/modules/bot/front/src/handlers.cpp b/modules/bot/front/src/handlers.cpp index d19166a..64fc082 100644 --- a/modules/bot/front/src/handlers.cpp +++ b/modules/bot/front/src/handlers.cpp @@ -40,10 +40,12 @@ void BotHandlers::handleMessage(TgBot::Message::Ptr message) { void BotHandlers::processCallbackImpl(TgBot::CallbackQuery::Ptr query) { const std::string& data = query->data; int64_t userId = query->from->id; + int64_t chatId = query->message->chat->id; + int64_t messageId = query->message->messageId; auto it = userContexts.find(userId); if (it == userContexts.end()) { // TODO: log - sendError(query, BotConstants::Text::AUTH_ERROR); + sendError(chatId, messageId, BotConstants::Text::AUTH_ERROR); std::cout << "Error: Не нашел пользователя " << userId; return; } @@ -97,11 +99,11 @@ void BotHandlers::increasePayload(int64_t& payload, const UserState curState) { } } -void BotHandlers::editMessage(TgBot::CallbackQuery::Ptr query, HandlerResult response) { +void BotHandlers::editMessage(int64_t chatId, int64_t messageId, HandlerResult response) { botApi.editMessageText( response.message, - query->message->chat->id, - query->message->messageId, + chatId, + messageId, "", "", nullptr, @@ -115,7 +117,7 @@ HandlerResult BotHandlers::showMainMenu() { return HandlerResult{BotConstants::Text::MAIN_MENU, keyboard}; } -void BotHandlers::sendError(TgBot::CallbackQuery::Ptr query, const std::string& errText) { +void BotHandlers::sendError(int64_t chatId, int64_t messageId, const std::string& errText) { //TODO: посылать сообщение с кнопкой возврата в главное меню TgBot::InlineKeyboardMarkup::Ptr keyboard; if (errText == BotConstants::Text::SAD_ERROR) { @@ -125,7 +127,7 @@ void BotHandlers::sendError(TgBot::CallbackQuery::Ptr query, const std::string& keyboard = nullptr; //KeyboardFactory::createError(BotConstants::Callback::ERROR_AUTH); } - editMessage(query, {errText, keyboard}); + editMessage(chatId, messageId, {errText, keyboard}); } void BotHandlers::createInitContext(int64_t chatId) { @@ -135,16 +137,18 @@ void BotHandlers::createInitContext(int64_t chatId) { void BotHandlers::handleError(TgBot::CallbackQuery::Ptr query, UserContext& ctx) { const std::string& data = query->data; + int64_t chatId = query->message->chat->id; + int64_t messageId = query->message->messageId; if(data == BotConstants::Callback::ERROR_NAVIGATION) { ctx.history.clear(); ctx.history.push_back({UserState::MAIN_MENU, 0}); auto result = showMainMenu(); - editMessage(query, result); + editMessage(chatId, messageId, result); } else if(data == BotConstants::Callback::ERROR_AUTH) { // TODO: продумать логику HandlerResult result = {BotConstants::Text::AUTH_ERROR, nullptr}; - editMessage(query, result); + editMessage(chatId, messageId, result); } } \ No newline at end of file From a7b47c564a593a77b568438fc751fc5f6a0e3b93 Mon Sep 17 00:00:00 2001 From: Kirill Date: Sat, 6 Dec 2025 01:59:28 +0300 Subject: [PATCH 6/9] refactor(tgbot-front): small cosmetics --- modules/bot/front/include/handlers.hpp | 3 +-- modules/bot/front/src/handleNavigation.cpp | 23 +++++++++------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/modules/bot/front/include/handlers.hpp b/modules/bot/front/include/handlers.hpp index e1197fc..417025f 100644 --- a/modules/bot/front/include/handlers.hpp +++ b/modules/bot/front/include/handlers.hpp @@ -105,8 +105,7 @@ private: /// @brief Отрисовка текущего экрана (соотв. контексту) /// @param ctx - текущий контекст - /// @return HandlerResult для нового состояния сообщения - HandlerResult renderCurrent(TgBot::CallbackQuery::Ptr query, const UserContext& ctx); + void renderCurrent(TgBot::CallbackQuery::Ptr query, const UserContext& ctx); /// @brief Логика переходов между контекстами (навигация на следующий шаг) /// @param query - запрос diff --git a/modules/bot/front/src/handleNavigation.cpp b/modules/bot/front/src/handleNavigation.cpp index c934885..b0515ae 100644 --- a/modules/bot/front/src/handleNavigation.cpp +++ b/modules/bot/front/src/handleNavigation.cpp @@ -30,9 +30,7 @@ void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query, UserContext& ctx.history.back().payload = newPayload; - auto result = renderCurrent(query, ctx); - if(result.message == "meow") return; // TODO: убрать - editMessage(chatId, messageId, result); + renderCurrent(query, ctx); return; } @@ -42,13 +40,11 @@ void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query, UserContext& sendError(chatId, messageId, BotConstants::Text::SAD_ERROR); return; } - auto result = renderCurrent(query, ctx); - if(result.message == "meow") return; // TODO: убрать - editMessage(chatId, messageId, result); + renderCurrent(query, ctx); return; } - // Переходы вперёд (pyush) + // Переходы вперёд (push) auto newStepOpt = computeNextStep(query, current); if (!newStepOpt.has_value()) { sendError(chatId, messageId, BotConstants::Text::SAD_ERROR); @@ -56,19 +52,18 @@ void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query, UserContext& } ctx.history.push_back(*newStepOpt); - auto result = renderCurrent(query, ctx); - if(result.message == "meow") return; // TODO: убрать - editMessage(chatId, messageId, result); + renderCurrent(query, ctx); } -HandlerResult BotHandlers::renderCurrent(TgBot::CallbackQuery::Ptr query, const UserContext& ctx) { +void BotHandlers::renderCurrent(TgBot::CallbackQuery::Ptr query, const UserContext& ctx) { const auto& step = ctx.history.back(); //int64_t userId = query->from->id; int64_t chatId = query->message->chat->id; int64_t messageId = query->message->messageId; switch (step.state) { case UserState::MAIN_MENU: - return showMainMenu(); + editMessage(chatId, messageId, showMainMenu()); + return; case UserState::VIEWING_MY_TITLES: server_.fetchUserTitlesAsync(std::to_string(2)) // ALARM: тестовое значение вместо userId .then([this, chatId, messageId](pplx::task> t) { @@ -86,7 +81,7 @@ HandlerResult BotHandlers::renderCurrent(TgBot::CallbackQuery::Ptr query, const } }); - return {"meow", nullptr}; + return; /* case UserState::VIEWING_TITLE_PAGE: return returnTitlePage(step.payload); // payload = titleId @@ -97,7 +92,7 @@ HandlerResult BotHandlers::renderCurrent(TgBot::CallbackQuery::Ptr query, const // ... */ default: - return HandlerResult{BotConstants::Text::SAD_ERROR, nullptr}; + return editMessage(chatId, messageId, HandlerResult{BotConstants::Text::SAD_ERROR, nullptr}); } } From b1c035ae350442814f788fae22dae5cbd8d4da9f Mon Sep 17 00:00:00 2001 From: Kirill Date: Sat, 6 Dec 2025 02:44:24 +0300 Subject: [PATCH 7/9] feat(tgbot-front): start creating thread-safe user context --- modules/bot/front/include/BotUserContext.hpp | 55 ++++++++++++++++++++ modules/bot/front/src/BotUserContext.cpp | 43 +++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 modules/bot/front/include/BotUserContext.hpp create mode 100644 modules/bot/front/src/BotUserContext.cpp diff --git a/modules/bot/front/include/BotUserContext.hpp b/modules/bot/front/include/BotUserContext.hpp new file mode 100644 index 0000000..bf69058 --- /dev/null +++ b/modules/bot/front/include/BotUserContext.hpp @@ -0,0 +1,55 @@ +#pragma once +#include +#include +#include +#include + +enum class UserState { + MAIN_MENU, // Главное меню + VIEWING_MY_TITLES, // Список моих тайтлов + AWAITING_TITLE_NAME, // Жду название тайтла для поиска + VIEWING_FOUND_TITLES, // Смотрю найденные тайтлы + VIEWING_TITLE_PAGE, // Смотрю страничку тайтла + AWAITING_REVIEW, // Жду ревью на тайтл + VIEWING_REVIEW_LIST, // Смотрю список ревью на тайтл + VIEWING_REVIEW, // Смотрю (конкретное) ревью на тайтл + VIEWING_DESCRIPTION, // Смотрю описание тайтла + ERROR, // Ошибка состояния +}; + +struct NavigationStep { + UserState state; + int64_t payload; // ID тайтла, ревью и т.д. +}; + + +struct UserContext { + int64_t userId; + std::vector history; // Текущее состояние пользователя + история предыдущих состояний +}; + +class BotUserContext { +private: + mutable std::mutex mtx; + std::unordered_map userContexts; + +public: + // Получить копию контекста пользователя (или std::nullopt, если не найден) + std::optional getContext(int64_t userId) const; + + // Установить/обновить контекст пользователя + void setContext(int64_t userId, const UserContext& context); + + // Добавить шаг навигации к существующему контексту пользователя + // Если пользователя нет — создаётся новый контекст + void pushNavigationStep(int64_t userId, const NavigationStep& step); + + // Заменить текущую историю (полезно, например, при сбросе состояния) + void setNavigationHistory(int64_t userId, const std::vector& history); + + // Получить текущий шаг (последний в истории) или std::nullopt, если нет истории + std::optional getCurrentStep(int64_t userId) const; + + // Удалить контекст пользователя (например, при логауте) + void removeContext(int64_t userId); +}; \ No newline at end of file diff --git a/modules/bot/front/src/BotUserContext.cpp b/modules/bot/front/src/BotUserContext.cpp new file mode 100644 index 0000000..04a423c --- /dev/null +++ b/modules/bot/front/src/BotUserContext.cpp @@ -0,0 +1,43 @@ +#include "BotUserContext.hpp" + +std::optional BotUserContext::getContext(int64_t userId) const { + std::lock_guard lock(mtx); + auto it = userContexts.find(userId); + if (it != userContexts.end()) { + return it->second; + } + return std::nullopt; +} + +void BotUserContext::setContext(int64_t userId, const UserContext& context) { + std::lock_guard lock(mtx); + userContexts[userId] = context; +} + +void BotUserContext::pushNavigationStep(int64_t userId, const NavigationStep& step) { + std::lock_guard lock(mtx); + auto& ctx = userContexts[userId]; + ctx.userId = userId; + ctx.history.push_back(step); +} + +void BotUserContext::setNavigationHistory(int64_t userId, const std::vector& history) { + std::lock_guard lock(mtx); + auto& ctx = userContexts[userId]; + ctx.userId = userId; + ctx.history = history; +} + +std::optional BotUserContext::getCurrentStep(int64_t userId) const { + std::lock_guard lock(mtx); + auto it = userContexts.find(userId); + if (it != userContexts.end() && !it->second.history.empty()) { + return it->second.history.back(); + } + return std::nullopt; +} + +void BotUserContext::removeContext(int64_t userId) { + std::lock_guard lock(mtx); + userContexts.erase(userId); +} \ No newline at end of file From a6848bb4d7589075060a2afd6f6f5b214463aaaf Mon Sep 17 00:00:00 2001 From: Kirill Date: Sat, 6 Dec 2025 05:02:34 +0300 Subject: [PATCH 8/9] refactor(tgbot-front): replaced the context usage with a new thread-safe one --- modules/bot/front/include/BotUserContext.hpp | 8 ++++ modules/bot/front/include/handlers.hpp | 49 +++----------------- modules/bot/front/src/BotUserContext.cpp | 15 ++++++ modules/bot/front/src/front.cpp | 2 +- modules/bot/front/src/handleNavigation.cpp | 37 ++++++++++----- 5 files changed, 55 insertions(+), 56 deletions(-) diff --git a/modules/bot/front/include/BotUserContext.hpp b/modules/bot/front/include/BotUserContext.hpp index bf69058..a3e2609 100644 --- a/modules/bot/front/include/BotUserContext.hpp +++ b/modules/bot/front/include/BotUserContext.hpp @@ -3,6 +3,7 @@ #include #include #include +#include "constants.hpp" enum class UserState { MAIN_MENU, // Главное меню @@ -50,6 +51,13 @@ public: // Получить текущий шаг (последний в истории) или std::nullopt, если нет истории std::optional getCurrentStep(int64_t userId) const; + // pop последнего состояния. true в случае удачи + bool popStep(int64_t userId); + // Удалить контекст пользователя (например, при логауте) void removeContext(int64_t userId); + + /// @brief Создает контекст начального меню для пользователя + /// @param userId + void createInitContext(int64_t userId); }; \ No newline at end of file diff --git a/modules/bot/front/include/handlers.hpp b/modules/bot/front/include/handlers.hpp index 417025f..327c17b 100644 --- a/modules/bot/front/include/handlers.hpp +++ b/modules/bot/front/include/handlers.hpp @@ -4,6 +4,7 @@ #include #include #include "BotToServer.hpp" +#include "BotUserContext.hpp" /// @brief Структура возвращаемого значения класса BotHandlers для изменения текущего сообщения struct HandlerResult { @@ -11,30 +12,6 @@ struct HandlerResult { TgBot::InlineKeyboardMarkup::Ptr keyboard; }; -enum class UserState { - MAIN_MENU, // Главное меню - VIEWING_MY_TITLES, // Список моих тайтлов - AWAITING_TITLE_NAME, // Жду название тайтла для поиска - VIEWING_FOUND_TITLES, // Смотрю найденные тайтлы - VIEWING_TITLE_PAGE, // Смотрю страничку тайтла - AWAITING_REVIEW, // Жду ревью на тайтл - VIEWING_REVIEW_LIST, // Смотрю список ревью на тайтл - VIEWING_REVIEW, // Смотрю (конкретное) ревью на тайтл - VIEWING_DESCRIPTION, // Смотрю описание тайтла - ERROR, // Ошибка состояния -}; - -struct NavigationStep { - UserState state; - int64_t payload; // ID тайтла, ревью и т.д. -}; - - -struct UserContext { - int64_t userId; - std::vector history; // Текущее состояние пользователя + история предыдущих состояний -}; - class BotHandlers { public: BotHandlers(TgBot::Api api) : botApi(api) {;} @@ -51,19 +28,16 @@ public: /// в боте. /// @param message обрабатываемое сообщение void handleMessage(TgBot::Message::Ptr message); - - /// @brief Создает контекст начального меню для пользователя - /// @param chatId id чата пользователя - void createInitContext(int64_t chatId); + void initUser(int64_t userId); private: TgBot::Api botApi; - std::unordered_map userContexts; + BotUserContext contextManager; BotToServer server_; - void handleNavigation(TgBot::CallbackQuery::Ptr query, UserContext& ctx); + void handleNavigation(TgBot::CallbackQuery::Ptr query); - void handleError(TgBot::CallbackQuery::Ptr query, UserContext& ctx); + void handleError(TgBot::CallbackQuery::Ptr query); void processCallbackImpl(TgBot::CallbackQuery::Ptr query); @@ -73,17 +47,6 @@ private: /// @return HandlerResult /// static HandlerResult returnMyTitles(int64_t userId, int64_t payload); - /// @brief Вход в новое состояние - /// @param ctx текущий контекст - /// @param newState новое состояние, добавляемое в стек - /// @param payload полезная нагрузка этого состояния - void pushState(UserContext& ctx, UserState newState, int64_t payload); - - /// @brief Возврат в предыдущее состояние - /// @param ctx Текущий контекст - /// @return true в случае успеха - bool popState(UserContext& ctx); - /// @brief Уменьшает значение нагрузки с учетом текущего состояния /// @param payload Изменяемое значение нагрузки /// @param curState Текущее состояние @@ -105,7 +68,7 @@ private: /// @brief Отрисовка текущего экрана (соотв. контексту) /// @param ctx - текущий контекст - void renderCurrent(TgBot::CallbackQuery::Ptr query, const UserContext& ctx); + void renderCurrent(TgBot::CallbackQuery::Ptr query); /// @brief Логика переходов между контекстами (навигация на следующий шаг) /// @param query - запрос diff --git a/modules/bot/front/src/BotUserContext.cpp b/modules/bot/front/src/BotUserContext.cpp index 04a423c..2b2eb41 100644 --- a/modules/bot/front/src/BotUserContext.cpp +++ b/modules/bot/front/src/BotUserContext.cpp @@ -40,4 +40,19 @@ std::optional BotUserContext::getCurrentStep(int64_t userId) con void BotUserContext::removeContext(int64_t userId) { std::lock_guard lock(mtx); userContexts.erase(userId); +} + +bool BotUserContext::popStep(int64_t userId) { + std::lock_guard lock(mtx); + auto it = userContexts.find(userId); + if (it != userContexts.end() && (it->second.history.size() > 1)) { + it->second.history.pop_back(); + return true; + } + return false; +} + +void BotUserContext::createInitContext(int64_t userId) { + NavigationStep initStep = {UserState::MAIN_MENU, BotConstants::NULL_PAYLOAD}; + setContext(userId, {userId, {initStep}}); } \ No newline at end of file diff --git a/modules/bot/front/src/front.cpp b/modules/bot/front/src/front.cpp index 4f463e1..b9abd73 100644 --- a/modules/bot/front/src/front.cpp +++ b/modules/bot/front/src/front.cpp @@ -13,7 +13,7 @@ void AnimeBot::setupHandlers() { bot.getEvents().onCommand("start", [this](TgBot::Message::Ptr message) { sendMainMenu(message->chat->id); //TODO: производить инициализацию контекста только после авторизации - handler.createInitContext(message->chat->id); + handler.initUser(message->from->id); }); bot.getEvents().onCallbackQuery([this](TgBot::CallbackQuery::Ptr query) { diff --git a/modules/bot/front/src/handleNavigation.cpp b/modules/bot/front/src/handleNavigation.cpp index b0515ae..b8af2ef 100644 --- a/modules/bot/front/src/handleNavigation.cpp +++ b/modules/bot/front/src/handleNavigation.cpp @@ -3,13 +3,15 @@ #include "structs.hpp" #include "constants.hpp" -void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query, UserContext& ctx) { - const auto& current = ctx.history.back(); // текущий экран +void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query) { + //const auto& current = ctx.history.back(); // текущий экран const std::string& data = query->data; + int64_t userId = query->from->id; int64_t chatId = query->message->chat->id; int64_t messageId = query->message->messageId; // Пагинация (в списках) + /* Временно отключаем, все равно не функционирует :) if ((data == BotConstants::Callback::LIST_PREV || data == BotConstants::Callback::LIST_NEXT) && (current.state == UserState::VIEWING_MY_TITLES || current.state == UserState::VIEWING_REVIEW_LIST || current.state == UserState::VIEWING_FOUND_TITLES)) { @@ -32,35 +34,46 @@ void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query, UserContext& renderCurrent(query, ctx); return; - } + }*/ // Обработка back по интерфейсу if (data == BotConstants::Callback::NAV_BACK) { - if (!popState(ctx)) { + if (!contextManager.popStep(userId)) { sendError(chatId, messageId, BotConstants::Text::SAD_ERROR); return; } - renderCurrent(query, ctx); + renderCurrent(query); return; } // Переходы вперёд (push) - auto newStepOpt = computeNextStep(query, current); + std::optional currentStep = contextManager.getCurrentStep(userId); + if(!currentStep.has_value()) { + sendError(chatId, messageId, BotConstants::Text::SAD_ERROR); + return; + } + auto newStepOpt = computeNextStep(query, currentStep.value()); if (!newStepOpt.has_value()) { sendError(chatId, messageId, BotConstants::Text::SAD_ERROR); return; } - ctx.history.push_back(*newStepOpt); - renderCurrent(query, ctx); + contextManager.pushNavigationStep(userId, newStepOpt.value()); + renderCurrent(query); } -void BotHandlers::renderCurrent(TgBot::CallbackQuery::Ptr query, const UserContext& ctx) { - const auto& step = ctx.history.back(); - //int64_t userId = query->from->id; +void BotHandlers::renderCurrent(TgBot::CallbackQuery::Ptr query) { + int64_t userId = query->from->id; int64_t chatId = query->message->chat->id; int64_t messageId = query->message->messageId; - switch (step.state) { + + auto step = contextManager.getCurrentStep(userId); + if(!step.has_value()) {; + sendError(chatId, messageId, BotConstants::Text::SAD_ERROR); + return; + } + + switch (step.value().state) { case UserState::MAIN_MENU: editMessage(chatId, messageId, showMainMenu()); return; From dc4c430231bc3ec674eea4f8087029a667e65873 Mon Sep 17 00:00:00 2001 From: Kirill Date: Sat, 6 Dec 2025 05:05:10 +0300 Subject: [PATCH 9/9] refactor(tgbot-front): replaced the context usage with a new thread-safe one --- modules/bot/front/src/handlers.cpp | 37 +++++++++++------------------- 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/modules/bot/front/src/handlers.cpp b/modules/bot/front/src/handlers.cpp index 64fc082..0555cc3 100644 --- a/modules/bot/front/src/handlers.cpp +++ b/modules/bot/front/src/handlers.cpp @@ -42,37 +42,26 @@ void BotHandlers::processCallbackImpl(TgBot::CallbackQuery::Ptr query) { int64_t userId = query->from->id; int64_t chatId = query->message->chat->id; int64_t messageId = query->message->messageId; - auto it = userContexts.find(userId); - if (it == userContexts.end()) { + + std::optional ctx = contextManager.getContext(userId); + if (!ctx.has_value()) { // TODO: log sendError(chatId, messageId, BotConstants::Text::AUTH_ERROR); std::cout << "Error: Не нашел пользователя " << userId; return; } - UserContext& ctx = it->second; - if (data.starts_with(BotConstants::Callback::NAVIGATION)) { - handleNavigation(query, ctx); + handleNavigation(query); } else if (data.starts_with(BotConstants::Callback::ERROR)) { - handleError(query, ctx); + handleError(query); } else { botApi.sendMessage(query->message->chat->id, BotConstants::Text::SAD_ERROR, nullptr, nullptr); } } -void BotHandlers::pushState(UserContext& ctx, UserState newState, int64_t payload) { - ctx.history.push_back({newState, payload}); -} - -bool BotHandlers::popState(UserContext& ctx) { - if (ctx.history.size() <= 1) return false; // нельзя выйти из MAIN_MENU - ctx.history.pop_back(); - return true; -} - void BotHandlers::reducePayload(int64_t& payload, const UserState curState) { if (curState == UserState::VIEWING_MY_TITLES || curState == UserState::VIEWING_FOUND_TITLES) { @@ -130,19 +119,15 @@ void BotHandlers::sendError(int64_t chatId, int64_t messageId, const std::string editMessage(chatId, messageId, {errText, keyboard}); } -void BotHandlers::createInitContext(int64_t chatId) { - NavigationStep init = {UserState::MAIN_MENU, BotConstants::NULL_PAYLOAD}; - userContexts[chatId] = {chatId, {init}}; -} - -void BotHandlers::handleError(TgBot::CallbackQuery::Ptr query, UserContext& ctx) { +void BotHandlers::handleError(TgBot::CallbackQuery::Ptr query) { const std::string& data = query->data; + int64_t userId = query->from->id; int64_t chatId = query->message->chat->id; int64_t messageId = query->message->messageId; if(data == BotConstants::Callback::ERROR_NAVIGATION) { - ctx.history.clear(); - ctx.history.push_back({UserState::MAIN_MENU, 0}); + contextManager.removeContext(userId); + contextManager.createInitContext(userId); auto result = showMainMenu(); editMessage(chatId, messageId, result); } @@ -151,4 +136,8 @@ void BotHandlers::handleError(TgBot::CallbackQuery::Ptr query, UserContext& ctx) HandlerResult result = {BotConstants::Text::AUTH_ERROR, nullptr}; editMessage(chatId, messageId, result); } +} + +void BotHandlers::initUser(int64_t userId) { + contextManager.createInitContext(userId); } \ No newline at end of file