Skip to content

Commit

Permalink
VIT-7518: Exceptions thrown from OkHttp interceptor must be okio.IOEx…
Browse files Browse the repository at this point in the history
…ception to be deemed recoverable (#166)
  • Loading branch information
andersio authored Sep 26, 2024
1 parent 840ad31 commit 3c3ad62
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ import io.tryvital.client.jwt.AbstractVitalJWTAuth
import kotlinx.coroutines.runBlocking
import okhttp3.Interceptor
import okhttp3.Response
import okio.IOException

data class VitalRequestError(val wrapped: Throwable): IOException()

internal class ApiKeyInterceptor(
private val configurationReader: ConfigurationReader,
private val jwtAuth: AbstractVitalJWTAuth,
): Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val authStrategy = configurationReader.authStrategy ?: throw VitalClientUnconfigured()
val authStrategy = configurationReader.authStrategy ?: throw VitalRequestError(VitalClientUnconfigured())

val request = when (authStrategy) {
is VitalClientAuthStrategy.APIKey -> {
Expand All @@ -22,17 +25,31 @@ internal class ApiKeyInterceptor(
}

is VitalClientAuthStrategy.JWT -> {
val token = runBlocking { jwtAuth.withAccessToken { it } }
val token = try {
runBlocking { jwtAuth.withAccessToken { it } }
} catch (e: Throwable) {
// Any new exception we introduced must be wrapped in VitalRequestError.
// Otherwise OkHttp will treat it as "unexpected" and crash the process.
// https://github.com/square/retrofit/issues/3505
throw VitalRequestError(e)
}
chain.request().newBuilder().addHeader("authorization", "Bearer $token").build()
}
}

val response = chain.proceed(request)

return if (response.code == 401 && authStrategy is VitalClientAuthStrategy.JWT) {
val token = runBlocking {
jwtAuth.refreshToken()
jwtAuth.withAccessToken { it }
val token = try {
runBlocking {
jwtAuth.refreshToken()
jwtAuth.withAccessToken { it }
}
} catch (e: Throwable) {
// Any new exception we introduced must be wrapped in VitalRequestError.
// Otherwise OkHttp will treat it as "unexpected" and crash the process.
// https://github.com/square/retrofit/issues/3505
throw VitalRequestError(e)
}
val retryRequest = chain.request().newBuilder().addHeader("authorization", "Bearer $token").build()
chain.proceed(retryRequest)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.health.connect.client.records.HeartRateRecord
import androidx.health.connect.client.records.HeartRateVariabilityRmssdRecord
import androidx.health.connect.client.records.HeightRecord
import androidx.health.connect.client.records.HydrationRecord
import androidx.health.connect.client.records.NutritionRecord
import androidx.health.connect.client.records.OxygenSaturationRecord
import androidx.health.connect.client.records.Record
import androidx.health.connect.client.records.RespiratoryRateRecord
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/io/tryvital/sample/AppSettingsStore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class AppSettingsStore(
val status = VitalClient.status

val isConfigured = VitalClient.Status.Configured in status
update { it.copy(isSDKConfigured = isConfigured) }
update { it.copy(isSDKConfigured = isConfigured, sdkUserId = VitalClient.currentUserId) }
}

companion object {
Expand Down Expand Up @@ -70,6 +70,7 @@ data class AppSettings(
val environment: Environment = Environment.Sandbox,
val region: Region = Region.US,
val userId: String = "",
val sdkUserId: String? = null,
val isSDKConfigured: Boolean = false,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ fun SettingsScreen(store: AppSettingsStore, navController: NavHostController) {
)

TextField(
state.value.sdkUserId,
state.value.sdkUserId ?: "null",
onValueChange = {},
label = { Text("User ID") },
readOnly = true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,16 +167,18 @@ enum class SettingsAuthMode {
data class SettingsState(
val appSettings: AppSettings = AppSettings(),

val currentError: Throwable? = null,
) {

/**
* The current SDK user ID.
*
* Does not have to match [AppSettings.userId] if new app settings have not yet applied to the
* SDK.
* */
val sdkUserId: String = "",
val sdkUserId: String?
get() = appSettings.sdkUserId

val currentError: Throwable? = null,
) {
val isApiKeyValid: Boolean
get() = appSettings.apiKey != ""

Expand Down

0 comments on commit 3c3ad62

Please sign in to comment.