Skip to content

Commit dc54714

Browse files
committed
More stable implementation of Firestore encoding
Make methods stricter and fail with Library exceptions so they can be caught consistently.
1 parent 194e802 commit dc54714

File tree

9 files changed

+302
-238
lines changed
  • firebase-common/src
  • firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database
  • firebase-firestore/src

9 files changed

+302
-238
lines changed

firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,18 @@ import kotlinx.serialization.descriptors.StructureKind
1010
import java.lang.IllegalArgumentException
1111
import kotlin.collections.set
1212

13-
actual data class EncodedObject(
14-
val android: Map<String, Any?>
15-
)
13+
actual data class EncodedObject(actual val raw: Map<String, Any?>) {
14+
actual companion object {
15+
actual val emptyEncodedObject: EncodedObject = EncodedObject(emptyMap())
16+
}
17+
val android: Map<String, Any?> get() = raw
18+
}
1619

17-
actual val emptyEncodedObject: EncodedObject = EncodedObject(emptyMap())
20+
@PublishedApi
21+
internal actual fun List<Pair<String, Any?>>.asEncodedObject() = EncodedObject(toMap())
1822

1923
@PublishedApi
20-
internal actual fun Map<*, *>.asEncodedObject() = EncodedObject(
21-
map { (key, value) ->
22-
if (key is String) {
23-
key to value
24-
} else {
25-
throw IllegalArgumentException("Expected a String key but received $key")
26-
}
27-
}.toMap()
28-
)
24+
internal actual fun Any.asNativeMap(): Map<*, *>? = this as? Map<*, *>
2925

