Skip to content

fix(cpp-httplib-server): generate top-level enum models - #24795

Open
stefanwitkowskiwmb wants to merge 18 commits into
OpenAPITools:masterfrom
stefanwitkowskiwmb:bugfix/httplib-server_top_level_enum_schema
Open

stefanwitkowskiwmb wants to merge 18 commits into
OpenAPITools:masterfrom
stefanwitkowskiwmb:bugfix/httplib-server_top_level_enum_schema

Conversation

@stefanwitkowskiwmb

@stefanwitkowskiwmb stefanwitkowskiwmb commented Aug 27, 2026 •

Copy link
Copy Markdown

PR checklist

  • Read the contribution guidelines.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in WSL)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

Fix for #23902

@ravinikam @stkrwork @etherealjoy @MartinDelille @muttleyxd @aminya


Summary by cubic

Fixes the cpp-httplib-server generator so top-level enum schemas are generated as enum class types with to_json/from_json helpers; previously standalone enum models were skipped. Addresses #23902.

  • String-backed enums serialize using the original spec strings (e.g. "active", not ACTIVE); integer-backed enums serialize as raw JSON numbers. String values are detected even when the declared type isn't plainly string (e.g. OpenAPI 3.1 type: [null, string]).
  • to_json/from_json for top-level enums throw nlohmann::json::type_error for values outside the declared set; property-level ToString/FromString keep their existing behavior (empty string fallback and std::invalid_argument).
  • Adds the enum header include to the model source template and fixes the namespace closure in the model header.
  • Adds TopLevelStatus and TopLevelPriority to the feature-test spec with tests asserting the rendered to_json/from_json output.

Written for commit 112e3e1. Summary will update on new commits.

Review in cubic

@stefanwitkowskiwmb
stefanwitkowskiwmb marked this pull request as ready for review August 27, 2026 14:58

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 6 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 46 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="samples/server/petstore/cpp-httplib-server/petstore/models/Tag.h">

<violation number="1" location="samples/server/petstore/cpp-httplib-server/petstore/models/Tag.h:11">
P3: Every regenerated model header now contains a duplicated `#include <string>`: the template emits it unconditionally while `filteredImports` (from `postProcessAllModels`) emits it again for each model with a string member. It compiles because of the standard header guard, but it clutters every generated header. `model-header.mustache` should not hardcode `#include <string>`; let `filteredImports` provide it (as the master template did), or dedupe `<string>` in the import filter.</violation>
</file>

<file name="samples/server/petstore/cpp-httplib-server/feature-test/models/TestBasicSecurity200Response.h">

<violation number="1" location="samples/server/petstore/cpp-httplib-server/feature-test/models/TestBasicSecurity200Response.h:11">
P3: Generated model headers now emit a duplicate `#include <string>` for every model with a string member. The template hardcodes `#include <string>` while `filteredImports` (CppHttplibServerCodegen.java) already emits it for string-typed vars, so the two overlap in the output. Harmless at compile time, but it regresses generated-code cleanliness across all string-membered models. Drop the hardcoded `#include <string>` from model-header.mustache; the enum and string-member headers don't need it from the template since filteredImports supplies it (enum serialization uses string literals only).</violation>
</file>

<file name="samples/server/petstore/cpp-httplib-server/feature-test/models/Address.h">

<violation number="1" location="samples/server/petstore/cpp-httplib-server/feature-test/models/Address.h:11">
P3: Every generated model header in this PR now emits `#include <string>` twice: once from the template's new hardcoded include and once from the model's filtered imports. The template at model-header.mustache line 8 unconditionally writes `#include <string>` before `{{#vendorExtensions.filteredImports}}`, which itself also emits `<string>` for any model with a std::string field, so the two sources collide. De-duplicate (e.g. drop `<string>` from the template's hardcoded block when it is already in the model's imports) so generated output contains a single include.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread samples/server/petstore/cpp-httplib-server/feature-test/README.md Outdated
Comment thread samples/server/petstore/cpp-httplib-server/feature-test/models/EnumTypes.h Outdated
Comment thread samples/server/petstore/cpp-httplib-server/feature-test/models/ErrorResponse.h Outdated
Comment thread samples/server/petstore/cpp-httplib-server/feature-test/models/NotFoundResponse.h Outdated
Comment thread samples/server/petstore/cpp-httplib-server/feature-test/models/ArrayTypes.h Outdated
Comment thread samples/server/petstore/cpp-httplib-server/feature-test/models/Dog.h Outdated
Comment thread samples/server/petstore/cpp-httplib-server/petstore/models/User.h Outdated
Comment thread samples/server/petstore/cpp-httplib-server/feature-test/models/CreatedResponse.h Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 46 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

