Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/samples-kotlin-client.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ jobs:
- samples/client/others/kotlin-jvm-okhttp-path-comments
- samples/client/others/kotlin-integer-enum
- samples/client/petstore/kotlin-allOf-discriminator-kotlinx-serialization
- samples/client/others/kotlin-oneOf-discriminator
steps:
- uses: actions/checkout@v5
- uses: actions/setup-java@v5
Expand Down
11 changes: 11 additions & 0 deletions bin/configs/kotlin-oneOf-discriminator.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
generatorName: kotlin
library: jvm-spring-restclient
outputDir: samples/client/others/kotlin-oneOf-discriminator
inputSpec: modules/openapi-generator/src/test/resources/3_0/kotlin/oneOf-with-discriminator-mapping.yaml
templateDir: modules/openapi-generator/src/main/resources/kotlin-client
additionalProperties:
artifactId: kotlin-oneOf-discriminator
serializableModel: "false"
dateLibrary: java8
useSpringBoot3: true
serializationLibrary: jackson
2 changes: 2 additions & 0 deletions docs/generators/kotlin.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ These options may be applied as additional-properties (cli) or configOptions (pl

| Extension name | Description | Applicable for | Default value |
| -------------- | ----------- | -------------- | ------------- |
|x-kotlin-implements|Ability to specify interfaces that model must implement|MODEL|empty array
|x-kotlin-implements-fields|Specify attributes that are implemented by the interface(s) added via `x-kotlin-implements`|MODEL|empty array
|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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ public enum VendorExtension {
X_OPERATION_EXTRA_ANNOTATION("x-operation-extra-annotation", ExtensionLevel.OPERATION, "List of custom annotations to be added to operation", null),
X_VERSION_PARAM("x-version-param", ExtensionLevel.OPERATION_PARAMETER, "Marker property that tells that this parameter would be used for endpoint versioning. Applicable for headers & query params. true/false", null),
X_PATTERN_MESSAGE("x-pattern-message", Arrays.asList(ExtensionLevel.FIELD, ExtensionLevel.OPERATION_PARAMETER), "Add this property whenever you need to customize the invalidation error message for the regex pattern of a variable", null),
X_ZERO_BASED_ENUM("x-zero-based-enum", ExtensionLevel.MODEL, "When used on an enum, the index will not be generated and the default numbering will be used, zero-based", "false"),
;
X_ZERO_BASED_ENUM("x-zero-based-enum", ExtensionLevel.MODEL, "When used on an enum, the index will not be generated and the default numbering will be used, zero-based", "false");

private final String name;
private final List<ExtensionLevel> levels;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -828,23 +828,13 @@ protected boolean isReservedWord(String word) {
protected boolean needToImport(String type) {
// provides extra protection against improperly trying to import language primitives and java types
return !type.startsWith("kotlin.") && !type.startsWith("java.") &&
!defaultIncludes.contains(type) && !languageSpecificPrimitives.contains(type) &&
!type.contains(".");
!defaultIncludes.contains(type) && !languageSpecificPrimitives.contains(type) &&
!type.contains(".");
}

@Override
public CodegenModel fromModel(String name, Schema schema) {
CodegenModel m = super.fromModel(name, schema);
List<String> implementedInterfacesClasses = (List<String>) m.getVendorExtensions().getOrDefault(VendorExtension.X_KOTLIN_IMPLEMENTS.getName(), List.of());
List<String> implementedInterfacesFields = Optional.ofNullable((List<String>) m.getVendorExtensions().get(VendorExtension.X_KOTLIN_IMPLEMENTS_FIELDS.getName()))
.map(xKotlinImplementsFields -> {
if (implementedInterfacesClasses.isEmpty() && !xKotlinImplementsFields.isEmpty()) {
LOGGER.warn("Annotating {} with {} without {} is not supported. {} will be ignored.",
name, VendorExtension.X_KOTLIN_IMPLEMENTS_FIELDS.getName(), VendorExtension.X_KOTLIN_IMPLEMENTS.getName(),
VendorExtension.X_KOTLIN_IMPLEMENTS_FIELDS.getName());
}
return xKotlinImplementsFields;
}).orElse(List.of());
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
Expand All @@ -860,11 +850,9 @@ public CodegenModel fromModel(String name, Schema schema) {
// Update any other vars (requiredVars, optionalVars)
Stream.of(m.requiredVars, m.optionalVars)
.flatMap(List::stream)
.filter(p -> allVarsMap.containsKey(p.baseName)
|| implementedInterfacesFields.contains(p.baseName)
)
.filter(p -> allVarsMap.containsKey(p.baseName))
.forEach(p -> p.isInherited = true);
return m;
return addIsInheritedBasedOnImplementsVendorExtension(name, m);
}

@Override
Expand Down Expand Up @@ -1172,4 +1160,44 @@ protected void doDataTypeAssignment(final String returnType, DataTypeAssigner da
}
}
}

/**
* Uses the x-kotlin-implements and the x-kotlin-implements-fields vendor extensions to set the isInherited CodegenProperty field.
* Will log a warning if an invalid vendor extension combination is used.
* @param name The name
* @param codegenModel The codegenModel
* @return The modified CodegenModel where isInherited has been added to the var CodegenProperty
*/
private CodegenModel addIsInheritedBasedOnImplementsVendorExtension(String name, CodegenModel codegenModel) {
String warningMessage = "Annotating {} with {} without {} is not supported. {} will be ignored.";
String kotlinImplements = VendorExtension.X_KOTLIN_IMPLEMENTS.getName();
String kotlinImplementsFields = VendorExtension.X_KOTLIN_IMPLEMENTS_FIELDS.getName();
Map<String, Object> vendorExtensions = codegenModel.getVendorExtensions();
List<String> implementedInterfacesClasses = (List<String>) vendorExtensions.getOrDefault(kotlinImplements, List.of());
List<String> implementedInterfacesFields = Optional.ofNullable((List<String>) vendorExtensions.get(kotlinImplementsFields))
.map(xKotlinImplementsFields -> {
if (implementedInterfacesClasses.isEmpty() && !xKotlinImplementsFields.isEmpty()) {
LOGGER.warn(warningMessage, name,
kotlinImplementsFields,
kotlinImplements,
kotlinImplementsFields
);
}
return xKotlinImplementsFields;
})
.orElse(List.of());
codegenModel.optionalVars.stream()
.filter(p -> implementedInterfacesFields.contains(p.baseName))
.forEach(p -> p.isInherited = true);
codegenModel.requiredVars.stream()
.filter(p -> implementedInterfacesFields.contains(p.baseName))
.forEach(p -> p.isInherited = true);
codegenModel.allVars.stream()
.filter(p -> implementedInterfacesFields.contains(p.baseName))
.forEach(p -> p.isInherited = true);
codegenModel.vars.stream()
.filter(p -> implementedInterfacesFields.contains(p.baseName))
.forEach(p -> p.isInherited = true);
return codegenModel;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1128,6 +1128,8 @@ public void postProcess() {
@Override
public List<VendorExtension> getSupportedVendorExtensions() {
var extensions = super.getSupportedVendorExtensions();
extensions.add(VendorExtension.X_KOTLIN_IMPLEMENTS);
extensions.add(VendorExtension.X_KOTLIN_IMPLEMENTS_FIELDS);
extensions.add(VendorExtension.X_CLASS_EXTRA_ANNOTATION);
extensions.add(VendorExtension.X_FIELD_EXTRA_ANNOTATION);
return extensions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ import {{packageName}}.infrastructure.ITransformForStorage
{{#required}}{{>data_class_req_var}}{{/required}}{{^required}}{{>data_class_opt_var}}{{/required}}{{^-last}},{{/-last}}

{{/allVars}}
){{/discriminator}}{{#parent}}{{^serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#kotlinx_serialization}}(){{/kotlinx_serialization}}{{#multiplatform}}(){{/multiplatform}}{{#isArray}}(){{/isArray}}{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{^serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{^parcelizeModels}} : Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{^serializableModel}}{{#parcelizeModels}} : Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{#parcelizeModels}} : Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#generateRoomModels}}{{#parent}}, {{/parent}}{{^discriminator}}{{^parent}}:{{/parent}} ITransformForStorage<{{classname}}RoomModel>{{/discriminator}}{{/generateRoomModels}}{{#vendorExtensions.x-has-data-class-body}} {
){{/discriminator}}{{#vendorExtensions.x-kotlin-implements}} : {{{.}}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-kotlin-implements}}{{#parent}}{{^serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#kotlinx_serialization}}(){{/kotlinx_serialization}}{{#multiplatform}}(){{/multiplatform}}{{#isArray}}(){{/isArray}}{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{^serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{^parcelizeModels}} : Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{^serializableModel}}{{#parcelizeModels}} : Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{#parcelizeModels}} : Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#generateRoomModels}}{{#parent}}, {{/parent}}{{^discriminator}}{{^parent}}:{{/parent}} ITransformForStorage<{{classname}}RoomModel>{{/discriminator}}{{/generateRoomModels}}{{#vendorExtensions.x-has-data-class-body}} {
{{/vendorExtensions.x-has-data-class-body}}
{{#generateRoomModels}}
companion object { }
Expand Down
Loading
Loading