diff --git a/firebase-functions/CHANGELOG.md b/firebase-functions/CHANGELOG.md index 26c83870573..24dbbc626ed 100644 --- a/firebase-functions/CHANGELOG.md +++ b/firebase-functions/CHANGELOG.md @@ -1,5 +1,6 @@ # Unreleased -* [fixed] Fixed HttpsCallableResult.data resolution in Kotlin +* [fixed] Minor internal infrastructure improvements. (#6544) +* [fixed] Fixed HttpsCallableResult.data resolution in Kotlin. (#6530) # 21.1.0 * [changed] Migrated to Kotlin @@ -209,4 +210,3 @@ updates. optional region to override the default "us-central1". * [feature] New `useFunctionsEmulator` method allows testing against a local instance of the [Cloud Functions Emulator](https://firebase.google.com/docs/functions/local-emulator). - diff --git a/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseContextProvider.kt b/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseContextProvider.kt index 860ce0ee842..7ab1f74bf5d 100644 --- a/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseContextProvider.kt +++ b/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseContextProvider.kt @@ -47,13 +47,16 @@ constructor( appCheckDeferred.whenAvailable { p: Provider -> val appCheck = p.get() appCheckRef.set(appCheck) - appCheck.addAppCheckTokenListener { unused: AppCheckTokenResult? -> } + appCheck.addAppCheckTokenListener { + // Do nothing; we just need to register a listener so that the App Check SDK knows + // to auto-refresh the token. + } } } - override fun getContext(limitedUseAppCheckToken: Boolean): Task? { + override fun getContext(getLimitedUseAppCheckToken: Boolean): Task? { val authToken = getAuthToken() - val appCheckToken = getAppCheckToken(limitedUseAppCheckToken) + val appCheckToken = getAppCheckToken(getLimitedUseAppCheckToken) return Tasks.whenAll(authToken, appCheckToken).onSuccessTask(executor) { _ -> Tasks.forResult( HttpsCallableContext(authToken.result, instanceId.get().token, appCheckToken.result) diff --git a/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseFunctions.kt b/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseFunctions.kt index 3f5fe5d63d9..3c0e7d6553e 100644 --- a/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseFunctions.kt +++ b/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseFunctions.kt @@ -58,16 +58,16 @@ internal constructor( @UiThread uiExecutor: Executor ) { // The network client to use for HTTPS requests. - private val client: OkHttpClient + private val client: OkHttpClient = OkHttpClient() // A serializer to encode/decode parameters and return values. - private val serializer: Serializer + private val serializer: Serializer = Serializer() // A provider of client metadata to include with calls. - private val contextProvider: ContextProvider + private val contextProvider: ContextProvider = Preconditions.checkNotNull(contextProvider) // The projectId to use for all functions references. - private val projectId: String + private val projectId: String = Preconditions.checkNotNull(projectId) // The region to use for all function references. private var region: String? = null @@ -82,12 +82,7 @@ internal constructor( private var emulatorSettings: EmulatedServiceSettings? = null init { - client = OkHttpClient() - serializer = Serializer() - this.contextProvider = Preconditions.checkNotNull(contextProvider) - this.projectId = Preconditions.checkNotNull(projectId) - val isRegion: Boolean - isRegion = + val isRegion: Boolean = try { URL(regionOrCustomDomain) false @@ -182,9 +177,7 @@ internal constructor( options: HttpsCallOptions ): Task { return providerInstalled.task - .continueWithTask(executor) { task: Task? -> - contextProvider.getContext(options.limitedUseAppCheckTokens) - } + .continueWithTask(executor) { contextProvider.getContext(options.limitedUseAppCheckTokens) } .continueWithTask(executor) { task: Task -> if (!task.isSuccessful) { return@continueWithTask Tasks.forException(task.exception!!) @@ -204,9 +197,7 @@ internal constructor( */ internal fun call(url: URL, data: Any?, options: HttpsCallOptions): Task { return providerInstalled.task - .continueWithTask(executor) { task: Task? -> - contextProvider.getContext(options.limitedUseAppCheckTokens) - } + .continueWithTask(executor) { contextProvider.getContext(options.limitedUseAppCheckTokens) } .continueWithTask(executor) { task: Task -> if (!task.isSuccessful) { return@continueWithTask Tasks.forException(task.exception!!) @@ -277,16 +268,15 @@ internal constructor( @Throws(IOException::class) override fun onResponse(ignored: Call, response: Response) { val code = fromHttpStatus(response.code()) - val body = response.body()!!.string() - val exception = fromResponse(code, body, serializer) + val bodyAsString = response.body()!!.string() + val exception = fromResponse(code, bodyAsString, serializer) if (exception != null) { tcs.setException(exception) return } - val bodyJSON: JSONObject - bodyJSON = + val bodyAsJson: JSONObject = try { - JSONObject(body) + JSONObject(bodyAsString) } catch (je: JSONException) { val e: Exception = FirebaseFunctionsException( @@ -298,10 +288,10 @@ internal constructor( tcs.setException(e) return } - var dataJSON = bodyJSON.opt("data") + var dataJSON = bodyAsJson.opt("data") // TODO: Allow "result" instead of "data" for now, for backwards compatibility. if (dataJSON == null) { - dataJSON = bodyJSON.opt("result") + dataJSON = bodyAsJson.opt("result") } if (dataJSON == null) { val e: Exception = diff --git a/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseFunctionsException.kt b/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseFunctionsException.kt index 1d8653a521c..87102e0fc7b 100644 --- a/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseFunctionsException.kt +++ b/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseFunctionsException.kt @@ -27,7 +27,7 @@ public class FirebaseFunctionsException : FirebaseException { * canonical error codes for Google APIs, as documented here: * https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto#L26 */ - public enum class Code(private val value: Int) { + public enum class Code(@Suppress("unused") private val value: Int) { /** * The operation completed successfully. FirebaseFunctionsException will never have a status of * OK. @@ -199,21 +199,21 @@ public class FirebaseFunctionsException : FirebaseException { serializer: Serializer ): FirebaseFunctionsException? { // Start with reasonable defaults from the status code. - var code = code - var description = code.name + var actualCode = code + var description = actualCode.name var details: Any? = null // Then look through the body for explicit details. try { - val json = JSONObject(body) + val json = JSONObject(body ?: "") val error = json.getJSONObject("error") if (error.opt("status") is String) { - code = Code.valueOf(error.getString("status")) + actualCode = Code.valueOf(error.getString("status")) // TODO: Add better default descriptions for error enums. // The default description needs to be updated for the new code. - description = code.name + description = actualCode.name } - if (error.opt("message") is String && !error.getString("message").isEmpty()) { + if (error.opt("message") is String && error.getString("message").isNotEmpty()) { description = error.getString("message") } details = error.opt("details") @@ -222,16 +222,16 @@ public class FirebaseFunctionsException : FirebaseException { } } catch (iae: IllegalArgumentException) { // This most likely means the status string was invalid, so consider this malformed. - code = Code.INTERNAL - description = code.name + actualCode = Code.INTERNAL + description = actualCode.name } catch (ioe: JSONException) { // If we couldn't parse explicit error data, that's fine. } - return if (code == Code.OK) { + return if (actualCode == Code.OK) { // Technically, there's an edge case where a developer could explicitly return an error code // of OK, and we will treat it as success, but that seems reasonable. null - } else FirebaseFunctionsException(description, code, details) + } else FirebaseFunctionsException(description, actualCode, details) } } } diff --git a/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsRegistrar.kt b/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsRegistrar.kt index ec774d6e479..d642872c9d4 100644 --- a/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsRegistrar.kt +++ b/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsRegistrar.kt @@ -27,7 +27,6 @@ import com.google.firebase.components.Dependency import com.google.firebase.components.Qualified import com.google.firebase.iid.internal.FirebaseInstanceIdInternal import com.google.firebase.platforminfo.LibraryVersionComponent -import java.util.Arrays import java.util.concurrent.Executor /** @@ -40,7 +39,7 @@ public class FunctionsRegistrar : ComponentRegistrar { override fun getComponents(): List> { val liteExecutor = Qualified.qualified(Lightweight::class.java, Executor::class.java) val uiExecutor = Qualified.qualified(UiThread::class.java, Executor::class.java) - return Arrays.asList( + return listOf( Component.builder(FunctionsMultiResourceComponent::class.java) .name(LIBRARY_NAME) .add(Dependency.required(Context::class.java)) @@ -60,7 +59,7 @@ public class FunctionsRegistrar : ComponentRegistrar { .setIid(c.getProvider(FirebaseInstanceIdInternal::class.java)) .setAppCheck(c.getDeferred(InteropAppCheckTokenProvider::class.java)) .build() - ?.multiResourceComponent + .multiResourceComponent } .build(), LibraryVersionComponent.create(LIBRARY_NAME, BuildConfig.VERSION_NAME) diff --git a/firebase-functions/src/main/java/com/google/firebase/functions/Serializer.kt b/firebase-functions/src/main/java/com/google/firebase/functions/Serializer.kt index fac34490845..619ae2d75f1 100644 --- a/firebase-functions/src/main/java/com/google/firebase/functions/Serializer.kt +++ b/firebase-functions/src/main/java/com/google/firebase/functions/Serializer.kt @@ -65,10 +65,9 @@ public class Serializer { } if (obj is Map<*, *>) { val result = JSONObject() - val m = obj - for (k in m.keys) { + for (k in obj.keys) { require(k is String) { "Object keys must be strings." } - val value = encode(m[k]) + val value = encode(obj[k]) try { result.put(k, value) } catch (e: JSONException) { @@ -87,11 +86,10 @@ public class Serializer { } if (obj is JSONObject) { val result = JSONObject() - val m = obj - val keys = m.keys() + val keys = obj.keys() while (keys.hasNext()) { val k = keys.next() ?: throw IllegalArgumentException("Object keys cannot be null.") - val value = encode(m.opt(k)) + val value = encode(obj.opt(k)) try { result.put(k, value) } catch (e: JSONException) { @@ -103,9 +101,8 @@ public class Serializer { } if (obj is JSONArray) { val result = JSONArray() - val l = obj - for (i in 0 until l.length()) { - val o = l.opt(i) + for (i in 0 until obj.length()) { + val o = obj.opt(i) result.put(encode(o)) } return result @@ -113,8 +110,9 @@ public class Serializer { throw IllegalArgumentException("Object cannot be encoded in JSON: $obj") } - public fun decode(obj: Any): Any? { + public fun decode(obj: Any?): Any? { // TODO: Maybe this should throw a FirebaseFunctionsException instead? + if (obj == null) return null if (obj is Number) { return obj } diff --git a/firebase-functions/src/test/java/com/google/firebase/functions/FunctionsTests.kt b/firebase-functions/src/test/java/com/google/firebase/functions/FunctionsTests.kt index 71428ab4660..642e2e3a5ca 100644 --- a/firebase-functions/src/test/java/com/google/firebase/functions/FunctionsTests.kt +++ b/firebase-functions/src/test/java/com/google/firebase/functions/FunctionsTests.kt @@ -98,7 +98,7 @@ class FunctionsTests : BaseTestCase() { class LibraryVersionTest : BaseTestCase() { @Test fun `library version should be registered with runtime`() { - val publisher = Firebase.app.get(UserAgentPublisher::class.java) + Firebase.app.get(UserAgentPublisher::class.java) } }