Skip to content

Commit 2f7f1b6

Browse files
[REQ] resolve #17544 Add x-field-extra-annotation and x-class-extra-annotation for kotlin generator
1 parent 38dac13 commit 2f7f1b6

File tree

14 files changed

+85
-39
lines changed

14 files changed

+85
-39
lines changed

docs/generators/kotlin.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ These options may be applied as additional-properties (cli) or configOptions (pl
5151
|useSettingsGradle|Whether the project uses settings.gradle.| |false|
5252
|useSpringBoot3|Whether to use the Spring Boot 3 with the jvm-spring-webclient library.| |false|
5353

54+
## SUPPORTED VENDOR EXTENSIONS
55+
56+
| Extension name | Description | Applicable for | Default value |
57+
| -------------- | ----------- | -------------- | ------------- |
58+
|x-class-extra-annotation|List of custom annotations to be added to model|MODEL|null
59+
|x-field-extra-annotation|List of custom annotations to be added to property|FIELD, OPERATION_PARAMETER|null
60+
61+
5462
## IMPORT MAPPING
5563

5664
| Type/Alias | Imports |

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.openapitools.codegen.CodegenProperty;
3939
import org.openapitools.codegen.CodegenType;
4040
import org.openapitools.codegen.SupportingFile;
41+
import org.openapitools.codegen.VendorExtension;
4142
import org.openapitools.codegen.meta.features.ClientModificationFeature;
4243
import org.openapitools.codegen.meta.features.DocumentationFeature;
4344
import org.openapitools.codegen.meta.features.GlobalFeature;
@@ -1049,4 +1050,12 @@ public void postProcess() {
10491050
System.out.println("# Please support his work directly via https://patreon.com/jimschubert \uD83D\uDE4F #");
10501051
System.out.println("################################################################################");
10511052
}
1053+
1054+
@Override
1055+
public List<VendorExtension> getSupportedVendorExtensions() {
1056+
var extensions = super.getSupportedVendorExtensions();
1057+
extensions.add(VendorExtension.X_CLASS_EXTRA_ANNOTATION);
1058+
extensions.add(VendorExtension.X_FIELD_EXTRA_ANNOTATION);
1059+
return extensions;
1060+
}
10521061
}

