diff --git a/.github/workflows/samples-cpp-httplib-server.yaml b/.github/workflows/samples-cpp-httplib-server.yaml new file mode 100644 index 000000000000..e40e666b8c02 --- /dev/null +++ b/.github/workflows/samples-cpp-httplib-server.yaml @@ -0,0 +1,53 @@ +name: Samples cpp httplib server + +on: + push: + branches: + - "samples/server/petstore/cpp-httplib/**" + pull_request: + paths: + - "samples/server/petstore/cpp-httplib/**" + +env: + GRADLE_VERSION: 6.9 + +jobs: + build: + name: Build cpp httplib server + strategy: + matrix: + sample: + - samples/server/petstore/cpp-httplib + os: + - ubuntu-latest + - macOS-latest + - windows-latest + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - name: Install dependencies + if: matrix.os == 'ubuntu-latest' + run: | + sudo apt-get update + sudo apt-get install -y nlohmann-json3-dev libssl-dev + # Install httplib from source since it's not in Ubuntu repos + git clone https://github.com/yhirose/cpp-httplib.git + cd cpp-httplib + mkdir build && cd build + cmake .. -DCMAKE_BUILD_TYPE=Release + make -j$(nproc) + sudo make install + - name: Install dependencies + if: matrix.os == 'macOS-latest' + run: | + brew install nlohmann-json openssl + # Install httplib via Homebrew + brew install cpp-httplib + - name: Install dependencies + if: matrix.os == 'windows-latest' + run: | + vcpkg install nlohmann-json:x64-windows openssl:x64-windows cpp-httplib:x64-windows + shell: cmd + - name: Build + working-directory: ${{ matrix.sample }} + run: cmake -B build && cmake --build build --verbose diff --git a/README.md b/README.md index e7af152c4336..b82afd9a29b2 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ OpenAPI Generator allows generation of API client libraries (SDK generation), se | | Languages/Frameworks | | -------------------------------- |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **API clients** | **ActionScript**, **Ada**, **Apex**, **Bash**, **C**, **C#** (.net 2.0, 3.5 or later, .NET Standard 1.3 - 2.1, .NET Core 3.1, .NET 5.0. Libraries: RestSharp, GenericHost, HttpClient), **C++** (Arduino, cpp-restsdk, Qt5, Tizen, Unreal Engine 4), **Clojure**, **Crystal**, **Dart**, **Elixir**, **Elm**, **Eiffel**, **Erlang**, **Go**, **Groovy**, **Haskell** (http-client, Servant), **Java** (Apache HttpClient 4.x, Apache HttpClient 5.x, Jersey2.x, OkHttp, Retrofit1.x, Retrofit2.x, Feign, RestTemplate, RESTEasy, Vertx, Google API Client Library for Java, Rest-assured, Spring 5 Web Client, Spring 6 RestClient, MicroProfile Rest Client, Helidon), **Jetbrains HTTP Client**, **Julia**, **k6**, **Kotlin**, **Lua**, **N4JS**, **Nim**, **Node.js/JavaScript** (ES5, ES6, AngularJS with Google Closure Compiler annotations, Flow types, Apollo GraphQL DataStore), **Objective-C**, **OCaml**, **Perl**, **PHP**, **PowerShell**, **Python**, **R**, **Ruby**, **Rust** (hyper, reqwest, rust-server), **Scala** (akka, http4s, scalaz, sttp, swagger-async-httpclient, pekko), **Swift** (2.x, 3.x, 4.x, 5.x, 6.x), **Typescript** (AngularJS, Angular (9.x - 19.x), Aurelia, Axios, Fetch, Inversify, jQuery, Nestjs, Node, redux-query, Rxjs), **XoJo**, **Zapier** | -| **Server stubs** | **Ada**, **C#** (ASP.NET Core, Azure Functions), **C++** (Oat++, Pistache, Restbed, Qt5 QHTTPEngine), **Erlang**, **F#** (Giraffe), **Go** (net/http, Gin, Echo), **Haskell** (Servant, Yesod), **Java** (MSF4J, Spring, Undertow, JAX-RS: CDI, CXF, Inflector, Jersey, RestEasy, Play Framework, [PKMST](https://github.com/ProKarma-Inc/pkmst-getting-started-examples), [Vert.x](https://vertx.io/), [Apache Camel](https://camel.apache.org/), [Helidon](https://helidon.io/)), **Julia**, **Kotlin** (Spring Boot, [Ktor](https://github.com/ktorio/ktor), [Vert.x](https://vertx.io/)), **PHP** ([Flight](https://docs.flightphp.com/), Laravel, Lumen, [Mezzio (fka Zend Expressive)](https://github.com/mezzio/mezzio), Slim, Silex, [Symfony](https://symfony.com/)), **Python** (FastAPI, Flask), **NodeJS**, **Ruby** (Sinatra, Rails5), **Rust** ([rust-server](https://openapi-generator.tech/docs/generators/rust-server/)), **Scala** (Akka, [Finch](https://github.com/finagle/finch), [Lagom](https://github.com/lagom/lagom), [Play](https://www.playframework.com/), [Cask](https://github.com/com-lihaoyi/cask), Scalatra) | +| **Server stubs** | **Ada**, **C#** (ASP.NET Core, Azure Functions), **C++** (Oat++, Pistache, Restbed, Qt5 QHTTPEngine, Httplib), **Erlang**, **F#** (Giraffe), **Go** (net/http, Gin, Echo), **Haskell** (Servant, Yesod), **Java** (MSF4J, Spring, Undertow, JAX-RS: CDI, CXF, Inflector, Jersey, RestEasy, Play Framework, [PKMST](https://github.com/ProKarma-Inc/pkmst-getting-started-examples), [Vert.x](https://vertx.io/), [Apache Camel](https://camel.apache.org/), [Helidon](https://helidon.io/)), **Julia**, **Kotlin** (Spring Boot, [Ktor](https://github.com/ktorio/ktor), [Vert.x](https://vertx.io/)), **PHP** ([Flight](https://docs.flightphp.com/), Laravel, Lumen, [Mezzio (fka Zend Expressive)](https://github.com/mezzio/mezzio), Slim, Silex, [Symfony](https://symfony.com/)), **Python** (FastAPI, Flask), **NodeJS**, **Ruby** (Sinatra, Rails5), **Rust** ([rust-server](https://openapi-generator.tech/docs/generators/rust-server/)), **Scala** (Akka, [Finch](https://github.com/finagle/finch), [Lagom](https://github.com/lagom/lagom), [Play](https://www.playframework.com/), [Cask](https://github.com/com-lihaoyi/cask), Scalatra) | | **API documentation generators** | **HTML**, **Confluence Wiki**, **Asciidoc**, **Markdown**, **PlantUML** | | **Configuration files** | [**Apache2**](https://httpd.apache.org/) | | **Others** | **GraphQL**, **JMeter**, **Ktorm**, **MySQL Schema**, **Postman Collection**, **Protocol Buffer**, **WSDL** | diff --git a/bin/configs/cpp-httplib-petstore.yaml b/bin/configs/cpp-httplib-petstore.yaml new file mode 100644 index 000000000000..e702c74b6cf5 --- /dev/null +++ b/bin/configs/cpp-httplib-petstore.yaml @@ -0,0 +1,8 @@ +generatorName: cpp-httplib +outputDir: samples/server/petstore/cpp-httplib-server +inputSpec: modules/openapi-generator/src/test/resources/3_0/cpp-httplib-server/petstore.yaml +templateDir: modules/openapi-generator/src/main/resources/cpp-httplib +additionalProperties: + apiNamespace: "sample::openapi::api" + modelNamespace: "sample::openapi::model" + projectName: "SampleProject" diff --git a/docs/generators.md b/docs/generators.md index 009bb56b4bcd..482ff82dc63e 100644 --- a/docs/generators.md +++ b/docs/generators.md @@ -91,7 +91,8 @@ The following generators are available: * [cpp-pistache-server](generators/cpp-pistache-server.md) * [cpp-qt-qhttpengine-server](generators/cpp-qt-qhttpengine-server.md) * [cpp-restbed-server](generators/cpp-restbed-server.md) -* [cpp-restbed-server-deprecated](generators/cpp-restbed-server-deprecated.md) +* [cpp-restbed-server-deprecated](generators/cpp-restbed-server-deprecated.md) +* [cpp-httplib-server](generators/cpp-httplib-server.md) * [csharp-functions](generators/csharp-functions.md) * [erlang-server](generators/erlang-server.md) * [erlang-server-deprecated (deprecated)](generators/erlang-server-deprecated.md) diff --git a/docs/generators/README.md b/docs/generators/README.md index d61f00351d5a..5b5b9065bf96 100644 --- a/docs/generators/README.md +++ b/docs/generators/README.md @@ -69,6 +69,7 @@ The following generators are available: * [cpp-pistache-server](cpp-pistache-server.md) * [cpp-qt5-qhttpengine-server](cpp-qt5-qhttpengine-server.md) * [cpp-restbed-server](cpp-restbed-server.md) +* [cpp-httplib-server](cpp-httplib-server.md) * [erlang-server](erlang-server.md) * [fsharp-functions (beta)](fsharp-functions.md) * [fsharp-giraffe-server (beta)](fsharp-giraffe-server.md) diff --git a/docs/generators/cpp-httplib-server.md b/docs/generators/cpp-httplib-server.md new file mode 100644 index 000000000000..7a7ccfbf9913 --- /dev/null +++ b/docs/generators/cpp-httplib-server.md @@ -0,0 +1,545 @@ +--- +title: Documentation for the cpp-httplib-server Generator +--- + +## METADATA + +| Property | Value | Notes | +| -------- | ----- | ----- | +| generator name | cpp-httplib-server | pass this to the generate command after -g | +| generator stability | STABLE | | +| generator type | SERVER | | +| generator language | C++ | | +| generator default templating engine | mustache | | +| helpTxt | Generates a C++ server using the httplib library. | | + +## OVERVIEW + +The cpp-httplib-server generator creates a modern C++17/20 server implementation using the [httplib](https://github.com/yhirose/cpp-httplib) library. This generator produces: + +- **Type-safe API handlers** with std::variant-based response types +- **Model classes** with JSON serialization/deserialization using nlohmann::json +- **CMake build configuration** for easy compilation +- **Automatic error handling** with proper HTTP status codes +- **Namespace organization** for clean code structure + +### Key Features + +- **Modern C++ Standards**: Uses C++17+ features like std::variant, std::optional +- **Type Safety**: Compile-time type checking for request/response handling +- **JSON Integration**: Built-in JSON serialization with nlohmann::json +- **Error Handling**: Automatic HTTP error response generation +- **Lightweight**: Uses httplib for minimal dependencies +- **Cross-platform**: Works on Windows, Linux, and macOS + +## GENERATED CODE STRUCTURE + +``` +generated/ +├── CMakeLists.txt # Build configuration +├── README.md # Project documentation +├── api/ # API handler classes +│ ├── PetApi.h # API header with virtual methods +│ └── PetApi.cpp # Route registration and request handling +└── model/ # Data model classes + ├── Pet.h # Model header with JSON methods + └── Pet.cpp # Model implementation +``` + +### Generated API Handler Features +- **Type aliases**: Only generated for operations with response schemas +- **Virtual methods**: Return variants for schema-based operations, void for others +- **Route registration**: Automatic HTTP method and path binding +- **Exception handling**: Built-in JSON parsing and error response generation + +## CONFIG OPTIONS + +These options may be applied as additional-properties (cli) or configOptions (plugins). Refer to [configuration docs](https://openapi-generator.tech/docs/configuration) for more details. + +| Option | Description | Values | Default | +| ------ | ----------- | ------ | ------- | +| projectName | Name of the generated C++ project | string | cpp-httplib-server | +| modelNamespace | Namespace for model classes | string | model | +| apiNamespace | Namespace for API classes | string | api | +| enumNamespace | Namespace for enum classes | string | enum | +| licenseHeader | Custom license header for generated files | string | OpenAPI Generator default | +| stripPathFromClassName | Enable path-based class name generation | boolean | false | + +## TYPE MAPPINGS + +The generator maps OpenAPI types to modern C++ types: + +| OpenAPI Type | C++ Type | Notes | +| ------------ | -------- | ----- | +| integer | int | 32-bit signed integer | +| long | long | 64-bit signed integer | +| float | float | 32-bit floating point | +| double | double | 64-bit floating point | +| number | double | Default numeric type | +| boolean | bool | C++ boolean | +| string | std::string | Standard string | +| byte | unsigned char | Single byte | +| binary | std::string | Binary data as string | +| date | std::string | ISO 8601 date string | +| date-time | std::string | ISO 8601 datetime string | +| password | std::string | Password field | +| object | nlohmann::json | Generic JSON object | +| array | std::vector\ | Dynamic array | +| file | std::string | File path or content | + +## ADVANCED TYPE FEATURES + +### Nullable Types +Nullable properties are automatically wrapped in `std::optional`: +```cpp +std::optional optionalField; +``` + +### Response Types +Each API endpoint generates a type-safe response based on the OpenAPI schema: + +**Operations with response schemas:** +```cpp +using PetResponse = std::variant< + model::Pet, // 200 success (custom model with schema) + std::string // 400 bad request (primitive type with schema) +>; +``` + +**Operations without response schemas:** +```cpp +// No response type alias generated - handler returns void +virtual void handleDeleteForPet(const model::DeletePetRequest& request) = 0; +``` + +### Container Types +- **Arrays**: `std::vector` +- **Maps**: `std::map` +- **oneOf**: `std::variant` +- **anyOf**: `std::any` (for flexible types) + +## NAMESPACE ORGANIZATION + +The generator creates a clean namespace hierarchy: + +```cpp +namespace model { + class Pet { /* ... */ }; + class User { /* ... */ }; +} + +namespace api { + class PetApi { /* ... */ }; + class UserApi { /* ... */ }; +} +``` + +## ERROR HANDLING INNOVATIONS + +### Schema-Based Response Generation +The generator only includes responses that have defined schemas in the OpenAPI specification: + +- **With custom schema**: Uses the specific model type (e.g., `model::ErrorResponse`) +- **With primitive schema**: Uses the primitive C++ type (e.g., `std::string`, `int`) +- **Without schema**: No response type generated - handler returns `void` + +This approach ensures: +- **Type Safety**: Only meaningful data types are included in variants +- **Simplicity**: No artificial wrapper types for schema-less responses +- **Clarity**: Handlers only deal with actual response data structures defined in the API + +### Response Handler Generation +- **Operations with schemas**: Generate `std::variant` return types and handler methods +- **Operations without schemas**: Generate `void` handler methods with automatic status code handling +- **Mixed operations**: Only schema-defined responses appear in the variant + +### Automatic Exception Handling +Generated route handlers include comprehensive exception handling: +- JSON parse errors → 400 Bad Request +- General exceptions → 500 Internal Server Error +- Custom error responses based on OpenAPI spec + +### Handler Method Patterns + +**Variant-based handlers** (operations with response schemas): +```cpp +PetResponse handleGetForPet(const model::GetPetRequest& request) override { + // Return success type or error types from the variant + return model::Pet{}; // or return std::string{"error message"}; +} +``` + +**Void handlers** (operations without response schemas): +```cpp +void handleDeleteForPet(const model::DeletePetRequest& request) override { + // No response schemas defined - HTTP status handled automatically + // Delete operation logic here + deletePetFromDatabase(request.getPetId()); +} +``` + +## CUSTOM MODIFICATIONS + +### Path-Based Class Naming +When `stripPathFromClassName=true`, the generator intelligently extracts class names from API paths: + +- `/api/v1/pets/{id}` → `PetsApi`, `handleGetForPets()` +- `/users/profile` → `ProfileApi`, `handlePostForProfile()` + +### String Conversion Enhancements +The generator includes robust string conversion utilities: +- Handles camelCase, PascalCase, snake_case +- Splits on multiple delimiters: `/`, `_`, `-`, spaces, camelCase boundaries +- Sanitizes invalid characters for C++ identifiers + +### Model Preprocessing +Automatic schema title generation for better class names: +- Inline request schemas: `{operationId}Request` +- Inline response schemas: `{operationId}Response{statusCode}` +- Nested object properties: `{parentName}_{propertyName}` + +## IMPORT MAPPING + +Standard C++ library includes are automatically generated: + +| C++ Type | Include | +| -------- | ------- | +| std::string | #include \ | +| std::vector | #include \ | +| std::map | #include \ | +| std::optional | #include \ | +| std::variant | #include \ | +| nlohmann::json | (external dependency) | + +## INSTANTIATION TYPES + +| Type/Alias | Instantiated By | +| ---------- | --------------- | +| ResponseType | std::variant\ (only when schemas exist) | +| ModelType | Generated C++ class with JSON methods | +| HandlerType | Virtual function returning ResponseType or void | + +**Note**: Response type aliases are only generated for operations that have at least one response with a schema. Operations without response schemas generate void handler methods. + +## LANGUAGE PRIMITIVES + +
    +
  • bool
  • +
  • char
  • +
  • double
  • +
  • float
  • +
  • int
  • +
  • long
  • +
  • size_t
  • +
  • std::any
  • +
  • std::deque
  • +
  • std::list
  • +
  • std::map
  • +
  • std::optional
  • +
  • std::pair
  • +
  • std::queue
  • +
  • std::set
  • +
  • std::stack
  • +
  • std::string
  • +
  • std::tuple
  • +
  • std::unordered_map
  • +
  • std::unordered_set
  • +
  • std::variant
  • +
  • std::vector
  • +
  • unsigned char
  • +
  • unsigned int
  • +
  • unsigned long
  • +
  • void
  • +
+ +## RESERVED WORDS + +
    +
  • alignas
  • +
  • alignof
  • +
  • and
  • +
  • and_eq
  • +
  • asm
  • +
  • auto
  • +
  • bitand
  • +
  • bitor
  • +
  • bool
  • +
  • break
  • +
  • case
  • +
  • catch
  • +
  • char
  • +
  • char16_t
  • +
  • char32_t
  • +
  • class
  • +
  • compl
  • +
  • concept
  • +
  • const
  • +
  • const_cast
  • +
  • constexpr
  • +
  • continue
  • +
  • decltype
  • +
  • default
  • +
  • delete
  • +
  • do
  • +
  • double
  • +
  • dynamic_cast
  • +
  • else
  • +
  • enum
  • +
  • explicit
  • +
  • export
  • +
  • extern
  • +
  • false
  • +
  • float
  • +
  • for
  • +
  • friend
  • +
  • goto
  • +
  • if
  • +
  • inline
  • +
  • int
  • +
  • linux
  • +
  • long
  • +
  • mutable
  • +
  • namespace
  • +
  • new
  • +
  • noexcept
  • +
  • not
  • +
  • not_eq
  • +
  • nullptr
  • +
  • operator
  • +
  • or
  • +
  • or_eq
  • +
  • private
  • +
  • protected
  • +
  • public
  • +
  • register
  • +
  • reinterpret_cast
  • +
  • requires
  • +
  • return
  • +
  • short
  • +
  • signed
  • +
  • sizeof
  • +
  • static
  • +
  • static_assert
  • +
  • static_cast
  • +
  • struct
  • +
  • switch
  • +
  • template
  • +
  • this
  • +
  • thread_local
  • +
  • throw
  • +
  • true
  • +
  • try
  • +
  • typedef
  • +
  • typeid
  • +
  • typename
  • +
  • union
  • +
  • unsigned
  • +
  • using
  • +
  • virtual
  • +
  • void
  • +
  • volatile
  • +
  • wchar_t
  • +
  • while
  • +
  • xor
  • +
  • xor_eq
  • +
