diff --git a/build.gradle b/build.gradle index 61ab1cb58..94fba35ee 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ buildscript { } ext.versions = [ - androidGradlePlugin : '4.0.0-beta01', + androidGradlePlugin : '3.6.1', kotlin : '1.3.70', dokkaGradlePlugin : '0.10.0', ktlintGradle : '9.1.1', diff --git a/store/api/store.api b/store/api/store.api index c141a8941..4c1fa5ea8 100644 --- a/store/api/store.api +++ b/store/api/store.api @@ -9,6 +9,30 @@ public abstract interface class com/dropbox/android/external/store4/DiskWrite { public abstract interface annotation class com/dropbox/android/external/store4/ExperimentalStoreApi : java/lang/annotation/Annotation { } +public abstract interface class com/dropbox/android/external/store4/Fetcher { + public static final field Companion Lcom/dropbox/android/external/store4/Fetcher$Companion; + public abstract fun invoke (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class com/dropbox/android/external/store4/Fetcher$Companion { + public final fun fromNonFlowingFetcher (Lkotlin/jvm/functions/Function2;)Lcom/dropbox/android/external/store4/Fetcher; + public final fun fromNonFlowingValueFetcher (Lkotlin/jvm/functions/Function2;)Lcom/dropbox/android/external/store4/Fetcher; + public final fun fromValueFetcher (Lkotlin/jvm/functions/Function1;)Lcom/dropbox/android/external/store4/Fetcher; +} + +public abstract class com/dropbox/android/external/store4/FetcherResponse { +} + +public final class com/dropbox/android/external/store4/FetcherResponse$Error : com/dropbox/android/external/store4/FetcherResponse { + public fun (Ljava/lang/Throwable;)V + public final fun getError ()Ljava/lang/Throwable; +} + +public final class com/dropbox/android/external/store4/FetcherResponse$Value : com/dropbox/android/external/store4/FetcherResponse { + public fun (Ljava/lang/Object;)V + public final fun getValue ()Ljava/lang/Object; +} + public final class com/dropbox/android/external/store4/MemoryPolicy { public static final field Companion Lcom/dropbox/android/external/store4/MemoryPolicy$Companion; public static final field DEFAULT_SIZE_POLICY J @@ -65,6 +89,7 @@ public abstract interface class com/dropbox/android/external/store4/StoreBuilder } public final class com/dropbox/android/external/store4/StoreBuilder$Companion { + public final fun from (Lcom/dropbox/android/external/store4/Fetcher;)Lcom/dropbox/android/external/store4/StoreBuilder; public final fun from (Lkotlin/jvm/functions/Function1;)Lcom/dropbox/android/external/store4/StoreBuilder; public final fun fromNonFlow (Lkotlin/jvm/functions/Function2;)Lcom/dropbox/android/external/store4/StoreBuilder; } diff --git a/store/src/main/java/com/dropbox/android/external/store4/Fetcher.kt b/store/src/main/java/com/dropbox/android/external/store4/Fetcher.kt new file mode 100644 index 000000000..56d47a55a --- /dev/null +++ b/store/src/main/java/com/dropbox/android/external/store4/Fetcher.kt @@ -0,0 +1,85 @@ +package com.dropbox.android.external.store4 + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map +import java.util.concurrent.CancellationException + +/** + * The interface that defines a Fetcher, which is responsible to fetch data from a remote data + * source. (e.g. make API calls). + * + * To create a fetcher, use the convenience methods ([fromValueFetcher], [fromNonFlowingFetcher], + * [fromNonFlowingValueFetcher]). + */ +interface Fetcher { + suspend operator fun invoke(key: Key): Flow> + + companion object { + /** + * Creates a [Fetcher] from the given flow generating function. If the returned [Flow] emits + * an error, it will be wrapped in a [FetcherResponse.Error]. + */ + fun fromValueFetcher( + doFetch: (key: Key) -> Flow + ): Fetcher = FlowingValueFetcher(doFetch) + + /** + * Creates a [Fetcher] from the given function. If it throws an error, the response will be + * wrapped in a [FetcherResponse.Error]. + */ + fun fromNonFlowingValueFetcher( + doFetch: suspend (key: Key) -> Output + ): Fetcher = NonFlowingValueFetcher(doFetch) + + /** + * Creates a [Fetcher] that returns only 1 value (e.g. a single web request, not a stream). + * An exception thrown from this function will not be caught by Store. + */ + fun fromNonFlowingFetcher( + doFetch: suspend (key: Key) -> FetcherResponse + ): Fetcher = NonFlowingFetcher(doFetch) + } +} + +internal class NonFlowingFetcher( + private val doFetch: suspend (key: Key) -> FetcherResponse +) : Fetcher { + override suspend fun invoke(key: Key): Flow> { + return flow { + emit(doFetch(key)) + } + } +} + +internal class NonFlowingValueFetcher( + private val doFetch: suspend (key: Key) -> Output +) : Fetcher { + override suspend fun invoke(key: Key): Flow> { + return flow { + try { + emit(FetcherResponse.Value(doFetch(key))) + } catch (th: Throwable) { + if (th is CancellationException) { + throw th + } + emit( + FetcherResponse.Error(th) + ) + } + } + } +} + +internal class FlowingValueFetcher( + private val doFetch: (key: Key) -> Flow +) : Fetcher { + override suspend fun invoke(key: Key): Flow> { + return doFetch(key).map { + FetcherResponse.Value(it) as FetcherResponse + }.catch { + emit(FetcherResponse.Error(it)) + } + } +} \ No newline at end of file diff --git a/store/src/main/java/com/dropbox/android/external/store4/FetcherResponse.kt b/store/src/main/java/com/dropbox/android/external/store4/FetcherResponse.kt new file mode 100644 index 000000000..984d55212 --- /dev/null +++ b/store/src/main/java/com/dropbox/android/external/store4/FetcherResponse.kt @@ -0,0 +1,21 @@ +package com.dropbox.android.external.store4 + +/** + * Value type emitted by the [Fetcher]'s `Flow`. + */ +sealed class FetcherResponse { + /** + * Success result, should include a non-null value. + */ + class Value( + val value: T + ) : FetcherResponse() + + /** + * Error result, it should contain the error information + */ + // TODO support non-throwable errors ? + class Error( + val error: Throwable + ) : FetcherResponse() +} \ No newline at end of file diff --git a/store/src/main/java/com/dropbox/android/external/store4/StoreBuilder.kt b/store/src/main/java/com/dropbox/android/external/store4/StoreBuilder.kt index 74fda0241..438aedc6f 100644 --- a/store/src/main/java/com/dropbox/android/external/store4/StoreBuilder.kt +++ b/store/src/main/java/com/dropbox/android/external/store4/StoreBuilder.kt @@ -118,13 +118,14 @@ interface StoreBuilder { * @param fetcher a function for fetching network records. */ @OptIn(ExperimentalTime::class) + @Deprecated(message = "Creating a flow from a function is deprecated, use Fetcher", + replaceWith = ReplaceWith( + expression = "StoreBuilder.from(Fetcher.fromNonFlowingValueFetcher(fetcher))", + imports = ["com.dropbox.android.external.store4.Fetcher"] + )) fun fromNonFlow( fetcher: suspend (key: Key) -> Output - ): StoreBuilder = BuilderImpl { key: Key -> - flow { - emit(fetcher(key)) - } - } + ): StoreBuilder = BuilderImpl(Fetcher.fromNonFlowingValueFetcher(fetcher)) /** * Creates a new [StoreBuilder] from a [Flow] fetcher. @@ -135,9 +136,18 @@ interface StoreBuilder { * @param fetcher a function for fetching a flow of network records. */ @OptIn(ExperimentalTime::class) + @Deprecated(message = "Creating a flow from a function is deprecated, use Fetcher", + replaceWith = ReplaceWith( + expression = "StoreBuilder.from(Fetcher.fromValueFetcher(fetcher))", + imports = ["com.dropbox.android.external.store4.Fetcher"] + )) fun from( fetcher: (key: Key) -> Flow - ): StoreBuilder = BuilderImpl(fetcher) + ): StoreBuilder = BuilderImpl(Fetcher.fromValueFetcher(fetcher)) + + fun from( + fetcher: Fetcher + ) : StoreBuilder = BuilderImpl(fetcher) } } @@ -146,7 +156,7 @@ interface StoreBuilder { @ExperimentalStdlibApi @ExperimentalCoroutinesApi private class BuilderImpl( - private val fetcher: (key: Key) -> Flow + private val fetcher: Fetcher ) : StoreBuilder { private var scope: CoroutineScope? = null private var cachePolicy: MemoryPolicy? = StoreDefaults.memoryPolicy @@ -249,7 +259,7 @@ private class BuilderImpl( @ExperimentalStdlibApi @ExperimentalCoroutinesApi private class BuilderWithSourceOfTruth( - private val fetcher: (key: Key) -> Flow, + private val fetcher: Fetcher, private val sourceOfTruth: SourceOfTruth? = null ) : StoreBuilder { private var scope: CoroutineScope? = null diff --git a/store/src/main/java/com/dropbox/android/external/store4/impl/FetcherController.kt b/store/src/main/java/com/dropbox/android/external/store4/impl/FetcherController.kt index 39044b037..10cad90c1 100644 --- a/store/src/main/java/com/dropbox/android/external/store4/impl/FetcherController.kt +++ b/store/src/main/java/com/dropbox/android/external/store4/impl/FetcherController.kt @@ -15,6 +15,8 @@ */ package com.dropbox.android.external.store4.impl +import com.dropbox.android.external.store4.Fetcher +import com.dropbox.android.external.store4.FetcherResponse import com.dropbox.android.external.store4.ResponseOrigin import com.dropbox.android.external.store4.StoreResponse import com.dropbox.flow.multicast.Multicaster @@ -22,7 +24,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map @@ -46,7 +47,7 @@ internal class FetcherController( /** * The function that provides the actualy fetcher flow when needed */ - private val realFetcher: (Key) -> Flow, + private val realFetcher: Fetcher, /** * [SourceOfTruth] to send the data each time fetcher dispatches a value. Can be `null` if * no [SourceOfTruth] is available. @@ -65,12 +66,18 @@ internal class FetcherController( scope = scope, bufferSize = 0, source = flow { emitAll(realFetcher(key)) }.map { - StoreResponse.Data( - it, - origin = ResponseOrigin.Fetcher - ) as StoreResponse - }.catch { - emit(StoreResponse.Error(it, origin = ResponseOrigin.Fetcher)) + when (it) { + is FetcherResponse.Value -> + StoreResponse.Data( + value = it.value, + origin = ResponseOrigin.Fetcher + ) as StoreResponse + is FetcherResponse.Error -> + StoreResponse.Error( + error = it.error, + origin = ResponseOrigin.Fetcher + ) + } }, piggybackingDownstream = enablePiggyback, onEach = { response -> diff --git a/store/src/main/java/com/dropbox/android/external/store4/impl/RealStore.kt b/store/src/main/java/com/dropbox/android/external/store4/impl/RealStore.kt index ee07cc869..9b6b2370a 100644 --- a/store/src/main/java/com/dropbox/android/external/store4/impl/RealStore.kt +++ b/store/src/main/java/com/dropbox/android/external/store4/impl/RealStore.kt @@ -18,6 +18,7 @@ package com.dropbox.android.external.store4.impl import com.dropbox.android.external.cache4.Cache import com.dropbox.android.external.store4.CacheType import com.dropbox.android.external.store4.ExperimentalStoreApi +import com.dropbox.android.external.store4.Fetcher import com.dropbox.android.external.store4.MemoryPolicy import com.dropbox.android.external.store4.ResponseOrigin import com.dropbox.android.external.store4.Store @@ -44,7 +45,7 @@ import kotlin.time.ExperimentalTime @FlowPreview internal class RealStore( scope: CoroutineScope, - fetcher: (Key) -> Flow, + fetcher: Fetcher, sourceOfTruth: SourceOfTruth? = null, private val memoryPolicy: MemoryPolicy? ) : Store { diff --git a/store/src/test/java/com/dropbox/android/external/store3/StoreTest.kt b/store/src/test/java/com/dropbox/android/external/store3/StoreTest.kt index f1b477651..584a1b595 100644 --- a/store/src/test/java/com/dropbox/android/external/store3/StoreTest.kt +++ b/store/src/test/java/com/dropbox/android/external/store3/StoreTest.kt @@ -1,7 +1,7 @@ package com.dropbox.android.external.store3 import com.dropbox.android.external.cache4.Cache -import com.dropbox.android.external.store4.Fetcher +import com.dropbox.android.external.store4.Store3Fetcher import com.dropbox.android.external.store4.Persister import com.dropbox.android.external.store4.fresh import com.dropbox.android.external.store4.get @@ -38,7 +38,7 @@ class StoreTest( ) { private val testScope = TestCoroutineScope() private val counter = AtomicInteger(0) - private val fetcher: Fetcher = mock() + private val fetcher: Store3Fetcher = mock() private var persister: Persister = mock() private val barCode = BarCode("key", "value") diff --git a/store/src/test/java/com/dropbox/android/external/store3/StoreThrowOnNoItems.kt b/store/src/test/java/com/dropbox/android/external/store3/StoreThrowOnNoItems.kt index 66dc5c0cd..88951950d 100644 --- a/store/src/test/java/com/dropbox/android/external/store3/StoreThrowOnNoItems.kt +++ b/store/src/test/java/com/dropbox/android/external/store3/StoreThrowOnNoItems.kt @@ -1,6 +1,6 @@ package com.dropbox.android.external.store3 -import com.dropbox.android.external.store4.Fetcher +import com.dropbox.android.external.store4.Store3Fetcher import com.dropbox.android.external.store4.Persister import com.dropbox.android.external.store4.get import com.dropbox.android.external.store4.legacy.BarCode @@ -26,7 +26,7 @@ class StoreThrowOnNoItems( ) { private val testScope = TestCoroutineScope() private val counter = AtomicInteger(0) - private val fetcher: Fetcher = mock() + private val fetcher: Store3Fetcher = mock() private var persister: Persister = mock() private val barCode = BarCode("key", "value") diff --git a/store/src/test/java/com/dropbox/android/external/store3/StreamOneKeyTest.kt b/store/src/test/java/com/dropbox/android/external/store3/StreamOneKeyTest.kt index e44af2234..15b34f664 100644 --- a/store/src/test/java/com/dropbox/android/external/store3/StreamOneKeyTest.kt +++ b/store/src/test/java/com/dropbox/android/external/store3/StreamOneKeyTest.kt @@ -1,6 +1,6 @@ package com.dropbox.android.external.store3 -import com.dropbox.android.external.store4.Fetcher +import com.dropbox.android.external.store4.Store3Fetcher import com.dropbox.android.external.store4.Persister import com.dropbox.android.external.store4.StoreRequest import com.dropbox.android.external.store4.fresh @@ -32,7 +32,7 @@ class StreamOneKeyTest( private val storeType: TestStoreType ) { - val fetcher: Fetcher = mock() + val fetcher: Store3Fetcher = mock() val persister: Persister = mock() private val barCode = BarCode("key", "value") private val barCode2 = BarCode("key2", "value2") diff --git a/store/src/test/java/com/dropbox/android/external/store3/TestStoreBuilder.kt b/store/src/test/java/com/dropbox/android/external/store3/TestStoreBuilder.kt index 93e5fee17..2fb0f5d67 100644 --- a/store/src/test/java/com/dropbox/android/external/store3/TestStoreBuilder.kt +++ b/store/src/test/java/com/dropbox/android/external/store3/TestStoreBuilder.kt @@ -17,6 +17,7 @@ package com.dropbox.android.external.store3 import com.dropbox.android.external.store3.util.KeyParser import com.dropbox.android.external.store4.Fetcher +import com.dropbox.android.external.store4.Store3Fetcher import com.dropbox.android.external.store4.MemoryPolicy import com.dropbox.android.external.store4.Persister import com.dropbox.android.external.store4.Store @@ -44,7 +45,7 @@ data class TestStoreBuilder( fun from( scope: CoroutineScope, - fetcher: Fetcher, + fetcher: Store3Fetcher, persister: Persister? = null, inflight: Boolean = true ): TestStoreBuilder = from( @@ -68,7 +69,7 @@ data class TestStoreBuilder( cached = cached, cacheMemoryPolicy = cacheMemoryPolicy, persister = persister, - fetcher = object : Fetcher { + fetcher = object : Store3Fetcher { override suspend fun invoke(key: Key): Output = fetcher(key) } ) @@ -84,21 +85,15 @@ data class TestStoreBuilder( fetchParser: KeyParser? = null, // parser that runs after get from db postParser: KeyParser? = null, - fetcher: Fetcher + fetcher: Store3Fetcher ): TestStoreBuilder { return TestStoreBuilder( buildStore = { - StoreBuilder - .from { key: Key -> - flow { - val value = fetcher.invoke(key = key) - if (fetchParser != null) { - emit(fetchParser.apply(key, value)) - } else { - emit(value) - } - } - } + StoreBuilder.from( + Fetcher.fromNonFlowingValueFetcher {key : Key -> + val value = fetcher.invoke(key = key) + fetchParser?.apply(key, value) ?: value + }) .scope(scope) .let { if (cached) { diff --git a/store/src/test/java/com/dropbox/android/external/store4/FetcherControllerTest.kt b/store/src/test/java/com/dropbox/android/external/store4/FetcherControllerTest.kt index 818400c4f..b4f90cdc6 100644 --- a/store/src/test/java/com/dropbox/android/external/store4/FetcherControllerTest.kt +++ b/store/src/test/java/com/dropbox/android/external/store4/FetcherControllerTest.kt @@ -15,7 +15,6 @@ */ package com.dropbox.android.external.store4 -import com.dropbox.android.external.store4.ResponseOrigin.Fetcher import com.dropbox.android.external.store4.StoreResponse.Data import com.dropbox.android.external.store4.impl.FetcherController import com.google.common.truth.Truth.assertThat @@ -42,7 +41,7 @@ class FetcherControllerTest { fun simple() = testScope.runBlockingTest { val fetcherController = FetcherController( scope = testScope, - realFetcher = { key: Int -> + realFetcher = Fetcher.fromValueFetcher{ key: Int -> flow { emit(key * key) } @@ -57,7 +56,7 @@ class FetcherControllerTest { assertThat(received).isEqualTo( Data( value = 9, - origin = Fetcher + origin = ResponseOrigin.Fetcher ) ) assertThat(fetcherController.fetcherSize()).isEqualTo(0) @@ -68,7 +67,7 @@ class FetcherControllerTest { var createdCnt = 0 val fetcherController = FetcherController( scope = testScope, - realFetcher = { key: Int -> + realFetcher = Fetcher.fromValueFetcher{ key: Int -> createdCnt++ flow { // make sure it takes time, otherwise, we may not share @@ -93,7 +92,7 @@ class FetcherControllerTest { assertThat(it.await()).isEqualTo( Data( value = 9, - origin = Fetcher + origin = ResponseOrigin.Fetcher ) ) } diff --git a/store/src/test/java/com/dropbox/android/external/store4/Fetcher.kt b/store/src/test/java/com/dropbox/android/external/store4/Store3Fetcher.kt similarity index 87% rename from store/src/test/java/com/dropbox/android/external/store4/Fetcher.kt rename to store/src/test/java/com/dropbox/android/external/store4/Store3Fetcher.kt index a21a6b28d..14db3b412 100644 --- a/store/src/test/java/com/dropbox/android/external/store4/Fetcher.kt +++ b/store/src/test/java/com/dropbox/android/external/store4/Store3Fetcher.kt @@ -6,7 +6,8 @@ package com.dropbox.android.external.store4 * @param data type before parsing */ @Deprecated("used in tests") -interface Fetcher { +// TODO cleanup +interface Store3Fetcher { /** * @param key Container with Key and Type used as a request param diff --git a/store/src/test/java/com/dropbox/android/external/store4/impl/FetcherResponseTest.kt b/store/src/test/java/com/dropbox/android/external/store4/impl/FetcherResponseTest.kt new file mode 100644 index 000000000..d5e06a512 --- /dev/null +++ b/store/src/test/java/com/dropbox/android/external/store4/impl/FetcherResponseTest.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dropbox.android.external.store4.impl + +import com.dropbox.android.external.store4.Fetcher +import com.dropbox.android.external.store4.FetcherResponse +import com.dropbox.android.external.store4.ResponseOrigin +import com.dropbox.android.external.store4.StoreBuilder +import com.dropbox.android.external.store4.StoreRequest +import com.dropbox.android.external.store4.StoreResponse +import com.dropbox.android.external.store4.testutil.assertThat +import com.google.common.truth.Truth + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.test.TestCoroutineScope +import kotlinx.coroutines.test.runBlockingTest +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +@OptIn(ExperimentalStdlibApi::class) +class FetcherResponseTest { + private val testScope = TestCoroutineScope() + @Test + fun `given a Fetcher that returns FetcherResponse, its exceptions should not be caught`() { + val store = StoreBuilder.from( + Fetcher.fromNonFlowingFetcher { + throw RuntimeException("don't catch me") + } + ).scope(testScope).build() + + val result = kotlin.runCatching { + testScope.runBlockingTest { + + val result = store.stream(StoreRequest.fresh(1)).toList() + Truth.assertThat(result).isEmpty() + } + } + Truth.assertThat(result.isFailure).isTrue() + Truth.assertThat(result.exceptionOrNull()).hasMessageThat().contains( + "don't catch me" + ) + } + + @Test + fun `given a Fetcher that emits FetcherResponse, it can emit value after an error`() { + val exception = RuntimeException("first error") + testScope.runBlockingTest { + val store = StoreBuilder.from( + object : Fetcher { + override suspend fun invoke(key: Int): Flow> { + return flowOf( + FetcherResponse.Error(exception), + FetcherResponse.Value("$key") + ) + } + } + ).scope(testScope).build() + assertThat(store.stream(StoreRequest.fresh(1))) + .emitsExactly( + StoreResponse.Loading(ResponseOrigin.Fetcher), + StoreResponse.Error(exception, ResponseOrigin.Fetcher), + StoreResponse.Data("1", ResponseOrigin.Fetcher) + ) + } + } +} \ No newline at end of file diff --git a/store/src/test/java/com/dropbox/android/external/store4/impl/FlowStoreTest.kt b/store/src/test/java/com/dropbox/android/external/store4/impl/FlowStoreTest.kt index b3db3847d..9da4eedd9 100644 --- a/store/src/test/java/com/dropbox/android/external/store4/impl/FlowStoreTest.kt +++ b/store/src/test/java/com/dropbox/android/external/store4/impl/FlowStoreTest.kt @@ -15,8 +15,9 @@ */ package com.dropbox.android.external.store4.impl +import com.dropbox.android.external.store4.Fetcher +import com.dropbox.android.external.store4.ResponseOrigin import com.dropbox.android.external.store4.ResponseOrigin.Cache -import com.dropbox.android.external.store4.ResponseOrigin.Fetcher import com.dropbox.android.external.store4.ResponseOrigin.Persister import com.dropbox.android.external.store4.Store import com.dropbox.android.external.store4.StoreBuilder @@ -67,10 +68,10 @@ class FlowStoreTest { assertThat(pipeline.stream(StoreRequest.cached(3, refresh = false))) .emitsExactly( Loading( - origin = Fetcher + origin = ResponseOrigin.Fetcher ), Data( value = "three-1", - origin = Fetcher + origin = ResponseOrigin.Fetcher ) ) assertThat( @@ -84,11 +85,11 @@ class FlowStoreTest { assertThat(pipeline.stream(StoreRequest.fresh(3))) .emitsExactly( Loading( - origin = Fetcher + origin = ResponseOrigin.Fetcher ), Data( value = "three-2", - origin = Fetcher + origin = ResponseOrigin.Fetcher ) ) assertThat( @@ -118,11 +119,11 @@ class FlowStoreTest { assertThat(pipeline.stream(StoreRequest.cached(3, refresh = false))) .emitsExactly( Loading( - origin = Fetcher + origin = ResponseOrigin.Fetcher ), Data( value = "three-1", - origin = Fetcher + origin = ResponseOrigin.Fetcher ) ) assertThat(pipeline.stream(StoreRequest.cached(3, refresh = false))) @@ -142,11 +143,11 @@ class FlowStoreTest { assertThat(pipeline.stream(StoreRequest.fresh(3))) .emitsExactly( Loading( - origin = Fetcher + origin = ResponseOrigin.Fetcher ), Data( value = "three-2", - origin = Fetcher + origin = ResponseOrigin.Fetcher ) ) assertThat(pipeline.stream(StoreRequest.cached(3, refresh = false))) @@ -180,11 +181,11 @@ class FlowStoreTest { assertThat(pipeline.stream(StoreRequest.cached(3, refresh = true))) .emitsExactly( Loading( - origin = Fetcher + origin = ResponseOrigin.Fetcher ), Data( value = "three-1", - origin = Fetcher + origin = ResponseOrigin.Fetcher ) ) @@ -199,11 +200,11 @@ class FlowStoreTest { origin = Persister ), Loading( - origin = Fetcher + origin = ResponseOrigin.Fetcher ), Data( value = "three-2", - origin = Fetcher + origin = ResponseOrigin.Fetcher ) ) } @@ -222,11 +223,11 @@ class FlowStoreTest { assertThat(pipeline.stream(StoreRequest.cached(3, refresh = true))) .emitsExactly( Loading( - origin = Fetcher + origin = ResponseOrigin.Fetcher ), Data( value = "three-1", - origin = Fetcher + origin = ResponseOrigin.Fetcher ) ) @@ -237,11 +238,11 @@ class FlowStoreTest { origin = Cache ), Loading( - origin = Fetcher + origin = ResponseOrigin.Fetcher ), Data( value = "three-2", - origin = Fetcher + origin = ResponseOrigin.Fetcher ) ) } @@ -260,22 +261,22 @@ class FlowStoreTest { assertThat(pipeline.stream(StoreRequest.skipMemory(3, refresh = false))) .emitsExactly( Loading( - origin = Fetcher + origin = ResponseOrigin.Fetcher ), Data( value = "three-1", - origin = Fetcher + origin = ResponseOrigin.Fetcher ) ) assertThat(pipeline.stream(StoreRequest.skipMemory(3, refresh = false))) .emitsExactly( Loading( - origin = Fetcher + origin = ResponseOrigin.Fetcher ), Data( value = "three-2", - origin = Fetcher + origin = ResponseOrigin.Fetcher ) ) } @@ -298,15 +299,15 @@ class FlowStoreTest { assertThat(pipeline.stream(StoreRequest.fresh(3))) .emitsExactly( Loading( - origin = Fetcher + origin = ResponseOrigin.Fetcher ), Data( value = "three-1", - origin = Fetcher + origin = ResponseOrigin.Fetcher ), Data( value = "three-2", - origin = Fetcher + origin = ResponseOrigin.Fetcher ) ) assertThat(pipeline.stream(StoreRequest.cached(3, refresh = true))) @@ -316,15 +317,15 @@ class FlowStoreTest { origin = Persister ), Loading( - origin = Fetcher + origin = ResponseOrigin.Fetcher ), Data( value = "three-1", - origin = Fetcher + origin = ResponseOrigin.Fetcher ), Data( value = "three-2", - origin = Fetcher + origin = ResponseOrigin.Fetcher ) ) } @@ -349,7 +350,7 @@ class FlowStoreTest { assertThat(pipeline.stream(StoreRequest.cached(3, refresh = true))) .emitsExactly( Loading( - origin = Fetcher + origin = ResponseOrigin.Fetcher ), Data( value = "local-1", @@ -383,7 +384,7 @@ class FlowStoreTest { assertThat(pipeline.stream(StoreRequest.cached(3, refresh = true))) .emitsExactly( Loading( - origin = Fetcher + origin = ResponseOrigin.Fetcher ), Data( value = "local-1", @@ -391,7 +392,7 @@ class FlowStoreTest { ), Data( value = "three-1", - origin = Fetcher + origin = ResponseOrigin.Fetcher ), Data( value = "local-2", @@ -399,7 +400,7 @@ class FlowStoreTest { ), Data( value = "three-2", - origin = Fetcher + origin = ResponseOrigin.Fetcher ) ) } @@ -423,11 +424,11 @@ class FlowStoreTest { assertThat(pipeline.stream(StoreRequest.cached(key = 3, refresh = true))) .emitsExactly( Loading( - origin = Fetcher + origin = ResponseOrigin.Fetcher ), StoreResponse.Error( error = exception, - origin = Fetcher + origin = ResponseOrigin.Fetcher ), Data( value = "local-1", @@ -441,11 +442,11 @@ class FlowStoreTest { origin = Persister ), Loading( - origin = Fetcher + origin = ResponseOrigin.Fetcher ), StoreResponse.Error( error = exception, - origin = Fetcher + origin = ResponseOrigin.Fetcher ) ) } @@ -488,7 +489,7 @@ class FlowStoreTest { ), Data( value = "three-2", - origin = Fetcher + origin = ResponseOrigin.Fetcher ) ) collection.cancelAndJoin() @@ -543,7 +544,7 @@ class FlowStoreTest { ), Data( value = "three-2", - origin = Fetcher + origin = ResponseOrigin.Fetcher ) ) collection.cancelAndJoin() @@ -571,29 +572,29 @@ class FlowStoreTest { testScope.advanceUntilIdle() assertThat(fetcher1Collected).isEqualTo( listOf( - Loading(origin = Fetcher), - Data(origin = Fetcher, value = "three-1") + Loading(origin = ResponseOrigin.Fetcher), + Data(origin = ResponseOrigin.Fetcher, value = "three-1") ) ) assertThat(pipeline.stream(StoreRequest.cached(3, refresh = true))) .emitsExactly( Data(origin = Cache, value = "three-1"), - Loading(origin = Fetcher), - Data(origin = Fetcher, value = "three-2") + Loading(origin = ResponseOrigin.Fetcher), + Data(origin = ResponseOrigin.Fetcher, value = "three-2") ) assertThat(pipeline.stream(StoreRequest.cached(3, refresh = true))) .emitsExactly( Data(origin = Cache, value = "three-2"), - Loading(origin = Fetcher), - Data(origin = Fetcher, value = "three-3") + Loading(origin = ResponseOrigin.Fetcher), + Data(origin = ResponseOrigin.Fetcher, value = "three-3") ) testScope.advanceUntilIdle() assertThat(fetcher1Collected).isEqualTo( listOf( - Loading(origin = Fetcher), - Data(origin = Fetcher, value = "three-1"), - Data(origin = Fetcher, value = "three-2"), - Data(origin = Fetcher, value = "three-3") + Loading(origin = ResponseOrigin.Fetcher), + Data(origin = ResponseOrigin.Fetcher, value = "three-1"), + Data(origin = ResponseOrigin.Fetcher, value = "three-2"), + Data(origin = ResponseOrigin.Fetcher, value = "three-3") ) ) fetcher1Job.cancelAndJoin() @@ -619,22 +620,22 @@ class FlowStoreTest { testScope.runCurrent() assertThat(fetcher1Collected).isEqualTo( listOf( - Loading(origin = Fetcher), - Data(origin = Fetcher, value = "three-1") + Loading(origin = ResponseOrigin.Fetcher), + Data(origin = ResponseOrigin.Fetcher, value = "three-1") ) ) assertThat(pipeline.stream(StoreRequest.cached(3, refresh = true))) .emitsExactly( Data(origin = Cache, value = "three-1"), - Loading(origin = Fetcher), - Data(origin = Fetcher, value = "three-2") + Loading(origin = ResponseOrigin.Fetcher), + Data(origin = ResponseOrigin.Fetcher, value = "three-2") ) testScope.runCurrent() assertThat(fetcher1Collected).isEqualTo( listOf( - Loading(origin = Fetcher), - Data(origin = Fetcher, value = "three-1"), - Data(origin = Fetcher, value = "three-2") + Loading(origin = ResponseOrigin.Fetcher), + Data(origin = ResponseOrigin.Fetcher, value = "three-1"), + Data(origin = ResponseOrigin.Fetcher, value = "three-2") ) ) fetcher1Job.cancelAndJoin() @@ -686,12 +687,16 @@ class FlowStoreTest { } return if (nonFlowingFetcher != null) { - StoreBuilder.fromNonFlow( - nonFlowingFetcher + StoreBuilder.from( + Fetcher.fromNonFlowingValueFetcher( + nonFlowingFetcher + ) ) } else { StoreBuilder.from( - flowingFetcher!! + Fetcher.fromValueFetcher( + flowingFetcher!! + ) ) }.scope(testScope) .let {