Skip to content

Commit

Permalink
Add ksp flag to kotlin generator to generate only KSP compatible clas…
Browse files Browse the repository at this point in the history
…ses. Fixed test swagger files
  • Loading branch information
altro3 committed Feb 4, 2024
1 parent 7ff25fb commit 81cc746
Show file tree
Hide file tree
Showing 21 changed files with 184 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand Down
Original file line number Diff line number Diff line change
@@ -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()}")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ public abstract class OpenApiGeneratorTask extends DefaultTask {
@Input
public abstract Property<Boolean> getGeneratedAnnotation();

@Input
public abstract Property<Boolean> getKsp();

@OutputDirectory
public abstract DirectoryProperty getOutputDirectory();

Expand Down Expand Up @@ -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());
Expand All @@ -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);
});
}
Expand Down
3 changes: 2 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ public abstract class AbstractMicronautKotlinCodegen<T extends GeneratorOptionsB
public static final String OPT_GENERATE_SWAGGER_ANNOTATIONS_TRUE = "true";
public static final String OPT_GENERATE_SWAGGER_ANNOTATIONS_FALSE = "false";
public static final String OPT_GENERATE_OPERATION_ONLY_FOR_FIRST_TAG = "generateOperationOnlyForFirstTag";
public static final String OPT_KSP = "ksp";
public static final String CONTENT_TYPE_APPLICATION_FORM_URLENCODED = "application/x-www-form-urlencoded";
public static final String CONTENT_TYPE_APPLICATION_JSON = "application/json";
public static final String CONTENT_TYPE_MULTIPART_FORM_DATA = "multipart/form-data";
Expand All @@ -124,6 +125,7 @@ public abstract class AbstractMicronautKotlinCodegen<T extends GeneratorOptionsB
protected boolean reactive;
protected boolean generateHttpResponseAlways;
protected boolean generateHttpResponseWhereRequired = true;
protected boolean ksp;
protected String appName;
protected String generateSwaggerAnnotations;
protected boolean generateOperationOnlyForFirstTag;
Expand Down Expand Up @@ -238,6 +240,7 @@ protected AbstractMicronautKotlinCodegen() {
cliOptions.add(CliOption.newBoolean(OPT_USE_PLURAL, "Whether or not to use plural for request body parameter name", plural));
cliOptions.add(CliOption.newBoolean(OPT_FLUX_FOR_ARRAYS, "Whether or not to use Flux<?> instead Mono<List<?>> 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));
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand All @@ -265,7 +272,8 @@ ClientOptions build() {
useAuth,
plural,
fluxForArrays,
generatedAnnotation
generatedAnnotation,
ksp
);
}
}
Expand All @@ -278,7 +286,8 @@ record ClientOptions(
boolean useAuth,
boolean plural,
boolean fluxForArrays,
boolean generatedAnnotation
boolean generatedAnnotation,
boolean ksp
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand All @@ -369,7 +375,8 @@ ServerOptions build() {
plural,
fluxForArrays,
generatedAnnotation,
aot
aot,
ksp
);
}
}
Expand All @@ -383,7 +390,8 @@ record ServerOptions(
boolean plural,
boolean fluxForArrays,
boolean generatedAnnotation,
boolean aot
boolean aot,
boolean ksp
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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());
Expand Down
Original file line number Diff line number Diff line change
@@ -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}}
Expand Down
Loading

0 comments on commit 81cc746

Please sign in to comment.