diff --git a/build-logic/src/main/groovy/io.micronaut.build.internal.openapi-java-generator-test-suite.gradle b/build-logic/src/main/groovy/io.micronaut.build.internal.openapi-java-generator-test-suite.gradle index e4a969307d..bbd544d888 100644 --- a/build-logic/src/main/groovy/io.micronaut.build.internal.openapi-java-generator-test-suite.gradle +++ b/build-logic/src/main/groovy/io.micronaut.build.internal.openapi-java-generator-test-suite.gradle @@ -22,6 +22,7 @@ dependencies { def openapiGenerate = tasks.register("generateOpenApi", OpenApiGeneratorTask) { lang = "java" generatedAnnotation = true + ksp = false classpath.from(configurations.openapiGenerator) openApiDefinition.convention(layout.projectDirectory.file("petstore.json")) outputDirectory.convention(layout.buildDirectory.dir("generated/openapi")) diff --git a/build-logic/src/main/groovy/io.micronaut.build.internal.openapi-kotlin-kapt-generator-test-suite.gradle b/build-logic/src/main/groovy/io.micronaut.build.internal.openapi-kotlin-kapt-generator-test-suite.gradle new file mode 100644 index 0000000000..0f9ea00483 --- /dev/null +++ b/build-logic/src/main/groovy/io.micronaut.build.internal.openapi-kotlin-kapt-generator-test-suite.gradle @@ -0,0 +1,54 @@ +import io.micronaut.build.internal.openapi.OpenApiGeneratorTask + +plugins { + id 'io.micronaut.minimal.application' +} + +repositories { + mavenCentral() +} + +configurations { + openapiGenerator { + canBeResolved = true + canBeConsumed = false + } +} + +dependencies { + openapiGenerator(project(":test-suite-generator-util")) +} + +def openapiGenerate = tasks.register("generateOpenApi", OpenApiGeneratorTask) { + lang = "kotlin" + generatedAnnotation = true + ksp = false + classpath.from(configurations.openapiGenerator) + openApiDefinition.convention(layout.projectDirectory.file("petstore.json")) + outputDirectory.convention(layout.buildDirectory.dir("generated/openapi")) + generatorKind.convention("client") + outputKinds.convention(["models", "apis", "apiDocs", "modelDocs", "supportingFiles", "modelTests", "apiTests"]) + parameterMappings.convention([]) + responseBodyMappings.convention([]) +} + +sourceSets { + main { + java.srcDir(openapiGenerate.map(OpenApiGeneratorTask::getGeneratedSourcesDirectory)) + } + test { + java.srcDir(openapiGenerate.map(OpenApiGeneratorTask::getGeneratedTestSourcesDirectory)) + } +} + +micronaut { + version = libs.versions.micronaut.platform.get() + runtime("netty") + testRuntime("junit5") +} + +dependencies { + constraints { + implementation("io.micronaut:micronaut-http-client:${libs.versions.micronaut.asProvider().get()}") + } +} diff --git a/build-logic/src/main/groovy/io.micronaut.build.internal.openapi-kotlin-generator-test-suite.gradle b/build-logic/src/main/groovy/io.micronaut.build.internal.openapi-kotlin-ksp-generator-test-suite.gradle similarity index 98% rename from build-logic/src/main/groovy/io.micronaut.build.internal.openapi-kotlin-generator-test-suite.gradle rename to build-logic/src/main/groovy/io.micronaut.build.internal.openapi-kotlin-ksp-generator-test-suite.gradle index 0875fc87cd..330a93e480 100644 --- a/build-logic/src/main/groovy/io.micronaut.build.internal.openapi-kotlin-generator-test-suite.gradle +++ b/build-logic/src/main/groovy/io.micronaut.build.internal.openapi-kotlin-ksp-generator-test-suite.gradle @@ -22,6 +22,7 @@ dependencies { def openapiGenerate = tasks.register("generateOpenApi", OpenApiGeneratorTask) { lang = "kotlin" generatedAnnotation = true + ksp = true classpath.from(configurations.openapiGenerator) openApiDefinition.convention(layout.projectDirectory.file("petstore.json")) outputDirectory.convention(layout.buildDirectory.dir("generated/openapi")) diff --git a/build-logic/src/main/groovy/io/micronaut/build/internal/openapi/OpenApiGeneratorTask.java b/build-logic/src/main/groovy/io/micronaut/build/internal/openapi/OpenApiGeneratorTask.java index 5a95d4e9ce..e973b7ecfc 100644 --- a/build-logic/src/main/groovy/io/micronaut/build/internal/openapi/OpenApiGeneratorTask.java +++ b/build-logic/src/main/groovy/io/micronaut/build/internal/openapi/OpenApiGeneratorTask.java @@ -62,6 +62,9 @@ public abstract class OpenApiGeneratorTask extends DefaultTask { @Input public abstract Property getGeneratedAnnotation(); + @Input + public abstract Property getKsp(); + @OutputDirectory public abstract DirectoryProperty getOutputDirectory(); @@ -95,6 +98,7 @@ public void execute() throws IOException { var generatedTestSourcesDir = getGeneratedTestSourcesDirectory().get().getAsFile(); var lang = getLang().get(); var generatedAnnotation = getGeneratedAnnotation().get(); + var ksp = getKsp().get(); Files.createDirectories(generatedSourcesDir.toPath()); Files.createDirectories(generatedTestSourcesDir.toPath()); getProject().getLogger().info("json: {}", getParameterMappings().get()); @@ -110,6 +114,7 @@ public void execute() throws IOException { args.add(getResponseBodyMappings().get().toString()); args.add(lang.toUpperCase()); args.add(Boolean.toString(generatedAnnotation)); + args.add(Boolean.toString(ksp)); javaexec.args(args); }); } diff --git a/gradle.properties b/gradle.properties index 7ca2fa3d38..326885a733 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,9 +7,10 @@ projectUrl=https://micronaut.io githubSlug=micronaut-projects/micronaut-openapi developers=Puneet Behl,Álvaro Sánchez-Mariscal,Iván López -org.gradle.caching=true +org.gradle.caching=false kotlin.stdlib.default.dependency=false org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8 +ksp.incremental = false # No matter which Java toolchain we use, the Kotlin Daemon is always invoked by the current JDK. # Therefor to fix Kapt errors when running tests under Java 21, we need to open up some modules for the Kotlin Daemon. diff --git a/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautKotlinCodegen.java b/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautKotlinCodegen.java index 73adbfe820..8b0e8ffd43 100644 --- a/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautKotlinCodegen.java +++ b/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautKotlinCodegen.java @@ -104,6 +104,7 @@ public abstract class AbstractMicronautKotlinCodegen instead Mono> for arrays in generated code", fluxForArrays)); cliOptions.add(CliOption.newBoolean(OPT_GENERATED_ANNOTATION, "Generate code with \"@Generated\" annotation", generatedAnnotation)); + cliOptions.add(CliOption.newBoolean(OPT_KSP, "Generate code compatible only with KSP", ksp)); cliOptions.add(CliOption.newBoolean(USE_BEANVALIDATION, "Use BeanValidation API annotations", useBeanValidation)); cliOptions.add(CliOption.newBoolean(OPT_VISITABLE, "Generate visitor for subtypes with a discriminator", visitable)); cliOptions.add(CliOption.newBoolean(OPT_REQUIRED_PROPERTIES_IN_CONSTRUCTOR, "Allow only to create models with all the required properties provided in constructor", requiredPropertiesInConstructor)); @@ -390,6 +393,10 @@ public void setGeneratedAnnotation(boolean generatedAnnotation) { this.generatedAnnotation = generatedAnnotation; } + public void setKsp(boolean ksp) { + this.ksp = ksp; + } + @Override public void processOpts() { super.processOpts(); @@ -432,6 +439,11 @@ public void processOpts() { } writePropertyBack(OPT_GENERATED_ANNOTATION, generatedAnnotation); + if (additionalProperties.containsKey(OPT_KSP)) { + ksp = convertPropertyToBoolean(OPT_KSP); + } + writePropertyBack(OPT_KSP, ksp); + if (additionalProperties.containsKey(OPT_VISITABLE)) { visitable = convertPropertyToBoolean(OPT_VISITABLE); } @@ -1155,6 +1167,7 @@ private void processProperty(CodegenProperty property, boolean isServer, Codegen property.vendorExtensions.put("inRequiredArgsConstructor", !property.isReadOnly || isServer); property.vendorExtensions.put("isServer", isServer); property.vendorExtensions.put("defaultValueIsNotNull", property.defaultValue != null && !property.defaultValue.equals("null")); + property.vendorExtensions.put("fieldAnnPrefix", ksp ? "" : "field:"); if ("null".equals(property.example)) { property.example = null; } diff --git a/openapi-generator/src/main/java/io/micronaut/openapi/generator/KotlinMicronautClientCodegen.java b/openapi-generator/src/main/java/io/micronaut/openapi/generator/KotlinMicronautClientCodegen.java index 41de549915..d8815751ac 100644 --- a/openapi-generator/src/main/java/io/micronaut/openapi/generator/KotlinMicronautClientCodegen.java +++ b/openapi-generator/src/main/java/io/micronaut/openapi/generator/KotlinMicronautClientCodegen.java @@ -207,6 +207,7 @@ static class DefaultClientOptionsBuilder implements KotlinMicronautClientOptions private boolean useAuth; private boolean fluxForArrays; private boolean generatedAnnotation = true; + private boolean ksp; @Override public KotlinMicronautClientOptionsBuilder withAuthorization(boolean useAuth) { @@ -256,6 +257,12 @@ public KotlinMicronautClientOptionsBuilder withGeneratedAnnotation(boolean gener return this; } + @Override + public KotlinMicronautClientOptionsBuilder withKsp(boolean ksp) { + this.ksp = ksp; + return this; + } + ClientOptions build() { return new ClientOptions( additionalClientTypeAnnotations, @@ -265,7 +272,8 @@ ClientOptions build() { useAuth, plural, fluxForArrays, - generatedAnnotation + generatedAnnotation, + ksp ); } } @@ -278,7 +286,8 @@ record ClientOptions( boolean useAuth, boolean plural, boolean fluxForArrays, - boolean generatedAnnotation + boolean generatedAnnotation, + boolean ksp ) { } } diff --git a/openapi-generator/src/main/java/io/micronaut/openapi/generator/KotlinMicronautClientOptionsBuilder.java b/openapi-generator/src/main/java/io/micronaut/openapi/generator/KotlinMicronautClientOptionsBuilder.java index 41b2cbc0c8..ee065dddfc 100644 --- a/openapi-generator/src/main/java/io/micronaut/openapi/generator/KotlinMicronautClientOptionsBuilder.java +++ b/openapi-generator/src/main/java/io/micronaut/openapi/generator/KotlinMicronautClientOptionsBuilder.java @@ -94,4 +94,13 @@ public interface KotlinMicronautClientOptionsBuilder extends GeneratorOptionsBui * @return this builder */ KotlinMicronautClientOptionsBuilder withGeneratedAnnotation(boolean generatedAnnotation); + + /** + * If set to true, generated code will be fully compatible with KSP, but not 100% with KAPT. + * + * @param ksp do we need to generate code compatible only with KSP + * + * @return this builder + */ + KotlinMicronautClientOptionsBuilder withKsp(boolean ksp); } diff --git a/openapi-generator/src/main/java/io/micronaut/openapi/generator/KotlinMicronautServerCodegen.java b/openapi-generator/src/main/java/io/micronaut/openapi/generator/KotlinMicronautServerCodegen.java index 099342735c..a3b7b83d14 100644 --- a/openapi-generator/src/main/java/io/micronaut/openapi/generator/KotlinMicronautServerCodegen.java +++ b/openapi-generator/src/main/java/io/micronaut/openapi/generator/KotlinMicronautServerCodegen.java @@ -95,7 +95,6 @@ public class KotlinMicronautServerCodegen extends AbstractMicronautKotlinCodegen cliOptions.add(CliOption.newBoolean(OPT_GENERATE_STREAMING_FILE_UPLOAD, "Whether to generate StreamingFileUpload type for file request body", generateStreamingFileUpload)); cliOptions.add(CliOption.newBoolean(OPT_AOT, "Generate compatible code with micronaut-aot", aot)); - // Set the type mappings // It could be also StreamingFileUpload typeMapping.put("file", "CompletedFileUpload"); @@ -304,6 +303,7 @@ static class DefaultServerOptionsBuilder implements KotlinMicronautServerOptions private boolean fluxForArrays; private boolean generatedAnnotation = true; private boolean aot; + private boolean ksp; @Override public KotlinMicronautServerOptionsBuilder withControllerPackage(String controllerPackage) { @@ -359,6 +359,12 @@ public KotlinMicronautServerOptionsBuilder withAot(boolean aot) { return this; } + @Override + public KotlinMicronautServerOptionsBuilder withKsp(boolean ksp) { + this.ksp = ksp; + return this; + } + ServerOptions build() { return new ServerOptions( controllerPackage, @@ -369,7 +375,8 @@ ServerOptions build() { plural, fluxForArrays, generatedAnnotation, - aot + aot, + ksp ); } } @@ -383,7 +390,8 @@ record ServerOptions( boolean plural, boolean fluxForArrays, boolean generatedAnnotation, - boolean aot + boolean aot, + boolean ksp ) { } } diff --git a/openapi-generator/src/main/java/io/micronaut/openapi/generator/KotlinMicronautServerOptionsBuilder.java b/openapi-generator/src/main/java/io/micronaut/openapi/generator/KotlinMicronautServerOptionsBuilder.java index f6d3bf5671..5aaa30d062 100644 --- a/openapi-generator/src/main/java/io/micronaut/openapi/generator/KotlinMicronautServerOptionsBuilder.java +++ b/openapi-generator/src/main/java/io/micronaut/openapi/generator/KotlinMicronautServerOptionsBuilder.java @@ -101,4 +101,13 @@ public interface KotlinMicronautServerOptionsBuilder extends GeneratorOptionsBui * @return this builder */ KotlinMicronautServerOptionsBuilder withAot(boolean aot); + + /** + * If set to true, generated code will be fully compatible with KSP, but not 100% with KAPT. + * + * @param ksp do we need to generate code compatible only with KSP + * + * @return this builder + */ + KotlinMicronautServerOptionsBuilder withKsp(boolean ksp); } diff --git a/openapi-generator/src/main/java/io/micronaut/openapi/generator/MicronautCodeGeneratorEntryPoint.java b/openapi-generator/src/main/java/io/micronaut/openapi/generator/MicronautCodeGeneratorEntryPoint.java index f07ca10a32..ca14147da5 100644 --- a/openapi-generator/src/main/java/io/micronaut/openapi/generator/MicronautCodeGeneratorEntryPoint.java +++ b/openapi-generator/src/main/java/io/micronaut/openapi/generator/MicronautCodeGeneratorEntryPoint.java @@ -225,6 +225,7 @@ private void configureKotlinServerOptions() { kotlinServerCodegen.setGenerateOperationsToReturnNotImplemented(kotlinServerOptions.generateOperationsToReturnNotImplemented()); kotlinServerCodegen.setGenerateControllerFromExamples(kotlinServerOptions.generateControllerFromExamples()); kotlinServerCodegen.setGeneratedAnnotation(kotlinServerOptions.generatedAnnotation()); + kotlinServerCodegen.setKsp(kotlinServerOptions.ksp()); kotlinServerCodegen.setUseAuth(kotlinServerOptions.useAuth()); kotlinServerCodegen.setPlural(kotlinServerOptions.plural()); kotlinServerCodegen.setFluxForArrays(kotlinServerOptions.fluxForArrays()); @@ -246,6 +247,7 @@ public void configureKotlinClientOptions() { kotlinClientCodegen.setBasePathSeparator(kotlinClientCodegen.basePathSeparator); } kotlinClientCodegen.setGeneratedAnnotation(kotlinClientOptions.generatedAnnotation()); + kotlinClientCodegen.setKsp(kotlinClientOptions.ksp()); kotlinClientCodegen.setConfigureAuthorization(kotlinClientOptions.useAuth()); kotlinClientCodegen.setPlural(kotlinClientOptions.plural()); kotlinClientCodegen.setFluxForArrays(kotlinClientOptions.fluxForArrays()); diff --git a/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/model/field_annotations.mustache b/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/model/field_annotations.mustache index de2018efde..1b756ecea3 100644 --- a/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/model/field_annotations.mustache +++ b/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/model/field_annotations.mustache @@ -1,31 +1,31 @@ {{>common/params/validation_field}} {{#withXml}} {{#isXmlAttribute}} - @field:XmlAttribute(name = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}" + @{{{vendorExtensions.fieldAnnPrefix}}}XmlAttribute(name = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}" {{/isXmlAttribute}} {{^isXmlAttribute}} {{^isContainer}} - @field:XmlElement({{#xmlNamespace}}namespace = "{{xmlNamespace}}", {{/xmlNamespace}}name = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}") + @{{{vendorExtensions.fieldAnnPrefix}}}XmlElement({{#xmlNamespace}}namespace = "{{xmlNamespace}}", {{/xmlNamespace}}name = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}") {{/isContainer}} {{#isContainer}} // Is a container wrapped = {{isXmlWrapped}} {{#items}} // items.name = {{name}} items.baseName = {{baseName}} items.xmlName = {{xmlName}} items.xmlNamespace = {{xmlNamespace}} // items.example = {{example}} items.type = {{dataType}} - @field:XmlElement({{#xmlNamespace}}namespace = "{{xmlNamespace}}", {{/xmlNamespace}}name = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}") + @{{{vendorExtensions.fieldAnnPrefix}}}XmlElement({{#xmlNamespace}}namespace = "{{xmlNamespace}}", {{/xmlNamespace}}name = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}") {{/items}} {{#isXmlWrapped}} - @field:XmlElementWrapper({{#xmlNamespace}}namespace = "{{xmlNamespace}}", {{/xmlNamespace}}name = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}") + @{{{vendorExtensions.fieldAnnPrefix}}}XmlElementWrapper({{#xmlNamespace}}namespace = "{{xmlNamespace}}", {{/xmlNamespace}}name = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}") {{/isXmlWrapped}} {{/isContainer}} {{/isXmlAttribute}} {{/withXml}} {{#generateSwagger2Annotations}} - @field:Schema(name = "{{{baseName}}}"{{#isReadOnly}}, accessMode = Schema.AccessMode.READ_ONLY{{/isReadOnly}}{{#example}}, example = "{{{.}}}"{{/example}}{{#description}}, description = "{{{.}}}"{{/description}}, requiredMode = {{#required}}Schema.RequiredMode.REQUIRED{{/required}}{{^required}}Schema.RequiredMode.NOT_REQUIRED{{/required}}) + @{{{vendorExtensions.fieldAnnPrefix}}}Schema(name = "{{{baseName}}}"{{#isReadOnly}}, accessMode = Schema.AccessMode.READ_ONLY{{/isReadOnly}}{{#example}}, example = "{{{.}}}"{{/example}}{{#description}}, description = "{{{.}}}"{{/description}}, requiredMode = {{#required}}Schema.RequiredMode.REQUIRED{{/required}}{{^required}}Schema.RequiredMode.NOT_REQUIRED{{/required}}) {{/generateSwagger2Annotations}} {{#vendorExtensions.x-is-jackson-optional-nullable}} {{!Unannotated, Jackson would pick this up automatically and add it *in addition* to the _JsonNullable getter field}} - @field:JsonIgnore + @{{{vendorExtensions.fieldAnnPrefix}}}JsonIgnore {{/vendorExtensions.x-is-jackson-optional-nullable}} {{^vendorExtensions.x-is-jackson-optional-nullable}} {{#jackson}} diff --git a/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/model/jackson_annotations.mustache b/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/model/jackson_annotations.mustache index eca6cb6c80..2a4387968d 100644 --- a/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/model/jackson_annotations.mustache +++ b/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/model/jackson_annotations.mustache @@ -4,34 +4,34 @@ * If the field is required, always include it, even if it is null. * Else use custom behaviour, IOW use whatever is defined on the object mapper }} - @field:JsonProperty(JSON_PROPERTY_{{nameInSnakeCase}}) + @{{{vendorExtensions.fieldAnnPrefix}}}JsonProperty(JSON_PROPERTY_{{nameInSnakeCase}}) {{#isMap}} {{#items.isNullable}} - @field:JsonInclude(content = JsonInclude.Include.ALWAYS{{^required}}, value = JsonInclude.Include.USE_DEFAULTS{{/required}}) + @{{{vendorExtensions.fieldAnnPrefix}}}JsonInclude(content = JsonInclude.Include.ALWAYS{{^required}}, value = JsonInclude.Include.USE_DEFAULTS{{/required}}) {{/items.isNullable}} {{^items.isNullable}}{{^required}} - @field:JsonInclude(JsonInclude.Include.USE_DEFAULTS) + @{{{vendorExtensions.fieldAnnPrefix}}}JsonInclude(JsonInclude.Include.USE_DEFAULTS) {{/required}}{{/items.isNullable}} {{/isMap}} {{^isMap}} {{#required}} {{#isReadOnly}} - @field:JsonInclude(JsonInclude.Include.USE_DEFAULTS) + @{{{vendorExtensions.fieldAnnPrefix}}}JsonInclude(JsonInclude.Include.USE_DEFAULTS) {{/isReadOnly}} {{/required}} {{^required}} - @field:JsonInclude(JsonInclude.Include.USE_DEFAULTS) + @{{{vendorExtensions.fieldAnnPrefix}}}JsonInclude(JsonInclude.Include.USE_DEFAULTS) {{/required}} {{/isMap}} {{#withXml}} {{^isContainer}} - @field:JacksonXmlProperty({{#isXmlAttribute}}isAttribute = true, {{/isXmlAttribute}}{{#xmlNamespace}}namespace = "{{xmlNamespace}}", {{/xmlNamespace}}localName = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}") - @field:JacksonXmlProperty({{#isXmlAttribute}}isAttribute = true, {{/isXmlAttribute}}{{#xmlNamespace}}namespace = "{{xmlNamespace}}", {{/xmlNamespace}}localName = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}") + @{{{vendorExtensions.fieldAnnPrefix}}}JacksonXmlProperty({{#isXmlAttribute}}isAttribute = true, {{/isXmlAttribute}}{{#xmlNamespace}}namespace = "{{xmlNamespace}}", {{/xmlNamespace}}localName = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}") + @{{{vendorExtensions.fieldAnnPrefix}}}JacksonXmlProperty({{#isXmlAttribute}}isAttribute = true, {{/isXmlAttribute}}{{#xmlNamespace}}namespace = "{{xmlNamespace}}", {{/xmlNamespace}}localName = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}") {{/isContainer}} {{#isContainer}} {{#isXmlWrapped}} // items.xmlName={{items.xmlName}} - @field:JacksonXmlElementWrapper(useWrapping = {{isXmlWrapped}}, {{#xmlNamespace}}namespace = "{{xmlNamespace}}", {{/xmlNamespace}}localName = "{{#items.xmlName}}{{items.xmlName}}{{/items.xmlName}}{{^items.xmlName}}{{items.baseName}}{{/items.xmlName}}") + @{{{vendorExtensions.fieldAnnPrefix}}}JacksonXmlElementWrapper(useWrapping = {{isXmlWrapped}}, {{#xmlNamespace}}namespace = "{{xmlNamespace}}", {{/xmlNamespace}}localName = "{{#items.xmlName}}{{items.xmlName}}{{/items.xmlName}}{{^items.xmlName}}{{items.baseName}}{{/items.xmlName}}") {{/isXmlWrapped}} {{/isContainer}} {{/withXml}} @@ -39,12 +39,12 @@ {{^micronaut_serde_jackson}} {{#isDateTime}} {{#datetimeFormat}} - @field:JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "{{{datetimeFormat}}}") + @{{{vendorExtensions.fieldAnnPrefix}}}JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "{{{datetimeFormat}}}") {{/datetimeFormat}} {{/isDateTime}} {{#isDate}} {{#dateFormat}} - @field:JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "{{{dateFormat}}}") + @{{{vendorExtensions.fieldAnnPrefix}}}JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "{{{dateFormat}}}") {{/dateFormat}} {{/isDate}} {{/micronaut_serde_jackson}} @@ -52,12 +52,12 @@ {{#micronaut_serde_jackson}} {{#isDateTime}} {{#datatimeFormat}} - @field:JsonFormat(pattern = "{{{datetimeFormat}}}") + @{{{vendorExtensions.fieldAnnPrefix}}}JsonFormat(pattern = "{{{datetimeFormat}}}") {{/datatimeFormat}} {{/isDateTime}} {{#isDate}} {{#dateFormat}} - @field:JsonFormat(pattern = "{{{dateFormat}}}") + @{{{vendorExtensions.fieldAnnPrefix}}}JsonFormat(pattern = "{{{dateFormat}}}") {{/dateFormat}} {{/isDate}} {{/micronaut_serde_jackson}} diff --git a/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/params/validation_field.mustache b/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/params/validation_field.mustache index cd20356173..02d2d1d70d 100644 --- a/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/params/validation_field.mustache +++ b/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/params/validation_field.mustache @@ -1,15 +1,15 @@ {{#isNullable}} - @field:Nullable + @{{{vendorExtensions.fieldAnnPrefix}}}Nullable {{/isNullable}} {{^isNullable}} {{#required}} {{#isReadOnly}} - @field:Nullable + @{{{vendorExtensions.fieldAnnPrefix}}}Nullable {{/isReadOnly}} {{/required}} {{^required}} - @field:Nullable + @{{{vendorExtensions.fieldAnnPrefix}}}Nullable {{/required}} {{/isNullable}} {{!All the validation}} @@ -17,75 +17,75 @@ {{^isNullable}} {{#required}} {{^isReadOnly}} - @field:NotNull + @{{{vendorExtensions.fieldAnnPrefix}}}NotNull {{/isReadOnly}} {{/required}} {{/isNullable}} {{!Validate all pojos and enums}} {{^isContainer}} {{#isModel}} - @field:Valid + @{{{vendorExtensions.fieldAnnPrefix}}}Valid {{/isModel}} {{/isContainer}} {{!Pattern}} {{#pattern}} {{^isByteArray}} - @field:Pattern(regexp = "{{{pattern}}}") + @{{{vendorExtensions.fieldAnnPrefix}}}Pattern(regexp = "{{{pattern}}}") {{/isByteArray}} {{/pattern}} {{!Min length && max length}} {{#minLength}} {{#maxLength}} - @field:Size(min = {{minLength}}, max = {{maxLength}}) + @{{{vendorExtensions.fieldAnnPrefix}}}Size(min = {{minLength}}, max = {{maxLength}}) {{/maxLength}} {{/minLength}} {{#minLength}}{{^maxLength}} - @field:Size(min = {{minLength}}) + @{{{vendorExtensions.fieldAnnPrefix}}}Size(min = {{minLength}}) {{/maxLength}}{{/minLength}} {{^minLength}}{{#maxLength}} - @field:Size(max = {{maxLength}}) + @{{{vendorExtensions.fieldAnnPrefix}}}Size(max = {{maxLength}}) {{/maxLength}}{{/minLength}} {{!Size}} {{#minItems}}{{#maxItems}} - @field:Size(min = {{minItems}}, max = {{maxItems}}) + @{{{vendorExtensions.fieldAnnPrefix}}}Size(min = {{minItems}}, max = {{maxItems}}) {{/maxItems}}{{/minItems}} {{#minItems}}{{^maxItems}} - @field:Size(min = {{minItems}}) + @{{{vendorExtensions.fieldAnnPrefix}}}Size(min = {{minItems}}) {{/maxItems}}{{/minItems}} {{^minItems}}{{#maxItems}} - @field:Size(max = {{maxItems}}) + @{{{vendorExtensions.fieldAnnPrefix}}}Size(max = {{maxItems}}) {{/maxItems}}{{/minItems}} {{!Email}} {{#isEmail}} - @field:Email + @{{{vendorExtensions.fieldAnnPrefix}}}Email {{/isEmail}} {{!check for integer or long / all others=decimal type with @Decimal isInteger set}} {{#isInteger}} {{#minimum}} - @field:Min({{minimum}}) + @{{{vendorExtensions.fieldAnnPrefix}}}Min({{minimum}}) {{/minimum}} {{#maximum}} - @field:Max({{maximum}}) + @{{{vendorExtensions.fieldAnnPrefix}}}Max({{maximum}}) {{/maximum}} {{/isInteger}} {{!isLong set}} {{#isLong}} {{#minimum}} - @field:Min({{minimum}}L) + @{{{vendorExtensions.fieldAnnPrefix}}}Min({{minimum}}L) {{/minimum}} {{#maximum}} - @field:Max({{maximum}}L) + @{{{vendorExtensions.fieldAnnPrefix}}}Max({{maximum}}L) {{/maximum}} {{/isLong}} {{!Not Integer, not Long => we have a decimal value!}} {{^isInteger}} {{^isLong}}{{!minimum for decimal value}} {{#minimum}} - @field:DecimalMin({{#exclusiveMinimum}}value = {{/exclusiveMinimum}}"{{minimum}}"{{#exclusiveMinimum}}, inclusive = false{{/exclusiveMinimum}}) + @{{{vendorExtensions.fieldAnnPrefix}}}DecimalMin({{#exclusiveMinimum}}value = {{/exclusiveMinimum}}"{{minimum}}"{{#exclusiveMinimum}}, inclusive = false{{/exclusiveMinimum}}) {{/minimum}} {{!maximal for decimal value}} {{#maximum}} - @field:DecimalMax({{#exclusiveMaximum}}value = {{/exclusiveMaximum}}"{{maximum}}"{{#exclusiveMaximum}}, inclusive = false{{/exclusiveMaximum}}) + @{{{vendorExtensions.fieldAnnPrefix}}}DecimalMax({{#exclusiveMaximum}}value = {{/exclusiveMaximum}}"{{maximum}}"{{#exclusiveMaximum}}, inclusive = false{{/exclusiveMaximum}}) {{/maximum}} {{/isLong}} {{/isInteger}} diff --git a/test-suite-generator-util/src/main/java/io/micronaut/openapi/testsuite/GeneratorMain.java b/test-suite-generator-util/src/main/java/io/micronaut/openapi/testsuite/GeneratorMain.java index cd7db9c1b8..cc0be0bbb8 100644 --- a/test-suite-generator-util/src/main/java/io/micronaut/openapi/testsuite/GeneratorMain.java +++ b/test-suite-generator-util/src/main/java/io/micronaut/openapi/testsuite/GeneratorMain.java @@ -54,6 +54,7 @@ public static void main(String[] args) throws URISyntaxException { boolean server = "server".equals(args[0]); var lang = GeneratorLanguage.valueOf(args[6].toUpperCase()); var generatedAnnotation = Boolean.parseBoolean(args[7]); + var ksp = Boolean.parseBoolean(args[8]); List parameterMappings = parseParameterMappings(args[4]); List responseBodyMappings = @@ -88,6 +89,7 @@ public static void main(String[] args) throws URISyntaxException { // because we generate both abstract classes _and_ dummy implementations serverOptions.withGenerateImplementationFiles(false) .withAuthentication(false) + .withKsp(ksp) .withGeneratedAnnotation(generatedAnnotation); }); } else { @@ -103,7 +105,8 @@ public static void main(String[] args) throws URISyntaxException { } else { if (lang == GeneratorLanguage.KOTLIN) { builder.forKotlinClient(client -> { - client.withGeneratedAnnotation(generatedAnnotation); + client.withGeneratedAnnotation(generatedAnnotation) + .withKsp(ksp); }); } else { builder.forJavaClient(client -> { diff --git a/test-suite-kotlin-kapt-client-generator/build.gradle b/test-suite-kotlin-kapt-client-generator/build.gradle index 6c0eecc9c9..69829d5225 100644 --- a/test-suite-kotlin-kapt-client-generator/build.gradle +++ b/test-suite-kotlin-kapt-client-generator/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'io.micronaut.build.internal.openapi-kotlin-generator-test-suite' + id 'io.micronaut.build.internal.openapi-kotlin-kapt-generator-test-suite' alias(libs.plugins.kotlin.jvm) alias(libs.plugins.kotlin.kapt) } diff --git a/test-suite-kotlin-kapt-server-generator/build.gradle b/test-suite-kotlin-kapt-server-generator/build.gradle index 2ff2489e6b..883b55a738 100644 --- a/test-suite-kotlin-kapt-server-generator/build.gradle +++ b/test-suite-kotlin-kapt-server-generator/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'io.micronaut.build.internal.openapi-kotlin-generator-test-suite' + id 'io.micronaut.build.internal.openapi-kotlin-kapt-generator-test-suite' alias(libs.plugins.kotlin.jvm) alias(libs.plugins.kotlin.kapt) } diff --git a/test-suite-kotlin-ksp-client-generator/build.gradle b/test-suite-kotlin-ksp-client-generator/build.gradle index 55c1b01267..089d31ffc9 100644 --- a/test-suite-kotlin-ksp-client-generator/build.gradle +++ b/test-suite-kotlin-ksp-client-generator/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'io.micronaut.build.internal.openapi-kotlin-generator-test-suite' + id 'io.micronaut.build.internal.openapi-kotlin-ksp-generator-test-suite' alias(libs.plugins.kotlin.jvm) alias(libs.plugins.kotlin.ksp) } diff --git a/test-suite-kotlin-ksp-client-generator/petstore.json b/test-suite-kotlin-ksp-client-generator/petstore.json index db56969e6f..af44e1734f 100644 --- a/test-suite-kotlin-ksp-client-generator/petstore.json +++ b/test-suite-kotlin-ksp-client-generator/petstore.json @@ -208,7 +208,7 @@ "pet" ], "summary": "Find pet by ID", - "description": "Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error conditions", + "description": "Returns a pet when ID < 10. ID > 10 or non-integers will simulate API error conditions", "operationId": "getPetById", "produces": [ "application/json", @@ -506,7 +506,7 @@ "store" ], "summary": "Delete purchase order by ID", - "description": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors", + "description": "For valid response try integer IDs with value < 1000. Anything above 1000 or non-integers will generate API errors", "operationId": "deleteOrder", "produces": [ "application/json", diff --git a/test-suite-kotlin-ksp-server-generator/build.gradle b/test-suite-kotlin-ksp-server-generator/build.gradle index 158641c177..9322a96010 100644 --- a/test-suite-kotlin-ksp-server-generator/build.gradle +++ b/test-suite-kotlin-ksp-server-generator/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'io.micronaut.build.internal.openapi-kotlin-generator-test-suite' + id 'io.micronaut.build.internal.openapi-kotlin-ksp-generator-test-suite' alias(libs.plugins.kotlin.jvm) alias(libs.plugins.kotlin.ksp) } diff --git a/test-suite-kotlin-ksp-server-generator/spec.yaml b/test-suite-kotlin-ksp-server-generator/spec.yaml index 4c1778315d..3328d4fb79 100644 --- a/test-suite-kotlin-ksp-server-generator/spec.yaml +++ b/test-suite-kotlin-ksp-server-generator/spec.yaml @@ -43,7 +43,7 @@ paths: schema: $ref: '#/components/schemas/SendPrimitivesResponse' default: - $ref: '#/responses/Error' + $ref: '#/components/responses/Error' /sendValidatedPrimitives: get: operationId: sendValidatedPrimitives @@ -86,7 +86,7 @@ paths: schema: type: string default: - $ref: '#/responses/Error' + $ref: '#/components/responses/Error' /sendDates: get: operationId: sendDates @@ -111,7 +111,7 @@ paths: schema: $ref: '#/components/schemas/SendDatesResponse' default: - $ref: '#/responses/Error' + $ref: '#/components/responses/Error' /sendParameterEnum: get: operationId: sendParameterEnum @@ -234,7 +234,7 @@ paths: schema: $ref: '#/components/schemas/SimpleModel' default: - $ref: '#/responses/Error' + $ref: '#/components/responses/Error' /sendListOfSimpleModels: post: operationId: sendListOfSimpleModels @@ -276,7 +276,7 @@ paths: schema: $ref: '#/components/schemas/ModelWithRequiredProperties' default: - $ref: '#/responses/Error' + $ref: '#/components/responses/Error' /sendDateModel: post: operationId: sendDateModel @@ -295,7 +295,7 @@ paths: schema: $ref: '#/components/schemas/DateModel' default: - $ref: '#/responses/Error' + $ref: '#/components/responses/Error' /sendEnum: post: operationId: sendEnum @@ -315,7 +315,7 @@ paths: schema: $ref: '#/components/schemas/ColorEnum' default: - $ref: '#/responses/Error' + $ref: '#/components/responses/Error' /sendEnumList: post: operationId: sendEnumList @@ -339,7 +339,7 @@ paths: items: $ref: '#/components/schemas/ColorEnum' default: - $ref: '#/responses/Error' + $ref: '#/components/responses/Error' /sendModelWithMapProperty: post: operationId: sendModelWithMapProperty @@ -391,7 +391,7 @@ paths: schema: $ref: '#/components/schemas/NestedModel' default: - $ref: '#/responses/Error' + $ref: '#/components/responses/Error' /sendModelWithInnerEnum: post: operationId: sendModelWithInnerEnum @@ -411,7 +411,7 @@ paths: schema: $ref: '#/components/schemas/ModelWithInnerEnum' default: - $ref: '#/responses/Error' + $ref: '#/components/responses/Error' /sendModelWithEnumList: post: operationId: sendModelWithEnumList @@ -431,7 +431,7 @@ paths: schema: $ref: '#/components/schemas/ModelWithEnumList' default: - $ref: '#/responses/Error' + $ref: '#/components/responses/Error' /sendModelWithDiscriminator: put: operationId: sendModelWithDiscriminator @@ -474,7 +474,7 @@ paths: type: string format: byte default: - $ref: '#/responses/Error' + $ref: '#/components/responses/Error' /sendBytes: put: operationId: sendBytes @@ -507,7 +507,7 @@ paths: schema: $ref: '#/components/schemas/SimpleModel' default: - $ref: '#/responses/Error' + $ref: '#/components/responses/Error' /getDateTime: get: operationId: getDateTime @@ -522,7 +522,7 @@ paths: type: string format: date-time default: - $ref: '#/responses/Error' + $ref: '#/components/responses/Error' /getDateModel: get: operationId: getDateModel @@ -536,7 +536,7 @@ paths: schema: $ref: '#/components/schemas/DateModel' default: - $ref: '#/responses/Error' + $ref: '#/components/responses/Error' /getSimpleModelWithNonStandardStatus: get: operationId: getSimpleModelWithNonStandardStatus @@ -550,7 +550,7 @@ paths: schema: $ref: '#/components/schemas/SimpleModel' default: - $ref: '#/responses/Error' + $ref: '#/components/responses/Error' /getModelWithValidatedList: get: operationId: getModelWithValidatedList @@ -564,7 +564,7 @@ paths: schema: $ref: '#/components/schemas/ModelWithValidatedListProperty' default: - $ref: '#/responses/Error' + $ref: '#/components/responses/Error' /getPaginatedSimpleModel: get: operationId: getPaginatedSimpleModel @@ -623,7 +623,7 @@ paths: schema: $ref: '#/components/schemas/SimpleModel' default: - $ref: '#/responses/Error' + $ref: '#/components/responses/Error' /getDatedSimpleModelWithNonMappedHeader: get: operationId: getDatedSimpleModelWithNonMappedHeader @@ -669,7 +669,7 @@ paths: type: string format: binary default: - $ref: '#/responses/Error' + $ref: '#/components/responses/Error' components: schemas: