diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f9823c93ce..3c3664fe683 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,28 @@ * `Versions`: * `Kotlin`: `1.4.31` -> `1.4.32` -* Add subproject `repos.cache` +* `Pagination`: + * New extensions `PaginationResult.changeResultsUnchecked` and `PaginationResult.changeResults` for mapping results + with the same parameters, but different data + * Extension `PaginationResult.thisPageIfNotEmpty` now is typed and will return `PaginationResult?` with the same + generic type as income `PaginationResult` + * New extension `PaginationResult.currentPageIfNotEmpty` - shortcut for `PaginationResult.thisPageIfNotEmpty` * `Coroutines`: * Rewrite `subscribeSafelyWithoutExceptions` * Now `subscribeSafelyWithoutExceptions` will use default handler instead of skipping * New extension `subscribeSafelySkippingExceptions` +* `Repos` + * New subproject `repos.cache` - this subproject will contain repos with data caching mechanisms + * `Pagination` + * New common functions. They were created as replacements for currently available for more comfortable work + with repos pagination: + * `doForAll` + * `doForAllWithNextPaging` + * `doForAllWithCurrentPaging` + * `getAll` + * `getAllWithNextPaging` + * `getAllWithCurrentPaging` + * Most old `doForAll` methods have been deprecated ## 0.4.30 diff --git a/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/PaginationResult.kt b/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/PaginationResult.kt index 35c3f3939a8..fbc092069d4 100644 --- a/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/PaginationResult.kt +++ b/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/PaginationResult.kt @@ -12,6 +12,22 @@ data class PaginationResult( fun emptyPaginationResult() = PaginationResult(0, 0, emptyList(), 0) +/** + * @return New [PaginationResult] with [data] without checking of data sizes equality + */ +fun PaginationResult.changeResultsUnchecked( + data: List +): PaginationResult = PaginationResult(page, pagesNumber, data, size) +/** + * @return New [PaginationResult] with [data] with checking of data sizes equality + */ +fun PaginationResult.changeResults( + data: List +): PaginationResult { + require(data.size == results.size) + return changeResultsUnchecked(data) +} + fun List.createPaginationResult( pagination: Pagination, commonObjectsNumber: Long diff --git a/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/WalkPagination.kt b/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/WalkPagination.kt index aed3d2716fa..05c1087fed8 100644 --- a/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/WalkPagination.kt +++ b/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/WalkPagination.kt @@ -21,8 +21,10 @@ inline fun PaginationResult<*>.nextPageIfNotEmpty() = if (results.isNotEmpty()) } @Suppress("NOTHING_TO_INLINE") -inline fun PaginationResult<*>.thisPageIfNotEmpty(): Pagination? = if (results.isNotEmpty()) { +inline fun PaginationResult.thisPageIfNotEmpty(): PaginationResult? = if (results.isNotEmpty()) { this } else { null } + +inline fun PaginationResult.currentPageIfNotEmpty() = thisPageIfNotEmpty() diff --git a/repos/cache/build.gradle b/repos/cache/build.gradle index a847ec69fcb..e128b1e3cff 100644 --- a/repos/cache/build.gradle +++ b/repos/cache/build.gradle @@ -11,6 +11,7 @@ kotlin { commonMain { dependencies { api internalProject("micro_utils.repos.common") + api internalProject("micro_utils.repos.inmemory") } } } diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/CRUDCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/CRUDCacheRepo.kt new file mode 100644 index 00000000000..22ffbb489c9 --- /dev/null +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/CRUDCacheRepo.kt @@ -0,0 +1,24 @@ +package dev.inmo.micro_utils.repos.cache + +import dev.inmo.micro_utils.repos.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach + +open class CRUDCacheRepo( + protected val parentRepo: CRUDRepo, + protected val kvCache: KVCache, + scope: CoroutineScope = CoroutineScope(Dispatchers.Default), + protected val idGetter: (ObjectType) -> IdType +) : CRUDRepo by parentRepo { + protected val onNewJob = parentRepo.newObjectsFlow.onEach { kvCache.set(idGetter(it), it) }.launchIn(scope) + protected val onUpdatedJob = parentRepo.updatedObjectsFlow.onEach { kvCache.set(idGetter(it), it) }.launchIn(scope) + protected val onRemoveJob = parentRepo.deletedObjectsIdsFlow.onEach { kvCache.unset(it) }.launchIn(scope) + + override suspend fun getById(id: IdType): ObjectType? = kvCache.get(id) ?: (parentRepo.getById(id) ?.also { + kvCache.set(id, it) + }) + + override suspend fun contains(id: IdType): Boolean = kvCache.contains(id) || parentRepo.contains(id) +} diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KVCache.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KVCache.kt new file mode 100644 index 00000000000..b75a741496d --- /dev/null +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KVCache.kt @@ -0,0 +1,41 @@ +package dev.inmo.micro_utils.repos.cache + +import dev.inmo.micro_utils.repos.* +import dev.inmo.micro_utils.repos.pagination.getAllWithNextPaging +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock + +interface KVCache : KeyValueRepo + +open class SimpleKVCache( + protected val cachedValuesCount: Int, + private val kvParent: KeyValueRepo = MapKeyValueRepo() +) : KVCache, KeyValueRepo by kvParent { + protected open val cacheStack = ArrayList(cachedValuesCount) + protected val syncMutex = Mutex() + + protected suspend fun makeUnset(toUnset: List) { + cacheStack.removeAll(toUnset) + kvParent.unset(toUnset) + } + + override suspend fun set(toSet: Map) { + syncMutex.withLock { + if (toSet.size > cachedValuesCount) { + cacheStack.clear() + + kvParent.unset(getAllWithNextPaging { kvParent.keys(it) }) + val keysToInclude = toSet.keys.drop(toSet.size - cachedValuesCount) + + cacheStack.addAll(keysToInclude) + kvParent.set(keysToInclude.associateWith { toSet.getValue(it) }) + } else { + makeUnset(cacheStack.take(toSet.size)) + } + } + } + + override suspend fun unset(toUnset: List) { + syncMutex.withLock { makeUnset(toUnset) } + } +} diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValueCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValueCacheRepo.kt index 0648dddb8f6..86d865afe03 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValueCacheRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValueCacheRepo.kt @@ -9,34 +9,12 @@ import kotlinx.coroutines.sync.withLock open class KeyValueCacheRepo( protected val parentRepo: KeyValueRepo, - protected val cachedValuesCount: Int, + protected val kvCache: KVCache, scope: CoroutineScope = CoroutineScope(Dispatchers.Default) ) : KeyValueRepo by parentRepo { - protected open val cache = mutableMapOf() - protected open val cacheStack = ArrayList(cachedValuesCount) - protected val syncMutex = Mutex() - protected val onNewJob = parentRepo.onNewValue.onEach { putCacheValue(it.first, it.second) }.launchIn(scope) - protected val onRemoveJob = parentRepo.onValueRemoved.onEach { removeCacheValue(it) }.launchIn(scope) + protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, it.second) }.launchIn(scope) + protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.unset(it) }.launchIn(scope) - protected suspend fun putCacheValue(k: Key, v: Value) = syncMutex.withLock { - if (cache.size >= cachedValuesCount) { - val key = cacheStack.removeAt(0) - cache.remove(key) - } - cacheStack.add(k) - cache[k] = v - } - - protected suspend fun removeCacheValue(k: Key) = syncMutex.withLock { - val i = cacheStack.indexOf(k) - if (i >= 0) { - val key = cacheStack.removeAt(i) - cache.remove(key) - } - } - - override suspend fun get(k: Key): Value? = syncMutex.withLock { - cache[k] ?: parentRepo.get(k) ?.also { cache[k] = it } - } - override suspend fun contains(key: Key): Boolean = cache.containsKey(key) || parentRepo.contains(key) + override suspend fun get(k: Key): Value? = kvCache.get(k) ?: parentRepo.get(k) ?.also { kvCache.set(k, it) } + override suspend fun contains(key: Key): Boolean = kvCache.contains(key) || parentRepo.contains(key) } diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValuesCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValuesCacheRepo.kt index 54f46cc1717..26b12af4fd2 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValuesCacheRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValuesCacheRepo.kt @@ -13,62 +13,25 @@ import kotlinx.coroutines.sync.withLock open class KeyValuesCacheRepo( protected val parentRepo: KeyValuesRepo, - protected val cachedValuesCount: Int, + protected val kvCache: KVCache>, scope: CoroutineScope = CoroutineScope(Dispatchers.Default) ) : KeyValuesRepo by parentRepo { - protected open val cache = mutableMapOf>() - protected open val cacheStack = ArrayList(cachedValuesCount) - protected val syncMutex = Mutex() - protected val onNewJob = parentRepo.onNewValue.onEach { putCacheValue(it.first, it.second) }.launchIn(scope) - protected val onRemoveJob = parentRepo.onValueRemoved.onEach { removeCacheValue(it.first, it.second) }.launchIn(scope) - protected val onDataClearedJob = parentRepo.onDataCleared.onEach { clearCacheValues(it) }.launchIn(scope) - - protected suspend fun putCacheValues(k: Key, v: List) = syncMutex.withLock { - if (cache.size >= cachedValuesCount) { - val key = cacheStack.removeAt(0) - cache.remove(key) - } - cacheStack.add(k) - cache[k] = v - } - protected suspend fun putCacheValue(k: Key, v: Value) = syncMutex.withLock { - cache[k] ?.let { - cache[k] = it + v - } - } ?: putCacheValues(k, listOf(v)) - - protected suspend fun removeCacheValue(k: Key, v: Value) = syncMutex.withLock { - cache[k] ?.let { - val newList = it - v - if (newList.isEmpty()) { - cache.remove(k) - cacheStack.remove(k) - } else { - cache[k] = newList - } - } - } - - protected suspend fun clearCacheValues(k: Key) = syncMutex.withLock { - val i = cacheStack.indexOf(k) - if (i >= 0) { - val key = cacheStack.removeAt(i) - cache.remove(key) - } - } + protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, kvCache.get(it.first) ?.plus(it.second) ?: listOf(it.second)) }.launchIn(scope) + protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.set(it.first, kvCache.get(it.first) ?.minus(it.second) ?: return@onEach) }.launchIn(scope) + protected val onDataClearedJob = parentRepo.onDataCleared.onEach { kvCache.unset(it) }.launchIn(scope) override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult { - return cache[k] ?.paginate( + return kvCache.get(k) ?.paginate( pagination.let { if (reversed) it.reverse(count(k)) else it } ) ?.let { if (reversed) it.copy(results = it.results.reversed()) else it } ?: parentRepo.get(k, pagination, reversed) } override suspend fun getAll(k: Key, reversed: Boolean): List { - return cache[k] ?.let { + return kvCache.get(k) ?.let { if (reversed) it.reversed() else it } ?: parentRepo.getAll(k, reversed) } - override suspend fun contains(k: Key, v: Value): Boolean = cache[k] ?.contains(v) ?: parentRepo.contains(k, v) - override suspend fun contains(k: Key): Boolean = cache.containsKey(k) || parentRepo.contains(k) + override suspend fun contains(k: Key, v: Value): Boolean = kvCache.get(k) ?.contains(v) ?: parentRepo.contains(k, v) + override suspend fun contains(k: Key): Boolean = kvCache.contains(k) || parentRepo.contains(k) } diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/OneToManyKeyValueRepo.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/OneToManyKeyValueRepo.kt index b08e10ff5da..e899c78c2e8 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/OneToManyKeyValueRepo.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/OneToManyKeyValueRepo.kt @@ -1,6 +1,7 @@ package dev.inmo.micro_utils.repos import dev.inmo.micro_utils.pagination.* +import dev.inmo.micro_utils.repos.pagination.getAllWithNextPaging import kotlinx.coroutines.flow.Flow interface ReadOneToManyKeyValueRepo : Repo { @@ -12,14 +13,12 @@ interface ReadOneToManyKeyValueRepo : Repo { suspend fun count(k: Key): Long suspend fun count(): Long - suspend fun getAll(k: Key, reversed: Boolean = false): List = mutableListOf().also { list -> - doWithPagination { - get(k, it).also { - list.addAll(it.results) - }.nextPageIfNotEmpty() - } - if (reversed) { - list.reverse() + suspend fun getAll(k: Key, reversed: Boolean = false): List { + val results = getAllWithNextPaging { get(k, it) } + return if (reversed) { + results.reversed() + } else { + results } } diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/StandartKeyValueRepo.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/StandartKeyValueRepo.kt index 449396889c9..7747120478a 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/StandartKeyValueRepo.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/StandartKeyValueRepo.kt @@ -1,6 +1,7 @@ package dev.inmo.micro_utils.repos import dev.inmo.micro_utils.pagination.* +import dev.inmo.micro_utils.repos.pagination.doAllWithCurrentPaging import kotlinx.coroutines.flow.Flow interface ReadStandardKeyValueRepo : Repo { @@ -41,7 +42,7 @@ suspend inline fun WriteStandardKeyValueRepo.unsetWithV interface StandardKeyValueRepo : ReadStandardKeyValueRepo, WriteStandardKeyValueRepo { override suspend fun unsetWithValues(toUnset: List) = toUnset.forEach { v -> - doWithPagination { + doAllWithCurrentPaging { keys(v, it).also { unset(it.results) } diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/CRUDPaginationExtensions.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/CRUDPaginationExtensions.kt index e626262f229..ae2a786b6c3 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/CRUDPaginationExtensions.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/CRUDPaginationExtensions.kt @@ -3,29 +3,29 @@ package dev.inmo.micro_utils.repos.pagination import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo +@Deprecated("Will be removed soon due to redundancy. Can be replaced with other doForAll extensions") suspend inline fun > REPO.doForAll( @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") - methodCaller: suspend REPO.(Pagination) -> PaginationResult, - block: (List) -> Unit + crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult, + crossinline block: (List) -> Unit ) { - doWithPagination { + doForAllWithNextPaging { methodCaller(it).also { block(it.results) - }.nextPageIfNotEmpty() + } } } +@Deprecated("Will be removed soon due to redundancy. Can be replaced with other doForAll extensions") suspend inline fun > REPO.doForAll( - block: (List) -> Unit -) = doForAll({ getByPagination(it) }, block) + crossinline block: (List) -> Unit +) = doForAllWithNextPaging { + getByPagination(it).also { block(it.results) } +} suspend inline fun > REPO.getAll( @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") - methodCaller: suspend REPO.(Pagination) -> PaginationResult -): List { - val resultList = mutableListOf() - doForAll(methodCaller) { - resultList.addAll(it) - } - return resultList + crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult +): List = getAllWithNextPaging { + methodCaller(this, it) } diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/CommonPaginationExtensions.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/CommonPaginationExtensions.kt new file mode 100644 index 00000000000..3d6b30523e7 --- /dev/null +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/CommonPaginationExtensions.kt @@ -0,0 +1,67 @@ +package dev.inmo.micro_utils.repos.pagination + +import dev.inmo.micro_utils.pagination.* + +suspend fun doForAll( + initialPagination: Pagination = FirstPagePagination(), + paginationMapper: (PaginationResult) -> Pagination?, + block: suspend (Pagination) -> PaginationResult +) { + doWithPagination(initialPagination) { + block(it).let(paginationMapper) + } +} + +suspend fun doForAllWithNextPaging( + initialPagination: Pagination = FirstPagePagination(), + block: suspend (Pagination) -> PaginationResult +) { + doForAll( + initialPagination, + { it.nextPageIfNotEmpty() }, + block + ) +} + +suspend fun doAllWithCurrentPaging( + initialPagination: Pagination = FirstPagePagination(), + block: suspend (Pagination) -> PaginationResult +) { + doForAll( + initialPagination, + { it.thisPageIfNotEmpty() }, + block + ) +} + +suspend fun getAll( + initialPagination: Pagination = FirstPagePagination(), + paginationMapper: (PaginationResult) -> Pagination?, + block: suspend (Pagination) -> PaginationResult +): List { + val results = mutableListOf() + doForAll(initialPagination, paginationMapper) { + block(it).also { + results.addAll(it.results) + } + } + return results.toList() +} + +suspend fun getAllWithNextPaging( + initialPagination: Pagination = FirstPagePagination(), + block: suspend (Pagination) -> PaginationResult +): List = getAll( + initialPagination, + { it.nextPageIfNotEmpty() }, + block +) + +suspend fun getAllWithCurrentPaging( + initialPagination: Pagination = FirstPagePagination(), + block: suspend (Pagination) -> PaginationResult +): List = getAll( + initialPagination, + { it.thisPageIfNotEmpty() }, + block +) diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/KeyValuePaginationExtensions.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/KeyValuePaginationExtensions.kt index 9a4b3391f2f..d4605b7dd1d 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/KeyValuePaginationExtensions.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/KeyValuePaginationExtensions.kt @@ -3,6 +3,7 @@ package dev.inmo.micro_utils.repos.pagination import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo +@Deprecated("Will be removed soon due to redundancy") suspend inline fun > REPO.doForAll( @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") methodCaller: suspend REPO.(Pagination) -> PaginationResult, @@ -15,17 +16,17 @@ suspend inline fun > REP } } +@Deprecated("Will be removed soon due to redundancy") suspend inline fun > REPO.doForAll( block: (List>) -> Unit ) = doForAll({ keys(it, false) }, block) suspend inline fun > REPO.getAll( @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") - methodCaller: suspend REPO.(Pagination) -> PaginationResult -): List> { - val resultList = mutableListOf>() - doForAll(methodCaller) { - resultList.addAll(it) - } - return resultList + crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult +): List> = getAllWithNextPaging { + val result = methodCaller(it) + result.changeResultsUnchecked( + result.results.mapNotNull { it to (get(it) ?: return@mapNotNull null) } + ) } diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/OneToManyPaginationExtensions.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/OneToManyPaginationExtensions.kt index 28d3405e595..47e43a7380c 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/OneToManyPaginationExtensions.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/OneToManyPaginationExtensions.kt @@ -3,6 +3,7 @@ package dev.inmo.micro_utils.repos.pagination import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo +@Deprecated("Will be removed soon due to redundancy") suspend inline fun > REPO.doForAll( @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") methodCaller: suspend REPO.(Pagination) -> PaginationResult, @@ -25,17 +26,19 @@ suspend inline fun > RE } } +@Deprecated("Will be removed soon due to redundancy") suspend inline fun > REPO.doForAll( block: (List>>) -> Unit ) = doForAll({ keys(it, false) }, block) suspend inline fun > REPO.getAll( @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") - methodCaller: suspend REPO.(Pagination) -> PaginationResult -): List>> { - val resultList = mutableListOf>>() - doForAll(methodCaller) { - resultList.addAll(it) - } - return resultList + crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult +): List>> = getAllWithNextPaging { + val keysResult = methodCaller(it) + keysResult.changeResultsUnchecked( + keysResult.results.map { k -> + k to getAll(k) + } + ) }