Skip to content
Merged
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-server.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ jobs:
- samples/server/petstore/kotlin-springboot-source-swagger1
- samples/server/petstore/kotlin-springboot-source-swagger2
- samples/server/petstore/kotlin-springboot-springfox
- samples/server/petstore/kotlin-springboot-x-kotlin-implements
- samples/server/petstore/kotlin-server/ktor
- samples/server/petstore/kotlin-server/ktor2
- samples/server/petstore/kotlin-server/jaxrs-spec
Expand Down
14 changes: 14 additions & 0 deletions bin/configs/kotlin-spring-boot-x-kotlin-implements.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
generatorName: kotlin-spring
outputDir: samples/server/petstore/kotlin-springboot-x-kotlin-implements
library: spring-boot
inputSpec: modules/openapi-generator/src/test/resources/3_0/kotlin/petstore-with-x-kotlin-implements.yaml
templateDir: modules/openapi-generator/src/main/resources/kotlin-spring
additionalProperties:
documentationProvider: none
annotationLibrary: none
useSwaggerUI: "false"
serviceImplementation: "false"
skipDefaultInterface: "true"
interfaceOnly: "true"
serializableModel: "true"
beanValidations: "true"
2 changes: 2 additions & 0 deletions docs/generators/kotlin-spring.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|x-discriminator-value|Used with model inheritance to specify value for discriminator that identifies current model|MODEL|
|x-field-extra-annotation|List of custom annotations to be added to property|FIELD, OPERATION_PARAMETER|null
|x-pattern-message|Add this property whenever you need to customize the invalidation error message for the regex pattern of a variable|FIELD, OPERATION_PARAMETER|null
|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


