Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d60f7bb9 authored by Guillaume Jacquart's avatar Guillaume Jacquart
Browse files

feat:3871: Retry with token refresh on Auth error on GPlay requests.

parent 507fd2bf
Loading
Loading
Loading
Loading
+16 −2
Original line number Diff line number Diff line
@@ -51,7 +51,9 @@ import foundation.e.apps.data.playstore.utils.GPlayHttpClient
import foundation.e.apps.data.playstore.utils.GplayHttpRequestException
import foundation.e.apps.data.preference.AppLoungeDataStore
import foundation.e.apps.di.qualifiers.IoCoroutineScope
import foundation.e.apps.domain.entities.HttpUnauthorizedException
import foundation.e.apps.utils.SystemInfoProvider
import foundation.e.apps.utils.exponentialBackoffRetry
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -67,6 +69,7 @@ import java.util.Properties
import javax.inject.Inject
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
import com.aurora.gplayapi.data.models.App as GplayApp

@Suppress("TooManyFunctions", "LongParameterList")
@@ -406,15 +409,26 @@ class PlayStoreRepository @Inject constructor(
    }

    private suspend fun <T> doAuthenticatedRequest(request: suspend (AuthData) -> T): T {
        return exponentialBackoffRetry(
            initialDelay = 100.milliseconds,
            maxDelay = 15.seconds, // exp: 0,2, 0,4, 0,8, 1,6, 3,2, 6,4 ; 12,8 .
            work = {
                gPlayRequestWithUnswallowedException { request(appLoungeDataStore.getAuthData()) }
            },
            predicate = { exception ->  exception is HttpUnauthorizedException },
            doBeforeRetry = { refreshPlayStoreAuthentication() }
        )
    }

    private suspend fun <T> gPlayRequestWithUnswallowedException(request: suspend () -> T): T {
        val lastErrorFlow = gPlayHttpClient.requestException.shareIn(
            scope = ioCoroutineScope,
            started = SharingStarted.Eagerly,
            replay = 1
        )

        val authData = appLoungeDataStore.getAuthData()
        return try {
            request(authData)
            request()
        } catch(e: Exception) {
            lastErrorFlow.timeout(gPlayHttpClientPukedExceptionWaitDuration)
                .catch {
+1 −1
Original line number Diff line number Diff line
@@ -205,7 +205,7 @@ class GPlayHttpClient @Inject constructor(
            }

            ioCoroutineScope.launch {
                rawErrorResponse.emit(parsedException)
                _requestException.emit(parsedException)
            }

            // Swallow the exception, until upper layer are upgraded.
+30 −0
Original line number Diff line number Diff line
package foundation.e.apps.utils

import kotlinx.coroutines.delay
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds

suspend fun <T> exponentialBackoffRetry(
    initialDelay: Duration = 100.milliseconds,
    maxDelay: Duration = 15.seconds,
    work: suspend () -> T,
    predicate: suspend (Throwable) -> Boolean = { true },
    doBeforeRetry: suspend () -> Unit = {}
): T {
    var d = initialDelay
    while (true) {
        try {
            if (d > initialDelay) {
                doBeforeRetry()
            }
            return work()
        } catch (exception: Exception) {
            d = d * 2
            if (predicate(exception) && d <= maxDelay) {
                delay(d)
            }
            throw exception
        }
    }
}