Skip to content

Commit

Permalink
Merge pull request #33 from LivingWithHippos/error-body
Browse files Browse the repository at this point in the history
Refresh Token
  • Loading branch information
LivingWithHippos authored Oct 2, 2020
2 parents 70dc503 + 5cb4a3e commit 775f46c
Show file tree
Hide file tree
Showing 22 changed files with 228 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import com.github.livingwithhippos.unchained.data.remote.UnrestrictApiHelperImpl
import com.github.livingwithhippos.unchained.data.remote.UserApi
import com.github.livingwithhippos.unchained.data.remote.UserApiHelper
import com.github.livingwithhippos.unchained.data.remote.UserApiHelperImpl
import com.github.livingwithhippos.unchained.data.remote.VariousApi
import com.github.livingwithhippos.unchained.data.remote.VariousApiHelper
import com.github.livingwithhippos.unchained.data.remote.VariousApiHelperImpl
import com.github.livingwithhippos.unchained.utilities.BASE_AUTH_URL
import com.github.livingwithhippos.unchained.utilities.BASE_URL
import com.squareup.moshi.Moshi
Expand All @@ -42,6 +45,12 @@ import javax.inject.Singleton
@Module
object ApiFactory {

/*********************************/
// N.B. all updates to this code //
// also need to be ported to the //
// release build flavor version. //
/*********************************/

@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
Expand Down Expand Up @@ -159,4 +168,16 @@ object ApiFactory {
@Singleton
fun provideHostsApiHelper(apiHelper: HostsApiHelperImpl): HostsApiHelper =
apiHelper

// various api injection
@Provides
@Singleton
fun provideVariousApi(@ApiRetrofit retrofit: Retrofit): VariousApi {
return retrofit.create(VariousApi::class.java)
}

@Provides
@Singleton
fun provideVariousApiHelper(apiHelper: VariousApiHelperImpl): VariousApiHelper =
apiHelper
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ class AuthenticationViewModel @ViewModelInject constructor(
* @param expireIn: the time in seconds before the deviceCode is not valid anymore for the secrets endpoint
*/
fun fetchSecrets(deviceCode: String, expireIn: Int) {
// 5 seconds is the value suggested by real debrid
val waitTime = 5000L
// this is just an estimate, keeping track of time would be more precise. As of now this value should be 120
var calls = (expireIn * 1000 / waitTime).toInt() - 10
// remove 10% of the calls to account for the api calls
calls -= calls / 10
viewModelScope.launch {
var secretData = authRepository.getSecrets(deviceCode)
secretLiveData.postValue(Event(secretData))
while (secretData?.clientId == null && calls-- > 0 && (getAuthState() != AuthenticationState.AUTHENTICATED || getAuthState() != AuthenticationState.AUTHENTICATED_NO_PREMIUM)) {
delay(waitTime)
secretData = authRepository.getSecrets(deviceCode)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,6 @@ class MainActivity : UnchainedActivity() {
if (bottomNavManager?.onBackPressed()==false) {
// check if we're in the home bottom bar, otherwise press back
val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav_view)
val asd = bottomNav.selectedItemId
val home = R.id.navigation_home
if (bottomNav.selectedItemId != R.id.navigation_home)
super.onBackPressed()
else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import com.squareup.moshi.JsonClass
data class APIError(
@Json(name = "error")
val error: String,
@Json(name = "error_details")
val errorDetails: String?,
@Json(name = "error_code")
val errorCode: Int?
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ data class Token(
@Json(name = "access_token")
val accessToken: String,
@Json(name = "expires_in")
val expiresIn: String,
val expiresIn: Int,
@Json(name = "token_type")
val tokenType: String,
@Json(name = "refresh_token")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,11 @@ sealed class NetworkResponse<out T : Any> {
data class Success<out T : Any>(val data: T) : NetworkResponse<T>()
data class SuccessEmptyBody(val code: Int) : NetworkResponse<Nothing>()
data class Error(val exception: Exception) : NetworkResponse<Nothing>()
}

sealed class CompleteNetworkResponse<out T : Any?, out U: APIError?> {
data class Success<out T : Any>(val data: T) : CompleteNetworkResponse<T, Nothing>()
data class SuccessEmptyBody(val code: Int) : CompleteNetworkResponse<Nothing, Nothing>()
data class Error(val errorMessage: String) : CompleteNetworkResponse<Nothing, Nothing>()
data class RDError<out U: APIError?>(val error: U) : CompleteNetworkResponse<Nothing, APIError?>()
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ interface AuthApiHelper {
suspend fun getToken(
clientId: String,
clientSecret: String,
deviceCode: String
code: String
): Response<Token>

suspend fun disableToken(
token: String
): Response<Any>
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ class AuthApiHelperImpl @Inject constructor(private val authenticationApi: Authe
override suspend fun getToken(
clientId: String,
clientSecret: String,
deviceCode: String
): Response<Token> = authenticationApi.getToken(clientId, clientSecret, deviceCode)

override suspend fun disableToken(token: String): Response<Any> =
authenticationApi.disableToken(token)
code: String
): Response<Token> = authenticationApi.getToken(clientId, clientSecret, code)
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,7 @@ interface AuthenticationApi {
suspend fun getToken(
@Field("client_id") clientId: String,
@Field("client_secret") clientSecret: String,
@Field("code") deviceCode: String,
@Field("code") code: String,
@Field("grant_type") grantType: String = OPEN_SOURCE_GRANT_TYPE
): Response<Token>

/**
* Disable the current access token
*/
@GET("disable_access_token")
suspend fun disableToken(
@Header("Authorization") token: String
): Response<Any>
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ interface DownloadsApi {
* @param limit Entries returned per page / request (must be within 0 and 100, default: 50)
* @return a Response<List<DownloadItem>> a list of download items
*/
@GET(" downloads")
@GET("downloads")
suspend fun getDownloads(
@Header("Authorization") token: String,
@Query("offset") offset: Int?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ interface TorrentsApi {
@Query("offset") offset: Int? = 0,
@Query("page") page: Int? = 1,
@Query("limit") limit: Int? = 10,
@Query("filter ") filter: String?
@Query("filter") filter: String?
): Response<List<TorrentItem>>

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ interface UnrestrictApi {
// Password to unlock the file access hoster side
@Field("password") password: String? = null,
// 0 or 1, use Remote traffic, dedicated servers and account sharing protections lifted
@Field("remote ") remote: Int? = null
@Field("remote") remote: Int? = null
): Response<DownloadItem>

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.github.livingwithhippos.unchained.data.remote

import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Header

/**
* This interface is used by Retrofit to manage various api calls not fitting elsewhere
*/
interface VariousApi {
/**
* Disable the current access token
*/
@GET("disable_access_token")
suspend fun disableToken(
@Header("Authorization") token: String
): Response<Unit>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.github.livingwithhippos.unchained.data.remote

import retrofit2.Response

interface VariousApiHelper {
suspend fun disableToken(token: String): Response<Unit>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.github.livingwithhippos.unchained.data.remote

import retrofit2.Response
import javax.inject.Inject

class VariousApiHelperImpl @Inject constructor(private val variousApi: VariousApi) :
VariousApiHelper {
override suspend fun disableToken(token: String): Response<Unit> = variousApi.disableToken(token)
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,55 +23,43 @@ class AuthenticationRepository @Inject constructor(private val apiHelper: AuthAp

suspend fun getSecrets(code: String): Secrets? {

val authResponse = safeApiCall(
val secretResponse = safeApiCall(
call = { apiHelper.getSecrets(deviceCode = code) },
errorMessage = "Error Fetching Secrets"
)

return authResponse;
return secretResponse;

}

suspend fun getToken(clientId: String, clientSecret: String, deviceCode: String): Token? {
suspend fun getToken(clientId: String, clientSecret: String, code: String): Token? {

val authResponse = safeApiCall(
val tokenResponse = safeApiCall(
call = {
apiHelper.getToken(
clientId = clientId,
clientSecret = clientSecret,
deviceCode = deviceCode
code = code
)
},
errorMessage = "Error Fetching Token"
)

return authResponse;
return tokenResponse;

}

suspend fun disableToken(token: String): Any? {

val authResponse = safeApiCall(
call = { apiHelper.disableToken("Bearer $token") },
errorMessage = "Error disabling token"
)

return authResponse;

}


/**
* Get a new open source Token that usually lasts one hour.
* You can not use both offset and page at the same time, page is prioritized in case it happens.
* @param clientId the client id obtained from the /device/credentials endpoint
* @param refreshCode the code obtained from the /token endpoint
* @param clientSecret the code obtained from the /token endpoint
* @param deviceCode the device code obtained from the /device/code endpoint
* @return the new Token
*/
suspend fun refreshToken(clientId: String, refreshCode: String, deviceCode: String): Token? =
getToken(clientId, refreshCode, deviceCode)
suspend fun refreshToken(clientId: String, clientSecret: String, refreshToken: String): Token? =
getToken(clientId, clientSecret, refreshToken)

suspend fun refreshToken(credentials: Credentials): Token? =
refreshToken(credentials.clientId!!, credentials.refreshToken!!, credentials.deviceCode)
refreshToken(credentials.clientId!!, credentials.clientSecret!!, credentials.refreshToken!!)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ package com.github.livingwithhippos.unchained.data.repositoy

import android.util.Log
import com.github.livingwithhippos.unchained.BuildConfig
import com.github.livingwithhippos.unchained.data.model.APIError
import com.github.livingwithhippos.unchained.data.model.CompleteNetworkResponse
import com.github.livingwithhippos.unchained.data.model.NetworkResponse
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import okhttp3.ResponseBody
import retrofit2.Call
import retrofit2.Callback
Expand All @@ -15,25 +19,10 @@ import java.io.IOException
*/
open class BaseRepository {

suspend fun <T : Any> unsafeApiCall(call: suspend () -> Response<T>, errorMessage: String): T? {

val result: NetworkResponse<T> = unsafeApiResult(call, errorMessage)
var data: T? = null

when (result) {
is NetworkResponse.Success ->
data = result.data
is NetworkResponse.SuccessEmptyBody ->
if (BuildConfig.DEBUG)
Log.d("BaseRepository", "Successful call with empty body : ${result.code}")
is NetworkResponse.Error ->
if (BuildConfig.DEBUG)
Log.d("BaseRepository", errorMessage)

}

return data
}
//todo: inject this
private val jsonAdapter: JsonAdapter<APIError> = Moshi.Builder()
.build()
.adapter(APIError::class.java)

suspend fun <T : Any> safeApiCall(call: suspend () -> Response<T>, errorMessage: String): T? {

Expand Down Expand Up @@ -70,35 +59,6 @@ open class BaseRepository {
return NetworkResponse.Error(IOException("Error Occurred while getting api result, error : $errorMessage"))
}

private suspend fun <T : Any> unsafeApiResult(
call: suspend () -> Response<T>,
errorMessage: String
): NetworkResponse<T> {
val response = call.invoke()
if (response.isSuccessful) {
val body = response.body()
if (body != null)
return NetworkResponse.Success(body)
else
return NetworkResponse.SuccessEmptyBody(response.code())
} else {
//todo: implement error handling as JSON APIError class
/*
if (response.code() in 400..599) {
val moshi = Moshi.Builder().build()
val adapter: JsonAdapter<APIError> =
moshi.adapter(APIError::class.java)
// todo: wrap with withContext(Dispatchers.IO)?
val apiError = adapter.fromJson(response.errorBody().toString())
if (apiError!=null)
throw APIException(apiError)
}*/

}

return NetworkResponse.Error(IOException("Error Occurred while getting api result, error : $errorMessage"))
}

suspend fun safeEmptyApiCall(call: suspend () -> Call<ResponseBody>, errorMessage: String): Int? {

//fixme: this fun returns always -1
Expand All @@ -119,4 +79,27 @@ open class BaseRepository {

return responseCode
}

public suspend fun <T : Any> errorApiResult(
call: suspend () -> Response<T>,
errorMessage: String
): CompleteNetworkResponse<T?, APIError?> {
val response = call.invoke()
if (response.isSuccessful) {
val body = response.body()
if (body != null)
return CompleteNetworkResponse.Success(body)
else
return CompleteNetworkResponse.SuccessEmptyBody(response.code())
} else {
try {
val error: APIError? = jsonAdapter.fromJson(response.errorBody()!!.string())
return CompleteNetworkResponse.RDError(error)
} catch (e: IOException) {
e.printStackTrace()
}
}

return CompleteNetworkResponse.Error(errorMessage)
}
}
Loading

0 comments on commit 775f46c

Please sign in to comment.