Save isFavorite flag to DB for recipes

This commit is contained in:
Kirill Kamakin
2022-12-12 21:26:57 +01:00
parent a42758f3d1
commit aefb974cb3
23 changed files with 352 additions and 30 deletions

View File

@@ -0,0 +1,11 @@
package gq.kirmanak.mealient.data.configuration
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.MainCoroutineDispatcher
interface AppDispatchers {
val io: CoroutineDispatcher
val main: MainCoroutineDispatcher
val default: CoroutineDispatcher
val unconfined: CoroutineDispatcher
}

View File

@@ -0,0 +1,17 @@
package gq.kirmanak.mealient.data.configuration
import kotlinx.coroutines.Dispatchers
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class AppDispatchersImpl @Inject constructor() : AppDispatchers {
override val io = Dispatchers.IO
override val main = Dispatchers.Main
override val default = Dispatchers.Default
override val unconfined = Dispatchers.Unconfined
}

View File

@@ -64,4 +64,8 @@ class MealieDataSourceWrapper @Inject constructor(
ServerVersion.V1 -> v1Source.parseRecipeFromURL(parseRecipeURLInfo.toV1Request()) ServerVersion.V1 -> v1Source.parseRecipeFromURL(parseRecipeURLInfo.toV1Request())
} }
override suspend fun getFavoriteRecipes(): List<String> = when (getVersion()) {
ServerVersion.V0 -> v0Source.requestUserInfo().favoriteRecipes
ServerVersion.V1 -> v1Source.requestUserInfo().favoriteRecipes
}
} }

View File

