Skip to content

Commit 128b537

Browse files
committed
Add OrderDirection support; a follow-up PR will add generated SDK tests once the data connect toolkit is released with OrderDirection support in Android codegen
1 parent a95da3f commit 128b537

File tree

8 files changed

+210
-4
lines changed

8 files changed

+210
-4
lines changed

firebase-dataconnect/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
([#6176](https://github.com/firebase/firebase-android-sdk/pull/6176))
77
* [feature] Added `AnyValue` to support the `Any` custom GraphQL scalar type.
88
([#6285](https://github.com/firebase/firebase-android-sdk/pull/6285))
9+
* [feature] Added `OrderDirection` enum support.
10+
([#6307](https://github.com/firebase/firebase-android-sdk/pull/6307))
911
* [feature] Added ability to specify `SerializersModule` when serializing.
1012
([#6297](https://github.com/firebase/firebase-android-sdk/pull/6297))
1113
* [feature] Added `CallerSdkType`, which enables tracking of the generated SDK usage.

firebase-dataconnect/emulator/dataconnect/connector/alltypes/alltypes_ops.gql

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,33 @@ query getFarm($id: String!) @auth(level: PUBLIC) {
190190
}
191191
}
192192
}
193+
194+
###############################################################################
195+
# Operations for table: OrderDirectionTest
196+
###############################################################################
197+
198+
mutation OrderDirectionTestInsert5(
199+
$tag: String!,
200+
$value1: Int!,
201+
$value2: Int!,
202+
$value3: Int!,
203+
$value4: Int!,
204+
$value5: Int!,
205+
) @auth(level: PUBLIC) {
206+
key1: orderDirectionTest_insert(data: { tag: $tag, value: $value1 })
207+
key2: orderDirectionTest_insert(data: { tag: $tag, value: $value2 })
208+
key3: orderDirectionTest_insert(data: { tag: $tag, value: $value3 })
209+
key4: orderDirectionTest_insert(data: { tag: $tag, value: $value4 })
210+
key5: orderDirectionTest_insert(data: { tag: $tag, value: $value5 })
211+
}
212+
213+
query OrderDirectionTestGetAllByTag(
214+
$tag: String!,
215+
$orderDirection: OrderDirection,
216+
) @auth(level: PUBLIC) {
217+
items: orderDirectionTests(
218+
limit: 10,
219+
orderBy: { value: $orderDirection },
220+
where: { tag: { eq: $tag } },
221+
) { id }
222+
}

firebase-dataconnect/emulator/dataconnect/schema/alltypes_schema.gql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,8 @@ type Farmer @table {
6262
name: String!
6363
parent: Farmer
6464
}
65+
66+
type OrderDirectionTest @table @index(fields: ["tag"]) {
67+
value: Int
68+
tag: String
69+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.dataconnect
18+
19+
import com.google.firebase.dataconnect.testutil.DataConnectIntegrationTestBase
20+
import com.google.firebase.dataconnect.testutil.sortedParallelTo
21+
import com.google.firebase.dataconnect.testutil.tag
22+
import io.kotest.common.DelicateKotest
23+
import io.kotest.matchers.collections.shouldContainExactly
24+
import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder
25+
import io.kotest.property.Arb
26+
import io.kotest.property.arbitrary.distinct
27+
import io.kotest.property.arbitrary.int
28+
import io.kotest.property.arbitrary.next
29+
import kotlinx.coroutines.test.runTest
30+
import kotlinx.serialization.Serializable
31+
import kotlinx.serialization.serializer
32+
import org.junit.Test
33+
34+
class OrderDirectionIntegrationTest : DataConnectIntegrationTestBase() {
35+
36+
private val dataConnect: FirebaseDataConnect by lazy {
37+
val connectorConfig = testConnectorConfig.copy(connector = "alltypes")
38+
dataConnectFactory.newInstance(connectorConfig)
39+
}
40+
41+
@OptIn(DelicateKotest::class) private val uniqueInts = Arb.int().distinct()
42+
43+
@Test
44+
fun orderDirectionQueryVariableOmittedShouldUseAscendingOrder() = runTest {
45+
val tag = Arb.tag().next(rs)
46+
val values = List(5) { uniqueInts.next(rs) }
47+
val insertedIds = insertRow(tag, values)
48+
49+
val queryIds = getRowIds(tag)
50+
51+
queryIds shouldContainExactlyInAnyOrder insertedIds
52+
}
53+
54+
@Test
55+
fun orderDirectionQueryVariableAscendingOrder() = runTest {
56+
val tag = Arb.tag().next(rs)
57+
val values = List(5) { uniqueInts.next(rs) }
58+
val insertedIds = insertRow(tag, values)
59+
60+
val queryIds = getRowIds(tag, orderDirection = "ASC")
61+
62+
val insertedIdsSorted = insertedIds.sortedParallelTo(values)
63+
queryIds shouldContainExactly insertedIdsSorted
64+
}
65+
66+
@Test
67+
fun orderDirectionQueryVariableDescendingOrder() = runTest {
68+
val tag = Arb.tag().next(rs)
69+
val values = List(5) { uniqueInts.next(rs) }
70+
val insertedIds = insertRow(tag, values)
71+
72+
val queryIds = getRowIds(tag, orderDirection = "DESC")
73+
74+
val insertedIdsSorted = insertedIds.sortedParallelTo(values).reversed()
75+
queryIds shouldContainExactly insertedIdsSorted
76+
}
77+
78+
private suspend fun insertRow(tag: String, values: List<Int>): List<String> {
79+
require(values.size == 5) { "values.size must be 5, but got ${values.size}" }
80+
return insertRow(tag, values[0], values[1], values[2], values[3], values[4])
81+
}
82+
83+
private suspend fun insertRow(
84+
tag: String,
85+
value1: Int,
86+
value2: Int,
87+
value3: Int,
88+
value4: Int,
89+
value5: Int
90+
): List<String> {
91+
val variables = OrderDirectionTestInsert5Variables(tag, value1, value2, value3, value4, value5)
92+
val mutationRef =
93+
dataConnect.mutation<OrderDirectionTestInsert5Data, OrderDirectionTestInsert5Variables>(
94+
operationName = "OrderDirectionTestInsert5",
95+
variables = variables,
96+
dataDeserializer = serializer(),
97+
variablesSerializer = serializer(),
98+
)
99+
val result = mutationRef.execute()
100+
return result.data.run { listOf(key1.id, key2.id, key3.id, key4.id, key5.id) }
101+
}
102+
103+
private suspend fun getRowIds(tag: String, orderDirection: String? = null): List<String> {
104+
val optionalOrderDirection =
105+
if (orderDirection !== null) OptionalVariable.Value(orderDirection)
106+
else OptionalVariable.Undefined
107+
val variables = OrderDirectionTestGetAllByTagVariables(tag, optionalOrderDirection)
108+
val queryRef =
109+
dataConnect.query<OrderDirectionTestGetAllByTagData, OrderDirectionTestGetAllByTagVariables>(
110+
operationName = "OrderDirectionTestGetAllByTag",
111+
variables = variables,
112+
dataDeserializer = serializer(),
113+
variablesSerializer = serializer(),
114+
)
115+
val result = queryRef.execute()
116+
return result.data.items.map { it.id }
117+
}
118+
119+
@Serializable
120+
data class OrderDirectionTestInsert5Variables(
121+
val tag: String,
122+
val value1: Int,
123+
val value2: Int,
124+
val value3: Int,
125+
val value4: Int,
126+
val value5: Int,
127+
)
128+
129+
@Serializable data class OrderDirectionTestKey(val id: String)
130+
131+
@Serializable
132+
data class OrderDirectionTestInsert5Data(
133+
val key1: OrderDirectionTestKey,
134+
val key2: OrderDirectionTestKey,
135+
val key3: OrderDirectionTestKey,
136+
val key4: OrderDirectionTestKey,
137+
val key5: OrderDirectionTestKey,
138+
)
139+
140+
@Serializable
141+
data class OrderDirectionTestGetAllByTagVariables(
142+
val tag: String,
143+
val orderDirection: OptionalVariable<String>,
144+
)
145+
146+
@Serializable
147+
data class OrderDirectionTestGetAllByTagData(val items: List<Item>) {
148+
@Serializable data class Item(val id: String)
149+
}
150+
}

firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/ProtoStructDecoder.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ private object ProtoDecoderUtil {
8080
fun decodeDouble(value: Value, path: String?): Double =
8181
decode(value, path, KindCase.NUMBER_VALUE) { it.numberValue }
8282

83-
fun decodeEnum(value: Value, path: String?): Int =
84-
decode(value, path, KindCase.NUMBER_VALUE) { it.numberValue.toInt() }
83+
fun decodeEnum(value: Value, path: String?): String =
84+
decode(value, path, KindCase.STRING_VALUE) { it.stringValue }
8585

8686
fun decodeFloat(value: Value, path: String?): Float =
8787
decode(value, path, KindCase.NUMBER_VALUE) { it.numberValue.toFloat() }
@@ -134,7 +134,10 @@ internal class ProtoValueDecoder(
134134

135135
override fun decodeDouble() = decodeDouble(valueProto, path)
136136

137-
override fun decodeEnum(enumDescriptor: SerialDescriptor) = decodeEnum(valueProto, path)
137+
override fun decodeEnum(enumDescriptor: SerialDescriptor): Int {
138+
val enumValueName = decodeEnum(valueProto, path)
139+
return enumDescriptor.getElementIndex(enumValueName)
140+
}
138141

139142
override fun decodeFloat() = decodeFloat(valueProto, path)
140143

firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/ProtoStructEncoder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ internal class ProtoValueEncoder(
6868
}
6969

7070
override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) {
71-
onValue(index.toValueProto())
71+
onValue(enumDescriptor.getElementName(index).toValueProto())
7272
}
7373

7474
override fun encodeFloat(value: Float) {

firebase-dataconnect/testutil/src/main/kotlin/com/google/firebase/dataconnect/testutil/Arbs.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import com.google.firebase.dataconnect.FirebaseDataConnect.CallerSdkType
2222
import com.google.firebase.util.nextAlphanumericString
2323
import io.kotest.property.Arb
2424
import io.kotest.property.arbitrary.Codepoint
25+
import io.kotest.property.arbitrary.alphanumeric
2526
import io.kotest.property.arbitrary.arabic
2627
import io.kotest.property.arbitrary.arbitrary
2728
import io.kotest.property.arbitrary.ascii
@@ -163,3 +164,7 @@ fun <A> Arb<List<A>>.filterNotIncludesAllMatchingAnyScalars(values: List<Any?>)
163164
fun Arb.Companion.callerSdkType(): Arb<CallerSdkType> = arbitrary {
164165
if (Arb.boolean().bind()) CallerSdkType.Base else CallerSdkType.Generated
165166
}
167+
168+
fun Arb.Companion.tag(): Arb<String> = arbitrary {
169+
"tag" + Arb.string(size = 10, Codepoint.alphanumeric()).bind()
170+
}

firebase-dataconnect/testutil/src/main/kotlin/com/google/firebase/dataconnect/testutil/TestUtils.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,3 +231,14 @@ fun TestScope.newBackgroundScopeThatAdvancesLikeForeground(): CoroutineScope {
231231
backgroundContextWithoutBackgroundWork + Job(backgroundContextWithoutBackgroundWork[Job])
232232
)
233233
}
234+
235+
/** Sorts the given list and makes the same transformation on this list. */
236+
fun <T, U : Comparable<U>> List<T>.sortedParallelTo(other: List<U>): List<T> {
237+
require(size == other.size) {
238+
"size must equal other.size, but they are unequal: size=$size other.size=${other.size}"
239+
}
240+
val zippedList = other.zip(this)
241+
val sortedZippedList = zippedList.sortedBy { it.first }
242+
val (_, sortedThis) = sortedZippedList.unzip()
243+
return sortedThis
244+
}

0 commit comments

Comments
 (0)