Handle server info inside of AuthDataSource

This commit is contained in:
Kirill Kamakin
2022-10-30 13:40:43 +01:00
parent 039ba20c83
commit 94c030c04d
7 changed files with 55 additions and 57 deletions

View File

@@ -1,23 +1,23 @@
package gq.kirmanak.mealient.data.add package gq.kirmanak.mealient.data.add
data class AddRecipeInfo( data class AddRecipeInfo(
val name: String = "", val name: String,
val description: String = "", val description: String,
val recipeYield: String = "", val recipeYield: String,
val recipeIngredient: List<AddRecipeIngredientInfo> = emptyList(), val recipeIngredient: List<AddRecipeIngredientInfo>,
val recipeInstructions: List<AddRecipeInstructionInfo> = emptyList(), val recipeInstructions: List<AddRecipeInstructionInfo>,
val settings: AddRecipeSettingsInfo = AddRecipeSettingsInfo(), val settings: AddRecipeSettingsInfo,
) )
data class AddRecipeSettingsInfo( data class AddRecipeSettingsInfo(
val disableComments: Boolean = false, val disableComments: Boolean,
val public: Boolean = true, val public: Boolean,
) )
data class AddRecipeIngredientInfo( data class AddRecipeIngredientInfo(
val note: String = "", val note: String,
) )
data class AddRecipeInstructionInfo( data class AddRecipeInstructionInfo(
val text: String = "", val text: String,
) )

View File

@@ -1,15 +1,8 @@
package gq.kirmanak.mealient.data.auth package gq.kirmanak.mealient.data.auth
import gq.kirmanak.mealient.data.baseurl.ServerVersion
interface AuthDataSource { interface AuthDataSource {
/** /**
* Tries to acquire authentication token using the provided credentials * Tries to acquire authentication token using the provided credentials
*/ */
suspend fun authenticate( suspend fun authenticate(username: String, password: String): String
username: String,
password: String,
baseUrl: String,
serverVersion: ServerVersion,
): String
} }

View File

