Extract Base URL from authentication

This commit is contained in:
Kirill Kamakin
2022-04-04 02:40:32 +05:00
parent 617bcc7eae
commit f44f54522d
47 changed files with 760 additions and 316 deletions

View File

@@ -2,7 +2,7 @@ package gq.kirmanak.mealient.data.auth
interface AuthDataSource {
/**
* Tries to acquire authentication token using the provided credentials on specified server.
* Tries to acquire authentication token using the provided credentials
*/
suspend fun authenticate(username: String, password: String, baseUrl: String): String
suspend fun authenticate(username: String, password: String): String
}

View File

@@ -3,11 +3,8 @@ package gq.kirmanak.mealient.data.auth
import kotlinx.coroutines.flow.Flow
interface AuthRepo {
suspend fun authenticate(username: String, password: String, baseUrl: String)
suspend fun getBaseUrl(): String?
suspend fun requireBaseUrl(): String
suspend fun authenticate(username: String, password: String)
suspend fun getAuthHeader(): String?

View File

@@ -3,9 +3,7 @@ package gq.kirmanak.mealient.data.auth
import kotlinx.coroutines.flow.Flow
interface AuthStorage {
suspend fun storeAuthData(authHeader: String, baseUrl: String)
suspend fun getBaseUrl(): String?
suspend fun storeAuthData(authHeader: String)
suspend fun getAuthHeader(): String?

View File

@@ -1,8 +1,8 @@
package gq.kirmanak.mealient.data.auth.impl
import gq.kirmanak.mealient.data.auth.AuthDataSource
import gq.kirmanak.mealient.data.auth.impl.AuthenticationError.*
import gq.kirmanak.mealient.data.network.ErrorDetail
import gq.kirmanak.mealient.data.network.NetworkError.*
import gq.kirmanak.mealient.data.network.ServiceFactory
import gq.kirmanak.mealient.extensions.decodeErrorBodyOrNull
import kotlinx.coroutines.CancellationException
@@ -20,13 +20,14 @@ class AuthDataSourceImpl @Inject constructor(
private val json: Json,
) : AuthDataSource {
override suspend fun authenticate(
username: String,
password: String,
baseUrl: String
): String {
Timber.v("authenticate() called with: username = $username, password = $password, baseUrl = $baseUrl")
val authService = authServiceFactory.provideService(baseUrl)
override suspend fun authenticate(username: String, password: String): String {
Timber.v("authenticate() called with: username = $username, password = $password")
val authService = try {
authServiceFactory.provideService()
} catch (e: Exception) {
Timber.e(e, "authenticate: can't create Retrofit service")
throw MalformedUrl(e)
}
val response = sendRequest(authService, username, password)
val accessToken = parseToken(response)
Timber.v("authenticate() returned: $accessToken")

View File

@@ -1,14 +1,10 @@
package gq.kirmanak.mealient.data.auth.impl
import android.net.Uri
import androidx.annotation.VisibleForTesting
import gq.kirmanak.mealient.data.auth.AuthDataSource
import gq.kirmanak.mealient.data.auth.AuthRepo
import gq.kirmanak.mealient.data.auth.AuthStorage
import gq.kirmanak.mealient.data.auth.impl.AuthenticationError.MalformedUrl
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import okhttp3.HttpUrl.Companion.toHttpUrl
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@@ -19,23 +15,13 @@ class AuthRepoImpl @Inject constructor(
private val storage: AuthStorage,
) : AuthRepo {
override suspend fun authenticate(
username: String,
password: String,
baseUrl: String
) {
Timber.v("authenticate() called with: username = $username, password = $password, baseUrl = $baseUrl")
val url = parseBaseUrl(baseUrl)
val accessToken = dataSource.authenticate(username, password, url)
override suspend fun authenticate(username: String, password: String) {
Timber.v("authenticate() called with: username = $username, password = $password")
val accessToken = dataSource.authenticate(username, password)
Timber.d("authenticate result is \"$accessToken\"")
storage.storeAuthData(AUTH_HEADER_FORMAT.format(accessToken), url)
storage.storeAuthData(AUTH_HEADER_FORMAT.format(accessToken))
}
override suspend fun getBaseUrl(): String? = storage.getBaseUrl()
override suspend fun requireBaseUrl(): String =
checkNotNull(getBaseUrl()) { "Base URL is null when it was required" }
override suspend fun getAuthHeader(): String? = storage.getAuthHeader()
override suspend fun requireAuthHeader(): String =
@@ -51,18 +37,6 @@ class AuthRepoImpl @Inject constructor(
storage.clearAuthData()
}
@VisibleForTesting
fun parseBaseUrl(baseUrl: String): String = try {
val withScheme = Uri.parse(baseUrl).let {
if (it.scheme == null) it.buildUpon().scheme("https").build()
else it
}.toString()
withScheme.toHttpUrl().toString()
} catch (e: Throwable) {
Timber.e(e, "authenticate: can't parse base url $baseUrl")
throw MalformedUrl(e)
}
companion object {
private const val AUTH_HEADER_FORMAT = "Bearer %s"
}

View File

@@ -13,20 +13,10 @@ class AuthStorageImpl @Inject constructor(
) : AuthStorage {
private val authHeaderKey by preferencesStorage::authHeaderKey
private val baseUrlKey by preferencesStorage::baseUrlKey
override suspend fun storeAuthData(authHeader: String, baseUrl: String) {
Timber.v("storeAuthData() called with: authHeader = $authHeader, baseUrl = $baseUrl")
preferencesStorage.storeValues(
Pair(authHeaderKey, authHeader),
Pair(baseUrlKey, baseUrl),
)
}
override suspend fun getBaseUrl(): String? {
val baseUrl = preferencesStorage.getValue(baseUrlKey)
Timber.d("getBaseUrl: base url is $baseUrl")
return baseUrl
override suspend fun storeAuthData(authHeader: String) {
Timber.v("storeAuthData() called with: authHeader = $authHeader")
preferencesStorage.storeValues(Pair(authHeaderKey, authHeader))
}
override suspend fun getAuthHeader(): String? {
@@ -43,6 +33,6 @@ class AuthStorageImpl @Inject constructor(
override suspend fun clearAuthData() {
Timber.v("clearAuthData() called")
preferencesStorage.removeValues(authHeaderKey, baseUrlKey)
preferencesStorage.removeValues(authHeaderKey)
}
}

View File

@@ -1,8 +0,0 @@
package gq.kirmanak.mealient.data.auth.impl
sealed class AuthenticationError(cause: Throwable) : RuntimeException(cause) {
class Unauthorized(cause: Throwable) : AuthenticationError(cause)
class NoServerConnection(cause: Throwable) : AuthenticationError(cause)
class NotMealie(cause: Throwable) : AuthenticationError(cause)
class MalformedUrl(cause: Throwable) : AuthenticationError(cause)
}