3026
actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder = when(descriptor.kind) {
3127
StructureKind.LIST -> mutableListOf<Any?>()

firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/encoders.kt

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@ import kotlinx.serialization.encoding.CompositeEncoder
1111
import kotlinx.serialization.encoding.Encoder
1212
import kotlinx.serialization.modules.SerializersModule
1313

14-
expect class EncodedObject
14+
expect class EncodedObject {
15+
companion object {
16+
val emptyEncodedObject: EncodedObject
17+
}
1518

16-
expect val emptyEncodedObject: EncodedObject
19+
val raw: Map<String, Any?>
20+
}
1721

1822
@Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("encode(strategy, value) { encodeDefaults = shouldEncodeElementDefault }"))
1923
fun <T> encode(strategy: SerializationStrategy<T>, value: T, shouldEncodeElementDefault: Boolean): Any? = encode(strategy, value) {
@@ -36,20 +40,12 @@ inline fun <reified T> encode(value: T, buildSettings: EncodeSettings.Builder.()
3640
encode(value, EncodeSettings.BuilderImpl().apply(buildSettings).buildEncodeSettings())
3741

3842
inline fun <T : Any> encodeAsObject(strategy: SerializationStrategy<T>, value: T, buildSettings: EncodeSettings.Builder.() -> Unit = {}): EncodedObject {
39-
val encoded = encode(strategy, value, buildSettings)
40-
return when (encoded) {
41-
is Map<*, *> -> encoded.asEncodedObject()
42-
null -> throw IllegalArgumentException("$value was encoded as null. Must be of the form Map<Key, Value>")
43-
else -> throw IllegalArgumentException("$value was encoded as ${encoded::class}. Must be of the form Map<Key, Value>")
44-
}
43+
val encoded = encode(strategy, value, buildSettings) ?: throw IllegalArgumentException("$value was encoded as null. Must be of the form Map<Key, Value>")
44+
return encoded.asNativeMap()?.asEncodedObject() ?: throw IllegalArgumentException("$value was encoded as ${encoded::class}. Must be of the form Map<Key, Value>")
4545
}
4646
inline fun <reified T : Any> encodeAsObject(value: T, buildSettings: EncodeSettings.Builder.() -> Unit = {}): EncodedObject {
47-
val encoded = encode(value, buildSettings)
48-
return when (encoded) {
49-
is Map<*, *> -> encoded.asEncodedObject()
50-
null -> throw IllegalArgumentException("$value was encoded as null. Must be of the form Map<Key, Value>")
51-
else -> throw IllegalArgumentException("$value was encoded as ${encoded::class}. Must be of the form Map<Key, Value>")
52-
}
47+
val encoded = encode(value, buildSettings) ?: throw IllegalArgumentException("$value was encoded as null. Must be of the form Map<Key, Value>")
48+
return encoded.asNativeMap()?.asEncodedObject() ?: throw IllegalArgumentException("$value was encoded as ${encoded::class}. Must be of the form Map<Key, Value>")
5349
}
5450

5551
@PublishedApi
@@ -67,7 +63,19 @@ internal inline fun <reified T> encode(value: T, encodeSettings: EncodeSettings)
6763
}
6864

6965
@PublishedApi
70-
internal expect fun Map<*, *>.asEncodedObject(): EncodedObject
66+
expect internal fun Any.asNativeMap(): Map<*, *>?
67+
68+
@PublishedApi
69+
internal fun Map<*, *>.asEncodedObject(): EncodedObject = map { (key, value) ->
70+
if (key is String) {
71+
key to value
72+
} else {
73+
throw IllegalArgumentException("Expected a String key but received $key")
74+
}
75+
}.asEncodedObject()
76+
77+
@PublishedApi
78+
internal expect fun List<Pair<String, Any?>>.asEncodedObject(): EncodedObject
7179

7280
/**
7381
* An extension which which serializer to use for value. Handy in updating fields by name or path

firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_encoders.kt

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,20 @@ import kotlinx.serialization.descriptors.SerialDescriptor
99
import kotlinx.serialization.descriptors.StructureKind
1010
import kotlin.collections.set
1111

12-
actual data class EncodedObject(
13-
val ios: Map<Any?, *>
14-
)
12+
actual data class EncodedObject(actual val raw: Map<String, Any?>) {
13+
actual companion object {
14+
actual val emptyEncodedObject: EncodedObject = EncodedObject(emptyMap())
15+
}
1516

16-
actual val emptyEncodedObject: EncodedObject = EncodedObject(emptyMap<Any?, Any?>())
17+
val ios: Map<Any?, *> get() = raw.mapKeys { (key, _) -> key as? Any }
18+
}
1719

1820
@PublishedApi
19-
internal actual fun Map<*, *>.asEncodedObject() = EncodedObject(
20-
map { (key, value) ->
21-
key to value
22-
}.toMap()
23-
)
21+
internal actual fun List<Pair<String, Any?>>.asEncodedObject() = EncodedObject(toMap())
22+
23+
@PublishedApi
24+
internal actual fun Any.asNativeMap(): Map<*, *>? = this as? Map<*, *>
25+
2426
actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder = when(descriptor.kind) {
2527
StructureKind.LIST -> encodeAsList()
2628
StructureKind.MAP -> mutableListOf<Any?>()

firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,28 @@ import kotlinx.serialization.descriptors.StructureKind
1010
import kotlin.js.Json
1111
import kotlin.js.json
1212

13-
actual typealias EncodedObject = Json
13+
actual data class EncodedObject(private val keyValues: List<Pair<String, Any?>>) {
14+
actual companion object {
15+
actual val emptyEncodedObject: EncodedObject = EncodedObject(emptyList())
16+
}
17+
18+
actual val raw get() = keyValues.toMap()
19+
val json get() = json(*keyValues.toTypedArray())
20+
}
21+
1422

15-
actual val emptyEncodedObject: EncodedObject = json()
1623
@PublishedApi
17-
internal actual fun Map<*, *>.asEncodedObject() = json(
18-
*map { (key, value) ->
19-
if (key is String) {
20-
key to value
21-
} else {
22-
throw IllegalArgumentException("Expected a String key but received $key")
23-
}
24-
}.toTypedArray()
25-
)
24+
internal actual fun List<Pair<String, Any?>>.asEncodedObject() = EncodedObject(this)
25+
26+
@PublishedApi
27+
internal actual fun Any.asNativeMap(): Map<*, *>? = (this as? Json)?.let { json ->
28+
val mutableMap = mutableMapOf<String, Any?>()
29+
for (key in js("Object").keys(json)) {
30+
mutableMap[key] = json[key]
31+
}
32+
mutableMap.toMap()
33+
}
34+
2635
actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder = when(descriptor.kind) {
2736
StructureKind.LIST -> encodeAsList(descriptor)
2837
StructureKind.MAP -> {

firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ internal actual class NativeDatabaseReference internal constructor(
184184
}
185185

186186
actual suspend fun updateEncodedChildren(encodedUpdate: EncodedObject) =
187-
rethrow { update(js, encodedUpdate).awaitWhileOnline(database) }
187+
rethrow { update(js, encodedUpdate.json).awaitWhileOnline(database) }
188188

189189

190190
actual suspend fun <T> runTransaction(strategy: KSerializer<T>, buildSettings: EncodeDecodeSettingsBuilder.() -> Unit, transactionUpdate: (currentData: T) -> T): DataSnapshot {
@@ -236,7 +236,7 @@ internal actual class NativeOnDisconnect internal constructor(
236236
rethrow { js.set(encodedValue).awaitWhileOnline(database) }
237237

238238
actual suspend fun updateEncodedChildren(encodedUpdate: EncodedObject) =
239-
rethrow { js.update(encodedUpdate).awaitWhileOnline(database) }
239+
rethrow { js.update(encodedUpdate.json).awaitWhileOnline(database) }
240240

241241
}
242242

0 commit comments

Comments
 (0)