[REQ] resolve #17544 Add x-field-extra-annotation and x-class-extra-annotation for kotlin generator (#19899)

Co-authored-by: Aliaksandr SEMIANKEVICH <aliaksandrsemian.epam@navan.tech>
This commit is contained in:
Aliaksandr SEMIANKEVICH 2024-10-22 12:05:38 +02:00 committed by GitHub
parent 071540f55a
commit e9c3c63732
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 84 additions and 38 deletions

View File

@ -51,6 +51,14 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|useSettingsGradle|Whether the project uses settings.gradle.| |false|
|useSpringBoot3|Whether to use the Spring Boot 3 with the jvm-spring-webclient library.| |false|
## SUPPORTED VENDOR EXTENSIONS
| Extension name | Description | Applicable for | Default value |
| -------------- | ----------- | -------------- | ------------- |
|x-class-extra-annotation|List of custom annotations to be added to model|MODEL|null
|x-field-extra-annotation|List of custom annotations to be added to property|FIELD, OPERATION_PARAMETER|null
## IMPORT MAPPING
| Type/Alias | Imports |

View File

@ -38,6 +38,7 @@ import org.openapitools.codegen.CodegenParameter;
import org.openapitools.codegen.CodegenProperty;
import org.openapitools.codegen.CodegenType;
import org.openapitools.codegen.SupportingFile;
import org.openapitools.codegen.VendorExtension;
import org.openapitools.codegen.meta.features.ClientModificationFeature;
import org.openapitools.codegen.meta.features.DocumentationFeature;
import org.openapitools.codegen.meta.features.GlobalFeature;
@ -1049,4 +1050,12 @@ public class KotlinClientCodegen extends AbstractKotlinCodegen {
System.out.println("# Please support his work directly via https://patreon.com/jimschubert \uD83D\uDE4F #");
System.out.println("################################################################################");
}
@Override
public List<VendorExtension> getSupportedVendorExtensions() {
var extensions = super.getSupportedVendorExtensions();
extensions.add(VendorExtension.X_CLASS_EXTRA_ANNOTATION);
extensions.add(VendorExtension.X_FIELD_EXTRA_ANNOTATION);
return extensions;
}
}

View File