@@ -2,16 +2,15 @@ package gq.kirmanak.mealient.data.recipes.db
import androidx.paging.PagingSource import androidx.paging.PagingSource
import gq.kirmanak.mealient.data.recipes.network.FullRecipeInfo import gq.kirmanak.mealient.data.recipes.network.FullRecipeInfo
import gq.kirmanak.mealient.data.recipes.network.RecipeSummaryInfo
import gq.kirmanak.mealient.database.recipe.entity.FullRecipeEntity import gq.kirmanak.mealient.database.recipe.entity.FullRecipeEntity
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
interface RecipeStorage { interface RecipeStorage {
suspend fun saveRecipes(recipes: List<RecipeSummaryInfo>) suspend fun saveRecipes(recipes: List<RecipeSummaryEntity>)
fun queryRecipes(query: String?): PagingSource<Int, RecipeSummaryEntity> fun queryRecipes(query: String?): PagingSource<Int, RecipeSummaryEntity>
suspend fun refreshAll(recipes: List<RecipeSummaryInfo>) suspend fun refreshAll(recipes: List<RecipeSummaryEntity>)
suspend fun clearAllLocalData() suspend fun clearAllLocalData()

View File

@@ -3,7 +3,6 @@ package gq.kirmanak.mealient.data.recipes.db
import androidx.paging.PagingSource import androidx.paging.PagingSource
import androidx.room.withTransaction import androidx.room.withTransaction
import gq.kirmanak.mealient.data.recipes.network.FullRecipeInfo import gq.kirmanak.mealient.data.recipes.network.FullRecipeInfo
import gq.kirmanak.mealient.data.recipes.network.RecipeSummaryInfo
import gq.kirmanak.mealient.database.AppDb import gq.kirmanak.mealient.database.AppDb
import gq.kirmanak.mealient.database.recipe.RecipeDao import gq.kirmanak.mealient.database.recipe.RecipeDao
import gq.kirmanak.mealient.database.recipe.entity.FullRecipeEntity import gq.kirmanak.mealient.database.recipe.entity.FullRecipeEntity
@@ -11,7 +10,6 @@ import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
import gq.kirmanak.mealient.extensions.toRecipeEntity import gq.kirmanak.mealient.extensions.toRecipeEntity
import gq.kirmanak.mealient.extensions.toRecipeIngredientEntity import gq.kirmanak.mealient.extensions.toRecipeIngredientEntity
import gq.kirmanak.mealient.extensions.toRecipeInstructionEntity import gq.kirmanak.mealient.extensions.toRecipeInstructionEntity
import gq.kirmanak.mealient.extensions.toRecipeSummaryEntity
import gq.kirmanak.mealient.logging.Logger import gq.kirmanak.mealient.logging.Logger
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@@ -23,11 +21,9 @@ class RecipeStorageImpl @Inject constructor(
) : RecipeStorage { ) : RecipeStorage {
private val recipeDao: RecipeDao by lazy { db.recipeDao() } private val recipeDao: RecipeDao by lazy { db.recipeDao() }
override suspend fun saveRecipes(recipes: List<RecipeSummaryInfo>) { override suspend fun saveRecipes(recipes: List<RecipeSummaryEntity>) {
logger.v { "saveRecipes() called with $recipes" } logger.v { "saveRecipes() called with $recipes" }
val entities = recipes.map { it.toRecipeSummaryEntity() } db.withTransaction { recipeDao.insertRecipes(recipes) }
logger.v { "saveRecipes: entities = $entities" }
db.withTransaction { recipeDao.insertRecipes(entities) }
} }
override fun queryRecipes(query: String?): PagingSource<Int, RecipeSummaryEntity> { override fun queryRecipes(query: String?): PagingSource<Int, RecipeSummaryEntity> {
@@ -36,7 +32,7 @@ class RecipeStorageImpl @Inject constructor(
else recipeDao.queryRecipesByPages(query) else recipeDao.queryRecipesByPages(query)
} }
override suspend fun refreshAll(recipes: List<RecipeSummaryInfo>) { override suspend fun refreshAll(recipes: List<RecipeSummaryEntity>) {
logger.v { "refreshAll() called with: recipes = $recipes" } logger.v { "refreshAll() called with: recipes = $recipes" }
db.withTransaction { db.withTransaction {
recipeDao.removeAllRecipes() recipeDao.removeAllRecipes()

View File

@@ -66,7 +66,7 @@ class RecipeRepoImpl @Inject constructor(
override suspend fun refreshRecipes() { override suspend fun refreshRecipes() {
logger.v { "refreshRecipes() called" } logger.v { "refreshRecipes() called" }
runCatchingExceptCancel { runCatchingExceptCancel {
storage.refreshAll(dataSource.requestRecipes(0, INITIAL_LOAD_PAGE_SIZE)) mediator.updateRecipes(0, INITIAL_LOAD_PAGE_SIZE)
}.onFailure { }.onFailure {
logger.e(it) { "Can't refresh recipes" } logger.e(it) { "Can't refresh recipes" }
} }

View File

@@ -4,11 +4,16 @@ import androidx.annotation.VisibleForTesting
import androidx.paging.* import androidx.paging.*
import androidx.paging.LoadType.PREPEND import androidx.paging.LoadType.PREPEND
import androidx.paging.LoadType.REFRESH import androidx.paging.LoadType.REFRESH
import gq.kirmanak.mealient.data.configuration.AppDispatchers
import gq.kirmanak.mealient.data.recipes.db.RecipeStorage import gq.kirmanak.mealient.data.recipes.db.RecipeStorage
import gq.kirmanak.mealient.data.recipes.network.RecipeDataSource import gq.kirmanak.mealient.data.recipes.network.RecipeDataSource
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
import gq.kirmanak.mealient.extensions.toRecipeSummaryEntity
import gq.kirmanak.mealient.logging.Logger import gq.kirmanak.mealient.logging.Logger
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.withContext
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@@ -19,14 +24,14 @@ class RecipesRemoteMediator @Inject constructor(
private val network: RecipeDataSource, private val network: RecipeDataSource,
private val pagingSourceFactory: RecipePagingSourceFactory, private val pagingSourceFactory: RecipePagingSourceFactory,
private val logger: Logger, private val logger: Logger,
private val dispatchers: AppDispatchers,
) : RemoteMediator<Int, RecipeSummaryEntity>() { ) : RemoteMediator<Int, RecipeSummaryEntity>() {
@VisibleForTesting @VisibleForTesting
var lastRequestEnd: Int = 0 var lastRequestEnd: Int = 0
override suspend fun load( override suspend fun load(
loadType: LoadType, loadType: LoadType, state: PagingState<Int, RecipeSummaryEntity>
state: PagingState<Int, RecipeSummaryEntity>
): MediatorResult { ): MediatorResult {
logger.v { "load() called with: lastRequestEnd = $lastRequestEnd, loadType = $loadType, state = $state" } logger.v { "load() called with: lastRequestEnd = $lastRequestEnd, loadType = $loadType, state = $state" }
@@ -39,10 +44,7 @@ class RecipesRemoteMediator @Inject constructor(
val limit = if (loadType == REFRESH) state.config.initialLoadSize else state.config.pageSize val limit = if (loadType == REFRESH) state.config.initialLoadSize else state.config.pageSize
val count: Int = runCatchingExceptCancel { val count: Int = runCatchingExceptCancel {
val recipes = network.requestRecipes(start, limit) updateRecipes(start, limit, loadType)
if (loadType == REFRESH) storage.refreshAll(recipes)
else storage.saveRecipes(recipes)
recipes.size
}.getOrElse { }.getOrElse {
logger.e(it) { "load: can't load recipes" } logger.e(it) { "load: can't load recipes" }
return MediatorResult.Error(it) return MediatorResult.Error(it)
@@ -58,4 +60,23 @@ class RecipesRemoteMediator @Inject constructor(
lastRequestEnd = start + count lastRequestEnd = start + count
return MediatorResult.Success(endOfPaginationReached = count < limit) return MediatorResult.Success(endOfPaginationReached = count < limit)
} }
suspend fun updateRecipes(
start: Int,
limit: Int,
loadType: LoadType = REFRESH,
): Int = coroutineScope {
val deferredRecipes = async { network.requestRecipes(start, limit) }
val favorites = network.getFavoriteRecipes().toHashSet()
val recipes = deferredRecipes.await()
val entities = withContext(dispatchers.default) {
recipes.map { recipe ->
val isFavorite = favorites.contains(recipe.slug)
recipe.toRecipeSummaryEntity(isFavorite)
}
}
if (loadType == REFRESH) storage.refreshAll(entities)
else storage.saveRecipes(entities)
recipes.size
}
} }

View File

@@ -4,4 +4,6 @@ interface RecipeDataSource {
suspend fun requestRecipes(start: Int, limit: Int): List<RecipeSummaryInfo> suspend fun requestRecipes(start: Int, limit: Int): List<RecipeSummaryInfo>
suspend fun requestRecipeInfo(slug: String): FullRecipeInfo suspend fun requestRecipeInfo(slug: String): FullRecipeInfo
suspend fun getFavoriteRecipes(): List<String>
} }

View File

@@ -5,6 +5,8 @@ import dagger.Module
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration
import gq.kirmanak.mealient.data.configuration.AppDispatchers
import gq.kirmanak.mealient.data.configuration.AppDispatchersImpl
import gq.kirmanak.mealient.data.configuration.BuildConfigurationImpl import gq.kirmanak.mealient.data.configuration.BuildConfigurationImpl
import javax.inject.Singleton import javax.inject.Singleton
@@ -15,4 +17,8 @@ interface ArchitectureModule {
@Binds @Binds
@Singleton @Singleton
fun bindBuildConfiguration(buildConfigurationImpl: BuildConfigurationImpl): BuildConfiguration fun bindBuildConfiguration(buildConfigurationImpl: BuildConfigurationImpl): BuildConfiguration
@Binds
@Singleton
fun bindAppDispatchers(appDispatchersImpl: AppDispatchersImpl): AppDispatchers
} }

View File

@@ -80,7 +80,7 @@ fun GetRecipeSummaryResponseV1.toRecipeSummaryInfo() = RecipeSummaryInfo(
imageId = remoteId, imageId = remoteId,
) )
fun RecipeSummaryInfo.toRecipeSummaryEntity() = RecipeSummaryEntity( fun RecipeSummaryInfo.toRecipeSummaryEntity(isFavorite: Boolean) = RecipeSummaryEntity(
remoteId = remoteId, remoteId = remoteId,
name = name, name = name,
slug = slug, slug = slug,
@@ -88,6 +88,7 @@ fun RecipeSummaryInfo.toRecipeSummaryEntity() = RecipeSummaryEntity(
dateAdded = dateAdded, dateAdded = dateAdded,
dateUpdated = dateUpdated, dateUpdated = dateUpdated,
imageId = imageId, imageId = imageId,
isFavorite = isFavorite,
) )
fun VersionResponseV0.toVersionInfo() = VersionInfo(version) fun VersionResponseV0.toVersionInfo() = VersionInfo(version)

View File

@@ -0,0 +1,198 @@
{
"formatVersion": 1,
"database": {
"version": 8,
"identityHash": "793673e401425db36544918dae6bf4c1",
"entities": [
{
"tableName": "recipe_summaries",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`remote_id` TEXT NOT NULL, `name` TEXT NOT NULL, `slug` TEXT NOT NULL, `description` TEXT NOT NULL, `date_added` INTEGER NOT NULL, `date_updated` INTEGER NOT NULL, `image_id` TEXT, `is_favorite` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`remote_id`))",
"fields": [
{
"fieldPath": "remoteId",
"columnName": "remote_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "slug",
"columnName": "slug",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "description",
"columnName": "description",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "dateAdded",
"columnName": "date_added",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dateUpdated",
"columnName": "date_updated",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "imageId",
"columnName": "image_id",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "isFavorite",
"columnName": "is_favorite",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "false"
}
],
"primaryKey": {
"columnNames": [
"remote_id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "recipe",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`remote_id` TEXT NOT NULL, `recipe_yield` TEXT NOT NULL, `disable_amounts` INTEGER NOT NULL DEFAULT true, PRIMARY KEY(`remote_id`))",
"fields": [
{
"fieldPath": "remoteId",
"columnName": "remote_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "recipeYield",
"columnName": "recipe_yield",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "disableAmounts",
"columnName": "disable_amounts",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "true"
}
],
"primaryKey": {
"columnNames": [
"remote_id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "recipe_ingredient",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`local_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `recipe_id` TEXT NOT NULL, `note` TEXT NOT NULL, `food` TEXT, `unit` TEXT, `quantity` REAL, `title` TEXT)",
"fields": [
{
"fieldPath": "localId",
"columnName": "local_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "recipeId",
"columnName": "recipe_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "note",
"columnName": "note",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "food",
"columnName": "food",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "unit",
"columnName": "unit",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "quantity",
"columnName": "quantity",
"affinity": "REAL",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"local_id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "recipe_instruction",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`local_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `recipe_id` TEXT NOT NULL, `text` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "localId",
"columnName": "local_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "recipeId",
"columnName": "recipe_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "text",
"columnName": "text",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"local_id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '793673e401425db36544918dae6bf4c1')"
]
}
}

View File

@@ -6,7 +6,7 @@ import gq.kirmanak.mealient.database.recipe.RecipeDao
import gq.kirmanak.mealient.database.recipe.entity.* import gq.kirmanak.mealient.database.recipe.entity.*
@Database( @Database(
version = 7, version = 8,
entities = [ entities = [
RecipeSummaryEntity::class, RecipeSummaryEntity::class,
RecipeEntity::class, RecipeEntity::class,
@@ -20,6 +20,7 @@ import gq.kirmanak.mealient.database.recipe.entity.*
AutoMigration(from = 4, to = 5, spec = AppDb.From4To5Migration::class), AutoMigration(from = 4, to = 5, spec = AppDb.From4To5Migration::class),
AutoMigration(from = 5, to = 6, spec = AppDb.From5To6Migration::class), AutoMigration(from = 5, to = 6, spec = AppDb.From5To6Migration::class),
AutoMigration(from = 6, to = 7), AutoMigration(from = 6, to = 7),
AutoMigration(from = 7, to = 8),
] ]
) )
@TypeConverters(RoomTypeConverters::class) @TypeConverters(RoomTypeConverters::class)

View File

@@ -15,4 +15,5 @@ data class RecipeSummaryEntity(
@ColumnInfo(name = "date_added") val dateAdded: LocalDate, @ColumnInfo(name = "date_added") val dateAdded: LocalDate,
@ColumnInfo(name = "date_updated") val dateUpdated: LocalDateTime, @ColumnInfo(name = "date_updated") val dateUpdated: LocalDateTime,
@ColumnInfo(name = "image_id") val imageId: String?, @ColumnInfo(name = "image_id") val imageId: String?,
@ColumnInfo(name = "is_favorite", defaultValue = "false") val isFavorite: Boolean,
) )

View File

@@ -5,13 +5,13 @@ interface NetworkRequestWrapper {
suspend fun <T> makeCall( suspend fun <T> makeCall(
block: suspend () -> T, block: suspend () -> T,
logMethod: () -> String, logMethod: () -> String,
logParameters: () -> String, logParameters: (() -> String)? = null,
): Result<T> ): Result<T>
suspend fun <T> makeCallAndHandleUnauthorized( suspend fun <T> makeCallAndHandleUnauthorized(
block: suspend () -> T, block: suspend () -> T,
logMethod: () -> String, logMethod: () -> String,
logParameters: () -> String, logParameters: (() -> String)? = null,
): T ): T
} }

View File

@@ -16,18 +16,40 @@ class NetworkRequestWrapperImpl @Inject constructor(
override suspend fun <T> makeCall( override suspend fun <T> makeCall(
block: suspend () -> T, block: suspend () -> T,
logMethod: () -> String, logMethod: () -> String,
logParameters: () -> String, logParameters: (() -> String)?,
): Result<T> { ): Result<T> {
logger.v { "${logMethod()} called with: ${logParameters()}" } logger.v {
if (logParameters == null) {
"${logMethod()} called"
} else {
"${logMethod()} called with: ${logParameters()}"
}
}
return runCatchingExceptCancel { block() } return runCatchingExceptCancel { block() }
.onFailure { logger.e(it) { "${logMethod()} request failed with: ${logParameters()}" } } .onFailure {
.onSuccess { logger.d { "${logMethod()} request succeeded with ${logParameters()}, result = $it" } } logger.e(it) {
if (logParameters == null) {
"${logMethod()} request failed"
} else {
"${logMethod()} request failed with: ${logParameters()}"
}
}
}
.onSuccess {
logger.d {
if (logParameters == null) {
"${logMethod()} request succeeded, result = $it"
} else {
"${logMethod()} request succeeded with: ${logParameters()}, result = $it"
}
}
}
} }
override suspend fun <T> makeCallAndHandleUnauthorized( override suspend fun <T> makeCallAndHandleUnauthorized(
block: suspend () -> T, block: suspend () -> T,
logMethod: () -> String, logMethod: () -> String,
logParameters: () -> String logParameters: (() -> String)?
): T = makeCall(block, logMethod, logParameters).getOrElse { ): T = makeCall(block, logMethod, logParameters).getOrElse {
throw if (it is HttpException && it.code() in listOf(401, 403)) { throw if (it is HttpException && it.code() in listOf(401, 403)) {
NetworkError.Unauthorized(it) NetworkError.Unauthorized(it)

View File

@@ -4,6 +4,7 @@ import gq.kirmanak.mealient.datasource.v0.models.AddRecipeRequestV0
import gq.kirmanak.mealient.datasource.v0.models.CreateApiTokenRequestV0 import gq.kirmanak.mealient.datasource.v0.models.CreateApiTokenRequestV0
import gq.kirmanak.mealient.datasource.v0.models.GetRecipeResponseV0 import gq.kirmanak.mealient.datasource.v0.models.GetRecipeResponseV0
import gq.kirmanak.mealient.datasource.v0.models.GetRecipeSummaryResponseV0 import gq.kirmanak.mealient.datasource.v0.models.GetRecipeSummaryResponseV0
import gq.kirmanak.mealient.datasource.v0.models.GetUserInfoResponseV0
import gq.kirmanak.mealient.datasource.v0.models.ParseRecipeURLRequestV0 import gq.kirmanak.mealient.datasource.v0.models.ParseRecipeURLRequestV0
import gq.kirmanak.mealient.datasource.v0.models.VersionResponseV0 import gq.kirmanak.mealient.datasource.v0.models.VersionResponseV0
@@ -21,8 +22,7 @@ interface MealieDataSourceV0 {
password: String, password: String,
): String ): String
suspend fun getVersionInfo( suspend fun getVersionInfo(): VersionResponseV0
): VersionResponseV0
suspend fun requestRecipes( suspend fun requestRecipes(
start: Int, start: Int,
@@ -40,4 +40,6 @@ interface MealieDataSourceV0 {
suspend fun createApiToken( suspend fun createApiToken(
request: CreateApiTokenRequestV0, request: CreateApiTokenRequestV0,
): String ): String
suspend fun requestUserInfo(): GetUserInfoResponseV0
} }

View File

@@ -8,6 +8,7 @@ import gq.kirmanak.mealient.datasource.v0.models.CreateApiTokenRequestV0
import gq.kirmanak.mealient.datasource.v0.models.ErrorDetailV0 import gq.kirmanak.mealient.datasource.v0.models.ErrorDetailV0
import gq.kirmanak.mealient.datasource.v0.models.GetRecipeResponseV0 import gq.kirmanak.mealient.datasource.v0.models.GetRecipeResponseV0
import gq.kirmanak.mealient.datasource.v0.models.GetRecipeSummaryResponseV0 import gq.kirmanak.mealient.datasource.v0.models.GetRecipeSummaryResponseV0
import gq.kirmanak.mealient.datasource.v0.models.GetUserInfoResponseV0
import gq.kirmanak.mealient.datasource.v0.models.ParseRecipeURLRequestV0 import gq.kirmanak.mealient.datasource.v0.models.ParseRecipeURLRequestV0
import gq.kirmanak.mealient.datasource.v0.models.VersionResponseV0 import gq.kirmanak.mealient.datasource.v0.models.VersionResponseV0
import kotlinx.serialization.SerializationException import kotlinx.serialization.SerializationException
@@ -49,7 +50,6 @@ class MealieDataSourceV0Impl @Inject constructor(
override suspend fun getVersionInfo(): VersionResponseV0 = networkRequestWrapper.makeCall( override suspend fun getVersionInfo(): VersionResponseV0 = networkRequestWrapper.makeCall(
block = { service.getVersion() }, block = { service.getVersion() },
logMethod = { "getVersionInfo" }, logMethod = { "getVersionInfo" },
logParameters = { "" },
).getOrElse { ).getOrElse {
throw when (it) { throw when (it) {
is HttpException, is SerializationException -> NetworkError.NotMealie(it) is HttpException, is SerializationException -> NetworkError.NotMealie(it)
@@ -90,4 +90,11 @@ class MealieDataSourceV0Impl @Inject constructor(
logMethod = { "createApiToken" }, logMethod = { "createApiToken" },
logParameters = { "request = $request" } logParameters = { "request = $request" }
) )
override suspend fun requestUserInfo(): GetUserInfoResponseV0 {
return networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.getUserSelfInfo() },
logMethod = { "requestUserInfo" },
)
}
} }

View File

@@ -40,4 +40,7 @@ interface MealieServiceV0 {
suspend fun createApiToken( suspend fun createApiToken(
@Body request: CreateApiTokenRequestV0, @Body request: CreateApiTokenRequestV0,
): String ): String
@GET("/api/users/self")
suspend fun getUserSelfInfo(): GetUserInfoResponseV0
} }

View File

@@ -0,0 +1,9 @@
package gq.kirmanak.mealient.datasource.v0.models
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class GetUserInfoResponseV0(
@SerialName("favoriteRecipes") val favoriteRecipes: List<String> = emptyList(),
)

View File

@@ -5,6 +5,7 @@ import gq.kirmanak.mealient.datasource.v1.models.CreateApiTokenResponseV1
import gq.kirmanak.mealient.datasource.v1.models.CreateRecipeRequestV1 import gq.kirmanak.mealient.datasource.v1.models.CreateRecipeRequestV1
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeResponseV1 import gq.kirmanak.mealient.datasource.v1.models.GetRecipeResponseV1
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeSummaryResponseV1 import gq.kirmanak.mealient.datasource.v1.models.GetRecipeSummaryResponseV1
import gq.kirmanak.mealient.datasource.v1.models.GetUserInfoResponseV1
import gq.kirmanak.mealient.datasource.v1.models.ParseRecipeURLRequestV1 import gq.kirmanak.mealient.datasource.v1.models.ParseRecipeURLRequestV1
import gq.kirmanak.mealient.datasource.v1.models.UpdateRecipeRequestV1 import gq.kirmanak.mealient.datasource.v1.models.UpdateRecipeRequestV1
import gq.kirmanak.mealient.datasource.v1.models.VersionResponseV1 import gq.kirmanak.mealient.datasource.v1.models.VersionResponseV1
@@ -47,4 +48,6 @@ interface MealieDataSourceV1 {
suspend fun createApiToken( suspend fun createApiToken(
request: CreateApiTokenRequestV1, request: CreateApiTokenRequestV1,
): CreateApiTokenResponseV1 ): CreateApiTokenResponseV1
suspend fun requestUserInfo(): GetUserInfoResponseV1
} }

View File

@@ -9,6 +9,7 @@ import gq.kirmanak.mealient.datasource.v1.models.CreateRecipeRequestV1
import gq.kirmanak.mealient.datasource.v1.models.ErrorDetailV1 import gq.kirmanak.mealient.datasource.v1.models.ErrorDetailV1
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeResponseV1 import gq.kirmanak.mealient.datasource.v1.models.GetRecipeResponseV1
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeSummaryResponseV1 import gq.kirmanak.mealient.datasource.v1.models.GetRecipeSummaryResponseV1
import gq.kirmanak.mealient.datasource.v1.models.GetUserInfoResponseV1
import gq.kirmanak.mealient.datasource.v1.models.ParseRecipeURLRequestV1 import gq.kirmanak.mealient.datasource.v1.models.ParseRecipeURLRequestV1
import gq.kirmanak.mealient.datasource.v1.models.UpdateRecipeRequestV1 import gq.kirmanak.mealient.datasource.v1.models.UpdateRecipeRequestV1
import gq.kirmanak.mealient.datasource.v1.models.VersionResponseV1 import gq.kirmanak.mealient.datasource.v1.models.VersionResponseV1
@@ -60,7 +61,6 @@ class MealieDataSourceV1Impl @Inject constructor(
override suspend fun getVersionInfo(): VersionResponseV1 = networkRequestWrapper.makeCall( override suspend fun getVersionInfo(): VersionResponseV1 = networkRequestWrapper.makeCall(
block = { service.getVersion() }, block = { service.getVersion() },
logMethod = { "getVersionInfo" }, logMethod = { "getVersionInfo" },
logParameters = { "" },
).getOrElse { ).getOrElse {
throw when (it) { throw when (it) {
is HttpException, is SerializationException -> NetworkError.NotMealie(it) is HttpException, is SerializationException -> NetworkError.NotMealie(it)
@@ -101,5 +101,12 @@ class MealieDataSourceV1Impl @Inject constructor(
logMethod = { "createApiToken" }, logMethod = { "createApiToken" },
logParameters = { "request = $request" } logParameters = { "request = $request" }
) )
override suspend fun requestUserInfo(): GetUserInfoResponseV1 {
return networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.getUserSelfInfo() },
logMethod = { "requestUserInfo" },
)
}
} }

View File

@@ -46,4 +46,7 @@ interface MealieServiceV1 {
suspend fun createApiToken( suspend fun createApiToken(
@Body request: CreateApiTokenRequestV1, @Body request: CreateApiTokenRequestV1,
): CreateApiTokenResponseV1 ): CreateApiTokenResponseV1
@GET("/api/users/self")
suspend fun getUserSelfInfo(): GetUserInfoResponseV1
} }

View File

@@ -0,0 +1,9 @@
package gq.kirmanak.mealient.datasource.v1.models
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class GetUserInfoResponseV1(
@SerialName("favoriteRecipes") val favoriteRecipes: List<String> = emptyList(),
)