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 e973b7ecfc..7a603f3bab 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 @@ -28,12 +28,14 @@ import org.gradle.api.file.DirectoryProperty; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.MapProperty; import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; import org.gradle.api.tasks.Classpath; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.OutputDirectory; import org.gradle.api.tasks.PathSensitive; import org.gradle.api.tasks.PathSensitivity; @@ -89,6 +91,26 @@ public Provider getGeneratedTestSourcesDirectory() { @Input public abstract ListProperty> getResponseBodyMappings(); + @Optional + @Input + public abstract MapProperty getNameMapping(); + + @Optional + @Input + public abstract Property getApiNamePrefix(); + + @Optional + @Input + public abstract Property getApiNameSuffix(); + + @Optional + @Input + public abstract Property getModelNamePrefix(); + + @Optional + @Input + public abstract Property getModelNameSuffix(); + @Inject protected abstract ExecOperations getExecOperations(); @@ -99,6 +121,9 @@ public void execute() throws IOException { var lang = getLang().get(); var generatedAnnotation = getGeneratedAnnotation().get(); var ksp = getKsp().get(); + + var apiPrefix = + Files.createDirectories(generatedSourcesDir.toPath()); Files.createDirectories(generatedTestSourcesDir.toPath()); getProject().getLogger().info("json: {}", getParameterMappings().get()); @@ -115,6 +140,11 @@ public void execute() throws IOException { args.add(lang.toUpperCase()); args.add(Boolean.toString(generatedAnnotation)); args.add(Boolean.toString(ksp)); + args.add(getNameMapping().get().toString()); + args.add(getApiNamePrefix().isPresent() ? getApiNamePrefix().get() : ""); + args.add(getApiNameSuffix().isPresent() ? getApiNameSuffix().get() : ""); + args.add(getModelNamePrefix().isPresent() ? getModelNamePrefix().get() : ""); + args.add(getModelNameSuffix().isPresent() ? getModelNameSuffix().get() : ""); javaexec.args(args); }); } diff --git a/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautJavaCodegen.java b/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautJavaCodegen.java index aa6b7487c4..ba9834cd18 100644 --- a/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautJavaCodegen.java +++ b/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautJavaCodegen.java @@ -129,6 +129,7 @@ protected AbstractMicronautJavaCodegen() { // CHECKSTYLE:OFF // Set all the fields useBeanValidation = true; + useJakartaEe = true; useOptional = false; visitable = false; testTool = OPT_TEST_JUNIT; @@ -290,6 +291,30 @@ public void setApiPackage(String apiPackage) { updateOption(CodegenConstants.API_PACKAGE, apiPackage); } + @Override + public void setApiNamePrefix(String apiNamePrefix) { + super.setApiNamePrefix(apiNamePrefix); + updateOption(CodegenConstants.API_NAME_PREFIX, apiNamePrefix); + } + + @Override + public void setApiNameSuffix(String apiNameSuffix) { + super.setApiNameSuffix(apiNameSuffix); + updateOption(CodegenConstants.API_NAME_SUFFIX, apiNameSuffix); + } + + @Override + public void setModelNamePrefix(String modelNamePrefix) { + super.setModelNamePrefix(modelNamePrefix); + updateOption(CodegenConstants.MODEL_NAME_PREFIX, modelNamePrefix); + } + + @Override + public void setModelNameSuffix(String modelNameSuffix) { + super.setModelNameSuffix(modelNameSuffix); + updateOption(CodegenConstants.MODEL_NAME_SUFFIX, modelNameSuffix); + } + @Override public void setInvokerPackage(String invokerPackage) { super.setInvokerPackage(invokerPackage); @@ -481,6 +506,42 @@ public void addResponseBodyMappings(List responseBodyMappin this.responseBodyMappings.addAll(responseBodyMappings); } + public void addSchemaMapping(Map schemaMapping) { + this.schemaMapping.putAll(schemaMapping); + } + + public void addImportMapping(Map importMapping) { + this.importMapping.putAll(importMapping); + } + + public void addNameMapping(Map nameMapping) { + this.nameMapping.putAll(nameMapping); + } + + public void addTypeMapping(Map typeMapping) { + this.typeMapping.putAll(typeMapping); + } + + public void addEnumNameMapping(Map enumNameMapping) { + this.enumNameMapping.putAll(enumNameMapping); + } + + public void addModelNameMapping(Map modelNameMapping) { + this.modelNameMapping.putAll(modelNameMapping); + } + + public void addInlineSchemaNameMapping(Map inlineSchemaNameMapping) { + this.inlineSchemaNameMapping.putAll(inlineSchemaNameMapping); + } + + public void addInlineSchemaOption(Map inlineSchemaOption) { + this.inlineSchemaOption.putAll(inlineSchemaOption); + } + + public void addOpenapiNormalizer(Map openapiNormalizer) { + this.openapiNormalizer.putAll(openapiNormalizer); + } + // CHECKSTYLE:OFF private void maybeSetSwagger() { if (additionalProperties.containsKey(OPT_GENERATE_SWAGGER_ANNOTATIONS)) { 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 8b0e8ffd43..3fc78b59ff 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 @@ -554,6 +554,42 @@ public void addResponseBodyMappings(List responseBodyMappin this.responseBodyMappings.addAll(responseBodyMappings); } + public void addSchemaMapping(Map schemaMapping) { + this.schemaMapping.putAll(schemaMapping); + } + + public void addImportMapping(Map importMapping) { + this.importMapping.putAll(importMapping); + } + + public void addNameMapping(Map nameMapping) { + this.nameMapping.putAll(nameMapping); + } + + public void addTypeMapping(Map typeMapping) { + this.typeMapping.putAll(typeMapping); + } + + public void addEnumNameMapping(Map enumNameMapping) { + this.enumNameMapping.putAll(enumNameMapping); + } + + public void addModelNameMapping(Map modelNameMapping) { + this.modelNameMapping.putAll(modelNameMapping); + } + + public void addInlineSchemaNameMapping(Map inlineSchemaNameMapping) { + this.inlineSchemaNameMapping.putAll(inlineSchemaNameMapping); + } + + public void addInlineSchemaOption(Map inlineSchemaOption) { + this.inlineSchemaOption.putAll(inlineSchemaOption); + } + + public void addOpenapiNormalizer(Map openapiNormalizer) { + this.openapiNormalizer.putAll(openapiNormalizer); + } + // CHECKSTYLE:OFF private void maybeSetSwagger() { if (additionalProperties.containsKey(OPT_GENERATE_SWAGGER_ANNOTATIONS)) { @@ -812,7 +848,7 @@ public String getTypeDeclaration(Schema p) { if (ModelUtils.isMapSchema(target)) { // Note: ModelUtils.isMapSchema(p) returns true when p is a composed schema that also defines - // additionalproperties: true + // additionalProperties: true Schema inner = ModelUtils.getAdditionalProperties(target); if (inner == null) { System.err.println("`" + p.getName() + "` (map property) does not have a proper inner type defined. Default to type:string"); diff --git a/openapi-generator/src/main/java/io/micronaut/openapi/generator/JavaMicronautClientCodegen.java b/openapi-generator/src/main/java/io/micronaut/openapi/generator/JavaMicronautClientCodegen.java index 95e6a32d39..1b3146ce70 100644 --- a/openapi-generator/src/main/java/io/micronaut/openapi/generator/JavaMicronautClientCodegen.java +++ b/openapi-generator/src/main/java/io/micronaut/openapi/generator/JavaMicronautClientCodegen.java @@ -51,7 +51,7 @@ public class JavaMicronautClientCodegen extends AbstractMicronautJavaCodegen parameterMappings; private List responseBodyMappings; + private Map schemaMapping; + private Map importMapping; + private Map nameMapping; + private Map typeMapping; + private Map enumNameMapping; + private Map modelNameMapping; + private Map inlineSchemaNameMapping; + private Map inlineSchemaOption; + private Map openapiNormalizer; + + private String apiNamePrefix; + private String apiNameSuffix; + private String modelNamePrefix; + private String modelNameSuffix; + private boolean optional; private boolean reactive = true; private boolean generateHttpResponseAlways; @@ -464,6 +560,84 @@ public MicronautCodeGeneratorOptionsBuilder withResponseBodyMappings(List schemaMapping) { + this.schemaMapping = schemaMapping; + return this; + } + + @Override + public MicronautCodeGeneratorOptionsBuilder withImportMapping(Map importMapping) { + this.importMapping = importMapping; + return this; + } + + @Override + public MicronautCodeGeneratorOptionsBuilder withNameMapping(Map nameMapping) { + this.nameMapping = nameMapping; + return this; + } + + @Override + public MicronautCodeGeneratorOptionsBuilder withTypeMapping(Map typeMapping) { + this.typeMapping = typeMapping; + return this; + } + + @Override + public MicronautCodeGeneratorOptionsBuilder withEnumNameMapping(Map enumNameMapping) { + this.enumNameMapping = enumNameMapping; + return this; + } + + @Override + public MicronautCodeGeneratorOptionsBuilder withModelNameMapping(Map modelNameMapping) { + this.modelNameMapping = modelNameMapping; + return this; + } + + @Override + public MicronautCodeGeneratorOptionsBuilder withInlineSchemaNameMapping(Map inlineSchemaNameMapping) { + this.inlineSchemaNameMapping = inlineSchemaNameMapping; + return this; + } + + @Override + public MicronautCodeGeneratorOptionsBuilder withInlineSchemaOption(Map inlineSchemaOption) { + this.inlineSchemaOption = inlineSchemaOption; + return this; + } + + @Override + public MicronautCodeGeneratorOptionsBuilder withOpenapiNormalizer(Map openapiNormalizer) { + this.openapiNormalizer = openapiNormalizer; + return this; + } + + @Override + public MicronautCodeGeneratorOptionsBuilder withApiNamePrefix(String apiNamePrefix) { + this.apiNamePrefix = apiNamePrefix; + return this; + } + + @Override + public MicronautCodeGeneratorOptionsBuilder withApiNameSuffix(String apiNameSuffix) { + this.apiNameSuffix = apiNameSuffix; + return this; + } + + @Override + public MicronautCodeGeneratorOptionsBuilder withModelNamePrefix(String modelNamePrefix) { + this.modelNamePrefix = modelNamePrefix; + return this; + } + + @Override + public MicronautCodeGeneratorOptionsBuilder withModelNameSuffix(String modelNameSuffix) { + this.modelNameSuffix = modelNameSuffix; + return this; + } + @Override public MicronautCodeGeneratorOptionsBuilder withReactive(boolean reactive) { this.reactive = reactive; @@ -513,7 +687,37 @@ public MicronautCodeGeneratorOptionsBuilder withDateTimeFormat(DateTimeFormat fo } private Options build() { - return new Options(lang, apiPackage, modelPackage, invokerPackage, artifactId, parameterMappings, responseBodyMappings, beanValidation, optional, reactive, generateHttpResponseAlways, generateHttpResponseWhereRequired, testFramework, serializationLibraryKind, dateTimeFormat); + return new Options( + lang, + apiPackage, + modelPackage, + invokerPackage, + artifactId, + parameterMappings, + responseBodyMappings, + schemaMapping, + importMapping, + nameMapping, + typeMapping, + enumNameMapping, + modelNameMapping, + inlineSchemaNameMapping, + inlineSchemaOption, + openapiNormalizer, + + apiNamePrefix, + apiNameSuffix, + modelNamePrefix, + modelNameSuffix, + + beanValidation, + optional, + reactive, + generateHttpResponseAlways, + generateHttpResponseWhereRequired, + testFramework, + serializationLibraryKind, + dateTimeFormat); } } } @@ -542,6 +746,21 @@ private record Options( String artifactId, List parameterMappings, List responseBodyMappings, + Map schemaMapping, + Map importMapping, + Map nameMapping, + Map typeMapping, + Map enumNameMapping, + Map modelNameMapping, + Map inlineSchemaNameMapping, + Map inlineSchemaOption, + Map openapiNormalizer, + + String apiNamePrefix, + String apiNameSuffix, + String modelNamePrefix, + String modelNameSuffix, + boolean beanValidation, boolean optional, boolean reactive, diff --git a/openapi-generator/src/main/java/io/micronaut/openapi/generator/MicronautCodeGeneratorOptionsBuilder.java b/openapi-generator/src/main/java/io/micronaut/openapi/generator/MicronautCodeGeneratorOptionsBuilder.java index 1119558a4a..21db670b3c 100644 --- a/openapi-generator/src/main/java/io/micronaut/openapi/generator/MicronautCodeGeneratorOptionsBuilder.java +++ b/openapi-generator/src/main/java/io/micronaut/openapi/generator/MicronautCodeGeneratorOptionsBuilder.java @@ -16,6 +16,7 @@ package io.micronaut.openapi.generator; import java.util.List; +import java.util.Map; /** * Builder for generic options that the Micronaut code generator supports. @@ -79,6 +80,109 @@ public interface MicronautCodeGeneratorOptionsBuilder { */ MicronautCodeGeneratorOptionsBuilder withResponseBodyMappings(List responseBodyMappings); + /** + * Add the schema mappings. + * + * @param schemaMapping the schema mappings + * @return this builder + */ + MicronautCodeGeneratorOptionsBuilder withSchemaMapping(Map schemaMapping); + + /** + * Add the import mappings. + * + * @param importMapping the import mappings + * @return this builder + */ + MicronautCodeGeneratorOptionsBuilder withImportMapping(Map importMapping); + + /** + * Add the name mappings. + * + * @param nameMapping the name mappings + * @return this builder + */ + MicronautCodeGeneratorOptionsBuilder withNameMapping(Map nameMapping); + + /** + * Add the type mappings. + * + * @param typeMapping the type mappings + * @return this builder + */ + MicronautCodeGeneratorOptionsBuilder withTypeMapping(Map typeMapping); + + /** + * Add the enum name mappings. + * + * @param enumNameMapping the enum name mappings + * @return this builder + */ + MicronautCodeGeneratorOptionsBuilder withEnumNameMapping(Map enumNameMapping); + + /** + * Add the model name mappings. + * + * @param modelNameMapping the model name mappings + * @return this builder + */ + MicronautCodeGeneratorOptionsBuilder withModelNameMapping(Map modelNameMapping); + + /** + * Add the inline schema name mappings. + * + * @param inlineSchemaNameMapping the inline schema name mappings + * @return this builder + */ + MicronautCodeGeneratorOptionsBuilder withInlineSchemaNameMapping(Map inlineSchemaNameMapping); + + /** + * Add the inline schema options. + * + * @param inlineSchemaOption the inline schema options + * @return this builder + */ + MicronautCodeGeneratorOptionsBuilder withInlineSchemaOption(Map inlineSchemaOption); + + /** + * Add the OpenAPi normalizer options. + * + * @param openapiNormalizer the OpenAPi normalizer options + * @return this builder + */ + MicronautCodeGeneratorOptionsBuilder withOpenapiNormalizer(Map openapiNormalizer); + + /** + * Set the api name prefix. + * + * @param apiNamePrefix the api name prefix + * @return this builder + */ + MicronautCodeGeneratorOptionsBuilder withApiNamePrefix(String apiNamePrefix); + + /** + * Set the api name suffix. + * + * @param apiNameSuffix the api name suffix + * @return this builder + */ + MicronautCodeGeneratorOptionsBuilder withApiNameSuffix(String apiNameSuffix); + + /** + * Set the model name prefix. + * + * @param modelNamePrefix the model name prefix + * @return this builder + */ + MicronautCodeGeneratorOptionsBuilder withModelNamePrefix(String modelNamePrefix); + + /** + * Set the model name suffix. + * + * @param modelNameSuffix the model name suffix + * @return this builder + */ + MicronautCodeGeneratorOptionsBuilder withModelNameSuffix(String modelNameSuffix); /** * If set to true, the generator will use reactive types. 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 cc0be0bbb8..df9f2b601c 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 @@ -60,6 +60,13 @@ public static void main(String[] args) throws URISyntaxException { List responseBodyMappings = parseResponseBodyMappings(args[5]); + Map nameMapping = parseNameMapping(args[9]); + + String apiPrefix = args[10]; + String apiSuffix = args[11]; + String modelPrefix = args[12]; + String modelSuffix = args[13]; + MicronautCodeGeneratorEntryPoint.OutputKind[] outputKinds = Arrays.stream(args[3].split(",")) .map(MicronautCodeGeneratorEntryPoint.OutputKind::of) @@ -77,9 +84,14 @@ public static void main(String[] args) throws URISyntaxException { .withBeanValidation(true) .withOptional(true) .withReactive(true) + .withApiNamePrefix(apiPrefix) + .withApiNameSuffix(apiSuffix) + .withModelNamePrefix(modelPrefix) + .withModelNameSuffix(modelSuffix) .withTestFramework(lang == JAVA ? MicronautCodeGeneratorEntryPoint.TestFramework.SPOCK : MicronautCodeGeneratorEntryPoint.TestFramework.JUNIT5) .withParameterMappings(parameterMappings) - .withResponseBodyMappings(responseBodyMappings); + .withResponseBodyMappings(responseBodyMappings) + .withNameMapping(nameMapping); }); if (server) { if (lang == GeneratorLanguage.KOTLIN) { @@ -136,6 +148,10 @@ private static List parseResponseBodyMappings(String string )).toList(); } + private static Map parseNameMapping(String string) { + return parseMap(string); + } + private static List> parseListOfMaps(String string) { List> result = new ArrayList<>(); if (string.isBlank()) { @@ -183,4 +199,36 @@ private static List> parseListOfMaps(String string) { return result; } + + private static Map parseMap(String string) { + var result = new HashMap(); + if (string.isBlank()) { + return result; + } + + assert string.charAt(0) == '{'; + int i = 1; + + int endIndex = string.indexOf('}', i); + + while (i < endIndex) { + if (string.charAt(i) == ' ') { + ++i; + } + int nameIndex = string.indexOf('=', i); + String name = string.substring(i, nameIndex); + i = nameIndex + 1; + int valueIndex = string.indexOf(',', i); + if (endIndex < valueIndex || valueIndex == -1) { + valueIndex = endIndex; + } + String value = string.substring(i, valueIndex); + i = valueIndex + 1; + + result.put(name, value); + } + assert i == string.length() - 1; + + return result; + } } diff --git a/test-suite-java-server-generator/build.gradle b/test-suite-java-server-generator/build.gradle index 40d3488b5f..1e41665221 100644 --- a/test-suite-java-server-generator/build.gradle +++ b/test-suite-java-server-generator/build.gradle @@ -68,4 +68,5 @@ tasks.named("generateOpenApi") { // Ignored header - Does not wrap the response in HttpResponse [headerName: "ignored-header"] ] + nameMapping = [test: "changedTest"] } diff --git a/test-suite-java-server-generator/spec.yaml b/test-suite-java-server-generator/spec.yaml index 3328d4fb79..befec924ef 100644 --- a/test-suite-java-server-generator/spec.yaml +++ b/test-suite-java-server-generator/spec.yaml @@ -895,6 +895,9 @@ components: message: type: string description: The error message + test: + type: string + description: Test prop parameters: PageQueryParam: diff --git a/test-suite-kotlin-ksp-server-generator/build.gradle b/test-suite-kotlin-ksp-server-generator/build.gradle index 9322a96010..a4963a5a0b 100644 --- a/test-suite-kotlin-ksp-server-generator/build.gradle +++ b/test-suite-kotlin-ksp-server-generator/build.gradle @@ -75,11 +75,6 @@ tasks.named("generateOpenApi") { ] } -// TODO: Wait fix https://github.com/micronaut-projects/micronaut-core/issues/10117 -test { - exclude 'io/micronaut/**' -} - kotlin { jvmToolchain(17) } diff --git a/test-suite-kotlin-ksp-server-generator/src/test/kotlin/io/micronaut/openapi/test/api/RequestBodyControllerTest.kt b/test-suite-kotlin-ksp-server-generator/src/test/kotlin/io/micronaut/openapi/test/api/RequestBodyControllerTest.kt index 3989bd56c6..e6a280b8e7 100644 --- a/test-suite-kotlin-ksp-server-generator/src/test/kotlin/io/micronaut/openapi/test/api/RequestBodyControllerTest.kt +++ b/test-suite-kotlin-ksp-server-generator/src/test/kotlin/io/micronaut/openapi/test/api/RequestBodyControllerTest.kt @@ -21,6 +21,7 @@ import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.EnumSource import org.junit.jupiter.params.provider.MethodSource +import spock.lang.Issue import java.math.BigDecimal import java.time.LocalDate import java.time.OffsetDateTime @@ -198,6 +199,8 @@ class RequestBodyControllerTest( assertEquals(model, response) } + @Issue("https://github.com/micronaut-projects/micronaut-serialization/issues/768") + @Disabled @MethodSource("discriminators") @ParameterizedTest fun testSendModelWithDiscriminatorChild(discriminatorName: String, model: Animal) {