diff --git a/bin/configs/other/cpp-pistache-server-cpp-pistache.yaml b/bin/configs/cpp-pistache-server-cpp-pistache.yaml similarity index 100% rename from bin/configs/other/cpp-pistache-server-cpp-pistache.yaml rename to bin/configs/cpp-pistache-server-cpp-pistache.yaml diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppPistacheServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppPistacheServerCodegen.java index bf49fd425e0..3064af52c20 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppPistacheServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppPistacheServerCodegen.java @@ -128,6 +128,7 @@ public class CppPistacheServerCodegen extends AbstractCppCodegen { typeMapping.put("boolean", "bool"); typeMapping.put("array", "std::vector"); typeMapping.put("map", "std::map"); + typeMapping.put("set", "std::vector"); typeMapping.put("file", "std::string"); typeMapping.put("object", "Object"); typeMapping.put("binary", "std::string"); diff --git a/modules/openapi-generator/src/main/resources/cpp-pistache-server/api-header.mustache b/modules/openapi-generator/src/main/resources/cpp-pistache-server/api-header.mustache index 7beb0990a96..f614d478e96 100644 --- a/modules/openapi-generator/src/main/resources/cpp-pistache-server/api-header.mustache +++ b/modules/openapi-generator/src/main/resources/cpp-pistache-server/api-header.mustache @@ -14,24 +14,21 @@ #include #include {{^hasModelImport}}#include {{/hasModelImport}} +#include {{#imports}}{{{import}}} {{/imports}} -{{#apiNamespaceDeclarations}} -namespace {{this}} { -{{/apiNamespaceDeclarations}} - -{{#hasModelImport}} -using namespace {{modelNamespace}};{{/hasModelImport}} +namespace {{apiNamespace}} +{ class {{declspec}} {{classname}} { public: - {{classname}}(std::shared_ptr); - virtual ~{{classname}}() {} + explicit {{classname}}(const std::shared_ptr& rtr); + virtual ~{{classname}}() = default; void init(); - const std::string base = "{{basePathWithoutHost}}"; + static const std::string base; private: void setupRoutes(); @@ -41,9 +38,21 @@ private: {{/operation}} void {{classnameSnakeLowerCase}}_default_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); - std::shared_ptr router; - {{#operation}} + const std::shared_ptr router; + /// + /// Helper function to handle unexpected Exceptions during Parameter parsing and validation. + /// May be overriden to return custom error formats. + /// + virtual std::pair handleParsingException(const std::exception& ex) const noexcept; + + /// + /// Helper function to handle unexpected Exceptions during processing of the request in handler functions. + /// May be overriden to return custom error formats. + /// + virtual std::pair handleOperationException(const std::exception& ex) const noexcept; + + {{#operation}} /// /// {{summary}} /// @@ -54,7 +63,7 @@ private: {{#allParams}} /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} {{/allParams}} - virtual void {{operationIdSnakeCase}}({{#allParams}}const {{{dataType}}} &{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}Pistache::Http::ResponseWriter &response) = 0; + virtual void {{operationIdSnakeCase}}({{#allParams}}const {{#isModel}}{{modelNamespace}}::{{/isModel}}{{{dataType}}} &{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}Pistache::Http::ResponseWriter &response) = 0; {{/vendorExtensions.x-codegen-pistache-is-parsing-supported}} {{^vendorExtensions.x-codegen-pistache-is-parsing-supported}} virtual void {{operationIdSnakeCase}}(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter &response) = 0; @@ -63,9 +72,7 @@ private: }; -{{#apiNamespaceDeclarations}} -} -{{/apiNamespaceDeclarations}} +} // namespace {{apiNamespace}} #endif /* {{classname}}_H_ */ diff --git a/modules/openapi-generator/src/main/resources/cpp-pistache-server/api-impl-header.mustache b/modules/openapi-generator/src/main/resources/cpp-pistache-server/api-impl-header.mustache index 18ff119ae20..bd20c1bdf5a 100644 --- a/modules/openapi-generator/src/main/resources/cpp-pistache-server/api-impl-header.mustache +++ b/modules/openapi-generator/src/main/resources/cpp-pistache-server/api-impl-header.mustache @@ -22,17 +22,16 @@ {{#imports}}{{{import}}} {{/imports}} -{{#apiNamespaceDeclarations}} -namespace {{this}} { -{{/apiNamespaceDeclarations}} +namespace {{apiNamespace}} +{ {{#hasModelImport}} using namespace {{modelNamespace}};{{/hasModelImport}} class {{classname}}Impl : public {{apiNamespace}}::{{classname}} { public: - {{classname}}Impl(std::shared_ptr); - ~{{classname}}Impl() {} + explicit {{classname}}Impl(const std::shared_ptr& rtr); + ~{{classname}}Impl() override = default; {{#operation}} {{#vendorExtensions.x-codegen-pistache-is-parsing-supported}} @@ -45,11 +44,9 @@ public: }; -{{#apiNamespaceDeclarations}} -} -{{/apiNamespaceDeclarations}} +} // namespace {{apiNamespace}} {{/operations}} -#endif \ No newline at end of file +#endif diff --git a/modules/openapi-generator/src/main/resources/cpp-pistache-server/api-impl-source.mustache b/modules/openapi-generator/src/main/resources/cpp-pistache-server/api-impl-source.mustache index e5517bb487c..2e25a8034b3 100644 --- a/modules/openapi-generator/src/main/resources/cpp-pistache-server/api-impl-source.mustache +++ b/modules/openapi-generator/src/main/resources/cpp-pistache-server/api-impl-source.mustache @@ -10,9 +10,10 @@ namespace {{this}} { {{#hasModelImport}} using namespace {{modelNamespace}};{{/hasModelImport}} -{{classname}}Impl::{{classname}}Impl(std::shared_ptr rtr) +{{classname}}Impl::{{classname}}Impl(const std::shared_ptr& rtr) : {{classname}}(rtr) - { } +{ +} {{#operation}} {{#vendorExtensions.x-codegen-pistache-is-parsing-supported}} @@ -31,4 +32,4 @@ void {{classname}}Impl::{{operationIdSnakeCase}}(const Pistache::Rest::Request & } {{/apiNamespaceDeclarations}} -{{/operations}} \ No newline at end of file +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/cpp-pistache-server/api-source.mustache b/modules/openapi-generator/src/main/resources/cpp-pistache-server/api-source.mustache index 9225a5610c7..9814ba0cd62 100644 --- a/modules/openapi-generator/src/main/resources/cpp-pistache-server/api-source.mustache +++ b/modules/openapi-generator/src/main/resources/cpp-pistache-server/api-source.mustache @@ -4,16 +4,18 @@ #include "{{classname}}.h" #include "{{prefix}}Helpers.h" -{{#apiNamespaceDeclarations}} -namespace {{this}} { -{{/apiNamespaceDeclarations}} +namespace {{apiNamespace}} +{ using namespace {{helpersNamespace}}; {{#hasModelImport}} using namespace {{modelNamespace}};{{/hasModelImport}} -{{classname}}::{{classname}}(std::shared_ptr rtr) { - router = rtr; +const std::string {{classname}}::base = "{{basePathWithoutHost}}"; + +{{classname}}::{{classname}}(const std::shared_ptr& rtr) + : router(rtr) +{ } void {{classname}}::init() { @@ -31,8 +33,26 @@ void {{classname}}::setupRoutes() { router->addCustomHandler(Routes::bind(&{{classname}}::{{classnameSnakeLowerCase}}_default_handler, this)); } +std::pair {{classname}}::handleParsingException(const std::exception& ex) const noexcept +{ + try { + throw ex; + } catch (nlohmann::detail::exception &e) { + return std::make_pair(Pistache::Http::Code::Bad_Request, e.what()); + } catch ({{helpersNamespace}}::ValidationException &e) { + return std::make_pair(Pistache::Http::Code::Bad_Request, e.what()); + } +} + +std::pair {{classname}}::handleOperationException(const std::exception& ex) const noexcept +{ + return std::make_pair(Pistache::Http::Code::Internal_Server_Error, ex.what()); +} + {{#operation}} void {{classname}}::{{operationIdSnakeCase}}_handler(const Pistache::Rest::Request &{{#hasParams}}request{{/hasParams}}, Pistache::Http::ResponseWriter response) { + try { + {{#vendorExtensions.x-codegen-pistache-is-parsing-supported}} {{#hasPathParams}} // Getting the path params @@ -71,32 +91,40 @@ void {{classname}}::{{operationIdSnakeCase}}_handler(const Pistache::Rest::Reque {{#hasBodyParam}} {{#bodyParam}} {{^isPrimitiveType}} - nlohmann::json::parse(request.body()).get_to({{paramName}}); + nlohmann::json::parse(request.body()).get_to({{paramName}}); + {{paramName}}.validate(); {{/isPrimitiveType}} {{#isPrimitiveType}} - {{paramName}} = request.body(); + {{paramName}} = request.body(); {{/isPrimitiveType}} + } catch (std::exception &e) { + const std::pair errorInfo = this->handleParsingException(e); + response.send(errorInfo.first, errorInfo.second); + return; + } + + try { {{/bodyParam}} {{/hasBodyParam}} - this->{{operationIdSnakeCase}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}response); + this->{{operationIdSnakeCase}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}response); {{/vendorExtensions.x-codegen-pistache-is-parsing-supported}} {{^vendorExtensions.x-codegen-pistache-is-parsing-supported}} try { this->{{operationIdSnakeCase}}(request, response); {{/vendorExtensions.x-codegen-pistache-is-parsing-supported}} - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); - return; } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } {{/operation}} @@ -104,8 +132,6 @@ void {{classname}}::{{classnameSnakeLowerCase}}_default_handler(const Pistache:: response.send(Pistache::Http::Code::Not_Found, "The requested method does not exist"); } -{{#apiNamespaceDeclarations}} -} -{{/apiNamespaceDeclarations}} +} // namespace {{apiNamespace}} {{/operations}} diff --git a/modules/openapi-generator/src/main/resources/cpp-pistache-server/helpers-header.mustache b/modules/openapi-generator/src/main/resources/cpp-pistache-server/helpers-header.mustache index c39cca04fe1..d3489fad402 100644 --- a/modules/openapi-generator/src/main/resources/cpp-pistache-server/helpers-header.mustache +++ b/modules/openapi-generator/src/main/resources/cpp-pistache-server/helpers-header.mustache @@ -14,16 +14,80 @@ #include #include -{{#helpersNamespaceDeclarations}} -namespace {{this}} { -{{/helpersNamespaceDeclarations}} +namespace {{helpersNamespace}} +{ + + class ValidationException : public std::runtime_error + { + public: + explicit ValidationException(const std::string& what) + : std::runtime_error(what) + { } + ~ValidationException() override = default; + }; + + /// + /// Validate a string against the full-date definition of RFC 3339, section 5.6. + /// + bool validateRfc3339_date(const std::string& str); + + /// + /// Validate a string against the date-time definition of RFC 3339, section 5.6. + /// + bool validateRfc3339_date_time(const std::string& str); + + namespace sfinae_helpers + { + struct NoType {}; + template NoType operator==(const T1&, const T2&); + + template class EqualsOperatorAvailable + { + public: + enum + { + value = !std::is_same< decltype(std::declval() == std::declval()), NoType >::value + }; + }; + } // namespace sfinae_helpers + + + /// + /// Determine if the given vector only has unique elements. T must provide the == operator. + /// + template + bool hasOnlyUniqueItems(const std::vector& vec) + { + static_assert(sfinae_helpers::EqualsOperatorAvailable::value, + "hasOnlyUniqueItems cannot be called, passed template type does not provide == operator."); + if (vec.size() <= 1) + { + return true; + } + // Compare every element of vec to every other element of vec. + // This isn't an elegant way to do this, since it's O(n^2), + // but it's the best solution working only with the == operator. + // This could be greatly improved if our models provided a valid hash + // and/or the < operator + for (size_t i = 0; i < vec.size() - 1; i++) + { + for (size_t j = i + 1; j < vec.size(); j++) + { + if (vec[i] == vec[j]) + { + return false; + } + } + } + return true; + } std::string toStringValue(const std::string &value); - std::string toStringValue(const int32_t &value); - std::string toStringValue(const int64_t &value); - std::string toStringValue(const bool &value); - std::string toStringValue(const float &value); - std::string toStringValue(const double &value); + std::string toStringValue(const int32_t value); + std::string toStringValue(const int64_t value); + std::string toStringValue(const bool value); + std::string toStringValue(const float value); + std::string toStringValue(const double value); bool fromStringValue(const std::string &inStr, std::string &value); bool fromStringValue(const std::string &inStr, int32_t &value); @@ -57,8 +121,6 @@ namespace {{this}} { return fromStringValue(inStrings, value); } -{{#helpersNamespaceDeclarations}} -} -{{/helpersNamespaceDeclarations}} +} // namespace {{helpersNamespace}} -#endif // {{prefix}}Helpers_H_ \ No newline at end of file +#endif // {{prefix}}Helpers_H_ diff --git a/modules/openapi-generator/src/main/resources/cpp-pistache-server/helpers-source.mustache b/modules/openapi-generator/src/main/resources/cpp-pistache-server/helpers-source.mustache index 82b4e2d81ef..214086354d5 100644 --- a/modules/openapi-generator/src/main/resources/cpp-pistache-server/helpers-source.mustache +++ b/modules/openapi-generator/src/main/resources/cpp-pistache-server/helpers-source.mustache @@ -1,32 +1,74 @@ {{>licenseInfo}} #include "{{prefix}}Helpers.h" +#include -{{#helpersNamespaceDeclarations}} -namespace {{this}} { -{{/helpersNamespaceDeclarations}} +namespace {{helpersNamespace}} +{ +const std::regex regexRfc3339_date(R"(^(\d{4})\-(\d{2})\-(\d{2})$)"); +const std::regex regexRfc3339_date_time( + R"(^(\d{4})\-(\d{2})\-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(\.\d+)?([Zz]|([\+\-])(\d{2}):(\d{2}))$)" +); + + +namespace +{ + // Determine if given year is a leap year + // See RFC 3339, Appendix C https://tools.ietf.org/html/rfc3339#appendix-C + bool isLeapYear(const uint16_t year) { + return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); + } + + bool validateDateValues(const uint16_t year, const uint16_t month, const uint16_t day) { + return !( + (month == 0 || month > 12) + || (day == 0) + || (month == 2 && day > (28 + (isLeapYear(year) ? 1 : 0))) + || (month <= 7 && day > (30 + month % 2)) + || (month >= 8 && day > (31 - month % 2)) + ); + } + + bool validateTimeValues(const uint16_t hours, const uint16_t minutes, const uint16_t seconds) { + return (hours <= 23) && (minutes <= 59) && (seconds <= 60); + } +} + +bool validateRfc3339_date(const std::string& str) { + std::smatch match; + const bool found = std::regex_search(str, match, regexRfc3339_date); + return found && validateDateValues(std::stoi(match[1]), std::stoi(match[2]), std::stoi(match[3])); +} + +bool validateRfc3339_date_time(const std::string& str) { + std::smatch match; + const bool found = std::regex_search(str, match, regexRfc3339_date_time); + return found + && validateDateValues(std::stoi(match[1]), std::stoi(match[2]), std::stoi(match[3])) + && validateTimeValues(std::stoi(match[4]), std::stoi(match[5]), std::stoi(match[6])); +} std::string toStringValue(const std::string &value){ return std::string(value); } -std::string toStringValue(const int32_t &value){ +std::string toStringValue(const int32_t value){ return std::to_string(value); } -std::string toStringValue(const int64_t &value){ +std::string toStringValue(const int64_t value){ return std::to_string(value); } -std::string toStringValue(const bool &value){ - return value?std::string("true"):std::string("false"); +std::string toStringValue(const bool value){ + return value ? std::string("true") : std::string("false"); } -std::string toStringValue(const float &value){ +std::string toStringValue(const float value){ return std::to_string(value); } -std::string toStringValue(const double &value){ +std::string toStringValue(const double value){ return std::to_string(value); } @@ -56,9 +98,15 @@ bool fromStringValue(const std::string &inStr, int64_t &value){ } bool fromStringValue(const std::string &inStr, bool &value){ - bool result = true; - inStr == "true"?value = true: inStr == "false"?value = false: result = false; - return result; + if (inStr == "true") { + value = true; + return true; + } + if (inStr == "false") { + value = false; + return true; + } + return false; } bool fromStringValue(const std::string &inStr, float &value){ @@ -81,6 +129,4 @@ bool fromStringValue(const std::string &inStr, double &value){ return true; } -{{#helpersNamespaceDeclarations}} -} -{{/helpersNamespaceDeclarations}} \ No newline at end of file +} // namespace {{helpersNamespace}} diff --git a/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-header.mustache b/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-header.mustache index c16fdbb0946..cb6d8d98ee0 100644 --- a/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-header.mustache +++ b/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-header.mustache @@ -13,9 +13,8 @@ {{/imports}} #include -{{#modelNamespaceDeclarations}} -namespace {{this}} { -{{/modelNamespaceDeclarations}} +namespace {{modelNamespace}} +{ /// /// {{description}} @@ -24,7 +23,7 @@ class {{declspec}} {{classname}} { public: {{classname}}(); - virtual ~{{classname}}(); + virtual ~{{classname}}() = default; {{#isEnum}}{{#allowableValues}} enum class e{{classname}} { // To have a valid default value. @@ -35,7 +34,20 @@ public: {{{name}}}{{^-last}}, {{/-last}} {{/enumVars}} };{{/allowableValues}}{{/isEnum}} - void validate(); + + /// + /// Validate the current data in the model. Throws a ValidationException on failure. + /// + void validate() const; + + /// + /// Validate the current data in the model. Returns false on error and writes an error + /// message into the given stringstream. + /// + bool validate(std::stringstream& msg) const; + + bool operator==(const {{classname}}& rhs) const; + bool operator!=(const {{classname}}& rhs) const; ///////////////////////////////////////////// /// {{classname}} members @@ -44,7 +56,7 @@ public: /// /// {{description}} /// - {{{dataType}}}{{#isContainer}}&{{/isContainer}} {{getter}}(){{^isContainer}} const{{/isContainer}}; + {{{dataType}}} {{getter}}() const; void {{setter}}({{{dataType}}} const{{^isPrimitiveType}}&{{/isPrimitiveType}} value);{{^required}} bool {{nameInCamelCase}}IsSet() const; void unset{{name}}();{{/required}} @@ -65,11 +77,12 @@ protected: {{#isEnum}} {{classname}}::e{{classname}} m_value = {{classname}}::e{{classname}}::INVALID_VALUE_OPENAPI_GENERATED; {{/isEnum}} + + // Helper overload for validate. Used when one model stores another model and calls it's validate. + bool validate(std::stringstream& msg, const std::string& pathPrefix) const; }; -{{#modelNamespaceDeclarations}} -} -{{/modelNamespaceDeclarations}} +} // namespace {{modelNamespace}} #endif /* {{classname}}_H_ */ {{/model}} diff --git a/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-source.mustache b/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-source.mustache index d603dc2186e..8ef6abf0e4c 100644 --- a/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-source.mustache +++ b/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-source.mustache @@ -2,12 +2,12 @@ {{#models}}{{#model}} #include "{{classname}}.h" -{{#isEnum}}#include -#include {{/isEnum}} +#include "{{prefix}}Helpers.h" +{{#isEnum}}#include {{/isEnum}} +#include -{{#modelNamespaceDeclarations}} -namespace {{this}} { -{{/modelNamespaceDeclarations}} +namespace {{modelNamespace}} +{ {{classname}}::{{classname}}() { @@ -18,13 +18,69 @@ namespace {{this}} { {{/required}}{{/vars}} } -{{classname}}::~{{classname}}() +void {{classname}}::validate() const { + std::stringstream msg; + if (!validate(msg)) + { + throw {{helpersNamespace}}::ValidationException(msg.str()); + } } -void {{classname}}::validate() +bool {{classname}}::validate(std::stringstream& msg) const { - // TODO: implement validation + return validate(msg, ""); +} + +bool {{classname}}::validate(std::stringstream& msg, const std::string& pathPrefix) const +{ + bool success = true; + const std::string _pathPrefix = pathPrefix.empty() ? "{{classname}}" : pathPrefix; + + {{#isEnum}}{{! Special case for enum types }} + if (m_value == {{classname}}::e{{classname}}::INVALID_VALUE_OPENAPI_GENERATED) + { + success = false; + msg << _pathPrefix << ": has no value;"; + } + {{/isEnum}} + {{^isEnum}} + {{#vars}} + {{#isArray}} {{! Always generate validation body for array types }} + {{^required}}if ({{nameInCamelCase}}IsSet()){{/required}} + {{#required}}/* {{name}} */ {{/required}}{ + const {{{dataType}}}& value = m_{{name}}; + const std::string currentValuePath = _pathPrefix + ".{{nameInCamelCase}}"; + {{> model-validation-body }} + } + {{/isArray}}{{^isArray}}{{#hasValidation}} {{! Only generate validation if necessary }} + {{^required}}if ({{nameInCamelCase}}IsSet()){{/required}} + {{#required}}/* {{name}} */ {{/required}}{ + const {{{dataType}}}& value = m_{{name}}; + const std::string currentValuePath = _pathPrefix + ".{{nameInCamelCase}}"; + {{> model-validation-body }} + } + {{/hasValidation}}{{/isArray}} + {{/vars}} + {{/isEnum}} + + return success; +} + +bool {{classname}}::operator==(const {{classname}}& rhs) const +{ + return + {{#isEnum}}getValue() == rhs.getValue(){{/isEnum}} + {{^isEnum}}{{#vars}} + {{#required}}({{getter}}() == rhs.{{getter}}()){{/required}} + {{^required}}((!{{nameInCamelCase}}IsSet() && !rhs.{{nameInCamelCase}}IsSet()) || ({{nameInCamelCase}}IsSet() && rhs.{{nameInCamelCase}}IsSet() && {{getter}}() == rhs.{{getter}}())){{/required}}{{^-last}} &&{{/-last}} + {{/vars}}{{/isEnum}} + ; +} + +bool {{classname}}::operator!=(const {{classname}}& rhs) const +{ + return !(*this == rhs); } void to_json(nlohmann::json& j, const {{classname}}& o) @@ -74,7 +130,7 @@ void from_json(const nlohmann::json& j, {{classname}}& o) {{/enumVars}}{{/allowableValues}}{{/isEnum}} } -{{#vars}}{{{dataType}}}{{#isContainer}}&{{/isContainer}} {{classname}}::{{getter}}(){{^isContainer}} const{{/isContainer}} +{{#vars}}{{{dataType}}} {{classname}}::{{getter}}() const { return m_{{name}}; } @@ -102,9 +158,7 @@ void {{classname}}::setValue({{classname}}::e{{classname}} value) m_value = value; }{{/isEnum}} -{{#modelNamespaceDeclarations}} -} -{{/modelNamespaceDeclarations}} +} // namespace {{modelNamespace}} {{/model}} {{/models}} diff --git a/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-struct-header.mustache b/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-struct-header.mustache index bcc64257e40..be0ea874da8 100644 --- a/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-struct-header.mustache +++ b/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-struct-header.mustache @@ -14,9 +14,8 @@ #include {{#hasOptional}}#include {{/hasOptional}} -{{#modelNamespaceDeclarations}} -namespace {{this}} { -{{/modelNamespaceDeclarations}} +namespace {{modelNamespace}} +{ struct {{classname}} { @@ -34,9 +33,9 @@ struct {{classname}} void to_json(nlohmann::json& j, const {{classname}}& o); void from_json(const nlohmann::json& j, {{classname}}& o); -{{#modelNamespaceDeclarations}} -} // {{this}} -{{/modelNamespaceDeclarations}} + +} // namespace {{modelNamespace}} + #endif /* {{classname}}_H_ */ {{/model}} diff --git a/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-struct-source.mustache b/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-struct-source.mustache index 52d3526e867..987926b7c4e 100644 --- a/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-struct-source.mustache +++ b/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-struct-source.mustache @@ -3,9 +3,8 @@ #include "{{classname}}.h" -{{#modelNamespaceDeclarations}} -namespace {{this}} { -{{/modelNamespaceDeclarations}} +namespace {{modelNamespace}} +{ nlohmann::json {{classname}}::to_json() const { @@ -51,9 +50,7 @@ void from_json(const nlohmann::json& j, {{classname}}& o) {{/vars}} } -{{#modelNamespaceDeclarations}} -} // {{this}} -{{/modelNamespaceDeclarations}} +} // namespace {{modelNamespace}} {{/model}} {{/models}} diff --git a/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-validation-body.mustache b/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-validation-body.mustache new file mode 100644 index 00000000000..ef92f2c3fbd --- /dev/null +++ b/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-validation-body.mustache @@ -0,0 +1,124 @@ +{{! +This template generates the validation logic for a model. +This template file calls itself recursively for arrays. +}} +{{! Check for eunm properties that are their own schema }} +{{! These are in their own class with a validate function, the way to check for this is quite a hack +since isEnum, isString and isModel are all false. }} + {{^isString}}{{#allowableValues.enumVars.0.value}} + success = value.validate(msg, currentValuePath) && success; + {{/allowableValues.enumVars.0.value}}{{/isString}} + {{#isModel}}success = value.validate(msg, currentValuePath + ".{{nameInCamelCase}}") && success;{{/isModel}} +{{! Date validation }} + {{#isDate}} + if (!{{helpersNamespace}}::validateRfc3339_date(value)) + { + success = false; + msg << currentValuePath << ": must be a valid RFC 3339 date-full string;"; + } + {{/isDate}} +{{! Date-Time validation }} + {{#isDateTime}} + if (!{{helpersNamespace}}::validateRfc3339_date_time(value)) + { + success = false; + msg << currentValuePath << ": must be a valid RFC 3339 date-time string;"; + } + {{/isDateTime}} +{{! string validation }} + {{#isString}} + {{#minLength}} + if (value.length() < {{minLength}}) + { + success = false; + msg << currentValuePath << ": must be at least {{minLength}} characters long;"; + } + {{/minLength}} + {{#maxLength}} + if (value.length() > {{maxLength}}) + { + success = false; + msg << currentValuePath << ": must be at most {{maxLength}} characters long;"; + } + {{/maxLength}} +{{! +TODO validate regex of string using pattern variable. This has two challenges + - Is compatibility with the given regex pattern guaranteed? + - Creating the std::regex on every validation would be rather slow. Ideally one would + initialize them for the class once as a static const and use them. +}} +{{! string encoded enum validation }} + {{#isEnum}} + {{#allowableValues}} + if ({{#enumVars}} + value != "{{value}}"{{^-last}} &&{{/-last}}{{/enumVars}} + ) { + success = false; + msg << currentValuePath << ": has invalid value \"" << value << "\";"; + } + {{/allowableValues}} + {{/isEnum}} + {{/isString}} +{{! numeric validation }} + {{#isNumeric}} + {{#minimum}} + if (value <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{#isFloat}}static_cast({{/isFloat}}{{minimum}}{{#isFloat}}){{/isFloat}}{{#isLong}}ll{{/isLong}}) + { + success = false; + msg << currentValuePath << ": must be greater than{{^exclusiveMinimum}} or equal to{{/exclusiveMinimum}} {{minimum}};"; + } + {{/minimum}} + {{#maximum}} + if (value >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{#isFloat}}static_cast({{/isFloat}}{{maximum}}{{#isFloat}}){{/isFloat}}{{#isLong}}ll{{/isLong}}) + { + success = false; + msg << currentValuePath << ": must be less than{{^exclusiveMaximum}} or equal to{{/exclusiveMaximum}} {{maximum}};"; + } + {{/maximum}} + {{#multipleOf}} + {{#isInteger}}if (value % {{multipleOf}}{{#isLong}}ll{{/isLong}} != 0){{/isInteger}} + {{#isFloat}}if (std::fmod(value, static_cast({{multipleOf}})) != 0){{/isFloat}} + {{#isDouble}}if (std::fmod(value, {{multipleOf}}) != 0){{/isDouble}} + { + success = false; + msg << currentValuePath << ": must be a multiple of {{multipleOf}};"; + } + {{/multipleOf}} + {{/isNumeric}} +{{! Array validation }} + {{#isArray}} + {{#minItems}} + if (value.size() < {{minItems}}) + { + success = false; + msg << currentValuePath << ": must have at least {{minItems}} elements;"; + } + {{/minItems}} + {{#maxItems}} + if (value.size() > {{maxItems}}) + { + success = false; + msg << currentValuePath << ": must have at most {{maxItems}} elements;"; + } + {{/maxItems}} + {{#uniqueItems}} + if (!{{helpersNamespace}}::hasOnlyUniqueItems(value)) + { + success = false; + msg << currentValuePath << ": may not contain the same item more than once;"; + } + {{/uniqueItems}} + { // Recursive validation of array elements + const std::string oldValuePath = currentValuePath; + int i = 0; + {{! the element var has the same name as the vector, so that the recursive template works - what a wonderful hack }} + for (const {{{items.dataType}}}& value : value) + { {{! and I do a similar hack with currentValuePath... }} + const std::string currentValuePath = oldValuePath + "[" + std::to_string(i) + "]"; + {{#items}} + {{> model-validation-body }} {{! Recursively apply template to array - this is where things will probbaly go wrong }} + {{/items}} + i++; + } + } + {{/isArray}} diff --git a/samples/server/petstore/cpp-pistache/.openapi-generator/VERSION b/samples/server/petstore/cpp-pistache/.openapi-generator/VERSION index c30f0ec2be7..d509cc92aa8 100644 --- a/samples/server/petstore/cpp-pistache/.openapi-generator/VERSION +++ b/samples/server/petstore/cpp-pistache/.openapi-generator/VERSION @@ -1 +1 @@ -5.1.0-SNAPSHOT \ No newline at end of file +5.1.1-SNAPSHOT \ No newline at end of file diff --git a/samples/server/petstore/cpp-pistache/api/PetApi.cpp b/samples/server/petstore/cpp-pistache/api/PetApi.cpp index 80e15bd6862..7b3641599ee 100644 --- a/samples/server/petstore/cpp-pistache/api/PetApi.cpp +++ b/samples/server/petstore/cpp-pistache/api/PetApi.cpp @@ -13,16 +13,17 @@ #include "PetApi.h" #include "Helpers.h" -namespace org { -namespace openapitools { -namespace server { -namespace api { +namespace org::openapitools::server::api +{ using namespace org::openapitools::server::helpers; using namespace org::openapitools::server::model; -PetApi::PetApi(std::shared_ptr rtr) { - router = rtr; +const std::string PetApi::base = "/v2"; + +PetApi::PetApi(const std::shared_ptr& rtr) + : router(rtr) +{ } void PetApi::init() { @@ -45,30 +46,58 @@ void PetApi::setupRoutes() { router->addCustomHandler(Routes::bind(&PetApi::pet_api_default_handler, this)); } +std::pair PetApi::handleParsingException(const std::exception& ex) const noexcept +{ + try { + throw ex; + } catch (nlohmann::detail::exception &e) { + return std::make_pair(Pistache::Http::Code::Bad_Request, e.what()); + } catch (org::openapitools::server::helpers::ValidationException &e) { + return std::make_pair(Pistache::Http::Code::Bad_Request, e.what()); + } +} + +std::pair PetApi::handleOperationException(const std::exception& ex) const noexcept +{ + return std::make_pair(Pistache::Http::Code::Internal_Server_Error, ex.what()); +} + void PetApi::add_pet_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { + try { + // Getting the body param Pet body; try { - nlohmann::json::parse(request.body()).get_to(body); - this->add_pet(body, response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); + nlohmann::json::parse(request.body()).get_to(body); + body.validate(); + } catch (std::exception &e) { + const std::pair errorInfo = this->handleParsingException(e); + response.send(errorInfo.first, errorInfo.second); return; + } + + try { + this->add_pet(body, response); } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void PetApi::delete_pet_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { + try { + // Getting the path params auto petId = request.param(":petId").as(); @@ -76,22 +105,24 @@ void PetApi::delete_pet_handler(const Pistache::Rest::Request &request, Pistache auto apiKey = request.headers().tryGetRaw("api_key"); try { - this->delete_pet(petId, apiKey, response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); - return; + this->delete_pet(petId, apiKey, response); } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void PetApi::find_pets_by_status_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { + try { + // Getting the query params auto statusQuery = request.query().get("status"); @@ -104,22 +135,24 @@ void PetApi::find_pets_by_status_handler(const Pistache::Rest::Request &request, } try { - this->find_pets_by_status(status, response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); - return; + this->find_pets_by_status(status, response); } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void PetApi::find_pets_by_tags_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { + try { + // Getting the query params auto tagsQuery = request.query().get("tags"); @@ -132,105 +165,118 @@ void PetApi::find_pets_by_tags_handler(const Pistache::Rest::Request &request, P } try { - this->find_pets_by_tags(tags, response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); - return; + this->find_pets_by_tags(tags, response); } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void PetApi::get_pet_by_id_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { + try { + // Getting the path params auto petId = request.param(":petId").as(); try { - this->get_pet_by_id(petId, response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); - return; + this->get_pet_by_id(petId, response); } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void PetApi::update_pet_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { + try { + // Getting the body param Pet body; try { - nlohmann::json::parse(request.body()).get_to(body); - this->update_pet(body, response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); + nlohmann::json::parse(request.body()).get_to(body); + body.validate(); + } catch (std::exception &e) { + const std::pair errorInfo = this->handleParsingException(e); + response.send(errorInfo.first, errorInfo.second); return; + } + + try { + this->update_pet(body, response); } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void PetApi::update_pet_with_form_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { + try { + try { this->update_pet_with_form(request, response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); - return; } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void PetApi::upload_file_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { + try { + try { this->upload_file(request, response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); - return; } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void PetApi::pet_api_default_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) { response.send(Pistache::Http::Code::Not_Found, "The requested method does not exist"); } -} -} -} -} +} // namespace org::openapitools::server::api diff --git a/samples/server/petstore/cpp-pistache/api/PetApi.h b/samples/server/petstore/cpp-pistache/api/PetApi.h index d991d13abae..f689a0675e1 100644 --- a/samples/server/petstore/cpp-pistache/api/PetApi.h +++ b/samples/server/petstore/cpp-pistache/api/PetApi.h @@ -24,25 +24,22 @@ #include #include +#include #include "ApiResponse.h" #include "Pet.h" #include -namespace org { -namespace openapitools { -namespace server { -namespace api { - -using namespace org::openapitools::server::model; +namespace org::openapitools::server::api +{ class PetApi { public: - PetApi(std::shared_ptr); - virtual ~PetApi() {} + explicit PetApi(const std::shared_ptr& rtr); + virtual ~PetApi() = default; void init(); - const std::string base = "/v2"; + static const std::string base; private: void setupRoutes(); @@ -57,7 +54,19 @@ private: void upload_file_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); void pet_api_default_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); - std::shared_ptr router; + const std::shared_ptr router; + + /// + /// Helper function to handle unexpected Exceptions during Parameter parsing and validation. + /// May be overriden to return custom error formats. + /// + virtual std::pair handleParsingException(const std::exception& ex) const noexcept; + + /// + /// Helper function to handle unexpected Exceptions during processing of the request in handler functions. + /// May be overriden to return custom error formats. + /// + virtual std::pair handleOperationException(const std::exception& ex) const noexcept; /// /// Add a new pet to the store @@ -66,8 +75,7 @@ private: /// /// /// Pet object that needs to be added to the store - virtual void add_pet(const Pet &body, Pistache::Http::ResponseWriter &response) = 0; - + virtual void add_pet(const org::openapitools::server::model::Pet &body, Pistache::Http::ResponseWriter &response) = 0; /// /// Deletes a pet /// @@ -77,7 +85,6 @@ private: /// Pet id to delete /// (optional, default to "") virtual void delete_pet(const int64_t &petId, const Pistache::Optional &apiKey, Pistache::Http::ResponseWriter &response) = 0; - /// /// Finds Pets by status /// @@ -86,7 +93,6 @@ private: /// /// Status values that need to be considered for filter virtual void find_pets_by_status(const Pistache::Optional> &status, Pistache::Http::ResponseWriter &response) = 0; - /// /// Finds Pets by tags /// @@ -95,7 +101,6 @@ private: /// /// Tags to filter by virtual void find_pets_by_tags(const Pistache::Optional> &tags, Pistache::Http::ResponseWriter &response) = 0; - /// /// Find pet by ID /// @@ -104,7 +109,6 @@ private: /// /// ID of pet to return virtual void get_pet_by_id(const int64_t &petId, Pistache::Http::ResponseWriter &response) = 0; - /// /// Update an existing pet /// @@ -112,8 +116,7 @@ private: /// /// /// Pet object that needs to be added to the store - virtual void update_pet(const Pet &body, Pistache::Http::ResponseWriter &response) = 0; - + virtual void update_pet(const org::openapitools::server::model::Pet &body, Pistache::Http::ResponseWriter &response) = 0; /// /// Updates a pet in the store with form data /// @@ -121,7 +124,6 @@ private: /// /// virtual void update_pet_with_form(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter &response) = 0; - /// /// uploads an image /// @@ -132,10 +134,7 @@ private: }; -} -} -} -} +} // namespace org::openapitools::server::api #endif /* PetApi_H_ */ diff --git a/samples/server/petstore/cpp-pistache/api/StoreApi.cpp b/samples/server/petstore/cpp-pistache/api/StoreApi.cpp index e3f1038f80a..ea0c0e1793b 100644 --- a/samples/server/petstore/cpp-pistache/api/StoreApi.cpp +++ b/samples/server/petstore/cpp-pistache/api/StoreApi.cpp @@ -13,16 +13,17 @@ #include "StoreApi.h" #include "Helpers.h" -namespace org { -namespace openapitools { -namespace server { -namespace api { +namespace org::openapitools::server::api +{ using namespace org::openapitools::server::helpers; using namespace org::openapitools::server::model; -StoreApi::StoreApi(std::shared_ptr rtr) { - router = rtr; +const std::string StoreApi::base = "/v2"; + +StoreApi::StoreApi(const std::shared_ptr& rtr) + : router(rtr) +{ } void StoreApi::init() { @@ -41,94 +42,123 @@ void StoreApi::setupRoutes() { router->addCustomHandler(Routes::bind(&StoreApi::store_api_default_handler, this)); } +std::pair StoreApi::handleParsingException(const std::exception& ex) const noexcept +{ + try { + throw ex; + } catch (nlohmann::detail::exception &e) { + return std::make_pair(Pistache::Http::Code::Bad_Request, e.what()); + } catch (org::openapitools::server::helpers::ValidationException &e) { + return std::make_pair(Pistache::Http::Code::Bad_Request, e.what()); + } +} + +std::pair StoreApi::handleOperationException(const std::exception& ex) const noexcept +{ + return std::make_pair(Pistache::Http::Code::Internal_Server_Error, ex.what()); +} + void StoreApi::delete_order_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { + try { + // Getting the path params auto orderId = request.param(":orderId").as(); try { - this->delete_order(orderId, response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); - return; + this->delete_order(orderId, response); } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void StoreApi::get_inventory_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) { + try { + try { - this->get_inventory(response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); - return; + this->get_inventory(response); } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void StoreApi::get_order_by_id_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { + try { + // Getting the path params auto orderId = request.param(":orderId").as(); try { - this->get_order_by_id(orderId, response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); - return; + this->get_order_by_id(orderId, response); } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void StoreApi::place_order_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { + try { + // Getting the body param Order body; try { - nlohmann::json::parse(request.body()).get_to(body); - this->place_order(body, response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); + nlohmann::json::parse(request.body()).get_to(body); + body.validate(); + } catch (std::exception &e) { + const std::pair errorInfo = this->handleParsingException(e); + response.send(errorInfo.first, errorInfo.second); return; + } + + try { + this->place_order(body, response); } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void StoreApi::store_api_default_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) { response.send(Pistache::Http::Code::Not_Found, "The requested method does not exist"); } -} -} -} -} +} // namespace org::openapitools::server::api diff --git a/samples/server/petstore/cpp-pistache/api/StoreApi.h b/samples/server/petstore/cpp-pistache/api/StoreApi.h index f69875c4678..6eb22438747 100644 --- a/samples/server/petstore/cpp-pistache/api/StoreApi.h +++ b/samples/server/petstore/cpp-pistache/api/StoreApi.h @@ -24,25 +24,22 @@ #include #include +#include #include "Order.h" #include #include -namespace org { -namespace openapitools { -namespace server { -namespace api { - -using namespace org::openapitools::server::model; +namespace org::openapitools::server::api +{ class StoreApi { public: - StoreApi(std::shared_ptr); - virtual ~StoreApi() {} + explicit StoreApi(const std::shared_ptr& rtr); + virtual ~StoreApi() = default; void init(); - const std::string base = "/v2"; + static const std::string base; private: void setupRoutes(); @@ -53,7 +50,19 @@ private: void place_order_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); void store_api_default_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); - std::shared_ptr router; + const std::shared_ptr router; + + /// + /// Helper function to handle unexpected Exceptions during Parameter parsing and validation. + /// May be overriden to return custom error formats. + /// + virtual std::pair handleParsingException(const std::exception& ex) const noexcept; + + /// + /// Helper function to handle unexpected Exceptions during processing of the request in handler functions. + /// May be overriden to return custom error formats. + /// + virtual std::pair handleOperationException(const std::exception& ex) const noexcept; /// /// Delete purchase order by ID @@ -63,7 +72,6 @@ private: /// /// ID of the order that needs to be deleted virtual void delete_order(const std::string &orderId, Pistache::Http::ResponseWriter &response) = 0; - /// /// Returns pet inventories by status /// @@ -71,7 +79,6 @@ private: /// Returns a map of status codes to quantities /// virtual void get_inventory(Pistache::Http::ResponseWriter &response) = 0; - /// /// Find purchase order by ID /// @@ -80,7 +87,6 @@ private: /// /// ID of pet that needs to be fetched virtual void get_order_by_id(const int64_t &orderId, Pistache::Http::ResponseWriter &response) = 0; - /// /// Place an order for a pet /// @@ -88,14 +94,11 @@ private: /// /// /// order placed for purchasing the pet - virtual void place_order(const Order &body, Pistache::Http::ResponseWriter &response) = 0; + virtual void place_order(const org::openapitools::server::model::Order &body, Pistache::Http::ResponseWriter &response) = 0; }; -} -} -} -} +} // namespace org::openapitools::server::api #endif /* StoreApi_H_ */ diff --git a/samples/server/petstore/cpp-pistache/api/UserApi.cpp b/samples/server/petstore/cpp-pistache/api/UserApi.cpp index 93d275611d7..94903581dc4 100644 --- a/samples/server/petstore/cpp-pistache/api/UserApi.cpp +++ b/samples/server/petstore/cpp-pistache/api/UserApi.cpp @@ -13,16 +13,17 @@ #include "UserApi.h" #include "Helpers.h" -namespace org { -namespace openapitools { -namespace server { -namespace api { +namespace org::openapitools::server::api +{ using namespace org::openapitools::server::helpers; using namespace org::openapitools::server::model; -UserApi::UserApi(std::shared_ptr rtr) { - router = rtr; +const std::string UserApi::base = "/v2"; + +UserApi::UserApi(const std::shared_ptr& rtr) + : router(rtr) +{ } void UserApi::init() { @@ -45,114 +46,166 @@ void UserApi::setupRoutes() { router->addCustomHandler(Routes::bind(&UserApi::user_api_default_handler, this)); } +std::pair UserApi::handleParsingException(const std::exception& ex) const noexcept +{ + try { + throw ex; + } catch (nlohmann::detail::exception &e) { + return std::make_pair(Pistache::Http::Code::Bad_Request, e.what()); + } catch (org::openapitools::server::helpers::ValidationException &e) { + return std::make_pair(Pistache::Http::Code::Bad_Request, e.what()); + } +} + +std::pair UserApi::handleOperationException(const std::exception& ex) const noexcept +{ + return std::make_pair(Pistache::Http::Code::Internal_Server_Error, ex.what()); +} + void UserApi::create_user_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { + try { + // Getting the body param User body; try { - nlohmann::json::parse(request.body()).get_to(body); - this->create_user(body, response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); + nlohmann::json::parse(request.body()).get_to(body); + body.validate(); + } catch (std::exception &e) { + const std::pair errorInfo = this->handleParsingException(e); + response.send(errorInfo.first, errorInfo.second); return; + } + + try { + this->create_user(body, response); } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void UserApi::create_users_with_array_input_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { + try { + // Getting the body param std::vector body; try { - nlohmann::json::parse(request.body()).get_to(body); - this->create_users_with_array_input(body, response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); + nlohmann::json::parse(request.body()).get_to(body); + body.validate(); + } catch (std::exception &e) { + const std::pair errorInfo = this->handleParsingException(e); + response.send(errorInfo.first, errorInfo.second); return; + } + + try { + this->create_users_with_array_input(body, response); } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void UserApi::create_users_with_list_input_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { + try { + // Getting the body param std::vector body; try { - nlohmann::json::parse(request.body()).get_to(body); - this->create_users_with_list_input(body, response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); + nlohmann::json::parse(request.body()).get_to(body); + body.validate(); + } catch (std::exception &e) { + const std::pair errorInfo = this->handleParsingException(e); + response.send(errorInfo.first, errorInfo.second); return; + } + + try { + this->create_users_with_list_input(body, response); } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void UserApi::delete_user_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { + try { + // Getting the path params auto username = request.param(":username").as(); try { - this->delete_user(username, response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); - return; + this->delete_user(username, response); } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void UserApi::get_user_by_name_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { + try { + // Getting the path params auto username = request.param(":username").as(); try { - this->get_user_by_name(username, response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); - return; + this->get_user_by_name(username, response); } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void UserApi::login_user_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { + try { + // Getting the query params auto usernameQuery = request.query().get("username"); @@ -173,40 +226,44 @@ void UserApi::login_user_handler(const Pistache::Rest::Request &request, Pistach } try { - this->login_user(username, password, response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); - return; + this->login_user(username, password, response); } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void UserApi::logout_user_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) { + try { + try { - this->logout_user(response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); - return; + this->logout_user(response); } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void UserApi::update_user_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { + try { + // Getting the path params auto username = request.param(":username").as(); @@ -215,29 +272,34 @@ void UserApi::update_user_handler(const Pistache::Rest::Request &request, Pistac User body; try { - nlohmann::json::parse(request.body()).get_to(body); - this->update_user(username, body, response); - } catch (nlohmann::detail::exception &e) { - //send a 400 error - response.send(Pistache::Http::Code::Bad_Request, e.what()); + nlohmann::json::parse(request.body()).get_to(body); + body.validate(); + } catch (std::exception &e) { + const std::pair errorInfo = this->handleParsingException(e); + response.send(errorInfo.first, errorInfo.second); return; + } + + try { + this->update_user(username, body, response); } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; } catch (std::exception &e) { - //send a 500 error - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); return; } + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void UserApi::user_api_default_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) { response.send(Pistache::Http::Code::Not_Found, "The requested method does not exist"); } -} -} -} -} +} // namespace org::openapitools::server::api diff --git a/samples/server/petstore/cpp-pistache/api/UserApi.h b/samples/server/petstore/cpp-pistache/api/UserApi.h index 6c589979dbc..003b6db4df4 100644 --- a/samples/server/petstore/cpp-pistache/api/UserApi.h +++ b/samples/server/petstore/cpp-pistache/api/UserApi.h @@ -24,25 +24,22 @@ #include #include +#include #include "User.h" #include #include -namespace org { -namespace openapitools { -namespace server { -namespace api { - -using namespace org::openapitools::server::model; +namespace org::openapitools::server::api +{ class UserApi { public: - UserApi(std::shared_ptr); - virtual ~UserApi() {} + explicit UserApi(const std::shared_ptr& rtr); + virtual ~UserApi() = default; void init(); - const std::string base = "/v2"; + static const std::string base; private: void setupRoutes(); @@ -57,7 +54,19 @@ private: void update_user_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); void user_api_default_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); - std::shared_ptr router; + const std::shared_ptr router; + + /// + /// Helper function to handle unexpected Exceptions during Parameter parsing and validation. + /// May be overriden to return custom error formats. + /// + virtual std::pair handleParsingException(const std::exception& ex) const noexcept; + + /// + /// Helper function to handle unexpected Exceptions during processing of the request in handler functions. + /// May be overriden to return custom error formats. + /// + virtual std::pair handleOperationException(const std::exception& ex) const noexcept; /// /// Create user @@ -66,8 +75,7 @@ private: /// This can only be done by the logged in user. /// /// Created user object - virtual void create_user(const User &body, Pistache::Http::ResponseWriter &response) = 0; - + virtual void create_user(const org::openapitools::server::model::User &body, Pistache::Http::ResponseWriter &response) = 0; /// /// Creates list of users with given input array /// @@ -76,7 +84,6 @@ private: /// /// List of user object virtual void create_users_with_array_input(const std::vector &body, Pistache::Http::ResponseWriter &response) = 0; - /// /// Creates list of users with given input array /// @@ -85,7 +92,6 @@ private: /// /// List of user object virtual void create_users_with_list_input(const std::vector &body, Pistache::Http::ResponseWriter &response) = 0; - /// /// Delete user /// @@ -94,7 +100,6 @@ private: /// /// The name that needs to be deleted virtual void delete_user(const std::string &username, Pistache::Http::ResponseWriter &response) = 0; - /// /// Get user by user name /// @@ -103,7 +108,6 @@ private: /// /// The name that needs to be fetched. Use user1 for testing. virtual void get_user_by_name(const std::string &username, Pistache::Http::ResponseWriter &response) = 0; - /// /// Logs user into the system /// @@ -113,7 +117,6 @@ private: /// The user name for login /// The password for login in clear text virtual void login_user(const Pistache::Optional &username, const Pistache::Optional &password, Pistache::Http::ResponseWriter &response) = 0; - /// /// Logs out current logged in user session /// @@ -121,7 +124,6 @@ private: /// /// virtual void logout_user(Pistache::Http::ResponseWriter &response) = 0; - /// /// Updated user /// @@ -130,14 +132,11 @@ private: /// /// name that need to be deleted /// Updated user object - virtual void update_user(const std::string &username, const User &body, Pistache::Http::ResponseWriter &response) = 0; + virtual void update_user(const std::string &username, const org::openapitools::server::model::User &body, Pistache::Http::ResponseWriter &response) = 0; }; -} -} -} -} +} // namespace org::openapitools::server::api #endif /* UserApi_H_ */ diff --git a/samples/server/petstore/cpp-pistache/impl/PetApiImpl.cpp b/samples/server/petstore/cpp-pistache/impl/PetApiImpl.cpp index 778117a18bb..94b49b151e4 100644 --- a/samples/server/petstore/cpp-pistache/impl/PetApiImpl.cpp +++ b/samples/server/petstore/cpp-pistache/impl/PetApiImpl.cpp @@ -19,9 +19,10 @@ namespace api { using namespace org::openapitools::server::model; -PetApiImpl::PetApiImpl(std::shared_ptr rtr) +PetApiImpl::PetApiImpl(const std::shared_ptr& rtr) : PetApi(rtr) - { } +{ +} void PetApiImpl::add_pet(const Pet &body, Pistache::Http::ResponseWriter &response) { response.send(Pistache::Http::Code::Ok, "Do some magic\n"); diff --git a/samples/server/petstore/cpp-pistache/impl/PetApiImpl.h b/samples/server/petstore/cpp-pistache/impl/PetApiImpl.h index 9e9f3d145ba..82c10b0a410 100644 --- a/samples/server/petstore/cpp-pistache/impl/PetApiImpl.h +++ b/samples/server/petstore/cpp-pistache/impl/PetApiImpl.h @@ -33,17 +33,15 @@ #include "Pet.h" #include -namespace org { -namespace openapitools { -namespace server { -namespace api { +namespace org::openapitools::server::api +{ using namespace org::openapitools::server::model; class PetApiImpl : public org::openapitools::server::api::PetApi { public: - PetApiImpl(std::shared_ptr); - ~PetApiImpl() {} + explicit PetApiImpl(const std::shared_ptr& rtr); + ~PetApiImpl() override = default; void add_pet(const Pet &body, Pistache::Http::ResponseWriter &response); void delete_pet(const int64_t &petId, const Pistache::Optional &apiKey, Pistache::Http::ResponseWriter &response); @@ -56,11 +54,8 @@ public: }; -} -} -} -} +} // namespace org::openapitools::server::api -#endif \ No newline at end of file +#endif diff --git a/samples/server/petstore/cpp-pistache/impl/StoreApiImpl.cpp b/samples/server/petstore/cpp-pistache/impl/StoreApiImpl.cpp index ef164eb0543..a746316b803 100644 --- a/samples/server/petstore/cpp-pistache/impl/StoreApiImpl.cpp +++ b/samples/server/petstore/cpp-pistache/impl/StoreApiImpl.cpp @@ -19,9 +19,10 @@ namespace api { using namespace org::openapitools::server::model; -StoreApiImpl::StoreApiImpl(std::shared_ptr rtr) +StoreApiImpl::StoreApiImpl(const std::shared_ptr& rtr) : StoreApi(rtr) - { } +{ +} void StoreApiImpl::delete_order(const std::string &orderId, Pistache::Http::ResponseWriter &response) { response.send(Pistache::Http::Code::Ok, "Do some magic\n"); diff --git a/samples/server/petstore/cpp-pistache/impl/StoreApiImpl.h b/samples/server/petstore/cpp-pistache/impl/StoreApiImpl.h index c9937246318..c5dda64490f 100644 --- a/samples/server/petstore/cpp-pistache/impl/StoreApiImpl.h +++ b/samples/server/petstore/cpp-pistache/impl/StoreApiImpl.h @@ -33,17 +33,15 @@ #include #include -namespace org { -namespace openapitools { -namespace server { -namespace api { +namespace org::openapitools::server::api +{ using namespace org::openapitools::server::model; class StoreApiImpl : public org::openapitools::server::api::StoreApi { public: - StoreApiImpl(std::shared_ptr); - ~StoreApiImpl() {} + explicit StoreApiImpl(const std::shared_ptr& rtr); + ~StoreApiImpl() override = default; void delete_order(const std::string &orderId, Pistache::Http::ResponseWriter &response); void get_inventory(Pistache::Http::ResponseWriter &response); @@ -52,11 +50,8 @@ public: }; -} -} -} -} +} // namespace org::openapitools::server::api -#endif \ No newline at end of file +#endif diff --git a/samples/server/petstore/cpp-pistache/impl/UserApiImpl.cpp b/samples/server/petstore/cpp-pistache/impl/UserApiImpl.cpp index 7163a560bd7..b477f053c5c 100644 --- a/samples/server/petstore/cpp-pistache/impl/UserApiImpl.cpp +++ b/samples/server/petstore/cpp-pistache/impl/UserApiImpl.cpp @@ -19,9 +19,10 @@ namespace api { using namespace org::openapitools::server::model; -UserApiImpl::UserApiImpl(std::shared_ptr rtr) +UserApiImpl::UserApiImpl(const std::shared_ptr& rtr) : UserApi(rtr) - { } +{ +} void UserApiImpl::create_user(const User &body, Pistache::Http::ResponseWriter &response) { response.send(Pistache::Http::Code::Ok, "Do some magic\n"); diff --git a/samples/server/petstore/cpp-pistache/impl/UserApiImpl.h b/samples/server/petstore/cpp-pistache/impl/UserApiImpl.h index 8f22c499889..856a30db5bb 100644 --- a/samples/server/petstore/cpp-pistache/impl/UserApiImpl.h +++ b/samples/server/petstore/cpp-pistache/impl/UserApiImpl.h @@ -33,17 +33,15 @@ #include #include -namespace org { -namespace openapitools { -namespace server { -namespace api { +namespace org::openapitools::server::api +{ using namespace org::openapitools::server::model; class UserApiImpl : public org::openapitools::server::api::UserApi { public: - UserApiImpl(std::shared_ptr); - ~UserApiImpl() {} + explicit UserApiImpl(const std::shared_ptr& rtr); + ~UserApiImpl() override = default; void create_user(const User &body, Pistache::Http::ResponseWriter &response); void create_users_with_array_input(const std::vector &body, Pistache::Http::ResponseWriter &response); @@ -56,11 +54,8 @@ public: }; -} -} -} -} +} // namespace org::openapitools::server::api -#endif \ No newline at end of file +#endif diff --git a/samples/server/petstore/cpp-pistache/model/ApiResponse.cpp b/samples/server/petstore/cpp-pistache/model/ApiResponse.cpp index 4b9ca741920..049f6c19d6d 100644 --- a/samples/server/petstore/cpp-pistache/model/ApiResponse.cpp +++ b/samples/server/petstore/cpp-pistache/model/ApiResponse.cpp @@ -12,11 +12,12 @@ #include "ApiResponse.h" +#include "Helpers.h" -namespace org { -namespace openapitools { -namespace server { -namespace model { +#include + +namespace org::openapitools::server::model +{ ApiResponse::ApiResponse() { @@ -29,13 +30,52 @@ ApiResponse::ApiResponse() } -ApiResponse::~ApiResponse() +void ApiResponse::validate() const { + std::stringstream msg; + if (!validate(msg)) + { + throw org::openapitools::server::helpers::ValidationException(msg.str()); + } } -void ApiResponse::validate() +bool ApiResponse::validate(std::stringstream& msg) const { - // TODO: implement validation + return validate(msg, ""); +} + +bool ApiResponse::validate(std::stringstream& msg, const std::string& pathPrefix) const +{ + bool success = true; + const std::string _pathPrefix = pathPrefix.empty() ? "ApiResponse" : pathPrefix; + + + + + + return success; +} + +bool ApiResponse::operator==(const ApiResponse& rhs) const +{ + return + + + + ((!codeIsSet() && !rhs.codeIsSet()) || (codeIsSet() && rhs.codeIsSet() && getCode() == rhs.getCode())) && + + + ((!typeIsSet() && !rhs.typeIsSet()) || (typeIsSet() && rhs.typeIsSet() && getType() == rhs.getType())) && + + + ((!messageIsSet() && !rhs.messageIsSet()) || (messageIsSet() && rhs.messageIsSet() && getMessage() == rhs.getMessage())) + + ; +} + +bool ApiResponse::operator!=(const ApiResponse& rhs) const +{ + return !(*this == rhs); } void to_json(nlohmann::json& j, const ApiResponse& o) @@ -123,8 +163,5 @@ void ApiResponse::unsetMessage() } -} -} -} -} +} // namespace org::openapitools::server::model diff --git a/samples/server/petstore/cpp-pistache/model/ApiResponse.h b/samples/server/petstore/cpp-pistache/model/ApiResponse.h index 7ecb6c9a82e..d963b76650b 100644 --- a/samples/server/petstore/cpp-pistache/model/ApiResponse.h +++ b/samples/server/petstore/cpp-pistache/model/ApiResponse.h @@ -22,10 +22,8 @@ #include #include -namespace org { -namespace openapitools { -namespace server { -namespace model { +namespace org::openapitools::server::model +{ /// /// Describes the result of uploading an image resource @@ -34,9 +32,22 @@ class ApiResponse { public: ApiResponse(); - virtual ~ApiResponse(); + virtual ~ApiResponse() = default; - void validate(); + + /// + /// Validate the current data in the model. Throws a ValidationException on failure. + /// + void validate() const; + + /// + /// Validate the current data in the model. Returns false on error and writes an error + /// message into the given stringstream. + /// + bool validate(std::stringstream& msg) const; + + bool operator==(const ApiResponse& rhs) const; + bool operator!=(const ApiResponse& rhs) const; ///////////////////////////////////////////// /// ApiResponse members @@ -72,11 +83,11 @@ protected: bool m_TypeIsSet; std::string m_Message; bool m_MessageIsSet; + + // Helper overload for validate. Used when one model stores another model and calls it's validate. + bool validate(std::stringstream& msg, const std::string& pathPrefix) const; }; -} -} -} -} +} // namespace org::openapitools::server::model #endif /* ApiResponse_H_ */ diff --git a/samples/server/petstore/cpp-pistache/model/Category.cpp b/samples/server/petstore/cpp-pistache/model/Category.cpp index 3b08114f61d..deb18ff9bb7 100644 --- a/samples/server/petstore/cpp-pistache/model/Category.cpp +++ b/samples/server/petstore/cpp-pistache/model/Category.cpp @@ -12,11 +12,12 @@ #include "Category.h" +#include "Helpers.h" -namespace org { -namespace openapitools { -namespace server { -namespace model { +#include + +namespace org::openapitools::server::model +{ Category::Category() { @@ -27,13 +28,48 @@ Category::Category() } -Category::~Category() +void Category::validate() const { + std::stringstream msg; + if (!validate(msg)) + { + throw org::openapitools::server::helpers::ValidationException(msg.str()); + } } -void Category::validate() +bool Category::validate(std::stringstream& msg) const { - // TODO: implement validation + return validate(msg, ""); +} + +bool Category::validate(std::stringstream& msg, const std::string& pathPrefix) const +{ + bool success = true; + const std::string _pathPrefix = pathPrefix.empty() ? "Category" : pathPrefix; + + + + + return success; +} + +bool Category::operator==(const Category& rhs) const +{ + return + + + + ((!idIsSet() && !rhs.idIsSet()) || (idIsSet() && rhs.idIsSet() && getId() == rhs.getId())) && + + + ((!nameIsSet() && !rhs.nameIsSet()) || (nameIsSet() && rhs.nameIsSet() && getName() == rhs.getName())) + + ; +} + +bool Category::operator!=(const Category& rhs) const +{ + return !(*this == rhs); } void to_json(nlohmann::json& j, const Category& o) @@ -97,8 +133,5 @@ void Category::unsetName() } -} -} -} -} +} // namespace org::openapitools::server::model diff --git a/samples/server/petstore/cpp-pistache/model/Category.h b/samples/server/petstore/cpp-pistache/model/Category.h index fa12fdc0de8..1929ce33e92 100644 --- a/samples/server/petstore/cpp-pistache/model/Category.h +++ b/samples/server/petstore/cpp-pistache/model/Category.h @@ -22,10 +22,8 @@ #include #include -namespace org { -namespace openapitools { -namespace server { -namespace model { +namespace org::openapitools::server::model +{ /// /// A category for a pet @@ -34,9 +32,22 @@ class Category { public: Category(); - virtual ~Category(); + virtual ~Category() = default; - void validate(); + + /// + /// Validate the current data in the model. Throws a ValidationException on failure. + /// + void validate() const; + + /// + /// Validate the current data in the model. Returns false on error and writes an error + /// message into the given stringstream. + /// + bool validate(std::stringstream& msg) const; + + bool operator==(const Category& rhs) const; + bool operator!=(const Category& rhs) const; ///////////////////////////////////////////// /// Category members @@ -63,11 +74,11 @@ protected: bool m_IdIsSet; std::string m_Name; bool m_NameIsSet; + + // Helper overload for validate. Used when one model stores another model and calls it's validate. + bool validate(std::stringstream& msg, const std::string& pathPrefix) const; }; -} -} -} -} +} // namespace org::openapitools::server::model #endif /* Category_H_ */ diff --git a/samples/server/petstore/cpp-pistache/model/Helpers.cpp b/samples/server/petstore/cpp-pistache/model/Helpers.cpp index a7774194c82..cfac2d78986 100644 --- a/samples/server/petstore/cpp-pistache/model/Helpers.cpp +++ b/samples/server/petstore/cpp-pistache/model/Helpers.cpp @@ -10,34 +10,75 @@ * Do not edit the class manually. */ #include "Helpers.h" +#include -namespace org { -namespace openapitools { -namespace server { -namespace helpers { +namespace org::openapitools::server::helpers +{ +const std::regex regexRfc3339_date(R"(^(\d{4})\-(\d{2})\-(\d{2})$)"); +const std::regex regexRfc3339_date_time( + R"(^(\d{4})\-(\d{2})\-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(\.\d+)?([Zz]|([\+\-])(\d{2}):(\d{2}))$)" +); + + +namespace +{ + // Determine if given year is a leap year + // See RFC 3339, Appendix C https://tools.ietf.org/html/rfc3339#appendix-C + bool isLeapYear(const uint16_t year) { + return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); + } + + bool validateDateValues(const uint16_t year, const uint16_t month, const uint16_t day) { + return !( + (month == 0 || month > 12) + || (day == 0) + || (month == 2 && day > (28 + (isLeapYear(year) ? 1 : 0))) + || (month <= 7 && day > (30 + month % 2)) + || (month >= 8 && day > (31 - month % 2)) + ); + } + + bool validateTimeValues(const uint16_t hours, const uint16_t minutes, const uint16_t seconds) { + return (hours <= 23) && (minutes <= 59) && (seconds <= 60); + } +} + +bool validateRfc3339_date(const std::string& str) { + std::smatch match; + const bool found = std::regex_search(str, match, regexRfc3339_date); + return found && validateDateValues(std::stoi(match[1]), std::stoi(match[2]), std::stoi(match[3])); +} + +bool validateRfc3339_date_time(const std::string& str) { + std::smatch match; + const bool found = std::regex_search(str, match, regexRfc3339_date_time); + return found + && validateDateValues(std::stoi(match[1]), std::stoi(match[2]), std::stoi(match[3])) + && validateTimeValues(std::stoi(match[4]), std::stoi(match[5]), std::stoi(match[6])); +} std::string toStringValue(const std::string &value){ return std::string(value); } -std::string toStringValue(const int32_t &value){ +std::string toStringValue(const int32_t value){ return std::to_string(value); } -std::string toStringValue(const int64_t &value){ +std::string toStringValue(const int64_t value){ return std::to_string(value); } -std::string toStringValue(const bool &value){ - return value?std::string("true"):std::string("false"); +std::string toStringValue(const bool value){ + return value ? std::string("true") : std::string("false"); } -std::string toStringValue(const float &value){ +std::string toStringValue(const float value){ return std::to_string(value); } -std::string toStringValue(const double &value){ +std::string toStringValue(const double value){ return std::to_string(value); } @@ -67,9 +108,15 @@ bool fromStringValue(const std::string &inStr, int64_t &value){ } bool fromStringValue(const std::string &inStr, bool &value){ - bool result = true; - inStr == "true"?value = true: inStr == "false"?value = false: result = false; - return result; + if (inStr == "true") { + value = true; + return true; + } + if (inStr == "false") { + value = false; + return true; + } + return false; } bool fromStringValue(const std::string &inStr, float &value){ @@ -92,7 +139,4 @@ bool fromStringValue(const std::string &inStr, double &value){ return true; } -} -} -} -} +} // namespace org::openapitools::server::helpers diff --git a/samples/server/petstore/cpp-pistache/model/Helpers.h b/samples/server/petstore/cpp-pistache/model/Helpers.h index 9c8c02c4ae6..176b817de1e 100644 --- a/samples/server/petstore/cpp-pistache/model/Helpers.h +++ b/samples/server/petstore/cpp-pistache/model/Helpers.h @@ -24,17 +24,80 @@ #include #include -namespace org { -namespace openapitools { -namespace server { -namespace helpers { +namespace org::openapitools::server::helpers +{ + + class ValidationException : public std::runtime_error + { + public: + explicit ValidationException(const std::string& what) + : std::runtime_error(what) + { } + ~ValidationException() override = default; + }; + + /// + /// Validate a string against the full-date definition of RFC 3339, section 5.6. + /// + bool validateRfc3339_date(const std::string& str); + + /// + /// Validate a string against the date-time definition of RFC 3339, section 5.6. + /// + bool validateRfc3339_date_time(const std::string& str); + + namespace sfinae_helpers + { + struct NoType {}; + template NoType operator==(const T1&, const T2&); + + template class EqualsOperatorAvailable + { + public: + enum + { + value = !std::is_same< decltype(std::declval() == std::declval()), NoType >::value + }; + }; + } // namespace sfinae_helpers + + + /// + /// Determine if the given vector only has unique elements. T must provide the == operator. + /// + template + bool hasOnlyUniqueItems(const std::vector& vec) + { + static_assert(sfinae_helpers::EqualsOperatorAvailable::value, + "hasOnlyUniqueItems cannot be called, passed template type does not provide == operator."); + if (vec.size() <= 1) + { + return true; + } + // Compare every element of vec to every other element of vec. + // This isn't an elegant way to do this, since it's O(n^2), + // but it's the best solution working only with the == operator. + // This could be greatly improved if our models provided a valid hash + // and/or the < operator + for (size_t i = 0; i < vec.size() - 1; i++) + { + for (size_t j = i + 1; j < vec.size(); j++) + { + if (vec[i] == vec[j]) + { + return false; + } + } + } + return true; + } std::string toStringValue(const std::string &value); - std::string toStringValue(const int32_t &value); - std::string toStringValue(const int64_t &value); - std::string toStringValue(const bool &value); - std::string toStringValue(const float &value); - std::string toStringValue(const double &value); + std::string toStringValue(const int32_t value); + std::string toStringValue(const int64_t value); + std::string toStringValue(const bool value); + std::string toStringValue(const float value); + std::string toStringValue(const double value); bool fromStringValue(const std::string &inStr, std::string &value); bool fromStringValue(const std::string &inStr, int32_t &value); @@ -68,9 +131,6 @@ namespace helpers { return fromStringValue(inStrings, value); } -} -} -} -} +} // namespace org::openapitools::server::helpers -#endif // Helpers_H_ \ No newline at end of file +#endif // Helpers_H_ diff --git a/samples/server/petstore/cpp-pistache/model/Order.cpp b/samples/server/petstore/cpp-pistache/model/Order.cpp index 986cf6b24fa..2d04f0ac637 100644 --- a/samples/server/petstore/cpp-pistache/model/Order.cpp +++ b/samples/server/petstore/cpp-pistache/model/Order.cpp @@ -12,11 +12,12 @@ #include "Order.h" +#include "Helpers.h" -namespace org { -namespace openapitools { -namespace server { -namespace model { +#include + +namespace org::openapitools::server::model +{ Order::Order() { @@ -35,13 +36,64 @@ Order::Order() } -Order::~Order() +void Order::validate() const { + std::stringstream msg; + if (!validate(msg)) + { + throw org::openapitools::server::helpers::ValidationException(msg.str()); + } } -void Order::validate() +bool Order::validate(std::stringstream& msg) const { - // TODO: implement validation + return validate(msg, ""); +} + +bool Order::validate(std::stringstream& msg, const std::string& pathPrefix) const +{ + bool success = true; + const std::string _pathPrefix = pathPrefix.empty() ? "Order" : pathPrefix; + + + + + + + + + return success; +} + +bool Order::operator==(const Order& rhs) const +{ + return + + + + ((!idIsSet() && !rhs.idIsSet()) || (idIsSet() && rhs.idIsSet() && getId() == rhs.getId())) && + + + ((!petIdIsSet() && !rhs.petIdIsSet()) || (petIdIsSet() && rhs.petIdIsSet() && getPetId() == rhs.getPetId())) && + + + ((!quantityIsSet() && !rhs.quantityIsSet()) || (quantityIsSet() && rhs.quantityIsSet() && getQuantity() == rhs.getQuantity())) && + + + ((!shipDateIsSet() && !rhs.shipDateIsSet()) || (shipDateIsSet() && rhs.shipDateIsSet() && getShipDate() == rhs.getShipDate())) && + + + ((!statusIsSet() && !rhs.statusIsSet()) || (statusIsSet() && rhs.statusIsSet() && getStatus() == rhs.getStatus())) && + + + ((!completeIsSet() && !rhs.completeIsSet()) || (completeIsSet() && rhs.completeIsSet() && isComplete() == rhs.isComplete())) + + ; +} + +bool Order::operator!=(const Order& rhs) const +{ + return !(*this == rhs); } void to_json(nlohmann::json& j, const Order& o) @@ -201,8 +253,5 @@ void Order::unsetComplete() } -} -} -} -} +} // namespace org::openapitools::server::model diff --git a/samples/server/petstore/cpp-pistache/model/Order.h b/samples/server/petstore/cpp-pistache/model/Order.h index d900b633f32..3d28e1eef6b 100644 --- a/samples/server/petstore/cpp-pistache/model/Order.h +++ b/samples/server/petstore/cpp-pistache/model/Order.h @@ -22,10 +22,8 @@ #include #include -namespace org { -namespace openapitools { -namespace server { -namespace model { +namespace org::openapitools::server::model +{ /// /// An order for a pets from the pet store @@ -34,9 +32,22 @@ class Order { public: Order(); - virtual ~Order(); + virtual ~Order() = default; - void validate(); + + /// + /// Validate the current data in the model. Throws a ValidationException on failure. + /// + void validate() const; + + /// + /// Validate the current data in the model. Returns false on error and writes an error + /// message into the given stringstream. + /// + bool validate(std::stringstream& msg) const; + + bool operator==(const Order& rhs) const; + bool operator!=(const Order& rhs) const; ///////////////////////////////////////////// /// Order members @@ -99,11 +110,11 @@ protected: bool m_StatusIsSet; bool m_Complete; bool m_CompleteIsSet; + + // Helper overload for validate. Used when one model stores another model and calls it's validate. + bool validate(std::stringstream& msg, const std::string& pathPrefix) const; }; -} -} -} -} +} // namespace org::openapitools::server::model #endif /* Order_H_ */ diff --git a/samples/server/petstore/cpp-pistache/model/Pet.cpp b/samples/server/petstore/cpp-pistache/model/Pet.cpp index 434b1339cd0..9c7ba886a44 100644 --- a/samples/server/petstore/cpp-pistache/model/Pet.cpp +++ b/samples/server/petstore/cpp-pistache/model/Pet.cpp @@ -12,11 +12,12 @@ #include "Pet.h" +#include "Helpers.h" -namespace org { -namespace openapitools { -namespace server { -namespace model { +#include + +namespace org::openapitools::server::model +{ Pet::Pet() { @@ -30,13 +31,106 @@ Pet::Pet() } -Pet::~Pet() +void Pet::validate() const { + std::stringstream msg; + if (!validate(msg)) + { + throw org::openapitools::server::helpers::ValidationException(msg.str()); + } } -void Pet::validate() +bool Pet::validate(std::stringstream& msg) const { - // TODO: implement validation + return validate(msg, ""); +} + +bool Pet::validate(std::stringstream& msg, const std::string& pathPrefix) const +{ + bool success = true; + const std::string _pathPrefix = pathPrefix.empty() ? "Pet" : pathPrefix; + + + + + + + /* PhotoUrls */ { + const std::vector& value = m_PhotoUrls; + const std::string currentValuePath = _pathPrefix + ".photoUrls"; + + + { // Recursive validation of array elements + const std::string oldValuePath = currentValuePath; + int i = 0; + for (const std::string& value : value) + { + const std::string currentValuePath = oldValuePath + "[" + std::to_string(i) + "]"; + + + + i++; + } + } + + } + + + if (tagsIsSet()) + { + const std::vector& value = m_Tags; + const std::string currentValuePath = _pathPrefix + ".tags"; + + + { // Recursive validation of array elements + const std::string oldValuePath = currentValuePath; + int i = 0; + for (const Tag& value : value) + { + const std::string currentValuePath = oldValuePath + "[" + std::to_string(i) + "]"; + + success = value.validate(msg, currentValuePath + ".tags") && success; + + i++; + } + } + + } + + + + return success; +} + +bool Pet::operator==(const Pet& rhs) const +{ + return + + + + ((!idIsSet() && !rhs.idIsSet()) || (idIsSet() && rhs.idIsSet() && getId() == rhs.getId())) && + + + ((!categoryIsSet() && !rhs.categoryIsSet()) || (categoryIsSet() && rhs.categoryIsSet() && getCategory() == rhs.getCategory())) && + + (getName() == rhs.getName()) + && + + (getPhotoUrls() == rhs.getPhotoUrls()) + && + + + ((!tagsIsSet() && !rhs.tagsIsSet()) || (tagsIsSet() && rhs.tagsIsSet() && getTags() == rhs.getTags())) && + + + ((!statusIsSet() && !rhs.statusIsSet()) || (statusIsSet() && rhs.statusIsSet() && getStatus() == rhs.getStatus())) + + ; +} + +bool Pet::operator!=(const Pet& rhs) const +{ + return !(*this == rhs); } void to_json(nlohmann::json& j, const Pet& o) @@ -124,7 +218,7 @@ void Pet::setName(std::string const& value) { m_Name = value; } -std::vector& Pet::getPhotoUrls() +std::vector Pet::getPhotoUrls() const { return m_PhotoUrls; } @@ -132,7 +226,7 @@ void Pet::setPhotoUrls(std::vector const& value) { m_PhotoUrls = value; } -std::vector& Pet::getTags() +std::vector Pet::getTags() const { return m_Tags; } @@ -168,8 +262,5 @@ void Pet::unsetStatus() } -} -} -} -} +} // namespace org::openapitools::server::model diff --git a/samples/server/petstore/cpp-pistache/model/Pet.h b/samples/server/petstore/cpp-pistache/model/Pet.h index eddf475f6ee..1cd841a08fa 100644 --- a/samples/server/petstore/cpp-pistache/model/Pet.h +++ b/samples/server/petstore/cpp-pistache/model/Pet.h @@ -25,10 +25,8 @@ #include #include -namespace org { -namespace openapitools { -namespace server { -namespace model { +namespace org::openapitools::server::model +{ /// /// A pet for sale in the pet store @@ -37,9 +35,22 @@ class Pet { public: Pet(); - virtual ~Pet(); + virtual ~Pet() = default; - void validate(); + + /// + /// Validate the current data in the model. Throws a ValidationException on failure. + /// + void validate() const; + + /// + /// Validate the current data in the model. Returns false on error and writes an error + /// message into the given stringstream. + /// + bool validate(std::stringstream& msg) const; + + bool operator==(const Pet& rhs) const; + bool operator!=(const Pet& rhs) const; ///////////////////////////////////////////// /// Pet members @@ -66,12 +77,12 @@ public: /// /// /// - std::vector& getPhotoUrls(); + std::vector getPhotoUrls() const; void setPhotoUrls(std::vector const& value); /// /// /// - std::vector& getTags(); + std::vector getTags() const; void setTags(std::vector const& value); bool tagsIsSet() const; void unsetTags(); @@ -98,11 +109,11 @@ protected: bool m_TagsIsSet; std::string m_Status; bool m_StatusIsSet; + + // Helper overload for validate. Used when one model stores another model and calls it's validate. + bool validate(std::stringstream& msg, const std::string& pathPrefix) const; }; -} -} -} -} +} // namespace org::openapitools::server::model #endif /* Pet_H_ */ diff --git a/samples/server/petstore/cpp-pistache/model/Tag.cpp b/samples/server/petstore/cpp-pistache/model/Tag.cpp index cba759bad1d..659565e4034 100644 --- a/samples/server/petstore/cpp-pistache/model/Tag.cpp +++ b/samples/server/petstore/cpp-pistache/model/Tag.cpp @@ -12,11 +12,12 @@ #include "Tag.h" +#include "Helpers.h" -namespace org { -namespace openapitools { -namespace server { -namespace model { +#include + +namespace org::openapitools::server::model +{ Tag::Tag() { @@ -27,13 +28,48 @@ Tag::Tag() } -Tag::~Tag() +void Tag::validate() const { + std::stringstream msg; + if (!validate(msg)) + { + throw org::openapitools::server::helpers::ValidationException(msg.str()); + } } -void Tag::validate() +bool Tag::validate(std::stringstream& msg) const { - // TODO: implement validation + return validate(msg, ""); +} + +bool Tag::validate(std::stringstream& msg, const std::string& pathPrefix) const +{ + bool success = true; + const std::string _pathPrefix = pathPrefix.empty() ? "Tag" : pathPrefix; + + + + + return success; +} + +bool Tag::operator==(const Tag& rhs) const +{ + return + + + + ((!idIsSet() && !rhs.idIsSet()) || (idIsSet() && rhs.idIsSet() && getId() == rhs.getId())) && + + + ((!nameIsSet() && !rhs.nameIsSet()) || (nameIsSet() && rhs.nameIsSet() && getName() == rhs.getName())) + + ; +} + +bool Tag::operator!=(const Tag& rhs) const +{ + return !(*this == rhs); } void to_json(nlohmann::json& j, const Tag& o) @@ -97,8 +133,5 @@ void Tag::unsetName() } -} -} -} -} +} // namespace org::openapitools::server::model diff --git a/samples/server/petstore/cpp-pistache/model/Tag.h b/samples/server/petstore/cpp-pistache/model/Tag.h index 4b422931d77..ddebeadae43 100644 --- a/samples/server/petstore/cpp-pistache/model/Tag.h +++ b/samples/server/petstore/cpp-pistache/model/Tag.h @@ -22,10 +22,8 @@ #include #include -namespace org { -namespace openapitools { -namespace server { -namespace model { +namespace org::openapitools::server::model +{ /// /// A tag for a pet @@ -34,9 +32,22 @@ class Tag { public: Tag(); - virtual ~Tag(); + virtual ~Tag() = default; - void validate(); + + /// + /// Validate the current data in the model. Throws a ValidationException on failure. + /// + void validate() const; + + /// + /// Validate the current data in the model. Returns false on error and writes an error + /// message into the given stringstream. + /// + bool validate(std::stringstream& msg) const; + + bool operator==(const Tag& rhs) const; + bool operator!=(const Tag& rhs) const; ///////////////////////////////////////////// /// Tag members @@ -63,11 +74,11 @@ protected: bool m_IdIsSet; std::string m_Name; bool m_NameIsSet; + + // Helper overload for validate. Used when one model stores another model and calls it's validate. + bool validate(std::stringstream& msg, const std::string& pathPrefix) const; }; -} -} -} -} +} // namespace org::openapitools::server::model #endif /* Tag_H_ */ diff --git a/samples/server/petstore/cpp-pistache/model/User.cpp b/samples/server/petstore/cpp-pistache/model/User.cpp index 2db9316b75d..c997bf9734d 100644 --- a/samples/server/petstore/cpp-pistache/model/User.cpp +++ b/samples/server/petstore/cpp-pistache/model/User.cpp @@ -12,11 +12,12 @@ #include "User.h" +#include "Helpers.h" -namespace org { -namespace openapitools { -namespace server { -namespace model { +#include + +namespace org::openapitools::server::model +{ User::User() { @@ -39,13 +40,72 @@ User::User() } -User::~User() +void User::validate() const { + std::stringstream msg; + if (!validate(msg)) + { + throw org::openapitools::server::helpers::ValidationException(msg.str()); + } } -void User::validate() +bool User::validate(std::stringstream& msg) const { - // TODO: implement validation + return validate(msg, ""); +} + +bool User::validate(std::stringstream& msg, const std::string& pathPrefix) const +{ + bool success = true; + const std::string _pathPrefix = pathPrefix.empty() ? "User" : pathPrefix; + + + + + + + + + + + return success; +} + +bool User::operator==(const User& rhs) const +{ + return + + + + ((!idIsSet() && !rhs.idIsSet()) || (idIsSet() && rhs.idIsSet() && getId() == rhs.getId())) && + + + ((!usernameIsSet() && !rhs.usernameIsSet()) || (usernameIsSet() && rhs.usernameIsSet() && getUsername() == rhs.getUsername())) && + + + ((!firstNameIsSet() && !rhs.firstNameIsSet()) || (firstNameIsSet() && rhs.firstNameIsSet() && getFirstName() == rhs.getFirstName())) && + + + ((!lastNameIsSet() && !rhs.lastNameIsSet()) || (lastNameIsSet() && rhs.lastNameIsSet() && getLastName() == rhs.getLastName())) && + + + ((!emailIsSet() && !rhs.emailIsSet()) || (emailIsSet() && rhs.emailIsSet() && getEmail() == rhs.getEmail())) && + + + ((!passwordIsSet() && !rhs.passwordIsSet()) || (passwordIsSet() && rhs.passwordIsSet() && getPassword() == rhs.getPassword())) && + + + ((!phoneIsSet() && !rhs.phoneIsSet()) || (phoneIsSet() && rhs.phoneIsSet() && getPhone() == rhs.getPhone())) && + + + ((!userStatusIsSet() && !rhs.userStatusIsSet()) || (userStatusIsSet() && rhs.userStatusIsSet() && getUserStatus() == rhs.getUserStatus())) + + ; +} + +bool User::operator!=(const User& rhs) const +{ + return !(*this == rhs); } void to_json(nlohmann::json& j, const User& o) @@ -253,8 +313,5 @@ void User::unsetUserStatus() } -} -} -} -} +} // namespace org::openapitools::server::model diff --git a/samples/server/petstore/cpp-pistache/model/User.h b/samples/server/petstore/cpp-pistache/model/User.h index 1f4c17ce6a8..02bd5f4230a 100644 --- a/samples/server/petstore/cpp-pistache/model/User.h +++ b/samples/server/petstore/cpp-pistache/model/User.h @@ -22,10 +22,8 @@ #include #include -namespace org { -namespace openapitools { -namespace server { -namespace model { +namespace org::openapitools::server::model +{ /// /// A User who is purchasing from the pet store @@ -34,9 +32,22 @@ class User { public: User(); - virtual ~User(); + virtual ~User() = default; - void validate(); + + /// + /// Validate the current data in the model. Throws a ValidationException on failure. + /// + void validate() const; + + /// + /// Validate the current data in the model. Returns false on error and writes an error + /// message into the given stringstream. + /// + bool validate(std::stringstream& msg) const; + + bool operator==(const User& rhs) const; + bool operator!=(const User& rhs) const; ///////////////////////////////////////////// /// User members @@ -117,11 +128,11 @@ protected: bool m_PhoneIsSet; int32_t m_UserStatus; bool m_UserStatusIsSet; + + // Helper overload for validate. Used when one model stores another model and calls it's validate. + bool validate(std::stringstream& msg, const std::string& pathPrefix) const; }; -} -} -} -} +} // namespace org::openapitools::server::model #endif /* User_H_ */