├── .circleci └── config.yml ├── .gitignore ├── README.md ├── build.gradle.kts ├── buildSrc ├── .gitignore ├── build.gradle.kts └── src │ └── main │ └── java │ └── com │ └── clientserver │ └── buildProcess │ ├── CollectUnitTest.kt │ └── Dependencies.kt ├── consumer-android ├── app │ ├── .gitignore │ ├── build.gradle.kts │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── kotlin │ │ │ └── client │ │ │ ├── PairScreenActivityTest.kt │ │ │ └── di │ │ │ ├── TestApplication.kt │ │ │ └── TestRepositoryModule.kt │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── kotlin │ │ │ └── client │ │ │ ├── MainApplication.kt │ │ │ ├── di │ │ │ ├── ActivityScope.kt │ │ │ ├── AppComponent.kt │ │ │ ├── AppModule.kt │ │ │ ├── DomainModule.kt │ │ │ ├── ScreensActivityModule.kt │ │ │ ├── pairscreen │ │ │ │ ├── PairScreenActivityModule.kt │ │ │ │ ├── PairScreenComponent.kt │ │ │ │ └── PairScreenModule.kt │ │ │ ├── tradescreen │ │ │ │ ├── HomeScreenActivityModule.kt │ │ │ │ ├── HomeScreenComponent.kt │ │ │ │ └── HomeScreenModule.kt │ │ │ └── worker │ │ │ │ ├── DaggerWorkerFactory.kt │ │ │ │ └── SyncWorkerModule.kt │ │ │ ├── job │ │ │ └── SyncWorker.kt │ │ │ └── view │ │ │ ├── pairscreen │ │ │ ├── PairScreenActivity.kt │ │ │ ├── PairScreenPresenter.kt │ │ │ └── PairSymbolAdapter.kt │ │ │ └── tradescreen │ │ │ ├── TradesActivity.kt │ │ │ ├── TradesAdapter.kt │ │ │ └── TradesScreenPresenter.kt │ │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_pairs.xml │ │ ├── activity_trades.xml │ │ ├── item_pair.xml │ │ └── item_trade.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml └── repository-android │ ├── .gitignore │ ├── build.gradle.kts │ ├── proguard-rules.pro │ └── src │ ├── androidTest │ └── java │ │ └── inakivillar │ │ └── com │ │ └── repository_android │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── kotlin │ │ │ └── client │ │ │ └── repository │ │ │ ├── GetTradesRepositoryImpl.kt │ │ │ ├── PairRepositoryImpl.kt │ │ │ ├── api │ │ │ ├── Api.kt │ │ │ ├── BxApi.kt │ │ │ ├── BxApiImpl.kt │ │ │ └── MarketDeserializer.kt │ │ │ ├── database │ │ │ ├── AppDatabase.kt │ │ │ ├── DbInterface.kt │ │ │ ├── PairDb.kt │ │ │ └── TradeDb.kt │ │ │ ├── di │ │ │ └── RepositoryModule.kt │ │ │ └── mapper │ │ │ ├── MapperToPairDb.kt │ │ │ ├── MapperToPairSymbol.kt │ │ │ └── MapperToTradeDb.kt │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── kotlin │ └── client │ └── repository │ ├── GetTradesRepositoryImplTest.kt │ └── PairRepositoryImplTest.kt ├── consumer-server ├── domain-server │ ├── .gitignore │ ├── build.gradle.kts │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── kotlin │ │ │ └── server │ │ │ └── repository │ │ │ └── domain │ │ │ ├── GetMarketImpl.kt │ │ │ ├── SyncTradesImpl.kt │ │ │ └── di │ │ │ └── DomainModule.kt │ │ └── test │ │ └── java │ │ └── com │ │ └── kotlin │ │ └── server │ │ └── repository │ │ └── domain │ │ ├── GetMarketImplTest.kt │ │ └── SyncTradesImplTest.kt ├── repository-server │ ├── .gitignore │ ├── build.gradle.kts │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── kotlin │ │ │ └── server │ │ │ └── repository │ │ │ ├── GetTradesRepositoryImpl.kt │ │ │ ├── PairRepositoryImpl.kt │ │ │ ├── SyncPairsRepositoryImpl.kt │ │ │ ├── api │ │ │ ├── Bx.kt │ │ │ ├── BxApi.kt │ │ │ ├── BxApiImpl.kt │ │ │ ├── PairsDeserializer.kt │ │ │ ├── entities │ │ │ │ └── PairsInfo.kt │ │ │ └── patch │ │ │ │ ├── CallFactoryWrapper.kt │ │ │ │ └── CallWrapper.kt │ │ │ ├── database │ │ │ ├── DbImpl.kt │ │ │ ├── DbInterface.kt │ │ │ ├── ExtensionQueries.kt │ │ │ ├── PairStore.kt │ │ │ └── TradeStore.kt │ │ │ ├── di │ │ │ └── RepositoryModule.kt │ │ │ └── mapper │ │ │ ├── MapperToPairStore.kt │ │ │ ├── MapperToPairSymbol.kt │ │ │ ├── MapperToTrade.kt │ │ │ └── MapperToTradeStore.kt │ │ └── test │ │ └── java │ │ └── com │ │ └── kotlin │ │ └── server │ │ └── repository │ │ ├── GetTradesRepositoryTest.kt │ │ ├── PairRepositoryImplTest.kt │ │ └── SyncPairsRepositoryTest.kt └── server │ ├── .gitignore │ ├── build.gradle.kts │ └── src │ └── main │ ├── java │ └── com │ │ └── kotlin │ │ └── server │ │ ├── Init.kt │ │ ├── cron │ │ ├── CronPairs.kt │ │ └── CronTrades.kt │ │ ├── di │ │ ├── AppModule.kt │ │ └── Injector.kt │ │ ├── endpoint │ │ └── EndPoint.kt │ │ └── service │ │ ├── GetMarketService.kt │ │ ├── GetTradesService.kt │ │ ├── SyncPairsService.kt │ │ └── SyncTradesService.kt │ └── webapp │ └── WEB-INF │ ├── appengine-web.xml │ ├── cron.xml │ ├── datastore-indexes.xml │ └── web.xml ├── core-domain ├── .gitignore ├── build.gradle.kts └── src │ ├── main │ └── java │ │ └── com │ │ └── kotlin │ │ └── core │ │ └── domain │ │ └── entities │ │ ├── Market.kt │ │ ├── PairSymbol.kt │ │ ├── Trade.kt │ │ ├── Trades.kt │ │ ├── mapper │ │ └── Mapper.kt │ │ ├── repository │ │ ├── PairsRepository.kt │ │ ├── SyncRepository.kt │ │ └── TradesRepository.kt │ │ └── usecases │ │ ├── GetMarket.kt │ │ ├── GetPairs.kt │ │ ├── GetTrades.kt │ │ ├── SyncTrades.kt │ │ └── impl │ │ ├── GetPairsImpl.kt │ │ └── GetTradesImpl.kt │ └── test │ └── java │ └── com │ └── kotlin │ └── core │ └── domain │ └── entities │ └── usecases │ └── impl │ └── GetPairsImplTest.kt ├── core-network ├── .gitignore ├── build.gradle.kts └── src │ └── main │ └── java │ └── com │ └── kotlin │ └── core │ └── network │ └── TradesDeserializer.kt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: circleci/android:api-28 6 | 7 | working_directory: ~/repo 8 | 9 | environment: 10 | JVM_OPTS: -Xmx3200m 11 | TERM: dumb 12 | 13 | steps: 14 | - checkout 15 | 16 | - restore_cache: 17 | keys: 18 | - v1-dependencies-{{ checksum "build.gradle.kts" }} 19 | - v1-dependencies- 20 | 21 | - run: ./gradlew dependencies 22 | 23 | - save_cache: 24 | paths: 25 | - ~/.gradle 26 | key: v1-dependencies-{{ checksum "build.gradle.kts" }} 27 | 28 | - run: ./gradlew :consumer-android:app:assembleDebug 29 | 30 | - run: ./gradlew :consumer-server:server:appengineStage 31 | 32 | - run: ./gradlew test collectUnitTest 33 | 34 | - store_test_results: 35 | path: build/reports/tests/ 36 | - store_artifacts: 37 | path: build/reports/tests/ 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Example Kotlin App Engine / Android 2 | 3 | [Second Part](https://medium.com/@inyaki_mwc/android-and-app-engine-with-kotlin-2nd-part-96e5472cfd7a) 4 | 5 | [First Part](https://medium.com/p/c2db393e576e) 6 | 7 | # What includes? 8 | - Simple client in Android with Kotin 9 | - Server side in App Engine with Kotlin 10 | - Additional module to include uses cases, implementations and domain entities 11 | 12 | # Usage 13 | 14 | ### App Engine 15 | 16 | #### Local conf App Engine 17 | 18 | - Install Google Cloud 19 | `curl https://sdk.cloud.google.com | bash` 20 | 21 | - Install Google App Engine Component 22 | `gcloud components install app-engine-java` 23 | 24 | - Deploy local instance: 25 | `./gradlew appengineRun` 26 | 27 | - Install Cloud Datastore Emulator 28 | `gcloud components install cloud-datastore-emulator` 29 | 30 | - Run Datastore Emulator 31 | `gcloud beta emulators datastore start` 32 | 33 | #### Deploy App Engine 34 | 35 | - Create project Google Cloud 36 | 37 | - `gcloud auth login` 38 | 39 | - `gcloud config set project PROJECT_ID` 40 | 41 | - `./gradlew appengineDeploy` 42 | 43 | ### Android 44 | 45 | For the Android App there are two flavours: 46 | 47 | - `local`: it uses the local instance of App Engine: http://10.0.2.2:8080 48 | 49 | - `gae`: it uses the remote instance of App Engine: https://PROJECT_ID.appspot.com 50 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | google() 5 | mavenCentral( 6 | 7 | ) 8 | maven { 9 | url = uri("https://plugins.gradle.org/m2/") 10 | } 11 | } 12 | dependencies { 13 | classpath(Dependencies.appengineGradle) 14 | classpath(Dependencies.kotlinGradle) 15 | classpath(Dependencies.androidGradle) 16 | classpath(Dependencies.androidJunit5) 17 | classpath(Dependencies.jetifierProcessor) 18 | } 19 | } 20 | allprojects { 21 | repositories { 22 | maven { 23 | url = uri("https://maven-central.storage.googleapis.com") 24 | } 25 | jcenter() 26 | google() 27 | mavenCentral() 28 | } 29 | } -------------------------------------------------------------------------------- /buildSrc/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.kotlin.dsl.`kotlin-dsl` 2 | 3 | 4 | buildscript { 5 | 6 | repositories { 7 | jcenter() 8 | mavenCentral() 9 | google() 10 | } 11 | 12 | dependencies { 13 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.61") 14 | } 15 | } 16 | 17 | plugins { 18 | `java-gradle-plugin` 19 | `kotlin-dsl` 20 | } 21 | 22 | repositories { 23 | google() 24 | jcenter() 25 | } 26 | 27 | -------------------------------------------------------------------------------- /buildSrc/src/main/java/com/clientserver/buildProcess/CollectUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.clientserver.buildProcess 2 | 3 | import org.gradle.api.Project 4 | import org.gradle.api.tasks.testing.TestReport 5 | import org.gradle.kotlin.dsl.closureOf 6 | import org.gradle.kotlin.dsl.create 7 | import org.gradle.kotlin.dsl.get 8 | 9 | 10 | object CollectUnitTest { 11 | fun Project.collectUnitTest() = this.run { 12 | val testTask = if (rootProject.tasks.findByPath("collectUnitTest") == null) { 13 | rootProject.tasks.create("collectUnitTest", TestReport::class) { 14 | } 15 | } else { 16 | rootProject.tasks["collectUnitTest"] 17 | } 18 | testTask.configure(closureOf { 19 | group = "Verification" 20 | description = "Collect all tests" 21 | 22 | val tasks = project.tasks 23 | 24 | val testTask = tasks.find { it.name == "testDebugUnitTest" } 25 | ?: tasks.find { it.name == "test" } 26 | 27 | testTask?.let { reportOn(testTask) } 28 | 29 | destinationDir = file("${rootProject.buildDir}/reports/tests") 30 | }) 31 | } 32 | } -------------------------------------------------------------------------------- /buildSrc/src/main/java/com/clientserver/buildProcess/Dependencies.kt: -------------------------------------------------------------------------------- 1 | object Versions { 2 | val kotlin = "1.3.0" 3 | val appengine = "2.0.0-rc3" 4 | val appengineApi = "1.9.49" 5 | val androidGradle = "3.2.1" 6 | val endpoints = "2.0.3" 7 | val javax_servlet = "2.5" 8 | val javax_inject = "1" 9 | val dagger = "2.19" 10 | val retrofit = "2.3.0" 11 | val gson = "2.7" 12 | val objectify = "6.0.2" 13 | val supportLibrary = "1.0.0" 14 | val coroutines = "1.0.1" 15 | val archComponents = "1.1.1" 16 | val workManager = "1.0.0-beta01" 17 | val mockito = "1.5.0" 18 | val kotlinTest = "3.1.11" 19 | val mockitoKotlin = "2.0.0-RC1" 20 | val jetifier = "1.0.0-beta02" 21 | val androidJunit5 = "1.3.1.1" 22 | } 23 | 24 | object Dependencies { 25 | val androidGradle = "com.android.tools.build:gradle:${Versions.androidGradle}" 26 | val appengineGradle = "com.google.cloud.tools:appengine-gradle-plugin:${Versions.appengine}" 27 | val kotlinGradle = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin}" 28 | val appengine = "com.google.appengine:appengine-java-sdk:${Versions.appengine}" 29 | val appengineApi = "com.google.appengine:appengine-api-1.0-sdk:${Versions.appengineApi}" 30 | val endpoints = "com.google.endpoints:endpoints-framework:${Versions.endpoints}" 31 | val javaxServlet = "javax.servlet:servlet-api:${Versions.javax_servlet}" 32 | val kotlin = "org.jetbrains.kotlin:kotlin-stdlib:${Versions.kotlin}" 33 | val kotlin8 = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${Versions.kotlin}" 34 | val javaxInject = "javax.inject:javax.inject:${Versions.javax_inject}" 35 | val daggerCompiler = "com.google.dagger:dagger-compiler:${Versions.dagger}" 36 | val daggerAndroid = "com.google.dagger:dagger-android:${Versions.dagger}" 37 | val daggerProcessor = "com.google.dagger:dagger-android-processor:${Versions.dagger}" 38 | val daggerProcessorAndroid = "com.google.dagger:dagger-processor:${Versions.dagger}" 39 | val dagger = "com.google.dagger:dagger:${Versions.dagger}" 40 | val workManager = "android.arch.work:work-runtime:${Versions.workManager}" 41 | 42 | val retrofitConverter = "com.squareup.retrofit2:converter-gson:${Versions.retrofit}" 43 | val retrofit = "com.squareup.retrofit2:retrofit:${Versions.retrofit}" 44 | val gson = "com.google.code.gson:gson:${Versions.gson}" 45 | val objectify = "com.googlecode.objectify:objectify:${Versions.objectify}" 46 | val supportAppcompat = "androidx.appcompat:appcompat:${Versions.supportLibrary}" 47 | val supportRecycler = "androidx.recyclerview:recyclerview:${Versions.supportLibrary}" 48 | val supportCardView = "androidx.cardview:cardview:${Versions.supportLibrary}" 49 | val coroutinesCore = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.coroutines}" 50 | val coroutinesAndroid = "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.coroutines}" 51 | val roomRuntime = "android.arch.persistence.room:runtime:${Versions.archComponents}" 52 | val roomCompiler = "android.arch.persistence.room:compiler:${Versions.archComponents}" 53 | val mockito = "com.nhaarman:mockito-kotlin:${Versions.mockito}" 54 | val kotlinTest = "io.kotlintest:kotlintest-runner-junit5:${Versions.kotlinTest}" 55 | val mockitoKotlin = "com.nhaarman.mockitokotlin2:mockito-kotlin:${Versions.mockitoKotlin}" 56 | val jetifierProcessor = "com.android.tools.build.jetifier:jetifier-processor:${Versions.jetifier}" 57 | val jetifierCore = "com.android.tools.build.jetifier:jetifier-core:${Versions.jetifier}" 58 | val androidJunit5 = "de.mannodermaus.gradle.plugins:android-junit5:${Versions.androidJunit5}" 59 | 60 | } -------------------------------------------------------------------------------- /consumer-android/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /consumer-android/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | id("kotlin-android") 4 | id("kotlin-android-extensions") 5 | id("kotlin-kapt") 6 | } 7 | 8 | android { 9 | 10 | compileSdkVersion(28) 11 | defaultConfig { 12 | applicationId = "com.kotlin.client" 13 | minSdkVersion(21) 14 | targetSdkVersion(28) 15 | versionCode = 1 16 | versionName = "1.0" 17 | testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner" 18 | javaCompileOptions { 19 | annotationProcessorOptions { 20 | includeCompileClasspath = true 21 | } 22 | } 23 | multiDexEnabled = true 24 | buildConfigField("String", "URL", "\"YOUR_GAE_ENDPOINT_PROJECT\"") 25 | } 26 | buildTypes { 27 | } 28 | 29 | flavorDimensions("environment") 30 | 31 | productFlavors { 32 | create("local") { 33 | buildConfigField("String", "URL", "\"http://10.0.2.2:8080/\"") 34 | dimension = "environment" 35 | } 36 | 37 | create("gae") { 38 | dimension = "environment" 39 | } 40 | } 41 | } 42 | 43 | dependencies { 44 | implementation(project(":consumer-android:repository-android")) 45 | implementation(project(":core-domain")) 46 | implementation(Dependencies.supportAppcompat) 47 | implementation(Dependencies.supportRecycler) 48 | implementation(Dependencies.supportCardView) 49 | implementation(Dependencies.workManager) 50 | implementation(Dependencies.coroutinesCore) 51 | implementation(Dependencies.coroutinesAndroid) 52 | implementation(Dependencies.javaxInject) 53 | kapt(Dependencies.daggerProcessor) 54 | kapt(Dependencies.daggerCompiler) 55 | kapt(Dependencies.jetifierCore) 56 | implementation(Dependencies.daggerAndroid) 57 | implementation(Dependencies.kotlin) 58 | androidTestImplementation(Dependencies.mockitoKotlin) 59 | } 60 | -------------------------------------------------------------------------------- /consumer-android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle.kts.kts. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /consumer-android/app/src/androidTest/java/com/kotlin/client/PairScreenActivityTest.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client 2 | 3 | import androidx.test.core.app.ActivityScenario 4 | import androidx.test.espresso.Espresso.onView 5 | import androidx.test.espresso.action.ViewActions.click 6 | import androidx.test.espresso.action.ViewActions.typeText 7 | import androidx.test.espresso.matcher.ViewMatchers.assertThat 8 | import androidx.test.espresso.matcher.ViewMatchers.withId 9 | import androidx.test.runner.AndroidJUnit4 10 | import com.kotlin.client.view.pairscreen.PairScreenActivity 11 | import org.junit.Test 12 | import org.junit.runner.RunWith 13 | 14 | @RunWith(AndroidJUnit4::class) 15 | class PairScreenActivityTest { 16 | 17 | @Test 18 | fun listOfPairsWithElements() { 19 | val scenario = ActivityScenario.launch(PairScreenActivity::class.java) 20 | // WHEN 21 | // onView(withId(R.id.recycler)).perform(typeText(“ test_user ”)) 22 | // onView(withId(R.id.password)) 23 | // .perform(typeText(“ correct_password ”)) 24 | // onView(withId(R.id.button)).perform(click()) 25 | // THEN 26 | // assertThat(getIntents().first()) 27 | // .hasComponentClass(HomeActivity::class.java) 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /consumer-android/app/src/androidTest/java/com/kotlin/client/di/TestApplication.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.di 2 | 3 | -------------------------------------------------------------------------------- /consumer-android/app/src/androidTest/java/com/kotlin/client/di/TestRepositoryModule.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.di 2 | 3 | import com.kotlin.core.domain.entities.Trades 4 | import com.kotlin.core.domain.entities.repository.PairsRepository 5 | import com.kotlin.core.domain.entities.repository.TradesRepository 6 | import com.nhaarman.mockitokotlin2.any 7 | import com.nhaarman.mockitokotlin2.mock 8 | import dagger.Module 9 | import dagger.Provides 10 | 11 | 12 | @Module 13 | class TestRepositoryModule { 14 | 15 | @Provides 16 | fun providesTradeRepository(): TradesRepository = mock { 17 | on { getTradesPersisted(any()) }.thenReturn(Trades(emptyList())) 18 | on { getTradesRemote(any()) }.thenReturn(Trades(emptyList())) 19 | } 20 | 21 | 22 | @Provides 23 | fun providesPairRepository(): PairsRepository = mock { 24 | on { getPairs() }.thenReturn(listOf()) 25 | on { syncPairs() }.thenReturn(listOf()) 26 | } 27 | 28 | 29 | } -------------------------------------------------------------------------------- /consumer-android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 31 | 32 | -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/MainApplication.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client 2 | 3 | import androidx.work.* 4 | import com.kotlin.client.di.DaggerAppComponent 5 | import com.kotlin.client.di.worker.DaggerWorkerFactory 6 | import com.kotlin.client.job.SyncWorker 7 | import dagger.android.AndroidInjector 8 | import dagger.android.DaggerApplication 9 | import java.util.concurrent.TimeUnit 10 | import javax.inject.Inject 11 | 12 | 13 | class MainApplication : DaggerApplication() { 14 | 15 | @Inject 16 | lateinit var workerFactory: DaggerWorkerFactory 17 | 18 | override fun applicationInjector(): AndroidInjector { 19 | val appComponent = DaggerAppComponent 20 | .builder() 21 | .application(this).build() 22 | appComponent.inject(this) 23 | val configuration = Configuration.Builder() 24 | .setWorkerFactory(workerFactory) 25 | .build() 26 | 27 | WorkManager.initialize(this, configuration) 28 | val workerRequest = 29 | PeriodicWorkRequest.Builder(SyncWorker::class.java, 60, TimeUnit.MINUTES) 30 | .setConstraints(Constraints.Builder() 31 | .setRequiresCharging(false) 32 | .setRequiredNetworkType(NetworkType.UNMETERED) 33 | .setRequiresStorageNotLow(true) 34 | .build()) 35 | .build() 36 | WorkManager.getInstance().enqueue(workerRequest) 37 | 38 | return appComponent 39 | } 40 | } -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/di/ActivityScope.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.di 2 | 3 | import javax.inject.Scope 4 | 5 | @Scope 6 | @Retention 7 | annotation class ActivityScope -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/di/AppComponent.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.di 2 | 3 | import android.app.Application 4 | import com.kotlin.client.MainApplication 5 | import com.kotlin.client.job.SyncWorker 6 | import dagger.BindsInstance 7 | import dagger.Component 8 | import dagger.android.AndroidInjectionModule 9 | import dagger.android.AndroidInjector 10 | import dagger.android.DaggerApplication 11 | 12 | @Component(modules = [AndroidInjectionModule::class, 13 | AppModule::class, 14 | ScreensActivityModule::class]) 15 | interface AppComponent : AndroidInjector { 16 | 17 | @Component.Builder 18 | interface Builder { 19 | @BindsInstance 20 | fun application(application: Application): Builder 21 | 22 | fun build(): AppComponent 23 | } 24 | 25 | fun inject(app: MainApplication) 26 | 27 | fun inject(syncWorker: SyncWorker) 28 | } 29 | 30 | 31 | -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/di/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.di 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import com.kotlin.client.BuildConfig 6 | import com.kotlin.client.repository.database.AppDatabase 7 | import com.kotlin.client.di.tradescreen.HomeScreenComponent 8 | import com.kotlin.client.di.pairscreen.PairScreenComponent 9 | import com.kotlin.client.di.worker.SyncWorkerModule 10 | import com.kotlin.client.repository.di.RepositoryModule 11 | import dagger.Module 12 | import dagger.Provides 13 | import javax.inject.Named 14 | 15 | @Module(subcomponents = [HomeScreenComponent::class, PairScreenComponent::class], 16 | includes = [RepositoryModule::class, DomainModule::class, SyncWorkerModule::class]) 17 | class AppModule { 18 | 19 | @Provides 20 | fun provideContext(application: Application) = application.applicationContext 21 | 22 | @Provides 23 | @Named("URL") 24 | fun provideUrl() = BuildConfig.URL 25 | 26 | @Provides 27 | fun providesDb(context: Context): AppDatabase { 28 | return AppDatabase.getInstance(context) 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/di/DomainModule.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.di 2 | 3 | import com.kotlin.core.domain.entities.repository.PairsRepository 4 | import com.kotlin.core.domain.entities.repository.TradesRepository 5 | import com.kotlin.core.domain.entities.usecases.GetPairs 6 | import com.kotlin.core.domain.entities.usecases.GetTrades 7 | import com.kotlin.core.domain.entities.usecases.impl.GetPairsImpl 8 | import com.kotlin.core.domain.entities.usecases.impl.GetTradesImpl 9 | import dagger.Module 10 | import dagger.Provides 11 | 12 | @Module 13 | class DomainModule { 14 | 15 | @Provides 16 | fun providesGetPairs(pairRepository: PairsRepository): GetPairs { 17 | return GetPairsImpl(pairRepository) 18 | } 19 | 20 | @Provides 21 | fun providesGetTrades(repository: TradesRepository) 22 | : GetTrades = GetTradesImpl(repository) 23 | } -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/di/ScreensActivityModule.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.di 2 | 3 | import com.kotlin.client.di.tradescreen.HomeScreenActivityModule 4 | import com.kotlin.client.di.pairscreen.PairScreenActivityModule 5 | import dagger.Module 6 | 7 | @Module(includes = [HomeScreenActivityModule::class, PairScreenActivityModule::class]) 8 | abstract class ScreensActivityModule -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/di/pairscreen/PairScreenActivityModule.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.di.pairscreen 2 | 3 | import com.kotlin.client.di.ActivityScope 4 | import com.kotlin.client.view.pairscreen.PairScreenActivity 5 | import dagger.Module 6 | import dagger.android.ContributesAndroidInjector 7 | 8 | @Module 9 | abstract class PairScreenActivityModule { 10 | @ActivityScope 11 | @ContributesAndroidInjector(modules = [PairScreenModule::class]) 12 | abstract fun contributePairScreenActivity(): PairScreenActivity 13 | } -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/di/pairscreen/PairScreenComponent.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.di.pairscreen 2 | 3 | import com.kotlin.client.view.pairscreen.PairScreenActivity 4 | import dagger.Subcomponent 5 | import dagger.android.AndroidInjector 6 | 7 | 8 | @Subcomponent(modules = [PairScreenModule::class]) 9 | interface PairScreenComponent : AndroidInjector { 10 | 11 | @Subcomponent.Builder 12 | abstract class Builder : AndroidInjector.Builder() 13 | } 14 | -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/di/pairscreen/PairScreenModule.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.di.pairscreen 2 | 3 | import com.kotlin.client.view.pairscreen.PairScreenPresenter 4 | import com.kotlin.core.domain.entities.usecases.GetPairs 5 | import dagger.Module 6 | import dagger.Provides 7 | 8 | @Module 9 | class PairScreenModule { 10 | @Provides 11 | fun providesPairPresenter(getPairs: GetPairs): PairScreenPresenter { 12 | return PairScreenPresenter(getPairs) 13 | } 14 | } -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/di/tradescreen/HomeScreenActivityModule.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.di.tradescreen 2 | 3 | import com.kotlin.client.di.ActivityScope 4 | import com.kotlin.client.view.tradescreen.TradesActivity 5 | import dagger.Module 6 | import dagger.android.ContributesAndroidInjector 7 | 8 | @Module 9 | abstract class HomeScreenActivityModule { 10 | @ActivityScope 11 | @ContributesAndroidInjector(modules = [HomeScreenModule::class]) 12 | abstract fun contributeHomeScreenActivity(): TradesActivity 13 | } -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/di/tradescreen/HomeScreenComponent.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.di.tradescreen 2 | 3 | import com.kotlin.client.view.tradescreen.TradesActivity 4 | import dagger.Subcomponent 5 | import dagger.android.AndroidInjector 6 | 7 | @Subcomponent(modules = arrayOf(HomeScreenModule::class)) 8 | interface HomeScreenComponent : AndroidInjector { 9 | 10 | @Subcomponent.Builder 11 | abstract class Builder : AndroidInjector.Builder() 12 | } 13 | -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/di/tradescreen/HomeScreenModule.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.di.tradescreen 2 | 3 | import com.kotlin.client.view.tradescreen.TradesScreenPresenter 4 | import com.kotlin.core.domain.entities.usecases.GetTrades 5 | import dagger.Module 6 | import dagger.Provides 7 | 8 | @Module 9 | class HomeScreenModule { 10 | @Provides 11 | fun providesPresenter(getTrades: GetTrades): TradesScreenPresenter { 12 | return TradesScreenPresenter(getTrades) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/di/worker/DaggerWorkerFactory.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.di.worker 2 | 3 | import android.content.Context 4 | import androidx.work.ListenableWorker 5 | import androidx.work.Worker 6 | import androidx.work.WorkerFactory 7 | import androidx.work.WorkerParameters 8 | import com.kotlin.core.domain.entities.usecases.GetPairs 9 | import java.lang.reflect.Constructor 10 | 11 | class DaggerWorkerFactory(private val syncTrades: GetPairs) : WorkerFactory() { 12 | override fun createWorker(appContext: Context, workerClassName: String, 13 | workerParameters: WorkerParameters): ListenableWorker? { 14 | 15 | val workerClass: Class = Class.forName(workerClassName).asSubclass(Worker::class.java) 16 | val constructor: Constructor = workerClass.getDeclaredConstructor(Context::class.java, 17 | WorkerParameters::class.java, GetPairs::class.java) 18 | return constructor.newInstance(appContext, workerParameters, syncTrades) 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/di/worker/SyncWorkerModule.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.di.worker 2 | 3 | import com.kotlin.core.domain.entities.usecases.GetPairs 4 | import dagger.Module 5 | import dagger.Provides 6 | 7 | @Module 8 | class SyncWorkerModule { 9 | @Provides 10 | fun providesWorkerFactory(getPairs: GetPairs) 11 | : DaggerWorkerFactory = DaggerWorkerFactory(getPairs) 12 | 13 | } -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/job/SyncWorker.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.job 2 | 3 | import android.content.Context 4 | import androidx.work.Worker 5 | import androidx.work.WorkerParameters 6 | import com.kotlin.core.domain.entities.usecases.GetPairs 7 | 8 | class SyncWorker(context: Context, 9 | workerParams: WorkerParameters, 10 | private val syncTrades: GetPairs) : Worker(context, workerParams) { 11 | 12 | override fun doWork(): Result { 13 | syncTrades.sync() 14 | return Result.success() 15 | } 16 | } -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/view/pairscreen/PairScreenActivity.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.view.pairscreen 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.recyclerview.widget.LinearLayoutManager 7 | import com.kotlin.client.R 8 | import com.kotlin.client.view.tradescreen.TradesActivity 9 | import kotlinx.android.synthetic.main.activity_pairs.* 10 | import com.kotlin.core.domain.entities.PairSymbol 11 | import dagger.android.AndroidInjection 12 | import kotlinx.coroutines.* 13 | import javax.inject.Inject 14 | import kotlin.coroutines.CoroutineContext 15 | 16 | class PairScreenActivity : AppCompatActivity(), PairScreenPresenter.ScreenView, 17 | PairListListener, CoroutineScope { 18 | override val coroutineContext: CoroutineContext 19 | get() = Dispatchers.Main + job 20 | private val job = SupervisorJob() 21 | 22 | @Inject 23 | lateinit var presenter: PairScreenPresenter 24 | 25 | override fun onCreate(savedInstanceState: Bundle?) { 26 | super.onCreate(savedInstanceState) 27 | inject() 28 | setContentView(R.layout.activity_trades) 29 | initComponents() 30 | getData() 31 | } 32 | 33 | private fun inject() { 34 | AndroidInjection.inject(this) 35 | } 36 | 37 | private fun initComponents() { 38 | val adapter = PairSymbolAdapter(emptyList(), this) 39 | recycler.adapter = adapter 40 | recycler.layoutManager = LinearLayoutManager(this) 41 | presenter.initView(this) 42 | swipe.isRefreshing = true 43 | swipe.setOnRefreshListener { 44 | swipe.isRefreshing = true 45 | refresh() 46 | } 47 | } 48 | 49 | private fun getData() = launch { 50 | presenter.getData() 51 | } 52 | 53 | private fun refresh() = launch { 54 | presenter.refresh() 55 | } 56 | 57 | override fun load(pairs: List) { 58 | swipe.isRefreshing = false 59 | recycler.adapter = PairSymbolAdapter(pairs, this) 60 | (recycler.adapter as PairSymbolAdapter).notifyDataSetChanged() 61 | } 62 | 63 | override fun onPairClicked(id: Long) { 64 | startActivity(Intent(this, TradesActivity::class.java).apply { 65 | putExtra("PAIR", id) 66 | }) 67 | } 68 | 69 | override fun onDestroy() { 70 | super.onDestroy() 71 | coroutineContext.cancelChildren() 72 | } 73 | } -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/view/pairscreen/PairScreenPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.view.pairscreen 2 | 3 | import com.kotlin.core.domain.entities.PairSymbol 4 | import com.kotlin.core.domain.entities.usecases.GetPairs 5 | import kotlinx.coroutines.GlobalScope 6 | import kotlinx.coroutines.async 7 | import javax.inject.Inject 8 | 9 | class PairScreenPresenter @Inject constructor(private val getPairs: GetPairs) { 10 | 11 | lateinit var view: PairScreenPresenter.ScreenView 12 | 13 | fun initView(view: PairScreenPresenter.ScreenView) { 14 | this.view = view 15 | } 16 | 17 | suspend fun getData() { 18 | val result = GlobalScope.async { getPairs.get() }.await() 19 | view.load(result) 20 | } 21 | 22 | suspend fun refresh() { 23 | val result = GlobalScope.async { getPairs.sync() }.await() 24 | view.load(result) 25 | } 26 | 27 | interface ScreenView { 28 | fun load(pairs: List) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/view/pairscreen/PairSymbolAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.view.pairscreen 2 | 3 | 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.TextView 8 | import androidx.cardview.widget.CardView 9 | import androidx.recyclerview.widget.RecyclerView 10 | import com.kotlin.client.R 11 | import com.kotlin.core.domain.entities.PairSymbol 12 | 13 | 14 | interface PairListListener { 15 | fun onPairClicked(id: Long) 16 | } 17 | 18 | class PairSymbolAdapter(private val pairs: List, 19 | val listener: PairListListener) : RecyclerView.Adapter() { 20 | 21 | 22 | override fun onBindViewHolder(holder: PairViewHolder, position: Int) = 23 | holder.bind(pairs[position]) 24 | 25 | 26 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PairViewHolder { 27 | 28 | val v = LayoutInflater.from(parent.context).inflate(R.layout.item_pair, parent, false) 29 | return PairViewHolder(v) 30 | } 31 | 32 | override fun getItemCount(): Int = pairs.size 33 | 34 | 35 | inner class PairViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 36 | var textView: TextView = itemView.findViewById(R.id.pair) 37 | var cardView: CardView = itemView.findViewById(R.id.cardView) 38 | var volume: TextView = itemView.findViewById(R.id.volume) 39 | var price: TextView = itemView.findViewById(R.id.price) 40 | 41 | fun bind(data: PairSymbol) { 42 | textView.text = "${data.primarySymbol}-${data.secondarySymbol}" 43 | volume.text = "${data.volume}" 44 | price.text = "${data.rate}" 45 | cardView.setOnClickListener { 46 | listener.onPairClicked(data.id) 47 | } 48 | 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/view/tradescreen/TradesActivity.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.view.tradescreen 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.recyclerview.widget.LinearLayoutManager 6 | import com.kotlin.client.R 7 | import com.kotlin.core.domain.entities.Trade 8 | import dagger.android.AndroidInjection 9 | import kotlinx.coroutines.* 10 | import javax.inject.Inject 11 | import kotlin.coroutines.CoroutineContext 12 | import kotlinx.android.synthetic.main.activity_trades.* 13 | 14 | 15 | class TradesActivity : AppCompatActivity(), 16 | TradesScreenPresenter.ScreenView, CoroutineScope { 17 | 18 | override val coroutineContext: CoroutineContext 19 | get() = Dispatchers.Main + job 20 | private val job = SupervisorJob() 21 | 22 | @Inject 23 | lateinit var presenter: TradesScreenPresenter 24 | 25 | override fun onCreate(savedInstanceState: Bundle?) { 26 | super.onCreate(savedInstanceState) 27 | inject() 28 | setContentView(R.layout.activity_trades) 29 | initComponents() 30 | } 31 | 32 | private fun inject() { 33 | AndroidInjection.inject(this) 34 | } 35 | 36 | private fun initComponents() { 37 | val adapter = TradesAdapter(emptyList()) 38 | recycler.layoutManager = LinearLayoutManager(this) 39 | recycler.adapter = adapter 40 | swipe.setOnRefreshListener { getData(getIntent().getLongExtra("PAIR", 1L)) } 41 | presenter.initView(this) 42 | getData(getIntent().getLongExtra("PAIR", 1L)) 43 | } 44 | 45 | private fun getData(longExtra: Long) = launch { 46 | swipe.isRefreshing = true 47 | presenter.getData(longExtra) 48 | } 49 | 50 | override fun load(result: List) { 51 | recycler.adapter = TradesAdapter(result) 52 | swipe.isRefreshing = false 53 | } 54 | 55 | override fun onDestroy() { 56 | super.onDestroy() 57 | coroutineContext.cancelChildren() 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/view/tradescreen/TradesAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.view.tradescreen 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.TextView 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.kotlin.client.R 9 | import com.kotlin.core.domain.entities.Trade 10 | 11 | class TradesAdapter(private val trades: List) : 12 | RecyclerView.Adapter() { 13 | 14 | override fun onBindViewHolder(holder: TradesAdapter.TradesViewHolder, position: Int) { 15 | holder.bind(trades[position]) 16 | } 17 | 18 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TradesAdapter.TradesViewHolder { 19 | val v = LayoutInflater.from(parent.context).inflate(R.layout.item_trade, parent, false) 20 | return TradesViewHolder(v) 21 | } 22 | 23 | override fun getItemCount(): Int = trades.size 24 | 25 | 26 | inner class TradesViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 27 | var date: TextView = itemView.findViewById(R.id.date) 28 | var amount: TextView = itemView.findViewById(R.id.amount) 29 | var type: TextView = itemView.findViewById(R.id.type) 30 | 31 | fun bind(data: Trade) { 32 | date.text = data.trade_date 33 | amount.text = "${data.amount}" 34 | type.text = data.trade_type 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /consumer-android/app/src/main/java/com/kotlin/client/view/tradescreen/TradesScreenPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.view.tradescreen 2 | 3 | 4 | import com.kotlin.core.domain.entities.Trade 5 | import com.kotlin.core.domain.entities.usecases.GetTrades 6 | import kotlinx.coroutines.GlobalScope 7 | import kotlinx.coroutines.async 8 | import javax.inject.Inject 9 | 10 | class TradesScreenPresenter @Inject constructor(private val getTrades: GetTrades) { 11 | 12 | lateinit var view: ScreenView 13 | 14 | fun initView(view: ScreenView) { 15 | this.view = view 16 | } 17 | 18 | suspend fun getData(id: Long) { 19 | val result = GlobalScope.async { getTrades.getTrades(id) }.await() 20 | view.load(result.trades) 21 | } 22 | 23 | suspend fun refreshData(id: Long) { 24 | val result = GlobalScope.async { getTrades.refreshTrades(id) }.await() 25 | view.load(result.trades) 26 | } 27 | 28 | interface ScreenView { 29 | fun load(result: List) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/layout/activity_pairs.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/layout/activity_trades.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/layout/item_pair.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 22 | 23 | 30 | 31 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/layout/item_trade.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 21 | 22 | 29 | 30 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdsap/Kotlin-Client-Server/9f07c3b5b94c8abe142bdceef70d4c4ad486cfc5/consumer-android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdsap/Kotlin-Client-Server/9f07c3b5b94c8abe142bdceef70d4c4ad486cfc5/consumer-android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdsap/Kotlin-Client-Server/9f07c3b5b94c8abe142bdceef70d4c4ad486cfc5/consumer-android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdsap/Kotlin-Client-Server/9f07c3b5b94c8abe142bdceef70d4c4ad486cfc5/consumer-android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdsap/Kotlin-Client-Server/9f07c3b5b94c8abe142bdceef70d4c4ad486cfc5/consumer-android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdsap/Kotlin-Client-Server/9f07c3b5b94c8abe142bdceef70d4c4ad486cfc5/consumer-android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdsap/Kotlin-Client-Server/9f07c3b5b94c8abe142bdceef70d4c4ad486cfc5/consumer-android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdsap/Kotlin-Client-Server/9f07c3b5b94c8abe142bdceef70d4c4ad486cfc5/consumer-android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdsap/Kotlin-Client-Server/9f07c3b5b94c8abe142bdceef70d4c4ad486cfc5/consumer-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdsap/Kotlin-Client-Server/9f07c3b5b94c8abe142bdceef70d4c4ad486cfc5/consumer-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Kotlin Client 3 | 4 | -------------------------------------------------------------------------------- /consumer-android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /consumer-android/repository-android/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /consumer-android/repository-android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.clientserver.buildProcess.CollectUnitTest.collectUnitTest 2 | 3 | plugins { 4 | id("com.android.library") 5 | id("kotlin-android") 6 | id("kotlin-kapt") 7 | id("de.mannodermaus.android-junit5") 8 | } 9 | 10 | android { 11 | compileSdkVersion(28) 12 | defaultConfig { 13 | minSdkVersion(21) 14 | targetSdkVersion(28) 15 | versionCode = 1 16 | versionName = "1.0" 17 | testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner" 18 | } 19 | buildTypes { 20 | } 21 | } 22 | 23 | collectUnitTest() 24 | 25 | 26 | dependencies { 27 | implementation(project(":core-domain")) 28 | implementation(project(":core-network")) 29 | implementation(Dependencies.dagger) 30 | kapt(Dependencies.roomCompiler) 31 | kapt(Dependencies.daggerCompiler) 32 | implementation(Dependencies.gson) 33 | api(Dependencies.retrofitConverter) 34 | api(Dependencies.retrofit) 35 | implementation(Dependencies.coroutinesCore) 36 | implementation(Dependencies.coroutinesAndroid) 37 | implementation(Dependencies.roomRuntime) 38 | testImplementation(Dependencies.kotlinTest) 39 | testImplementation(Dependencies.mockitoKotlin) 40 | 41 | } 42 | -------------------------------------------------------------------------------- /consumer-android/repository-android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle.kts.kts. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /consumer-android/repository-android/src/androidTest/java/inakivillar/com/repository_android/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package inakivillar.com.repository_android; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("inakivillar.com.repository_android.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /consumer-android/repository-android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /consumer-android/repository-android/src/main/java/com/kotlin/client/repository/GetTradesRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.repository 2 | 3 | import com.kotlin.client.repository.database.DbInterface 4 | import com.kotlin.client.repository.database.TradeDb 5 | import com.kotlin.client.repository.api.BxApi 6 | import com.kotlin.core.domain.entities.Trade 7 | import com.kotlin.core.domain.entities.Trades 8 | import com.kotlin.core.domain.entities.repository.TradesRepository 9 | 10 | 11 | class GetTradesRepositoryImpl(private val db: DbInterface, 12 | private val api: BxApi) : TradesRepository { 13 | 14 | override fun getTradesPersisted(id: Long): Trades = Trades(db.getTradeDb(id).map { 15 | Trade(trade_id = it.trade_id, 16 | trade_date = it.trade_date, 17 | trade_type = it.trade_type, 18 | amount = it.amount, 19 | rate = it.rate) 20 | }.toList()) 21 | 22 | override fun getTradesRemote(id: Long): Trades = api.getTrades(id) 23 | 24 | override fun save(trade: Trade) { 25 | db.insertTrade(TradeDb(trade_type = trade.trade_type, 26 | trade_date = trade.trade_date, 27 | trade_id = trade.trade_id, 28 | amount = trade.amount, 29 | rate = trade.rate, 30 | pair = trade.pair)) 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /consumer-android/repository-android/src/main/java/com/kotlin/client/repository/PairRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.repository 2 | 3 | import com.kotlin.client.repository.database.DbInterface 4 | import com.kotlin.client.repository.api.BxApi 5 | import com.kotlin.client.repository.mapper.MapperToPairDb 6 | import com.kotlin.client.repository.mapper.MapperToPairSymbol 7 | import com.kotlin.client.repository.mapper.MapperToTradeDb 8 | import com.kotlin.core.domain.entities.PairSymbol 9 | import com.kotlin.core.domain.entities.repository.PairsRepository 10 | 11 | class PairRepositoryImpl(val db: DbInterface, 12 | val api: BxApi) : PairsRepository { 13 | private val mapperToPairDb = MapperToPairDb() 14 | private val mapperToPairSymbol = MapperToPairSymbol() 15 | private val mapperToTradeDb = MapperToTradeDb() 16 | 17 | override fun syncPairs(): List { 18 | api.syncTrades().forEach { 19 | 20 | if (db.getPairs(it.pairSymbol.id) == null) { 21 | db.insertPair(mapperToPairDb.transform(it)) 22 | } else { 23 | db.updatePair(mapperToPairDb.transform(it)) 24 | } 25 | it.trades.trades.map { trade -> 26 | db.insertTrade(mapperToTradeDb.transform(trade)) 27 | } 28 | } 29 | return getPairs() 30 | } 31 | 32 | override fun getPairs(): List { 33 | return db.getPairs().map { 34 | mapperToPairSymbol.transform(it) 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /consumer-android/repository-android/src/main/java/com/kotlin/client/repository/api/Api.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.repository.api 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import com.kotlin.core.domain.entities.Market 5 | import com.kotlin.core.domain.entities.Trades 6 | import retrofit2.Call 7 | import retrofit2.http.GET 8 | import retrofit2.http.Path 9 | 10 | interface Api { 11 | 12 | @GET("api/v2/trades/{id}") 13 | fun getTrades(@Path("id") pair: Long): Call 14 | 15 | @GET("api/v2/market") 16 | fun syncTrades(): Call 17 | } 18 | 19 | 20 | data class MarketOverall(@SerializedName("items") val items: List) 21 | 22 | -------------------------------------------------------------------------------- /consumer-android/repository-android/src/main/java/com/kotlin/client/repository/api/BxApi.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.repository.api 2 | 3 | import com.kotlin.core.domain.entities.Market 4 | import com.kotlin.core.domain.entities.Trades 5 | 6 | interface BxApi { 7 | 8 | fun getTrades(pair: Long): Trades 9 | 10 | fun syncTrades(): List 11 | } 12 | -------------------------------------------------------------------------------- /consumer-android/repository-android/src/main/java/com/kotlin/client/repository/api/BxApiImpl.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.repository.api 2 | 3 | import com.kotlin.core.domain.entities.Market 4 | import com.kotlin.core.domain.entities.Trades 5 | import retrofit2.Retrofit 6 | 7 | class BxApiImpl(retrofit: Retrofit) : BxApi { 8 | 9 | private val api: Api = retrofit.create(Api::class.java) 10 | 11 | 12 | override fun getTrades(pair: Long): Trades { 13 | return try { 14 | val a = api.getTrades(pair).execute() 15 | a.body()!! 16 | 17 | } catch (e: Exception) { 18 | Trades(listOf()) 19 | } 20 | } 21 | 22 | override fun syncTrades(): List = 23 | try { 24 | val a = api.syncTrades().execute() 25 | a.body()!!.items 26 | } catch (e: Exception) { 27 | emptyList() 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /consumer-android/repository-android/src/main/java/com/kotlin/client/repository/api/MarketDeserializer.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.repository.api 2 | 3 | import com.google.gson.* 4 | import com.kotlin.core.domain.entities.Market 5 | import java.lang.reflect.Type 6 | 7 | class MarketDeserializer : JsonDeserializer { 8 | 9 | companion object { 10 | const val ENTITY = "items" 11 | } 12 | 13 | private val gson = Gson() 14 | 15 | @Throws(JsonParseException::class) 16 | override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): MarketOverall { 17 | val trades = mutableListOf() 18 | json.asJsonObject.get(ENTITY).asJsonArray.forEach { 19 | val trade = gson.fromJson(it, Market::class.java) 20 | trades.add(trade) 21 | } 22 | return MarketOverall(trades) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /consumer-android/repository-android/src/main/java/com/kotlin/client/repository/database/AppDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.repository.database 2 | 3 | import android.content.Context 4 | import androidx.room.Database 5 | import androidx.room.Room 6 | import androidx.room.RoomDatabase 7 | 8 | @Database(entities = [TradeDb::class, PairDb::class], 9 | version = 6, 10 | exportSchema = false) 11 | abstract class AppDatabase : RoomDatabase() { 12 | abstract fun dbInterface(): DbInterface 13 | 14 | companion object { 15 | 16 | @Volatile 17 | private var INSTANCE: AppDatabase? = null 18 | 19 | fun getInstance(context: Context): AppDatabase = 20 | INSTANCE ?: synchronized(this) { 21 | INSTANCE ?: buildDatabase(context).also { INSTANCE = it } 22 | } 23 | 24 | private fun buildDatabase(context: Context): AppDatabase { 25 | return Room.databaseBuilder(context.applicationContext, 26 | AppDatabase:: 27 | class.java, 28 | "bx.db") 29 | .build() 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /consumer-android/repository-android/src/main/java/com/kotlin/client/repository/database/DbInterface.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.repository.database 2 | 3 | import androidx.room.* 4 | 5 | @Dao 6 | interface DbInterface { 7 | 8 | @Query("Select * FROM TradeDb where pair = :id order by trade_id DESC") 9 | fun getTradeDb(id: Long): List 10 | 11 | @Insert(onConflict = OnConflictStrategy.IGNORE) 12 | fun insertTrade(pair: TradeDb): Long 13 | 14 | @Update 15 | fun updatePair(pair: PairDb) 16 | 17 | 18 | @Insert(onConflict = OnConflictStrategy.IGNORE) 19 | fun insertPair(pair: PairDb): Long 20 | 21 | @Query("Select * FROM PairDb") 22 | fun getPairs(): List 23 | 24 | @Query("Select * FROM PairDb where id=:id") 25 | fun getPairs(id: Long): PairDb 26 | 27 | } -------------------------------------------------------------------------------- /consumer-android/repository-android/src/main/java/com/kotlin/client/repository/database/PairDb.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.repository.database 2 | 3 | import androidx.room.Entity 4 | import androidx.room.ForeignKey 5 | import androidx.room.PrimaryKey 6 | 7 | @Entity 8 | class PairDb( 9 | @PrimaryKey 10 | val id: Long, 11 | val primaryPairId: String, 12 | val secondaryPairId: String, 13 | val lastPrice: Double, 14 | val volume: Double 15 | ) -------------------------------------------------------------------------------- /consumer-android/repository-android/src/main/java/com/kotlin/client/repository/database/TradeDb.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.repository.database 2 | 3 | import androidx.room.Entity 4 | import androidx.room.ForeignKey 5 | import androidx.room.PrimaryKey 6 | import com.kotlin.client.repository.database.PairDb 7 | 8 | @Entity 9 | data class TradeDb( 10 | @ForeignKey(entity = PairDb::class, 11 | childColumns = arrayOf("pair"), 12 | parentColumns = arrayOf("id")) 13 | @PrimaryKey 14 | var trade_id: Long = 0, 15 | var rate: Double = 0.0, 16 | var amount: Double = 0.0, 17 | var trade_date: String = "", 18 | var trade_type: String = "", 19 | var pair: Long = 1L 20 | ) -------------------------------------------------------------------------------- /consumer-android/repository-android/src/main/java/com/kotlin/client/repository/di/RepositoryModule.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.repository.di 2 | 3 | import com.google.gson.GsonBuilder 4 | import com.kotlin.client.repository.api.MarketDeserializer 5 | import com.kotlin.client.repository.database.AppDatabase 6 | import com.kotlin.client.repository.database.DbInterface 7 | import com.kotlin.client.repository.GetTradesRepositoryImpl 8 | import com.kotlin.client.repository.PairRepositoryImpl 9 | import com.kotlin.client.repository.api.BxApi 10 | import com.kotlin.client.repository.api.BxApiImpl 11 | import com.kotlin.client.repository.api.MarketOverall 12 | import com.kotlin.core.domain.entities.Trades 13 | import com.kotlin.core.network.TradesDeserializer 14 | import com.kotlin.core.domain.entities.repository.PairsRepository 15 | import com.kotlin.core.domain.entities.repository.TradesRepository 16 | import dagger.Module 17 | import dagger.Provides 18 | import retrofit2.Converter 19 | import retrofit2.Retrofit 20 | import retrofit2.converter.gson.GsonConverterFactory 21 | import javax.inject.Named 22 | 23 | @Module 24 | class RepositoryModule { 25 | 26 | @Provides 27 | fun providesTradeRepository(db: DbInterface, 28 | api: BxApi): TradesRepository = 29 | GetTradesRepositoryImpl(db, api) 30 | 31 | @Provides 32 | fun providesPairRepository(db: DbInterface, 33 | api: BxApi): PairsRepository = 34 | PairRepositoryImpl(db, api) 35 | 36 | @Provides 37 | fun providesRestApi(retrofit: Retrofit): BxApi = BxApiImpl(retrofit) 38 | 39 | 40 | @Provides 41 | fun providesRetrofit(@Named("URL") url: String): Retrofit { 42 | return Retrofit.Builder() 43 | .baseUrl(url) 44 | .addConverterFactory(createGsonConverterPair()) 45 | .build() 46 | } 47 | 48 | @Provides 49 | fun providesDbInterface(appDatabase: AppDatabase): DbInterface { 50 | return appDatabase.dbInterface() 51 | } 52 | 53 | 54 | private fun createGsonConverterPair(): Converter.Factory { 55 | val gsonBuilder = GsonBuilder() 56 | gsonBuilder.registerTypeAdapter(MarketOverall::class.java, MarketDeserializer()) 57 | gsonBuilder.registerTypeAdapter(Trades::class.java, TradesDeserializer()) 58 | val gson = gsonBuilder.create() 59 | return GsonConverterFactory.create(gson) 60 | } 61 | } -------------------------------------------------------------------------------- /consumer-android/repository-android/src/main/java/com/kotlin/client/repository/mapper/MapperToPairDb.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.repository.mapper 2 | 3 | import com.kotlin.client.repository.database.PairDb 4 | import com.kotlin.core.domain.entities.Market 5 | import com.kotlin.core.domain.entities.mapper.Mapper 6 | 7 | class MapperToPairDb : Mapper { 8 | override fun transform(origin: Market): PairDb { 9 | return PairDb( 10 | id = origin.pairSymbol.id, 11 | volume = origin.pairSymbol.volume, 12 | primaryPairId = origin.pairSymbol.primarySymbol, 13 | secondaryPairId = origin.pairSymbol.secondarySymbol, 14 | lastPrice = origin.pairSymbol.rate 15 | ) 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /consumer-android/repository-android/src/main/java/com/kotlin/client/repository/mapper/MapperToPairSymbol.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.repository.mapper 2 | 3 | import com.kotlin.client.repository.database.PairDb 4 | import com.kotlin.core.domain.entities.PairSymbol 5 | import com.kotlin.core.domain.entities.mapper.Mapper 6 | 7 | class MapperToPairSymbol : Mapper { 8 | override fun transform(origin: PairDb): PairSymbol { 9 | return PairSymbol(id = origin.id, 10 | primarySymbol = origin.primaryPairId, 11 | secondarySymbol = origin.secondaryPairId, 12 | volume = origin.volume, 13 | rate = origin.lastPrice) 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /consumer-android/repository-android/src/main/java/com/kotlin/client/repository/mapper/MapperToTradeDb.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.repository.mapper 2 | 3 | import com.kotlin.client.repository.database.TradeDb 4 | import com.kotlin.core.domain.entities.Trade 5 | import com.kotlin.core.domain.entities.mapper.Mapper 6 | 7 | class MapperToTradeDb : Mapper { 8 | override fun transform(origin: Trade): TradeDb { 9 | return TradeDb(trade_type = origin.trade_type, 10 | trade_date = origin.trade_date, 11 | trade_id = origin.trade_id, 12 | amount = origin.amount, 13 | rate = origin.rate, 14 | pair = origin.pair) 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /consumer-android/repository-android/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | repository_android 3 | 4 | -------------------------------------------------------------------------------- /consumer-android/repository-android/src/test/java/com/kotlin/client/repository/GetTradesRepositoryImplTest.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.repository 2 | 3 | import com.kotlin.client.repository.api.BxApi 4 | import com.kotlin.client.repository.database.DbInterface 5 | import com.kotlin.client.repository.database.TradeDb 6 | import com.kotlin.core.domain.entities.Trade 7 | import com.kotlin.core.domain.entities.Trades 8 | import com.nhaarman.mockitokotlin2.argThat 9 | import com.nhaarman.mockitokotlin2.mock 10 | import com.nhaarman.mockitokotlin2.verify 11 | import com.nhaarman.mockitokotlin2.whenever 12 | import io.kotlintest.specs.BehaviorSpec 13 | 14 | 15 | class GetTradesRepositoryImplTest : BehaviorSpec({ 16 | 17 | given("GetTradesRepository Implementation") { 18 | 19 | `when`("Trades are coming from Remote Source") { 20 | val db = mock {} 21 | val api = mock {} 22 | val getTradesRepository = GetTradesRepositoryImpl(db, api) 23 | whenever(api.getTrades(1L)).thenReturn(Trades(listOf(Trade( 24 | rate = 1.0, 25 | amount = 2.0, 26 | pair = 1L, 27 | trade_date = "", 28 | trade_id = 10L, 29 | trade_type = "sell" 30 | )))) 31 | val trades = getTradesRepository.getTradesRemote(1L) 32 | then("Remote source is called ") { 33 | verify(api).getTrades(1L) 34 | 35 | } 36 | then("List of trades is returned ") { 37 | assert(trades.trades.size == 1) 38 | assert(trades.trades[0].pair == 1L) 39 | assert(trades.trades[0].trade_type == "sell") 40 | 41 | } 42 | } 43 | `when`("Trades are coming from Local/Persosted Source") { 44 | val db = mock {} 45 | val api = mock {} 46 | val getTradesRepository = GetTradesRepositoryImpl(db, api) 47 | whenever(db.getTradeDb(1L)).thenReturn(listOf(TradeDb( 48 | rate = 1.0, 49 | amount = 2.0, 50 | pair = 1L, 51 | trade_date = "", 52 | trade_id = 10L, 53 | trade_type = "sell" 54 | ))) 55 | val trades = getTradesRepository.getTradesPersisted(1L) 56 | then("Remote source is called ") { 57 | verify(db).getTradeDb(1L) 58 | 59 | } 60 | then("List of trades is returned ") { 61 | assert(trades.trades.size == 1) 62 | assert(trades.trades[0].pair == 1L) 63 | assert(trades.trades[0].trade_type == "sell") 64 | 65 | } 66 | } 67 | `when`("Saving Trade") { 68 | val db = mock {} 69 | val api = mock {} 70 | val getTradesRepository = GetTradesRepositoryImpl(db, api) 71 | 72 | val trades = getTradesRepository.save(Trade( 73 | rate = 1.0, 74 | amount = 2.0, 75 | pair = 1L, 76 | trade_date = "", 77 | trade_id = 10L, 78 | trade_type = "sell" 79 | )) 80 | then("RTrade is transfdomerd ") { 81 | verify(db).insertTrade(argThat { 82 | this.amount == 2.0 && 83 | rate == 1.0 && 84 | trade_date == "" && 85 | trade_id == 10L && 86 | trade_type == "sell" 87 | 88 | }) 89 | 90 | } 91 | 92 | } 93 | } 94 | }) 95 | 96 | -------------------------------------------------------------------------------- /consumer-android/repository-android/src/test/java/com/kotlin/client/repository/PairRepositoryImplTest.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.client.repository 2 | 3 | import com.kotlin.client.repository.api.BxApi 4 | import com.kotlin.client.repository.database.DbInterface 5 | import com.kotlin.client.repository.database.PairDb 6 | import com.kotlin.core.domain.entities.Market 7 | import com.kotlin.core.domain.entities.PairSymbol 8 | import com.kotlin.core.domain.entities.Trade 9 | import com.kotlin.core.domain.entities.Trades 10 | import com.nhaarman.mockitokotlin2.argThat 11 | import com.nhaarman.mockitokotlin2.mock 12 | import com.nhaarman.mockitokotlin2.verify 13 | import com.nhaarman.mockitokotlin2.whenever 14 | import io.kotlintest.specs.BehaviorSpec 15 | 16 | 17 | class PairRepositoryImplTest : BehaviorSpec({ 18 | given("Pair Repository Implementation") { 19 | `when`("Get pairs") { 20 | val db = mock {} 21 | val api = mock {} 22 | val pairRepository = PairRepositoryImpl(db, api) 23 | whenever(db.getPairs()).thenReturn(listOf(PairDb(id = 1L, 24 | lastPrice = 0.1, 25 | volume = 0.1, 26 | primaryPairId = "BTC", 27 | secondaryPairId = "OMG"))) 28 | 29 | then("a list of Domain Pairs is returned ") { 30 | val pairs = pairRepository.getPairs() 31 | assert(pairs.get(0).id == 1L) 32 | verify(db).getPairs() 33 | } 34 | } 35 | `when`("Syncing pairs") { 36 | val db = mock {} 37 | val api = mock {} 38 | val pairRepository = PairRepositoryImpl(db, api) 39 | whenever(db.getPairs(1L)).thenReturn(null) 40 | whenever(api.syncTrades()).thenReturn(listOf(Market(PairSymbol(id = 1L, 41 | rate = 1.0, 42 | volume = 1.0, 43 | primarySymbol = "BTC", 44 | secondarySymbol = "OMG"), Trades(listOf(Trade(trade_id = 1L, 45 | rate = 1.3, 46 | trade_type = "sell", 47 | trade_date = "", 48 | pair = 1L, 49 | amount = 2.0)))))) 50 | 51 | 52 | then("we shouoild inserta the pari and later the trades ") { 53 | pairRepository.syncPairs() 54 | verify(db).insertPair(argThat { 55 | id == 1L 56 | && primaryPairId == "BTC" 57 | && secondaryPairId == "OMG" 58 | && volume == 1.0 59 | && lastPrice == 1.0 60 | }) 61 | verify(db).insertTrade(argThat { 62 | this.trade_id == 1L 63 | && trade_type == "sell" 64 | && rate == 1.3 65 | && pair == 1L 66 | }) 67 | 68 | } 69 | 70 | } 71 | } 72 | }) -------------------------------------------------------------------------------- /consumer-server/domain-server/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /consumer-server/domain-server/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.clientserver.buildProcess.CollectUnitTest.collectUnitTest 2 | 3 | plugins { 4 | id("kotlin") 5 | id("kotlin-kapt") 6 | } 7 | 8 | val test by tasks.getting(Test::class) { 9 | useJUnitPlatform { } 10 | } 11 | 12 | collectUnitTest() 13 | 14 | 15 | dependencies { 16 | api(project(":core-domain")) 17 | implementation(Dependencies.javaxInject) 18 | implementation(Dependencies.kotlin) 19 | kapt(Dependencies.daggerProcessor) 20 | kapt(Dependencies.daggerCompiler) 21 | implementation(Dependencies.daggerCompiler) 22 | implementation(Dependencies.kotlin) 23 | testImplementation(Dependencies.kotlinTest) 24 | testImplementation(Dependencies.mockitoKotlin) 25 | } 26 | -------------------------------------------------------------------------------- /consumer-server/domain-server/src/main/java/com/kotlin/server/repository/domain/GetMarketImpl.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.domain 2 | 3 | import com.kotlin.core.domain.entities.Market 4 | import com.kotlin.core.domain.entities.repository.SyncRepository 5 | import com.kotlin.core.domain.entities.usecases.GetMarket 6 | 7 | 8 | class GetMarketImpl(private val syncRepository: SyncRepository) 9 | : GetMarket { 10 | 11 | override fun get(): List = syncRepository.get() 12 | } 13 | -------------------------------------------------------------------------------- /consumer-server/domain-server/src/main/java/com/kotlin/server/repository/domain/SyncTradesImpl.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.domain 2 | 3 | import com.kotlin.core.domain.entities.repository.PairsRepository 4 | import com.kotlin.core.domain.entities.repository.TradesRepository 5 | import com.kotlin.core.domain.entities.usecases.SyncTrades 6 | 7 | 8 | class SyncTradesImpl( 9 | private val tradesRepository: TradesRepository, 10 | private val pairRepository: PairsRepository) 11 | : SyncTrades { 12 | 13 | override fun syncTrades(): Unit = 14 | pairRepository.getPairs().forEach { pair -> 15 | tradesRepository.getTradesRemote(pair.id).trades.forEach { 16 | tradesRepository.save(it.copy(pair = pair.id)) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /consumer-server/domain-server/src/main/java/com/kotlin/server/repository/domain/di/DomainModule.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.domain.di 2 | 3 | import com.kotlin.core.domain.entities.repository.PairsRepository 4 | import com.kotlin.core.domain.entities.repository.SyncRepository 5 | import com.kotlin.core.domain.entities.repository.TradesRepository 6 | import com.kotlin.core.domain.entities.usecases.GetMarket 7 | import com.kotlin.core.domain.entities.usecases.GetPairs 8 | import com.kotlin.core.domain.entities.usecases.GetTrades 9 | import com.kotlin.core.domain.entities.usecases.SyncTrades 10 | import com.kotlin.core.domain.entities.usecases.impl.GetPairsImpl 11 | import com.kotlin.core.domain.entities.usecases.impl.GetTradesImpl 12 | import com.kotlin.server.repository.domain.GetMarketImpl 13 | import com.kotlin.server.repository.domain.SyncTradesImpl 14 | import dagger.Module 15 | import dagger.Provides 16 | 17 | @Module 18 | class DomainModule { 19 | 20 | @Provides 21 | fun provideGetTrades(tradesRepository: TradesRepository): 22 | GetTrades = GetTradesImpl(tradesRepository) 23 | 24 | @Provides 25 | fun providePairRepository(pairRepository: PairsRepository): 26 | GetPairs = GetPairsImpl(pairRepository) 27 | 28 | @Provides 29 | fun provideSyncTrades(tradesRepository: TradesRepository, 30 | pairRepository: PairsRepository) 31 | : SyncTrades = SyncTradesImpl(tradesRepository, pairRepository) 32 | 33 | @Provides 34 | fun providesGetMarket(syncRepository: SyncRepository): GetMarket = GetMarketImpl(syncRepository) 35 | } -------------------------------------------------------------------------------- /consumer-server/domain-server/src/test/java/com/kotlin/server/repository/domain/GetMarketImplTest.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.domain 2 | 3 | import com.kotlin.core.domain.entities.repository.SyncRepository 4 | import com.nhaarman.mockitokotlin2.* 5 | import io.kotlintest.specs.BehaviorSpec 6 | 7 | class GetMarketImplTest : BehaviorSpec({ 8 | given("GetMarket Implementation") { 9 | 10 | `when`("I want to sync Market ") { 11 | val syncRepository = mock { } 12 | val getMarket = GetMarketImpl(syncRepository) 13 | whenever(syncRepository.get()).thenReturn(emptyList()) 14 | getMarket.get() 15 | then("I should call repository") { 16 | verify(syncRepository).get() 17 | } 18 | } 19 | } 20 | }) -------------------------------------------------------------------------------- /consumer-server/domain-server/src/test/java/com/kotlin/server/repository/domain/SyncTradesImplTest.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.domain; 2 | 3 | import com.kotlin.core.domain.entities.PairSymbol 4 | import com.kotlin.core.domain.entities.Trade 5 | import com.kotlin.core.domain.entities.Trades 6 | import com.kotlin.core.domain.entities.repository.PairsRepository 7 | import com.kotlin.core.domain.entities.repository.TradesRepository 8 | import com.nhaarman.mockitokotlin2.* 9 | import io.kotlintest.specs.BehaviorSpec 10 | 11 | 12 | class SyncTradesImplTest : BehaviorSpec({ 13 | given("SyncTades Implementation") { 14 | 15 | `when`("There are no Pairs ") { 16 | val pairRepository = mock { } 17 | val tradesRepository = mock { } 18 | val syncTrades = SyncTradesImpl(tradesRepository, pairRepository) 19 | whenever(pairRepository.getPairs()).thenReturn(emptyList()) 20 | syncTrades.syncTrades() 21 | then("I shouldn't fetch remote trades") { 22 | verify(tradesRepository, never()).getTradesRemote(any()) 23 | } 24 | } 25 | `when`("There are Pairs") { 26 | val pairRepository = mock { } 27 | val tradesRepository = mock { } 28 | val syncTrades = SyncTradesImpl(tradesRepository, pairRepository) 29 | whenever(pairRepository.getPairs()).thenReturn(listOf(PairSymbol(1, "BTC", "EUR", 30 | 0.0, 0.0))) 31 | whenever(tradesRepository.getTradesRemote(any())).thenReturn(Trades(listOf(Trade()))) 32 | syncTrades.syncTrades() 33 | then("I should fetch remote trades and save trades on DB") { 34 | verify(tradesRepository).getTradesRemote(any()) 35 | verify(tradesRepository).save(argThat { 36 | trade_id == 0L && pair == 1L 37 | 38 | }) 39 | } 40 | } 41 | } 42 | }) -------------------------------------------------------------------------------- /consumer-server/repository-server/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /consumer-server/repository-server/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.clientserver.buildProcess.CollectUnitTest.collectUnitTest 2 | 3 | plugins { 4 | id("kotlin") 5 | id("kotlin-kapt") 6 | } 7 | 8 | val test by tasks.getting(Test::class) { 9 | useJUnitPlatform { } 10 | } 11 | 12 | collectUnitTest() 13 | 14 | dependencies { 15 | implementation(project(":consumer-server:domain-server")) 16 | implementation(project(":core-domain")) 17 | api(project(":core-network")) 18 | implementation(Dependencies.objectify) 19 | implementation(Dependencies.kotlin) 20 | implementation(Dependencies.retrofitConverter) 21 | implementation(Dependencies.retrofit) 22 | implementation(Dependencies.gson) 23 | implementation(Dependencies.javaxInject) 24 | implementation(Dependencies.daggerCompiler) 25 | implementation(Dependencies.appengineApi) 26 | kapt(Dependencies.daggerProcessor) 27 | kapt(Dependencies.daggerCompiler) 28 | testImplementation(Dependencies.kotlinTest) 29 | testImplementation(Dependencies.mockitoKotlin) 30 | } 31 | -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/GetTradesRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository 2 | 3 | import com.kotlin.core.domain.entities.Trade 4 | import com.kotlin.core.domain.entities.Trades 5 | import com.kotlin.core.domain.entities.repository.TradesRepository 6 | import com.kotlin.server.repository.api.BxApi 7 | import com.kotlin.server.repository.database.DbInterface 8 | import com.kotlin.server.repository.mapper.MapperToTradeStore 9 | 10 | class GetTradesRepositoryImpl(private val db: DbInterface, 11 | private val api: BxApi) : TradesRepository { 12 | 13 | private val mapperToTradeStore = MapperToTradeStore() 14 | 15 | override fun save(trade: Trade) { 16 | db.saveTrade(mapperToTradeStore.transform(trade)) 17 | } 18 | 19 | override fun getTradesRemote(id: Long): Trades = api.getTrades(id) 20 | 21 | override fun getTradesPersisted(id: Long): Trades = Trades(db.queryTrades(id)) 22 | 23 | } 24 | 25 | -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/PairRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository 2 | 3 | 4 | import com.kotlin.core.domain.entities.PairSymbol 5 | import com.kotlin.core.domain.entities.repository.PairsRepository 6 | import com.kotlin.server.repository.api.BxApi 7 | import com.kotlin.server.repository.database.DbInterface 8 | import com.kotlin.server.repository.mapper.MapperToPairStore 9 | import com.kotlin.server.repository.mapper.MapperToPairSymbol 10 | 11 | class PairRepositoryImpl(private val db: DbInterface, 12 | val api: BxApi) : PairsRepository { 13 | 14 | private val mapperToPairSymbol = MapperToPairSymbol() 15 | private val mapperToPairStore = MapperToPairStore() 16 | 17 | override fun syncPairs(): List { 18 | api.getPairsInfo().pairInfoList.forEach { 19 | 20 | if (db.queryPairById(it.pairing_id) == null) { 21 | db.savePair(mapperToPairStore.transform(it)) 22 | } else { 23 | val pairStore = db.queryPairById(it.pairing_id) 24 | pairStore.volume = it.volume_24_hours 25 | pairStore.rate = it.last_price 26 | db.savePair(pairStore) 27 | } 28 | 29 | } 30 | return getPairs() 31 | } 32 | 33 | override fun getPairs(): List = 34 | db.getPairs().map { mapperToPairSymbol.transform(it) } 35 | } 36 | -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/SyncPairsRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository 2 | 3 | import com.kotlin.core.domain.entities.Market 4 | import com.kotlin.core.domain.entities.Trades 5 | import com.kotlin.core.domain.entities.repository.SyncRepository 6 | import com.kotlin.server.repository.api.BxApi 7 | import com.kotlin.server.repository.database.* 8 | import com.kotlin.server.repository.mapper.MapperToPairStore 9 | import com.kotlin.server.repository.mapper.MapperToPairSymbol 10 | 11 | class SyncPairsRepositoryImpl(private val db: DbInterface, 12 | private val api: BxApi) : SyncRepository { 13 | private val mapperToPairStore = MapperToPairStore() 14 | private val mapperToPairSymbol = MapperToPairSymbol() 15 | 16 | override fun get(): List = 17 | db.getPairs() 18 | .map { 19 | Market(mapperToPairSymbol.transform(it), 20 | Trades(db.queryTrades(it.id))) 21 | } 22 | 23 | 24 | override fun sync() { 25 | api.getPairsInfo().pairInfoList.forEach { 26 | if (db.queryPairById(it.pairing_id) == null) { 27 | db.savePair(mapperToPairStore.transform(it)) 28 | } else { 29 | val pairStore = db.queryPairById(it.pairing_id) 30 | pairStore.volume = it.volume_24_hours 31 | pairStore.rate = it.last_price 32 | db.savePair(pairStore) 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/api/Bx.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.api 2 | 3 | import com.kotlin.core.domain.entities.Trades 4 | import com.kotlin.server.repository.api.entities.PairsInfo 5 | import retrofit2.Call 6 | import retrofit2.http.GET 7 | import retrofit2.http.Query 8 | 9 | interface Bx { 10 | 11 | @GET("/api/trade/?") 12 | fun getTrades(@Query("pairing") pairing_id: Long): Call 13 | 14 | @GET("/api/") 15 | fun getPairInfo(): Call 16 | } 17 | 18 | -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/api/BxApi.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.api 2 | 3 | import com.kotlin.core.domain.entities.Trades 4 | import com.kotlin.server.repository.api.entities.PairsInfo 5 | 6 | 7 | interface BxApi { 8 | fun getTrades(id: Long): Trades 9 | fun getPairsInfo(): PairsInfo 10 | } -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/api/BxApiImpl.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.api 2 | 3 | import com.kotlin.core.domain.entities.Trades 4 | import com.kotlin.server.repository.api.entities.PairsInfo 5 | import retrofit2.Retrofit 6 | 7 | class BxApiImpl(retrofit: Retrofit) : BxApi { 8 | 9 | private val bx = retrofit.create(Bx::class.java) 10 | 11 | override fun getTrades(id: Long): Trades { 12 | val a = bx.getTrades(id).execute() 13 | return a?.body() ?: Trades(listOf()) 14 | } 15 | 16 | override fun getPairsInfo(): PairsInfo { 17 | val a = bx.getPairInfo().execute() 18 | return a?.body()!! 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/api/PairsDeserializer.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.api 2 | 3 | import com.google.gson.* 4 | import com.kotlin.server.repository.api.entities.PairInfo 5 | import com.kotlin.server.repository.api.entities.PairsInfo 6 | import java.lang.reflect.Type 7 | 8 | class PairsDeserializer : JsonDeserializer { 9 | private val gson = Gson() 10 | 11 | @Throws(JsonParseException::class) 12 | override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): PairsInfo { 13 | val value = mutableListOf() 14 | for ((_, pair) in json.asJsonObject.entrySet()) { 15 | val pairInfo = gson.fromJson(pair, PairInfo::class.java) 16 | value.add(pairInfo) 17 | } 18 | return PairsInfo(value) 19 | } 20 | } -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/api/entities/PairsInfo.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.api.entities 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class PairsInfo(val pairInfoList: List) 6 | 7 | data class PairInfo(@SerializedName("pairing_id") val pairing_id: Long = 1L, 8 | @SerializedName("last_price") val last_price: Double = 0.0, 9 | @SerializedName("volume_24hours") val volume_24_hours: Double = 0.0, 10 | @SerializedName("primary_currency") val primary_currency: String = "", 11 | @SerializedName("secondary_currency") val secondary_currency: String = "") 12 | 13 | -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/api/patch/CallFactoryWrapper.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.api.patch 2 | 3 | import okhttp3.Call 4 | import okhttp3.Request 5 | 6 | class CallFactoryWrapper : Call.Factory { 7 | override fun newCall(request: Request?): Call = CallWrapper(request!!) 8 | } -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/api/patch/CallWrapper.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.api.patch 2 | 3 | import okhttp3.* 4 | import okhttp3.internal.http.HttpMethod 5 | import okio.Buffer 6 | import java.io.BufferedInputStream 7 | import java.io.ByteArrayOutputStream 8 | import java.io.DataOutputStream 9 | import java.io.IOException 10 | import java.net.HttpURLConnection 11 | import java.net.URL 12 | import java.net.URLConnection 13 | 14 | class CallWrapper(private val request: Request, private var executed: Boolean = false, 15 | private var cancelled: Boolean = false) : Call { 16 | 17 | override fun enqueue(responseCallback: Callback?) {} 18 | 19 | override fun isExecuted() = executed 20 | 21 | override fun clone() = CallWrapper(request) 22 | 23 | override fun isCanceled() = cancelled 24 | 25 | override fun cancel() { 26 | cancelled = true 27 | } 28 | 29 | override fun request() = request 30 | 31 | override fun execute(): Response { 32 | synchronized(this) { 33 | if (executed) { 34 | throw IllegalStateException("Already Executed") 35 | } 36 | executed = true 37 | } 38 | if ("GET".equals(request.method(), ignoreCase = true) || 39 | "DELETE".equals(request.method(), ignoreCase = true) || 40 | "POST".equals(request.method(), ignoreCase = true) || 41 | "PATCH".equals(request.method(), ignoreCase = true) || 42 | "PUT".equals(request.method(), ignoreCase = true)) { 43 | 44 | val url = URL(request.url().url().toString()) 45 | 46 | val con = url.openConnection() as HttpURLConnection 47 | con.addRequestProperty("User-Agent", "Mozilla/4.76") 48 | 49 | if ("PATCH" == request.method()) { 50 | con.setRequestProperty("X-HTTP-Method-Override", "PATCH") 51 | con.requestMethod = "POST" 52 | } else { 53 | con.requestMethod = request.method() 54 | } 55 | 56 | setHeaders(request, url, con) 57 | 58 | if (HttpMethod.requiresRequestBody(request.method()) && request.body()!!.contentLength() > 0) { 59 | 60 | val payload = Buffer() 61 | request.body()!!.writeTo(payload) 62 | 63 | con.doOutput = true 64 | val wr = DataOutputStream(con.outputStream) 65 | wr.write(payload.readByteArray()) 66 | wr.flush() 67 | wr.close() 68 | } 69 | 70 | val builder = parseResponse(con) 71 | return builder.build() 72 | 73 | } else { 74 | throw RuntimeException("Unsupported HTTP method : " + request.method()) 75 | 76 | } 77 | } 78 | 79 | private fun setHeaders(request: Request, url: URL, con: URLConnection) { 80 | val headers = request.headers() 81 | for (header in headers.names()) { 82 | con.setRequestProperty(header, headers.get(header)) 83 | } 84 | 85 | if (request.isHttps) { 86 | var port = url.port 87 | if (port == -1) { 88 | port = 443 89 | } 90 | con.setRequestProperty("Host", url.host + ":" + port) 91 | } 92 | } 93 | 94 | @Throws(IOException::class) 95 | private fun parseResponse(connection: HttpURLConnection): Response.Builder { 96 | val builder = Response.Builder() 97 | builder.request(request()) 98 | 99 | builder.protocol(Protocol.HTTP_1_1) 100 | builder.code(connection.responseCode) 101 | builder.message(connection.responseMessage.toString()) 102 | 103 | val `in` = connection.inputStream 104 | if (`in` != null) { 105 | `in`.use { 106 | val bis = BufferedInputStream(it) 107 | val buffer = ByteArray(8192) 108 | 109 | val baos = ByteArrayOutputStream() 110 | 111 | var len = bis.read(buffer) 112 | while (len != -1) { 113 | baos.write(buffer, 0, len) 114 | len = bis.read(buffer) 115 | } 116 | builder.body(ResponseBody.create(MediaType.parse(connection.contentType), baos.toByteArray())) 117 | } 118 | } else { 119 | builder.body(ResponseBody.create(null, 0, Buffer())) 120 | } 121 | 122 | return builder 123 | } 124 | } -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/database/DbImpl.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.database 2 | 3 | import com.googlecode.objectify.Objectify 4 | import com.kotlin.core.domain.entities.Trade 5 | 6 | class DbImpl(private val db: Objectify) : DbInterface { 7 | override fun savePair(pairStore: PairStore) { 8 | db.savePair(pairStore) 9 | } 10 | 11 | override fun saveTrade(tradeStore: TradeStore) { 12 | db.saveTrade(tradeStore) 13 | } 14 | 15 | override fun getPairs(): List = db.queryPairs() 16 | 17 | override fun queryPairById(id: Long): PairStore = db.queryPairById(id).now() 18 | 19 | override fun queryTrades(id: Long): List = db.queryTrades(id) 20 | 21 | } 22 | -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/database/DbInterface.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.database 2 | 3 | import com.kotlin.core.domain.entities.Trade 4 | 5 | interface DbInterface { 6 | 7 | fun getPairs(): List 8 | 9 | fun queryPairById(id: Long): PairStore 10 | 11 | fun queryTrades(id: Long): List 12 | 13 | fun savePair(pairStore: PairStore) 14 | 15 | fun saveTrade(tradeStore: TradeStore) 16 | 17 | } -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/database/ExtensionQueries.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.database 2 | 3 | import com.googlecode.objectify.Key 4 | import com.googlecode.objectify.Objectify 5 | import com.kotlin.core.domain.entities.Trade 6 | import com.kotlin.server.repository.mapper.MapperToTrade 7 | 8 | fun Objectify.queryTrades(id: Long): List { 9 | val mapperTradeStore = MapperToTrade() 10 | val trades = mutableListOf() 11 | this.load().type(TradeStore::class.java) 12 | .filter("pair", Key.create(PairStore::class.java, id)) 13 | .limit(50) 14 | .order(ORDER) 15 | .list().map { 16 | trades.add(mapperTradeStore.transform(it)) 17 | } 18 | return trades 19 | } 20 | 21 | 22 | fun Objectify.queryPairs() = this.load().type(PairStore::class.java).list() 23 | 24 | fun Objectify.queryPairById(id: Long) = this.load().type(PairStore::class.java) 25 | .id(id) 26 | 27 | fun Objectify.savePair(pairStore: PairStore) = this.save().entity(pairStore) 28 | 29 | fun Objectify.saveTrade(trade : TradeStore) = this.save().entity(trade) 30 | 31 | private const val FIELD = "__key__" 32 | private const val DIRECTION = "-" 33 | const val ORDER = "$DIRECTION$FIELD" 34 | -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/database/PairStore.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.database 2 | 3 | import com.googlecode.objectify.annotation.Entity 4 | import com.googlecode.objectify.annotation.Id 5 | 6 | 7 | @Entity 8 | class PairStore( 9 | @Id var id: Long = 0L, 10 | var primaryPairId: String = "", 11 | var secondaryPairId: String = "", 12 | var rate: Double = 0.0, 13 | var volume: Double = 0.0 14 | ) 15 | -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/database/TradeStore.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.database 2 | 3 | import com.googlecode.objectify.ObjectifyService 4 | import com.googlecode.objectify.Ref 5 | import com.googlecode.objectify.annotation.Entity 6 | import com.googlecode.objectify.annotation.Id 7 | import com.googlecode.objectify.annotation.Index 8 | 9 | @Entity 10 | class TradeStore( 11 | @Id 12 | var trade_id: Long = 0L, 13 | var rate: Double = 0.0, 14 | var amount: Double = 0.0, 15 | var trade_date: String = "", 16 | var trade_type: String = "", 17 | @Index 18 | var pair: Ref = Ref 19 | .create(ObjectifyService.ofy().load() 20 | .type(PairStore::class.java).id(1L).safe())) 21 | -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/di/RepositoryModule.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.di 2 | 3 | 4 | import com.google.appengine.api.utils.SystemProperty 5 | import com.google.cloud.datastore.DatastoreOptions 6 | import com.google.gson.GsonBuilder 7 | import com.googlecode.objectify.Objectify 8 | import com.googlecode.objectify.ObjectifyFactory 9 | import com.googlecode.objectify.ObjectifyService 10 | import com.kotlin.core.domain.entities.Trades 11 | import com.kotlin.core.network.TradesDeserializer 12 | import com.kotlin.core.domain.entities.repository.PairsRepository 13 | import com.kotlin.core.domain.entities.repository.SyncRepository 14 | import com.kotlin.core.domain.entities.repository.TradesRepository 15 | import com.kotlin.server.repository.GetTradesRepositoryImpl 16 | import com.kotlin.server.repository.PairRepositoryImpl 17 | import com.kotlin.server.repository.SyncPairsRepositoryImpl 18 | import com.kotlin.server.repository.api.BxApiImpl 19 | import com.kotlin.server.repository.api.PairsDeserializer 20 | import com.kotlin.server.repository.api.entities.PairsInfo 21 | import com.kotlin.server.repository.api.patch.CallFactoryWrapper 22 | import com.kotlin.server.repository.database.DbImpl 23 | import com.kotlin.server.repository.database.DbInterface 24 | import com.kotlin.server.repository.database.PairStore 25 | import com.kotlin.server.repository.database.TradeStore 26 | import dagger.Module 27 | import dagger.Provides 28 | import retrofit2.Retrofit 29 | import retrofit2.converter.gson.GsonConverterFactory 30 | 31 | @Module 32 | class RepositoryModule { 33 | init { 34 | initData() 35 | } 36 | 37 | @Provides 38 | fun providesDbInterface(objectify: Objectify): DbInterface = DbImpl(objectify) 39 | 40 | @Provides 41 | fun providesRetrofit(gsonConverterFactory: GsonConverterFactory): Retrofit = 42 | Retrofit.Builder() 43 | .baseUrl(URL) 44 | .addConverterFactory(gsonConverterFactory) 45 | .callFactory(CallFactoryWrapper()) 46 | .build() 47 | 48 | 49 | @Provides 50 | fun providesGsonConverter(): GsonConverterFactory { 51 | val gsonBuilder = GsonBuilder() 52 | gsonBuilder.registerTypeAdapter(PairsInfo::class.java, PairsDeserializer()) 53 | gsonBuilder.registerTypeAdapter(Trades::class.java, TradesDeserializer()) 54 | val gson = gsonBuilder.create() 55 | return GsonConverterFactory.create(gson) 56 | } 57 | 58 | @Provides 59 | fun providesTradesRepository(dbInterface: DbInterface, 60 | api: BxApiImpl): TradesRepository = 61 | GetTradesRepositoryImpl(dbInterface, api) 62 | 63 | @Provides 64 | fun providesPairRepository(dbInterface: DbInterface, 65 | api: BxApiImpl): PairsRepository = 66 | PairRepositoryImpl(dbInterface, api) 67 | 68 | @Provides 69 | fun providesSyncPairRepository(dbInterface: DbInterface, 70 | api: BxApiImpl): SyncRepository = SyncPairsRepositoryImpl(dbInterface, api) 71 | 72 | @Provides 73 | fun providesRestApi(retrofit: Retrofit): BxApiImpl = BxApiImpl(retrofit) 74 | 75 | @Provides 76 | fun providesObjectifyService(): Objectify { 77 | return ObjectifyService.ofy() 78 | } 79 | 80 | private fun initData() { 81 | if (SystemProperty.environment.value() == SystemProperty.Environment.Value.Production) { 82 | ObjectifyService.init() 83 | } else { 84 | val dataStore = DatastoreOptions.newBuilder() 85 | .setHost("http://localhost:8081") 86 | .setProjectId("YOUR_GAE_PROJECT_ID") 87 | .build() 88 | .service 89 | ObjectifyService.init(ObjectifyFactory(dataStore)) 90 | } 91 | ObjectifyService.begin() 92 | ObjectifyService.register(TradeStore::class.java) 93 | ObjectifyService.register(PairStore::class.java) 94 | } 95 | 96 | companion object { 97 | const val URL = "https://bx.in.th" 98 | } 99 | } 100 | 101 | -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/mapper/MapperToPairStore.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.mapper 2 | 3 | import com.kotlin.core.domain.entities.mapper.Mapper 4 | import com.kotlin.server.repository.api.entities.PairInfo 5 | import com.kotlin.server.repository.database.PairStore 6 | 7 | class MapperToPairStore : Mapper { 8 | 9 | override fun transform(origin: PairInfo): PairStore { 10 | return PairStore( 11 | id = origin.pairing_id, 12 | volume = origin.volume_24_hours, 13 | rate = origin.last_price, 14 | primaryPairId = origin.primary_currency, 15 | secondaryPairId = origin.secondary_currency) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/mapper/MapperToPairSymbol.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.mapper 2 | 3 | import com.kotlin.core.domain.entities.PairSymbol 4 | import com.kotlin.core.domain.entities.mapper.Mapper 5 | import com.kotlin.server.repository.database.PairStore 6 | 7 | class MapperToPairSymbol : Mapper { 8 | override fun transform(origin: PairStore): PairSymbol { 9 | return PairSymbol(id = origin.id, 10 | primarySymbol = origin.primaryPairId, 11 | secondarySymbol = origin.secondaryPairId, 12 | volume = origin.volume, 13 | rate = origin.rate) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/mapper/MapperToTrade.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.mapper 2 | 3 | import com.kotlin.core.domain.entities.Trade 4 | import com.kotlin.core.domain.entities.mapper.Mapper 5 | import com.kotlin.server.repository.database.TradeStore 6 | 7 | class MapperToTrade : Mapper { 8 | 9 | override fun transform(origin: TradeStore): Trade { 10 | return Trade(trade_date = origin.trade_date, trade_id = origin.trade_id, 11 | trade_type = origin.trade_type, amount = origin.amount, rate = origin.rate, 12 | pair = origin.pair.get().id) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /consumer-server/repository-server/src/main/java/com/kotlin/server/repository/mapper/MapperToTradeStore.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository.mapper 2 | 3 | import com.googlecode.objectify.Ref 4 | import com.kotlin.core.domain.entities.Trade 5 | import com.kotlin.core.domain.entities.mapper.Mapper 6 | import com.kotlin.server.repository.database.PairStore 7 | import com.kotlin.server.repository.database.TradeStore 8 | 9 | class MapperToTradeStore : Mapper { 10 | override fun transform(origin: Trade): TradeStore { 11 | return TradeStore( 12 | trade_id = origin.trade_id, 13 | rate = origin.rate, 14 | amount = origin.amount, 15 | trade_date = origin.trade_date, 16 | trade_type = origin.trade_type, 17 | pair = Ref.create(PairStore(origin.pair))) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /consumer-server/repository-server/src/test/java/com/kotlin/server/repository/GetTradesRepositoryTest.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository 2 | 3 | import com.kotlin.core.domain.entities.Trade 4 | import com.kotlin.core.domain.entities.Trades 5 | import com.kotlin.server.repository.api.BxApi 6 | import com.kotlin.server.repository.database.DbInterface 7 | import com.nhaarman.mockitokotlin2.mock 8 | import com.nhaarman.mockitokotlin2.verify 9 | import com.nhaarman.mockitokotlin2.whenever 10 | import io.kotlintest.specs.BehaviorSpec 11 | 12 | class GetTradesRepositoryTest : BehaviorSpec({ 13 | given("GetTradesRepository Implementation") { 14 | 15 | `when`("Trades are coming from Remote Source") { 16 | val db = mock {} 17 | val api = mock {} 18 | val getTradesRepository = GetTradesRepositoryImpl(db, api) 19 | whenever(api.getTrades(1L)).thenReturn(Trades(listOf(Trade( 20 | rate = 1.0, 21 | amount = 2.0, 22 | pair = 1L, 23 | trade_date = "", 24 | trade_id = 10L, 25 | trade_type = "sell" 26 | )))) 27 | val trades = getTradesRepository.getTradesRemote(1L) 28 | then("Remote source is called ") { 29 | verify(api).getTrades(1L) 30 | 31 | } 32 | then("List of trades is returned ") { 33 | assert(trades.trades.size == 1) 34 | assert(trades.trades[0].pair == 1L) 35 | assert(trades.trades[0].trade_type == "sell") 36 | 37 | } 38 | } 39 | `when`("Trades are coming from Local/Persosted Source") { 40 | val db = mock {} 41 | val api = mock {} 42 | val getTradesRepository = GetTradesRepositoryImpl(db, api) 43 | whenever(db.queryTrades(1L)).thenReturn(listOf(Trade( 44 | rate = 1.0, 45 | amount = 2.0, 46 | pair = 1L, 47 | trade_date = "", 48 | trade_id = 10L, 49 | trade_type = "sell" 50 | ))) 51 | val trades = getTradesRepository.getTradesPersisted(1L) 52 | then("Remote source is called ") { 53 | verify(db).queryTrades(1L) 54 | 55 | } 56 | then("List of trades is returned ") { 57 | assert(trades.trades.size == 1) 58 | assert(trades.trades[0].pair == 1L) 59 | assert(trades.trades[0].trade_type == "sell") 60 | 61 | } 62 | } 63 | } 64 | }) 65 | -------------------------------------------------------------------------------- /consumer-server/repository-server/src/test/java/com/kotlin/server/repository/PairRepositoryImplTest.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository 2 | 3 | import com.kotlin.server.repository.api.BxApi 4 | import com.kotlin.server.repository.api.entities.PairInfo 5 | import com.kotlin.server.repository.api.entities.PairsInfo 6 | import com.kotlin.server.repository.database.DbInterface 7 | import com.kotlin.server.repository.database.PairStore 8 | import com.nhaarman.mockitokotlin2.argThat 9 | import com.nhaarman.mockitokotlin2.mock 10 | import com.nhaarman.mockitokotlin2.verify 11 | import com.nhaarman.mockitokotlin2.whenever 12 | import io.kotlintest.specs.BehaviorSpec 13 | 14 | class PairRepositoryImplTest : BehaviorSpec({ 15 | given("PairRepiository Implementation") { 16 | 17 | `when`("Retrieve pairs") { 18 | val db = mock {} 19 | val api = mock {} 20 | val pairRepository = PairRepositoryImpl(db, api) 21 | whenever(db.getPairs()).thenReturn(listOf(PairStore(id = 1L, 22 | rate = 0.1, 23 | volume = 0.1, 24 | primaryPairId = "BTC", 25 | secondaryPairId = "OMG"))) 26 | 27 | then("a list of Domain Pairs is returned ") { 28 | val pairs = pairRepository.getPairs() 29 | assert(pairs.get(0).id == 1L) 30 | // verify(pairRepository).syncPairs() 31 | } 32 | } 33 | `when`("Syncing pairs") { 34 | val db = mock {} 35 | val api = mock {} 36 | val pairRepository = PairRepositoryImpl(db, api) 37 | whenever(db.queryPairById(1L)).thenReturn(null) 38 | whenever(api.getPairsInfo()).thenReturn(PairsInfo(listOf(PairInfo( 39 | pairing_id = 1L, 40 | last_price = 0.1, 41 | volume_24_hours = 12.0, 42 | primary_currency = "BTC", 43 | secondary_currency = "OMG")))) 44 | then("a list of Dosasasasddsmain Pairs is returned ") { 45 | pairRepository.syncPairs() 46 | verify(db).savePair(argThat { 47 | this.id == 1L 48 | && this.primaryPairId == "BTC" 49 | && this.secondaryPairId == "OMG" 50 | && this.volume == 12.0 51 | && this.rate == 0.1 52 | }) 53 | 54 | } 55 | 56 | } 57 | `when`("Synceeeeing pairs") { 58 | val db = mock {} 59 | val api = mock {} 60 | val pairRepository = PairRepositoryImpl(db, api) 61 | whenever(db.queryPairById(1L)).thenReturn(PairStore( 62 | id = 1L, 63 | primaryPairId = "BTC", 64 | secondaryPairId = "OMG", 65 | rate = 0.1, 66 | volume = 0.1 67 | )) 68 | whenever(api.getPairsInfo()).thenReturn(PairsInfo(listOf(PairInfo( 69 | pairing_id = 1L, 70 | last_price = 1.0, 71 | volume_24_hours = 12.0, 72 | primary_currency = "BTC", 73 | secondary_currency = "OMG")))) 74 | then("a list of Dosasasdsdsdasddsmain Pairs is returned ") { 75 | pairRepository.syncPairs() 76 | verify(db).savePair(argThat { 77 | this.id == 1L 78 | && this.primaryPairId == "BTC" 79 | && this.secondaryPairId == "OMG" 80 | && this.volume == 12.0 81 | && this.rate == 1.0 82 | }) 83 | 84 | } 85 | 86 | } 87 | 88 | } 89 | }) 90 | -------------------------------------------------------------------------------- /consumer-server/repository-server/src/test/java/com/kotlin/server/repository/SyncPairsRepositoryTest.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.repository 2 | 3 | import com.kotlin.server.repository.api.BxApi 4 | import com.kotlin.server.repository.database.DbInterface 5 | import com.kotlin.server.repository.database.PairStore 6 | import com.nhaarman.mockitokotlin2.mock 7 | import com.nhaarman.mockitokotlin2.verify 8 | import com.nhaarman.mockitokotlin2.whenever 9 | import io.kotlintest.specs.BehaviorSpec 10 | 11 | 12 | class SyncPairsRepositoryTest : BehaviorSpec({ 13 | given("SyncPairRepository Implementation") { 14 | 15 | `when`("GET pAI pairs") { 16 | val db = mock {} 17 | val api = mock {} 18 | val syncRepository = SyncPairsRepositoryImpl(db, api) 19 | whenever(db.getPairs()).thenReturn(listOf(PairStore(id = 1L, 20 | rate = 0.1, 21 | volume = 0.1, 22 | primaryPairId = "BTC", 23 | secondaryPairId = "OMG"))) 24 | 25 | then("a list of Domain Pairs is returned ") { 26 | val markets = syncRepository.get() 27 | verify(db).getPairs() 28 | assert(markets[0].pairSymbol.id == 1L) 29 | assert(markets[0].pairSymbol.rate == 0.1) 30 | assert(markets[0].pairSymbol.primarySymbol == "BTC") 31 | assert(markets[0].pairSymbol.secondarySymbol == "OMG") 32 | 33 | } 34 | } 35 | } 36 | }) 37 | -------------------------------------------------------------------------------- /consumer-server/server/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /consumer-server/server/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.clientserver.buildProcess.CollectUnitTest.collectUnitTest 2 | 3 | plugins { 4 | id("war") 5 | id("com.google.cloud.tools.appengine-standard") 6 | id("kotlin") 7 | id("kotlin-kapt") 8 | } 9 | 10 | val test by tasks.getting(Test::class) { 11 | useJUnitPlatform { } 12 | } 13 | 14 | collectUnitTest() 15 | 16 | 17 | dependencies { 18 | api(project(":consumer-server:domain-server")) 19 | api(project(":consumer-server:repository-server")) 20 | implementation(project(":core-domain")) 21 | implementation(project(":core-network")) 22 | implementation(Dependencies.objectify) 23 | implementation(Dependencies.endpoints) 24 | implementation(Dependencies.javaxServlet) 25 | implementation(Dependencies.kotlin) 26 | implementation(Dependencies.javaxInject) 27 | implementation(Dependencies.daggerCompiler) 28 | implementation(Dependencies.retrofitConverter) 29 | implementation(Dependencies.retrofit) 30 | kapt(Dependencies.dagger) 31 | implementation(Dependencies.gson) 32 | testImplementation(Dependencies.mockito) 33 | } 34 | 35 | appengine { 36 | deploy { 37 | version = "2" 38 | projectId = "APPENGINE_CONFIG" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /consumer-server/server/src/main/java/com/kotlin/server/Init.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server 2 | 3 | import com.kotlin.core.domain.entities.usecases.GetPairs 4 | import javax.inject.Inject 5 | import javax.servlet.GenericServlet 6 | import javax.servlet.ServletRequest 7 | import javax.servlet.ServletResponse 8 | import com.kotlin.server.di.DaggerInjector 9 | 10 | 11 | class Init : GenericServlet() { 12 | init { 13 | DaggerInjector.builder().build().inject(this) 14 | } 15 | 16 | @Inject 17 | lateinit var getPairs: GetPairs 18 | 19 | override fun service(req: ServletRequest?, res: ServletResponse?) {} 20 | 21 | override fun init() { 22 | super.init() 23 | getPairs.sync() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /consumer-server/server/src/main/java/com/kotlin/server/cron/CronPairs.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.cron 2 | 3 | import com.kotlin.server.endpoint.EndPoint.Companion.BASE_URL 4 | import com.kotlin.server.endpoint.EndPoint.Companion.ENDPOINT 5 | import com.kotlin.server.endpoint.EndPoint.Companion.VERSION 6 | import java.net.HttpURLConnection 7 | import java.net.URL 8 | import javax.servlet.http.HttpServlet 9 | import javax.servlet.http.HttpServletRequest 10 | import javax.servlet.http.HttpServletResponse 11 | 12 | 13 | class CronPairs : HttpServlet() { 14 | 15 | override fun doGet(req: HttpServletRequest?, resp: HttpServletResponse?) { 16 | val url = URL(URL) 17 | val conn = url.openConnection() as HttpURLConnection 18 | conn.requestMethod = "GET" 19 | conn.inputStream 20 | conn.disconnect() 21 | } 22 | 23 | companion object { 24 | const val URL = "$BASE_URL/$ENDPOINT/$VERSION/syncPairs/" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /consumer-server/server/src/main/java/com/kotlin/server/cron/CronTrades.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.cron 2 | 3 | import com.kotlin.server.endpoint.EndPoint.Companion.BASE_URL 4 | import com.kotlin.server.endpoint.EndPoint.Companion.ENDPOINT 5 | import com.kotlin.server.endpoint.EndPoint.Companion.VERSION 6 | import java.net.HttpURLConnection 7 | import java.net.URL 8 | import javax.servlet.http.HttpServlet 9 | import javax.servlet.http.HttpServletRequest 10 | import javax.servlet.http.HttpServletResponse 11 | 12 | 13 | class CronTrades : HttpServlet() { 14 | 15 | override fun doGet(req: HttpServletRequest?, resp: HttpServletResponse?) { 16 | val url = URL(URL) 17 | val conn = url.openConnection() as HttpURLConnection 18 | conn.requestMethod = "GET" 19 | conn.inputStream 20 | conn.disconnect() 21 | } 22 | 23 | companion object { 24 | const val URL = "$BASE_URL/$ENDPOINT/$VERSION/syncTrades/" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /consumer-server/server/src/main/java/com/kotlin/server/di/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.di 2 | 3 | 4 | import com.kotlin.server.repository.di.RepositoryModule 5 | import com.kotlin.server.repository.domain.di.DomainModule 6 | import dagger.Module 7 | 8 | @Module(includes = arrayOf( 9 | RepositoryModule::class, 10 | DomainModule::class)) 11 | class AppModule { 12 | } 13 | -------------------------------------------------------------------------------- /consumer-server/server/src/main/java/com/kotlin/server/di/Injector.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.di 2 | 3 | import com.kotlin.server.Init 4 | import com.kotlin.server.endpoint.EndPoint 5 | import com.kotlin.server.repository.di.RepositoryModule 6 | import com.kotlin.server.repository.domain.di.DomainModule 7 | import dagger.Component 8 | import javax.inject.Singleton 9 | 10 | 11 | @Singleton 12 | @Component(modules = (arrayOf( 13 | RepositoryModule::class, 14 | DomainModule::class))) 15 | interface Injector { 16 | 17 | fun inject(endPoint: EndPoint) 18 | 19 | fun inject(init: Init) 20 | } 21 | -------------------------------------------------------------------------------- /consumer-server/server/src/main/java/com/kotlin/server/endpoint/EndPoint.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.endpoint 2 | 3 | import com.kotlin.server.service.GetTradesService 4 | import com.kotlin.server.service.SyncTradesService 5 | import com.google.api.server.spi.config.Api 6 | import com.google.api.server.spi.config.ApiMethod 7 | import javax.inject.Inject 8 | import com.kotlin.server.di.DaggerInjector 9 | import com.kotlin.server.endpoint.EndPoint.Companion.ENDPOINT 10 | import com.kotlin.server.endpoint.EndPoint.Companion.VERSION 11 | import com.kotlin.server.service.GetMarketService 12 | import com.kotlin.server.service.SyncPairsService 13 | import javax.inject.Named 14 | 15 | @Api(name = ENDPOINT, version = VERSION) 16 | class EndPoint { 17 | init { 18 | DaggerInjector.builder().build().inject(this) 19 | } 20 | 21 | @Inject 22 | lateinit var syncService: SyncTradesService 23 | 24 | @Inject 25 | lateinit var syncPairs: SyncPairsService 26 | 27 | @Inject 28 | lateinit var getTradesService: GetTradesService 29 | 30 | @Inject 31 | lateinit var getMarketService: GetMarketService 32 | 33 | @ApiMethod(name = "syncTrades", 34 | httpMethod = ApiMethod.HttpMethod.GET, 35 | path = "syncTrades/") 36 | fun sync() = syncService.sync() 37 | 38 | @ApiMethod(name = "syncPairs", 39 | httpMethod = ApiMethod.HttpMethod.GET, 40 | path = "syncPairs/") 41 | fun sync2() = syncPairs.sync() 42 | 43 | 44 | @ApiMethod(name = "trades", 45 | httpMethod = ApiMethod.HttpMethod.GET, 46 | path = "trades/{pair}") 47 | fun getTrades(@Named("pair") id: String) = getTradesService.getTrades(id) 48 | 49 | @ApiMethod(name = "market", 50 | httpMethod = ApiMethod.HttpMethod.GET, 51 | path = "market/") 52 | fun getMarket() = getMarketService.getMarket() 53 | 54 | companion object { 55 | const val BASE_URL = "YOUR_GAE_ENDPOINT_PROJECT" 56 | const val VERSION = "v2" 57 | const val ENDPOINT = "api" 58 | 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /consumer-server/server/src/main/java/com/kotlin/server/service/GetMarketService.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.service 2 | 3 | import com.kotlin.core.domain.entities.Market 4 | import com.kotlin.core.domain.entities.usecases.GetMarket 5 | import javax.inject.Inject 6 | 7 | 8 | class GetMarketService @Inject constructor(private val getMarket: GetMarket) { 9 | 10 | fun getMarket(): List = getMarket.get() 11 | } 12 | -------------------------------------------------------------------------------- /consumer-server/server/src/main/java/com/kotlin/server/service/GetTradesService.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.service 2 | 3 | import com.kotlin.core.domain.entities.Trades 4 | import com.kotlin.core.domain.entities.usecases.GetTrades 5 | import javax.inject.Inject 6 | 7 | 8 | class GetTradesService @Inject constructor(private val getTrades: GetTrades) { 9 | 10 | fun getTrades(pair: String): Trades = getTrades.getTrades(pair.toLong()) 11 | } 12 | -------------------------------------------------------------------------------- /consumer-server/server/src/main/java/com/kotlin/server/service/SyncPairsService.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.service 2 | 3 | import com.kotlin.core.domain.entities.usecases.GetPairs 4 | import javax.inject.Inject 5 | 6 | 7 | class SyncPairsService @Inject constructor(private val getPairs: GetPairs) { 8 | 9 | fun sync() = getPairs.sync() 10 | 11 | } -------------------------------------------------------------------------------- /consumer-server/server/src/main/java/com/kotlin/server/service/SyncTradesService.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.server.service 2 | 3 | import com.kotlin.core.domain.entities.usecases.SyncTrades 4 | import javax.inject.Inject 5 | 6 | 7 | class SyncTradesService @Inject constructor(private val syncTrades: SyncTrades) { 8 | 9 | fun sync(): Unit = syncTrades.syncTrades() 10 | 11 | } -------------------------------------------------------------------------------- /consumer-server/server/src/main/webapp/WEB-INF/appengine-web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | YOUR_GAE_PROJECT_IDr 4 | 2 5 | true 6 | java8 7 | 8 | -------------------------------------------------------------------------------- /consumer-server/server/src/main/webapp/WEB-INF/cron.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /syncTrades 5 | Update every 30 minutes 6 | every 30 minutes 7 | 8 | 9 | /syncPairs 10 | Update every 20 minutes 11 | every 20 minutes 12 | 13 | -------------------------------------------------------------------------------- /consumer-server/server/src/main/webapp/WEB-INF/datastore-indexes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /consumer-server/server/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | EndpointsServlet 7 | com.google.api.server.spi.EndpointsServlet 8 | 9 | services 10 | com.kotlin.server.endpoint.EndPoint 11 | 12 | 13 | restricted 14 | false 15 | 16 | 17 | 18 | CronServlet 19 | com.kotlin.server.cron.CronTrades 20 | 21 | 22 | CronServlet2 23 | com.kotlin.server.cron.CronPairs 24 | 25 | 26 | 27 | Init 28 | com.kotlin.server.Init 29 | 1 30 | 31 | 32 | 33 | EndpointsServlet 34 | /* 35 | 36 | 37 | CronServlet 38 | /syncTrades 39 | 40 | 41 | CronServlet2 42 | /syncPairs 43 | 44 | 45 | ObjectifyFilter 46 | com.googlecode.objectify.ObjectifyFilter 47 | 48 | 49 | ObjectifyFilter 50 | /* 51 | REQUEST 52 | INCLUDE 53 | FORWARD 54 | 55 | 56 | -------------------------------------------------------------------------------- /core-domain/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /core-domain/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.clientserver.buildProcess.CollectUnitTest.collectUnitTest 2 | 3 | plugins { 4 | id("kotlin") 5 | } 6 | 7 | val test by tasks.getting(Test::class) { 8 | useJUnitPlatform { } 9 | } 10 | 11 | collectUnitTest() 12 | 13 | dependencies { 14 | implementation(Dependencies.kotlin) 15 | testImplementation(Dependencies.kotlinTest) 16 | testImplementation(Dependencies.mockitoKotlin) 17 | } 18 | -------------------------------------------------------------------------------- /core-domain/src/main/java/com/kotlin/core/domain/entities/Market.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.core.domain.entities 2 | 3 | data class Market( 4 | val pairSymbol: PairSymbol, 5 | val trades: Trades 6 | ) -------------------------------------------------------------------------------- /core-domain/src/main/java/com/kotlin/core/domain/entities/PairSymbol.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.core.domain.entities 2 | 3 | data class PairSymbol( 4 | val id: Long, 5 | val primarySymbol: String, 6 | val secondarySymbol: String, 7 | val rate: Double, 8 | val volume: Double 9 | ) -------------------------------------------------------------------------------- /core-domain/src/main/java/com/kotlin/core/domain/entities/Trade.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.core.domain.entities 2 | 3 | 4 | data class Trade( 5 | val trade_id: Long = 0, 6 | val rate: Double = 0.0, 7 | val amount: Double = 0.0, 8 | val trade_date: String = "", 9 | val trade_type: String = "", 10 | val pair: Long = 1 11 | ) -------------------------------------------------------------------------------- /core-domain/src/main/java/com/kotlin/core/domain/entities/Trades.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.core.domain.entities 2 | 3 | data class Trades(val trades: List) -------------------------------------------------------------------------------- /core-domain/src/main/java/com/kotlin/core/domain/entities/mapper/Mapper.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.core.domain.entities.mapper 2 | 3 | interface Mapper { 4 | fun transform(origin: T): R 5 | } 6 | -------------------------------------------------------------------------------- /core-domain/src/main/java/com/kotlin/core/domain/entities/repository/PairsRepository.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.core.domain.entities.repository 2 | 3 | import com.kotlin.core.domain.entities.PairSymbol 4 | 5 | interface PairsRepository { 6 | fun getPairs(): List 7 | 8 | fun syncPairs(): List 9 | } 10 | -------------------------------------------------------------------------------- /core-domain/src/main/java/com/kotlin/core/domain/entities/repository/SyncRepository.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.core.domain.entities.repository 2 | 3 | import com.kotlin.core.domain.entities.Market 4 | 5 | interface SyncRepository { 6 | fun sync() 7 | 8 | fun get(): List 9 | } -------------------------------------------------------------------------------- /core-domain/src/main/java/com/kotlin/core/domain/entities/repository/TradesRepository.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.core.domain.entities.repository 2 | 3 | import com.kotlin.core.domain.entities.Trade 4 | import com.kotlin.core.domain.entities.Trades 5 | 6 | interface TradesRepository { 7 | fun getTradesRemote(id: Long): Trades 8 | 9 | fun getTradesPersisted(id: Long): Trades 10 | 11 | fun save(trade: Trade) 12 | } -------------------------------------------------------------------------------- /core-domain/src/main/java/com/kotlin/core/domain/entities/usecases/GetMarket.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.core.domain.entities.usecases 2 | 3 | import com.kotlin.core.domain.entities.Market 4 | 5 | interface GetMarket { 6 | fun get(): List 7 | } -------------------------------------------------------------------------------- /core-domain/src/main/java/com/kotlin/core/domain/entities/usecases/GetPairs.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.core.domain.entities.usecases 2 | 3 | import com.kotlin.core.domain.entities.PairSymbol 4 | 5 | interface GetPairs { 6 | fun get(): List 7 | 8 | fun sync(): List 9 | } -------------------------------------------------------------------------------- /core-domain/src/main/java/com/kotlin/core/domain/entities/usecases/GetTrades.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.core.domain.entities.usecases 2 | 3 | import com.kotlin.core.domain.entities.Trades 4 | 5 | interface GetTrades { 6 | fun getTrades(id: Long): Trades 7 | 8 | fun refreshTrades(id: Long): Trades 9 | } -------------------------------------------------------------------------------- /core-domain/src/main/java/com/kotlin/core/domain/entities/usecases/SyncTrades.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.core.domain.entities.usecases 2 | 3 | 4 | interface SyncTrades { 5 | fun syncTrades() 6 | } -------------------------------------------------------------------------------- /core-domain/src/main/java/com/kotlin/core/domain/entities/usecases/impl/GetPairsImpl.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.core.domain.entities.usecases.impl 2 | 3 | import com.kotlin.core.domain.entities.PairSymbol 4 | import com.kotlin.core.domain.entities.repository.PairsRepository 5 | import com.kotlin.core.domain.entities.usecases.GetPairs 6 | 7 | class GetPairsImpl(private val pairRepository: PairsRepository) : GetPairs { 8 | 9 | override fun get(): List { 10 | val list = pairRepository.getPairs() 11 | return if (list.isEmpty()) { 12 | sync() 13 | } else { 14 | pairRepository.getPairs() 15 | } 16 | } 17 | 18 | override fun sync(): List = pairRepository.syncPairs() 19 | } -------------------------------------------------------------------------------- /core-domain/src/main/java/com/kotlin/core/domain/entities/usecases/impl/GetTradesImpl.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.core.domain.entities.usecases.impl 2 | 3 | 4 | import com.kotlin.core.domain.entities.Trades 5 | import com.kotlin.core.domain.entities.repository.TradesRepository 6 | import com.kotlin.core.domain.entities.usecases.GetTrades 7 | 8 | 9 | class GetTradesImpl(private val tradesRepository: TradesRepository) 10 | : GetTrades { 11 | 12 | override fun refreshTrades(id: Long): Trades { 13 | tradesRepository.getTradesRemote(id).trades.map { 14 | tradesRepository.save(it) 15 | } 16 | return tradesRepository.getTradesPersisted(id) 17 | } 18 | 19 | 20 | override fun getTrades(id: Long): Trades { 21 | val list = tradesRepository.getTradesPersisted(id) 22 | return if (list.trades.isEmpty()) { 23 | refreshTrades(id) 24 | } else { 25 | list 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /core-domain/src/test/java/com/kotlin/core/domain/entities/usecases/impl/GetPairsImplTest.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.core.domain.entities.usecases.impl; 2 | 3 | import com.kotlin.core.domain.entities.PairSymbol 4 | import com.kotlin.core.domain.entities.repository.PairsRepository 5 | import com.nhaarman.mockitokotlin2.mock 6 | import com.nhaarman.mockitokotlin2.never 7 | import com.nhaarman.mockitokotlin2.verify 8 | import com.nhaarman.mockitokotlin2.whenever 9 | import io.kotlintest.specs.BehaviorSpec 10 | 11 | class GetPairsImplTest : BehaviorSpec({ 12 | given("GetPairs Implementation") { 13 | 14 | `when`("There are no Pairs ") { 15 | val pairRepository = mock { } 16 | val getPairs = GetPairsImpl(pairRepository) 17 | whenever(pairRepository.getPairs()).thenReturn(emptyList()) 18 | getPairs.get() 19 | then("I should sync Pairs") { 20 | verify(pairRepository).syncPairs() 21 | } 22 | } 23 | `when`("There are Pairs") { 24 | val pairRepository = mock { } 25 | val getPairs = GetPairsImpl(pairRepository) 26 | whenever(pairRepository.getPairs()).thenReturn(listOf(PairSymbol(1, "BTC", "EUR", 27 | 0.0, 0.0))) 28 | getPairs.get() 29 | then("I shouldn't sync Pairs") { 30 | verify(pairRepository, never()).syncPairs() 31 | } 32 | } 33 | } 34 | }) -------------------------------------------------------------------------------- /core-network/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /core-network/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kotlin") 3 | } 4 | 5 | dependencies { 6 | implementation(project(":core-domain")) 7 | implementation(Dependencies.kotlin) 8 | api(Dependencies.retrofitConverter) 9 | api(Dependencies.retrofit) 10 | api(Dependencies.gson) 11 | } 12 | -------------------------------------------------------------------------------- /core-network/src/main/java/com/kotlin/core/network/TradesDeserializer.kt: -------------------------------------------------------------------------------- 1 | package com.kotlin.core.network 2 | 3 | import com.google.gson.* 4 | import com.kotlin.core.domain.entities.Trade 5 | import com.kotlin.core.domain.entities.Trades 6 | import java.lang.reflect.Type 7 | 8 | class TradesDeserializer : JsonDeserializer { 9 | 10 | companion object { 11 | const val TRADES_ENTITY = "trades" 12 | } 13 | 14 | private val gson = Gson() 15 | 16 | @Throws(JsonParseException::class) 17 | override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Trades { 18 | val trades = mutableListOf() 19 | json.asJsonObject.get(TRADES_ENTITY).asJsonArray.forEach { 20 | trades.add(gson.fromJson(it, Trade::class.java)) 21 | } 22 | return Trades(trades) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | android.useAndroidX=true 15 | android.enableJetifier=true 16 | android.enableR8=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdsap/Kotlin-Client-Server/9f07c3b5b94c8abe142bdceef70d4c4ad486cfc5/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Apr 12 16:52:16 ICT 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | include(":core-domain", ":core-network") 2 | include(":consumer-android:app", ":consumer-android:repository-android") 3 | include(":consumer-server:server", ":consumer-server:domain-server", ":consumer-server:repository-server") 4 | --------------------------------------------------------------------------------