From 5a8181c9151c863a293acb60165d26dfe349add6 Mon Sep 17 00:00:00 2001 From: Nicklas Lundin Date: Wed, 5 Jul 2023 21:58:32 +0200 Subject: [PATCH] Format ISO8601 dates with milliseconds. Serializes Date objects (backing of Value.Instant) as ISO8601 WITH milliseconds. Also adds fallback parsing support where milliseconds are not passed. --- .../main/java/dev/openfeature/sdk/Value.kt | 22 +++++++++++++------ .../java/dev/openfeature/sdk/ValueTests.kt | 10 ++++++--- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/OpenFeature/src/main/java/dev/openfeature/sdk/Value.kt b/OpenFeature/src/main/java/dev/openfeature/sdk/Value.kt index ad25cb6..20fa25c 100644 --- a/OpenFeature/src/main/java/dev/openfeature/sdk/Value.kt +++ b/OpenFeature/src/main/java/dev/openfeature/sdk/Value.kt @@ -53,6 +53,7 @@ sealed interface Value { override fun equals(other: Any?): kotlin.Boolean { return other is Null } + override fun hashCode(): Int { return javaClass.hashCode() } @@ -73,14 +74,21 @@ object ValueSerializer : JsonContentPolymorphicSerializer(Value::class) { } } +@SuppressLint("SimpleDateFormat") object DateSerializer : KSerializer { - @SuppressLint("SimpleDateFormat") - private val df = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").apply { timeZone = TimeZone.getTimeZone("UTC") } + private val dateFormatter = + SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").apply { timeZone = TimeZone.getTimeZone("UTC") } + private val fallbackDateFormatter = + SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").apply { timeZone = TimeZone.getTimeZone("UTC") } override val descriptor = PrimitiveSerialDescriptor("Instant", PrimitiveKind.STRING) - override fun serialize(encoder: Encoder, value: Date) = encoder.encodeString(df.format(value)) - override fun deserialize(decoder: Decoder): Date = try { - df.parse(decoder.decodeString()) ?: throw IllegalArgumentException("unable to parse ${decoder.decodeString()}") - } catch (e: Exception) { - throw IllegalArgumentException(e) + override fun serialize(encoder: Encoder, value: Date) = encoder.encodeString(dateFormatter.format(value)) + override fun deserialize(decoder: Decoder): Date = with(decoder.decodeString()) { + try { + dateFormatter.parse(this) + ?: throw IllegalArgumentException("unable to parse $this") + } catch (e: Exception) { + fallbackDateFormatter.parse(this) + ?: throw IllegalArgumentException("unable to parse $this") + } } } \ No newline at end of file diff --git a/OpenFeature/src/test/java/dev/openfeature/sdk/ValueTests.kt b/OpenFeature/src/test/java/dev/openfeature/sdk/ValueTests.kt index 91e8d62..e71727e 100644 --- a/OpenFeature/src/test/java/dev/openfeature/sdk/ValueTests.kt +++ b/OpenFeature/src/test/java/dev/openfeature/sdk/ValueTests.kt @@ -48,8 +48,12 @@ class ValueTests { @Test fun testStructShouldConvertToStruct() { - val value = Value.Structure(mapOf("field1" to Value.Integer(3), "field2" to Value.String("test"))) - Assert.assertEquals(value.asStructure(), mapOf("field1" to Value.Integer(3), "field2" to Value.String("test"))) + val value = + Value.Structure(mapOf("field1" to Value.Integer(3), "field2" to Value.String("test"))) + Assert.assertEquals( + value.asStructure(), + mapOf("field1" to Value.Integer(3), "field2" to Value.String("test")) + ) } @Test @@ -82,7 +86,7 @@ class ValueTests { @Test fun testJsonDecode() { - val stringInstant = "2023-03-01T14:01:46Z" + val stringInstant = "2023-03-01T14:01:46.321Z" val json = "{" + " \"structure\": {" + " \"null\": {}," +