From eb37957d82835dd8186f3ba07e9fe91dc2bc2a42 Mon Sep 17 00:00:00 2001 From: Chuckame Date: Thu, 11 Jul 2024 17:55:54 +0200 Subject: [PATCH] feat: Remove AvroDecimal defaults --- Migrating-from-v1.md | 57 +++++++++++++++++- README.md | 58 +++++++++---------- api/avro4k-core.api | 14 ----- .../avrokotlin/benchmark/internal/Clients.kt | 6 +- .../github/avrokotlin/avro4k/Annotations.kt | 4 +- .../serializer/JavaStdLibSerializers.kt | 47 +++++---------- .../encoding/AvroDefaultEncodingTest.kt | 10 ++-- .../avro4k/encoding/BytesEncodingTest.kt | 4 +- .../encoding/LogicalTypesEncodingTest.kt | 34 +++-------- .../avro4k/encoding/MapEncodingTest.kt | 9 +-- .../avro4k/schema/AvroDefaultSchemaTest.kt | 5 ++ .../avro4k/schema/BigDecimalSchemaTest.kt | 13 ++++- 12 files changed, 137 insertions(+), 124 deletions(-) diff --git a/Migrating-from-v1.md b/Migrating-from-v1.md index dbc96f49..1fdf5c2b 100644 --- a/Migrating-from-v1.md +++ b/Migrating-from-v1.md @@ -136,4 +136,59 @@ data class TheDataClass( val listOfBytes: ByteArray, val setOfBytes: ByteArray, ) -``` \ No newline at end of file +``` + +## Serialize a `BigDecimal` as a string + +> [!INFO] +> Note that you can replace `@Serializable(with = BigDecimalAsStringSerializer::class)` with `@Contextual` to use the default global `BigDecimalSerializer` already registered, +> which is already compatible with the `@AvroStringable` feature. + +```kotlin +// Previously +@Serializable +data class MyData( + @Serializable(with = BigDecimalAsStringSerializer::class) + val bigDecimalAsString: BigDecimal, +) + +// Now +@Serializable +data class MyData( + @Contextual + @AvroStringable + val bigDecimalAsString: BigDecimal, +) +``` + +## Serialize a `BigDecimal` as a BYTES or FIXED + +Previously, a BigDecimal was serialized as bytes with 2 as scale and 8 as precision. Now you have to explicitly declare the needed scale and precision using `@AvroDecimal`, +or use `@AvroStringable` to serialize it as a string which doesn't need scale nor precision. + +> [!INFO] +> Note that you can replace `@Serializable(with = BigDecimalSerializer::class)` with `@Contextual` to use the default global `BigDecimalSerializer` already registered. + +```kotlin +// Previously +@Serializable +data class MyData( + @Serializable(with = BigDecimalSerializer::class) + val bigDecimalAsBytes: BigDecimal, + @AvroFixed(10) + @Serializable(with = BigDecimalSerializer::class) + val bigDecimalAsFixed: BigDecimal, +) + +// Now +@Serializable +data class MyData( + @Contextual + @AvroDecimal(scale = 8, precision = 2) + val bigDecimalAsBytes: BigDecimal, + @Contextual + @AvroFixed(10) + @AvroDecimal(scale = 8, precision = 2) + val bigDecimalAsFixed: BigDecimal, +) +``` diff --git a/README.md b/README.md index d28d2c57..ecb64d98 100644 --- a/README.md +++ b/README.md @@ -327,35 +327,35 @@ yourAvroInstance.schema() ## Types matrix -| Kotlin type | Generated schema type | Other compatible writer types | Compatible logical type | Note / Serializer class | -|------------------------------|-----------------------|--------------------------------------------------------------|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Boolean` | `boolean` | `string` | | | -| `Byte`, `Short`, `Int` | `int` | `long`, `float`, `double`, `string` | | | -| `Long` | `long` | `int`, `float`, `double`, `string` | | | -| `Float` | `float` | `double`, `string` | | | -| `Double` | `double` | `float`, `string` | | | -| `Char` | `int` | `string` (exactly 1 char required) | `char` | The value serialized is the char code. When reading from a `string`, requires exactly 1 char | -| `String` | `string` | `bytes` (UTF8), `fixed` (UTF8) | | | -| `ByteArray` | `bytes` | `string` (UTF8), `fixed` (UTF8) | | | -| `Map<*, *>` | `map` | | | The map key must be string-able. Mainly everything is string-able except null and composite types (collection, data classes) | -| `Collection<*>` | `array` | | | | -| `data class` | `record` | | | | -| `enum class` | `enum` | `string` | | | -| `@AvroFixed`-compatible | `fixed` | `bytes`, `string` | | Throws an error at runtime if the writer type is not present in the column "other compatible writer types" | -| `@AvroStringable`-compatible | `string` | `int`, `long`, `float`, `double`, `string`, `fixed`, `bytes` | | Ignored when the writer type is not present in the column "other compatible writer types" | -| `java.math.BigDecimal` | `bytes` | `int`, `long`, `float`, `double`, `string`, `fixed`, `bytes` | `decimal` | By default, the scale is `2` and the precision `8`. To change it, annotate the field with `@AvroDecimal` | -| `java.math.BigDecimal` | `string` | `int`, `long`, `float`, `double`, `fixed`, `bytes` | | To use it, [register the serializer](#support-additional-non-serializable-types) `com.github.avrokotlin.avro4k.serializer.BigDecimalAsStringSerializer`. `@AvroDecimal` is ignored in that case | -| `java.util.UUID` | `string` | | `uuid` | To use it, just annotate the field with `@Contextual` | -| `java.net.URL` | `string` | | | To use it, just annotate the field with `@Contextual` | -| `java.math.BigInteger` | `string` | `int`, `long`, `float`, `double` | | To use it, just annotate the field with `@Contextual` | -| `java.time.LocalDate` | `int` | `long`, `string` (ISO8601) | `date` | To use it, just annotate the field with `@Contextual` | -| `java.time.Instant` | `long` | `string` (ISO8601) | `timestamp-millis` | To use it, just annotate the field with `@Contextual` | -| `java.time.Instant` | `long` | `string` (ISO8601) | `timestamp-micros` | To use it, [register the serializer](#support-additional-non-serializable-types) `com.github.avrokotlin.avro4k.serializer.InstantToMicroSerializer` | -| `java.time.LocalDateTime` | `long` | `string` (ISO8601) | `timestamp-millis` | To use it, just annotate the field with `@Contextual` | -| `java.time.LocalTime` | `int` | `long`, `string` (ISO8601) | `time-millis` | To use it, just annotate the field with `@Contextual` | -| `java.time.Duration` | `fixed` of 12 | `string` (ISO8601) | `duration` | To use it, just annotate the field with `@Contextual` | -| `java.time.Period` | `fixed` of 12 | `string` (ISO8601) | `duration` | To use it, just annotate the field with `@Contextual` | -| `kotlin.time.Duration` | `fixed` of 12 | `string` (ISO8601) | `duration` | | +| Kotlin type | Generated schema type | Other compatible writer types | Compatible logical type | Note / Serializer class | +|------------------------------|-----------------------|--------------------------------------------------------------|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------| +| `Boolean` | `boolean` | `string` | | | +| `Byte`, `Short`, `Int` | `int` | `long`, `float`, `double`, `string` | | | +| `Long` | `long` | `int`, `float`, `double`, `string` | | | +| `Float` | `float` | `double`, `string` | | | +| `Double` | `double` | `float`, `string` | | | +| `Char` | `int` | `string` (exactly 1 char required) | `char` | The value serialized is the char code. When reading from a `string`, requires exactly 1 char | +| `String` | `string` | `bytes` (UTF8), `fixed` (UTF8) | | | +| `ByteArray` | `bytes` | `string` (UTF8), `fixed` (UTF8) | | | +| `Map<*, *>` | `map` | | | The map key must be string-able. Mainly everything is string-able except null and composite types (collection, data classes) | +| `Collection<*>` | `array` | | | | +| `data class` | `record` | | | | +| `enum class` | `enum` | `string` | | | +| `@AvroFixed`-compatible | `fixed` | `bytes`, `string` | | Throws an error at runtime if the writer type is not present in the column "other compatible writer types" | +| `@AvroStringable`-compatible | `string` | `int`, `long`, `float`, `double`, `string`, `fixed`, `bytes` | | Ignored when the writer type is not present in the column "other compatible writer types" | +| `java.math.BigDecimal` | `bytes` | `int`, `long`, `float`, `double`, `string`, `fixed`, `bytes` | `decimal` | To use it, annotate the field with `@AvroDecimal` to give the `scale` and the `precision` | +| `java.math.BigDecimal` | `string` | `int`, `long`, `float`, `double`, `fixed`, `bytes` | | To use it, annotate the field with `@AvroStringable`. `@AvroDecimal` is ignored in that case | +| `java.util.UUID` | `string` | | `uuid` | To use it, just annotate the field with `@Contextual` | +| `java.net.URL` | `string` | | | To use it, just annotate the field with `@Contextual` | +| `java.math.BigInteger` | `string` | `int`, `long`, `float`, `double` | | To use it, just annotate the field with `@Contextual` | +| `java.time.LocalDate` | `int` | `long`, `string` (ISO8601) | `date` | To use it, just annotate the field with `@Contextual` | +| `java.time.Instant` | `long` | `string` (ISO8601) | `timestamp-millis` | To use it, just annotate the field with `@Contextual` | +| `java.time.Instant` | `long` | `string` (ISO8601) | `timestamp-micros` | To use it, [register the serializer](#support-additional-non-serializable-types) `com.github.avrokotlin.avro4k.serializer.InstantToMicroSerializer` | +| `java.time.LocalDateTime` | `long` | `string` (ISO8601) | `timestamp-millis` | To use it, just annotate the field with `@Contextual` | +| `java.time.LocalTime` | `int` | `long`, `string` (ISO8601) | `time-millis` | To use it, just annotate the field with `@Contextual` | +| `java.time.Duration` | `fixed` of 12 | `string` (ISO8601) | `duration` | To use it, just annotate the field with `@Contextual` | +| `java.time.Period` | `fixed` of 12 | `string` (ISO8601) | `duration` | To use it, just annotate the field with `@Contextual` | +| `kotlin.time.Duration` | `fixed` of 12 | `string` (ISO8601) | `duration` | | > [!NOTE] > For more details, check the [built-in classes in kotlinx-serialization](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/builtin-classes.md) diff --git a/api/avro4k-core.api b/api/avro4k-core.api index b65aa06d..9c85b916 100644 --- a/api/avro4k-core.api +++ b/api/avro4k-core.api @@ -62,7 +62,6 @@ public abstract interface annotation class com/github/avrokotlin/avro4k/AvroDeci public synthetic class com/github/avrokotlin/avro4k/AvroDecimal$Impl : com/github/avrokotlin/avro4k/AvroDecimal { public fun (II)V - public synthetic fun (IIILkotlin/jvm/internal/DefaultConstructorMarker;)V public final synthetic fun precision ()I public final synthetic fun scale ()I } @@ -375,19 +374,6 @@ public final class com/github/avrokotlin/avro4k/serializer/AvroSerializerKt { public static final fun getStringable (Lcom/github/avrokotlin/avro4k/serializer/ElementLocation;)Lcom/github/avrokotlin/avro4k/AvroStringable; } -public final class com/github/avrokotlin/avro4k/serializer/BigDecimalAsStringSerializer : com/github/avrokotlin/avro4k/serializer/AvroSerializer { - public static final field INSTANCE Lcom/github/avrokotlin/avro4k/serializer/BigDecimalAsStringSerializer; - public synthetic fun deserializeAvro (Lcom/github/avrokotlin/avro4k/AvroDecoder;)Ljava/lang/Object; - public fun deserializeAvro (Lcom/github/avrokotlin/avro4k/AvroDecoder;)Ljava/math/BigDecimal; - public synthetic fun deserializeGeneric (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; - public fun deserializeGeneric (Lkotlinx/serialization/encoding/Decoder;)Ljava/math/BigDecimal; - public fun getSchema (Lcom/github/avrokotlin/avro4k/serializer/SchemaSupplierContext;)Lorg/apache/avro/Schema; - public synthetic fun serializeAvro (Lcom/github/avrokotlin/avro4k/AvroEncoder;Ljava/lang/Object;)V - public fun serializeAvro (Lcom/github/avrokotlin/avro4k/AvroEncoder;Ljava/math/BigDecimal;)V - public synthetic fun serializeGeneric (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V - public fun serializeGeneric (Lkotlinx/serialization/encoding/Encoder;Ljava/math/BigDecimal;)V -} - public final class com/github/avrokotlin/avro4k/serializer/BigDecimalSerializer : com/github/avrokotlin/avro4k/serializer/AvroSerializer { public static final field INSTANCE Lcom/github/avrokotlin/avro4k/serializer/BigDecimalSerializer; public synthetic fun deserializeAvro (Lcom/github/avrokotlin/avro4k/AvroDecoder;)Ljava/lang/Object; diff --git a/benchmark/src/main/kotlin/com/github/avrokotlin/benchmark/internal/Clients.kt b/benchmark/src/main/kotlin/com/github/avrokotlin/benchmark/internal/Clients.kt index a571701f..ac63ea02 100644 --- a/benchmark/src/main/kotlin/com/github/avrokotlin/benchmark/internal/Clients.kt +++ b/benchmark/src/main/kotlin/com/github/avrokotlin/benchmark/internal/Clients.kt @@ -10,9 +10,10 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize import com.fasterxml.jackson.databind.deser.std.StdDeserializer import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper import com.fasterxml.jackson.databind.ser.std.StdSerializer -import com.github.avrokotlin.avro4k.serializer.BigDecimalAsStringSerializer +import com.github.avrokotlin.avro4k.AvroStringable import com.github.avrokotlin.avro4k.serializer.InstantSerializer import com.github.avrokotlin.avro4k.serializer.LocalDateSerializer +import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable import java.math.BigDecimal import java.time.Instant @@ -44,7 +45,8 @@ internal data class Client( val id: Long, val index: Int, val isActive: Boolean, - @Serializable(with = BigDecimalAsStringSerializer::class) + @Contextual + @AvroStringable @JsonFormat(shape = JsonFormat.Shape.STRING) val balance: BigDecimal?, val picture: ByteArray?, diff --git a/src/main/kotlin/com/github/avrokotlin/avro4k/Annotations.kt b/src/main/kotlin/com/github/avrokotlin/avro4k/Annotations.kt index a1474702..02f28a16 100644 --- a/src/main/kotlin/com/github/avrokotlin/avro4k/Annotations.kt +++ b/src/main/kotlin/com/github/avrokotlin/avro4k/Annotations.kt @@ -79,8 +79,8 @@ public annotation class AvroStringable @ExperimentalSerializationApi @Target(AnnotationTarget.PROPERTY) public annotation class AvroDecimal( - val scale: Int = 2, - val precision: Int = 8, + val scale: Int, + val precision: Int, ) /** diff --git a/src/main/kotlin/com/github/avrokotlin/avro4k/serializer/JavaStdLibSerializers.kt b/src/main/kotlin/com/github/avrokotlin/avro4k/serializer/JavaStdLibSerializers.kt index 2a39a958..5eea0d1d 100644 --- a/src/main/kotlin/com/github/avrokotlin/avro4k/serializer/JavaStdLibSerializers.kt +++ b/src/main/kotlin/com/github/avrokotlin/avro4k/serializer/JavaStdLibSerializers.kt @@ -6,6 +6,7 @@ import com.github.avrokotlin.avro4k.AvroDecoder import com.github.avrokotlin.avro4k.AvroEncoder import com.github.avrokotlin.avro4k.decodeResolvingAny import com.github.avrokotlin.avro4k.encodeResolving +import com.github.avrokotlin.avro4k.internal.AvroSchemaGenerationException import com.github.avrokotlin.avro4k.internal.BadEncodedValueError import com.github.avrokotlin.avro4k.internal.UnexpectedDecodeSchemaError import com.github.avrokotlin.avro4k.internal.copy @@ -180,15 +181,21 @@ public object BigIntegerSerializer : AvroSerializer(BigInteger::clas } } -private val converter = Conversions.DecimalConversion() -private val defaultAnnotation = AvroDecimal() - public object BigDecimalSerializer : AvroSerializer(BigDecimal::class.qualifiedName!!) { + private val converter = Conversions.DecimalConversion() + override fun getSchema(context: SchemaSupplierContext): Schema { - val logicalType = (context.inlinedElements.firstNotNullOfOrNull { it.decimal } ?: defaultAnnotation).logicalType + val logicalType = context.inlinedElements.firstNotNullOfOrNull { it.decimal }?.logicalType + + fun nonNullLogicalType(): LogicalTypes.Decimal { + if (logicalType == null) { + throw AvroSchemaGenerationException("BigDecimal requires @${AvroDecimal::class.qualifiedName} to works with 'fixed' or 'bytes' schema types.") + } + return logicalType + } return context.inlinedElements.firstNotNullOfOrNull { - it.stringable?.createSchema() ?: it.fixed?.createSchema(it)?.copy(logicalType = logicalType) - } ?: Schema.create(Schema.Type.BYTES).copy(logicalType = logicalType) + it.stringable?.createSchema() ?: it.fixed?.createSchema(it)?.copy(logicalType = nonNullLogicalType()) + } ?: Schema.create(Schema.Type.BYTES).copy(logicalType = nonNullLogicalType()) } override fun serializeAvro( @@ -308,32 +315,4 @@ public object BigDecimalSerializer : AvroSerializer(BigDecimal::clas get() { return LogicalTypes.decimal(precision, scale) } -} - -public object BigDecimalAsStringSerializer : AvroSerializer(BigDecimal::class.qualifiedName!!) { - override fun getSchema(context: SchemaSupplierContext): Schema { - return Schema.create(Schema.Type.STRING) - } - - override fun serializeAvro( - encoder: AvroEncoder, - value: BigDecimal, - ) { - BigDecimalSerializer.serializeAvro(encoder, value) - } - - override fun serializeGeneric( - encoder: Encoder, - value: BigDecimal, - ) { - encoder.encodeString(value.toString()) - } - - override fun deserializeAvro(decoder: AvroDecoder): BigDecimal { - return BigDecimalSerializer.deserializeAvro(decoder) - } - - override fun deserializeGeneric(decoder: Decoder): BigDecimal { - return decoder.decodeString().toBigDecimal() - } } \ No newline at end of file diff --git a/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/AvroDefaultEncodingTest.kt b/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/AvroDefaultEncodingTest.kt index c6c64e7e..3502b33f 100644 --- a/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/AvroDefaultEncodingTest.kt +++ b/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/AvroDefaultEncodingTest.kt @@ -93,28 +93,30 @@ internal class AvroDefaultEncodingTest : StringSpec({ @AvroDefault("""[{"content":"bar"}]""") val filledFooList: List, @Contextual - @AvroDecimal(scale = 0) + @AvroDecimal(scale = 0, precision = 8) @AvroDefault("\u0000") val bigDecimal: BigDecimal, @Contextual - @AvroDecimal(scale = 0) + @AvroDecimal(scale = 0, precision = 8) @AvroDefault("\u0000") val bigDecimalNullable: BigDecimal?, @Contextual + @AvroDecimal(scale = 2, precision = 8) @AvroDefault("null") val bigDecimalNullableNull: BigDecimal?, @Contextual @AvroFixed(size = 16) - @AvroDecimal(scale = 0) + @AvroDecimal(scale = 0, precision = 8) @AvroDefault("\u0000") val bigDecimalFixed: BigDecimal, @Contextual @AvroFixed(size = 16) - @AvroDecimal(scale = 0) + @AvroDecimal(scale = 0, precision = 8) @AvroDefault("\u0000") val bigDecimalFixedNullable: BigDecimal?, @Contextual @AvroFixed(size = 16) + @AvroDecimal(scale = 2, precision = 8) @AvroDefault("null") val bigDecimalFixedNullableNull: BigDecimal?, val kotlinDefault: Int = 42, diff --git a/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/BytesEncodingTest.kt b/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/BytesEncodingTest.kt index 2c4f07f5..8b0d8c40 100644 --- a/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/BytesEncodingTest.kt +++ b/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/BytesEncodingTest.kt @@ -14,11 +14,11 @@ internal class BytesEncodingTest : StringSpec({ AvroAssertions.assertThat(NullableByteArrayTest(null)) .isEncodedAs(record(null)) - AvroAssertions.assertThat() + AvroAssertions.assertThat(byteArrayOf(1, 4, 9)) .generatesSchema(Schema.create(Schema.Type.BYTES).nullable) - AvroAssertions.assertThat(byteArrayOf(1, 4, 9)) .isEncodedAs(byteArrayOf(1, 4, 9)) AvroAssertions.assertThat(null) + .generatesSchema(Schema.create(Schema.Type.BYTES).nullable) .isEncodedAs(null) } diff --git a/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/LogicalTypesEncodingTest.kt b/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/LogicalTypesEncodingTest.kt index 8bbcaca2..1e60f0d8 100644 --- a/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/LogicalTypesEncodingTest.kt +++ b/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/LogicalTypesEncodingTest.kt @@ -2,10 +2,11 @@ package com.github.avrokotlin.avro4k.encoding import com.github.avrokotlin.avro4k.Avro import com.github.avrokotlin.avro4k.AvroAssertions +import com.github.avrokotlin.avro4k.AvroDecimal import com.github.avrokotlin.avro4k.AvroFixed +import com.github.avrokotlin.avro4k.AvroStringable import com.github.avrokotlin.avro4k.record import com.github.avrokotlin.avro4k.schema -import com.github.avrokotlin.avro4k.serializer.BigDecimalAsStringSerializer import com.github.avrokotlin.avro4k.serializer.InstantToMicroSerializer import io.kotest.core.spec.style.StringSpec import kotlinx.serialization.Contextual @@ -59,25 +60,6 @@ internal class LogicalTypesEncodingTest : StringSpec({ (36.hours + 24456.seconds).toJavaDuration() ) ) -// .generatesSchema( -// SchemaBuilder.record("LogicalTypes") -// .fields() -// .name("decimalBytes").type(SchemaBuilder.builder().bytesType().copy(logicalType = org.apache.avro.LogicalTypes.decimal(8, 2))).noDefault() -// .name("decimalFixed").type(SchemaBuilder.builder().fixed("decimalFixed").size(42).copy(logicalType = org.apache.avro.LogicalTypes.decimal(8, 2))).noDefault() -// .name("decimalString").type().stringType().noDefault() -// .name("date").type().intType().noDefault() -// .name("time").type().intType().noDefault() -// .name("instant").type().longType().noDefault() -// .name("instantMicros").type().longType().noDefault() -// .name("uuid").type().stringType().noDefault() -// .name("url").type().stringType().noDefault() -// .name("bigInteger").type().stringType().noDefault() -// .name("dateTime").type().longType().noDefault() -// .name("period").type(SchemaBuilder.fixed("duration").size(12).copy(logicalType = LogicalType("duration"))).noDefault() -// .name("javaDuration").type("duration").noDefault() -// .name("kotlinDuration").type("duration").noDefault() -// .endRecord() -// ) .isEncodedAs( record( Conversions.DecimalConversion().toBytes( @@ -192,9 +174,9 @@ internal class LogicalTypesEncodingTest : StringSpec({ @Serializable @SerialName("LogicalTypes") private data class LogicalTypes( - @Contextual val decimalBytes: BigDecimal, - @Contextual @AvroFixed(42) val decimalFixed: BigDecimal, - @Serializable(BigDecimalAsStringSerializer::class) val decimalString: BigDecimal, + @Contextual @AvroDecimal(scale = 2, precision = 8) val decimalBytes: BigDecimal, + @Contextual @AvroDecimal(scale = 2, precision = 8) @AvroFixed(42) val decimalFixed: BigDecimal, + @Contextual @AvroStringable val decimalString: BigDecimal, @Contextual val date: LocalDate, @Contextual val time: LocalTime, @Contextual val instant: Instant, @@ -210,9 +192,9 @@ internal class LogicalTypesEncodingTest : StringSpec({ @Serializable private data class NullableLogicalTypes( - @Contextual val decimalBytesNullable: BigDecimal?, - @Contextual @AvroFixed(42) val decimalFixedNullable: BigDecimal?, - @Serializable(BigDecimalAsStringSerializer::class) val decimalStringNullable: BigDecimal?, + @Contextual @AvroDecimal(scale = 2, precision = 8) val decimalBytesNullable: BigDecimal?, + @Contextual @AvroDecimal(scale = 2, precision = 8) @AvroFixed(42) val decimalFixedNullable: BigDecimal?, + @Contextual @AvroStringable val decimalStringNullable: BigDecimal?, @Contextual val dateNullable: LocalDate?, @Contextual val timeNullable: LocalTime?, @Contextual val instantNullable: Instant?, diff --git a/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/MapEncodingTest.kt b/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/MapEncodingTest.kt index 34e8816d..95a1c4f8 100644 --- a/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/MapEncodingTest.kt +++ b/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/MapEncodingTest.kt @@ -122,7 +122,7 @@ internal class MapEncodingTest : FunSpec({ } test("support maps with contextual keys") { - AvroAssertions.assertThat() + AvroAssertions.assertThat(ContextualKeyTests(mapOf(NonSerializableKey("a") to 12))) .withConfig { serializersModule = serializersModuleOf(NonSerializableKeyKSerializer) } @@ -131,10 +131,6 @@ internal class MapEncodingTest : FunSpec({ .name("map").type(Schema.createMap(Schema.create(Schema.Type.INT))).noDefault() .endRecord() ) - AvroAssertions.assertThat(ContextualKeyTests(mapOf(NonSerializableKey("a") to 12))) - .withConfig { - serializersModule = serializersModuleOf(NonSerializableKeyKSerializer) - } .isEncodedAs(record(mapOf("a" to 12))) } @@ -151,9 +147,8 @@ internal class MapEncodingTest : FunSpec({ SomeEnum.B ).forEach { keyValue -> test("handle string-able key type: ${keyValue::class.simpleName}") { - AvroAssertions.assertThat(GenericMapForTests.serializer(keyValue::class.serializer())) - .generatesSchema(Path("/map_string.json")) AvroAssertions.assertThat(GenericMapForTests(mapOf(keyValue to "something")), GenericMapForTests.serializer(keyValue::class.serializer())) + .generatesSchema(Path("/map_string.json")) .isEncodedAs(record(mapOf(keyValue.toString() to "something"))) } } diff --git a/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroDefaultSchemaTest.kt b/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroDefaultSchemaTest.kt index 64414c59..ecd904e1 100644 --- a/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroDefaultSchemaTest.kt +++ b/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroDefaultSchemaTest.kt @@ -2,6 +2,7 @@ package com.github.avrokotlin.avro4k.schema import com.github.avrokotlin.avro4k.Avro import com.github.avrokotlin.avro4k.AvroAssertions +import com.github.avrokotlin.avro4k.AvroDecimal import com.github.avrokotlin.avro4k.AvroDefault import com.github.avrokotlin.avro4k.schema import com.github.avrokotlin.avro4k.serializer.BigDecimalSerializer @@ -117,14 +118,18 @@ internal class AvroDefaultSchemaTest : FunSpec() { @Serializable private data class BarDecimal( + @AvroDecimal(scale = 2, precision = 8) @Serializable(BigDecimalSerializer::class) val a: BigDecimal, + @AvroDecimal(scale = 2, precision = 8) @Serializable(BigDecimalSerializer::class) @AvroDefault("\u0000") val b: BigDecimal, + @AvroDecimal(scale = 2, precision = 8) @Serializable(BigDecimalSerializer::class) @AvroDefault("null") val nullableString: BigDecimal?, + @AvroDecimal(scale = 2, precision = 8) @Serializable(BigDecimalSerializer::class) @AvroDefault("\u0000") val c: BigDecimal?, diff --git a/src/test/kotlin/com/github/avrokotlin/avro4k/schema/BigDecimalSchemaTest.kt b/src/test/kotlin/com/github/avrokotlin/avro4k/schema/BigDecimalSchemaTest.kt index d95f13cc..3a6aa220 100644 --- a/src/test/kotlin/com/github/avrokotlin/avro4k/schema/BigDecimalSchemaTest.kt +++ b/src/test/kotlin/com/github/avrokotlin/avro4k/schema/BigDecimalSchemaTest.kt @@ -31,19 +31,26 @@ internal class BigDecimalSchemaTest : FunSpec({ @JvmInline @Serializable private value class BigDecimalTest( - @Contextual val bigDecimal: BigDecimal, + @AvroDecimal(scale = 2, precision = 8) + @Contextual + val bigDecimal: BigDecimal, ) @JvmInline @Serializable @SerialName("BigDecimalFixedTest") private value class BigDecimalFixedTest( - @AvroDecimal(3, 5) @AvroFixed(5) @Contextual val field: BigDecimal, + @AvroDecimal(scale = 3, precision = 5) + @AvroFixed(5) + @Contextual + val field: BigDecimal, ) @JvmInline @Serializable private value class BigDecimalNullableTest( - @AvroDecimal(1, 2) @Contextual val bigDecimal: BigDecimal?, + @AvroDecimal(scale = 1, precision = 2) + @Contextual + val bigDecimal: BigDecimal?, ) } \ No newline at end of file