diff --git a/build.gradle b/build.gradle index bb3d0a3..1a346b0 100644 --- a/build.gradle +++ b/build.gradle @@ -44,8 +44,6 @@ buildscript { classpath 'com.android.tools.build:gradle:3.2.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version" -// classpath 'com.google.gms:google-services:3.1.0' - classpath 'io.realm:realm-gradle-plugin:4.2.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' diff --git a/sampleApp/build.gradle b/sampleApp/build.gradle index bf22254..b02c17b 100644 --- a/sampleApp/build.gradle +++ b/sampleApp/build.gradle @@ -1,7 +1,7 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' -apply plugin: 'realm-android' +apply plugin: 'kotlin-kapt' android { compileSdkVersion 28 @@ -97,6 +97,10 @@ dependencies { implementation "org.koin:koin-android-architecture:0.9.3" implementation "android.arch.lifecycle:reactivestreams:1.1.1" + implementation "android.arch.persistence.room:runtime:$room_version" + implementation "android.arch.persistence.room:rxjava2:$room_version" + kapt "android.arch.persistence.room:compiler:$room_version" + // Support implementation "com.android.support:appcompat-v7:$supportLibrary" implementation "com.android.support:support-v4:$supportLibrary" diff --git a/sampleApp/src/main/java/com/zeyad/usecases/app/AppDatabase.kt b/sampleApp/src/main/java/com/zeyad/usecases/app/AppDatabase.kt new file mode 100644 index 0000000..970e1a6 --- /dev/null +++ b/sampleApp/src/main/java/com/zeyad/usecases/app/AppDatabase.kt @@ -0,0 +1,41 @@ +package com.zeyad.usecases.app + +import android.arch.persistence.room.Database +import android.arch.persistence.room.Room +import android.arch.persistence.room.RoomDatabase +import android.content.Context +import com.zeyad.usecases.app.screens.user.User +import com.zeyad.usecases.app.screens.user.detail.Repository + +@Database(entities = [User::class, Repository::class], version = 1) +abstract class AppDatabase : RoomDatabase() { + + abstract fun userDao(): UserDao + abstract fun repoDao(): RepoDao + + companion object { + + @Volatile + private var instance: AppDatabase? = null + + fun getInstance(context: Context): AppDatabase { + return instance ?: synchronized(this) { + instance ?: buildDatabase(context).also { instance = it } + } + } + + // Create and pre-populate the database. See this article for more details: + // https://medium.com/google-developers/7-pro-tips-for-room-fbadea4bfbd1#4785 + private fun buildDatabase(context: Context): AppDatabase { + return Room.databaseBuilder(context, AppDatabase::class.java, "app.room") +// .addCallback(object : RoomDatabase.Callback() { +// override fun onCreate(db: SupportSQLiteDatabase) { +// super.onCreate(db) +// val request = OneTimeWorkRequestBuilder().build() +// WorkManager.getInstance().enqueue(request) +// } +// }) + .build() + } + } +} \ No newline at end of file diff --git a/sampleApp/src/main/java/com/zeyad/usecases/app/Daos.kt b/sampleApp/src/main/java/com/zeyad/usecases/app/Daos.kt new file mode 100644 index 0000000..a517144 --- /dev/null +++ b/sampleApp/src/main/java/com/zeyad/usecases/app/Daos.kt @@ -0,0 +1,12 @@ +package com.zeyad.usecases.app + +import android.arch.persistence.room.Dao +import com.zeyad.usecases.app.screens.user.User +import com.zeyad.usecases.app.screens.user.detail.Repository +import com.zeyad.usecases.db.BaseDao + +@Dao +interface UserDao : BaseDao + +@Dao +interface RepoDao : BaseDao \ No newline at end of file diff --git a/sampleApp/src/main/java/com/zeyad/usecases/app/GenericApplication.kt b/sampleApp/src/main/java/com/zeyad/usecases/app/GenericApplication.kt index 6ba12e8..b18354b 100644 --- a/sampleApp/src/main/java/com/zeyad/usecases/app/GenericApplication.kt +++ b/sampleApp/src/main/java/com/zeyad/usecases/app/GenericApplication.kt @@ -21,9 +21,6 @@ import io.reactivex.disposables.Disposable import io.reactivex.functions.Action import io.reactivex.functions.Consumer import io.reactivex.schedulers.Schedulers -import io.realm.Realm -import io.realm.RealmConfiguration -import io.realm.rx.RealmObservableFactory import okhttp3.CertificatePinner import okhttp3.ConnectionSpec import okhttp3.OkHttpClient @@ -124,13 +121,13 @@ open class GenericApplication : Application() { } private fun initializeRealm() { - Realm.init(this) - Realm.setDefaultConfiguration(RealmConfiguration.Builder() - .name("app.realm") - .modules(Realm.getDefaultModule(), LibraryModule()) - .rxFactory(RealmObservableFactory()) - .deleteRealmIfMigrationNeeded() - .build()) +// Realm.init(this) +// Realm.setDefaultConfiguration(RealmConfiguration.Builder() +// .name("app.realm") +// .modules(Realm.getDefaultModule(), LibraryModule()) +// .rxFactory(RealmObservableFactory()) +// .deleteRealmIfMigrationNeeded() +// .build()) } private fun checkAppTampering(context: Context): Boolean { diff --git a/sampleApp/src/main/java/com/zeyad/usecases/app/LibraryModule.kt b/sampleApp/src/main/java/com/zeyad/usecases/app/LibraryModule.kt deleted file mode 100644 index 258c06e..0000000 --- a/sampleApp/src/main/java/com/zeyad/usecases/app/LibraryModule.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.zeyad.usecases.app - -import io.realm.annotations.RealmModule - -@RealmModule(library = true, allClasses = true) -internal class LibraryModule diff --git a/sampleApp/src/main/java/com/zeyad/usecases/app/di/myModule.kt b/sampleApp/src/main/java/com/zeyad/usecases/app/di/myModule.kt index ac56026..dfe7c0c 100644 --- a/sampleApp/src/main/java/com/zeyad/usecases/app/di/myModule.kt +++ b/sampleApp/src/main/java/com/zeyad/usecases/app/di/myModule.kt @@ -1,15 +1,22 @@ package com.zeyad.usecases.app.di import android.content.Context -import android.os.HandlerThread import android.util.Log import com.zeyad.usecases.api.DataServiceConfig import com.zeyad.usecases.api.DataServiceFactory import com.zeyad.usecases.api.IDataService +import com.zeyad.usecases.app.AppDatabase import com.zeyad.usecases.app.BuildConfig +import com.zeyad.usecases.app.screens.user.User +import com.zeyad.usecases.app.screens.user.detail.Repository import com.zeyad.usecases.app.screens.user.detail.UserDetailVM import com.zeyad.usecases.app.screens.user.list.UserListVM import com.zeyad.usecases.app.utils.Constants.URLS.API_BASE_URL +import com.zeyad.usecases.db.BaseDao +import com.zeyad.usecases.db.DaoResolver +import com.zeyad.usecases.db.DataBaseManager +import com.zeyad.usecases.db.RoomManager +import com.zeyad.usecases.utils.DataBaseManagerUtil import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import org.koin.android.architecture.ext.viewModel @@ -21,16 +28,32 @@ val myModule: Module = applicationContext { viewModel { UserListVM(get()) } viewModel { UserDetailVM(get()) } - bean { createDataService(get()) } + bean { createDataService(get(), get()) } + + bean { createDataBase(get()) } } -fun createDataService(context: Context): IDataService { - DataServiceFactory(DataServiceConfig.Builder(context) +fun createDataBase(context: Context): AppDatabase = AppDatabase.getInstance(context) + +fun createDataService(context: Context, db: AppDatabase): IDataService { + return DataServiceFactory(DataServiceConfig.Builder(context) .baseUrl(API_BASE_URL) .okHttpBuilder(getOkHttpBuilder()) - .withRealm(HandlerThread("BackgroundHandlerThread")) + .withSQLite(object : DataBaseManagerUtil { + override fun getDataBaseManager(dataClass: Class<*>): DataBaseManager? { + return RoomManager(db, object : DaoResolver { + override fun getDao(dataClass: Class): BaseDao { + return when (dataClass) { + User::class.java -> db.userDao() as BaseDao + Repository::class.java -> db.repoDao() as BaseDao + else -> throw IllegalArgumentException("") + } + } + }) + } + }) .build()) - return DataServiceFactory.dataService!! + .getInstance() } fun getOkHttpBuilder(): OkHttpClient.Builder { diff --git a/sampleApp/src/main/java/com/zeyad/usecases/app/screens/user/User.kt b/sampleApp/src/main/java/com/zeyad/usecases/app/screens/user/User.kt index 2302c0b..695911c 100644 --- a/sampleApp/src/main/java/com/zeyad/usecases/app/screens/user/User.kt +++ b/sampleApp/src/main/java/com/zeyad/usecases/app/screens/user/User.kt @@ -1,26 +1,33 @@ package com.zeyad.usecases.app.screens.user +import android.arch.persistence.room.ColumnInfo +import android.arch.persistence.room.Entity +import android.arch.persistence.room.PrimaryKey import android.os.Parcelable import com.google.gson.annotations.SerializedName -import io.realm.RealmObject -import io.realm.annotations.PrimaryKey +import com.zeyad.usecases.app.R.id.id import kotlinx.android.parcel.Parcelize /** * @author zeyad on 1/10/17. */ +@Entity @Parcelize -open class User(@PrimaryKey - @SerializedName(LOGIN) +open class User(@SerializedName(LOGIN) var login: String = "", @SerializedName(ID) - var id: Int = 0, + @PrimaryKey var id: Int = 0, @SerializedName(AVATAR_URL) - var avatarUrl: String = "") : RealmObject(), Parcelable { + @ColumnInfo(name = AVATAR_URL) + var avatarUrl: String = "") : Parcelable { companion object { const val LOGIN = "login" private const val ID = "id" private const val AVATAR_URL = "avatar_url" + + fun isEmpty(user: User): Boolean { + return user.login.isEmpty() && id <= 0 && user.avatarUrl.isEmpty() + } } } diff --git a/sampleApp/src/main/java/com/zeyad/usecases/app/screens/user/detail/Repository.kt b/sampleApp/src/main/java/com/zeyad/usecases/app/screens/user/detail/Repository.kt index 5dae01d..050d505 100644 --- a/sampleApp/src/main/java/com/zeyad/usecases/app/screens/user/detail/Repository.kt +++ b/sampleApp/src/main/java/com/zeyad/usecases/app/screens/user/detail/Repository.kt @@ -1,28 +1,25 @@ package com.zeyad.usecases.app.screens.user.detail +import android.arch.persistence.room.ColumnInfo +import android.arch.persistence.room.Entity +import android.arch.persistence.room.PrimaryKey import android.os.Parcelable import com.google.gson.annotations.SerializedName -import com.zeyad.usecases.app.screens.user.User -import io.realm.RealmObject import kotlinx.android.parcel.Parcelize /** * @author zeyad on 1/25/17. */ +@Entity @Parcelize open class Repository(@SerializedName("id") - var id: Int = 0, + @PrimaryKey var id: Int = 0, @SerializedName("name") - var name: String? = null, + var name: String = "", @SerializedName("full_name") - var fullName: String? = null, - @SerializedName("owner") - internal var owner: User? = null) : RealmObject(), Parcelable { - - companion object { - - fun isEmpty(repository: Repository?): Boolean { - return repository == null || repository.name == null && repository.fullName == null && repository.owner == null - } - } -} + @ColumnInfo(name = "full_name") + var fullName: String = "" +// , +// @SerializedName("owner") +// var owner: User = User()) : Parcelable +) : Parcelable diff --git a/sampleApp/src/main/java/com/zeyad/usecases/app/screens/user/list/UserListVM.kt b/sampleApp/src/main/java/com/zeyad/usecases/app/screens/user/list/UserListVM.kt index 18aec6a..c2b4b84 100644 --- a/sampleApp/src/main/java/com/zeyad/usecases/app/screens/user/list/UserListVM.kt +++ b/sampleApp/src/main/java/com/zeyad/usecases/app/screens/user/list/UserListVM.kt @@ -37,7 +37,7 @@ class UserListVM(private var dataUseCase: IDataService) : BaseViewModel, currentStateBundle: UserListState?): UserListState { var users: MutableList users = if (currentStateBundle?.users == null) - ArrayList() + ArrayList() else Observable.fromIterable(currentStateBundle.users) .map { it.getData() } @@ -49,8 +49,6 @@ class UserListVM(private var dataUseCase: IDataService) : BaseViewModel users = Observable.fromIterable(users) .filter { user -> !(newResult as List<*>).contains(user.login) } .distinct().toList().blockingGet() - else -> { - } } return UserListState.builder() .users(users) @@ -98,5 +96,6 @@ class UserListVM(private var dataUseCase: IDataService) : BaseViewModel convertToListOfId(jsonArray: JSONArray?, idType: Class): List { +fun convertToListOfId(jsonArray: JSONArray, idType: Class): List { var idList: MutableList = ArrayList() - if (jsonArray != null && jsonArray.length() > 0) { + if (jsonArray.length() > 0) { val length = jsonArray.length() idList = ArrayList(length) for (i in 0 until length) { try { - idList.add(idType.cast(jsonArray.get(i))) + idList.add(idType.cast(jsonArray.get(i))!!) } catch (e: JSONException) { Log.e("Utils", "convertToListOfId", e) } - } } return idList } -fun convertToStringListOfId(jsonArray: JSONArray?): List { - var idList: MutableList = ArrayList() +fun convertToStringListOfId(jsonArray: JSONArray?): List { + var idList: MutableList = ArrayList() if (jsonArray != null && jsonArray.length() > 0) { idList = ArrayList(jsonArray.length()) val length = jsonArray.length() for (i in 0 until length) { try { - idList.add(jsonArray.get(i).toString()) + idList.add(jsonArray.get(i).toString().toLong()) } catch (e: JSONException) { Log.e("Utils", "convertToListOfId", e) } @@ -55,6 +50,8 @@ fun convertToStringListOfId(jsonArray: JSONArray?): List { return idList } +fun isNetworkNotAvailable(context: Context): Boolean = !isNetworkAvailable(context) + fun isNetworkAvailable(context: Context): Boolean { val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { diff --git a/usecases/src/main/java/com/zeyad/usecases/RequiredModel.kt b/usecases/src/main/java/com/zeyad/usecases/RequiredModel.kt deleted file mode 100644 index 6c95080..0000000 --- a/usecases/src/main/java/com/zeyad/usecases/RequiredModel.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.zeyad.usecases - -import io.realm.RealmObject - -/** - * DO NOT DELETE! Essential for compilation! - */ -open class RequiredModel : RealmObject() { - internal var value: Boolean = false -} diff --git a/usecases/src/main/java/com/zeyad/usecases/api/DataService.kt b/usecases/src/main/java/com/zeyad/usecases/api/DataService.kt index 8a9cc81..6006c76 100644 --- a/usecases/src/main/java/com/zeyad/usecases/api/DataService.kt +++ b/usecases/src/main/java/com/zeyad/usecases/api/DataService.kt @@ -2,18 +2,13 @@ package com.zeyad.usecases.api import android.util.Log import com.jakewharton.rx.ReplayingShare -import com.zeyad.usecases.db.RealmQueryProvider import com.zeyad.usecases.requests.FileIORequest import com.zeyad.usecases.requests.GetRequest import com.zeyad.usecases.requests.PostRequest import com.zeyad.usecases.stores.DataStoreFactory import com.zeyad.usecases.withCache import com.zeyad.usecases.withDisk -import io.reactivex.Flowable -import io.reactivex.FlowableTransformer -import io.reactivex.Scheduler -import io.reactivex.Single -import io.realm.RealmModel +import io.reactivex.* import org.json.JSONException import java.io.File @@ -60,8 +55,8 @@ internal class DataService(private val mDataStoreFactory: DataStoreFactory, val url = getRequest.fullUrl val simpleName = dataClass.simpleName val dynamicGetObject = mDataStoreFactory.dynamically(url, dataClass) - .dynamicGetObject(url, getRequest.idColumnName, itemId, getRequest.idType, - dataClass, getRequest.persist, shouldCache) + .dynamicGetObject(url, getRequest.idColumnName, itemId, dataClass, + getRequest.persist, shouldCache) result = if (withCache(shouldCache)) { mDataStoreFactory.memory()!! .getItem(itemId.toString(), dataClass) @@ -79,102 +74,98 @@ internal class DataService(private val mDataStoreFactory: DataStoreFactory, return result.compose(applySchedulers()) } - override fun patchObject(postRequest: PostRequest): Flowable { - val result: Flowable = try { + override fun patchObject(postRequest: PostRequest): Single { + val result: Single = try { mDataStoreFactory.dynamically(postRequest.fullUrl, postRequest.requestType) .dynamicPatchObject(postRequest.fullUrl, postRequest.idColumnName, - postRequest.idType, postRequest.getObjectBundle(), - postRequest.requestType, postRequest.getTypedResponseClass(), - postRequest.persist, postRequest.cache, postRequest.queuable) + postRequest.getObjectBundle(), postRequest.requestType, + postRequest.getTypedResponseClass(), postRequest.persist, + postRequest.cache) } catch (e: IllegalAccessException) { - Flowable.error(e) + Single.error(e) } catch (e: JSONException) { - Flowable.error(e) + Single.error(e) } - return result.compose(applySchedulers()) + return result.compose(applySingleSchedulers()) } - override fun postObject(postRequest: PostRequest): Flowable { - val result: Flowable = try { + override fun postObject(postRequest: PostRequest): Single { + val result: Single = try { mDataStoreFactory.dynamically(postRequest.fullUrl, postRequest.requestType) .dynamicPostObject(postRequest.fullUrl, postRequest.idColumnName, - postRequest.idType, postRequest.getObjectBundle(), - postRequest.requestType, postRequest.getTypedResponseClass(), - postRequest.persist, postRequest.cache, postRequest.queuable) + postRequest.getObjectBundle(), postRequest.requestType, + postRequest.getTypedResponseClass(), postRequest.persist, postRequest.cache) } catch (e: IllegalAccessException) { - Flowable.error(e) + Single.error(e) } catch (e: JSONException) { - Flowable.error(e) + Single.error(e) } - return result.compose(applySchedulers()) + return result.compose(applySingleSchedulers()) } - override fun postList(postRequest: PostRequest): Flowable { - val result: Flowable = try { + override fun postList(postRequest: PostRequest): Single { + val result: Single = try { mDataStoreFactory.dynamically(postRequest.fullUrl, postRequest.requestType) .dynamicPostList(postRequest.fullUrl, postRequest.idColumnName, - postRequest.idType, postRequest.getArrayBundle(), - postRequest.requestType, postRequest.getTypedResponseClass(), - postRequest.persist, postRequest.cache, postRequest.queuable) + postRequest.getArrayBundle(), postRequest.requestType, + postRequest.getTypedResponseClass(), postRequest.persist, postRequest.cache) } catch (e: Exception) { - Flowable.error(e) + Single.error(e) } catch (e: JSONException) { - Flowable.error(e) + Single.error(e) } - return result.compose(applySchedulers()) + return result.compose(applySingleSchedulers()) } - override fun putObject(postRequest: PostRequest): Flowable { - val result: Flowable = try { + override fun putObject(postRequest: PostRequest): Single { + val result: Single = try { mDataStoreFactory.dynamically(postRequest.fullUrl, postRequest.requestType) .dynamicPutObject(postRequest.fullUrl, postRequest.idColumnName, - postRequest.idType, postRequest.getObjectBundle(), - postRequest.requestType, postRequest.getTypedResponseClass(), - postRequest.persist, postRequest.cache, postRequest.queuable) + postRequest.getObjectBundle(), postRequest.requestType, + postRequest.getTypedResponseClass(), postRequest.persist, postRequest.cache) } catch (e: IllegalAccessException) { - Flowable.error(e) + Single.error(e) } catch (e: JSONException) { - Flowable.error(e) + Single.error(e) } - return result.compose(applySchedulers()) + return result.compose(applySingleSchedulers()) } - override fun putList(postRequest: PostRequest): Flowable { - val result: Flowable = try { + override fun putList(postRequest: PostRequest): Single { + val result: Single = try { mDataStoreFactory.dynamically(postRequest.fullUrl, postRequest.requestType) .dynamicPutList(postRequest.fullUrl, postRequest.idColumnName, - postRequest.idType, postRequest.getArrayBundle(), - postRequest.requestType, postRequest.getTypedResponseClass(), - postRequest.persist, postRequest.cache, postRequest.queuable) + postRequest.getArrayBundle(), postRequest.requestType, + postRequest.getTypedResponseClass(), postRequest.persist, postRequest.cache) } catch (e: IllegalAccessException) { - Flowable.error(e) + Single.error(e) } catch (e: JSONException) { - Flowable.error(e) + Single.error(e) } - return result.compose(applySchedulers()) + return result.compose(applySingleSchedulers()) } - override fun deleteItemById(request: PostRequest): Flowable { + override fun deleteItemById(request: PostRequest): Single { return deleteCollectionByIds(request) } - override fun deleteCollectionByIds(deleteRequest: PostRequest): Flowable { - val result: Flowable = try { + override fun deleteCollectionByIds(deleteRequest: PostRequest): Single { + val result: Single = try { mDataStoreFactory.dynamically(deleteRequest.fullUrl, deleteRequest.requestType) .dynamicDeleteCollection(deleteRequest.fullUrl, deleteRequest.idColumnName, deleteRequest.idType, deleteRequest.getArrayBundle(), deleteRequest.requestType, deleteRequest.getTypedResponseClass(), - deleteRequest.persist, deleteRequest.cache, deleteRequest.queuable) - .compose(applySchedulers()) + deleteRequest.persist, deleteRequest.cache) + .compose(applySingleSchedulers()) } catch (e: IllegalAccessException) { - Flowable.error(e) + Single.error(e) } catch (e: JSONException) { - Flowable.error(e) + Single.error(e) } - return result.compose(applySchedulers()) + return result.compose(applySingleSchedulers()) } override fun deleteAll(deleteRequest: PostRequest): Single { @@ -193,14 +184,14 @@ internal class DataService(private val mDataStoreFactory: DataStoreFactory, } } - override fun queryDisk(realmQueryProvider: RealmQueryProvider): Flowable> { - val result: Flowable> = try { - mDataStoreFactory.disk(Any::class.java).queryDisk(realmQueryProvider) - .compose(ReplayingShare.instance()) + override fun queryDisk(query: String, clazz: Class): Single { + val result: Single = try { + mDataStoreFactory.disk(Any::class.java).queryDisk(query, clazz) + .compose(ReplayingShare.instance()).singleOrError() } catch (e: IllegalAccessException) { - Flowable.error(e) + Single.error(e) } - return result.compose(applySchedulers()) + return result.compose(applySingleSchedulers()) } override fun getListOffLineFirst(getRequest: GetRequest): Flowable> { @@ -245,7 +236,6 @@ internal class DataService(private val mDataStoreFactory: DataStoreFactory, try { val itemId = getRequest.itemId val dataClass = getRequest.getTypedDataClass() - val idType = getRequest.idType val idColumnName = getRequest.idColumnName val simpleName = dataClass.simpleName val persist = getRequest.persist @@ -253,11 +243,11 @@ internal class DataService(private val mDataStoreFactory: DataStoreFactory, val withDisk = withDisk(persist) val withCache = withCache(shouldCache) val cloud = mDataStoreFactory.cloud(dataClass) - .dynamicGetObject(getRequest.fullUrl, idColumnName, itemId, idType, dataClass, + .dynamicGetObject(getRequest.fullUrl, idColumnName, itemId, dataClass, persist, shouldCache) .doOnNext { Log.d(GET_OBJECT_OFFLINE_FIRST, "Cloud Hit $simpleName") } val disk = mDataStoreFactory.disk(dataClass) - .dynamicGetObject("", idColumnName, itemId, idType, dataClass, persist, shouldCache) + .dynamicGetObject("", idColumnName, itemId, dataClass, persist, shouldCache) .doOnNext { Log.d(GET_OBJECT_OFFLINE_FIRST, "Disk Hit $simpleName") } .doOnError { throwable -> Log.e(GET_OBJECT_OFFLINE_FIRST, "Disk Miss $simpleName", @@ -281,26 +271,22 @@ internal class DataService(private val mDataStoreFactory: DataStoreFactory, return result.compose(ReplayingShare.instance()).compose(applySchedulers()) } - override fun uploadFile(fileIORequest: FileIORequest): Flowable { + override fun uploadFile(fileIORequest: FileIORequest): Single { return fileIORequest.dataClass?.let { fileIORequest.keyFileMap?.let { it1 -> mDataStoreFactory.cloud(it) - .dynamicUploadFile(fileIORequest.url, it1, - fileIORequest.parameters, fileIORequest.onWifi, - fileIORequest.whileCharging, fileIORequest.queuable, + .dynamicUploadFile(fileIORequest.url, it1, fileIORequest.parameters, fileIORequest.getTypedResponseClass()) - .compose(applySchedulers()) + .compose(applySingleSchedulers()) } }!! } - override fun downloadFile(fileIORequest: FileIORequest): Flowable { + override fun downloadFile(fileIORequest: FileIORequest): Single { return fileIORequest.dataClass?.let { fileIORequest.file?.let { it1 -> mDataStoreFactory.cloud(it) - .dynamicDownloadFile(fileIORequest.url, it1, - fileIORequest.onWifi, fileIORequest.whileCharging, - fileIORequest.queuable).compose(applySchedulers()) + .dynamicDownloadFile(fileIORequest.url, it1).compose(applySingleSchedulers()) } }!! } @@ -322,6 +308,17 @@ internal class DataService(private val mDataStoreFactory: DataStoreFactory, } } + private fun applySingleSchedulers(): SingleTransformer { + return if (mPostThreadExist) + SingleTransformer { + it.subscribeOn(mBackgroundThread).observeOn(mPostExecutionThread!!).unsubscribeOn(mBackgroundThread) + } + else + SingleTransformer { + it.subscribeOn(mBackgroundThread).unsubscribeOn(mBackgroundThread) + } + } + companion object { private const val CACHE_HIT = "cache Hit " diff --git a/usecases/src/main/java/com/zeyad/usecases/api/DataServiceConfig.kt b/usecases/src/main/java/com/zeyad/usecases/api/DataServiceConfig.kt index 2bc8e27..7d74696 100644 --- a/usecases/src/main/java/com/zeyad/usecases/api/DataServiceConfig.kt +++ b/usecases/src/main/java/com/zeyad/usecases/api/DataServiceConfig.kt @@ -1,7 +1,7 @@ package com.zeyad.usecases.api +import android.arch.persistence.room.RoomDatabase import android.content.Context -import android.os.HandlerThread import com.zeyad.usecases.mapper.DAOMapper import com.zeyad.usecases.utils.DataBaseManagerUtil import io.reactivex.Scheduler @@ -10,24 +10,22 @@ import okhttp3.Cache import okhttp3.OkHttpClient import java.util.concurrent.TimeUnit - /** * @author by ZIaDo on 12/9/16. */ -data class DataServiceConfig private constructor(val context: Context, - val okHttpBuilder: OkHttpClient.Builder?, - val okHttpCache: Cache? = null, - val baseUrl: String = "", - val isWithCache: Boolean = false, - val cacheSize: Int = 8192, - val cacheDuration: Long = 3, - val timeUnit: TimeUnit = TimeUnit.SECONDS, - val withSQL: Boolean = false, - val withRealm: Boolean = false, - val postExecutionThread: Scheduler = Schedulers.io(), - val handlerThread: HandlerThread = HandlerThread("backgroundHandler"), - val dataBaseManagerUtil: DataBaseManagerUtil? = null, - val entityMapper: DAOMapper = DAOMapper()) { +data class DataServiceConfig internal constructor(val context: Context, + val okHttpBuilder: OkHttpClient.Builder?, + val okHttpCache: Cache? = null, + val baseUrl: String = "", + val isWithCache: Boolean = false, + val cacheSize: Int = 8192, + val cacheDuration: Long = 3, + val timeUnit: TimeUnit = TimeUnit.SECONDS, + val withSQL: Boolean = false, + val postExecutionThread: Scheduler = Schedulers.io(), + val dataBaseManagerUtil: DataBaseManagerUtil? = null, + val db: RoomDatabase? = null, + val entityMapper: DAOMapper = DAOMapper()) { constructor(dataUseCaseConfigBuilder: Builder) : this( dataUseCaseConfigBuilder.context, @@ -38,11 +36,10 @@ data class DataServiceConfig private constructor(val context: Context, dataUseCaseConfigBuilder.cacheSize, dataUseCaseConfigBuilder.cacheDuration, dataUseCaseConfigBuilder.timeUnit, - dataUseCaseConfigBuilder.withSQL, - dataUseCaseConfigBuilder.withRealm, + dataUseCaseConfigBuilder.withSQLite, dataUseCaseConfigBuilder.postExecutionThread, - dataUseCaseConfigBuilder.handlerThread, - dataUseCaseConfigBuilder.dataBaseManagerUtil + dataUseCaseConfigBuilder.dataBaseManagerUtil, + dataUseCaseConfigBuilder.db ) class Builder(internal val context: Context) { @@ -50,14 +47,13 @@ data class DataServiceConfig private constructor(val context: Context, internal var okHttpCache: Cache? = null internal var baseUrl: String = "" internal var withCache: Boolean = false - internal var withRealm: Boolean = false - internal var withSQL: Boolean = false + internal var withSQLite: Boolean = false internal var cacheSize: Int = 0 internal var cacheDuration: Long = 0 internal var timeUnit: TimeUnit = TimeUnit.SECONDS - internal var handlerThread: HandlerThread = HandlerThread("backgroundHandler") internal var postExecutionThread: Scheduler = Schedulers.io() internal var dataBaseManagerUtil: DataBaseManagerUtil? = null + internal var db: RoomDatabase? = null fun postExecutionThread(postExecutionThread: Scheduler): Builder { this.postExecutionThread = postExecutionThread @@ -74,24 +70,11 @@ data class DataServiceConfig private constructor(val context: Context, return this } - fun okhttpCache(cache: Cache): Builder { + fun okHttpCache(cache: Cache): Builder { this.okHttpCache = cache return this } - fun withRealm(handlerThread: HandlerThread): Builder { - this.withRealm = true - this.dataBaseManagerUtil = null - this.handlerThread = handlerThread - return this - } - - fun withRealm(): Builder { - this.withRealm = true - this.dataBaseManagerUtil = null - return this - } - fun withCache(expiryAmount: Long, timeUnit: TimeUnit): Builder { this.withCache = true this.cacheDuration = expiryAmount @@ -106,7 +89,7 @@ data class DataServiceConfig private constructor(val context: Context, fun withSQLite(dataBaseManagerUtil: DataBaseManagerUtil): Builder { this.dataBaseManagerUtil = dataBaseManagerUtil - this.withRealm = false + this.withSQLite = true return this } diff --git a/usecases/src/main/java/com/zeyad/usecases/api/DataServiceFactory.kt b/usecases/src/main/java/com/zeyad/usecases/api/DataServiceFactory.kt index 8bf667f..851f4fd 100644 --- a/usecases/src/main/java/com/zeyad/usecases/api/DataServiceFactory.kt +++ b/usecases/src/main/java/com/zeyad/usecases/api/DataServiceFactory.kt @@ -4,11 +4,9 @@ import android.app.Application import android.content.Context import com.zeyad.usecases.Config import com.zeyad.usecases.db.DataBaseManager -import com.zeyad.usecases.db.RealmManager import com.zeyad.usecases.network.ApiConnection import com.zeyad.usecases.stores.DataStoreFactory import com.zeyad.usecases.utils.DataBaseManagerUtil -import io.reactivex.android.schedulers.AndroidSchedulers import st.lowlevel.storo.StoroBuilder class DataServiceFactory(val config: DataServiceConfig) { @@ -24,7 +22,7 @@ class DataServiceFactory(val config: DataServiceConfig) { Config.cacheDuration = config.cacheDuration Config.cacheTimeUnit = config.timeUnit Config.withSQLite = config.withSQL - Config.withRealm = config.withRealm + if (config.isWithCache) { StoroBuilder.configure(config.cacheSize.toLong()) .setCacheDirectory(config.context, StoroBuilder.Storage.INTERNAL) @@ -32,48 +30,32 @@ class DataServiceFactory(val config: DataServiceConfig) { .setGsonInstance(Config.gson) .initialize() } - val handlerThread = config.handlerThread - if (config.withRealm) { - handlerThread.start() - Config.backgroundThread = AndroidSchedulers.from(handlerThread.looper) - } + val apiConnection = ApiConnection(ApiConnection.init(config.okHttpBuilder), ApiConnection.initWithCache(config.okHttpBuilder, config.okHttpCache)) - - dataBaseManagerUtil = when { - Config.isWithDisk() -> + dataBaseManagerUtil = if (config.withSQL) dataBaseManagerUtil else object : DataBaseManagerUtil { override fun getDataBaseManager(dataClass: Class<*>): DataBaseManager? { - return RealmManager() + return null } } - else -> object : DataBaseManagerUtil { - override fun getDataBaseManager(dataClass: Class<*>): DataBaseManager? { - return null - } - } - } + dataService = DataService(DataStoreFactory(dataBaseManagerUtil, apiConnection, config.entityMapper), config.postExecutionThread, Config.backgroundThread) Config.apiConnection = apiConnection } - /** - * Destroys the singleton instance of DataUseCase. - */ fun destoryInstance() { dataService = null } + fun getInstance() = dataService!! + companion object { - /** - * @return IDataService the implementation instance of IDataService, throws NullPointerException - * if null. - */ var dataService: IDataService? = null } diff --git a/usecases/src/main/java/com/zeyad/usecases/api/IDataService.kt b/usecases/src/main/java/com/zeyad/usecases/api/IDataService.kt index ab46fa8..21998e5 100644 --- a/usecases/src/main/java/com/zeyad/usecases/api/IDataService.kt +++ b/usecases/src/main/java/com/zeyad/usecases/api/IDataService.kt @@ -1,12 +1,10 @@ package com.zeyad.usecases.api -import com.zeyad.usecases.db.RealmQueryProvider import com.zeyad.usecases.requests.FileIORequest import com.zeyad.usecases.requests.GetRequest import com.zeyad.usecases.requests.PostRequest import io.reactivex.Flowable import io.reactivex.Single -import io.realm.RealmModel import java.io.File interface IDataService { @@ -33,55 +31,55 @@ interface IDataService { * @param postRequest contains the attributes of the request. * @return Flowable with the Object. */ - fun patchObject(postRequest: PostRequest): Flowable + fun patchObject(postRequest: PostRequest): Single /** * Post Object to postRequest. * * @param postRequest contains the attributes of the request. - * @return Flowable with the Object. + * @return Single with the Object. */ - fun postObject(postRequest: PostRequest): Flowable + fun postObject(postRequest: PostRequest): Single /** * Post list to postRequest. * * @param postRequest contains the attributes of the request. - * @return Flowable with the list. + * @return Single with the list. */ - fun postList(postRequest: PostRequest): Flowable + fun postList(postRequest: PostRequest): Single /** * Put Object to postRequest. * * @param postRequest contains the attributes of the request. - * @return Flowable with the Object. + * @return Single with the Object. */ - fun putObject(postRequest: PostRequest): Flowable + fun putObject(postRequest: PostRequest): Single /** * Put list to postRequest. * * @param postRequest contains the attributes of the request. - * @return Flowable with the list. + * @return Single with the list. */ - fun putList(postRequest: PostRequest): Flowable + fun putList(postRequest: PostRequest): Single /** * Deletes item from postRequest. * * @param request contains the attributes of the request. - * @return Flowable with the list. + * @return Single with the list. */ - fun deleteItemById(request: PostRequest): Flowable + fun deleteItemById(request: PostRequest): Single /** * Deletes list from postRequest. * * @param deleteRequest contains the attributes of the request. - * @return Flowable with the list. + * @return Single with the list. */ - fun deleteCollectionByIds(deleteRequest: PostRequest): Flowable + fun deleteCollectionByIds(deleteRequest: PostRequest): Single /** * Deletes All. @@ -97,7 +95,7 @@ interface IDataService { * @param realmQueryProvider query tp select list of item(s). * @return */ - fun queryDisk(realmQueryProvider: RealmQueryProvider): Flowable> + fun queryDisk(query: String, clazz: Class): Single /** * Creates a repository pattern with live objects @@ -111,7 +109,7 @@ interface IDataService { * Creates a repository pattern with live objects * * @param getRequest contains the attributes of the request. - * @return [>][Flowable] with the data. + * @return [>][Single] with the data. */ fun getObjectOffLineFirst(getRequest: GetRequest): Flowable @@ -119,15 +117,15 @@ interface IDataService { * Uploads a file to a url. * * @param fileIORequest contains the attributes of the request, - * @return Flowable with the Object response. + * @return Single with the Object response. */ - fun uploadFile(fileIORequest: FileIORequest): Flowable + fun uploadFile(fileIORequest: FileIORequest): Single /** * Downloads file from the give url. * * @param fileIORequest contains the attributes of the request, - * @return Flowable with the ResponseBody + * @return Single with the ResponseBody */ - fun downloadFile(fileIORequest: FileIORequest): Flowable + fun downloadFile(fileIORequest: FileIORequest): Single } diff --git a/usecases/src/main/java/com/zeyad/usecases/db/BaseDao.kt b/usecases/src/main/java/com/zeyad/usecases/db/BaseDao.kt new file mode 100644 index 0000000..40e018e --- /dev/null +++ b/usecases/src/main/java/com/zeyad/usecases/db/BaseDao.kt @@ -0,0 +1,59 @@ +package com.zeyad.usecases.db + +import android.arch.persistence.db.SupportSQLiteQuery +import android.arch.persistence.room.* +import io.reactivex.Single + +interface BaseDao { + + @RawQuery + fun getItem(query: SupportSQLiteQuery): Single + + @RawQuery + fun getAllItems(query: SupportSQLiteQuery): Single> + + @RawQuery + fun getQuery(query: SupportSQLiteQuery): Single + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertItemsReplace(vararg objects: E): List + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertItemsReplace(objects: List): List + + @Insert + fun insertItemsAbort(vararg objects: E): List + + @Insert(onConflict = OnConflictStrategy.IGNORE) + fun insertItemsIgnore(vararg objects: E): List + + @Insert(onConflict = OnConflictStrategy.FAIL) + fun insertItemsFail(vararg objects: E): List + + @Insert(onConflict = OnConflictStrategy.ROLLBACK) + fun insertItemsRollback(vararg objects: E): List + + @Update(onConflict = OnConflictStrategy.REPLACE) + fun updateItemsReplace(vararg objects: E): Int + + @Update(onConflict = OnConflictStrategy.ABORT) + fun updateItemsAbort(vararg objects: E): Int + + @Update(onConflict = OnConflictStrategy.IGNORE) + fun updateItemsIgnore(vararg objects: E): Int + + @Update(onConflict = OnConflictStrategy.FAIL) + fun updateItemsFail(vararg objects: E): Int + + @Update(onConflict = OnConflictStrategy.ROLLBACK) + fun updateItemsRollback(vararg objects: E): Int + + @Delete + fun deleteItems(vararg objects: E): Int + + @Delete + fun deleteItems(objects: List): Int + + @RawQuery + fun deleteAllItems(query: SupportSQLiteQuery): Int +} \ No newline at end of file diff --git a/usecases/src/main/java/com/zeyad/usecases/db/DaoResolver.kt b/usecases/src/main/java/com/zeyad/usecases/db/DaoResolver.kt new file mode 100644 index 0000000..e1673b2 --- /dev/null +++ b/usecases/src/main/java/com/zeyad/usecases/db/DaoResolver.kt @@ -0,0 +1,5 @@ +package com.zeyad.usecases.db + +interface DaoResolver { + fun getDao(dataClass: Class): BaseDao +} \ No newline at end of file diff --git a/usecases/src/main/java/com/zeyad/usecases/db/DataBaseManager.java b/usecases/src/main/java/com/zeyad/usecases/db/DataBaseManager.java deleted file mode 100644 index f92664c..0000000 --- a/usecases/src/main/java/com/zeyad/usecases/db/DataBaseManager.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.zeyad.usecases.db; - -import android.support.annotation.NonNull; - -import org.json.JSONArray; -import org.json.JSONObject; - -import java.util.List; - -import io.reactivex.Flowable; -import io.reactivex.Single; -import io.realm.RealmModel; - -/** - * Interface for the Database modules. - */ -public interface DataBaseManager { - - /** - * Gets an {@link Flowable} which will emit an Object - * @param idColumnName name of ID variable - * @param itemId ID value - * @param itemIdType type of the ID - * @param dataClass type of the data requested - * @param type of the data requested - * @return a {@link Flowable} containing an object of type M. - */ - @NonNull - Flowable getById(@NonNull final String idColumnName, final Object itemId, - final Class itemIdType, Class dataClass); - - /** - * Gets an {@link Flowable} which will emit a List of Objects. - * - * @param clazz Class type of the items to get. - */ - @NonNull - Flowable> getAll(Class clazz); - - /** - * Get list of items according to the query passed. - * - * @param queryFactory The query used to look for inside the DB. - */ - @NonNull - Flowable> getQuery(RealmQueryProvider queryFactory); - - /** - * Puts and element into the DB. - * - * @param realmModel Element to insert in the DB. - * @param dataClass Class type of the items to be put. - */ - @NonNull - Single put(M realmModel, Class dataClass); - - /** - * Puts and element into the DB. - * - * @param jsonObject Element to insert in the DB. - * @param dataClass Class type of the items to be put. - */ - @NonNull - Single put(JSONObject jsonObject, String idColumnName, Class itemIdType, Class dataClass); - - /** - * Puts and element into the DB. - * - * @param realmObjects Element to insert in the DB. - * @param dataClass Class type of the items to be put. - */ - @NonNull - Single putAll(List realmObjects, Class dataClass); - - /** - * Puts and element into the DB. - * - * @param jsonArray Element to insert in the DB. - * @param idColumnName Name of the id field. - * @param dataClass Class type of the items to be put. - */ - @NonNull - Single putAll(JSONArray jsonArray, String idColumnName, Class itemIdType, Class dataClass); - - /** - * Evict all elements of the DB. - * - * @param clazz Class type of the items to be deleted. - */ - @NonNull - Single evictAll(Class clazz); - - /** - * Evict a collection elements of the DB. - * - * @param idFieldName The id used to look for inside the DB. - * @param list List of ids to be deleted. - * @param dataClass Class type of the items to be deleted. - */ - @NonNull - Single evictCollection(String idFieldName, List list, Class itemIdType, Class dataClass); - - /** - * Evict element by id of the DB. - * - * @param clazz Class type of the items to be deleted. - * @param idFieldName The id used to look for inside the DB. - * @param idFieldValue Name of the id field. - */ - boolean evictById(Class clazz, String idFieldName, Object idFieldValue, Class itemIdType); -} - diff --git a/usecases/src/main/java/com/zeyad/usecases/db/DataBaseManager.kt b/usecases/src/main/java/com/zeyad/usecases/db/DataBaseManager.kt new file mode 100644 index 0000000..a610500 --- /dev/null +++ b/usecases/src/main/java/com/zeyad/usecases/db/DataBaseManager.kt @@ -0,0 +1,100 @@ +package com.zeyad.usecases.db + +import io.reactivex.Flowable +import io.reactivex.Single +import org.json.JSONArray +import org.json.JSONObject + +/** + * Interface for the ServiceDatabase modules. + */ +interface DataBaseManager { + + /** + * Gets an [Flowable] which will emit an Object + * @param idColumnName name of ID variable + * @param itemId ID value + * @param clazz type of the data requested + * @param type of the data requested + * @return a [Flowable] containing an object of type E. + */ + fun getById(idColumnName: String, itemId: Any, clazz: Class): Flowable + + /** + * Gets an [Flowable] which will emit a List of Objects. + * + * @param clazz Class type of the items to get. + */ + fun getAll(clazz: Class): Flowable> + + /** + * Get list of items according to the query passed. + * + * @param query The query used to look for inside the DB. + */ + fun getQuery(query: String, clazz: Class): Flowable + + /** + * Puts and element into the DB. + * + * @param entity Element to insert in the DB. + */ + fun put(entity: E, clazz: Class): Single + + /** + * Puts and element into the DB. + * + * @param jsonObject Element to insert in the DB. + * @param clazz Class type of the items to be put. + */ + fun put(jsonObject: JSONObject, clazz: Class): Single + + /** + * Puts and element into the DB. + * + * @param entities Element to insert in the DB. + * @param clazz Class type of the items to be put. + */ + fun putAll(entities: List, clazz: Class): Single + + /** + * Puts and element into the DB. + * + * @param jsonArray Element to insert in the DB. + * @param clazz Class type of the items to be put. + */ + fun putAll(jsonArray: JSONArray, clazz: Class): Single + + /** + * Evict all elements of the DB. + * + * @param clazz Class type of the items to be deleted. + */ + fun evictAll(clazz: Class): Single + + /** + * Evict a collection elements of the DB. + * + * @param list List to be deleted. + * @param clazz Class type of the items to be deleted. + */ + fun evictCollection(list: List, clazz: Class): Single + + /** + * Evict a collection elements of the DB. + * + * @param list List of ids to be deleted. + * @param clazz Class type of the items to be deleted. + */ + fun evictCollectionById(list: List, clazz: Class, idFieldName: String): Single + + /** + * Evict element by id of the DB. + * + * @param clazz Class type of the items to be deleted. + * @param idFieldName The id used to look for inside the DB. + * @param idFieldValue Name of the id field. + */ + fun evictById(clazz: Class, idFieldName: String, idFieldValue: Any): Single +} + diff --git a/usecases/src/main/java/com/zeyad/usecases/db/RealmManager.java b/usecases/src/main/java/com/zeyad/usecases/db/RealmManager.java index 006920e..114c57f 100644 --- a/usecases/src/main/java/com/zeyad/usecases/db/RealmManager.java +++ b/usecases/src/main/java/com/zeyad/usecases/db/RealmManager.java @@ -1,328 +1,328 @@ -package com.zeyad.usecases.db; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.util.Log; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.List; - -import io.reactivex.BackpressureStrategy; -import io.reactivex.Flowable; -import io.reactivex.Observable; -import io.reactivex.Single; -import io.reactivex.disposables.Disposables; -import io.realm.Realm; -import io.realm.RealmChangeListener; -import io.realm.RealmConfiguration; -import io.realm.RealmModel; -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * {@link DataBaseManager} implementation. - */ -public class RealmManager implements DataBaseManager { - - private static final String NO_ID = "Could not find id!"; - - /** - * Gets an {@link Flowable} which will emit an Object - * - * @param idColumnName name of ID variable - * @param itemId ID value - * @param itemIdType type of the ID - * @param dataClass type of the data requested - * @param type of the data requested - * @return a {@link Flowable} containing an object of type M. - */ - @NonNull - @Override - public Flowable getById(@NonNull final String idColumnName, final Object itemId, - final Class itemIdType, Class dataClass) { - return Flowable.defer(() -> { - Realm realm = Realm.getDefaultInstance(); - return getItemById(realm, dataClass, idColumnName, itemId, itemIdType) - .map(realmModel -> (M) realm.copyFromRealm(realmModel)); - }); - } - - /** - * Gets an {@link Flowable} which will emit a List of Objects. - * - * @param clazz Class type of the items to get. - */ - @NonNull - @Override - public Flowable> getAll(Class clazz) { - return Flowable.defer(() -> { - Realm realm = Realm.getDefaultInstance(); - return realm.where(clazz).findAll().asFlowable() - .filter(o -> ((RealmResults) o).isLoaded()) - .map(o -> realm.copyFromRealm((RealmResults) o)) - .flatMap(o -> ((List) o).isEmpty() ? - Flowable.error(new IllegalAccessException(String - .format("%s(s) were not found!", clazz.getSimpleName()))) - : Flowable.just(o)); - }); - } - - /** - * Takes a query to be executed and return a list of containing the result. - * - * @param queryFactory The query used to look for inside the DB. - * @param the return type from the query - * @return {@link List} a result list that matches the given query. - */ - @NonNull - @Override - public Flowable> getQuery(@NonNull RealmQueryProvider queryFactory) { - return Flowable.defer(() -> { - Realm realm = Realm.getDefaultInstance(); - return queryFactory.create(realm).findAll().asFlowable() - .filter(RealmResults::isLoaded) - .map(realm::copyFromRealm); - }); - } - - /** - * Puts and element into the DB. - * - * @param realmModel Element to insert in the DB. - * @param dataClass Class type of the items to be put. - */ - @NonNull - @Override - // TODO: 6/13/17 Remove ? remove : create flow - public Single put(@NonNull M realmModel, @NonNull Class dataClass) { - return Single.fromCallable(() -> - RealmObject.isValid(executeWriteOperationInRealm(Realm.getDefaultInstance(), - (ExecuteAndReturn) realm -> realm.copyToRealmOrUpdate(realmModel)))); - } - - - /** - * Puts and element into the DB. - * - * @param jsonObject Element to insert in the DB. - * @param dataClass Class type of the items to be put. - */ - @NonNull - @Override - public Single put(@Nullable JSONObject jsonObject, @Nullable String idColumnName, - Class itemIdType, @NonNull Class dataClass) { - return Single.fromCallable(() -> { - if (jsonObject == null) { - throw new IllegalArgumentException("JSONObject is invalid"); - } else { - final JSONObject updatedJSON = - updateJsonObjectWithIdValue(jsonObject, idColumnName, itemIdType, dataClass); - return RealmObject.isValid(executeWriteOperationInRealm(Realm.getDefaultInstance(), - (ExecuteAndReturn) realm -> - realm.createOrUpdateObjectFromJson(dataClass, updatedJSON))); - } - }); - } - - @NonNull - @Override - public Single putAll(List realmObjects, Class dataClass) { - return Single.fromCallable(() -> executeWriteOperationInRealm(Realm.getDefaultInstance(), - (ExecuteAndReturn>) realm -> - realm.copyToRealmOrUpdate(realmObjects)).size() == realmObjects.size()); - } - - /** - * Puts and element into the DB. - * - * @param jsonArray Element to insert in the DB. - * @param idColumnName Name of the id field. - * @param dataClass Class type of the items to be put. - */ - @NonNull - @Override - public Single putAll(@NonNull JSONArray jsonArray, String idColumnName, Class itemIdType, - @NonNull Class dataClass) { - return Single.fromCallable(() -> { - final JSONArray updatedJSONArray = - updateJsonArrayWithIdValue(jsonArray, idColumnName, itemIdType, dataClass); - executeWriteOperationInRealm(Realm.getDefaultInstance(), (Execute) realm -> - realm.createOrUpdateAllFromJson(dataClass, updatedJSONArray)); - return true; - }); - } - - /** - * Evict all elements of the DB. - * - * @param clazz Class type of the items to be deleted. - */ - @NonNull - @Override - public Single evictAll(@NonNull Class clazz) { - return Single.fromCallable(() -> { - executeWriteOperationInRealm(Realm.getDefaultInstance(), (Execute) realm -> realm.delete(clazz)); - return true; - }); - } - - /** - * Evict a collection elements of the DB. - * - * @param idFieldName The id used to look for inside the DB. - * @param list List of ids to be deleted. - * @param dataClass Class type of the items to be deleted. - */ - @NonNull - @Override - public Single evictCollection(@NonNull String idFieldName, @NonNull List list, - final Class itemIdType, @NonNull Class dataClass) { - return Single.fromCallable(() -> list.isEmpty() ? false : Observable.fromIterable(list) - .map(id -> evictById(dataClass, idFieldName, id, itemIdType)) - .reduce((aBoolean, aBoolean2) -> aBoolean && aBoolean2) - .blockingGet()); - } - - /** - * Evict element by id of the DB. - * - * @param dataClass Class type of the items to be deleted. - * @param idColumnName The id used to look for inside the DB. - * @param itemId Name of the id field. - * @param itemIdType Class type of the item id to be deleted. - */ - @Override - public boolean evictById(@NonNull Class dataClass, @NonNull String idColumnName, final Object itemId, - final Class itemIdType) { - Realm realm = Realm.getDefaultInstance(); - return getItemById(realm, dataClass, idColumnName, itemId, itemIdType) - .map(realmModel -> { - if (realmModel == null) { - return false; - } else { - executeWriteOperationInRealm(realm, (Execute) unused -> RealmObject.deleteFromRealm - (realmModel)); - return !RealmObject.isValid(realmModel); - } - }) - .blockingFirst(); - } - - private Flowable getItemById(Realm realm, @NonNull Class dataClass, @NonNull String idColumnName, - final Object itemId, final Class itemIdType) { - Object result; - if (itemIdType.equals(long.class)) { - result = realm.where(dataClass).equalTo(idColumnName, (long) itemId).findFirst(); - } else if (itemIdType.equals(Long.class)) { - result = realm.where(dataClass).equalTo(idColumnName, (Long) itemId).findFirst(); - } else if (itemIdType.equals(int.class)) { - result = realm.where(dataClass).equalTo(idColumnName, (int) itemId).findFirst(); - } else if (itemIdType.equals(Integer.class)) { - result = realm.where(dataClass).equalTo(idColumnName, (Integer) itemId).findFirst(); - } - // else if (itemIdType.equals(short.class) || itemIdType.equals(Short.class)) { - // result = realm.where(dataClass).equalTo(idColumnName, (short) itemId).findFirst(); - // } - else if (itemIdType.equals(byte.class) || itemIdType.equals(Byte.class)) { - result = realm.where(dataClass).equalTo(idColumnName, (byte) itemId).findFirst(); - } else if (itemIdType.equals(String.class)) { - result = realm.where(dataClass).equalTo(idColumnName, String.valueOf(itemId)).findFirst(); - } else { - return Flowable.error(new IllegalArgumentException("Unsupported ID type!")); - } - if (result == null) { - return Flowable.error(new IllegalAccessException(String - .format("%s with ID: %s was not found!", dataClass.getSimpleName(), itemId))); - } - return Flowable.just((RealmModel) result); - } - - private void executeWriteOperationInRealm(@NonNull Realm realm, @NonNull Execute execute) { - if (realm.isInTransaction()) { - realm.cancelTransaction(); - } - realm.beginTransaction(); - execute.run(realm); - realm.commitTransaction(); - } - - private T executeWriteOperationInRealm(@NonNull Realm realm, @NonNull ExecuteAndReturn executor) { - T toReturnValue; - if (realm.isInTransaction()) { - realm.cancelTransaction(); - } - realm.beginTransaction(); - toReturnValue = executor.runAndReturn(realm); - realm.commitTransaction(); - return toReturnValue; - } - - @NonNull - private JSONArray updateJsonArrayWithIdValue(@NonNull JSONArray jsonArray, @Nullable String idColumnName, - Class itemIdType, Class dataClass) throws JSONException { - if (idColumnName == null || idColumnName.isEmpty()) { - throw new IllegalArgumentException(NO_ID); - } - int length = jsonArray.length(); - JSONArray updatedJSONArray = new JSONArray(); - for (int i = 0; i < length; i++) { - if (jsonArray.opt(i) instanceof JSONObject) { - updatedJSONArray.put(updateJsonObjectWithIdValue(jsonArray.optJSONObject(i), idColumnName, itemIdType, - dataClass)); - } - } - return updatedJSONArray; - } - - @NonNull - private JSONObject updateJsonObjectWithIdValue(@NonNull JSONObject jsonObject, @Nullable String idColumnName, - Class itemIdType, Class dataClass) throws JSONException { - if (idColumnName == null || idColumnName.isEmpty()) { - throw new IllegalArgumentException(NO_ID); - } - if (itemIdType.equals(String.class)) { - return jsonObject; - } else if (jsonObject.optInt(idColumnName) == 0) { - jsonObject.put(idColumnName, getNextId(dataClass, idColumnName)); - } - return jsonObject; - } - - private int getNextId(Class clazz, String column) { - Realm realm = Realm.getDefaultInstance(); - try { - Number currentMax = realm.where(clazz).max(column); - return currentMax != null ? currentMax.intValue() + 1 : 1; - } finally { - realm.close(); - } - } - - @Deprecated - private Flowable getRealm(Realm realm) { - return Flowable.create(emitter -> { - RealmConfiguration realmConfiguration = realm.getConfiguration(); - Realm observableRealm = Realm.getInstance(realmConfiguration); - final RealmChangeListener listener = emitter::onNext; - emitter.setDisposable(Disposables.fromRunnable(() -> { - observableRealm.removeChangeListener(listener); - observableRealm.close(); - Log.d(RealmManager.class.getSimpleName(), "Realm instance closed!"); - })); - observableRealm.addChangeListener(listener); - emitter.onNext(observableRealm); - }, BackpressureStrategy.BUFFER); - } - - private interface Execute { - void run(Realm realm); - } - - private interface ExecuteAndReturn { - @NonNull - T runAndReturn(Realm realm); - } -} +//package com.zeyad.usecases.db; +// +//import android.support.annotation.NonNull; +//import android.support.annotation.Nullable; +//import android.util.Log; +// +//import org.json.JSONArray; +//import org.json.JSONException; +//import org.json.JSONObject; +// +//import java.util.List; +// +//import io.reactivex.BackpressureStrategy; +//import io.reactivex.Flowable; +//import io.reactivex.Observable; +//import io.reactivex.Single; +//import io.reactivex.disposables.Disposables; +//import io.realm.Realm; +//import io.realm.RealmChangeListener; +//import io.realm.RealmConfiguration; +//import io.realm.RealmModel; +//import io.realm.RealmObject; +//import io.realm.RealmResults; +// +///** +// * {@link DataBaseManager} implementation. +// */ +//public class RealmManager implements DataBaseManager { +// +// private static final String NO_ID = "Could not find id!"; +// +// /** +// * Gets an {@link Flowable} which will emit an Object +// * +// * @param idColumnName name of ID variable +// * @param itemId ID value +// * @param itemIdType type of the ID +// * @param dataClass type of the data requested +// * @param type of the data requested +// * @return a {@link Flowable} containing an object of type M. +// */ +// @NonNull +// @Override +// public Flowable getById(@NonNull final String idColumnName, final Object itemId, +// final Class itemIdType, Class dataClass) { +// return Flowable.defer(() -> { +// Realm realm = Realm.getDefaultInstance(); +// return getItemById(realm, dataClass, idColumnName, itemId, itemIdType) +// .map(realmModel -> (M) realm.copyFromRealm(realmModel)); +// }); +// } +// +// /** +// * Gets an {@link Flowable} which will emit a List of Objects. +// * +// * @param clazz Class type of the items to get. +// */ +// @NonNull +// @Override +// public Flowable> getAll(Class clazz) { +// return Flowable.defer(() -> { +// Realm realm = Realm.getDefaultInstance(); +// return realm.where(clazz).findAll().asFlowable() +// .filter(o -> ((RealmResults) o).isLoaded()) +// .map(o -> realm.copyFromRealm((RealmResults) o)) +// .flatMap(o -> ((List) o).isEmpty() ? +// Flowable.error(new IllegalAccessException(String +// .format("%s(s) were not found!", clazz.getSimpleName()))) +// : Flowable.just(o)); +// }); +// } +// +// /** +// * Takes a query to be executed and return a list of containing the result. +// * +// * @param queryFactory The query used to look for inside the DB. +// * @param the return type from the query +// * @return {@link List} a result list that matches the given query. +// */ +// @NonNull +// @Override +// public Flowable> getQuery(@NonNull RealmQueryProvider queryFactory) { +// return Flowable.defer(() -> { +// Realm realm = Realm.getDefaultInstance(); +// return queryFactory.create(realm).findAll().asFlowable() +// .filter(RealmResults::isLoaded) +// .map(realm::copyFromRealm); +// }); +// } +// +// /** +// * Puts and element into the DB. +// * +// * @param realmModel Element to insert in the DB. +// * @param dataClass Class type of the items to be put. +// */ +// @NonNull +// @Override +// // TODO: 6/13/17 Remove ? remove : create flow +// public Single put(@NonNull M realmModel, @NonNull Class dataClass) { +// return Single.fromCallable(() -> +// RealmObject.isValid(executeWriteOperationInRealm(Realm.getDefaultInstance(), +// (ExecuteAndReturn) realm -> realm.copyToRealmOrUpdate(realmModel)))); +// } +// +// +// /** +// * Puts and element into the DB. +// * +// * @param jsonObject Element to insert in the DB. +// * @param dataClass Class type of the items to be put. +// */ +// @NonNull +// @Override +// public Single put(@Nullable JSONObject jsonObject, @Nullable String idColumnName, +// Class itemIdType, @NonNull Class dataClass) { +// return Single.fromCallable(() -> { +// if (jsonObject == null) { +// throw new IllegalArgumentException("JSONObject is invalid"); +// } else { +// final JSONObject updatedJSON = +// updateJsonObjectWithIdValue(jsonObject, idColumnName, itemIdType, dataClass); +// return RealmObject.isValid(executeWriteOperationInRealm(Realm.getDefaultInstance(), +// (ExecuteAndReturn) realm -> +// realm.createOrUpdateObjectFromJson(dataClass, updatedJSON))); +// } +// }); +// } +// +// @NonNull +// @Override +// public Single putAll(List realmObjects, Class dataClass) { +// return Single.fromCallable(() -> executeWriteOperationInRealm(Realm.getDefaultInstance(), +// (ExecuteAndReturn>) realm -> +// realm.copyToRealmOrUpdate(realmObjects)).size() == realmObjects.size()); +// } +// +// /** +// * Puts and element into the DB. +// * +// * @param jsonArray Element to insert in the DB. +// * @param idColumnName Name of the id field. +// * @param dataClass Class type of the items to be put. +// */ +// @NonNull +// @Override +// public Single putAll(@NonNull JSONArray jsonArray, String idColumnName, Class itemIdType, +// @NonNull Class dataClass) { +// return Single.fromCallable(() -> { +// final JSONArray updatedJSONArray = +// updateJsonArrayWithIdValue(jsonArray, idColumnName, itemIdType, dataClass); +// executeWriteOperationInRealm(Realm.getDefaultInstance(), (Execute) realm -> +// realm.createOrUpdateAllFromJson(dataClass, updatedJSONArray)); +// return true; +// }); +// } +// +// /** +// * Evict all elements of the DB. +// * +// * @param clazz Class type of the items to be deleted. +// */ +// @NonNull +// @Override +// public Single evictAll(@NonNull Class clazz) { +// return Single.fromCallable(() -> { +// executeWriteOperationInRealm(Realm.getDefaultInstance(), (Execute) realm -> realm.delete(clazz)); +// return true; +// }); +// } +// +// /** +// * Evict a collection elements of the DB. +// * +// * @param idFieldName The id used to look for inside the DB. +// * @param list List of ids to be deleted. +// * @param dataClass Class type of the items to be deleted. +// */ +// @NonNull +// @Override +// public Single evictCollection(@NonNull String idFieldName, @NonNull List list, +// final Class itemIdType, @NonNull Class dataClass) { +// return Single.fromCallable(() -> list.isEmpty() ? false : Observable.fromIterable(list) +// .map(id -> evictById(dataClass, idFieldName, id, itemIdType)) +// .reduce((aBoolean, aBoolean2) -> aBoolean && aBoolean2) +// .blockingGet()); +// } +// +// /** +// * Evict element by id of the DB. +// * +// * @param dataClass Class type of the items to be deleted. +// * @param idColumnName The id used to look for inside the DB. +// * @param itemId Name of the id field. +// * @param itemIdType Class type of the item id to be deleted. +// */ +// @Override +// public boolean evictById(@NonNull Class dataClass, @NonNull String idColumnName, final Object itemId, +// final Class itemIdType) { +// Realm realm = Realm.getDefaultInstance(); +// return getItemById(realm, dataClass, idColumnName, itemId, itemIdType) +// .map(realmModel -> { +// if (realmModel == null) { +// return false; +// } else { +// executeWriteOperationInRealm(realm, (Execute) unused -> RealmObject.deleteFromRealm +// (realmModel)); +// return !RealmObject.isValid(realmModel); +// } +// }) +// .blockingFirst(); +// } +// +// private Flowable getItemById(Realm realm, @NonNull Class dataClass, @NonNull String idColumnName, +// final Object itemId, final Class itemIdType) { +// Object result; +// if (itemIdType.equals(long.class)) { +// result = realm.where(dataClass).equalTo(idColumnName, (long) itemId).findFirst(); +// } else if (itemIdType.equals(Long.class)) { +// result = realm.where(dataClass).equalTo(idColumnName, (Long) itemId).findFirst(); +// } else if (itemIdType.equals(int.class)) { +// result = realm.where(dataClass).equalTo(idColumnName, (int) itemId).findFirst(); +// } else if (itemIdType.equals(Integer.class)) { +// result = realm.where(dataClass).equalTo(idColumnName, (Integer) itemId).findFirst(); +// } +// // else if (itemIdType.equals(short.class) || itemIdType.equals(Short.class)) { +// // result = realm.where(dataClass).equalTo(idColumnName, (short) itemId).findFirst(); +// // } +// else if (itemIdType.equals(byte.class) || itemIdType.equals(Byte.class)) { +// result = realm.where(dataClass).equalTo(idColumnName, (byte) itemId).findFirst(); +// } else if (itemIdType.equals(String.class)) { +// result = realm.where(dataClass).equalTo(idColumnName, String.valueOf(itemId)).findFirst(); +// } else { +// return Flowable.error(new IllegalArgumentException("Unsupported ID type!")); +// } +// if (result == null) { +// return Flowable.error(new IllegalAccessException(String +// .format("%s with ID: %s was not found!", dataClass.getSimpleName(), itemId))); +// } +// return Flowable.just((RealmModel) result); +// } +// +// private void executeWriteOperationInRealm(@NonNull Realm realm, @NonNull Execute execute) { +// if (realm.isInTransaction()) { +// realm.cancelTransaction(); +// } +// realm.beginTransaction(); +// execute.run(realm); +// realm.commitTransaction(); +// } +// +// private T executeWriteOperationInRealm(@NonNull Realm realm, @NonNull ExecuteAndReturn executor) { +// T toReturnValue; +// if (realm.isInTransaction()) { +// realm.cancelTransaction(); +// } +// realm.beginTransaction(); +// toReturnValue = executor.runAndReturn(realm); +// realm.commitTransaction(); +// return toReturnValue; +// } +// +// @NonNull +// private JSONArray updateJsonArrayWithIdValue(@NonNull JSONArray jsonArray, @Nullable String idColumnName, +// Class itemIdType, Class dataClass) throws JSONException { +// if (idColumnName == null || idColumnName.isEmpty()) { +// throw new IllegalArgumentException(NO_ID); +// } +// int length = jsonArray.length(); +// JSONArray updatedJSONArray = new JSONArray(); +// for (int i = 0; i < length; i++) { +// if (jsonArray.opt(i) instanceof JSONObject) { +// updatedJSONArray.put(updateJsonObjectWithIdValue(jsonArray.optJSONObject(i), idColumnName, itemIdType, +// dataClass)); +// } +// } +// return updatedJSONArray; +// } +// +// @NonNull +// private JSONObject updateJsonObjectWithIdValue(@NonNull JSONObject jsonObject, @Nullable String idColumnName, +// Class itemIdType, Class dataClass) throws JSONException { +// if (idColumnName == null || idColumnName.isEmpty()) { +// throw new IllegalArgumentException(NO_ID); +// } +// if (itemIdType.equals(String.class)) { +// return jsonObject; +// } else if (jsonObject.optInt(idColumnName) == 0) { +// jsonObject.put(idColumnName, getNextId(dataClass, idColumnName)); +// } +// return jsonObject; +// } +// +// private int getNextId(Class clazz, String column) { +// Realm realm = Realm.getDefaultInstance(); +// try { +// Number currentMax = realm.where(clazz).max(column); +// return currentMax != null ? currentMax.intValue() + 1 : 1; +// } finally { +// realm.close(); +// } +// } +// +// @Deprecated +// private Flowable getRealm(Realm realm) { +// return Flowable.create(emitter -> { +// RealmConfiguration realmConfiguration = realm.getConfiguration(); +// Realm observableRealm = Realm.getInstance(realmConfiguration); +// final RealmChangeListener listener = emitter::onNext; +// emitter.setDisposable(Disposables.fromRunnable(() -> { +// observableRealm.removeChangeListener(listener); +// observableRealm.close(); +// Log.d(RealmManager.class.getSimpleName(), "Realm instance closed!"); +// })); +// observableRealm.addChangeListener(listener); +// emitter.onNext(observableRealm); +// }, BackpressureStrategy.BUFFER); +// } +// +// private interface Execute { +// void run(Realm realm); +// } +// +// private interface ExecuteAndReturn { +// @NonNull +// T runAndReturn(Realm realm); +// } +//} diff --git a/usecases/src/main/java/com/zeyad/usecases/db/RealmQueryProvider.kt b/usecases/src/main/java/com/zeyad/usecases/db/RealmQueryProvider.kt deleted file mode 100644 index cdb9cc6..0000000 --- a/usecases/src/main/java/com/zeyad/usecases/db/RealmQueryProvider.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.zeyad.usecases.db - -import io.realm.Realm -import io.realm.RealmModel -import io.realm.RealmQuery - -interface RealmQueryProvider { - fun create(realm: Realm): RealmQuery -} diff --git a/usecases/src/main/java/com/zeyad/usecases/db/RoomManager.kt b/usecases/src/main/java/com/zeyad/usecases/db/RoomManager.kt new file mode 100644 index 0000000..6307c7f --- /dev/null +++ b/usecases/src/main/java/com/zeyad/usecases/db/RoomManager.kt @@ -0,0 +1,108 @@ +package com.zeyad.usecases.db + +import android.arch.persistence.db.SimpleSQLiteQuery +import android.arch.persistence.room.RoomDatabase +import com.google.gson.Gson +import com.zeyad.usecases.Config +import com.zeyad.usecases.Mockable +import io.reactivex.Flowable +import io.reactivex.Single +import org.json.JSONArray +import org.json.JSONObject + +@Mockable +class RoomManager(private val db: RoomDatabase, private val daoResolver: DaoResolver) : DataBaseManager { + + override fun getQuery(query: String, clazz: Class): Flowable { + return singleTypedTransactionBlock { + daoResolver.getDao(clazz).getQuery(SimpleSQLiteQuery(query)) + }.toFlowable() + } + + override fun getById(idColumnName: String, itemId: Any, clazz: Class): Flowable { + return singleTypedTransactionBlock { + daoResolver.getDao(clazz) + .getItem(SimpleSQLiteQuery("Select * From ${clazz.simpleName} " + + "Where $idColumnName=$itemId")) + }.toFlowable() + } + + override fun getAll(clazz: Class): Flowable> { + return singleTypedTransactionBlock { + daoResolver.getDao(clazz) + .getAllItems(SimpleSQLiteQuery("Select * From ${clazz.simpleName}")) + }.toFlowable() + } + + override fun put(entity: E, clazz: Class): Single { + return singleTransactionBlock { + Single.just(daoResolver.getDao(clazz).insertItemsReplace(listOf(entity)).isNotEmpty()) + } + } + + override fun put(jsonObject: JSONObject, clazz: Class): Single { + return singleTransactionBlock { + Single.just(daoResolver.getDao(clazz) + .insertItemsReplace(Config.gson.fromJson(jsonObject.toString(), clazz)).isNotEmpty()) + } + } + + override fun putAll(entities: List, clazz: Class): Single { + return singleTransactionBlock { + Single.just(daoResolver.getDao(clazz).insertItemsReplace(entities).isNotEmpty()) + } + } + + override fun putAll(jsonArray: JSONArray, clazz: Class): Single { + return singleTransactionBlock { + Single.just(daoResolver.getDao(clazz) + .insertItemsReplace(Gson().fromJson(jsonArray.toString(), clazz)).isNotEmpty()) + } + } + + override fun evictAll(clazz: Class): Single { + return singleTransactionBlock { + Single.just(daoResolver.getDao(clazz) + .deleteAllItems(SimpleSQLiteQuery("DELETE FROM ${clazz.simpleName}")) > 0) + } + } + + override fun evictCollection(list: List, clazz: Class): Single { + return singleTransactionBlock { Single.just(daoResolver.getDao(clazz).deleteItems(list) > 0) } + } + + override fun evictCollectionById(list: List, clazz: Class, idFieldName: String): Single { + return singleTransactionBlock { + Single.just(list).flatMap { evictById(clazz, idFieldName, it) } + } + } + + override fun evictById(clazz: Class, idFieldName: String, idFieldValue: Any): Single { + return singleTransactionBlock { + Single.just(daoResolver.getDao(clazz).deleteItems(getById(idFieldName, idFieldValue, + clazz).blockingFirst()) > 0) + } + } + + private inline fun singleTypedTransactionBlock(r: RoomDatabase.() -> Single): Single { + db.beginTransaction() + try { + val single = r.invoke(db) + db.setTransactionSuccessful() + return single + } finally { + db.endTransaction() + } + } + + private inline fun singleTransactionBlock(r: RoomDatabase.() -> Single): Single { + db.beginTransaction() + try { + val single = r.invoke(db) + db.setTransactionSuccessful() + return single + } finally { + db.endTransaction() + } + } +} diff --git a/usecases/src/main/java/com/zeyad/usecases/network/ApiConnection.kt b/usecases/src/main/java/com/zeyad/usecases/network/ApiConnection.kt index 04d710a..236d085 100644 --- a/usecases/src/main/java/com/zeyad/usecases/network/ApiConnection.kt +++ b/usecases/src/main/java/com/zeyad/usecases/network/ApiConnection.kt @@ -5,6 +5,7 @@ import com.zeyad.usecases.BuildConfig import com.zeyad.usecases.Config import com.zeyad.usecases.Mockable import io.reactivex.Flowable +import io.reactivex.Single import okhttp3.* import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit @@ -21,7 +22,7 @@ class ApiConnection(val restApiWithoutCache: RestApi, val restApiWithCache: Rest private val restApi: RestApi get() = if (Config.useApiWithCache) restApiWithCache else restApiWithoutCache - fun dynamicDownload(url: String): Flowable { + fun dynamicDownload(url: String): Single { return restApi.dynamicDownload(url) } @@ -47,31 +48,30 @@ class ApiConnection(val restApiWithoutCache: RestApi, val restApiWithCache: Rest return restApi.dynamicGetList(url) as Flowable> } - fun dynamicPost(url: String, requestBody: RequestBody): Flowable { - return restApi.dynamicPost(url, requestBody) as Flowable + fun dynamicPost(url: String, requestBody: RequestBody): Single { + return restApi.dynamicPost(url, requestBody) as Single } - fun dynamicPut(url: String, requestBody: RequestBody): Flowable { - return restApi.dynamicPut(url, requestBody) as Flowable + fun dynamicPut(url: String, requestBody: RequestBody): Single { + return restApi.dynamicPut(url, requestBody) as Single } - fun dynamicUpload(url: String, partMap: Map, files: List): Flowable { - return restApi.dynamicUpload(url, partMap, files) as Flowable + fun dynamicUpload(url: String, partMap: Map, files: List): Single { + return restApi.dynamicUpload(url, partMap, files) as Single } - fun dynamicDelete(url: String): Flowable { - return restApi.dynamicDelete(url) as Flowable + fun dynamicDelete(url: String, requestBody: RequestBody): Single { + return restApi.dynamicDelete(url, requestBody) as Single } - fun dynamicPatch(url: String, body: RequestBody): Flowable { - return restApi.dynamicPatch(url, body) as Flowable + fun dynamicPatch(url: String, body: RequestBody): Single { + return restApi.dynamicPatch(url, body) as Single } private fun logNoCache() { Log.e(javaClass.simpleName, CACHING_DISABLED) } - // private Interceptor provideGzipRequestInterceptor() { // return chain -> { // Request originalRequest = chain.request(); @@ -153,9 +153,9 @@ class ApiConnection(val restApiWithoutCache: RestApi, val restApiWithCache: Rest // } companion object { - private val CACHING_DISABLED = "There would be no caching. Since caching module is disabled."//, + private const val CACHING_DISABLED = "There would be no caching. Since caching module is disabled."//, // CACHE_CONTROL = "Cache-Control"; - private val TIME_OUT = 15 + private const val TIME_OUT = 15 fun initWithCache( okHttpBuilder: OkHttpClient.Builder?, cache: Cache?): RestApi { diff --git a/usecases/src/main/java/com/zeyad/usecases/network/RestApi.kt b/usecases/src/main/java/com/zeyad/usecases/network/RestApi.kt index f9437f4..12f3e14 100644 --- a/usecases/src/main/java/com/zeyad/usecases/network/RestApi.kt +++ b/usecases/src/main/java/com/zeyad/usecases/network/RestApi.kt @@ -1,6 +1,7 @@ package com.zeyad.usecases.network import io.reactivex.Flowable +import io.reactivex.Single import okhttp3.MultipartBody import okhttp3.RequestBody import okhttp3.ResponseBody @@ -24,23 +25,23 @@ interface RestApi { fun dynamicGetList(@Url url: String, shouldCache: Boolean): Flowable> @POST - fun dynamicPost(@Url url: String, @Body body: RequestBody): Flowable + fun dynamicPost(@Url url: String, @Body body: RequestBody): Single @PUT - fun dynamicPut(@Url url: String, @Body body: RequestBody): Flowable + fun dynamicPut(@Url url: String, @Body body: RequestBody): Single @PATCH - fun dynamicPatch(@Url url: String, @Body requestBody: RequestBody): Flowable + fun dynamicPatch(@Url url: String, @Body requestBody: RequestBody): Single @DELETE - fun dynamicDelete(@Url url: String): Flowable + fun dynamicDelete(@Url url: String, @Body requestBody: RequestBody): Single @Streaming @GET - fun dynamicDownload(@Url fileUrl: String): Flowable + fun dynamicDownload(@Url fileUrl: String): Single @Multipart @POST fun dynamicUpload(@Url url: String, @PartMap partMap: Map, - @Part file: List): Flowable + @Part file: List): Single } diff --git a/usecases/src/main/java/com/zeyad/usecases/requests/FileIORequest.kt b/usecases/src/main/java/com/zeyad/usecases/requests/FileIORequest.kt index c016632..27f6cb3 100644 --- a/usecases/src/main/java/com/zeyad/usecases/requests/FileIORequest.kt +++ b/usecases/src/main/java/com/zeyad/usecases/requests/FileIORequest.kt @@ -5,15 +5,11 @@ import android.os.Parcelable import com.zeyad.usecases.Mockable import java.io.File - /** * @author zeyad on 7/29/16. */ @Mockable data class FileIORequest private constructor(val url: String = "", - val onWifi: Boolean = false, - val whileCharging: Boolean = false, - val queuable: Boolean = false, val file: File? = File(""), val dataClass: Class<*>? = Any::class.java, val keyFileMap: HashMap? = hashMapOf(), @@ -21,9 +17,6 @@ data class FileIORequest private constructor(val url: String = "", constructor(parcel: Parcel) : this( parcel.readString(), - parcel.readByte() != 0.toByte(), - parcel.readByte() != 0.toByte(), - parcel.readByte() != 0.toByte(), File(""), Any::class.java, hashMapOf(), @@ -31,9 +24,6 @@ data class FileIORequest private constructor(val url: String = "", constructor (uploadRequestBuilder: Builder) : this( uploadRequestBuilder.url, - uploadRequestBuilder.onWifi, - uploadRequestBuilder.whileCharging, - uploadRequestBuilder.queuable, uploadRequestBuilder.file, uploadRequestBuilder.dataClass, uploadRequestBuilder.keyFileMap, @@ -44,9 +34,6 @@ data class FileIORequest private constructor(val url: String = "", override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeString(url) - parcel.writeByte(if (onWifi) 1 else 0) - parcel.writeByte(if (whileCharging) 1 else 0) - parcel.writeByte(if (queuable) 1 else 0) } override fun describeContents() = 0 @@ -85,13 +72,6 @@ data class FileIORequest private constructor(val url: String = "", return this } - fun queuable(onWifi: Boolean, whileCharging: Boolean): Builder { - this.queuable = true - this.onWifi = onWifi - this.whileCharging = whileCharging - return this - } - fun build(): FileIORequest { return FileIORequest(this) } diff --git a/usecases/src/main/java/com/zeyad/usecases/requests/GetRequest.kt b/usecases/src/main/java/com/zeyad/usecases/requests/GetRequest.kt index e38aa04..f37bd61 100644 --- a/usecases/src/main/java/com/zeyad/usecases/requests/GetRequest.kt +++ b/usecases/src/main/java/com/zeyad/usecases/requests/GetRequest.kt @@ -2,13 +2,11 @@ package com.zeyad.usecases.requests import com.zeyad.usecases.Config - /** * @author zeyad on 7/29/16. */ data class GetRequest private constructor(val fullUrl: String = "", val dataClass: Class<*>, - val idType: Class<*>, val persist: Boolean = false, val idColumnName: String = "id", val itemId: Any = Any(), @@ -16,7 +14,6 @@ data class GetRequest private constructor(val fullUrl: String = "", constructor(builder: Builder) : this(builder.url, builder.dataClass, - builder.idType, builder.persist, builder.idColumnName, builder.itemId, @@ -29,7 +26,6 @@ data class GetRequest private constructor(val fullUrl: String = "", internal var shouldCache: Boolean = false internal var idColumnName: String = "" internal var url: String = "" - internal var idType: Class<*> = Any::class.java fun url(url: String): Builder { this.url = Config.baseURL + url @@ -47,9 +43,8 @@ data class GetRequest private constructor(val fullUrl: String = "", return this } - fun id(id: Any, idColumnName: String, type: Class<*>): Builder { + fun id(id: Any, idColumnName: String): Builder { itemId = id - idType = type this.idColumnName = idColumnName return this } diff --git a/usecases/src/main/java/com/zeyad/usecases/requests/PostRequest.kt b/usecases/src/main/java/com/zeyad/usecases/requests/PostRequest.kt index 675ba54..07a02eb 100644 --- a/usecases/src/main/java/com/zeyad/usecases/requests/PostRequest.kt +++ b/usecases/src/main/java/com/zeyad/usecases/requests/PostRequest.kt @@ -15,9 +15,6 @@ class PostRequest private constructor(val fullUrl: String = "", val requestType: Class<*> = Any::class.java, private val responseType: Class<*> = Any::class.java, val persist: Boolean = false, - val onWifi: Boolean = false, - val whileCharging: Boolean = false, - val queuable: Boolean = false, val cache: Boolean = false, val idColumnName: String, val idType: Class<*> = Any::class.java, @@ -41,9 +38,6 @@ class PostRequest private constructor(val fullUrl: String = "", builder.requestType, builder.responseType, builder.persist, - builder.onWifi, - builder.whileCharging, - builder.queuable, builder.cache, builder.idColumnName, builder.idType, @@ -51,7 +45,7 @@ class PostRequest private constructor(val fullUrl: String = "", builder.keyValuePairs, builder.jsonArray, builder.jsonObject, - builder.`object`) + builder.any) constructor(parcel: Parcel) : this( parcel.readString(), @@ -59,9 +53,6 @@ class PostRequest private constructor(val fullUrl: String = "", Any::class.java, parcel.readByte() != 0.toByte(), parcel.readByte() != 0.toByte(), - parcel.readByte() != 0.toByte(), - parcel.readByte() != 0.toByte(), - parcel.readByte() != 0.toByte(), parcel.readString(), Any::class.java, parcel.readString(), @@ -134,9 +125,6 @@ class PostRequest private constructor(val fullUrl: String = "", override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeString(fullUrl) parcel.writeByte(if (persist) 1 else 0) - parcel.writeByte(if (onWifi) 1 else 0) - parcel.writeByte(if (whileCharging) 1 else 0) - parcel.writeByte(if (queuable) 1 else 0) parcel.writeByte(if (cache) 1 else 0) parcel.writeString(idColumnName) parcel.writeString(method) @@ -157,7 +145,7 @@ class PostRequest private constructor(val fullUrl: String = "", } class Builder(internal var requestType: Class<*>, internal var persist: Boolean) { - internal var `object`: Any? = null + internal var any: Any? = null internal var jsonArray: JSONArray? = null internal var jsonObject: JSONObject? = null internal var keyValuePairs: HashMap? = null @@ -186,13 +174,6 @@ class PostRequest private constructor(val fullUrl: String = "", return this } - fun queuable(onWifi: Boolean, whileCharging: Boolean): Builder { - queuable = true - this.onWifi = onWifi - this.whileCharging = whileCharging - return this - } - fun cache(): Builder { cache = true return this @@ -204,8 +185,8 @@ class PostRequest private constructor(val fullUrl: String = "", return this } - fun payLoad(`object`: Any): Builder { - this.`object` = `object` + fun payLoad(any: Any): Builder { + this.any = any return this } diff --git a/usecases/src/main/java/com/zeyad/usecases/stores/CloudStore.kt b/usecases/src/main/java/com/zeyad/usecases/stores/CloudStore.kt index a57c6e7..2a9fd22 100644 --- a/usecases/src/main/java/com/zeyad/usecases/stores/CloudStore.kt +++ b/usecases/src/main/java/com/zeyad/usecases/stores/CloudStore.kt @@ -4,7 +4,6 @@ import android.util.Log import com.zeyad.usecases.* import com.zeyad.usecases.Config.gson import com.zeyad.usecases.db.DataBaseManager -import com.zeyad.usecases.db.RealmQueryProvider import com.zeyad.usecases.exceptions.NetworkConnectionException import com.zeyad.usecases.mapper.DAOMapper import com.zeyad.usecases.network.ApiConnection @@ -12,7 +11,6 @@ import io.reactivex.Flowable import io.reactivex.Single import io.reactivex.SingleObserver import io.reactivex.disposables.Disposable -import io.realm.RealmModel import okhttp3.MediaType import okhttp3.MultipartBody import okhttp3.RequestBody @@ -22,7 +20,7 @@ import java.io.* import java.util.* @Mockable -class CloudStore(private val mApiConnection: ApiConnection, // private static final int COUNTER_START = 1, ATTEMPTS = 3; +class CloudStore(private val mApiConnection: ApiConnection, private val mDataBaseManager: DataBaseManager?, private val mEntityDataMapper: DAOMapper, private val mMemoryStore: MemoryStore?) : DataStore { @@ -30,132 +28,149 @@ class CloudStore(private val mApiConnection: ApiConnection, // private static Config.cloudStore = this } - private fun getErrorFlowableNotPersisted(): Flowable { - return Flowable.error(NetworkConnectionException("Could not reach server and could not persist to queue!")) + private fun getErrorSingleNotPersisted(): Single { + return Single.error(NetworkConnectionException("Could not reach server and could not persist to queue!")) + } + + private inline fun persistErrorExecute(disk: () -> Unit, network: () -> Single): Single { + disk.invoke() + return if (isNetworkNotAvailable(Config.context)) { + getErrorSingleNotPersisted() + } else network.invoke() } override fun dynamicGetList(url: String, idColumnName: String, requestType: Class, persist: Boolean, shouldCache: Boolean): Flowable> { return mApiConnection.dynamicGetList(url, shouldCache) - .map { entities: List -> mEntityDataMapper.mapAllTo>(entities, requestType) } - .doOnNext { list: List -> - if (withDisk(persist)) { - saveAllToDisk(list, requestType) - } - if (withCache(shouldCache)) { - saveAllToMemory(idColumnName, JSONArray(gson.toJson(list)), requestType) - } - } + .map { mEntityDataMapper.mapAllTo>(it, requestType) } + .doOnNext { saveAllLocally(idColumnName, it, requestType, persist, shouldCache) } } - override fun dynamicGetObject(url: String, idColumnName: String, itemId: Any, itemIdType: Class<*>, + override fun dynamicGetObject(url: String, idColumnName: String, itemId: Any, requestType: Class, persist: Boolean, shouldCache: Boolean): Flowable { return mApiConnection.dynamicGetObject(url, shouldCache) - .doOnNext { m: M -> - saveLocally(idColumnName, itemIdType, - JSONObject(gson.toJson(m)), requestType, persist, shouldCache) + .doOnNext { + saveLocally(idColumnName, JSONObject(gson.toJson(it)), requestType, persist, shouldCache) } - .map { entity: M -> mEntityDataMapper.mapTo(entity, requestType) } + .map { mEntityDataMapper.mapTo(it, requestType) } } - override fun queryDisk(queryFactory: RealmQueryProvider): Flowable> { + override fun queryDisk(query: String, clazz: Class): Flowable { return Flowable.error(IllegalAccessException("Can not search disk in cloud data store!")) } - override fun dynamicPatchObject(url: String, idColumnName: String, itemIdType: Class<*>, + override fun dynamicPatchObject(url: String, idColumnName: String, jsonObject: JSONObject, requestType: Class<*>, responseType: Class, - persist: Boolean, cache: Boolean, queuable: Boolean): Flowable { - return Flowable.defer { - saveLocally(idColumnName, itemIdType, jsonObject, requestType, persist, cache) - if (!isNetworkAvailable(Config.context)) { - getErrorFlowableNotPersisted() - } else + persist: Boolean, cache: Boolean): Single { + return Single.defer { + persistErrorExecute({ + saveLocally(idColumnName, jsonObject, requestType, persist, cache) + }, { mApiConnection.dynamicPatch(url, RequestBody.create(MediaType.parse(APPLICATION_JSON), jsonObject.toString())) - .map { `object`: M -> daoMapHelper(responseType, `object`) } + .map { daoMapHelper(responseType, it) } + }) } } - override fun dynamicPostObject(url: String, idColumnName: String, itemIdType: Class<*>, + override fun dynamicPostObject(url: String, idColumnName: String, jsonObject: JSONObject, requestType: Class<*>, responseType: Class, - persist: Boolean, cache: Boolean, queuable: Boolean): Flowable { - return Flowable.defer { - saveLocally(idColumnName, itemIdType, jsonObject, requestType, persist, cache) - if (!isNetworkAvailable(Config.context)) { - getErrorFlowableNotPersisted() - } else + persist: Boolean, cache: Boolean): Single { + return Single.defer { + persistErrorExecute({ + saveLocally(idColumnName, jsonObject, requestType, persist, cache) + }, { mApiConnection.dynamicPost(url, RequestBody.create(MediaType.parse(APPLICATION_JSON), jsonObject.toString())) - .map { `object`: M -> daoMapHelper(responseType, `object`) } + .map { daoMapHelper(responseType, it) } + }) } } - override fun dynamicPostList(url: String, idColumnName: String, itemIdType: Class<*>, + override fun dynamicPostList(url: String, idColumnName: String, jsonArray: JSONArray, requestType: Class<*>, responseType: Class, - persist: Boolean, cache: Boolean, queuable: Boolean): Flowable { - return Flowable.defer { - saveAllLocally(idColumnName, itemIdType, jsonArray, requestType, persist, cache) - if (!isNetworkAvailable(Config.context)) { - getErrorFlowableNotPersisted() - } else + persist: Boolean, cache: Boolean): Single { + return Single.defer { + persistErrorExecute({ + saveAllLocally(idColumnName, jsonArray, requestType, persist, cache) + }, { mApiConnection.dynamicPost(url, RequestBody.create(MediaType.parse(APPLICATION_JSON), jsonArray.toString())) - .map { `object`: M -> daoMapHelper(responseType, `object`) } + .map { daoMapHelper(responseType, it) } + }) } } - override fun dynamicPutObject(url: String, idColumnName: String, itemIdType: Class<*>, + override fun dynamicPutObject(url: String, idColumnName: String, jsonObject: JSONObject, requestType: Class<*>, responseType: Class, - persist: Boolean, cache: Boolean, queuable: Boolean): Flowable { - return Flowable.defer { - saveLocally(idColumnName, itemIdType, jsonObject, requestType, persist, cache) - if (!isNetworkAvailable(Config.context)) { - getErrorFlowableNotPersisted() - } else + persist: Boolean, cache: Boolean): Single { + return Single.defer { + persistErrorExecute({ + saveLocally(idColumnName, jsonObject, requestType, persist, cache) + }, { mApiConnection.dynamicPut(url, RequestBody.create(MediaType.parse(APPLICATION_JSON), jsonObject.toString())) - .map { `object` -> daoMapHelper(responseType, `object`) } + .map { daoMapHelper(responseType, it) } + }) } } - override fun dynamicPutList(url: String, idColumnName: String, itemIdType: Class<*>, + override fun dynamicPutList(url: String, idColumnName: String, jsonArray: JSONArray, requestType: Class<*>, responseType: Class, - persist: Boolean, cache: Boolean, queuable: Boolean): Flowable { - return Flowable.defer { - saveAllLocally(idColumnName, itemIdType, jsonArray, requestType, persist, cache) - if (!isNetworkAvailable(Config.context)) { - getErrorFlowableNotPersisted() - } else + persist: Boolean, cache: Boolean): Single { + return Single.defer { + persistErrorExecute({ + saveAllLocally(idColumnName, jsonArray, requestType, persist, cache) + }, { mApiConnection.dynamicPut(url, RequestBody.create(MediaType.parse(APPLICATION_JSON), jsonArray.toString())) - .map { `object` -> daoMapHelper(responseType, `object`) } + .map { daoMapHelper(responseType, it) } + }) } } + // TODO("Finish and Fix") override fun dynamicDeleteCollection(url: String, idColumnName: String, itemIdType: Class<*>, jsonArray: JSONArray, requestType: Class<*>, responseType: Class, - persist: Boolean, cache: Boolean, queuable: Boolean): Flowable { - return Flowable.defer { - deleteLocally(convertToListOfId(jsonArray, itemIdType), idColumnName, itemIdType, - requestType, persist, cache) - if (!isNetworkAvailable(Config.context)) { - getErrorFlowableNotPersisted() - } else - mApiConnection.dynamicDelete(url) - .map { `object`: M -> daoMapHelper(responseType, `object`) } + persist: Boolean, cache: Boolean): Single { + return Single.defer { + persistErrorExecute({ + val list = mutableListOf() + for (i in 0..(jsonArray.length() - 1)) { + list.add(gson.fromJson(jsonArray.getJSONObject(i).toString(), requestType)) + } + deleteLocally(list, idColumnName, requestType, persist, cache) + }, { + mApiConnection.dynamicDelete(url, + RequestBody.create(MediaType.parse(APPLICATION_JSON), jsonArray.toString())) + .map { daoMapHelper(responseType, it) } + }) + } + } + + override fun dynamicDeleteCollectionById(url: String, idColumnName: String, itemIdType: Class<*>, jsonArray: JSONArray, requestType: Class<*>, responseType: Class, persist: Boolean, cache: Boolean): Single { + return Single.defer { + persistErrorExecute({ + deleteLocallyById(convertToListOfId(jsonArray, itemIdType), idColumnName, requestType, + persist, cache) + }, { + mApiConnection.dynamicDelete(url, + RequestBody.create(MediaType.parse(APPLICATION_JSON), jsonArray.toString())) + .map { daoMapHelper(responseType, it) } + }) } } + override fun dynamicDeleteAll(requestType: Class<*>): Single { return Single.error(IllegalStateException("Can not delete all from cloud data store!")) } - override fun dynamicDownloadFile(url: String, file: File, onWifi: Boolean, - whileCharging: Boolean, queuable: Boolean): Flowable { - return Flowable.defer { - if (!isNetworkAvailable(Config.context)) { - getErrorFlowableNotPersisted() + override fun dynamicDownloadFile(url: String, file: File): Single { + return Single.defer { + if (isNetworkNotAvailable(Config.context)) { + getErrorSingleNotPersisted() } else mApiConnection.dynamicDownload(url) .map { responseBody -> try { @@ -192,9 +207,8 @@ class CloudStore(private val mApiConnection: ApiConnection, // private static } override fun dynamicUploadFile(url: String, keyFileMap: HashMap, - parameters: HashMap, onWifi: Boolean, whileCharging: Boolean, - queuable: Boolean, responseType: Class): Flowable { - return Flowable.defer { + parameters: HashMap, responseType: Class): Single { + return Single.defer { val multiPartBodyParts = ArrayList() keyFileMap.toMap().forEach { entry -> multiPartBodyParts.add(MultipartBody.Part.createFormData(entry.key, @@ -206,60 +220,88 @@ class CloudStore(private val mApiConnection: ApiConnection, // private static map[key] = RequestBody.create(MediaType.parse(MULTIPART_FORM_DATA), value.toString()) } - if (!isNetworkAvailable(Config.context)) { - getErrorFlowableNotPersisted() + if (isNetworkNotAvailable(Config.context)) { + getErrorSingleNotPersisted() } else mApiConnection.dynamicUpload(url, map, multiPartBodyParts) - .map { `object` -> daoMapHelper(responseType, `object`) } + .map { daoMapHelper(responseType, it) } } } - private fun daoMapHelper(requestType: Class<*>, `object`: M): M? { - return if (`object` is List<*>) - mEntityDataMapper.mapAllTo(`object` as List<*>, requestType) + private fun daoMapHelper(requestType: Class<*>, model: M): M? { + return if (model is List<*>) + mEntityDataMapper.mapAllTo(model as List<*>, requestType) else - mEntityDataMapper.mapTo(`object`, requestType) + mEntityDataMapper.mapTo(model, requestType) } - private fun saveAllToDisk(collection: List<*>, requestType: Class<*>) { - mDataBaseManager?.putAll(collection as List, requestType) + private fun saveAllToDisk(collection: List, requestType: Class) { + mDataBaseManager?.putAll(collection, requestType) ?.subscribeOn(Config.backgroundThread) - ?.subscribe(SimpleSubscriber(requestType)) + ?.subscribe(SimpleSingleObserver()) } private fun saveAllToMemory(idColumnName: String, jsonArray: JSONArray, requestType: Class<*>) { mMemoryStore?.cacheList(idColumnName, jsonArray, requestType) } - private fun saveLocally(idColumnName: String, itemIdType: Class<*>, jsonObject: JSONObject, + private fun saveAllLocally(idColumnName: String, collection: List, + requestType: Class, persist: Boolean, cache: Boolean) { + if (withDisk(persist)) { + saveAllToDisk(collection, requestType) + } + if (withCache(cache)) { + saveAllToMemory(idColumnName, JSONArray(gson.toJson(collection)), requestType) + } + } + + private fun saveLocally(idColumnName: String, jsonObject: JSONObject, requestType: Class<*>, persist: Boolean, cache: Boolean) { if (withDisk(persist)) { - mDataBaseManager?.put(jsonObject, idColumnName, itemIdType, requestType) + mDataBaseManager?.put(jsonObject, requestType) ?.subscribeOn(Config.backgroundThread) - ?.subscribe(SimpleSubscriber(requestType)) + ?.subscribe(SimpleSingleObserver()) } if (withCache(cache)) { mMemoryStore?.cacheObject(idColumnName, jsonObject, requestType) } } - private fun saveAllLocally(idColumnName: String, itemIdType: Class<*>, jsonArray: JSONArray, - requestType: Class<*>, persist: Boolean, cache: Boolean) { + private fun saveAllLocally(idColumnName: String, jsonArray: JSONArray, requestType: Class<*>, + persist: Boolean, cache: Boolean) { if (withDisk(persist)) { - mDataBaseManager?.putAll(jsonArray, idColumnName, itemIdType, requestType) + mDataBaseManager?.putAll(jsonArray, requestType) ?.subscribeOn(Config.backgroundThread) - ?.subscribe(SimpleSubscriber(requestType)) + ?.subscribe(SimpleSingleObserver()) } if (withCache(cache)) { mMemoryStore?.cacheList(idColumnName, jsonArray, requestType) } } - private fun deleteLocally(ids: List, idColumnName: String, itemIdType: Class<*>, requestType: Class<*>, + private fun deleteLocally(list: List, idColumnName: String, requestType: Class<*>, persist: Boolean, cache: Boolean) { + if (withDisk(persist)) { + val collectionSize = list.size + for (i in 0 until collectionSize) { +// mDataBaseManager?.evictCollection(list, requestType) + } + } + if (withCache(cache)) { + val stringIds = Flowable.fromIterable(list) + .map { JSONObject(gson.toJson(it)).opt(idColumnName) } + .map { it.toString() } + .toList(list.size) + .blockingGet() + mMemoryStore?.deleteListById(stringIds, requestType) + } + } + + private fun deleteLocallyById(ids: List, idColumnName: String, requestType: Class<*>, + persist: Boolean, cache: Boolean) { if (withDisk(persist)) { val collectionSize = ids.size for (i in 0 until collectionSize) { - mDataBaseManager?.evictById(requestType, idColumnName, ids[i], itemIdType) + mDataBaseManager?.evictById(requestType, idColumnName, ids[i]) } } if (withCache(cache)) { @@ -267,18 +309,17 @@ class CloudStore(private val mApiConnection: ApiConnection, // private static .map { it.toString() } .toList(ids.size) .blockingGet() - mMemoryStore?.deleteList(stringIds, requestType) + mMemoryStore?.deleteListById(stringIds, requestType) } } - private class SimpleSubscriber internal constructor(private val mClass: Class<*>) : SingleObserver { + private class SimpleSingleObserver : SingleObserver { + private var subscription: Disposable? = null + override fun onSuccess(t: Any) { subscription!!.dispose() - Log.d(TAG, mClass.simpleName + " persisted!") } - private var subscription: Disposable? = null - override fun onSubscribe(d: Disposable) { subscription = d } diff --git a/usecases/src/main/java/com/zeyad/usecases/stores/DataStore.kt b/usecases/src/main/java/com/zeyad/usecases/stores/DataStore.kt index 853149c..3721215 100644 --- a/usecases/src/main/java/com/zeyad/usecases/stores/DataStore.kt +++ b/usecases/src/main/java/com/zeyad/usecases/stores/DataStore.kt @@ -1,10 +1,8 @@ package com.zeyad.usecases.stores -import com.zeyad.usecases.db.RealmQueryProvider import io.reactivex.Completable import io.reactivex.Flowable import io.reactivex.Single -import io.realm.RealmModel import org.json.JSONArray import org.json.JSONObject import java.io.File @@ -15,67 +13,107 @@ import java.util.* */ interface DataStore { - fun dynamicGetList(url: String, idColumnName: String, requestType: Class, - persist: Boolean, shouldCache: Boolean): Flowable> + fun dynamicGetList(url: String, + idColumnName: String, + requestType: Class, + persist: Boolean, + shouldCache: Boolean): Flowable> /** * Get an [Flowable] which will emit a Object by its id. */ - fun dynamicGetObject(url: String, idColumnName: String, itemId: Any, itemIdType: Class<*>, - requestType: Class, persist: Boolean, shouldCache: Boolean): Flowable + fun dynamicGetObject(url: String, + idColumnName: String, + itemId: Any, + requestType: Class, + persist: Boolean, + shouldCache: Boolean): Flowable /** - * Search disk with a RealmQuery which returns an [Flowable] that will emit a list of + * Search disk with a query which returns an [Flowable] that will emit a list of * Object. */ - fun queryDisk(queryFactory: RealmQueryProvider): Flowable> + fun queryDisk(query: String, clazz: Class): Flowable /** * Patch a JSONObject which returns an [Flowable] that will emit a Object. */ - fun dynamicPatchObject(url: String, idColumnName: String, itemIdType: Class<*>, - jsonObject: JSONObject, requestType: Class<*>, - responseType: Class, persist: Boolean, cache: Boolean, - queuable: Boolean): Flowable + fun dynamicPatchObject(url: String, + idColumnName: String, + jsonObject: JSONObject, + requestType: Class<*>, + responseType: Class, + persist: Boolean, + cache: Boolean): Single /** * Post a JSONObject which returns an [Flowable] that will emit a Object. */ - fun dynamicPostObject(url: String, idColumnName: String, itemIdType: Class<*>, - jsonObject: JSONObject, requestType: Class<*>, responseType: Class, - persist: Boolean, cache: Boolean, queuable: Boolean): Flowable + fun dynamicPostObject(url: String, + idColumnName: String, + jsonObject: JSONObject, + requestType: Class<*>, + responseType: Class, + persist: Boolean, + cache: Boolean): Single /** * Post a HashMap, Object> which returns an [Flowable] that will emit a list of * Object. */ - fun dynamicPostList(url: String, idColumnName: String, itemIdType: Class<*>, - jsonArray: JSONArray, requestType: Class<*>, responseType: Class, - persist: Boolean, cache: Boolean, queuable: Boolean): Flowable + fun dynamicPostList(url: String, + idColumnName: String, + jsonArray: JSONArray, + requestType: Class<*>, + responseType: Class, + persist: Boolean, + cache: Boolean): Single /** * Put a HashMap, Object> disk with a RealmQuery which returns an [Flowable] that * will emit a Object. */ - fun dynamicPutObject(url: String, idColumnName: String, itemIdType: Class<*>, - jsonObject: JSONObject, requestType: Class<*>, responseType: Class, - persist: Boolean, cache: Boolean, queuable: Boolean): Flowable + fun dynamicPutObject(url: String, + idColumnName: String, + jsonObject: JSONObject, + requestType: Class<*>, + responseType: Class, + persist: Boolean, + cache: Boolean): Single /** * Put a HashMap, Object> disk with a RealmQuery which returns an [Flowable] that * will emit a list of Object. */ - fun dynamicPutList(url: String, idColumnName: String, itemIdType: Class<*>, - jsonArray: JSONArray, requestType: Class<*>, responseType: Class, - persist: Boolean, cache: Boolean, queuable: Boolean): Flowable + fun dynamicPutList(url: String, + idColumnName: String, + jsonArray: JSONArray, + requestType: Class<*>, + responseType: Class, + persist: Boolean, + cache: Boolean): Single /** * Delete a HashMap, Object> from cloud which returns an [Flowable] that will emit * a Object. */ - fun dynamicDeleteCollection(url: String, idColumnName: String, itemIdType: Class<*>, - jsonArray: JSONArray, requestType: Class<*>, responseType: Class, - persist: Boolean, cache: Boolean, queuable: Boolean): Flowable + fun dynamicDeleteCollection(url: String, + idColumnName: String, + itemIdType: Class<*>, + jsonArray: JSONArray, + requestType: Class<*>, + responseType: Class, + persist: Boolean, + cache: Boolean): Single + + fun dynamicDeleteCollectionById(url: String, + idColumnName: String, + itemIdType: Class<*>, + jsonArray: JSONArray, + requestType: Class<*>, + responseType: Class, + persist: Boolean, + cache: Boolean): Single /** * Delete all items of the same type from cloud or disk which returns an [Completable] @@ -83,10 +121,11 @@ interface DataStore { */ fun dynamicDeleteAll(requestType: Class<*>): Single - fun dynamicDownloadFile(url: String, file: File, onWifi: Boolean, whileCharging: Boolean, - queuable: Boolean): Flowable + fun dynamicDownloadFile(url: String, + file: File): Single - fun dynamicUploadFile(url: String, keyFileMap: HashMap, parameters: HashMap, - onWifi: Boolean, whileCharging: Boolean, queuable: Boolean, - responseType: Class): Flowable + fun dynamicUploadFile(url: String, + keyFileMap: HashMap, + parameters: HashMap, + responseType: Class): Single } diff --git a/usecases/src/main/java/com/zeyad/usecases/stores/DataStoreFactory.kt b/usecases/src/main/java/com/zeyad/usecases/stores/DataStoreFactory.kt index eb98756..14656d2 100644 --- a/usecases/src/main/java/com/zeyad/usecases/stores/DataStoreFactory.kt +++ b/usecases/src/main/java/com/zeyad/usecases/stores/DataStoreFactory.kt @@ -7,6 +7,7 @@ import com.zeyad.usecases.network.ApiConnection import com.zeyad.usecases.utils.DataBaseManagerUtil @Mockable +//class DataStoreFactory(private val dataBaseManagerUtil: DataBaseManagerUtil?, class DataStoreFactory(private val dataBaseManagerUtil: DataBaseManagerUtil?, private val restApi: ApiConnection, private val daoMapper: DAOMapper) { @@ -61,6 +62,6 @@ class DataStoreFactory(private val dataBaseManagerUtil: DataBaseManagerUtil?, private var cloudStore: CloudStore? = null private var diskStore: DiskStore? = null private var memoryStore: MemoryStore? = null - private const val DB_NOT_ENABLED = "Database not enabled!" + private const val DB_NOT_ENABLED = "ServiceDatabase not enabled!" } } diff --git a/usecases/src/main/java/com/zeyad/usecases/stores/DiskStore.kt b/usecases/src/main/java/com/zeyad/usecases/stores/DiskStore.kt index b58854a..e704dab 100644 --- a/usecases/src/main/java/com/zeyad/usecases/stores/DiskStore.kt +++ b/usecases/src/main/java/com/zeyad/usecases/stores/DiskStore.kt @@ -2,14 +2,11 @@ package com.zeyad.usecases.stores import com.zeyad.usecases.Config.gson import com.zeyad.usecases.Mockable -import com.zeyad.usecases.convertToListOfId import com.zeyad.usecases.convertToStringListOfId import com.zeyad.usecases.db.DataBaseManager -import com.zeyad.usecases.db.RealmQueryProvider import com.zeyad.usecases.withCache import io.reactivex.Flowable import io.reactivex.Single -import io.realm.RealmModel import org.json.JSONArray import org.json.JSONObject import java.io.File @@ -19,21 +16,27 @@ import java.util.* class DiskStore(private val mDataBaseManager: DataBaseManager, private val mMemoryStore: MemoryStore?) : DataStore { + private inline fun onNext(shouldCache: Boolean, block: () -> Unit) { + if (withCache(shouldCache)) { + block.invoke() + } + } + override fun dynamicGetList(url: String, idColumnName: String, requestType: Class, persist: Boolean, shouldCache: Boolean): Flowable> { - return mDataBaseManager.getAll(requestType) + return mDataBaseManager.getAll(requestType) .doOnNext { ms: List -> - if (withCache(shouldCache)) { + onNext(shouldCache) { mMemoryStore?.cacheList(idColumnName, JSONArray(gson.toJson(ms)), requestType) } } } - override fun dynamicGetObject(url: String, idColumnName: String, itemId: Any, itemIdType: Class<*>, + override fun dynamicGetObject(url: String, idColumnName: String, itemId: Any, requestType: Class, persist: Boolean, shouldCache: Boolean): Flowable { - return mDataBaseManager.getById(idColumnName, itemId, itemIdType, requestType) + return mDataBaseManager.getById(idColumnName, itemId, requestType) .doOnNext { m: M -> - if (withCache(shouldCache)) { + onNext(shouldCache) { mMemoryStore?.cacheObject(idColumnName, JSONObject(gson.toJson(m)), requestType) } } @@ -42,128 +45,144 @@ class DiskStore(private val mDataBaseManager: DataBaseManager, /** * Patch a JSONObject which returns an [Flowable] that will emit a Object. */ - override fun dynamicPatchObject(url: String, idColumnName: String, itemIdType: Class<*>, + override fun dynamicPatchObject(url: String, idColumnName: String, jsonObject: JSONObject, requestType: Class<*>, responseType: Class, - persist: Boolean, cache: Boolean, queuable: Boolean): Flowable { - return mDataBaseManager.put(jsonObject, idColumnName, itemIdType, requestType) + persist: Boolean, cache: Boolean): Single { + return mDataBaseManager.put(jsonObject, requestType) .doOnSuccess { - if (withCache(cache)) { + onNext(cache) { mMemoryStore?.cacheObject(idColumnName, JSONObject(gson.toJson(jsonObject)), requestType) } } .map { it as M } - .toFlowable() } /** - * Post a JSONObject which returns an [Flowable] that will emit a Object. + * Post a JSONObject which returns an [Single] that will emit a Object. */ - override fun dynamicPostObject(url: String, idColumnName: String, itemIdType: Class<*>, - jsonObject: JSONObject, requestType: Class<*>, - responseType: Class, persist: Boolean, cache: Boolean, - queuable: Boolean): Flowable { - return mDataBaseManager.put(jsonObject, idColumnName, itemIdType, requestType) + override fun dynamicPostObject(url: String, idColumnName: String, jsonObject: JSONObject, + requestType: Class<*>, responseType: Class, persist: Boolean, + cache: Boolean): Single { + return mDataBaseManager.put(jsonObject, requestType) .doOnSuccess { - if (withCache(cache)) { + onNext(cache) { mMemoryStore?.cacheObject(idColumnName, JSONObject(gson.toJson(jsonObject)), requestType) } } .map { it as M } - .toFlowable() } /** - * Post a HashMap, Object> which returns an [Flowable] that will emit a list of + * Post a HashMap, Object> which returns an [Single] that will emit a list of * Object. */ - override fun dynamicPostList(url: String, idColumnName: String, itemIdType: Class<*>, + override fun dynamicPostList(url: String, idColumnName: String, jsonArray: JSONArray, requestType: Class<*>, responseType: Class, - persist: Boolean, cache: Boolean, queuable: Boolean): Flowable { - return mDataBaseManager.putAll(jsonArray, idColumnName, itemIdType, requestType) + persist: Boolean, cache: Boolean): Single { + return mDataBaseManager.putAll(jsonArray, requestType) .doOnSuccess { - if (withCache(cache)) { + onNext(cache) { mMemoryStore?.cacheList(idColumnName, jsonArray, requestType) } } .map { it as M } - .toFlowable() } /** - * Put a HashMap, Object> disk with a RealmQuery which returns an [Flowable] that + * Put a HashMap, Object> disk with a RealmQuery which returns an [Single] that * will emit a Object. */ - override fun dynamicPutObject(url: String, idColumnName: String, itemIdType: Class<*>, + override fun dynamicPutObject(url: String, idColumnName: String, jsonObject: JSONObject, requestType: Class<*>, responseType: Class, - persist: Boolean, cache: Boolean, queuable: Boolean): Flowable { - return mDataBaseManager.put(jsonObject, idColumnName, itemIdType, requestType) + persist: Boolean, cache: Boolean): Single { + return mDataBaseManager.put(jsonObject, requestType) .doOnSuccess { - if (withCache(cache)) { + onNext(cache) { mMemoryStore?.cacheObject(idColumnName, JSONObject(gson.toJson(jsonObject)), requestType) } } .map { it as M } - .toFlowable() } /** - * Put a HashMap, Object> disk with a RealmQuery which returns an [Flowable] that + * Put a HashMap, Object> disk with a RealmQuery which returns an [Single] that * will emit a list of Object. */ - override fun dynamicPutList(url: String, idColumnName: String, itemIdType: Class<*>, + override fun dynamicPutList(url: String, idColumnName: String, jsonArray: JSONArray, requestType: Class<*>, responseType: Class, - persist: Boolean, cache: Boolean, queuable: Boolean): Flowable { - return mDataBaseManager.putAll(jsonArray, idColumnName, itemIdType, requestType) + persist: Boolean, cache: Boolean): Single { + return mDataBaseManager.putAll(jsonArray, requestType) .doOnSuccess { - if (withCache(cache)) { + onNext(cache) { mMemoryStore?.cacheList(idColumnName, jsonArray, requestType) } } .map { it as M } - .toFlowable() } /** - * Delete a HashMap, Object> from cloud which returns an [Flowable] that will emit + * Delete a HashMap, Object> from cloud which returns an [Single] that will emit * a Object. */ - override fun dynamicDeleteCollection(url: String, idColumnName: String, itemIdType: Class<*>, - jsonArray: JSONArray, requestType: Class<*>, responseType: Class, - persist: Boolean, cache: Boolean, queuable: Boolean): Flowable { + override fun dynamicDeleteCollection(url: String, + idColumnName: String, + itemIdType: Class<*>, + jsonArray: JSONArray, + requestType: Class<*>, + responseType: Class, + persist: Boolean, + cache: Boolean): Single { + throw NotImplementedError("Not Implemented Yet") +// val stringIds = convertToStringListOfId(jsonArray) +// return mDataBaseManager.evictCollection(idColumnName, convertToListOfId(jsonArray, itemIdType), +// itemIdType, requestType) +// .doOnSuccess { +// onNext(cache) { +// mMemoryStore?.deleteListById(stringIds, requestType) +// } +// } +// .map { it as M } +// .toSingle() + } + + override fun dynamicDeleteCollectionById(url: String, + idColumnName: String, + itemIdType: Class<*>, + jsonArray: JSONArray, + requestType: Class<*>, + responseType: Class, + persist: Boolean, + cache: Boolean): Single { val stringIds = convertToStringListOfId(jsonArray) - return mDataBaseManager.evictCollection(idColumnName, convertToListOfId(jsonArray, itemIdType), - itemIdType, requestType) + return mDataBaseManager.evictCollectionById(stringIds, requestType, idColumnName) .doOnSuccess { - if (withCache(cache)) { - mMemoryStore?.deleteList(stringIds, requestType) + onNext(cache) { + mMemoryStore?.deleteListById(stringIds.map { toString() }, requestType) } } .map { it as M } - .toFlowable() } - override fun queryDisk(queryFactory: RealmQueryProvider): Flowable> { - return mDataBaseManager.getQuery(queryFactory) + override fun queryDisk(query: String, clazz: Class): Flowable { + return mDataBaseManager.getQuery(query, clazz) } override fun dynamicDeleteAll(requestType: Class<*>): Single { - return mDataBaseManager.evictAll(requestType) + return mDataBaseManager.evictAll(requestType).map { it as Boolean } } - override fun dynamicDownloadFile(url: String, file: File, onWifi: Boolean, whileCharging: Boolean, - queuable: Boolean): Flowable { - return Flowable.error(IllegalStateException(IO_DB_ERROR)) + override fun dynamicDownloadFile(url: String, file: File): Single { + return Single.error(IllegalStateException(IO_DB_ERROR)) } override fun dynamicUploadFile(url: String, keyFileMap: HashMap, - parameters: HashMap, onWifi: Boolean, - whileCharging: Boolean, queuable: Boolean, responseType: Class): Flowable { - return Flowable.error(IllegalStateException(IO_DB_ERROR)) + parameters: HashMap, responseType: Class): Single { + return Single.error(IllegalStateException(IO_DB_ERROR)) } companion object { private const val IO_DB_ERROR = "Can not file IO to local DB" } -} \ No newline at end of file +} diff --git a/usecases/src/main/java/com/zeyad/usecases/stores/MemoryStore.kt b/usecases/src/main/java/com/zeyad/usecases/stores/MemoryStore.kt index fb87a3b..026872a 100644 --- a/usecases/src/main/java/com/zeyad/usecases/stores/MemoryStore.kt +++ b/usecases/src/main/java/com/zeyad/usecases/stores/MemoryStore.kt @@ -19,8 +19,8 @@ class MemoryStore(private val gson: Gson, private val mapOfIds: MutableMap, MutableSet> = mutableMapOf()) { fun getItem(itemId: String, dataClass: Class<*>): Single { - val key = dataClass.simpleName + itemId return Single.defer { + val key = dataClass.simpleName + itemId if (isValid(key)) { Storo.get(key, dataClass).async().firstElement().toSingle() } else { @@ -43,8 +43,8 @@ class MemoryStore(private val gson: Gson, false } } - .filter { s -> !missed[0] } - .map { key -> Storo.get(key, dataClass).execute() } + .filter { !missed[0] } + .map { Storo.get(it, dataClass).execute() } .toList(if (missed[0]) 0 else stringSet.size) .blockingGet() .toList() @@ -77,7 +77,7 @@ class MemoryStore(private val gson: Gson, } } - fun deleteList(ids: List, dataClass: Class<*>) { + fun deleteListById(ids: List, dataClass: Class<*>) { if (ids.isEmpty()) { return } diff --git a/usecases/src/test/java/com/zeyad/usecases/TestRealmModel.kt b/usecases/src/test/java/com/zeyad/usecases/TestRealmModel.kt index 59f2985..f51fe35 100644 --- a/usecases/src/test/java/com/zeyad/usecases/TestRealmModel.kt +++ b/usecases/src/test/java/com/zeyad/usecases/TestRealmModel.kt @@ -1,16 +1,12 @@ package com.zeyad.usecases +import android.arch.persistence.room.PrimaryKey import com.google.gson.annotations.SerializedName -import io.realm.RealmModel -import io.realm.annotations.PrimaryKey -import io.realm.annotations.RealmModule - /** * @author by ZIaDo on 2/13/17. */ -@RealmModule -class TestRealmModel : RealmModel { +class TestRealmModel { @SerializedName("id") @PrimaryKey var id: Int = 0 diff --git a/usecases/src/test/java/com/zeyad/usecases/api/DataServiceConfigTest.kt b/usecases/src/test/java/com/zeyad/usecases/api/DataServiceConfigTest.kt index da718ba..6906d1d 100644 --- a/usecases/src/test/java/com/zeyad/usecases/api/DataServiceConfigTest.kt +++ b/usecases/src/test/java/com/zeyad/usecases/api/DataServiceConfigTest.kt @@ -1,9 +1,10 @@ package com.zeyad.usecases.api import android.content.Context -import android.os.HandlerThread import android.support.test.rule.BuildConfig +import com.zeyad.usecases.db.DataBaseManager import com.zeyad.usecases.mapper.DAOMapper +import com.zeyad.usecases.utils.DataBaseManagerUtil import io.reactivex.Scheduler import io.reactivex.schedulers.Schedulers import okhttp3.Cache @@ -14,8 +15,8 @@ import org.hamcrest.Matchers.equalTo import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mockito.mock import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment import org.robolectric.annotation.Config import java.io.File import java.util.concurrent.TimeUnit @@ -36,7 +37,7 @@ class DataServiceConfigTest { @Before @Throws(Exception::class) fun setUp() { - mockContext = mock(Context::class.java) + mockContext = RuntimeEnvironment.application // mock(Context::class.java) builder = OkHttpClient.Builder() .connectTimeout(15, TimeUnit.SECONDS) .readTimeout(15, TimeUnit.SECONDS) @@ -46,9 +47,13 @@ class DataServiceConfigTest { .baseUrl(URL) .cacheSize(cacheSize) .okHttpBuilder(builder) - .okhttpCache(cache) + .okHttpCache(cache) .withCache(3, TimeUnit.MINUTES) - .withRealm() + .withSQLite(object : DataBaseManagerUtil { + override fun getDataBaseManager(dataClass: Class<*>): DataBaseManager? { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + }) .build() } @@ -118,11 +123,4 @@ class DataServiceConfigTest { fun getTimeUnit() { assertThat(mDataServiceConfig.timeUnit, `is`(equalTo(TimeUnit.MINUTES))) } - - @Test - @Throws(Exception::class) - fun getHandlerThread() { - assertThat( - mDataServiceConfig.handlerThread.javaClass, `is`(equalTo(HandlerThread::class.java))) - } } diff --git a/usecases/src/test/java/com/zeyad/usecases/api/DataServiceFactoryTest.kt b/usecases/src/test/java/com/zeyad/usecases/api/DataServiceFactoryTest.kt index c1cc8e4..02ef4fc 100644 --- a/usecases/src/test/java/com/zeyad/usecases/api/DataServiceFactoryTest.kt +++ b/usecases/src/test/java/com/zeyad/usecases/api/DataServiceFactoryTest.kt @@ -3,6 +3,8 @@ package com.zeyad.usecases.api import android.app.Application import android.content.Context import android.support.test.rule.BuildConfig +import com.zeyad.usecases.db.DataBaseManager +import com.zeyad.usecases.utils.DataBaseManagerUtil import junit.framework.Assert.assertEquals import junit.framework.Assert.assertNotNull import okhttp3.Cache @@ -43,8 +45,13 @@ class DataServiceFactoryTest { .baseUrl(URL) .cacheSize(cacheSize) .okHttpBuilder(builder) - .okhttpCache(cache) - .withRealm() + .okHttpCache(cache) + .withSQLite(object : DataBaseManagerUtil { + override fun getDataBaseManager(dataClass: Class<*>): DataBaseManager? { +// RoomManager() + return null + } + }) .build() } diff --git a/usecases/src/test/java/com/zeyad/usecases/api/DataServiceTest.kt b/usecases/src/test/java/com/zeyad/usecases/api/DataServiceTest.kt index 8ea9534..37d9509 100644 --- a/usecases/src/test/java/com/zeyad/usecases/api/DataServiceTest.kt +++ b/usecases/src/test/java/com/zeyad/usecases/api/DataServiceTest.kt @@ -3,7 +3,6 @@ package com.zeyad.usecases.api import android.support.test.rule.BuildConfig import com.zeyad.usecases.TestRealmModel import com.zeyad.usecases.anyObject -import com.zeyad.usecases.db.RealmQueryProvider import com.zeyad.usecases.requests.FileIORequest import com.zeyad.usecases.requests.GetRequest import com.zeyad.usecases.requests.PostRequest @@ -12,8 +11,6 @@ import io.reactivex.Flowable import io.reactivex.Scheduler import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers -import io.realm.Realm -import io.realm.RealmQuery import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -26,6 +23,7 @@ import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import java.io.File import java.util.* + /** * @author by ZIaDo on 5/9/17. */ @@ -38,17 +36,19 @@ class DataServiceTest { private lateinit var getRequest: GetRequest private lateinit var postRequest: PostRequest private lateinit var flowable: Flowable - private lateinit var fileFlowable: Flowable + private lateinit var single: Single + private lateinit var fileFlowable: Single @Before @Throws(Exception::class) fun setUp() { flowable = Flowable.just(true) - fileFlowable = Flowable.just(File("")) + single = Single.just(true) + fileFlowable = Single.just(File("")) postRequest = PostRequest.Builder(TestRealmModel::class.java, false).payLoad(Any()).build() getRequest = GetRequest.Builder(TestRealmModel::class.java, false) .url("") - .id(37, "id", Int::class.java) + .id(37, "id") .cache("id") .build() dataStoreFactory = mock(DataStoreFactory::class.java) @@ -96,7 +96,6 @@ class DataServiceTest { anyObject(), anyObject(), anyObject(), - anyBoolean(), anyBoolean())) .thenReturn(flowable) @@ -109,7 +108,6 @@ class DataServiceTest { anyObject(), anyObject(), anyObject(), - anyBoolean(), anyBoolean()) } @@ -146,7 +144,6 @@ class DataServiceTest { anyObject(), anyObject(), anyObject(), - anyBoolean(), anyBoolean())) .thenReturn(flowable) `when`(dataStoreFactory @@ -157,7 +154,6 @@ class DataServiceTest { anyObject(), anyObject(), anyObject(), - anyBoolean(), anyBoolean())) .thenReturn(flowable) @@ -175,7 +171,6 @@ class DataServiceTest { anyObject(), anyObject(), anyObject(), - anyBoolean(), anyBoolean()) verify(dataStoreFactory.disk(Any::class.java), times(1)) .dynamicGetObject( @@ -184,7 +179,6 @@ class DataServiceTest { anyObject(), anyObject(), anyObject(), - anyBoolean(), anyBoolean()) } @@ -199,10 +193,8 @@ class DataServiceTest { anyObject(), anyObject(), anyObject(), - anyBoolean(), - anyBoolean(), anyBoolean())) - .thenReturn(flowable) + .thenReturn(single) dataService.patchObject(postRequest) @@ -214,8 +206,6 @@ class DataServiceTest { anyObject(), anyObject(), anyObject(), - anyBoolean(), - anyBoolean(), anyBoolean()) } @@ -231,10 +221,8 @@ class DataServiceTest { anyObject(), anyObject(), anyObject(), - anyBoolean(), - anyBoolean(), anyBoolean())) - .thenReturn(flowable) + .thenReturn(single) dataService.postObject(postRequest) @@ -246,8 +234,6 @@ class DataServiceTest { anyObject(), anyObject(), anyObject(), - anyBoolean(), - anyBoolean(), anyBoolean()) } @@ -263,10 +249,8 @@ class DataServiceTest { anyObject(), anyObject(), anyObject(), - anyBoolean(), - anyBoolean(), anyBoolean())) - .thenReturn(flowable) + .thenReturn(single) dataService.postList(postRequest) @@ -278,8 +262,6 @@ class DataServiceTest { anyObject(), anyObject(), anyObject(), - anyBoolean(), - anyBoolean(), anyBoolean()) } @@ -295,10 +277,8 @@ class DataServiceTest { anyObject(), anyObject(), anyObject(), - anyBoolean(), - anyBoolean(), anyBoolean())) - .thenReturn(flowable) + .thenReturn(single) dataService.putObject(postRequest) @@ -310,8 +290,6 @@ class DataServiceTest { anyObject(), anyObject(), anyObject(), - anyBoolean(), - anyBoolean(), anyBoolean()) } @@ -327,10 +305,8 @@ class DataServiceTest { anyObject(), anyObject(), anyObject(), - anyBoolean(), - anyBoolean(), anyBoolean())) - .thenReturn(flowable) + .thenReturn(single) dataService.putList(postRequest) @@ -342,8 +318,6 @@ class DataServiceTest { anyObject(), anyObject(), anyObject(), - anyBoolean(), - anyBoolean(), anyBoolean()) } @@ -360,9 +334,8 @@ class DataServiceTest { anyObject(), anyObject(), anyBoolean(), - anyBoolean(), anyBoolean())) - .thenReturn(flowable) + .thenReturn(single) dataService.deleteItemById(postRequest) @@ -375,7 +348,6 @@ class DataServiceTest { anyObject(), anyObject(), anyBoolean(), - anyBoolean(), anyBoolean()) } @@ -392,9 +364,8 @@ class DataServiceTest { anyObject(), anyObject(), anyBoolean(), - anyBoolean(), anyBoolean())) - .thenReturn(flowable) + .thenReturn(single) dataService.deleteCollectionByIds(postRequest) @@ -407,7 +378,6 @@ class DataServiceTest { anyObject(), anyObject(), anyBoolean(), - anyBoolean(), anyBoolean()) } @@ -426,32 +396,26 @@ class DataServiceTest { @Throws(Exception::class) fun queryDisk() { `when`(dataStoreFactory.disk(Any::class.java) - .queryDisk(anyObject>())) + .queryDisk(anyString(), any(Class::class.java))) .thenReturn(Flowable.just(listOf(TestRealmModel()))) - dataService.queryDisk(object : RealmQueryProvider { - override fun create(realm: Realm): RealmQuery = - realm.where(TestRealmModel::class.java) - }) + dataService.queryDisk("", TestRealmModel::class.java) verify(dataStoreFactory.disk(Any::class.java), times(1)) - .queryDisk(anyObject>()) + .queryDisk(anyString(), any(Class::class.java)) } @Test @Throws(Exception::class) fun uploadFile() { - `when`>(dataStoreFactory + `when`>(dataStoreFactory .cloud(Any::class.java) .dynamicUploadFile( anyString(), anyMap() as HashMap, anyMap() as HashMap, - anyBoolean(), - anyBoolean(), - anyBoolean(), - anyObject())) - .thenReturn(flowable) + any(Class::class.java) as Class)) + .thenReturn(fileFlowable) dataService.uploadFile( FileIORequest.Builder("", File("")).responseType(Any::class.java).build()) @@ -461,31 +425,23 @@ class DataServiceTest { anyString(), anyMap() as HashMap, anyMap() as HashMap, - anyBoolean(), - anyBoolean(), - anyBoolean(), anyObject()) } @Test @Throws(Exception::class) fun downloadFile() { - `when`>(dataStoreFactory + `when`>(dataStoreFactory .cloud(Any::class.java) .dynamicDownloadFile( anyString(), - anyObject(), - anyBoolean(), - anyBoolean(), - anyBoolean())) + any(File::class.java))) .thenReturn(fileFlowable) dataService.downloadFile( FileIORequest.Builder("", File("")).responseType(Any::class.java).build()) verify(dataStoreFactory.cloud(Any::class.java), times(1)) - .dynamicDownloadFile( - anyString(), anyObject(), anyBoolean(), anyBoolean(), anyBoolean()) + .dynamicDownloadFile(anyString(), any(File::class.java)) } } - diff --git a/usecases/src/test/java/com/zeyad/usecases/db/RealmManagerTest.java b/usecases/src/test/java/com/zeyad/usecases/db/RealmManagerTest.java index 3a3fad4..d7ef9c2 100644 --- a/usecases/src/test/java/com/zeyad/usecases/db/RealmManagerTest.java +++ b/usecases/src/test/java/com/zeyad/usecases/db/RealmManagerTest.java @@ -1,168 +1,168 @@ -package com.zeyad.usecases.db; - -import android.support.test.rule.BuildConfig; - -import com.google.gson.Gson; -import com.zeyad.usecases.TestRealmModel; - -import org.json.JSONArray; -import org.json.JSONObject; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.rule.PowerMockRule; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; - -import io.reactivex.Flowable; -import io.reactivex.Single; -import io.reactivex.observers.TestObserver; -import io.reactivex.subscribers.TestSubscriber; -import io.realm.Realm; -import io.realm.RealmConfiguration; -import io.realm.RealmQuery; -import io.realm.RealmResults; - -import static junit.framework.Assert.assertEquals; -import static org.powermock.api.mockito.PowerMockito.mock; - -/** - * @author by ZIaDo on 2/15/17. - */ -@RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, sdk = 25) -@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"}) -@PrepareForTest({Realm.class, RealmQuery.class, RealmResults.class}) -public class RealmManagerTest { - - @Rule - public PowerMockRule rule = new PowerMockRule(); - private RealmManager mRealmManager; - - public Realm mockRealm() { - PowerMockito.mockStatic(Realm.class); - Realm mockRealm = mock(Realm.class); - RealmConfiguration realmConfiguration = mock(RealmConfiguration.class); - RealmQuery realmQuery = mock(RealmQuery.class); - RealmResults realmResults = mock(RealmResults.class); - realmResults.addAll(Collections.singleton(new TestRealmModel())); - Flowable flowable = Flowable.just(realmResults); - - PowerMockito.when(realmResults.isLoaded()).thenReturn(true); - PowerMockito.when(mockRealm.where(TestRealmModel.class)).thenReturn(realmQuery); - PowerMockito.when(mockRealm.where(TestRealmModel.class).equalTo("id", 1L)) - .thenReturn(realmQuery); - TestRealmModel value = new TestRealmModel(); - PowerMockito.when(mockRealm.where(TestRealmModel.class).equalTo("id", 1L).findFirst()) - .thenReturn(value); - - PowerMockito.when(mockRealm.where(TestRealmModel.class).equalTo("id", new Long(1))) - .thenReturn(realmQuery); - PowerMockito.when(mockRealm.where(TestRealmModel.class).equalTo("id", new Long(1)) - .findFirst()) - .thenReturn(value); - - PowerMockito.when(mockRealm.where(TestRealmModel.class).findAll()).thenReturn(realmResults); - PowerMockito.when(mockRealm.where(TestRealmModel.class).findAll().asFlowable()) - .thenReturn(flowable); - PowerMockito.when(Realm.getDefaultInstance()).thenReturn(mockRealm); - PowerMockito.when(mockRealm.getConfiguration()).thenReturn(realmConfiguration); - PowerMockito.when(Realm.getInstance(realmConfiguration)).thenReturn(mockRealm); - PowerMockito.when(mockRealm.copyFromRealm(value)).thenReturn(value); - return mockRealm; - } - - @Before - public void before() { - mockRealm(); - mRealmManager = new RealmManager(); - } - - @Test - public void getById() { - Flowable flowable = mRealmManager.getById("id", 1L, long.class, TestRealmModel.class); - - applyTestSubscriber(flowable); - - assertEquals(flowable.firstElement().blockingGet().getClass(), TestRealmModel.class); - } - - @Test - public void getAll() { - Flowable flowable = mRealmManager.getAll(TestRealmModel.class); - - TestSubscriber testSubscriber = new TestSubscriber<>(); - flowable.subscribe(testSubscriber); - testSubscriber.assertSubscribed(); - testSubscriber.assertErrorMessage("TestRealmModel(s) were not found!"); - } - - private void applyTestSubscriber(Flowable flowable) { - TestSubscriber testSubscriber = new TestSubscriber<>(); - flowable.subscribe(testSubscriber); - testSubscriber.assertNoErrors(); - testSubscriber.assertSubscribed(); - testSubscriber.assertComplete(); - } - - @Test - public void getQuery() { - Flowable flowable = mRealmManager.getQuery(realm -> realm.where(TestRealmModel.class)); - - applyTestSubscriber(flowable); - - assertEquals(LinkedList.class, flowable.firstElement().blockingGet().getClass()); - } - - @Test - public void putJSONObject() throws Exception { - Single completable = mRealmManager.put(new JSONObject(new Gson() - .toJson(new TestRealmModel())), "id", int.class, TestRealmModel.class); - applyTestSubscriber(completable); - } - - private void applyTestSubscriber(Single single) { - TestObserver testSubscriber = new TestObserver<>(); - single.subscribe(testSubscriber); - testSubscriber.assertComplete(); - } - - @Test - public void putAllJSONArray() { - Single completable = mRealmManager.putAll(new JSONArray(), "id", int.class, - TestRealmModel.class); - applyTestSubscriber(completable); - } - - @Test - public void putAllRealmObject() { - Single completable = mRealmManager.putAll(new ArrayList<>(), TestRealmModel.class); - applyTestSubscriber(completable); - } - - @Test - public void evictAll() { - Single completable = mRealmManager.evictAll(TestRealmModel.class); - applyTestSubscriber(completable); - } - - @Test - public void evictCollection() { - Single completable = - mRealmManager.evictCollection("id", new ArrayList<>(), String.class, TestRealmModel.class); - applyTestSubscriber(completable); - } - +//package com.zeyad.usecases.db; +// +//import android.support.test.rule.BuildConfig; +// +//import com.google.gson.Gson; +//import com.zeyad.usecases.TestRealmModel; +// +//import org.json.JSONArray; +//import org.json.JSONObject; +//import org.junit.Before; +//import org.junit.Rule; +//import org.junit.Test; +//import org.junit.runner.RunWith; +//import org.powermock.api.mockito.PowerMockito; +//import org.powermock.core.classloader.annotations.PowerMockIgnore; +//import org.powermock.core.classloader.annotations.PrepareForTest; +//import org.powermock.modules.junit4.rule.PowerMockRule; +//import org.robolectric.RobolectricTestRunner; +//import org.robolectric.annotation.Config; +// +//import java.util.ArrayList; +//import java.util.Collections; +//import java.util.LinkedList; +// +//import io.reactivex.Flowable; +//import io.reactivex.Single; +//import io.reactivex.observers.TestObserver; +//import io.reactivex.subscribers.TestSubscriber; +//import io.realm.Realm; +//import io.realm.RealmConfiguration; +//import io.realm.RealmQuery; +//import io.realm.RealmResults; +// +//import static junit.framework.Assert.assertEquals; +//import static org.powermock.api.mockito.PowerMockito.mock; +// +///** +// * @author by ZIaDo on 2/15/17. +// */ +//@RunWith(RobolectricTestRunner.class) +//@Config(constants = BuildConfig.class, sdk = 25) +//@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"}) +//@PrepareForTest({Realm.class, RealmQuery.class, RealmResults.class}) +//public class RealmManagerTest { +// +// @Rule +// public PowerMockRule rule = new PowerMockRule(); +// private RealmManager mRealmManager; +// +// public Realm mockRealm() { +// PowerMockito.mockStatic(Realm.class); +// Realm mockRealm = mock(Realm.class); +// RealmConfiguration realmConfiguration = mock(RealmConfiguration.class); +// RealmQuery realmQuery = mock(RealmQuery.class); +// RealmResults realmResults = mock(RealmResults.class); +// realmResults.addAll(Collections.singleton(new TestRealmModel())); +// Flowable flowable = Flowable.just(realmResults); +// +// PowerMockito.when(realmResults.isLoaded()).thenReturn(true); +// PowerMockito.when(mockRealm.where(TestRealmModel.class)).thenReturn(realmQuery); +// PowerMockito.when(mockRealm.where(TestRealmModel.class).equalTo("id", 1L)) +// .thenReturn(realmQuery); +// TestRealmModel value = new TestRealmModel(); +// PowerMockito.when(mockRealm.where(TestRealmModel.class).equalTo("id", 1L).findFirst()) +// .thenReturn(value); +// +// PowerMockito.when(mockRealm.where(TestRealmModel.class).equalTo("id", new Long(1))) +// .thenReturn(realmQuery); +// PowerMockito.when(mockRealm.where(TestRealmModel.class).equalTo("id", new Long(1)) +// .findFirst()) +// .thenReturn(value); +// +// PowerMockito.when(mockRealm.where(TestRealmModel.class).findAll()).thenReturn(realmResults); +// PowerMockito.when(mockRealm.where(TestRealmModel.class).findAll().asFlowable()) +// .thenReturn(flowable); +// PowerMockito.when(Realm.getDefaultInstance()).thenReturn(mockRealm); +// PowerMockito.when(mockRealm.getConfiguration()).thenReturn(realmConfiguration); +// PowerMockito.when(Realm.getInstance(realmConfiguration)).thenReturn(mockRealm); +// PowerMockito.when(mockRealm.copyFromRealm(value)).thenReturn(value); +// return mockRealm; +// } +// +// @Before +// public void before() { +// mockRealm(); +// mRealmManager = new RealmManager(); +// } +// +// @Test +// public void getById() { +// Flowable flowable = mRealmManager.getById("id", 1L, long.class, TestRealmModel.class); +// +// applyTestSubscriber(flowable); +// +// assertEquals(flowable.firstElement().blockingGet().getClass(), TestRealmModel.class); +// } +// +// @Test +// public void getAll() { +// Flowable flowable = mRealmManager.getAll(TestRealmModel.class); +// +// TestSubscriber testSubscriber = new TestSubscriber<>(); +// flowable.subscribe(testSubscriber); +// testSubscriber.assertSubscribed(); +// testSubscriber.assertErrorMessage("TestRealmModel(s) were not found!"); +// } +// +// private void applyTestSubscriber(Flowable flowable) { +// TestSubscriber testSubscriber = new TestSubscriber<>(); +// flowable.subscribe(testSubscriber); +// testSubscriber.assertNoErrors(); +// testSubscriber.assertSubscribed(); +// testSubscriber.assertComplete(); +// } +// +// @Test +// public void getQuery() { +// Flowable flowable = mRealmManager.getQuery(realm -> realm.where(TestRealmModel.class)); +// +// applyTestSubscriber(flowable); +// +// assertEquals(LinkedList.class, flowable.firstElement().blockingGet().getClass()); +// } +// +// @Test +// public void putJSONObject() throws Exception { +// Single completable = mRealmManager.put(new JSONObject(new Gson() +// .toJson(new TestRealmModel())), "id", int.class, TestRealmModel.class); +// applyTestSubscriber(completable); +// } +// +// private void applyTestSubscriber(Single single) { +// TestObserver testSubscriber = new TestObserver<>(); +// single.subscribe(testSubscriber); +// testSubscriber.assertComplete(); +// } +// +// @Test +// public void putAllJSONArray() { +// Single completable = mRealmManager.putAll(new JSONArray(), "id", int.class, +// TestRealmModel.class); +// applyTestSubscriber(completable); +// } +// +// @Test +// public void putAllRealmObject() { +// Single completable = mRealmManager.putAll(new ArrayList<>(), TestRealmModel.class); +// applyTestSubscriber(completable); +// } +// +// @Test +// public void evictAll() { +// Single completable = mRealmManager.evictAll(TestRealmModel.class); +// applyTestSubscriber(completable); +// } +// // @Test -// public void evictById() { -// assertEquals(mRealmManager.evictById(TestRealmModel.class, "id", 1, Integer.class), true); +// public void evictCollection() { +// Single completable = +// mRealmManager.evictCollection("id", new ArrayList<>(), String.class, TestRealmModel.class); +// applyTestSubscriber(completable); // } -} +// +//// @Test +//// public void evictById() { +//// assertEquals(mRealmManager.evictById(TestRealmModel.class, "id", 1, Integer.class), true); +//// } +//} diff --git a/usecases/src/test/java/com/zeyad/usecases/integration/APIIntegrationTest.java b/usecases/src/test/java/com/zeyad/usecases/integration/APIIntegrationTest.java index ce08ba7..84ec172 100644 --- a/usecases/src/test/java/com/zeyad/usecases/integration/APIIntegrationTest.java +++ b/usecases/src/test/java/com/zeyad/usecases/integration/APIIntegrationTest.java @@ -22,6 +22,7 @@ import io.appflate.restmock.RESTMockServer; import io.appflate.restmock.RequestsVerifier; +import io.reactivex.observers.TestObserver; import io.reactivex.subscribers.TestSubscriber; import okhttp3.mockwebserver.MockResponse; @@ -74,7 +75,7 @@ public void testValidUser() { final TestSubscriber testSubscriber = new TestSubscriber<>(); dataService.getObject(new GetRequest.Builder(User.class, false) .url(getUserPath) - .id("Zeyad-37", User.LOGIN, String.class) + .id("Zeyad-37", User.LOGIN) // .cache(User.LOGIN) .build()) .subscribe(testSubscriber); @@ -101,7 +102,7 @@ public void testValidUserList() { TestSubscriber> testSubscriber = new TestSubscriber<>(); dataService.getList(new GetRequest.Builder(User.class, false) .url(getUserListPath) - .id("Zeyad-37", User.LOGIN, String.class) + .id("Zeyad-37", User.LOGIN) // .cache(User.LOGIN) .build()) .subscribe(testSubscriber); @@ -125,7 +126,7 @@ public void testPatchObject() { .setResponseCode(HttpURLConnection.HTTP_OK) .setBody(SUCCESS)); - TestSubscriber testSubscriber = new TestSubscriber<>(); + TestObserver testSubscriber = new TestObserver<>(); dataService.patchObject(new PostRequest.Builder(User.class, false) .url(path) .payLoad(testUser) @@ -152,7 +153,7 @@ public void testPostObject() { .setResponseCode(HttpURLConnection.HTTP_OK) .setBody(SUCCESS)); - TestSubscriber testSubscriber = new TestSubscriber<>(); + TestObserver testSubscriber = new TestObserver<>(); dataService.postObject(new PostRequest.Builder(User.class, false) .url(path) .payLoad(testUser) @@ -179,7 +180,7 @@ public void testPostList() { .setResponseCode(HttpURLConnection.HTTP_OK) .setBody(SUCCESS)); - TestSubscriber testSubscriber = new TestSubscriber<>(); + TestObserver testSubscriber = new TestObserver<>(); dataService.postList(new PostRequest.Builder(User.class, false) .url(path) .payLoad(userListResponse) @@ -206,7 +207,7 @@ public void testPutObject() { .setResponseCode(HttpURLConnection.HTTP_OK) .setBody(SUCCESS)); - TestSubscriber testSubscriber = new TestSubscriber<>(); + TestObserver testSubscriber = new TestObserver<>(); dataService.putObject(new PostRequest.Builder(User.class, false) .url(path) .payLoad(testUser) @@ -233,7 +234,7 @@ public void testPutList() { .setResponseCode(HttpURLConnection.HTTP_OK) .setBody(SUCCESS)); - TestSubscriber testSubscriber = new TestSubscriber<>(); + TestObserver testSubscriber = new TestObserver<>(); dataService.putList(new PostRequest.Builder(User.class, false) .url(path) .payLoad(userListResponse) @@ -260,7 +261,7 @@ public void testDeleteItemById() { .setResponseCode(HttpURLConnection.HTTP_OK) .setBody(SUCCESS)); - TestSubscriber testSubscriber = new TestSubscriber<>(); + TestObserver testSubscriber = new TestObserver<>(); dataService.deleteItemById(new PostRequest.Builder(User.class, false) .url(path) .payLoad("{\"id\": \"Zeyad-37\"}") @@ -282,7 +283,7 @@ public void testDeleteItemById() { @Test public void testDeleteCollectionByIds() { - String path = "deleteList/user"; + String path = "deleteListById/user"; RESTMockServer.whenDELETE(pathContains(path)) .thenReturn(new MockResponse() .setResponseCode(HttpURLConnection.HTTP_OK) @@ -290,7 +291,7 @@ public void testDeleteCollectionByIds() { List payload = new ArrayList<>(2); payload.add("Zeyad-37"); payload.add("Zeyad-37"); - TestSubscriber testSubscriber = new TestSubscriber<>(); + TestObserver testSubscriber = new TestObserver<>(); dataService.deleteCollectionByIds(new PostRequest.Builder(User.class, false) .url(path) // .payLoad(Arrays.array("Zeyad-37", "Zeyad-37")) @@ -415,7 +416,7 @@ public void testGetObjectOffLineFirst() { TestSubscriber testSubscriber = new TestSubscriber<>(); dataService.getObject(new GetRequest.Builder(User.class, false) .url(getUserPath) - .id("Zeyad-37", User.LOGIN, String.class) + .id("Zeyad-37", User.LOGIN) // .cache(User.LOGIN) .build()) .subscribe(testSubscriber); @@ -442,7 +443,7 @@ public void testGetListOffLineFirst() { TestSubscriber> testSubscriber = new TestSubscriber<>(); dataService.getList(new GetRequest.Builder(User.class, false) .url(getUserListPath) - .id("Zeyad-37", User.LOGIN, String.class) + .id("Zeyad-37", User.LOGIN) // .cache(User.LOGIN) .build()) .subscribe(testSubscriber); diff --git a/usecases/src/test/java/com/zeyad/usecases/integration/User.kt b/usecases/src/test/java/com/zeyad/usecases/integration/User.kt index eb1f8ea..4f75fa4 100644 --- a/usecases/src/test/java/com/zeyad/usecases/integration/User.kt +++ b/usecases/src/test/java/com/zeyad/usecases/integration/User.kt @@ -1,14 +1,13 @@ package com.zeyad.usecases.integration +import android.arch.persistence.room.PrimaryKey import com.google.gson.annotations.SerializedName -import io.realm.RealmObject -import io.realm.annotations.PrimaryKey /** * @author zeyad on 1/10/17. */ -class User internal constructor() : RealmObject() { +class User internal constructor() { @SerializedName(ID) var id: Int = 0 @PrimaryKey diff --git a/usecases/src/test/java/com/zeyad/usecases/network/ApiConnectionTest.java b/usecases/src/test/java/com/zeyad/usecases/network/ApiConnectionTest.java index 77f7b66..01478cf 100644 --- a/usecases/src/test/java/com/zeyad/usecases/network/ApiConnectionTest.java +++ b/usecases/src/test/java/com/zeyad/usecases/network/ApiConnectionTest.java @@ -16,6 +16,7 @@ import java.util.Map; import io.reactivex.Flowable; +import io.reactivex.Single; import okhttp3.MultipartBody; import okhttp3.RequestBody; import okhttp3.logging.HttpLoggingInterceptor; @@ -61,13 +62,13 @@ public void setUp() { .thenReturn(Flowable.just(Collections.singletonList(new TestRealmModel()))); when(mRestApiWithoutCache.dynamicPost(mValidUrl, mMockedRequestBody)) - .thenReturn(Flowable.just(Collections.singletonList(new TestRealmModel()))); + .thenReturn(Single.just(Collections.singletonList(new TestRealmModel()))); when(mRestApiWithoutCache.dynamicPut(mValidUrl, mMockedRequestBody)) - .thenReturn(Flowable.just(Collections.singletonList(new TestRealmModel()))); - when(mRestApiWithoutCache.dynamicDelete(mValidUrl)) - .thenReturn(Flowable.just(Collections.singletonList(new TestRealmModel()))); + .thenReturn(Single.just(Collections.singletonList(new TestRealmModel()))); + when(mRestApiWithoutCache.dynamicDelete(mValidUrl, mMockedRequestBody)) + .thenReturn(Single.just(Collections.singletonList(new TestRealmModel()))); when(mRestApiWithoutCache.dynamicUpload(mValidUrl, mPartMap, mMultipartBodyParts)) - .thenReturn(Flowable.just(Collections.singletonList(new TestRealmModel()))); + .thenReturn(Single.just(Collections.singletonList(new TestRealmModel()))); mApiConnection = getApiImplementation(mRestApiWithoutCache, mRestApiWithCache); } @@ -158,8 +159,8 @@ public void testUploadPartAndRequestBody() { @Test public void testDynamicDeleteObject() { - mApiConnection.dynamicDelete(mValidUrl); - Mockito.verify(mRestApiWithoutCache).dynamicDelete(eq(mValidUrl)); + mApiConnection.dynamicDelete(mValidUrl, mMockedRequestBody); + Mockito.verify(mRestApiWithoutCache).dynamicDelete(eq(mValidUrl), eq(mMockedRequestBody)); } private RestApi getCurrentSetRestApiWithoutCache(@NonNull ApiConnection apiConnection) { diff --git a/usecases/src/test/java/com/zeyad/usecases/requests/FileIORequestTest.kt b/usecases/src/test/java/com/zeyad/usecases/requests/FileIORequestTest.kt index cb5e6a7..e22d8d6 100644 --- a/usecases/src/test/java/com/zeyad/usecases/requests/FileIORequestTest.kt +++ b/usecases/src/test/java/com/zeyad/usecases/requests/FileIORequestTest.kt @@ -15,9 +15,6 @@ import java.io.File @RunWith(JUnit4::class) class FileIORequestTest { - private val ON_WIFI = false - private val QUEUABLE = false - private val WHILE_CHARGING = false private val URL = "www.google.com" private val FILE = Mockito.mock(File::class.java) private val DATA_CLASS = TestRealmModel::class.java @@ -41,23 +38,8 @@ class FileIORequestTest { assertThat(mFileIORequest!!.url, `is`(equalTo(URL))) } - @Test - fun testIsPersist() { - assertThat(mFileIORequest!!.queuable, `is`(equalTo(QUEUABLE))) - } - @Test fun testGetFile() { assertThat(mFileIORequest!!.file, `is`(equalTo(FILE))) } - - @Test - fun testOnWifi() { - assertThat(mFileIORequest!!.onWifi, `is`(equalTo(ON_WIFI))) - } - - @Test - fun testWhileChargingGetFile() { - assertThat(mFileIORequest!!.whileCharging, `is`(equalTo(WHILE_CHARGING))) - } } diff --git a/usecases/src/test/java/com/zeyad/usecases/requests/GetRequestTest.kt b/usecases/src/test/java/com/zeyad/usecases/requests/GetRequestTest.kt index ccf6ba4..3c8861f 100644 --- a/usecases/src/test/java/com/zeyad/usecases/requests/GetRequestTest.kt +++ b/usecases/src/test/java/com/zeyad/usecases/requests/GetRequestTest.kt @@ -26,7 +26,7 @@ class GetRequestTest { mGetRequest = GetRequest.Builder(DATA_CLASS, TO_PERSIST) .fullUrl(URL) .cache(ID_COLUMN_NAME) - .id(ID, ID_COLUMN_NAME, Long::class.javaPrimitiveType!!) + .id(ID, ID_COLUMN_NAME) .build() } diff --git a/usecases/src/test/java/com/zeyad/usecases/stores/CloudStoreTest.kt b/usecases/src/test/java/com/zeyad/usecases/stores/CloudStoreTest.kt index 6107c80..aff1cf9 100644 --- a/usecases/src/test/java/com/zeyad/usecases/stores/CloudStoreTest.kt +++ b/usecases/src/test/java/com/zeyad/usecases/stores/CloudStoreTest.kt @@ -10,8 +10,7 @@ import android.support.test.rule.BuildConfig import com.zeyad.usecases.TestRealmModel import com.zeyad.usecases.anyObject import com.zeyad.usecases.db.DataBaseManager -import com.zeyad.usecases.db.RealmManager -import com.zeyad.usecases.db.RealmQueryProvider +import com.zeyad.usecases.db.RoomManager import com.zeyad.usecases.exceptions.NetworkConnectionException import com.zeyad.usecases.mapper.DAOMapper import com.zeyad.usecases.network.ApiConnection @@ -20,9 +19,6 @@ import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.observers.TestObserver import io.reactivex.subscribers.TestSubscriber -import io.realm.Realm -import io.realm.RealmModel -import io.realm.RealmQuery import junit.framework.Assert.assertEquals import okhttp3.MultipartBody import okhttp3.RequestBody @@ -50,24 +46,24 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications private lateinit var mockContext: Context private lateinit var mockApiConnection: ApiConnection private lateinit var mockDataBaseManager: DataBaseManager - private lateinit var observable: Flowable - private lateinit var fileFlowable: Flowable + private lateinit var observable: Single + private lateinit var fileFlowable: Single private val errorMessage = "Could not reach server!" @Before fun setUp() { - observable = Flowable.just(Any()) - fileFlowable = Flowable.just(ResponseBody.create(null, "")) + observable = Single.just(Any()) + fileFlowable = Single.just(ResponseBody.create(null, "")) mockContext = mock(Context::class.java) com.zeyad.usecases.Config.context = mockContext mockApiConnection = mock(ApiConnection::class.java) - mockDataBaseManager = mock(RealmManager::class.java) + mockDataBaseManager = mock(RoomManager::class.java) changeStateOfNetwork(mockContext, true) - `when`(mockDataBaseManager.put(any(JSONObject::class.java), anyString(), any(Class::class.java), any(Class::class.java))) + `when`(mockDataBaseManager.put(any(JSONObject::class.java), any(Class::class.java))) .thenReturn(Single.just(true)) - `when`(mockDataBaseManager.putAll(any(JSONArray::class.java), anyString(), any(Class::class.java), any(Class::class.java))) + `when`(mockDataBaseManager.putAll(any(JSONArray::class.java), any(Class::class.java))) .thenReturn(Single.just(true)) - `when`(mockDataBaseManager.putAll(anyListOf(RealmModel::class.java), anyObject())) + `when`(mockDataBaseManager.putAll(anyListOf(Any::class.java), anyObject())) .thenReturn(Single.just(true)) cloudStore = CloudStore(mockApiConnection, mockDataBaseManager, DAOMapper(), MemoryStore(com.zeyad.usecases.Config.gson, HashMap())) @@ -81,11 +77,12 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications @Test fun dynamicGetObject() { - `when`(mockApiConnection.dynamicGetObject(anyString(), anyBoolean())).thenReturn(observable) + `when`(mockApiConnection.dynamicGetObject(anyString(), anyBoolean())) + .thenReturn(Flowable.just(Any())) val testSubscriber = TestSubscriber() - cloudStore.dynamicGetObject("", "", 0L, Long::class.java, Any::class.java, - false, false).subscribe(testSubscriber) + cloudStore.dynamicGetObject("", "", 0L, Any::class.java, false, false) + .subscribe(testSubscriber) testSubscriber.assertNoErrors() testSubscriber.assertComplete() @@ -97,10 +94,10 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications @Test fun dynamicGetObjectCanWillPersist() { - `when`(mockApiConnection.dynamicGetObject(anyString(), anyBoolean())).thenReturn(observable) + `when`(mockApiConnection.dynamicGetObject(anyString(), anyBoolean())).thenReturn(Flowable.just(Any())) val testSubscriber = TestSubscriber() - cloudStore.dynamicGetObject("", "", 0L, Long::class.java, Any::class.java, true, false) + cloudStore.dynamicGetObject("", "", 0L, Any::class.java, true, false) .subscribe(testSubscriber) testSubscriber.assertNoErrors() @@ -151,9 +148,9 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications `when`(mockApiConnection.dynamicPatch(anyString(), anyObject())) .thenReturn(observable) - val testSubscriber = TestSubscriber() + val testSubscriber = TestObserver() cloudStore.dynamicPatchObject( - "", "", Int::class.java, JSONObject(), Any::class.java, Any::class.java, false, false, false) + "", "", JSONObject(), Any::class.java, Any::class.java, false, false) .subscribe(testSubscriber) testSubscriber.assertNoErrors() @@ -170,9 +167,9 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications `when`(mockApiConnection.dynamicPatch(anyString(), anyObject())) .thenReturn(observable) - val testSubscriber = TestSubscriber() + val testSubscriber = TestObserver() cloudStore.dynamicPatchObject( - "", "", Int::class.java, JSONObject(), Any::class.java, Any::class.java, true, true, true) + "", "", JSONObject(), Any::class.java, Any::class.java, true, true) .subscribe(testSubscriber) testSubscriber.assertNoErrors() @@ -187,9 +184,9 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications fun dynamicPatchObjectNoNetwork() { changeStateOfNetwork(mockContext, false) - val testSubscriber = TestSubscriber() + val testSubscriber = TestObserver() cloudStore.dynamicPatchObject( - "", "", Int::class.java, JSONObject(), Any::class.java, Any::class.java, false, false, true) + "", "", JSONObject(), Any::class.java, Any::class.java, false, false) .subscribe(testSubscriber) testSubscriber.assertErrorMessage(errorMessage) @@ -202,9 +199,9 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications fun dynamicPatchObjectNoNetworkNoQueue() { changeStateOfNetwork(mockContext, false) - val testSubscriber = TestSubscriber() + val testSubscriber = TestObserver() cloudStore.dynamicPatchObject( - "", "", Int::class.java, JSONObject(), Any::class.java, Any::class.java, false, false, false) + "", "", JSONObject(), Any::class.java, Any::class.java, false, false) .subscribe(testSubscriber) testSubscriber.assertError(NetworkConnectionException::class.java) @@ -216,9 +213,9 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications fun dynamicPostObject() { `when`(mockApiConnection.dynamicPost(anyString(), anyObject())).thenReturn(observable) - val testSubscriber = TestSubscriber() + val testSubscriber = TestObserver() cloudStore.dynamicPostObject( - "", "", Int::class.java, JSONObject(), Any::class.java, Any::class.java, false, false, false) + "", "", JSONObject(), Any::class.java, Any::class.java, false, false) .subscribe(testSubscriber) testSubscriber.assertNoErrors() @@ -234,9 +231,9 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications `when`(mockApiConnection.dynamicPost(anyString(), anyObject())) .thenReturn(observable) - val testSubscriber = TestSubscriber() + val testSubscriber = TestObserver() cloudStore.dynamicPostObject( - "", "", Int::class.java, JSONObject(), Any::class.java, Any::class.java, true, false, false) + "", "", JSONObject(), Any::class.java, Any::class.java, true, false) .subscribe(testSubscriber) testSubscriber.assertNoErrors() @@ -251,9 +248,9 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications fun dynamicPostObjectNoNetwork() { changeStateOfNetwork(mockContext, false) - val testSubscriber = TestSubscriber() + val testSubscriber = TestObserver() cloudStore.dynamicPostObject( - "", "", Int::class.java, JSONObject(), Any::class.java, Any::class.java, false, false, true) + "", "", JSONObject(), Any::class.java, Any::class.java, false, false) .subscribe(testSubscriber) testSubscriber.assertErrorMessage(errorMessage) @@ -266,9 +263,9 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications fun dynamicPostObjectNoNetworkNoQueue() { changeStateOfNetwork(mockContext, false) - val testSubscriber = TestSubscriber() + val testSubscriber = TestObserver() cloudStore.dynamicPostObject( - "", "", Int::class.java, JSONObject(), Any::class.java, Any::class.java, false, false, false) + "", "", JSONObject(), Any::class.java, Any::class.java, false, false) .subscribe(testSubscriber) testSubscriber.assertError(NetworkConnectionException::class.java) @@ -282,8 +279,8 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications `when`(mockApiConnection.dynamicPost(anyString(), anyObject())) .thenReturn(observable) - val testSubscriber = TestSubscriber() - cloudStore.dynamicPostList("", "", Int::class.java, JSONArray(), Any::class.java, Any::class.java, false, + val testSubscriber = TestObserver() + cloudStore.dynamicPostList("", "", JSONArray(), Any::class.java, Any::class.java, false, false) .subscribe(testSubscriber) @@ -300,9 +297,9 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications `when`(mockApiConnection.dynamicPost(anyString(), anyObject())) .thenReturn(observable) - val testSubscriber = TestSubscriber() - cloudStore.dynamicPostList("", "", Int::class.java, JSONArray(), Any::class.java, Any::class.java, - true, false, false) + val testSubscriber = TestObserver() + cloudStore.dynamicPostList("", "", JSONArray(), Any::class.java, Any::class.java, + true, false) .subscribe(testSubscriber) testSubscriber.assertNoErrors() @@ -317,9 +314,8 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications fun dynamicPostListNoNetwork() { changeStateOfNetwork(mockContext, false) - val testSubscriber = TestSubscriber() - cloudStore.dynamicPostList("", "", Int::class.java, JSONArray(), Any::class.java, Any::class.java, false, false, - true) + val testSubscriber = TestObserver() + cloudStore.dynamicPostList("", "", JSONArray(), Any::class.java, Any::class.java, false, false) .subscribe(testSubscriber) testSubscriber.assertErrorMessage(errorMessage) @@ -332,9 +328,8 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications fun dynamicPostListNoNetworkNoQueue() { changeStateOfNetwork(mockContext, false) - val testSubscriber = TestSubscriber() - cloudStore.dynamicPostList("", "", Int::class.java, JSONArray(), Any::class.java, Any::class.java, false, false, - false) + val testSubscriber = TestObserver() + cloudStore.dynamicPostList("", "", JSONArray(), Any::class.java, Any::class.java, false, false) .subscribe(testSubscriber) testSubscriber.assertError(NetworkConnectionException::class.java) @@ -348,9 +343,9 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications `when`(mockApiConnection.dynamicPut(anyString(), anyObject())) .thenReturn(observable) - val testSubscriber = TestSubscriber() + val testSubscriber = TestObserver() cloudStore.dynamicPutObject( - "", "", Int::class.java, JSONObject(), Any::class.java, Any::class.java, false, false, false) + "", "", JSONObject(), Any::class.java, Any::class.java, false, false) .subscribe(testSubscriber) testSubscriber.assertNoErrors() @@ -366,9 +361,9 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications `when`(mockApiConnection.dynamicPut(anyString(), anyObject())) .thenReturn(observable) - val testSubscriber = TestSubscriber() + val testSubscriber = TestObserver() cloudStore.dynamicPutObject( - "", "", Int::class.java, JSONObject(), Any::class.java, Any::class.java, true, false, false) + "", "", JSONObject(), Any::class.java, Any::class.java, true, false) .subscribe(testSubscriber) testSubscriber.assertNoErrors() @@ -383,9 +378,9 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications fun dynamicPutObjectNoNetwork() { changeStateOfNetwork(mockContext, false) - val testSubscriber = TestSubscriber() + val testSubscriber = TestObserver() cloudStore.dynamicPutObject( - "", "", Int::class.java, JSONObject(), Any::class.java, Any::class.java, false, false, true) + "", "", JSONObject(), Any::class.java, Any::class.java, false, false) .subscribe(testSubscriber) testSubscriber.assertErrorMessage(errorMessage) @@ -398,9 +393,9 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications fun dynamicPutObjectNoNetworkNoQueue() { changeStateOfNetwork(mockContext, false) - val testSubscriber = TestSubscriber() + val testSubscriber = TestObserver() cloudStore.dynamicPutObject( - "", "", Int::class.java, JSONObject(), Any::class.java, Any::class.java, false, false, false) + "", "", JSONObject(), Any::class.java, Any::class.java, false, false) .subscribe(testSubscriber) testSubscriber.assertError(NetworkConnectionException::class.java) @@ -414,9 +409,8 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications `when`(mockApiConnection.dynamicPut(anyString(), anyObject())) .thenReturn(observable) - val testSubscriber = TestSubscriber() - cloudStore.dynamicPutList("", "", Int::class.java, JSONArray(), Any::class.java, Any::class.java, false, false, - false) + val testSubscriber = TestObserver() + cloudStore.dynamicPutList("", "", JSONArray(), Any::class.java, Any::class.java, false, false) .subscribe(testSubscriber) testSubscriber.assertNoErrors() @@ -432,9 +426,8 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications `when`(mockApiConnection.dynamicPut(anyString(), anyObject())) .thenReturn(observable) - val testSubscriber = TestSubscriber() - cloudStore.dynamicPutList("", "", Int::class.java, JSONArray(), Any::class.java, Any::class.java, true, false, - false) + val testSubscriber = TestObserver() + cloudStore.dynamicPutList("", "", JSONArray(), Any::class.java, Any::class.java, true, false) .subscribe(testSubscriber) testSubscriber.assertNoErrors() @@ -449,8 +442,8 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications fun dynamicPutListNoNetwork() { changeStateOfNetwork(mockContext, false) - val testSubscriber = TestSubscriber() - cloudStore.dynamicPutList("", "", Int::class.java, JSONArray(), Any::class.java, Any::class.java, false, false, true) + val testSubscriber = TestObserver() + cloudStore.dynamicPutList("", "", JSONArray(), Any::class.java, Any::class.java, false, false) .subscribe(testSubscriber) testSubscriber.assertErrorMessage(errorMessage) @@ -463,9 +456,8 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications fun dynamicPutListNoNetworkNoQueue() { changeStateOfNetwork(mockContext, false) - val testSubscriber = TestSubscriber() - cloudStore.dynamicPutList("", "", Int::class.java, JSONArray(), Any::class.java, Any::class.java, false, false, - false) + val testSubscriber = TestObserver() + cloudStore.dynamicPutList("", "", JSONArray(), Any::class.java, Any::class.java, false, false) .subscribe(testSubscriber) testSubscriber.assertError(NetworkConnectionException::class.java) @@ -476,36 +468,36 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications @Test fun dynamicDeleteCollection() { - `when`(mockApiConnection.dynamicDelete(anyString())) + `when`(mockApiConnection.dynamicDelete(anyString(), any())) .thenReturn(observable) - val testSubscriber = TestSubscriber() + val testSubscriber = TestObserver() cloudStore.dynamicDeleteCollection( - "", "", String::class.java, JSONArray(), Any::class.java, Any::class.java, false, false, false) + "", "", String::class.java, JSONArray(), Any::class.java, Any::class.java, false, false) .subscribe(testSubscriber) testSubscriber.assertNoErrors() testSubscriber.assertComplete() testSubscriber.assertValueCount(1) - verify(mockApiConnection, times(1)).dynamicDelete(anyString()) + verify(mockApiConnection, times(1)).dynamicDelete(anyString(), any()) verifyDBInteractions(0, 0, 0, 0) } @Test fun dynamicDeleteCollectionCanWillPersist() { - `when`(mockApiConnection.dynamicDelete(anyString())).thenReturn(observable) + `when`(mockApiConnection.dynamicDelete(anyString(), any())).thenReturn(observable) - val testSubscriber = TestSubscriber() + val testSubscriber = TestObserver() cloudStore.dynamicDeleteCollection( - "", "", String::class.java, JSONArray(), Any::class.java, Any::class.java, true, false, false) + "", "", String::class.java, JSONArray(), Any::class.java, Any::class.java, true, false) .subscribe(testSubscriber) testSubscriber.assertNoErrors() testSubscriber.assertComplete() testSubscriber.assertValueCount(1) - verify(mockApiConnection, times(1)).dynamicDelete(anyString()) + verify(mockApiConnection, times(1)).dynamicDelete(anyString(), any()) verifyDBInteractions(0, 0, 0, 0) } @@ -513,14 +505,14 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications fun dynamicDeleteCollectionNoNetwork() { changeStateOfNetwork(mockContext, false) - val testSubscriber = TestSubscriber() + val testSubscriber = TestObserver() cloudStore.dynamicDeleteCollection( - "", "", String::class.java, JSONArray(), Any::class.java, Any::class.java, false, false, true) + "", "", String::class.java, JSONArray(), Any::class.java, Any::class.java, false, false) .subscribe(testSubscriber) testSubscriber.assertErrorMessage(errorMessage) - verify(mockApiConnection, times(0)).dynamicDelete(anyString()) + verify(mockApiConnection, times(0)).dynamicDelete(anyString(), any()) verifyDBInteractions(0, 0, 0, 0) } @@ -528,14 +520,14 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications fun dynamicDeleteCollectionNoNetworkNoQueue() { changeStateOfNetwork(mockContext, false) - val testSubscriber = TestSubscriber() + val testSubscriber = TestObserver() cloudStore.dynamicDeleteCollection( - "", "", String::class.java, JSONArray(), Any::class.java, Any::class.java, false, false, false) + "", "", String::class.java, JSONArray(), Any::class.java, Any::class.java, false, false) .subscribe(testSubscriber) testSubscriber.assertError(NetworkConnectionException::class.java) - verify(mockApiConnection, times(0)).dynamicDelete(anyString()) + verify(mockApiConnection, times(0)).dynamicDelete(anyString(), any()) verifyDBInteractions(0, 0, 0, 0) } @@ -562,9 +554,9 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications anyListOf(MultipartBody.Part::class.java))) .thenReturn(observable) - val testSubscriber = TestSubscriber() + val testSubscriber = TestObserver() cloudStore.dynamicUploadFile( - "", HashMap(), HashMap(), false, false, false, Any::class.java) + "", HashMap(), HashMap(), Any::class.java) .subscribe(testSubscriber) testSubscriber.assertNoErrors() @@ -581,9 +573,9 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications fun dynamicUploadFileNoNetwork() { changeStateOfNetwork(mockContext, false) - val testSubscriber = TestSubscriber() + val testSubscriber = TestObserver() cloudStore.dynamicUploadFile( - "", HashMap(), HashMap(), false, false, true, Any::class.java) + "", HashMap(), HashMap(), Any::class.java) .subscribe(testSubscriber) testSubscriber.assertNoValues() @@ -594,9 +586,9 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications fun dynamicUploadFileNoNetworkNoQueue() { changeStateOfNetwork(mockContext, false) - val testSubscriber = TestSubscriber() + val testSubscriber = TestObserver() cloudStore.dynamicUploadFile( - "", HashMap(), HashMap(), false, false, false, Any::class.java) + "", HashMap(), HashMap(), Any::class.java) .subscribe(testSubscriber) testSubscriber.assertError(NetworkConnectionException::class.java) @@ -607,8 +599,8 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications fun dynamicDownloadFile() { `when`(mockApiConnection.dynamicDownload(anyString())).thenReturn(fileFlowable) - val testSubscriber = TestSubscriber() - cloudStore.dynamicDownloadFile("", File(""), false, false, false) + val testSubscriber = TestObserver() + cloudStore.dynamicDownloadFile("", File("")) .subscribe(testSubscriber) testSubscriber.assertNoErrors() @@ -623,8 +615,8 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications fun dynamicDownloadFileNoNetwork() { changeStateOfNetwork(mockContext, false) - val testSubscriber = TestSubscriber() - cloudStore.dynamicDownloadFile("", File(""), false, false, true) + val testSubscriber = TestObserver() + cloudStore.dynamicDownloadFile("", File("")) .subscribe(testSubscriber) testSubscriber.assertNoValues() @@ -635,8 +627,8 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications fun dynamicDownloadFileNoNetworkNoQueue() { changeStateOfNetwork(mockContext, false) - val testSubscriber = TestSubscriber() - cloudStore.dynamicDownloadFile("", File(""), false, false, false) + val testSubscriber = TestObserver() + cloudStore.dynamicDownloadFile("", File("")) .subscribe(testSubscriber) testSubscriber.assertError(NetworkConnectionException::class.java) @@ -645,9 +637,7 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications @Test(expected = RuntimeException::class) fun queryDisk() { - val observable = cloudStore.queryDisk(object : RealmQueryProvider { - override fun create(realm: Realm): RealmQuery = realm.where(TestRealmModel::class.java) - }) + val observable = cloudStore.queryDisk("", TestRealmModel::class.java) // Verify repository interactions verifyZeroInteractions(mockApiConnection) @@ -660,13 +650,13 @@ class CloudStoreTest { // TODO: 6/5/17 add disk and cache verifications private fun verifyDBInteractions(putAllJ: Int, putAllL: Int, putJ: Int, evict: Int) { verify(mockDataBaseManager, times(putAllJ)) - .putAll(any(JSONArray::class.java), anyString(), any(Class::class.java), anyObject()) - verify(mockDataBaseManager, times(putAllL)).putAll(Matchers - .anyListOf(RealmModel::class.java), anyObject()) + .putAll(any(JSONArray::class.java), any(Class::class.java)) + verify(mockDataBaseManager, times(putAllL)).putAll(Matchers + .anyListOf(TestRealmModel::class.java), anyObject()) verify(mockDataBaseManager, times(putJ)) - .put(any(JSONObject::class.java), anyString(), any(Class::class.java), anyObject()) - verify(mockDataBaseManager, atLeast(evict)).evictById(anyObject(), anyString(), any(), - anyObject()) + .put(any(JSONObject::class.java), any(Class::class.java)) + verify(mockDataBaseManager, atLeast(evict)).evictById(any(Class::class.java), + anyString(), any()) } private fun changeStateOfNetwork(mockedContext: Context, toEnable: Boolean): Context { diff --git a/usecases/src/test/java/com/zeyad/usecases/stores/DataStoreFactoryTest.kt b/usecases/src/test/java/com/zeyad/usecases/stores/DataStoreFactoryTest.kt index 9c7ab21..6ca4de3 100644 --- a/usecases/src/test/java/com/zeyad/usecases/stores/DataStoreFactoryTest.kt +++ b/usecases/src/test/java/com/zeyad/usecases/stores/DataStoreFactoryTest.kt @@ -3,7 +3,7 @@ package com.zeyad.usecases.stores import android.content.Context import com.zeyad.usecases.Config import com.zeyad.usecases.db.DataBaseManager -import com.zeyad.usecases.db.RealmManager +import com.zeyad.usecases.db.RoomManager import com.zeyad.usecases.mapper.DAOMapper import com.zeyad.usecases.network.ApiConnection import com.zeyad.usecases.utils.DataBaseManagerUtil @@ -31,7 +31,7 @@ class DataStoreFactoryTest { com.zeyad.usecases.Config.context = mock(Context::class.java) mDataStoreFactory = DataStoreFactory(object : DataBaseManagerUtil { override fun getDataBaseManager(dataClass: Class<*>): DataBaseManager? { - return mock(RealmManager::class.java) + return mock(RoomManager::class.java) } }, mock(ApiConnection::class.java), DAOMapper()) @@ -100,7 +100,7 @@ class DataStoreFactoryTest { @Test @Throws(IllegalAccessException::class) fun testDiskMethod_ifExpectedDataStoreIsReturned_whenMockedEntityMapperIsPassed() { - Config.withRealm = true + Config.withSQLite = true assertThat(mDataStoreFactory!!.disk(Any::class.java), `is`(notNullValue())) } diff --git a/usecases/src/test/java/com/zeyad/usecases/stores/DiskStoreTest.kt b/usecases/src/test/java/com/zeyad/usecases/stores/DiskStoreTest.kt index 665cfa7..f7c5898 100644 --- a/usecases/src/test/java/com/zeyad/usecases/stores/DiskStoreTest.kt +++ b/usecases/src/test/java/com/zeyad/usecases/stores/DiskStoreTest.kt @@ -3,12 +3,9 @@ package com.zeyad.usecases.stores import com.zeyad.usecases.Config import com.zeyad.usecases.TestRealmModel import com.zeyad.usecases.db.DataBaseManager -import com.zeyad.usecases.db.RealmQueryProvider import io.reactivex.Flowable import io.reactivex.Single -import io.reactivex.subscribers.TestSubscriber -import io.realm.Realm -import io.realm.RealmQuery +import io.reactivex.observers.TestObserver import junit.framework.Assert.assertEquals import org.json.JSONArray import org.json.JSONObject @@ -16,6 +13,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +import org.mockito.Matchers import org.mockito.Mockito.* import java.io.File import java.util.* @@ -38,36 +36,36 @@ class DiskStoreTest { // TODO: 6/5/17 add cache verifications val testRealmObjects = ArrayList() testRealmObjects.add(TestRealmModel()) val observable = Flowable.just>(testRealmObjects) - `when`(dbManager.getAll(any(Class::class.java))).thenReturn(observable) + `when`(dbManager.getAll(any(Class::class.java) as Class)) + .thenReturn(observable) mDiskStore.dynamicGetList("", "", Any::class.java, false, false) - verify(dbManager, times(1)).getAll(any(Class::class.java)) + verify(dbManager, times(1)) + .getAll(any(Class::class.java) as Class) } @Test fun testGetObject() { val observable = Flowable.just(TestRealmModel()) - `when`(dbManager.getById(anyString(), any(), any(Class::class.java), any(Class::class.java))) + `when`(dbManager.getById(anyString(), any(), any(Class::class.java) as Class)) .thenReturn(observable) - mDiskStore.dynamicGetObject("", "", 0L, Long::class.javaPrimitiveType!!, Any::class.java, false, false) + mDiskStore.dynamicGetObject("", "", 0L, TestRealmModel::class.java, false, false) verify(dbManager, times(1)) - .getById(anyString(), any(), any(Class::class.java), any(Class::class.java)) + .getById(anyString(), any(), any(Class::class.java) as Class) } @Test fun testSearchDiskRealmQuery() { - `when`(dbManager.getQuery(anyObject>())) - .thenReturn(Flowable.just(listOf(TestRealmModel()))) + `when`(dbManager.getQuery(anyString(), any(Class::class.java) as Class)) + .thenReturn(Flowable.just(TestRealmModel())) - mDiskStore.queryDisk(object : RealmQueryProvider { - override fun create(realm: Realm): RealmQuery = - realm.where(TestRealmModel::class.java) - }) + mDiskStore.queryDisk("", TestRealmModel::class.java) - verify(dbManager, times(1)).getQuery(anyObject>()) + verify(dbManager, times(1)) + .getQuery(Matchers.anyString(), any(Class::class.java) as Class) } @Test @@ -81,92 +79,91 @@ class DiskStoreTest { // TODO: 6/5/17 add cache verifications @Test fun testDynamicDeleteCollection() { - `when`(dbManager.evictCollection(anyString(), anyList(), any(Class::class.java), any(Class::class.java))) + `when`(dbManager.evictCollection(anyList() as List, any(Class::class.java) as Class)) .thenReturn(Single.just(true)) mDiskStore.dynamicDeleteCollection( - "", "", String::class.java, JSONArray(), Any::class.java, Any::class.java, false, false, false) + "", "", String::class.java, JSONArray(), Any::class.java, Any::class.java, false, false) verify(dbManager, times(1)) - .evictCollection(anyString(), anyList(), any(Class::class.java), any(Class::class.java)) + .evictCollection(anyList() as List, any(Class::class.java) as Class) } @Test fun testDynamicPatchObject() { - `when`(dbManager.put(any(JSONObject::class.java), anyString(), any(Class::class.java), any(Class::class.java))) + `when`(dbManager.put(any(JSONObject::class.java), any(Class::class.java))) .thenReturn(Single.just(true)) mDiskStore.dynamicPatchObject( - "", "", Int::class.javaPrimitiveType!!, JSONObject(), Any::class.java, Any::class.java, false, false, false) + "", "", JSONObject(), Any::class.java, Any::class.java, false, false) verify(dbManager, times(1)) - .put(any(JSONObject::class.java), anyString(), any(Class::class.java), any(Class::class.java)) + .put(any(JSONObject::class.java), any(Class::class.java)) } @Test fun testDynamicPostObject() { - `when`(dbManager.put(any(JSONObject::class.java), anyString(), any(Class::class.java), any(Class::class.java))) + `when`(dbManager.put(any(JSONObject::class.java), any(Class::class.java))) .thenReturn(Single.just(true)) mDiskStore.dynamicPostObject( - "", "", Int::class.javaPrimitiveType!!, JSONObject(), Any::class.java, Any::class.java, false, false, false) + "", "", JSONObject(), Any::class.java, Any::class.java, false, false) verify(dbManager, times(1)) - .put(any(JSONObject::class.java), anyString(), any(Class::class.java), any(Class::class.java)) + .put(any(JSONObject::class.java), any(Class::class.java)) } @Test fun testDynamicPutObject() { - `when`(dbManager.put(any(JSONObject::class.java), anyString(), any(Class::class.java), any(Class::class.java))) + `when`(dbManager.put(any(JSONObject::class.java), any(Class::class.java))) .thenReturn(Single.just(true)) mDiskStore.dynamicPutObject( - "", "", Int::class.javaPrimitiveType!!, JSONObject(), Any::class.java, Any::class.java, false, false, false) + "", "", JSONObject(), Any::class.java, Any::class.java, false, false) verify(dbManager, times(1)) - .put(any(JSONObject::class.java), anyString(), any(Class::class.java), any(Class::class.java)) + .put(any(JSONObject::class.java), any(Class::class.java)) } @Test fun testDynamicPostList() { - `when`(dbManager.putAll(any(JSONArray::class.java), anyString(), any(Class::class.java), any(Class::class.java))) + `when`(dbManager.putAll(any(JSONArray::class.java), any(Class::class.java))) .thenReturn(Single.just(true)) mDiskStore.dynamicPostList( - "", "", Int::class.javaPrimitiveType!!, JSONArray(), Any::class.java, Any::class.java, false, false, false) + "", "", JSONArray(), Any::class.java, Any::class.java, false, false) verify(dbManager, times(1)) - .putAll(any(JSONArray::class.java), anyString(), any(Class::class.java), any(Class::class.java)) + .putAll(any(JSONArray::class.java), any(Class::class.java)) } @Test fun testDynamicPutList() { - `when`(dbManager.putAll(any(JSONArray::class.java), anyString(), any(Class::class.java), any(Class::class.java))) + `when`(dbManager.putAll(any(JSONArray::class.java), any(Class::class.java))) .thenReturn(Single.just(true)) mDiskStore.dynamicPutList( - "", "", Int::class.javaPrimitiveType!!, JSONArray(), Any::class.java, Any::class.java, false, false, false) + "", "", JSONArray(), Any::class.java, Any::class.java, false, false) verify(dbManager, times(1)) - .putAll(any(JSONArray::class.java), anyString(), any(Class::class.java), any(Class::class.java)) + .putAll(any(JSONArray::class.java), any(Class::class.java)) } @Test fun testDynamicDownloadFile() { - val observable = mDiskStore.dynamicDownloadFile("", File(""), false, false, false) - + val observable = mDiskStore.dynamicDownloadFile("", File("")) // Verify repository interactions verifyZeroInteractions(dbManager) - val v: TestSubscriber = TestSubscriber() + val v: TestObserver = TestObserver() observable.subscribe(v) v.assertErrorMessage("Can not file IO to local DB") } @Test(expected = IllegalStateException::class) fun testDynamicUploadFile() { - val observable = mDiskStore.dynamicUploadFile( - "", HashMap(), HashMap(), false, false, false, Any::class.java) + val observable = mDiskStore.dynamicUploadFile( + "", HashMap(), HashMap(), Any::class.java) // Verify repository interactions verifyZeroInteractions(dbManager) @@ -175,6 +172,6 @@ class DiskStoreTest { // TODO: 6/5/17 add cache verifications val expected = IllegalStateException("Can not IO file to local DB") assertEquals( expected.message, - (observable.first(expected).blockingGet() as IllegalStateException).message) + (observable.blockingGet() as IllegalStateException).message) } } diff --git a/usecases/src/test/java/com/zeyad/usecases/stores/MemoryStoreTest.kt b/usecases/src/test/java/com/zeyad/usecases/stores/MemoryStoreTest.kt index 469b5da..4bf5c1e 100644 --- a/usecases/src/test/java/com/zeyad/usecases/stores/MemoryStoreTest.kt +++ b/usecases/src/test/java/com/zeyad/usecases/stores/MemoryStoreTest.kt @@ -53,7 +53,7 @@ class MemoryStoreTest { // TODO: 6/15/17 test ids } // @Test - // public void deleteList() { - // memoryStore.deleteList(Collections.singletonList("1"), TestRealmModel.class); + // public void deleteListById() { + // memoryStore.deleteListById(Collections.singletonList("1"), TestRealmModel.class); // } }