From 850699afa75e45d168d522788290565cce069d03 Mon Sep 17 00:00:00 2001 From: Richard Alm Date: Thu, 7 Nov 2024 23:00:09 +0100 Subject: [PATCH 1/4] Added feature to set schema mappings when generation clients. --- .../generator/deployment/CodegenConfig.java | 1 + .../generator/deployment/CommonItemConfig.java | 7 +++++++ .../codegen/OpenApiGeneratorCodeGenBase.java | 3 +++ .../wrapper/OpenApiClientGeneratorWrapper.java | 5 +++++ .../src/main/openapi/type-mappings-testing.yml | 6 ++++++ .../src/main/resources/application.properties | 1 + .../TypeAndImportMappingRestEasyClassicTest.java | 9 ++++++++- .../TypeAndImportMappingRestEasyReactiveTest.java | 9 +++++++++ docs/modules/ROOT/pages/client.adoc | 14 +++++++++++++- 9 files changed, 53 insertions(+), 2 deletions(-) diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CodegenConfig.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CodegenConfig.java index c913586e..7e13e55b 100644 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CodegenConfig.java +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CodegenConfig.java @@ -58,6 +58,7 @@ public enum ConfigName { ADDITIONAL_API_TYPE_ANNOTATIONS("additional-api-type-annotations"), TYPE_MAPPINGS("type-mappings"), IMPORT_MAPPINGS("import-mappings"), + SCHEMA_MAPPINGS("schema-mappings"), NORMALIZER("open-api-normalizer"), RETURN_RESPONSE("return-response"), ENABLE_SECURITY_GENERATION("enable-security-generation"), diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CommonItemConfig.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CommonItemConfig.java index 9a6baed5..a43e7b81 100644 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CommonItemConfig.java +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CommonItemConfig.java @@ -36,6 +36,13 @@ public class CommonItemConfig { @ConfigItem(name = "import-mappings") public Map importMappings; + /** + * Schema Mapping is an OpenAPI Generator configuration specifying which Java types (the values) should be + * imported when a given schema type (the keys of this map) is used + */ + @ConfigItem(name = "schema-mappings") + public Map schemaMappings; + /** * The specified annotations will be added to the generated model files */ diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorCodeGenBase.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorCodeGenBase.java index c2b0dd8f..c2de0465 100644 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorCodeGenBase.java +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorCodeGenBase.java @@ -296,6 +296,9 @@ protected void generate(OpenApiGeneratorOptions options) { getValues(smallRyeConfig, openApiFilePath, CodegenConfig.ConfigName.IMPORT_MAPPINGS, String.class, String.class) .ifPresent(generator::withImportMappings); + getValues(smallRyeConfig, openApiFilePath, CodegenConfig.ConfigName.SCHEMA_MAPPINGS, String.class, String.class) + .ifPresent(generator::withSchemaMappings); + getValues(smallRyeConfig, openApiFilePath, CodegenConfig.ConfigName.NORMALIZER, String.class, String.class) .ifPresent(generator::withOpenApiNormalizer); diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/wrapper/OpenApiClientGeneratorWrapper.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/wrapper/OpenApiClientGeneratorWrapper.java index f7aa635c..5c081671 100644 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/wrapper/OpenApiClientGeneratorWrapper.java +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/wrapper/OpenApiClientGeneratorWrapper.java @@ -164,6 +164,11 @@ public OpenApiClientGeneratorWrapper withImportMappings(final Map typeMappings) { + typeMappings.forEach(configurator::addSchemaMapping); + return this; + } + public OpenApiClientGeneratorWrapper withOpenApiNormalizer(final Map openApiNormalizer) { configurator.setOpenapiNormalizer(openApiNormalizer); return this; diff --git a/client/integration-tests/type-mapping/src/main/openapi/type-mappings-testing.yml b/client/integration-tests/type-mapping/src/main/openapi/type-mappings-testing.yml index d48b71c3..a7ad5dea 100644 --- a/client/integration-tests/type-mapping/src/main/openapi/type-mappings-testing.yml +++ b/client/integration-tests/type-mapping/src/main/openapi/type-mappings-testing.yml @@ -36,6 +36,10 @@ components: UserId: type: string format: uuid + YearMonth: + format: date-time + type: string + example: 2022-03-10T12:15:50 MultipartRequestBody: type: object @@ -46,5 +50,7 @@ components: $ref: '#/components/schemas/SomeDateTime' binaryStringFile: $ref: '#/components/schemas/BinaryStringFile' + yearMonth: + $ref: '#/components/schemas/YearMonth' diff --git a/client/integration-tests/type-mapping/src/main/resources/application.properties b/client/integration-tests/type-mapping/src/main/resources/application.properties index e7665f40..2d34b5c8 100644 --- a/client/integration-tests/type-mapping/src/main/resources/application.properties +++ b/client/integration-tests/type-mapping/src/main/resources/application.properties @@ -1,5 +1,6 @@ quarkus.openapi-generator.codegen.spec.type_mappings_testing_yml.type-mappings.UUID=String quarkus.openapi-generator.codegen.spec.type_mappings_testing_yml.type-mappings.File=InputStream quarkus.openapi-generator.codegen.spec.type_mappings_testing_yml.import-mappings.File=java.io.InputStream +quarkus.openapi-generator.codegen.spec.type_mappings_testing_yml.schema-mappings.YearMonth=java.time.YearMonth quarkus.openapi-generator.codegen.spec.type_mappings_testing_yml.base-package=org.acme.openapi.typemapping quarkus.openapi-generator.codegen.spec.type_mappings_testing_yml.additional-api-type-annotations=@org.eclipse.microprofile.rest.client.annotation.RegisterProvider(io.quarkiverse.openapi.generator.it.type.mapping.OffsetDateTimeParamConverterProvider.class) \ No newline at end of file diff --git a/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyClassicTest.java b/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyClassicTest.java index 89d21966..77ee7be1 100644 --- a/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyClassicTest.java +++ b/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyClassicTest.java @@ -9,6 +9,7 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.time.OffsetDateTime; +import java.time.YearMonth; import java.time.ZoneOffset; import jakarta.inject.Inject; @@ -41,10 +42,12 @@ class TypeAndImportMappingRestEasyClassicTest { public void canMapTypesAndImportToDifferentValues() { final String testUuid = "00112233-4455-6677-8899-aabbccddeeff"; final InputStream testFile = new ByteArrayInputStream("Content of the file".getBytes(StandardCharsets.UTF_8)); + final YearMonth testYearMonth = YearMonth.parse("2024-06"); TypeMappingApi.PostTheDataMultipartForm requestBody = new TypeMappingApi.PostTheDataMultipartForm(); requestBody.id = testUuid; // String instead of UUID requestBody.binaryStringFile = testFile; // InputStream instead of File + requestBody.yearMonth = testYearMonth; // YearMonth instead of String // dateTime remains OffsetDateTime (as is default) requestBody.dateTime = OffsetDateTime.of(2000, 2, 13, 4, 5, 6, 0, ZoneOffset.UTC); @@ -63,6 +66,10 @@ public void canMapTypesAndImportToDifferentValues() { .withName("binaryStringFile") .withHeader("Content-Disposition", containing("filename=")) .withHeader(ContentTypeHeader.KEY, equalTo(MediaType.APPLICATION_OCTET_STREAM)) - .withBody(equalTo("Content of the file")).build())); + .withBody(equalTo("Content of the file")).build()) + .withRequestBodyPart(new MultipartValuePatternBuilder() + .withName("yearMonth") + .withHeader(ContentTypeHeader.KEY, equalTo(MediaType.APPLICATION_JSON)) + .withBody(equalTo("\"2024-06\"")).build())); } } diff --git a/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyReactiveTest.java b/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyReactiveTest.java index f18fc10f..3fe20323 100644 --- a/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyReactiveTest.java +++ b/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyReactiveTest.java @@ -5,6 +5,7 @@ import java.io.*; import java.nio.charset.StandardCharsets; import java.time.OffsetDateTime; +import java.time.YearMonth; import java.time.ZoneOffset; import jakarta.inject.Inject; @@ -37,10 +38,12 @@ public class TypeAndImportMappingRestEasyReactiveTest { public void canMapTypesAndImportToDifferentValues() { final String testUuid = "00112233-4455-6677-8899-aabbccddeeff"; final InputStream testFile = new ByteArrayInputStream("Content of the file".getBytes(StandardCharsets.UTF_8)); + final YearMonth testYearMonth = YearMonth.parse("2024-06"); TypeMappingApi.PostTheDataMultipartForm requestBody = new TypeMappingApi.PostTheDataMultipartForm(); requestBody.id = testUuid; // String instead of UUID requestBody.binaryStringFile = testFile; // InputStream instead of File + requestBody.yearMonth = testYearMonth; // YearMonth instead of String // dateTime remains OffsetDateTime (as is default) requestBody.dateTime = OffsetDateTime.of(2000, 2, 13, 4, 5, 6, 0, ZoneOffset.UTC); @@ -52,6 +55,12 @@ public void canMapTypesAndImportToDifferentValues() { .withHeader(ContentTypeHeader.KEY, equalTo(MediaType.TEXT_PLAIN + "; charset=UTF-8")) .withBody(equalTo(testUuid)).build())); + typeMappingServer.verify(postRequestedFor(urlEqualTo("/type-mapping")) + .withRequestBodyPart(new MultipartValuePatternBuilder() + .withName("yearMonth") + .withHeader(ContentTypeHeader.KEY, equalTo(MediaType.APPLICATION_JSON + "; charset=UTF-8")) + .withBody(equalTo(testYearMonth.toString())).build())); + typeMappingServer.verify(postRequestedFor(urlEqualTo("/type-mapping")) .withRequestBodyPart(new MultipartValuePatternBuilder() .withName("dateTime") diff --git a/docs/modules/ROOT/pages/client.adoc b/docs/modules/ROOT/pages/client.adoc index 95ad693f..d335f60e 100644 --- a/docs/modules/ROOT/pages/client.adoc +++ b/docs/modules/ROOT/pages/client.adoc @@ -115,7 +115,7 @@ See the module `integration-tests/register-provider` for an example of how to us Use the property key `quarkus.openapi-generator.codegen.validateSpec=false` to disable validating the input specification file before code generation. By default, invalid specifications will result in an error. -== Type and import mappings +== Type, schema and import mappings It's possible to remap types in the generated files. For example, instead of a `File` you can configure the code generator to use `InputStream` for all file upload parts of multipart request, or you could change all `UUID` types to `String`. You can configure this in your `application.properties` using the following configuration keys: @@ -129,6 +129,9 @@ It's possible to remap types in the generated files. For example, instead of a ` |Import Mapping |`quarkus.openapi-generator.codegen.spec.[filename].import-mappings.[type]` |`quarkus.openapi-generator.codegen.spec.my_spec_yml.import-mappings.File=java.io.InputStream` will replace the default `import java.io.File` with `import java.io.InputStream` +|Schema Mapping +|`quarkus.openapi-generator.codegen.spec.[filename].schema-mappings.[type]` +|`quarkus.openapi-generator.codegen.spec.my_spec_yml.schema-mappings.YearMonth=java.time.YearMonth` will use `java.time.YearMonth` as type for all schemas of the chosen type in the file. |=== Note that these configuration properties are maps. For the type-mapping the keys are OAS data types and the values are Java types. @@ -141,6 +144,15 @@ quarkus.openapi-generator.codegen.spec.my_spec_yml.type-mappings.DateTime=Instan quarkus.openapi-generator.codegen.spec.my_spec_yml.import-mappings.Instant=java.time.Instant ---- +If you have an object you want to map to an datatype, you can use the schema mappings. For instance, if your schema is based +on `java.Time.YearMonth`, it will be mapped to an string when generating. If you want to convert the object `YearMonth`then +you can use the following settings: + +[source,properties] +---- +quarkus.openapi-generator.codegen.spec.my_spec_yml.schema-mappings.YearMonth=java.time.YearMonth +---- + It's also possible to only use a type mapping with a fully qualified name, for instance `quarkus.openapi-generator.codegen.spec.my_spec_yml.type-mappings.File=java.io.InputStream`. For more information and a list of all types see the OpenAPI generator documentation on https://openapi-generator.tech/docs/usage/#type-mappings-and-import-mappings[Type Mappings and Import Mappings]. See the module https://github.com/quarkiverse/quarkus-openapi-generator/tree/main/integration-tests/type-mapping[type-mapping] for an example of how to use this feature. From 17bd9b7b7d371b8a277e1c14a1e2a259c40c1409 Mon Sep 17 00:00:00 2001 From: Richard Alm Date: Sat, 9 Nov 2024 10:41:28 +0100 Subject: [PATCH 2/4] Updated Restreactive test with schemamapping --- .../mapping/TypeAndImportMappingRestEasyReactiveTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyReactiveTest.java b/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyReactiveTest.java index 3fe20323..6909b89f 100644 --- a/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyReactiveTest.java +++ b/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyReactiveTest.java @@ -58,8 +58,8 @@ public void canMapTypesAndImportToDifferentValues() { typeMappingServer.verify(postRequestedFor(urlEqualTo("/type-mapping")) .withRequestBodyPart(new MultipartValuePatternBuilder() .withName("yearMonth") - .withHeader(ContentTypeHeader.KEY, equalTo(MediaType.APPLICATION_JSON + "; charset=UTF-8")) - .withBody(equalTo(testYearMonth.toString())).build())); + .withHeader(ContentTypeHeader.KEY, equalTo(MediaType.APPLICATION_JSON)) + .withBody(equalTo("\"2024-06\"")).build())); typeMappingServer.verify(postRequestedFor(urlEqualTo("/type-mapping")) .withRequestBodyPart(new MultipartValuePatternBuilder() From 4463f97fe281791521ae78c6aae0f826b022555e Mon Sep 17 00:00:00 2001 From: Richard Alm Date: Sun, 10 Nov 2024 14:36:39 +0100 Subject: [PATCH 3/4] Updated the YearMonth schema to be correct format. --- .../main/openapi/type-mappings-testing.yml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/client/integration-tests/type-mapping/src/main/openapi/type-mappings-testing.yml b/client/integration-tests/type-mapping/src/main/openapi/type-mappings-testing.yml index a7ad5dea..0a54c5c9 100644 --- a/client/integration-tests/type-mapping/src/main/openapi/type-mappings-testing.yml +++ b/client/integration-tests/type-mapping/src/main/openapi/type-mappings-testing.yml @@ -37,9 +37,22 @@ components: type: string format: uuid YearMonth: - format: date-time - type: string - example: 2022-03-10T12:15:50 + type: object + properties: + year: + format: int32 + type: integer + month: + format: int32 + type: integer + prolepticMonth: + format: int64 + type: integer + monthValue: + format: int32 + type: integer + leapYear: + type: boolean MultipartRequestBody: type: object From 751912424962226e0658b0a544c3e459526a3134 Mon Sep 17 00:00:00 2001 From: Richard Alm Date: Tue, 12 Nov 2024 22:56:42 +0100 Subject: [PATCH 4/4] Updated the documentation --- docs/modules/ROOT/pages/client.adoc | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/docs/modules/ROOT/pages/client.adoc b/docs/modules/ROOT/pages/client.adoc index d335f60e..7f14d4e8 100644 --- a/docs/modules/ROOT/pages/client.adoc +++ b/docs/modules/ROOT/pages/client.adoc @@ -144,16 +144,7 @@ quarkus.openapi-generator.codegen.spec.my_spec_yml.type-mappings.DateTime=Instan quarkus.openapi-generator.codegen.spec.my_spec_yml.import-mappings.Instant=java.time.Instant ---- -If you have an object you want to map to an datatype, you can use the schema mappings. For instance, if your schema is based -on `java.Time.YearMonth`, it will be mapped to an string when generating. If you want to convert the object `YearMonth`then -you can use the following settings: - -[source,properties] ----- -quarkus.openapi-generator.codegen.spec.my_spec_yml.schema-mappings.YearMonth=java.time.YearMonth ----- - -It's also possible to only use a type mapping with a fully qualified name, for instance `quarkus.openapi-generator.codegen.spec.my_spec_yml.type-mappings.File=java.io.InputStream`. For more information and a list of all types see the OpenAPI generator documentation on https://openapi-generator.tech/docs/usage/#type-mappings-and-import-mappings[Type Mappings and Import Mappings]. +It's also possible to only use a type mapping with a fully qualified name, for instance `quarkus.openapi-generator.codegen.spec.my_spec_yml.type-mappings.File=java.io.InputStream`. For more information and a list of all types see the OpenAPI generator documentation on https://openapi-generator.tech/docs/usage/#type-mappings-and-import-mappings[Type Mappings and Import Mappings] and https://openapi-generator.tech/docs/customization#schema-mapping[Schema mapping]. See the module https://github.com/quarkiverse/quarkus-openapi-generator/tree/main/integration-tests/type-mapping[type-mapping] for an example of how to use this feature.