Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/samples-java-server-jdk8.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ on:
push:
paths:
- 'samples/server/petstore/java-vertx-web/**'
- 'samples/server/petstore/java-vertx-web-interface-only/**'
- 'samples/server/petstore/java-inflector/**'
- 'samples/server/petstore/java-pkmst/**'
# test in circleci instead
- 'samples/server/petstore/java-undertow/**'
#- 'samples/server/petstore/java-undertow/**'
- 'samples/server/petstore/java-microprofile/**'
pull_request:
paths:
- 'samples/server/petstore/java-vertx-web/**'
- 'samples/server/petstore/java-vertx-web-interface-only/**'
- 'samples/server/petstore/java-inflector/**'
- 'samples/server/petstore/java-inflector/**'
- 'samples/server/petstore/java-pkmst/**'
#- 'samples/server/petstore/java-undertow/**'
Expand All @@ -26,6 +29,7 @@ jobs:
sample:
# servers
- samples/server/petstore/java-vertx-web/
- samples/server/petstore/java-vertx-web-interface-only/
- samples/server/petstore/java-inflector/
- samples/server/petstore/java-pkmst/
#- samples/server/petstore/java-undertow/
Expand Down
8 changes: 8 additions & 0 deletions bin/configs/java-vertx-web-server-interface-only.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
generatorName: java-vertx-web
outputDir: samples/server/petstore/java-vertx-web-interface-only
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/JavaVertXWebServer
additionalProperties:
hideGenerationTimestamp: "true"
artifactId: java-vertx-web-server-interface-only
interfaceOnly: "true"
2 changes: 1 addition & 1 deletion bin/generate-samples.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ declare root="$(cd "$cwd" && cd ../ && pwd)"
declare executable="${root}/modules/openapi-generator-cli/target/openapi-generator-cli.jar"

if [ ! -f "$executable" ]; then
(cd "${root}" && mvn -B --no-snapshot-updates clean package -DskipTests=true -Dmaven.javadoc.skip=true -Djacoco.skip=true)
(cd "${root}" && ./mvnw -B --no-snapshot-updates clean package -DskipTests=true -Dmaven.javadoc.skip=true -Djacoco.skip=true)
fi