@ -74,6 +74,9 @@ import {{packageName}}.infrastructure.ITransformForStorage
@Deprecated(message = "This schema is deprecated.")
{{/isDeprecated}}
{{>additionalModelTypeAnnotations}}
{{#vendorExtensions.x-class-extra-annotation}}
{{{vendorExtensions.x-class-extra-annotation}}}
{{/vendorExtensions.x-class-extra-annotation}}
{{#nonPublicApi}}internal {{/nonPublicApi}}{{#discriminator}}interface{{/discriminator}}{{^discriminator}}{{#hasVars}}data {{/hasVars}}class{{/discriminator}} {{classname}}{{^discriminator}} (
{{#allVars}}
@ -194,7 +197,7 @@ import {{packageName}}.infrastructure.ITransformForStorage
companion object {
var openapiFields = HashSet<String>()
var openapiRequiredFields = HashSet<String>()
init {
{{#allVars}}
{{#-first}}
@ -210,7 +213,7 @@ import {{packageName}}.infrastructure.ITransformForStorage
openapiRequiredFields.add("{{baseName}}")
{{/requiredVars}}
}
/**
* Validates the JSON Element and throws an exception if issues found
*
@ -227,7 +230,7 @@ import {{packageName}}.infrastructure.ITransformForStorage
{{^hasChildren}}
{{#requiredVars}}
{{#-first}}
// check to make sure all required properties/fields are present in the JSON string
for (requiredField in openapiRequiredFields) {
requireNotNull(jsonElement!!.getAsJsonObject()[requiredField]) {
@ -249,7 +252,7 @@ import {{packageName}}.infrastructure.ITransformForStorage
if (!jsonObj.get("{{{baseName}}}").isJsonArray) {
throw IllegalArgumentException(String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()))
}
// validate the required field `{{{baseName}}}` (array)
for (i in 0 until jsonObj.getAsJsonArray("{{{baseName}}}").size()) {
{{{items.dataType}}}.validateJsonElement(jsonObj.getAsJsonArray("{{{baseName}}}").get(i))
@ -262,7 +265,7 @@ import {{packageName}}.infrastructure.ITransformForStorage
require(jsonObj["{{{baseName}}}"].isJsonArray) {
String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString())
}
// validate the optional field `{{{baseName}}}` (array)
for (i in 0 until jsonObj.getAsJsonArray("{{{baseName}}}").size()) {
{{{items.dataType}}}.validateJsonElement(jsonObj.getAsJsonArray("{{{baseName}}}").get(i))

View File

@ -15,6 +15,9 @@
{{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}")
{{/kotlinx_serialization}}
{{/multiplatform}}
{{#vendorExtensions.x-field-extra-annotation}}
{{{vendorExtensions.x-field-extra-annotation}}}
{{/vendorExtensions.x-field-extra-annotation}}
{{#deprecated}}
@Deprecated(message = "This property is deprecated.")
{{/deprecated}}

View File

@ -15,6 +15,9 @@
{{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}")
{{/kotlinx_serialization}}
{{/multiplatform}}
{{#vendorExtensions.x-field-extra-annotation}}
{{{vendorExtensions.x-field-extra-annotation}}}
{{/vendorExtensions.x-field-extra-annotation}}
{{#deprecated}}
@Deprecated(message = "This property is deprecated.")
{{/deprecated}}

View File

@ -50,7 +50,7 @@ import java.util.Map;
@SuppressWarnings("static-method")
public class KotlinClientCodegenModelTest {
private Schema getArrayTestSchema() {
private Schema<?> getArrayTestSchema() {
return new ObjectSchema()
.description("a sample model")
.addProperties("id", new IntegerSchema().format("int64"))
@ -58,7 +58,7 @@ public class KotlinClientCodegenModelTest {
.addRequiredItem("id");
}
private Schema getSimpleSchema() {
private Schema<?> getSimpleSchema() {
return new ObjectSchema()
.description("a sample model")
.addProperties("id", new IntegerSchema().format("int64"))
@ -68,14 +68,14 @@ public class KotlinClientCodegenModelTest {
.addRequiredItem("name");
}
private Schema getMapSchema() {
private Schema<?> getMapSchema() {
return new ObjectSchema()
.description("a sample model")
.addProperties("mapping", new MapSchema()
.additionalProperties(new StringSchema()));
}
private Schema getComplexSchema() {
private Schema<?> getComplexSchema() {
return new ObjectSchema()
.description("a sample model")
.addProperties("child", new ObjectSchema().$ref("#/components/schemas/Child"));
@ -83,7 +83,7 @@ public class KotlinClientCodegenModelTest {
@Test(description = "convert a simple model")
public void simpleModelTest() {
final Schema schema = getSimpleSchema();
final Schema<?> schema = getSimpleSchema();
final DefaultCodegen codegen = new KotlinClientCodegen();
codegen.processOpts();
@ -128,7 +128,7 @@ public class KotlinClientCodegenModelTest {
@Test(description = "convert a simple model: threetenbp")
public void selectDateLibraryAsThreetenbp() {
final Schema schema = getSimpleSchema();
final Schema<?> schema = getSimpleSchema();
final KotlinClientCodegen codegen = new KotlinClientCodegen();
codegen.setDateLibrary(KotlinClientCodegen.DateLibrary.THREETENBP.value);
codegen.processOpts();
@ -149,7 +149,7 @@ public class KotlinClientCodegenModelTest {
@Test(description = "convert a simple model: threetenbp-localdatetime")
public void selectDateLibraryAsThreetenbpLocalDateTime() {
final Schema schema = getSimpleSchema();
final Schema<?> schema = getSimpleSchema();
final KotlinClientCodegen codegen = new KotlinClientCodegen();
String value = KotlinClientCodegen.DateLibrary.THREETENBP_LOCALDATETIME.value;
Assert.assertEquals(value, "threetenbp-localdatetime");
@ -172,7 +172,7 @@ public class KotlinClientCodegenModelTest {
@Test(description = "convert a simple model: date string")
public void selectDateLibraryAsString() {
final Schema schema = getSimpleSchema();
final Schema<?> schema = getSimpleSchema();
final KotlinClientCodegen codegen = new KotlinClientCodegen();
codegen.setDateLibrary(KotlinClientCodegen.DateLibrary.STRING.value);
codegen.processOpts();
@ -193,7 +193,7 @@ public class KotlinClientCodegenModelTest {
@Test(description = "convert a simple model: date java8")
public void selectDateLibraryAsJava8() {
final Schema schema = getSimpleSchema();
final Schema<?> schema = getSimpleSchema();
final KotlinClientCodegen codegen = new KotlinClientCodegen();
codegen.setDateLibrary(KotlinClientCodegen.DateLibrary.JAVA8.value);
codegen.processOpts();
@ -214,7 +214,7 @@ public class KotlinClientCodegenModelTest {
@Test(description = "convert a simple model: date java8-localdatetime")
public void selectDateLibraryAsJava8LocalDateTime() {
final Schema schema = getSimpleSchema();
final Schema<?> schema = getSimpleSchema();
final KotlinClientCodegen codegen = new KotlinClientCodegen();
String value = KotlinClientCodegen.DateLibrary.JAVA8_LOCALDATETIME.value;
Assert.assertEquals(value, "java8-localdatetime");
@ -237,7 +237,7 @@ public class KotlinClientCodegenModelTest {
@Test(description = "convert a model with array property to default kotlin.Array")
public void arrayPropertyTest() {
final Schema model = getArrayTestSchema();
final Schema<?> model = getArrayTestSchema();
final DefaultCodegen codegen = new KotlinClientCodegen();
OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("sample", model);
@ -264,7 +264,7 @@ public class KotlinClientCodegenModelTest {
@Test(description = "convert a model with array property to a kotlin.collections.List")
public void listPropertyTest() {
final Schema model = getArrayTestSchema();
final Schema<?> model = getArrayTestSchema();
final KotlinClientCodegen codegen = new KotlinClientCodegen();
codegen.setCollectionType(KotlinClientCodegen.CollectionType.LIST.value);
@ -293,7 +293,7 @@ public class KotlinClientCodegenModelTest {
@Test(description = "convert a model with a map property")
public void mapPropertyTest() {
final Schema schema = getMapSchema();
final Schema<?> schema = getMapSchema();
final DefaultCodegen codegen = new KotlinClientCodegen();
OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("sample", schema);
codegen.setOpenAPI(openAPI);
@ -317,7 +317,7 @@ public class KotlinClientCodegenModelTest {
@Test(description = "convert a model with complex property")
public void complexPropertyTest() {
final Schema schema = getComplexSchema();
final Schema<?> schema = getComplexSchema();
final DefaultCodegen codegen = new KotlinClientCodegen();
OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("sample", schema);
codegen.setOpenAPI(openAPI);
@ -351,7 +351,7 @@ public class KotlinClientCodegenModelTest {
@Test(dataProvider = "modelNames", description = "sanitize model names")
public void sanitizeModelNames(final String name, final ModelNameTest testCase) {
final Schema schema = getComplexSchema();
final Schema<?> schema = getComplexSchema();
final DefaultCodegen codegen = new KotlinClientCodegen();
OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema(name, schema);
codegen.setOpenAPI(openAPI);
@ -448,4 +448,3 @@ public class KotlinClientCodegenModelTest {
}
}
}

View File

@ -17,9 +17,14 @@ import org.testng.annotations.Test;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assumptions.assumeThat;
import static org.openapitools.codegen.TestUtils.assertFileContains;
import static org.openapitools.codegen.VendorExtension.X_CLASS_EXTRA_ANNOTATION;
import static org.openapitools.codegen.VendorExtension.X_FIELD_EXTRA_ANNOTATION;
public class KotlinModelCodegenTest {
@ -110,4 +115,20 @@ public class KotlinModelCodegenTest {
assertFileContains(Paths.get(outputPath + "/src/main/kotlin/models/UniqueArray.kt"),
"var array: kotlin.collections.MutableSet<kotlin.String>");
}
@Test(dataProvider = "generators")
public void xFieldExtraAnnotation(AbstractKotlinCodegen codegen) throws IOException {
assumeThat(codegen.getSupportedVendorExtensions().contains(X_FIELD_EXTRA_ANNOTATION)).isTrue();
String outputPath = generateModels(codegen, "src/test/resources/3_0/issue_11772.yml", true);
Path ktClassPath = Paths.get(outputPath + "/src/main/kotlin/models/Employee.kt");
assertThat(ktClassPath).content().contains("@javax.persistence.Id");
}
@Test(dataProvider = "generators")
public void xClassExtraAnnotation(AbstractKotlinCodegen codegen) throws IOException {
assumeThat(codegen.getSupportedVendorExtensions().contains(X_CLASS_EXTRA_ANNOTATION)).isTrue();
String outputPath = generateModels(codegen, "src/test/resources/3_0/issue_11772.yml", true);
Path ktClassPath = Paths.get(outputPath + "/src/main/kotlin/models/Employee.kt");
assertThat(ktClassPath).content().contains("@javax.persistence.MappedSuperclass");
}
}

View File

@ -71,13 +71,13 @@ data class ApiAnnotation (
companion object {
var openapiFields = HashSet<String>()
var openapiRequiredFields = HashSet<String>()
init {
// a set of all properties/fields (JSON key names)
openapiFields.add("id")
}
/**
* Validates the JSON Element and throws an exception if issues found
*

View File

@ -79,7 +79,7 @@ data class ApiApiResponse (
companion object {
var openapiFields = HashSet<String>()
var openapiRequiredFields = HashSet<String>()
init {
// a set of all properties/fields (JSON key names)
openapiFields.add("code")
@ -87,7 +87,7 @@ data class ApiApiResponse (
openapiFields.add("message")
}
/**
* Validates the JSON Element and throws an exception if issues found
*

View File

@ -75,14 +75,14 @@ data class ApiCategory (
companion object {
var openapiFields = HashSet<String>()
var openapiRequiredFields = HashSet<String>()
init {
// a set of all properties/fields (JSON key names)
openapiFields.add("id")
openapiFields.add("name")
}
/**
* Validates the JSON Element and throws an exception if issues found
*

View File

@ -102,7 +102,7 @@ data class ApiOrder (
companion object {
var openapiFields = HashSet<String>()
var openapiRequiredFields = HashSet<String>()
init {
// a set of all properties/fields (JSON key names)
openapiFields.add("id")
@ -113,7 +113,7 @@ data class ApiOrder (
openapiFields.add("complete")
}
/**
* Validates the JSON Element and throws an exception if issues found
*

View File

@ -105,7 +105,7 @@ data class ApiPet (
companion object {
var openapiFields = HashSet<String>()
var openapiRequiredFields = HashSet<String>()
init {
// a set of all properties/fields (JSON key names)
openapiFields.add("name")
@ -119,7 +119,7 @@ data class ApiPet (
openapiRequiredFields.add("name")
openapiRequiredFields.add("photoUrls")
}
/**
* Validates the JSON Element and throws an exception if issues found
*
@ -133,7 +133,7 @@ data class ApiPet (
String.format("The required field(s) %s in ApiPet is not found in the empty JSON string", ApiPet.openapiRequiredFields.toString())
}
}
// check to make sure all required properties/fields are present in the JSON string
for (requiredField in openapiRequiredFields) {
requireNotNull(jsonElement!!.getAsJsonObject()[requiredField]) {
@ -161,7 +161,7 @@ data class ApiPet (
require(jsonObj["tags"].isJsonArray) {
String.format("Expected the field `tags` to be an array in the JSON string but got `%s`", jsonObj["tags"].toString())
}
// validate the optional field `tags` (array)
for (i in 0 until jsonObj.getAsJsonArray("tags").size()) {
ApiTag.validateJsonElement(jsonObj.getAsJsonArray("tags").get(i))

View File

@ -75,14 +75,14 @@ data class ApiTag (
companion object {
var openapiFields = HashSet<String>()
var openapiRequiredFields = HashSet<String>()
init {
// a set of all properties/fields (JSON key names)
openapiFields.add("id")
openapiFields.add("name")
}
/**
* Validates the JSON Element and throws an exception if issues found
*

View File

@ -100,7 +100,7 @@ data class ApiUser (
companion object {
var openapiFields = HashSet<String>()
var openapiRequiredFields = HashSet<String>()
init {
// a set of all properties/fields (JSON key names)
openapiFields.add("username")
@ -115,7 +115,7 @@ data class ApiUser (
// a set of required properties/fields (JSON key names)
openapiRequiredFields.add("username")
}
/**
* Validates the JSON Element and throws an exception if issues found
*
@ -129,7 +129,7 @@ data class ApiUser (
String.format("The required field(s) %s in ApiUser is not found in the empty JSON string", ApiUser.openapiRequiredFields.toString())
}
}
// check to make sure all required properties/fields are present in the JSON string
for (requiredField in openapiRequiredFields) {
requireNotNull(jsonElement!!.getAsJsonObject()[requiredField]) {