Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
package dev.androidbroadcast.featured.firebase

/**
* Thrown when a Firebase Remote Config fetch operation fails.
* Thrown when a Firebase Remote Config operation fails.
*
* This exception wraps any underlying error (e.g. network failure, timeout, or
* Firebase service error) that occurs during [FirebaseConfigValueProvider.fetch].
* Firebase service error) that occurs during any [FirebaseConfigValueProvider] operation,
* including [FirebaseConfigValueProvider.initialize] and [FirebaseConfigValueProvider.fetch].
*
* ## Retry recommendation
*
* Firebase Remote Config applies its own throttle limits; callers should not retry
* immediately on every failure. A typical strategy is:
* - Catch [FetchException] and log the [cause] for diagnostics.
* immediately on every fetch failure. A typical strategy is:
* - Catch [FirebaseConfigException] and log the [cause] for diagnostics.
* - Retry only on subsequent app launches or after a significant delay (e.g. 1 hour).
* - Use `FirebaseRemoteConfigSettings.minimumFetchIntervalInSeconds` to control
* how aggressively Firebase fetches from the server.
*
* @param message A human-readable description of the failure.
* @param cause The underlying exception that triggered this failure.
*/
public class FetchException(
public class FirebaseConfigException(
message: String,
cause: Throwable,
) : Exception(message, cause)
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public class FirebaseConfigValueProvider(
* This does NOT activate fetched-but-unactivated config — use [fetch] with `activate = true`
* or call `FirebaseRemoteConfig.activate()` directly when activation is desired.
*
* @throws FetchException if [FirebaseRemoteConfig.ensureInitialized] fails.
* @throws FirebaseConfigException if [FirebaseRemoteConfig.ensureInitialized] fails.
* @throws kotlinx.coroutines.CancellationException if the coroutine is cancelled; propagated
* without wrapping.
*/
Expand All @@ -103,7 +103,7 @@ public class FirebaseConfigValueProvider(
} catch (e: CancellationException) {
throw e
} catch (e: Exception) {
throw FetchException("Firebase Remote Config initialize failed", e)
throw FirebaseConfigException("Firebase Remote Config initialize failed", e)
}
}

Expand All @@ -112,11 +112,11 @@ public class FirebaseConfigValueProvider(
*
* @param activate When `true`, calls `fetchAndActivate()` so values become available
* immediately after this call. When `false`, only fetches without activating.
* @throws FetchException if the Firebase fetch operation fails for any reason, including
* @throws FirebaseConfigException if the Firebase fetch operation fails for any reason, including
* network errors, timeouts, or service unavailability. This wraps all non-cancellation
* exceptions — including [RuntimeException] thrown by [kotlinx.coroutines.tasks.await]
* on Firebase task failure. The [FetchException.cause] holds the original exception for
* diagnostics. See [FetchException] for retry recommendations.
* on Firebase task failure. The [FirebaseConfigException.cause] holds the original exception for
* diagnostics. See [FirebaseConfigException] for retry recommendations.
* @throws kotlinx.coroutines.CancellationException if the coroutine is cancelled while
* the fetch is in progress; propagated without wrapping.
*/
Expand All @@ -132,7 +132,7 @@ public class FirebaseConfigValueProvider(
} catch (e: CancellationException) {
throw e
} catch (e: Exception) {
throw FetchException("Firebase Remote Config fetch failed", e)
throw FirebaseConfigException("Firebase Remote Config fetch failed", e)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,17 +260,17 @@ class FirebaseConfigValueProviderTest {
}

@Test
fun `initialize wraps task failure in FetchException`() =
fun `initialize wraps task failure in FirebaseConfigException`() =
runTest {
val cause = RuntimeException("disk read error")
every { remoteConfig.ensureInitialized() } returns Tasks.forException(cause)

val ex = assertFailsWith<FetchException> { provider.initialize() }
val ex = assertFailsWith<FirebaseConfigException> { provider.initialize() }
assertSame(cause, ex.cause)
}

@Test
fun `initialize rethrows CancellationException without wrapping in FetchException`() =
fun `initialize rethrows CancellationException without wrapping in FirebaseConfigException`() =
runTest {
every { remoteConfig.ensureInitialized() } returns Tasks.forException(CancellationException("cancelled"))

Expand Down Expand Up @@ -319,7 +319,7 @@ class FirebaseConfigValueProviderTest {
val networkError = RuntimeException("Network unavailable")
every { remoteConfig.fetchAndActivate() } returns Tasks.forException(networkError)

assertFailsWith<FetchException> { provider.fetch(activate = true) }
assertFailsWith<FirebaseConfigException> { provider.fetch(activate = true) }
}

@Test
Expand All @@ -328,7 +328,7 @@ class FirebaseConfigValueProviderTest {
val networkError = RuntimeException("Network unavailable")
every { remoteConfig.fetch() } returns Tasks.forException(networkError)

assertFailsWith<FetchException> { provider.fetch(activate = false) }
assertFailsWith<FirebaseConfigException> { provider.fetch(activate = false) }
}

// --- Converters registry ---
Expand Down
Loading