From c9b90eb461a1d23cb219da930fdd1e61f36b9ef0 Mon Sep 17 00:00:00 2001 From: Joscha Feth Date: Wed, 18 Sep 2024 00:21:25 +0100 Subject: [PATCH 1/2] feat(avro-schema): logical type support --- bin/configs/avro-schema.yaml | 3 ++ docs/generators/avro-schema.md | 2 + .../codegen/languages/AvroSchemaCodegen.java | 41 +++++++++++++++++++ .../resources/avro-schema/dataType.mustache | 1 + .../avro-schema/typeProperty.mustache | 2 +- .../schema/petstore/avro-schema/Order.avsc | 2 +- 6 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/avro-schema/dataType.mustache diff --git a/bin/configs/avro-schema.yaml b/bin/configs/avro-schema.yaml index 872043a69680..92827ff83c61 100644 --- a/bin/configs/avro-schema.yaml +++ b/bin/configs/avro-schema.yaml @@ -2,3 +2,6 @@ generatorName: avro-schema outputDir: samples/openapi3/schema/petstore/avro-schema inputSpec: modules/openapi-generator/src/test/resources/3_0/avro-schema/petstore.yaml templateDir: modules/openapi-generator/src/main/resources/avro-schema +additionalProperties: + useLogicalTypes: true + logicalTypeTimeQuantifier: nanos \ No newline at end of file diff --git a/docs/generators/avro-schema.md b/docs/generators/avro-schema.md index 82ca92af9f97..488988699447 100644 --- a/docs/generators/avro-schema.md +++ b/docs/generators/avro-schema.md @@ -23,10 +23,12 @@ These options may be applied as additional-properties (cli) or configOptions (pl |ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true| |enumUnknownDefaultCase|If the server adds new enum cases, that are unknown by an old spec/client, the client will fail to parse the network response.With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the server sends an enum case that is not known by the client/spec, they can safely fallback to this case.|
**false**
No changes to the enum's are made, this is the default option.
**true**
With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the enum case sent by the server is not known by the client/spec, can safely be decoded to this case.
|false| |legacyDiscriminatorBehavior|Set to false for generators with better support for discriminators. (Python, Java, Go, PowerShell, C# have this enabled by default).|
**true**
The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.
**false**
The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.
|true| +|logicalTypeTimeQuantifier|The quantifier for time-related logical types (`timestamp` and `local-timestamp`).|
**nanos**
nanoseconds
**micros**
microseconds
**millis**
milliseconds
|millis| |packageName|package for generated classes (where supported)| |null| |prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false| |sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true| |sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true| +|useLogicalTypes|Use logical types for fields, when matching OpenAPI types. Currently supported: `date-time`, `date`.| |false| ## IMPORT MAPPING diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AvroSchemaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AvroSchemaCodegen.java index 69bcb447628d..81fa8c13530c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AvroSchemaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AvroSchemaCodegen.java @@ -27,6 +27,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -37,11 +38,32 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import lombok.Getter; +import lombok.Setter; + public class AvroSchemaCodegen extends DefaultCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(AvroSchemaCodegen.class); private static final String AVRO = "avro-schema"; + + /** + * See https://avro.apache.org/docs/++version++/specification/#logical-types + */ + public static final String USE_LOGICAL_TYPES = "useLogicalTypes"; + public static final String USE_LOGICAL_TYPES_DESC = "Use logical types for fields, when matching OpenAPI types. Currently supported: `date-time`, `date`."; + /** + * See https://avro.apache.org/docs/++version++/specification/#timestamps + */ + public static final String LOGICAL_TYPES_TIME_QUANTIFIER = "logicalTypeTimeQuantifier"; + public static final String LOGICAL_TYPES_TIME_QUANTIFIER_DESC = "The quantifier for time-related logical types (`timestamp` and `local-timestamp`)."; + protected String packageName = "model"; + @Getter @Setter + protected boolean useLogicalTypes = false; // this defaults to false for backwards compatibility + + @Getter @Setter + protected String logicalTypeTimeQuantifier = "millis"; + public AvroSchemaCodegen() { super(); @@ -96,6 +118,15 @@ public AvroSchemaCodegen() { typeMapping.put("BigDecimal", "string"); cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, CodegenConstants.PACKAGE_NAME_DESC)); + cliOptions.add(CliOption.newBoolean(USE_LOGICAL_TYPES, USE_LOGICAL_TYPES_DESC).defaultValue(Boolean.FALSE.toString())); + + CliOption logicalTimeQuantifier = new CliOption(LOGICAL_TYPES_TIME_QUANTIFIER, LOGICAL_TYPES_TIME_QUANTIFIER_DESC).defaultValue(this.getLogicalTypeTimeQuantifier()); + Map timeQuantifierOptions = new HashMap<>(); + timeQuantifierOptions.put("nanos", "nanoseconds"); + timeQuantifierOptions.put("micros", "microseconds"); + timeQuantifierOptions.put("millis", "milliseconds"); + logicalTimeQuantifier.setEnum(timeQuantifierOptions); + cliOptions.add(logicalTimeQuantifier); } @Override @@ -109,6 +140,16 @@ public void processOpts() { } additionalProperties.put("packageName", packageName); + + if (!convertPropertyToBooleanAndWriteBack(USE_LOGICAL_TYPES, this::setUseLogicalTypes)) { + // This sets the default if the option was not specified. + additionalProperties.put(USE_LOGICAL_TYPES, useLogicalTypes); + } + + if (convertPropertyToStringAndWriteBack(LOGICAL_TYPES_TIME_QUANTIFIER, this::setLogicalTypeTimeQuantifier) == null) { + // This sets the default if the option was not specified. + additionalProperties.put(LOGICAL_TYPES_TIME_QUANTIFIER, logicalTypeTimeQuantifier); + } } @Override diff --git a/modules/openapi-generator/src/main/resources/avro-schema/dataType.mustache b/modules/openapi-generator/src/main/resources/avro-schema/dataType.mustache new file mode 100644 index 000000000000..5ab4c4315b89 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/avro-schema/dataType.mustache @@ -0,0 +1 @@ +{{#useLogicalTypes}}{{#isDate}}{ "type": "int", "logicalType": "date" }{{/isDate}}{{#isDateTime}}{ "type": "long", "logicalType": "timestamp-{{{logicalTypeTimeQuantifier}}}" }{{/isDateTime}}{{^isDate}}{{^isDateTime}}"{{{dataType}}}"{{/isDateTime}}{{/isDate}}{{/useLogicalTypes}}{{^useLogicalTypes}}"{{{dataType}}}"{{/useLogicalTypes}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/avro-schema/typeProperty.mustache b/modules/openapi-generator/src/main/resources/avro-schema/typeProperty.mustache index 574d9b171248..00c7b877ae88 100644 --- a/modules/openapi-generator/src/main/resources/avro-schema/typeProperty.mustache +++ b/modules/openapi-generator/src/main/resources/avro-schema/typeProperty.mustache @@ -1 +1 @@ -{{^isEnum}}{{^isContainer}}{{#isPrimitiveType}}"{{dataType}}"{{/isPrimitiveType}}{{^isPrimitiveType}}"{{package}}.{{dataType}}"{{/isPrimitiveType}}{{/isContainer}}{{#isContainer}}{{>typeArray}}{{/isContainer}}{{/isEnum}}{{#isEnum}}{{>typeEnum}}{{/isEnum}} \ No newline at end of file +{{^isEnum}}{{^isContainer}}{{#isPrimitiveType}}{{>dataType}}{{/isPrimitiveType}}{{^isPrimitiveType}}"{{package}}.{{dataType}}"{{/isPrimitiveType}}{{/isContainer}}{{#isContainer}}{{>typeArray}}{{/isContainer}}{{/isEnum}}{{#isEnum}}{{>typeEnum}}{{/isEnum}} \ No newline at end of file diff --git a/samples/openapi3/schema/petstore/avro-schema/Order.avsc b/samples/openapi3/schema/petstore/avro-schema/Order.avsc index c1d5e6c0e6ea..1f1f7564db93 100644 --- a/samples/openapi3/schema/petstore/avro-schema/Order.avsc +++ b/samples/openapi3/schema/petstore/avro-schema/Order.avsc @@ -24,7 +24,7 @@ }, { "name": "shipDate", - "type": ["null", "string"], + "type": ["null", { "type": "long", "logicalType": "timestamp-nanos" }], "doc": "", "default": null }, From a3679e8027a179cfab308399865f043997b9c307 Mon Sep 17 00:00:00 2001 From: Joscha Feth Date: Wed, 18 Sep 2024 09:27:13 +1000 Subject: [PATCH 2/2] style: newline at eof --- bin/configs/avro-schema.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/configs/avro-schema.yaml b/bin/configs/avro-schema.yaml index 92827ff83c61..0de42f8ae558 100644 --- a/bin/configs/avro-schema.yaml +++ b/bin/configs/avro-schema.yaml @@ -4,4 +4,4 @@ inputSpec: modules/openapi-generator/src/test/resources/3_0/avro-schema/petstore templateDir: modules/openapi-generator/src/main/resources/avro-schema additionalProperties: useLogicalTypes: true - logicalTypeTimeQuantifier: nanos \ No newline at end of file + logicalTypeTimeQuantifier: nanos