## IMPORT MAPPING
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
public enum VendorExtension {

X_IMPLEMENTS("x-implements", ExtensionLevel.MODEL, "Ability to specify interfaces that model must implements", "empty array"),
X_KOTLIN_IMPLEMENTS("x-kotlin-implements", ExtensionLevel.MODEL, "Ability to specify interfaces that model must implement", "empty array"),
X_KOTLIN_IMPLEMENTS_FIELDS("x-kotlin-implements-fields", ExtensionLevel.MODEL, "Specify attributes that are implemented by the interface(s) added via `x-kotlin-implements`", "empty array"),
X_SPRING_PAGINATED("x-spring-paginated", ExtensionLevel.OPERATION, "Add `org.springframework.data.domain.Pageable` to controller method. Can be used to handle `page`, `size` and `sort` query parameters. If these query parameters are also specified in the operation spec, they will be removed from the controller method as their values can be obtained from the `Pageable` object.", "false"),
X_SPRING_PROVIDE_ARGS("x-spring-provide-args", ExtensionLevel.OPERATION, "Allows adding additional hidden parameters in the API specification to allow access to content such as header values or properties", "empty array"),
X_DISCRIMINATOR_VALUE("x-discriminator-value", ExtensionLevel.MODEL, "Used with model inheritance to specify value for discriminator that identifies current model", ""),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,16 @@ protected boolean needToImport(String type) {
@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 @@ -850,7 +860,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))
.filter(p -> allVarsMap.containsKey(p.baseName)
|| implementedInterfacesFields.contains(p.baseName)
)
.forEach(p -> p.isInherited = true);
return m;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,8 @@ public List<VendorExtension> getSupportedVendorExtensions() {
extensions.add(VendorExtension.X_DISCRIMINATOR_VALUE);
extensions.add(VendorExtension.X_FIELD_EXTRA_ANNOTATION);
extensions.add(VendorExtension.X_PATTERN_MESSAGE);
extensions.add(VendorExtension.X_KOTLIN_IMPLEMENTS);
extensions.add(VendorExtension.X_KOTLIN_IMPLEMENTS_FIELDS);
return extensions;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,34 @@
{{/-last}}{{/requiredVars}}{{#hasRequired}}{{#hasOptional}},
{{/hasOptional}}{{/hasRequired}}{{#optionalVars}}{{>dataClassOptVar}}{{^-last}},
{{/-last}}{{/optionalVars}}
) {{/discriminator}}{{#parent}}: {{{.}}}{{#serializableModel}}, Serializable{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}: Serializable{{/serializableModel}}{{/parent}}{
){{/discriminator}}{{! no newline
}}{{#parent}} : {{{.}}}{{! no newline
Comment on lines +20 to +21
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This misuse of comments to prevent line break might be controversial. However it seemed to improve the readability as otherwise the line would be very very long. But I can do one-liner if it is preferred to this.

}}{{#serializableModel}}{{! no newline
}}{{^vendorExtensions.x-kotlin-implements}}, Serializable{{/vendorExtensions.x-kotlin-implements}}{{! no newline
}}{{#vendorExtensions.x-kotlin-implements}}, Serializable, {{! no newline
}}{{#-first}} {{{.}}}{{/-first}}{{! no newline
}}{{^-first}}, {{{.}}}{{/-first}}{{! no newline
}}{{/vendorExtensions.x-kotlin-implements}}{{! no newline
}}{{/serializableModel}}{{! no newline
}}{{^serializableModel}}{{! no newline
}}{{#vendorExtensions.x-kotlin-implements}}, {{{.}}}{{/vendorExtensions.x-kotlin-implements}}{{! no newline
}}{{/serializableModel}}{{! no newline
}}{{/parent}}{{! no newline
}}{{^parent}}{{! no newline
}}{{#serializableModel}}{{! no newline
}}{{^vendorExtensions.x-kotlin-implements}} : Serializable{{/vendorExtensions.x-kotlin-implements}}{{! no newline
}}{{#vendorExtensions.x-kotlin-implements}}{{! no newline
}}{{#-first}} : Serializable, {{{.}}}{{/-first}}{{! no newline
}}{{^-first}}, {{{.}}}{{/-first}}{{! no newline
}}{{/vendorExtensions.x-kotlin-implements}}{{! no newline
}}{{/serializableModel}}{{! no newline
}}{{^serializableModel}}{{! no newline
}}{{#vendorExtensions.x-kotlin-implements}}{{! no newline
}}{{#-first}} : {{{.}}}{{/-first}}{{! no newline
}}{{^-first}}, {{{.}}}{{/-first}}{{! no newline
}}{{/vendorExtensions.x-kotlin-implements}}{{! no newline
}}{{/serializableModel}}{{! no newline
}}{{/parent}} {
{{#discriminator}}
{{#requiredVars}}
{{>interfaceReqVar}}{{! prevent indent}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* {{{description}}}
* Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}}
*/
enum class {{classname}}(@get:JsonValue val value: {{dataType}}) {
enum class {{classname}}(@get:JsonValue val value: {{dataType}}) {{#vendorExtensions.x-kotlin-implements}}{{#-first}}: {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}} {{/vendorExtensions.x-kotlin-implements}}{
{{#allowableValues}}{{#enumVars}}
{{&name}}({{{value}}}){{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -946,10 +946,80 @@ public void generateSerializableModel() throws Exception {

generator.opts(input).generate();

Path path = Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Pet.kt");
assertFileContains(
Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Pet.kt"),
path,
"import java.io.Serializable",
") : Serializable{",
") : Serializable {",
"private const val serialVersionUID: kotlin.Long = 1"
);
}
@Test
public void generateSerializableModelWithXimplements() throws Exception {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();
String outputPath = output.getAbsolutePath().replace('\\', '/');

KotlinSpringServerCodegen codegen = new KotlinSpringServerCodegen();
codegen.setOutputDir(output.getAbsolutePath());
codegen.additionalProperties().put(CodegenConstants.SERIALIZABLE_MODEL, true);

ClientOptInput input = new ClientOptInput()
.openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/petstore-with-x-kotlin-implements.yaml"))
.config(codegen);
DefaultGenerator generator = new DefaultGenerator();

generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "true");
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false");
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false");
generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "false");
generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "false");

generator.opts(input).generate();

Path path = Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Dog.kt");
assertFileContains(
path,
"import java.io.Serializable",
"@get:JsonProperty(\"likesFetch\", required = true) override val likesFetch: kotlin.Boolean,",
") : Pet, Serializable, com.some.pack.Fetchable {",
"private const val serialVersionUID: kotlin.Long = 1"
);
}

@Test
public void generateNonSerializableModelWithXimplements() throws Exception {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();
String outputPath = output.getAbsolutePath().replace('\\', '/');

KotlinSpringServerCodegen codegen = new KotlinSpringServerCodegen();
codegen.setOutputDir(output.getAbsolutePath());

ClientOptInput input = new ClientOptInput()
.openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/petstore-with-x-kotlin-implements.yaml"))
.config(codegen);
DefaultGenerator generator = new DefaultGenerator();

generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "true");
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false");
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false");
generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "false");
generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "false");

generator.opts(input).generate();

Path path = Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Dog.kt");
assertFileContains(
path,
"@get:JsonProperty(\"likesFetch\", required = true) override val likesFetch: kotlin.Boolean,",
") : Pet, com.some.pack.Fetchable {"
);
assertFileNotContains(
path,
"import java.io.Serializable",
") : Pet, Serializable, com.some.pack.Fetchable {",
") : Pet, Serializable {",
"private const val serialVersionUID: kotlin.Long = 1"
);
}
Expand Down
Loading
Loading