Skip to content

Commit e7087a1

Browse files
authored
Enabling custom data and datetime mappings (#182)
* Enabling custom datetime mappings, removing default java8 datelibrary * Fixing formatting and spelling * Updating readme
1 parent c46cee4 commit e7087a1

File tree

5 files changed

+169
-7
lines changed

5 files changed

+169
-7
lines changed

README.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -615,14 +615,20 @@ Use the property key `quarkus.openapi-generator.codegen.validateSpec=false` to d
615615

616616
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:
617617

618-
| Description | Property Key | Example |
619-
|----------------|--------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
620-
| Type Mapping | `quarkus.openapi-generator.codegen.spec.[filename].type-mappings.[oas_type]` | `quarkus.openapi-generator.codegen.spec.my_spec_yml.type-mappings.File=InputStream` will use `InputStream` as type for all objects of the OAS File type. |
621-
| Import Mapping | `quarkus.openapi-generator.codegen.spec.[filename].import-mappings.[oas_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` |
618+
| Description | Property Key | Example |
619+
|----------------|------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
620+
| Type Mapping | `quarkus.openapi-generator.codegen.spec.[filename].type-mappings.[oas_type]` | `quarkus.openapi-generator.codegen.spec.my_spec_yml.type-mappings.File=InputStream` will use `InputStream` as type for all objects of the OAS File type. |
621+
| 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` |
622622

623-
Note that these configuration properties are maps where the keys are OAS data types and the values are Java types.
623+
Note that these configuration properties are maps. For the type-mapping the keys are OAS data types and the values are Java types.
624624

625-
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 [Type Mappings and Import Mappings](https://openapi-generator.tech/docs/usage/#type-mappings-and-import-mappings). (Note that you won't be able to change all types as some are hardcoded in the generator, e.g. the OAS type DateTime.)
625+
Another common example is needing `java.time.Instant` as type for date-time fields in your POJO classes. You can achieve with these settings:
626+
```properties
627+
quarkus.openapi-generator.codegen.spec.my_spec_yml.type-mappings.DateTime=Instant
628+
quarkus.openapi-generator.codegen.spec.my_spec_yml.import-mappings.Instant=java.time.Instant
629+
```
630+
631+
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 [Type Mappings and Import Mappings](https://openapi-generator.tech/docs/usage/#type-mappings-and-import-mappings).
626632

627633
See the module [type-mapping](integration-tests/type-mapping) for an example of how to use this feature.
628634

deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/wrapper/OpenApiClientGeneratorWrapper.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ public class OpenApiClientGeneratorWrapper {
3333
* Security scheme for which to apply security constraints even if the OpenAPI definition has no security definition
3434
*/
3535
public static final String DEFAULT_SECURITY_SCHEME = "defaultSecurityScheme";
36+
private static final Map<String, String> defaultTypeMappings = Map.of(
37+
"date", "LocalDate",
38+
"DateTime", "OffsetDateTime");
39+
private static final Map<String, String> defaultImportMappings = Map.of(
40+
"LocalDate", "java.time.LocalDate",
41+
"OffsetDateTime", "java.time.OffsetDateTime");
3642
private final QuarkusCodegenConfigurator configurator;
3743
private final DefaultGenerator generator;
3844

@@ -62,6 +68,9 @@ public OpenApiClientGeneratorWrapper(final Path specFilePath, final Path outputD
6268
Collections.singletonMap("openApiSpecId", getSanitizedFileName(specFilePath)));
6369
this.configurator.addAdditionalProperty("openApiNullable", false);
6470
this.configurator.setValidateSpec(validateSpec);
71+
defaultTypeMappings.forEach(this.configurator::addTypeMapping);
72+
defaultImportMappings.forEach(this.configurator::addImportMapping);
73+
6574
this.generator = new DefaultGenerator();
6675
}
6776

deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/wrapper/QuarkusJavaClientCodegen.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ public class QuarkusJavaClientCodegen extends JavaClientCodegen {
2525

2626
public QuarkusJavaClientCodegen() {
2727
// immutable properties
28-
this.setDateLibrary(JavaClientCodegen.JAVA8_MODE);
2928
this.setSerializationLibrary(SERIALIZATION_LIBRARY_JACKSON);
3029
this.setTemplateDir("templates");
3130
}

deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/wrapper/OpenApiClientGeneratorWrapperTest.java

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.github.javaparser.StaticJavaParser;
2525
import com.github.javaparser.ast.CompilationUnit;
2626
import com.github.javaparser.ast.ImportDeclaration;
27+
import com.github.javaparser.ast.Node;
2728
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
2829
import com.github.javaparser.ast.body.EnumConstantDeclaration;
2930
import com.github.javaparser.ast.body.FieldDeclaration;
@@ -32,6 +33,8 @@
3233
import com.github.javaparser.ast.body.VariableDeclarator;
3334
import com.github.javaparser.ast.expr.AnnotationExpr;
3435
import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr;
36+
import com.github.javaparser.ast.nodeTypes.NodeWithName;
37+
import com.github.javaparser.ast.type.Type;
3538

3639
import io.quarkiverse.openapi.generator.annotations.GeneratedClass;
3740
import io.quarkiverse.openapi.generator.annotations.GeneratedMethod;
@@ -353,6 +356,87 @@ void shouldReplaceFileImportWithInputStream() throws URISyntaxException, FileNot
353356
assertThat(imports).doesNotContain("java.io.File");
354357
}
355358

359+
@Test
360+
void withoutAnyTypeOrImportMappingsItShouldGenerateUsingJava8DatesAndTimes()
361+
throws URISyntaxException, FileNotFoundException {
362+
List<File> generatedFiles = createGeneratorWrapper("datetime-regression.yml")
363+
.generate("org.datetime.regression");
364+
365+
Optional<File> file = generatedFiles.stream()
366+
.filter(f -> f.getName().endsWith("SomeName.java"))
367+
.findAny();
368+
assertThat(file).isNotEmpty();
369+
CompilationUnit compilationUnit = StaticJavaParser.parse(file.orElseThrow());
370+
List<ClassOrInterfaceDeclaration> classes = compilationUnit.findAll(ClassOrInterfaceDeclaration.class);
371+
assertThat(classes).hasSize(1);
372+
ClassOrInterfaceDeclaration generatedPojoClass = classes.get(0);
373+
374+
verifyGeneratedDateAndTimeTypes(
375+
generatedPojoClass,
376+
Map.of(
377+
"someDate", "LocalDate",
378+
"someDateTime", "OffsetDateTime",
379+
"dateArray", "List<LocalDate>",
380+
"dateTimeArray", "List<OffsetDateTime>",
381+
"dateSet", "Set<LocalDate>",
382+
"dateTimeSet", "Set<OffsetDateTime>",
383+
"dateMap", "Map<String,LocalDate>",
384+
"dateTimeMap", "Map<String,OffsetDateTime>"));
385+
assertThat(compilationUnit.getImports().stream().map(NodeWithName::getNameAsString))
386+
.contains("java.time.LocalDate", "java.time.OffsetDateTime");
387+
}
388+
389+
@Test
390+
void shouldBeAbleToAddCustomDateAndTimeMappings() throws URISyntaxException, FileNotFoundException {
391+
List<File> generatedFiles = createGeneratorWrapper("datetime-regression.yml")
392+
.withTypeMappings(Map.of(
393+
"date", "ThaiBuddhistDate",
394+
"DateTime", "Instant"))
395+
.withImportMappings(Map.of(
396+
"ThaiBuddhistDate", "java.time.chrono.ThaiBuddhistDate",
397+
"Instant", "java.time.Instant"))
398+
.generate("org.datetime.mappings");
399+
Optional<File> file = generatedFiles.stream()
400+
.filter(f -> f.getName().endsWith("SomeName.java"))
401+
.findAny();
402+
assertThat(file).isNotEmpty();
403+
CompilationUnit compilationUnit = StaticJavaParser.parse(file.orElseThrow());
404+
List<ClassOrInterfaceDeclaration> classes = compilationUnit.findAll(ClassOrInterfaceDeclaration.class);
405+
assertThat(classes).hasSize(1);
406+
ClassOrInterfaceDeclaration generatedPojoClass = classes.get(0);
407+
408+
verifyGeneratedDateAndTimeTypes(
409+
generatedPojoClass,
410+
Map.of(
411+
"someDate", "ThaiBuddhistDate",
412+
"someDateTime", "Instant",
413+
"dateArray", "List<ThaiBuddhistDate>",
414+
"dateTimeArray", "List<Instant>",
415+
"dateSet", "Set<ThaiBuddhistDate>",
416+
"dateTimeSet", "Set<Instant>",
417+
"dateMap", "Map<String,ThaiBuddhistDate>",
418+
"dateTimeMap", "Map<String,Instant>"));
419+
assertThat(compilationUnit.getImports().stream().map(NodeWithName::getNameAsString))
420+
.contains("java.time.chrono.ThaiBuddhistDate", "java.time.Instant");
421+
}
422+
423+
private void verifyGeneratedDateAndTimeTypes(
424+
ClassOrInterfaceDeclaration classDeclaration,
425+
Map<String, String> expectedFieldsAndTypes) {
426+
expectedFieldsAndTypes.forEach((fieldName, expectedFieldType) -> {
427+
Optional<FieldDeclaration> fieldDeclaration = classDeclaration.getFieldByName(fieldName);
428+
assertThat(fieldDeclaration).isPresent();
429+
430+
Optional<Node> fieldVariableDeclaration = fieldDeclaration.orElseThrow().getChildNodes().stream()
431+
.filter(it -> it instanceof VariableDeclarator)
432+
.findFirst();
433+
assertThat(fieldVariableDeclaration).isPresent();
434+
435+
Type fieldType = ((VariableDeclarator) fieldVariableDeclaration.orElseThrow()).getType();
436+
assertThat(fieldType.asString()).isEqualTo(expectedFieldType);
437+
});
438+
}
439+
356440
@Test
357441
void verifyAdditionalModelTypeAnnotations() throws URISyntaxException {
358442
List<File> generatedFiles = createGeneratorWrapper("petstore-openapi.json")
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
openapi: 3.0.3
2+
info:
3+
title: "Multipart form data API"
4+
version: 1.0.0
5+
6+
servers:
7+
- url: "http://my.endpoint.com/api/v1"
8+
9+
paths:
10+
/dates-and-times:
11+
get:
12+
responses:
13+
"200":
14+
description: The response
15+
content:
16+
application/json:
17+
schema:
18+
$ref: '#/components/schemas/SomeName'
19+
20+
components:
21+
schemas:
22+
SomeName:
23+
type: object
24+
properties:
25+
someDate:
26+
type: string
27+
format: date
28+
someDateTime:
29+
type: string
30+
format: date-time
31+
dateArray:
32+
type: array
33+
items:
34+
type: string
35+
format: date
36+
dateTimeArray:
37+
type: array
38+
items:
39+
type: string
40+
format: date-time
41+
dateSet:
42+
type: array
43+
uniqueItems: true
44+
items:
45+
type: string
46+
format: date
47+
dateTimeSet:
48+
type: array
49+
uniqueItems: true
50+
items:
51+
type: string
52+
format: date-time
53+
dateMap:
54+
type: object
55+
additionalProperties:
56+
type: string
57+
format: date
58+
dateTimeMap:
59+
type: object
60+
additionalProperties:
61+
type: string
62+
format: date-time
63+
64+

0 commit comments

Comments
 (0)