Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion buildsystem/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ ext.versions = [
navigation : '2.2.1',
constraintLayout : '1.1.3',
rx2 : '2.2.19',
rx3 : '3.0.3',

// Testing.
junit : '4.13',
Expand Down Expand Up @@ -69,14 +70,16 @@ ext.libraries = [
roomRuntime : "androidx.room:room-ktx:$versions.room",
coreKtx : "androidx.core:core-ktx:$versions.coreKtx",
rx2 : "io.reactivex.rxjava2:rxjava:$versions.rx2",
rx3 : "io.reactivex.rxjava3:rxjava:$versions.rx3",

// Testing.
junit : "junit:junit:$versions.junit",
truth : "com.google.truth:truth:$versions.truth",
mockito : "org.mockito:mockito-core:$versions.mockito",
mockitoKotlin : "com.nhaarman.mockitokotlin2:mockito-kotlin:$versions.mockitoKotlin",
coroutinesCore : "org.jetbrains.kotlinx:kotlinx-coroutines-core:$versions.coroutines",
coroutinesRx : "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:$versions.coroutines",
coroutinesRx2 : "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:$versions.coroutines",
coroutinesRx3 : "org.jetbrains.kotlinx:kotlinx-coroutines-rx3:$versions.coroutines",
coroutinesReactive : "org.jetbrains.kotlinx:kotlinx-coroutines-reactive:$versions.coroutines",
coroutinesAndroid : "org.jetbrains.kotlinx:kotlinx-coroutines-android:$versions.coroutines",
coroutinesTest : "org.jetbrains.kotlinx:kotlinx-coroutines-test:$versions.coroutines",
Expand Down
2 changes: 1 addition & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
@@ -1 +1 @@
include ':app', ':store', ':cache', ':filesystem', ':multicast',':store-rx2'
include ':app', ':store', ':cache', ':filesystem', ':multicast',':store-rx2', ':store-rx3'
2 changes: 1 addition & 1 deletion store-rx2/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {
dependencies {
implementation libraries.kotlinStdLib
implementation libraries.coroutinesCore
implementation libraries.coroutinesRx
implementation libraries.coroutinesRx2
implementation libraries.coroutinesReactive
implementation project(path: ':store')
implementation libraries.rx2
Expand Down
1 change: 1 addition & 0 deletions store-rx3/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
26 changes: 26 additions & 0 deletions store-rx3/api/store-rx3.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
public final class com/dropbox/store/rx3/RxFetcherKt {
public static final fun flowableFetcher (Lkotlin/jvm/functions/Function1;)Lkotlin/jvm/functions/Function1;
public static final fun flowableValueFetcher (Lkotlin/jvm/functions/Function1;)Lkotlin/jvm/functions/Function1;
public static final fun singleFetcher (Lkotlin/jvm/functions/Function1;)Lkotlin/jvm/functions/Function1;
public static final fun singleValueFetcher (Lkotlin/jvm/functions/Function1;)Lkotlin/jvm/functions/Function1;
}

public final class com/dropbox/store/rx3/RxSourceOfTruthKt {
public static final fun fromFlowable (Lcom/dropbox/android/external/store4/SourceOfTruth$Companion;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;)Lcom/dropbox/android/external/store4/SourceOfTruth;
public static synthetic fun fromFlowable$default (Lcom/dropbox/android/external/store4/SourceOfTruth$Companion;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lcom/dropbox/android/external/store4/SourceOfTruth;
public static final fun fromMaybe (Lcom/dropbox/android/external/store4/SourceOfTruth$Companion;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;)Lcom/dropbox/android/external/store4/SourceOfTruth;
public static synthetic fun fromMaybe$default (Lcom/dropbox/android/external/store4/SourceOfTruth$Companion;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lcom/dropbox/android/external/store4/SourceOfTruth;
}

public final class com/dropbox/store/rx3/RxStoreBuilderKt {
public static final fun withScheduler (Lcom/dropbox/android/external/store4/StoreBuilder;Lio/reactivex/rxjava3/core/Scheduler;)Lcom/dropbox/android/external/store4/StoreBuilder;
}

public final class com/dropbox/store/rx3/RxStoreKt {
public static final fun freshSingle (Lcom/dropbox/android/external/store4/Store;Ljava/lang/Object;)Lio/reactivex/rxjava3/core/Single;
public static final fun getSingle (Lcom/dropbox/android/external/store4/Store;Ljava/lang/Object;)Lio/reactivex/rxjava3/core/Single;
public static final fun observe (Lcom/dropbox/android/external/store4/Store;Lcom/dropbox/android/external/store4/StoreRequest;)Lio/reactivex/rxjava3/core/Flowable;
public static final fun observeClear (Lcom/dropbox/android/external/store4/Store;Ljava/lang/Object;)Lio/reactivex/rxjava3/core/Completable;
public static final fun observeClearAll (Lcom/dropbox/android/external/store4/Store;)Lio/reactivex/rxjava3/core/Completable;
}

39 changes: 39 additions & 0 deletions store-rx3/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
plugins {
id 'org.jetbrains.kotlin.jvm'
id 'org.jetbrains.dokka'
}

dependencies {
implementation libraries.kotlinStdLib
implementation libraries.coroutinesCore
implementation libraries.coroutinesRx3
implementation libraries.coroutinesReactive
implementation project(path: ':store')
implementation libraries.rx3

testImplementation libraries.junit
testImplementation libraries.truth
testImplementation libraries.mockito
testImplementation libraries.coroutinesTest

}
group = GROUP
version = VERSION_NAME
apply from: rootProject.file("gradle/maven-push.gradle")
apply from: rootProject.file("gradle/jacoco.gradle")
targetCompatibility = 1.8
sourceCompatibility = 1.8

compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs += [
'-Xopt-in=kotlin.RequiresOptIn',
]
}
}
18 changes: 18 additions & 0 deletions store-rx3/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#
# Copyright 2020 Dropbox 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.
#
POM_NAME=com.dropbox.mobile.store
POM_ARTIFACT_ID=store-rx3
POM_PACKAGING=jar
71 changes: 71 additions & 0 deletions store-rx3/src/main/kotlin/com/dropbox/store/rx3/RxFetcher.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.dropbox.store.rx3

import com.dropbox.android.external.store4.Fetcher
import com.dropbox.android.external.store4.FetcherResult
import com.dropbox.android.external.store4.Store
import com.dropbox.android.external.store4.valueFetcher
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.core.Single
import kotlinx.coroutines.reactive.asFlow

/**
* Creates a new [Fetcher] from a [flowableFactory].
*
* [Store] does not catch exception thrown in [flowableFactory] or in the returned [Flowable]. These
* exception will be propagated to the caller.
*
* Use when creating a [Store] that fetches objects in a multiple responses per request
* network protocol (e.g Web Sockets).
*
* @param flowableFactory a factory for a [Flowable] source of network records.
*/
fun <Key : Any, Output : Any> flowableFetcher(
flowableFactory: (key: Key) -> Flowable<FetcherResult<Output>>
): Fetcher<Key, Output> = { key: Key -> flowableFactory(key).asFlow() }

/**
* "Creates" a [Fetcher] from a [singleFactory].
*
* [Store] does not catch exception thrown in [singleFactory] or in the returned [Single]. These
* exception will be propagated to the caller.
*
* Use when creating a [Store] that fetches objects in a single response per request network
* protocol (e.g Http).
*
* @param singleFactory a factory for a [Single] source of network records.
*/
fun <Key : Any, Output : Any> singleFetcher(
singleFactory: (key: Key) -> Single<FetcherResult<Output>>
): Fetcher<Key, Output> = { key: Key -> singleFactory(key).toFlowable().asFlow() }

/**
* "Creates" a [Fetcher] from a [flowableFactory] and translate the results to a [FetcherResult].
*
* Emitted values will be wrapped in [FetcherResult.Data]. if an exception disrupts the stream then
* it will be wrapped in [FetcherResult.Error]. Exceptions thrown in [flowableFactory] itself are
* not caught and will be returned to the caller.
*
* Use when creating a [Store] that fetches objects in a multiple responses per request
* network protocol (e.g Web Sockets).
*
* @param flowFactory a factory for a [Flowable] source of network records.
*/
fun <Key : Any, Output : Any> flowableValueFetcher(
flowableFactory: (key: Key) -> Flowable<Output>
): Fetcher<Key, Output> = valueFetcher { key: Key -> flowableFactory(key).asFlow() }

/**
* Creates a new [Fetcher] from a [singleFactory] and translate the results to a [FetcherResult].
*
* The emitted value will be wrapped in [FetcherResult.Data]. if an exception is returned then
* it will be wrapped in [FetcherResult.Error]. Exceptions thrown in [singleFactory] itself are
* not caught and will be returned to the caller.
*
* Use when creating a [Store] that fetches objects in a single response per request network
* protocol (e.g Http).
*
* @param singleFactory a factory for a [Single] source of network records.
*/
fun <Key : Any, Output : Any> singleValueFetcher(
singleFactory: (key: Key) -> Single<Output>
): Fetcher<Key, Output> = flowableValueFetcher { key: Key -> singleFactory(key).toFlowable() }
62 changes: 62 additions & 0 deletions store-rx3/src/main/kotlin/com/dropbox/store/rx3/RxSourceOfTruth.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.dropbox.store.rx3

import com.dropbox.android.external.store4.SourceOfTruth
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.core.Maybe
import kotlinx.coroutines.reactive.asFlow
import kotlinx.coroutines.rx3.await

/**
* Creates a [Maybe] source of truth that is accessible via [reader], [writer], [delete] and
* [deleteAll].
*
* @param reader function for reading records from the source of truth
* @param writer function for writing updates to the backing source of truth
* @param delete function for deleting records in the source of truth for the given key
* @param deleteAll function for deleting all records in the source of truth
*
*/
fun <Key : Any, Input : Any, Output : Any> SourceOfTruth.Companion.fromMaybe(
reader: (Key) -> Maybe<Output>,
writer: (Key, Input) -> Completable,
delete: ((Key) -> Completable)? = null,
deleteAll: (() -> Completable)? = null
): SourceOfTruth<Key, Input, Output> {
val deleteFun: (suspend (Key) -> Unit)? =
if (delete != null) { key -> delete(key).await() } else null
val deleteAllFun: (suspend () -> Unit)? = deleteAll?.let { { deleteAll().await() } }
return fromNonFlow(
reader = { key -> reader.invoke(key).await() },
writer = { key, output -> writer.invoke(key, output).await() },
delete = deleteFun,
deleteAll = deleteAllFun
)
}

/**
* Creates a ([Flowable]) source of truth that is accessed via [reader], [writer], [delete] and
* [deleteAll].
*
* @param reader function for reading records from the source of truth
* @param writer function for writing updates to the backing source of truth
* @param delete function for deleting records in the source of truth for the given key
* @param deleteAll function for deleting all records in the source of truth
*
*/
fun <Key : Any, Input : Any, Output : Any> SourceOfTruth.Companion.fromFlowable(
reader: (Key) -> Flowable<Output>,
writer: (Key, Input) -> Completable,
delete: ((Key) -> Completable)? = null,
deleteAll: (() -> Completable)? = null
): SourceOfTruth<Key, Input, Output> {
val deleteFun: (suspend (Key) -> Unit)? =
if (delete != null) { key -> delete(key).await() } else null
val deleteAllFun: (suspend () -> Unit)? = deleteAll?.let { { deleteAll().await() } }
return from(
reader = { key -> reader.invoke(key).asFlow() },
writer = { key, output -> writer.invoke(key, output).await() },
delete = deleteFun,
deleteAll = deleteAllFun
)
}
52 changes: 52 additions & 0 deletions store-rx3/src/main/kotlin/com/dropbox/store/rx3/RxStore.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.dropbox.store.rx3

import com.dropbox.android.external.store4.ExperimentalStoreApi
import com.dropbox.android.external.store4.Store
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.fresh
import com.dropbox.android.external.store4.get
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.core.Flowable
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.rx3.asFlowable
import kotlinx.coroutines.rx3.rxCompletable
import kotlinx.coroutines.rx3.rxSingle

/**
* Return a [Flowable] for the given key
* @param request - see [StoreRequest] for configurations
*/
@ExperimentalCoroutinesApi
fun <Key : Any, Output : Any> Store<Key, Output>.observe(request: StoreRequest<Key>): Flowable<StoreResponse<Output>> =
stream(request).asFlowable()

/**
* Purge a particular entry from memory and disk cache.
* Persistent storage will only be cleared if a delete function was passed to
* [StoreBuilder.persister] or [StoreBuilder.nonFlowingPersister] when creating the [Store].
*/
fun <Key : Any, Output : Any> Store<Key, Output>.observeClear(key: Key): Completable =
rxCompletable { clear(key) }

/**
* Purge all entries from memory and disk cache.
* Persistent storage will only be cleared if a deleteAll function was passed to
* [StoreBuilder.persister] or [StoreBuilder.nonFlowingPersister] when creating the [Store].
*/
@ExperimentalStoreApi
fun <Key : Any, Output : Any> Store<Key, Output>.observeClearAll(): Completable =
rxCompletable { clearAll() }

/**
* Helper factory that will return data as a [Single] for [key] if it is cached otherwise will return fresh/network data (updating your caches)
*/
fun <Key : Any, Output : Any> Store<Key, Output>.getSingle(key: Key) =
rxSingle { [email protected](key) }

/**
* Helper factory that will return fresh data as a [Single] for [key] while updating your caches
*/
fun <Key : Any, Output : Any> Store<Key, Output>.freshSingle(key: Key) =
rxSingle { [email protected](key) }
21 changes: 21 additions & 0 deletions store-rx3/src/main/kotlin/com/dropbox/store/rx3/RxStoreBuilder.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.dropbox.store.rx3

import com.dropbox.android.external.store4.StoreBuilder
import io.reactivex.rxjava3.core.Scheduler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.rx3.asCoroutineDispatcher

/**
* Define what scheduler fetcher requests will be called on,
* if a scheduler is not set Store will use [GlobalScope]
*/
@FlowPreview
@ExperimentalCoroutinesApi
fun <Key : Any, Output : Any> StoreBuilder<Key, Output>.withScheduler(
scheduler: Scheduler
): StoreBuilder<Key, Output> {
return scope(CoroutineScope(scheduler.asCoroutineDispatcher()))
}
Loading