diff options
author | Torsten Grote <t@grobox.de> | 2020-07-20 16:37:46 -0300 |
---|---|---|
committer | Torsten Grote <t@grobox.de> | 2020-07-20 16:37:46 -0300 |
commit | c9fb036798fc533a07b4b75386b51151b31f8be0 (patch) | |
tree | 383b60c8a1f6acbba5e01c8634e45682ce50ddcf | |
parent | de69768ac75e1608601751bd0a187e6a687dbdd2 (diff) | |
download | taler-android-c9fb036798fc533a07b4b75386b51151b31f8be0.tar.gz taler-android-c9fb036798fc533a07b4b75386b51151b31f8be0.tar.bz2 taler-android-c9fb036798fc533a07b4b75386b51151b31f8be0.zip |
[pos] create merchant-lib and move first v1 API endpoint there
-rw-r--r-- | .gitlab-ci.yml | 1 | ||||
-rw-r--r-- | .idea/gradle.xml | 1 | ||||
-rw-r--r-- | merchant-lib/.gitignore | 1 | ||||
-rw-r--r-- | merchant-lib/.gitlab-ci.yml | 11 | ||||
-rw-r--r-- | merchant-lib/build.gradle | 58 | ||||
-rw-r--r-- | merchant-lib/consumer-rules.pro | 0 | ||||
-rw-r--r-- | merchant-lib/proguard-rules.pro | 21 | ||||
-rw-r--r-- | merchant-lib/src/main/AndroidManifest.xml | 23 | ||||
-rw-r--r-- | merchant-lib/src/main/java/net/taler/merchantlib/ConfigResponse.kt | 34 | ||||
-rw-r--r-- | merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt | 43 | ||||
-rw-r--r-- | merchant-lib/src/test/java/net/taler/merchantlib/MerchantApiTest.kt | 43 | ||||
-rw-r--r-- | merchant-lib/src/test/java/net/taler/merchantlib/MockHttpClient.kt | 58 | ||||
-rw-r--r-- | merchant-terminal/build.gradle | 8 | ||||
-rw-r--r-- | merchant-terminal/src/main/java/net/taler/merchantpos/MainViewModel.kt | 4 | ||||
-rw-r--r-- | merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt | 31 | ||||
-rw-r--r-- | merchant-terminal/src/main/res/values/strings.xml | 1 | ||||
-rw-r--r-- | settings.gradle | 1 |
17 files changed, 325 insertions, 14 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index da3611e..48f1aec 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -11,6 +11,7 @@ stages: include: - local: 'cashier/.gitlab-ci.yml' + - local: 'merchant-lib/.gitlab-ci.yml' - local: 'merchant-terminal/.gitlab-ci.yml' - local: 'taler-kotlin-common/.gitlab-ci.yml' - local: 'wallet/.gitlab-ci.yml' diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 697ff36..581abbf 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -12,6 +12,7 @@ <option value="$PROJECT_DIR$" /> <option value="$PROJECT_DIR$/anastasis-ui" /> <option value="$PROJECT_DIR$/cashier" /> + <option value="$PROJECT_DIR$/merchant-lib" /> <option value="$PROJECT_DIR$/merchant-terminal" /> <option value="$PROJECT_DIR$/taler-kotlin-common" /> <option value="$PROJECT_DIR$/wallet" /> diff --git a/merchant-lib/.gitignore b/merchant-lib/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/merchant-lib/.gitignore @@ -0,0 +1 @@ +/build
\ No newline at end of file diff --git a/merchant-lib/.gitlab-ci.yml b/merchant-lib/.gitlab-ci.yml new file mode 100644 index 0000000..62a7516 --- /dev/null +++ b/merchant-lib/.gitlab-ci.yml @@ -0,0 +1,11 @@ +merchant_lib_test: + stage: test + only: + changes: + - merchant-lib/**/* + - build.gradle + script: ./gradlew :merchant-lib:check + artifacts: + paths: + - merchant-lib/build/reports/lint-results.html + expire_in: 1 week diff --git a/merchant-lib/build.gradle b/merchant-lib/build.gradle new file mode 100644 index 0000000..08da35d --- /dev/null +++ b/merchant-lib/build.gradle @@ -0,0 +1,58 @@ +/* + * This file is part of GNU Taler + * (C) 2020 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +plugins { + id 'org.jetbrains.kotlin.plugin.serialization' version "$kotlin_version" +} + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' + +android { + compileSdkVersion 29 + //noinspection GradleDependency + buildToolsVersion "$build_tools_version" + + defaultConfig { + minSdkVersion 21 + targetSdkVersion 29 + versionCode 1 + versionName "0.1" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + + def ktor_version = "1.3.2" + implementation "io.ktor:ktor-client:$ktor_version" + implementation "io.ktor:ktor-client-okhttp:$ktor_version" + implementation "io.ktor:ktor-client-serialization-jvm:$ktor_version" + + testImplementation 'junit:junit:4.13' + testApi "io.ktor:ktor-client-mock-jvm:$ktor_version" +} diff --git a/merchant-lib/consumer-rules.pro b/merchant-lib/consumer-rules.pro new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/merchant-lib/consumer-rules.pro diff --git a/merchant-lib/proguard-rules.pro b/merchant-lib/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/merchant-lib/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile
\ No newline at end of file diff --git a/merchant-lib/src/main/AndroidManifest.xml b/merchant-lib/src/main/AndroidManifest.xml new file mode 100644 index 0000000..7318c07 --- /dev/null +++ b/merchant-lib/src/main/AndroidManifest.xml @@ -0,0 +1,23 @@ +<!-- + ~ This file is part of GNU Taler + ~ (C) 2020 Taler Systems S.A. + ~ + ~ GNU Taler is free software; you can redistribute it and/or modify it under the + ~ terms of the GNU General Public License as published by the Free Software + ~ Foundation; either version 3, or (at your option) any later version. + ~ + ~ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + ~ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + ~ A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License along with + ~ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="net.taler.merchantlib"> + + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> + <uses-permission android:name="android.permission.INTERNET" /> + +</manifest> diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/ConfigResponse.kt b/merchant-lib/src/main/java/net/taler/merchantlib/ConfigResponse.kt new file mode 100644 index 0000000..49164e6 --- /dev/null +++ b/merchant-lib/src/main/java/net/taler/merchantlib/ConfigResponse.kt @@ -0,0 +1,34 @@ +/* + * This file is part of GNU Taler + * (C) 2020 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +package net.taler.merchantlib + +import kotlinx.serialization.Serializable + +@Serializable +data class ConfigResponse( + /** + * libtool-style representation of the Merchant protocol version, see + * https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning + * The format is "current:revision:age". + */ + val version: String, + + /** + Currency supported by this backend. + */ + val currency: String +) diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt b/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt new file mode 100644 index 0000000..656b093 --- /dev/null +++ b/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt @@ -0,0 +1,43 @@ +/* + * This file is part of GNU Taler + * (C) 2020 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +package net.taler.merchantlib + +import io.ktor.client.HttpClient +import io.ktor.client.engine.okhttp.OkHttp +import io.ktor.client.features.json.JsonFeature +import io.ktor.client.features.json.serializer.KotlinxSerializer +import io.ktor.client.request.get +import io.ktor.client.request.header +import io.ktor.http.HttpHeaders.Authorization + +class MerchantApi(private val httpClient: HttpClient) { + + constructor() : this(getDefaultHttpClient()) + + suspend fun getConfig(baseUrl: String, apiKey: String = "sandbox"): ConfigResponse { + return httpClient.get("$baseUrl/config") { + header(Authorization, "ApiKey $apiKey") + } + } + +} + +private fun getDefaultHttpClient(): HttpClient = HttpClient(OkHttp) { + install(JsonFeature) { + serializer = KotlinxSerializer() + } +} diff --git a/merchant-lib/src/test/java/net/taler/merchantlib/MerchantApiTest.kt b/merchant-lib/src/test/java/net/taler/merchantlib/MerchantApiTest.kt new file mode 100644 index 0000000..6b2199b --- /dev/null +++ b/merchant-lib/src/test/java/net/taler/merchantlib/MerchantApiTest.kt @@ -0,0 +1,43 @@ +/* + * This file is part of GNU Taler + * (C) 2020 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +package net.taler.merchantlib + +import kotlinx.coroutines.runBlocking +import net.taler.merchantlib.MockHttpClient.giveJsonResponse +import net.taler.merchantlib.MockHttpClient.httpClient +import org.junit.Assert.assertEquals +import org.junit.Test + +class MerchantApiTest { + + private val api = MerchantApi(httpClient) + + @Test + fun testGetConfig() = runBlocking { + httpClient.giveJsonResponse("https://backend.int.taler.net/config") { + """ + { + "currency": "INTKUDOS", + "version": "0:0:0" + } + """.trimIndent() + } + val response = api.getConfig("https://backend.int.taler.net") + assertEquals(ConfigResponse("0:0:0", "INTKUDOS"), response) + } + +} diff --git a/merchant-lib/src/test/java/net/taler/merchantlib/MockHttpClient.kt b/merchant-lib/src/test/java/net/taler/merchantlib/MockHttpClient.kt new file mode 100644 index 0000000..076b77e --- /dev/null +++ b/merchant-lib/src/test/java/net/taler/merchantlib/MockHttpClient.kt @@ -0,0 +1,58 @@ +/* + * This file is part of GNU Taler + * (C) 2020 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +package net.taler.merchantlib + +import io.ktor.client.HttpClient +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.MockEngineConfig +import io.ktor.client.engine.mock.respond +import io.ktor.client.features.json.JsonFeature +import io.ktor.client.features.json.serializer.KotlinxSerializer +import io.ktor.http.ContentType.Application.Json +import io.ktor.http.Url +import io.ktor.http.fullPath +import io.ktor.http.headersOf +import io.ktor.http.hostWithPort + +object MockHttpClient { + + val httpClient = HttpClient(MockEngine) { + install(JsonFeature) { + serializer = KotlinxSerializer() + } + engine { + addHandler { error("No response handler set") } + } + } + + fun HttpClient.giveJsonResponse(url: String, jsonProducer: () -> String) { + val httpConfig = engineConfig as MockEngineConfig + httpConfig.requestHandlers.removeAt(0) + httpConfig.requestHandlers.add { request -> + if (request.url.fullUrl == url) { + val headers = headersOf("Content-Type" to listOf(Json.toString())) + respond(jsonProducer(), headers = headers) + } else { + error("Unexpected URL: ${request.url.fullUrl}") + } + } + } + + private val Url.hostWithPortIfRequired: String get() = if (port == protocol.defaultPort) host else hostWithPort + private val Url.fullUrl: String get() = "${protocol.name}://$hostWithPortIfRequired$fullPath" + +} diff --git a/merchant-terminal/build.gradle b/merchant-terminal/build.gradle index 8a7eac7..2ba1a66 100644 --- a/merchant-terminal/build.gradle +++ b/merchant-terminal/build.gradle @@ -46,10 +46,16 @@ android { // https://github.com/material-components/material-components-android/issues/504 ignore "WrongConstant" } + + packagingOptions { + exclude 'META-INF/common.kotlin_module' + exclude 'META-INF/*.kotlin_module' + } } dependencies { implementation project(":taler-kotlin-common") + implementation project(":merchant-lib") implementation 'com.google.android.material:material:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' @@ -63,7 +69,7 @@ dependencies { // HTTP Requests implementation 'com.android.volley:volley:1.1.1' - implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" + implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" // JSON parsing and serialization implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.10.2" diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/MainViewModel.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/MainViewModel.kt index 3fe472d..2dd2c24 100644 --- a/merchant-terminal/src/main/java/net/taler/merchantpos/MainViewModel.kt +++ b/merchant-terminal/src/main/java/net/taler/merchantpos/MainViewModel.kt @@ -23,6 +23,7 @@ import com.android.volley.toolbox.Volley import com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.KotlinModule +import net.taler.merchantlib.MerchantApi import net.taler.merchantpos.config.ConfigManager import net.taler.merchantpos.history.HistoryManager import net.taler.merchantpos.history.RefundManager @@ -31,13 +32,14 @@ import net.taler.merchantpos.payment.PaymentManager class MainViewModel(app: Application) : AndroidViewModel(app) { + private val api = MerchantApi() private val mapper = ObjectMapper() .registerModule(KotlinModule()) .configure(FAIL_ON_UNKNOWN_PROPERTIES, false) private val queue = Volley.newRequestQueue(app) val orderManager = OrderManager(app, mapper) - val configManager = ConfigManager(app, viewModelScope, mapper, queue).apply { + val configManager = ConfigManager(app, viewModelScope, api, mapper, queue).apply { addConfigurationReceiver(orderManager) } val paymentManager = PaymentManager(configManager, queue, mapper) diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt index 171cf28..eee7905 100644 --- a/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt +++ b/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt @@ -34,10 +34,14 @@ import com.fasterxml.jackson.module.kotlin.readValue import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import net.taler.merchantlib.ConfigResponse +import net.taler.merchantlib.MerchantApi import net.taler.merchantpos.LogErrorListener import net.taler.merchantpos.R import org.json.JSONObject +private const val VERSION = "0:0:0" + private const val SETTINGS_NAME = "taler-merchant-terminal" private const val SETTINGS_CONFIG_URL = "configUrl" @@ -60,6 +64,7 @@ interface ConfigurationReceiver { class ConfigManager( private val context: Context, private val scope: CoroutineScope, + private val api: MerchantApi, private val mapper: ObjectMapper, private val queue: RequestQueue ) { @@ -114,25 +119,27 @@ class ConfigManager( return } - val params = mapOf("instance" to merchantConfig.instance) - val req = MerchantRequest(GET, merchantConfig, "config", params, null, - Listener { onMerchantConfigReceived(config, json, merchantConfig, it) }, - LogErrorListener { onNetworkError(it) } - ) - queue.add(req) + scope.launch(Dispatchers.IO) { + val configResponse = api.getConfig(merchantConfig.baseUrl, merchantConfig.apiKey) + onMerchantConfigReceived(config, json, merchantConfig, configResponse) + } } private fun onMerchantConfigReceived( newConfig: Config?, configJson: JSONObject, merchantConfig: MerchantConfig, - json: JSONObject + configResponse: ConfigResponse ) = scope.launch(Dispatchers.Default) { - val currency = json.getString("currency") - + // TODO do real matching + if (VERSION != configResponse.version) { + val str = context.getString(R.string.config_error_version) + mConfigUpdateResult.postValue(ConfigUpdateResult.Error(str)) + return@launch + } for (receiver in configurationReceivers) { val result = try { - receiver.onConfigurationReceived(configJson, currency) + receiver.onConfigurationReceived(configJson, configResponse.currency) } catch (e: Exception) { Log.e(TAG, "Error handling configuration by ${receiver::class.java.simpleName}", e) context.getString(R.string.config_error_unknown) @@ -146,8 +153,8 @@ class ConfigManager( config = it saveConfig(it) } - this@ConfigManager.merchantConfig = merchantConfig.copy(currency = currency) - mConfigUpdateResult.postValue(ConfigUpdateResult.Success(currency)) + this@ConfigManager.merchantConfig = merchantConfig.copy(currency = configResponse.currency) + mConfigUpdateResult.postValue(ConfigUpdateResult.Success(configResponse.currency)) } fun forgetPassword() { diff --git a/merchant-terminal/src/main/res/values/strings.xml b/merchant-terminal/src/main/res/values/strings.xml index b3dcd8d..931f31c 100644 --- a/merchant-terminal/src/main/res/values/strings.xml +++ b/merchant-terminal/src/main/res/values/strings.xml @@ -22,6 +22,7 @@ <string name="config_password">Password</string> <string name="config_ok">Fetch configuration</string> <string name="config_auth_error">Error: Invalid username or password</string> + <string name="config_error_version">Error: Incompatible backend version</string> <string name="config_error_network">Error: Could not connect to configuration server</string> <string name="config_error_category">Error: No valid product category found</string> <string name="config_error_malformed">Error: The configuration JSON is malformed</string> diff --git a/settings.gradle b/settings.gradle index b45a276..14d898d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,4 @@ include ':cashier', ':merchant-terminal', ':wallet' include ':taler-kotlin-common' +include ':merchant-lib' include ':anastasis-ui' |