From 63d70ac08953a76a829f48fc4be38b00f40a3bda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mathieu?= Date: Wed, 23 Oct 2024 15:55:32 +0200 Subject: [PATCH] fix(core): serialize duration as strings Fixes #5615 Since Jackson 2.10, to serialize duration as string we need to switch WRITE_DURATIONS_AS_TIMESTAMPS off as it no longuer use WRITE_DATES_AS_TIMESTAMPS. I tested and an old flow with a duration as timestamp is still readable so this is a backward compatible change. But for the State duration, we still need to serialize it as a number as the database expect a number --- core/src/main/java/io/kestra/core/models/flows/State.java | 7 ++++++- .../java/io/kestra/core/serializers/JacksonMapper.java | 1 + core/src/test/java/io/kestra/plugin/core/kv/SetTest.java | 3 +-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/io/kestra/core/models/flows/State.java b/core/src/main/java/io/kestra/core/models/flows/State.java index 76cd800f14a..5bef62cbc16 100644 --- a/core/src/main/java/io/kestra/core/models/flows/State.java +++ b/core/src/main/java/io/kestra/core/models/flows/State.java @@ -1,8 +1,12 @@ package io.kestra.core.models.flows; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.ser.DurationSerializer; +import com.fasterxml.jackson.datatype.jsr310.util.DurationUnitConverter; import io.micronaut.core.annotation.Introspected; import lombok.Value; import lombok.extern.slf4j.Slf4j; @@ -84,10 +88,11 @@ public State reset() { } @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(shape = JsonFormat.Shape.NUMBER_INT) // force serialization as timestamp as the database column is using a number public Duration getDuration() { return Duration.between( this.histories.getFirst().getDate(), - this.histories.size() > 1 ? this.histories.get(this.histories.size() - 1).getDate() : Instant.now() + this.histories.size() > 1 ? this.histories.getLast().getDate() : Instant.now() ); } diff --git a/core/src/main/java/io/kestra/core/serializers/JacksonMapper.java b/core/src/main/java/io/kestra/core/serializers/JacksonMapper.java index abcb9528f78..cc9db385b7f 100644 --- a/core/src/main/java/io/kestra/core/serializers/JacksonMapper.java +++ b/core/src/main/java/io/kestra/core/serializers/JacksonMapper.java @@ -121,6 +121,7 @@ public static ObjectMapper ofIon() { private static ObjectMapper configure(ObjectMapper mapper) { return mapper .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .configure(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, false) .setSerializationInclusion(JsonInclude.Include.NON_NULL) .registerModule(new JavaTimeModule()) .registerModule(new Jdk8Module()) diff --git a/core/src/test/java/io/kestra/plugin/core/kv/SetTest.java b/core/src/test/java/io/kestra/plugin/core/kv/SetTest.java index 36e79846610..58abf00303b 100644 --- a/core/src/test/java/io/kestra/plugin/core/kv/SetTest.java +++ b/core/src/test/java/io/kestra/plugin/core/kv/SetTest.java @@ -209,8 +209,7 @@ void typeSpecified() throws Exception { assertThat(kv.getValue(TEST_KEY).get().value(), is(Instant.parse("2023-05-02T01:02:03Z"))); set.toBuilder().value("P1DT5S").kvType(KVType.DURATION).build().run(runContext); - // TODO Hack meanwhile we handle duration serialization as currently they are stored as bigint... - assertThat((long) Double.parseDouble(kv.getValue(TEST_KEY).get().value().toString()), is(Duration.ofDays(1).plus(Duration.ofSeconds(5)).toSeconds())); + assertThat(kv.getValue(TEST_KEY).get().value(), is(Duration.ofDays(1).plus(Duration.ofSeconds(5)))); set.toBuilder().value("[{\"some\":\"value\"},{\"another\":\"value\"}]").kvType(KVType.JSON).build().run(runContext); assertThat(kv.getValue(TEST_KEY).get().value(), is(List.of(Map.of("some", "value"), Map.of("another", "value"))));