Set base url through Interceptor
This commit is contained in:
@@ -8,6 +8,7 @@ import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.multibindings.IntoSet
|
||||
import gq.kirmanak.mealient.datasource.impl.AuthInterceptor
|
||||
import gq.kirmanak.mealient.datasource.impl.BaseUrlInterceptor
|
||||
import gq.kirmanak.mealient.datasource.impl.CacheBuilderImpl
|
||||
import gq.kirmanak.mealient.datasource.impl.NetworkRequestWrapperImpl
|
||||
import gq.kirmanak.mealient.datasource.impl.OkHttpBuilderImpl
|
||||
@@ -20,7 +21,6 @@ import gq.kirmanak.mealient.datasource.v1.MealieDataSourceV1Impl
|
||||
import gq.kirmanak.mealient.datasource.v1.MealieServiceV1
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Converter
|
||||
@@ -55,8 +55,11 @@ interface DataSourceModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRetrofit(retrofitBuilder: RetrofitBuilder): Retrofit =
|
||||
retrofitBuilder.buildRetrofit("https://beta.mealie.io/")
|
||||
fun provideRetrofit(retrofitBuilder: RetrofitBuilder): Retrofit {
|
||||
// Fake base URL which will be replaced later by BaseUrlInterceptor
|
||||
// Solution was suggested here https://github.com/square/retrofit/issues/2161#issuecomment-274204152
|
||||
return retrofitBuilder.buildRetrofit("http://localhost/")
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@@ -92,5 +95,10 @@ interface DataSourceModule {
|
||||
@Binds
|
||||
@Singleton
|
||||
@IntoSet
|
||||
fun bindAuthInterceptor(authInterceptor: AuthInterceptor): Interceptor
|
||||
fun bindAuthInterceptor(authInterceptor: AuthInterceptor): LocalInterceptor
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
@IntoSet
|
||||
fun bindBaseUrlInterceptor(baseUrlInterceptor: BaseUrlInterceptor): LocalInterceptor
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package gq.kirmanak.mealient.datasource
|
||||
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
/**
|
||||
* Marker interface which is different from [Interceptor] only in how it is handled.
|
||||
* [Interceptor]s are added as network interceptors to OkHttpClient whereas [LocalInterceptor]s
|
||||
* are added via [OkHttpClient.Builder.addInterceptor] function. They will observe the
|
||||
* full call lifecycle, whereas network interceptors will see only the network part.
|
||||
*/
|
||||
interface LocalInterceptor : Interceptor
|
||||
@@ -0,0 +1,6 @@
|
||||
package gq.kirmanak.mealient.datasource
|
||||
|
||||
interface ServerUrlProvider {
|
||||
|
||||
suspend fun getUrl(): String?
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package gq.kirmanak.mealient.datasource.impl
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import gq.kirmanak.mealient.datasource.AuthenticationProvider
|
||||
import gq.kirmanak.mealient.datasource.LocalInterceptor
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.Interceptor
|
||||
@@ -14,13 +15,13 @@ import javax.inject.Singleton
|
||||
class AuthInterceptor @Inject constructor(
|
||||
private val logger: Logger,
|
||||
private val authenticationProviderProvider: Provider<AuthenticationProvider>,
|
||||
) : Interceptor {
|
||||
) : LocalInterceptor {
|
||||
|
||||
private val authenticationProvider: AuthenticationProvider
|
||||
get() = authenticationProviderProvider.get()
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
logger.v { "intercept() was called" }
|
||||
logger.v { "intercept() was called with: request = ${chain.request()}" }
|
||||
val header = getAuthHeader()
|
||||
val request = chain.request().let {
|
||||
if (header == null) it else it.newBuilder().header(HEADER_NAME, header).build()
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package gq.kirmanak.mealient.datasource.impl
|
||||
|
||||
import gq.kirmanak.mealient.datasource.LocalInterceptor
|
||||
import gq.kirmanak.mealient.datasource.ServerUrlProvider
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class BaseUrlInterceptor @Inject constructor(
|
||||
private val serverUrlProviderProvider: Provider<ServerUrlProvider>,
|
||||
private val logger: Logger,
|
||||
) : LocalInterceptor {
|
||||
|
||||
private val serverUrlProvider: ServerUrlProvider
|
||||
get() = serverUrlProviderProvider.get()
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
logger.v { "intercept() was called with: request = ${chain.request()}" }
|
||||
val oldRequest = chain.request()
|
||||
val baseUrl = getBaseUrl()
|
||||
val correctUrl = oldRequest.url
|
||||
.newBuilder()
|
||||
.host(baseUrl.host)
|
||||
.scheme(baseUrl.scheme)
|
||||
.build()
|
||||
val newRequest = oldRequest.newBuilder().url(correctUrl).build()
|
||||
logger.d { "Replaced ${oldRequest.url} with ${newRequest.url}" }
|
||||
return chain.proceed(newRequest)
|
||||
}
|
||||
|
||||
private fun getBaseUrl() = runBlocking {
|
||||
serverUrlProvider.getUrl()?.toHttpUrlOrNull() ?: throw IOException("Base URL is unknown")
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
package gq.kirmanak.mealient.datasource.impl
|
||||
|
||||
import gq.kirmanak.mealient.datasource.CacheBuilder
|
||||
import gq.kirmanak.mealient.datasource.LocalInterceptor
|
||||
import gq.kirmanak.mealient.datasource.OkHttpBuilder
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import javax.inject.Inject
|
||||
@@ -12,10 +14,16 @@ class OkHttpBuilderImpl @Inject constructor(
|
||||
private val cacheBuilder: CacheBuilder,
|
||||
// Use @JvmSuppressWildcards because otherwise dagger can't inject it (https://stackoverflow.com/a/43149382)
|
||||
private val interceptors: Set<@JvmSuppressWildcards Interceptor>,
|
||||
private val localInterceptors: Set<@JvmSuppressWildcards LocalInterceptor>,
|
||||
private val logger: Logger,
|
||||
) : OkHttpBuilder {
|
||||
|
||||
override fun buildOkHttp(): OkHttpClient = OkHttpClient.Builder()
|
||||
.apply { interceptors.forEach(::addNetworkInterceptor) }
|
||||
.cache(cacheBuilder.buildCache())
|
||||
.build()
|
||||
override fun buildOkHttp(): OkHttpClient {
|
||||
logger.v { "buildOkHttp() was called with cacheBuilder = $cacheBuilder, interceptors = $interceptors, localInterceptors = $localInterceptors" }
|
||||
return OkHttpClient.Builder().apply {
|
||||
localInterceptors.forEach(::addInterceptor)
|
||||
interceptors.forEach(::addNetworkInterceptor)
|
||||
cache(cacheBuilder.buildCache())
|
||||
}.build()
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import gq.kirmanak.mealient.datasource.v0.models.VersionResponseV0
|
||||
interface MealieDataSourceV0 {
|
||||
|
||||
suspend fun addRecipe(
|
||||
baseUrl: String,
|
||||
recipe: AddRecipeRequestV0,
|
||||
): String
|
||||
|
||||
@@ -18,33 +17,27 @@ interface MealieDataSourceV0 {
|
||||
* Tries to acquire authentication token using the provided credentials
|
||||
*/
|
||||
suspend fun authenticate(
|
||||
baseUrl: String,
|
||||
username: String,
|
||||
password: String,
|
||||
): String
|
||||
|
||||
suspend fun getVersionInfo(
|
||||
baseUrl: String,
|
||||
): VersionResponseV0
|
||||
|
||||
suspend fun requestRecipes(
|
||||
baseUrl: String,
|
||||
start: Int,
|
||||
limit: Int,
|
||||
): List<GetRecipeSummaryResponseV0>
|
||||
|
||||
suspend fun requestRecipeInfo(
|
||||
baseUrl: String,
|
||||
slug: String,
|
||||
): GetRecipeResponseV0
|
||||
|
||||
suspend fun parseRecipeFromURL(
|
||||
baseUrl: String,
|
||||
request: ParseRecipeURLRequestV0,
|
||||
): String
|
||||
|
||||
suspend fun createApiToken(
|
||||
baseUrl: String,
|
||||
request: CreateApiTokenRequestV0,
|
||||
): String
|
||||
}
|
||||
@@ -26,34 +26,30 @@ class MealieDataSourceV0Impl @Inject constructor(
|
||||
) : MealieDataSourceV0 {
|
||||
|
||||
override suspend fun addRecipe(
|
||||
baseUrl: String,
|
||||
recipe: AddRecipeRequestV0,
|
||||
): String = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.addRecipe("$baseUrl/api/recipes/create", recipe) },
|
||||
block = { service.addRecipe(recipe) },
|
||||
logMethod = { "addRecipe" },
|
||||
logParameters = { "baseUrl = $baseUrl, recipe = $recipe" }
|
||||
logParameters = { "recipe = $recipe" }
|
||||
)
|
||||
|
||||
override suspend fun authenticate(
|
||||
baseUrl: String,
|
||||
username: String,
|
||||
password: String,
|
||||
): String = networkRequestWrapper.makeCall(
|
||||
block = { service.getToken("$baseUrl/api/auth/token", username, password) },
|
||||
block = { service.getToken(username, password) },
|
||||
logMethod = { "authenticate" },
|
||||
logParameters = { "baseUrl = $baseUrl, username = $username, password = $password" }
|
||||
logParameters = { "username = $username, password = $password" }
|
||||
).map { it.accessToken }.getOrElse {
|
||||
val errorBody = (it as? HttpException)?.response()?.errorBody() ?: throw it
|
||||
val errorDetailV0 = errorBody.decode<ErrorDetailV0>(json)
|
||||
throw if (errorDetailV0.detail == "Unauthorized") NetworkError.Unauthorized(it) else it
|
||||
}
|
||||
|
||||
override suspend fun getVersionInfo(
|
||||
baseUrl: String
|
||||
): VersionResponseV0 = networkRequestWrapper.makeCall(
|
||||
block = { service.getVersion("$baseUrl/api/debug/version") },
|
||||
override suspend fun getVersionInfo(): VersionResponseV0 = networkRequestWrapper.makeCall(
|
||||
block = { service.getVersion() },
|
||||
logMethod = { "getVersionInfo" },
|
||||
logParameters = { "baseUrl = $baseUrl" },
|
||||
logParameters = { "" },
|
||||
).getOrElse {
|
||||
throw when (it) {
|
||||
is HttpException, is SerializationException -> NetworkError.NotMealie(it)
|
||||
@@ -63,39 +59,35 @@ class MealieDataSourceV0Impl @Inject constructor(
|
||||
}
|
||||
|
||||
override suspend fun requestRecipes(
|
||||
baseUrl: String,
|
||||
start: Int,
|
||||
limit: Int,
|
||||
): List<GetRecipeSummaryResponseV0> = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.getRecipeSummary("$baseUrl/api/recipes/summary", start, limit) },
|
||||
block = { service.getRecipeSummary(start, limit) },
|
||||
logMethod = { "requestRecipes" },
|
||||
logParameters = { "baseUrl = $baseUrl, start = $start, limit = $limit" }
|
||||
logParameters = { "start = $start, limit = $limit" }
|
||||
)
|
||||
|
||||
override suspend fun requestRecipeInfo(
|
||||
baseUrl: String,
|
||||
slug: String,
|
||||
): GetRecipeResponseV0 = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.getRecipe("$baseUrl/api/recipes/$slug") },
|
||||
block = { service.getRecipe(slug) },
|
||||
logMethod = { "requestRecipeInfo" },
|
||||
logParameters = { "baseUrl = $baseUrl, slug = $slug" }
|
||||
logParameters = { "slug = $slug" }
|
||||
)
|
||||
|
||||
override suspend fun parseRecipeFromURL(
|
||||
baseUrl: String,
|
||||
request: ParseRecipeURLRequestV0
|
||||
): String = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.createRecipeFromURL("$baseUrl/api/recipes/create-url", request) },
|
||||
block = { service.createRecipeFromURL(request) },
|
||||
logMethod = { "parseRecipeFromURL" },
|
||||
logParameters = { "baseUrl = $baseUrl, request = $request" },
|
||||
logParameters = { "request = $request" },
|
||||
)
|
||||
|
||||
override suspend fun createApiToken(
|
||||
baseUrl: String,
|
||||
request: CreateApiTokenRequestV0,
|
||||
): String = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.createApiToken("$baseUrl/api/users/api-tokens", request) },
|
||||
block = { service.createApiToken(request) },
|
||||
logMethod = { "createApiToken" },
|
||||
logParameters = { "baseUrl = $baseUrl, request = $request" }
|
||||
logParameters = { "request = $request" }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,45 +6,38 @@ import retrofit2.http.*
|
||||
interface MealieServiceV0 {
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST
|
||||
@POST("/api/auth/token")
|
||||
suspend fun getToken(
|
||||
@Url url: String,
|
||||
@Field("username") username: String,
|
||||
@Field("password") password: String,
|
||||
): GetTokenResponseV0
|
||||
|
||||
@POST
|
||||
@POST("/api/recipes/create")
|
||||
suspend fun addRecipe(
|
||||
@Url url: String,
|
||||
@Body addRecipeRequestV0: AddRecipeRequestV0,
|
||||
): String
|
||||
|
||||
@GET
|
||||
suspend fun getVersion(
|
||||
@Url url: String,
|
||||
): VersionResponseV0
|
||||
@GET("/api/debug/version")
|
||||
suspend fun getVersion(): VersionResponseV0
|
||||
|
||||
@GET
|
||||
@GET("/api/recipes/summary")
|
||||
suspend fun getRecipeSummary(
|
||||
@Url url: String,
|
||||
@Query("start") start: Int,
|
||||
@Query("limit") limit: Int,
|
||||
): List<GetRecipeSummaryResponseV0>
|
||||
|
||||
@GET
|
||||
@GET("/api/recipes/{slug}")
|
||||
suspend fun getRecipe(
|
||||
@Url url: String,
|
||||
@Path("slug") slug: String,
|
||||
): GetRecipeResponseV0
|
||||
|
||||
@POST
|
||||
@POST("/api/recipes/create-url")
|
||||
suspend fun createRecipeFromURL(
|
||||
@Url url: String,
|
||||
@Body request: ParseRecipeURLRequestV0,
|
||||
): String
|
||||
|
||||
@POST
|
||||
@POST("/api/users/api-tokens")
|
||||
suspend fun createApiToken(
|
||||
@Url url: String,
|
||||
@Body request: CreateApiTokenRequestV0,
|
||||
): String
|
||||
}
|
||||
@@ -12,12 +12,10 @@ import gq.kirmanak.mealient.datasource.v1.models.VersionResponseV1
|
||||
interface MealieDataSourceV1 {
|
||||
|
||||
suspend fun createRecipe(
|
||||
baseUrl: String,
|
||||
recipe: CreateRecipeRequestV1,
|
||||
): String
|
||||
|
||||
suspend fun updateRecipe(
|
||||
baseUrl: String,
|
||||
slug: String,
|
||||
recipe: UpdateRecipeRequestV1,
|
||||
): GetRecipeResponseV1
|
||||
@@ -26,33 +24,27 @@ interface MealieDataSourceV1 {
|
||||
* Tries to acquire authentication token using the provided credentials
|
||||
*/
|
||||
suspend fun authenticate(
|
||||
baseUrl: String,
|
||||
username: String,
|
||||
password: String,
|
||||
): String
|
||||
|
||||
suspend fun getVersionInfo(
|
||||
baseUrl: String,
|
||||
): VersionResponseV1
|
||||
|
||||
suspend fun requestRecipes(
|
||||
baseUrl: String,
|
||||
page: Int,
|
||||
perPage: Int,
|
||||
): List<GetRecipeSummaryResponseV1>
|
||||
|
||||
suspend fun requestRecipeInfo(
|
||||
baseUrl: String,
|
||||
slug: String,
|
||||
): GetRecipeResponseV1
|
||||
|
||||
suspend fun parseRecipeFromURL(
|
||||
baseUrl: String,
|
||||
request: ParseRecipeURLRequestV1,
|
||||
): String
|
||||
|
||||
suspend fun createApiToken(
|
||||
baseUrl: String,
|
||||
request: CreateApiTokenRequestV1,
|
||||
): CreateApiTokenResponseV1
|
||||
}
|
||||
@@ -28,44 +28,39 @@ class MealieDataSourceV1Impl @Inject constructor(
|
||||
) : MealieDataSourceV1 {
|
||||
|
||||
override suspend fun createRecipe(
|
||||
baseUrl: String,
|
||||
recipe: CreateRecipeRequestV1
|
||||
): String = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.createRecipe("$baseUrl/api/recipes", recipe) },
|
||||
block = { service.createRecipe(recipe) },
|
||||
logMethod = { "createRecipe" },
|
||||
logParameters = { "baseUrl = $baseUrl, recipe = $recipe" }
|
||||
logParameters = { "recipe = $recipe" }
|
||||
)
|
||||
|
||||
override suspend fun updateRecipe(
|
||||
baseUrl: String,
|
||||
slug: String,
|
||||
recipe: UpdateRecipeRequestV1
|
||||
): GetRecipeResponseV1 = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.updateRecipe("$baseUrl/api/recipes/$slug", recipe) },
|
||||
block = { service.updateRecipe(recipe, slug) },
|
||||
logMethod = { "updateRecipe" },
|
||||
logParameters = { "baseUrl = $baseUrl, slug = $slug, recipe = $recipe" }
|
||||
logParameters = { "slug = $slug, recipe = $recipe" }
|
||||
)
|
||||
|
||||
override suspend fun authenticate(
|
||||
baseUrl: String,
|
||||
username: String,
|
||||
password: String,
|
||||
): String = networkRequestWrapper.makeCall(
|
||||
block = { service.getToken("$baseUrl/api/auth/token", username, password) },
|
||||
block = { service.getToken(username, password) },
|
||||
logMethod = { "authenticate" },
|
||||
logParameters = { "baseUrl = $baseUrl, username = $username, password = $password" }
|
||||
logParameters = { "username = $username, password = $password" }
|
||||
).map { it.accessToken }.getOrElse {
|
||||
val errorBody = (it as? HttpException)?.response()?.errorBody() ?: throw it
|
||||
val errorDetailV0 = errorBody.decode<ErrorDetailV1>(json)
|
||||
throw if (errorDetailV0.detail == "Unauthorized") NetworkError.Unauthorized(it) else it
|
||||
}
|
||||
|
||||
override suspend fun getVersionInfo(
|
||||
baseUrl: String,
|
||||
): VersionResponseV1 = networkRequestWrapper.makeCall(
|
||||
block = { service.getVersion("$baseUrl/api/app/about") },
|
||||
override suspend fun getVersionInfo(): VersionResponseV1 = networkRequestWrapper.makeCall(
|
||||
block = { service.getVersion() },
|
||||
logMethod = { "getVersionInfo" },
|
||||
logParameters = { "baseUrl = $baseUrl" },
|
||||
logParameters = { "" },
|
||||
).getOrElse {
|
||||
throw when (it) {
|
||||
is HttpException, is SerializationException -> NetworkError.NotMealie(it)
|
||||
@@ -75,40 +70,36 @@ class MealieDataSourceV1Impl @Inject constructor(
|
||||
}
|
||||
|
||||
override suspend fun requestRecipes(
|
||||
baseUrl: String,
|
||||
page: Int,
|
||||
perPage: Int
|
||||
): List<GetRecipeSummaryResponseV1> = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.getRecipeSummary("$baseUrl/api/recipes", page, perPage) },
|
||||
block = { service.getRecipeSummary(page, perPage) },
|
||||
logMethod = { "requestRecipes" },
|
||||
logParameters = { "baseUrl = $baseUrl, page = $page, perPage = $perPage" }
|
||||
logParameters = { "page = $page, perPage = $perPage" }
|
||||
).items
|
||||
|
||||
override suspend fun requestRecipeInfo(
|
||||
baseUrl: String,
|
||||
slug: String
|
||||
): GetRecipeResponseV1 = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.getRecipe("$baseUrl/api/recipes/$slug") },
|
||||
block = { service.getRecipe(slug) },
|
||||
logMethod = { "requestRecipeInfo" },
|
||||
logParameters = { "baseUrl = $baseUrl, slug = $slug" }
|
||||
logParameters = { "slug = $slug" }
|
||||
)
|
||||
|
||||
override suspend fun parseRecipeFromURL(
|
||||
baseUrl: String,
|
||||
request: ParseRecipeURLRequestV1
|
||||
): String = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.createRecipeFromURL("$baseUrl/api/recipes/create-url", request) },
|
||||
block = { service.createRecipeFromURL(request) },
|
||||
logMethod = { "parseRecipeFromURL" },
|
||||
logParameters = { "baseUrl = $baseUrl, request = $request" }
|
||||
logParameters = { "request = $request" }
|
||||
)
|
||||
|
||||
override suspend fun createApiToken(
|
||||
baseUrl: String,
|
||||
request: CreateApiTokenRequestV1
|
||||
): CreateApiTokenResponseV1 = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.createApiToken("$baseUrl/api/users/api-tokens", request) },
|
||||
block = { service.createApiToken(request) },
|
||||
logMethod = { "createApiToken" },
|
||||
logParameters = { "baseUrl = $baseUrl, request = $request" }
|
||||
logParameters = { "request = $request" }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,51 +6,44 @@ import retrofit2.http.*
|
||||
interface MealieServiceV1 {
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST
|
||||
@POST("/api/auth/token")
|
||||
suspend fun getToken(
|
||||
@Url url: String,
|
||||
@Field("username") username: String,
|
||||
@Field("password") password: String,
|
||||
): GetTokenResponseV1
|
||||
|
||||
@POST
|
||||
@POST("/api/recipes")
|
||||
suspend fun createRecipe(
|
||||
@Url url: String,
|
||||
@Body addRecipeRequest: CreateRecipeRequestV1,
|
||||
): String
|
||||
|
||||
@PATCH
|
||||
@PATCH("/api/recipes/{slug}")
|
||||
suspend fun updateRecipe(
|
||||
@Url url: String,
|
||||
@Body addRecipeRequest: UpdateRecipeRequestV1,
|
||||
@Path("slug") slug: String,
|
||||
): GetRecipeResponseV1
|
||||
|
||||
@GET
|
||||
suspend fun getVersion(
|
||||
@Url url: String,
|
||||
): VersionResponseV1
|
||||
@GET("/api/app/about")
|
||||
suspend fun getVersion(): VersionResponseV1
|
||||
|
||||
@GET
|
||||
@GET("/api/recipes")
|
||||
suspend fun getRecipeSummary(
|
||||
@Url url: String,
|
||||
@Query("page") page: Int,
|
||||
@Query("perPage") perPage: Int,
|
||||
): GetRecipesResponseV1
|
||||
|
||||
@GET
|
||||
@GET("/api/recipes/{slug}")
|
||||
suspend fun getRecipe(
|
||||
@Url url: String,
|
||||
@Path("slug") slug: String,
|
||||
): GetRecipeResponseV1
|
||||
|
||||
@POST
|
||||
@POST("/api/recipes/create-url")
|
||||
suspend fun createRecipeFromURL(
|
||||
@Url url: String,
|
||||
@Body request: ParseRecipeURLRequestV1,
|
||||
): String
|
||||
|
||||
@POST
|
||||
@POST("/api/users/api-tokens")
|
||||
suspend fun createApiToken(
|
||||
@Url url: String,
|
||||
@Body request: CreateApiTokenRequestV1,
|
||||
): CreateApiTokenResponseV1
|
||||
}
|
||||
@@ -38,34 +38,34 @@ class MealieDataSourceV0ImplTest : BaseUnitTest() {
|
||||
@Test(expected = NetworkError.NotMealie::class)
|
||||
fun `when getVersionInfo and getVersion throws HttpException then NotMealie`() = runTest {
|
||||
val error = HttpException(Response.error<VersionResponseV0>(404, "".toJsonResponseBody()))
|
||||
coEvery { service.getVersion(any()) } throws error
|
||||
subject.getVersionInfo(TEST_BASE_URL)
|
||||
coEvery { service.getVersion() } throws error
|
||||
subject.getVersionInfo()
|
||||
}
|
||||
|
||||
@Test(expected = NetworkError.NotMealie::class)
|
||||
fun `when getVersionInfo and getVersion throws SerializationException then NotMealie`() =
|
||||
runTest {
|
||||
coEvery { service.getVersion(any()) } throws SerializationException()
|
||||
subject.getVersionInfo(TEST_BASE_URL)
|
||||
coEvery { service.getVersion() } throws SerializationException()
|
||||
subject.getVersionInfo()
|
||||
}
|
||||
|
||||
@Test(expected = NetworkError.NoServerConnection::class)
|
||||
fun `when getVersionInfo and getVersion throws IOException then NoServerConnection`() =
|
||||
runTest {
|
||||
coEvery { service.getVersion(any()) } throws ConnectException()
|
||||
subject.getVersionInfo(TEST_BASE_URL)
|
||||
coEvery { service.getVersion() } throws ConnectException()
|
||||
subject.getVersionInfo()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when getVersionInfo and getVersion returns result then result`() = runTest {
|
||||
val versionResponse = VersionResponseV0("v0.5.6")
|
||||
coEvery { service.getVersion(any()) } returns versionResponse
|
||||
assertThat(subject.getVersionInfo(TEST_BASE_URL)).isSameInstanceAs(versionResponse)
|
||||
coEvery { service.getVersion() } returns versionResponse
|
||||
assertThat(subject.getVersionInfo()).isSameInstanceAs(versionResponse)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when authentication is successful then token is correct`() = runTest {
|
||||
coEvery { service.getToken(any(), any(), any()) } returns GetTokenResponseV0(TEST_TOKEN)
|
||||
coEvery { service.getToken(any(), any()) } returns GetTokenResponseV0(TEST_TOKEN)
|
||||
assertThat(callAuthenticate()).isEqualTo(TEST_TOKEN)
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ class MealieDataSourceV0ImplTest : BaseUnitTest() {
|
||||
fun `when authenticate receives 401 and Unauthorized then throws Unauthorized`() = runTest {
|
||||
val body = "{\"detail\":\"Unauthorized\"}".toJsonResponseBody()
|
||||
coEvery {
|
||||
service.getToken(any(), any(), any())
|
||||
service.getToken(any(), any())
|
||||
} throws HttpException(Response.error<GetTokenResponseV0>(401, body))
|
||||
callAuthenticate()
|
||||
}
|
||||
@@ -82,7 +82,7 @@ class MealieDataSourceV0ImplTest : BaseUnitTest() {
|
||||
fun `when authenticate receives 401 but not Unauthorized then throws NotMealie`() = runTest {
|
||||
val body = "{\"detail\":\"Something\"}".toJsonResponseBody()
|
||||
coEvery {
|
||||
service.getToken(any(), any(), any())
|
||||
service.getToken(any(), any())
|
||||
} throws HttpException(Response.error<GetTokenResponseV0>(401, body))
|
||||
callAuthenticate()
|
||||
}
|
||||
@@ -91,22 +91,21 @@ class MealieDataSourceV0ImplTest : BaseUnitTest() {
|
||||
fun `when authenticate receives 404 and empty body then throws NotMealie`() = runTest {
|
||||
val body = "".toJsonResponseBody()
|
||||
coEvery {
|
||||
service.getToken(any(), any(), any())
|
||||
service.getToken(any(), any())
|
||||
} throws HttpException(Response.error<GetTokenResponseV0>(401, body))
|
||||
callAuthenticate()
|
||||
}
|
||||
|
||||
@Test(expected = IOException::class)
|
||||
fun `when authenticate and getToken throws then throws NoServerConnection`() = runTest {
|
||||
coEvery { service.getToken(any(), any(), any()) } throws IOException("Server not found")
|
||||
coEvery { service.getToken(any(), any()) } throws IOException("Server not found")
|
||||
callAuthenticate()
|
||||
}
|
||||
|
||||
private suspend fun callAuthenticate(): String =
|
||||
subject.authenticate(TEST_USERNAME, TEST_PASSWORD, TEST_BASE_URL)
|
||||
subject.authenticate(TEST_PASSWORD, TEST_BASE_URL)
|
||||
|
||||
companion object {
|
||||
const val TEST_USERNAME = "TEST_USERNAME"
|
||||
const val TEST_PASSWORD = "TEST_PASSWORD"
|
||||
const val TEST_BASE_URL = "https://example.com/"
|
||||
const val TEST_TOKEN = "TEST_TOKEN"
|
||||
|
||||
Reference in New Issue
Block a user