Skip to content

Commit b724185

Browse files
authored
Merge branch 'main' into davidmotson.safety_scores
2 parents 71a280e + 7b21bd6 commit b724185

File tree

11 files changed

+261
-5
lines changed

11 files changed

+261
-5
lines changed

.github/workflows/dataconnect.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ env:
3434
FDC_JAVA_VERSION: ${{ inputs.javaVersion || '17' }}
3535
FDC_ANDROID_EMULATOR_API_LEVEL: ${{ inputs.androidEmulatorApiLevel || '34' }}
3636
FDC_NODEJS_VERSION: ${{ inputs.nodeJsVersion || '20' }}
37-
FDC_FIREBASE_TOOLS_VERSION: ${{ inputs.firebaseToolsVersion || '14.12.0' }}
37+
FDC_FIREBASE_TOOLS_VERSION: ${{ inputs.firebaseToolsVersion || '14.15.1' }}
3838
FDC_FIREBASE_TOOLS_DIR: /tmp/firebase-tools
3939
FDC_FIREBASE_COMMAND: /tmp/firebase-tools/node_modules/.bin/firebase
4040
FDC_PYTHON_VERSION: ${{ inputs.pythonVersion || '3.13' }}

.github/workflows/dataconnect_demo_app.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ on:
1818

1919
env:
2020
FDC_NODE_VERSION: ${{ inputs.nodeVersion || '20' }}
21-
FDC_FIREBASE_TOOLS_VERSION: ${{ inputs.firebaseToolsVersion || '14.12.0' }}
21+
FDC_FIREBASE_TOOLS_VERSION: ${{ inputs.firebaseToolsVersion || '14.15.1' }}
2222
FDC_JAVA_VERSION: ${{ inputs.javaVersion || '17' }}
2323
FDC_FIREBASE_TOOLS_DIR: ${{ github.workspace }}/firebase-tools
2424
FDC_FIREBASE_COMMAND: ${{ github.workspace }}/firebase-tools/node_modules/.bin/firebase

