summaryrefslogtreecommitdiff
path: root/merchant-terminal/src/main/java/net/taler/merchantpos/config
diff options
context:
space:
mode:
authorTorsten Grote <t@grobox.de>2020-07-23 15:41:50 -0300
committerTorsten Grote <t@grobox.de>2020-07-23 15:42:43 -0300
commit8eb241ccce345a35b05a6335d11306465220f66d (patch)
treefe78879b2c0cafdb9ba47f38db6a3572a7116594 /merchant-terminal/src/main/java/net/taler/merchantpos/config
parent08b10a2408f958cae96ae0c674ee450a35109e8a (diff)
downloadtaler-android-8eb241ccce345a35b05a6335d11306465220f66d.tar.gz
taler-android-8eb241ccce345a35b05a6335d11306465220f66d.tar.bz2
taler-android-8eb241ccce345a35b05a6335d11306465220f66d.zip
[pos] refactor configuration fetching and validation
Diffstat (limited to 'merchant-terminal/src/main/java/net/taler/merchantpos/config')
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt106
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantRequest.kt14
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/config/PosConfig.kt (renamed from merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantConfig.kt)52
3 files changed, 85 insertions, 87 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 3f45e32..c0b01a2 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
@@ -22,15 +22,14 @@ import android.util.Base64.NO_WRAP
import android.util.Base64.encodeToString
import android.util.Log
import androidx.annotation.UiThread
+import androidx.annotation.WorkerThread
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
-import com.android.volley.Request.Method.GET
-import com.android.volley.RequestQueue
-import com.android.volley.Response.Listener
-import com.android.volley.VolleyError
-import com.android.volley.toolbox.JsonObjectRequest
-import com.fasterxml.jackson.databind.ObjectMapper
-import com.fasterxml.jackson.module.kotlin.readValue
+import io.ktor.client.HttpClient
+import io.ktor.client.features.ClientRequestException
+import io.ktor.client.request.get
+import io.ktor.client.request.header
+import io.ktor.http.HttpHeaders.Authorization
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -38,9 +37,8 @@ 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.merchantlib.MerchantConfig
import net.taler.merchantpos.R
-import org.json.JSONObject
private const val SETTINGS_NAME = "taler-merchant-terminal"
@@ -60,15 +58,14 @@ interface ConfigurationReceiver {
/**
* Returns null if the configuration was valid, or a error string for user display otherwise.
*/
- suspend fun onConfigurationReceived(json: JSONObject, currency: String): String?
+ suspend fun onConfigurationReceived(posConfig: PosConfig, currency: String): String?
}
class ConfigManager(
private val context: Context,
private val scope: CoroutineScope,
- private val api: MerchantApi,
- private val mapper: ObjectMapper,
- private val queue: RequestQueue
+ private val httpClient: HttpClient,
+ private val api: MerchantApi
) {
private val prefs = context.getSharedPreferences(SETTINGS_NAME, MODE_PRIVATE)
@@ -79,8 +76,12 @@ class ConfigManager(
username = prefs.getString(SETTINGS_USERNAME, CONFIG_USERNAME_DEMO)!!,
password = prefs.getString(SETTINGS_PASSWORD, CONFIG_PASSWORD_DEMO)!!
)
+ @Volatile
var merchantConfig: MerchantConfig? = null
private set
+ @Volatile
+ var currency: String? = null
+ private set
private val mConfigUpdateResult = MutableLiveData<ConfigUpdateResult>()
val configUpdateResult: LiveData<ConfigUpdateResult> = mConfigUpdateResult
@@ -96,74 +97,76 @@ class ConfigManager(
if (savePassword) config else config.copy(password = "")
} else null
- val stringRequest = object : JsonObjectRequest(GET, config.configUrl, null,
- Listener { onConfigReceived(it, configToSave) },
- LogErrorListener { onNetworkError(it) }
- ) {
- // send basic auth header
- override fun getHeaders(): MutableMap<String, String> {
- val credentials = "${config.username}:${config.password}"
- val auth = ("Basic ${encodeToString(credentials.toByteArray(), NO_WRAP)}")
- return mutableMapOf("Authorization" to auth)
- }
- }
- queue.add(stringRequest)
- }
-
- @UiThread
- private fun onConfigReceived(json: JSONObject, config: Config?) {
- val merchantConfig: MerchantConfig = try {
- mapper.readValue(json.getString("config"))
- } catch (e: Exception) {
- Log.e(TAG, "Error parsing merchant config", e)
- val msg = context.getString(R.string.config_error_malformed)
- mConfigUpdateResult.value = ConfigUpdateResult.Error(msg)
- return
- }
-
scope.launch(Dispatchers.IO) {
- val configResponse = api.getConfig(merchantConfig.baseUrl)
- onMerchantConfigReceived(config, json, merchantConfig, configResponse)
+ try {
+ // get PoS configuration
+ val posConfig: PosConfig = httpClient.get(config.configUrl) {
+ val credentials = "${config.username}:${config.password}"
+ val auth = ("Basic ${encodeToString(credentials.toByteArray(), NO_WRAP)}")
+ header(Authorization, auth)
+ }
+ val merchantConfig = posConfig.merchantConfig
+ // get config from merchant backend API
+ api.getConfig(merchantConfig.baseUrl).handleSuspend(::onNetworkError) {
+ onMerchantConfigReceived(configToSave, posConfig, merchantConfig, it)
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Error retrieving merchant config", e)
+ val msg = if (e is ClientRequestException) {
+ context.getString(
+ if (e.response.status.value == 401) R.string.config_auth_error
+ else R.string.config_error_network
+ )
+ } else {
+ context.getString(R.string.config_error_malformed)
+ }
+ onNetworkError(msg)
+ }
}
}
- private fun onMerchantConfigReceived(
+ @WorkerThread
+ private suspend fun onMerchantConfigReceived(
newConfig: Config?,
- configJson: JSONObject,
+ posConfig: PosConfig,
merchantConfig: MerchantConfig,
configResponse: ConfigResponse
- ) = scope.launch(Dispatchers.Default) {
- val versionIncompatible = VERSION.getIncompatibleStringOrNull(context, configResponse.version)
+ ) {
+ val versionIncompatible =
+ VERSION.getIncompatibleStringOrNull(context, configResponse.version)
if (versionIncompatible != null) {
mConfigUpdateResult.postValue(ConfigUpdateResult.Error(versionIncompatible))
- return@launch
+ return
}
for (receiver in configurationReceivers) {
val result = try {
- receiver.onConfigurationReceived(configJson, configResponse.currency)
+ receiver.onConfigurationReceived(posConfig, configResponse.currency)
} catch (e: Exception) {
Log.e(TAG, "Error handling configuration by ${receiver::class.java.simpleName}", e)
context.getString(R.string.config_error_unknown)
}
if (result != null) { // error
mConfigUpdateResult.postValue(ConfigUpdateResult.Error(result))
- return@launch
+ return
}
}
newConfig?.let {
config = it
saveConfig(it)
}
- this@ConfigManager.merchantConfig = merchantConfig.copy(currency = configResponse.currency)
+ this.merchantConfig = merchantConfig
+ this.currency = configResponse.currency
mConfigUpdateResult.postValue(ConfigUpdateResult.Success(configResponse.currency))
}
+ @UiThread
fun forgetPassword() {
config = config.copy(password = "")
saveConfig(config)
merchantConfig = null
}
+ @UiThread
private fun saveConfig(config: Config) {
prefs.edit()
.putString(SETTINGS_CONFIG_URL, config.configUrl)
@@ -172,12 +175,7 @@ class ConfigManager(
.apply()
}
- @UiThread
- private fun onNetworkError(it: VolleyError?) {
- val msg = context.getString(
- if (it?.networkResponse?.statusCode == 401) R.string.config_auth_error
- else R.string.config_error_network
- )
+ private fun onNetworkError(msg: String) = scope.launch(Dispatchers.Main) {
mConfigUpdateResult.value = ConfigUpdateResult.Error(msg)
}
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantRequest.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantRequest.kt
index 9cfae94..5d41196 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantRequest.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantRequest.kt
@@ -16,9 +16,11 @@
package net.taler.merchantpos.config
+import android.net.Uri
import android.util.ArrayMap
import com.android.volley.Response
import com.android.volley.toolbox.JsonObjectRequest
+import net.taler.merchantlib.MerchantConfig
import net.taler.merchantpos.LogErrorListener
import org.json.JSONObject
@@ -33,7 +35,7 @@ class MerchantRequest(
) :
JsonObjectRequest(
method,
- merchantConfig.urlFor(endpoint, params),
+ merchantConfig.legacyUrl(endpoint, params),
jsonRequest,
listener,
errorListener
@@ -44,4 +46,14 @@ class MerchantRequest(
headerMap["Authorization"] = "ApiKey " + merchantConfig.apiKey
return headerMap
}
+
+}
+
+private fun MerchantConfig.legacyUrl(endpoint: String, params: Map<String, String>?): String {
+ val uriBuilder = Uri.parse(baseUrl).buildUpon()
+ uriBuilder.appendPath(endpoint)
+ params?.forEach {
+ uriBuilder.appendQueryParameter(it.key, it.value)
+ }
+ return uriBuilder.toString()
}
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantConfig.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/config/PosConfig.kt
index 0c7e3b7..2d8c040 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantConfig.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/config/PosConfig.kt
@@ -16,9 +16,8 @@
package net.taler.merchantpos.config
-import android.net.Uri
-import com.fasterxml.jackson.annotation.JsonIgnore
-import com.fasterxml.jackson.annotation.JsonProperty
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
import net.taler.common.Amount
import net.taler.common.ContractProduct
import net.taler.common.Product
@@ -34,51 +33,40 @@ data class Config(
fun hasPassword() = !password.isBlank()
}
-data class MerchantConfig(
- @JsonProperty("base_url")
- val baseUrl: String,
- val instance: String,
- @JsonProperty("api_key")
- val apiKey: String,
- val currency: String?
-) {
- fun urlFor(endpoint: String, params: Map<String, String>?): String {
- val uriBuilder = Uri.parse(baseUrl).buildUpon()
- uriBuilder.appendPath(endpoint)
- params?.forEach {
- uriBuilder.appendQueryParameter(it.key, it.value)
- }
- return uriBuilder.toString()
- }
- fun convert() = net.taler.merchantlib.MerchantConfig(
- baseUrl, instance, apiKey
- )
-}
+@Serializable
+data class PosConfig(
+ @SerialName("config")
+ val merchantConfig: net.taler.merchantlib.MerchantConfig,
+ val categories: List<Category>,
+ val products: List<ConfigProduct>
+)
+@Serializable
data class Category(
val id: Int,
val name: String,
- @JsonProperty("name_i18n")
- val nameI18n: Map<String, String>?
+ @SerialName("name_i18n")
+ val nameI18n: Map<String, String>? = null
) {
var selected: Boolean = false
val localizedName: String get() = TalerUtils.getLocalizedString(nameI18n, name)
}
+@Serializable
data class ConfigProduct(
- @JsonIgnore
val id: String = UUID.randomUUID().toString(),
- override val productId: String?,
+ @SerialName("product_id")
+ override val productId: String? = null,
override val description: String,
- override val descriptionI18n: Map<String, String>?,
+ @SerialName("description_i18n")
+ override val descriptionI18n: Map<String, String>? = null,
override val price: Amount,
- override val location: String?,
- override val image: String?,
+ @SerialName("delivery_location")
+ override val location: String? = null,
+ override val image: String? = null,
val categories: List<Int>,
- @JsonIgnore
val quantity: Int = 0
) : Product() {
- @get:JsonIgnore
val totalPrice by lazy { price * quantity }
fun toContractProduct() = ContractProduct(