Skip to content
This repository was archived by the owner on Nov 9, 2024. It is now read-only.

Commit 5ed1acb

Browse files
authored
Simplify network layer (#175)
* Use Ktor for network requests * Remove V0 version * Remove Retrofit dependency * Fix url * Update versions of dependencies * Revert kotlinx-datetime Due to Kotlin/kotlinx-datetime#304 * Rename leftovers * Remove OkHttp * Remove unused manifest * Remove unused Hilt module * Fix building empty image URLs * Use OkHttp as engine for Ktor * Reduce visibility of internal classes * Fix first set up test * Store only auth token, not header * Remove UnitInfo/FoodInfo/VersionInfo/NewShoppingListItemInfo * Remove RecipeSummaryInfo and ShoppingListsInfo * Remove FullShoppingListInfo * Remove ParseRecipeURLInfo * Remove FullRecipeInfo * Sign out if access token does not work * Rename getVersionInfo method * Update version name
1 parent 888783b commit 5ed1acb

File tree

144 files changed

+1208
-2788
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

144 files changed

+1208
-2788
lines changed

app/build.gradle.kts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ plugins {
1616
android {
1717
defaultConfig {
1818
applicationId = "gq.kirmanak.mealient"
19-
versionCode = 30
20-
versionName = "0.4.1"
19+
versionCode = 31
20+
versionName = "0.4.2"
2121
testInstrumentationRunner = "gq.kirmanak.mealient.MealientTestRunner"
2222
testInstrumentationRunnerArguments += mapOf("clearPackageData" to "true")
2323
resourceConfigurations += listOf("en", "es", "ru", "fr", "nl", "pt", "de")
@@ -55,7 +55,7 @@ android {
5555

5656
namespace = "gq.kirmanak.mealient"
5757

58-
packagingOptions {
58+
packaging {
5959
resources.excludes += "DebugProbesKt.bin"
6060
}
6161

app/src/androidTest/kotlin/gq/kirmanak/mealient/response.VersionResponses.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ import okhttp3.mockwebserver.MockResponse
55
import okhttp3.mockwebserver.MockWebServer
66
import okhttp3.mockwebserver.RecordedRequest
77

8-
val versionV1Response = MockResponse().setResponseCode(200).setBody(
9-
"""{"production":true,"version":"v1.0.0beta-5","demoStatus":false,"allowSignup":true}"""
10-
)
8+
val versionV1Response = MockResponse()
9+
.setResponseCode(200)
10+
.setHeader("Content-Type", "application/json")
11+
.setBody("""{"production":true,"version":"v1.0.0beta-5","demoStatus":false,"allowSignup":true}""")
1112

12-
val notFoundResponse = MockResponse().setResponseCode(404).setBody("""{"detail":"Not found"}"""")
13+
val notFoundResponse = MockResponse()
14+
.setResponseCode(404)
15+
.setHeader("Content-Type", "application/json")
16+
.setBody("""{"detail":"Not found"}"""")
1317

1418
fun MockWebServer.dispatch(block: (String, RecordedRequest) -> MockResponse) {
1519
dispatcher = object : Dispatcher() {

app/src/main/java/gq/kirmanak/mealient/data/auth/AuthRepo.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ interface AuthRepo : ShoppingListsAuthRepo {
99

1010
suspend fun authenticate(email: String, password: String)
1111

12-
suspend fun getAuthHeader(): String?
12+
suspend fun getAuthToken(): String?
1313

1414
suspend fun logout()
1515
}

app/src/main/java/gq/kirmanak/mealient/data/auth/AuthStorage.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import kotlinx.coroutines.flow.Flow
44

55
interface AuthStorage {
66

7-
val authHeaderFlow: Flow<String?>
7+
val authTokenFlow: Flow<String?>
88

9-
suspend fun setAuthHeader(authHeader: String?)
9+
suspend fun setAuthToken(authToken: String?)
1010

11-
suspend fun getAuthHeader(): String?
11+
suspend fun getAuthToken(): String?
1212
}
Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,19 @@
11
package gq.kirmanak.mealient.data.auth.impl
22

33
import gq.kirmanak.mealient.data.auth.AuthDataSource
4-
import gq.kirmanak.mealient.data.baseurl.ServerInfoRepo
5-
import gq.kirmanak.mealient.data.baseurl.ServerVersion
6-
import gq.kirmanak.mealient.datasource.v0.MealieDataSourceV0
7-
import gq.kirmanak.mealient.datasource.v0.models.CreateApiTokenRequestV0
8-
import gq.kirmanak.mealient.datasource.v1.MealieDataSourceV1
9-
import gq.kirmanak.mealient.datasource.v1.models.CreateApiTokenRequestV1
4+
import gq.kirmanak.mealient.datasource.MealieDataSource
5+
import gq.kirmanak.mealient.datasource.models.CreateApiTokenRequest
106
import javax.inject.Inject
117

128
class AuthDataSourceImpl @Inject constructor(
13-
private val serverInfoRepo: ServerInfoRepo,
14-
private val v0Source: MealieDataSourceV0,
15-
private val v1Source: MealieDataSourceV1,
9+
private val dataSource: MealieDataSource,
1610
) : AuthDataSource {
1711

18-
private suspend fun getVersion(): ServerVersion = serverInfoRepo.getVersion()
19-
20-
override suspend fun authenticate(
21-
username: String,
22-
password: String,
23-
): String = when (getVersion()) {
24-
ServerVersion.V0 -> v0Source.authenticate(username, password)
25-
ServerVersion.V1 -> v1Source.authenticate(username, password)
12+
override suspend fun authenticate(username: String, password: String): String {
13+
return dataSource.authenticate(username, password)
2614
}
2715

28-
override suspend fun createApiToken(name: String): String = when (getVersion()) {
29-
ServerVersion.V0 -> v0Source.createApiToken(CreateApiTokenRequestV0(name)).token
30-
ServerVersion.V1 -> v1Source.createApiToken(CreateApiTokenRequestV1(name)).token
16+
override suspend fun createApiToken(name: String): String {
17+
return dataSource.createApiToken(CreateApiTokenRequest(name)).token
3118
}
3219
}

app/src/main/java/gq/kirmanak/mealient/data/auth/impl/AuthRepoImpl.kt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import gq.kirmanak.mealient.data.auth.AuthDataSource
44
import gq.kirmanak.mealient.data.auth.AuthRepo
55
import gq.kirmanak.mealient.data.auth.AuthStorage
66
import gq.kirmanak.mealient.datasource.AuthenticationProvider
7+
import gq.kirmanak.mealient.datasource.SignOutHandler
78
import gq.kirmanak.mealient.logging.Logger
89
import kotlinx.coroutines.flow.Flow
910
import kotlinx.coroutines.flow.map
@@ -13,28 +14,29 @@ class AuthRepoImpl @Inject constructor(
1314
private val authStorage: AuthStorage,
1415
private val authDataSource: AuthDataSource,
1516
private val logger: Logger,
17+
private val signOutHandler: SignOutHandler,
1618
) : AuthRepo, AuthenticationProvider {
1719

1820
override val isAuthorizedFlow: Flow<Boolean>
19-
get() = authStorage.authHeaderFlow.map { it != null }
21+
get() = authStorage.authTokenFlow.map { it != null }
2022

2123
override suspend fun authenticate(email: String, password: String) {
2224
logger.v { "authenticate() called with: email = $email, password = $password" }
2325
val token = authDataSource.authenticate(email, password)
24-
authStorage.setAuthHeader(AUTH_HEADER_FORMAT.format(token))
26+
authStorage.setAuthToken(token)
2527
val apiToken = authDataSource.createApiToken(API_TOKEN_NAME)
26-
authStorage.setAuthHeader(AUTH_HEADER_FORMAT.format(apiToken))
28+
authStorage.setAuthToken(apiToken)
2729
}
2830

29-
override suspend fun getAuthHeader(): String? = authStorage.getAuthHeader()
31+
override suspend fun getAuthToken(): String? = authStorage.getAuthToken()
3032

3133
override suspend fun logout() {
3234
logger.v { "logout() called" }
33-
authStorage.setAuthHeader(null)
35+
authStorage.setAuthToken(null)
36+
signOutHandler.signOut()
3437
}
3538

3639
companion object {
37-
private const val AUTH_HEADER_FORMAT = "Bearer %s"
3840
private const val API_TOKEN_NAME = "Mealient"
3941
}
4042
}

app/src/main/java/gq/kirmanak/mealient/data/auth/impl/AuthStorageImpl.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ class AuthStorageImpl @Inject constructor(
2222
private val logger: Logger,
2323
) : AuthStorage {
2424

25-
override val authHeaderFlow: Flow<String?>
25+
override val authTokenFlow: Flow<String?>
2626
get() = sharedPreferences
27-
.prefsChangeFlow(logger) { getString(AUTH_HEADER_KEY, null) }
27+
.prefsChangeFlow(logger) { getString(AUTH_TOKEN_KEY, null) }
2828
.distinctUntilChanged()
2929
private val singleThreadDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
3030

31-
override suspend fun setAuthHeader(authHeader: String?) = putString(AUTH_HEADER_KEY, authHeader)
31+
override suspend fun setAuthToken(authToken: String?) = putString(AUTH_TOKEN_KEY, authToken)
3232

33-
override suspend fun getAuthHeader(): String? = getString(AUTH_HEADER_KEY)
33+
override suspend fun getAuthToken(): String? = getString(AUTH_TOKEN_KEY)
3434

3535
private suspend fun putString(
3636
key: String,
@@ -48,6 +48,6 @@ class AuthStorageImpl @Inject constructor(
4848

4949
companion object {
5050
@VisibleForTesting
51-
const val AUTH_HEADER_KEY = "authHeader"
51+
const val AUTH_TOKEN_KEY = "authToken"
5252
}
5353
}
Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
11
package gq.kirmanak.mealient.data.baseurl
22

3-
import kotlinx.coroutines.flow.Flow
4-
53
interface ServerInfoRepo {
64

75
suspend fun getUrl(): String?
86

9-
suspend fun getVersion(): ServerVersion
10-
117
suspend fun tryBaseURL(baseURL: String): Result<Unit>
128

13-
fun versionUpdates(): Flow<ServerVersion>
14-
159
}
1610

Lines changed: 8 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
package gq.kirmanak.mealient.data.baseurl
22

33
import gq.kirmanak.mealient.datasource.ServerUrlProvider
4-
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
54
import gq.kirmanak.mealient.logging.Logger
6-
import kotlinx.coroutines.flow.Flow
7-
import kotlinx.coroutines.flow.filterNotNull
8-
import kotlinx.coroutines.flow.map
95
import javax.inject.Inject
106

117
class ServerInfoRepoImpl @Inject constructor(
@@ -20,47 +16,18 @@ class ServerInfoRepoImpl @Inject constructor(
2016
return result
2117
}
2218

23-
override suspend fun getVersion(): ServerVersion {
24-
var version = serverInfoStorage.getServerVersion()
25-
val serverVersion = if (version == null) {
26-
logger.d { "getVersion: version is null, requesting" }
27-
version = versionDataSource.getVersionInfo().version
28-
val result = determineServerVersion(version)
29-
serverInfoStorage.storeServerVersion(version)
30-
result
31-
} else {
32-
determineServerVersion(version)
33-
}
34-
logger.v { "getVersion() returned: $serverVersion from $version" }
35-
return serverVersion
36-
}
37-
38-
private fun determineServerVersion(version: String): ServerVersion = when {
39-
version.startsWith("v0") -> ServerVersion.V0
40-
version.startsWith("v1") -> ServerVersion.V1
41-
else -> {
42-
logger.w { "Unknown server version: $version" }
43-
ServerVersion.V1
44-
}
45-
}
46-
4719
override suspend fun tryBaseURL(baseURL: String): Result<Unit> {
48-
val oldVersion = serverInfoStorage.getServerVersion()
4920
val oldBaseUrl = serverInfoStorage.getBaseURL()
21+
serverInfoStorage.storeBaseURL(baseURL)
5022

51-
return runCatchingExceptCancel {
52-
serverInfoStorage.storeBaseURL(baseURL)
53-
val version = versionDataSource.getVersionInfo().version
54-
serverInfoStorage.storeServerVersion(version)
55-
}.onFailure {
56-
serverInfoStorage.storeBaseURL(oldBaseUrl, oldVersion)
23+
try {
24+
versionDataSource.requestVersion()
25+
} catch (e: Throwable) {
26+
serverInfoStorage.storeBaseURL(oldBaseUrl)
27+
return Result.failure(e)
5728
}
58-
}
5929

60-
override fun versionUpdates(): Flow<ServerVersion> {
61-
return serverInfoStorage
62-
.serverVersionUpdates()
63-
.filterNotNull()
64-
.map { determineServerVersion(it) }
30+
return Result.success(Unit)
6531
}
32+
6633
}
Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,9 @@
11
package gq.kirmanak.mealient.data.baseurl
22

3-
import kotlinx.coroutines.flow.Flow
4-
53
interface ServerInfoStorage {
64

75
suspend fun getBaseURL(): String?
86

9-
suspend fun storeBaseURL(baseURL: String)
10-
11-
suspend fun storeBaseURL(baseURL: String?, version: String?)
12-
13-
suspend fun storeServerVersion(version: String)
14-
15-
suspend fun getServerVersion(): String?
7+
suspend fun storeBaseURL(baseURL: String?)
168

17-
fun serverVersionUpdates(): Flow<String?>
189
}

app/src/main/java/gq/kirmanak/mealient/data/baseurl/ServerVersion.kt

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package gq.kirmanak.mealient.data.baseurl
22

3-
import gq.kirmanak.mealient.datasource.models.VersionInfo
3+
import gq.kirmanak.mealient.datasource.models.VersionResponse
44

55
interface VersionDataSource {
66

7-
suspend fun getVersionInfo(): VersionInfo
7+
suspend fun requestVersion(): VersionResponse
88
}
Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,14 @@
11
package gq.kirmanak.mealient.data.baseurl
22

3-
import gq.kirmanak.mealient.datasource.models.VersionInfo
4-
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
5-
import gq.kirmanak.mealient.datasource.v0.MealieDataSourceV0
6-
import gq.kirmanak.mealient.datasource.v1.MealieDataSourceV1
7-
import gq.kirmanak.mealient.model_mapper.ModelMapper
8-
import kotlinx.coroutines.async
9-
import kotlinx.coroutines.awaitAll
10-
import kotlinx.coroutines.coroutineScope
3+
import gq.kirmanak.mealient.datasource.MealieDataSource
4+
import gq.kirmanak.mealient.datasource.models.VersionResponse
115
import javax.inject.Inject
126

137
class VersionDataSourceImpl @Inject constructor(
14-
private val v0Source: MealieDataSourceV0,
15-
private val v1Source: MealieDataSourceV1,
16-
private val modelMapper: ModelMapper,
8+
private val dataSource: MealieDataSource,
179
) : VersionDataSource {
1810

19-
override suspend fun getVersionInfo(): VersionInfo {
20-
val responses = coroutineScope {
21-
val v0Deferred = async {
22-
runCatchingExceptCancel { modelMapper.toVersionInfo(v0Source.getVersionInfo()) }
23-
}
24-
val v1Deferred = async {
25-
runCatchingExceptCancel { modelMapper.toVersionInfo(v1Source.getVersionInfo()) }
26-
}
27-
listOf(v0Deferred, v1Deferred).awaitAll()
28-
}
29-
val firstSuccess = responses.firstNotNullOfOrNull { it.getOrNull() }
30-
if (firstSuccess == null) {
31-
throw responses.firstNotNullOf { it.exceptionOrNull() }
32-
} else {
33-
return firstSuccess
34-
}
11+
override suspend fun requestVersion(): VersionResponse {
12+
return dataSource.getVersionInfo()
3513
}
36-
3714
}

0 commit comments

Comments
 (0)