diff --git a/gradle/build.gradle.kts b/gradle/build.gradle.kts index c678d033..08192e37 100644 --- a/gradle/build.gradle.kts +++ b/gradle/build.gradle.kts @@ -3,7 +3,7 @@ import io.gitlab.arturbosch.detekt.CONFIGURATION_DETEKT_PLUGINS val javaVersion = JavaVersion.VERSION_17 plugins { - `kotlin-dsl` version "2.3.3" apply false + `kotlin-dsl` apply false id("com.github.ben-manes.versions") version "0.42.0" id("org.cyclonedx.bom") version "1.5.0" @@ -11,16 +11,6 @@ plugins { id("io.gitlab.arturbosch.detekt") version "1.20.0" apply false } -allprojects { - configurations.all { - resolutionStrategy.eachDependency { - if (requested.group == "org.jetbrains.kotlin") { - useVersion("1.6.21") - } - } - } -} - subprojects { group = "com.equisoft.standards" @@ -84,10 +74,10 @@ subprojects { name = "gprWrite" username = project.findProperty("gpr.write.user")?.toString() ?: System.getenv("GPR_USER") - ?: System.getenv("GHCR_USER") + ?: System.getenv("GHCR_USER") password = project.findProperty("gpr.write.key")?.toString() ?: System.getenv("GPR_KEY") - ?: System.getenv("GHCR_TOKEN") + ?: System.getenv("GHCR_TOKEN") } } } @@ -143,6 +133,6 @@ tasks { wrapper { distributionType = Wrapper.DistributionType.ALL - gradleVersion = "7.5" + gradleVersion = "7.5.1" } } diff --git a/gradle/global-conventions/build.gradle.kts b/gradle/global-conventions/build.gradle.kts index 467f0cf2..9af7db40 100644 --- a/gradle/global-conventions/build.gradle.kts +++ b/gradle/global-conventions/build.gradle.kts @@ -2,7 +2,7 @@ version = "1.2.0-SNAPSHOT" dependencies { implementation("com.github.ben-manes:gradle-versions-plugin:0.42.0") - implementation("org.cyclonedx:cyclonedx-gradle-plugin:1.7.0") + implementation("org.cyclonedx:cyclonedx-gradle-plugin:1.7.1") } gradlePlugin { diff --git a/gradle/gradle/wrapper/gradle-wrapper.jar b/gradle/gradle/wrapper/gradle-wrapper.jar index 41d9927a..249e5832 100644 Binary files a/gradle/gradle/wrapper/gradle-wrapper.jar and b/gradle/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/gradle/wrapper/gradle-wrapper.properties b/gradle/gradle/wrapper/gradle-wrapper.properties index 2ec77e51..8fad3f5a 100644 --- a/gradle/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradle/gradlew b/gradle/gradlew index 1b6c7873..a69d9cb6 100755 --- a/gradle/gradlew +++ b/gradle/gradlew @@ -205,6 +205,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradle/gradlew.bat b/gradle/gradlew.bat index 107acd32..f127cfd4 100644 --- a/gradle/gradlew.bat +++ b/gradle/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/gradle/kotlin/build.gradle.kts b/gradle/kotlin/build.gradle.kts index dc3fa272..7d13d7bc 100644 --- a/gradle/kotlin/build.gradle.kts +++ b/gradle/kotlin/build.gradle.kts @@ -9,7 +9,7 @@ val functionalTestImplementation = configurations val detektVersion = "1.20.0" dependencies { - val kotlinVersion = "1.6.21" + val kotlinVersion = "1.7.10" implementation(platform("org.jetbrains.kotlin:kotlin-bom:$kotlinVersion")) implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") diff --git a/gradle/kotlin/src/functionalTest/kotlin/com/equisoft/standards/kotlin/KotlinStandardsPluginFunctionalTest.kt b/gradle/kotlin/src/functionalTest/kotlin/com/equisoft/standards/kotlin/KotlinStandardsPluginFunctionalTest.kt index 64b1ff61..70fcea51 100644 --- a/gradle/kotlin/src/functionalTest/kotlin/com/equisoft/standards/kotlin/KotlinStandardsPluginFunctionalTest.kt +++ b/gradle/kotlin/src/functionalTest/kotlin/com/equisoft/standards/kotlin/KotlinStandardsPluginFunctionalTest.kt @@ -42,12 +42,12 @@ class KotlinStandardsPluginFunctionalTest { projectDir.resolve("build.gradle.kts").writeText( """ plugins { - kotlin("jvm") version "1.6.20" + kotlin("jvm") version "1.7.10" id("com.equisoft.standards.kotlin") } dependencies { - implementation("io.micronaut.test:micronaut-test-junit5:2.3.3") - implementation("org.junit.jupiter:junit-jupiter-api:5.8.2") + implementation("io.micronaut.test:micronaut-test-junit5:3.5.0") + implementation("org.junit.jupiter:junit-jupiter-api:5.9.0") } repositories { mavenCentral() diff --git a/gradle/micronaut/build.gradle.kts b/gradle/micronaut/build.gradle.kts index b52f9c5a..ad7e6c52 100644 --- a/gradle/micronaut/build.gradle.kts +++ b/gradle/micronaut/build.gradle.kts @@ -1,12 +1,12 @@ version = "0.5.0-SNAPSHOT" dependencies { - val kotlinVersion = "1.6.21" + val kotlinVersion = "1.7.10" implementation(platform("org.jetbrains.kotlin:kotlin-bom:$kotlinVersion")) implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") implementation("org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion") - val micronautPluginVersion = "3.5.0" + val micronautPluginVersion = "3.5.3" implementation("io.micronaut.gradle:micronaut-docker-plugin:$micronautPluginVersion") implementation("io.micronaut.gradle:micronaut-graalvm-plugin:$micronautPluginVersion") implementation("io.micronaut.gradle:micronaut-gradle-plugin:$micronautPluginVersion") @@ -16,7 +16,7 @@ dependencies { testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") - val junit5Version = "5.8.2" + val junit5Version = "5.9.0" testImplementation("org.junit.jupiter:junit-jupiter-api:$junit5Version") testImplementation("org.junit.jupiter:junit-jupiter-engine:$junit5Version") } diff --git a/gradle/openapi-sdk/build.gradle.kts b/gradle/openapi-sdk/build.gradle.kts index 09d2b59c..2d8e9a31 100644 --- a/gradle/openapi-sdk/build.gradle.kts +++ b/gradle/openapi-sdk/build.gradle.kts @@ -1,6 +1,6 @@ import org.apache.tools.ant.taskdefs.Patch -version = "0.5.0-SNAPSHOT" +version = "0.6.0-SNAPSHOT" val openApiPatchesSourceDirectory = layout.projectDirectory.dir("src/patches") val openApiTemplatesDirectory = layout.buildDirectory.dir("tmp/openapi-templates") @@ -12,17 +12,17 @@ val openApiGenerator by configurations.creating { } dependencies { - val openApiVersion = "6.0.0" + val openApiVersion = "6.0.1" - compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21") + compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.10") implementation("org.openapitools:openapi-generator-gradle-plugin:$openApiVersion") - implementation("com.equisoft.openapi.generator.micronaut:micronaut-project-openapi-generator:0.4.0") + implementation("com.equisoft.openapi.generator.micronaut:micronaut-project-openapi-generator:0.5.0") openApiGenerator("org.openapitools:openapi-generator:$openApiVersion") } java { - sourceCompatibility = JavaVersion.VERSION_13 + sourceCompatibility = JavaVersion.VERSION_17 } gradlePlugin { diff --git a/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/DefaultCodegen.java index b391e149..ac5dafef 100644 --- a/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -1,3 +1,4 @@ + /* * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) * Copyright 2018 SmartBear Software @@ -93,55 +94,55 @@ public class DefaultCodegen implements CodegenConfig { static { DefaultFeatureSet = FeatureSet.newBuilder() - .includeDataTypeFeatures( - DataTypeFeature.Int32, DataTypeFeature.Int64, DataTypeFeature.Float, DataTypeFeature.Double, - DataTypeFeature.Decimal, DataTypeFeature.String, DataTypeFeature.Byte, DataTypeFeature.Binary, - DataTypeFeature.Boolean, DataTypeFeature.Date, DataTypeFeature.DateTime, DataTypeFeature.Password, - DataTypeFeature.File, DataTypeFeature.Array, DataTypeFeature.Object, DataTypeFeature.Maps, DataTypeFeature.CollectionFormat, - DataTypeFeature.CollectionFormatMulti, DataTypeFeature.Enum, DataTypeFeature.ArrayOfEnum, DataTypeFeature.ArrayOfModel, - DataTypeFeature.ArrayOfCollectionOfPrimitives, DataTypeFeature.ArrayOfCollectionOfModel, DataTypeFeature.ArrayOfCollectionOfEnum, - DataTypeFeature.MapOfEnum, DataTypeFeature.MapOfModel, DataTypeFeature.MapOfCollectionOfPrimitives, - DataTypeFeature.MapOfCollectionOfModel, DataTypeFeature.MapOfCollectionOfEnum - // Custom types are template specific - ) - .includeDocumentationFeatures( - DocumentationFeature.Api, DocumentationFeature.Model - // README is template specific - ) - .includeGlobalFeatures( - GlobalFeature.Host, GlobalFeature.BasePath, GlobalFeature.Info, GlobalFeature.PartialSchemes, - GlobalFeature.Consumes, GlobalFeature.Produces, GlobalFeature.ExternalDocumentation, GlobalFeature.Examples, - GlobalFeature.Callbacks - // TODO: xml structures, styles, link objects, parameterized servers, full schemes for OAS 2.0 - ) - .includeSchemaSupportFeatures( - SchemaSupportFeature.Simple, SchemaSupportFeature.Composite, - SchemaSupportFeature.Polymorphism - // Union (OneOf) not 100% yet. - ) - .includeParameterFeatures( - ParameterFeature.Path, ParameterFeature.Query, ParameterFeature.Header, ParameterFeature.Body, - ParameterFeature.FormUnencoded, ParameterFeature.FormMultipart, ParameterFeature.Cookie - ) - .includeSecurityFeatures( - SecurityFeature.BasicAuth, SecurityFeature.ApiKey, SecurityFeature.BearerToken, - SecurityFeature.OAuth2_Implicit, SecurityFeature.OAuth2_Password, - SecurityFeature.OAuth2_ClientCredentials, SecurityFeature.OAuth2_AuthorizationCode - // OpenIDConnect not yet supported - ) - .includeWireFormatFeatures( - WireFormatFeature.JSON, WireFormatFeature.XML - // PROTOBUF and Custom are generator specific - ) - .build(); + .includeDataTypeFeatures( + DataTypeFeature.Int32, DataTypeFeature.Int64, DataTypeFeature.Float, DataTypeFeature.Double, + DataTypeFeature.Decimal, DataTypeFeature.String, DataTypeFeature.Byte, DataTypeFeature.Binary, + DataTypeFeature.Boolean, DataTypeFeature.Date, DataTypeFeature.DateTime, DataTypeFeature.Password, + DataTypeFeature.File, DataTypeFeature.Array, DataTypeFeature.Object, DataTypeFeature.Maps, DataTypeFeature.CollectionFormat, + DataTypeFeature.CollectionFormatMulti, DataTypeFeature.Enum, DataTypeFeature.ArrayOfEnum, DataTypeFeature.ArrayOfModel, + DataTypeFeature.ArrayOfCollectionOfPrimitives, DataTypeFeature.ArrayOfCollectionOfModel, DataTypeFeature.ArrayOfCollectionOfEnum, + DataTypeFeature.MapOfEnum, DataTypeFeature.MapOfModel, DataTypeFeature.MapOfCollectionOfPrimitives, + DataTypeFeature.MapOfCollectionOfModel, DataTypeFeature.MapOfCollectionOfEnum + // Custom types are template specific + ) + .includeDocumentationFeatures( + DocumentationFeature.Api, DocumentationFeature.Model + // README is template specific + ) + .includeGlobalFeatures( + GlobalFeature.Host, GlobalFeature.BasePath, GlobalFeature.Info, GlobalFeature.PartialSchemes, + GlobalFeature.Consumes, GlobalFeature.Produces, GlobalFeature.ExternalDocumentation, GlobalFeature.Examples, + GlobalFeature.Callbacks + // TODO: xml structures, styles, link objects, parameterized servers, full schemes for OAS 2.0 + ) + .includeSchemaSupportFeatures( + SchemaSupportFeature.Simple, SchemaSupportFeature.Composite, + SchemaSupportFeature.Polymorphism + // Union (OneOf) not 100% yet. + ) + .includeParameterFeatures( + ParameterFeature.Path, ParameterFeature.Query, ParameterFeature.Header, ParameterFeature.Body, + ParameterFeature.FormUnencoded, ParameterFeature.FormMultipart, ParameterFeature.Cookie + ) + .includeSecurityFeatures( + SecurityFeature.BasicAuth, SecurityFeature.ApiKey, SecurityFeature.BearerToken, + SecurityFeature.OAuth2_Implicit, SecurityFeature.OAuth2_Password, + SecurityFeature.OAuth2_ClientCredentials, SecurityFeature.OAuth2_AuthorizationCode + // OpenIDConnect not yet supported + ) + .includeWireFormatFeatures( + WireFormatFeature.JSON, WireFormatFeature.XML + // PROTOBUF and Custom are generator specific + ) + .build(); int cacheSize = Integer.parseInt(GlobalSettings.getProperty(NAME_CACHE_SIZE_PROPERTY, "500")); int cacheExpiry = Integer.parseInt(GlobalSettings.getProperty(NAME_CACHE_EXPIRY_PROPERTY, "10")); sanitizedNameCache = Caffeine.newBuilder() - .maximumSize(cacheSize) - .expireAfterAccess(cacheExpiry, TimeUnit.SECONDS) - .ticker(Ticker.systemTicker()) - .build(); + .maximumSize(cacheSize) + .expireAfterAccess(cacheExpiry, TimeUnit.SECONDS) + .ticker(Ticker.systemTicker()) + .build(); } protected GeneratorMetadata generatorMetadata; @@ -153,8 +154,12 @@ public class DefaultCodegen implements CodegenConfig { protected Set reservedWords; protected Set languageSpecificPrimitives = new HashSet<>(); protected Map importMapping = new HashMap<>(); + // a map to store the mappping between a schema and the new one + protected Map schemaMapping = new HashMap<>(); // a map to store the mappping between inline schema and the name provided by the user protected Map inlineSchemaNameMapping = new HashMap<>(); + // a map to store the inline schema naming conventions + protected Map inlineSchemaNameDefault = new HashMap<>(); protected String modelPackage = "", apiPackage = "", fileSuffix; protected String modelNamePrefix = "", modelNameSuffix = ""; protected String apiNamePrefix = "", apiNameSuffix = "Api"; @@ -302,27 +307,27 @@ public void processOpts() { if (additionalProperties.containsKey(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG)) { this.setSortParamsByRequiredFlag(Boolean.valueOf(additionalProperties - .get(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG).toString())); + .get(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG).toString())); } if (additionalProperties.containsKey(CodegenConstants.SORT_MODEL_PROPERTIES_BY_REQUIRED_FLAG)) { this.setSortModelPropertiesByRequiredFlag(Boolean.valueOf(additionalProperties - .get(CodegenConstants.SORT_MODEL_PROPERTIES_BY_REQUIRED_FLAG).toString())); + .get(CodegenConstants.SORT_MODEL_PROPERTIES_BY_REQUIRED_FLAG).toString())); } if (additionalProperties.containsKey(CodegenConstants.PREPEND_FORM_OR_BODY_PARAMETERS)) { this.setPrependFormOrBodyParameters(Boolean.valueOf(additionalProperties - .get(CodegenConstants.PREPEND_FORM_OR_BODY_PARAMETERS).toString())); + .get(CodegenConstants.PREPEND_FORM_OR_BODY_PARAMETERS).toString())); } if (additionalProperties.containsKey(CodegenConstants.ENSURE_UNIQUE_PARAMS)) { this.setEnsureUniqueParams(Boolean.valueOf(additionalProperties - .get(CodegenConstants.ENSURE_UNIQUE_PARAMS).toString())); + .get(CodegenConstants.ENSURE_UNIQUE_PARAMS).toString())); } if (additionalProperties.containsKey(CodegenConstants.ALLOW_UNICODE_IDENTIFIERS)) { this.setAllowUnicodeIdentifiers(Boolean.valueOf(additionalProperties - .get(CodegenConstants.ALLOW_UNICODE_IDENTIFIERS).toString())); + .get(CodegenConstants.ALLOW_UNICODE_IDENTIFIERS).toString())); } if (additionalProperties.containsKey(CodegenConstants.API_NAME_PREFIX)) { @@ -343,55 +348,55 @@ public void processOpts() { if (additionalProperties.containsKey(CodegenConstants.REMOVE_OPERATION_ID_PREFIX)) { this.setRemoveOperationIdPrefix(Boolean.parseBoolean(additionalProperties - .get(CodegenConstants.REMOVE_OPERATION_ID_PREFIX).toString())); + .get(CodegenConstants.REMOVE_OPERATION_ID_PREFIX).toString())); } if (additionalProperties.containsKey(CodegenConstants.REMOVE_OPERATION_ID_PREFIX_DELIMITER)) { this.setRemoveOperationIdPrefixDelimiter(additionalProperties - .get(CodegenConstants.REMOVE_OPERATION_ID_PREFIX_DELIMITER).toString()); + .get(CodegenConstants.REMOVE_OPERATION_ID_PREFIX_DELIMITER).toString()); } if (additionalProperties.containsKey(CodegenConstants.REMOVE_OPERATION_ID_PREFIX_COUNT)) { this.setRemoveOperationIdPrefixCount(Integer.parseInt(additionalProperties - .get(CodegenConstants.REMOVE_OPERATION_ID_PREFIX_COUNT).toString())); + .get(CodegenConstants.REMOVE_OPERATION_ID_PREFIX_COUNT).toString())); } if (additionalProperties.containsKey(CodegenConstants.SKIP_OPERATION_EXAMPLE)) { this.setSkipOperationExample(Boolean.parseBoolean(additionalProperties - .get(CodegenConstants.SKIP_OPERATION_EXAMPLE).toString())); + .get(CodegenConstants.SKIP_OPERATION_EXAMPLE).toString())); } if (additionalProperties.containsKey(CodegenConstants.DOCEXTENSION)) { this.setDocExtension(String.valueOf(additionalProperties - .get(CodegenConstants.DOCEXTENSION).toString())); + .get(CodegenConstants.DOCEXTENSION).toString())); } if (additionalProperties.containsKey(CodegenConstants.ENABLE_POST_PROCESS_FILE)) { this.setEnablePostProcessFile(Boolean.parseBoolean(additionalProperties - .get(CodegenConstants.ENABLE_POST_PROCESS_FILE).toString())); + .get(CodegenConstants.ENABLE_POST_PROCESS_FILE).toString())); } if (additionalProperties.containsKey(CodegenConstants.GENERATE_ALIAS_AS_MODEL)) { ModelUtils.setGenerateAliasAsModel(Boolean.parseBoolean(additionalProperties - .get(CodegenConstants.GENERATE_ALIAS_AS_MODEL).toString())); + .get(CodegenConstants.GENERATE_ALIAS_AS_MODEL).toString())); } if (additionalProperties.containsKey(CodegenConstants.REMOVE_ENUM_VALUE_PREFIX)) { this.setRemoveEnumValuePrefix(Boolean.parseBoolean(additionalProperties - .get(CodegenConstants.REMOVE_ENUM_VALUE_PREFIX).toString())); + .get(CodegenConstants.REMOVE_ENUM_VALUE_PREFIX).toString())); } if (additionalProperties.containsKey(CodegenConstants.LEGACY_DISCRIMINATOR_BEHAVIOR)) { this.setLegacyDiscriminatorBehavior(Boolean.parseBoolean(additionalProperties - .get(CodegenConstants.LEGACY_DISCRIMINATOR_BEHAVIOR).toString())); + .get(CodegenConstants.LEGACY_DISCRIMINATOR_BEHAVIOR).toString())); } if (additionalProperties.containsKey(CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT)) { this.setDisallowAdditionalPropertiesIfNotPresent(Boolean.parseBoolean(additionalProperties - .get(CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT).toString())); + .get(CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT).toString())); } if (additionalProperties.containsKey(CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE)) { this.setEnumUnknownDefaultCase(Boolean.parseBoolean(additionalProperties - .get(CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE).toString())); + .get(CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE).toString())); } } @@ -409,16 +414,16 @@ public void processOpts() { protected ImmutableMap.Builder addMustacheLambdas() { return new ImmutableMap.Builder() - .put("lowercase", new LowercaseLambda().generator(this)) - .put("uppercase", new UppercaseLambda()) - .put("snakecase", new SnakecaseLambda()) - .put("titlecase", new TitlecaseLambda()) - .put("camelcase", new CamelCaseLambda(true).generator(this)) - .put("pascalcase", new CamelCaseLambda(false).generator(this)) - .put("indented", new IndentedLambda()) - .put("indented_8", new IndentedLambda(8, " ")) - .put("indented_12", new IndentedLambda(12, " ")) - .put("indented_16", new IndentedLambda(16, " ")); + .put("lowercase", new LowercaseLambda().generator(this)) + .put("uppercase", new UppercaseLambda()) + .put("snakecase", new SnakecaseLambda()) + .put("titlecase", new TitlecaseLambda()) + .put("camelcase", new CamelCaseLambda(true).generator(this)) + .put("pascalcase", new CamelCaseLambda(false).generator(this)) + .put("indented", new IndentedLambda()) + .put("indented_8", new IndentedLambda(8, " ")) + .put("indented_12", new IndentedLambda(12, " ")) + .put("indented_16", new IndentedLambda(16, " ")); } private void registerMustacheLambdas() { @@ -599,8 +604,8 @@ public Map updateAllModels(Map objs) { protected void removeSelfReferenceImports(CodegenModel model) { for (CodegenProperty cp : model.allVars) { // detect self import - if (cp.dataType.equalsIgnoreCase(model.classname) || - (cp.isContainer && cp.items != null && cp.items.dataType.equalsIgnoreCase(model.classname))) { + if (cp.dataType != null && cp.dataType.equalsIgnoreCase(model.classname) || + (cp.isContainer && cp.items != null && cp.items.dataType.equalsIgnoreCase(model.classname))) { model.imports.remove(model.classname); // remove self import cp.isSelfReference = true; } @@ -609,40 +614,40 @@ protected void removeSelfReferenceImports(CodegenModel model) { public void setCircularReferences(Map models) { final Map> dependencyMap = models.entrySet().stream() - .collect(Collectors.toMap(Entry::getKey, entry -> getModelDependencies(entry.getValue()))); + .collect(Collectors.toMap(Entry::getKey, entry -> getModelDependencies(entry.getValue()))); models.keySet().forEach(name -> setCircularReferencesOnProperties(name, dependencyMap)); } private List getModelDependencies(CodegenModel model) { return model.getAllVars().stream() - .map(prop -> { - if (prop.isContainer) { - return prop.items.dataType == null ? null : prop; - } - return prop.dataType == null ? null : prop; - }) - .filter(Objects::nonNull) - .collect(Collectors.toList()); + .map(prop -> { + if (prop.isContainer) { + return prop.items.dataType == null ? null : prop; + } + return prop.dataType == null ? null : prop; + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); } private void setCircularReferencesOnProperties(final String root, - final Map> dependencyMap) { + final Map> dependencyMap) { dependencyMap.getOrDefault(root, new ArrayList<>()) - .forEach(prop -> { - final List unvisited = - Collections.singletonList(prop.isContainer ? prop.items.dataType : prop.dataType); - prop.isCircularReference = isCircularReference(root, - new HashSet<>(), - new ArrayList<>(unvisited), - dependencyMap); - }); + .forEach(prop -> { + final List unvisited = + Collections.singletonList(prop.isContainer ? prop.items.dataType : prop.dataType); + prop.isCircularReference = isCircularReference(root, + new HashSet<>(), + new ArrayList<>(unvisited), + dependencyMap); + }); } private boolean isCircularReference(final String root, - final Set visited, - final List unvisited, - final Map> dependencyMap) { + final Set visited, + final List unvisited, + final Map> dependencyMap) { for (int i = 0; i < unvisited.size(); i++) { final String next = unvisited.get(i); if (!visited.contains(next)) { @@ -650,7 +655,7 @@ private boolean isCircularReference(final String root, return true; } dependencyMap.getOrDefault(next, new ArrayList<>()) - .forEach(prop -> unvisited.add(prop.isContainer ? prop.items.dataType : prop.dataType)); + .forEach(prop -> unvisited.add(prop.isContainer ? prop.items.dataType : prop.dataType)); visited.add(next); } } @@ -956,12 +961,12 @@ public String escapeText(String input) { // outer unescape to retain the original multi-byte characters // finally escalate characters avoiding code injection return escapeUnsafeCharacters( - StringEscapeUtils.unescapeJava( - StringEscapeUtils.escapeJava(input) - .replace("\\/", "/")) - .replaceAll("[\\t\\n\\r]", " ") - .replace("\\", "\\\\") - .replace("\"", "\\\"")); + StringEscapeUtils.unescapeJava( + StringEscapeUtils.escapeJava(input) + .replace("\\/", "/")) + .replaceAll("[\\t\\n\\r]", " ") + .replace("\\", "\\\\") + .replace("\"", "\\\"")); } /** @@ -982,12 +987,12 @@ public String escapeTextWhileAllowingNewLines(String input) { // outer unescape to retain the original multi-byte characters // finally escalate characters avoiding code injection return escapeUnsafeCharacters( - StringEscapeUtils.unescapeJava( - StringEscapeUtils.escapeJava(input) - .replace("\\/", "/")) - .replaceAll("[\\t]", " ") - .replace("\\", "\\\\") - .replace("\"", "\\\"")); + StringEscapeUtils.unescapeJava( + StringEscapeUtils.escapeJava(input) + .replace("\\/", "/")) + .replaceAll("[\\t]", " ") + .replace("\\", "\\\\") + .replace("\"", "\\\"")); } // override with any special encoding and escaping logic @@ -1007,7 +1012,7 @@ public String encodePath(String input) { @Override public String escapeUnsafeCharacters(String input) { LOGGER.warn("escapeUnsafeCharacters should be overridden in the code generator with proper logic to escape " + - "unsafe characters"); + "unsafe characters"); // doing nothing by default and code generator should implement // the logic to prevent code injection // later we'll make this method abstract to make sure @@ -1024,7 +1029,7 @@ public String escapeUnsafeCharacters(String input) { @Override public String escapeQuotationMark(String input) { LOGGER.warn("escapeQuotationMark should be overridden in the code generator with proper logic to escape " + - "single/double quote"); + "single/double quote"); return input.replace("\"", "\\\""); } @@ -1058,11 +1063,21 @@ public Map importMapping() { return importMapping; } + @Override + public Map schemaMapping() { + return schemaMapping; + } + @Override public Map inlineSchemaNameMapping() { return inlineSchemaNameMapping; } + @Override + public Map inlineSchemaNameDefault() { + return inlineSchemaNameDefault; + } + @Override public String testPackage() { return testPackage; @@ -1576,26 +1591,26 @@ public DefaultCodegen() { } generatorMetadata = GeneratorMetadata.newBuilder() - .stability(Stability.STABLE) - .featureSet(DefaultFeatureSet) - .generationMessage(String.format(Locale.ROOT, "OpenAPI Generator: %s (%s)", getName(), codegenType.toValue())) - .build(); + .stability(Stability.STABLE) + .featureSet(DefaultFeatureSet) + .generationMessage(String.format(Locale.ROOT, "OpenAPI Generator: %s (%s)", getName(), codegenType.toValue())) + .build(); defaultIncludes = new HashSet<>( - Arrays.asList("double", - "int", - "long", - "short", - "char", - "float", - "String", - "boolean", - "Boolean", - "Double", - "Void", - "Integer", - "Long", - "Float") + Arrays.asList("double", + "int", + "long", + "short", + "char", + "float", + "String", + "boolean", + "Boolean", + "Double", + "Void", + "Integer", + "Long", + "Float") ); typeMapping = new HashMap<>(); @@ -1628,17 +1643,17 @@ public DefaultCodegen() { reservedWords = new HashSet<>(); cliOptions.add(CliOption.newBoolean(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG, - CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG_DESC).defaultValue(Boolean.TRUE.toString())); + CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG_DESC).defaultValue(Boolean.TRUE.toString())); cliOptions.add(CliOption.newBoolean(CodegenConstants.SORT_MODEL_PROPERTIES_BY_REQUIRED_FLAG, - CodegenConstants.SORT_MODEL_PROPERTIES_BY_REQUIRED_FLAG_DESC).defaultValue(Boolean.TRUE.toString())); + CodegenConstants.SORT_MODEL_PROPERTIES_BY_REQUIRED_FLAG_DESC).defaultValue(Boolean.TRUE.toString())); cliOptions.add(CliOption.newBoolean(CodegenConstants.ENSURE_UNIQUE_PARAMS, CodegenConstants - .ENSURE_UNIQUE_PARAMS_DESC).defaultValue(Boolean.TRUE.toString())); + .ENSURE_UNIQUE_PARAMS_DESC).defaultValue(Boolean.TRUE.toString())); // name formatting options cliOptions.add(CliOption.newBoolean(CodegenConstants.ALLOW_UNICODE_IDENTIFIERS, CodegenConstants - .ALLOW_UNICODE_IDENTIFIERS_DESC).defaultValue(Boolean.FALSE.toString())); + .ALLOW_UNICODE_IDENTIFIERS_DESC).defaultValue(Boolean.FALSE.toString())); // option to change the order of form/body parameter cliOptions.add(CliOption.newBoolean(CodegenConstants.PREPEND_FORM_OR_BODY_PARAMETERS, - CodegenConstants.PREPEND_FORM_OR_BODY_PARAMETERS_DESC).defaultValue(Boolean.FALSE.toString())); + CodegenConstants.PREPEND_FORM_OR_BODY_PARAMETERS_DESC).defaultValue(Boolean.FALSE.toString())); // option to change how we process + set the data in the discriminator mapping CliOption legacyDiscriminatorBehaviorOpt = CliOption.newBoolean(CodegenConstants.LEGACY_DISCRIMINATOR_BEHAVIOR, CodegenConstants.LEGACY_DISCRIMINATOR_BEHAVIOR_DESC).defaultValue(Boolean.TRUE.toString()); @@ -1650,25 +1665,25 @@ public DefaultCodegen() { // option to change how we process + set the data in the 'additionalProperties' keyword. CliOption disallowAdditionalPropertiesIfNotPresentOpt = CliOption.newBoolean( - CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT, - CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT_DESC).defaultValue(Boolean.TRUE.toString()); + CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT, + CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT_DESC).defaultValue(Boolean.TRUE.toString()); Map disallowAdditionalPropertiesIfNotPresentOpts = new HashMap<>(); disallowAdditionalPropertiesIfNotPresentOpts.put("false", - "The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications."); + "The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications."); disallowAdditionalPropertiesIfNotPresentOpts.put("true", - "Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default."); + "Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default."); disallowAdditionalPropertiesIfNotPresentOpt.setEnum(disallowAdditionalPropertiesIfNotPresentOpts); cliOptions.add(disallowAdditionalPropertiesIfNotPresentOpt); this.setDisallowAdditionalPropertiesIfNotPresent(true); CliOption enumUnknownDefaultCaseOpt = CliOption.newBoolean( - CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE, - CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE_DESC).defaultValue(Boolean.FALSE.toString()); + CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE, + CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE_DESC).defaultValue(Boolean.FALSE.toString()); Map enumUnknownDefaultCaseOpts = new HashMap<>(); enumUnknownDefaultCaseOpts.put("false", - "No changes to the enum's are made, this is the default option."); + "No changes to the enum's are made, this is the default option."); enumUnknownDefaultCaseOpts.put("true", - "With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the enum case sent by the server is not known by the client/spec, can safely be decoded to this case."); + "With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the enum case sent by the server is not known by the client/spec, can safely be decoded to this case."); enumUnknownDefaultCaseOpt.setEnum(enumUnknownDefaultCaseOpts); cliOptions.add(enumUnknownDefaultCaseOpt); this.setEnumUnknownDefaultCase(false); @@ -2184,18 +2199,20 @@ public String toOneOfName(List names, ComposedSchema composedSchema) { if (exts != null && exts.containsKey("x-one-of-name")) { return (String) exts.get("x-one-of-name"); } + // Begin Equisoft patch if (Boolean.TRUE.equals(composedSchema.getNullable()) || ModelUtils.isNullableComposedSchema(composedSchema)) { String singleType = names.stream().filter(p -> !p.equals("null")).findFirst().orElse(null); if(singleType != null) { return singleType; } } + // End Equisoft patch return "oneOf<" + String.join(",", names) + ">"; } @Override - public Schema unaliasSchema(Schema schema, Map usedImportMappings) { - return ModelUtils.unaliasSchema(this.openAPI, schema, usedImportMappings); + public Schema unaliasSchema(Schema schema, Map schemaMappings) { + return ModelUtils.unaliasSchema(this.openAPI, schema, schemaMappings); } /** @@ -2205,13 +2222,13 @@ public Schema unaliasSchema(Schema schema, Map usedImportMapping * @return the string representation of the schema type. */ protected String getSingleSchemaType(Schema schema) { - Schema unaliasSchema = unaliasSchema(schema, importMapping); + Schema unaliasSchema = unaliasSchema(schema, schemaMapping); if (StringUtils.isNotBlank(unaliasSchema.get$ref())) { // reference to another definition/schema // get the schema/model name from $ref String schemaName = ModelUtils.getSimpleRef(unaliasSchema.get$ref()); if (StringUtils.isNotEmpty(schemaName)) { - if (importMapping.containsKey(schemaName)) { + if (schemaMapping.containsKey(schemaName)) { return schemaName; } return getAlias(schemaName); @@ -2307,8 +2324,8 @@ private String getPrimitiveType(Schema schema) { } else if (ModelUtils.isAnyType(schema)) { return "AnyType"; } else if (StringUtils.isNotEmpty(schema.getType())) { - if (!importMapping.containsKey(schema.getType())) { - LOGGER.warn("Unknown type found in the schema: {}", schema.getType()); + if (!schemaMapping.containsKey(schema.getType())) { + LOGGER.warn("Unknown type found in the schema: {}. To map it, please use the schema mapping option (e.g. --schema-mappings in CLI)", schema.getType()); } return schema.getType(); } @@ -2461,7 +2478,7 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; NamedSchema that = (NamedSchema) o; return Objects.equals(name, that.name) && - Objects.equals(schema, that.schema); + Objects.equals(schema, that.schema); } @Override @@ -2483,7 +2500,7 @@ protected void updateModelForComposedSchema(CodegenModel m, Schema schema, Map(); for (Schema interfaceSchema : interfaces) { - interfaceSchema = unaliasSchema(interfaceSchema, importMapping); + interfaceSchema = unaliasSchema(interfaceSchema, schemaMapping); if (StringUtils.isBlank(interfaceSchema.get$ref())) { // primitive type @@ -2699,6 +2716,52 @@ protected void updateModelForAnyType(CodegenModel m, Schema schema) { setAddProps(schema, m); } + protected String toTestCaseName(String specTestCaseName) { + return specTestCaseName; + } + + /** + * A method that allows generators to pre-process test example payloads + * This can be useful if one needs to change how values like null in string are represnted + * @param data the test data payload + * @return the updated test data payload + */ + protected Object processTestExampleData(Object data) { + return data; + } + + /** + * Processes any test cases if they exist in the components.x-test-examples vendor extensions + * If they exist then cast them to java class instances and return them back in a map + * @param schemaName the component schema name that the test cases are for + * @param vendorExtensions the extensions that may or may not hold the data + */ + private HashMap extractSchemaTestCases(String schemaName, HashMap vendorExtensions) { + String testExamplesKey = "x-schema-test-examples"; + // schemaName to a map of test case name to test case + if (vendorExtensions == null || !vendorExtensions.containsKey(testExamplesKey)) { + return null; + } + HashMap schemaTestCases = new HashMap<>(); + LinkedHashMap schemaNameToTestCases = (LinkedHashMap) vendorExtensions.get(testExamplesKey); + + if (!schemaNameToTestCases.containsKey(schemaName)) { + return null; + } + LinkedHashMap> testNameToTesCase = (LinkedHashMap>) schemaNameToTestCases.get(schemaName); + for (Entry> entry: testNameToTesCase.entrySet()) { + LinkedHashMap testExample = (LinkedHashMap) entry.getValue(); + String nameInSnakeCase = toTestCaseName(entry.getKey()); + Object data = processTestExampleData(testExample.get("data")); + SchemaTestCase testCase = new SchemaTestCase( + (String) testExample.getOrDefault("description", ""), + new ObjectWithTypeBooleans(data), + (boolean) testExample.get("valid") + ); + schemaTestCases.put(nameInSnakeCase, testCase); + } + return schemaTestCases; + } /** * Convert OAS Model object to Codegen Model object. @@ -2716,7 +2779,7 @@ public CodegenModel fromModel(String name, Schema schema) { } // unalias schema - schema = unaliasSchema(schema, importMapping); + schema = unaliasSchema(schema, schemaMapping); if (schema == null) { LOGGER.warn("Schema {} not found", name); return null; @@ -2724,6 +2787,11 @@ public CodegenModel fromModel(String name, Schema schema) { CodegenModel m = CodegenModelFactory.newInstance(CodegenModelType.MODEL); ModelUtils.syncValidationProperties(schema, m); + if (openAPI != null) { + HashMap vendorExtensions = (HashMap) openAPI.getComponents().getExtensions(); + HashMap schemaTestCases = extractSchemaTestCases(name, vendorExtensions); + m.testCases = schemaTestCases; + } if (reservedWords.contains(name)) { m.name = escapeReservedWord(name); @@ -2742,7 +2810,7 @@ public CodegenModel fromModel(String name, Schema schema) { m.getVendorExtensions().putAll(schema.getExtensions()); } m.isAlias = (typeAliases.containsKey(name) - || isAliasOfSimpleTypes(schema)); // check if the unaliased schema is an alias of simple OAS types + || isAliasOfSimpleTypes(schema)); // check if the unaliased schema is an alias of simple OAS types m.setDiscriminator(createDiscriminator(name, schema, this.openAPI)); if (!this.getLegacyDiscriminatorBehavior()) { m.addDiscriminatorMappedModelsImports(); @@ -2965,8 +3033,8 @@ private CodegenProperty discriminatorFound(String composedSchemaName, Schema sc, CodegenProperty thisCp = discriminatorFound(composedSchemaName, oneOf, discPropName, openAPI); if (thisCp == null) { LOGGER.warn( - "'{}' defines discriminator '{}', but the referenced OneOf schema '{}' is missing {}", - composedSchemaName, discPropName, modelName, discPropName); + "'{}' defines discriminator '{}', but the referenced OneOf schema '{}' is missing {}", + composedSchemaName, discPropName, modelName, discPropName); } if (cp != null && cp.dataType == null) { cp = thisCp; @@ -2974,8 +3042,8 @@ private CodegenProperty discriminatorFound(String composedSchemaName, Schema sc, } if (cp != thisCp) { LOGGER.warn( - "'{}' defines discriminator '{}', but the OneOf schema '{}' has a different {} definition than the prior OneOf schema's. Make sure the {} type and required values are the same", - composedSchemaName, discPropName, modelName, discPropName, discPropName); + "'{}' defines discriminator '{}', but the OneOf schema '{}' has a different {} definition than the prior OneOf schema's. Make sure the {} type and required values are the same", + composedSchemaName, discPropName, modelName, discPropName, discPropName); } } return cp; @@ -2988,8 +3056,8 @@ private CodegenProperty discriminatorFound(String composedSchemaName, Schema sc, CodegenProperty thisCp = discriminatorFound(composedSchemaName, anyOf, discPropName, openAPI); if (thisCp == null) { LOGGER.warn( - "'{}' defines discriminator '{}', but the referenced AnyOf schema '{}' is missing {}", - composedSchemaName, discPropName, modelName, discPropName); + "'{}' defines discriminator '{}', but the referenced AnyOf schema '{}' is missing {}", + composedSchemaName, discPropName, modelName, discPropName); } if (cp != null && cp.dataType == null) { cp = thisCp; @@ -2997,8 +3065,8 @@ private CodegenProperty discriminatorFound(String composedSchemaName, Schema sc, } if (cp != thisCp) { LOGGER.warn( - "'{}' defines discriminator '{}', but the AnyOf schema '{}' has a different {} definition than the prior AnyOf schema's. Make sure the {} type and required values are the same", - composedSchemaName, discPropName, modelName, discPropName, discPropName); + "'{}' defines discriminator '{}', but the AnyOf schema '{}' has a different {} definition than the prior AnyOf schema's. Make sure the {} type and required values are the same", + composedSchemaName, discPropName, modelName, discPropName, discPropName); } } return cp; @@ -3058,7 +3126,7 @@ private Discriminator recursiveGetDiscriminator(Schema sc, OpenAPI openAPI) { } if (discriminatorsPropNames.size() > 1) { LOGGER.warn("The oneOf schemas have conflicting discriminator property names. " + - "oneOf schemas must have the same property name, but found " + String.join(", ", discriminatorsPropNames)); + "oneOf schemas must have the same property name, but found " + String.join(", ", discriminatorsPropNames)); } if (foundDisc != null && (hasDiscriminatorCnt + hasNullTypeCnt) == composedSchema.getOneOf().size() && discriminatorsPropNames.size() == 1) { disc.setPropertyName(foundDisc.getPropertyName()); @@ -3087,7 +3155,7 @@ private Discriminator recursiveGetDiscriminator(Schema sc, OpenAPI openAPI) { } if (discriminatorsPropNames.size() > 1) { LOGGER.warn("The anyOf schemas have conflicting discriminator property names. " + - "anyOf schemas must have the same property name, but found " + String.join(", ", discriminatorsPropNames)); + "anyOf schemas must have the same property name, but found " + String.join(", ", discriminatorsPropNames)); } if (foundDisc != null && (hasDiscriminatorCnt + hasNullTypeCnt) == composedSchema.getAnyOf().size() && discriminatorsPropNames.size() == 1) { disc.setPropertyName(foundDisc.getPropertyName()); @@ -3137,8 +3205,8 @@ protected List getOneOfAnyOfDescendants(String composedSchemaName, // Note: if it is only inline one level, then the inline model resolver will move it into its own // schema and make it a $ref schema in the oneOf/anyOf location LOGGER.warn( - "Invalid inline schema defined in oneOf/anyOf in '{}'. Per the OpenApi spec, for this case when a composed schema defines a discriminator, the oneOf/anyOf schemas must use $ref. Change this inline definition to a $ref definition", - composedSchemaName); + "Invalid inline schema defined in oneOf/anyOf in '{}'. Per the OpenApi spec, for this case when a composed schema defines a discriminator, the oneOf/anyOf schemas must use $ref. Change this inline definition to a $ref definition", + composedSchemaName); } CodegenProperty df = discriminatorFound(composedSchemaName, sc, discPropName, openAPI); String modelName = ModelUtils.getSimpleRef(ref); @@ -3159,7 +3227,7 @@ protected List getOneOfAnyOfDescendants(String composedSchemaName, } } LOGGER.warn("'{}' defines discriminator '{}', but the referenced schema '{}' is incorrect. {}", - composedSchemaName, discPropName, modelName, msgSuffix); + composedSchemaName, discPropName, modelName, msgSuffix); } MappedModel mm = new MappedModel(modelName, toModelName(modelName)); descendentSchemas.add(mm); @@ -3384,7 +3452,7 @@ protected void updatePropertyForMap(CodegenProperty property, Schema p) { property.maxItems = p.getMaxProperties(); // handle inner property - Schema innerSchema = unaliasSchema(getAdditionalProperties(p), importMapping); + Schema innerSchema = unaliasSchema(getAdditionalProperties(p), schemaMapping); if (innerSchema == null) { LOGGER.error("Undefined map inner type for `{}`. Default to String.", p.getName()); innerSchema = new StringSchema().description("//TODO automatically added by openapi-generator due to undefined type"); @@ -3487,7 +3555,7 @@ public CodegenProperty fromProperty(String name, Schema p) { return cpc; } // unalias schema - p = unaliasSchema(p, importMapping); + p = unaliasSchema(p, schemaMapping); CodegenProperty property = CodegenModelFactory.newInstance(CodegenModelType.PROPERTY); ModelUtils.syncValidationProperties(p, property); @@ -3655,7 +3723,7 @@ public CodegenProperty fromProperty(String name, Schema p) { itemName = property.name; } ArraySchema arraySchema = (ArraySchema) p; - Schema innerSchema = unaliasSchema(getSchemaItems(arraySchema), importMapping); + Schema innerSchema = unaliasSchema(getSchemaItems(arraySchema), schemaMapping); CodegenProperty cp = fromProperty(itemName, innerSchema); updatePropertyForArray(property, cp); } else if (ModelUtils.isTypeObjectSchema(p)) { @@ -3668,9 +3736,9 @@ public CodegenProperty fromProperty(String name, Schema p) { } boolean isAnyTypeWithNothingElseSet = (ModelUtils.isAnyType(p) && - (p.getProperties() == null || p.getProperties().isEmpty()) && - !ModelUtils.isComposedSchema(p) && - p.getAdditionalProperties() == null && p.getNot() == null && p.getEnum() == null); + (p.getProperties() == null || p.getProperties().isEmpty()) && + !ModelUtils.isComposedSchema(p) && + p.getAdditionalProperties() == null && p.getNot() == null && p.getEnum() == null); if (!ModelUtils.isArraySchema(p) && !ModelUtils.isMapSchema(p) && !isFreeFormObject(p) && !isAnyTypeWithNothingElseSet) { /* schemas that are not Array, not ModelUtils.isMapSchema, not isFreeFormObject, not AnyType with nothing else set @@ -3772,7 +3840,7 @@ protected Boolean isPropertyInnerMostEnum(CodegenProperty property) { protected CodegenProperty getMostInnerItems(CodegenProperty property) { CodegenProperty currentProperty = property; while (currentProperty != null && (Boolean.TRUE.equals(currentProperty.isMap) - || Boolean.TRUE.equals(currentProperty.isArray)) && currentProperty.items != null) { + || Boolean.TRUE.equals(currentProperty.isArray)) && currentProperty.items != null) { currentProperty = currentProperty.items; } return currentProperty; @@ -3792,7 +3860,7 @@ protected Map getInnerEnumAllowableValues(CodegenProperty proper protected void updateDataTypeWithEnumForArray(CodegenProperty property) { CodegenProperty baseItem = property.items; while (baseItem != null && (Boolean.TRUE.equals(baseItem.isMap) - || Boolean.TRUE.equals(baseItem.isArray))) { + || Boolean.TRUE.equals(baseItem.isArray))) { baseItem = baseItem.items; } if (baseItem != null) { @@ -3820,7 +3888,7 @@ protected void updateDataTypeWithEnumForArray(CodegenProperty property) { protected void updateDataTypeWithEnumForMap(CodegenProperty property) { CodegenProperty baseItem = property.items; while (baseItem != null && (Boolean.TRUE.equals(baseItem.isMap) - || Boolean.TRUE.equals(baseItem.isArray))) { + || Boolean.TRUE.equals(baseItem.isArray))) { baseItem = baseItem.items; } @@ -3881,9 +3949,9 @@ protected ApiResponse findMethodResponse(ApiResponses responses) { * @param methodResponse the default ApiResponse for the endpoint */ protected void handleMethodResponse(Operation operation, - Map schemas, - CodegenOperation op, - ApiResponse methodResponse) { + Map schemas, + CodegenOperation op, + ApiResponse methodResponse) { handleMethodResponse(operation, schemas, op, methodResponse, Collections.emptyMap()); } @@ -3894,14 +3962,14 @@ protected void handleMethodResponse(Operation operation, * @param schemas a map of the schemas in the openapi spec * @param op endpoint CodegenOperation * @param methodResponse the default ApiResponse for the endpoint - * @param importMappings mappings of external types to be omitted by unaliasing + * @param schemaMappings mappings of external types to be omitted by unaliasing */ protected void handleMethodResponse(Operation operation, - Map schemas, - CodegenOperation op, - ApiResponse methodResponse, - Map importMappings) { - Schema responseSchema = unaliasSchema(ModelUtils.getSchemaFromResponse(methodResponse), importMapping); + Map schemas, + CodegenOperation op, + ApiResponse methodResponse, + Map schemaMappings) { + Schema responseSchema = unaliasSchema(ModelUtils.getSchemaFromResponse(methodResponse), schemaMapping); if (responseSchema != null) { CodegenProperty cm = fromProperty("response", responseSchema); @@ -3980,9 +4048,9 @@ protected void handleMethodResponse(Operation operation, */ @Override public CodegenOperation fromOperation(String path, - String httpMethod, - Operation operation, - List servers) { + String httpMethod, + Operation operation, + List servers) { LOGGER.debug("fromOperation => operation: {}", operation); if (operation == null) throw new RuntimeException("operation cannot be null in fromOperation"); @@ -4065,8 +4133,8 @@ public CodegenOperation fromOperation(String path, r.setContent(getContent(response.getContent(), imports, mediaTypeSchemaSuffix)); if (r.baseType != null && - !defaultIncludes.contains(r.baseType) && - !languageSpecificPrimitives.contains(r.baseType)) { + !defaultIncludes.contains(r.baseType) && + !languageSpecificPrimitives.contains(r.baseType)) { imports.add(r.baseType); } if ("set".equals(r.containerType) && typeMapping.containsKey(r.containerType)) { @@ -4084,10 +4152,17 @@ public CodegenOperation fromOperation(String path, // check if any 4xx or 5xx response has an error response object defined if ((Boolean.TRUE.equals(r.is4xx) || Boolean.TRUE.equals(r.is5xx)) && - Boolean.FALSE.equals(r.primitiveType) && Boolean.FALSE.equals(r.simpleType)) { + Boolean.FALSE.equals(r.primitiveType) && Boolean.FALSE.equals(r.simpleType)) { op.hasErrorResponseObject = Boolean.TRUE; } } + + // check if the operation can both return a 2xx response with a body and without + if (op.responses.stream().anyMatch(response -> response.is2xx && response.dataType != null) && + op.responses.stream().anyMatch(response -> response.is2xx && response.dataType == null)) { + op.isResponseOptional = Boolean.TRUE; + } + op.responses.sort((a, b) -> { int aScore = a.isWildcard() ? 2 : a.isRange() ? 1 : 0; int bScore = b.isWildcard() ? 2 : b.isRange() ? 1 : 0; @@ -4125,8 +4200,8 @@ public CodegenOperation fromOperation(String path, contentType = contentType.toLowerCase(Locale.ROOT); } if (contentType != null && - (contentType.startsWith("application/x-www-form-urlencoded") || - contentType.startsWith("multipart"))) { + (contentType.startsWith("application/x-www-form-urlencoded") || + contentType.startsWith("multipart"))) { // process form parameters formParams = fromRequestBodyToFormParameters(requestBody, imports); op.isMultipart = contentType.startsWith("multipart"); @@ -4333,7 +4408,7 @@ public CodegenResponse fromResponse(String responseCode, ApiResponse response) { Schema responseSchema; if (this.openAPI != null && this.openAPI.getComponents() != null) { - responseSchema = unaliasSchema(ModelUtils.getSchemaFromResponse(response), importMapping); + responseSchema = unaliasSchema(ModelUtils.getSchemaFromResponse(response), schemaMapping); } else { // no model/alias defined responseSchema = ModelUtils.getSchemaFromResponse(response); } @@ -4487,38 +4562,38 @@ public CodegenCallback fromCallback(String name, Callback callback, List } Stream.of( - Pair.of("get", pi.getGet()), - Pair.of("head", pi.getHead()), - Pair.of("put", pi.getPut()), - Pair.of("post", pi.getPost()), - Pair.of("delete", pi.getDelete()), - Pair.of("patch", pi.getPatch()), - Pair.of("options", pi.getOptions())) - .filter(p -> p.getValue() != null) - .forEach(p -> { - String method = p.getKey(); - Operation op = p.getValue(); - - boolean genId = op.getOperationId() == null; - if (genId) { - op.setOperationId(getOrGenerateOperationId(op, c.name + "_" + expression.replaceAll("\\{\\$.*}", ""), method)); - } + Pair.of("get", pi.getGet()), + Pair.of("head", pi.getHead()), + Pair.of("put", pi.getPut()), + Pair.of("post", pi.getPost()), + Pair.of("delete", pi.getDelete()), + Pair.of("patch", pi.getPatch()), + Pair.of("options", pi.getOptions())) + .filter(p -> p.getValue() != null) + .forEach(p -> { + String method = p.getKey(); + Operation op = p.getValue(); + + boolean genId = op.getOperationId() == null; + if (genId) { + op.setOperationId(getOrGenerateOperationId(op, c.name + "_" + expression.replaceAll("\\{\\$.*}", ""), method)); + } - if (op.getExtensions() == null) { - op.setExtensions(new HashMap<>()); - } - // This extension will be removed later by `fromOperation()` as it is only needed here to - // distinguish between normal operations and callback requests - op.getExtensions().put("x-callback-request", true); - - CodegenOperation co = fromOperation(expression, method, op, servers); - if (genId) { - co.operationIdOriginal = null; - // legacy (see `fromOperation()`) - co.nickname = co.operationId; - } - u.requests.add(co); - }); + if (op.getExtensions() == null) { + op.setExtensions(new HashMap<>()); + } + // This extension will be removed later by `fromOperation()` as it is only needed here to + // distinguish between normal operations and callback requests + op.getExtensions().put("x-callback-request", true); + + CodegenOperation co = fromOperation(expression, method, op, servers); + if (genId) { + co.operationIdOriginal = null; + // legacy (see `fromOperation()`) + co.nickname = co.operationId; + } + u.requests.add(co); + }); c.urls.add(u); }); @@ -4671,6 +4746,7 @@ public CodegenParameter fromParameter(Parameter parameter, Set imports) return codegenParameter; } + // TODO need to reivew replacing empty map with schemaMapping instead parameterSchema = unaliasSchema(parameterSchema, Collections.emptyMap()); if (parameterSchema == null) { LOGGER.warn("warning! Schema not found for parameter \" {} \"", parameter.getName()); @@ -4838,19 +4914,19 @@ public CodegenParameter fromParameter(Parameter parameter, Set imports) Map> properties = schema.getProperties(); if (properties != null) { codegenParameter.items.vars = - properties.entrySet().stream() - .map(entry -> { - CodegenProperty property = fromProperty(entry.getKey(), entry.getValue()); - property.baseName = codegenParameter.baseName + "[" + entry.getKey() + "]"; - return property; - }).collect(Collectors.toList()); + properties.entrySet().stream() + .map(entry -> { + CodegenProperty property = fromProperty(entry.getKey(), entry.getValue()); + property.baseName = codegenParameter.baseName + "[" + entry.getKey() + "]"; + return property; + }).collect(Collectors.toList()); } else { //LOGGER.error("properties is null: {}", schema); } } else { LOGGER.warn( - "No object schema found for deepObject parameter{} deepObject won't have specific properties", - codegenParameter); + "No object schema found for deepObject parameter{} deepObject won't have specific properties", + codegenParameter); } } @@ -5080,7 +5156,7 @@ protected String getOrGenerateOperationId(Operation operation, String path, Stri */ protected boolean needToImport(String type) { return StringUtils.isNotBlank(type) && !defaultIncludes.contains(type) - && !languageSpecificPrimitives.contains(type); + && !languageSpecificPrimitives.contains(type); } @SuppressWarnings("static-method") @@ -5144,7 +5220,7 @@ protected void addHeaders(ApiResponse response, List properties @Override @SuppressWarnings("static-method") public void addOperationToGroup(String tag, String resourcePath, Operation operation, CodegenOperation - co, Map> operations) { + co, Map> operations) { List opList = operations.get(tag); if (opList == null) { opList = new ArrayList<>(); @@ -5240,13 +5316,13 @@ protected void addImport(CodegenModel m, String type) { addImport(m.imports, type); } - private void addImport(Set importsToBeAddedTo, String type) { + protected void addImport(Set importsToBeAddedTo, String type) { if (shouldAddImport(type)) { importsToBeAddedTo.add(type); } } - private boolean shouldAddImport(String type) { + protected boolean shouldAddImport(String type) { return type != null && needToImport(type); } @@ -5259,7 +5335,7 @@ private boolean shouldAddImport(String type) { protected Map unaliasPropertySchema(Map properties) { if (properties != null) { for (String key : properties.keySet()) { - properties.put(key, unaliasSchema(properties.get(key), importMapping())); + properties.put(key, unaliasSchema(properties.get(key), schemaMapping())); } } @@ -5268,14 +5344,14 @@ protected Map unaliasPropertySchema(Map properti } protected void addVars(CodegenModel m, Map properties, List required, - Map allProperties, List allRequired) { + Map allProperties, List allRequired) { m.hasRequired = false; if (properties != null && !properties.isEmpty()) { m.hasVars = true; Set mandatory = required == null ? Collections.emptySet() - : new TreeSet<>(required); + : new TreeSet<>(required); // update "vars" without parent's properties (all, required) addVars(m, m.vars, properties, mandatory); @@ -5288,7 +5364,7 @@ protected void addVars(CodegenModel m, Map properties, List allMandatory = allRequired == null ? Collections.emptySet() - : new TreeSet<>(allRequired); + : new TreeSet<>(allRequired); // update "vars" with parent's properties (all, required) addVars(m, m.allVars, allProperties, allMandatory); m.allMandatory = allMandatory; @@ -5419,10 +5495,10 @@ Map getAllAliases(Map schemas) { private static Boolean isAliasOfSimpleTypes(Schema schema) { return (!ModelUtils.isObjectSchema(schema) - && !ModelUtils.isArraySchema(schema) - && !ModelUtils.isMapSchema(schema) - && !ModelUtils.isComposedSchema(schema) - && schema.getEnum() == null); + && !ModelUtils.isArraySchema(schema) + && !ModelUtils.isMapSchema(schema) + && !ModelUtils.isComposedSchema(schema) + && schema.getEnum() == null); } /** @@ -5445,8 +5521,8 @@ public String removeNonNameElementToCamelCase(String name) { */ protected String removeNonNameElementToCamelCase(final String name, final String nonNameElementPattern) { String result = Arrays.stream(name.split(nonNameElementPattern)) - .map(StringUtils::capitalize) - .collect(Collectors.joining("")); + .map(StringUtils::capitalize) + .collect(Collectors.joining("")); if (result.length() > 0) { result = result.substring(0, 1).toLowerCase(Locale.ROOT) + result.substring(1); } @@ -5966,9 +6042,9 @@ public void updateCodegenPropertyEnum(CodegenProperty var) { String varDataType = var.mostInnerItems != null ? var.mostInnerItems.dataType : var.dataType; Optional referencedSchema = ModelUtils.getSchemas(openAPI).entrySet().stream() - .filter(entry -> Objects.equals(varDataType, toModelName(entry.getKey()))) - .map(Map.Entry::getValue) - .findFirst(); + .filter(entry -> Objects.equals(varDataType, toModelName(entry.getKey()))) + .map(Map.Entry::getValue) + .findFirst(); String dataType = (referencedSchema.isPresent()) ? getTypeDeclaration(referencedSchema.get()) : varDataType; List> enumVars = buildEnumVars(values, dataType); @@ -6190,7 +6266,7 @@ private void addConsumesInfo(Operation operation, CodegenOperation codegenOperat // skip as it implies `consumes` in OAS2 is not defined continue; } else { - mediaType.put("mediaType", escapeText(escapeQuotationMark(key))); + mediaType.put("mediaType", escapeQuotationMark(key)); } mediaTypeList.add(mediaType); } @@ -6219,8 +6295,8 @@ public boolean hasFormParameter(OpenAPI openAPI, Operation operation) { for (String consume : consumesInfo) { if (consume != null && - (consume.toLowerCase(Locale.ROOT).startsWith("application/x-www-form-urlencoded") || - consume.toLowerCase(Locale.ROOT).startsWith("multipart"))) { + (consume.toLowerCase(Locale.ROOT).startsWith("application/x-www-form-urlencoded") || + consume.toLowerCase(Locale.ROOT).startsWith("multipart"))) { return true; } } @@ -6256,7 +6332,7 @@ private void addProducesInfo(ApiResponse inputResponse, CodegenOperation codegen for (String key : produces) { // escape quotation to avoid code injection, "*/*" is a special case, do nothing - String encodedKey = "*/*".equals(key) ? key : escapeText(escapeQuotationMark(key)); + String encodedKey = "*/*".equals(key) ? key : escapeQuotationMark(key); //Only unique media types should be added to "produces" if (!existingMediaTypes.contains(encodedKey)) { Map mediaType = new HashMap<>(); @@ -6369,7 +6445,7 @@ public CodegenParameter fromFormProperty(String name, Schema propertySchema, Set LOGGER.debug("Debugging fromFormProperty {}: {}", name, propertySchema); CodegenProperty codegenProperty = fromProperty(name, propertySchema); - Schema ps = unaliasSchema(propertySchema, importMapping); + Schema ps = unaliasSchema(propertySchema, schemaMapping); ModelUtils.syncValidationProperties(ps, codegenParameter); codegenParameter.setTypeProperties(ps); codegenParameter.setComposedSchemas(getComposedSchemas(ps)); @@ -6571,9 +6647,9 @@ protected void addBodyModelSchema(CodegenParameter codegenParameter, String name codegenModelDescription = codegenModel.description; } else { LOGGER.warn("The following schema has undefined (null) baseType. " + - "It could be due to form parameter defined in OpenAPI v2 spec with incorrect consumes. " + - "A correct 'consumes' for form parameters should be " + - "'application/x-www-form-urlencoded' or 'multipart/?'"); + "It could be due to form parameter defined in OpenAPI v2 spec with incorrect consumes. " + + "A correct 'consumes' for form parameters should be " + + "'application/x-www-form-urlencoded' or 'multipart/?'"); LOGGER.warn("schema: {}", schema); LOGGER.warn("codegenModel is null. Default to UNKNOWN_BASE_TYPE"); codegenModelName = "UNKNOWN_BASE_TYPE"; @@ -6829,11 +6905,11 @@ protected LinkedHashMap getContent(Content content, Se } } CodegenEncoding ce = new CodegenEncoding( - enc.getContentType(), - headers, - enc.getStyle().toString(), - enc.getExplode().booleanValue(), - enc.getAllowReserved().booleanValue() + enc.getContentType(), + headers, + enc.getStyle().toString(), + enc.getExplode() == null ? false : enc.getExplode().booleanValue(), + enc.getAllowReserved() == null ? false : enc.getAllowReserved().booleanValue() ); String propName = encodingEntry.getKey(); ceMap.put(propName, ce); @@ -6880,7 +6956,7 @@ public CodegenParameter fromRequestBody(RequestBody body, Set imports, S name = ModelUtils.getSimpleRef(schema.get$ref()); } - Schema unaliasedSchema = unaliasSchema(schema, importMapping); + Schema unaliasedSchema = unaliasSchema(schema, schemaMapping); schema = ModelUtils.getReferencedSchema(this.openAPI, schema); ModelUtils.syncValidationProperties(unaliasedSchema, codegenParameter); @@ -6963,8 +7039,8 @@ protected void addVarsRequiredVarsAdditionalProps(Schema schema, IJsonSchemaVali } addVars(property, property.getVars(), objSchema.getProperties(), requiredVars); List requireCpVars = property.getVars() - .stream() - .filter(p -> Boolean.TRUE.equals(p.required)).collect(Collectors.toList()); + .stream() + .filter(p -> Boolean.TRUE.equals(p.required)).collect(Collectors.toList()); property.setRequiredVars(requireCpVars); if (property.getRequiredVars() != null && property.getRequiredVars().size() > 0) { property.setHasRequired(true); @@ -7267,7 +7343,7 @@ protected void modifyFeatureSet(Consumer processor) { FeatureSet.Builder builder = getFeatureSet().modify(); processor.accept(builder); this.generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata) - .featureSet(builder.build()).build(); + .featureSet(builder.build()).build(); } /** @@ -7306,8 +7382,8 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; SanitizeNameOptions that = (SanitizeNameOptions) o; return Objects.equals(getName(), that.getName()) && - Objects.equals(getRemoveCharRegEx(), that.getRemoveCharRegEx()) && - Objects.equals(getExceptions(), that.getExceptions()); + Objects.equals(getRemoveCharRegEx(), that.getRemoveCharRegEx()) && + Objects.equals(getExceptions(), that.getExceptions()); } @Override @@ -7351,9 +7427,9 @@ public boolean isAnyTypeSchema(Schema schema) { } if (schema.getClass().equals(Schema.class) && schema.get$ref() == null && schema.getType() == null && - (schema.getProperties() == null || schema.getProperties().isEmpty()) && - schema.getAdditionalProperties() == null && schema.getNot() == null && - schema.getEnum() == null) { + (schema.getProperties() == null || schema.getProperties().isEmpty()) && + schema.getAdditionalProperties() == null && schema.getNot() == null && + schema.getEnum() == null) { return true; // If and when type arrays are supported in a future OAS specification, // we could return true if the type array includes all possible JSON schema types. @@ -7488,10 +7564,10 @@ private CodegenComposedSchemas getComposedSchemas(Schema schema) { anyOf = getComposedProperties(cs.getAnyOf(), "anyOf"); } return new CodegenComposedSchemas( - allOf, - oneOf, - anyOf, - notProperty + allOf, + oneOf, + anyOf, + notProperty ); } diff --git a/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/InlineModelResolver.java b/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/InlineModelResolver.java index b1d3aa22..20a98a71 100644 --- a/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/InlineModelResolver.java +++ b/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/InlineModelResolver.java @@ -42,6 +42,7 @@ public class InlineModelResolver { private Map addedModels = new HashMap<>(); private Map generatedSignature = new HashMap<>(); private Map inlineSchemaNameMapping = new HashMap<>(); + private Map inlineSchemaNameDefaults = new HashMap<>(); private Set inlineSchemaNameMappingValues = new HashSet<>(); public boolean resolveInlineEnums = false; @@ -60,11 +61,20 @@ public class InlineModelResolver { final Logger LOGGER = LoggerFactory.getLogger(InlineModelResolver.class); + public InlineModelResolver() { + this.inlineSchemaNameDefaults.put("arrayItemSuffix", "_inner"); + this.inlineSchemaNameDefaults.put("mapItemSuffix", "_value"); + } + public void setInlineSchemaNameMapping(Map inlineSchemaNameMapping) { this.inlineSchemaNameMapping = inlineSchemaNameMapping; this.inlineSchemaNameMappingValues = new HashSet<>(inlineSchemaNameMapping.values()); } + public void setInlineSchemaNameDefaults(Map inlineSchemaNameDefaults) { + this.inlineSchemaNameDefaults.putAll(inlineSchemaNameDefaults); + } + void flatten(OpenAPI openAPI) { this.openAPI = openAPI; @@ -123,9 +133,9 @@ private void flattenPaths() { Map callbacks = operation.getCallbacks(); if (callbacks != null) { operations.addAll(callbacks.values().stream() - .flatMap(callback -> callback.values().stream()) - .flatMap(pathItem -> pathItem.readOperations().stream()) - .collect(Collectors.toList())); + .flatMap(callback -> callback.values().stream()) + .flatMap(pathItem -> pathItem.readOperations().stream()) + .collect(Collectors.toList())); } } @@ -172,7 +182,7 @@ private boolean isModelNeeded(Schema schema) { if (m.getAnyOf() != null && !m.getAnyOf().isEmpty()) { return true; } - if (m.getOneOf() != null && !m.getOneOf().isEmpty() && !isNullableOneOfComposedSchema(m)) { + if (m.getOneOf() != null && !m.getOneOf().isEmpty() && !isNullableOneOfComposedSchema(m)) { // Equisoft patch return true; } } @@ -192,10 +202,10 @@ private void gatherInlineModels(Schema schema, String modelPrefix) { // if ref already, no inline schemas should be present but check for // any to catch OpenAPI violations if (isModelNeeded(schema) || "object".equals(schema.getType()) || - schema.getProperties() != null || schema.getAdditionalProperties() != null || - schema instanceof ComposedSchema) { + schema.getProperties() != null || schema.getAdditionalProperties() != null || + schema instanceof ComposedSchema) { LOGGER.error("Illegal schema found with $ref combined with other properties," + - " no properties should be defined alongside a $ref:\n " + schema.toString()); + " no properties should be defined alongside a $ref:\n " + schema.toString()); } return; } @@ -218,7 +228,7 @@ private void gatherInlineModels(Schema schema, String modelPrefix) { } else if (prop instanceof ComposedSchema) { ComposedSchema m = (ComposedSchema) prop; if (m.getAllOf() != null && m.getAllOf().size() == 1 && - !(m.getAllOf().get(0).getType() == null || "object".equals(m.getAllOf().get(0).getType()))) { + !(m.getAllOf().get(0).getType() == null || "object".equals(m.getAllOf().get(0).getType()))) { // allOf with only 1 type (non-model) LOGGER.info("allOf schema used by the property `{}` replaced by its only item (a type)", propName); props.put(propName, m.getAllOf().get(0)); @@ -230,7 +240,7 @@ private void gatherInlineModels(Schema schema, String modelPrefix) { if (schema.getAdditionalProperties() != null) { if (schema.getAdditionalProperties() instanceof Schema) { Schema inner = (Schema) schema.getAdditionalProperties(); - String schemaName = resolveModelName(schema.getTitle(), modelPrefix + "_value"); + String schemaName = resolveModelName(schema.getTitle(), modelPrefix + this.inlineSchemaNameDefaults.get("mapItemSuffix")); // Recurse to create $refs for inner models gatherInlineModels(inner, schemaName); if (isModelNeeded(inner)) { @@ -243,13 +253,13 @@ private void gatherInlineModels(Schema schema, String modelPrefix) { } else if (schema.getProperties() != null) { // If non-object type is specified but also properties LOGGER.error("Illegal schema found with non-object type combined with properties," + - " no properties should be defined:\n " + schema.toString()); + " no properties should be defined:\n " + schema.toString()); return; } else if (schema.getAdditionalProperties() != null) { // If non-object type is specified but also additionalProperties LOGGER.error("Illegal schema found with non-object type combined with" + - " additionalProperties, no additionalProperties should be defined:\n " + - schema.toString()); + " additionalProperties, no additionalProperties should be defined:\n " + + schema.toString()); return; } // Check array items @@ -262,10 +272,10 @@ private void gatherInlineModels(Schema schema, String modelPrefix) { }*/ if (items == null) { LOGGER.error("Illegal schema found with array type but no items," + - " items must be defined for array schemas:\n " + schema.toString()); + " items must be defined for array schemas:\n " + schema.toString()); return; } - String schemaName = resolveModelName(items.getTitle(), modelPrefix + "_inner"); + String schemaName = resolveModelName(items.getTitle(), modelPrefix + this.inlineSchemaNameDefaults.get("arrayItemSuffix")); // Recurse to create $refs for inner models gatherInlineModels(items, schemaName); @@ -322,7 +332,7 @@ private void gatherInlineModels(Schema schema, String modelPrefix) { } m.setAnyOf(newAnyOf); } - if (m.getOneOf() != null && !isNullableOneOfComposedSchema(m)) { + if (m.getOneOf() != null && !isNullableOneOfComposedSchema(m)) { // Equisoft patch List newOneOf = new ArrayList(); for (Schema inner : m.getOneOf()) { String schemaName = resolveModelName(inner.getTitle(), modelPrefix + "_oneOf"); @@ -402,7 +412,7 @@ private void flattenRequestBody(String modelName, Operation operation) { } flattenContent(requestBody.getContent(), - (operation.getOperationId() == null ? modelName : operation.getOperationId()) + "_request"); + (operation.getOperationId() == null ? modelName : operation.getOperationId()) + "_request"); } /** @@ -428,7 +438,7 @@ private void flattenParameters(String modelName, Operation operation) { continue; } String schemaName = resolveModelName(parameterSchema.getTitle(), - (operation.getOperationId() == null ? modelName : operation.getOperationId()) + "_" + parameter.getName() + "_parameter"); + (operation.getOperationId() == null ? modelName : operation.getOperationId()) + "_" + parameter.getName() + "_parameter"); // Recursively gather/make inline models within this schema if any gatherInlineModels(parameterSchema, schemaName); if (isModelNeeded(parameterSchema)) { @@ -456,7 +466,7 @@ private void flattenResponses(String modelName, Operation operation) { ApiResponse response = responsesEntry.getValue(); flattenContent(response.getContent(), - (operation.getOperationId() == null ? modelName : operation.getOperationId()) + "_" + key + "_response"); + (operation.getOperationId() == null ? modelName : operation.getOperationId()) + "_" + key + "_response"); } } @@ -494,9 +504,9 @@ private void flattenComposedChildren(String key, List children) { while (listIterator.hasNext()) { Schema component = listIterator.next(); if ((component != null) && - (component.get$ref() == null) && - ((component.getProperties() != null && !component.getProperties().isEmpty()) || - (component.getEnum() != null && !component.getEnum().isEmpty()))) { + (component.get$ref() == null) && + ((component.getProperties() != null && !component.getProperties().isEmpty()) || + (component.getEnum() != null && !component.getEnum().isEmpty()))) { // If a `title` attribute is defined in the inline schema, codegen uses it to name the // inline schema. Otherwise, we'll use the default naming such as InlineObject1, etc. // We know that this is not the best way to name the model. @@ -623,8 +633,8 @@ private void addGenerated(String name, Schema model) { */ private String sanitizeName(final String name) { return name - .replaceAll("^[0-9]", "_$0") // e.g. 12object => _12object - .replaceAll("[^A-Za-z0-9]", "_"); // e.g. io.schema.User name => io_schema_User_name + .replaceAll("^[0-9]", "_$0") // e.g. 12object => _12object + .replaceAll("[^A-Za-z0-9]", "_"); // e.g. io.schema.User name => io_schema_User_name } /** @@ -657,7 +667,7 @@ private void flattenProperties(OpenAPI openAPI, Map properties, String key = propertiesEntry.getKey(); Schema property = propertiesEntry.getValue(); if (property instanceof ObjectSchema && ((ObjectSchema) property).getProperties() != null - && ((ObjectSchema) property).getProperties().size() > 0) { + && ((ObjectSchema) property).getProperties().size() > 0) { ObjectSchema op = (ObjectSchema) property; String modelName = resolveModelName(op.getTitle(), path + "_" + key); Schema model = modelFromProperty(openAPI, op, modelName); diff --git a/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java b/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java deleted file mode 100644 index e01e1965..00000000 --- a/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java +++ /dev/null @@ -1,1079 +0,0 @@ -/* - * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) - * Copyright 2018 SmartBear Software - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.openapitools.codegen.languages; - -import com.fasterxml.jackson.databind.node.ArrayNode; -import io.swagger.v3.oas.models.media.ArraySchema; -import io.swagger.v3.oas.models.media.ComposedSchema; -import io.swagger.v3.oas.models.media.Schema; -import io.swagger.v3.oas.models.media.StringSchema; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.lang3.StringUtils; -import org.openapitools.codegen.*; -import org.openapitools.codegen.model.ModelMap; -import org.openapitools.codegen.model.ModelsMap; -import org.openapitools.codegen.utils.ModelUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.openapitools.codegen.utils.StringUtils.*; - -public abstract class AbstractKotlinCodegen extends DefaultCodegen implements CodegenConfig { - - public static final String SERIALIZATION_LIBRARY_DESC = "What serialization library to use: 'moshi' (default), or 'gson' or 'jackson'"; - - public enum SERIALIZATION_LIBRARY_TYPE {moshi, gson, jackson, kotlinx_serialization} - - public static final String MODEL_MUTABLE = "modelMutable"; - public static final String MODEL_MUTABLE_DESC = "Create mutable models"; - - private final Logger LOGGER = LoggerFactory.getLogger(AbstractKotlinCodegen.class); - - protected String artifactId; - protected String artifactVersion = "1.0.0"; - protected String groupId = "org.openapitools"; - protected String packageName = "org.openapitools"; - protected String apiSuffix = "Api"; - - protected String sourceFolder = "src/main/kotlin"; - protected String testFolder = "src/test/kotlin"; - - protected String apiDocPath = "docs/"; - protected String modelDocPath = "docs/"; - protected boolean parcelizeModels = false; - protected boolean serializableModel = false; - protected boolean needsDataClassBody = false; - - protected boolean nonPublicApi = false; - - protected CodegenConstants.ENUM_PROPERTY_NAMING_TYPE enumPropertyNaming = CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.camelCase; - protected SERIALIZATION_LIBRARY_TYPE serializationLibrary = SERIALIZATION_LIBRARY_TYPE.moshi; - - // model classes cannot use the same property names defined in HashMap - // ref: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-hash-map/ - protected Set propertyAdditionalKeywords = new HashSet<>(Arrays.asList("entries", "keys", "size", "values")); - - public AbstractKotlinCodegen() { - super(); - - supportsInheritance = true; - setSortModelPropertiesByRequiredFlag(true); - - languageSpecificPrimitives = new HashSet<>(Arrays.asList( - "kotlin.Byte", - "kotlin.ByteArray", - "kotlin.Short", - "kotlin.Int", - "kotlin.Long", - "kotlin.Float", - "kotlin.Double", - "kotlin.Boolean", - "kotlin.Char", - "kotlin.String", - "kotlin.Array", - "kotlin.collections.List", - "kotlin.collections.MutableList", - "kotlin.collections.Map", - "kotlin.collections.MutableMap", - "kotlin.collections.Set", - "kotlin.collections.MutableSet" - )); - - // this includes hard reserved words defined by https://github.com/JetBrains/kotlin/blob/master/core/descriptors/src/org/jetbrains/kotlin/renderer/KeywordStringsGenerated.java - // as well as keywords from https://kotlinlang.org/docs/reference/keyword-reference.html - reservedWords = new HashSet<>(Arrays.asList( - "ApiResponse", // Used in the auto-generated api client - "abstract", - "actual", - "annotation", - "as", - "break", - "class", - "companion", - "const", - "constructor", - "continue", - "crossinline", - "data", - "delegate", - "do", - "dynamic", - "else", - "enum", - "expect", - "external", - "false", - "field", - "final", - "finally", - "for", - "fun", - "if", - "import", - "in", - "infix", - "init", - "inline", - "inner", - "interface", - "internal", - "is", - "it", - "lateinit", - "noinline", - "null", - "object", - "open", - "operator", - "out", - "override", - "package", - "param", - "private", - "property", - "protected", - "public", - "receiver", - "reified", - "return", - "sealed", - "setparam", - "super", - "suspend", - "tailrec", - "this", - "throw", - "true", - "try", - "typealias", - "typeof", - "val", - "value", - "var", - "vararg", - "when", - "where", - "while" - )); - - defaultIncludes = new HashSet<>(Arrays.asList( - "kotlin.Byte", - "kotlin.ByteArray", - "kotlin.Short", - "kotlin.Int", - "kotlin.Long", - "kotlin.Float", - "kotlin.Double", - "kotlin.Boolean", - "kotlin.Char", - "kotlin.Array", - "kotlin.collections.List", - "kotlin.collections.MutableList", - "kotlin.collections.Set", - "kotlin.collections.MutableSet", - "kotlin.collections.Map", - "kotlin.collections.MutableMap" - )); - - typeMapping = new HashMap<>(); - typeMapping.put("string", "kotlin.String"); - typeMapping.put("boolean", "kotlin.Boolean"); - typeMapping.put("integer", "kotlin.Int"); - typeMapping.put("float", "kotlin.Float"); - typeMapping.put("long", "kotlin.Long"); - typeMapping.put("double", "kotlin.Double"); - typeMapping.put("ByteArray", "kotlin.ByteArray"); - typeMapping.put("number", "java.math.BigDecimal"); - typeMapping.put("decimal", "java.math.BigDecimal"); - typeMapping.put("date-time", "java.time.OffsetDateTime"); - typeMapping.put("date", "java.time.LocalDate"); - typeMapping.put("file", "java.io.File"); - typeMapping.put("array", "kotlin.Array"); - typeMapping.put("list", "kotlin.collections.List"); - typeMapping.put("set", "kotlin.collections.Set"); - typeMapping.put("map", "kotlin.collections.Map"); - typeMapping.put("object", "kotlin.Any"); - typeMapping.put("binary", "kotlin.ByteArray"); - typeMapping.put("Date", "java.time.LocalDate"); - typeMapping.put("DateTime", "java.time.OffsetDateTime"); - typeMapping.put("AnyType", "kotlin.Any"); - - instantiationTypes.put("array", "kotlin.collections.ArrayList"); - instantiationTypes.put("list", "kotlin.collections.ArrayList"); - instantiationTypes.put("map", "kotlin.collections.HashMap"); - - importMapping = new HashMap<>(); - importMapping.put("BigDecimal", "java.math.BigDecimal"); - importMapping.put("UUID", "java.util.UUID"); - importMapping.put("URI", "java.net.URI"); - importMapping.put("File", "java.io.File"); - importMapping.put("Date", "java.time.LocalDate"); - importMapping.put("Timestamp", "java.sql.Timestamp"); - importMapping.put("DateTime", "java.time.OffsetDateTime"); - importMapping.put("LocalDateTime", "java.time.LocalDateTime"); - importMapping.put("LocalDate", "java.time.LocalDate"); - importMapping.put("LocalTime", "java.time.LocalTime"); - - specialCharReplacements.put(";", "Semicolon"); - - cliOptions.clear(); - addOption(CodegenConstants.SOURCE_FOLDER, CodegenConstants.SOURCE_FOLDER_DESC, sourceFolder); - addOption(CodegenConstants.PACKAGE_NAME, "Generated artifact package name.", packageName); - addOption(CodegenConstants.API_SUFFIX, CodegenConstants.API_SUFFIX_DESC, apiSuffix); - addOption(CodegenConstants.GROUP_ID, "Generated artifact package's organization (i.e. maven groupId).", groupId); - addOption(CodegenConstants.ARTIFACT_ID, "Generated artifact id (name of jar).", artifactId); - addOption(CodegenConstants.ARTIFACT_VERSION, "Generated artifact's package version.", artifactVersion); - - CliOption enumPropertyNamingOpt = new CliOption(CodegenConstants.ENUM_PROPERTY_NAMING, CodegenConstants.ENUM_PROPERTY_NAMING_DESC); - cliOptions.add(enumPropertyNamingOpt.defaultValue(enumPropertyNaming.name())); - - CliOption serializationLibraryOpt = new CliOption(CodegenConstants.SERIALIZATION_LIBRARY, SERIALIZATION_LIBRARY_DESC); - cliOptions.add(serializationLibraryOpt.defaultValue(serializationLibrary.name())); - - cliOptions.add(new CliOption(CodegenConstants.PARCELIZE_MODELS, CodegenConstants.PARCELIZE_MODELS_DESC)); - cliOptions.add(new CliOption(CodegenConstants.SERIALIZABLE_MODEL, CodegenConstants.SERIALIZABLE_MODEL_DESC)); - cliOptions.add(new CliOption(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG, CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG_DESC)); - cliOptions.add(new CliOption(CodegenConstants.SORT_MODEL_PROPERTIES_BY_REQUIRED_FLAG, CodegenConstants.SORT_MODEL_PROPERTIES_BY_REQUIRED_FLAG_DESC)); - - cliOptions.add(CliOption.newBoolean(MODEL_MUTABLE, MODEL_MUTABLE_DESC, false)); - } - - @Override - public String apiDocFileFolder() { - return (outputFolder + File.separator + apiDocPath).replace('/', File.separatorChar); - } - - @Override - public String apiFileFolder() { - return (outputFolder + File.separator + sourceFolder + File.separator + apiPackage().replace('.', File.separatorChar)).replace('/', File.separatorChar); - } - - @Override - public String apiTestFileFolder() { - return (outputFolder + File.separator + testFolder + File.separator + apiPackage().replace('.', File.separatorChar)).replace('/', File.separatorChar); - } - - @Override - public String escapeQuotationMark(String input) { - // remove " to avoid code injection - return input.replace("\"", ""); - } - - @Override - public String escapeReservedWord(String name) { - // TODO: Allow enum escaping as an option (e.g. backticks vs append/prepend underscore vs match model property escaping). - return String.format(Locale.ROOT, "`%s`", name); - } - - @Override - public String escapeUnsafeCharacters(String input) { - return input.replace("*/", "*_/").replace("/*", "/_*"); - } - - public CodegenConstants.ENUM_PROPERTY_NAMING_TYPE getEnumPropertyNaming() { - return this.enumPropertyNaming; - } - - public SERIALIZATION_LIBRARY_TYPE getSerializationLibrary() { - return this.serializationLibrary; - } - - /** - * Sets the naming convention for Kotlin enum properties - * - * @param enumPropertyNamingType The string representation of the naming convention, as defined by {@link org.openapitools.codegen.CodegenConstants.ENUM_PROPERTY_NAMING_TYPE} - */ - public void setEnumPropertyNaming(final String enumPropertyNamingType) { - try { - this.enumPropertyNaming = CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.valueOf(enumPropertyNamingType); - } catch (IllegalArgumentException ex) { - StringBuilder sb = new StringBuilder(enumPropertyNamingType + " is an invalid enum property naming option. Please choose from:"); - for (CodegenConstants.ENUM_PROPERTY_NAMING_TYPE t : CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.values()) { - sb.append("\n ").append(t.name()); - } - throw new RuntimeException(sb.toString()); - } - } - - /** - * Sets the serialization engine for Kotlin - * - * @param enumSerializationLibrary The string representation of the serialization library as defined by - * {@link org.openapitools.codegen.languages.AbstractKotlinCodegen.SERIALIZATION_LIBRARY_TYPE} - */ - public void setSerializationLibrary(final String enumSerializationLibrary) { - try { - this.serializationLibrary = SERIALIZATION_LIBRARY_TYPE.valueOf(enumSerializationLibrary); - } catch (IllegalArgumentException ex) { - StringBuilder sb = new StringBuilder(enumSerializationLibrary + " is an invalid enum property naming option. Please choose from:"); - for (SERIALIZATION_LIBRARY_TYPE t : SERIALIZATION_LIBRARY_TYPE.values()) { - sb.append("\n ").append(t.name()); - } - throw new RuntimeException(sb.toString()); - } - } - - /** - * returns the OpenAPI type for the property - * - * @param p OpenAPI property object - * @return string presentation of the type - **/ - @Override - public String getSchemaType(Schema p) { - String openAPIType = super.getSchemaType(p); - String type; - // This maps, for example, long -> kotlin.Long based on hashes in this type's constructor - if (typeMapping.containsKey(openAPIType)) { - type = typeMapping.get(openAPIType); - if (languageSpecificPrimitives.contains(type)) { - return toModelName(type); - } - } else { - type = openAPIType; - } - return toModelName(type); - } - - /** - * Output the type declaration of the property - * - * @param p OpenAPI Property object - * @return a string presentation of the property type - */ - @Override - public String getTypeDeclaration(Schema p) { - Schema schema = ModelUtils.unaliasSchema(this.openAPI, p, importMapping); - Schema target = ModelUtils.isGenerateAliasAsModel() ? p : schema; - if (ModelUtils.isArraySchema(target)) { - Schema items = getSchemaItems((ArraySchema) schema); - return getSchemaType(target) + "<" + getTypeDeclaration(items) + ">"; - } else if (ModelUtils.isMapSchema(target)) { - // Note: ModelUtils.isMapSchema(p) returns true when p is a composed schema that also defines - // additionalproperties: true - Schema inner = getAdditionalProperties(target); - if (inner == null) { - LOGGER.error("`{}` (map property) does not have a proper inner type defined. Default to type:string", p.getName()); - inner = new StringSchema().description("TODO default missing map inner type to string"); - p.setAdditionalProperties(inner); - } - return getSchemaType(target) + ""; - } - return super.getTypeDeclaration(target); - } - - @Override - public String modelDocFileFolder() { - return (outputFolder + "/" + modelDocPath).replace('/', File.separatorChar); - } - - @Override - public String modelFileFolder() { - return outputFolder + File.separator + sourceFolder + File.separator + modelPackage().replace('.', File.separatorChar); - } - - @Override - public ModelsMap postProcessModels(ModelsMap objs) { - objs = super.postProcessModelsEnum(objs); - for (ModelMap mo : objs.getModels()) { - CodegenModel cm = mo.getModel(); - if (cm.getDiscriminator() != null) { - cm.vendorExtensions.put("x-has-data-class-body", true); - break; - } - - for (CodegenProperty var : cm.vars) { - if (var.isEnum || isSerializableModel()) { - cm.vendorExtensions.put("x-has-data-class-body", true); - break; - } - } - } - return postProcessModelsEnum(objs); - } - - @Override - public void processOpts() { - super.processOpts(); - - if (StringUtils.isEmpty(System.getenv("KOTLIN_POST_PROCESS_FILE"))) { - LOGGER.info("Environment variable KOTLIN_POST_PROCESS_FILE not defined so the Kotlin code may not be properly formatted. To define it, try 'export KOTLIN_POST_PROCESS_FILE=\"/usr/local/bin/ktlint -F\"' (Linux/Mac)"); - LOGGER.info("NOTE: To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI)."); - } - - if (additionalProperties.containsKey(MODEL_MUTABLE)) { - additionalProperties.put(MODEL_MUTABLE, Boolean.parseBoolean(additionalProperties.get(MODEL_MUTABLE).toString())); - } - - if (additionalProperties.containsKey(CodegenConstants.ENUM_PROPERTY_NAMING)) { - setEnumPropertyNaming((String) additionalProperties.get(CodegenConstants.ENUM_PROPERTY_NAMING)); - } - - if (additionalProperties.containsKey(CodegenConstants.SERIALIZATION_LIBRARY)) { - setSerializationLibrary((String) additionalProperties.get(CodegenConstants.SERIALIZATION_LIBRARY)); - additionalProperties.put(this.serializationLibrary.name(), true); - } else { - additionalProperties.put(this.serializationLibrary.name(), true); - } - - if (additionalProperties.containsKey(CodegenConstants.SOURCE_FOLDER)) { - this.setSourceFolder((String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER)); - } else { - additionalProperties.put(CodegenConstants.SOURCE_FOLDER, sourceFolder); - } - - if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { - this.setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME)); - if (!additionalProperties.containsKey(CodegenConstants.MODEL_PACKAGE)) - this.setModelPackage(packageName + ".models"); - if (!additionalProperties.containsKey(CodegenConstants.API_PACKAGE)) - this.setApiPackage(packageName + ".apis"); - } else { - additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName); - } - - if (additionalProperties.containsKey(CodegenConstants.API_SUFFIX)) { - this.setApiSuffix((String) additionalProperties.get(CodegenConstants.API_SUFFIX)); - } - - if (additionalProperties.containsKey(CodegenConstants.ARTIFACT_ID)) { - this.setArtifactId((String) additionalProperties.get(CodegenConstants.ARTIFACT_ID)); - } else { - additionalProperties.put(CodegenConstants.ARTIFACT_ID, artifactId); - } - - if (additionalProperties.containsKey(CodegenConstants.GROUP_ID)) { - this.setGroupId((String) additionalProperties.get(CodegenConstants.GROUP_ID)); - } else { - additionalProperties.put(CodegenConstants.GROUP_ID, groupId); - } - - if (additionalProperties.containsKey(CodegenConstants.ARTIFACT_VERSION)) { - this.setArtifactVersion((String) additionalProperties.get(CodegenConstants.ARTIFACT_VERSION)); - } else { - additionalProperties.put(CodegenConstants.ARTIFACT_VERSION, artifactVersion); - } - - if (additionalProperties.containsKey(CodegenConstants.INVOKER_PACKAGE)) { - LOGGER.warn("{} with {} generator is ignored. Use {}.", CodegenConstants.INVOKER_PACKAGE, this.getName(), CodegenConstants.PACKAGE_NAME); - } - - if (additionalProperties.containsKey(CodegenConstants.SERIALIZABLE_MODEL)) { - this.setSerializableModel(convertPropertyToBooleanAndWriteBack(CodegenConstants.SERIALIZABLE_MODEL)); - } else { - additionalProperties.put(CodegenConstants.SERIALIZABLE_MODEL, serializableModel); - } - - if (additionalProperties.containsKey(CodegenConstants.LIBRARY)) { - this.setLibrary((String) additionalProperties.get(CodegenConstants.LIBRARY)); - } - - if (additionalProperties.containsKey(CodegenConstants.PARCELIZE_MODELS)) { - this.setParcelizeModels(convertPropertyToBooleanAndWriteBack(CodegenConstants.PARCELIZE_MODELS)); - } else { - additionalProperties.put(CodegenConstants.PARCELIZE_MODELS, parcelizeModels); - } - - if (additionalProperties.containsKey(CodegenConstants.NON_PUBLIC_API)) { - this.setNonPublicApi(convertPropertyToBooleanAndWriteBack(CodegenConstants.NON_PUBLIC_API)); - } else { - additionalProperties.put(CodegenConstants.NON_PUBLIC_API, nonPublicApi); - } - - additionalProperties.put(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG, getSortParamsByRequiredFlag()); - additionalProperties.put(CodegenConstants.SORT_MODEL_PROPERTIES_BY_REQUIRED_FLAG, getSortModelPropertiesByRequiredFlag()); - - additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage()); - additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage()); - - additionalProperties.put("apiDocPath", apiDocPath); - additionalProperties.put("modelDocPath", modelDocPath); - - if (isModelMutable()) { - typeMapping.put("list", "kotlin.collections.MutableList"); - typeMapping.put("set", "kotlin.collections.MutableSet"); - typeMapping.put("map", "kotlin.collections.MutableMap"); - } - } - - protected boolean isModelMutable() { - return Boolean.TRUE.equals(additionalProperties.get(MODEL_MUTABLE)); - } - - public void setArtifactId(String artifactId) { - this.artifactId = artifactId; - } - - public void setArtifactVersion(String artifactVersion) { - this.artifactVersion = artifactVersion; - } - - public void setGroupId(String groupId) { - this.groupId = groupId; - } - - public void setPackageName(String packageName) { - this.packageName = packageName; - } - - public void setApiSuffix(String apiSuffix) { - this.apiSuffix = apiSuffix; - } - - public void setSourceFolder(String sourceFolder) { - this.sourceFolder = sourceFolder; - } - - public void setTestFolder(String testFolder) { - this.testFolder = testFolder; - } - - public Boolean getParcelizeModels() { - return parcelizeModels; - } - - public void setParcelizeModels(Boolean parcelizeModels) { - this.parcelizeModels = parcelizeModels; - } - - public boolean isSerializableModel() { - return serializableModel; - } - - public void setSerializableModel(boolean serializableModel) { - this.serializableModel = serializableModel; - } - - public boolean nonPublicApi() { - return nonPublicApi; - } - - public void setNonPublicApi(boolean nonPublicApi) { - this.nonPublicApi = nonPublicApi; - } - - public boolean isNeedsDataClassBody() { - return needsDataClassBody; - } - - public void setNeedsDataClassBody(boolean needsDataClassBody) { - this.needsDataClassBody = needsDataClassBody; - } - - /** - * Return the sanitized variable name for enum - * - * @param value enum variable name - * @param datatype data type - * @return the sanitized variable name for enum - */ - @Override - public String toEnumVarName(String value, String datatype) { - String modified; - if (value.length() == 0) { - modified = "EMPTY"; - } else { - modified = value; - modified = sanitizeKotlinSpecificNames(modified); - } - - switch (getEnumPropertyNaming()) { - case original: - // NOTE: This is provided as a last-case allowance, but will still result in reserved words being escaped. - modified = value; - break; - case camelCase: - // NOTE: Removes hyphens and underscores - modified = camelize(modified, true); - break; - case PascalCase: - // NOTE: Removes hyphens and underscores - String result = camelize(modified); - modified = titleCase(result); - break; - case snake_case: - // NOTE: Removes hyphens - modified = underscore(modified); - break; - case UPPERCASE: - modified = underscore(modified).toUpperCase(Locale.ROOT); - break; - } - - if (reservedWords.contains(modified)) { - return escapeReservedWord(modified); - } - // NOTE: another sanitize because camelize can create an invalid name - return sanitizeKotlinSpecificNames(modified); - } - - @Override - public String toEnumName(CodegenProperty property) { - return property.nameInCamelCase; - } - - @Override - public String toApiName(String name) { - if (name.length() == 0) { - return "DefaultApi"; - } - return (this.apiSuffix.isEmpty() ? camelize(name) : camelize(name) + this.apiSuffix); - } - - /** - * Return the fully-qualified "Model" name for import - * - * @param name the name of the "Model" - * @return the fully-qualified "Model" name for import - */ - @Override - public String toModelImport(String name) { - // toModelImport is called while processing operations, but DefaultCodegen doesn't - // define imports correctly with fully qualified primitives and models as defined in this generator. - if (needToImport(name)) { - return super.toModelImport(name); - } - - return name; - } - - /** - * Output the proper model name (capitalized). - * In case the name belongs to the TypeSystem it won't be renamed. - * - * @param name the name of the model - * @return capitalized model name - */ - @Override - public String toModelName(final String name) { - - // Allow for explicitly configured kotlin.* and java.* types - if (name.startsWith("kotlin.") || name.startsWith("java.")) { - return name; - } - - // If importMapping contains name, assume this is a legitimate model name. - if (importMapping.containsKey(name)) { - return importMapping.get(name); - } - - String modifiedName = name.replaceAll("\\.", ""); - String sanitizedName = sanitizeKotlinSpecificNames(modifiedName); - - String nameWithPrefixSuffix = sanitizedName; - if (!StringUtils.isEmpty(modelNamePrefix)) { - // add '_' so that model name can be camelized correctly - nameWithPrefixSuffix = modelNamePrefix + "_" + nameWithPrefixSuffix; - } - - if (!StringUtils.isEmpty(modelNameSuffix)) { - // add '_' so that model name can be camelized correctly - nameWithPrefixSuffix = nameWithPrefixSuffix + "_" + modelNameSuffix; - } - - // Camelize name of nested properties - modifiedName = camelize(nameWithPrefixSuffix); - - // model name cannot use reserved keyword, e.g. return - if (isReservedWord(modifiedName)) { - final String modelName = "Model" + modifiedName; - LOGGER.warn("{} (reserved word) cannot be used as model name. Renamed to {}", modifiedName, modelName); - return modelName; - } - - // model name starts with number - if (modifiedName.matches("^\\d.*")) { - final String modelName = "Model" + modifiedName; // e.g. 200Response => Model200Response (after camelize) - LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, - modelName); - return modelName; - } - - return titleCase(modifiedName); - } - - /** - * Return the operation ID (method name) - * - * @param operationId operation ID - * @return the sanitized method name - */ - @Override - public String toOperationId(String operationId) { - // throw exception if method name is empty - if (StringUtils.isEmpty(operationId)) - throw new RuntimeException("Empty method/operation name (operationId) not allowed"); - - operationId = camelize(sanitizeName(operationId), true); - - // method name cannot use reserved keyword, e.g. return - if (isReservedWord(operationId)) { - String newOperationId = camelize("call_" + operationId, true); - LOGGER.warn("{} (reserved word) cannot be used as method name. Renamed to {}", operationId, newOperationId); - return newOperationId; - } - - // operationId starts with a number - if (operationId.matches("^\\d.*")) { - LOGGER.warn(operationId + " (starting with a number) cannot be used as method sname. Renamed to " + camelize("call_" + operationId), true); - operationId = camelize("call_" + operationId, true); - } - - return operationId; - } - - @Override - public String toModelFilename(String name) { - // Should be the same as the model name - return toModelName(name); - } - - /** - * Sanitize against Kotlin specific naming conventions, which may differ from those required by {@link DefaultCodegen#sanitizeName}. - * - * @param name string to be sanitize - * @return sanitized string - */ - private String sanitizeKotlinSpecificNames(final String name) { - String word = name; - for (Map.Entry specialCharacters : specialCharReplacements.entrySet()) { - word = replaceSpecialCharacters(word, specialCharacters); - } - - // Fallback, replace unknowns with underscore. - word = word.replaceAll("\\W+", "_"); - if (word.matches("\\d.*")) { - word = "_" + word; - } - - // _, __, and ___ are reserved in Kotlin. Treat all names with only underscores consistently, regardless of count. - if (word.matches("^_*$")) { - word = word.replaceAll("\\Q_\\E", "Underscore"); - } - - return word; - } - - private String replaceSpecialCharacters(String word, Map.Entry specialCharacters) { - String specialChar = specialCharacters.getKey(); - String replacementChar = specialCharacters.getValue(); - // Underscore is the only special character we'll allow - if (!specialChar.equals("_") && word.contains(specialChar)) { - return replaceCharacters(word, specialChar, replacementChar); - } - return word; - } - - private String replaceCharacters(String word, String oldValue, String newValue) { - if (!word.contains(oldValue)) { - return word; - } - if (word.equals(oldValue)) { - return newValue; - } - int i = word.indexOf(oldValue); - String start = word.substring(0, i); - String end = recurseOnEndOfWord(word, oldValue, newValue, i); - return start + newValue + end; - } - - private String recurseOnEndOfWord(String word, String oldValue, String newValue, int lastReplacedValue) { - String end = word.substring(lastReplacedValue + 1); - if (!end.isEmpty()) { - end = titleCase(end); - end = replaceCharacters(end, oldValue, newValue); - } - return end; - } - - private String titleCase(final String input) { - return input.substring(0, 1).toUpperCase(Locale.ROOT) + input.substring(1); - } - - @Override - protected boolean isReservedWord(String word) { - // We want case-sensitive escaping, to avoid unnecessary backtick-escaping. - return reservedWords.contains(word); - } - - /** - * Check the type to see if it needs import the library/module/package - * - * @param type name of the type - * @return true if the library/module/package of the corresponding type needs to be imported - */ - @Override - protected boolean needToImport(String type) { - // provides extra protection against improperly trying to import language primitives and java types - boolean imports = !type.startsWith("kotlin.") && !type.startsWith("java.") && - !defaultIncludes.contains(type) && !languageSpecificPrimitives.contains(type) && - !type.contains("."); - return imports; - } - - @Override - public CodegenModel fromModel(String name, Schema schema) { - CodegenModel m = super.fromModel(name, schema); - m.optionalVars = m.optionalVars.stream().distinct().collect(Collectors.toList()); - // Update allVars/requiredVars/optionalVars with isInherited - // Each of these lists contains elements that are similar, but they are all cloned - // via CodegenModel.removeAllDuplicatedProperty and therefore need to be updated - // separately. - // First find only the parent vars via baseName matching - Map allVarsMap = m.allVars.stream() - .collect(Collectors.toMap(CodegenProperty::getBaseName, Function.identity())); - allVarsMap.keySet() - .removeAll(m.vars.stream().map(CodegenProperty::getBaseName).collect(Collectors.toSet())); - // Update the allVars - allVarsMap.values().forEach(p -> p.isInherited = true); - // Update any other vars (requiredVars, optionalVars) - Stream.of(m.requiredVars, m.optionalVars) - .flatMap(List::stream) - .filter(p -> allVarsMap.containsKey(p.baseName)) - .forEach(p -> p.isInherited = true); - return m; - } - - @Override - public String toEnumValue(String value, String datatype) { - if ("kotlin.Int".equals(datatype) || "kotlin.Long".equals(datatype)) { - return value; - } else if ("kotlin.Double".equals(datatype)) { - if (value.contains(".")) { - return value; - } else { - return value + ".0"; // Float and double must have .0 - } - } else if ("kotlin.Float".equals(datatype)) { - return value + "f"; - } else { - return "\"" + escapeText(value) + "\""; - } - } - - @Override - public boolean isDataTypeString(final String dataType) { - return "String".equals(dataType) || "kotlin.String".equals(dataType); - } - - @Override - public String toParamName(String name) { - // to avoid conflicts with 'callback' parameter for async call - if ("callback".equals(name)) { - return "paramCallback"; - } - - // should be the same as variable name - return toVariableName(name); - } - - @Override - public String toVarName(String name) { - name = toVariableName(name); - if (propertyAdditionalKeywords.contains(name)) { - return camelize("property_" + name, true); - } else { - return name; - } - } - - protected String toVariableName(String name) { - // sanitize name - name = sanitizeName(name, "\\W-[\\$]"); - name = sanitizeKotlinSpecificNames(name); - - if (name.toLowerCase(Locale.ROOT).matches("^_*class$")) { - return "propertyClass"; - } - - if ("_".equals(name)) { - name = "_u"; - } - - // if it's all upper case, do nothing - if (name.matches("^[A-Z0-9_]*$")) { - return name; - } - - if (startsWithTwoUppercaseLetters(name)) { - name = name.substring(0, 2).toLowerCase(Locale.ROOT) + name.substring(2); - } - - // If name contains special chars -> replace them. - if ((name.chars().anyMatch(character -> specialCharReplacements.keySet().contains(String.valueOf((char) character))))) { - List allowedCharacters = new ArrayList<>(); - allowedCharacters.add("_"); - allowedCharacters.add("$"); - name = escape(name, specialCharReplacements, allowedCharacters, "_"); - } - - // camelize (lower first character) the variable name - // pet_id => petId - name = camelize(name, true); - - // for reserved word or word starting with number or containing dollar symbol, escape it - if (isReservedWord(name) || name.matches("(^\\d.*)|(.*[$].*)")) { - name = escapeReservedWord(name); - } - - return name; - } - - @Override - public String toRegularExpression(String pattern) { - return escapeText(pattern); - } - - private boolean startsWithTwoUppercaseLetters(String name) { - boolean startsWithTwoUppercaseLetters = false; - if (name.length() > 1) { - startsWithTwoUppercaseLetters = name.substring(0, 2).equals(name.substring(0, 2).toUpperCase(Locale.ROOT)); - } - return startsWithTwoUppercaseLetters; - } - - @Override - public void postProcessFile(File file, String fileType) { - if (file == null) { - return; - } - - String kotlinPostProcessFile = System.getenv("KOTLIN_POST_PROCESS_FILE"); - if (StringUtils.isEmpty(kotlinPostProcessFile)) { - return; // skip if KOTLIN_POST_PROCESS_FILE env variable is not defined - } - - // only process files with kt extension - if ("kt".equals(FilenameUtils.getExtension(file.toString()))) { - String command = kotlinPostProcessFile + " " + file; - try { - Process p = Runtime.getRuntime().exec(command); - p.waitFor(); - int exitValue = p.exitValue(); - if (exitValue != 0) { - LOGGER.error("Error running the command ({}). Exit value: {}", command, exitValue); - } else { - LOGGER.info("Successfully executed: {}", command); - } - } catch (InterruptedException | IOException e) { - LOGGER.error("Error running the command ({}). Exception: {}", command, e.getMessage()); - // Restore interrupted state - Thread.currentThread().interrupt(); - } - } - } - - @Override - public String toDefaultValue(Schema schema) { - Schema p = ModelUtils.getReferencedSchema(this.openAPI, schema); - if (ModelUtils.isBooleanSchema(p)) { - if (p.getDefault() != null) { - return p.getDefault().toString(); - } - } else if (ModelUtils.isDateSchema(p)) { - // TODO - } else if (ModelUtils.isDateTimeSchema(p)) { - // TODO - } else if (ModelUtils.isNumberSchema(p)) { - if (p.getDefault() != null) { - return p.getDefault().toString(); - } - } else if (ModelUtils.isIntegerSchema(p)) { - if (p.getDefault() != null) { - return p.getDefault().toString(); - } - } else if (ModelUtils.isURISchema(p)) { - if (p.getDefault() != null) { - return importMapping.get("URI") + ".create(\"" + p.getDefault() + "\")"; - } - } else if (ModelUtils.isArraySchema(p)) { - if (p.getDefault() != null) { - String arrInstantiationType = ModelUtils.isSet(p) ? "set" : "arrayList"; - - ArrayNode _default = (ArrayNode) p.getDefault(); - if (_default.isEmpty()) { - return arrInstantiationType + "Of()"; - } - - StringBuilder defaultContent = new StringBuilder(); - Schema itemsSchema = getSchemaItems((ArraySchema) schema); - _default.elements().forEachRemaining((element) -> { - itemsSchema.setDefault(element.asText()); - defaultContent.append(toDefaultValue(itemsSchema)).append(","); - }); - defaultContent.deleteCharAt(defaultContent.length() - 1); // remove trailing comma - return arrInstantiationType + "Of(" + defaultContent + ")"; - } - } else if (ModelUtils.isStringSchema(p)) { - if (p.getDefault() != null) { - String _default = (String) p.getDefault(); - if (p.getEnum() == null) { - return "\"" + escapeText(_default) + "\""; - } else { - // convert to enum var name later in postProcessModels - return _default; - } - } - return null; - } - - return null; - } - - @Override - public GeneratorLanguage generatorLanguage() { - return GeneratorLanguage.KOTLIN; - } - - /** - * Equisoft Backport https://github.com/OpenAPITools/openapi-generator/pull/12594 - */ - @Override - protected void updateModelForObject(CodegenModel m, Schema schema) { - /** - * - * we have a custom version of this function so we only set isMap to true if - * ModelUtils.isMapSchema - * In other generators, isMap is true for all type object schemas - */ - if (schema.getProperties() != null || schema.getRequired() != null && !(schema instanceof ComposedSchema)) { - // passing null to allProperties and allRequired as there's no parent - addVars(m, unaliasPropertySchema(schema.getProperties()), schema.getRequired(), null, null); - } - if (ModelUtils.isMapSchema(schema)) { - // an object or anyType composed schema that has additionalProperties set - addAdditionPropertiesToCodeGenModel(m, schema); - } else { - m.setIsMap(false); - if (ModelUtils.isFreeFormObject(openAPI, schema)) { - // non-composed object type with no properties + additionalProperties - // additionalProperties must be null, ObjectSchema, or empty Schema - addAdditionPropertiesToCodeGenModel(m, schema); - } - } - // process 'additionalProperties' - setAddProps(schema, m); - } -} diff --git a/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java b/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java index 739cb678..502990ef 100644 --- a/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java +++ b/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java @@ -332,7 +332,9 @@ public String getTypeDeclaration(Schema p) { String type = super.getTypeDeclaration(p); return (!languageSpecificPrimitives.contains(type)) ? "\\" + modelPackage + "\\" + type : type; - } else if (p instanceof ComposedSchema) { + } + // Begin Equisoft patch + else if (p instanceof ComposedSchema) { // Support nullable defined using oneOf construct ComposedSchema composedSchema = (ComposedSchema)p; Boolean isNullable = Boolean.TRUE.equals(p.getNullable()) @@ -349,6 +351,7 @@ public String getTypeDeclaration(Schema p) { } } } + // End Equisoft patch return super.getTypeDeclaration(p); } diff --git a/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java b/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java index f149c2df..cb0c7c55 100644 --- a/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java +++ b/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java @@ -55,7 +55,7 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp public static final String SNAPSHOT = "snapshot"; public static final String MODEL_PROPERTY_NAMING_DESC_WITH_WARNING = CodegenConstants.MODEL_PROPERTY_NAMING_DESC - + ". Only change it if you provide your own run-time code for (de-)serialization of models"; + + ". Only change it if you provide your own run-time code for (de-)serialization of models"; public static final String NULL_SAFE_ADDITIONAL_PROPS = "nullSafeAdditionalProps"; public static final String NULL_SAFE_ADDITIONAL_PROPS_DESC = "Set to make additional properties types declare that their indexer may return undefined"; @@ -81,23 +81,23 @@ public AbstractTypeScriptClientCodegen() { super(); modifyFeatureSet(features -> features - .includeDocumentationFeatures(DocumentationFeature.Readme) - .wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON, WireFormatFeature.XML)) - .securityFeatures(EnumSet.noneOf( - SecurityFeature.class - )) - .excludeGlobalFeatures( - GlobalFeature.XMLStructureDefinitions, - GlobalFeature.Callbacks, - GlobalFeature.LinkObjects, - GlobalFeature.ParameterStyling - ) - .includeSchemaSupportFeatures( - SchemaSupportFeature.Polymorphism - ) - .includeClientModificationFeatures( - ClientModificationFeature.BasePath - ) + .includeDocumentationFeatures(DocumentationFeature.Readme) + .wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON, WireFormatFeature.XML)) + .securityFeatures(EnumSet.noneOf( + SecurityFeature.class + )) + .excludeGlobalFeatures( + GlobalFeature.XMLStructureDefinitions, + GlobalFeature.Callbacks, + GlobalFeature.LinkObjects, + GlobalFeature.ParameterStyling + ) + .includeSchemaSupportFeatures( + SchemaSupportFeature.Polymorphism + ) + .includeClientModificationFeatures( + ClientModificationFeature.BasePath + ) ); // clear import mapping (from default generator) as TS does not use it @@ -111,37 +111,37 @@ public AbstractTypeScriptClientCodegen() { // NOTE: TypeScript uses camel cased reserved words, while models are title cased. We don't want lowercase comparisons. reservedWords.addAll(Arrays.asList( - // local variable names used in API methods (endpoints) - "varLocalPath", "queryParameters", "headerParams", "formParams", "useFormData", "varLocalDeferred", - "requestOptions", - // Typescript reserved words - "abstract", "await", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "debugger", "default", "delete", "do", "double", "else", "enum", "export", "extends", "false", "final", "finally", "float", "for", "function", "goto", "if", "implements", "import", "in", "instanceof", "int", "interface", "let", "long", "native", "new", "null", "package", "private", "protected", "public", "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "transient", "true", "try", "typeof", "var", "void", "volatile", "while", "with", "yield")); + // local variable names used in API methods (endpoints) + "varLocalPath", "queryParameters", "headerParams", "formParams", "useFormData", "varLocalDeferred", + "requestOptions", + // Typescript reserved words + "abstract", "await", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "debugger", "default", "delete", "do", "double", "else", "enum", "export", "extends", "false", "final", "finally", "float", "for", "function", "goto", "if", "implements", "import", "in", "instanceof", "int", "interface", "let", "long", "native", "new", "null", "package", "private", "protected", "public", "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "transient", "true", "try", "typeof", "var", "void", "volatile", "while", "with", "yield")); languageSpecificPrimitives = new HashSet<>(Arrays.asList( - "string", - "String", - "boolean", - "Boolean", - "Double", - "Integer", - "Long", - "Float", - "Object", - "Array", - "ReadonlyArray", - "Date", - "number", - "any", - "File", - "Error", - "Map", - "object", - "Set", - "null" + "string", + "String", + "boolean", + "Boolean", + "Double", + "Integer", + "Long", + "Float", + "Object", + "Array", + "ReadonlyArray", + "Date", + "number", + "any", + "File", + "Error", + "Map", + "object", + "Set", + "null" // Equisoft patch )); languageGenericTypes = new HashSet<>(Collections.singletonList( - "Array" + "Array" )); instantiationTypes.put("array", "Array"); @@ -174,7 +174,7 @@ public AbstractTypeScriptClientCodegen() { typeMapping.put("URI", "string"); typeMapping.put("Error", "Error"); typeMapping.put("AnyType", "any"); - typeMapping.put("null", "null"); + typeMapping.put("null", "null"); // Equisoft patch cliOptions.add(new CliOption(CodegenConstants.ENUM_NAME_SUFFIX, CodegenConstants.ENUM_NAME_SUFFIX_DESC).defaultValue(this.enumSuffix)); cliOptions.add(new CliOption(CodegenConstants.ENUM_PROPERTY_NAMING, CodegenConstants.ENUM_PROPERTY_NAMING_DESC).defaultValue(this.enumPropertyNaming.name())); @@ -182,11 +182,11 @@ public AbstractTypeScriptClientCodegen() { cliOptions.add(new CliOption(CodegenConstants.SUPPORTS_ES6, CodegenConstants.SUPPORTS_ES6_DESC).defaultValue(String.valueOf(this.getSupportsES6()))); cliOptions.add(new CliOption(CodegenConstants.PARAM_NAMING, CodegenConstants.PARAM_NAMING_DESC).defaultValue(this.paramNaming.name())); this.cliOptions.add(new CliOption(NPM_NAME, "The name under which you want to publish generated npm package." + - " Required to generate a full package")); + " 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." + SNAPSHOT_SUFFIX_FORMAT.get().toPattern(), - false)); + "When setting this property to true, the version will be suffixed with -SNAPSHOT." + SNAPSHOT_SUFFIX_FORMAT.get().toPattern(), + false)); this.cliOptions.add(new CliOption(NULL_SAFE_ADDITIONAL_PROPS, NULL_SAFE_ADDITIONAL_PROPS_DESC).defaultValue(String.valueOf(this.getNullSafeAdditionalProps()))); } @@ -239,12 +239,12 @@ public void processOpts() { @Override public String toModelImport(String name){ if(isUnionType(name)){ - LOGGER.warn("The import is a union type. Consider using the toModelImportMap method."); - return toModelImportMap(name).values().stream().collect(Collectors.joining("|")); + LOGGER.warn("The import is a union type. Consider using the toModelImportMap method."); + return toModelImportMap(name).values().stream().collect(Collectors.joining("|")); } if(isIntersectionType(name)){ - LOGGER.warn("The import is a intersection type. Consider using the toModelImportMap method."); - return toModelImportMap(name).values().stream().collect(Collectors.joining("&")); + LOGGER.warn("The import is a intersection type. Consider using the toModelImportMap method."); + return toModelImportMap(name).values().stream().collect(Collectors.joining("&")); } return super.toModelImport(name); } @@ -262,7 +262,7 @@ public Map toModelImportMap(String name){ } private String[] splitComposedType (String name) { - return name.replace(" ","").split("[|&<>]"); + return name.replace(" ","").split("[|&<>]"); } private boolean isUnionType(String name){ @@ -408,14 +408,14 @@ protected String toTypescriptTypeName(final String name, String safePrefix) { if (sanName.matches("^\\d.*")) { String modelName = safePrefix + sanName; // e.g. 200Response => Model200Response LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", sanName, - modelName); + modelName); return modelName; } if (languageSpecificPrimitives.contains(sanName)) { String modelName = safePrefix + sanName; LOGGER.warn("{} (model name matches existing language type) cannot be used as a model name. Renamed to {}", - sanName, modelName); + sanName, modelName); return modelName; } @@ -599,8 +599,8 @@ public void setModelPropertyNaming(String naming) { modelPropertyNaming = MODEL_PROPERTY_NAMING_TYPE.valueOf(naming); } catch (IllegalArgumentException e) { String values = Stream.of(MODEL_PROPERTY_NAMING_TYPE.values()) - .map(value -> "'" + value.name() + "'") - .collect(Collectors.joining(", ")); + .map(value -> "'" + value.name() + "'") + .collect(Collectors.joining(", ")); String msg = String.format(Locale.ROOT, "Invalid model property naming '%s'. Must be one of %s.", naming, values); throw new IllegalArgumentException(msg); @@ -612,8 +612,8 @@ public void setParamNaming(String naming) { paramNaming = PARAM_NAMING_TYPE.valueOf(naming); } catch (IllegalArgumentException e) { String values = Stream.of(PARAM_NAMING_TYPE.values()) - .map(value -> "'" + value.name() + "'") - .collect(Collectors.joining(", ")); + .map(value -> "'" + value.name() + "'") + .collect(Collectors.joining(", ")); String msg = String.format(Locale.ROOT, "Invalid parameter naming '%s'. Must be one of %s.", naming, values); throw new IllegalArgumentException(msg); @@ -640,8 +640,8 @@ private String getNameUsingParamNaming(String name) { return underscore(name); default: throw new IllegalArgumentException("Invalid param naming '" + - name + "'. Must be 'original', 'camelCase', " + - "'PascalCase' or 'snake_case'"); + name + "'. Must be 'original', 'camelCase', " + + "'PascalCase' or 'snake_case'"); } } @@ -658,8 +658,8 @@ private String getNameUsingModelPropertyNaming(String name) { return underscore(name); default: throw new IllegalArgumentException("Invalid model property naming '" + - name + "'. Must be 'original', 'camelCase', " + - "'PascalCase' or 'snake_case'"); + name + "'. Must be 'original', 'camelCase', " + + "'PascalCase' or 'snake_case'"); } } @@ -725,8 +725,8 @@ protected void setEnumPropertyNaming(String naming) { enumPropertyNaming = ENUM_PROPERTY_NAMING_TYPE.valueOf(naming); } catch (IllegalArgumentException e) { String values = Stream.of(ENUM_PROPERTY_NAMING_TYPE.values()) - .map(value -> "'" + value.name() + "'") - .collect(Collectors.joining(", ")); + .map(value -> "'" + value.name() + "'") + .collect(Collectors.joining(", ")); String msg = String.format(Locale.ROOT, "Invalid enum property naming '%s'. Must be one of %s.",naming, values); throw new IllegalArgumentException(msg); @@ -785,7 +785,7 @@ public ModelsMap postProcessModels(ModelsMap objs) { for (CodegenProperty var : cm.allVars) { if (Boolean.TRUE.equals(var.isEnum)) { var.datatypeWithEnum = var.datatypeWithEnum - .replace(var.enumName, cm.classname + classEnumSeparator + var.enumName); + .replace(var.enumName, cm.classname + classEnumSeparator + var.enumName); } } } @@ -859,7 +859,7 @@ private void setDiscriminatorValue(CodegenModel model, String baseName, String v private String getDiscriminatorValue(CodegenModel model) { return model.vendorExtensions.containsKey(X_DISCRIMINATOR_TYPE) ? - (String) model.vendorExtensions.get(X_DISCRIMINATOR_TYPE) : model.classname; + (String) model.vendorExtensions.get(X_DISCRIMINATOR_TYPE) : model.classname; } @Override @@ -925,7 +925,7 @@ public String toAnyOfName(List names, ComposedSchema composedSchema) { public String toOneOfName(List names, ComposedSchema composedSchema) { List types = getTypesFromSchemas(composedSchema.getOneOf()); // Null is already handled by the isNullable flag - types.removeIf(p -> p.equals("null")); + types.removeIf(p -> p.equals("null")); // Equisoft patch return String.join(" | ", types); } diff --git a/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java b/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java index b20468ed..c9807fd6 100644 --- a/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java +++ b/gradle/openapi-sdk/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java @@ -124,35 +124,34 @@ public TypeScriptClientCodegen() { // NOTE: TypeScript uses camel cased reserved words, while models are title cased. We don't want lowercase comparisons. reservedWords.addAll(Arrays.asList( - // local variable names used in API methods (endpoints) - "varLocalPath", "queryParameters", "headerParams", "formParams", "useFormData", "varLocalDeferred", - "requestOptions", "from", - // Typescript reserved words - "abstract", "await", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "debugger", "default", "delete", "do", "double", "else", "enum", "export", "extends", "false", "final", "finally", "float", "for", "function", "goto", "if", "implements", "import", "in", "instanceof", "int", "interface", "let", "long", "native", "new", "null", "package", "private", "protected", "public", "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "transient", "true", "try", "typeof", "var", "void", "volatile", "while", "with", "yield")); + // local variable names used in API methods (endpoints) + "varLocalPath", "queryParameters", "headerParams", "formParams", "useFormData", "varLocalDeferred", + "requestOptions", "from", + // Typescript reserved words + "abstract", "await", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "debugger", "default", "delete", "do", "double", "else", "enum", "export", "extends", "false", "final", "finally", "float", "for", "function", "goto", "if", "implements", "import", "in", "instanceof", "int", "interface", "let", "long", "native", "new", "null", "package", "private", "protected", "public", "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "transient", "true", "try", "typeof", "var", "void", "volatile", "while", "with", "yield")); languageSpecificPrimitives = new HashSet<>(Arrays.asList( - "string", - "String", - "boolean", - "Boolean", - "Double", - "Integer", - "Long", - "Float", - "Object", - "Array", - "Date", - "number", - "any", - "File", - "Error", - "Map", - "Set", - "null" + "string", + "String", + "boolean", + "Boolean", + "Double", + "Integer", + "Long", + "Float", + "Object", + "Array", + "Date", + "number", + "any", + "File", + "Error", + "Map", + "Set" )); languageGenericTypes = new HashSet<>(Arrays.asList( - "Array" + "Array" )); instantiationTypes.put("array", "Array"); @@ -185,16 +184,15 @@ public TypeScriptClientCodegen() { typeMapping.put("UUID", "string"); typeMapping.put("Error", "Error"); typeMapping.put("AnyType", "any"); - typeMapping.put("null", "null"); cliOptions.add(new CliOption(NPM_NAME, "The name under which you want to publish generated npm package." + - " Required to generate a full package")); + " Required to generate a full package")); 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())); cliOptions.add(new CliOption(NPM_REPOSITORY, "Use this property to set an url your private npmRepo in the package.json")); cliOptions.add(CliOption.newBoolean(SNAPSHOT, - "When setting this property to true, the version will be suffixed with -SNAPSHOT." + SNAPSHOT_SUFFIX_FORMAT.get().toPattern(), - false)); + "When setting this property to true, the version will be suffixed with -SNAPSHOT." + SNAPSHOT_SUFFIX_FORMAT.get().toPattern(), + false)); cliOptions.add(new CliOption(CodegenConstants.MODEL_PROPERTY_NAMING, CodegenConstants.MODEL_PROPERTY_NAMING_DESC).defaultValue("camelCase")); cliOptions.add(new CliOption(CodegenConstants.SUPPORTS_ES6, CodegenConstants.SUPPORTS_ES6_DESC).defaultValue("false")); @@ -448,14 +446,14 @@ protected String toTypescriptTypeName(final String name, String safePrefix) { if (sanName.matches("^\\d.*")) { String modelName = safePrefix + sanName; // e.g. 200Response => Model200Response LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", sanName, - modelName); + modelName); return modelName; } if (languageSpecificPrimitives.contains(sanName)) { String modelName = safePrefix + sanName; LOGGER.warn("{} (model name matches existing language type) cannot be used as a model name. Renamed to {}", - sanName, modelName); + sanName, modelName); return modelName; } @@ -544,10 +542,10 @@ public String toDefaultValue(Schema p) { } else if (ModelUtils.isDateTimeSchema(p)) { return UNDEFINED_VALUE; } else if (ModelUtils.isNumberSchema(p)) { - if (p.getDefault() != null) { - return p.getDefault().toString(); - } - return UNDEFINED_VALUE; + if (p.getDefault() != null) { + return p.getDefault().toString(); + } + return UNDEFINED_VALUE; } else if (ModelUtils.isIntegerSchema(p)) { if (p.getDefault() != null) { return p.getDefault().toString(); @@ -603,12 +601,12 @@ public String toOperationId(String operationId) { public void setModelPropertyNaming(String naming) { if ("original".equals(naming) || "camelCase".equals(naming) || - "PascalCase".equals(naming) || "snake_case".equals(naming)) { + "PascalCase".equals(naming) || "snake_case".equals(naming)) { this.modelPropertyNaming = naming; } else { throw new IllegalArgumentException("Invalid model property naming '" + - naming + "'. Must be 'original', 'camelCase', " + - "'PascalCase' or 'snake_case'"); + naming + "'. Must be 'original', 'camelCase', " + + "'PascalCase' or 'snake_case'"); } } @@ -628,8 +626,8 @@ public String getNameUsingModelPropertyNaming(String name) { return underscore(name); default: throw new IllegalArgumentException("Invalid model property naming '" + - name + "'. Must be 'original', 'camelCase', " + - "'PascalCase' or 'snake_case'"); + name + "'. Must be 'original', 'camelCase', " + + "'PascalCase' or 'snake_case'"); } } @@ -713,7 +711,7 @@ public ModelsMap postProcessModels(ModelsMap objs) { for (CodegenProperty var : cm.allVars) { if (Boolean.TRUE.equals(var.isEnum)) { var.datatypeWithEnum = var.datatypeWithEnum - .replace(var.enumName, cm.classname + var.enumName); + .replace(var.enumName, cm.classname + var.enumName); } } } @@ -774,7 +772,7 @@ private void setDiscriminatorValue(CodegenModel model, String baseName, String v private String getDiscriminatorValue(CodegenModel model) { return model.vendorExtensions.containsKey(X_DISCRIMINATOR_TYPE) ? - (String) model.vendorExtensions.get(X_DISCRIMINATOR_TYPE) : model.classname; + (String) model.vendorExtensions.get(X_DISCRIMINATOR_TYPE) : model.classname; } @Override @@ -819,8 +817,8 @@ public void processOpts() { String httpLibName = this.getHttpLibForFramework(additionalProperties.get(FRAMEWORK_SWITCH).toString()); supportingFiles.add(new SupportingFile( - "http" + File.separator + httpLibName + ".mustache", - "http", httpLibName + ".ts" + "http" + File.separator + httpLibName + ".mustache", + "http", httpLibName + ".ts" )); Object propPlatform = additionalProperties.get(PLATFORM_SWITCH); @@ -942,7 +940,7 @@ public String typescriptDateTime(Object dateTimeValue) { public String getModelName(Schema sc) { if (sc.get$ref() != null) { - Schema unaliasedSchema = unaliasSchema(sc, importMapping); + Schema unaliasedSchema = unaliasSchema(sc, schemaMapping); if (unaliasedSchema.get$ref() != null) { return toModelName(ModelUtils.getSimpleRef(sc.get$ref())); } @@ -1390,8 +1388,8 @@ protected String setPropertyExampleValue(CodegenProperty p) { example = "789"; } } else if (Boolean.TRUE.equals(p.isDouble) - || Boolean.TRUE.equals(p.isFloat) - || Boolean.TRUE.equals(p.isNumber)) { + || Boolean.TRUE.equals(p.isFloat) + || Boolean.TRUE.equals(p.isNumber)) { if (example == null) { example = "3.4"; } @@ -1530,8 +1528,7 @@ public String toAnyOfName(List names, ComposedSchema composedSchema) { @Override public String toOneOfName(List names, ComposedSchema composedSchema) { List types = getTypesFromSchemas(composedSchema.getOneOf()); - // Null is already handled by the isNullable flag - types.removeIf(p -> p.equals("null")); + return String.join(" | ", types); } @@ -1551,8 +1548,8 @@ public String toAllOfName(List names, ComposedSchema composedSchema) { */ protected List getTypesFromSchemas(List schemas) { List filteredSchemas = schemas.size() > 1 - ? schemas.stream().filter(schema -> !"AnyType".equals(super.getSchemaType(schema))).collect(Collectors.toList()) - : schemas; + ? schemas.stream().filter(schema -> !"AnyType".equals(super.getSchemaType(schema))).collect(Collectors.toList()) + : schemas; return filteredSchemas.stream().map(schema -> { String schemaType = getSchemaType(schema); @@ -1571,7 +1568,7 @@ protected void addImport(CodegenModel m, String type) { return; } - String[] parts = type.split("( [|&] )|[<>]"); + String[] parts = splitComposedType(type); for (String s : parts) { if (needToImport(s)) { m.imports.add(s); @@ -1579,6 +1576,29 @@ protected void addImport(CodegenModel m, String type) { } } + @Override + protected void addImport(Set importsToBeAddedTo, String type) { + if (type == null) { + return; + } + + String[] parts = splitComposedType(type); + for (String s : parts) { + super.addImport(importsToBeAddedTo, s); + } + } + + /** + * Split composed types + * e.g. TheFirstType | TheSecondType to TheFirstType and TheSecondType + * + * @param type String with composed types + * @return list of types + */ + protected String[] splitComposedType(String type) { + return type.replace(" ","").split("[|&<>]"); + } + @Override public GeneratorLanguage generatorLanguage() { return GeneratorLanguage.TYPESCRIPT; } } diff --git a/gradle/openapi-sdk/src/main/kotlin/com/equisoft/standards/gradle/openapisdk/generators/MicronautSdkGenerator.kt b/gradle/openapi-sdk/src/main/kotlin/com/equisoft/standards/gradle/openapisdk/generators/MicronautSdkGenerator.kt index a934419e..d95cd6e4 100644 --- a/gradle/openapi-sdk/src/main/kotlin/com/equisoft/standards/gradle/openapisdk/generators/MicronautSdkGenerator.kt +++ b/gradle/openapi-sdk/src/main/kotlin/com/equisoft/standards/gradle/openapisdk/generators/MicronautSdkGenerator.kt @@ -1,6 +1,12 @@ package com.equisoft.standards.gradle.openapisdk.generators import com.equisoft.standards.gradle.openapisdk.OpenApiSdkExtension +import org.openapitools.codegen.languages.AbstractJavaCodegen.OPENAPI_NULLABLE +import org.openapitools.codegen.languages.AbstractJavaCodegen.SUPPORT_ASYNC +import org.openapitools.codegen.languages.MicronautCodegen.CLIENT_ID +import org.openapitools.codegen.languages.MicronautCodegen.INTROSPECTED +import org.openapitools.codegen.languages.features.OptionalFeatures.USE_OPTIONAL +import org.openapitools.codegen.languages.features.UseGenericResponseFeatures.USE_GENERIC_RESPONSE import org.openapitools.generator.gradle.plugin.tasks.GenerateTask class MicronautSdkGenerator( @@ -10,21 +16,32 @@ class MicronautSdkGenerator( generatorName = "micronaut", openApiSdk ) { + @Suppress("LongMethod") override fun assembleSdk(task: GenerateTask): Unit = with(task) { super.assembleSdk(this) - id.set(openApiSdk.projectKey.map { "$it-sdk-micronaut" }) + val gitProject = openApiSdk.projectKey.map { "$it-sdk-micronaut" } + id.set(gitProject) configOptions.putAll( project.provider { mapOf( - "clientId" to openApiSdk.projectKey.get(), - "introspected" to "true", - "jacksonDatabindNullable" to "false", - "supportAsync" to "false", - "useGenericResponse" to "true", - "useOptional" to "false", - "useReferencedSchemaAsDefault" to "false" + CLIENT_ID to openApiSdk.projectKey.get(), + INTROSPECTED to "true", + OPENAPI_NULLABLE to "false", + SUPPORT_ASYNC to "false", + USE_GENERIC_RESPONSE to "true", + USE_OPTIONAL to "false", + ) + } + ) + + additionalProperties.putAll( + project.provider { + val gitProjectNameValue = gitProject.get() + mapOf( + "gitProjectName" to gitProjectNameValue, + "packageUrl" to "https://maven.pkg.github.com/${openApiSdk.git.userId.get()}/$gitProjectNameValue" ) } ) diff --git a/gradle/openapi-sdk/src/patches/kotlin-client/build.gradle.mustache.patch b/gradle/openapi-sdk/src/patches/kotlin-client/build.gradle.mustache.patch index f403448f..80379626 100644 --- a/gradle/openapi-sdk/src/patches/kotlin-client/build.gradle.mustache.patch +++ b/gradle/openapi-sdk/src/patches/kotlin-client/build.gradle.mustache.patch @@ -9,9 +9,13 @@ distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip" } -@@ -46,6 +46,12 @@ - apply plugin: 'kotlinx-serialization' - {{/kotlinx_serialization}} +@@ -11,2 +11,2 @@ +- ext.kotlin_version = '1.5.10' ++ ext.kotlin_version = '1.7.10' + {{#jvm-ktor}} +@@ -51,6 +51,12 @@ + apply plugin: 'idea' + {{/idea}} +files( + "local.build.gradle", diff --git a/gradle/openapi-sdk/src/patches/kotlin-client/libraries/jvm-okhttp/api.mustache.patch b/gradle/openapi-sdk/src/patches/kotlin-client/libraries/jvm-okhttp/api.mustache.patch index 98f9b5d5..d096e5ee 100644 --- a/gradle/openapi-sdk/src/patches/kotlin-client/libraries/jvm-okhttp/api.mustache.patch +++ b/gradle/openapi-sdk/src/patches/kotlin-client/libraries/jvm-okhttp/api.mustache.patch @@ -4,7 +4,7 @@ import {{packageName}}.infrastructure.toMultiValue {{#operations}} --{{#nonPublicApi}}internal {{/nonPublicApi}}class {{classname}}(basePath: kotlin.String = defaultBasePath, client: OkHttpClient = ApiClient.defaultClient) : ApiClient(basePath) { +-{{#nonPublicApi}}internal {{/nonPublicApi}}class {{classname}}(basePath: kotlin.String = defaultBasePath, client: OkHttpClient = ApiClient.defaultClient) : ApiClient(basePath, client) { +{{#nonPublicApi}}internal {{/nonPublicApi}}class {{classname}}( + basePath: kotlin.String = defaultBasePath, + accessToken: String? = null,{{! Equisoft: Add optional accessToken}}