Skip to content

Commit dda33f3

Browse files
authored
Merge branch 'main' into td/record-exception
2 parents bf9d546 + 01bd3d7 commit dda33f3

File tree

17 files changed

+131
-83
lines changed

17 files changed

+131
-83
lines changed

firebase-crashlytics/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Unreleased
2+
* [fixed] Fixed inefficiency in the Kotlin `FirebaseCrashlytics.setCustomKeys` extension.
23
* [fixed] Execute failure listener outside the main thread [#6535]
34

45
# 19.2.1
@@ -634,3 +635,4 @@ The following release notes describe changes in the new SDK.
634635
from your `AndroidManifest.xml` file.
635636
* [removed] The `fabric.properties` and `crashlytics.properties` files are no
636637
longer supported. Remove them from your app.
638+

firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/CrashlyticsTests.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,31 @@ class CrashlyticsTests {
5959
Firebase.app.get(UserAgentPublisher::class.java)
6060
}
6161

62+
@Test
63+
fun keyValueBuilder() {
64+
val keyValueBuilder = KeyValueBuilder()
65+
keyValueBuilder.key("hello", "world")
66+
keyValueBuilder.key("hello2", 23)
67+
keyValueBuilder.key("hello3", 0.1)
68+
69+
val result: Map<String, String> = keyValueBuilder.build().keysAndValues
70+
71+
assertThat(result).containsExactly("hello", "world", "hello2", "23", "hello3", "0.1")
72+
}
73+
74+
@Test
75+
fun keyValueBuilder_withCrashlyticsInstance() {
76+
@Suppress("DEPRECATION") val keyValueBuilder = KeyValueBuilder(Firebase.crashlytics)
77+
keyValueBuilder.key("hello", "world")
78+
keyValueBuilder.key("hello2", 23)
79+
keyValueBuilder.key("hello3", 0.1)
80+
81+
val result: Map<String, String> = keyValueBuilder.build().keysAndValues
82+
83+
// The result is empty because it called crashlytics.setCustomKey for every key.
84+
assertThat(result).isEmpty()
85+
}
86+
6287
companion object {
6388
private const val APP_ID = "1:1:android:1a"
6489
private const val API_KEY = "API-KEY-API-KEY-API-KEY-API-KEY-API-KEY"

firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/metadata/MetaDataStoreTest.java

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,13 @@ public void testWriteUserData_noFields() throws Exception {
118118
}
119119

120120
@Test
121-
// TODO(b/375437048): Let @daymxn know if this test fails. It's flaky and running away from me:(
122121
public void testWriteUserData_singleField() throws Exception {
123122
crashlyticsWorkers.diskWrite.submit(
124-
() -> {
125-
storeUnderTest.writeUserData(SESSION_ID_1, metadataWithUserId(SESSION_ID_1).getUserId());
126-
});
123+
() ->
124+
storeUnderTest.writeUserData(
125+
SESSION_ID_1, metadataWithUserId(SESSION_ID_1).getUserId()));
127126
crashlyticsWorkers.diskWrite.await();
127+
Thread.sleep(10);
128128
UserMetadata userData =
129129
UserMetadata.loadFromExistingSession(SESSION_ID_1, fileStore, crashlyticsWorkers);
130130
assertEquals(USER_ID, userData.getUserId());
@@ -168,17 +168,15 @@ public void testWriteUserData_unicode() throws Exception {
168168
}
169169

170170
@Test
171-
// TODO(b/375437048): Let @daymxn know if this test fails. It's flaky and running away from me:(
172171
public void testWriteUserData_escaped() throws Exception {
173172
crashlyticsWorkers.diskWrite.submit(
174-
() -> {
175-
storeUnderTest.writeUserData(
176-
SESSION_ID_1, metadataWithUserId(SESSION_ID_1, ESCAPED).getUserId());
177-
});
173+
() ->
174+
storeUnderTest.writeUserData(
175+
SESSION_ID_1, metadataWithUserId(SESSION_ID_1, ESCAPED).getUserId()));
178176
crashlyticsWorkers.diskWrite.await();
177+
Thread.sleep(10);
179178
UserMetadata userData =
180179
UserMetadata.loadFromExistingSession(SESSION_ID_1, fileStore, crashlyticsWorkers);
181-
Thread.sleep(10);
182180
assertEquals(ESCAPED.trim(), userData.getUserId());
183181
}
184182

firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/FirebaseCrashlytics.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,8 @@ val Firebase.crashlytics: FirebaseCrashlytics
2727
get() = FirebaseCrashlytics.getInstance()
2828

2929
/** Associates all key-value parameters with the reports */
30-
fun FirebaseCrashlytics.setCustomKeys(init: KeyValueBuilder.() -> Unit) {
31-
val builder = KeyValueBuilder(this)
32-
builder.init()
33-
}
30+
fun FirebaseCrashlytics.setCustomKeys(init: KeyValueBuilder.() -> Unit) =
31+
setCustomKeys(KeyValueBuilder().apply(init).build())
3432

3533
/** @suppress */
3634
@Keep

firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/KeyValueBuilder.kt

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,48 @@
1616

1717
package com.google.firebase.crashlytics
1818

19-
/** Helper class to enable fluent syntax in [setCustomKeys] */
20-
class KeyValueBuilder(private val crashlytics: FirebaseCrashlytics) {
19+
/** Helper class to enable convenient syntax in [setCustomKeys] */
20+
class KeyValueBuilder
21+
private constructor(
22+
private val crashlytics: FirebaseCrashlytics?,
23+
private val builder: CustomKeysAndValues.Builder,
24+
) {
25+
@Deprecated(
26+
"Do not construct this directly. Use `setCustomKeys` instead. To be removed in the next major release."
27+
)
28+
constructor(crashlytics: FirebaseCrashlytics) : this(crashlytics, CustomKeysAndValues.Builder())
29+
30+
internal constructor() : this(crashlytics = null, CustomKeysAndValues.Builder())
31+
32+
internal fun build(): CustomKeysAndValues = builder.build()
2133

2234
/** Sets a custom key and value that are associated with reports. */
23-
fun key(key: String, value: Boolean) = crashlytics.setCustomKey(key, value)
35+
fun key(key: String, value: Boolean) {
36+
crashlytics?.setCustomKey(key, value) ?: builder.putBoolean(key, value)
37+
}
2438

2539
/** Sets a custom key and value that are associated with reports. */
26-
fun key(key: String, value: Double) = crashlytics.setCustomKey(key, value)
40+
fun key(key: String, value: Double) {
41+
crashlytics?.setCustomKey(key, value) ?: builder.putDouble(key, value)
42+
}
2743

2844
/** Sets a custom key and value that are associated with reports. */
29-
fun key(key: String, value: Float) = crashlytics.setCustomKey(key, value)
45+
fun key(key: String, value: Float) {
46+
crashlytics?.setCustomKey(key, value) ?: builder.putFloat(key, value)
47+
}
3048

3149
/** Sets a custom key and value that are associated with reports. */
32-
fun key(key: String, value: Int) = crashlytics.setCustomKey(key, value)
50+
fun key(key: String, value: Int) {
51+
crashlytics?.setCustomKey(key, value) ?: builder.putInt(key, value)
52+
}
3353

3454
/** Sets a custom key and value that are associated with reports. */
35-
fun key(key: String, value: Long) = crashlytics.setCustomKey(key, value)
55+
fun key(key: String, value: Long) {
56+
crashlytics?.setCustomKey(key, value) ?: builder.putLong(key, value)
57+
}
3658

3759
/** Sets a custom key and value that are associated with reports. */
38-
fun key(key: String, value: String) = crashlytics.setCustomKey(key, value)
60+
fun key(key: String, value: String) {
61+
crashlytics?.setCustomKey(key, value) ?: builder.putString(key, value)
62+
}
3963
}

firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/CrashlyticsCore.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,9 @@ public void setCustomKey(String key, String value) {
368368
* @throws NullPointerException if any key in keysAndValues is null.
369369
*/
370370
public void setCustomKeys(Map<String, String> keysAndValues) {
371-
crashlyticsWorkers.common.submit(() -> controller.setCustomKeys(keysAndValues));
371+
if (!keysAndValues.isEmpty()) {
372+
crashlyticsWorkers.common.submit(() -> controller.setCustomKeys(keysAndValues));
373+
}
372374
}
373375

374376
// endregion

firebase-functions/CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Unreleased
2-
* [fixed] Fixed HttpsCallableResult.data resolution in Kotlin
2+
* [fixed] Minor internal infrastructure improvements. (#6544)
3+
* [fixed] Fixed HttpsCallableResult.data resolution in Kotlin. (#6530)
34

45
# 21.1.0
56
* [changed] Migrated to Kotlin
@@ -209,4 +210,3 @@ updates.
209210
optional region to override the default "us-central1".
210211
* [feature] New `useFunctionsEmulator` method allows testing against a local
211212
instance of the [Cloud Functions Emulator](https://firebase.google.com/docs/functions/local-emulator).
212-

firebase-functions/src/main/java/com/google/firebase/functions/FirebaseContextProvider.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,16 @@ constructor(
4747
appCheckDeferred.whenAvailable { p: Provider<InteropAppCheckTokenProvider> ->
4848
val appCheck = p.get()
4949
appCheckRef.set(appCheck)
50-
appCheck.addAppCheckTokenListener { unused: AppCheckTokenResult? -> }
50+
appCheck.addAppCheckTokenListener {
51+
// Do nothing; we just need to register a listener so that the App Check SDK knows
52+
// to auto-refresh the token.
53+
}
5154
}
5255
}
5356

54-
override fun getContext(limitedUseAppCheckToken: Boolean): Task<HttpsCallableContext?>? {
57+
override fun getContext(getLimitedUseAppCheckToken: Boolean): Task<HttpsCallableContext?>? {
5558
val authToken = getAuthToken()
56-
val appCheckToken = getAppCheckToken(limitedUseAppCheckToken)
59+
val appCheckToken = getAppCheckToken(getLimitedUseAppCheckToken)
5760
return Tasks.whenAll(authToken, appCheckToken).onSuccessTask(executor) { _ ->
5861
Tasks.forResult(
5962
HttpsCallableContext(authToken.result, instanceId.get().token, appCheckToken.result)

firebase-functions/src/main/java/com/google/firebase/functions/FirebaseFunctions.kt

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,16 @@ internal constructor(
5858
@UiThread uiExecutor: Executor
5959
) {
6060
// The network client to use for HTTPS requests.
61-
private val client: OkHttpClient
61+
private val client: OkHttpClient = OkHttpClient()
6262

6363
// A serializer to encode/decode parameters and return values.
64-
private val serializer: Serializer
64+
private val serializer: Serializer = Serializer()
6565

6666
// A provider of client metadata to include with calls.
67-
private val contextProvider: ContextProvider
67+
private val contextProvider: ContextProvider = Preconditions.checkNotNull(contextProvider)
6868

6969
// The projectId to use for all functions references.
70-
private val projectId: String
70+
private val projectId: String = Preconditions.checkNotNull(projectId)
7171

7272
// The region to use for all function references.
7373
private var region: String? = null
@@ -82,12 +82,7 @@ internal constructor(
8282
private var emulatorSettings: EmulatedServiceSettings? = null
8383

8484
init {
85-
client = OkHttpClient()
86-
serializer = Serializer()
87-
this.contextProvider = Preconditions.checkNotNull(contextProvider)
88-
this.projectId = Preconditions.checkNotNull(projectId)
89-
val isRegion: Boolean
90-
isRegion =
85+
val isRegion: Boolean =
9186
try {
9287
URL(regionOrCustomDomain)
9388
false
@@ -182,9 +177,7 @@ internal constructor(
182177
options: HttpsCallOptions
183178
): Task<HttpsCallableResult> {
184179
return providerInstalled.task
185-
.continueWithTask(executor) { task: Task<Void>? ->
186-
contextProvider.getContext(options.limitedUseAppCheckTokens)
187-
}
180+
.continueWithTask(executor) { contextProvider.getContext(options.limitedUseAppCheckTokens) }
188181
.continueWithTask(executor) { task: Task<HttpsCallableContext?> ->
189182
if (!task.isSuccessful) {
190183
return@continueWithTask Tasks.forException<HttpsCallableResult>(task.exception!!)
@@ -204,9 +197,7 @@ internal constructor(
204197
*/
205198
internal fun call(url: URL, data: Any?, options: HttpsCallOptions): Task<HttpsCallableResult> {
206199
return providerInstalled.task
207-
.continueWithTask(executor) { task: Task<Void>? ->
208-
contextProvider.getContext(options.limitedUseAppCheckTokens)
209-
}
200+
.continueWithTask(executor) { contextProvider.getContext(options.limitedUseAppCheckTokens) }
210201
.continueWithTask(executor) { task: Task<HttpsCallableContext?> ->
211202
if (!task.isSuccessful) {
212203
return@continueWithTask Tasks.forException<HttpsCallableResult>(task.exception!!)
@@ -277,16 +268,15 @@ internal constructor(
277268
@Throws(IOException::class)
278269
override fun onResponse(ignored: Call, response: Response) {
279270
val code = fromHttpStatus(response.code())
280-
val body = response.body()!!.string()
281-
val exception = fromResponse(code, body, serializer)
271+
val bodyAsString = response.body()!!.string()
272+
val exception = fromResponse(code, bodyAsString, serializer)
282273
if (exception != null) {
283274
tcs.setException(exception)
284275
return
285276
}
286-
val bodyJSON: JSONObject
287-
bodyJSON =
277+
val bodyAsJson: JSONObject =
288278
try {
289-
JSONObject(body)
279+
JSONObject(bodyAsString)
290280
} catch (je: JSONException) {
291281
val e: Exception =
292282
FirebaseFunctionsException(
@@ -298,10 +288,10 @@ internal constructor(
298288
tcs.setException(e)
299289
return
300290
}
301-
var dataJSON = bodyJSON.opt("data")
291+
var dataJSON = bodyAsJson.opt("data")
302292
// TODO: Allow "result" instead of "data" for now, for backwards compatibility.
303293
if (dataJSON == null) {
304-
dataJSON = bodyJSON.opt("result")
294+
dataJSON = bodyAsJson.opt("result")
305295
}
306296
if (dataJSON == null) {
307297
val e: Exception =

firebase-functions/src/main/java/com/google/firebase/functions/FirebaseFunctionsException.kt

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public class FirebaseFunctionsException : FirebaseException {
2727
* canonical error codes for Google APIs, as documented here:
2828
* https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto#L26
2929
*/
30-
public enum class Code(private val value: Int) {
30+
public enum class Code(@Suppress("unused") private val value: Int) {
3131
/**
3232
* The operation completed successfully. FirebaseFunctionsException will never have a status of
3333
* OK.
@@ -199,21 +199,21 @@ public class FirebaseFunctionsException : FirebaseException {
199199
serializer: Serializer
200200
): FirebaseFunctionsException? {
201201
// Start with reasonable defaults from the status code.
202-
var code = code
203-
var description = code.name
202+
var actualCode = code
203+
var description = actualCode.name
204204
var details: Any? = null
205205

206206
// Then look through the body for explicit details.
207207
try {
208-
val json = JSONObject(body)
208+
val json = JSONObject(body ?: "")
209209
val error = json.getJSONObject("error")
210210
if (error.opt("status") is String) {
211-
code = Code.valueOf(error.getString("status"))
211+
actualCode = Code.valueOf(error.getString("status"))
212212
// TODO: Add better default descriptions for error enums.
213213
// The default description needs to be updated for the new code.
214-
description = code.name
214+
description = actualCode.name
215215
}
216-
if (error.opt("message") is String && !error.getString("message").isEmpty()) {
216+
if (error.opt("message") is String && error.getString("message").isNotEmpty()) {
217217
description = error.getString("message")
218218
}
219219
details = error.opt("details")
@@ -222,16 +222,16 @@ public class FirebaseFunctionsException : FirebaseException {
222222
}
223223
} catch (iae: IllegalArgumentException) {
224224
// This most likely means the status string was invalid, so consider this malformed.
225-
code = Code.INTERNAL
226-
description = code.name
225+
actualCode = Code.INTERNAL
226+
description = actualCode.name
227227
} catch (ioe: JSONException) {
228228
// If we couldn't parse explicit error data, that's fine.
229229
}
230-
return if (code == Code.OK) {
230+
return if (actualCode == Code.OK) {
231231
// Technically, there's an edge case where a developer could explicitly return an error code
232232
// of OK, and we will treat it as success, but that seems reasonable.
233233
null
234-
} else FirebaseFunctionsException(description, code, details)
234+
} else FirebaseFunctionsException(description, actualCode, details)
235235
}
236236
}
237237
}

0 commit comments

Comments
 (0)