@@ -1,6 +1,7 @@
package gq.kirmanak.mealient.data.auth.impl package gq.kirmanak.mealient.data.auth.impl
import gq.kirmanak.mealient.data.auth.AuthDataSource import gq.kirmanak.mealient.data.auth.AuthDataSource
import gq.kirmanak.mealient.data.baseurl.ServerInfoRepo
import gq.kirmanak.mealient.data.baseurl.ServerVersion import gq.kirmanak.mealient.data.baseurl.ServerVersion
import gq.kirmanak.mealient.datasource.v0.MealieDataSourceV0 import gq.kirmanak.mealient.datasource.v0.MealieDataSourceV0
import gq.kirmanak.mealient.datasource.v1.MealieDataSourceV1 import gq.kirmanak.mealient.datasource.v1.MealieDataSourceV1
@@ -9,6 +10,7 @@ import javax.inject.Singleton
@Singleton @Singleton
class AuthDataSourceImpl @Inject constructor( class AuthDataSourceImpl @Inject constructor(
private val serverInfoRepo: ServerInfoRepo,
private val v0Source: MealieDataSourceV0, private val v0Source: MealieDataSourceV0,
private val v1Source: MealieDataSourceV1, private val v1Source: MealieDataSourceV1,
) : AuthDataSource { ) : AuthDataSource {
@@ -16,10 +18,11 @@ class AuthDataSourceImpl @Inject constructor(
override suspend fun authenticate( override suspend fun authenticate(
username: String, username: String,
password: String, password: String,
baseUrl: String, ): String {
serverVersion: ServerVersion, val baseUrl = serverInfoRepo.requireUrl()
): String = when (serverVersion) { return when (serverInfoRepo.getVersion()) {
ServerVersion.V0 -> v0Source.authenticate(baseUrl, username, password) ServerVersion.V0 -> v0Source.authenticate(baseUrl, username, password)
ServerVersion.V1 -> v1Source.authenticate(baseUrl, username, password) ServerVersion.V1 -> v1Source.authenticate(baseUrl, username, password)
}
} }
} }

View File

@@ -3,7 +3,6 @@ package gq.kirmanak.mealient.data.auth.impl
import gq.kirmanak.mealient.data.auth.AuthDataSource import gq.kirmanak.mealient.data.auth.AuthDataSource
import gq.kirmanak.mealient.data.auth.AuthRepo import gq.kirmanak.mealient.data.auth.AuthRepo
import gq.kirmanak.mealient.data.auth.AuthStorage import gq.kirmanak.mealient.data.auth.AuthStorage
import gq.kirmanak.mealient.data.baseurl.ServerInfoRepo
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
import gq.kirmanak.mealient.logging.Logger import gq.kirmanak.mealient.logging.Logger
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@@ -15,7 +14,6 @@ import javax.inject.Singleton
class AuthRepoImpl @Inject constructor( class AuthRepoImpl @Inject constructor(
private val authStorage: AuthStorage, private val authStorage: AuthStorage,
private val authDataSource: AuthDataSource, private val authDataSource: AuthDataSource,
private val serverInfoRepo: ServerInfoRepo,
private val logger: Logger, private val logger: Logger,
) : AuthRepo { ) : AuthRepo {
@@ -24,11 +22,9 @@ class AuthRepoImpl @Inject constructor(
override suspend fun authenticate(email: String, password: String) { override suspend fun authenticate(email: String, password: String) {
logger.v { "authenticate() called with: email = $email, password = $password" } logger.v { "authenticate() called with: email = $email, password = $password" }
val version = serverInfoRepo.getVersion() val token = authDataSource.authenticate(email, password)
val url = serverInfoRepo.requireUrl() val header = AUTH_HEADER_FORMAT.format(token)
authDataSource.authenticate(email, password, url, version) authStorage.setAuthHeader(header)
.let { AUTH_HEADER_FORMAT.format(it) }
.let { authStorage.setAuthHeader(it) }
authStorage.setEmail(email) authStorage.setEmail(email)
authStorage.setPassword(password) authStorage.setPassword(password)
} }

View File

@@ -42,7 +42,7 @@ class AuthRepoImplTest {
@Before @Before
fun setUp() { fun setUp() {
MockKAnnotations.init(this) MockKAnnotations.init(this)
subject = AuthRepoImpl(storage, dataSource, serverInfoRepo, logger) subject = AuthRepoImpl(storage, dataSource, logger)
} }
@Test @Test
@@ -54,14 +54,7 @@ class AuthRepoImplTest {
@Test @Test
fun `when authenticate successfully then saves to storage`() = runTest { fun `when authenticate successfully then saves to storage`() = runTest {
coEvery { serverInfoRepo.getVersion() } returns TEST_SERVER_VERSION coEvery { serverInfoRepo.getVersion() } returns TEST_SERVER_VERSION
coEvery { coEvery { dataSource.authenticate(eq(TEST_USERNAME), eq(TEST_PASSWORD)) } returns TEST_TOKEN
dataSource.authenticate(
eq(TEST_USERNAME),
eq(TEST_PASSWORD),
eq(TEST_BASE_URL),
eq(TEST_SERVER_VERSION),
)
} returns TEST_TOKEN
coEvery { serverInfoRepo.requireUrl() } returns TEST_BASE_URL coEvery { serverInfoRepo.requireUrl() } returns TEST_BASE_URL
subject.authenticate(TEST_USERNAME, TEST_PASSWORD) subject.authenticate(TEST_USERNAME, TEST_PASSWORD)
coVerifyAll { coVerifyAll {
@@ -74,7 +67,7 @@ class AuthRepoImplTest {
@Test @Test
fun `when authenticate fails then does not change storage`() = runTest { fun `when authenticate fails then does not change storage`() = runTest {
coEvery { dataSource.authenticate(any(), any(), any(), any()) } throws RuntimeException() coEvery { dataSource.authenticate(any(), any()) } throws RuntimeException()
coEvery { serverInfoRepo.requireUrl() } returns TEST_BASE_URL coEvery { serverInfoRepo.requireUrl() } returns TEST_BASE_URL
runCatchingExceptCancel { subject.authenticate("invalid", "") } runCatchingExceptCancel { subject.authenticate("invalid", "") }
confirmVerified(storage) confirmVerified(storage)
@@ -113,17 +106,9 @@ class AuthRepoImplTest {
coEvery { storage.getPassword() } returns TEST_PASSWORD coEvery { storage.getPassword() } returns TEST_PASSWORD
coEvery { serverInfoRepo.getVersion() } returns TEST_SERVER_VERSION coEvery { serverInfoRepo.getVersion() } returns TEST_SERVER_VERSION
coEvery { serverInfoRepo.requireUrl() } returns TEST_BASE_URL coEvery { serverInfoRepo.requireUrl() } returns TEST_BASE_URL
coEvery { coEvery { dataSource.authenticate(eq(TEST_USERNAME), eq(TEST_PASSWORD)) } returns TEST_TOKEN
dataSource.authenticate(
eq(TEST_USERNAME), eq(TEST_PASSWORD), eq(TEST_BASE_URL), eq(TEST_SERVER_VERSION)
)
} returns TEST_TOKEN
subject.invalidateAuthHeader() subject.invalidateAuthHeader()
coVerifyAll { coVerifyAll { dataSource.authenticate(eq(TEST_USERNAME), eq(TEST_PASSWORD)) }
dataSource.authenticate(
eq(TEST_USERNAME), eq(TEST_PASSWORD), eq(TEST_BASE_URL), eq(TEST_SERVER_VERSION)
)
}
} }
@Test @Test
@@ -131,7 +116,7 @@ class AuthRepoImplTest {
coEvery { storage.getEmail() } returns "invalid" coEvery { storage.getEmail() } returns "invalid"
coEvery { storage.getPassword() } returns "" coEvery { storage.getPassword() } returns ""
coEvery { serverInfoRepo.requireUrl() } returns TEST_BASE_URL coEvery { serverInfoRepo.requireUrl() } returns TEST_BASE_URL
coEvery { dataSource.authenticate(any(), any(), any(), any()) } throws RuntimeException() coEvery { dataSource.authenticate(any(), any()) } throws RuntimeException()
subject.invalidateAuthHeader() subject.invalidateAuthHeader()
coVerify { storage.setEmail(null) } coVerify { storage.setEmail(null) }
} }

View File

@@ -1,5 +1,9 @@
package gq.kirmanak.mealient.test package gq.kirmanak.mealient.test
import gq.kirmanak.mealient.data.add.AddRecipeInfo
import gq.kirmanak.mealient.data.add.AddRecipeIngredientInfo
import gq.kirmanak.mealient.data.add.AddRecipeInstructionInfo
import gq.kirmanak.mealient.data.add.AddRecipeSettingsInfo
import gq.kirmanak.mealient.data.recipes.network.FullRecipeInfo import gq.kirmanak.mealient.data.recipes.network.FullRecipeInfo
import gq.kirmanak.mealient.data.recipes.network.RecipeIngredientInfo import gq.kirmanak.mealient.data.recipes.network.RecipeIngredientInfo
import gq.kirmanak.mealient.data.recipes.network.RecipeInstructionInfo import gq.kirmanak.mealient.data.recipes.network.RecipeInstructionInfo
@@ -174,4 +178,21 @@ object RecipeImplTestData {
PORRIDGE_BOIL_RECIPE_INSTRUCTION_ENTITY, PORRIDGE_BOIL_RECIPE_INSTRUCTION_ENTITY,
) )
) )
val PORRIDGE_ADD_RECIPE_INFO = AddRecipeInfo(
name = "Porridge",
description = "Tasty breakfast",
recipeYield = "5 servings",
recipeIngredient = listOf(
AddRecipeIngredientInfo("Milk"),
AddRecipeIngredientInfo("Sugar"),
AddRecipeIngredientInfo("Salt"),
AddRecipeIngredientInfo("Porridge"),
),
recipeInstructions = listOf(
AddRecipeInstructionInfo("Mix"),
AddRecipeInstructionInfo("Cook"),
),
settings = AddRecipeSettingsInfo(disableComments = false, public = true),
)
} }

View File

@@ -1,9 +1,9 @@
package gq.kirmanak.mealient.ui.add package gq.kirmanak.mealient.ui.add
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import gq.kirmanak.mealient.data.add.AddRecipeInfo
import gq.kirmanak.mealient.data.add.AddRecipeRepo import gq.kirmanak.mealient.data.add.AddRecipeRepo
import gq.kirmanak.mealient.logging.Logger import gq.kirmanak.mealient.logging.Logger
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_ADD_RECIPE_INFO
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.coEvery import io.mockk.coEvery
import io.mockk.coVerify import io.mockk.coVerify
@@ -61,21 +61,21 @@ class AddRecipeViewModelTest {
@Test @Test
fun `when preserve then doesn't update UI`() { fun `when preserve then doesn't update UI`() {
coEvery { addRecipeRepo.addRecipeRequestFlow } returns flowOf(AddRecipeInfo()) coEvery { addRecipeRepo.addRecipeRequestFlow } returns flowOf(PORRIDGE_ADD_RECIPE_INFO)
subject.preserve(AddRecipeInfo()) subject.preserve(PORRIDGE_ADD_RECIPE_INFO)
coVerify(inverse = true) { addRecipeRepo.addRecipeRequestFlow } coVerify(inverse = true) { addRecipeRepo.addRecipeRequestFlow }
} }
@Test @Test
fun `when preservedAddRecipeRequest without loadPreservedRequest then empty`() = runTest { fun `when preservedAddRecipeRequest without loadPreservedRequest then empty`() = runTest {
coEvery { addRecipeRepo.addRecipeRequestFlow } returns flowOf(AddRecipeInfo()) coEvery { addRecipeRepo.addRecipeRequestFlow } returns flowOf(PORRIDGE_ADD_RECIPE_INFO)
val actual = withTimeoutOrNull(10) { subject.preservedAddRecipeRequest.firstOrNull() } val actual = withTimeoutOrNull(10) { subject.preservedAddRecipeRequest.firstOrNull() }
assertThat(actual).isNull() assertThat(actual).isNull()
} }
@Test @Test
fun `when loadPreservedRequest then updates preservedAddRecipeRequest`() = runTest { fun `when loadPreservedRequest then updates preservedAddRecipeRequest`() = runTest {
val expected = AddRecipeInfo() val expected = PORRIDGE_ADD_RECIPE_INFO
coEvery { addRecipeRepo.addRecipeRequestFlow } returns flowOf(expected) coEvery { addRecipeRepo.addRecipeRequestFlow } returns flowOf(expected)
subject.loadPreservedRequest() subject.loadPreservedRequest()
assertThat(subject.preservedAddRecipeRequest.first()).isSameInstanceAs(expected) assertThat(subject.preservedAddRecipeRequest.first()).isSameInstanceAs(expected)
@@ -83,7 +83,7 @@ class AddRecipeViewModelTest {
@Test @Test
fun `when clear then updates preservedAddRecipeRequest`() = runTest { fun `when clear then updates preservedAddRecipeRequest`() = runTest {
val expected = AddRecipeInfo() val expected = PORRIDGE_ADD_RECIPE_INFO
coEvery { addRecipeRepo.addRecipeRequestFlow } returns flowOf(expected) coEvery { addRecipeRepo.addRecipeRequestFlow } returns flowOf(expected)
subject.clear() subject.clear()
assertThat(subject.preservedAddRecipeRequest.first()).isSameInstanceAs(expected) assertThat(subject.preservedAddRecipeRequest.first()).isSameInstanceAs(expected)