diff options
author | Torsten Grote <t@grobox.de> | 2020-07-21 10:44:32 -0300 |
---|---|---|
committer | Torsten Grote <t@grobox.de> | 2020-07-21 15:06:53 -0300 |
commit | 5d3d85fa7190a70eea8fa67866c343005b9922b0 (patch) | |
tree | 784e53d20b17f63e5def8f0704e7539f338ed637 | |
parent | c9fb036798fc533a07b4b75386b51151b31f8be0 (diff) | |
download | taler-android-5d3d85fa7190a70eea8fa67866c343005b9922b0.tar.gz taler-android-5d3d85fa7190a70eea8fa67866c343005b9922b0.tar.bz2 taler-android-5d3d85fa7190a70eea8fa67866c343005b9922b0.zip |
[pos] compare version from backend and show error if incompatible
6 files changed, 172 insertions, 7 deletions
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 eee7905..a7aff18 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,14 +34,14 @@ import com.fasterxml.jackson.module.kotlin.readValue import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import net.taler.common.Version +import net.taler.common.getIncompatibleStringOrNull 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" @@ -52,6 +52,8 @@ internal const val CONFIG_URL_DEMO = "https://docs.taler.net/_static/sample-pos- internal const val CONFIG_USERNAME_DEMO = "" internal const val CONFIG_PASSWORD_DEMO = "" +private val VERSION = Version(1, 0, 0) + private val TAG = ConfigManager::class.java.simpleName interface ConfigurationReceiver { @@ -131,10 +133,9 @@ class ConfigManager( merchantConfig: MerchantConfig, configResponse: ConfigResponse ) = scope.launch(Dispatchers.Default) { - // TODO do real matching - if (VERSION != configResponse.version) { - val str = context.getString(R.string.config_error_version) - mConfigUpdateResult.postValue(ConfigUpdateResult.Error(str)) + val versionIncompatible = VERSION.getIncompatibleStringOrNull(context, configResponse.version) + if (versionIncompatible != null) { + mConfigUpdateResult.postValue(ConfigUpdateResult.Error(versionIncompatible)) return@launch } for (receiver in configurationReceivers) { diff --git a/merchant-terminal/src/main/res/values/strings.xml b/merchant-terminal/src/main/res/values/strings.xml index 931f31c..b3dcd8d 100644 --- a/merchant-terminal/src/main/res/values/strings.xml +++ b/merchant-terminal/src/main/res/values/strings.xml @@ -22,7 +22,6 @@ <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/taler-kotlin-common/src/main/java/net/taler/common/AndroidUtils.kt b/taler-kotlin-common/src/main/java/net/taler/common/AndroidUtils.kt index ba6ee1c..b46f306 100644 --- a/taler-kotlin-common/src/main/java/net/taler/common/AndroidUtils.kt +++ b/taler-kotlin-common/src/main/java/net/taler/common/AndroidUtils.kt @@ -112,3 +112,12 @@ fun Long.toShortDate(context: Context): CharSequence { val flags = FORMAT_SHOW_DATE or FORMAT_SHOW_YEAR or FORMAT_ABBREV_ALL return formatDateTime(context, this, flags) } + +fun Version.getIncompatibleStringOrNull(context: Context, otherVersion: String): String? { + val other = Version.parse(otherVersion) ?: return context.getString(R.string.version_invalid) + val match = compare(other) ?: return context.getString(R.string.version_invalid) + if (match.compatible) return null + if (match.currentCmp < 0) return context.getString(R.string.version_too_old) + if (match.currentCmp > 0) return context.getString(R.string.version_too_new) + throw AssertionError("$this == $other") +} diff --git a/taler-kotlin-common/src/main/java/net/taler/common/Version.kt b/taler-kotlin-common/src/main/java/net/taler/common/Version.kt new file mode 100644 index 0000000..8774115 --- /dev/null +++ b/taler-kotlin-common/src/main/java/net/taler/common/Version.kt @@ -0,0 +1,70 @@ +/* + * 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.common + +import kotlin.math.sign + +/** + * Semantic versioning, but libtool-style. + * See https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html + */ +data class Version( + val current: Int, + val revision: Int, + val age: Int +) { + companion object { + fun parse(v: String): Version? { + val elements = v.split(":") + if (elements.size != 3) return null + val (currentStr, revisionStr, ageStr) = elements + val current = currentStr.toIntOrNull() + val revision = revisionStr.toIntOrNull() + val age = ageStr.toIntOrNull() + if (current == null || revision == null || age == null) return null + return Version(current, revision, age) + } + } + + /** + * Compare two libtool-style versions. + * + * Returns a [VersionMatchResult] or null if the given version was null. + */ + fun compare(other: Version?): VersionMatchResult? { + if (other == null) return null + val compatible = current - age <= other.current && + current >= other.current - other.age + val currentCmp = sign((current - other.current).toDouble()).toInt() + return VersionMatchResult(compatible, currentCmp) + } + + /** + * Result of comparing two libtool versions. + */ + data class VersionMatchResult( + /** + * Is the first version compatible with the second? + */ + val compatible: Boolean, + /** + * Is the first version older (-1), newer (+1) or identical (0)? + */ + val currentCmp: Int + ) + +} diff --git a/taler-kotlin-common/src/main/res/values/strings.xml b/taler-kotlin-common/src/main/res/values/strings.xml new file mode 100644 index 0000000..a5b1df1 --- /dev/null +++ b/taler-kotlin-common/src/main/res/values/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ 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/> + --> + +<resources> + <string name="version_invalid">Invalid version. Please try again later!</string> + <string name="version_too_old">App too old. Please upgrade!</string> + <string name="version_too_new">Service outdated. Please wait until it gets upgraded.</string> +</resources> diff --git a/taler-kotlin-common/src/test/java/net/taler/common/VersionTest.kt b/taler-kotlin-common/src/test/java/net/taler/common/VersionTest.kt new file mode 100644 index 0000000..70f30eb --- /dev/null +++ b/taler-kotlin-common/src/test/java/net/taler/common/VersionTest.kt @@ -0,0 +1,65 @@ +/* + * 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.common + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test + +class VersionTest { + + @Test + fun testParse() { + assertNull(Version.parse("")) + assertNull(Version.parse("foo")) + assertNull(Version.parse("foo:bar:foo")) + assertNull(Version.parse("0:0:0:")) + assertNull(Version.parse("0:0:")) + assertEquals(Version(0, 0, 0), Version.parse("0:0:0")) + assertEquals(Version(1, 2, 3), Version.parse("1:2:3")) + assertEquals(Version(1337, 42, 23), Version.parse("1337:42:23")) + } + + @Test + fun testComparision() { + assertEquals( + Version.VersionMatchResult(true, 0), + Version.parse("0:0:0")!!.compare(Version.parse("0:0:0")) + ) + assertEquals( + Version.VersionMatchResult(true, -1), + Version.parse("0:0:0")!!.compare(Version.parse("1:0:1")) + ) + assertEquals( + Version.VersionMatchResult(true, -1), + Version.parse("0:0:0")!!.compare(Version.parse("1:5:1")) + ) + assertEquals( + Version.VersionMatchResult(false, -1), + Version.parse("0:0:0")!!.compare(Version.parse("1:5:0")) + ) + assertEquals( + Version.VersionMatchResult(false, 1), + Version.parse("1:0:0")!!.compare(Version.parse("0:5:0")) + ) + assertEquals( + Version.VersionMatchResult(true, 0), + Version.parse("1:0:1")!!.compare(Version.parse("1:5:1")) + ) + } + +} |