From 5d99c1d1d39a914e5d2430676a7ad34c729ad28a Mon Sep 17 00:00:00 2001 From: altro3 Date: Thu, 5 Sep 2024 16:08:29 +0700 Subject: [PATCH] Fix process pattern with date and date-time format. Fixed #1755 --- .../AbstractMicronautJavaCodegen.java | 62 ++++++++++++++----- .../AbstractMicronautKotlinCodegen.java | 37 ++++++++++- .../io/micronaut/openapi/generator/Utils.java | 24 +++++-- .../client/params/type.mustache | 10 +-- .../common/model/jackson_annotations.mustache | 40 +++++++++--- .../java-micronaut/common/model/pojo.mustache | 34 +++++----- .../common/params/validation.mustache | 4 +- .../server/params/type.mustache | 4 +- .../client/params/type.mustache | 9 +-- .../common/model/jackson_annotations.mustache | 40 +++++++++--- .../common/params/validation.mustache | 4 +- .../server/controller-interface.mustache | 10 ++- .../JavaMicronautClientCodegenTest.java | 10 ++- .../JavaMicronautServerCodegenTest.java | 4 +- .../KotlinMicronautClientCodegenTest.java | 7 ++- .../KotlinMicronautServerCodegenTest.java | 3 +- .../src/test/resources/3_0/issue_11772.yml | 1 + .../resources/3_0/modelwithprimitivelist.yml | 11 ++++ 18 files changed, 227 insertions(+), 87 deletions(-) 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 8020214e1c..eb1e046279 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 @@ -80,6 +80,7 @@ import static io.micronaut.openapi.generator.Utils.EXT_ANNOTATIONS_OPERATION; import static io.micronaut.openapi.generator.Utils.EXT_ANNOTATIONS_SETTER; import static io.micronaut.openapi.generator.Utils.addStrValueToEnum; +import static io.micronaut.openapi.generator.Utils.isDateType; import static io.micronaut.openapi.generator.Utils.normalizeExtraAnnotations; import static io.micronaut.openapi.generator.Utils.processGenericAnnotations; import static org.openapitools.codegen.CodegenConstants.API_PACKAGE; @@ -111,7 +112,7 @@ public abstract class AbstractMicronautJavaCodegen o.getOpt().equals(DATE_LIBRARY)) .findFirst() .ifPresent(opt -> { - Map valuesEnum = new HashMap<>(); + var valuesEnum = new HashMap(); valuesEnum.put(OPT_DATE_LIBRARY_OFFSET_DATETIME, opt.getEnum().get(OPT_DATE_LIBRARY_OFFSET_DATETIME)); valuesEnum.put(OPT_DATE_LIBRARY_LOCAL_DATETIME, opt.getEnum().get(OPT_DATE_LIBRARY_LOCAL_DATETIME)); opt.setEnum(valuesEnum); @@ -448,6 +451,21 @@ public void processOpts() { } writePropertyBack(OPT_REACTIVE, reactive); + if (additionalProperties.containsKey(OPT_DATE_FORMAT)) { + dateFormat = (String) additionalProperties.get(OPT_DATE_FORMAT); + } + writePropertyBack(OPT_DATE_FORMAT, dateFormat); + + if (additionalProperties.containsKey(OPT_DATE_TIME_FORMAT)) { + dateTimeFormat = (String) additionalProperties.get(OPT_DATE_TIME_FORMAT); + } + writePropertyBack(OPT_DATE_TIME_FORMAT, dateFormat); + + if (additionalProperties.containsKey(OPT_GENERATE_HTTP_RESPONSE_ALWAYS)) { + generateHttpResponseAlways = convertPropertyToBoolean(OPT_GENERATE_HTTP_RESPONSE_ALWAYS); + } + writePropertyBack(OPT_GENERATE_HTTP_RESPONSE_ALWAYS, generateHttpResponseAlways); + if (additionalProperties.containsKey(OPT_GENERATE_HTTP_RESPONSE_ALWAYS)) { generateHttpResponseAlways = convertPropertyToBoolean(OPT_GENERATE_HTTP_RESPONSE_ALWAYS); } @@ -837,18 +855,14 @@ private Pair calcDefaultValues(String itemsDatatypeWithEnum, Str } else if (ModelUtils.isStringSchema(schema)) { if (schema.getDefault() != null) { if (schema.getDefault() instanceof Date date) { - if ("java8".equals(getDateLibrary())) { - LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); - defaultValueInit = String.format(Locale.ROOT, "LocalDate.parse(\"%s\")", localDate.toString()); - defaultValueStr = localDate.toString(); - } + LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + defaultValueInit = String.format(Locale.ROOT, "LocalDate.parse(\"%s\")", localDate.toString()); + defaultValueStr = localDate.toString(); } else if (schema.getDefault() instanceof java.time.OffsetDateTime offsetDateTime) { - if ("java8".equals(getDateLibrary())) { - defaultValueInit = String.format(Locale.ROOT, "OffsetDateTime.parse(\"%s\", %s)", - offsetDateTime.atZoneSameInstant(ZoneId.systemDefault()), - "java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME.withZone(java.time.ZoneId.systemDefault())"); - defaultValueStr = offsetDateTime.toString(); - } + defaultValueInit = String.format(Locale.ROOT, "OffsetDateTime.parse(\"%s\", %s)", + offsetDateTime.atZoneSameInstant(ZoneId.systemDefault()), + "java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME.withZone(java.time.ZoneId.systemDefault())"); + defaultValueStr = offsetDateTime.toString(); } else if (schema.getDefault() instanceof UUID) { defaultValueInit = "UUID.fromString(\"" + schema.getDefault() + "\")"; defaultValueStr = schema.getDefault().toString(); @@ -1232,6 +1246,11 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List DATE_TIME_TYPES = List.of( + "date", + "Date", + "DateTime", + "LocalDateTime", + "OffsetDateTime", + "ZonedDateTime", + "LocalDate", + "LocalTime" + ); public static final String DEFAULT_BODY_PARAM_NAME = "requestBody"; public static final String EXT_ANNOTATIONS_OPERATION = "x-operation-extra-annotation"; public static final String EXT_ANNOTATIONS_CLASS = "x-class-extra-annotation"; @@ -46,7 +56,7 @@ public static void processGenericAnnotations(CodegenParameter parameter, boolean CodegenProperty items = parameter.isMap ? parameter.additionalProperties : parameter.items; String datatypeWithEnum = parameter.datatypeWithEnum == null ? parameter.dataType : parameter.datatypeWithEnum; processGenericAnnotations(parameter.dataType, datatypeWithEnum, parameter.isMap, parameter.containerTypeMapped, - items, parameter.vendorExtensions, useBeanValidation, isGenerateHardNullable, isNullable, isRequired, isReadonly, withNullablePostfix); + items, parameter.vendorExtensions, useBeanValidation, isGenerateHardNullable, isNullable, isRequired, isReadonly, withNullablePostfix); } public static void processGenericAnnotations(CodegenProperty property, boolean useBeanValidation, boolean isGenerateHardNullable, @@ -54,7 +64,7 @@ public static void processGenericAnnotations(CodegenProperty property, boolean u CodegenProperty items = property.isMap ? property.additionalProperties : property.items; String datatypeWithEnum = property.datatypeWithEnum == null ? property.dataType : property.datatypeWithEnum; processGenericAnnotations(property.dataType, datatypeWithEnum, property.isMap, property.containerTypeMapped, - items, property.vendorExtensions, useBeanValidation, isGenerateHardNullable, isNullable, isRequired, isReadonly, withNullablePostfix); + items, property.vendorExtensions, useBeanValidation, isGenerateHardNullable, isNullable, isRequired, isReadonly, withNullablePostfix); } public static void processGenericAnnotations(String dataType, String dataTypeWithEnum, boolean isMap, String containerType, CodegenProperty itemsProp, Map ext, @@ -94,7 +104,7 @@ private static String genericAnnotations(CodegenProperty prop, boolean isGenerat return result.toString(); } - if (StringUtils.isNotEmpty(prop.pattern)) { + if (StringUtils.isNotEmpty(prop.pattern) && !prop.isDate && !prop.isDateTime) { if ("email".equals(type)) { result.append("@Email(regexp = \""); } else { @@ -244,8 +254,8 @@ public static void addStrValueToEnum(List enumVars, boolean isNumeric) { } var upperValue = value.toUpperCase(); if (upperValue.endsWith("F") - || upperValue.endsWith("L") - || upperValue.endsWith("D")) { + || upperValue.endsWith("L") + || upperValue.endsWith("D")) { value = value.substring(0, value.length() - 1); } if (!value.contains("\"")) { @@ -302,4 +312,8 @@ private static List normalizeExtraAnnotations(String prefix, Collection< private static String normalizeExtraAnnotation(String prefix, String annotationStr) { return prefix + (annotationStr.startsWith("@") ? annotationStr.substring(1) : annotationStr); } + + public static boolean isDateType(String type) { + return DATE_TIME_TYPES.contains(type); + } } diff --git a/openapi-generator/src/main/resources/templates/java-micronaut/client/params/type.mustache b/openapi-generator/src/main/resources/templates/java-micronaut/client/params/type.mustache index b7f6b0d4f2..d17641dc57 100644 --- a/openapi-generator/src/main/resources/templates/java-micronaut/client/params/type.mustache +++ b/openapi-generator/src/main/resources/templates/java-micronaut/client/params/type.mustache @@ -1,7 +1,3 @@ -{{! -default type -}}{{^isDate}}{{^isDateTime}}{{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}{{/isDateTime}}{{/isDate}}{{! -date-time -}}{{#isDateTime}}@Format("{{{datetimeFormat}}}") {{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}{{/isDateTime}}{{! -date -}}{{#isDate}}@Format("{{{dateFormat}}}") {{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}{{/isDate}} +{{^isDate}}{{^isDateTime}}{{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}{{/isDateTime}}{{/isDate}} +{{#isDateTime}}{{#vendorExtensions.formatPattern}}@Format("{{{vendorExtensions.formatPattern}}}"){{/vendorExtensions.formatPattern}}{{^vendorExtensions.formatPattern}}{{#dateTimeFormat}}@Format("{{{dateTimeFormat}}}"){{/dateTimeFormat}}{{/vendorExtensions.formatPattern}} {{{dataType}}} {{/isDateTime}} +{{#isDate}}{{#vendorExtensions.formatPattern}}@Format("{{{vendorExtensions.formatPattern}}}"){{/vendorExtensions.formatPattern}}{{^vendorExtensions.formatPattern}}{{#dateFormat}}@Format("{{{dateFormat}}}"){{/dateFormat}}{{/vendorExtensions.formatPattern}} {{{dataType}}} {{/isDate}} \ No newline at end of file diff --git a/openapi-generator/src/main/resources/templates/java-micronaut/common/model/jackson_annotations.mustache b/openapi-generator/src/main/resources/templates/java-micronaut/common/model/jackson_annotations.mustache index 85afdbc24f..577ca6496a 100644 --- a/openapi-generator/src/main/resources/templates/java-micronaut/common/model/jackson_annotations.mustache +++ b/openapi-generator/src/main/resources/templates/java-micronaut/common/model/jackson_annotations.mustache @@ -38,26 +38,46 @@ {{#jackson}} {{^micronaut_serde_jackson}} {{#isDateTime}} - {{#datetimeFormat}} - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "{{{datetimeFormat}}}") - {{/datetimeFormat}} + {{#vendorExtensions.formatPattern}} + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "{{{vendorExtensions.formatPattern}}}") + {{/vendorExtensions.formatPattern}} + {{^vendorExtensions.formatPattern}} + {{#dateTimeFormat}} + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "{{{dateTimeFormat}}}") + {{/dateTimeFormat}} + {{/vendorExtensions.formatPattern}} {{/isDateTime}} {{#isDate}} - {{#dateFormat}} + {{#vendorExtensions.formatPattern}} + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "{{{vendorExtensions.formatPattern}}}") + {{/vendorExtensions.formatPattern}} + {{^vendorExtensions.formatPattern}} + {{#dateFormat}} @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "{{{dateFormat}}}") - {{/dateFormat}} + {{/dateFormat}} + {{/vendorExtensions.formatPattern}} {{/isDate}} {{/micronaut_serde_jackson}} {{/jackson}} {{#micronaut_serde_jackson}} {{#isDateTime}} - {{#datatimeFormat}} - @JsonFormat(pattern = "{{{datetimeFormat}}}") - {{/datatimeFormat}} + {{#vendorExtensions.formatPattern}} + @JsonFormat(pattern = "{{{vendorExtensions.formatPattern}}}") + {{/vendorExtensions.formatPattern}} + {{^vendorExtensions.formatPattern}} + {{#dateTimeFormat}} + @JsonFormat(pattern = "{{{dateTimeFormat}}}") + {{/dateTimeFormat}} + {{/vendorExtensions.formatPattern}} {{/isDateTime}} {{#isDate}} - {{#dateFormat}} + {{#vendorExtensions.formatPattern}} + @JsonFormat(pattern = "{{{vendorExtensions.formatPattern}}}") + {{/vendorExtensions.formatPattern}} + {{^vendorExtensions.formatPattern}} + {{#dateFormat}} @JsonFormat(pattern = "{{{dateFormat}}}") - {{/dateFormat}} + {{/dateFormat}} + {{/vendorExtensions.formatPattern}} {{/isDate}} {{/micronaut_serde_jackson}} diff --git a/openapi-generator/src/main/resources/templates/java-micronaut/common/model/pojo.mustache b/openapi-generator/src/main/resources/templates/java-micronaut/common/model/pojo.mustache index c0059aad38..65537f7def 100644 --- a/openapi-generator/src/main/resources/templates/java-micronaut/common/model/pojo.mustache +++ b/openapi-generator/src/main/resources/templates/java-micronaut/common/model/pojo.mustache @@ -159,7 +159,7 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#vendorE {{/micronaut_serde_jackson}} {{#formatNoEmptyLines}} {{#vendorExtensions.withRequiredVars}} - public {{classname}}({{#vendorExtensions.requiredVarsWithoutDiscriminator}}{{#isReadOnly}}{{#vendorExtensions.isServer}}{{^-first}}, {{/-first}}{{{datatypeWithEnum}}} {{name}}{{/vendorExtensions.isServer}}{{/isReadOnly}}{{^isReadOnly}}{{^-first}}, {{/-first}}{{{datatypeWithEnum}}} {{name}}{{/isReadOnly}}{{/vendorExtensions.requiredVarsWithoutDiscriminator}}) { + public {{classname}}({{#vendorExtensions.requiredVarsWithoutDiscriminator}}{{#isReadOnly}}{{#vendorExtensions.isServer}}{{^-first}}, {{/-first}}{{{vendorExtensions.typeWithEnumWithGenericAnnotations}}} {{name}}{{/vendorExtensions.isServer}}{{/isReadOnly}}{{^isReadOnly}}{{^-first}}, {{/-first}}{{{vendorExtensions.typeWithEnumWithGenericAnnotations}}} {{name}}{{/isReadOnly}}{{/vendorExtensions.requiredVarsWithoutDiscriminator}}) { {{#parent}} {{#vendorExtensions.requiredParentVarsWithoutDiscriminator.0}}super({{/vendorExtensions.requiredParentVarsWithoutDiscriminator.0}}{{#vendorExtensions.requiredParentVarsWithoutDiscriminator}}{{#vendorExtensions.isServerOrNotReadOnly}}{{^-first}}, {{/-first}}{{name}}{{/vendorExtensions.isServerOrNotReadOnly}}{{/vendorExtensions.requiredParentVarsWithoutDiscriminator}}{{#vendorExtensions.requiredParentVarsWithoutDiscriminator.0}});{{/vendorExtensions.requiredParentVarsWithoutDiscriminator.0}} {{/parent}} @@ -207,12 +207,12 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#vendorE {{/isOverridden}} {{/parent}} {{/isDiscriminator}} - public {{{datatypeWithEnum}}} {{getter}}() { + public {{{vendorExtensions.typeWithEnumWithGenericAnnotations}}} {{getter}}() { {{#vendorExtensions.x-is-jackson-optional-nullable}} {{#isReadOnly}} {{! A readonly attribute doesn't have setter => jackson will set null directly if explicitly returned by API, so make sure we have an empty JsonNullable}} if ({{name}} == null) { - {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>{{#defaultValue}}of({{{.}}}){{/defaultValue}}{{^defaultValue}}undefined(){{/defaultValue}}; + {{name}} = JsonNullable.<{{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}>{{#defaultValue}}of({{{.}}}){{/defaultValue}}{{^defaultValue}}undefined(){{/defaultValue}}; } {{/isReadOnly}} return {{name}}.orElse(null); @@ -235,7 +235,7 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#vendorE {{#jackson}} @JsonIgnore {{/jackson}} - public Optional<{{{datatypeWithEnum}}}> {{getter}}Optional() { + public Optional<{{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}> {{getter}}Optional() { {{^vendorExtensions.x-is-jackson-optional-nullable}} return Optional.ofNullable({{name}}); {{/vendorExtensions.x-is-jackson-optional-nullable}} @@ -253,9 +253,9 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#vendorE {{#vendorExtensions.x-setter-extra-annotation}} {{{.}}} {{/vendorExtensions.x-setter-extra-annotation}} - public void {{setter}}({{{datatypeWithEnum}}} {{name}}) { + public void {{setter}}({{{vendorExtensions.typeWithEnumWithGenericAnnotations}}} {{name}}) { {{#vendorExtensions.x-is-jackson-optional-nullable}} - this.{{name}} = JsonNullable.<{{{datatypeWithEnum}}}>of({{name}}); + this.{{name}} = JsonNullable.<{{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}>of({{name}}); {{/vendorExtensions.x-is-jackson-optional-nullable}} {{^vendorExtensions.x-is-jackson-optional-nullable}} this.{{name}} = {{name}}; @@ -270,12 +270,12 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#vendorE * @return JsonNullable version of {{name}} */ {{>common/model/jackson_annotations}} - public JsonNullable<{{{datatypeWithEnum}}}> {{getter}}_JsonNullable() { + public JsonNullable<{{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}> {{getter}}_JsonNullable() { return {{name}}; } @JsonProperty(JSON_PROPERTY_{{nameInSnakeCase}}) - public void {{setter}}_JsonNullable(JsonNullable<{{{datatypeWithEnum}}}> {{name}}) { + public void {{setter}}_JsonNullable(JsonNullable<{{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}> {{name}}) { {{! For getters/setters that have name differing from attribute name, we must include setter (albeit private) for jackson to be able to set the attribute}} this.{{name}} = {{name}}; } {{/vendorExtensions.x-is-jackson-optional-nullable}} @@ -287,9 +287,9 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#vendorE * * @return The same instance of {{classname}} for chaining. */ - public {{classname}} {{name}}({{{datatypeWithEnum}}} {{name}}) { + public {{classname}} {{name}}({{{vendorExtensions.typeWithEnumWithGenericAnnotations}}} {{name}}) { {{#vendorExtensions.x-is-jackson-optional-nullable}} - this.{{name}} = JsonNullable.<{{{datatypeWithEnum}}}>of({{name}}); + this.{{name}} = JsonNullable.<{{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}>of({{name}}); {{/vendorExtensions.x-is-jackson-optional-nullable}} {{^vendorExtensions.x-is-jackson-optional-nullable}} this.{{name}} = {{name}}; @@ -303,10 +303,10 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#vendorE * * @return The same instance of {{classname}} for chaining. */ - public {{classname}} add{{nameInCamelCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) { + public {{classname}} add{{nameInCamelCase}}Item({{{items.vendorExtensions.typeWithEnumWithGenericAnnotations}}} {{name}}Item) { {{#vendorExtensions.x-is-jackson-optional-nullable}} if ({{name}} == null || !{{name}}.isPresent()) { - {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>of({{{defaultValue}}}{{^defaultValue}}new {{#uniqueItems}}LinkedHashSet{{/uniqueItems}}{{^uniqueItems}}ArrayList{{/uniqueItems}}<>(){{/defaultValue}}); + {{name}} = JsonNullable.<{{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}>of({{{defaultValue}}}{{^defaultValue}}new {{#uniqueItems}}LinkedHashSet{{/uniqueItems}}{{^uniqueItems}}ArrayList{{/uniqueItems}}<>(){{/defaultValue}}); } try { {{name}}.get().add({{name}}Item); @@ -361,14 +361,14 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#vendorE {{#parentVars}} {{^isReadOnly}} @Override - public {{classname}} {{#lombok}}{{{setter}}}{{/lombok}}{{^lombok}}{{name}}{{/lombok}}({{{datatypeWithEnum}}} {{name}}) { + public {{classname}} {{#lombok}}{{{setter}}}{{/lombok}}{{^lombok}}{{name}}{{/lombok}}({{{vendorExtensions.typeWithEnumWithGenericAnnotations}}} {{name}}) { super.{{{setter}}}({{name}}); return this; } {{#isArray}} @Override - public {{classname}} add{{nameInCamelCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) { + public {{classname}} add{{nameInCamelCase}}Item({{{items.vendorExtensions.typeWithEnumWithGenericAnnotations}}} {{name}}Item) { super.add{{nameInCamelCase}}Item({{name}}Item); return this; } @@ -376,7 +376,7 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#vendorE {{#isMap}} @Override - public {{classname}} put{{nameInCamelCase}}Item(String key, {{{items.datatypeWithEnum}}} {{name}}Item) { + public {{classname}} put{{nameInCamelCase}}Item(String key, {{{items.vendorExtensions.typeWithEnumWithGenericAnnotations}}} {{name}}Item) { super.put{{nameInCamelCase}}Item(key, {{name}}Item); return this; } @@ -457,10 +457,10 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#vendorE {{/parent}} {{#vars}} {{#isPrimitiveType}} - {{name}} = ({{{datatypeWithEnum}}})in.readValue(null); + {{name}} = ({{{vendorExtensions.typeWithEnumWithGenericAnnotations}}})in.readValue(null); {{/isPrimitiveType}} {{^isPrimitiveType}} - {{name}} = ({{{datatypeWithEnum}}})in.readValue({{complexType}}.class.getClassLoader()); + {{name}} = ({{{vendorExtensions.typeWithEnumWithGenericAnnotations}}})in.readValue({{complexType}}.class.getClassLoader()); {{/isPrimitiveType}} {{/vars}} {{/isArray}} diff --git a/openapi-generator/src/main/resources/templates/java-micronaut/common/params/validation.mustache b/openapi-generator/src/main/resources/templates/java-micronaut/common/params/validation.mustache index 35f95ab2c2..29e017b88c 100644 --- a/openapi-generator/src/main/resources/templates/java-micronaut/common/params/validation.mustache +++ b/openapi-generator/src/main/resources/templates/java-micronaut/common/params/validation.mustache @@ -50,9 +50,9 @@ {{/isContainer}} {{!Pattern}} {{#pattern}} - {{^isByteArray}} + {{^isByteArray}}{{^isDate}}{{^isDateTime}} @Pattern(regexp = "{{{pattern}}}") - {{/isByteArray}} + {{/isDateTime}}{{/isDate}}{{/isByteArray}} {{/pattern}} {{!Min length && max length}} {{#minLength}} diff --git a/openapi-generator/src/main/resources/templates/java-micronaut/server/params/type.mustache b/openapi-generator/src/main/resources/templates/java-micronaut/server/params/type.mustache index d66ab02ba0..15e874eaf6 100644 --- a/openapi-generator/src/main/resources/templates/java-micronaut/server/params/type.mustache +++ b/openapi-generator/src/main/resources/templates/java-micronaut/server/params/type.mustache @@ -2,10 +2,10 @@ {{{vendorExtensions.typeWithGenericAnnotations}}} {{/isDate}}{{/isDateTime}}{{/isEnum}} {{#isDateTime}} - {{#dateFormat}}@Format("{{{datetimeFormat}}}"){{/dateFormat}} {{{dataType}}} + {{#vendorExtensions.formatPattern}}@Format("{{{.}}}"){{/vendorExtensions.formatPattern}}{{^vendorExtensions.formatPattern}}{{#dateTimeFormat}}@Format("{{{.}}}"){{/dateTimeFormat}}{{/vendorExtensions.formatPattern}} {{{dataType}}} {{/isDateTime}} {{#isDate}} - {{#dateTimeFormat}}@Format("{{{dateFormat}}}"){{/dateTimeFormat}} {{{dataType}}} + {{#vendorExtensions.formatPattern}}@Format("{{{.}}}"){{/vendorExtensions.formatPattern}}{{^vendorExtensions.formatPattern}}{{#dateFormat}}@Format("{{{.}}}"){{/dateFormat}}{{/vendorExtensions.formatPattern}} {{{dataType}}} {{/isDate}} {{#isEnum}} {{{vendorExtensions.typeWithEnumWithGenericAnnotations}}} diff --git a/openapi-generator/src/main/resources/templates/kotlin-micronaut/client/params/type.mustache b/openapi-generator/src/main/resources/templates/kotlin-micronaut/client/params/type.mustache index d215bc747a..c1a8e8541f 100644 --- a/openapi-generator/src/main/resources/templates/kotlin-micronaut/client/params/type.mustache +++ b/openapi-generator/src/main/resources/templates/kotlin-micronaut/client/params/type.mustache @@ -1,7 +1,2 @@ -{{! -default type -}}{{^isDate}}{{^isDateTime}}{{/isDateTime}}{{/isDate}}{{! -date-time -}}{{#isDateTime}}@Format("{{{datetimeFormat}}}") {{/isDateTime}}{{! -date -}}{{#isDate}}@Format("{{{dateFormat}}}") {{/isDate}} \ No newline at end of file +{{#isDateTime}}{{#vendorExtensions.formatPattern}}@Format("{{{vendorExtensions.formatPattern}}}"){{/vendorExtensions.formatPattern}}{{^vendorExtensions.formatPattern}}{{#dateTimeFormat}}@Format("{{{dateTimeFormat}}}"){{/dateTimeFormat}}{{/vendorExtensions.formatPattern}} {{/isDateTime}} +{{#isDate}}{{#vendorExtensions.formatPattern}}@Format("{{{vendorExtensions.formatPattern}}}"){{/vendorExtensions.formatPattern}}{{^vendorExtensions.formatPattern}}{{#dateFormat}}@Format("{{{dateFormat}}}"){{/dateFormat}}{{/vendorExtensions.formatPattern}} {{/isDate}} \ No newline at end of file diff --git a/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/model/jackson_annotations.mustache b/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/model/jackson_annotations.mustache index 2a4387968d..61387f5d67 100644 --- a/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/model/jackson_annotations.mustache +++ b/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/model/jackson_annotations.mustache @@ -38,26 +38,46 @@ {{#jackson}} {{^micronaut_serde_jackson}} {{#isDateTime}} - {{#datetimeFormat}} - @{{{vendorExtensions.fieldAnnPrefix}}}JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "{{{datetimeFormat}}}") - {{/datetimeFormat}} + {{#vendorExtensions.formatPattern}} + @{{{vendorExtensions.fieldAnnPrefix}}}JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "{{{vendorExtensions.formatPattern}}}") + {{/vendorExtensions.formatPattern}} + {{^vendorExtensions.formatPattern}} + {{#dateTimeFormat}} + @{{{vendorExtensions.fieldAnnPrefix}}}JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "{{{dateTimeFormat}}}") + {{/dateTimeFormat}} + {{/vendorExtensions.formatPattern}} {{/isDateTime}} {{#isDate}} - {{#dateFormat}} + {{#vendorExtensions.formatPattern}} + @{{{vendorExtensions.fieldAnnPrefix}}}JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "{{{vendorExtensions.formatPattern}}}") + {{/vendorExtensions.formatPattern}} + {{^vendorExtensions.formatPattern}} + {{#dateFormat}} @{{{vendorExtensions.fieldAnnPrefix}}}JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "{{{dateFormat}}}") - {{/dateFormat}} + {{/dateFormat}} + {{/vendorExtensions.formatPattern}} {{/isDate}} {{/micronaut_serde_jackson}} {{/jackson}} {{#micronaut_serde_jackson}} {{#isDateTime}} - {{#datatimeFormat}} - @{{{vendorExtensions.fieldAnnPrefix}}}JsonFormat(pattern = "{{{datetimeFormat}}}") - {{/datatimeFormat}} + {{#vendorExtensions.formatPattern}} + @{{{vendorExtensions.fieldAnnPrefix}}}JsonFormat(pattern = "{{{vendorExtensions.formatPattern}}}") + {{/vendorExtensions.formatPattern}} + {{^vendorExtensions.formatPattern}} + {{#dateTimeFormat}} + @{{{vendorExtensions.fieldAnnPrefix}}}JsonFormat(pattern = "{{{dateTimeFormat}}}") + {{/dateTimeFormat}} + {{/vendorExtensions.formatPattern}} {{/isDateTime}} {{#isDate}} - {{#dateFormat}} + {{#vendorExtensions.formatPattern}} + @{{{vendorExtensions.fieldAnnPrefix}}}JsonFormat(pattern = "{{{vendorExtensions.formatPattern}}}") + {{/vendorExtensions.formatPattern}} + {{^vendorExtensions.formatPattern}} + {{#dateFormat}} @{{{vendorExtensions.fieldAnnPrefix}}}JsonFormat(pattern = "{{{dateFormat}}}") - {{/dateFormat}} + {{/dateFormat}} + {{/vendorExtensions.formatPattern}} {{/isDate}} {{/micronaut_serde_jackson}} diff --git a/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/params/validation.mustache b/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/params/validation.mustache index 6219430a15..85c0c13486 100644 --- a/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/params/validation.mustache +++ b/openapi-generator/src/main/resources/templates/kotlin-micronaut/common/params/validation.mustache @@ -30,9 +30,9 @@ {{/isContainer}} {{!Pattern}} {{#pattern}} - {{^isByteArray}} + {{^isByteArray}}{{^isDate}}{{^isDateTime}} @Pattern(regexp = "{{{pattern}}}") - {{/isByteArray}} + {{/isDateTime}}{{/isDate}}{{/isByteArray}} {{/pattern}} {{!Min length && max length}} {{#minLength}} diff --git a/openapi-generator/src/main/resources/templates/kotlin-micronaut/server/controller-interface.mustache b/openapi-generator/src/main/resources/templates/kotlin-micronaut/server/controller-interface.mustache index ba9ee798d8..67756a7167 100644 --- a/openapi-generator/src/main/resources/templates/kotlin-micronaut/server/controller-interface.mustache +++ b/openapi-generator/src/main/resources/templates/kotlin-micronaut/server/controller-interface.mustache @@ -66,7 +66,15 @@ interface {{classname}} { {{{.}}} {{/vendorExtensions.x-operation-extra-annotation}} fun {{nickname}}({{#allParams}} - {{#formatSingleLine}}{{>server/params/annotations}}{{#indent}}{{>common/params/validation}}{{/indent}}{{#isDateTime}}{{#dateFormat}}@Format("{{{datetimeFormat}}}"){{/dateFormat}}{{/isDateTime}}{{#isDate}}{{#dateTimeFormat}}@Format("{{{dateFormat}}}"){{/dateTimeFormat}}{{/isDate}} {{{paramName}}}: {{#isEnum}}{{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}{{/isEnum}}{{^isEnum}}{{{vendorExtensions.typeWithGenericAnnotations}}}{{/isEnum}}{{^-last}},{{/-last}}{{/formatSingleLine}} + {{#formatSingleLine}}{{>server/params/annotations}}{{#indent}}{{>common/params/validation}}{{/indent}} + {{#vendorExtensions.formatPattern}} + @Format("{{{.}}}") + {{/vendorExtensions.formatPattern}} + {{^vendorExtensions.formatPattern}} + {{#isDateTime}}{{#dateTimeFormat}}@Format("{{{dateTimeFormat}}}"){{/dateTimeFormat}}{{/isDateTime}} + {{#isDate}}{{#dateFormat}}@Format("{{{dateFormat}}}"){{/dateFormat}}{{/isDate}} + {{/vendorExtensions.formatPattern}} + {{{paramName}}}: {{#isEnum}}{{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}{{/isEnum}}{{^isEnum}}{{{vendorExtensions.typeWithGenericAnnotations}}}{{/isEnum}}{{^-last}},{{/-last}}{{/formatSingleLine}} {{/allParams}}){{#returnType}}: {{{returnType}}}{{/returnType}} {{/formatNoEmptyLines}} diff --git a/openapi-generator/src/test/java/io/micronaut/openapi/generator/JavaMicronautClientCodegenTest.java b/openapi-generator/src/test/java/io/micronaut/openapi/generator/JavaMicronautClientCodegenTest.java index bb41f5a8f0..d6c51ad090 100644 --- a/openapi-generator/src/test/java/io/micronaut/openapi/generator/JavaMicronautClientCodegenTest.java +++ b/openapi-generator/src/test/java/io/micronaut/openapi/generator/JavaMicronautClientCodegenTest.java @@ -161,7 +161,7 @@ void doGenerateRequiredPropertiesInConstructor() { // Constructor should have properties String modelPath = outputPath + "src/main/java/org/openapitools/model/"; - assertFileContains(modelPath + "Pet.java", "public Pet(String name, List photoUrls)"); + assertFileContains(modelPath + "Pet.java", "public Pet(String name, List<@NotNull String> photoUrls)"); assertFileNotContains(modelPath + "Pet.java", "public Pet()"); } @@ -328,8 +328,12 @@ void testGenericAnnotations() { String apiPath = outputPath + "src/main/java/org/openapitools/api/"; String modelPath = outputPath + "src/main/java/org/openapitools/model/"; - assertFileContains(apiPath + "BooksApi.java", "List<@Pattern(regexp = \"[a-zA-Z ]+\") @Size(max = 10) @NotNull String> requestBody"); - assertFileContains(modelPath + "CountsContainer.java", "private List<@NotEmpty List<@NotNull List<@Size(max = 10) @NotNull String>>> counts;"); + assertFileContains(apiPath + "BooksApi.java", + "@QueryValue(\"before\") @NotNull @Format(\"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'\") ZonedDateTime before,", + "List<@Pattern(regexp = \"[a-zA-Z ]+\") @Size(max = 10) @NotNull String> requestBody", + "" + ); + assertFileContains(modelPath + "CountsContainer.java", "private List<@NotEmpty List<@NotNull List<@Size(max = 10) @NotNull ZonedDateTime>>> counts;"); assertFileContains(modelPath + "BooksContainer.java", "private List<@Pattern(regexp = \"[a-zA-Z ]+\") @Size(max = 10) @NotNull String> books;"); } diff --git a/openapi-generator/src/test/java/io/micronaut/openapi/generator/JavaMicronautServerCodegenTest.java b/openapi-generator/src/test/java/io/micronaut/openapi/generator/JavaMicronautServerCodegenTest.java index d6617f2cd6..ec387e4344 100644 --- a/openapi-generator/src/test/java/io/micronaut/openapi/generator/JavaMicronautServerCodegenTest.java +++ b/openapi-generator/src/test/java/io/micronaut/openapi/generator/JavaMicronautServerCodegenTest.java @@ -142,7 +142,7 @@ void doGenerateRequiredPropertiesInConstructor() { // Constructor should have properties String modelPath = outputPath + "src/main/java/org/openapitools/model/"; - assertFileContains(modelPath + "Pet.java", "public Pet(String name, List photoUrls)"); + assertFileContains(modelPath + "Pet.java", "public Pet(String name, List<@NotNull String> photoUrls)"); assertFileContains(modelPath + "Pet.java", "private Pet()"); } @@ -353,7 +353,7 @@ void testGenericAnnotations() { String modelPath = outputPath + "src/main/java/org/openapitools/model/"; assertFileContains(apiPath + "BooksApi.java", "@Body @NotNull List<@Pattern(regexp = \"[a-zA-Z ]+\") @Size(max = 10) @NotNull String> requestBody"); - assertFileContains(modelPath + "CountsContainer.java", "private List<@NotEmpty List<@NotNull List<@Size(max = 10) @NotNull String>>> counts;"); + assertFileContains(modelPath + "CountsContainer.java", "private List<@NotEmpty List<@NotNull List<@Size(max = 10) @NotNull ZonedDateTime>>> counts;"); assertFileContains(modelPath + "BooksContainer.java", "private List<@Pattern(regexp = \"[a-zA-Z ]+\") @Size(max = 10) @NotNull String> books;"); } diff --git a/openapi-generator/src/test/java/io/micronaut/openapi/generator/KotlinMicronautClientCodegenTest.java b/openapi-generator/src/test/java/io/micronaut/openapi/generator/KotlinMicronautClientCodegenTest.java index 4fc59e1f99..6509200156 100644 --- a/openapi-generator/src/test/java/io/micronaut/openapi/generator/KotlinMicronautClientCodegenTest.java +++ b/openapi-generator/src/test/java/io/micronaut/openapi/generator/KotlinMicronautClientCodegenTest.java @@ -375,8 +375,11 @@ void testGenericAnnotations() { String apiPath = outputPath + "src/main/kotlin/org/openapitools/api/"; String modelPath = outputPath + "src/main/kotlin/org/openapitools/model/"; - assertFileContains(apiPath + "BooksApi.kt", "requestBody: List<@Pattern(regexp = \"[a-zA-Z ]+\") @Size(max = 10) @NotNull String>"); - assertFileContains(modelPath + "CountsContainer.kt", "var counts: List<@NotEmpty List<@NotNull List<@Size(max = 10) @NotNull String>>>"); + assertFileContains(apiPath + "BooksApi.kt", + "requestBody: List<@Pattern(regexp = \"[a-zA-Z ]+\") @Size(max = 10) @NotNull String>", + "@QueryValue(\"before\") @NotNull @Format(\"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'\") before: ZonedDateTime," + ); + assertFileContains(modelPath + "CountsContainer.kt", "var counts: List<@NotEmpty List<@NotNull List<@Size(max = 10) @NotNull ZonedDateTime>>>"); assertFileContains(modelPath + "BooksContainer.kt", "var books: List<@Pattern(regexp = \"[a-zA-Z ]+\") @Size(max = 10) @NotNull String>"); } diff --git a/openapi-generator/src/test/java/io/micronaut/openapi/generator/KotlinMicronautServerCodegenTest.java b/openapi-generator/src/test/java/io/micronaut/openapi/generator/KotlinMicronautServerCodegenTest.java index 4266773d64..1ee7562433 100644 --- a/openapi-generator/src/test/java/io/micronaut/openapi/generator/KotlinMicronautServerCodegenTest.java +++ b/openapi-generator/src/test/java/io/micronaut/openapi/generator/KotlinMicronautServerCodegenTest.java @@ -1,5 +1,6 @@ package io.micronaut.openapi.generator; +import io.micronaut.openapi.generator.assertions.TestUtils; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.servers.Server; @@ -457,7 +458,7 @@ void testGenericAnnotations() { String modelPath = outputPath + "src/main/kotlin/org/openapitools/model/"; assertFileContains(apiPath + "BooksApi.kt", "@Body @NotNull requestBody: List<@Pattern(regexp = \"[a-zA-Z ]+\") @Size(max = 10) @NotNull String>"); - assertFileContains(modelPath + "CountsContainer.kt", "var counts: List<@NotEmpty List<@NotNull List<@Size(max = 10) @NotNull String>>>"); + assertFileContains(modelPath + "CountsContainer.kt", "var counts: List<@NotEmpty List<@NotNull List<@Size(max = 10) @NotNull ZonedDateTime>>>"); assertFileContains(modelPath + "BooksContainer.kt", "var books: List<@Pattern(regexp = \"[a-zA-Z ]+\") @Size(max = 10) @NotNull String>"); } diff --git a/openapi-generator/src/test/resources/3_0/issue_11772.yml b/openapi-generator/src/test/resources/3_0/issue_11772.yml index 68e50e0f9e..79b535f964 100644 --- a/openapi-generator/src/test/resources/3_0/issue_11772.yml +++ b/openapi-generator/src/test/resources/3_0/issue_11772.yml @@ -124,6 +124,7 @@ components: description: The date the employee accepted the terms of the usage agreement. type: string format: date + pattern: "yyyy-MM-dd" example: "2021-02-09" termsVersionNumber: description: The version number of terms of the usage agreement. diff --git a/openapi-generator/src/test/resources/3_0/modelwithprimitivelist.yml b/openapi-generator/src/test/resources/3_0/modelwithprimitivelist.yml index 3404f278be..330941428b 100644 --- a/openapi-generator/src/test/resources/3_0/modelwithprimitivelist.yml +++ b/openapi-generator/src/test/resources/3_0/modelwithprimitivelist.yml @@ -15,6 +15,15 @@ paths: tags: [books] summary: Add a new book operationId: addBook + parameters: + - name: before + in: query + description: The date to search before (ATOM format) + required: true + schema: + type: string + format: date-time + pattern: "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" requestBody: required: true content: @@ -136,6 +145,8 @@ components: type: array items: type: string + format: date-time + pattern: "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" maxLength: 10 minLength: 1 minItems: 1