Skip to content

Commit

Permalink
[kotlin] Enum should match spec (#18062)
Browse files Browse the repository at this point in the history
* [kotlin] Enum should match spec

Adjust the enum generation to match what is in the spec, rather than
camel-casing it.
Add tests for capitalization scenarios, including for kotlin keywords

* [kotlin] export docs generators for enum change

* [kotlin] export docs generators for enum change

* fix conflicts

---------

Co-authored-by: Tyler B. Thrailkill <[email protected]>
Co-authored-by: Jari Nystedt <[email protected]>
  • Loading branch information
3 people committed Mar 9, 2024
1 parent 81e33ed commit 494fc7d
Show file tree
Hide file tree
Showing 40 changed files with 336 additions and 117 deletions.
2 changes: 1 addition & 1 deletion docs/generators/kotlin-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|apiSuffix|suffix for api classes| |Api|
|artifactId|Generated artifact id (name of jar).| |kotlin-server|
|artifactVersion|Generated artifact's package version.| |1.0.0|
|enumPropertyNaming|Naming convention for enum properties: 'camelCase', 'PascalCase', 'snake_case', 'UPPERCASE', and 'original'| |camelCase|
|enumPropertyNaming|Naming convention for enum properties: 'camelCase', 'PascalCase', 'snake_case', 'UPPERCASE', and 'original'| |original|
|featureAutoHead|Automatically provide responses to HEAD requests for existing routes that have the GET verb defined.| |true|
|featureCORS|Ktor by default provides an interceptor for implementing proper support for Cross-Origin Resource Sharing (CORS). See enable-cors.org.| |false|
|featureCompression|Adds ability to compress outgoing content using gzip, deflate or custom encoder and thus reduce size of the response.| |true|
Expand Down
2 changes: 1 addition & 1 deletion docs/generators/kotlin-spring.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|configPackage|configuration package for generated code| |org.openapitools.configuration|
|delegatePattern|Whether to generate the server files using the delegate pattern| |false|
|documentationProvider|Select the OpenAPI documentation provider.|<dl><dt>**none**</dt><dd>Do not publish an OpenAPI specification.</dd><dt>**source**</dt><dd>Publish the original input OpenAPI specification.</dd><dt>**springfox**</dt><dd>Generate an OpenAPI 2 (fka Swagger RESTful API Documentation Specification) specification using SpringFox 2.x. Deprecated (for removal); use springdoc instead.</dd><dt>**springdoc**</dt><dd>Generate an OpenAPI 3 specification using SpringDoc.</dd></dl>|springdoc|
|enumPropertyNaming|Naming convention for enum properties: 'camelCase', 'PascalCase', 'snake_case', 'UPPERCASE', and 'original'| |camelCase|
|enumPropertyNaming|Naming convention for enum properties: 'camelCase', 'PascalCase', 'snake_case', 'UPPERCASE', and 'original'| |original|
|exceptionHandler|generate default global exception handlers (not compatible with reactive. enabling reactive will disable exceptionHandler )| |true|
|gradleBuildFile|generate a gradle build file using the Kotlin DSL| |true|
|groupId|Generated artifact package's organization (i.e. maven groupId).| |org.openapitools|
Expand Down
2 changes: 1 addition & 1 deletion docs/generators/kotlin-vertx.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|apiSuffix|suffix for api classes| |Api|
|artifactId|Generated artifact id (name of jar).| |null|
|artifactVersion|Generated artifact's package version.| |1.0.0|
|enumPropertyNaming|Naming convention for enum properties: 'camelCase', 'PascalCase', 'snake_case', 'UPPERCASE', and 'original'| |camelCase|
|enumPropertyNaming|Naming convention for enum properties: 'camelCase', 'PascalCase', 'snake_case', 'UPPERCASE', and 'original'| |original|
|groupId|Generated artifact package's organization (i.e. maven groupId).| |org.openapitools|
|modelMutable|Create mutable models| |false|
|packageName|Generated artifact package name.| |org.openapitools|
Expand Down
2 changes: 1 addition & 1 deletion docs/generators/kotlin.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|artifactVersion|Generated artifact's package version.| |1.0.0|
|collectionType|Option. Collection type to use|<dl><dt>**array**</dt><dd>kotlin.Array</dd><dt>**list**</dt><dd>kotlin.collections.List</dd></dl>|list|
|dateLibrary|Option. Date library to use|<dl><dt>**threetenbp-localdatetime**</dt><dd>Threetenbp - Backport of JSR310 (jvm only, for legacy app only)</dd><dt>**kotlinx-datetime**</dt><dd>kotlinx-datetime (preferred for multiplatform)</dd><dt>**string**</dt><dd>String</dd><dt>**java8-localdatetime**</dt><dd>Java 8 native JSR310 (jvm only, for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (jvm only, preferred for jdk 1.8+)</dd><dt>**threetenbp**</dt><dd>Threetenbp - Backport of JSR310 (jvm only, preferred for jdk &lt; 1.8)</dd></dl>|java8|
|enumPropertyNaming|Naming convention for enum properties: 'camelCase', 'PascalCase', 'snake_case', 'UPPERCASE', and 'original'| |camelCase|
|enumPropertyNaming|Naming convention for enum properties: 'camelCase', 'PascalCase', 'snake_case', 'UPPERCASE', and 'original'| |original|
|generateRoomModels|Generate Android Room database models in addition to API models (JVM Volley library only)| |false|
|groupId|Generated artifact package's organization (i.e. maven groupId).| |org.openapitools|
|idea|Add IntellJ Idea plugin and mark Kotlin main and test folders as source folders.| |false|
Expand Down
2 changes: 1 addition & 1 deletion docs/generators/ktorm-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|artifactId|Generated artifact id (name of jar).| |ktorm|
|artifactVersion|Generated artifact's package version.| |1.0.0|
|defaultDatabaseName|Default database name for all queries| |sqlite.db|
|enumPropertyNaming|Naming convention for enum properties: 'camelCase', 'PascalCase', 'snake_case', 'UPPERCASE', and 'original'| |camelCase|
|enumPropertyNaming|Naming convention for enum properties: 'camelCase', 'PascalCase', 'snake_case', 'UPPERCASE', and 'original'| |original|
|groupId|Generated artifact package's organization (i.e. maven groupId).| |org.openapitools|
|identifierNamingConvention|Naming convention of Ktorm identifiers(table names and column names). This is not related to database name which is defined by defaultDatabaseName option|<dl><dt>**original**</dt><dd>Do not transform original names</dd><dt>**snake_case**</dt><dd>Use snake_case names</dd></dl>|original|
|importModelPackageName|Package name of the imported models| |org.openapitools.database.models|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public abstract class AbstractKotlinCodegen extends DefaultCodegen implements Co

protected boolean nonPublicApi = false;

protected CodegenConstants.ENUM_PROPERTY_NAMING_TYPE enumPropertyNaming = CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.camelCase;
protected CodegenConstants.ENUM_PROPERTY_NAMING_TYPE enumPropertyNaming = CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.original;

// model classes cannot use the same property names defined in HashMap
// ref: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-hash-map/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,38 @@

{{unescapedDescription}}


{{^allowableValues}}
[.fields-{{classname}}]
[cols="2,1,2,4,1"]
[cols="2,1,1,2,4,1"]
|===
| Field Name| Required| Type| Description| Format
| Field Name| Required| Nullable | Type| Description | Format

{{#vars}}
| {{baseName}}
| {{#required}}X{{/required}}
| {{dataType}} {{#isContainer}} of <<{{complexType}}>>{{/isContainer}}
| {{#isNullable}}X{{/isNullable}}
| {{#isModel}}<<{{ dataType }}>>{{/isModel}} {{^container}}{{^allowableValues}} {{^isModel}}{{ dataType }}{{/isModel}}{{/allowableValues}}{{#allowableValues}}{{^allowableValues.empty}}<<{{ dataType }}>>{{/allowableValues.empty}}{{/allowableValues}} {{/container}} {{#isContainer}} of <<{{complexType}}>>{{/isContainer}}
| {{description}}
| {{{dataFormat}}} {{#isEnum}}_Enum:_ {{#_enum}}{{this}}, {{/_enum}}{{/isEnum}}
| {{{dataFormat}}} {{#isEnum}}_Enum:_ {{#_enum}}{{this}}, {{/_enum}}{{/isEnum}} {{^isEnum}}{{^container}}{{^allowableValues.empty}} {{#allowableValues.values}}{{this}}, {{/allowableValues.values}} {{/allowableValues.empty}}{{/container}}{{/isEnum}}

{{/vars}}
|===
{{/allowableValues}}

{{#allowableValues}}

[.fields-{{classname}}]
[cols="1"]
|===
| Enum Values

{{#allowableValues.values}}
| {{this}}
{{/allowableValues.values}}

|===
{{/allowableValues}}

{{/model}}
{{/models}}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
import org.testng.annotations.Test;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -278,9 +281,70 @@ public void testEnumPropertyWithDefaultValue() {
// Assert the enum default value is properly generated
CodegenProperty cp1 = cm1.vars.get(0);
Assert.assertEquals(cp1.getEnumName(), "PropertyName");
Assert.assertEquals(cp1.getDefaultValue(), "PropertyName.vALUE");
Assert.assertEquals(cp1.getDefaultValue(), "PropertyName.VALUE");
}

@Test(description = "Issue #3804")
public void testEnumPropertyWithCapitalization() {
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/kotlin/issue3804-enum-enum-capitalization.yaml");
final AbstractKotlinCodegen codegen = new P_AbstractKotlinCodegen();

Schema test1 = openAPI.getComponents().getSchemas().get("ModelWithEnumPropertyHavingDefault");
CodegenModel cm1 = codegen.fromModel("ModelWithEnumPropertyHavingDefault", test1);

// We need to postProcess the model for enums to be processed
codegen.postProcessModels(createCodegenModelWrapper(cm1));

// Assert the enums are generated without changing capitalization
CodegenProperty cp0 = cm1.vars.get(0);
Assert.assertEquals(cp0.getEnumName(), "PropertyName");
Assert.assertEquals(((HashMap)((ArrayList) cp0.getAllowableValues().get("enumVars")).get(0)).get("name"), "VALUE");
CodegenProperty cp1 = cm1.vars.get(1);
Assert.assertEquals(cp1.getEnumName(), "PropertyName2");
Assert.assertEquals(((HashMap)((ArrayList) cp1.getAllowableValues().get("enumVars")).get(0)).get("name"), "Value");
CodegenProperty cp2 = cm1.vars.get(2);
Assert.assertEquals(cp2.getEnumName(), "PropertyName3");
Assert.assertEquals(((HashMap)((ArrayList) cp2.getAllowableValues().get("enumVars")).get(0)).get("name"), "nonkeywordvalue");
}

@Test(description = "Issue #3804")
public void testEnumPropertyDefaultWithCapitalization() {
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/kotlin/issue3804-enum-enum-capitalization.yaml");
final AbstractKotlinCodegen codegen = new P_AbstractKotlinCodegen();

Schema test1 = openAPI.getComponents().getSchemas().get("ModelWithEnumPropertyHavingDefault");
CodegenModel cm1 = codegen.fromModel("ModelWithEnumPropertyHavingDefault", test1);

// We need to postProcess the model for enums to be processed
codegen.postProcessModels(createCodegenModelWrapper(cm1));

// Assert the enum default value is properly generated
CodegenProperty cp0 = cm1.vars.get(0);
Assert.assertEquals(cp0.getDefaultValue(), "PropertyName.VALUE");
CodegenProperty cp1 = cm1.vars.get(1);
Assert.assertEquals(cp1.getDefaultValue(), "PropertyName2.Value");
CodegenProperty cp2 = cm1.vars.get(2);
Assert.assertEquals(cp2.getDefaultValue(), "PropertyName3.nonkeywordvalue");
}

@Test(description = "Issue #3804")
public void testEnumPropertyWithKeyword() {
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/kotlin/issue3804-enum-enum-capitalization.yaml");
final AbstractKotlinCodegen codegen = new P_AbstractKotlinCodegen();

Schema test1 = openAPI.getComponents().getSchemas().get("ModelWithEnumPropertyHavingDefault");
CodegenModel cm1 = codegen.fromModel("ModelWithEnumPropertyHavingDefault", test1);

// We need to postProcess the model for enums to be processed
codegen.postProcessModels(createCodegenModelWrapper(cm1));

// Assert the enum default value is properly generated
CodegenProperty cp3 = cm1.vars.get(3);
Assert.assertEquals(cp3.getEnumName(), "PropertyName4");
Assert.assertEquals(cp3.getDefaultValue(), "PropertyName4.`value`");
}


@Test(description = "Issue #10792")
public void handleInheritanceWithObjectTypeShouldNotBeAMap() {
Schema parent = new ObjectSchema()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1399,9 +1399,21 @@
"items": {
"$ref": "#/components/schemas/TaskWeek"
}
},
"pricingModelType":{
"$ref": "#/components/schemas/PricingModelType"
}
},
"description": "week, holds all work and working assignments."
},
"PricingModelType": {
"type": "string",
"enum": [
"Unsupported",
"Flex",
"Community",
"Freedom"
]
}
},
"securitySchemes": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
openapi: 3.0.0
info:
title: 'Issue 10591 Enum default value'
version: latest
paths:
'/':
get:
operationId: operation
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/ModelWithEnumPropertyHavingDefault'
components:
schemas:
ModelWithEnumPropertyHavingDefault:
required:
- propertyName
properties:
propertyName:
type: string
default: VALUE
enum:
- VALUE
propertyName2:
type: string
default: Value
enum:
- Value
propertyName3:
type: string
default: nonkeywordvalue
enum:
- nonkeywordvalue
propertyName4:
type: string
default: value
enum:
- value
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

* `unclassified` (value: `"unclassified"`)

* `unknownDefaultOpenApi` (value: `"unknown_default_open_api"`)
* `unknown_default_open_api` (value: `"unknown_default_open_api"`)



Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,13 @@ data class DefaultValue (
/**
*
*
* Values: success,failure,unclassified,unknownDefaultOpenApi
* Values: success,failure,unclassified,unknown_default_open_api
*/
enum class ArrayStringEnumDefault(val value: kotlin.String) {
@JsonProperty(value = "success") success("success"),
@JsonProperty(value = "failure") failure("failure"),
@JsonProperty(value = "unclassified") unclassified("unclassified"),
@JsonProperty(value = "unknown_default_open_api") @JsonEnumDefaultValue unknownDefaultOpenApi("unknown_default_open_api");
@JsonProperty(value = "unknown_default_open_api") @JsonEnumDefaultValue unknown_default_open_api("unknown_default_open_api");
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,13 @@ data class Pet (
/**
* pet status in the store
*
* Values: available,pending,sold,unknownDefaultOpenApi
* Values: available,pending,sold,unknown_default_open_api
*/
enum class Status(val value: kotlin.String) {
@JsonProperty(value = "available") available("available"),
@JsonProperty(value = "pending") pending("pending"),
@JsonProperty(value = "sold") sold("sold"),
@JsonProperty(value = "unknown_default_open_api") @JsonEnumDefaultValue unknownDefaultOpenApi("unknown_default_open_api");
@JsonProperty(value = "unknown_default_open_api") @JsonEnumDefaultValue unknown_default_open_api("unknown_default_open_api");
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ data class Query (
/**
*
*
* Values: sUCCESS,fAILURE,sKIPPED,unknownDefaultOpenApi
* Values: SUCCESS,FAILURE,SKIPPED,unknown_default_open_api
*/
enum class Outcomes(val value: kotlin.String) {
@JsonProperty(value = "SUCCESS") sUCCESS("SUCCESS"),
@JsonProperty(value = "FAILURE") fAILURE("FAILURE"),
@JsonProperty(value = "SKIPPED") sKIPPED("SKIPPED"),
@JsonProperty(value = "unknown_default_open_api") @JsonEnumDefaultValue unknownDefaultOpenApi("unknown_default_open_api");
@JsonProperty(value = "SUCCESS") SUCCESS("SUCCESS"),
@JsonProperty(value = "FAILURE") FAILURE("FAILURE"),
@JsonProperty(value = "SKIPPED") SKIPPED("SKIPPED"),
@JsonProperty(value = "unknown_default_open_api") @JsonEnumDefaultValue unknown_default_open_api("unknown_default_open_api");
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import com.fasterxml.jackson.annotation.JsonProperty
/**
*
*
* Values: success,failure,unclassified,unknownDefaultOpenApi
* Values: success,failure,unclassified,unknown_default_open_api
*/

enum class StringEnumRef(val value: kotlin.String) {
Expand All @@ -36,7 +36,7 @@ enum class StringEnumRef(val value: kotlin.String) {
unclassified("unclassified"),

@JsonProperty(value = "unknown_default_open_api")
unknownDefaultOpenApi("unknown_default_open_api");
unknown_default_open_api("unknown_default_open_api");

/**
* Override [toString()] to avoid using the enum variable name as the value, and instead use
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

* `unclassified` (value: `"unclassified"`)

* `unknownDefaultOpenApi` (value: `"unknown_default_open_api"`)
* `unknown_default_open_api` (value: `"unknown_default_open_api"`)



Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,13 @@ data class DefaultValue (
/**
*
*
* Values: success,failure,unclassified,unknownDefaultOpenApi
* Values: success,failure,unclassified,unknown_default_open_api
*/
enum class ArrayStringEnumDefault(val value: kotlin.String) {
@JsonProperty(value = "success") success("success"),
@JsonProperty(value = "failure") failure("failure"),
@JsonProperty(value = "unclassified") unclassified("unclassified"),
@JsonProperty(value = "unknown_default_open_api") @JsonEnumDefaultValue unknownDefaultOpenApi("unknown_default_open_api");
@JsonProperty(value = "unknown_default_open_api") @JsonEnumDefaultValue unknown_default_open_api("unknown_default_open_api");
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,13 @@ data class Pet (
/**
* pet status in the store
*
* Values: available,pending,sold,unknownDefaultOpenApi
* Values: available,pending,sold,unknown_default_open_api
*/
enum class Status(val value: kotlin.String) {
@JsonProperty(value = "available") available("available"),
@JsonProperty(value = "pending") pending("pending"),
@JsonProperty(value = "sold") sold("sold"),
@JsonProperty(value = "unknown_default_open_api") @JsonEnumDefaultValue unknownDefaultOpenApi("unknown_default_open_api");
@JsonProperty(value = "unknown_default_open_api") @JsonEnumDefaultValue unknown_default_open_api("unknown_default_open_api");
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ data class Query (
/**
*
*
* Values: sUCCESS,fAILURE,sKIPPED,unknownDefaultOpenApi
* Values: SUCCESS,FAILURE,SKIPPED,unknown_default_open_api
*/
enum class Outcomes(val value: kotlin.String) {
@JsonProperty(value = "SUCCESS") sUCCESS("SUCCESS"),
@JsonProperty(value = "FAILURE") fAILURE("FAILURE"),
@JsonProperty(value = "SKIPPED") sKIPPED("SKIPPED"),
@JsonProperty(value = "unknown_default_open_api") @JsonEnumDefaultValue unknownDefaultOpenApi("unknown_default_open_api");
@JsonProperty(value = "SUCCESS") SUCCESS("SUCCESS"),
@JsonProperty(value = "FAILURE") FAILURE("FAILURE"),
@JsonProperty(value = "SKIPPED") SKIPPED("SKIPPED"),
@JsonProperty(value = "unknown_default_open_api") @JsonEnumDefaultValue unknown_default_open_api("unknown_default_open_api");
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import com.fasterxml.jackson.annotation.JsonProperty
/**
*
*
* Values: success,failure,unclassified,unknownDefaultOpenApi
* Values: success,failure,unclassified,unknown_default_open_api
*/

enum class StringEnumRef(val value: kotlin.String) {
Expand All @@ -36,7 +36,7 @@ enum class StringEnumRef(val value: kotlin.String) {
unclassified("unclassified"),

@JsonProperty(value = "unknown_default_open_api")
unknownDefaultOpenApi("unknown_default_open_api");
unknown_default_open_api("unknown_default_open_api");

/**
* Override [toString()] to avoid using the enum variable name as the value, and instead use
Expand Down
Loading

0 comments on commit 494fc7d

Please sign in to comment.