firebase-ai/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
have not been granted by the user.
66
- [feature] Added helper functions to `LiveSession` to allow developers to track the status of the
77
audio session and the underlying websocket connection.
8+
- [changed] Added new values to `HarmCategory` (#7324)
89

910
# 17.2.0
1011

firebase-ai/api.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,10 @@ package com.google.firebase.ai.type {
531531
field public static final com.google.firebase.ai.type.HarmCategory DANGEROUS_CONTENT;
532532
field public static final com.google.firebase.ai.type.HarmCategory HARASSMENT;
533533
field public static final com.google.firebase.ai.type.HarmCategory HATE_SPEECH;
534+
field public static final com.google.firebase.ai.type.HarmCategory IMAGE_DANGEROUS_CONTENT;
535+
field public static final com.google.firebase.ai.type.HarmCategory IMAGE_HARASSMENT;
536+
field public static final com.google.firebase.ai.type.HarmCategory IMAGE_HATE;
537+
field public static final com.google.firebase.ai.type.HarmCategory IMAGE_SEXUALLY_EXPLICIT;
534538
field public static final com.google.firebase.ai.type.HarmCategory SEXUALLY_EXPLICIT;
535539
field public static final com.google.firebase.ai.type.HarmCategory UNKNOWN;
536540
}

firebase-ai/src/main/kotlin/com/google/firebase/ai/type/HarmCategory.kt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ public class HarmCategory private constructor(public val ordinal: Int) {
3131
SEXUALLY_EXPLICIT -> Internal.SEXUALLY_EXPLICIT
3232
DANGEROUS_CONTENT -> Internal.DANGEROUS_CONTENT
3333
CIVIC_INTEGRITY -> Internal.CIVIC_INTEGRITY
34+
IMAGE_HATE -> Internal.IMAGE_HATE
35+
IMAGE_DANGEROUS_CONTENT -> Internal.IMAGE_DANGEROUS_CONTENT
36+
IMAGE_HARASSMENT -> Internal.IMAGE_HARASSMENT
37+
IMAGE_SEXUALLY_EXPLICIT -> Internal.IMAGE_SEXUALLY_EXPLICIT
3438
UNKNOWN -> Internal.UNKNOWN
3539
else -> throw makeMissingCaseException("HarmCategory", ordinal)
3640
}
@@ -41,7 +45,11 @@ public class HarmCategory private constructor(public val ordinal: Int) {
4145
@SerialName("HARM_CATEGORY_HATE_SPEECH") HATE_SPEECH,
4246
@SerialName("HARM_CATEGORY_SEXUALLY_EXPLICIT") SEXUALLY_EXPLICIT,
4347
@SerialName("HARM_CATEGORY_DANGEROUS_CONTENT") DANGEROUS_CONTENT,
44-
@SerialName("HARM_CATEGORY_CIVIC_INTEGRITY") CIVIC_INTEGRITY;
48+
@SerialName("HARM_CATEGORY_CIVIC_INTEGRITY") CIVIC_INTEGRITY,
49+
@SerialName("HARM_CATEGORY_IMAGE_HATE") IMAGE_HATE,
50+
@SerialName("HARM_CATEGORY_IMAGE_DANGEROUS_CONTENT") IMAGE_DANGEROUS_CONTENT,
51+
@SerialName("HARM_CATEGORY_IMAGE_HARASSMENT") IMAGE_HARASSMENT,
52+
@SerialName("HARM_CATEGORY_IMAGE_SEXUALLY_EXPLICIT") IMAGE_SEXUALLY_EXPLICIT;
4553

4654
internal object Serializer : KSerializer<Internal> by FirstOrdinalSerializer(Internal::class)
4755

@@ -52,6 +60,10 @@ public class HarmCategory private constructor(public val ordinal: Int) {
5260
SEXUALLY_EXPLICIT -> HarmCategory.SEXUALLY_EXPLICIT
5361
DANGEROUS_CONTENT -> HarmCategory.DANGEROUS_CONTENT
5462
CIVIC_INTEGRITY -> HarmCategory.CIVIC_INTEGRITY
63+
IMAGE_HATE -> HarmCategory.IMAGE_HATE
64+
IMAGE_DANGEROUS_CONTENT -> HarmCategory.IMAGE_DANGEROUS_CONTENT
65+
IMAGE_HARASSMENT -> HarmCategory.IMAGE_HARASSMENT
66+
IMAGE_SEXUALLY_EXPLICIT -> HarmCategory.IMAGE_SEXUALLY_EXPLICIT
5567
else -> HarmCategory.UNKNOWN
5668
}
5769
}
@@ -73,5 +85,17 @@ public class HarmCategory private constructor(public val ordinal: Int) {
7385

7486
/** Content that may be used to harm civic integrity. */
7587
@JvmField public val CIVIC_INTEGRITY: HarmCategory = HarmCategory(5)
88+
89+
/** Content that is image hate. */
90+
@JvmField public val IMAGE_HATE: HarmCategory = HarmCategory(6)
91+
92+
/** Image dangerous content. */
93+
@JvmField public val IMAGE_DANGEROUS_CONTENT: HarmCategory = HarmCategory(7)
94+
95+
/** Content is image harassment. */
96+
@JvmField public val IMAGE_HARASSMENT: HarmCategory = HarmCategory(8)
97+
98+
/** Image sexually explicit content. */
99+
@JvmField public val IMAGE_SEXUALLY_EXPLICIT: HarmCategory = HarmCategory(9)
76100
}
77101
}

firebase-dataconnect/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Unreleased
22

3+
- [changed] Ignore unknown fields in response data instead of throwing a
4+
`DataConnectOperationException` with message "decoding data from the server's response failed: An
5+
unknown field for index -3"
6+
37
# 17.0.0
48

59
- [changed] **Breaking Change**: Updated minSdkVersion to API level 23 or higher.

firebase-dataconnect/emulator/dataconnect/connector/person/person_ops.gql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,10 @@ mutation createPersonWithPartialFailureInTransaction($id: String!, $name: String
105105
person2: person_insert(data: { id_expr: "uuidV4()", name: $name }) @check(expr: "false", message: "te36b3zkvn")
106106
}
107107

108+
mutation create5People @auth(level: PUBLIC) {
109+
person1: person_upsert(data: { id: "8a218894521f457a9be45a0986494058", name: "Name1" })
110+
person2: person_upsert(data: { id: "464443371f284194be4b2e78c3ef000c", name: "Name2" })
111+
person3: person_upsert(data: { id: "903d83db81754bd29860458f127ef124", name: "Name3" })
112+
person4: person_upsert(data: { id: "ef8de2a4a6de400e94d555f148b643c0", name: "Name4" })
113+
person5: person_upsert(data: { id: "8584fd7ca2b6453da18d21d4341f1804", name: "Name5" })
114+
}

