Add support for interface only with vertx - #24655
Conversation
…ithub.com/corbs9/openapi-generator into corbs9-add-support-for-interface-only-with-vertx
There was a problem hiding this comment.
8 issues found across 32 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/test/java/org/openapitools/codegen/java/vertx/JavaVertXWebServerCodegenTest.java">
<violation number="1" location="modules/openapi-generator/src/test/java/org/openapitools/codegen/java/vertx/JavaVertXWebServerCodegenTest.java:46">
P3: This test is named "RedactCredentials" but it only asserts that a body param is logged as "(body omitted)" (`assertFileContains(..., "Parameter user is (body omitted)")`). It never exercises the `isPassword` redaction path, and the petstore spec used by `generatePetstoreServer()` contains no password-typed parameter, so the `{{#isPassword}}` branch in apiHandler.mustache is left untested despite being the core security behavior of this PR. Consider renaming the test to reflect what it covers and adding a case that generates a spec with a password parameter to verify `(redacted)` output.</violation>
</file>
<file name="samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/model/User.java">
<violation number="1" location="samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/model/User.java:144">
P2: User.toString() prints the password field in plaintext, which directly contradicts this PR's stated security goal of hiding password values in logs. If a User model is ever logged at debug level (or anywhere via toString()), the password leaks; generate the toString with the password field omitted or masked instead.</violation>
</file>
<file name="samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/api/StoreApi.java">
<violation number="1" location="samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/api/StoreApi.java:8">
P3: This generated interface imports `io.vertx.core.json.JsonObject` and `java.util.List` that are never used by any method. These are dead imports in the sample and reflect the template emitting `JsonObject`/`List` unconditionally even when no operation needs them. Consider making those imports conditional in api.mustache so generated interfaces only contain imports they actually use.</violation>
</file>
<file name="modules/openapi-generator/src/main/resources/JavaVertXWebServer/formParams.mustache">
<violation number="1" location="modules/openapi-generator/src/main/resources/JavaVertXWebServer/formParams.mustache:7">
P1: Required multipart fields are validated only against any upload, so generated handlers accept a request missing one required named file and bind the first uploaded file to every file parameter. Select `FileUpload` by `baseName` and fail when that specific parameter is absent.</violation>
<violation number="2" location="modules/openapi-generator/src/main/resources/JavaVertXWebServer/formParams.mustache:8">
P2: For non-required file form params the new template produces a logically-dead empty `if` body followed by an `else`. The generated sample shows the concrete result in `PetApiHandler.uploadFile()`:
```java
FileUpload _file = null;
if (routingContext.fileUploads().isEmpty()) {
} else {
_file = routingContext.fileUploads().iterator().next();
}
The {{#required}} gating only injects routingContext.fail(400); return; for required params, so optional file params yield an empty branch. This is awkward, easily-misread generated code. Since the whole point of the guard is to safely detect an empty upload, consider restructuring the template so optional params collapse to a single guard (or a null-safe ternary) without an empty block, e.g. FileUpload _file = routingContext.fileUploads().isEmpty() ? null : routingContext.fileUploads().iterator().next();, and for required params emit the fail/return followed by a direct assignment (no else).
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
| {{{dataType}}} {{paramName}} = null; | ||
| if (routingContext.fileUploads().isEmpty()) { | ||
| {{#required}} | ||
| routingContext.fail(400); | ||
| return; | ||
| {{/required}} | ||
| } else { | ||
| {{paramName}} = routingContext.fileUploads().iterator().next(); | ||
| } |
There was a problem hiding this comment.
P1: Required multipart fields are validated only against any upload, so generated handlers accept a request missing one required named file and bind the first uploaded file to every file parameter. Select FileUpload by baseName and fail when that specific parameter is absent.
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/JavaVertXWebServer/formParams.mustache, line 7:
<comment>Required multipart fields are validated only against any upload, so generated handlers accept a request missing one required named file and bind the first uploaded file to every file parameter. Select `FileUpload` by `baseName` and fail when that specific parameter is absent.</comment>
<file context>
@@ -4,6 +4,14 @@
{{/isFile}}
{{#isFile}}
- {{{dataType}}} {{paramName}} = routingContext.fileUploads().iterator().next();
+ {{{dataType}}} {{paramName}} = null;
+ if (routingContext.fileUploads().isEmpty()) {
+{{#required}}
</file context>
| {{{dataType}}} {{paramName}} = null; | |
| if (routingContext.fileUploads().isEmpty()) { | |
| {{#required}} | |
| routingContext.fail(400); | |
| return; | |
| {{/required}} | |
| } else { | |
| {{paramName}} = routingContext.fileUploads().iterator().next(); | |
| } | |
| {{{dataType}}} {{paramName}} = routingContext.fileUploads().stream() | |
| .filter(upload -> "{{baseName}}".equals(upload.name())) | |
| .findFirst() | |
| .orElse(null); | |
| if ({{paramName}} == null) { | |
| {{#required}} | |
| routingContext.fail(400); | |
| return; | |
| {{/required}} | |
| } |
| String password = requestParameters.queryParameter("password") != null ? requestParameters.queryParameter("password").getString() : null; | ||
|
|
||
| logger.debug("Parameter username is {}", username); | ||
| logger.debug("Parameter password is {}", password); |
There was a problem hiding this comment.
P1: The loginUser() handler logs the user's password value in plaintext (logger.debug("Parameter password is {}", password);), which writes the credential to the debug log output. This contradicts the PR's stated goal to 'hide password values' in handler logs. The redaction branch in apiHandler.mustache only triggers when isPassword is true, which is not set for this plain query parameter, so the password is leaked here. Please extend the redaction logic so the actual password value is never logged for this endpoint (e.g. emit a redacted marker for the password parameter).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/api/UserApiHandler.java, line 165:
<comment>The loginUser() handler logs the user's password value in plaintext (`logger.debug("Parameter password is {}", password);`), which writes the credential to the debug log output. This contradicts the PR's stated goal to 'hide password values' in handler logs. The redaction branch in `apiHandler.mustache` only triggers when `isPassword` is true, which is not set for this plain query parameter, so the password is leaked here. Please extend the redaction logic so the actual password value is never logged for this endpoint (e.g. emit a redacted marker for the `password` parameter).</comment>
<file context>
@@ -0,0 +1,224 @@
+ String password = requestParameters.queryParameter("password") != null ? requestParameters.queryParameter("password").getString() : null;
+
+ logger.debug("Parameter username is {}", username);
+ logger.debug("Parameter password is {}", password);
+
+ api.loginUser(username, password)
</file context>
| logger.debug("Parameter password is {}", password); | |
| logger.debug("Parameter password is (redacted)"); |
| sb.append(" firstName: ").append(toIndentedString(firstName)).append("\n"); | ||
| sb.append(" lastName: ").append(toIndentedString(lastName)).append("\n"); | ||
| sb.append(" email: ").append(toIndentedString(email)).append("\n"); | ||
| sb.append(" password: ").append(toIndentedString(password)).append("\n"); |
There was a problem hiding this comment.
P2: User.toString() prints the password field in plaintext, which directly contradicts this PR's stated security goal of hiding password values in logs. If a User model is ever logged at debug level (or anywhere via toString()), the password leaks; generate the toString with the password field omitted or masked instead.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/model/User.java, line 144:
<comment>User.toString() prints the password field in plaintext, which directly contradicts this PR's stated security goal of hiding password values in logs. If a User model is ever logged at debug level (or anywhere via toString()), the password leaks; generate the toString with the password field omitted or masked instead.</comment>
<file context>
@@ -0,0 +1,158 @@
+ sb.append(" firstName: ").append(toIndentedString(firstName)).append("\n");
+ sb.append(" lastName: ").append(toIndentedString(lastName)).append("\n");
+ sb.append(" email: ").append(toIndentedString(email)).append("\n");
+ sb.append(" password: ").append(toIndentedString(password)).append("\n");
+ sb.append(" phone: ").append(toIndentedString(phone)).append("\n");
+ sb.append(" userStatus: ").append(toIndentedString(userStatus)).append("\n");
</file context>
| sb.append(" password: ").append(toIndentedString(password)).append("\n"); | |
| sb.append(" password: ").append("[REDACTED]").append("\n"); |
| String apiKey = requestParameters.headerParameter("api_key") != null ? requestParameters.headerParameter("api_key").getString() : null; | ||
|
|
||
| logger.debug("Parameter petId is {}", petId); | ||
| logger.debug("Parameter apiKey is {}", apiKey); |
There was a problem hiding this comment.
P2: Debug logging exposes the caller's API-key credential when deletePet receives api_key; redact API-key/security-header parameters in the handler template as well as this generated sample.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/api/PetApiHandler.java, line 76:
<comment>Debug logging exposes the caller's API-key credential when `deletePet` receives `api_key`; redact API-key/security-header parameters in the handler template as well as this generated sample.</comment>
<file context>
@@ -0,0 +1,232 @@
+ String apiKey = requestParameters.headerParameter("api_key") != null ? requestParameters.headerParameter("api_key").getString() : null;
+
+ logger.debug("Parameter petId is {}", petId);
+ logger.debug("Parameter apiKey is {}", apiKey);
+
+ api.deletePet(petId, apiKey)
</file context>
| {{#allParams}}{{>headerParams}}{{>pathParams}}{{>queryParams}}{{>formParams}}{{>bodyParams}}{{/allParams}} | ||
| {{#allParams}} | ||
| {{#isPassword}} | ||
| logger.debug("Parameter {{paramName}} is (redacted)"); |
There was a problem hiding this comment.
P2: The new password-redaction path only fires when a parameter is directly flagged as isPassword, but for form-encoded submissions those parameters are never individually flagged. In JavaVertXWebServerCodegen.postProcessOperationsWithModels, any operation with non-file form params has its form params collapsed into a single dummy formBody JsonObject parameter, so a form field such as a password never reaches the {{#isPassword}} branch — it is instead dumped verbatim by the fall-through logger.debug("Parameter formBody is {}", formBody). As a result, the stated security goal of hiding password values in handler logs does not hold for form-encoded credentials, which are logged in full (at debug level) as part of the form payload. Consider omitting form-body contents from debug logging (or redacting known sensitive keys) rather than only suppressing individually-typed password params.
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/JavaVertXWebServer/apiHandler.mustache, line 56:
<comment>The new password-redaction path only fires when a parameter is directly flagged as `isPassword`, but for form-encoded submissions those parameters are never individually flagged. In `JavaVertXWebServerCodegen.postProcessOperationsWithModels`, any operation with non-file form params has its form params collapsed into a single dummy `formBody` `JsonObject` parameter, so a form field such as a `password` never reaches the `{{#isPassword}}` branch — it is instead dumped verbatim by the fall-through `logger.debug("Parameter formBody is {}", formBody)`. As a result, the stated security goal of hiding password values in handler logs does not hold for form-encoded credentials, which are logged in full (at debug level) as part of the form payload. Consider omitting form-body contents from debug logging (or redacting known sensitive keys) rather than only suppressing individually-typed password params.</comment>
<file context>
@@ -50,7 +52,17 @@ public class {{classname}}Handler {
{{#allParams}}{{>headerParams}}{{>pathParams}}{{>queryParams}}{{>formParams}}{{>bodyParams}}{{/allParams}}
{{#allParams}}
+{{#isPassword}}
+ logger.debug("Parameter {{paramName}} is (redacted)");
+{{/isPassword}}
+{{^isPassword}}
</file context>
| {{#isFile}} | ||
| {{{dataType}}} {{paramName}} = routingContext.fileUploads().iterator().next(); | ||
| {{{dataType}}} {{paramName}} = null; | ||
| if (routingContext.fileUploads().isEmpty()) { |
There was a problem hiding this comment.
P2: For non-required file form params the new template produces a logically-dead empty if body followed by an else. The generated sample shows the concrete result in PetApiHandler.uploadFile():
FileUpload _file = null;
if (routingContext.fileUploads().isEmpty()) {
} else {
_file = routingContext.fileUploads().iterator().next();
}The {{#required}} gating only injects routingContext.fail(400); return; for required params, so optional file params yield an empty branch. This is awkward, easily-misread generated code. Since the whole point of the guard is to safely detect an empty upload, consider restructuring the template so optional params collapse to a single guard (or a null-safe ternary) without an empty block, e.g. FileUpload _file = routingContext.fileUploads().isEmpty() ? null : routingContext.fileUploads().iterator().next();, and for required params emit the fail/return followed by a direct assignment (no else).
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/JavaVertXWebServer/formParams.mustache, line 8:
<comment>For non-required file form params the new template produces a logically-dead empty `if` body followed by an `else`. The generated sample shows the concrete result in `PetApiHandler.uploadFile()`:
```java
FileUpload _file = null;
if (routingContext.fileUploads().isEmpty()) {
} else {
_file = routingContext.fileUploads().iterator().next();
}
The {{#required}} gating only injects routingContext.fail(400); return; for required params, so optional file params yield an empty branch. This is awkward, easily-misread generated code. Since the whole point of the guard is to safely detect an empty upload, consider restructuring the template so optional params collapse to a single guard (or a null-safe ternary) without an empty block, e.g. FileUpload _file = routingContext.fileUploads().isEmpty() ? null : routingContext.fileUploads().iterator().next();, and for required params emit the fail/return followed by a direct assignment (no else).
| } | ||
|
|
||
| @Test | ||
| public void itShouldRedactCredentialsInBodyParams() throws IOException { |
There was a problem hiding this comment.
P3: This test is named "RedactCredentials" but it only asserts that a body param is logged as "(body omitted)" (assertFileContains(..., "Parameter user is (body omitted)")). It never exercises the isPassword redaction path, and the petstore spec used by generatePetstoreServer() contains no password-typed parameter, so the {{#isPassword}} branch in apiHandler.mustache is left untested despite being the core security behavior of this PR. Consider renaming the test to reflect what it covers and adding a case that generates a spec with a password parameter to verify (redacted) output.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/test/java/org/openapitools/codegen/java/vertx/JavaVertXWebServerCodegenTest.java, line 46:
<comment>This test is named "RedactCredentials" but it only asserts that a body param is logged as "(body omitted)" (`assertFileContains(..., "Parameter user is (body omitted)")`). It never exercises the `isPassword` redaction path, and the petstore spec used by `generatePetstoreServer()` contains no password-typed parameter, so the `{{#isPassword}}` branch in apiHandler.mustache is left untested despite being the core security behavior of this PR. Consider renaming the test to reflect what it covers and adding a case that generates a spec with a password parameter to verify `(redacted)` output.</comment>
<file context>
@@ -0,0 +1,91 @@
+ }
+
+ @Test
+ public void itShouldRedactCredentialsInBodyParams() throws IOException {
+ Map<String, File> files = generatePetstoreServer();
+ String apiHandlerPath = files.keySet().stream()
</file context>
| import org.openapitools.vertxweb.server.ApiResponse; | ||
|
|
||
| import io.vertx.core.Future; | ||
| import io.vertx.core.json.JsonObject; |
There was a problem hiding this comment.
P3: This generated interface imports io.vertx.core.json.JsonObject and java.util.List that are never used by any method. These are dead imports in the sample and reflect the template emitting JsonObject/List unconditionally even when no operation needs them. Consider making those imports conditional in api.mustache so generated interfaces only contain imports they actually use.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/server/petstore/java-vertx-web-interface-only/src/main/java/org/openapitools/vertxweb/server/api/StoreApi.java, line 8:
<comment>This generated interface imports `io.vertx.core.json.JsonObject` and `java.util.List` that are never used by any method. These are dead imports in the sample and reflect the template emitting `JsonObject`/`List` unconditionally even when no operation needs them. Consider making those imports conditional in api.mustache so generated interfaces only contain imports they actually use.</comment>
<file context>
@@ -0,0 +1,18 @@
+import org.openapitools.vertxweb.server.ApiResponse;
+
+import io.vertx.core.Future;
+import io.vertx.core.json.JsonObject;
+
+import java.util.List;
</file context>
based on #24497 with updated workflow
PR checklist
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.
Summary by cubic
Adds
interfaceOnlysupport to thejava-vertx-webserver generator so you can generate only API interfaces and handlers, without server bootstrap files. Also hardens handler logging and multipart handling, adds tests, docs, and a new sample.New Features
interfaceOnlyboolean forjava-vertx-web; when true, generate onlyapi+Handler(omitapiImplandHttpServerVerticle) and adjust generatedREADME/pom.samples/server/petstore/java-vertx-web-interface-onlyand CI workflow updated to build it.interfaceOnlyindocs/generators/java-vertx-web.md.Bug Fixes
fileUploads()and return 400 when a required file is missing.interfaceOnlytemplate selection, redacted logging, and file upload checks.Written for commit 759954e. Summary will update on new commits.