export JAVA_OPTS="${JAVA_OPTS} -ea -server -Duser.timezone=UTC"
Expand Down
1 change: 1 addition & 0 deletions docs/generators/java-vertx-web.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|ignoreAnyOfInEnum|Ignore anyOf keyword in enum| |false|
|implicitHeaders|Skip header parameters in the generated API methods using @ApiImplicitParams annotation.| |false|
|implicitHeadersRegex|Skip header parameters that matches given regex in the generated API methods using @ApiImplicitParams annotation. Note: this parameter is ignored when implicitHeaders=true| |null|
|interfaceOnly|Whether to generate only API interface stubs without the server files.| |false|
|invokerPackage|root package for generated code| |org.openapitools.vertxweb.server|
|legacyDiscriminatorBehavior|Set to false for generators with better support for discriminators. (Python, Java, Go, PowerShell, C# have this enabled by default).|<dl><dt>**true**</dt><dd>The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.</dd><dt>**false**</dt><dd>The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.</dd></dl>|true|
|licenseName|The name of the license| |Unlicense|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ public class JavaVertXWebServerCodegen extends AbstractJavaCodegen {
protected String resourceFolder = "src/main/resources";
protected String apiVersion = "1.0.0-SNAPSHOT";

public static final String INTERFACE_ONLY = "interfaceOnly";
protected boolean interfaceOnly = false;

public JavaVertXWebServerCodegen() {
super();

Expand All @@ -52,11 +55,6 @@ public JavaVertXWebServerCodegen() {
modelTemplateFiles.clear();
modelTemplateFiles.put("model.mustache", ".java");

apiTemplateFiles.clear();
apiTemplateFiles.put("api.mustache", ".java");
apiTemplateFiles.put("apiImpl.mustache", "Impl.java");
apiTemplateFiles.put("apiHandler.mustache", "Handler.java");

embeddedTemplateDir = templateDir = "JavaVertXWebServer";

invokerPackage = "org.openapitools.vertxweb.server";
Expand All @@ -73,6 +71,8 @@ public JavaVertXWebServerCodegen() {
updateOption(CodegenConstants.MODEL_PACKAGE, modelPackage);
updateOption(CodegenConstants.INVOKER_PACKAGE, invokerPackage);
updateOption(DATE_LIBRARY, this.getDateLibrary());

cliOptions.add(CliOption.newBoolean(INTERFACE_ONLY, "Whether to generate only API interface stubs without the server files."));

// Override type mapping
typeMapping.put("file", "FileUpload");
Expand Down Expand Up @@ -100,6 +100,18 @@ public String getHelp() {
public void processOpts() {
super.processOpts();

if (additionalProperties.containsKey(INTERFACE_ONLY)) {
interfaceOnly = Boolean.parseBoolean(additionalProperties.get(INTERFACE_ONLY).toString());
}
additionalProperties.put(INTERFACE_ONLY, interfaceOnly);

apiTemplateFiles.clear();
apiTemplateFiles.put("api.mustache", ".java");
apiTemplateFiles.put("apiHandler.mustache", "Handler.java");
if (!interfaceOnly) {
apiTemplateFiles.put("apiImpl.mustache", "Impl.java");
}

apiTestTemplateFiles.clear();

importMapping.remove("JsonCreator");
Expand All @@ -116,7 +128,9 @@ public void processOpts() {
String sourcePackageFolder = sourceFolder + File.separator + invokerPackage.replace(".", File.separator);
supportingFiles.clear();
supportingFiles.add(new SupportingFile("supportFiles/openapi.mustache", resourceFolder, "openapi.yaml"));
supportingFiles.add(new SupportingFile("supportFiles/HttpServerVerticle.mustache", sourcePackageFolder, "HttpServerVerticle.java"));
if (!interfaceOnly) {
supportingFiles.add(new SupportingFile("supportFiles/HttpServerVerticle.mustache", sourcePackageFolder, "HttpServerVerticle.java"));
}
supportingFiles.add(new SupportingFile("supportFiles/ApiResponse.mustache", sourcePackageFolder, "ApiResponse.java"));
supportingFiles.add(new SupportingFile("supportFiles/pom.mustache", "", "pom.xml"));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ To build the project using maven, run:
mvn package
```

{{^interfaceOnly}}
To run the project, run the jar or use maven exec plugin:

```bash
mvn exec:java
```

If all builds successfully, the server should run on [http://localhost:8080/](http://localhost:8080/)
{{/interfaceOnly}}
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ public class {{classname}}Handler {
this.api = api;
}

{{^interfaceOnly}}
@Deprecated
public {{classname}}Handler() {
this(new {{classname}}Impl());
}
{{/interfaceOnly}}

public void mount(RouterBuilder builder) {
{{#operations}}
Expand All @@ -50,7 +52,17 @@ public class {{classname}}Handler {

{{#allParams}}{{>headerParams}}{{>pathParams}}{{>queryParams}}{{>formParams}}{{>bodyParams}}{{/allParams}}
{{#allParams}}
{{#isPassword}}
logger.debug("Parameter {{paramName}} is (redacted)");

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.

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>

{{/isPassword}}
{{^isPassword}}
{{#isBodyParam}}
logger.debug("Parameter {{paramName}} is (body omitted)");
{{/isBodyParam}}
{{^isBodyParam}}
logger.debug("Parameter {{paramName}} is {}", {{paramName}});
{{/isBodyParam}}
{{/isPassword}}
{{/allParams}}

api.{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@
JsonObject {{paramName}} = body != null ? body.getJsonObject() : null;
{{/isFile}}
{{#isFile}}
{{{dataType}}} {{paramName}} = routingContext.fileUploads().iterator().next();
{{{dataType}}} {{paramName}} = null;
if (routingContext.fileUploads().isEmpty()) {

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.

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).

@@ -4,6 +4,14 @@ {{#isFile}} - {{{dataType}}} {{paramName}} = routingContext.fileUploads().iterator().next(); + {{{dataType}}} {{paramName}} = null; + if (routingContext.fileUploads().isEmpty()) { +{{#required}} + routingContext.fail(400); ```

{{#required}}
routingContext.fail(400);
return;
{{/required}}
} else {
{{paramName}} = routingContext.fileUploads().iterator().next();
}
Comment on lines +7 to +15

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: 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>
Suggested change
{{{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}}
}

{{/isFile}}
{{/isFormParam}}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
<slf4j.version>1.7.30</slf4j.version>
<jackson.version>2.18.9</jackson.version>

{{^interfaceOnly}}
<main.verticle>{{invokerPackage}}.HttpServerVerticle</main.verticle>
{{/interfaceOnly}}
</properties>

<!-- Vert.x BOM -->
Expand Down Expand Up @@ -86,6 +88,7 @@
<target>1.8</target>
</configuration>
</plugin>
{{^interfaceOnly}}
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>${maven-shade-plugin.version}</version>
Expand Down Expand Up @@ -117,10 +120,12 @@
</execution>
</executions>
</plugin>
{{/interfaceOnly}}
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
</plugin>
{{^interfaceOnly}}
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
Expand All @@ -133,6 +138,7 @@
</arguments>
</configuration>
</plugin>
{{/interfaceOnly}}
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package org.openapitools.codegen.java.vertx;

import io.swagger.v3.oas.models.OpenAPI;
import org.openapitools.codegen.ClientOptInput;
import org.openapitools.codegen.DefaultGenerator;
import org.openapitools.codegen.TestUtils;
import org.openapitools.codegen.languages.JavaVertXWebServerCodegen;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class JavaVertXWebServerCodegenTest {

private JavaVertXWebServerCodegen underTest;

@BeforeMethod
public void setup() {
this.underTest = new JavaVertXWebServerCodegen();
}

@Test
public void itShouldSetTheDefaultTemplateKeys() {
underTest.processOpts();

Assert.assertTrue(underTest.apiTemplateFiles().containsKey("api.mustache"));
Assert.assertTrue(underTest.apiTemplateFiles().containsKey("apiHandler.mustache"));
Assert.assertTrue(underTest.apiTemplateFiles().containsKey("apiImpl.mustache"));
}

@Test
public void itShouldNotSetApiImplMustacheKeyWhenInterfaceOnlyIsTrue() {
underTest.additionalProperties().put(JavaVertXWebServerCodegen.INTERFACE_ONLY, "true");
underTest.processOpts();

Assert.assertFalse(underTest.apiTemplateFiles().containsKey("apiImpl.mustache"));
}

@Test
public void itShouldRedactCredentialsInBodyParams() throws IOException {

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: 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>

Map<String, File> files = generatePetstoreServer();
String apiHandlerPath = files.keySet().stream()
.filter(path -> path.endsWith("UserApiHandler.java"))
.findFirst()
.orElseThrow(() -> new AssertionError("UserApiHandler.java not found"));

File userApiHandler = files.get(apiHandlerPath);

TestUtils.assertFileContains(userApiHandler.toPath(), "logger.debug(\"Parameter user is (body omitted)\");");
}

@Test
public void itShouldCheckFileUploadEmptiness() throws IOException {
Map<String, File> files = generatePetstoreServer();
String apiHandlerPath = files.keySet().stream()
.filter(path -> path.endsWith("PetApiHandler.java"))
.findFirst()
.orElseThrow(() -> new AssertionError("PetApiHandler.java not found"));

File petApiHandler = files.get(apiHandlerPath);

TestUtils.assertFileContains(petApiHandler.toPath(), "if (routingContext.fileUploads().isEmpty()) {");
TestUtils.assertFileContains(petApiHandler.toPath(), "} else {");
TestUtils.assertFileContains(petApiHandler.toPath(), "_file = routingContext.fileUploads().iterator().next();");
}

private Map<String, File> generatePetstoreServer() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();

OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/petstore.yaml");
DefaultGenerator defaultGenerator = new DefaultGenerator();
ClientOptInput clientOptInput = new ClientOptInput();
clientOptInput.openAPI(openAPI);

JavaVertXWebServerCodegen codegen = new JavaVertXWebServerCodegen();
codegen.setOutputDir(output.getAbsolutePath());

clientOptInput.config(codegen);
defaultGenerator.opts(clientOptInput);

return defaultGenerator.generate().stream()
.collect(Collectors.toMap(File::getPath, Function.identity()));
}
}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
README.md
pom.xml
src/main/java/org/openapitools/vertxweb/server/ApiResponse.java
src/main/java/org/openapitools/vertxweb/server/api/PetApi.java
src/main/java/org/openapitools/vertxweb/server/api/PetApiHandler.java
src/main/java/org/openapitools/vertxweb/server/api/StoreApi.java
src/main/java/org/openapitools/vertxweb/server/api/StoreApiHandler.java
src/main/java/org/openapitools/vertxweb/server/api/UserApi.java
src/main/java/org/openapitools/vertxweb/server/api/UserApiHandler.java
src/main/java/org/openapitools/vertxweb/server/model/Category.java
src/main/java/org/openapitools/vertxweb/server/model/ModelApiResponse.java
src/main/java/org/openapitools/vertxweb/server/model/Order.java
src/main/java/org/openapitools/vertxweb/server/model/Pet.java
src/main/java/org/openapitools/vertxweb/server/model/Tag.java
src/main/java/org/openapitools/vertxweb/server/model/User.java
src/main/resources/openapi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
7.25.0-SNAPSHOT
12 changes: 12 additions & 0 deletions samples/server/petstore/java-vertx-web-interface-only/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Generator version: 7.25.0-SNAPSHOT

## Getting Started

This document assumes you have maven available.

To build the project using maven, run:

```bash
mvn package
```

Loading
Loading