modules/openapi-generator/src/main/resources/kotlin-client/data_class.mustache

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ import {{packageName}}.infrastructure.ITransformForStorage
7474
@Deprecated(message = "This schema is deprecated.")
7575
{{/isDeprecated}}
7676
{{>additionalModelTypeAnnotations}}
77+
{{#vendorExtensions.x-class-extra-annotation}}
78+
{{{vendorExtensions.x-class-extra-annotation}}}
79+
{{/vendorExtensions.x-class-extra-annotation}}
7780
{{#nonPublicApi}}internal {{/nonPublicApi}}{{#discriminator}}interface{{/discriminator}}{{^discriminator}}{{#hasVars}}data {{/hasVars}}class{{/discriminator}} {{classname}}{{^discriminator}} (
7881

7982
{{#allVars}}
@@ -194,7 +197,7 @@ import {{packageName}}.infrastructure.ITransformForStorage
194197
companion object {
195198
var openapiFields = HashSet<String>()
196199
var openapiRequiredFields = HashSet<String>()
197-
200+
198201
init {
199202
{{#allVars}}
200203
{{#-first}}
@@ -210,7 +213,7 @@ import {{packageName}}.infrastructure.ITransformForStorage
210213
openapiRequiredFields.add("{{baseName}}")
211214
{{/requiredVars}}
212215
}
213-
216+
214217
/**
215218
* Validates the JSON Element and throws an exception if issues found
216219
*
@@ -227,7 +230,7 @@ import {{packageName}}.infrastructure.ITransformForStorage
227230
{{^hasChildren}}
228231
{{#requiredVars}}
229232
{{#-first}}
230-
233+
231234
// check to make sure all required properties/fields are present in the JSON string
232235
for (requiredField in openapiRequiredFields) {
233236
requireNotNull(jsonElement!!.getAsJsonObject()[requiredField]) {
@@ -249,7 +252,7 @@ import {{packageName}}.infrastructure.ITransformForStorage
249252
if (!jsonObj.get("{{{baseName}}}").isJsonArray) {
250253
throw IllegalArgumentException(String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()))
251254
}
252-
255+
253256
// validate the required field `{{{baseName}}}` (array)
254257
for (i in 0 until jsonObj.getAsJsonArray("{{{baseName}}}").size()) {
255258
{{{items.dataType}}}.validateJsonElement(jsonObj.getAsJsonArray("{{{baseName}}}").get(i))
@@ -262,7 +265,7 @@ import {{packageName}}.infrastructure.ITransformForStorage
262265
require(jsonObj["{{{baseName}}}"].isJsonArray) {
263266
String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString())
264267
}
265-
268+
266269
// validate the optional field `{{{baseName}}}` (array)
267270
for (i in 0 until jsonObj.getAsJsonArray("{{{baseName}}}").size()) {
268271
{{{items.dataType}}}.validateJsonElement(jsonObj.getAsJsonArray("{{{baseName}}}").get(i))

modules/openapi-generator/src/main/resources/kotlin-client/data_class_opt_var.mustache

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
{{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}")
1616
{{/kotlinx_serialization}}
1717
{{/multiplatform}}
18+
{{#vendorExtensions.x-field-extra-annotation}}
19+
{{{vendorExtensions.x-field-extra-annotation}}}
20+
{{/vendorExtensions.x-field-extra-annotation}}
1821
{{#deprecated}}
1922
@Deprecated(message = "This property is deprecated.")
2023
{{/deprecated}}

modules/openapi-generator/src/main/resources/kotlin-client/data_class_req_var.mustache

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
{{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}")
1616
{{/kotlinx_serialization}}
1717
{{/multiplatform}}
18+
{{#vendorExtensions.x-field-extra-annotation}}
19+
{{{vendorExtensions.x-field-extra-annotation}}}
20+
{{/vendorExtensions.x-field-extra-annotation}}
1821
{{#deprecated}}
1922
@Deprecated(message = "This property is deprecated.")
2023
{{/deprecated}}

modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenModelTest.java

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,15 @@
5050
@SuppressWarnings("static-method")
5151
public class KotlinClientCodegenModelTest {
5252

53-
private Schema getArrayTestSchema() {
53+
private Schema<?> getArrayTestSchema() {
5454
return new ObjectSchema()
5555
.description("a sample model")
5656
.addProperties("id", new IntegerSchema().format("int64"))
5757
.addProperties("examples", new ArraySchema().items(new StringSchema()))
5858
.addRequiredItem("id");
5959
}
6060

61-
private Schema getSimpleSchema() {
61+
private Schema<?> getSimpleSchema() {
6262
return new ObjectSchema()
6363
.description("a sample model")
6464
.addProperties("id", new IntegerSchema().format("int64"))
@@ -68,22 +68,22 @@ private Schema getSimpleSchema() {
6868
.addRequiredItem("name");
6969
}
7070

71-
private Schema getMapSchema() {
71+
private Schema<?> getMapSchema() {
7272
return new ObjectSchema()
7373
.description("a sample model")
7474
.addProperties("mapping", new MapSchema()
7575
.additionalProperties(new StringSchema()));
7676
}
7777

78-
private Schema getComplexSchema() {
78+
private Schema<?> getComplexSchema() {
7979
return new ObjectSchema()
8080
.description("a sample model")
8181
.addProperties("child", new ObjectSchema().$ref("#/components/schemas/Child"));
8282
}
8383

8484
@Test(description = "convert a simple model")
8585
public void simpleModelTest() {
86-
final Schema schema = getSimpleSchema();
86+
final Schema<?> schema = getSimpleSchema();
8787
final DefaultCodegen codegen = new KotlinClientCodegen();
8888
codegen.processOpts();
8989

@@ -128,7 +128,7 @@ public void simpleModelTest() {
128128

129129
@Test(description = "convert a simple model: threetenbp")
130130
public void selectDateLibraryAsThreetenbp() {
131-
final Schema schema = getSimpleSchema();
131+
final Schema<?> schema = getSimpleSchema();
132132
final KotlinClientCodegen codegen = new KotlinClientCodegen();
133133
codegen.setDateLibrary(KotlinClientCodegen.DateLibrary.THREETENBP.value);
134134
codegen.processOpts();
@@ -149,7 +149,7 @@ public void selectDateLibraryAsThreetenbp() {
149149

150150
@Test(description = "convert a simple model: threetenbp-localdatetime")
151151
public void selectDateLibraryAsThreetenbpLocalDateTime() {
152-
final Schema schema = getSimpleSchema();
152+
final Schema<?> schema = getSimpleSchema();
153153
final KotlinClientCodegen codegen = new KotlinClientCodegen();
154154
String value = KotlinClientCodegen.DateLibrary.THREETENBP_LOCALDATETIME.value;
155155
Assert.assertEquals(value, "threetenbp-localdatetime");
@@ -172,7 +172,7 @@ public void selectDateLibraryAsThreetenbpLocalDateTime() {
172172

173173
@Test(description = "convert a simple model: date string")
174174
public void selectDateLibraryAsString() {
175-
final Schema schema = getSimpleSchema();
175+
final Schema<?> schema = getSimpleSchema();
176176
final KotlinClientCodegen codegen = new KotlinClientCodegen();
177177
codegen.setDateLibrary(KotlinClientCodegen.DateLibrary.STRING.value);
178178
codegen.processOpts();
@@ -193,7 +193,7 @@ public void selectDateLibraryAsString() {
193193

194194
@Test(description = "convert a simple model: date java8")
195195
public void selectDateLibraryAsJava8() {
196-
final Schema schema = getSimpleSchema();
196+
final Schema<?> schema = getSimpleSchema();
197197
final KotlinClientCodegen codegen = new KotlinClientCodegen();
198198
codegen.setDateLibrary(KotlinClientCodegen.DateLibrary.JAVA8.value);
199199
codegen.processOpts();
@@ -214,7 +214,7 @@ public void selectDateLibraryAsJava8() {
214214

215215
@Test(description = "convert a simple model: date java8-localdatetime")
216216
public void selectDateLibraryAsJava8LocalDateTime() {
217-
final Schema schema = getSimpleSchema();
217+
final Schema<?> schema = getSimpleSchema();
218218
final KotlinClientCodegen codegen = new KotlinClientCodegen();
219219
String value = KotlinClientCodegen.DateLibrary.JAVA8_LOCALDATETIME.value;
220220
Assert.assertEquals(value, "java8-localdatetime");
@@ -237,7 +237,7 @@ public void selectDateLibraryAsJava8LocalDateTime() {
237237

238238
@Test(description = "convert a model with array property to default kotlin.Array")
239239
public void arrayPropertyTest() {
240-
final Schema model = getArrayTestSchema();
240+
final Schema<?> model = getArrayTestSchema();
241241

242242
final DefaultCodegen codegen = new KotlinClientCodegen();
243243
OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("sample", model);
@@ -264,7 +264,7 @@ public void arrayPropertyTest() {
264264

265265
@Test(description = "convert a model with array property to a kotlin.collections.List")
266266
public void listPropertyTest() {
267-
final Schema model = getArrayTestSchema();
267+
final Schema<?> model = getArrayTestSchema();
268268

269269
final KotlinClientCodegen codegen = new KotlinClientCodegen();
270270
codegen.setCollectionType(KotlinClientCodegen.CollectionType.LIST.value);
@@ -293,7 +293,7 @@ public void listPropertyTest() {
293293

294294
@Test(description = "convert a model with a map property")
295295
public void mapPropertyTest() {
296-
final Schema schema = getMapSchema();
296+
final Schema<?> schema = getMapSchema();
297297
final DefaultCodegen codegen = new KotlinClientCodegen();
298298
OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("sample", schema);
299299
codegen.setOpenAPI(openAPI);
@@ -317,7 +317,7 @@ public void mapPropertyTest() {
317317

318318
@Test(description = "convert a model with complex property")
319319
public void complexPropertyTest() {
320-
final Schema schema = getComplexSchema();
320+
final Schema<?> schema = getComplexSchema();
321321
final DefaultCodegen codegen = new KotlinClientCodegen();
322322
OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("sample", schema);
323323
codegen.setOpenAPI(openAPI);
@@ -351,7 +351,7 @@ public static Object[][] modelNames() {
351351

352352
@Test(dataProvider = "modelNames", description = "sanitize model names")
353353
public void sanitizeModelNames(final String name, final ModelNameTest testCase) {
354-
final Schema schema = getComplexSchema();
354+
final Schema<?> schema = getComplexSchema();
355355
final DefaultCodegen codegen = new KotlinClientCodegen();
356356
OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema(name, schema);
357357
codegen.setOpenAPI(openAPI);
@@ -448,4 +448,3 @@ private ModelNameTest(String expectedName, String expectedClassName) {
448448
}
449449
}
450450
}
451-

modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinModelCodegenTest.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,14 @@
1717
import java.io.File;
1818
import java.io.IOException;
1919
import java.nio.file.Files;
20+
import java.nio.file.Path;
2021
import java.nio.file.Paths;
2122

23+
import static org.assertj.core.api.Assertions.assertThat;
24+
import static org.assertj.core.api.Assumptions.assumeThat;
2225
import static org.openapitools.codegen.TestUtils.assertFileContains;
26+
import static org.openapitools.codegen.VendorExtension.X_CLASS_EXTRA_ANNOTATION;
27+
import static org.openapitools.codegen.VendorExtension.X_FIELD_EXTRA_ANNOTATION;
2328

2429
public class KotlinModelCodegenTest {
2530

@@ -110,4 +115,20 @@ public void mutableArrayWithUniqueItems(AbstractKotlinCodegen codegen) throws IO
110115
assertFileContains(Paths.get(outputPath + "/src/main/kotlin/models/UniqueArray.kt"),
111116
"var array: kotlin.collections.MutableSet<kotlin.String>");
112117
}
113-
}
118+
119+
@Test(dataProvider = "generators")
120+
public void xFieldExtraAnnotation(AbstractKotlinCodegen codegen) throws IOException {
121+
assumeThat(codegen.getSupportedVendorExtensions().contains(X_FIELD_EXTRA_ANNOTATION)).isTrue();
122+
String outputPath = generateModels(codegen, "src/test/resources/3_0/issue_11772.yml", true);
123+
Path ktClassPath = Paths.get(outputPath + "/src/main/kotlin/models/Employee.kt");
124+
assertThat(ktClassPath).content().contains("@javax.persistence.Id");
125+
}
126+
127+
@Test(dataProvider = "generators")
128+
public void xClassExtraAnnotation(AbstractKotlinCodegen codegen) throws IOException {
129+
assumeThat(codegen.getSupportedVendorExtensions().contains(X_CLASS_EXTRA_ANNOTATION)).isTrue();
130+
String outputPath = generateModels(codegen, "src/test/resources/3_0/issue_11772.yml", true);
131+
Path ktClassPath = Paths.get(outputPath + "/src/main/kotlin/models/Employee.kt");
132+
assertThat(ktClassPath).content().contains("@javax.persistence.MappedSuperclass");
133+
}
134+
}

samples/client/petstore/kotlin-model-prefix-type-mappings/src/main/kotlin/org/openapitools/client/models/ApiAnnotation.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,13 @@ data class ApiAnnotation (
7171
companion object {
7272
var openapiFields = HashSet<String>()
7373
var openapiRequiredFields = HashSet<String>()
74-
74+
7575
init {
7676
// a set of all properties/fields (JSON key names)
7777
openapiFields.add("id")
7878

7979
}
80-
80+
8181
/**
8282
* Validates the JSON Element and throws an exception if issues found
8383
*

samples/client/petstore/kotlin-model-prefix-type-mappings/src/main/kotlin/org/openapitools/client/models/ApiApiResponse.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,15 @@ data class ApiApiResponse (
7979
companion object {
8080
var openapiFields = HashSet<String>()
8181
var openapiRequiredFields = HashSet<String>()
82-
82+
8383
init {
8484
// a set of all properties/fields (JSON key names)
8585
openapiFields.add("code")
8686
openapiFields.add("type")
8787
openapiFields.add("message")
8888

8989
}
90-
90+
9191
/**
9292
* Validates the JSON Element and throws an exception if issues found
9393
*

samples/client/petstore/kotlin-model-prefix-type-mappings/src/main/kotlin/org/openapitools/client/models/ApiCategory.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,14 @@ data class ApiCategory (
7575
companion object {
7676
var openapiFields = HashSet<String>()
7777
var openapiRequiredFields = HashSet<String>()
78-
78+
7979
init {
8080
// a set of all properties/fields (JSON key names)
8181
openapiFields.add("id")
8282
openapiFields.add("name")
8383

8484
}
85-
85+
8686
/**
8787
* Validates the JSON Element and throws an exception if issues found
8888
*

samples/client/petstore/kotlin-model-prefix-type-mappings/src/main/kotlin/org/openapitools/client/models/ApiOrder.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ data class ApiOrder (
102102
companion object {
103103
var openapiFields = HashSet<String>()
104104
var openapiRequiredFields = HashSet<String>()
105-
105+
106106
init {
107107
// a set of all properties/fields (JSON key names)
108108
openapiFields.add("id")
@@ -113,7 +113,7 @@ data class ApiOrder (
113113
openapiFields.add("complete")
114114

115115
}
116-
116+
117117
/**
118118
* Validates the JSON Element and throws an exception if issues found
119119
*

samples/client/petstore/kotlin-model-prefix-type-mappings/src/main/kotlin/org/openapitools/client/models/ApiPet.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ data class ApiPet (
105105
companion object {
106106
var openapiFields = HashSet<String>()
107107
var openapiRequiredFields = HashSet<String>()
108-
108+
109109
init {
110110
// a set of all properties/fields (JSON key names)
111111
openapiFields.add("name")
@@ -119,7 +119,7 @@ data class ApiPet (
119119
openapiRequiredFields.add("name")
120120
openapiRequiredFields.add("photoUrls")
121121
}
122-
122+
123123
/**
124124
* Validates the JSON Element and throws an exception if issues found
125125
*
@@ -133,7 +133,7 @@ data class ApiPet (
133133
String.format("The required field(s) %s in ApiPet is not found in the empty JSON string", ApiPet.openapiRequiredFields.toString())
134134
}
135135
}
136-
136+
137137
// check to make sure all required properties/fields are present in the JSON string
138138
for (requiredField in openapiRequiredFields) {
139139
requireNotNull(jsonElement!!.getAsJsonObject()[requiredField]) {
@@ -161,7 +161,7 @@ data class ApiPet (
161161
require(jsonObj["tags"].isJsonArray) {
162162
String.format("Expected the field `tags` to be an array in the JSON string but got `%s`", jsonObj["tags"].toString())
163163
}
164-
164+
165165
// validate the optional field `tags` (array)
166166
for (i in 0 until jsonObj.getAsJsonArray("tags").size()) {
167167
ApiTag.validateJsonElement(jsonObj.getAsJsonArray("tags").get(i))

0 commit comments

Comments
 (0)