stefanwitkowskiwmb and others added 6 commits September 3, 2026 15:05
The isStringEnum fallback added in f368b5a had no test that
distinguished it from the previous ModelUtils.isStringSchema check.

ModelUtils.getType returns the first element of a 3.1 type set, so a
`type: [null, string]` enum reports its type as "null": isStringSchema is
false while isEnum is true, and model-header.mustache then emits
`j = available;` instead of `j = "available";`, which does not compile.

Reverting CppHttplibServerCodegen to the plain isStringSchema check makes
this test fail.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…evel enums

The existing enum tests only assert the isStringEnum vendor extension on
the fromModel result. That flag is just the input to model-header.mustache,
so a break in the template itself -- a renamed flag, an inverted section --
passed the whole suite unnoticed.

Add a test that builds a string-backed and an integer-backed top-level enum
schema, runs DefaultGenerator over them, and asserts on the rendered enum
headers: quoted values for the former, bare values for the latter, plus the
inverse via assertFileNotContains. Renaming vendorExtensions.isStringEnum
throughout model-header.mustache makes it fail.

Also add an integer-backed top-level enum to feature-test.json and
regenerate its sample. The template's `{{^vendorExtensions.isStringEnum}}`
branch had no checked-in sample output, since the spec carried a top-level
string enum but no integer one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 6 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

@wing328 wing328 added this to the 7.26.0 milestone Sep 20, 2026
@wing328

wing328 commented Sep 20, 2026

Copy link
Copy Markdown
Member

thanks for the PR

can you please review the build failure when you've time?

https://github.com/OpenAPITools/openapi-generator/actions/runs/34101485793/job/106096543175?pr=24795

…ot directly related to nlohmann but are more general in nature.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 12 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/resources/cpp-httplib-server/model-header.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/cpp-httplib-server/model-header.mustache:31">
P1: When a top-level string enum contains a quote, backslash, or control character, this raw interpolation generates an invalid or changed C++ literal in both `to_json` and `from_json`. Generate a C++-escaped string literal in the codegen metadata and emit that literal without adding another pair of quotes.</violation>
</file>

<file name="samples/server/petstore/cpp-httplib-server/feature-test/README.md">

<violation number="1" location="samples/server/petstore/cpp-httplib-server/feature-test/README.md:527">
P3: The added enum snippets document `models::TopLevelPriority` and `models::TopLevelStatus` without an `UNSPECIFIED` entry, but the same README's "### Enum Handling" section (line ~1049) promises that "All generated enums automatically include an `UNSPECIFIED` value as the first enum entry". Embedded enums generated elsewhere in this sample (e.g. `EnumTypes.h`, `CardType.h`) do start with `UNSPECIFIED`, so the new top-level enums are generated differently from the pattern this README documents. The two sections now contradict each other, and users following the README's Enum Handling guidance will look for `TopLevelPriority::UNSPECIFIED` and won't find it. Either align the top-level enum generation with the embedded-enum pattern (add the `UNSPECIFIED` entry) or update the "Enum Handling" claim so the generated documentation is consistent.</violation>
</file>

<file name="samples/server/petstore/cpp-httplib-server/feature-test/models/TopLevelStatus.h">

<violation number="1" location="samples/server/petstore/cpp-httplib-server/feature-test/models/TopLevelStatus.h:17">
P3: The enum body is emitted with whitespace-only lines (`    `) between enumerators, producing trailing-whitespace lines and extra vertical padding in the generated header. This comes from the enum section of `model-header.mustache`, which puts each `{{name}}` and the closing `{{/enumVars}}` on their own indented lines. The same pattern appears in `TopLevelPriority.h`, and both `.cpp` files also start with an empty first line (from the blank first line in `model-source.mustache`). Collapse the enumerators onto a single line in the template so regenerated samples are free of trailing whitespace.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

