diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index 76c8a9a864f..dbe6db54b6e 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -21,4 +21,4 @@ jobs: - name: Jacoco Aggregate run: mvn jacoco:report-aggregate - name: Publish to Sonar - run: mvn sonar:sonar -Dsonar.projectKey=OpenAPITools_openapi-generator -Dsonar.organization=openapitools -Dsonar.host.url=https://sonarcloud.io -Dsonar.login=${{ secrets.SONAR_LOGIN }} -Dsonar.branch.name=${GITHUB_REF##*/} + run: mvn -B -q sonar:sonar -Dsonar.projectKey=OpenAPITools_openapi-generator -Dsonar.organization=openapitools -Dsonar.host.url=https://sonarcloud.io -Dsonar.login=${{ secrets.SONAR_LOGIN }} -Dsonar.branch.name=${GITHUB_REF##*/} diff --git a/bin/ci/java-jaxrs-datelib-j8.json b/bin/ci/java-jaxrs-datelib-j8.json index 89e9b9c7fec..f97c7cfa718 100644 --- a/bin/ci/java-jaxrs-datelib-j8.json +++ b/bin/ci/java-jaxrs-datelib-j8.json @@ -1,5 +1,5 @@ { - "!include": "./bin/jaxrs-datelib-j8.json", + "!include": "bin/jaxrs-datelib-j8.json", "generatorName": "jaxrs-jersey", "inputSpec": "modules/openapi-generator/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml", "outputDir": "samples/server/petstore/jaxrs-datelib-j8/", diff --git a/bin/ci/java-jaxrs-resteasy-eap-java8-server.json b/bin/ci/java-jaxrs-resteasy-eap-java8-server.json index d16a63b474c..2a7af42d82b 100644 --- a/bin/ci/java-jaxrs-resteasy-eap-java8-server.json +++ b/bin/ci/java-jaxrs-resteasy-eap-java8-server.json @@ -1,5 +1,5 @@ { - "!include": "./bin/jaxrs-resteasy-eap-java8-petstore-server.json", + "!include": "bin/jaxrs-resteasy-eap-java8-petstore-server.json", "artifactId": "jaxrs-resteasy-eap-java8-server", "generatorName": "jaxrs-resteasy-eap", "inputSpec": "modules/openapi-generator/src/test/resources/2_0/petstore.yaml", diff --git a/bin/ci/java-jaxrs-resteasy-eap-joda-server.json b/bin/ci/java-jaxrs-resteasy-eap-joda-server.json index d974ed72f8e..242744045b1 100644 --- a/bin/ci/java-jaxrs-resteasy-eap-joda-server.json +++ b/bin/ci/java-jaxrs-resteasy-eap-joda-server.json @@ -1,5 +1,5 @@ { - "!include": "./bin/jaxrs-resteasy-eap-joda-petstore-server.json", + "!include": "bin/jaxrs-resteasy-eap-joda-petstore-server.json", "artifactId": "jaxrs-resteasy-eap-joda-server", "generatorName": "jaxrs-resteasy-eap", "inputSpec": "modules/openapi-generator/src/test/resources/2_0/petstore.yaml", diff --git a/bin/ci/java-jaxrs-resteasy-joda-server.json b/bin/ci/java-jaxrs-resteasy-joda-server.json index ea2484571fe..aa243b2b0d2 100644 --- a/bin/ci/java-jaxrs-resteasy-joda-server.json +++ b/bin/ci/java-jaxrs-resteasy-joda-server.json @@ -1,5 +1,5 @@ { - "!include": "./bin/jaxrs-resteasy-joda-petstore-server.json", + "!include": "bin/jaxrs-resteasy-joda-petstore-server.json", "artifactId": "jaxrs-resteasy-joda-server", "generatorName": "jaxrs-resteasy", "inputSpec": "modules/openapi-generator/src/test/resources/2_0/petstore.yaml", diff --git a/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/GenerateBatch.java b/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/GenerateBatch.java index f1e6632b339..fde5f6ea4a3 100644 --- a/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/GenerateBatch.java +++ b/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/GenerateBatch.java @@ -123,27 +123,12 @@ public class GenerateBatch implements Runnable { } } - LOGGER.info(String.format(Locale.ROOT, "Batch generation using up to %d threads.\nIncludes: %s\nRoot: %s", numThreads, includesDir.getAbsolutePath(), rootDir.toAbsolutePath().toString())); // Create a module which loads our config files, but supports a special "!include" key which can point to an existing config file. // This allows us to create a sort of meta-config which holds configs which are otherwise required at CLI time (via generate task). // That is, this allows us to create a wrapper config for generatorName, inputSpec, outputDir, etc. - SimpleModule module = new SimpleModule("GenerateBatch"); - module.setDeserializerModifier(new BeanDeserializerModifier() { - @Override - public JsonDeserializer modifyDeserializer(DeserializationConfig config, - BeanDescription bd, JsonDeserializer original) { - JsonDeserializer result; - if (bd.getBeanClass() == DynamicSettings.class) { - result = new DynamicSettingsRefSupport(original, includesDir); - } else { - result = original; - } - return result; - } - }); - + SimpleModule module = getCustomDeserializationModel(includesDir); List configurators = configs.stream().map(config -> CodegenConfigurator.fromFile(config, module)).collect(Collectors.toList()); // it doesn't make sense to interleave INFO level logs, so limit these to only ERROR. @@ -169,6 +154,8 @@ public class GenerateBatch implements Runnable { System.out.println("COMPLETE."); } catch (InterruptedException e) { e.printStackTrace(); + // re-interrupt + Thread.currentThread().interrupt(); } } @@ -227,6 +214,28 @@ public class GenerateBatch implements Runnable { } } + static SimpleModule getCustomDeserializationModel(final File includesDir) { + // Create a module which loads our config files, but supports a special "!include" key which can point to an existing config file. + // This allows us to create a sort of meta-config which holds configs which are otherwise required at CLI time (via generate task). + // That is, this allows us to create a wrapper config for generatorName, inputSpec, outputDir, etc. + SimpleModule module = new SimpleModule("GenerateBatch"); + module.setDeserializerModifier(new BeanDeserializerModifier() { + @Override + public JsonDeserializer modifyDeserializer(DeserializationConfig config, + BeanDescription bd, JsonDeserializer original) { + JsonDeserializer result; + if (bd.getBeanClass() == DynamicSettings.class) { + result = new DynamicSettingsRefSupport(original, includesDir); + } else { + result = original; + } + return result; + } + }); + + return module; + } + static class DynamicSettingsRefSupport extends DelegatingDeserializer { private static final String INCLUDE = "!include"; private File scanDir; @@ -255,11 +264,13 @@ public class GenerateBatch implements Runnable { // load the file into the tree node and continue parsing as normal ((ObjectNode) node).remove(INCLUDE); - JsonParser includeParser = codec.getFactory().createParser(includeFile); - TreeNode includeNode = includeParser.readValueAsTree(); + TreeNode includeNode; + try (JsonParser includeParser = codec.getFactory().createParser(includeFile)) { + includeNode = includeParser.readValueAsTree(); + } ObjectReader reader = codec.readerForUpdating(node); - TreeNode updated = reader.readValue(includeFile); + TreeNode updated = reader.readValue(includeNode.traverse()); JsonParser updatedParser = updated.traverse(); updatedParser.nextToken(); return super.deserialize(updatedParser, ctx); diff --git a/modules/openapi-generator-cli/src/test/java/org/openapitools/codegen/cmd/GenerateBatchTest.java b/modules/openapi-generator-cli/src/test/java/org/openapitools/codegen/cmd/GenerateBatchTest.java new file mode 100644 index 00000000000..c06d7abfb93 --- /dev/null +++ b/modules/openapi-generator-cli/src/test/java/org/openapitools/codegen/cmd/GenerateBatchTest.java @@ -0,0 +1,102 @@ +package org.openapitools.codegen.cmd; + +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.openapitools.codegen.config.CodegenConfigurator; +import org.openapitools.codegen.config.Context; +import org.openapitools.codegen.config.GeneratorSettings; +import org.openapitools.codegen.config.WorkflowSettings; +import org.testng.ITestContext; +import org.testng.TestRunner; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import static org.testng.Assert.*; + +@SuppressWarnings("RedundantThrows") +public class GenerateBatchTest { + private static final String SPEC_FILE = "batch/specs/petstore.yaml"; + private static final String JAXRS_DATELIB_J8_JSON = "jaxrs-datelib-j8.json"; + private static final String JAXRS_DATELIB_J8_YAML = "jaxrs-datelib-j8.yaml"; + private static final String JAXRS_DATELIB_J8_YAML_INCLUDE_JSON = "jaxrs-datelib-j8-yaml-include.json"; + private static final String JAXRS_DATELIB_J8_JSON_INCLUDE_YAML = "jaxrs-datelib-j8-json-include.yaml"; + Path workingDirectory; + + @BeforeTest + public void setUp(ITestContext ctx) throws IOException { + workingDirectory = Paths.get("src", "test", "resources", "batch"); + } + + @DataProvider(name = "customIncludeDeserializerFiles") + public Object[][] customIncludeDeserializerFiles() { + return new Object[][] { + {JAXRS_DATELIB_J8_JSON}, + {JAXRS_DATELIB_J8_YAML}, + {JAXRS_DATELIB_J8_JSON_INCLUDE_YAML} + }; + } + + @Test(dataProvider = "customIncludeDeserializerFiles") + public void testDeserializerWithJsonInclude(String file) throws IOException { + String config = getTargetResourceAsFile(file).toString(); + SimpleModule module = GenerateBatch.getCustomDeserializationModel(getIncludesDir()); + CodegenConfigurator loaded = CodegenConfigurator.fromFile(config, module); + + Map expectedAdditionalProperties = new HashMap<>(); + expectedAdditionalProperties.put("serverPort", "8082"); + expectedAdditionalProperties.put("dateLibrary", "java8"); + expectedAdditionalProperties.put("hideGenerationTimestamp", true); + expectedAdditionalProperties.put("serializableModel", true); + expectedAdditionalProperties.put("withXml", true); + expectedAdditionalProperties.put("java8", true); + expectedAdditionalProperties.put("useBeanValidation", true); + + assertNotNull(loaded); + + Context context = loaded.toContext(); + WorkflowSettings workflowSettings = context.getWorkflowSettings(); + GeneratorSettings generatorSettings = context.getGeneratorSettings(); + + assertNotNull(workflowSettings); + assertNotNull(generatorSettings); + + assertEquals(generatorSettings.getGeneratorName(), "jaxrs-jersey"); + assertEquals(workflowSettings.getOutputDir(), "outputDir"); + assertEquals(workflowSettings.getInputSpec(), SPEC_FILE); + assertTrue(generatorSettings.getAdditionalProperties().size() >= 7); + + Set> actualSet = generatorSettings.getAdditionalProperties().entrySet(); + assertTrue(actualSet.containsAll(expectedAdditionalProperties.entrySet())); + } + + @SuppressWarnings("unused") + @Test( + expectedExceptions = { RuntimeException.class }, + expectedExceptionsMessageRegExp = "Unable to deserialize config file: .*" + ) + public void testInvalidDeserializerWithIncludeOption() { + // JSON is valid YAML, but not the other way around, so we can't load a YAML include from a JSON config + // to do so would require additional work. + String config = getTargetResourceAsFile(JAXRS_DATELIB_J8_YAML_INCLUDE_JSON).toString(); + SimpleModule module = GenerateBatch.getCustomDeserializationModel(getIncludesDir()); + CodegenConfigurator loaded = CodegenConfigurator.fromFile(config, module); + fail("Expected an exception when trying to load a YAML include from a JSON file"); + } + + private File getIncludesDir() { + // The includes directory would be "batch" under resources here, as everything is relative to this directory. + return workingDirectory.toFile(); + } + + private File getTargetResourceAsFile(String relative) { + return workingDirectory.resolve(relative).toAbsolutePath().toFile(); + } +} \ No newline at end of file diff --git a/modules/openapi-generator-cli/src/test/resources/batch/common/jaxrs-datelib-j8.json b/modules/openapi-generator-cli/src/test/resources/batch/common/jaxrs-datelib-j8.json new file mode 100644 index 00000000000..45c69a2d305 --- /dev/null +++ b/modules/openapi-generator-cli/src/test/resources/batch/common/jaxrs-datelib-j8.json @@ -0,0 +1,7 @@ +{ + "serializableModel": true, + "withXml": true, + "dateLibrary": "java8", + "java8": true, + "useBeanValidation": true +} diff --git a/modules/openapi-generator-cli/src/test/resources/batch/common/jaxrs-datelib-j8.yaml b/modules/openapi-generator-cli/src/test/resources/batch/common/jaxrs-datelib-j8.yaml new file mode 100644 index 00000000000..216aa6d2dcb --- /dev/null +++ b/modules/openapi-generator-cli/src/test/resources/batch/common/jaxrs-datelib-j8.yaml @@ -0,0 +1,6 @@ +--- +serializableModel: true +withXml: true +dateLibrary: java8 +java8: true +useBeanValidation: true diff --git a/modules/openapi-generator-cli/src/test/resources/batch/jaxrs-datelib-j8-json-include.yaml b/modules/openapi-generator-cli/src/test/resources/batch/jaxrs-datelib-j8-json-include.yaml new file mode 100644 index 00000000000..121076c9652 --- /dev/null +++ b/modules/openapi-generator-cli/src/test/resources/batch/jaxrs-datelib-j8-json-include.yaml @@ -0,0 +1,8 @@ +--- +"!include": common/jaxrs-datelib-j8.json +generatorName: jaxrs-jersey +inputSpec: batch/specs/petstore.yaml +outputDir: outputDir +additionalProperties: + hideGenerationTimestamp: true + serverPort: '8082' diff --git a/modules/openapi-generator-cli/src/test/resources/batch/jaxrs-datelib-j8-yaml-include.json b/modules/openapi-generator-cli/src/test/resources/batch/jaxrs-datelib-j8-yaml-include.json new file mode 100644 index 00000000000..bb8957cf653 --- /dev/null +++ b/modules/openapi-generator-cli/src/test/resources/batch/jaxrs-datelib-j8-yaml-include.json @@ -0,0 +1,10 @@ +{ + "!include": "common/jaxrs-datelib-j8.yaml", + "generatorName": "jaxrs-jersey", + "inputSpec": "batch/specs/petstore.yaml", + "outputDir": "outputDir", + "additionalProperties": { + "hideGenerationTimestamp": true, + "serverPort": "8082" + } +} \ No newline at end of file diff --git a/modules/openapi-generator-cli/src/test/resources/batch/jaxrs-datelib-j8.json b/modules/openapi-generator-cli/src/test/resources/batch/jaxrs-datelib-j8.json new file mode 100644 index 00000000000..c3f03859f04 --- /dev/null +++ b/modules/openapi-generator-cli/src/test/resources/batch/jaxrs-datelib-j8.json @@ -0,0 +1,10 @@ +{ + "!include": "common/jaxrs-datelib-j8.json", + "generatorName": "jaxrs-jersey", + "inputSpec": "batch/specs/petstore.yaml", + "outputDir": "outputDir", + "additionalProperties": { + "hideGenerationTimestamp": true, + "serverPort": "8082" + } +} \ No newline at end of file diff --git a/modules/openapi-generator-cli/src/test/resources/batch/jaxrs-datelib-j8.yaml b/modules/openapi-generator-cli/src/test/resources/batch/jaxrs-datelib-j8.yaml new file mode 100644 index 00000000000..bab10df8dd1 --- /dev/null +++ b/modules/openapi-generator-cli/src/test/resources/batch/jaxrs-datelib-j8.yaml @@ -0,0 +1,8 @@ +--- +"!include": common/jaxrs-datelib-j8.yaml +generatorName: jaxrs-jersey +inputSpec: batch/specs/petstore.yaml +outputDir: outputDir +additionalProperties: + hideGenerationTimestamp: true + serverPort: '8082' diff --git a/modules/openapi-generator-cli/src/test/resources/batch/specs/petstore.yaml b/modules/openapi-generator-cli/src/test/resources/batch/specs/petstore.yaml new file mode 100644 index 00000000000..10f4c499ff8 --- /dev/null +++ b/modules/openapi-generator-cli/src/test/resources/batch/specs/petstore.yaml @@ -0,0 +1,111 @@ +openapi: "3.0.0" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/modules/openapi-generator-maven-plugin/src/main/java/org/openapitools/codegen/plugin/CodeGenMojo.java b/modules/openapi-generator-maven-plugin/src/main/java/org/openapitools/codegen/plugin/CodeGenMojo.java index 6dc8fb5fd0f..49fb45efa07 100644 --- a/modules/openapi-generator-maven-plugin/src/main/java/org/openapitools/codegen/plugin/CodeGenMojo.java +++ b/modules/openapi-generator-maven-plugin/src/main/java/org/openapitools/codegen/plugin/CodeGenMojo.java @@ -777,12 +777,13 @@ public class CodeGenMojo extends AbstractMojo { conn.setRequestProperty(auth.getKeyName(), auth.getValue()); } } - ReadableByteChannel readableByteChannel = Channels.newChannel(conn.getInputStream()); - - FileOutputStream fileOutputStream = new FileOutputStream(inputSpecTempFile); - FileChannel fileChannel = fileOutputStream.getChannel(); - - fileChannel.transferFrom(readableByteChannel, 0, Long.MAX_VALUE); + try (ReadableByteChannel readableByteChannel = Channels.newChannel(conn.getInputStream())) { + FileChannel fileChannel; + try (FileOutputStream fileOutputStream = new FileOutputStream(inputSpecTempFile)) { + fileChannel = fileOutputStream.getChannel(); + fileChannel.transferFrom(readableByteChannel, 0, Long.MAX_VALUE); + } + } } ByteSource inputSpecByteSource = diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java index ab6869f5c37..0285772a69a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java @@ -49,7 +49,9 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp public static final String NPM_VERSION = "npmVersion"; public static final String SNAPSHOT = "snapshot"; - protected static final SimpleDateFormat SNAPSHOT_SUFFIX_FORMAT = new SimpleDateFormat("yyyyMMddHHmm", Locale.ROOT); + // NOTE: SimpleDateFormat is not thread-safe and may not be static unless it is thread-local + @SuppressWarnings("squid:S5164") + protected static final ThreadLocal SNAPSHOT_SUFFIX_FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMddHHmm", Locale.ROOT)); protected String modelPropertyNaming = "camelCase"; protected Boolean supportsES6 = false; @@ -158,7 +160,7 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp " Required to generate a full package")); this.cliOptions.add(new CliOption(NPM_VERSION, "The version of your npm package. If not provided, using the version from the OpenAPI specification file.").defaultValue(this.getNpmVersion())); this.cliOptions.add(CliOption.newBoolean(SNAPSHOT, - "When setting this property to true, the version will be suffixed with -SNAPSHOT." + this.SNAPSHOT_SUFFIX_FORMAT.toPattern(), + "When setting this property to true, the version will be suffixed with -SNAPSHOT." + this.SNAPSHOT_SUFFIX_FORMAT.get().toPattern(), false)); } @@ -204,9 +206,9 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp if (additionalProperties.containsKey(SNAPSHOT) && Boolean.parseBoolean(additionalProperties.get(SNAPSHOT).toString())) { if (npmVersion.toUpperCase(Locale.ROOT).matches("^.*-SNAPSHOT$")) { - this.setNpmVersion(npmVersion + "." + SNAPSHOT_SUFFIX_FORMAT.format(new Date())); + this.setNpmVersion(npmVersion + "." + SNAPSHOT_SUFFIX_FORMAT.get().format(new Date())); } else { - this.setNpmVersion(npmVersion + "-SNAPSHOT." + SNAPSHOT_SUFFIX_FORMAT.format(new Date())); + this.setNpmVersion(npmVersion + "-SNAPSHOT." + SNAPSHOT_SUFFIX_FORMAT.get().format(new Date())); } } additionalProperties.put(NPM_VERSION, npmVersion); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaCXFExtServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaCXFExtServerCodegen.java index 355afddd0b5..0ed1116bff6 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaCXFExtServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaCXFExtServerCodegen.java @@ -25,24 +25,11 @@ import java.math.BigDecimal; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Base64; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; -import java.util.TreeSet; +import java.util.*; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.openapitools.codegen.CliOption; -import org.openapitools.codegen.CodegenConstants; import org.openapitools.codegen.CodegenModel; import org.openapitools.codegen.CodegenOperation; import org.openapitools.codegen.CodegenParameter; @@ -275,10 +262,25 @@ public class JavaCXFExtServerCodegen extends JavaCXFServerCodegen implements CXF private static final String INDENT = " "; - private static final SimpleDateFormat ISO8601_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); + // SimpleDateFormat is not thread-safe, and may not be stored in a static field unless stored by ThreadLocal. + // It's not enough to add a ThreadLocal at the usage site. + @SuppressWarnings("squid:S5164") + private static final ThreadLocal ISO8601_DATE_FORMAT = ThreadLocal.withInitial(() -> + { + SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); + f.setTimeZone(TimeZone.getTimeZone("UTC")); + return f; + }); - private static final SimpleDateFormat ISO8601_DATETIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", - Locale.getDefault()); + // SimpleDateFormat is not thread-safe, and may not be stored in a static field unless stored by ThreadLocal. + // It's not enough to add a ThreadLocal at the usage site. + @SuppressWarnings("squid:S5164") + private static final ThreadLocal ISO8601_DATETIME_FORMAT = ThreadLocal.withInitial(() -> + { + SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.getDefault()); + f.setTimeZone(TimeZone.getTimeZone("UTC")); + return f; + }); private static final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000; @@ -292,13 +294,11 @@ public class JavaCXFExtServerCodegen extends JavaCXFServerCodegen implements CXF "LocalDateTime", "LocalDate"); static { - ISO8601_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); - ISO8601_DATETIME_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); long minDate = 0; long maxDate = 0; try { - minDate = ISO8601_DATETIME_FORMAT.parse("1970-01-01T00:00:00Z").getTime(); - maxDate = ISO8601_DATETIME_FORMAT.parse("2099-12-31T23:59:59Z").getTime(); + minDate = ISO8601_DATETIME_FORMAT.get().parse("1970-01-01T00:00:00Z").getTime(); + maxDate = ISO8601_DATETIME_FORMAT.get().parse("2099-12-31T23:59:59Z").getTime(); } catch (ParseException e) { // Won't happen with the values provided. } @@ -383,12 +383,15 @@ public class JavaCXFExtServerCodegen extends JavaCXFServerCodegen implements CXF short max = var == null || var.maximum == null ? Byte.MAX_VALUE : Byte.parseByte(var.maximum); short exclusiveMin = (short) (var != null && var.exclusiveMinimum ? 1 : 0); short inclusiveMax = (short) (var == null || !var.exclusiveMaximum ? 1 : 0); - int itemCount = Math.max(var.itemCount, var.minItems == null ? 1 : Math.max(1, var.minItems)); + int itemCount = 0; + if (var != null) { + itemCount = Math.max(var.itemCount, var.minItems == null ? 1 : Math.max(1, var.minItems)); + } byte[] randomBytes = new byte[itemCount]; for (int i = 0; i < itemCount; i++) randomBytes[i] = (byte) (min + exclusiveMin + ((max + inclusiveMax - min - exclusiveMin) * Math.random())); String randomBytesBase64 = Base64.getEncoder().encodeToString(randomBytes); - if (loadTestDataFromFile) + if (loadTestDataFromFile && var != null) var.addTestData(randomBytesBase64); else buffer.append('"'); @@ -431,13 +434,10 @@ public class JavaCXFExtServerCodegen extends JavaCXFServerCodegen implements CXF * @param buffer * @param indent * @param op + * @param var * @param localVars * @param models - * @param type - * @param baseType - * @param isListContainer - * @param isMapContainer - * @param localVar + * * @return localVar with a numeric suffix if necessary to ensure uniqueness. */ private String appendLocalVariable(StringBuilder buffer, String indent, CodegenOperation op, CodegenVariable var, @@ -582,7 +582,7 @@ public class JavaCXFExtServerCodegen extends JavaCXFServerCodegen implements CXF long minDate = MIN_DATE; long maxDate = MAX_DATE; if (var != null) { - DateFormat df = var.dataFormat.equals("date-time") ? ISO8601_DATETIME_FORMAT : ISO8601_DATE_FORMAT; + DateFormat df = var.dataFormat.equals("date-time") ? ISO8601_DATETIME_FORMAT.get() : ISO8601_DATE_FORMAT.get(); String isoFormat = var.dataFormat.equals("date-time") ? "date-time" : "full-date"; if (var.minimum != null) { try { @@ -622,10 +622,10 @@ public class JavaCXFExtServerCodegen extends JavaCXFServerCodegen implements CXF Date randomDate = new Date(randomDateLong); switch (var.dataFormat) { case "date": - var.addTestData(ISO8601_DATE_FORMAT.format(randomDate)); + var.addTestData(ISO8601_DATE_FORMAT.get().format(randomDate)); break; case "date-time": - var.addTestData(ISO8601_DATETIME_FORMAT.format(randomDate)); + var.addTestData(ISO8601_DATETIME_FORMAT.get().format(randomDate)); break; } } else { @@ -787,7 +787,6 @@ public class JavaCXFExtServerCodegen extends JavaCXFServerCodegen implements CXF * @param localVar The variable whose value is to be set. * @param localVars Tracks local variables which have been allocated. * @param models A map of models, keyed on class name. - * @param type The value type. */ private void appendScalarValue(StringBuilder buffer, String indent, CodegenOperation op, CodegenVariable var, String localVar, Collection localVars, Map models) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonAbstractConnexionServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonAbstractConnexionServerCodegen.java index 3954e98f9bb..21dd07fb653 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonAbstractConnexionServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonAbstractConnexionServerCodegen.java @@ -516,7 +516,7 @@ public class PythonAbstractConnexionServerCodegen extends DefaultCodegen impleme if (pathExtensions != null) { // Get and remove the (temporary) vendor extension String openapiPathname = (String) pathExtensions.remove("x-python-connexion-openapi-name"); - if (openapiPathname != null && openapiPathname != pythonPathname) { + if (openapiPathname != null && !openapiPathname.equals(pythonPathname)) { LOGGER.info("Path '" + pythonPathname + "' is not consistant with the original OpenAPI definition. It will be replaced back by '" + openapiPathname + "'"); paths.remove(pythonPathname); paths.put(openapiPathname, path); @@ -535,7 +535,7 @@ public class PythonAbstractConnexionServerCodegen extends DefaultCodegen impleme String swaggerParameterName = (String) parameterExtensions.remove("x-python-connexion-openapi-name"); if (swaggerParameterName != null) { String pythonParameterName = parameter.getName(); - if (swaggerParameterName != pythonParameterName) { + if (!swaggerParameterName.equals(pythonParameterName)) { LOGGER.info("Reverting name of parameter '" + pythonParameterName + "' of operation '" + operation.getOperationId() + "' back to '" + swaggerParameterName + "'"); parameter.setName(swaggerParameterName); } else { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientExperimentalCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientExperimentalCodegen.java index 7e31e615773..b2a6a04b958 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientExperimentalCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientExperimentalCodegen.java @@ -818,7 +818,7 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen { if (ModelUtils.isFreeFormObject(p) && ModelUtils.getAdditionalProperties(p) == null) { return prefix + "bool, date, datetime, dict, float, int, list, str" + fullSuffix; } - if ((ModelUtils.isMapSchema(p) || p.getType() == "object") && ModelUtils.getAdditionalProperties(p) != null) { + if ((ModelUtils.isMapSchema(p) || "object".equals(p.getType())) && ModelUtils.getAdditionalProperties(p) != null) { Schema inner = ModelUtils.getAdditionalProperties(p); return prefix + "{str: " + getTypeString(inner, "(", ")") + "}" + fullSuffix; } else if (ModelUtils.isArraySchema(p)) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java index 273978d0a71..a6551c03012 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java @@ -710,11 +710,7 @@ public class RClientCodegen extends DefaultCodegen implements CodegenConfig { } else if (codegenParameter.isMapContainer) { // TODO: map return "TODO"; } else if (languageSpecificPrimitives.contains(codegenParameter.dataType)) { // primitive type - if ("character".equals(codegenParameter.dataType)) { - return codegenParameter.example; - } else { - return codegenParameter.example; - } + return codegenParameter.example; } else { // model // look up the model if (modelMaps.containsKey(codegenParameter.dataType)) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java index ed185188667..8815ca4e511 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java @@ -664,7 +664,7 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig { // Get the original API response so we get process the schema // directly. ApiResponse original; - if (rsp.code == "0") { + if ("0".equals(rsp.code)) { original = operation.getResponses().get("default"); } else { original = operation.getResponses().get(rsp.code); @@ -714,10 +714,7 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig { String firstProduces = null; if (original.getContent() != null) { - for (String mimetype : original.getContent().keySet()) { - firstProduces = mimetype; - break; - } + firstProduces = original.getContent().keySet().stream().findFirst().orElse(null); } // The output mime type. This allows us to do sensible fallback diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift4Codegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift4Codegen.java index eb669b02df1..77389ce3f90 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift4Codegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift4Codegen.java @@ -1011,7 +1011,7 @@ public class Swift4Codegen extends DefaultCodegen implements CodegenConfig { return "\"" + codegenParameter.paramName + "_example\""; } } else if ("Bool".equals(codegenParameter.dataType)) { // boolean - if (Boolean.TRUE.equals(codegenParameter.example)) { + if (Boolean.parseBoolean(codegenParameter.example)) { return "true"; } else { return "false"; @@ -1051,7 +1051,7 @@ public class Swift4Codegen extends DefaultCodegen implements CodegenConfig { return "\"" + codegenProperty.name + "_example\""; } } else if ("Bool".equals(codegenProperty.dataType)) { // boolean - if (Boolean.TRUE.equals(codegenProperty.example)) { + if (Boolean.parseBoolean(codegenProperty.example)) { return "true"; } else { return "false"; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java index 5bc9f1ad4d4..610da984c85 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java @@ -993,7 +993,7 @@ public class Swift5ClientCodegen extends DefaultCodegen implements CodegenConfig return "\"" + codegenParameter.paramName + "_example\""; } } else if ("Bool".equals(codegenParameter.dataType)) { // boolean - if (Boolean.TRUE.equals(codegenParameter.example)) { + if (Boolean.parseBoolean(codegenParameter.example)) { return "true"; } else { return "false"; @@ -1033,7 +1033,7 @@ public class Swift5ClientCodegen extends DefaultCodegen implements CodegenConfig return "\"" + codegenProperty.name + "_example\""; } } else if ("Bool".equals(codegenProperty.dataType)) { // boolean - if (Boolean.TRUE.equals(codegenProperty.example)) { + if (Boolean.parseBoolean(codegenProperty.example)) { return "true"; } else { return "false"; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java index b21869c7489..7e6bdd3d131 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java @@ -283,7 +283,7 @@ public class TypeScriptFetchClientCodegen extends AbstractTypeScriptClientCodege Map _operations = (Map) operations.get("operations"); List operationList = (List) _operations.get("operation"); for (CodegenOperation op : operationList) { - if(op.returnType == "object") { + if("object".equals(op.returnType)) { op.isMapContainer = true; op.returnSimpleType = false; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptReduxQueryClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptReduxQueryClientCodegen.java index 560083e6018..020752f84a0 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptReduxQueryClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptReduxQueryClientCodegen.java @@ -255,7 +255,7 @@ public class TypeScriptReduxQueryClientCodegen extends AbstractTypeScriptClientC Map _operations = (Map) operations.get("operations"); List operationList = (List) _operations.get("operation"); for (CodegenOperation op : operationList) { - if(op.returnType == "object") { + if("object".equals(op.returnType)) { op.isMapContainer = true; op.returnSimpleType = false; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java index cf2ca082caa..a539cddb1fd 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java @@ -35,11 +35,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; import java.util.stream.Collectors; @@ -246,10 +242,14 @@ public class ModelUtils { if (parameters != null) { for (Parameter p : parameters) { Parameter parameter = getReferencedParameter(openAPI, p); - if (parameter.getSchema() != null) { - visitSchema(openAPI, parameter.getSchema(), null, visitedSchemas, visitor); + if (parameter != null) { + if (parameter.getSchema() != null) { + visitSchema(openAPI, parameter.getSchema(), null, visitedSchemas, visitor); + } + visitContent(openAPI, parameter.getContent(), visitor, visitedSchemas); + } else { + LOGGER.warn("Unreferenced parameter found."); } - visitContent(openAPI, parameter.getContent(), visitor, visitedSchemas); } } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/URLPathUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/URLPathUtils.java index abb50ca4963..e1d3611fedf 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/URLPathUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/URLPathUtils.java @@ -195,29 +195,31 @@ public class URLPathUtils { */ public static String getHost(OpenAPI openAPI, final Map userDefinedVariables) { if (openAPI.getServers() != null && openAPI.getServers().size() > 0) { - return sanitizeUrl(getServerURL(openAPI.getServers().get(0), userDefinedVariables).toString()); + URL url = getServerURL(openAPI.getServers().get(0), userDefinedVariables); + return url != null ? sanitizeUrl(url.toString()) : ""; } return LOCAL_HOST; } private static String sanitizeUrl(String url) { - if (url.startsWith("//")) { - url = "http:" + url; - LOGGER.warn("'scheme' not defined in the spec (2.0). Default to [http] for server URL [{}]", url); - } else if (url.startsWith("/")) { - url = LOCAL_HOST + url; - LOGGER.warn("'host' (OAS 2.0) or 'servers' (OAS 3.0) not defined in the spec. Default to [{}] for server URL [{}]", LOCAL_HOST, url); - } else if (!url.matches("[a-zA-Z][0-9a-zA-Z.+\\-]+://.+")) { - // Add http scheme for urls without a scheme. - // 2.0 spec is restricted to the following schemes: "http", "https", "ws", "wss" - // 3.0 spec does not have an enumerated list of schemes - // This regex attempts to capture all schemes in IANA example schemes which - // can have alpha-numeric characters and [.+-]. Examples are here: - // https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml - url = "http://" + url; - LOGGER.warn("'scheme' not defined in the spec (2.0). Default to [http] for server URL [{}]", url); + if (url != null) { + if (url.startsWith("//")) { + url = "http:" + url; + LOGGER.warn("'scheme' not defined in the spec (2.0). Default to [http] for server URL [{}]", url); + } else if (url.startsWith("/")) { + url = LOCAL_HOST + url; + LOGGER.warn("'host' (OAS 2.0) or 'servers' (OAS 3.0) not defined in the spec. Default to [{}] for server URL [{}]", LOCAL_HOST, url); + } else if (!url.matches("[a-zA-Z][0-9a-zA-Z.+\\-]+://.+")) { + // Add http scheme for urls without a scheme. + // 2.0 spec is restricted to the following schemes: "http", "https", "ws", "wss" + // 3.0 spec does not have an enumerated list of schemes + // This regex attempts to capture all schemes in IANA example schemes which + // can have alpha-numeric characters and [.+-]. Examples are here: + // https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml + url = "http://" + url; + LOGGER.warn("'scheme' not defined in the spec (2.0). Default to [http] for server URL [{}]", url); + } } - return url; }