diff --git a/build.gradle b/build.gradle index 6c89e51..b1eaab7 100644 --- a/build.gradle +++ b/build.gradle @@ -6,13 +6,12 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:2.3.3' - classpath 'com.google.gms:google-services:3.0.0' - classpath 'io.realm:realm-gradle-plugin:3.3.1' +// classpath 'com.google.gms:google-services:3.1.0' + classpath 'io.realm:realm-gradle-plugin:3.4.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' - classpath "com.uber:infer-plugin:0.7.4" + classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' +// classpath "com.uber:infer-plugin:0.7.4" } } diff --git a/sampleApp/build.gradle b/sampleApp/build.gradle index 432e0dd..ed2373b 100644 --- a/sampleApp/build.gradle +++ b/sampleApp/build.gradle @@ -52,7 +52,7 @@ android { minifyEnabled false shrinkResources false debuggable true - proguardFiles getDefaultProguardFile('proguard-android.txt'), "$project.rootDir/tools/rules-proguard-debug.pro" +// proguardFiles getDefaultProguardFile('proguard-android.txt'), "$project.rootDir/tools/rules-proguard-debug.pro" // proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' signingConfig signingConfigs.debugConfig } @@ -100,6 +100,7 @@ android { splits.density.enable = false aaptOptions.cruncherEnabled = false } + sourceSets { main { assets.srcDirs = ['src/main/assets', 'src/androidTest/java/com/zeyad/usecases/app/assets/'] @@ -109,7 +110,6 @@ android { ext { supportLibraryVersion = '25.3.1' - googlePlayVersion = '10.0.1' butterKnifeVersion = '8.6.0' rxbindingVersion = '2.0.0' rxLifeCycle = '2.0.1' @@ -135,7 +135,7 @@ dependencies { // Bootstrap compile 'com.github.Zeyad-37:GenericRecyclerViewAdapter:1.1.0' - compile 'com.github.Zeyad-37:RxRedux:1.2.0' + compile 'com.github.Zeyad-37:RxRedux:1.3.0' // compile "android.arch.lifecycle:runtime:$archCompVersion" compile "android.arch.lifecycle:extensions:$archCompVersion" @@ -158,9 +158,9 @@ dependencies { testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1' compile "org.parceler:parceler-api:$parcelerVersion" annotationProcessor "org.parceler:parceler:$parcelerVersion" - compile("io.flowup:android-sdk:0.2.4") { - exclude group: 'com.google.android.gms' - } +// compile("io.flowup:android-sdk:0.2.4") { +// exclude group: 'com.google.android.gms' +// } testCompile 'io.flowup:android-sdk-no-op:0.2.4' // Testing androidTestCompile "com.android.support.test:runner:$androidSupportTest" @@ -180,12 +180,11 @@ dependencies { testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.10.19' -// testCompile 'org.mockito:mockito-core:2.0.57-beta' } //apply from: "$project.rootDir/tools/script-git-version.gradle" apply from: "$project.rootDir/tools/script-findbugs.gradle" -apply from: "$project.rootDir/tools/script-pmd.gradle" +//apply from: "$project.rootDir/tools/script-pmd.gradle" //apply from: "$project.rootDir/tools/script-java-code-coverage.gradle" -apply from: "$project.rootDir/tools/script-infer.gradle" +//apply from: "$project.rootDir/tools/script-infer.gradle" //apply from: "$project.rootDir/tools/script-check-style.gradle" diff --git a/sampleApp/src/main/java/com/zeyad/usecases/app/GenericApplication.java b/sampleApp/src/main/java/com/zeyad/usecases/app/GenericApplication.java index cce1055..1f0bd12 100644 --- a/sampleApp/src/main/java/com/zeyad/usecases/app/GenericApplication.java +++ b/sampleApp/src/main/java/com/zeyad/usecases/app/GenericApplication.java @@ -7,7 +7,6 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.Signature; -import android.os.StrictMode; import android.support.annotation.NonNull; import android.util.Base64; import android.util.Log; @@ -17,6 +16,7 @@ import com.zeyad.usecases.api.DataServiceConfig; import com.zeyad.usecases.api.DataServiceFactory; +import java.lang.reflect.InvocationTargetException; import java.security.MessageDigest; import java.util.Arrays; import java.util.concurrent.TimeUnit; @@ -24,7 +24,6 @@ import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; -import io.flowup.FlowUp; import io.reactivex.Completable; import io.reactivex.schedulers.Schedulers; import io.realm.Realm; @@ -85,7 +84,9 @@ private static boolean checkEmulator() { return false; } - private static String getSystemProperty(String name) throws Exception { + private static String getSystemProperty(String name) + throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, + IllegalAccessException { Class systemPropertyClazz = Class.forName("android.os.SystemProperties"); return (String) systemPropertyClazz.getMethod("get", new Class[]{String.class}) .invoke(systemPropertyClazz, name); @@ -97,7 +98,7 @@ private static boolean checkDebuggable(Context context) { @Override public void onCreate() { -// initializeStrictMode(); + initializeStrictMode(); super.onCreate(); if (LeakCanary.isInAnalyzerProcess(this)) { // This process is dedicated to LeakCanary for heap analysis. @@ -106,8 +107,10 @@ public void onCreate() { } LeakCanary.install(this); Completable.fromAction(() -> { -// checkAppTampering(this); - initializeFlowUp(); + if (!checkAppTampering(this)) { + throw new IllegalAccessException("App might be tampered with!"); + } + // initializeFlowUp(); Rollbar.init(this, "c8c8b4cb1d4f4650a77ae1558865ca87", BuildConfig.DEBUG ? "debug" : "production"); }).subscribeOn(Schedulers.io()) .subscribe(() -> { @@ -140,8 +143,9 @@ OkHttpClient.Builder getOkHttpBuilder() { .build()) .connectionSpecs(Arrays.asList(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS)); - if (getSSlSocketFactory() != null && getX509TrustManager() != null) + if (getSSlSocketFactory() != null && getX509TrustManager() != null) { builder.sslSocketFactory(getSSlSocketFactory(), getX509TrustManager()); + } return builder; } @@ -151,12 +155,12 @@ String getApiBaseUrl() { } private void initializeStrictMode() { - if (BuildConfig.DEBUG) { - StrictMode.setThreadPolicy( - new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build()); - StrictMode.setVmPolicy( - new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build()); - } + // if (BuildConfig.DEBUG) { + // StrictMode.setThreadPolicy( + // new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build()); + // StrictMode.setVmPolicy( + // new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build()); + // } } private void initializeRealm() { @@ -170,17 +174,18 @@ private void initializeRealm() { } private void initializeFlowUp() { - FlowUp.Builder.with(this) - .apiKey(getString(R.string.flow_up_api_key)) - .forceReports(BuildConfig.DEBUG) - .start(); + // FlowUp.Builder.with(this) + // .apiKey(getString(R.string.flow_up_api_key)) + // .forceReports(BuildConfig.DEBUG) + // .start(); } private boolean checkAppTampering(Context context) { - return checkAppSignature(context) - && verifyInstaller(context) - && checkEmulator() - && checkDebuggable(context); + return true; + // return checkAppSignature(context) + // && verifyInstaller(context) + // && checkEmulator() + // && checkDebuggable(context); } X509TrustManager getX509TrustManager() { diff --git a/sampleApp/src/main/java/com/zeyad/usecases/app/components/LifecycleRxJavaBinder.java b/sampleApp/src/main/java/com/zeyad/usecases/app/components/LifecycleRxJavaBinder.java index e27ff06..7d85ba7 100644 --- a/sampleApp/src/main/java/com/zeyad/usecases/app/components/LifecycleRxJavaBinder.java +++ b/sampleApp/src/main/java/com/zeyad/usecases/app/components/LifecycleRxJavaBinder.java @@ -10,20 +10,25 @@ import io.reactivex.MaybeTransformer; import io.reactivex.ObservableTransformer; import io.reactivex.SingleTransformer; +import io.reactivex.annotations.NonNull; /** * @author by ZIaDo on 6/14/17. */ public class LifecycleRxJavaBinder { - public static FlowableTransformer applyFlowable(LifecycleOwner lifecycleOwner) { + + private LifecycleRxJavaBinder() { + } + + public static FlowableTransformer applyFlowable(@NonNull final LifecycleOwner lifecycleOwner) { return flowable -> { LiveData liveData = LiveDataReactiveStreams.fromPublisher(flowable); return Flowable.fromPublisher(LiveDataReactiveStreams.toPublisher(lifecycleOwner, liveData)); }; } - public static ObservableTransformer applyObservable(LifecycleOwner lifecycleOwner, - BackpressureStrategy strategy) { + public static ObservableTransformer applyObservable(@NonNull final LifecycleOwner lifecycleOwner, + final BackpressureStrategy strategy) { return observable -> { LiveData liveData = LiveDataReactiveStreams.fromPublisher(observable.toFlowable(strategy)); return Flowable.fromPublisher(LiveDataReactiveStreams @@ -31,7 +36,7 @@ public static ObservableTransformer applyObservable(LifecycleOwner lif }; } - public static ObservableTransformer applyObservable(LifecycleOwner lifecycleOwner) { + public static ObservableTransformer applyObservable(@NonNull final LifecycleOwner lifecycleOwner) { return observable -> { LiveData liveData = LiveDataReactiveStreams.fromPublisher(observable .toFlowable(BackpressureStrategy.BUFFER)); @@ -40,7 +45,7 @@ public static ObservableTransformer applyObservable(LifecycleOwner lif }; } - public static SingleTransformer applySingle(LifecycleOwner lifecycleOwner) { + public static SingleTransformer applySingle(@NonNull final LifecycleOwner lifecycleOwner) { return single -> { LiveData liveData = LiveDataReactiveStreams.fromPublisher(single.toFlowable()); return Flowable.fromPublisher(LiveDataReactiveStreams @@ -48,11 +53,11 @@ public static SingleTransformer applySingle(LifecycleOwner lifecycleOw }; } - public static MaybeTransformer applyMaybe(LifecycleOwner lifecycleOwner) { + public static MaybeTransformer applyMaybe(@NonNull final LifecycleOwner lifecycleOwner) { return maybe -> { LiveData liveData = LiveDataReactiveStreams.fromPublisher(maybe.toFlowable()); return Flowable.fromPublisher(LiveDataReactiveStreams .toPublisher(lifecycleOwner, liveData)).firstElement(); }; } -} \ No newline at end of file +} diff --git a/sampleApp/src/main/java/com/zeyad/usecases/app/components/MyGlideModule.java b/sampleApp/src/main/java/com/zeyad/usecases/app/components/MyGlideModule.java index 1bdd973..bf062c9 100644 --- a/sampleApp/src/main/java/com/zeyad/usecases/app/components/MyGlideModule.java +++ b/sampleApp/src/main/java/com/zeyad/usecases/app/components/MyGlideModule.java @@ -10,7 +10,7 @@ public class MyGlideModule implements GlideModule { @Override public void applyOptions(Context context, GlideBuilder builder) { - // builder.setDiskCache(new DiskCacheFactory(context, “.”, IMAGE_CACHE_SIZE)); + // builder.setDiskCache(new DiskCacheFactory(context, ".", IMAGE_CACHE_SIZE)); } @Override diff --git a/sampleApp/src/main/java/com/zeyad/usecases/app/screens/user/list/UserListActivity.java b/sampleApp/src/main/java/com/zeyad/usecases/app/screens/user/list/UserListActivity.java index 702b8ba..fd82f91 100644 --- a/sampleApp/src/main/java/com/zeyad/usecases/app/screens/user/list/UserListActivity.java +++ b/sampleApp/src/main/java/com/zeyad/usecases/app/screens/user/list/UserListActivity.java @@ -109,12 +109,23 @@ public void initialize() { // protected void onResume() { // super.onResume(); // viewModel.getUser() -// .compose(bindToLifecycle()) -// .subscribe(user -> Log.d("Test", user.toString()), -// throwable -> { -// }); + // .compose(bindToLifecycle()) + // .doOnCancel(() -> Log.d("Test", "Cancelled")) + // .subscribe(user -> Log.d("Test", user.toString()), + // throwable -> { + // }); // } + @Override + public void setupUI() { + setContentView(R.layout.activity_user_list); + ButterKnife.bind(this); + setSupportActionBar(toolbar); + toolbar.setTitle(getTitle()); + setupRecyclerView(); + twoPane = findViewById(R.id.user_detail_container) != null; + } + @NonNull private SuccessStateAccumulator getUserListStateSuccessStateAccumulator() { return (newResult, event, currentStateBundle) -> { @@ -137,6 +148,8 @@ private SuccessStateAccumulator getUserListStateSuccessStateAccum .toList() .blockingGet(); break; + default: + break; } int lastId = users.get(users.size() - 1).getId(); users = new ArrayList<>(new HashSet<>(users)); @@ -146,16 +159,6 @@ private SuccessStateAccumulator getUserListStateSuccessStateAccum }; } - @Override - public void setupUI() { - setContentView(R.layout.activity_user_list); - ButterKnife.bind(this); - setSupportActionBar(toolbar); - toolbar.setTitle(getTitle()); - setupRecyclerView(); - twoPane = findViewById(R.id.user_detail_container) != null; - } - @Override public void renderState(UserListState state) { viewState = state; @@ -172,6 +175,17 @@ public void renderState(UserListState state) { } } + @Override + public void toggleViews(boolean toggle) { + loaderLayout.bringToFront(); + loaderLayout.setVisibility(toggle ? View.VISIBLE : View.GONE); + } + + @Override + public void showError(String message) { + showErrorSnackBar(message, userRecycler, Snackbar.LENGTH_LONG); + } + private void setupRecyclerView() { usersAdapter = new GenericRecyclerViewAdapter( (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE), new ArrayList<>()) { @@ -260,17 +274,6 @@ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { .doOnNext(searchUsersEvent -> Log.d("NextPageEvent", "fired!")))); } - @Override - public void toggleViews(boolean toggle) { - loaderLayout.bringToFront(); - loaderLayout.setVisibility(toggle ? View.VISIBLE : View.GONE); - } - - @Override - public void showError(String message) { - showErrorSnackBar(message, userRecycler, Snackbar.LENGTH_LONG); - } - @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.list_menu, menu); diff --git a/sampleApp/src/main/java/com/zeyad/usecases/app/utils/Utils.java b/sampleApp/src/main/java/com/zeyad/usecases/app/utils/Utils.java index 8eb0dee..23bcbc4 100644 --- a/sampleApp/src/main/java/com/zeyad/usecases/app/utils/Utils.java +++ b/sampleApp/src/main/java/com/zeyad/usecases/app/utils/Utils.java @@ -7,6 +7,9 @@ /** @author by ZIaDo on 10/1/16. */ public class Utils { + private Utils() { + } + public static boolean isNotEmpty(String text) { return text != null && !text.isEmpty() && !text.equalsIgnoreCase("null"); } diff --git a/tools/script-infer.gradle b/tools/script-infer.gradle index 1a27ffc..650b7a3 100644 --- a/tools/script-infer.gradle +++ b/tools/script-infer.gradle @@ -1,13 +1,13 @@ -apply plugin: 'com.uber.infer.android' -check.dependsOn 'inferDebug' -check.dependsOn 'eradicateDebug' -inferPlugin { - infer { - include = project.files("src/main") - exclude = project.files("build/") - } - eradicate { - include = project.files("src/main") - exclude = project.files("build/") - } -} \ No newline at end of file +//apply plugin: 'com.uber.infer.android' +//check.dependsOn 'inferDebug' +//check.dependsOn 'eradicateDebug' +//inferPlugin { +// infer { +// include = project.files("src/main") +// exclude = project.files("build/") +// } +// eradicate { +// include = project.files("src/main") +// exclude = project.files("build/") +// } +//} \ No newline at end of file diff --git a/usecases/build.gradle b/usecases/build.gradle index e6bb357..d4701e6 100644 --- a/usecases/build.gradle +++ b/usecases/build.gradle @@ -4,40 +4,13 @@ apply plugin: 'android-apt' apply plugin: 'maven' apply plugin: 'realm-android' -apply plugin: 'com.jfrog.bintray' apply plugin: 'com.github.dcendents.android-maven' apply plugin: "net.ltgt.errorprone" -//def siteUrl = "https://github.com/Zeyad-37/UseCases" -//def gitUrl = "https://github.com/Zeyad-37/UseCases.git" version = "1.0.1" group = "com.github.zeyad-37" -ext { - bintrayRepo = 'maven' - bintrayName = 'usecases' - - publishedGroupId = 'com.github.zeyad-37' - libraryName = 'UseCases' - artifact = 'usecases' - - libraryDescription = 'Is a library that is a generic implementation of the Domain and Data layers in a clean architecture.' - - siteUrl = 'https://github.com/Zeyad-37/UseCases' - gitUrl = 'https://github.com/Zeyad-37/UseCases.git' - - libraryVersion = version - - developerId = 'zeyad-37' - developerName = 'Zeyad Gasser' - developerEmail = 'zeyad.gasser@gmail.com' - - licenseName = 'The Apache Software License, Version 2.0' - licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' - allLicenses = ["Apache-2.0"] -} - buildscript { repositories { jcenter() @@ -60,9 +33,7 @@ android { targetSdkVersion 25 versionCode 1 versionName "1.0" -// multiDexEnabled true -// testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" -// testInstrumentationRunner "io.appflate.restmock.android.RESTMockTestRunner" + testInstrumentationRunner "com.zeyad.usecases.integration.AndroidRobolectricRunner" } buildTypes { @@ -71,7 +42,7 @@ android { // consumerProguardFiles 'proguard-project.txt' } release { -// consumerProguardFiles 'proguard-project.txt' +// proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } @@ -131,7 +102,7 @@ ext { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) //Job Dispatcher - compile 'com.firebase:firebase-jobdispatcher-with-gcm-dep:0.6.0' + compile 'com.firebase:firebase-jobdispatcher:0.6.0' // Network compile "com.squareup.retrofit2:retrofit:$retrofitVersion" compile "com.squareup.retrofit2:converter-gson:$retrofitVersion" @@ -152,8 +123,6 @@ dependencies { // Testing testCompile 'junit:junit:4.12' testCompile "com.android.support:support-annotations:$supportLibraryVersion" -// testCompile "com.android.support.test.espresso:espresso-core:$espressoCore" -// testCompile "org.mockito:mockito-core:2.0.57-beta" testCompile "org.mockito:mockito-core:1.10.19" testCompile "org.robolectric:robolectric:$robolectric" testCompile "org.robolectric:shadows-support-v4:$robolectric" @@ -164,96 +133,16 @@ dependencies { testCompile "org.powermock:powermock-classloading-xstream:$powerMock" testCompile "com.squareup.okhttp3:mockwebserver:$okhttpVersion" - testCompile 'com.github.andrzejchm.RESTMock:android:0.2.1' - testCompile 'org.khronos:opengl-api:gl1.1-android-2.1_r1' + testCompile 'com.github.andrzejchm.RESTMock:android:0.2.2' //required to resolve robolectric test problems testCompile("com.jakewharton.espresso:okhttp3-idling-resource:$okhttpIdelingResourceVersion") { exclude group: 'com.android.support' } - -// androidTestCompile("com.android.support.test.espresso:espresso-core:$espressoCore") { -// exclude group: 'com.android.support' -// } -// androidTestCompile("com.jakewharton.espresso:okhttp3-idling-resource:$okhttpIdelingResourceVersion") { -// exclude group: 'com.android.support' -// } -// androidTestCompile "com.squareup.okhttp3:mockwebserver:$okhttpVersion" -} - -task createPom { - apply plugin: 'maven' - description "Generates pom.xml" - pom { - project { - groupId group - artifactId 'usecases' - version version - packaging 'aar' - } - }.withXml { - def dependenciesNode = asNode().appendNode('dependencies') - - configurations.compile.allDependencies.each { dependency -> - def dependencyNode = dependenciesNode.appendNode('dependency') - dependencyNode.appendNode('groupId', dependency.group) - dependencyNode.appendNode('artifactId', dependency.name) - dependencyNode.appendNode('version', dependency.version) - } - }.writeTo("$buildDir/pom.xml") -} - -task sourcesJar(type: Jar) { - from android.sourceSets.main.java.srcDirs - classifier = 'sources' -} - -task javadoc(type: Javadoc) { - source = android.sourceSets.main.java.srcDirs - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) -} - -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir -} - -artifacts { - archives javadocJar - archives sourcesJar -} - -bintray { - user = 'zeyad-37' - key = '24dde90ae6153839f9a6053e9154d03619db2e63' - - configurations = ['archives'] - - pkg { - repo = "maven" - name = 'usecases' - -// websiteUrl = siteUrl -// vcsUrl = gitUrl - websiteUrl = 'https://github.com/Zeyad-37/UseCases' - vcsUrl = 'https://github.com/Zeyad-37/UseCases.git' - - licenses = ["Apache-2.0"] - publish = true - version { - gpg { - sign = true //Determines whether to GPG sign the files. The default is false - passphrase = 'ziado123' //Optional. The passphrase for GPG signing' - } - } - } } //apply from: "$project.rootDir/tools/script-git-version.gradle" apply from: "$project.rootDir/tools/script-findbugs.gradle" -apply from: "$project.rootDir/tools/script-pmd.gradle" +//apply from: "$project.rootDir/tools/script-pmd.gradle" //apply from: "$project.rootDir/tools/script-java-code-coverage.gradle" -apply from: "$project.rootDir/tools/script-infer.gradle" +//apply from: "$project.rootDir/tools/script-infer.gradle" //apply from: "$project.rootDir/tools/script-check-style.gradle" - -//apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' -//apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' \ No newline at end of file diff --git a/usecases/src/main/java/com/zeyad/usecases/api/DataService.java b/usecases/src/main/java/com/zeyad/usecases/api/DataService.java index 6e57bfa..5a129ae 100644 --- a/usecases/src/main/java/com/zeyad/usecases/api/DataService.java +++ b/usecases/src/main/java/com/zeyad/usecases/api/DataService.java @@ -25,6 +25,8 @@ */ class DataService implements IDataService { + private static final String CACHE_HIT = "cache Hit ", CACHE_MISS = "cache Miss ", + GET_LIST_OFFLINE_FIRST = "getListOffLineFirst", GET_OBJECT_OFFLINE_FIRST = "getObjectOffLineFirst"; private final DataStoreFactory mDataStoreFactory; private final Scheduler mPostExecutionThread; private final Scheduler mBackgroundThread; @@ -50,8 +52,8 @@ public Flowable> getList(@NonNull GetRequest getListRequest) { getListRequest.isPersist(), shouldCache); if (Utils.getInstance().withCache(shouldCache)) { result = mDataStoreFactory.memory().getAllItems(dataClass) - .doOnSuccess(m -> Log.d("getList", "cache Hit " + simpleName)) - .doOnError(throwable -> Log.d("getList", "cache Miss " + simpleName)) + .doOnSuccess(m -> Log.d("getList", CACHE_HIT + simpleName)) + .doOnError(throwable -> Log.d("getList", CACHE_MISS + simpleName)) .toFlowable() .onErrorResumeNext(t -> dynamicGetList); } else { @@ -63,45 +65,6 @@ public Flowable> getList(@NonNull GetRequest getListRequest) { return result.compose(applySchedulers()); } - @Override - public Flowable> getListOffLineFirst(@NonNull GetRequest getRequest) { - Flowable> result; - try { - Utils utils = Utils.getInstance(); - Class dataClass = getRequest.getDataClass(); - String simpleName = dataClass.getSimpleName(); - String idColumnName = getRequest.getIdColumnName(); - boolean persist = getRequest.isPersist(); - boolean shouldCache = getRequest.isShouldCache(); - boolean withDisk = utils.withDisk(persist); - boolean withCache = utils.withCache(shouldCache); - Flowable> cloud = mDataStoreFactory.cloud(dataClass) - .dynamicGetList(getRequest.getUrl(), idColumnName, dataClass, persist, shouldCache); - Flowable> disk = mDataStoreFactory.disk(dataClass) - .dynamicGetList("", idColumnName, dataClass, persist, shouldCache) - .doOnNext(m -> Log.d("getListOffLineFirst", "Disk Hit " + simpleName)) - .doOnError(throwable -> Log.e("getListOffLineFirst", "Disk Miss " + simpleName, - throwable)) - .flatMap(m -> m.isEmpty() ? cloud : Flowable.just(m)) - .onErrorResumeNext(t -> cloud); - Flowable> memory = mDataStoreFactory.memory().getAllItems(dataClass) - .doOnSuccess(m -> Log.d("getListOffLineFirst", "cache Hit " + simpleName)) - .doOnError(throwable -> Log.d("getListOffLineFirst", "cache Miss " + simpleName)) - .toFlowable() - .onErrorResumeNext(throwable -> withDisk ? disk : cloud); - if (withCache) { - result = memory; - } else if (withDisk) { - result = disk; - } else { - result = cloud; - } - } catch (IllegalAccessException e) { - result = Flowable.error(e); - } - return result.compose(ReplayingShare.instance()).compose(applySchedulers()); - } - @Override public Flowable getObject(@NonNull GetRequest getRequest) { Flowable result; @@ -112,13 +75,13 @@ public Flowable getObject(@NonNull GetRequest getRequest) { String url = getRequest.getUrl(); String simpleName = dataClass.getSimpleName(); Flowable dynamicGetObject = mDataStoreFactory.dynamically(url, dataClass) - .dynamicGetObject(url, getRequest.getIdColumnName(), itemId, getRequest.getIdType(), - dataClass, getRequest.isPersist(), shouldCache); + .dynamicGetObject(url, getRequest.getIdColumnName(), itemId, getRequest.getIdType(), + dataClass, getRequest.isPersist(), shouldCache); if (Utils.getInstance().withCache(shouldCache)) { result = mDataStoreFactory.memory() - .getItem(String.valueOf(itemId), dataClass) - .doOnSuccess(m -> Log.d("getObject", "cache Hit " + simpleName)) - .doOnError(throwable -> Log.d("getObject", "cache Miss " + simpleName)) + . getItem(String.valueOf(itemId), dataClass) + .doOnSuccess(m -> Log.d("getObject", CACHE_HIT + simpleName)) + .doOnError(throwable -> Log.d("getObject", CACHE_MISS + simpleName)) .toFlowable() .onErrorResumeNext(t -> dynamicGetObject); } else { @@ -130,61 +93,6 @@ public Flowable getObject(@NonNull GetRequest getRequest) { return result.compose(applySchedulers()); } - @Override - public Flowable getObjectOffLineFirst(@NonNull GetRequest getRequest) { - Flowable result; - try { - Utils utils = Utils.getInstance(); - Object itemId = getRequest.getItemId(); - Class dataClass = getRequest.getDataClass(); - Class idType = getRequest.getIdType(); - String idColumnName = getRequest.getIdColumnName(); - String simpleName = dataClass.getSimpleName(); - boolean persist = getRequest.isPersist(); - boolean shouldCache = getRequest.isShouldCache(); - boolean withDisk = utils.withDisk(persist); - boolean withCache = utils.withCache(shouldCache); - Flowable cloud = mDataStoreFactory.cloud(dataClass) - .dynamicGetObject(getRequest.getUrl(), idColumnName, itemId, idType, dataClass, - persist, shouldCache) - .doOnNext(m -> Log.d("getObjectOffLineFirst", "Cloud Hit " + simpleName)); - Flowable disk = mDataStoreFactory.disk(dataClass) - .dynamicGetObject("", idColumnName, itemId, idType, dataClass, persist, shouldCache) - .doOnNext(m -> Log.d("getObjectOffLineFirst", "Disk Hit " + simpleName)) - .doOnError(throwable -> Log.e("getObjectOffLineFirst", "Disk Miss " + simpleName, - throwable)) - .onErrorResumeNext(t -> cloud); - Flowable memory = mDataStoreFactory.memory() - .getItem(String.valueOf(itemId), dataClass) - .doOnSuccess(m -> Log.d("getObjectOffLineFirst", "cache Hit " + simpleName)) - .doOnError(throwable -> Log.d("getObjectOffLineFirst", "cache Miss " + simpleName)) - .toFlowable() - .onErrorResumeNext(t -> withDisk ? disk : cloud); - if (withCache) { - result = memory; - } else if (withDisk) { - result = disk; - } else { - result = cloud; - } - } catch (IllegalAccessException e) { - result = Flowable.error(e); - } - return result.compose(ReplayingShare.instance()).compose(applySchedulers()); - } - - @Override - public Flowable> queryDisk(RealmQueryProvider realmQueryProvider) { - Flowable> result; - try { - result = mDataStoreFactory.disk(Object.class).queryDisk(realmQueryProvider) - .compose(ReplayingShare.instance()); - } catch (IllegalAccessException e) { - result = Flowable.error(e); - } - return result.compose(applySchedulers()); - } - @Override public Flowable patchObject(@NonNull PostRequest postRequest) { Flowable result; @@ -304,6 +212,100 @@ public Single deleteAll(@NonNull PostRequest deleteRequest) { upstream.subscribeOn(mBackgroundThread).unsubscribeOn(mBackgroundThread)); } + @Override + public Flowable> queryDisk(RealmQueryProvider realmQueryProvider) { + Flowable> result; + try { + result = mDataStoreFactory.disk(Object.class). queryDisk(realmQueryProvider) + .compose(ReplayingShare.instance()); + } catch (IllegalAccessException e) { + result = Flowable.error(e); + } + return result.compose(applySchedulers()); + } + + @Override + public Flowable> getListOffLineFirst(@NonNull GetRequest getRequest) { + Flowable> result; + try { + Utils utils = Utils.getInstance(); + Class dataClass = getRequest.getDataClass(); + String simpleName = dataClass.getSimpleName(); + String idColumnName = getRequest.getIdColumnName(); + boolean persist = getRequest.isPersist(); + boolean shouldCache = getRequest.isShouldCache(); + boolean withDisk = utils.withDisk(persist); + boolean withCache = utils.withCache(shouldCache); + Flowable> cloud = mDataStoreFactory.cloud(dataClass) + .dynamicGetList(getRequest.getUrl(), idColumnName, dataClass, persist, shouldCache); + Flowable> disk = mDataStoreFactory.disk(dataClass) + . dynamicGetList("", idColumnName, dataClass, persist, shouldCache) + .doOnNext(m -> Log.d(GET_LIST_OFFLINE_FIRST, "Disk Hit " + simpleName)) + .doOnError(throwable -> Log.e(GET_LIST_OFFLINE_FIRST, "Disk Miss " + simpleName, + throwable)) + .flatMap(m -> m.isEmpty() ? cloud : Flowable.just(m)) + .onErrorResumeNext(t -> cloud); + Flowable> memory = mDataStoreFactory.memory(). getAllItems(dataClass) + .doOnSuccess(m -> Log.d(GET_LIST_OFFLINE_FIRST, CACHE_HIT + simpleName)) + .doOnError(throwable -> Log.d(GET_LIST_OFFLINE_FIRST, CACHE_MISS + simpleName)) + .toFlowable() + .onErrorResumeNext(throwable -> withDisk ? disk : cloud); + if (withCache) { + result = memory; + } else if (withDisk) { + result = disk; + } else { + result = cloud; + } + } catch (IllegalAccessException e) { + result = Flowable.error(e); + } + return result.compose(ReplayingShare.instance()).compose(applySchedulers()); + } + + @Override + public Flowable getObjectOffLineFirst(@NonNull GetRequest getRequest) { + Flowable result; + try { + Utils utils = Utils.getInstance(); + Object itemId = getRequest.getItemId(); + Class dataClass = getRequest.getDataClass(); + Class idType = getRequest.getIdType(); + String idColumnName = getRequest.getIdColumnName(); + String simpleName = dataClass.getSimpleName(); + boolean persist = getRequest.isPersist(); + boolean shouldCache = getRequest.isShouldCache(); + boolean withDisk = utils.withDisk(persist); + boolean withCache = utils.withCache(shouldCache); + Flowable cloud = mDataStoreFactory.cloud(dataClass) + . dynamicGetObject(getRequest.getUrl(), idColumnName, itemId, idType, dataClass, + persist, shouldCache) + .doOnNext(m -> Log.d(GET_OBJECT_OFFLINE_FIRST, "Cloud Hit " + simpleName)); + Flowable disk = mDataStoreFactory.disk(dataClass) + . dynamicGetObject("", idColumnName, itemId, idType, dataClass, persist, shouldCache) + .doOnNext(m -> Log.d(GET_OBJECT_OFFLINE_FIRST, "Disk Hit " + simpleName)) + .doOnError(throwable -> Log.e(GET_OBJECT_OFFLINE_FIRST, "Disk Miss " + simpleName, + throwable)) + .onErrorResumeNext(t -> cloud); + Flowable memory = mDataStoreFactory.memory() + . getItem(String.valueOf(itemId), dataClass) + .doOnSuccess(m -> Log.d(GET_OBJECT_OFFLINE_FIRST, CACHE_HIT + simpleName)) + .doOnError(throwable -> Log.d(GET_OBJECT_OFFLINE_FIRST, CACHE_MISS + simpleName)) + .toFlowable() + .onErrorResumeNext(t -> withDisk ? disk : cloud); + if (withCache) { + result = memory; + } else if (withDisk) { + result = disk; + } else { + result = cloud; + } + } catch (IllegalAccessException e) { + result = Flowable.error(e); + } + return result.compose(ReplayingShare.instance()).compose(applySchedulers()); + } + @Override public Flowable uploadFile(@NonNull FileIORequest fileIORequest) { return mDataStoreFactory.cloud(fileIORequest.getDataClass()) diff --git a/usecases/src/main/java/com/zeyad/usecases/api/DataServiceConfig.java b/usecases/src/main/java/com/zeyad/usecases/api/DataServiceConfig.java index 9ab32d5..8681a15 100644 --- a/usecases/src/main/java/com/zeyad/usecases/api/DataServiceConfig.java +++ b/usecases/src/main/java/com/zeyad/usecases/api/DataServiceConfig.java @@ -96,7 +96,7 @@ HandlerThread getHandlerThread() { } public static class Builder { - private Context context; + private final Context context; private OkHttpClient.Builder okHttpBuilder; private Cache okHttpCache; private String baseUrl; diff --git a/usecases/src/main/java/com/zeyad/usecases/api/DataServiceFactory.java b/usecases/src/main/java/com/zeyad/usecases/api/DataServiceFactory.java index d3e8414..3095d99 100644 --- a/usecases/src/main/java/com/zeyad/usecases/api/DataServiceFactory.java +++ b/usecases/src/main/java/com/zeyad/usecases/api/DataServiceFactory.java @@ -30,7 +30,7 @@ private DataServiceFactory() { @Nullable public static IDataService getInstance() { if (sDataUseCase == null) { - throw new NullPointerException( + throw new IllegalAccessError( "DataServiceFactory#init must be called before calling getInstance()"); } return sDataUseCase; 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 1fb216d..034bced 100644 --- a/usecases/src/main/java/com/zeyad/usecases/db/RealmManager.java +++ b/usecases/src/main/java/com/zeyad/usecases/db/RealmManager.java @@ -29,9 +29,6 @@ public class RealmManager implements DataBaseManager { private static final String NO_ID = "Could not find id!"; - public RealmManager() { - } - /** * Gets an {@link Flowable} which will emit an Object * @param idColumnName name of ID variable @@ -133,6 +130,15 @@ public Single put(@Nullable JSONObject jsonObject, @Nullable String idC }); } + @NonNull + @Override + public Single putAll(List realmObjects, Class dataClass) { + return Single.fromCallable(() -> getRealm(Realm.getDefaultInstance()) + .map(realm1 -> executeWriteOperationInRealm(realm1, + () -> realm1.copyToRealmOrUpdate(realmObjects)).size() == realmObjects.size()) + .blockingFirst()); + } + /** * Puts and element into the DB. * @@ -154,15 +160,6 @@ public Single putAll(@NonNull JSONArray jsonArray, String idColumnName, }); } - @NonNull - @Override - public Single putAll(List realmObjects, Class dataClass) { - return Single.fromCallable(() -> getRealm(Realm.getDefaultInstance()) - .map(realm1 -> executeWriteOperationInRealm(realm1, - () -> realm1.copyToRealmOrUpdate(realmObjects)).size() == realmObjects.size()) - .blockingFirst()); - } - /** * Evict all elements of the DB. * @@ -178,6 +175,23 @@ public Single evictAll(@NonNull Class clazz) { }).blockingFirst()); } + /** + * 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. * @@ -208,23 +222,6 @@ public void run() { .blockingFirst(); } - /** - * 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()); - } - private Flowable getItemById(Realm realm, @NonNull Class dataClass, @NonNull String idColumnName, final Object itemId, final Class itemIdType) { RealmModel result; @@ -232,9 +229,11 @@ private Flowable getItemById(Realm realm, @NonNull Class dataClass, result = realm.where(dataClass).equalTo(idColumnName, (long) itemId).findFirst(); } else if (itemIdType.equals(int.class) || itemIdType.equals(Integer.class)) { result = realm.where(dataClass).equalTo(idColumnName, (int) 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)) { + } + // 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(); @@ -289,9 +288,9 @@ private JSONObject updateJsonObjectWithIdValue(@NonNull JSONObject jsonObject, @ if (idColumnName == null || idColumnName.isEmpty()) { throw new IllegalArgumentException(NO_ID); } - if (itemIdType.equals(String.class)) + if (itemIdType.equals(String.class)) { return jsonObject; - else if (jsonObject.optInt(idColumnName) == 0) { + } else if (jsonObject.optInt(idColumnName) == 0) { jsonObject.put(idColumnName, getNextId(dataClass, idColumnName)); } return jsonObject; diff --git a/usecases/src/main/java/com/zeyad/usecases/network/ApiConnection.java b/usecases/src/main/java/com/zeyad/usecases/network/ApiConnection.java index 024bbd3..3b51a8e 100644 --- a/usecases/src/main/java/com/zeyad/usecases/network/ApiConnection.java +++ b/usecases/src/main/java/com/zeyad/usecases/network/ApiConnection.java @@ -6,11 +6,7 @@ import com.zeyad.usecases.BuildConfig; import com.zeyad.usecases.Config; -import com.zeyad.usecases.utils.Utils; -import java.io.File; -import java.io.IOException; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; @@ -18,23 +14,11 @@ import io.reactivex.Flowable; import okhttp3.Cache; -import okhttp3.CacheControl; -import okhttp3.CertificatePinner; -import okhttp3.ConnectionSpec; -import okhttp3.Interceptor; -import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.OkHttpClient; -import okhttp3.Request; import okhttp3.RequestBody; -import okhttp3.Response; import okhttp3.ResponseBody; -import okhttp3.TlsVersion; import okhttp3.logging.HttpLoggingInterceptor; -import okio.Buffer; -import okio.BufferedSink; -import okio.GzipSink; -import okio.Okio; import retrofit2.Retrofit; import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; import retrofit2.converter.gson.GsonConverterFactory; @@ -44,9 +28,8 @@ * executed asynchronously can return a value. */ public class ApiConnection { - private static final String - CACHING_DISABLED = "There would be no caching. Since caching module is disabled.", - CACHE_CONTROL = "Cache-Control"; + private static final String CACHING_DISABLED = "There would be no caching. Since caching module is disabled.";//, + // CACHE_CONTROL = "Cache-Control"; private static final int TIME_OUT = 15; private final RestApi mRestApiWithoutCache, mRestApiWithCache; @@ -57,17 +40,14 @@ public ApiConnection(RestApi restApiWithoutCache, RestApi restApiWithCache) { public static RestApi initWithCache( @Nullable OkHttpClient.Builder okHttpBuilder, @Nullable Cache cache) { - if (okHttpBuilder == null) { - okHttpBuilder = getBuilderForOkHttp(); - } - return createRetro2Client(provideOkHttpClient(okHttpBuilder, cache)).create(RestApi.class); + + return createRetro2Client(provideOkHttpClient(okHttpBuilder == null ? + getBuilderForOkHttp() : okHttpBuilder, cache)).create(RestApi.class); } public static RestApi init(@Nullable OkHttpClient.Builder okHttpBuilder) { - if (okHttpBuilder == null) { - okHttpBuilder = getBuilderForOkHttp(); - } - return createRetro2Client(provideOkHttpClient(okHttpBuilder, null)).create(RestApi.class); + return createRetro2Client(provideOkHttpClient(okHttpBuilder == null ? + getBuilderForOkHttp() : okHttpBuilder, null)).create(RestApi.class); } @NonNull @@ -77,29 +57,29 @@ static HttpLoggingInterceptor provideHttpLoggingInterceptor() { HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.NONE); } - private static Interceptor provideCacheInterceptor() { - return chain -> { - Response response = chain.proceed(chain.request()); - // re-write response header to force use of cache - CacheControl cacheControl = - new CacheControl.Builder().maxAge(2, TimeUnit.MINUTES).build(); - return response.newBuilder().header(CACHE_CONTROL, cacheControl.toString()).build(); - }; - } - - private static Interceptor provideOfflineCacheInterceptor() { - return chain -> { - Request request = chain.request(); - if (!Utils.getInstance().isNetworkAvailable(Config.getInstance().getContext())) { - request = request.newBuilder() - .cacheControl(new CacheControl.Builder() - .maxStale(1, TimeUnit.DAYS) - .build()) - .build(); - } - return chain.proceed(request); - }; - } + // private static Interceptor provideCacheInterceptor() { + // return chain -> { + // Response response = chain.proceed(chain.request()); + // // re-write response header to force use of cache + // CacheControl cacheControl = + // new CacheControl.Builder().maxAge(2, TimeUnit.MINUTES).build(); + // return response.newBuilder().header(CACHE_CONTROL, cacheControl.toString()).build(); + // }; + // } + + // private static Interceptor provideOfflineCacheInterceptor() { + // return chain -> { + // Request request = chain.request(); + // if (!Utils.getInstance().isNetworkAvailable(Config.getInstance().getContext())) { + // request = request.newBuilder() + // .cacheControl(new CacheControl.Builder() + // .maxStale(1, TimeUnit.DAYS) + // .build()) + // .build(); + // } + // return chain.proceed(request); + // }; + // } @NonNull private static OkHttpClient.Builder getBuilderForOkHttp() { @@ -200,88 +180,85 @@ RestApi getRestApiWithCache() { return mRestApiWithCache; } - @SuppressWarnings("unused") - private Interceptor provideGzipRequestInterceptor() { - return chain -> { - Request originalRequest = chain.request(); - return originalRequest.body() == null || originalRequest.header("Content-Encoding") != null ? - chain.proceed(originalRequest) : - chain.proceed(originalRequest.newBuilder().header("Content-Encoding", "gzip") - .method(originalRequest.method(), forceContentLength(gzip(originalRequest.body()))) - .build()); - }; - } - - @SuppressWarnings("unused") - private CertificatePinner provideCertificatePinner() { - return new CertificatePinner.Builder() - // .add("api.github.com", "sha256/6wJsqVDF8K19zxfLxV5DGRneLyzso9adVdUN/exDacw=") - .build(); - } - - @SuppressWarnings("unused") - private List provideConnectionSpecsList() { - return Collections.singletonList(new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) - .tlsVersions(TlsVersion.TLS_1_2) - .build()); - } - - @NonNull - private RequestBody forceContentLength(@NonNull final RequestBody requestBody) throws IOException { - final Buffer buffer = new Buffer(); - requestBody.writeTo(buffer); - return new RequestBody() { - @Override - public MediaType contentType() { - return requestBody.contentType(); - } - - @Override - public long contentLength() { - return buffer.size(); - } - - @Override - public void writeTo(@NonNull BufferedSink sink) throws IOException { - sink.write(buffer.snapshot()); - } - }; - } - - @NonNull - private RequestBody gzip(@NonNull final RequestBody body) { - return new RequestBody() { - @Override - public MediaType contentType() { - return body.contentType(); - } - - @Override - public long contentLength() { - return -1; // We don't know the compressed length in advance! - } - - @Override - public void writeTo(@NonNull BufferedSink sink) throws IOException { - BufferedSink gzipSink = Okio.buffer(new GzipSink(sink)); - body.writeTo(gzipSink); - gzipSink.close(); - } - }; - } - - @SuppressWarnings("unused") - @Nullable - private Cache provideCache() { - Cache cache = null; - try { - cache = new Cache(new File(Config.getInstance().getContext().getCacheDir(), "http-cache"), - 10 * 1024 * 1024); // 10 MB - } catch (Exception e) { - Log.e("ApiConnection", "", e); - } - return cache; - } + // private Interceptor provideGzipRequestInterceptor() { + // return chain -> { + // Request originalRequest = chain.request(); + // return originalRequest.body() == null || originalRequest.header("Content-Encoding") != null ? + // chain.proceed(originalRequest) : + // chain.proceed(originalRequest.newBuilder().header("Content-Encoding", "gzip") + // .method(originalRequest.method(), forceContentLength(gzip(originalRequest.body()))) + // .build()); + // }; + // } + + // private CertificatePinner provideCertificatePinner() { + // return new CertificatePinner.Builder() + // // .add("api.github.com", "sha256/6wJsqVDF8K19zxfLxV5DGRneLyzso9adVdUN/exDacw=") + // .build(); + // } + + // @SuppressWarnings("unused") + // private List provideConnectionSpecsList() { + // return Collections.singletonList(new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + // .tlsVersions(TlsVersion.TLS_1_2) + // .build()); + // } + + // @NonNull + // private RequestBody forceContentLength(@NonNull final RequestBody requestBody) throws IOException { + // final Buffer buffer = new Buffer(); + // requestBody.writeTo(buffer); + // return new RequestBody() { + // @Override + // public MediaType contentType() { + // return requestBody.contentType(); + // } + // + // @Override + // public long contentLength() { + // return buffer.size(); + // } + // + // @Override + // public void writeTo(@NonNull BufferedSink sink) throws IOException { + // sink.write(buffer.snapshot()); + // } + // }; + // } + + // @NonNull + // private RequestBody gzip(@NonNull final RequestBody body) { + // return new RequestBody() { + // @Override + // public MediaType contentType() { + // return body.contentType(); + // } + // + // @Override + // public long contentLength() { + // return -1; // We don't know the compressed length in advance! + // } + // + // @Override + // public void writeTo(@NonNull BufferedSink sink) throws IOException { + // BufferedSink gzipSink = Okio.buffer(new GzipSink(sink)); + // body.writeTo(gzipSink); + // gzipSink.close(); + // } + // }; + // } + + // @Nullable + // private Cache provideCache() { + // Cache cache = null; + // try { + // cache = new Cache(new File(Config.getInstance().getContext().getCacheDir(), "http-cache"), + // 10 * 1024 * 1024); // 10 MB + // } catch (Exception e) { + // Log.e("ApiConnection", "", e); + // } + // return cache; + // } private void logNoCache() { Log.e(getClass().getSimpleName(), CACHING_DISABLED); diff --git a/usecases/src/main/java/com/zeyad/usecases/network/TLSSocketFactory.java b/usecases/src/main/java/com/zeyad/usecases/network/TLSSocketFactory.java index bf1415a..fe0cfda 100644 --- a/usecases/src/main/java/com/zeyad/usecases/network/TLSSocketFactory.java +++ b/usecases/src/main/java/com/zeyad/usecases/network/TLSSocketFactory.java @@ -6,9 +6,9 @@ import java.io.IOException; import java.net.InetAddress; import java.net.Socket; -import java.security.GeneralSecurityException; import java.security.KeyManagementException; import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.util.Arrays; @@ -24,7 +24,7 @@ public class TLSSocketFactory extends SSLSocketFactory { private final SSLSocketFactory internalSSLSocketFactory; - public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException { + public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException { SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, new TrustManager[] {systemDefaultTrustManager()}, null); internalSSLSocketFactory = sslContext.getSocketFactory(); @@ -78,26 +78,22 @@ public Socket createSocket( @Nullable private Socket enableTLSOnSocket(@Nullable Socket socket) { - if (socket != null && (socket instanceof SSLSocket)) { + if (socket instanceof SSLSocket) { ((SSLSocket) socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"}); } return socket; } @NonNull - private X509TrustManager systemDefaultTrustManager() { - try { - TrustManagerFactory trustManagerFactory = - TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - trustManagerFactory.init((KeyStore) null); - TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); - if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { - throw new IllegalStateException( - "Unexpected default trust managers:" + Arrays.toString(trustManagers)); - } - return (X509TrustManager) trustManagers[0]; - } catch (GeneralSecurityException e) { - throw new AssertionError(); // The system has no TLS. Just give up. + private X509TrustManager systemDefaultTrustManager() throws NoSuchAlgorithmException, KeyStoreException { + TrustManagerFactory trustManagerFactory = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init((KeyStore) null); + TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); + if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { + throw new IllegalStateException( + "Unexpected default trust managers:" + Arrays.toString(trustManagers)); } + return (X509TrustManager) trustManagers[0]; } } diff --git a/usecases/src/main/java/com/zeyad/usecases/requests/FileIORequest.java b/usecases/src/main/java/com/zeyad/usecases/requests/FileIORequest.java index 829cb68..46d074c 100644 --- a/usecases/src/main/java/com/zeyad/usecases/requests/FileIORequest.java +++ b/usecases/src/main/java/com/zeyad/usecases/requests/FileIORequest.java @@ -109,7 +109,7 @@ public void writeToParcel(@NonNull Parcel dest, int flags) { public static class Builder { - private File file; + private final File file; private String url, key; private boolean onWifi, whileCharging, queuable; private Class dataClass; diff --git a/usecases/src/main/java/com/zeyad/usecases/requests/GetRequest.java b/usecases/src/main/java/com/zeyad/usecases/requests/GetRequest.java index cee7251..0889023 100644 --- a/usecases/src/main/java/com/zeyad/usecases/requests/GetRequest.java +++ b/usecases/src/main/java/com/zeyad/usecases/requests/GetRequest.java @@ -93,10 +93,12 @@ public void writeToParcel(Parcel dest, int flags) { } public static class Builder { + private final boolean mPersist; + private final Class mDataClass; private Object mItemId; - private boolean mShouldCache, mPersist; + private boolean mShouldCache; private String mIdColumnName, mUrl; - private Class mDataClass, idType; + private Class idType; public Builder(Class dataClass, boolean persist) { mDataClass = dataClass; diff --git a/usecases/src/main/java/com/zeyad/usecases/requests/PostRequest.java b/usecases/src/main/java/com/zeyad/usecases/requests/PostRequest.java index 77b5484..81e0fbf 100644 --- a/usecases/src/main/java/com/zeyad/usecases/requests/PostRequest.java +++ b/usecases/src/main/java/com/zeyad/usecases/requests/PostRequest.java @@ -103,7 +103,7 @@ public JSONArray getArrayBundle() { jsonArray.put(object); } return jsonArray; - } else if (object != null && object instanceof List) { + } else if (object instanceof List) { final JSONArray jsonArray = new JSONArray(); List ids = (List) object; for (Object object : ids) { diff --git a/usecases/src/main/java/com/zeyad/usecases/services/jobs/FileIO.java b/usecases/src/main/java/com/zeyad/usecases/services/jobs/FileIO.java index 46a5018..02c6ee0 100644 --- a/usecases/src/main/java/com/zeyad/usecases/services/jobs/FileIO.java +++ b/usecases/src/main/java/com/zeyad/usecases/services/jobs/FileIO.java @@ -19,13 +19,13 @@ */ public class FileIO { private static final String TAG = FileIO.class.getSimpleName(), ON_ERROR = "onError"; - private static int mTrailCount; @NonNull private final FirebaseJobDispatcher mDispatcher; private final FileIORequest mFileIORequest; private final CloudStore mCloudStore; private final Utils mUtils; private final boolean mIsDownload; + private final int mTrailCount; public FileIO(int trailCount, FileIORequest payLoad, Context context, boolean isDownload, CloudStore cloudStore, Utils utils) { @@ -61,9 +61,8 @@ private void onError(Throwable throwable) { } void queueIOFile() { - mTrailCount++; if (mTrailCount < 3) { - mUtils.queueFileIOCore(mDispatcher, mIsDownload, mFileIORequest); + mUtils.queueFileIOCore(mDispatcher, mIsDownload, mFileIORequest, mTrailCount + 1); } } } diff --git a/usecases/src/main/java/com/zeyad/usecases/services/jobs/Post.java b/usecases/src/main/java/com/zeyad/usecases/services/jobs/Post.java index 4087670..86283df 100644 --- a/usecases/src/main/java/com/zeyad/usecases/services/jobs/Post.java +++ b/usecases/src/main/java/com/zeyad/usecases/services/jobs/Post.java @@ -23,12 +23,12 @@ */ public class Post { private static final String TAG = Post.class.getSimpleName(), ON_ERROR = "onError", COMPLETED = "Completed"; - private static int mTrailCount; @NonNull private final FirebaseJobDispatcher mDispatcher; private final PostRequest mPostRequest; private final ApiConnection mRestApi; private final Utils mUtils; + private final int mTrailCount; public Post(Context context, PostRequest postRequest, ApiConnection restApi, int trailCount, Utils utils) { mPostRequest = postRequest; @@ -112,9 +112,8 @@ private void onError(Throwable throwable) { } void queuePost() { - mTrailCount++; if (mTrailCount < 3) { // inject value at init! - mUtils.queuePostCore(mDispatcher, mPostRequest); + mUtils.queuePostCore(mDispatcher, mPostRequest, mTrailCount + 1); } } } diff --git a/usecases/src/main/java/com/zeyad/usecases/stores/CloudStore.java b/usecases/src/main/java/com/zeyad/usecases/stores/CloudStore.java index a88c6ed..298d406 100644 --- a/usecases/src/main/java/com/zeyad/usecases/stores/CloudStore.java +++ b/usecases/src/main/java/com/zeyad/usecases/stores/CloudStore.java @@ -35,10 +35,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.TimeUnit; import io.reactivex.Flowable; -import io.reactivex.FlowableTransformer; import io.reactivex.Single; import io.reactivex.SingleObserver; import io.reactivex.disposables.Disposable; @@ -55,7 +53,7 @@ public class CloudStore implements DataStore { public static final String APPLICATION_JSON = "application/json"; private static final String TAG = CloudStore.class.getSimpleName(), MULTIPART_FORM_DATA = "multipart/form-data"; - private static final int COUNTER_START = 1, ATTEMPTS = 3; + // private static final int COUNTER_START = 1, ATTEMPTS = 3; private final DataBaseManager mDataBaseManager; @NonNull private final DAOMapper mEntityDataMapper; @@ -86,16 +84,6 @@ private Flowable getErrorFlowableNotPersisted() { return Flowable.error(new NetworkConnectionException("Could not reach server and could not saveToDisk to queue!")); } - @NonNull - @Override - public Flowable dynamicGetObject(String url, String idColumnName, Object itemId, Class itemIdType, - @NonNull Class dataClass, boolean saveToDisk, boolean shouldCache) { - return mApiConnection.dynamicGetObject(url, shouldCache) - .doOnNext(m -> saveLocally(idColumnName, itemIdType, - new JSONObject(gson.toJson(m)), dataClass, saveToDisk, shouldCache)) - .map(entity -> mEntityDataMapper.mapTo(entity, dataClass)); - } - @NonNull @Override public Flowable> dynamicGetList(String url, String idColumnName, @@ -111,6 +99,22 @@ public Flowable> dynamicGetList(String url, String idColumnName, }); } + @NonNull + @Override + public Flowable dynamicGetObject(String url, String idColumnName, Object itemId, Class itemIdType, + @NonNull Class dataClass, boolean saveToDisk, boolean shouldCache) { + return mApiConnection. dynamicGetObject(url, shouldCache) + .doOnNext(m -> saveLocally(idColumnName, itemIdType, + new JSONObject(gson.toJson(m)), dataClass, saveToDisk, shouldCache)) + .map(entity -> mEntityDataMapper. mapTo(entity, dataClass)); + } + + @NonNull + @Override + public Flowable> queryDisk(RealmQueryProvider queryFactory) { + return Flowable.error(new IllegalAccessException("Can not search disk in cloud data store!")); + } + @NonNull @Override public Flowable dynamicPatchObject(String url, String idColumnName, Class itemIdType, @@ -283,42 +287,6 @@ public Single dynamicDeleteAll(Class dataClass) { return Single.error(new IllegalStateException("Can not delete all from cloud data store!")); } - @NonNull - @Override - public Flowable dynamicUploadFile(String url, @NonNull File file, @NonNull String key, - @Nullable Map parameters, - boolean onWifi, boolean whileCharging, boolean queuable, - @NonNull Class dataClass) { - return Flowable.defer(() -> { - if (isQueuableIfOutOfNetwork(queuable) && isOnWifi(Config.getInstance().getContext()) == onWifi - && isChargingReqCompatible(isCharging(Config.getInstance().getContext()), whileCharging)) { - queueIOFile(url, file, true, whileCharging, false); - return Flowable.empty(); - } else if (!mUtils.isNetworkAvailable(Config.getInstance().getContext())) { - return getErrorFlowableNotPersisted(); - } - RequestBody requestFile = RequestBody.create(MediaType.parse(MULTIPART_FORM_DATA), file); - HashMap map = new HashMap<>(); - map.put(key, requestFile); - if (parameters != null && !parameters.isEmpty()) { - for (Map.Entry entry : parameters.entrySet()) { - map.put(entry.getKey(), RequestBody.create(MediaType.parse(MULTIPART_FORM_DATA), - String.valueOf(entry.getValue()))); - } - } - return mApiConnection.dynamicUpload(url, map, MultipartBody.Part.createFormData(key, - file.getName(), requestFile)) - .map(object -> daoMapHelper(dataClass, object)) - .onErrorResumeNext(throwable -> { - if (isQueuableIfOutOfNetwork(queuable) && isNetworkFailure(throwable)) { - queueIOFile(url, file, true, whileCharging, false); - return Flowable.empty(); - } - return Flowable.error(throwable); - }); - }); - } - @NonNull @Override public Flowable dynamicDownloadFile(String url, @NonNull File file, boolean onWifi, @@ -379,21 +347,51 @@ && isChargingReqCompatible(isCharging(Config.getInstance().getContext()), whileC @NonNull @Override - public Flowable> queryDisk(RealmQueryProvider queryFactory) { - return Flowable.error(new IllegalAccessException("Can not search disk in cloud data store!")); - } - - private FlowableTransformer applyExponentialBackoff() { - return observable -> observable.retryWhen(attempts -> { - return attempts.zipWith( - Flowable.range(COUNTER_START, ATTEMPTS), (n, i) -> i) - .flatMap(i -> { - Log.d(TAG, "delay retry by " + i + " second(s)"); - return Flowable.timer(5 * i, TimeUnit.SECONDS); + public Flowable dynamicUploadFile(String url, @NonNull File file, @NonNull String key, + @Nullable Map parameters, + boolean onWifi, boolean whileCharging, boolean queuable, + @NonNull Class dataClass) { + return Flowable.defer(() -> { + if (isQueuableIfOutOfNetwork(queuable) && isOnWifi(Config.getInstance().getContext()) == onWifi + && isChargingReqCompatible(isCharging(Config.getInstance().getContext()), whileCharging)) { + queueIOFile(url, file, true, whileCharging, false); + return Flowable.empty(); + } else if (!mUtils.isNetworkAvailable(Config.getInstance().getContext())) { + return getErrorFlowableNotPersisted(); + } + RequestBody requestFile = RequestBody.create(MediaType.parse(MULTIPART_FORM_DATA), file); + HashMap map = new HashMap<>(); + map.put(key, requestFile); + if (parameters != null && !parameters.isEmpty()) { + for (Map.Entry entry : parameters.entrySet()) { + map.put(entry.getKey(), RequestBody.create(MediaType.parse(MULTIPART_FORM_DATA), + String.valueOf(entry.getValue()))); + } + } + return mApiConnection. dynamicUpload(url, map, MultipartBody.Part.createFormData(key, + file.getName(), requestFile)) + .map(object -> daoMapHelper(dataClass, object)) + .onErrorResumeNext(throwable -> { + if (isQueuableIfOutOfNetwork(queuable) && isNetworkFailure(throwable)) { + queueIOFile(url, file, true, whileCharging, false); + return Flowable.empty(); + } + return Flowable.error(throwable); }); }); } + // private FlowableTransformer applyExponentialBackoff() { + // return observable -> observable.retryWhen(attempts -> { + // return attempts.zipWith( + // Flowable.range(COUNTER_START, ATTEMPTS), (n, i) -> i) + // .flatMap(i -> { + // Log.d(TAG, "delay retry by " + i + " second(s)"); + // return Flowable.timer(5 * i, TimeUnit.SECONDS); + // }); + // }); + // } + @Nullable private M daoMapHelper(@NonNull Class dataClass, M object) { return object instanceof List ? @@ -452,7 +450,7 @@ private void queueIOFile(String url, File file, boolean onWifi, boolean whileCha mUtils.queueFileIOCore(mDispatcher, isDownload, new FileIORequest.Builder(url, file) .onWifi(onWifi) .whileCharging(whileCharging) - .build()); + .build(), 0); } private void queuePost(String method, String url, String idColumnName, Class idType, @@ -476,7 +474,7 @@ private void queuePost(String method, String url, String idColumnName, Class idT } private void queuePostCore(@NonNull PostRequest postRequest) { - mUtils.queuePostCore(mDispatcher, postRequest); + mUtils.queuePostCore(mDispatcher, postRequest, 0); } private void saveAllToDisk(List collection, Class dataClass) { diff --git a/usecases/src/main/java/com/zeyad/usecases/stores/DataStoreFactory.java b/usecases/src/main/java/com/zeyad/usecases/stores/DataStoreFactory.java index 50d5376..71183fd 100644 --- a/usecases/src/main/java/com/zeyad/usecases/stores/DataStoreFactory.java +++ b/usecases/src/main/java/com/zeyad/usecases/stores/DataStoreFactory.java @@ -17,7 +17,7 @@ public class DataStoreFactory { private final DataBaseManagerUtil mDataBaseManagerUtil; private final ApiConnection mApiConnection; private final DAOMapper mDAOMapper; - private boolean withCache; + private final boolean withCache; public DataStoreFactory(@Nullable DataBaseManagerUtil dataBaseManagerUtil, ApiConnection restApi, DAOMapper daoMapper) { diff --git a/usecases/src/main/java/com/zeyad/usecases/stores/MemoryStore.java b/usecases/src/main/java/com/zeyad/usecases/stores/MemoryStore.java index bb1e995..3ae6e70 100644 --- a/usecases/src/main/java/com/zeyad/usecases/stores/MemoryStore.java +++ b/usecases/src/main/java/com/zeyad/usecases/stores/MemoryStore.java @@ -50,13 +50,14 @@ public Single> getAllItems(@NonNull Class dataClass) { return Single.defer(() -> { final boolean[] missed = new boolean[1]; Set stringSet = mapOfIds.get(dataClass); - if (stringSet == null) + if (stringSet == null) { return Single.error(new IllegalAccessException("Cache Miss!")); + } List result = Observable.fromIterable(stringSet) .filter((key) -> { - if (isValid(key)) + if (isValid(key)) { return true; - else { + } else { missed[0] = true; return false; } @@ -97,22 +98,22 @@ void deleteList(@NonNull List ids, @NonNull Class dataClass) { } String className = dataClass.getSimpleName(); Observable.fromIterable(ids) - .map(id -> className + String.valueOf(id)) - .filter((key) -> { - if (isValid(key)) + .map(id -> className + id) + .filter((key) -> { + if (isValid(key)) { return true; - else { + } else { removeKey(dataClass, key); return false; } }) - .doOnEach(stringNotification -> { + .doOnEach(stringNotification -> { String key = stringNotification.getValue(); removeKey(dataClass, key); Log.d(TAG, String.format("%s %s deleted!, id = %s", className, (Storo.delete(key) ? "" : "not "), key)); }) - .blockingSubscribe(); + .blockingSubscribe(); } private void addKey(Class dataType, String key) { diff --git a/usecases/src/main/java/com/zeyad/usecases/utils/Utils.java b/usecases/src/main/java/com/zeyad/usecases/utils/Utils.java index 99c8fdb..c6a8294 100644 --- a/usecases/src/main/java/com/zeyad/usecases/utils/Utils.java +++ b/usecases/src/main/java/com/zeyad/usecases/utils/Utils.java @@ -39,7 +39,7 @@ public class Utils { private Utils() { } - public static Utils getInstance() { + public static synchronized Utils getInstance() { if (instance == null) { instance = new Utils(); } @@ -72,10 +72,11 @@ public boolean isNetworkAvailable(@NonNull Context context) { return false; } - public void queuePostCore(@NonNull FirebaseJobDispatcher dispatcher, @NonNull PostRequest postRequest) { + public void queuePostCore(@NonNull FirebaseJobDispatcher dispatcher, @NonNull PostRequest postRequest, int trailCount) { Bundle extras = new Bundle(2); extras.putString(GenericJobService.JOB_TYPE, GenericJobService.POST); extras.putParcelable(GenericJobService.PAYLOAD, postRequest); + extras.putInt(GenericJobService.TRIAL_COUNT, trailCount); dispatcher.mustSchedule(dispatcher.newJobBuilder() .setService(GenericJobService.class) .setTag(GenericJobService.POST) @@ -91,11 +92,12 @@ public void queuePostCore(@NonNull FirebaseJobDispatcher dispatcher, @NonNull Po } public void queueFileIOCore(@NonNull FirebaseJobDispatcher dispatcher, boolean isDownload, - @NonNull FileIORequest fileIORequest) { + @NonNull FileIORequest fileIORequest, int trailCount) { Bundle extras = new Bundle(2); extras.putString(GenericJobService.JOB_TYPE, isDownload ? GenericJobService.DOWNLOAD_FILE : GenericJobService.UPLOAD_FILE); extras.putParcelable(GenericJobService.PAYLOAD, fileIORequest); + extras.putInt(GenericJobService.TRIAL_COUNT, trailCount); dispatcher.mustSchedule(dispatcher.newJobBuilder() .setService(GenericJobService.class) .setTag(isDownload ? GenericJobService.DOWNLOAD_FILE : GenericJobService.UPLOAD_FILE) diff --git a/usecases/src/test/java/com/zeyad/usecases/integration/APIIntegrationJUnitTest.java b/usecases/src/test/java/com/zeyad/usecases/integration/APIIntegrationJUnitTest.java deleted file mode 100644 index bb62dc8..0000000 --- a/usecases/src/test/java/com/zeyad/usecases/integration/APIIntegrationJUnitTest.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.zeyad.usecases.integration; - -import com.zeyad.usecases.api.DataServiceConfig; -import com.zeyad.usecases.api.DataServiceFactory; -import com.zeyad.usecases.api.IDataService; -import com.zeyad.usecases.mapper.DAOMapper; -import com.zeyad.usecases.network.ApiConnection; -import com.zeyad.usecases.requests.GetRequest; -import com.zeyad.usecases.stores.CloudStore; -import com.zeyad.usecases.utils.Utils; - -import org.junit.Before; -import org.junit.Test; -import org.robolectric.RuntimeEnvironment; - -import io.appflate.restmock.RESTMockServer; -import io.appflate.restmock.RESTMockServerStarter; -import io.reactivex.subscribers.TestSubscriber; - -import static io.appflate.restmock.utils.RequestMatchers.pathContains; -import static junit.framework.Assert.assertEquals; - -/** - * @author by ZIaDo on 6/27/17. - */ -public class APIIntegrationJUnitTest { - private final static String userResponse = "\"{\\n \\\"login\\\": \\\"Zeyad-37\\\",\\n \\\"id\\\": 5938141,\\n \\\"avatar_url\\\": \\\"https://avatars2.githubusercontent.com/u/5938141?v=3\\\",\\n}\""; - private IDataService dataService; - private CloudStore cloudStore; - - @Before - public void setUp() { - // Be sure to reset the server before each test - RESTMockServerStarter.startSync(new JVMFileParser()); - RESTMockServer.reset(); - DataServiceFactory.init(new DataServiceConfig.Builder(RuntimeEnvironment.application) - .baseUrl(RESTMockServer.getUrl()) - .baseUrl("https://api.github.com/") -// .okHttpBuilder(new OkHttpClient.Builder() -// .addNetworkInterceptor(provideMockInterceptor()) -// .addInterceptor(provideMockInterceptor())) -// .withCache(3, TimeUnit.MINUTES) -// .withRealm() - .build()); - - cloudStore = new CloudStore(new ApiConnection(ApiConnection.init(null), - ApiConnection.initWithCache(null, null)), null, new DAOMapper(), null, Utils.getInstance()); - } - - @Test - public void testValidUser() throws Exception { - String path = String.format("users/%s", "Zeyad-37"); - - RESTMockServer.whenGET(pathContains(path)).thenReturnFile(200, userResponse); - -// RESTMockServerStarter.startSync(new JVMFileParser()); - -// DataServiceFactory.init(new DataServiceConfig.Builder(RuntimeEnvironment.application) -// .baseUrl(RESTMockServer.getUrl()) -//// .withCache(3, TimeUnit.MINUTES) -// .withRealm() -// .build()); - dataService = DataServiceFactory.getInstance(); - - User testUser = new User(); - testUser.setAvatarUrl("https://avatars2.githubusercontent.com/u/5938141?v=3"); - testUser.setId(5938141); - testUser.setLogin("Zeyad-37"); - -// TestSubscriber testSubscriber = new TestSubscriber<>(); - TestSubscriber testSubscriber = new TestSubscriber<>(); - GetRequest getRequest = new GetRequest.Builder(User.class, false) - .url(path) - .id("Zeyad-37", User.LOGIN, String.class) - .build(); - dataService.getObject(getRequest) - .subscribe(testSubscriber); -// cloudStore.dynamicGetObject(getRequest.getUrl(), getRequest.getIdColumnName(), -// getRequest.getItemId(), getRequest.getIdType(), getRequest.getDataClass(), -// getRequest.isPersist(), getRequest.isShouldCache()) -// .subscribe(testSubscriber); -// RequestsVerifier.verifyGET(pathContains(path)).invoked(); - - testSubscriber.awaitTerminalEvent(); - testSubscriber.assertSubscribed() - .assertNoErrors() - .assertValueCount(1) - .assertValue(testUser) - .assertResult(testUser) - .assertComplete(); - assertEquals(1, testSubscriber.valueCount()); - assertEquals(testUser, testSubscriber.values().get(0)); - } -} 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 613d246..d2833ca 100644 --- a/usecases/src/test/java/com/zeyad/usecases/integration/APIIntegrationTest.java +++ b/usecases/src/test/java/com/zeyad/usecases/integration/APIIntegrationTest.java @@ -21,6 +21,7 @@ import java.net.HttpURLConnection; import io.appflate.restmock.RESTMockServer; +import io.appflate.restmock.RequestsVerifier; import io.reactivex.subscribers.TestSubscriber; import okhttp3.Interceptor; import okhttp3.MediaType; @@ -28,71 +29,51 @@ import okhttp3.Response; import okhttp3.ResponseBody; import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; import static com.zeyad.usecases.stores.CloudStore.APPLICATION_JSON; import static io.appflate.restmock.utils.RequestMatchers.pathContains; -import static junit.framework.Assert.assertEquals; /** * @author by ZIaDo on 6/17/17. */ -//@RunWith(RobolectricTestRunner.class) @RunWith(AndroidRobolectricRunner.class) @Config(constants = BuildConfig.class, sdk = 25, application = TestApplication.class) public class APIIntegrationTest { private final static String userResponse = "\"{\\n \\\"login\\\": \\\"Zeyad-37\\\",\\n \\\"id\\\": 5938141,\\n \\\"avatar_url\\\": \\\"https://avatars2.githubusercontent.com/u/5938141?v=3\\\",\\n}\""; private IDataService dataService; - private String url; - private MockWebServer mockWebServer; private CloudStore cloudStore; + private String path = String.format("users/%s", "Zeyad-37"); @Before public void setUp() throws IOException { -// RESTMockServer.reset(); -// mockWebServer = new MockWebServer(); -// mockWebServer.start(); - - // Be sure to reset the server before each test -// RESTMockServerStarter.startSync(new JVMFileParser()); + RESTMockServer.reset(); DataServiceFactory.init(new DataServiceConfig.Builder(RuntimeEnvironment.application) -// .baseUrl(RESTMockServer.getUrl()) -// .baseUrl(mockWebServer.url("/").toString()) - .baseUrl("https://api.github.com/") -// .okHttpBuilder(new OkHttpClient.Builder() -// .addNetworkInterceptor(provideMockInterceptor()) -// .addInterceptor(provideMockInterceptor())) -// .withCache(3, TimeUnit.MINUTES) - .withRealm() + .baseUrl(RESTMockServer.getUrl()) + // .okHttpBuilder(new OkHttpClient.Builder() + // .addNetworkInterceptor(provideMockInterceptor()) + // .addInterceptor(provideMockInterceptor())) + // .withCache(3, TimeUnit.MINUTES) + // .withRealm() .build()); cloudStore = new CloudStore(new ApiConnection(ApiConnection.init(null), - ApiConnection.initWithCache(null, null)), null, new DAOMapper(), null, Utils.getInstance()); -// dataService = DataServiceFactory.getInstance(); + ApiConnection.initWithCache(null, null)), null, + new DAOMapper(), null, Utils.getInstance()); + dataService = DataServiceFactory.getInstance(); } @Test public void testValidUser() throws Exception { - RESTMockServer.reset(); - -// RESTMockServerStarter.startSync(new JVMFileParser()); - -// DataServiceFactory.init(new DataServiceConfig.Builder(RuntimeEnvironment.application) -// .baseUrl(RESTMockServer.getUrl()) -//// .withCache(3, TimeUnit.MINUTES) -// .withRealm() -// .build()); - dataService = DataServiceFactory.getInstance(); - - String path = String.format("users/%s", "Zeyad-37"); RESTMockServer.whenGET(pathContains(path)) - .thenReturnString(200, userResponse); + .thenReturn(new MockResponse() + .setResponseCode(HttpURLConnection.HTTP_OK) + .setBody(userResponse)); User testUser = new User(); testUser.setAvatarUrl("https://avatars2.githubusercontent.com/u/5938141?v=3"); - testUser.setId(5938141); + testUser.setId(37); testUser.setLogin("Zeyad-37"); - GetRequest getRequest = new GetRequest.Builder(User.class, true) + GetRequest getRequest = new GetRequest.Builder(User.class, false) .url(path) .id("Zeyad-37", User.LOGIN, String.class) .build(); @@ -104,58 +85,16 @@ public void testValidUser() throws Exception { getRequest.isPersist(), getRequest.isShouldCache()) .subscribe(testSubscriber); -// RequestsVerifier.verifyGET(pathContains(path)).invoked(); + RequestsVerifier.verifyGET(pathContains(path)).invoked(); // testSubscriber.awaitTerminalEvent(); - testSubscriber.assertSubscribed() - .assertNoErrors() - .assertValueCount(1) - .assertValue(testUser) - .assertComplete(); - assertEquals(1, testSubscriber.valueCount()); - assertEquals(testUser, testSubscriber.values().get(0)); - } - - @Test - public void testValidUser2() throws Exception { - mockWebServer = new MockWebServer(); - mockWebServer.start(); -// - mockWebServer.enqueue(new MockResponse() - .setResponseCode(HttpURLConnection.HTTP_OK) - .setBody(userResponse)); -// -// DataServiceFactory.init(new DataServiceConfig.Builder(RuntimeEnvironment.application) -// .baseUrl(mockWebServer.url("/").toString()) -//// .withCache(3, TimeUnit.MINUTES) -//// .withRealm() -// .build()); - dataService = DataServiceFactory.getInstance(); -// - User testUser = new User(); - testUser.setAvatarUrl("https://avatars2.githubusercontent.com/u/5938141?v=3"); - testUser.setId(5938141); - testUser.setLogin("Zeyad-37"); - - TestSubscriber testSubscriber = new TestSubscriber<>(); - dataService.getObject(new GetRequest.Builder(User.class, false) - .url(String.format("users/%s", "Zeyad-37")) - .id("Zeyad-37", User.LOGIN, String.class) -// .cache(User.LOGIN) - .build()); -// .subscribe(testSubscriber); -// -// RecordedRequest request = mockWebServer.takeRequest(); -// assertEquals("/Zeyad-37", request.getPath()); -// assertEquals("GET", request.getMethod()); -// - testSubscriber.awaitTerminalEvent(); - testSubscriber.assertSubscribed() - .assertNoErrors() - .assertValueCount(1) - .assertValue(testUser) - .assertComplete(); - mockWebServer.shutdown(); + // testSubscriber.assertSubscribed() + // .assertNoErrors() + // .assertValueCount(1) + // .assertValue(testUser) + // .assertComplete(); + // assertEquals(1, testSubscriber.valueCount()); + // assertEquals(testUser, testSubscriber.values().get(0)); } public Interceptor provideMockInterceptor() { diff --git a/usecases/src/test/java/com/zeyad/usecases/services/jobs/FileIOTest.java b/usecases/src/test/java/com/zeyad/usecases/services/jobs/FileIOTest.java index 068033b..b13d007 100644 --- a/usecases/src/test/java/com/zeyad/usecases/services/jobs/FileIOTest.java +++ b/usecases/src/test/java/com/zeyad/usecases/services/jobs/FileIOTest.java @@ -26,6 +26,7 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyMap; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; @@ -74,10 +75,10 @@ public void testReQueue() throws JSONException { FileIORequest fileIOReq = mockFileIoReq(true, true, getValidFile()); fileIO = createFileIO(fileIOReq, true); Mockito.doNothing() - .when(utils) - .queueFileIOCore(any(), anyBoolean(), any(FileIORequest.class)); + .when(utils) + .queueFileIOCore(any(), anyBoolean(), any(FileIORequest.class), anyInt()); fileIO.queueIOFile(); - verify(utils, times(1)).queueFileIOCore(any(), anyBoolean(), any(FileIORequest.class)); + verify(utils, times(1)).queueFileIOCore(any(), anyBoolean(), any(FileIORequest.class), anyInt()); } private String getValidUrl() { diff --git a/usecases/src/test/java/com/zeyad/usecases/services/jobs/PostTest.java b/usecases/src/test/java/com/zeyad/usecases/services/jobs/PostTest.java index d66edac..b309a0b 100644 --- a/usecases/src/test/java/com/zeyad/usecases/services/jobs/PostTest.java +++ b/usecases/src/test/java/com/zeyad/usecases/services/jobs/PostTest.java @@ -34,6 +34,7 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; @@ -155,9 +156,9 @@ public void testReQueue() throws JSONException { createPostRequestForJsonArray(PostRequest.DELETE), apiConnection, 0); - Mockito.doNothing().when(utils).queuePostCore(any(), any(PostRequest.class)); + Mockito.doNothing().when(utils).queuePostCore(any(), any(PostRequest.class), anyInt()); post.queuePost(); - verify(utils, times(1)).queuePostCore(any(), any(PostRequest.class)); + verify(utils, times(1)).queuePostCore(any(), any(PostRequest.class), anyInt()); } //--------------------------------------------------------------------------------------------//