firebase-dataconnect/gradleplugin/plugin/src/main/resources/com/google/firebase/dataconnect/gradle/plugin/DataConnectExecutableVersions.json

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"defaultVersion": "2.11.0",
2+
"defaultVersion": "2.12.0",
33
"versions": [
44
{
55
"version": "1.3.4",
@@ -936,6 +936,60 @@
936936
"os": "linux",
937937
"size": 29159608,
938938
"sha512DigestHex": "af3bad81cc0c0d822a75381db06d4b191c64dd73abdcbd368830e736574b7212d22a3a87c74ce52adee3ca9630876da7b600f8c31f1498d4a76359e51f872a39"
939+
},
940+
{
941+
"version": "2.11.1",
942+
"os": "windows",
943+
"size": 29841920,
944+
"sha512DigestHex": "0f1a200157cbfbe61e8d7c292f9f152bc0eccb58b3fd16c8996ffca157966d18b369f0325eeeb4c853d68d74801908fee89f3fed1fbacfcee8f032d1883b3cad"
945+
},
946+
{
947+
"version": "2.11.1",
948+
"os": "macos",
949+
"size": 29352800,
950+
"sha512DigestHex": "6dc4d2515b8235fe35d9027d54e5d460ebf1b41ba577c9ccc710a94ef89e95cddac26753c6b9a86b00950ba60fb6b7b24730d42832a7ae5c378492f0b4b80d69"
951+
},
952+
{
953+
"version": "2.11.1",
954+
"os": "linux",
955+
"size": 29282488,
956+
"sha512DigestHex": "a8b315a858c341ec210e4732f928f4145f1c7d132351ea84ce0217d0e9ac7fc1226049c2ae08fe6f02bbe817807857cd3425864c1ad8ac7fa1bacf3d54b14698"
957+
},
958+
{
959+
"version": "2.11.2",
960+
"os": "windows",
961+
"size": 29934592,
962+
"sha512DigestHex": "4f23cad7208f84508a597b4675783cafebab074efa203013fff14a0870e6a5a6c2db4b4c7600a3058a19973dbe78fb8089f25b08bcbf55ead008af57868e705f"
963+
},
964+
{
965+
"version": "2.11.2",
966+
"os": "macos",
967+
"size": 29447008,
968+
"sha512DigestHex": "280bac276b2770582706f0f8694c2d51b438472c98f935a30534b0b7bffe711cedd7ce49548b04528a51e2491d20993a9c272485ece7be25246661dc5f1ad879"
969+
},
970+
{
971+
"version": "2.11.2",
972+
"os": "linux",
973+
"size": 29368504,
974+
"sha512DigestHex": "4cd8e86467238c03a4ab54a770fd1e69b8abfccfd15d160e21adb4fbf3eb6d145e912a3a0bc65db779683e5c084637299df92e396707c2cbb52ec2c2d1161cc0"
975+
},
976+
{
977+
"version": "2.12.0",
978+
"os": "windows",
979+
"size": 29936128,
980+
"sha512DigestHex": "97fd6278c45fe03ff33a886be3e1ca743c0a965cb5dddf0ad63038738ea46b1a68a72b2a64487df25bd46a0916fedcbc6e21bf3228831a978e28325e356871a5"
981+
},
982+
{
983+
"version": "2.12.0",
984+
"os": "macos",
985+
"size": 29447008,
986+
"sha512DigestHex": "7e8041b163bcbda7a99dd75db65e87e7347169e2d3cc9f9dd047b1db76e088d988de544841795b7cae79fac676520361e1e0545f92d5ab75f7c583342a2e9ec2"
987+
},
988+
{
989+
"version": "2.12.0",
990+
"os": "linux",
991+
"size": 29372600,
992+
"sha512DigestHex": "caca15808010440226cb75853d1539291c69f0377f2973a03c53fbd9861142242842f954f60024c8db91c8221d66a7f824bd15c5b52a8a67c33a153ce8868808"
939993
}
940994
]
941995
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* Copyright 2025 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+
@file:OptIn(ExperimentalFirebaseDataConnect::class)
18+
19+
package com.google.firebase.dataconnect
20+
21+
import com.google.firebase.dataconnect.testutil.DataConnectIntegrationTestBase
22+
import com.google.firebase.dataconnect.testutil.property.arbitrary.dataConnect
23+
import com.google.firebase.dataconnect.testutil.schemas.AllTypesSchema
24+
import com.google.firebase.dataconnect.testutil.schemas.PersonSchema
25+
import io.kotest.assertions.withClue
26+
import io.kotest.matchers.shouldBe
27+
import io.kotest.property.Arb
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 UnknownKeysIntegrationTest : DataConnectIntegrationTestBase() {
35+
36+
private val personSchema by lazy { PersonSchema(dataConnectFactory) }
37+
private val allTypesSchema by lazy { AllTypesSchema(dataConnectFactory) }
38+
39+
@Test
40+
fun unknownKeysInQueryResponseDataShouldBeIgnored() = runTest {
41+
val id = Arb.dataConnect.uuid().next()
42+
allTypesSchema
43+
.createPrimitive(
44+
AllTypesSchema.PrimitiveData(
45+
id = id,
46+
idFieldNullable = "0d2fcdf1c4a84c64a87f3c7932b31749",
47+
intField = 42,
48+
intFieldNullable = 43,
49+
floatField = 123.45,
50+
floatFieldNullable = 678.91,
51+
booleanField = true,
52+
booleanFieldNullable = false,
53+
stringField = "TestString",
54+
stringFieldNullable = "TestNullableString"
55+
)
56+
)
57+
.execute()
58+
59+
@Serializable
60+
data class PrimitiveQueryDataValues(
61+
val intFieldNullable: Int?,
62+
val booleanField: Boolean,
63+
val booleanFieldNullable: Boolean?,
64+
val stringField: String,
65+
val stringFieldNullable: String?,
66+
)
67+
68+
/** An adaptation of [AllTypesSchema.GetPrimitiveQuery.Data] with some keys missing. */
69+
@Serializable
70+
data class PrimitiveQueryDataMissingSomeKeys(val primitive: PrimitiveQueryDataValues)
71+
72+
val result =
73+
allTypesSchema
74+
.getPrimitive(id = id)
75+
.withDataDeserializer(serializer<PrimitiveQueryDataMissingSomeKeys>())
76+
.execute()
77+
78+
result.data.primitive shouldBe
79+
PrimitiveQueryDataValues(
80+
intFieldNullable = 43,
81+
booleanField = true,
82+
booleanFieldNullable = false,
83+
stringField = "TestString",
84+
stringFieldNullable = "TestNullableString"
85+
)
86+
}
87+
88+
@Test
89+
fun unknownKeysInMutationResponseDataShouldBeIgnored() = runTest {
90+
@Serializable data class PersonKeyWithId(val id: String)
91+
val person1 = PersonKeyWithId(id = "8a218894521f457a9be45a0986494058")
92+
val person4 = PersonKeyWithId(id = "ef8de2a4a6de400e94d555f148b643c0")
93+
val mutationRef =
94+
personSchema.dataConnect.mutation("create5People", Unit, serializer<Unit>(), serializer())
95+
96+
// Precondition check: Verify that the response contains person1..person5 and the expected IDs.
97+
withClue("precondition check") {
98+
@Serializable
99+
data class Create5PeopleData(
100+
val person1: PersonKeyWithId,
101+
val person3: PersonKeyWithId,
102+
val person2: PersonKeyWithId,
103+
val person4: PersonKeyWithId,
104+
val person5: PersonKeyWithId,
105+
)
106+
107+
val mutationResult =
108+
mutationRef.withDataDeserializer(serializer<Create5PeopleData>()).execute()
109+
mutationResult.data shouldBe
110+
Create5PeopleData(
111+
person1 = person1,
112+
person2 = PersonKeyWithId(id = "464443371f284194be4b2e78c3ef000c"),
113+
person3 = PersonKeyWithId(id = "903d83db81754bd29860458f127ef124"),
114+
person4 = person4,
115+
person5 = PersonKeyWithId(id = "8584fd7ca2b6453da18d21d4341f1804"),
116+
)
117+
}
118+
119+
withClue("actual test") {
120+
// Create5PeopleDataWithMissingKeys is missing "person2.id", "person3", and "person5" which
121+
// will be present in the response.
122+
@Serializable data class PersonKeyWithoutId(val foo: Nothing? = null)
123+
@Serializable
124+
data class Create5PeopleDataWithMissingKeys(
125+
val person1: PersonKeyWithId,
126+
val person2: PersonKeyWithoutId,
127+
val person4: PersonKeyWithId,
128+
)
129+
130+
val mutationResult =
131+
mutationRef.withDataDeserializer(serializer<Create5PeopleDataWithMissingKeys>()).execute()
132+
mutationResult.data shouldBe
133+
Create5PeopleDataWithMissingKeys(
134+
person1 = person1,
135+
person2 = PersonKeyWithoutId(),
136+
person4 = PersonKeyWithId(id = "ef8de2a4a6de400e94d555f148b643c0"),
137+
)
138+
}
139+
}
140+
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,12 @@ private class ProtoStructValueDecoder(
177177
addAll(struct.fieldsMap.keys)
178178
addAll(descriptor.elementNames)
179179
}
180-
elementIndexes = names.map(descriptor::getElementIndex).sorted().iterator()
180+
elementIndexes =
181+
names
182+
.map(descriptor::getElementIndex)
183+
.filter { it != CompositeDecoder.UNKNOWN_NAME } // ignore unknown keys
184+
.sorted()
185+
.iterator()
181186
}
182187

183188
return elementIndexes

0 commit comments

Comments
 (0)