Skip to content

Commit b2463cc

Browse files
authored
Merge pull request #36 from splendo/35-add-batch-methods
Add set/update extended methods
2 parents 23bf6dc + 30fc496 commit b2463cc

File tree

7 files changed

+228
-8
lines changed

7 files changed

+228
-8
lines changed

firebase-common/src/commonTest/kotlin/dev/gitlive/firebase/EncodersTest.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class EncodersTest {
6767
val decoded = decode(TestData.serializer(), mapOf("map" to mapOf("key" to "value"), "nullableBool" to null))
6868
assertNull(decoded.nullableBool)
6969
}
70-
}
70+
7171
@Test
7272
fun testEncodeDecodedSealedClass() {
7373
val test = SealedClass.Test("Foo")

firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt

+36
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,26 @@ actual class WriteBatch(val android: com.google.firebase.firestore.WriteBatch) {
9999
android.set(documentRef.android, encode(strategy, data, encodeDefaults)!!, SetOptions.mergeFieldPaths(mergeFieldPaths.map { it.android }))
100100
.let { this }
101101

102+
actual fun <T> set(
103+
documentRef: DocumentReference,
104+
strategy: SerializationStrategy<T>,
105+
data: T,
106+
encodeDefaults: Boolean,
107+
merge: Boolean,
108+
vararg fieldsAndValues: Pair<String, Any?>
109+
): WriteBatch {
110+
val serializedItem = encodeAsMap(strategy, data, encodeDefaults)
111+
val serializedFieldAndValues = encodeAsMap(fieldsAndValues = fieldsAndValues)
112+
113+
val result = serializedItem + (serializedFieldAndValues ?: emptyMap())
114+
if (merge) {
115+
android.set(documentRef.android, result, SetOptions.merge())
116+
} else {
117+
android.set(documentRef.android, result)
118+
}
119+
return this
120+
}
121+
102122
@Suppress("UNCHECKED_CAST")
103123
actual inline fun <reified T> update(documentRef: DocumentReference, data: T, encodeDefaults: Boolean) =
104124
android.update(documentRef.android, encode(data, encodeDefaults) as Map<String, Any>).let { this }
@@ -114,6 +134,22 @@ actual class WriteBatch(val android: com.google.firebase.firestore.WriteBatch) {
114134
?.let { encoded -> android.update(documentRef.android, encoded.toMap()) }
115135
.let { this }
116136

137+
@JvmName("updateFieldsExtended")
138+
actual inline fun <reified T> update(
139+
documentRef: DocumentReference,
140+
strategy: SerializationStrategy<T>,
141+
data: T,
142+
encodeDefaults: Boolean,
143+
vararg fieldsAndValues: Pair<String, Any?>
144+
): WriteBatch {
145+
val serializedItem = encodeAsMap(strategy, data, encodeDefaults)
146+
val serializedFieldAndValues = encodeAsMap(fieldsAndValues = fieldsAndValues)
147+
148+
val result = serializedItem + (serializedFieldAndValues ?: emptyMap())
149+
return android.update(documentRef.android, result).let { this }
150+
}
151+
152+
117153
@JvmName("updateFieldPaths")
118154
actual fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair<FieldPath, Any?>) =
119155
fieldsAndValues.takeUnless { fieldsAndValues.isEmpty() }

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

+15
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,25 @@ internal inline fun <reified T> encode(value: T, shouldEncodeElementDefault: Boo
1212
dev.gitlive.firebase.encode(value, shouldEncodeElementDefault, FieldValue.serverTimestamp())
1313
}
1414

15+
@PublishedApi
1516
internal fun <T> encode(strategy: SerializationStrategy<T>, value: T, shouldEncodeElementDefault: Boolean): Any? =
1617
dev.gitlive.firebase.encode(
1718
strategy,
1819
value,
1920
shouldEncodeElementDefault,
2021
FieldValue.serverTimestamp()
2122
)
23+
24+
@PublishedApi
25+
internal fun <T> encodeAsMap(
26+
strategy: SerializationStrategy<T>,
27+
data: T,
28+
encodeDefaults: Boolean = false
29+
): Map<String, Any?> = encode(strategy, data, encodeDefaults) as Map<String, Any?>
30+
31+
@PublishedApi
32+
internal fun encodeAsMap(
33+
encodeDefaults: Boolean = false,
34+
vararg fieldsAndValues: Pair<String, Any?>
35+
): Map<String, Any?>? = fieldsAndValues.takeUnless { fieldsAndValues.isEmpty() }
36+
?.map { (field, value) -> field to encode(value, encodeDefaults) }?.toMap()

firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt

+2
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,11 @@ expect class WriteBatch {
103103
fun <T> set(documentRef: DocumentReference, strategy: SerializationStrategy<T>, data: T, encodeDefaults: Boolean = true, merge: Boolean = false): WriteBatch
104104
fun <T> set(documentRef: DocumentReference, strategy: SerializationStrategy<T>, data: T, encodeDefaults: Boolean = true, vararg mergeFields: String): WriteBatch
105105
fun <T> set(documentRef: DocumentReference, strategy: SerializationStrategy<T>, data: T, encodeDefaults: Boolean = true, vararg mergeFieldPaths: FieldPath): WriteBatch
106+
fun <T> set(documentRef: DocumentReference, strategy: SerializationStrategy<T>, data: T, encodeDefaults: Boolean = true, merge: Boolean = false, vararg fieldsAndValues: Pair<String, Any?>): WriteBatch
106107

107108
inline fun <reified T> update(documentRef: DocumentReference, data: T, encodeDefaults: Boolean = true): WriteBatch
108109
fun <T> update(documentRef: DocumentReference, strategy: SerializationStrategy<T>, data: T, encodeDefaults: Boolean = true): WriteBatch
110+
inline fun <reified T> update(documentRef: DocumentReference, strategy: SerializationStrategy<T>, data: T, encodeDefaults: Boolean, vararg fieldsAndValues: Pair<String, Any?>): WriteBatch
109111

110112
fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair<String, Any?>): WriteBatch
111113
fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair<FieldPath, Any?>): WriteBatch

firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt

+109-6
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ expect fun runTest(test: suspend () -> Unit)
1515
class FirebaseFirestoreTest {
1616

1717
@Serializable
18-
data class FirestoreTest(val prop1: String, val time: Double = 0.0)
18+
data class FirestoreTest(val prop1: String, val time: Double? = 0.0)
1919

2020
@BeforeTest
2121
fun initializeFirebase() {
@@ -51,7 +51,6 @@ class FirebaseFirestoreTest {
5151
@Test
5252
fun testFieldOrderBy() = runTest {
5353
setupFirestoreData()
54-
5554
val resultDocs = Firebase.firestore.collection("FirebaseFirestoreTest")
5655
.orderBy(FieldPath("prop1")).get().documentChanges
5756
assertEquals(3, resultDocs.size)
@@ -63,7 +62,6 @@ class FirebaseFirestoreTest {
6362
@Test
6463
fun testStringOrderByAscending() = runTest {
6564
setupFirestoreData()
66-
6765
val resultDocs = Firebase.firestore.collection("FirebaseFirestoreTest")
6866
.orderBy("prop1", Direction.ASCENDING).get().documentChanges
6967
assertEquals(3, resultDocs.size)
@@ -113,12 +111,117 @@ class FirebaseFirestoreTest {
113111
val doc = Firebase.firestore
114112
.collection("testServerTimestampFieldValue")
115113
.document("test")
114+
doc.set(
115+
FirestoreTest.serializer(),
116+
FirestoreTest("ServerTimestamp"),
117+
)
118+
assertEquals(0.0, doc.get().get("time"))
119+
120+
doc.update(
121+
fieldsAndValues = arrayOf(
122+
"time" to 123.0
123+
)
124+
)
125+
assertEquals(123.0, doc.get().data(FirestoreTest.serializer()).time)
126+
127+
}
116128

117-
doc.set(FirestoreTest.serializer(), FirestoreTest("ServerTimestamp", FieldValue.serverTimestamp().toString().toDouble()))
129+
@Test
130+
fun testExtendedSetBatch() = runTest {
131+
val doc = Firebase.firestore
132+
.collection("testServerTestSetBatch")
133+
.document("test")
134+
val batch = Firebase.firestore.batch()
135+
batch.set(
136+
documentRef = doc,
137+
strategy = FirestoreTest.serializer(),
138+
data = FirestoreTest(
139+
prop1 = "prop1",
140+
time = 123.0
141+
),
142+
fieldsAndValues = arrayOf(
143+
"time" to 124.0
144+
)
145+
)
146+
batch.commit()
147+
148+
assertEquals(124.0, doc.get().get("time"))
149+
assertEquals("prop1", doc.get().data(FirestoreTest.serializer()).prop1)
118150

119-
assertNotEquals(FieldValue.serverTimestamp(), doc.get().get("time"))
120-
assertNotEquals(FieldValue.serverTimestamp(), doc.get().data(FirestoreTest.serializer()).time)
151+
}
121152

153+
@Test
154+
fun testSetBatchDoesNotEncodeEmptyValues() = runTest {
155+
val doc = Firebase.firestore
156+
.collection("testServerTestSetBatch")
157+
.document("test")
158+
val batch = Firebase.firestore.batch()
159+
batch.set(
160+
documentRef = doc,
161+
strategy = FirestoreTest.serializer(),
162+
data = FirestoreTest(
163+
prop1 = "prop1-set",
164+
time = 125.0
165+
),
166+
fieldsAndValues = arrayOf<Pair<String, Any>>()
167+
)
168+
batch.commit()
169+
170+
assertEquals(125.0, doc.get().get("time") as Double?)
171+
assertEquals("prop1-set", doc.get().data(FirestoreTest.serializer()).prop1)
172+
}
173+
174+
@Test
175+
fun testExtendedUpdateBatch() = runTest {
176+
val doc = Firebase.firestore
177+
.collection("testServerTestSetBatch")
178+
.document("test").apply {
179+
set(
180+
FirestoreTest(
181+
prop1 = "prop1",
182+
time = 123.0
183+
)
184+
)
185+
}
186+
val batch = Firebase.firestore.batch()
187+
batch.update(
188+
documentRef = doc,
189+
strategy = FirestoreTest.serializer(),
190+
data = FirestoreTest(
191+
prop1 = "prop1-updated",
192+
time = 123.0
193+
),
194+
encodeDefaults = false,
195+
fieldsAndValues = arrayOf(
196+
"time" to FieldValue.delete
197+
)
198+
)
199+
batch.commit()
200+
201+
assertEquals(null, doc.get().get("time") as Double?)
202+
assertEquals("prop1-updated", doc.get().data(FirestoreTest.serializer()).prop1)
203+
}
204+
205+
@Test
206+
fun testUpdateBatchDoesNotEncodeEmptyValues() = runTest {
207+
val doc = Firebase.firestore
208+
.collection("testServerTestSetBatch")
209+
.document("test")
210+
val batch = Firebase.firestore.batch()
211+
batch.update(
212+
documentRef = doc,
213+
strategy = FirestoreTest.serializer(),
214+
data = FirestoreTest(
215+
prop1 = "prop1-set",
216+
time = 126.0
217+
),
218+
encodeDefaults = false,
219+
fieldsAndValues = arrayOf<Pair<String, Any>>()
220+
)
221+
batch.commit()
222+
223+
assertEquals(126.0, doc.get().get("time") as Double?)
224+
assertEquals("prop1-set", doc.get().data(FirestoreTest.serializer()).prop1)
122225
}
123226

124227
private suspend fun setupFirestoreData() {

firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt

+31-1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,22 @@ actual class WriteBatch(val ios: FIRWriteBatch) {
9090
actual fun <T> set(documentRef: DocumentReference, strategy: SerializationStrategy<T>, data: T, encodeDefaults: Boolean, vararg mergeFieldPaths: FieldPath) =
9191
ios.setData(encode(strategy, data, encodeDefaults)!! as Map<Any?, *>, documentRef.ios, mergeFieldPaths.map { it.ios }).let { this }
9292

93+
actual fun <T> set(
94+
documentRef: DocumentReference,
95+
strategy: SerializationStrategy<T>,
96+
data: T,
97+
encodeDefaults: Boolean,
98+
merge: Boolean,
99+
vararg fieldsAndValues: Pair<String, Any?>
100+
): WriteBatch {
101+
val serializedItem = encodeAsMap(strategy, data, encodeDefaults)
102+
val serializedFieldAndValues = encodeAsMap(fieldsAndValues = fieldsAndValues)
103+
104+
val result = serializedItem + (serializedFieldAndValues ?: emptyMap())
105+
ios.setData(result as Map<Any?, *>, documentRef.ios, merge)
106+
return this
107+
}
108+
93109
actual inline fun <reified T> update(documentRef: DocumentReference, data: T, encodeDefaults: Boolean) =
94110
ios.updateData(encode(data, encodeDefaults) as Map<Any?, *>, documentRef.ios).let { this }
95111

@@ -102,7 +118,21 @@ actual class WriteBatch(val ios: FIRWriteBatch) {
102118
documentRef.ios
103119
).let { this }
104120

105-
actual fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair<FieldPath, Any?>) =
121+
actual inline fun <reified T> update(
122+
documentRef: DocumentReference,
123+
strategy: SerializationStrategy<T>,
124+
data: T,
125+
encodeDefaults: Boolean,
126+
vararg fieldsAndValues: Pair<String, Any?>
127+
): WriteBatch {
128+
val serializedItem = encodeAsMap(strategy, data, encodeDefaults)
129+
val serializedFieldAndValues = encodeAsMap(fieldsAndValues = fieldsAndValues)
130+
131+
val result = serializedItem + (serializedFieldAndValues ?: emptyMap())
132+
return ios.updateData(result as Map<Any?, *>, documentRef.ios).let { this }
133+
}
134+
135+
actual inline fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair<FieldPath, Any?>) =
106136
ios.updateData(
107137
fieldsAndValues.associate { (path, value) -> path.ios to encode(value, true) },
108138
documentRef.ios

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

+34
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,26 @@ actual class WriteBatch(val js: firebase.firestore.WriteBatch) {
8686
rethrow { js.set(documentRef.js, encode(strategy, data, encodeDefaults)!!, json("mergeFields" to mergeFieldPaths.map { it.js }.toTypedArray())) }
8787
.let { this }
8888

89+
actual fun <T> set(
90+
documentRef: DocumentReference,
91+
strategy: SerializationStrategy<T>,
92+
data: T,
93+
encodeDefaults: Boolean,
94+
merge: Boolean,
95+
vararg fieldsAndValues: Pair<String, Any?>
96+
): WriteBatch {
97+
val serializedItem = encodeAsMap(strategy, data, encodeDefaults)
98+
val serializedFieldAndValues = encodeAsMap(fieldsAndValues = fieldsAndValues)
99+
100+
val result = serializedItem + (serializedFieldAndValues ?: emptyMap())
101+
if (merge) {
102+
js.set(documentRef.js, result, json("merge" to merge))
103+
} else {
104+
js.set(documentRef.js, result)
105+
}
106+
return this
107+
}
108+
89109
actual inline fun <reified T> update(documentRef: DocumentReference, data: T, encodeDefaults: Boolean) =
90110
rethrow { js.update(documentRef.js, encode(data, encodeDefaults)!!) }
91111
.let { this }
@@ -100,6 +120,20 @@ actual class WriteBatch(val js: firebase.firestore.WriteBatch) {
100120
?.let { encoded -> js.update(documentRef.js, encoded.toMap()) }
101121
}.let { this }
102122

123+
actual inline fun <reified T> update(
124+
documentRef: DocumentReference,
125+
strategy: SerializationStrategy<T>,
126+
data: T,
127+
encodeDefaults: Boolean,
128+
vararg fieldsAndValues: Pair<String, Any?>
129+
): WriteBatch {
130+
val serializedItem = encodeAsMap(strategy, data, encodeDefaults)
131+
val serializedFieldAndValues = encodeAsMap(fieldsAndValues = fieldsAndValues)
132+
133+
val result = serializedItem + (serializedFieldAndValues ?: emptyMap())
134+
return js.update(documentRef.js, result).let { this }
135+
}
136+
103137
actual fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair<FieldPath, Any?>) = rethrow {
104138
fieldsAndValues.takeUnless { fieldsAndValues.isEmpty() }
105139
?.map { (field, value) -> field.js to encode(value, true) }

0 commit comments

Comments
 (0)