From eb1ceae4bec95d29f1a9efacd4552daa9f4b05c6 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 8 Apr 2026 13:03:59 -0400 Subject: [PATCH 1/4] dataconnect(change): refactor toCompactString() for future code reuse --- firebase-dataconnect/CHANGELOG.md | 2 + .../dataconnect/util/DeserializeUtils.kt | 49 ++++++++++++++++++ .../firebase/dataconnect/util/ProtoUtil.kt | 51 ++++++++++++------- 3 files changed, 83 insertions(+), 19 deletions(-) create mode 100644 firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/DeserializeUtils.kt diff --git a/firebase-dataconnect/CHANGELOG.md b/firebase-dataconnect/CHANGELOG.md index 78097416fc2..afc83dfd537 100644 --- a/firebase-dataconnect/CHANGELOG.md +++ b/firebase-dataconnect/CHANGELOG.md @@ -7,6 +7,8 @@ ([#7910](https://github.com/firebase/firebase-android-sdk/pull/7910)) - [changed] Internal refactor to use immutable byte arrays. ([#7957](https://github.com/firebase/firebase-android-sdk/pull/7957)) +- [changed] Internal refactor for calculating debug logging strings. + ([#NNNN](https://github.com/firebase/firebase-android-sdk/pull/NNNN)) # 17.2.0 diff --git a/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/DeserializeUtils.kt b/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/DeserializeUtils.kt new file mode 100644 index 00000000000..5643092c688 --- /dev/null +++ b/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/DeserializeUtils.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.firebase.dataconnect.util + +import com.google.firebase.dataconnect.DataConnectPathSegment +import com.google.firebase.dataconnect.core.DataConnectOperationFailureResponseImpl +import com.google.firebase.dataconnect.util.ProtoUtil.toCompactString +import com.google.protobuf.ListValue +import com.google.protobuf.Value +import google.firebase.dataconnect.proto.GraphqlError + +internal object DeserializeUtils { + + fun GraphqlError.toErrorInfoImpl() = + DataConnectOperationFailureResponseImpl.ErrorInfoImpl( + message = message, + path = path.toPathSegment(), + ) + + private fun ListValue.toPathSegment() = + valuesList.map { + when (it.kindCase) { + Value.KindCase.STRING_VALUE -> DataConnectPathSegment.Field(it.stringValue) + Value.KindCase.NUMBER_VALUE -> DataConnectPathSegment.ListIndex(it.numberValue.toInt()) + // The cases below are expected to never occur; however, implement some logic for them + // to avoid things like throwing exceptions in those cases. + Value.KindCase.NULL_VALUE -> DataConnectPathSegment.Field("null") + Value.KindCase.BOOL_VALUE -> DataConnectPathSegment.Field(it.boolValue.toString()) + Value.KindCase.LIST_VALUE -> DataConnectPathSegment.Field(it.listValue.toCompactString()) + Value.KindCase.STRUCT_VALUE -> + DataConnectPathSegment.Field(it.structValue.toCompactString()) + else -> DataConnectPathSegment.Field(it.toString()) + } + } +} diff --git a/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/ProtoUtil.kt b/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/ProtoUtil.kt index beebc6ba45f..5b364839de7 100644 --- a/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/ProtoUtil.kt +++ b/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/ProtoUtil.kt @@ -18,8 +18,8 @@ package com.google.firebase.dataconnect.util import com.google.firebase.dataconnect.DataConnectPath import com.google.firebase.dataconnect.DataConnectPathSegment -import com.google.firebase.dataconnect.core.DataConnectGrpcClientGlobals.toErrorInfoImpl import com.google.firebase.dataconnect.toPathString +import com.google.firebase.dataconnect.util.DeserializeUtils.toErrorInfoImpl import com.google.firebase.dataconnect.util.ProtoUtil.nullProtoValue import com.google.firebase.dataconnect.util.ProtoUtil.toValueProto import com.google.protobuf.Duration @@ -37,6 +37,8 @@ import google.firebase.dataconnect.proto.ExecuteMutationRequest import google.firebase.dataconnect.proto.ExecuteMutationResponse import google.firebase.dataconnect.proto.ExecuteQueryRequest import google.firebase.dataconnect.proto.ExecuteQueryResponse +import google.firebase.dataconnect.proto.GraphqlError +import google.firebase.dataconnect.proto.GraphqlResponseExtensions import google.firebase.dataconnect.proto.ServiceInfo import java.io.BufferedWriter import java.io.CharArrayWriter @@ -223,25 +225,28 @@ internal object ProtoUtil { fun ExecuteQueryResponse.toStructProto(): Struct = buildStructProto { if (hasData()) put("data", data) - putList("errors") { errorsList.forEach { add(it.toErrorInfoImpl().toString()) } } - + if (errorsCount > 0) { + put("errors", errorsList) + } if (hasExtensions()) { - putStruct("extensions") { - putList("data_connect") { - extensions.dataConnectList.forEach { dataConnectProperties -> - addStruct { - if (dataConnectProperties.hasPath()) { - put("path", dataConnectProperties.path.toDataConnectPath().toPathString()) - } - dataConnectProperties.entityId - .takeIf { it.isNotEmpty() } - ?.let { put("entity_id", it) } - dataConnectProperties.entityIdsList - .takeIf { it.isNotEmpty() } - ?.let { putList("entity_ids") { it.forEach(::add) } } - if (dataConnectProperties.hasMaxAge()) { - put("max_age", dataConnectProperties.maxAge.toHumanFriendlyString()) - } + put("extensions", extensions) + } + } + + fun StructProtoBuilder.put(key: String, extensions: GraphqlResponseExtensions) { + putStruct(key) { + putList("data_connect") { + extensions.dataConnectList.forEach { dataConnectProperties -> + addStruct { + if (dataConnectProperties.hasPath()) { + put("path", dataConnectProperties.path.toDataConnectPath().toPathString()) + } + dataConnectProperties.entityId.takeIf { it.isNotEmpty() }?.let { put("entity_id", it) } + dataConnectProperties.entityIdsList + .takeIf { it.isNotEmpty() } + ?.let { putList("entity_ids") { it.forEach(::add) } } + if (dataConnectProperties.hasMaxAge()) { + put("max_age", dataConnectProperties.maxAge.toHumanFriendlyString()) } } } @@ -249,6 +254,10 @@ internal object ProtoUtil { } } + fun StructProtoBuilder.put(key: String, errors: Collection) { + putList(key) { errors.forEach { add(it.toErrorInfoImpl().toString()) } } + } + fun Duration.toHumanFriendlyString(): String = humanFriendlyStringForDuration(seconds, nanos) private fun humanFriendlyStringForDuration(seconds: Long, nanos: Int): String = buildString { @@ -510,6 +519,10 @@ internal class StructProtoBuilder(struct: Struct? = null) { builder.putFields(key, value?.toValueProto() ?: nullProtoValue) } + fun putValue(key: String, value: Value?) { + builder.putFields(key, value ?: nullProtoValue) + } + fun putList(key: String, block: ListValueProtoBuilder.() -> Unit) { val initialValue = builder.getFieldsOrDefault(key, Value.getDefaultInstance()).listValueOrNull builder.putFields(key, ListValueProtoBuilder(initialValue).apply(block).build().toValueProto()) From 7d96f521d828d42fa54a2141aab30ca6e938fa6d Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 8 Apr 2026 13:07:40 -0400 Subject: [PATCH 2/4] CHANGELOG.md: update pr number --- firebase-dataconnect/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firebase-dataconnect/CHANGELOG.md b/firebase-dataconnect/CHANGELOG.md index afc83dfd537..b6eb119fb90 100644 --- a/firebase-dataconnect/CHANGELOG.md +++ b/firebase-dataconnect/CHANGELOG.md @@ -8,7 +8,7 @@ - [changed] Internal refactor to use immutable byte arrays. ([#7957](https://github.com/firebase/firebase-android-sdk/pull/7957)) - [changed] Internal refactor for calculating debug logging strings. - ([#NNNN](https://github.com/firebase/firebase-android-sdk/pull/NNNN)) + ([#8024](https://github.com/firebase/firebase-android-sdk/pull/8024)) # 17.2.0 From 8a6d170c1c198f277cd84786a91bfab8256f7898 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 8 Apr 2026 14:00:05 -0400 Subject: [PATCH 3/4] DeserializeUtils.kt: simplify `when` clause of `toPathSegment()` [skip actions] --- .../google/firebase/dataconnect/util/DeserializeUtils.kt | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/DeserializeUtils.kt b/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/DeserializeUtils.kt index 5643092c688..8cc0f0a7933 100644 --- a/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/DeserializeUtils.kt +++ b/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/DeserializeUtils.kt @@ -36,14 +36,9 @@ internal object DeserializeUtils { when (it.kindCase) { Value.KindCase.STRING_VALUE -> DataConnectPathSegment.Field(it.stringValue) Value.KindCase.NUMBER_VALUE -> DataConnectPathSegment.ListIndex(it.numberValue.toInt()) - // The cases below are expected to never occur; however, implement some logic for them + // The other cases are expected to never occur; however, implement some logic for them // to avoid things like throwing exceptions in those cases. - Value.KindCase.NULL_VALUE -> DataConnectPathSegment.Field("null") - Value.KindCase.BOOL_VALUE -> DataConnectPathSegment.Field(it.boolValue.toString()) - Value.KindCase.LIST_VALUE -> DataConnectPathSegment.Field(it.listValue.toCompactString()) - Value.KindCase.STRUCT_VALUE -> - DataConnectPathSegment.Field(it.structValue.toCompactString()) - else -> DataConnectPathSegment.Field(it.toString()) + else -> DataConnectPathSegment.Field(it.toCompactString()) } } } From c9e4e5db60770ba2ba87f47781b060d3594537d4 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 8 Apr 2026 14:01:33 -0400 Subject: [PATCH 4/4] ProtoUtil.kt: remove unused function: `fun putValue(String, Value?)` --- .../kotlin/com/google/firebase/dataconnect/util/ProtoUtil.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/ProtoUtil.kt b/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/ProtoUtil.kt index 5b364839de7..4c0466c9bb5 100644 --- a/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/ProtoUtil.kt +++ b/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/ProtoUtil.kt @@ -519,10 +519,6 @@ internal class StructProtoBuilder(struct: Struct? = null) { builder.putFields(key, value?.toValueProto() ?: nullProtoValue) } - fun putValue(key: String, value: Value?) { - builder.putFields(key, value ?: nullProtoValue) - } - fun putList(key: String, block: ListValueProtoBuilder.() -> Unit) { val initialValue = builder.getFieldsOrDefault(key, Value.getDefaultInstance()).listValueOrNull builder.putFields(key, ListValueProtoBuilder(initialValue).apply(block).build().toValueProto())