switch (value)
{
{{#allowableValues}}{{#enumVars}}
case {{vendorExtensions.modelClassName}}::{{name}}: j = {{#vendorExtensions.isStringEnum}}"{{{value}}}"{{/vendorExtensions.isStringEnum}}{{^vendorExtensions.isStringEnum}}{{value}}{{/vendorExtensions.isStringEnum}}; break;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: When a top-level string enum contains a quote, backslash, or control character, this raw interpolation generates an invalid or changed C++ literal in both to_json and from_json. Generate a C++-escaped string literal in the codegen metadata and emit that literal without adding another pair of quotes.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/cpp-httplib-server/model-header.mustache, line 31:

<comment>When a top-level string enum contains a quote, backslash, or control character, this raw interpolation generates an invalid or changed C++ literal in both `to_json` and `from_json`. Generate a C++-escaped string literal in the codegen metadata and emit that literal without adding another pair of quotes.</comment>

<file context>
@@ -16,6 +16,38 @@
+    switch (value)
+    {
+        {{#allowableValues}}{{#enumVars}}
+        case {{vendorExtensions.modelClassName}}::{{name}}: j = {{#vendorExtensions.isStringEnum}}"{{{value}}}"{{/vendorExtensions.isStringEnum}}{{^vendorExtensions.isStringEnum}}{{value}}{{/vendorExtensions.isStringEnum}}; break;
+        {{/enumVars}}{{/allowableValues}}
+        default: throw nlohmann::json::type_error::create(302, "Invalid value for {{vendorExtensions.modelClassName}}", &j);
</file context>


```cpp
// Select an enum value
auto model = models::TopLevelPriority::_0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The added enum snippets document models::TopLevelPriority and models::TopLevelStatus without an UNSPECIFIED entry, but the same README's "### Enum Handling" section (line ~1049) promises that "All generated enums automatically include an UNSPECIFIED value as the first enum entry". Embedded enums generated elsewhere in this sample (e.g. EnumTypes.h, CardType.h) do start with UNSPECIFIED, so the new top-level enums are generated differently from the pattern this README documents. The two sections now contradict each other, and users following the README's Enum Handling guidance will look for TopLevelPriority::UNSPECIFIED and won't find it. Either align the top-level enum generation with the embedded-enum pattern (add the UNSPECIFIED entry) or update the "Enum Handling" claim so the generated documentation is consistent.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/server/petstore/cpp-httplib-server/feature-test/README.md, line 527:

<comment>The added enum snippets document `models::TopLevelPriority` and `models::TopLevelStatus` without an `UNSPECIFIED` entry, but the same README's "### Enum Handling" section (line ~1049) promises that "All generated enums automatically include an `UNSPECIFIED` value as the first enum entry". Embedded enums generated elsewhere in this sample (e.g. `EnumTypes.h`, `CardType.h`) do start with `UNSPECIFIED`, so the new top-level enums are generated differently from the pattern this README documents. The two sections now contradict each other, and users following the README's Enum Handling guidance will look for `TopLevelPriority::UNSPECIFIED` and won't find it. Either align the top-level enum generation with the embedded-enum pattern (add the `UNSPECIFIED` entry) or update the "Enum Handling" claim so the generated documentation is consistent.</comment>

<file context>
@@ -520,6 +520,32 @@ std::string jsonString = json.dump();
+
+```cpp
+// Select an enum value
+auto model = models::TopLevelPriority::_0;
+
+// Serialize to JSON via the generated to_json free function
</file context>

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wing328
I am considering what is the reason of automatically adding UNSPECIFIED? In my opinion if user require UNSPECIFIED, UNSET or NONE it should be listed in openapi specs. Now we can have enum value in C++, that don't have equivalent in generated client code (ex. typescript). I can rework README or add it to top level Enum but I'm not sure what is desired way. For my needs i do not need it at all, because where i expect it I add it into specs.
What is your call?


enum class TopLevelStatus {

ACTIVE,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The enum body is emitted with whitespace-only lines ( ) between enumerators, producing trailing-whitespace lines and extra vertical padding in the generated header. This comes from the enum section of model-header.mustache, which puts each {{name}} and the closing {{/enumVars}} on their own indented lines. The same pattern appears in TopLevelPriority.h, and both .cpp files also start with an empty first line (from the blank first line in model-source.mustache). Collapse the enumerators onto a single line in the template so regenerated samples are free of trailing whitespace.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/server/petstore/cpp-httplib-server/feature-test/models/TopLevelStatus.h, line 17:

<comment>The enum body is emitted with whitespace-only lines (`    `) between enumerators, producing trailing-whitespace lines and extra vertical padding in the generated header. This comes from the enum section of `model-header.mustache`, which puts each `{{name}}` and the closing `{{/enumVars}}` on their own indented lines. The same pattern appears in `TopLevelPriority.h`, and both `.cpp` files also start with an empty first line (from the blank first line in `model-source.mustache`). Collapse the enumerators onto a single line in the template so regenerated samples are free of trailing whitespace.</comment>

<file context>
@@ -0,0 +1,65 @@
+
+enum class TopLevelStatus {
+    
+    ACTIVE,
+    
+    INACTIVE,
</file context>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants