add api cache cleanup

This commit is contained in:
2026-08-17 14:47:29 +03:00
parent ea67ed80a5
commit 0243466cf1
5 changed files with 58 additions and 43 deletions
@@ -0,0 +1,40 @@
package ru.shadowsparky.vbox.shared.data.api
import app.cash.sqldelight.async.coroutines.awaitAsOneOrNull
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import org.koin.core.annotation.Factory
import ru.shadowsparky.domain.DispatcherProvider
import ru.shadowsparky.vbox.domain.ApiCacheQueries
import ru.shadowsparky.vbox.shared.data.AppDatabaseProvider
@Factory
class ApiCache(
val dbProvider: AppDatabaseProvider,
val json: Json,
val dispatcherProvider: DispatcherProvider
) {
suspend inline fun <reified T> writeByKey(key: String, value: T) =
withContext(dispatcherProvider.io) {
getCache().insert(
key,
json.encodeToString(value)
)
}
suspend fun clear() {
getCache().clear()
}
suspend inline fun <reified T> getByKey(key: String): T? {
val value = getCache().selectByKey(key)
.awaitAsOneOrNull()?.cache_value
?: return null
return json.decodeFromString(value)
}
suspend fun getCache(): ApiCacheQueries {
return dbProvider.get().apiCacheQueries
}
}
@@ -1,31 +1,23 @@
package ru.shadowsparky.vbox.shared.data.api package ru.shadowsparky.vbox.shared.data.api
import app.cash.sqldelight.async.coroutines.awaitAsOneOrNull
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import ru.shadowsparky.domain.DispatcherProvider
import ru.shadowsparky.vbox.domain.ApiCacheQueries
import ru.shadowsparky.vbox.shared.data.AppDatabaseProvider
import ru.shadowsparky.vbox.shared.domain.VideoApi import ru.shadowsparky.vbox.shared.domain.VideoApi
import ru.shadowsparky.vbox.shared.domain.model.VideoDetails import ru.shadowsparky.vbox.shared.domain.model.VideoDetails
import ru.shadowsparky.vbox.shared.domain.model.VideoLinksResponse import ru.shadowsparky.vbox.shared.domain.model.VideoLinksResponse
import ru.shadowsparky.vbox.shared.domain.model.VideosResponse import ru.shadowsparky.vbox.shared.domain.model.VideosResponse
class FallbackVideoApi( class FallbackVideoApi(
private val dbProvider: AppDatabaseProvider, private val cache: ApiCache,
private val wrapper: VideoApi, private val wrapper: VideoApi
private val json: Json,
private val dispatcherProvider: DispatcherProvider
) : VideoApi { ) : VideoApi {
override suspend fun fetchNewVideos(query: String?): VideosResponse { override suspend fun fetchNewVideos(query: String?): VideosResponse {
val key = "fetchNewVideos_${query};1;date;" val key = "fetchNewVideos_${query};1;date;"
return try { return try {
wrapper.fetchNewVideos(query).apply { wrapper.fetchNewVideos(query).apply {
writeByKey(key, this) cache.writeByKey(key, this)
} }
} catch (e: Exception) { } catch (e: Exception) {
getByKey(key) ?: throw e cache.getByKey(key) ?: throw e
} }
} }
@@ -33,10 +25,10 @@ class FallbackVideoApi(
val key = "fetchDetails_${id};" val key = "fetchDetails_${id};"
return try { return try {
wrapper.fetchDetails(id).apply { wrapper.fetchDetails(id).apply {
writeByKey(key, this) cache.writeByKey(key, this)
} }
} catch (e: Exception) { } catch (e: Exception) {
getByKey(key) ?: throw e cache.getByKey(key) ?: throw e
} }
} }
@@ -44,29 +36,10 @@ class FallbackVideoApi(
val key = "fetchVideoLinks_${id};$seasonId;" val key = "fetchVideoLinks_${id};$seasonId;"
return try { return try {
wrapper.fetchVideoLinks(id, seasonId).apply { wrapper.fetchVideoLinks(id, seasonId).apply {
writeByKey(key, this) cache.writeByKey(key, this)
} }
} catch (e: Exception) { } catch (e: Exception) {
getByKey(key) ?: throw e cache.getByKey(key) ?: throw e
} }
} }
private suspend fun getCache(): ApiCacheQueries {
return dbProvider.get().apiCacheQueries
}
private suspend inline fun <reified T> getByKey(key: String): T? {
val value = getCache().selectByKey(key)
.awaitAsOneOrNull()?.cache_value
?: return null
return json.decodeFromString(value)
}
private suspend inline fun <reified T> writeByKey(key: String, value: T) =
withContext(dispatcherProvider.io) {
getCache().insert(
key,
json.encodeToString(value)
)
}
} }
@@ -1,11 +1,9 @@
package ru.shadowsparky.vbox.shared.di package ru.shadowsparky.vbox.shared.di
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import kotlinx.serialization.json.Json
import org.koin.core.annotation.Module import org.koin.core.annotation.Module
import org.koin.core.annotation.Single import org.koin.core.annotation.Single
import ru.shadowsparky.domain.DispatcherProvider import ru.shadowsparky.vbox.shared.data.api.ApiCache
import ru.shadowsparky.vbox.shared.data.AppDatabaseProvider
import ru.shadowsparky.vbox.shared.data.api.FallbackVideoApi import ru.shadowsparky.vbox.shared.data.api.FallbackVideoApi
import ru.shadowsparky.vbox.shared.data.api.PosterWithoutSuffixVideoApi import ru.shadowsparky.vbox.shared.data.api.PosterWithoutSuffixVideoApi
import ru.shadowsparky.vbox.shared.data.api.VideoApiImpl import ru.shadowsparky.vbox.shared.data.api.VideoApiImpl
@@ -19,13 +17,11 @@ class HttpModule {
fun provideVideoApi( fun provideVideoApi(
httpClient: HttpClient, httpClient: HttpClient,
serverConfigurationProvider: ServerConfigurationRepository, serverConfigurationProvider: ServerConfigurationRepository,
json: Json, apiCache: ApiCache
dbProvider: AppDatabaseProvider,
dispatcherProvider: DispatcherProvider
): VideoApi { ): VideoApi {
val impl = VideoApiImpl(httpClient, serverConfigurationProvider) val impl = VideoApiImpl(httpClient, serverConfigurationProvider)
return PosterWithoutSuffixVideoApi( return PosterWithoutSuffixVideoApi(
FallbackVideoApi(dbProvider, impl, json, dispatcherProvider) FallbackVideoApi(apiCache, impl)
) )
} }
} }
@@ -6,6 +6,7 @@ import org.koin.core.annotation.Factory
import org.koin.core.annotation.Named import org.koin.core.annotation.Named
import ru.shadowsparky.http.domain.LogoutAction import ru.shadowsparky.http.domain.LogoutAction
import ru.shadowsparky.http.domain.TokenStorage import ru.shadowsparky.http.domain.TokenStorage
import ru.shadowsparky.vbox.shared.data.api.ApiCache
import ru.shadowsparky.vbox.shared.di.DbType import ru.shadowsparky.vbox.shared.di.DbType
import ru.shadowsparky.vbox.shared.domain.tag.MovieTagRepository import ru.shadowsparky.vbox.shared.domain.tag.MovieTagRepository
import ru.shadowsparky.vbox.shared.domain.tag.UserTagRepository import ru.shadowsparky.vbox.shared.domain.tag.UserTagRepository
@@ -21,7 +22,8 @@ class LogoutUseCase(
private val bearerAuthProvider: BearerAuthProvider, private val bearerAuthProvider: BearerAuthProvider,
private val tokenStorage: TokenStorage, private val tokenStorage: TokenStorage,
private val authTokenRepository: AuthTokenRepository, private val authTokenRepository: AuthTokenRepository,
private val pendingOperationRepository: PendingOperationRepository private val pendingOperationRepository: PendingOperationRepository,
private val apiCache: ApiCache
) : LogoutAction { ) : LogoutAction {
override suspend fun logout() { override suspend fun logout() {
@@ -37,5 +39,6 @@ class LogoutUseCase(
localUserTagRepository.clear() localUserTagRepository.clear()
AggregateType.entries.forEach { syncRepository.set(it, false) } AggregateType.entries.forEach { syncRepository.set(it, false) }
pendingOperationRepository.clear() pendingOperationRepository.clear()
apiCache.clear()
} }
} }
@@ -12,3 +12,6 @@ deleteByKey:
selectByKey: selectByKey:
SELECT * FROM ApiCache WHERE cache_key = ?; SELECT * FROM ApiCache WHERE cache_key = ?;
clear:
DELETE FROM ApiCache;