+ +## USAGE EXAMPLE + +### 1. Generate Server Code +```bash +openapi-generator generate \ + -i petstore.yaml \ + -g cpp-httplib-server \ + -o ./generated \ + --additional-properties=projectName=PetStore,modelNamespace=model,apiNamespace=api +``` + +### 2. Implement Handler +```cpp +#include "api/PetApi.h" + +class PetApiImpl : public api::PetApi { +public: + // Handler for operation with response schemas (variant return type) + PetResponse handleGetForPet(const model::GetPetRequest& request) override { + try { + model::Pet pet; + pet.setId(request.getPetId()); + pet.setName("Fluffy"); + return pet; // 200 OK with Pet model + } catch (const std::invalid_argument& e) { + return std::string("Invalid pet ID"); // 400 with string schema + } + } + + // Handler for operation without response schemas (void return type) + void handleDeleteForPet(const model::DeletePetRequest& request) override { + // Delete the pet from database - no response schema defined + deletePetFromDatabase(request.getPetId()); + // HTTP status handled automatically by framework + } +}; +``` + +### 3. Start Server +```cpp +#include +#include "api/PetApi.h" + +int main() { + httplib::Server server; + PetApiImpl petApi; + + petApi.RegisterRoutes(server); + + server.listen("localhost", 8080); + return 0; +} +``` + +## DEPENDENCIES + +### Required +- **C++17 or later**: For std::variant, std::optional +- **nlohmann/json**: For JSON serialization +- **httplib**: For HTTP server functionality + +### CMake Integration +The generated CMakeLists.txt handles dependency management: +```cmake +find_package(nlohmann_json REQUIRED) +find_package(httplib REQUIRED) + +target_link_libraries(${PROJECT_NAME} + nlohmann_json::nlohmann_json + httplib::httplib +) +``` + +## FEATURE SET + +### Client Modification Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|BasePath|✗|ToolingExtension +|Authorizations|✗|ToolingExtension +|UserAgent|✗|ToolingExtension +|MockServer|✗|ToolingExtension + +### Data Type Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Custom|✓|OAS2,OAS3 +|Int32|✓|OAS2,OAS3 +|Int64|✓|OAS2,OAS3 +|Float|✓|OAS2,OAS3 +|Double|✓|OAS2,OAS3 +|Decimal|✓|ToolingExtension +|String|✓|OAS2,OAS3 +|Byte|✓|OAS2,OAS3 +|Binary|✓|OAS2,OAS3 +|Boolean|✓|OAS2,OAS3 +|Date|✓|OAS2,OAS3 +|DateTime|✓|OAS2,OAS3 +|Password|✓|OAS2,OAS3 +|File|✓|OAS2 +|Uuid|✗| +|Array|✓|OAS2,OAS3 +|Null|✓|OAS3 +|AnyType|✓|OAS2,OAS3 +|Object|✓|OAS2,OAS3 +|Maps|✓|ToolingExtension +|CollectionFormat|✓|OAS2 +|CollectionFormatMulti|✓|OAS2 +|Enum|✓|OAS2,OAS3 +|ArrayOfEnum|✓|ToolingExtension +|ArrayOfModel|✓|ToolingExtension +|ArrayOfCollectionOfPrimitives|✓|ToolingExtension +|ArrayOfCollectionOfModel|✓|ToolingExtension +|ArrayOfCollectionOfEnum|✓|ToolingExtension +|MapOfEnum|✓|ToolingExtension +|MapOfModel|✓|ToolingExtension +|MapOfCollectionOfPrimitives|✓|ToolingExtension +|MapOfCollectionOfModel|✓|ToolingExtension +|MapOfCollectionOfEnum|✓|ToolingExtension + +### Documentation Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Readme|✓|ToolingExtension +|Model|✓|ToolingExtension +|Api|✓|ToolingExtension + +### Global Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Host|✓|OAS2,OAS3 +|BasePath|✓|OAS2,OAS3 +|Info|✓|OAS2,OAS3 +|Schemes|✗|OAS2,OAS3 +|PartialSchemes|✓|OAS2,OAS3 +|Consumes|✓|OAS2 +|Produces|✓|OAS2 +|ExternalDocumentation|✓|OAS2,OAS3 +|Examples|✓|OAS2,OAS3 +|XMLStructureDefinitions|✗|OAS2,OAS3 +|MultiServer|✗|OAS3 +|ParameterizedServer|✗|OAS3 +|ParameterStyling|✗|OAS3 +|Callbacks|✗|OAS3 +|LinkObjects|✗|OAS3 + +### Parameter Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Path|✓|OAS2,OAS3 +|Query|✓|OAS2,OAS3 +|Header|✓|OAS2,OAS3 +|Body|✓|OAS2 +|FormUnencoded|✓|OAS2 +|FormMultipart|✓|OAS2 +|Cookie|✗|OAS3 + +### Schema Support Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Simple|✓|OAS2,OAS3 +|Composite|✓|OAS2,OAS3 +|Polymorphism|✗|OAS2,OAS3 +|Union|✓|OAS3 +|allOf|✓|OAS2,OAS3 +|anyOf|✓|OAS3 +|oneOf|✓|OAS3 +|not|✗|OAS3 + +### Security Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|BasicAuth|✗|OAS2,OAS3 +|ApiKey|✗|OAS2,OAS3 +|OpenIDConnect|✗|OAS3 +|BearerToken|✗|OAS3 +|OAuth2_Implicit|✗|OAS2,OAS3 +|OAuth2_Password|✗|OAS2,OAS3 +|OAuth2_ClientCredentials|✗|OAS2,OAS3 +|OAuth2_AuthorizationCode|✗|OAS2,OAS3 +|SignatureAuth|✗|OAS3 +|AWSV4Signature|✗|ToolingExtension + +### Wire Format Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|JSON|✓|OAS2,OAS3 +|XML|✗|OAS2,OAS3 +|PROTOBUF|✗|ToolingExtension +|Custom|✗|OAS2,OAS3 + +## ROADMAP + +Future enhancements planned: +- Input validation with custom validators +- Authentication middleware integration +- OpenAPI 3.1 support +- Performance optimizations +- WebSocket support consideration diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppHttplibServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppHttplibServerCodegen.java new file mode 100644 index 000000000000..ee959c96d8ac --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppHttplibServerCodegen.java @@ -0,0 +1,1159 @@ +package org.openapitools.codegen.languages; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.responses.ApiResponse; +import org.openapitools.codegen.utils.*; +import org.openapitools.codegen.utils.CamelizeOption; +import org.openapitools.codegen.CodegenModel; +import org.openapitools.codegen.CodegenOperation; +import org.openapitools.codegen.CodegenParameter; +import org.openapitools.codegen.CodegenProperty; +import org.openapitools.codegen.CodegenResponse; +import org.openapitools.codegen.SupportingFile; +import org.openapitools.codegen.meta.features.DocumentationFeature; +import org.openapitools.codegen.meta.features.GlobalFeature; +import org.openapitools.codegen.meta.features.SchemaSupportFeature; +import org.openapitools.codegen.meta.features.SecurityFeature; +import org.openapitools.codegen.meta.features.WireFormatFeature; +import org.openapitools.codegen.model.ModelMap; +import org.openapitools.codegen.model.ModelsMap; +import org.openapitools.codegen.model.OperationMap; +import org.openapitools.codegen.model.OperationsMap; +import org.openapitools.codegen.utils.ModelUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.*; + +/** + * C++ HTTP Library Server Code Generator. + * This code generator creates C++ server stubs using the httplib library for handling HTTP requests. + * It generates: + * - Model classes with proper C++ typing (std::string, std::vector, std::optional, etc.) + * - API handler interfaces with type-safe request/response handling + * - CMake build configuration + * - JSON serialization/deserialization using nlohmann::json + * Key features: + * - Supports nullable types using std::optional + * - Proper namespace organization for models and APIs + * - Status code to error type mapping for responses + * - Standard C++ container types (vector, map, etc.) + * - Automatic include generation for dependencies + * + * @author OpenAPI Generator Contributors + */ +public class CppHttplibServerCodegen extends AbstractCppCodegen { + private final Logger LOGGER = LoggerFactory.getLogger(CppHttplibServerCodegen.class); + + public static final String PROJECT_NAME = "projectName"; + private static final String DEFAULT_PROJECT_NAME = "cpp-httplib-server"; + + //Model Namespace + public static final String MODEL_NAMESPACE = "modelNamespace"; + //Api Namespace + public static final String API_NAMESPACE = "apiNamespace"; + //Enum Namespace + public static final String ENUM_NAMESPACE = "enumNamespace"; + //Model Suffix + public static final String MODEL_SUFFIX = "models"; + //Api Suffix + public static final String API_SUFFIX = "api"; + //Enum Suffix + public static final String ENUM_SUFFIX = "enum"; + + + // Standard includes for mapped C++ types + private static final Map standardIncludes = Map.ofEntries( + Map.entry("std::string", "#include "), + Map.entry("std::vector", "#include "), + Map.entry("std::map", "#include "), + Map.entry("std::set", "#include "), + Map.entry("std::list", "#include "), + Map.entry("std::unordered_map", "#include "), + Map.entry("std::unordered_set", "#include "), + Map.entry("std::deque", "#include "), + Map.entry("std::queue", "#include "), + Map.entry("std::stack", "#include "), + Map.entry("std::pair", "#include "), + Map.entry("std::tuple", "#include "), + Map.entry("std::optional", "#include "), + Map.entry("std::any", "#include "), + Map.entry("std::variant", "#include ") + ); + + // Numeric types for include + private static final Set cstdintTypes = Set.of( + "byte", "int", "integer", "long", "short", "unsigned int", "unsigned long", + "unsigned short", "size_t", "ssize_t", "ptrdiff_t", "double", "float", + "boolean", "bool", "char", "wchar_t", "char16_t", "char32_t" + ); + + /** + * Constructor for CppHttplibServerCodegen. + * Initializes the code generator with C++ httplib server specific configurations, + * type mappings, and template files. + */ + public CppHttplibServerCodegen() { + super(); + embeddedTemplateDir = templateDir = "cpp-httplib-server"; + + // Map OpenAPI types/formats to C++ types (always use std:: prefix) + typeMapping.put("integer", "int"); + typeMapping.put("long", "long"); + typeMapping.put("float", "float"); + typeMapping.put("double", "double"); + typeMapping.put("number", "double"); + typeMapping.put("boolean", "bool"); + typeMapping.put("string", "std::string"); + typeMapping.put("byte", "unsigned char"); + typeMapping.put("binary", "std::string"); + typeMapping.put("date", "std::string"); + typeMapping.put("date-time", "std::string"); + typeMapping.put("password", "std::string"); + typeMapping.put("object", "nlohmann::json"); + typeMapping.put("array", "std::vector"); + typeMapping.put("file", "std::string"); + + // Only mapped C++ types as primitives + languageSpecificPrimitives.addAll(Arrays.asList( + "int", "long", "float", "double", "bool", "char", "unsigned int", "unsigned long", "unsigned char", "size_t", "void", + "std::string", "std::vector", "std::map", "std::set", "std::list", "std::unordered_map", "std::unordered_set", "std::deque", "std::queue", "std::stack", "std::pair", "std::tuple", "std::optional", "std::any", "std::variant" + )); + + // Configure template files for code generation + modelTemplateFiles.put("model-header.mustache", ".h"); + modelTemplateFiles.put("model-source.mustache", ".cpp"); + apiTemplateFiles.put("api-header.mustache", ".h"); + apiTemplateFiles.put("api-source.mustache", ".cpp"); + + // Add supporting files for project structure + supportingFiles.add(new SupportingFile("CMakeLists.txt.mustache", "", "CMakeLists.txt")); + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("License.mustache", "", "LICENSE")); + + // Initialize feature set for httplib server + modifyFeatureSet(features -> features + .includeDocumentationFeatures(DocumentationFeature.Readme) + .wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON)) + .securityFeatures(EnumSet.noneOf(SecurityFeature.class)) + .excludeGlobalFeatures( + GlobalFeature.XMLStructureDefinitions, + GlobalFeature.Callbacks, + GlobalFeature.LinkObjects, + GlobalFeature.ParameterStyling + ) + .excludeSchemaSupportFeatures( + SchemaSupportFeature.Polymorphism + ) + ); + + } + + /** + * Returns the name identifier for this code generator. + * + * @return the generator name + */ + @Override + public String getName() { + return DEFAULT_PROJECT_NAME; + } + + /** + * Returns a brief description of what this code generator does. + * + * @return help text describing the generator's purpose + */ + @Override + public String getHelp() { + return "Generates a C++ server using the httplib library."; + } + + /** + * Converts a model name to its corresponding import statement. + * Handles both standard C++ types and custom model types. + * + * @param name the model name to import + * @return the appropriate #include directive or empty string + */ + @Override + public String toModelImport(String name) { + // Skip if already formatted as an include + String includeFile = ""; + if (name.startsWith("#include")) { + includeFile = null; + } + // Use import mapping if available + if (importMapping.containsKey(name)) { + includeFile = importMapping.get(name); + } + // Map OpenAPI type to C++ type if possible + String mappedType = typeMapping.getOrDefault(name, name); + if(languageSpecificPrimitives.contains(mappedType)) { + String include = getStandardIncludeForType(mappedType); + includeFile = include != null ? include : ""; + } + else { + Map pathFromClassName = stripPathFromClassName(mappedType); + includeFile = "#include \"" + pathFromClassName.get("className") + ".h\""; + } + return includeFile; + } + + /** + * Returns the appropriate #include directive for a given C++ standard type. + * + * @param typeName the C++ type name + * @return the include directive or null if no include is needed + */ + private String getStandardIncludeForType(String typeName) { + if (standardIncludes.containsKey(typeName)) { + return standardIncludes.get(typeName); + } + if (cstdintTypes.contains(typeName)) { + return "#include "; + } + return null; + } + + /** + * Post-processes operations after model processing to add C++ specific configurations. + * This method: + * - Sets up class names and namespaces for API classes + * - Processes request and response models for each operation + * - Maps response status codes to their corresponding types + * - Collects all used models for proper include generation + * + * @param objs the operations map to process + * @param allModels list of all models in the API + * @return the processed operations map + */ + @Override + public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List allModels) { + if (objs == null || objs.getOperations() == null) { + LOGGER.warn("Operations or operations map is null"); + return objs; + } + OperationMap operations = objs.getOperations(); + List operationList = operations.getOperation(); + + + // Set classname and compute apiNamespace ONCE per API class + String classname = operations.getClassname(); + if ("DefaultApi".equals(classname) || classname == null || classname.isEmpty()) { + classname = addProjectOrDefaultName(classname, "DefaultApi"); + operations.setClassname(classname); + } + objs.put("apiHeaderFileName", "#include \"" + toPascalCase(classname) + toPascalCase(API_SUFFIX) + ".h\""); + objs.put("apiClassnameInPascalCase", toPascalCase(classname)); + + // Compute API namespace ONCE, append classname only if not already present as last segment + String apiNamespace = (String) additionalProperties.get(API_NAMESPACE); + if (apiNamespace == null || apiNamespace.isEmpty()) { + apiNamespace = toLowerCase(API_SUFFIX); + } + + objs.put("apiNamespace", apiNamespace); + + // Track all models used for includes + Set modelsUsed = new HashSet<>(); + for (CodegenOperation op : operationList) { + if (op.vendorExtensions == null) { + op.vendorExtensions = new HashMap<>(); + } + op.vendorExtensions.put("operationIdCamelCase", toCamelCase(op.operationId)); + op.vendorExtensions.put("httpMethod", op.httpMethod != null ? toPascalCase(op.httpMethod) : ""); + op.vendorExtensions.put("handlerFunctionName", toHandlerFunctionName(op.httpMethod, op.path.toString())); + op.vendorExtensions.put("handlerFunctionResponse", toHandlerFunctionResponse(op.path.toString())); + if (op.path != null) { + op.vendorExtensions.put("path", op.path); + } + // Handle request model + if (op.bodyParam != null && op.bodyParam.baseType != null) { + String className = stripPathFromClassName(op.bodyParam.baseType).get("className"); + String requestModel = "models::" + toPascalCase(className); + op.vendorExtensions.put("requestModel", requestModel); + modelsUsed.add(className); + } + + // Process responses + String successType = ""; + List errorTypes = new ArrayList<>(); + List> statusCodeToTypes = new ArrayList<>(); + for (CodegenResponse resp : op.responses) { + Boolean hasAnyResponseSchema = false; + if (resp.code != null) { + if (resp.code.equals("200") && resp.baseType != null) { + hasAnyResponseSchema = true; + if(!typeMapping.containsKey(resp.baseType)) { + String className = stripPathFromClassName(resp.baseType).get("className"); + successType = "models::" + toPascalCase(className); + modelsUsed.add(className); + } else { + // Primitive type - use the mapped C++ type + successType = typeMapping.get(resp.baseType); + } + } else { + String errorBaseType = resp.baseType; + String errorType=""; + + if (errorBaseType != null && !errorBaseType.isEmpty()) { + hasAnyResponseSchema = true; + if(!typeMapping.containsKey(errorBaseType)) { + // Custom model type + String className = stripPathFromClassName(errorBaseType).get("className"); + errorType = "models::" + toPascalCase(className); + modelsUsed.add(className); + } + else { + errorType = typeMapping.get(resp.baseType); + } + } + if (hasAnyResponseSchema) { + errorTypes.add(errorType); + Map item = new HashMap<>(); + item.put("typeName", errorType); + item.put("statusCode", resp.code); + statusCodeToTypes.add(item); + } + } + } + } + // Store vendor extensions for Mustache templates + if(successType != null && !successType.isEmpty()) { + op.vendorExtensions.put("successType", successType); + } + if(errorTypes != null && !errorTypes.isEmpty()) { + op.vendorExtensions.put("errorTypes", errorTypes); + } + if(!statusCodeToTypes.isEmpty()) { + op.vendorExtensions.put("statusCodeToTypes", statusCodeToTypes); + } + op.vendorExtensions.put("hasAnyResponseSchema", (successType != null && !successType.isEmpty()) || !errorTypes.isEmpty()); + } + // Add modelsUsed to objs for header includes + objs.put("modelsUsed", new ArrayList<>(modelsUsed)); + return objs; + } /** + * Preprocesses the OpenAPI specification to enhance inline schemas with proper titles. + * This method ensures that request and response schemas without explicit titles + * get automatically generated names based on the operation ID. + * + * @param openAPI the OpenAPI specification to preprocess + */ + @Override + @SuppressWarnings("rawtypes") + public void preprocessOpenAPI(OpenAPI openAPI) { + super.preprocessOpenAPI(openAPI); + + // Process schemas in components section first + if (openAPI.getComponents() != null && openAPI.getComponents().getSchemas() != null) { + for (Map.Entry entry : openAPI.getComponents().getSchemas().entrySet()) { + Schema schema = entry.getValue(); + if (schema.getTitle() == null) { + String title = toPascalCase(entry.getKey()); + schema.setTitle(title); + } + } + } + + // Process response schemas in components section + if (openAPI.getComponents() != null && openAPI.getComponents().getResponses() != null) { + for (Map.Entry entry : openAPI.getComponents().getResponses().entrySet()) { + ApiResponse apiResponse = entry.getValue(); + String responseName = entry.getKey(); + + if (apiResponse.getContent() != null) { + MediaType mediaType = apiResponse.getContent().get("application/json"); + if (mediaType != null && mediaType.getSchema() != null) { + Schema schema = mediaType.getSchema(); + if (schema.getTitle() == null) { + String title = toPascalCase(responseName); + schema.setTitle(title); + } + + // Also process nested schemas within the response schema + processNestedSchemas(schema, responseName); + } + } + } + } + + if (openAPI.getPaths() != null) { + for (Map.Entry entry : openAPI.getPaths().entrySet()) { + PathItem pathItem = entry.getValue(); + for (Map.Entry opEntry : pathItem.readOperationsMap().entrySet()) { + Operation op = opEntry.getValue(); + String operationId = op.getOperationId(); + + // Generate a fallback name if operationId is null + if (operationId == null || operationId.isEmpty()) { + operationId = toPascalCase(opEntry.getKey().name()) + "_" + toPascalCase(entry.getKey().replaceAll("[{}]", "")); + } + + // Set title for inline request schemas + if (op.getRequestBody() != null && op.getRequestBody().getContent() != null) { + Map content = op.getRequestBody().getContent(); + MediaType mediaType = content.get("application/json"); + if (mediaType != null && mediaType.getSchema() != null) { + Schema schema = mediaType.getSchema(); + if (schema.getTitle() == null && schema.get$ref() == null) { + String title = toPascalCase(operationId) + "Request"; + schema.setTitle(title); + } + } + } + + // Set title for inline response schemas + if (op.getResponses() != null) { + for (Map.Entry respEntry : op.getResponses().entrySet()) { + ApiResponse apiResp = respEntry.getValue(); + if (apiResp.getContent() != null) { + MediaType mediaType = apiResp.getContent().get("application/json"); + if (mediaType != null && mediaType.getSchema() != null) { + Schema schema = mediaType.getSchema(); + if (schema.getTitle() == null && schema.get$ref() == null) { + String title = toPascalCase(operationId) + "Response" + respEntry.getKey(); + schema.setTitle(title); + } + } + } + } + } + + // Set title for inline parameter schemas + if (op.getParameters() != null) { + for (io.swagger.v3.oas.models.parameters.Parameter parameter : op.getParameters()) { + if (parameter.getSchema() != null) { + Schema schema = parameter.getSchema(); + if (schema.getTitle() == null && schema.get$ref() == null) { + String title = toPascalCase(operationId) + toPascalCase(parameter.getName()) + "Parameter"; + schema.setTitle(title); + } + } + } + } + } + } + } + } + + /** + * Recursively processes nested schemas within a parent schema to set meaningful titles. + * This handles cases like component responses that have nested inline object schemas. + * + * @param schema the parent schema to process + * @param parentName the name of the parent (used for generating child names) + */ + @SuppressWarnings("rawtypes") + private void processNestedSchemas(Schema schema, String parentName) { + if (schema == null) { + return; + } + + // Process properties of object schemas + if (schema.getProperties() != null) { + for (Map.Entry propEntry : ((Map) schema.getProperties()).entrySet()) { + Schema propSchema = propEntry.getValue(); + String propertyName = propEntry.getKey(); + + // Set title for nested inline schemas + if (propSchema.getTitle() == null && propSchema.get$ref() == null) { + // For nested objects, create a meaningful name + if ("object".equals(propSchema.getType()) || (propSchema.getType() == null && propSchema.getProperties() != null)) { + String title = toPascalCase(parentName + "_" + propertyName); + propSchema.setTitle(title); + + // Recursively process nested properties + processNestedSchemas(propSchema, title); + } + } + } + } + + // Process array items + if (schema.getItems() != null) { + Schema itemSchema = schema.getItems(); + if (itemSchema.getTitle() == null && itemSchema.get$ref() == null) { + if ("object".equals(itemSchema.getType()) || (itemSchema.getType() == null && itemSchema.getProperties() != null)) { + String title = toPascalCase(parentName + "_item"); + itemSchema.setTitle(title); + processNestedSchemas(itemSchema, title); + } + } + } + + // Process additionalProperties + if (schema.getAdditionalProperties() instanceof Schema) { + Schema additionalSchema = (Schema) schema.getAdditionalProperties(); + if (additionalSchema.getTitle() == null && additionalSchema.get$ref() == null) { + if ("object".equals(additionalSchema.getType()) || (additionalSchema.getType() == null && additionalSchema.getProperties() != null)) { + String title = toPascalCase(parentName + "_additional"); + additionalSchema.setTitle(title); + processNestedSchemas(additionalSchema, title); + } + } + } + } + + /** + * Post-processes all models to add C++ specific configurations and namespace handling. + * This method: + * - Computes model namespaces based on class names + * - Filters imports to exclude primitives and standard types + * - Processes model variables for proper C++ typing + * - Sets up proper getter/setter configurations + * + * @param objs map of all models to process + * @return the processed models map + */ + @Override + public Map postProcessAllModels(Map objs) { + Map processed = super.postProcessAllModels(objs); + + // Compute modelNamespace ONCE and append className + String modelNamespaceBase = (String) additionalProperties.get(MODEL_NAMESPACE); + if (modelNamespaceBase == null || modelNamespaceBase.isEmpty()) { + modelNamespaceBase = toLowerCase(MODEL_SUFFIX); + } + + for (Map.Entry entry : processed.entrySet()) { + ModelsMap modelsMap = entry.getValue(); + + // Compute className from entry key + Map pathFromClassName = stripPathFromClassName(entry.getKey()); + String modelClassName = pathFromClassName.getOrDefault("className", toModelName(entry.getKey())); + String modelNamespace = modelNamespaceBase; + // Append className to modelNamespace + if(!modelClassName.isEmpty()) { + modelNamespace = modelNamespaceBase;// + "::" + toLowerCase(modelClassName); + } + + for (ModelMap modelMap : modelsMap.getModels()) { + CodegenModel model = modelMap.getModel(); + if (model != null) { + // Set in vendorExtensions for backward compatibility + model.vendorExtensions.put("modelNamespace", modelNamespace); + model.vendorExtensions.put("modelClassName", toPascalCase(modelClassName)); + + // --- Filter and set imports for mustache --- + Set filteredImports = new LinkedHashSet<>(); + if (model.imports != null) { + for (String imp : model.imports) { + // Only add if not a primitive, not a mapped type, and not "object" or "nlohmann::json" + if (!languageSpecificPrimitives.contains(imp) + && !typeMapping.containsKey(imp) + && !"object".equals(imp) + && !"nlohmann::json".equals(imp)) { + + String headerName = toPascalCase(imp); + filteredImports.add("#include \"" + headerName + ".h\""); + } + } + } + // Set the imports for mustache template + model.vendorExtensions.put("filteredImports", new ArrayList<>(filteredImports)); + // --- End filter and set imports --- + + // Process model variables + if (model.vars != null) { + for (CodegenProperty var : model.vars) { + processModelVariable(var, model); + } + } + if (model.allVars != null) { + for (CodegenProperty var : model.allVars) { + processModelVariable(var, model); + } + } + if ((model.vars == null || model.vars.isEmpty()) && model.allVars != null && !model.allVars.isEmpty()) { + model.vars = new ArrayList<>(model.allVars); + } + } + } + } + + return processed; + } + + /** + * Processes individual model variables to configure C++ specific attributes. + * Handles nullable types, container types, and default values according to C++ conventions. + * + * @param var the model variable to process + * @param model the parent model containing this variable + */ + private void processModelVariable(CodegenProperty var, CodegenModel model) { + // Ensure baseName is set for JSON serialization + if (var.baseName == null || var.baseName.isEmpty()) { + var.baseName = var.name; + } + + //Handle getters and setters + if (var.getter != null) { + var.vendorExtensions.put("getter", var.getter); + var.vendorExtensions.put("getterType", var.datatypeWithEnum); + } + if (var.setter != null) { + var.vendorExtensions.put("setter", var.setter); + var.vendorExtensions.put("setterType", var.datatypeWithEnum); + } + // Handle nullable types + if (var.isNullable) { + var.vendorExtensions.put("isOptional", true); + // Don't wrap again if already wrapped + if (!var.dataType.startsWith("std::optional<")) { + // Store the inner type for the template + var.vendorExtensions.put("innerType", var.dataType); + var.dataType = "std::optional<" + var.dataType + ">"; + } + } else { + var.vendorExtensions.put("isOptional", false); + } + + // Handle container types + if (var.isArray && !var.dataType.startsWith("std::vector<")) { + String itemType = var.items != null ? var.items.dataType : "std::string"; + var.dataType = "std::vector<" + itemType + ">"; + } + + if (var.isMap && !var.dataType.startsWith("std::map<")) { + String itemType = var.items != null ? var.items.dataType : "std::string"; + var.dataType = "std::map"; + } + + // Set default values + if (var.defaultValue == null) { + var.defaultValue = getDefaultValueForType(var.dataType, var.isNullable); + } + } + + /** + * Generates appropriate default values for C++ types based on the data type and nullability. + * + * @param dataType the C++ data type + * @param isNullable whether the type is nullable + * @return the appropriate default value string + */ + private String getDefaultValueForType(String dataType, boolean isNullable) { + if (isNullable) { + return "std::nullopt"; + } + + if (dataType.equals("std::string")) { + return "\"\""; + } else if (dataType.equals("bool")) { + return "false"; + } else if (dataType.equals("int") || dataType.equals("long") || dataType.equals("float") || dataType.equals("double")) { + return "0"; + } else if (dataType.startsWith("std::vector<")) { + return "{}"; + } else if (dataType.startsWith("std::map<")) { + return "{}"; + } else if (dataType.startsWith("std::optional<")) { + return "std::nullopt"; + } + + return "{}"; + } + + /** + * Converts an OpenAPI model name to a C++ class name. + * Handles inline model prefixes and converts to PascalCase. + * + * @param name the original model name + * @return the C++ class name + */ + @Override + public String toModelName(String name) { + if (name == null) { + return "Model"; + } + + // Handle generic object names that need better naming + if (name.equals("object") || name.startsWith("object_") || name.contains("inline_object")) { + // Try to extract a number suffix for uniqueness + if (name.contains("_")) { + String[] parts = name.split("_"); + if (parts.length > 1 && parts[parts.length - 1].matches("\\d+")) { + return "InlineModel" + parts[parts.length - 1]; + } + } + return "InlineModel"; + } + + // Handle inline model names starting with underscore + if (name.startsWith("_")) { + name = name.substring(1); + } + + // Handle inline model names starting with "inline_" + if (name.startsWith("inline_")) { + name = name.substring(7); // Remove "inline_" prefix + } + + // Convert to PascalCase + return toPascalCase(sanitizeName(name)); + } + + /** + * Converts a model name to its corresponding filename. + * + * @param name the model name + * @return the filename without extension + */ + @Override + public String toModelFilename(String name) { + String fileName = (String) stripPathFromClassName(name).get("className"); + if (fileName == null || fileName.isEmpty()) { + fileName = "Model"; + } + return toPascalCase(fileName); + } + + /** + * Converts an API name to a C++ API class name. + * Handles default naming and path-based names. + * + * @param name the original API name + * @return the C++ API class name + */ + @Override + public String toApiName(String name) { + String apiName = name; + if ("Default".equals(apiName) || apiName == null || apiName.isEmpty()) { + apiName = addProjectOrDefaultName(apiName, "Default"); + } + else { + String[] parts = apiName.split("[/]"); + if(parts.length > 1) + { + apiName = toPascalCase(parts[1]); + } else { + apiName = toPascalCase(apiName); + } + } + return apiName; + } + + /** + * Converts an API name to its corresponding filename. + * + * @param name the API name + * @return the filename without extension + */ + @Override + public String toApiFilename(String name) { + String apiFileName = name; + if ("Default".equals(apiFileName) || apiFileName == null || apiFileName.isEmpty()) { + apiFileName = addProjectOrDefaultName(apiFileName, "Default"); + } else { + apiFileName = toPascalCase(name); + } + return apiFileName + toPascalCase(API_SUFFIX); + } + + @Override + @SuppressWarnings({"rawtypes", "unchecked"}) + public String getTypeDeclaration(Schema p) { + if (p.getOneOf() != null && !p.getOneOf().isEmpty()) { + // For oneOf, map to std::variant of possible types + StringBuilder variant = new StringBuilder("std::variant<"); + List schemas = p.getOneOf(); + for (Schema schema : schemas) { + variant.append(getTypeDeclaration(schema)).append(", "); + } + if (variant.length() > 0) { + variant.setLength(variant.length() - 2); // Remove last comma + } + variant.append(">"); + return variant.toString(); + } else if (p.getAnyOf() != null && !p.getAnyOf().isEmpty()) { + // Similar to oneOf, use std::any for anyOf + return "std::any"; + } else if (ModelUtils.isArraySchema(p)) { + // Handle arrays with full type declaration + String inner = getTypeDeclaration(ModelUtils.getSchemaItems(p)); + return "std::vector<" + inner + ">"; + } else if (ModelUtils.isMapSchema(p)) { + // Handle maps with full type declaration + String inner = getTypeDeclaration(ModelUtils.getAdditionalProperties(p)); + return "std::map"; + } else if (ModelUtils.isComposedSchema(p) && p.getAllOf() != null) { + // allOf is handled by composition in fromModel + return super.getTypeDeclaration(p); + } + return super.getTypeDeclaration(p); + } + + @Override + @SuppressWarnings({"rawtypes", "unchecked"}) + public CodegenModel fromModel(String name, Schema schema) { + // Use schema title if available for better naming + String modelName = name; + if (schema.getTitle() != null && !schema.getTitle().isEmpty()) { + modelName = schema.getTitle(); + } else if (name != null && (name.startsWith("object") || name.contains("inline_object"))) { + // Try to generate a better name for inline objects + } + + CodegenModel model = super.fromModel(modelName, schema); + + // Ensure the model name is properly set + if (model != null) { + model.name = toModelName(modelName); + model.classname = model.name; + + if (schema.getAllOf() != null && !schema.getAllOf().isEmpty()) { + // Add composition logic if needed (e.g., inherit from allOf schemas) + for (Schema allOfSchema : (List) schema.getAllOf()) { + if (allOfSchema.get$ref() != null) { + String parentRef = ModelUtils.getSimpleRef(allOfSchema.get$ref()); + model.parent = toModelName(parentRef); + break; + } + } + } + } + return model; + } + @Override + @SuppressWarnings("rawtypes") + public String toDefaultValue(Schema p) { + if (ModelUtils.isStringSchema(p) || ModelUtils.isDateSchema(p) || + ModelUtils.isDateTimeSchema(p) || ModelUtils.isByteArraySchema(p)) { + return p.getDefault() != null ? "\"" + p.getDefault().toString() + "\"" : "\"\""; + } + if (ModelUtils.isBooleanSchema(p)) { + return p.getDefault() != null ? p.getDefault().toString() : "false"; + } + if (ModelUtils.isIntegerSchema(p)) { + return p.getDefault() != null ? p.getDefault().toString() : "0"; + } + if (ModelUtils.isNumberSchema(p)) { + if (ModelUtils.isFloatSchema(p)) { + return p.getDefault() != null ? p.getDefault().toString() + "f" : "0.0f"; + } + return p.getDefault() != null ? p.getDefault().toString() : "0.0"; + } + if (ModelUtils.isArraySchema(p)) { + String inner = getTypeDeclaration(ModelUtils.getSchemaItems(p)); + return "std::vector<" + inner + ">()"; + } + if (ModelUtils.isMapSchema(p)) { + String inner = getTypeDeclaration(ModelUtils.getAdditionalProperties(p)); + return "std::map()"; + } + if (p.get$ref() != null && !p.get$ref().isEmpty()) { + return "std::make_shared<" + toModelName(ModelUtils.getSimpleRef(p.get$ref())) + ">()"; + } + return "nullptr"; + } + + /** + * Returns the directory path for generated model files. + * + * @return the model file directory path + */ + @Override + public String modelFileFolder() { + return (outputFolder + "/models").replace("/", File.separator); + } + + /** + * Returns the directory path for generated API files. + * + * @return the API file directory path + */ + @Override + public String apiFileFolder() { + return (outputFolder + "/api").replace("/", File.separator); + } + + + private Map stripPathFromClassName(String path) { + Object stripPathValue = additionalProperties.get("stripPathFromClassName"); + Boolean isStripPath = false; + if (stripPathValue instanceof Boolean) { + isStripPath = (Boolean) stripPathValue; + } else if (stripPathValue instanceof String) { + isStripPath = Boolean.parseBoolean((String) stripPathValue); + } + Map result = new HashMap<>(); + if (isStripPath != null && isStripPath) { + if (path == null || path.isEmpty()) { + result.put("namespace", ""); + result.put("className", "DefaultClass"); + return result; + } + if (path.contains("_error") || path.contains("_Error") || path.contains("error_") + || path.contains("Error_")) { + path = path.replace("_error", "").replace("_Error", "").replace("error_", "").replace("Error_", ""); + } + // Remove leading underscores + String clean = path.replaceAll("^_+", ""); + + // If the path is now empty or whitespace only, return default + if (clean.isEmpty() || clean.trim().isEmpty()) { + result.put("namespace", ""); + result.put("className", "DefaultClass"); + return result; + } + + // Split by "/", "_", "-", " ", camelCase boundary + String[] segments = clean.split("(?<=[a-z0-9])(?=[A-Z])|[^a-zA-Z0-9]"); + List parts = new ArrayList<>(); + for (String seg : segments) { + if (seg != null && !seg.trim().isEmpty()) { + parts.add(seg.trim()); + } + } + + if (parts.size() == 0) { + result.put("namespace", ""); + result.put("className", "DefaultClass"); + return result; + } + if (clean.toLowerCase().contains("error")) { + result.put("namespace", "error"); + result.put("className", toPascalCase(clean)); + return result; + } + + // ClassName is the rest, joined and PascalCased + StringBuilder classNameBuilder = new StringBuilder(); + String namespace = ""; + if (parts.size() > 1) { + // Use first part as namespace only for API class names, not models + namespace = toLowerCase(parts.get(0)); + for (int i = 1; i < parts.size(); i++) { + String pascalPart = toPascalCase(parts.get(i)); + if (pascalPart != null && !pascalPart.isEmpty()) { + classNameBuilder.append(pascalPart); + } + } + } else { + // Single part: use as class name + String pascalPart = toPascalCase(parts.get(0)); + if (pascalPart != null && !pascalPart.isEmpty()) { + classNameBuilder.append(pascalPart); + } + } + + // Ensure we have a valid class name + String finalClassName = classNameBuilder.toString(); + if (finalClassName.isEmpty()) { + finalClassName = "DefaultClass"; + } + + result.put("namespace", namespace); + result.put("className", finalClassName); + return result; + } else { + result.put("namespace",""); + result.put("className", toPascalCase(path)); + return result; + } + + } + + /** + * Adds project name to a class name or returns default name if project name is not set. + * + * @param name the base name + * @param defaultName the default name to use if input is null/empty + * @return the project-prefixed name or default name + */ + public String addProjectOrDefaultName(String name, String defaultName) { + String apiName = name; + if (name == null || name.isEmpty()) { + apiName = defaultName; + } + String projectName = (String) additionalProperties.get(PROJECT_NAME); + if (projectName != null && !projectName.isEmpty()) { + apiName = toPascalCase(projectName) + API_SUFFIX; + } + return apiName; + } + + /** + * Converts a model namespace string to proper format. + * + * @param modelNamespace the namespace to convert + * @return the formatted model namespace + */ + public String toModelNamespace(String modelNamespace) { + if (modelNamespace == null || modelNamespace.isEmpty()) { + return toPascalCase(MODEL_SUFFIX); + } + return modelNamespace; + } + + /** + * Converts a string to PascalCase formatting. + * Used for class names and type names. + * + * @param name the string to convert + * @return the PascalCase formatted string + */ + public String toPascalCase(String name) { + if(name == null || name.isEmpty()) { + return "DefaultClass"; + } + + String result = camelize(name, false); + return result.isEmpty() ? "DefaultClass" : result; + } + + /** + * Converts a string to camelCase formatting. + * Used for variable names and method names. + * + * @param name the string to convert + * @return the camelCase formatted string + */ + public String toCamelCase(String name) { + if(name == null || name.isEmpty()) { + return "defaultVariable"; + } + String result = camelize(name, true); + return result.isEmpty() ? "defaultVariable" : result; + } + + /** + * Converts a string to camelCase or PascalCase with proper case handling. + * Handles uppercase words like "POST" -> "Post" and "GET" -> "Get". + * + * @param name the string to convert + * @param lowercaseFirstLetter whether to lowercase the first letter (camelCase) + * @return the camelized string + */ + public String camelize(String name, boolean lowercaseFirstLetter) { + if (name == null || name.isEmpty()) { + return ""; + } + + // First, try the framework utility + String result; + if (lowercaseFirstLetter) { + result = org.openapitools.codegen.utils.StringUtils.camelize(name, CamelizeOption.LOWERCASE_FIRST_LETTER); + } else { + result = org.openapitools.codegen.utils.StringUtils.camelize(name); + } + + // If the result is the same as input and input is all uppercase, + if (result.equals(name) && name.equals(name.toUpperCase()) && name.matches("[A-Z]+")) { + // Handle all-caps words manually + result = name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); + if (lowercaseFirstLetter) { + result = result.substring(0, 1).toLowerCase() + result.substring(1); + } + } + + return result; + } + + /** + * Generates a handler function name from HTTP method and path. + * + * @param httpMethod the HTTP method (GET, POST, etc.) + * @param path the API path + * @return the generated handler function name + */ + public String toHandlerFunctionName(String httpMethod, String path) { + String method = toPascalCase(httpMethod); + String className = stripPathFromClassName(path).get("className"); + if (className == null || className.isEmpty()) { + className = "Default"; + } + return "handle" + method + "For" + className; + } + + /** + * Generates a handler function response type from the API path. + * + * @param http Method the HTTP method (GET, POST, etc.) + * @param path the API path + * @return the generated handler function name + */ + public String toHandlerFunctionResponse(String path) { + String className = stripPathFromClassName(path).get("className"); + if (className == null || className.isEmpty()) { + className = "Default"; + } + return toPascalCase(className + "Response"); + } + + /** + * Converts a string to lowercase. + * Used for namespace names and identifiers. + * + * @param name the string to convert + * @return the lowercase string + */ + public String toLowerCase(String name) { + return name == null? "" : name.toLowerCase(Locale.ROOT); + } + + /** + * Processes command line options and additional properties. + * Sets up default values for project configuration. + */ + @Override + public void processOpts() { + super.processOpts(); + + // Set default model package if not provided + if (modelPackage == null || modelPackage.isEmpty()) { + modelPackage = DEFAULT_PROJECT_NAME; + } + + // Get project name from additional properties + String projectName = DEFAULT_PROJECT_NAME; + if (additionalProperties.containsKey(PROJECT_NAME)) { + projectName = (String) additionalProperties.get(PROJECT_NAME); + } + + // Make package name available to templates + additionalProperties.put("packageName", modelPackage); + additionalProperties.put("projectName", projectName); + additionalProperties.put("cmakeProjectName", (String) StringUtils.underscore(projectName)); + + + // Set Enum namespace + String enumNamespace = (String) additionalProperties.get(ENUM_NAMESPACE); + if (enumNamespace == null || enumNamespace.isEmpty()) { + enumNamespace = toPascalCase(ENUM_SUFFIX); + } + additionalProperties.put(ENUM_NAMESPACE, enumNamespace); + + } + + /** + * Post-processes parameters to set up C++ specific configurations. + * Handles the required flag and parameter type conversions. + * + * @param parameter the parameter to process + */ + @Override + public void postProcessParameter(CodegenParameter parameter) { + super.postProcessParameter(parameter); + + // Set vendor extensions for template usage + parameter.vendorExtensions.put("isRequired", parameter.required); + parameter.vendorExtensions.put("isOptional", !parameter.required); + + // Handle nullable parameters for C++ + if (!parameter.required) { + // For non-required parameters, we can make them optional in C++ + if (!parameter.dataType.startsWith("std::optional<") && !parameter.isContainer) { + parameter.vendorExtensions.put("optionalType", "std::optional<" + parameter.dataType + ">"); + } + } + } +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig index 1d48c0c0d66c..9e3e01a3e454 100644 --- a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig +++ b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig @@ -153,10 +153,10 @@ org.openapitools.codegen.languages.TypeScriptFetchClientCodegen org.openapitools.codegen.languages.TypeScriptInversifyClientCodegen org.openapitools.codegen.languages.TypeScriptJqueryClientCodegen org.openapitools.codegen.languages.TypeScriptNestjsClientCodegen -org.openapitools.codegen.languages.TypeScriptNestjsServerCodegen org.openapitools.codegen.languages.TypeScriptNodeClientCodegen org.openapitools.codegen.languages.TypeScriptReduxQueryClientCodegen org.openapitools.codegen.languages.TypeScriptRxjsClientCodegen org.openapitools.codegen.languages.WsdlSchemaCodegen org.openapitools.codegen.languages.XojoClientCodegen org.openapitools.codegen.languages.ZapierClientCodegen +org.openapitools.codegen.languages.CppHttplibServerCodegen diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/CMakeLists.txt.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/CMakeLists.txt.mustache new file mode 100644 index 000000000000..50f40c65ef11 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/CMakeLists.txt.mustache @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.10) +project({{cmakeProjectName}} LANGUAGES CXX) + +set(TARGET_NAME {{cmakeProjectName}}_openapi_lib) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +file(GLOB API_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/api/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/api/*.cpp +) +file(GLOB MODEL_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/models/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/models/*.cpp +) +add_library(${TARGET_NAME} ${API_SRCS} ${MODEL_SRCS}) +target_include_directories (${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +# Make sure these libraries/headers are available in the build environment before linking +# Required libraries/headers are httplib,ssl,nlohmann::json +target_link_libraries(${TARGET_NAME} httplib ssl nlohmann_json::nlohmann_json) \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/README.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/README.mustache new file mode 100644 index 000000000000..7f9008281b4b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/README.mustache @@ -0,0 +1,160 @@ +# {{projectName}} - C++ Server + +## Overview + +This server was generated using the [OpenAPI Generator](https://openapi-generator.tech) project. +It uses the [cpp-httplib](https://github.com/yhirose/cpp-httplib) library to implement a lightweight HTTP server +with JSON request/response handling via [nlohmann/json](https://github.com/nlohmann/json). + +## Requirements + +- C++17 compatible compiler +- [cpp-httplib](https://github.com/yhirose/cpp-httplib) library +- [nlohmann/json](https://github.com/nlohmann/json) library +- CMake (3.14 or higher) + +## Project Structure + +``` +├── CMakeLists.txt # Project build configuration +├── README.md # This file +├── model/ # Generated model classes +└── api/ # Generated API handler classes +``` + +## Building the Project + +```bash +mkdir build +cd build +cmake .. +make +``` + +## Working with Models + +### Model Classes + +{{#models}} +{{#model}} +#### {{vendorExtensions.modelNamespace}}::{{vendorExtensions.modelClassName}} + +```cpp +// Create a model +auto model = {{vendorExtensions.modelNamespace}}::{{vendorExtensions.modelClassName}}(); +{{#vars}} +model.{{vendorExtensions.setter}}(/* value */); // Set {{baseName}} +{{/vars}} + +// Serialize to JSON +nlohmann::json json = {{vendorExtensions.modelNamespace}}::{{vendorExtensions.modelClassName}}::toJson(model); +std::string jsonString = json.dump(); + +// Deserialize from JSON +auto parsedModel = {{vendorExtensions.modelNamespace}}::{{vendorExtensions.modelClassName}}::fromJson(nlohmann::json::parse(jsonString)); +``` +{{/model}} +{{/models}} + +## Implementing API Handlers + +### API Classes + +{{#apiInfo}} +{{#apis}} +{{#operations}} +#### {{classname}} + +To implement this API, create a class that inherits from the generated base class: + +```cpp +class {{classname}}Impl : public {{apiNamespace}}::{{classname}} { +public: + {{#operation}} + {{#vendorExtensions}} + {{handlerFunctionResponse}} {{handlerFunctionName}}({{#requestModel}}const {{requestModel}}& request{{/requestModel}}) override { + // Implement your logic here + {{#requestModel}} + // Access request data with request.getters + {{/requestModel}} + {{^requestModel}} + // Access request parameters with req.get_param_value("param_name") + {{/requestModel}} + + // For successful response: + return {{successType}}(); + + // For error responses: + {{#statusCodeToTypes}} + // return {{typeName}}(); // Returns a {{statusCode}} status code + {{/statusCodeToTypes}} + } + + {{/vendorExtensions}} + {{/operation}} +}; +``` +{{/operations}} +{{/apis}} +{{/apiInfo}} + +## Running the Server + +```cpp +#include +#include "api/YourApiImpl.h" + +int main() { + // Create server + auto svr = std::make_unique(); + + // Create API implementation + auto Impl = std::make_shared(); + + // Register routes + Impl->RegisterRoutes(std::move(svr)); + + // Start server on port 8080 + svr->listen("localhost", 8080); + + return 0; +} +``` + +## Error Handling + +Each API endpoint returns a variant type that can hold either a success response or one of several error responses. +The server automatically handles this variant and returns the appropriate HTTP status code. + +## Working with Optional Fields + +Optional parameters and model fields are represented using `std::optional`: + +```cpp +if (model.getOptionalField()) { + // Field is present + auto value = *model.getOptionalField(); +} else { + // Field is not present +} +``` + +## Additional Resources + +- [cpp-httplib Documentation](https://github.com/yhirose/cpp-httplib) +- [nlohmann/json Documentation](https://github.com/nlohmann/json) +- [OpenAPI Generator Documentation](https://openapi-generator.tech/docs/generators/) +```cpp +if (model.getOptionalField()) { + // Field is present + auto value = *model.getOptionalField(); +} else { + // Field is not present +} +``` + +## Additional Resources + +- [cpp-httplib Documentation](https://github.com/yhirose/cpp-httplib) +- [nlohmann/json Documentation](https://github.com/nlohmann/json) +- [OpenAPI Generator Documentation](https://openapi-generator.tech/docs/generators/) diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/api-header.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/api-header.mustache new file mode 100644 index 000000000000..cbf4b86521d8 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/api-header.mustache @@ -0,0 +1,71 @@ +{{#licenseHeader}} +{{{licenseHeader}}} +{{/licenseHeader}} +{{^licenseHeader}} +/* + * Generated by OpenAPI Generator, do not edit. + * https://openapi-generator.tech + */ +{{/licenseHeader}} + +#pragma once +#include +#include +{{#modelsUsed}} +#include "models/{{.}}.h" +{{/modelsUsed}} + +namespace {{apiNamespace}} { + +class {{apiClassnameInPascalCase}} { +public: + {{apiClassnameInPascalCase}}() = default; + virtual ~{{apiClassnameInPascalCase}}() = default; + void registerRoutes(httplib::Server& svr); + {{#operations}} + {{#operation}} + {{#vendorExtensions}} + {{#hasAnyResponseSchema}} + /** + * @brief Response type for {{handlerFunctionName}}. + */ + using {{handlerFunctionResponse}} = std::variant< + {{#successType}} + {{successType}}{{#errorTypes}}{{#-first}},{{/-first}}{{/errorTypes}}{{^errorTypes}}>;{{/errorTypes}}//success type + {{/successType}} + {{#errorTypes}} + {{.}}{{#-last}}>;{{/-last}}{{^-last}},{{/-last}}{{#-first}}// error types{{/-first}} + {{/errorTypes}} + + /** + * @brief Handler function for processing API requests. + * + * {{handlerFunctionName}} is a pure virtual function that must be implemented by derived classes to handle. + {{#requestModel}} + * @param request model containing the input data for the API endpoint. + {{/requestModel}} + * @return {{handlerFunctionResponse}} The response type returned by the handler. + */ + virtual {{handlerFunctionResponse}} {{handlerFunctionName}}({{^requestModel}})=0;{{/requestModel}} +{{#requestModel}} const {{requestModel}}& request) = 0;{{/requestModel}} + {{/hasAnyResponseSchema}} + {{^hasAnyResponseSchema}} + + /** + * @brief {{handlerFunctionName}} is a Pure virtual handler function for API endpoint. + * + * This function must be implemented by derived classes to handle the corresponding API request. + {{#requestModel}} + * @param "{{requestModel}}" The request model containing the input data for the API endpoint. + {{/requestModel}} + */ + virtual void {{handlerFunctionName}}({{^requestModel}})=0;{{/requestModel}} +{{#requestModel}} const {{requestModel}}& request) = 0;{{/requestModel}} + {{/hasAnyResponseSchema}} + + {{/vendorExtensions}} + {{/operation}} + {{/operations}} +}; + +} // namespace {{apiNamespace}} diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/api-source.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/api-source.mustache new file mode 100644 index 000000000000..5ce51247699f --- /dev/null +++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/api-source.mustache @@ -0,0 +1,78 @@ +{{#licenseHeader}} +{{{licenseHeader}}} +{{/licenseHeader}} +{{^licenseHeader}} +/* + * Generated by OpenAPI Generator, do not edit + * https://openapi-generator.tech + */ +{{/licenseHeader}} + +{{{apiHeaderFileName}}} + +namespace {{apiNamespace}} { + +void {{apiClassnameInPascalCase}}::registerRoutes(httplib::Server& svr) { + {{#operations}} + {{#operation}} + {{#vendorExtensions}} + svr.{{httpMethod}}("{{path}}", [this](const httplib::Request& req, httplib::Response& res) { + {{#hasAnyResponseSchema}} + try { + {{#requestModel}} + //request has schema in json, Hence parsing the request body to the model + {{requestModel}} request = {{requestModel}}::fromJson(nlohmann::json::parse(req.body)); + auto result = {{handlerFunctionName}}(request); + {{/requestModel}} + {{^requestModel}} + auto result = {{handlerFunctionName}}(); + {{/requestModel}} + + std::visit([&](const auto& value) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + res.status = 200; + res.set_content(value.toJson(value).dump(), "application/json"); + } + {{#statusCodeToTypes}} + else if constexpr (std::is_same_v) { + res.status = {{statusCode}}; + res.set_content(value.toJson(value).dump(), "application/json"); + } + {{/statusCodeToTypes}} + }, + static_cast(result)); + } catch (const nlohmann::json::parse_error& e) + { + res.status = 400; + nlohmann::json errorJson = { + {"message", "Invalid JSON: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } catch (const std::exception& e) + { + res.status = 500; + nlohmann::json errorJson = { + {"message", "Internal Server Error: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } + {{/hasAnyResponseSchema}} + {{^hasAnyResponseSchema}} + {{#requestModel}} + //request has schema in json, Hence parsing the request body to the model + {{requestModel}} request = {{requestModel}}::fromJson(nlohmann::json::parse(req.body)); + {{handlerFunctionName}}(request); + {{/requestModel}} + {{^requestModel}} + {{handlerFunctionName}}(); + {{/requestModel}} + {{/hasAnyResponseSchema}} + }); + {{/vendorExtensions}} + + {{/operation}} + {{/operations}} +} + +} // namespace {{apiNamespace}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/CMakeLists.txt.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/CMakeLists.txt.mustache new file mode 100644 index 000000000000..50f40c65ef11 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/CMakeLists.txt.mustache @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.10) +project({{cmakeProjectName}} LANGUAGES CXX) + +set(TARGET_NAME {{cmakeProjectName}}_openapi_lib) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +file(GLOB API_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/api/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/api/*.cpp +) +file(GLOB MODEL_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/models/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/models/*.cpp +) +add_library(${TARGET_NAME} ${API_SRCS} ${MODEL_SRCS}) +target_include_directories (${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +# Make sure these libraries/headers are available in the build environment before linking +# Required libraries/headers are httplib,ssl,nlohmann::json +target_link_libraries(${TARGET_NAME} httplib ssl nlohmann_json::nlohmann_json) \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/License.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/License.mustache new file mode 100644 index 000000000000..286685933869 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/License.mustache @@ -0,0 +1,5 @@ +/** +* This file is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/README.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/README.mustache new file mode 100644 index 000000000000..7f9008281b4b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/README.mustache @@ -0,0 +1,160 @@ +# {{projectName}} - C++ Server + +## Overview + +This server was generated using the [OpenAPI Generator](https://openapi-generator.tech) project. +It uses the [cpp-httplib](https://github.com/yhirose/cpp-httplib) library to implement a lightweight HTTP server +with JSON request/response handling via [nlohmann/json](https://github.com/nlohmann/json). + +## Requirements + +- C++17 compatible compiler +- [cpp-httplib](https://github.com/yhirose/cpp-httplib) library +- [nlohmann/json](https://github.com/nlohmann/json) library +- CMake (3.14 or higher) + +## Project Structure + +``` +├── CMakeLists.txt # Project build configuration +├── README.md # This file +├── model/ # Generated model classes +└── api/ # Generated API handler classes +``` + +## Building the Project + +```bash +mkdir build +cd build +cmake .. +make +``` + +## Working with Models + +### Model Classes + +{{#models}} +{{#model}} +#### {{vendorExtensions.modelNamespace}}::{{vendorExtensions.modelClassName}} + +```cpp +// Create a model +auto model = {{vendorExtensions.modelNamespace}}::{{vendorExtensions.modelClassName}}(); +{{#vars}} +model.{{vendorExtensions.setter}}(/* value */); // Set {{baseName}} +{{/vars}} + +// Serialize to JSON +nlohmann::json json = {{vendorExtensions.modelNamespace}}::{{vendorExtensions.modelClassName}}::toJson(model); +std::string jsonString = json.dump(); + +// Deserialize from JSON +auto parsedModel = {{vendorExtensions.modelNamespace}}::{{vendorExtensions.modelClassName}}::fromJson(nlohmann::json::parse(jsonString)); +``` +{{/model}} +{{/models}} + +## Implementing API Handlers + +### API Classes + +{{#apiInfo}} +{{#apis}} +{{#operations}} +#### {{classname}} + +To implement this API, create a class that inherits from the generated base class: + +```cpp +class {{classname}}Impl : public {{apiNamespace}}::{{classname}} { +public: + {{#operation}} + {{#vendorExtensions}} + {{handlerFunctionResponse}} {{handlerFunctionName}}({{#requestModel}}const {{requestModel}}& request{{/requestModel}}) override { + // Implement your logic here + {{#requestModel}} + // Access request data with request.getters + {{/requestModel}} + {{^requestModel}} + // Access request parameters with req.get_param_value("param_name") + {{/requestModel}} + + // For successful response: + return {{successType}}(); + + // For error responses: + {{#statusCodeToTypes}} + // return {{typeName}}(); // Returns a {{statusCode}} status code + {{/statusCodeToTypes}} + } + + {{/vendorExtensions}} + {{/operation}} +}; +``` +{{/operations}} +{{/apis}} +{{/apiInfo}} + +## Running the Server + +```cpp +#include +#include "api/YourApiImpl.h" + +int main() { + // Create server + auto svr = std::make_unique(); + + // Create API implementation + auto Impl = std::make_shared(); + + // Register routes + Impl->RegisterRoutes(std::move(svr)); + + // Start server on port 8080 + svr->listen("localhost", 8080); + + return 0; +} +``` + +## Error Handling + +Each API endpoint returns a variant type that can hold either a success response or one of several error responses. +The server automatically handles this variant and returns the appropriate HTTP status code. + +## Working with Optional Fields + +Optional parameters and model fields are represented using `std::optional`: + +```cpp +if (model.getOptionalField()) { + // Field is present + auto value = *model.getOptionalField(); +} else { + // Field is not present +} +``` + +## Additional Resources + +- [cpp-httplib Documentation](https://github.com/yhirose/cpp-httplib) +- [nlohmann/json Documentation](https://github.com/nlohmann/json) +- [OpenAPI Generator Documentation](https://openapi-generator.tech/docs/generators/) +```cpp +if (model.getOptionalField()) { + // Field is present + auto value = *model.getOptionalField(); +} else { + // Field is not present +} +``` + +## Additional Resources + +- [cpp-httplib Documentation](https://github.com/yhirose/cpp-httplib) +- [nlohmann/json Documentation](https://github.com/nlohmann/json) +- [OpenAPI Generator Documentation](https://openapi-generator.tech/docs/generators/) diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/api-header.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/api-header.mustache new file mode 100644 index 000000000000..a279b64df642 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/api-header.mustache @@ -0,0 +1,63 @@ +{{>License}} + +#pragma once +#include +#include +{{#modelsUsed}} +#include "models/{{.}}.h" +{{/modelsUsed}} + +namespace {{apiNamespace}} { + +class {{apiClassnameInPascalCase}} { +public: + {{apiClassnameInPascalCase}}() = default; + virtual ~{{apiClassnameInPascalCase}}() = default; + void registerRoutes(httplib::Server& svr); + {{#operations}} + {{#operation}} + {{#vendorExtensions}} + {{#hasAnyResponseSchema}} + /** + * @brief Response type for {{handlerFunctionName}}. + */ + using {{handlerFunctionResponse}} = std::variant< + {{#successType}} + {{successType}}{{#errorTypes}}{{#-first}},{{/-first}}{{/errorTypes}}{{^errorTypes}}>;{{/errorTypes}}//success type + {{/successType}} + {{#errorTypes}} + {{.}}{{#-last}}>;{{/-last}}{{^-last}},{{/-last}}{{#-first}}// error types{{/-first}} + {{/errorTypes}} + + /** + * @brief Handler function for processing API requests. + * + * {{handlerFunctionName}} is a pure virtual function that must be implemented by derived classes to handle. + {{#requestModel}} + * @param request model containing the input data for the API endpoint. + {{/requestModel}} + * @return {{handlerFunctionResponse}} The response type returned by the handler. + */ + virtual {{handlerFunctionResponse}} {{handlerFunctionName}}({{^requestModel}})=0;{{/requestModel}} +{{#requestModel}} const {{requestModel}}& request) = 0;{{/requestModel}} + {{/hasAnyResponseSchema}} + {{^hasAnyResponseSchema}} + + /** + * @brief {{handlerFunctionName}} is a Pure virtual handler function for API endpoint. + * + * This function must be implemented by derived classes to handle the corresponding API request. + {{#requestModel}} + * @param "{{requestModel}}" The request model containing the input data for the API endpoint. + {{/requestModel}} + */ + virtual void {{handlerFunctionName}}({{^requestModel}})=0;{{/requestModel}} +{{#requestModel}} const {{requestModel}}& request) = 0;{{/requestModel}} + {{/hasAnyResponseSchema}} + + {{/vendorExtensions}} + {{/operation}} + {{/operations}} +}; + +} // namespace {{apiNamespace}} diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/api-source.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/api-source.mustache new file mode 100644 index 000000000000..b05098c51d0d --- /dev/null +++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/api-source.mustache @@ -0,0 +1,70 @@ +{{>License}} + +{{{apiHeaderFileName}}} + +namespace {{apiNamespace}} { + +void {{apiClassnameInPascalCase}}::registerRoutes(httplib::Server& svr) { + {{#operations}} + {{#operation}} + {{#vendorExtensions}} + svr.{{httpMethod}}("{{path}}", [this]([[maybe_unused]]const httplib::Request& req, httplib::Response& res) { + {{#hasAnyResponseSchema}} + try { + {{#requestModel}} + //request has schema in json, Hence parsing the request body to the model + {{requestModel}} request = {{requestModel}}::fromJson(nlohmann::json::parse(req.body)); + auto result = {{handlerFunctionName}}(request); + {{/requestModel}} + {{^requestModel}} + auto result = {{handlerFunctionName}}(); + {{/requestModel}} + + std::visit([&](const auto& value) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + res.status = 200; + res.set_content(value.toJson(value).dump(), "application/json"); + } + {{#statusCodeToTypes}} + else if constexpr (std::is_same_v) { + res.status = {{statusCode}}; + res.set_content(value.toJson(value).dump(), "application/json"); + } + {{/statusCodeToTypes}} + }, + static_cast(result)); + } catch (const nlohmann::json::parse_error& e) + { + res.status = 400; + nlohmann::json errorJson = { + {"message", "Invalid JSON: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } catch (const std::exception& e) + { + res.status = 500; + nlohmann::json errorJson = { + {"message", "Internal Server Error: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } + {{/hasAnyResponseSchema}} + {{^hasAnyResponseSchema}} + {{#requestModel}} + //request has schema in json, Hence parsing the request body to the model + {{requestModel}} request = {{requestModel}}::fromJson(nlohmann::json::parse(req.body)); + {{handlerFunctionName}}(request); + {{/requestModel}} + {{^requestModel}} + {{handlerFunctionName}}(); + {{/requestModel}} + {{/hasAnyResponseSchema}} + }); + {{/vendorExtensions}} + + {{/operation}} + {{/operations}} +} + +} // namespace {{apiNamespace}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/model-header.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/model-header.mustache new file mode 100644 index 000000000000..a7b3f4cfd93a --- /dev/null +++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/model-header.mustache @@ -0,0 +1,47 @@ +{{>License}} + +#pragma once +{{#models}} +{{#model}} +#include + +{{#vendorExtensions.filteredImports}} +{{.}} +{{/vendorExtensions.filteredImports}} + +{{#vars}} +{{#isPrimitiveType}} +{{#dataType}} +#include <{{#isNumeric}}cstdint{{/isNumeric}}{{#isString}}string{{/isString}}> +{{/dataType}} +{{/isPrimitiveType}} +{{/vars}} + +namespace {{vendorExtensions.modelNamespace}} { + +class {{vendorExtensions.modelClassName}} +{ +public: + {{vendorExtensions.modelClassName}}(); + virtual ~{{vendorExtensions.modelClassName}}() = default; + + // JSON serialization + static nlohmann::json toJson(const {{vendorExtensions.modelClassName}}& obj); + static {{vendorExtensions.modelClassName}} fromJson(const nlohmann::json& json); + + // Getters and setters + {{#vars}} + [[nodiscard]] {{{vendorExtensions.getterType}}} {{vendorExtensions.getter}}() const; + void {{vendorExtensions.setter}}(const {{{vendorExtensions.setterType}}}& {{nameInCamelCase}}); + {{/vars}} + +private: + // Member variables + {{#vars}} + {{{datatypeWithEnum}}} m_{{nameInCamelCase}}; + {{/vars}} +}; + +} // namespace {{vendorExtensions.modelNamespace}} +{{/model}} +{{/models}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/model-source.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/model-source.mustache new file mode 100644 index 000000000000..40315558fe5c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/cpp-httplib-server/model-source.mustache @@ -0,0 +1,62 @@ +{{>License}} + +{{#models}} +{{#model}} +#include "{{vendorExtensions.modelClassName}}.h" + +namespace {{vendorExtensions.modelNamespace}} { + +{{vendorExtensions.modelClassName}}::{{vendorExtensions.modelClassName}}() +{{#vars}} +{{#-first}}:{{/-first}} m_{{nameInCamelCase}}({{{defaultValue}}}){{^-last}},{{/-last}} +{{/vars}} +{ +} + +nlohmann::json {{vendorExtensions.modelClassName}}::toJson(const {{vendorExtensions.modelClassName}}& obj) +{ + nlohmann::json json; + {{#vars}} + {{#isPrimitiveType}} + json["{{baseName}}"] = obj.{{getter}}(); + {{/isPrimitiveType}} + {{^isPrimitiveType}} + json["{{baseName}}"] = obj.{{getter}}(); + {{/isPrimitiveType}} + {{/vars}} + return json; +} + +{{vendorExtensions.modelClassName}} {{vendorExtensions.modelClassName}}::fromJson(const nlohmann::json& json) +{ + {{vendorExtensions.modelClassName}} obj; + {{#vars}} + if (json.contains("{{baseName}}")) + { + {{#isPrimitiveType}} + obj.{{setter}}(json.at("{{baseName}}").get<{{datatypeWithEnum}}>()); + {{/isPrimitiveType}} + {{^isPrimitiveType}} + obj.{{setter}}(json.at("{{baseName}}")); + {{/isPrimitiveType}} + } + {{/vars}} + return obj; +} + + +{{#vars}} +{{{vendorExtensions.getterType}}} {{model.vendorExtensions.modelClassName}}::{{vendorExtensions.getter}}() const +{ + return m_{{nameInCamelCase}}; +} + +void {{model.vendorExtensions.modelClassName}}::{{vendorExtensions.setter}}(const {{{vendorExtensions.setterType}}}& {{nameInCamelCase}}) +{ + m_{{nameInCamelCase}} = {{nameInCamelCase}}; +} +{{/vars}} + +} // namespace {{vendorExtensions.modelNamespace}} +{{/model}} +{{/models}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/model-header.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/model-header.mustache new file mode 100644 index 000000000000..874f4f2fc175 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/model-header.mustache @@ -0,0 +1,55 @@ +{{#licenseHeader}} +{{{licenseHeader}}} +{{/licenseHeader}} +{{^licenseHeader}} +/* + * Generated by OpenAPI Generator, do not edit. + * https://openapi-generator.tech + */ +{{/licenseHeader}} + +#pragma once +{{#models}} +{{#model}} +#include + +{{#vendorExtensions.filteredImports}} +{{.}} +{{/vendorExtensions.filteredImports}} + +{{#vars}} +{{#isPrimitiveType}} +{{#dataType}} +#include <{{#isNumeric}}cstdint{{/isNumeric}}{{#isString}}string{{/isString}}> +{{/dataType}} +{{/isPrimitiveType}} +{{/vars}} + +namespace {{vendorExtensions.modelNamespace}} { + +class {{vendorExtensions.modelClassName}} +{ +public: + {{vendorExtensions.modelClassName}}(); + virtual ~{{vendorExtensions.modelClassName}}() = default; + + // JSON serialization + static nlohmann::json toJson(const {{vendorExtensions.modelClassName}}& obj); + static {{vendorExtensions.modelClassName}} fromJson(const nlohmann::json& json); + + // Getters and setters + {{#vars}} + [[nodiscard]] {{{vendorExtensions.getterType}}} {{vendorExtensions.getter}}() const; + void {{vendorExtensions.setter}}(const {{{vendorExtensions.setterType}}}& {{nameInCamelCase}}); + {{/vars}} + +private: + // Member variables + {{#vars}} + {{{datatypeWithEnum}}} m_{{nameInCamelCase}}; + {{/vars}} +}; + +} // namespace {{vendorExtensions.modelNamespace}} +{{/model}} +{{/models}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/model-source.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/model-source.mustache new file mode 100644 index 000000000000..6b243a219714 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/model-source.mustache @@ -0,0 +1,70 @@ +{{#licenseHeader}} +{{{licenseHeader}}} +{{/licenseHeader}} +{{^licenseHeader}} +/* + * Generated by OpenAPI Generator, do not edit. + * https://openapi-generator.tech + */ +{{/licenseHeader}} + +{{#models}} +{{#model}} +#include "{{vendorExtensions.modelClassName}}.h" + +namespace {{vendorExtensions.modelNamespace}} { + +{{vendorExtensions.modelClassName}}::{{vendorExtensions.modelClassName}}() +{{#vars}} +{{#-first}}:{{/-first}} m_{{nameInCamelCase}}({{{defaultValue}}}){{^-last}},{{/-last}} +{{/vars}} +{ +} + +nlohmann::json {{vendorExtensions.modelClassName}}::toJson(const {{vendorExtensions.modelClassName}}& obj) +{ + nlohmann::json json; + {{#vars}} + {{#isPrimitiveType}} + json["{{baseName}}"] = obj.{{getter}}(); + {{/isPrimitiveType}} + {{^isPrimitiveType}} + json["{{baseName}}"] = obj.{{getter}}(); + {{/isPrimitiveType}} + {{/vars}} + return json; +} + +{{vendorExtensions.modelClassName}} {{vendorExtensions.modelClassName}}::fromJson(const nlohmann::json& json) +{ + {{vendorExtensions.modelClassName}} obj; + {{#vars}} + if (json.contains("{{baseName}}")) + { + {{#isPrimitiveType}} + obj.{{setter}}(json.at("{{baseName}}").get<{{datatypeWithEnum}}>()); + {{/isPrimitiveType}} + {{^isPrimitiveType}} + obj.{{setter}}(json.at("{{baseName}}")); + {{/isPrimitiveType}} + } + {{/vars}} + return obj; +} + + +{{#vars}} +{{{vendorExtensions.getterType}}} {{model.vendorExtensions.modelClassName}}::{{vendorExtensions.getter}}() const +{ + return m_{{nameInCamelCase}}; +} + +void {{model.vendorExtensions.modelClassName}}::{{vendorExtensions.setter}}(const {{{vendorExtensions.setterType}}}& {{nameInCamelCase}}) +{ + m_{{nameInCamelCase}} = {{nameInCamelCase}}; +} +{{/vars}} + +} // namespace {{vendorExtensions.modelNamespace}} +{{/model}} +{{/models}} \ No newline at end of file diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/cpphttplib/CppHttplibServerCodegenModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/cpphttplib/CppHttplibServerCodegenModelTest.java new file mode 100644 index 000000000000..d6df30dbb57b --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/cpphttplib/CppHttplibServerCodegenModelTest.java @@ -0,0 +1,129 @@ +package org.openapitools.codegen.cpphttplib; + +import org.openapitools.codegen.*; +import org.openapitools.codegen.languages.CppHttplibServerCodegen; +import io.swagger.v3.oas.models.media.*; +import org.testng.Assert; +import org.testng.annotations.Test; + +@SuppressWarnings("static-method") +public class CppHttplibServerCodegenModelTest { + + @Test(description = "convert a simple model with properties") + public void simpleModelTest() { + final CppHttplibServerCodegen codegen = new CppHttplibServerCodegen(); + codegen.processOpts(); + + // Create a simple schema with properties + ObjectSchema schema = new ObjectSchema(); + schema.description("a sample model"); + schema.addProperty("id", new IntegerSchema().format("int64")); + schema.addProperty("name", new StringSchema()); + schema.addProperty("isActive", new BooleanSchema()); + schema.setRequired(java.util.Arrays.asList("id", "name")); + + final CodegenModel model = codegen.fromModel("User", schema); + + Assert.assertEquals(model.name, "User"); + Assert.assertEquals(model.classname, "User"); + Assert.assertNotNull(model.vars); + Assert.assertEquals(model.vars.size(), 3); + + // Check id property + CodegenProperty idProp = model.vars.get(0); + Assert.assertEquals(idProp.name, "Id"); + Assert.assertEquals(idProp.baseName, "id"); + Assert.assertEquals(idProp.dataType, "long"); + Assert.assertTrue(idProp.required); + + // Check name property + CodegenProperty nameProp = model.vars.get(1); + Assert.assertEquals(nameProp.name, "Name"); + Assert.assertEquals(nameProp.baseName, "name"); + Assert.assertEquals(nameProp.dataType, "std::string"); + Assert.assertTrue(nameProp.required); + + // Check isActive property + CodegenProperty activeProp = model.vars.get(2); + Assert.assertEquals(activeProp.name, "IsActive"); + Assert.assertEquals(activeProp.baseName, "isActive"); + Assert.assertEquals(activeProp.dataType, "bool"); + Assert.assertFalse(activeProp.required); + } + + @Test(description = "convert model with array property") + public void arrayPropertyTest() { + final CppHttplibServerCodegen codegen = new CppHttplibServerCodegen(); + codegen.processOpts(); + + ObjectSchema schema = new ObjectSchema(); + schema.addProperty("tags", new ArraySchema().items(new StringSchema())); + + final CodegenModel model = codegen.fromModel("ModelWithArray", schema); + + Assert.assertEquals(model.vars.size(), 1); + CodegenProperty arrayProp = model.vars.get(0); + Assert.assertEquals(arrayProp.baseName, "tags"); + Assert.assertEquals(arrayProp.dataType, "std::vector"); + Assert.assertTrue(arrayProp.isArray); + } + + @Test(description = "convert model with map property") + public void mapPropertyTest() { + final CppHttplibServerCodegen codegen = new CppHttplibServerCodegen(); + codegen.processOpts(); + + ObjectSchema schema = new ObjectSchema(); + schema.addProperty("metadata", new MapSchema().additionalProperties(new StringSchema())); + + final CodegenModel model = codegen.fromModel("ModelWithMap", schema); + + Assert.assertEquals(model.vars.size(), 1); + CodegenProperty mapProp = model.vars.get(0); + Assert.assertEquals(mapProp.name, "Metadata"); + Assert.assertEquals(mapProp.dataType, "std::map"); + Assert.assertTrue(mapProp.isMap); + } + + @Test(description = "convert enum model") + public void enumModelTest() { + final CppHttplibServerCodegen codegen = new CppHttplibServerCodegen(); + codegen.processOpts(); + + StringSchema enumSchema = new StringSchema(); + enumSchema.setEnum(java.util.Arrays.asList("ACTIVE", "INACTIVE", "PENDING")); + + final CodegenModel model = codegen.fromModel("Status", enumSchema); + + // Note: The C++ httplib server generator may not process enum-only models + // in the same way as regular object models. The model might be null or empty. + if (model != null) { + Assert.assertEquals(model.name, "Status"); + // Check if it's marked as an enum in vendor extensions + if (model.vendorExtensions.containsKey("x-is-enum")) { + Assert.assertEquals(model.vendorExtensions.get("x-is-enum"), true); + } + } + } + + @Test(description = "convert model with nullable property") + public void nullablePropertyTest() { + final CppHttplibServerCodegen codegen = new CppHttplibServerCodegen(); + codegen.processOpts(); + + StringSchema nullableStringSchema = new StringSchema(); + nullableStringSchema.setNullable(true); + + ObjectSchema schema = new ObjectSchema(); + schema.addProperty("optionalField", nullableStringSchema); + + final CodegenModel model = codegen.fromModel("ModelWithNullable", schema); + + Assert.assertEquals(model.vars.size(), 1); + CodegenProperty nullableProp = model.vars.get(0); + Assert.assertEquals(nullableProp.name, "OptionalField"); + Assert.assertTrue(nullableProp.isNullable); + Assert.assertEquals(nullableProp.dataType, "std::string"); + } +} + diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/cpphttplib/CppHttplibServerCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/cpphttplib/CppHttplibServerCodegenTest.java new file mode 100644 index 000000000000..056121c735b7 --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/cpphttplib/CppHttplibServerCodegenTest.java @@ -0,0 +1,97 @@ +package org.openapitools.codegen.cpphttplib; + +import static org.openapitools.codegen.utils.StringUtils.underscore; + +import org.openapitools.codegen.*; +import org.openapitools.codegen.languages.CppHttplibServerCodegen; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class CppHttplibServerCodegenTest { + + CppHttplibServerCodegen codegen = new CppHttplibServerCodegen(); + + @Test + public void testInitialConfigValues() throws Exception { + codegen.processOpts(); + + Assert.assertEquals(codegen.additionalProperties().get("projectName"), "cpp-httplib-server"); + Assert.assertEquals(codegen.modelPackage(), "cpp-httplib-server"); + Assert.assertEquals(codegen.additionalProperties().get("cmakeProjectName"), "cpp_httplib_server"); + } + + @Test + public void testSettersForConfigValues() throws Exception { + codegen.additionalProperties().put("projectName", "TestProject"); + codegen.additionalProperties().put("modelNamespace", "test::models"); + codegen.additionalProperties().put("apiNamespace", "test::apis"); + codegen.processOpts(); + + Assert.assertEquals(codegen.additionalProperties().get("projectName"), "TestProject"); + Assert.assertEquals(codegen.additionalProperties().get("modelNamespace"), "test::models"); + Assert.assertEquals(codegen.additionalProperties().get("apiNamespace"), "test::apis"); + } + + @Test + public void testToModelName() { + Assert.assertEquals(codegen.toModelName("User"), "User"); + Assert.assertEquals(codegen.toModelName("user"), "User"); + Assert.assertEquals(codegen.toModelName("user_model"), "UserModel"); + Assert.assertEquals(codegen.toModelName("object"), "InlineModel"); + Assert.assertEquals(codegen.toModelName("object_1"), "InlineModel1"); + Assert.assertEquals(codegen.toModelName("inline_object_1"), "InlineModel1"); + Assert.assertEquals(codegen.toModelName("_inline_model"), "Model"); + } + + @Test + public void testToApiName() { + Assert.assertEquals(codegen.toApiName("users"), "Users"); + Assert.assertEquals(codegen.toApiName("testproject"), "Testproject"); + } + + @Test + public void testGetTypeDeclaration() { + Assert.assertEquals(codegen.getTypeDeclaration(new io.swagger.v3.oas.models.media.StringSchema()), "std::string"); + Assert.assertEquals(codegen.getTypeDeclaration(new io.swagger.v3.oas.models.media.IntegerSchema()), "int"); + Assert.assertEquals(codegen.getTypeDeclaration(new io.swagger.v3.oas.models.media.BooleanSchema()), "bool"); + + io.swagger.v3.oas.models.media.ArraySchema arraySchema = new io.swagger.v3.oas.models.media.ArraySchema(); + arraySchema.setItems(new io.swagger.v3.oas.models.media.StringSchema()); + Assert.assertEquals(codegen.getTypeDeclaration(arraySchema), "std::vector"); + } + + @Test + public void testStringUtilityMethods() { + Assert.assertEquals(codegen.toPascalCase("user_name"), "UserName"); + Assert.assertEquals(codegen.toPascalCase("userName"), "UserName"); + Assert.assertEquals(codegen.toCamelCase("user_name"), "userName"); + Assert.assertEquals(codegen.toPascalCase("UserName"), "UserName"); + Assert.assertEquals(codegen.toCamelCase("UserName"), "userName"); + Assert.assertEquals(codegen.toLowerCase("UserName"), "username"); + Assert.assertEquals(underscore("UserName"), "user_name"); + } + + @Test + public void testInlineObjectNaming() { + // Test the enhanced inline object naming that we implemented + Assert.assertEquals(codegen.toModelName("object"), "InlineModel"); + Assert.assertEquals(codegen.toModelName("object_1"), "InlineModel1"); + Assert.assertEquals(codegen.toModelName("object_123"), "InlineModel123"); + Assert.assertEquals(codegen.toModelName("inline_object"), "InlineModel"); + Assert.assertEquals(codegen.toModelName("inline_object_5"), "InlineModel5"); + } + + @Test + public void testModelFilename() { + Assert.assertEquals(codegen.toModelFilename("User"), "User"); + Assert.assertEquals(codegen.toModelFilename("UserModel"), "UserModel"); + Assert.assertEquals(codegen.toModelFilename("object"), "Object"); + Assert.assertEquals(codegen.toModelFilename("usermodel"), "Usermodel"); + } + + @Test + public void testApiFilename() { + Assert.assertEquals(codegen.toApiFilename("User"), "UserApi"); + Assert.assertEquals(codegen.toApiFilename("Default"), "DefaultApi"); + } +} diff --git a/modules/openapi-generator/src/test/resources/3_0/cpp-httplib-server/petstore.json b/modules/openapi-generator/src/test/resources/3_0/cpp-httplib-server/petstore.json new file mode 100644 index 000000000000..acf96c1ed5b1 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/cpp-httplib-server/petstore.json @@ -0,0 +1,1061 @@ +{ + "openapi": "3.0.0", + "servers": [ + { + "url": "http://petstore.swagger.io/v2" + } + ], + "info": { + "description": "This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.", + "version": "1.0.0", + "title": "OpenAPI Petstore", + "license": { + "name": "Apache-2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets" + }, + { + "name": "store", + "description": "Access to Petstore orders" + }, + { + "name": "user", + "description": "Operations about user" + } + ], + "paths": { + "/pet": { + "post": { + "tags": ["pet"], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": ["write:pets", "read:pets"] + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/Pet" + } + }, + "put": { + "tags": ["pet"], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "externalDocs": { + "url": "http://petstore.swagger.io/v2/doc/updatePet", + "description": "API documentation for the updatePet operation" + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": ["write:pets", "read:pets"] + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/Pet" + } + } + }, + "/pet/findByStatus": { + "get": { + "tags": ["pet"], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": true, + "style": "form", + "explode": false, + "deprecated": true, + "schema": { + "type": "array", + "items": { + "type": "string", + "enum": ["available", "pending", "sold"], + "default": "available" + } + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": ["read:pets"] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": ["pet"], + "summary": "Finds Pets by tags", + "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": true, + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": ["read:pets"] + } + ], + "deprecated": true + } + }, + "/pet/{petId}": { + "get": { + "tags": ["pet"], + "summary": "Find pet by ID", + "description": "Returns a single pet", + "operationId": "getPetById", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to return", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "api_key": [] + } + ] + }, + "post": { + "tags": ["pet"], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": ["write:pets", "read:pets"] + } + ], + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "type": "object", + "properties": { + "name": { + "description": "Updated name of the pet", + "type": "string" + }, + "status": { + "description": "Updated status of the pet", + "type": "string" + }, + "ids": { + "description": "Ids of the pet", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + } + } + } + } + } + } + }, + "delete": { + "tags": ["pet"], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "parameters": [ + { + "name": "api_key", + "in": "header", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "400": { + "description": "Invalid pet value" + } + }, + "security": [ + { + "petstore_auth": ["write:pets", "read:pets"] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": ["pet"], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + }, + "security": [ + { + "petstore_auth": ["write:pets", "read:pets"] + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "additionalMetadata": { + "description": "Additional data to pass to server", + "type": "string" + }, + "file": { + "description": "file to upload", + "type": "string", + "format": "binary" + } + } + } + } + } + } + } + }, + "/store/inventory": { + "get": { + "tags": ["store"], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": ["store"], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Order" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "400": { + "description": "Invalid Order" + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + }, + "description": "order placed for purchasing the pet", + "required": true + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": ["store"], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions", + "operationId": "getOrderById", + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "schema": { + "type": "integer", + "format": "int64", + "minimum": 1, + "maximum": 5 + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Order" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": ["store"], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors", + "operationId": "deleteOrder", + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": ["user"], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "responses": { + "default": { + "description": "successful operation" + } + }, + "security": [ + { + "api_key": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + }, + "description": "Created user object", + "required": true + } + } + }, + "/user/createWithArray": { + "post": { + "tags": ["user"], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "responses": { + "default": { + "description": "successful operation" + } + }, + "security": [ + { + "api_key": [] + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/UserArray" + } + } + }, + "/user/createWithList": { + "post": { + "tags": ["user"], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "responses": { + "default": { + "description": "successful operation" + } + }, + "security": [ + { + "api_key": [] + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/UserArray" + } + } + }, + "/user/login": { + "get": { + "tags": ["user"], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": true, + "schema": { + "type": "string", + "pattern": "^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$" + } + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Set-Cookie": { + "description": "Cookie authentication key for use with the `api_key` apiKey authentication.", + "schema": { + "type": "string", + "example": "AUTH_KEY=abcde12345; Path=/; HttpOnly" + } + }, + "X-Rate-Limit": { + "description": "calls per hour allowed by the user", + "schema": { + "type": "integer", + "format": "int32" + } + }, + "X-Expires-After": { + "description": "date in UTC when token expires", + "schema": { + "type": "string", + "format": "date-time" + } + } + }, + "content": { + "application/xml": { + "schema": { + "type": "string" + } + }, + "application/json": { + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": ["user"], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "responses": { + "default": { + "description": "successful operation" + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/user/{username}": { + "get": { + "tags": ["user"], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": ["user"], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be deleted", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + }, + "security": [ + { + "api_key": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + }, + "description": "Updated user object", + "required": true + } + }, + "delete": { + "tags": ["user"], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + }, + "security": [ + { + "api_key": [] + } + ] + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + }, + "components": { + "requestBodies": { + "UserArray": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "description": "List of user object", + "required": true + }, + "Pet": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "description": "Pet object that needs to be added to the store", + "required": true + } + }, + "securitySchemes": { + "petstore_auth": { + "type": "oauth2", + "flows": { + "implicit": { + "authorizationUrl": "http://petstore.swagger.io/api/oauth/dialog", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + } + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + }, + "schemas": { + "Order": { + "description": "An order for a pets from the pet store", + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": ["placed", "approved", "delivered"] + }, + "complete": { + "type": "boolean", + "default": false + } + }, + "xml": { + "name": "Order" + } + }, + "Category": { + "description": "A category for a pet", + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "pattern": "^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$" + } + }, + "xml": { + "name": "Category" + } + }, + "User": { + + "description": "A User who is purchasing from the pet store", + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Tag": { + "description": "A tag for a pet", + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "description": "A pet for sale in the pet store", + "type": "object", + "required": ["name", "photoUrls"], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/components/schemas/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/components/schemas/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "deprecated": true, + "enum": ["available", "pending", "sold"] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "description": "Describes the result of uploading an image resource", + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + } + } +} diff --git a/samples/server/petstore/cpp-httplib-server/.openapi-generator-ignore b/samples/server/petstore/cpp-httplib-server/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/server/petstore/cpp-httplib-server/.openapi-generator/FILES b/samples/server/petstore/cpp-httplib-server/.openapi-generator/FILES new file mode 100644 index 000000000000..152626973c7c --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/.openapi-generator/FILES @@ -0,0 +1,20 @@ +CMakeLists.txt +README.md +api/PetApi.cpp +api/PetApi.h +api/StoreApi.cpp +api/StoreApi.h +api/UserApi.cpp +api/UserApi.h +models/ApiResponse.cpp +models/ApiResponse.h +models/Category.cpp +models/Category.h +models/Order.cpp +models/Order.h +models/Pet.cpp +models/Pet.h +models/Tag.cpp +models/Tag.h +models/User.cpp +models/User.h diff --git a/samples/server/petstore/cpp-httplib-server/.openapi-generator/VERSION b/samples/server/petstore/cpp-httplib-server/.openapi-generator/VERSION new file mode 100644 index 000000000000..fc74d6ceba8e --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.15.0-SNAPSHOT diff --git a/samples/server/petstore/cpp-httplib-server/CMakeLists.txt b/samples/server/petstore/cpp-httplib-server/CMakeLists.txt new file mode 100644 index 000000000000..715d32efa3ff --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.10) +project(cpp_httplib_server LANGUAGES CXX) + +set(TARGET_NAME cpp_httplib_server_openapi_lib) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +file(GLOB API_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/api/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/api/*.cpp +) +file(GLOB MODEL_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/models/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/models/*.cpp +) +add_library(${TARGET_NAME} ${API_SRCS} ${MODEL_SRCS}) +target_include_directories (${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +# Make sure these libraries/headers are available in the build environment before linking +# Required libraries/headers are httplib,ssl,nlohmann::json +target_link_libraries(${TARGET_NAME} httplib ssl nlohmann_json::nlohmann_json) \ No newline at end of file diff --git a/samples/server/petstore/cpp-httplib-server/README.md b/samples/server/petstore/cpp-httplib-server/README.md new file mode 100644 index 000000000000..ee0ea0a6965e --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/README.md @@ -0,0 +1,435 @@ +# cpp-httplib-server - C++ Server + +## Overview + +This server was generated using the [OpenAPI Generator](https://openapi-generator.tech) project. +It uses the [cpp-httplib](https://github.com/yhirose/cpp-httplib) library to implement a lightweight HTTP server +with JSON request/response handling via [nlohmann/json](https://github.com/nlohmann/json). + +## Requirements + +- C++17 compatible compiler +- [cpp-httplib](https://github.com/yhirose/cpp-httplib) library +- [nlohmann/json](https://github.com/nlohmann/json) library +- CMake (3.14 or higher) + +## Project Structure + +``` +├── CMakeLists.txt # Project build configuration +├── README.md # This file +├── model/ # Generated model classes +└── api/ # Generated API handler classes +``` + +## Building the Project + +```bash +mkdir build +cd build +cmake .. +make +``` + +## Working with Models + +### Model Classes + +#### models::ApiResponse + +```cpp +// Create a model +auto model = models::ApiResponse(); +model.setCode(/* value */); // Set code +model.setType(/* value */); // Set type +model.setMessage(/* value */); // Set message + +// Serialize to JSON +nlohmann::json json = models::ApiResponse::toJson(model); +std::string jsonString = json.dump(); + +// Deserialize from JSON +auto parsedModel = models::ApiResponse::fromJson(nlohmann::json::parse(jsonString)); +``` +#### models::Category + +```cpp +// Create a model +auto model = models::Category(); +model.setId(/* value */); // Set id +model.setName(/* value */); // Set name + +// Serialize to JSON +nlohmann::json json = models::Category::toJson(model); +std::string jsonString = json.dump(); + +// Deserialize from JSON +auto parsedModel = models::Category::fromJson(nlohmann::json::parse(jsonString)); +``` +#### models::Order + +```cpp +// Create a model +auto model = models::Order(); +model.setId(/* value */); // Set id +model.setPetId(/* value */); // Set petId +model.setQuantity(/* value */); // Set quantity +model.setShipDate(/* value */); // Set shipDate +model.setStatus(/* value */); // Set status +model.setComplete(/* value */); // Set complete + +// Serialize to JSON +nlohmann::json json = models::Order::toJson(model); +std::string jsonString = json.dump(); + +// Deserialize from JSON +auto parsedModel = models::Order::fromJson(nlohmann::json::parse(jsonString)); +``` +#### models::Pet + +```cpp +// Create a model +auto model = models::Pet(); +model.setId(/* value */); // Set id +model.setCategory(/* value */); // Set category +model.setName(/* value */); // Set name +model.setPhotoUrls(/* value */); // Set photoUrls +model.setTags(/* value */); // Set tags +model.setStatus(/* value */); // Set status + +// Serialize to JSON +nlohmann::json json = models::Pet::toJson(model); +std::string jsonString = json.dump(); + +// Deserialize from JSON +auto parsedModel = models::Pet::fromJson(nlohmann::json::parse(jsonString)); +``` +#### models::Tag + +```cpp +// Create a model +auto model = models::Tag(); +model.setId(/* value */); // Set id +model.setName(/* value */); // Set name + +// Serialize to JSON +nlohmann::json json = models::Tag::toJson(model); +std::string jsonString = json.dump(); + +// Deserialize from JSON +auto parsedModel = models::Tag::fromJson(nlohmann::json::parse(jsonString)); +``` +#### models::User + +```cpp +// Create a model +auto model = models::User(); +model.setId(/* value */); // Set id +model.setUsername(/* value */); // Set username +model.setFirstName(/* value */); // Set firstName +model.setLastName(/* value */); // Set lastName +model.setEmail(/* value */); // Set email +model.setPassword(/* value */); // Set password +model.setPhone(/* value */); // Set phone +model.setUserStatus(/* value */); // Set userStatus + +// Serialize to JSON +nlohmann::json json = models::User::toJson(model); +std::string jsonString = json.dump(); + +// Deserialize from JSON +auto parsedModel = models::User::fromJson(nlohmann::json::parse(jsonString)); +``` + +## Implementing API Handlers + +### API Classes + +#### Pet + +To implement this API, create a class that inherits from the generated base class: + +```cpp +class PetImpl : public api::Pet { +public: + PetResponse handlePostForPet(const models::Pet& request) override { + // Implement your logic here + // Access request data with request.getters + + // For successful response: + return models::Pet(); + + // For error responses: + } + + Pet{petId}Response handleDeleteForPet{petId}() override { + // Implement your logic here + // Access request parameters with req.get_param_value("param_name") + + // For successful response: + return (); + + // For error responses: + } + + PetFindByStatusResponse handleGetForPetFindByStatus() override { + // Implement your logic here + // Access request parameters with req.get_param_value("param_name") + + // For successful response: + return models::Pet(); + + // For error responses: + } + + PetFindByTagsResponse handleGetForPetFindByTags() override { + // Implement your logic here + // Access request parameters with req.get_param_value("param_name") + + // For successful response: + return models::Pet(); + + // For error responses: + } + + Pet{petId}Response handleGetForPet{petId}() override { + // Implement your logic here + // Access request parameters with req.get_param_value("param_name") + + // For successful response: + return models::Pet(); + + // For error responses: + } + + PetResponse handlePutForPet(const models::Pet& request) override { + // Implement your logic here + // Access request data with request.getters + + // For successful response: + return models::Pet(); + + // For error responses: + } + + Pet{petId}Response handlePostForPet{petId}() override { + // Implement your logic here + // Access request parameters with req.get_param_value("param_name") + + // For successful response: + return (); + + // For error responses: + } + + Pet{petId}UploadImageResponse handlePostForPet{petId}UploadImage() override { + // Implement your logic here + // Access request parameters with req.get_param_value("param_name") + + // For successful response: + return models::ApiResponse(); + + // For error responses: + } + +}; +``` +#### Store + +To implement this API, create a class that inherits from the generated base class: + +```cpp +class StoreImpl : public api::Store { +public: + StoreOrder{orderId}Response handleDeleteForStoreOrder{orderId}() override { + // Implement your logic here + // Access request parameters with req.get_param_value("param_name") + + // For successful response: + return (); + + // For error responses: + } + + StoreInventoryResponse handleGetForStoreInventory() override { + // Implement your logic here + // Access request parameters with req.get_param_value("param_name") + + // For successful response: + return int(); + + // For error responses: + } + + StoreOrder{orderId}Response handleGetForStoreOrder{orderId}() override { + // Implement your logic here + // Access request parameters with req.get_param_value("param_name") + + // For successful response: + return models::Order(); + + // For error responses: + } + + StoreOrderResponse handlePostForStoreOrder(const models::Order& request) override { + // Implement your logic here + // Access request data with request.getters + + // For successful response: + return models::Order(); + + // For error responses: + } + +}; +``` +#### User + +To implement this API, create a class that inherits from the generated base class: + +```cpp +class UserImpl : public api::User { +public: + UserResponse handlePostForUser(const models::User& request) override { + // Implement your logic here + // Access request data with request.getters + + // For successful response: + return (); + + // For error responses: + } + + UserCreateWithArrayResponse handlePostForUserCreateWithArray(const models::User& request) override { + // Implement your logic here + // Access request data with request.getters + + // For successful response: + return (); + + // For error responses: + } + + UserCreateWithListResponse handlePostForUserCreateWithList(const models::User& request) override { + // Implement your logic here + // Access request data with request.getters + + // For successful response: + return (); + + // For error responses: + } + + User{username}Response handleDeleteForUser{username}() override { + // Implement your logic here + // Access request parameters with req.get_param_value("param_name") + + // For successful response: + return (); + + // For error responses: + } + + User{username}Response handleGetForUser{username}() override { + // Implement your logic here + // Access request parameters with req.get_param_value("param_name") + + // For successful response: + return models::User(); + + // For error responses: + } + + UserLoginResponse handleGetForUserLogin() override { + // Implement your logic here + // Access request parameters with req.get_param_value("param_name") + + // For successful response: + return std::string(); + + // For error responses: + } + + UserLogoutResponse handleGetForUserLogout() override { + // Implement your logic here + // Access request parameters with req.get_param_value("param_name") + + // For successful response: + return (); + + // For error responses: + } + + User{username}Response handlePutForUser{username}(const models::User& request) override { + // Implement your logic here + // Access request data with request.getters + + // For successful response: + return (); + + // For error responses: + } + +}; +``` + +## Running the Server + +```cpp +#include +#include "api/YourApiImpl.h" + +int main() { + // Create server + auto svr = std::make_unique(); + + // Create API implementation + auto Impl = std::make_shared(); + + // Register routes + Impl->RegisterRoutes(std::move(svr)); + + // Start server on port 8080 + svr->listen("localhost", 8080); + + return 0; +} +``` + +## Error Handling + +Each API endpoint returns a variant type that can hold either a success response or one of several error responses. +The server automatically handles this variant and returns the appropriate HTTP status code. + +## Working with Optional Fields + +Optional parameters and model fields are represented using `std::optional`: + +```cpp +if (model.getOptionalField()) { + // Field is present + auto value = *model.getOptionalField(); +} else { + // Field is not present +} +``` + +## Additional Resources + +- [cpp-httplib Documentation](https://github.com/yhirose/cpp-httplib) +- [nlohmann/json Documentation](https://github.com/nlohmann/json) +- [OpenAPI Generator Documentation](https://openapi-generator.tech/docs/generators/) +```cpp +if (model.getOptionalField()) { + // Field is present + auto value = *model.getOptionalField(); +} else { + // Field is not present +} +``` + +## Additional Resources + +- [cpp-httplib Documentation](https://github.com/yhirose/cpp-httplib) +- [nlohmann/json Documentation](https://github.com/nlohmann/json) +- [OpenAPI Generator Documentation](https://openapi-generator.tech/docs/generators/) diff --git a/samples/server/petstore/cpp-httplib-server/api/PetApi.cpp b/samples/server/petstore/cpp-httplib-server/api/PetApi.cpp new file mode 100644 index 000000000000..30469e39e2f7 --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/api/PetApi.cpp @@ -0,0 +1,199 @@ +/* + * Generated by OpenAPI Generator, do not edit + * https://openapi-generator.tech + */ + +#include "PetApi.h" + +namespace api { + +void Pet::registerRoutes(httplib::Server& svr) { + svr.Post("/pet", [this](const httplib::Request& req, httplib::Response& res) { + try { + //request has schema in json, Hence parsing the request body to the model + models::Pet request = models::Pet::fromJson(nlohmann::json::parse(req.body)); + auto result = handlePostForPet(request); + + std::visit([&](const auto& value) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + res.status = 200; + res.set_content(value.toJson(value).dump(), "application/json"); + } + }, + static_cast(result)); + } catch (const nlohmann::json::parse_error& e) + { + res.status = 400; + nlohmann::json errorJson = { + {"message", "Invalid JSON: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } catch (const std::exception& e) + { + res.status = 500; + nlohmann::json errorJson = { + {"message", "Internal Server Error: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } + }); + + svr.Delete("/pet/{petId}", [this](const httplib::Request& req, httplib::Response& res) { + handleDeleteForPet{petId}(); + }); + + svr.Get("/pet/findByStatus", [this](const httplib::Request& req, httplib::Response& res) { + try { + auto result = handleGetForPetFindByStatus(); + + std::visit([&](const auto& value) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + res.status = 200; + res.set_content(value.toJson(value).dump(), "application/json"); + } + }, + static_cast(result)); + } catch (const nlohmann::json::parse_error& e) + { + res.status = 400; + nlohmann::json errorJson = { + {"message", "Invalid JSON: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } catch (const std::exception& e) + { + res.status = 500; + nlohmann::json errorJson = { + {"message", "Internal Server Error: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } + }); + + svr.Get("/pet/findByTags", [this](const httplib::Request& req, httplib::Response& res) { + try { + auto result = handleGetForPetFindByTags(); + + std::visit([&](const auto& value) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + res.status = 200; + res.set_content(value.toJson(value).dump(), "application/json"); + } + }, + static_cast(result)); + } catch (const nlohmann::json::parse_error& e) + { + res.status = 400; + nlohmann::json errorJson = { + {"message", "Invalid JSON: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } catch (const std::exception& e) + { + res.status = 500; + nlohmann::json errorJson = { + {"message", "Internal Server Error: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } + }); + + svr.Get("/pet/{petId}", [this](const httplib::Request& req, httplib::Response& res) { + try { + auto result = handleGetForPet{petId}(); + + std::visit([&](const auto& value) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + res.status = 200; + res.set_content(value.toJson(value).dump(), "application/json"); + } + }, + static_cast(result)); + } catch (const nlohmann::json::parse_error& e) + { + res.status = 400; + nlohmann::json errorJson = { + {"message", "Invalid JSON: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } catch (const std::exception& e) + { + res.status = 500; + nlohmann::json errorJson = { + {"message", "Internal Server Error: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } + }); + + svr.Put("/pet", [this](const httplib::Request& req, httplib::Response& res) { + try { + //request has schema in json, Hence parsing the request body to the model + models::Pet request = models::Pet::fromJson(nlohmann::json::parse(req.body)); + auto result = handlePutForPet(request); + + std::visit([&](const auto& value) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + res.status = 200; + res.set_content(value.toJson(value).dump(), "application/json"); + } + }, + static_cast(result)); + } catch (const nlohmann::json::parse_error& e) + { + res.status = 400; + nlohmann::json errorJson = { + {"message", "Invalid JSON: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } catch (const std::exception& e) + { + res.status = 500; + nlohmann::json errorJson = { + {"message", "Internal Server Error: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } + }); + + svr.Post("/pet/{petId}", [this](const httplib::Request& req, httplib::Response& res) { + handlePostForPet{petId}(); + }); + + svr.Post("/pet/{petId}/uploadImage", [this](const httplib::Request& req, httplib::Response& res) { + try { + auto result = handlePostForPet{petId}UploadImage(); + + std::visit([&](const auto& value) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + res.status = 200; + res.set_content(value.toJson(value).dump(), "application/json"); + } + }, + static_cast(result)); + } catch (const nlohmann::json::parse_error& e) + { + res.status = 400; + nlohmann::json errorJson = { + {"message", "Invalid JSON: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } catch (const std::exception& e) + { + res.status = 500; + nlohmann::json errorJson = { + {"message", "Internal Server Error: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } + }); + +} + +} // namespace api \ No newline at end of file diff --git a/samples/server/petstore/cpp-httplib-server/api/PetApi.h b/samples/server/petstore/cpp-httplib-server/api/PetApi.h new file mode 100644 index 000000000000..a1ea009111de --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/api/PetApi.h @@ -0,0 +1,131 @@ +/* + * Generated by OpenAPI Generator, do not edit. + * https://openapi-generator.tech + */ + +#pragma once +#include +#include +#include "models/Pet.h" +#include "models/ApiResponse.h" + +namespace api { + +class Pet { +public: + Pet() = default; + virtual ~Pet() = default; + void registerRoutes(httplib::Server& svr); + /** + * @brief Response type for handlePostForPet. + */ + using PetResponse = std::variant< + models::Pet>;//success type + + /** + * @brief Handler function for processing API requests. + * + * handlePostForPet is a pure virtual function that must be implemented by derived classes to handle. + * @param request model containing the input data for the API endpoint. + * @return PetResponse The response type returned by the handler. + */ + virtual PetResponse handlePostForPet( + const models::Pet& request) = 0; + + + /** + * @brief handleDeleteForPet{petId} is a Pure virtual handler function for API endpoint. + * + * This function must be implemented by derived classes to handle the corresponding API request. + */ + virtual void handleDeleteForPet{petId}()=0; + + + /** + * @brief Response type for handleGetForPetFindByStatus. + */ + using PetFindByStatusResponse = std::variant< + models::Pet>;//success type + + /** + * @brief Handler function for processing API requests. + * + * handleGetForPetFindByStatus is a pure virtual function that must be implemented by derived classes to handle. + * @return PetFindByStatusResponse The response type returned by the handler. + */ + virtual PetFindByStatusResponse handleGetForPetFindByStatus()=0; + + + /** + * @brief Response type for handleGetForPetFindByTags. + */ + using PetFindByTagsResponse = std::variant< + models::Pet>;//success type + + /** + * @brief Handler function for processing API requests. + * + * handleGetForPetFindByTags is a pure virtual function that must be implemented by derived classes to handle. + * @return PetFindByTagsResponse The response type returned by the handler. + */ + virtual PetFindByTagsResponse handleGetForPetFindByTags()=0; + + + /** + * @brief Response type for handleGetForPet{petId}. + */ + using Pet{petId}Response = std::variant< + models::Pet>;//success type + + /** + * @brief Handler function for processing API requests. + * + * handleGetForPet{petId} is a pure virtual function that must be implemented by derived classes to handle. + * @return Pet{petId}Response The response type returned by the handler. + */ + virtual Pet{petId}Response handleGetForPet{petId}()=0; + + + /** + * @brief Response type for handlePutForPet. + */ + using PetResponse = std::variant< + models::Pet>;//success type + + /** + * @brief Handler function for processing API requests. + * + * handlePutForPet is a pure virtual function that must be implemented by derived classes to handle. + * @param request model containing the input data for the API endpoint. + * @return PetResponse The response type returned by the handler. + */ + virtual PetResponse handlePutForPet( + const models::Pet& request) = 0; + + + /** + * @brief handlePostForPet{petId} is a Pure virtual handler function for API endpoint. + * + * This function must be implemented by derived classes to handle the corresponding API request. + */ + virtual void handlePostForPet{petId}()=0; + + + /** + * @brief Response type for handlePostForPet{petId}UploadImage. + */ + using Pet{petId}UploadImageResponse = std::variant< + models::ApiResponse>;//success type + + /** + * @brief Handler function for processing API requests. + * + * handlePostForPet{petId}UploadImage is a pure virtual function that must be implemented by derived classes to handle. + * @return Pet{petId}UploadImageResponse The response type returned by the handler. + */ + virtual Pet{petId}UploadImageResponse handlePostForPet{petId}UploadImage()=0; + + +}; + +} // namespace api diff --git a/samples/server/petstore/cpp-httplib-server/api/StoreApi.cpp b/samples/server/petstore/cpp-httplib-server/api/StoreApi.cpp new file mode 100644 index 000000000000..b8792cd62d32 --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/api/StoreApi.cpp @@ -0,0 +1,106 @@ +/* + * Generated by OpenAPI Generator, do not edit + * https://openapi-generator.tech + */ + +#include "StoreApi.h" + +namespace api { + +void Store::registerRoutes(httplib::Server& svr) { + svr.Delete("/store/order/{orderId}", [this](const httplib::Request& req, httplib::Response& res) { + handleDeleteForStoreOrder{orderId}(); + }); + + svr.Get("/store/inventory", [this](const httplib::Request& req, httplib::Response& res) { + try { + auto result = handleGetForStoreInventory(); + + std::visit([&](const auto& value) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + res.status = 200; + res.set_content(value.toJson(value).dump(), "application/json"); + } + }, + static_cast(result)); + } catch (const nlohmann::json::parse_error& e) + { + res.status = 400; + nlohmann::json errorJson = { + {"message", "Invalid JSON: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } catch (const std::exception& e) + { + res.status = 500; + nlohmann::json errorJson = { + {"message", "Internal Server Error: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } + }); + + svr.Get("/store/order/{orderId}", [this](const httplib::Request& req, httplib::Response& res) { + try { + auto result = handleGetForStoreOrder{orderId}(); + + std::visit([&](const auto& value) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + res.status = 200; + res.set_content(value.toJson(value).dump(), "application/json"); + } + }, + static_cast(result)); + } catch (const nlohmann::json::parse_error& e) + { + res.status = 400; + nlohmann::json errorJson = { + {"message", "Invalid JSON: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } catch (const std::exception& e) + { + res.status = 500; + nlohmann::json errorJson = { + {"message", "Internal Server Error: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } + }); + + svr.Post("/store/order", [this](const httplib::Request& req, httplib::Response& res) { + try { + //request has schema in json, Hence parsing the request body to the model + models::Order request = models::Order::fromJson(nlohmann::json::parse(req.body)); + auto result = handlePostForStoreOrder(request); + + std::visit([&](const auto& value) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + res.status = 200; + res.set_content(value.toJson(value).dump(), "application/json"); + } + }, + static_cast(result)); + } catch (const nlohmann::json::parse_error& e) + { + res.status = 400; + nlohmann::json errorJson = { + {"message", "Invalid JSON: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } catch (const std::exception& e) + { + res.status = 500; + nlohmann::json errorJson = { + {"message", "Internal Server Error: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } + }); + +} + +} // namespace api \ No newline at end of file diff --git a/samples/server/petstore/cpp-httplib-server/api/StoreApi.h b/samples/server/petstore/cpp-httplib-server/api/StoreApi.h new file mode 100644 index 000000000000..2422a99a2333 --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/api/StoreApi.h @@ -0,0 +1,75 @@ +/* + * Generated by OpenAPI Generator, do not edit. + * https://openapi-generator.tech + */ + +#pragma once +#include +#include +#include "models/Order.h" + +namespace api { + +class Store { +public: + Store() = default; + virtual ~Store() = default; + void registerRoutes(httplib::Server& svr); + + /** + * @brief handleDeleteForStoreOrder{orderId} is a Pure virtual handler function for API endpoint. + * + * This function must be implemented by derived classes to handle the corresponding API request. + */ + virtual void handleDeleteForStoreOrder{orderId}()=0; + + + /** + * @brief Response type for handleGetForStoreInventory. + */ + using StoreInventoryResponse = std::variant< + int>;//success type + + /** + * @brief Handler function for processing API requests. + * + * handleGetForStoreInventory is a pure virtual function that must be implemented by derived classes to handle. + * @return StoreInventoryResponse The response type returned by the handler. + */ + virtual StoreInventoryResponse handleGetForStoreInventory()=0; + + + /** + * @brief Response type for handleGetForStoreOrder{orderId}. + */ + using StoreOrder{orderId}Response = std::variant< + models::Order>;//success type + + /** + * @brief Handler function for processing API requests. + * + * handleGetForStoreOrder{orderId} is a pure virtual function that must be implemented by derived classes to handle. + * @return StoreOrder{orderId}Response The response type returned by the handler. + */ + virtual StoreOrder{orderId}Response handleGetForStoreOrder{orderId}()=0; + + + /** + * @brief Response type for handlePostForStoreOrder. + */ + using StoreOrderResponse = std::variant< + models::Order>;//success type + + /** + * @brief Handler function for processing API requests. + * + * handlePostForStoreOrder is a pure virtual function that must be implemented by derived classes to handle. + * @param request model containing the input data for the API endpoint. + * @return StoreOrderResponse The response type returned by the handler. + */ + virtual StoreOrderResponse handlePostForStoreOrder( + const models::Order& request) = 0; + +}; + +} // namespace api diff --git a/samples/server/petstore/cpp-httplib-server/api/UserApi.cpp b/samples/server/petstore/cpp-httplib-server/api/UserApi.cpp new file mode 100644 index 000000000000..a02cff7b2878 --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/api/UserApi.cpp @@ -0,0 +1,103 @@ +/* + * Generated by OpenAPI Generator, do not edit + * https://openapi-generator.tech + */ + +#include "UserApi.h" + +namespace api { + +void User::registerRoutes(httplib::Server& svr) { + svr.Post("/user", [this](const httplib::Request& req, httplib::Response& res) { + //request has schema in json, Hence parsing the request body to the model + models::User request = models::User::fromJson(nlohmann::json::parse(req.body)); + handlePostForUser(request); + }); + + svr.Post("/user/createWithArray", [this](const httplib::Request& req, httplib::Response& res) { + //request has schema in json, Hence parsing the request body to the model + models::User request = models::User::fromJson(nlohmann::json::parse(req.body)); + handlePostForUserCreateWithArray(request); + }); + + svr.Post("/user/createWithList", [this](const httplib::Request& req, httplib::Response& res) { + //request has schema in json, Hence parsing the request body to the model + models::User request = models::User::fromJson(nlohmann::json::parse(req.body)); + handlePostForUserCreateWithList(request); + }); + + svr.Delete("/user/{username}", [this](const httplib::Request& req, httplib::Response& res) { + handleDeleteForUser{username}(); + }); + + svr.Get("/user/{username}", [this](const httplib::Request& req, httplib::Response& res) { + try { + auto result = handleGetForUser{username}(); + + std::visit([&](const auto& value) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + res.status = 200; + res.set_content(value.toJson(value).dump(), "application/json"); + } + }, + static_cast(result)); + } catch (const nlohmann::json::parse_error& e) + { + res.status = 400; + nlohmann::json errorJson = { + {"message", "Invalid JSON: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } catch (const std::exception& e) + { + res.status = 500; + nlohmann::json errorJson = { + {"message", "Internal Server Error: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } + }); + + svr.Get("/user/login", [this](const httplib::Request& req, httplib::Response& res) { + try { + auto result = handleGetForUserLogin(); + + std::visit([&](const auto& value) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + res.status = 200; + res.set_content(value.toJson(value).dump(), "application/json"); + } + }, + static_cast(result)); + } catch (const nlohmann::json::parse_error& e) + { + res.status = 400; + nlohmann::json errorJson = { + {"message", "Invalid JSON: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } catch (const std::exception& e) + { + res.status = 500; + nlohmann::json errorJson = { + {"message", "Internal Server Error: " + std::string(e.what())} + }; + res.set_content(errorJson.dump(), "application/json"); + } + }); + + svr.Get("/user/logout", [this](const httplib::Request& req, httplib::Response& res) { + handleGetForUserLogout(); + }); + + svr.Put("/user/{username}", [this](const httplib::Request& req, httplib::Response& res) { + //request has schema in json, Hence parsing the request body to the model + models::User request = models::User::fromJson(nlohmann::json::parse(req.body)); + handlePutForUser{username}(request); + }); + +} + +} // namespace api \ No newline at end of file diff --git a/samples/server/petstore/cpp-httplib-server/api/UserApi.h b/samples/server/petstore/cpp-httplib-server/api/UserApi.h new file mode 100644 index 000000000000..3a59ebc5cb9a --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/api/UserApi.h @@ -0,0 +1,108 @@ +/* + * Generated by OpenAPI Generator, do not edit. + * https://openapi-generator.tech + */ + +#pragma once +#include +#include +#include "models/User.h" + +namespace api { + +class User { +public: + User() = default; + virtual ~User() = default; + void registerRoutes(httplib::Server& svr); + + /** + * @brief handlePostForUser is a Pure virtual handler function for API endpoint. + * + * This function must be implemented by derived classes to handle the corresponding API request. + * @param "models::User" The request model containing the input data for the API endpoint. + */ + virtual void handlePostForUser( + const models::User& request) = 0; + + + /** + * @brief handlePostForUserCreateWithArray is a Pure virtual handler function for API endpoint. + * + * This function must be implemented by derived classes to handle the corresponding API request. + * @param "models::User" The request model containing the input data for the API endpoint. + */ + virtual void handlePostForUserCreateWithArray( + const models::User& request) = 0; + + + /** + * @brief handlePostForUserCreateWithList is a Pure virtual handler function for API endpoint. + * + * This function must be implemented by derived classes to handle the corresponding API request. + * @param "models::User" The request model containing the input data for the API endpoint. + */ + virtual void handlePostForUserCreateWithList( + const models::User& request) = 0; + + + /** + * @brief handleDeleteForUser{username} is a Pure virtual handler function for API endpoint. + * + * This function must be implemented by derived classes to handle the corresponding API request. + */ + virtual void handleDeleteForUser{username}()=0; + + + /** + * @brief Response type for handleGetForUser{username}. + */ + using User{username}Response = std::variant< + models::User>;//success type + + /** + * @brief Handler function for processing API requests. + * + * handleGetForUser{username} is a pure virtual function that must be implemented by derived classes to handle. + * @return User{username}Response The response type returned by the handler. + */ + virtual User{username}Response handleGetForUser{username}()=0; + + + /** + * @brief Response type for handleGetForUserLogin. + */ + using UserLoginResponse = std::variant< + std::string>;//success type + + /** + * @brief Handler function for processing API requests. + * + * handleGetForUserLogin is a pure virtual function that must be implemented by derived classes to handle. + * @return UserLoginResponse The response type returned by the handler. + */ + virtual UserLoginResponse handleGetForUserLogin()=0; + + + + /** + * @brief handleGetForUserLogout is a Pure virtual handler function for API endpoint. + * + * This function must be implemented by derived classes to handle the corresponding API request. + */ + virtual void handleGetForUserLogout()=0; + + + + /** + * @brief handlePutForUser{username} is a Pure virtual handler function for API endpoint. + * + * This function must be implemented by derived classes to handle the corresponding API request. + * @param "models::User" The request model containing the input data for the API endpoint. + */ + virtual void handlePutForUser{username}( + const models::User& request) = 0; + +}; + +} // namespace api diff --git a/samples/server/petstore/cpp-httplib-server/models/ApiResponse.cpp b/samples/server/petstore/cpp-httplib-server/models/ApiResponse.cpp new file mode 100644 index 000000000000..e19cd2db847f --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/models/ApiResponse.cpp @@ -0,0 +1,73 @@ +/* + * Generated by OpenAPI Generator, do not edit. + * https://openapi-generator.tech + */ + +#include "ApiResponse.h" + +namespace models { + +ApiResponse::ApiResponse() +: m_code(0), + m_type(""), + m_message("") +{ +} + +nlohmann::json ApiResponse::toJson(const ApiResponse& obj) +{ + nlohmann::json json; + json["code"] = obj.getCode(); + json["type"] = obj.getType(); + json["message"] = obj.getMessage(); + return json; +} + +ApiResponse ApiResponse::fromJson(const nlohmann::json& json) +{ + ApiResponse obj; + if (json.contains("code")) + { + obj.setCode(json.at("code")); + } + if (json.contains("type")) + { + obj.setType(json.at("type")); + } + if (json.contains("message")) + { + obj.setMessage(json.at("message")); + } + return obj; +} + + +int ApiResponse::getCode() const +{ + return m_code; +} + +void ApiResponse::setCode(const int& code) +{ + m_code = code; +} +std::string ApiResponse::getType() const +{ + return m_type; +} + +void ApiResponse::setType(const std::string& type) +{ + m_type = type; +} +std::string ApiResponse::getMessage() const +{ + return m_message; +} + +void ApiResponse::setMessage(const std::string& message) +{ + m_message = message; +} + +} // namespace models diff --git a/samples/server/petstore/cpp-httplib-server/models/ApiResponse.h b/samples/server/petstore/cpp-httplib-server/models/ApiResponse.h new file mode 100644 index 000000000000..982874659d6f --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/models/ApiResponse.h @@ -0,0 +1,38 @@ +/* + * Generated by OpenAPI Generator, do not edit. + * https://openapi-generator.tech + */ + +#pragma once +#include + + + +namespace models { + +class ApiResponse +{ +public: + ApiResponse(); + virtual ~ApiResponse() = default; + + // JSON serialization + static nlohmann::json toJson(const ApiResponse& obj); + static ApiResponse fromJson(const nlohmann::json& json); + + // Getters and setters + [[nodiscard]] int getCode() const; + void setCode(const int& code); + [[nodiscard]] std::string getType() const; + void setType(const std::string& type); + [[nodiscard]] std::string getMessage() const; + void setMessage(const std::string& message); + +private: + // Member variables + int m_code; + std::string m_type; + std::string m_message; +}; + +} // namespace models diff --git a/samples/server/petstore/cpp-httplib-server/models/Category.cpp b/samples/server/petstore/cpp-httplib-server/models/Category.cpp new file mode 100644 index 000000000000..a0f80d59135b --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/models/Category.cpp @@ -0,0 +1,58 @@ +/* + * Generated by OpenAPI Generator, do not edit. + * https://openapi-generator.tech + */ + +#include "Category.h" + +namespace models { + +Category::Category() +: m_id(0), + m_name("") +{ +} + +nlohmann::json Category::toJson(const Category& obj) +{ + nlohmann::json json; + json["id"] = obj.getId(); + json["name"] = obj.getName(); + return json; +} + +Category Category::fromJson(const nlohmann::json& json) +{ + Category obj; + if (json.contains("id")) + { + obj.setId(json.at("id").get()); + } + if (json.contains("name")) + { + obj.setName(json.at("name")); + } + return obj; +} + + +long Category::getId() const +{ + return m_id; +} + +void Category::setId(const long& id) +{ + m_id = id; +} +std::string Category::getName() const +{ + return m_name; +} + +void Category::setName(const std::string& name) +{ + m_name = name; +} + +} // namespace models diff --git a/samples/server/petstore/cpp-httplib-server/models/Category.h b/samples/server/petstore/cpp-httplib-server/models/Category.h new file mode 100644 index 000000000000..04697c5de868 --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/models/Category.h @@ -0,0 +1,36 @@ +/* + * Generated by OpenAPI Generator, do not edit. + * https://openapi-generator.tech + */ + +#pragma once +#include + + +#include + +namespace models { + +class Category +{ +public: + Category(); + virtual ~Category() = default; + + // JSON serialization + static nlohmann::json toJson(const Category& obj); + static Category fromJson(const nlohmann::json& json); + + // Getters and setters + [[nodiscard]] long getId() const; + void setId(const long& id); + [[nodiscard]] std::string getName() const; + void setName(const std::string& name); + +private: + // Member variables + long m_id; + std::string m_name; +}; + +} // namespace models diff --git a/samples/server/petstore/cpp-httplib-server/models/Order.cpp b/samples/server/petstore/cpp-httplib-server/models/Order.cpp new file mode 100644 index 000000000000..5d43e0550619 --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/models/Order.cpp @@ -0,0 +1,118 @@ +/* + * Generated by OpenAPI Generator, do not edit. + * https://openapi-generator.tech + */ + +#include "Order.h" + +namespace models { + +Order::Order() +: m_id(0), + m_petId(0), + m_quantity(0), + m_shipDate(""), + m_status(""), + m_complete(false) +{ +} + +nlohmann::json Order::toJson(const Order& obj) +{ + nlohmann::json json; + json["id"] = obj.getId(); + json["petId"] = obj.getPetId(); + json["quantity"] = obj.getQuantity(); + json["shipDate"] = obj.getShipDate(); + json["status"] = obj.getStatus(); + json["complete"] = obj.isComplete(); + return json; +} + +Order Order::fromJson(const nlohmann::json& json) +{ + Order obj; + if (json.contains("id")) + { + obj.setId(json.at("id").get()); + } + if (json.contains("petId")) + { + obj.setPetId(json.at("petId").get()); + } + if (json.contains("quantity")) + { + obj.setQuantity(json.at("quantity")); + } + if (json.contains("shipDate")) + { + obj.setShipDate(json.at("shipDate")); + } + if (json.contains("status")) + { + obj.setStatus(json.at("status")); + } + if (json.contains("complete")) + { + obj.setComplete(json.at("complete")); + } + return obj; +} + + +long Order::getId() const +{ + return m_id; +} + +void Order::setId(const long& id) +{ + m_id = id; +} +long Order::getPetId() const +{ + return m_petId; +} + +void Order::setPetId(const long& petId) +{ + m_petId = petId; +} +int Order::getQuantity() const +{ + return m_quantity; +} + +void Order::setQuantity(const int& quantity) +{ + m_quantity = quantity; +} +Date Order::getShipDate() const +{ + return m_shipDate; +} + +void Order::setShipDate(const Date& shipDate) +{ + m_shipDate = shipDate; +} +StatusEnum Order::getStatus() const +{ + return m_status; +} + +void Order::setStatus(const StatusEnum& status) +{ + m_status = status; +} +bool Order::isComplete() const +{ + return m_complete; +} + +void Order::setComplete(const bool& complete) +{ + m_complete = complete; +} + +} // namespace models diff --git a/samples/server/petstore/cpp-httplib-server/models/Order.h b/samples/server/petstore/cpp-httplib-server/models/Order.h new file mode 100644 index 000000000000..258d9fbe4ee1 --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/models/Order.h @@ -0,0 +1,49 @@ +/* + * Generated by OpenAPI Generator, do not edit. + * https://openapi-generator.tech + */ + +#pragma once +#include + + +#include +#include + +namespace models { + +class Order +{ +public: + Order(); + virtual ~Order() = default; + + // JSON serialization + static nlohmann::json toJson(const Order& obj); + static Order fromJson(const nlohmann::json& json); + + // Getters and setters + [[nodiscard]] long getId() const; + void setId(const long& id); + [[nodiscard]] long getPetId() const; + void setPetId(const long& petId); + [[nodiscard]] int getQuantity() const; + void setQuantity(const int& quantity); + [[nodiscard]] Date getShipDate() const; + void setShipDate(const Date& shipDate); + [[nodiscard]] StatusEnum getStatus() const; + void setStatus(const StatusEnum& status); + [[nodiscard]] bool isComplete() const; + void setComplete(const bool& complete); + +private: + // Member variables + long m_id; + long m_petId; + int m_quantity; + Date m_shipDate; + StatusEnum m_status; + bool m_complete; +}; + +} // namespace models diff --git a/samples/server/petstore/cpp-httplib-server/models/Pet.cpp b/samples/server/petstore/cpp-httplib-server/models/Pet.cpp new file mode 100644 index 000000000000..f05aab6e693b --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/models/Pet.cpp @@ -0,0 +1,118 @@ +/* + * Generated by OpenAPI Generator, do not edit. + * https://openapi-generator.tech + */ + +#include "Pet.h" + +namespace models { + +Pet::Pet() +: m_id(0), + m_category(std::make_shared()), + m_name(""), + m_photoUrls(std::vector()), + m_tags(std::vector()), + m_status("") +{ +} + +nlohmann::json Pet::toJson(const Pet& obj) +{ + nlohmann::json json; + json["id"] = obj.getId(); + json["category"] = obj.getCategory(); + json["name"] = obj.getName(); + json["photoUrls"] = obj.getPhotoUrls(); + json["tags"] = obj.getTags(); + json["status"] = obj.getStatus(); + return json; +} + +Pet Pet::fromJson(const nlohmann::json& json) +{ + Pet obj; + if (json.contains("id")) + { + obj.setId(json.at("id").get()); + } + if (json.contains("category")) + { + obj.setCategory(json.at("category")); + } + if (json.contains("name")) + { + obj.setName(json.at("name")); + } + if (json.contains("photoUrls")) + { + obj.setPhotoUrls(json.at("photoUrls")); + } + if (json.contains("tags")) + { + obj.setTags(json.at("tags")); + } + if (json.contains("status")) + { + obj.setStatus(json.at("status")); + } + return obj; +} + + +long Pet::getId() const +{ + return m_id; +} + +void Pet::setId(const long& id) +{ + m_id = id; +} +Category Pet::getCategory() const +{ + return m_category; +} + +void Pet::setCategory(const Category& category) +{ + m_category = category; +} +std::string Pet::getName() const +{ + return m_name; +} + +void Pet::setName(const std::string& name) +{ + m_name = name; +} +std::vector Pet::getPhotoUrls() const +{ + return m_photoUrls; +} + +void Pet::setPhotoUrls(const std::vector& photoUrls) +{ + m_photoUrls = photoUrls; +} +std::vector Pet::getTags() const +{ + return m_tags; +} + +void Pet::setTags(const std::vector& tags) +{ + m_tags = tags; +} +StatusEnum Pet::getStatus() const +{ + return m_status; +} + +void Pet::setStatus(const StatusEnum& status) +{ + m_status = status; +} + +} // namespace models diff --git a/samples/server/petstore/cpp-httplib-server/models/Pet.h b/samples/server/petstore/cpp-httplib-server/models/Pet.h new file mode 100644 index 000000000000..09765a021727 --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/models/Pet.h @@ -0,0 +1,50 @@ +/* + * Generated by OpenAPI Generator, do not edit. + * https://openapi-generator.tech + */ + +#pragma once +#include + +#include "Category.h" +#include "Tag.h" + +#include + +namespace models { + +class Pet +{ +public: + Pet(); + virtual ~Pet() = default; + + // JSON serialization + static nlohmann::json toJson(const Pet& obj); + static Pet fromJson(const nlohmann::json& json); + + // Getters and setters + [[nodiscard]] long getId() const; + void setId(const long& id); + [[nodiscard]] Category getCategory() const; + void setCategory(const Category& category); + [[nodiscard]] std::string getName() const; + void setName(const std::string& name); + [[nodiscard]] std::vector getPhotoUrls() const; + void setPhotoUrls(const std::vector& photoUrls); + [[nodiscard]] std::vector getTags() const; + void setTags(const std::vector& tags); + [[nodiscard]] StatusEnum getStatus() const; + void setStatus(const StatusEnum& status); + +private: + // Member variables + long m_id; + Category m_category; + std::string m_name; + std::vector m_photoUrls; + std::vector m_tags; + StatusEnum m_status; +}; + +} // namespace models diff --git a/samples/server/petstore/cpp-httplib-server/models/Tag.cpp b/samples/server/petstore/cpp-httplib-server/models/Tag.cpp new file mode 100644 index 000000000000..cb8f7c93df5a --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/models/Tag.cpp @@ -0,0 +1,58 @@ +/* + * Generated by OpenAPI Generator, do not edit. + * https://openapi-generator.tech + */ + +#include "Tag.h" + +namespace models { + +Tag::Tag() +: m_id(0), + m_name("") +{ +} + +nlohmann::json Tag::toJson(const Tag& obj) +{ + nlohmann::json json; + json["id"] = obj.getId(); + json["name"] = obj.getName(); + return json; +} + +Tag Tag::fromJson(const nlohmann::json& json) +{ + Tag obj; + if (json.contains("id")) + { + obj.setId(json.at("id").get()); + } + if (json.contains("name")) + { + obj.setName(json.at("name")); + } + return obj; +} + + +long Tag::getId() const +{ + return m_id; +} + +void Tag::setId(const long& id) +{ + m_id = id; +} +std::string Tag::getName() const +{ + return m_name; +} + +void Tag::setName(const std::string& name) +{ + m_name = name; +} + +} // namespace models diff --git a/samples/server/petstore/cpp-httplib-server/models/Tag.h b/samples/server/petstore/cpp-httplib-server/models/Tag.h new file mode 100644 index 000000000000..3083fdf8d4f2 --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/models/Tag.h @@ -0,0 +1,36 @@ +/* + * Generated by OpenAPI Generator, do not edit. + * https://openapi-generator.tech + */ + +#pragma once +#include + + +#include + +namespace models { + +class Tag +{ +public: + Tag(); + virtual ~Tag() = default; + + // JSON serialization + static nlohmann::json toJson(const Tag& obj); + static Tag fromJson(const nlohmann::json& json); + + // Getters and setters + [[nodiscard]] long getId() const; + void setId(const long& id); + [[nodiscard]] std::string getName() const; + void setName(const std::string& name); + +private: + // Member variables + long m_id; + std::string m_name; +}; + +} // namespace models diff --git a/samples/server/petstore/cpp-httplib-server/models/User.cpp b/samples/server/petstore/cpp-httplib-server/models/User.cpp new file mode 100644 index 000000000000..2acce29ddf65 --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/models/User.cpp @@ -0,0 +1,148 @@ +/* + * Generated by OpenAPI Generator, do not edit. + * https://openapi-generator.tech + */ + +#include "User.h" + +namespace models { + +User::User() +: m_id(0), + m_username(""), + m_firstName(""), + m_lastName(""), + m_email(""), + m_password(""), + m_phone(""), + m_userStatus(0) +{ +} + +nlohmann::json User::toJson(const User& obj) +{ + nlohmann::json json; + json["id"] = obj.getId(); + json["username"] = obj.getUsername(); + json["firstName"] = obj.getFirstName(); + json["lastName"] = obj.getLastName(); + json["email"] = obj.getEmail(); + json["password"] = obj.getPassword(); + json["phone"] = obj.getPhone(); + json["userStatus"] = obj.getUserStatus(); + return json; +} + +User User::fromJson(const nlohmann::json& json) +{ + User obj; + if (json.contains("id")) + { + obj.setId(json.at("id").get()); + } + if (json.contains("username")) + { + obj.setUsername(json.at("username")); + } + if (json.contains("firstName")) + { + obj.setFirstName(json.at("firstName")); + } + if (json.contains("lastName")) + { + obj.setLastName(json.at("lastName")); + } + if (json.contains("email")) + { + obj.setEmail(json.at("email")); + } + if (json.contains("password")) + { + obj.setPassword(json.at("password")); + } + if (json.contains("phone")) + { + obj.setPhone(json.at("phone")); + } + if (json.contains("userStatus")) + { + obj.setUserStatus(json.at("userStatus")); + } + return obj; +} + + +long User::getId() const +{ + return m_id; +} + +void User::setId(const long& id) +{ + m_id = id; +} +std::string User::getUsername() const +{ + return m_username; +} + +void User::setUsername(const std::string& username) +{ + m_username = username; +} +std::string User::getFirstName() const +{ + return m_firstName; +} + +void User::setFirstName(const std::string& firstName) +{ + m_firstName = firstName; +} +std::string User::getLastName() const +{ + return m_lastName; +} + +void User::setLastName(const std::string& lastName) +{ + m_lastName = lastName; +} +std::string User::getEmail() const +{ + return m_email; +} + +void User::setEmail(const std::string& email) +{ + m_email = email; +} +std::string User::getPassword() const +{ + return m_password; +} + +void User::setPassword(const std::string& password) +{ + m_password = password; +} +std::string User::getPhone() const +{ + return m_phone; +} + +void User::setPhone(const std::string& phone) +{ + m_phone = phone; +} +int User::getUserStatus() const +{ + return m_userStatus; +} + +void User::setUserStatus(const int& userStatus) +{ + m_userStatus = userStatus; +} + +} // namespace models diff --git a/samples/server/petstore/cpp-httplib-server/models/User.h b/samples/server/petstore/cpp-httplib-server/models/User.h new file mode 100644 index 000000000000..83c1ed8f0471 --- /dev/null +++ b/samples/server/petstore/cpp-httplib-server/models/User.h @@ -0,0 +1,54 @@ +/* + * Generated by OpenAPI Generator, do not edit. + * https://openapi-generator.tech + */ + +#pragma once +#include + + +#include + +namespace models { + +class User +{ +public: + User(); + virtual ~User() = default; + + // JSON serialization + static nlohmann::json toJson(const User& obj); + static User fromJson(const nlohmann::json& json); + + // Getters and setters + [[nodiscard]] long getId() const; + void setId(const long& id); + [[nodiscard]] std::string getUsername() const; + void setUsername(const std::string& username); + [[nodiscard]] std::string getFirstName() const; + void setFirstName(const std::string& firstName); + [[nodiscard]] std::string getLastName() const; + void setLastName(const std::string& lastName); + [[nodiscard]] std::string getEmail() const; + void setEmail(const std::string& email); + [[nodiscard]] std::string getPassword() const; + void setPassword(const std::string& password); + [[nodiscard]] std::string getPhone() const; + void setPhone(const std::string& phone); + [[nodiscard]] int getUserStatus() const; + void setUserStatus(const int& userStatus); + +private: + // Member variables + long m_id; + std::string m_username; + std::string m_firstName; + std::string m_lastName; + std::string m_email; + std::string m_password; + std::string m_phone; + int m_userStatus; +}; + +} // namespace models diff --git a/website/i18n/en.json b/website/i18n/en.json index 9f06b9b90e6f..626a16c030d6 100644 --- a/website/i18n/en.json +++ b/website/i18n/en.json @@ -115,6 +115,10 @@ "title": "Config Options for cpp-tizen", "sidebar_label": "cpp-tizen" }, + "generators/cpp-httplib-server": { + "title": "Config Options for cpp-httplib-server", + "sidebar_label": "cpp-httplib-server" + }, "generators/csharp-dotnet2": { "title": "Config Options for csharp-dotnet2", "sidebar_label": "csharp-dotnet2"