gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-taler-android] 03/04: [wallet] start to move deserialization into


From: gnunet
Subject: [taler-taler-android] 03/04: [wallet] start to move deserialization into the backend API
Date: Tue, 11 Aug 2020 22:35:47 +0200

This is an automated email from the git hooks/post-receive script.

torsten-grote pushed a commit to branch master
in repository taler-android.

commit d13be7c5c1be2492d38959a29e1b1c33df4938ff
Author: Torsten Grote <t@grobox.de>
AuthorDate: Tue Aug 11 15:24:25 2020 -0300

    [wallet] start to move deserialization into the backend API
    
    and off the UI thread for less sluggish, i.e. more responsive UI
---
 wallet/build.gradle                                |   1 +
 .../main/java/net/taler/wallet/MainViewModel.kt    |  63 ++++++-----
 .../net/taler/wallet/backend/WalletBackendApi.kt   |  75 +++++++++----
 .../net/taler/wallet/backend/WalletResponse.kt     |  82 ++++++++++++++
 .../net/taler/wallet/balances/BalanceAdapter.kt    |   2 +
 .../net/taler/wallet/balances/BalanceResponse.kt   |  24 ++++
 .../net/taler/wallet/exchanges/ExchangeAdapter.kt  |   2 +
 .../wallet/transactions/TransactionManager.kt      |   2 +-
 .../net/taler/wallet/transactions/Transactions.kt  |   2 +
 .../java/net/taler/wallet/withdraw/TosSection.kt   |   7 ++
 .../net/taler/wallet/withdraw/WithdrawManager.kt   | 122 +++++++++++----------
 .../net/taler/wallet/backend/WalletResponseTest.kt |  56 ++++++++++
 12 files changed, 323 insertions(+), 115 deletions(-)

diff --git a/wallet/build.gradle b/wallet/build.gradle
index ef5ddfa..d0fd97d 100644
--- a/wallet/build.gradle
+++ b/wallet/build.gradle
@@ -20,6 +20,7 @@ plugins {
     id "com.android.application"
     id "kotlin-android"
     id "kotlin-android-extensions"
+    id 'kotlinx-serialization'
     id "de.undercouch.download"
 }
 
diff --git a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt 
b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
index 2c5e318..330704e 100644
--- a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
+++ b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
@@ -27,7 +27,8 @@ import androidx.lifecycle.viewModelScope
 import 
com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
 import com.fasterxml.jackson.databind.ObjectMapper
 import com.fasterxml.jackson.module.kotlin.KotlinModule
-import com.fasterxml.jackson.module.kotlin.readValue
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
 import net.taler.common.Amount
 import net.taler.common.AmountMixin
 import net.taler.common.Event
@@ -37,6 +38,7 @@ import net.taler.common.assertUiThread
 import net.taler.common.toEvent
 import net.taler.wallet.backend.WalletBackendApi
 import net.taler.wallet.balances.BalanceItem
+import net.taler.wallet.balances.BalanceResponse
 import net.taler.wallet.exchanges.ExchangeManager
 import net.taler.wallet.payment.PaymentManager
 import net.taler.wallet.pending.PendingOperationsManager
@@ -68,15 +70,19 @@ class MainViewModel(val app: Application) : 
AndroidViewModel(app) {
     var merchantVersion: String? = null
         private set
 
-    private val walletBackendApi = WalletBackendApi(app, {
-        // nothing to do when we connect, balance will be requested by 
BalanceFragment in onStart()
-    }) { payload ->
+    private val mapper = ObjectMapper()
+        .registerModule(KotlinModule())
+        .configure(FAIL_ON_UNKNOWN_PROPERTIES, false)
+        .addMixIn(Amount::class.java, AmountMixin::class.java)
+        .addMixIn(Timestamp::class.java, TimestampMixin::class.java)
+
+    private val api = WalletBackendApi(app) { payload ->
         if (payload.optString("operation") == "init") {
             val result = payload.getJSONObject("result")
             val versions = result.getJSONObject("supported_protocol_versions")
             exchangeVersion = versions.getString("exchange")
             merchantVersion = versions.getString("merchant")
-        } else if (payload.getString("type") != "waiting-for-retry") {  // 
ignore ping
+        } else if (payload.getString("type") != "waiting-for-retry") { // 
ignore ping
             Log.i(TAG, "Received notification from wallet-core: 
${payload.toString(2)}")
             loadBalances()
             if (payload.optString("type") in transactionNotifications) {
@@ -92,20 +98,12 @@ class MainViewModel(val app: Application) : 
AndroidViewModel(app) {
         }
     }
 
-    private val mapper = ObjectMapper()
-        .registerModule(KotlinModule())
-        .configure(FAIL_ON_UNKNOWN_PROPERTIES, false)
-        .addMixIn(Amount::class.java, AmountMixin::class.java)
-        .addMixIn(Timestamp::class.java, TimestampMixin::class.java)
-
-    val withdrawManager = WithdrawManager(walletBackendApi, mapper)
-    val paymentManager = PaymentManager(walletBackendApi, mapper)
-    val pendingOperationsManager: PendingOperationsManager =
-        PendingOperationsManager(walletBackendApi)
-    val transactionManager: TransactionManager =
-        TransactionManager(walletBackendApi, viewModelScope, mapper)
-    val refundManager = RefundManager(walletBackendApi)
-    val exchangeManager: ExchangeManager = ExchangeManager(walletBackendApi, 
mapper)
+    val withdrawManager = WithdrawManager(api, viewModelScope)
+    val paymentManager = PaymentManager(api, mapper)
+    val pendingOperationsManager: PendingOperationsManager = 
PendingOperationsManager(api)
+    val transactionManager: TransactionManager = TransactionManager(api, 
viewModelScope, mapper)
+    val refundManager = RefundManager(api)
+    val exchangeManager: ExchangeManager = ExchangeManager(api, mapper)
 
     private val mTransactionsEvent = MutableLiveData<Event<String>>()
     val transactionsEvent: LiveData<Event<String>> = mTransactionsEvent
@@ -118,20 +116,21 @@ class MainViewModel(val app: Application) : 
AndroidViewModel(app) {
     val lastBackup: LiveData<Long> = mLastBackup
 
     override fun onCleared() {
-        walletBackendApi.destroy()
+        api.destroy()
         super.onCleared()
     }
 
     @UiThread
-    fun loadBalances() {
+    fun loadBalances(): Job = viewModelScope.launch {
         showProgressBar.value = true
-        walletBackendApi.sendRequest("getBalances") { isError, result ->
-            if (isError) {
-                Log.e(TAG, "Error retrieving balances: ${result.toString(2)}")
-                return@sendRequest
-            }
-            mBalances.value = mapper.readValue(result.getString("balances"))
-            showProgressBar.value = false
+        val response = api.request("getBalances", BalanceResponse.serializer())
+        showProgressBar.value = false
+        response.onError {
+            // TODO expose in UI
+            Log.e(TAG, "Error retrieving balances: $it")
+        }
+        response.onSuccess {
+            mBalances.value = it.balances
         }
     }
 
@@ -145,22 +144,22 @@ class MainViewModel(val app: Application) : 
AndroidViewModel(app) {
 
     @UiThread
     fun dangerouslyReset() {
-        walletBackendApi.sendRequest("reset")
+        api.sendRequest("reset")
         withdrawManager.testWithdrawalInProgress.value = false
         mBalances.value = emptyList()
     }
 
     fun startTunnel() {
-        walletBackendApi.sendRequest("startTunnel")
+        api.sendRequest("startTunnel")
     }
 
     fun stopTunnel() {
-        walletBackendApi.sendRequest("stopTunnel")
+        api.sendRequest("stopTunnel")
     }
 
     fun tunnelResponse(resp: String) {
         val respJson = JSONObject(resp)
-        walletBackendApi.sendRequest("tunnelResponse", respJson)
+        api.sendRequest("tunnelResponse", respJson)
     }
 
 }
diff --git a/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt 
b/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt
index 51b3419..ea8f26f 100644
--- a/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt
+++ b/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt
@@ -14,7 +14,6 @@
  * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-
 package net.taler.wallet.backend
 
 import android.app.Application
@@ -27,21 +26,35 @@ import android.os.IBinder
 import android.os.Message
 import android.os.Messenger
 import android.util.Log
-import android.util.SparseArray
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import kotlinx.serialization.KSerializer
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.JsonConfiguration
+import net.taler.wallet.backend.WalletBackendService.Companion.MSG_COMMAND
+import net.taler.wallet.backend.WalletBackendService.Companion.MSG_NOTIFY
+import net.taler.wallet.backend.WalletBackendService.Companion.MSG_REPLY
+import 
net.taler.wallet.backend.WalletBackendService.Companion.MSG_SUBSCRIBE_NOTIFY
 import org.json.JSONObject
 import java.lang.ref.WeakReference
 import java.util.LinkedList
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.atomic.AtomicInteger
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
 
 class WalletBackendApi(
     private val app: Application,
-    private val onConnected: (() -> Unit),
     private val notificationHandler: ((payload: JSONObject) -> Unit)
 ) {
-
+    private val json = Json(
+        JsonConfiguration.Stable.copy(ignoreUnknownKeys = true)
+    )
     private var walletBackendMessenger: Messenger? = null
     private val queuedMessages = LinkedList<Message>()
-    private val handlers = SparseArray<(isError: Boolean, message: JSONObject) 
-> Unit>()
-    private var nextRequestID = 1
+    private val handlers = ConcurrentHashMap<Int, (isError: Boolean, message: 
JSONObject) -> Unit>()
+    private var nextRequestID = AtomicInteger(0)
+    private val incomingMessenger = Messenger(IncomingHandler(this))
 
     private val walletBackendConn = object : ServiceConnection {
         override fun onServiceDisconnected(p0: ComponentName?) {
@@ -54,10 +67,15 @@ class WalletBackendApi(
             val bm = Messenger(binder)
             walletBackendMessenger = bm
             pumpQueue(bm)
-            val msg = Message.obtain(null, 
WalletBackendService.MSG_SUBSCRIBE_NOTIFY)
+            val msg = Message.obtain(null, MSG_SUBSCRIBE_NOTIFY)
             msg.replyTo = incomingMessenger
             bm.send(msg)
-            onConnected.invoke()
+        }
+    }
+
+    init {
+        Intent(app, WalletBackendService::class.java).also { intent ->
+            app.bindService(intent, walletBackendConn, 
Context.BIND_AUTO_CREATE)
         }
     }
 
@@ -66,11 +84,11 @@ class WalletBackendApi(
         override fun handleMessage(msg: Message) {
             val api = weakApi.get() ?: return
             when (msg.what) {
-                WalletBackendService.MSG_REPLY -> {
+                MSG_REPLY -> {
                     val requestID = msg.data.getInt("requestID", 0)
                     val operation = msg.data.getString("operation", "??")
                     Log.i(TAG, "got reply for operation $operation 
($requestID)")
-                    val h = api.handlers.get(requestID)
+                    val h = api.handlers.remove(requestID)
                     if (h == null) {
                         Log.e(TAG, "request ID not associated with a handler")
                         return
@@ -84,7 +102,7 @@ class WalletBackendApi(
                     val json = JSONObject(response)
                     h(isError, json)
                 }
-                WalletBackendService.MSG_NOTIFY -> {
+                MSG_NOTIFY -> {
                     val payloadStr = msg.data.getString("payload")
                     if (payloadStr == null) {
                         Log.e(TAG, "Notification had no payload: $msg")
@@ -97,14 +115,6 @@ class WalletBackendApi(
         }
     }
 
-    private val incomingMessenger = Messenger(IncomingHandler(this))
-
-    init {
-        Intent(app, WalletBackendService::class.java).also { intent ->
-            app.bindService(intent, walletBackendConn, 
Context.BIND_AUTO_CREATE)
-        }
-    }
-
     private fun pumpQueue(bm: Messenger) {
         while (true) {
             val msg = queuedMessages.pollFirst() ?: return
@@ -112,16 +122,15 @@ class WalletBackendApi(
         }
     }
 
-
     fun sendRequest(
         operation: String,
         args: JSONObject? = null,
         onResponse: (isError: Boolean, message: JSONObject) -> Unit = { _, _ 
-> }
     ) {
-        val requestID = nextRequestID++
+        val requestID = nextRequestID.incrementAndGet()
         Log.i(TAG, "sending request for operation $operation ($requestID)")
-        val msg = Message.obtain(null, WalletBackendService.MSG_COMMAND)
-        handlers.put(requestID, onResponse)
+        val msg = Message.obtain(null, MSG_COMMAND)
+        handlers[requestID] = onResponse
         msg.replyTo = incomingMessenger
         val data = msg.data
         data.putString("operation", operation)
@@ -137,6 +146,26 @@ class WalletBackendApi(
         }
     }
 
+    suspend fun <T> request(
+        operation: String,
+        serializer: KSerializer<T>? = null,
+        args: (JSONObject.() -> JSONObject)? = null
+    ): WalletResponse<T> = withContext(Dispatchers.Default) {
+        suspendCoroutine<WalletResponse<T>> { cont ->
+            sendRequest(operation, args?.invoke(JSONObject())) { isError, 
message ->
+                val response = if (isError) {
+                    val error = json.parse(WalletErrorInfo.serializer(), 
message.toString())
+                    WalletResponse.Error<T>(error)
+                } else {
+                    @Suppress("UNCHECKED_CAST") // if serializer is null, T 
must be Unit
+                    val t: T = serializer?.let { json.parse(serializer, 
message.toString()) } ?: Unit as T
+                    WalletResponse.Success(t)
+                }
+                cont.resume(response)
+            }
+        }
+    }
+
     fun destroy() {
         // FIXME: implement this!
     }
diff --git a/wallet/src/main/java/net/taler/wallet/backend/WalletResponse.kt 
b/wallet/src/main/java/net/taler/wallet/backend/WalletResponse.kt
new file mode 100644
index 0000000..05a53f3
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/backend/WalletResponse.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.wallet.backend
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+import org.json.JSONObject
+
+@Serializable
+sealed class WalletResponse<T> {
+    @Serializable
+    @SerialName("response")
+    data class Success<T>(
+        val result: T
+    ) : WalletResponse<T>()
+
+    @Serializable
+    @SerialName("error")
+    data class Error<T>(
+        val error: WalletErrorInfo
+    ) : WalletResponse<T>()
+
+    fun onSuccess(block: (result: T) -> Unit): WalletResponse<T> {
+        if (this is Success) block(this.result)
+        return this
+    }
+
+    fun onError(block: (result: WalletErrorInfo) -> Unit): WalletResponse<T> {
+        if (this is Error) block(this.error)
+        return this
+    }
+}
+
+@Serializable
+data class WalletErrorInfo(
+    // Numeric error code defined defined in the
+    // GANA gnu-taler-error-codes registry.
+    val talerErrorCode: Int,
+
+    // English description of the error code.
+    val talerErrorHint: String,
+
+    // English diagnostic message that can give details
+    // for the instance of the error.
+    val message: String,
+
+    // Error details, type depends
+    // on talerErrorCode
+    val details: String?
+) {
+    val userFacingMsg: String
+        get() {
+            return StringBuilder().apply {
+                append(talerErrorCode)
+                append(" ")
+                append(message)
+                details?.let { it ->
+                    val details = JSONObject(it)
+                    details.optJSONObject("errorResponse")?.let { 
errorResponse ->
+                        append("\n\n")
+                        append(errorResponse.optString("code"))
+                        append(" ")
+                        append(errorResponse.optString("hint"))
+                    }
+                }
+            }.toString()
+        }
+}
diff --git a/wallet/src/main/java/net/taler/wallet/balances/BalanceAdapter.kt 
b/wallet/src/main/java/net/taler/wallet/balances/BalanceAdapter.kt
index c090e75..24ee1a1 100644
--- a/wallet/src/main/java/net/taler/wallet/balances/BalanceAdapter.kt
+++ b/wallet/src/main/java/net/taler/wallet/balances/BalanceAdapter.kt
@@ -24,10 +24,12 @@ import android.view.ViewGroup
 import android.widget.TextView
 import androidx.recyclerview.widget.RecyclerView
 import androidx.recyclerview.widget.RecyclerView.Adapter
+import kotlinx.serialization.Serializable
 import net.taler.common.Amount
 import net.taler.wallet.R
 import net.taler.wallet.balances.BalanceAdapter.BalanceViewHolder
 
+@Serializable
 data class BalanceItem(
     val available: Amount,
     val pendingIncoming: Amount,
diff --git a/wallet/src/main/java/net/taler/wallet/balances/BalanceResponse.kt 
b/wallet/src/main/java/net/taler/wallet/balances/BalanceResponse.kt
new file mode 100644
index 0000000..d1a111f
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/balances/BalanceResponse.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.wallet.balances
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class BalanceResponse(
+    val balances: List<BalanceItem>
+)
diff --git a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeAdapter.kt 
b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeAdapter.kt
index 189f444..17ac50f 100644
--- a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeAdapter.kt
+++ b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeAdapter.kt
@@ -24,10 +24,12 @@ import android.widget.TextView
 import androidx.appcompat.widget.PopupMenu
 import androidx.recyclerview.widget.RecyclerView
 import androidx.recyclerview.widget.RecyclerView.Adapter
+import kotlinx.serialization.Serializable
 import net.taler.wallet.R
 import net.taler.wallet.cleanExchange
 import net.taler.wallet.exchanges.ExchangeAdapter.ExchangeItemViewHolder
 
+@Serializable
 data class ExchangeItem(
     val exchangeBaseUrl: String,
     val currency: String,
diff --git 
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt 
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt
index 8ec3914..b9f86b3 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt
@@ -87,7 +87,7 @@ class TransactionManager(
     @WorkerThread
     private fun onTransactionsLoaded(
         liveData: MutableLiveData<TransactionsResult>,
-        currency: String?,  // only non-null if we should update all 
transactions cache
+        currency: String?, // only non-null if we should update all 
transactions cache
         result: JSONObject
     ) {
         val transactionsArray = result.getString("transactions")
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt 
b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
index 5363834..721522c 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
@@ -36,6 +36,8 @@ import net.taler.wallet.cleanExchange
 import net.taler.wallet.transactions.WithdrawalDetails.ManualTransfer
 import net.taler.wallet.transactions.WithdrawalDetails.TalerBankIntegrationApi
 
+data class Transactions(val transactions: List<Transaction>)
+
 @JsonTypeInfo(use = NAME, include = PROPERTY, property = "type")
 @JsonSubTypes(
     Type(value = TransactionWithdrawal::class, name = "withdrawal"),
diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/TosSection.kt 
b/wallet/src/main/java/net/taler/wallet/withdraw/TosSection.kt
index b27de42..b198478 100644
--- a/wallet/src/main/java/net/taler/wallet/withdraw/TosSection.kt
+++ b/wallet/src/main/java/net/taler/wallet/withdraw/TosSection.kt
@@ -17,6 +17,7 @@
 package net.taler.wallet.withdraw
 
 import io.noties.markwon.Markwon
+import kotlinx.serialization.Serializable
 import org.commonmark.node.Code
 import org.commonmark.node.Document
 import org.commonmark.node.Heading
@@ -73,3 +74,9 @@ private fun getNodeText(rootNode: Node): String {
     }
     return text
 }
+
+@Serializable
+data class TosResponse(
+    val tos: String,
+    val currentEtag: String
+)
diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt 
b/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt
index 6fb9390..1066550 100644
--- a/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt
@@ -19,16 +19,16 @@ package net.taler.wallet.withdraw
 import android.util.Log
 import androidx.annotation.UiThread
 import androidx.lifecycle.MutableLiveData
-import com.fasterxml.jackson.databind.ObjectMapper
-import com.fasterxml.jackson.module.kotlin.readValue
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.serialization.Serializable
 import net.taler.common.Amount
 import net.taler.wallet.TAG
 import net.taler.wallet.backend.WalletBackendApi
+import net.taler.wallet.backend.WalletErrorInfo
 import net.taler.wallet.exchanges.ExchangeFees
 import net.taler.wallet.exchanges.ExchangeItem
-import net.taler.wallet.getErrorString
 import net.taler.wallet.withdraw.WithdrawStatus.ReceivedDetails
-import org.json.JSONObject
 
 sealed class WithdrawStatus {
     data class Loading(val talerWithdrawUri: String? = null) : WithdrawStatus()
@@ -53,12 +53,14 @@ sealed class WithdrawStatus {
     data class Error(val message: String?) : WithdrawStatus()
 }
 
+@Serializable
 data class WithdrawalDetailsForUri(
     val amount: Amount,
     val defaultExchangeBaseUrl: String?,
     val possibleExchanges: List<ExchangeItem>
 )
 
+@Serializable
 data class WithdrawalDetails(
     val tosAccepted: Boolean,
     val amountRaw: Amount,
@@ -66,8 +68,8 @@ data class WithdrawalDetails(
 )
 
 class WithdrawManager(
-    private val walletBackendApi: WalletBackendApi,
-    private val mapper: ObjectMapper
+    private val api: WalletBackendApi,
+    private val scope: CoroutineScope
 ) {
 
     val withdrawStatus = MutableLiveData<WithdrawStatus>()
@@ -78,22 +80,21 @@ class WithdrawManager(
 
     fun withdrawTestkudos() {
         testWithdrawalInProgress.value = true
-        walletBackendApi.sendRequest("withdrawTestkudos") { _, _ ->
+        api.sendRequest("withdrawTestkudos") { _, _ ->
             testWithdrawalInProgress.postValue(false)
         }
     }
 
-    fun getWithdrawalDetails(uri: String) {
+    fun getWithdrawalDetails(uri: String) = scope.launch {
         withdrawStatus.value = WithdrawStatus.Loading(uri)
-        val args = JSONObject().apply {
-            put("talerWithdrawUri", uri)
-        }
-        walletBackendApi.sendRequest("getWithdrawalDetailsForUri", args) { 
isError, result ->
-            if (isError) {
-                handleError("getWithdrawalDetailsForUri", result)
-                return@sendRequest
+        val response =
+            api.request("getWithdrawalDetailsForUri", 
WithdrawalDetailsForUri.serializer()) {
+                put("talerWithdrawUri", uri)
             }
-            val details: WithdrawalDetailsForUri = 
mapper.readValue(result.toString())
+        response.onError { error ->
+            handleError("getWithdrawalDetailsForUri", error)
+        }
+        response.onSuccess { details ->
             if (details.defaultExchangeBaseUrl == null) {
                 // TODO go to exchange selection screen instead
                 val chosenExchange = 
details.possibleExchanges[0].exchangeBaseUrl
@@ -104,45 +105,51 @@ class WithdrawManager(
         }
     }
 
-    fun getWithdrawalDetails(exchangeBaseUrl: String, amount: Amount, uri: 
String? = null) {
+    fun getWithdrawalDetails(
+        exchangeBaseUrl: String,
+        amount: Amount,
+        uri: String? = null
+    ) = scope.launch {
         withdrawStatus.value = WithdrawStatus.Loading(uri)
-        val args = JSONObject().apply {
-            put("exchangeBaseUrl", exchangeBaseUrl)
-            put("amount", amount.toJSONString())
-        }
-        walletBackendApi.sendRequest("getWithdrawalDetailsForAmount", args) { 
isError, result ->
-            if (isError) {
-                handleError("getWithdrawalDetailsForAmount", result)
-                return@sendRequest
+        val response =
+            api.request("getWithdrawalDetailsForAmount", 
WithdrawalDetails.serializer()) {
+                put("exchangeBaseUrl", exchangeBaseUrl)
+                put("amount", amount.toJSONString())
             }
-            val details: WithdrawalDetails = 
mapper.readValue(result.toString())
-            if (details.tosAccepted)
+        response.onError { error ->
+            handleError("getWithdrawalDetailsForAmount", error)
+        }
+        response.onSuccess { details ->
+            if (details.tosAccepted) {
                 withdrawStatus.value = ReceivedDetails(
                     talerWithdrawUri = uri,
                     exchangeBaseUrl = exchangeBaseUrl,
                     amountRaw = details.amountRaw,
                     amountEffective = details.amountEffective
                 )
-            else getExchangeTos(exchangeBaseUrl, details, uri)
+            } else getExchangeTos(exchangeBaseUrl, details, uri)
         }
     }
 
-    private fun getExchangeTos(exchangeBaseUrl: String, details: 
WithdrawalDetails, uri: String?) {
-        val args = JSONObject().apply {
+    private fun getExchangeTos(
+        exchangeBaseUrl: String,
+        details: WithdrawalDetails,
+        uri: String?
+    ) = scope.launch {
+        val response = api.request("getExchangeTos", TosResponse.serializer()) 
{
             put("exchangeBaseUrl", exchangeBaseUrl)
         }
-        walletBackendApi.sendRequest("getExchangeTos", args) { isError, result 
->
-            if (isError) {
-                handleError("getExchangeTos", result)
-                return@sendRequest
-            }
+        response.onError {
+            handleError("getExchangeTos", it)
+        }
+        response.onSuccess {
             withdrawStatus.value = WithdrawStatus.TosReviewRequired(
                 talerWithdrawUri = uri,
                 exchangeBaseUrl = exchangeBaseUrl,
                 amountRaw = details.amountRaw,
                 amountEffective = details.amountEffective,
-                tosText = result.getString("tos"),
-                tosEtag = result.getString("currentEtag")
+                tosText = it.tos,
+                tosEtag = it.currentEtag
             )
         }
     }
@@ -150,17 +157,14 @@ class WithdrawManager(
     /**
      * Accept the currently displayed terms of service.
      */
-    fun acceptCurrentTermsOfService() {
+    fun acceptCurrentTermsOfService() = scope.launch {
         val s = withdrawStatus.value as WithdrawStatus.TosReviewRequired
-        val args = JSONObject().apply {
+        api.request<Unit>("setExchangeTosAccepted") {
             put("exchangeBaseUrl", s.exchangeBaseUrl)
             put("etag", s.tosEtag)
-        }
-        walletBackendApi.sendRequest("setExchangeTosAccepted", args) { 
isError, result ->
-            if (isError) {
-                handleError("setExchangeTosAccepted", result)
-                return@sendRequest
-            }
+        }.onError {
+            handleError("setExchangeTosAccepted", it)
+        }.onSuccess {
             withdrawStatus.value = ReceivedDetails(
                 talerWithdrawUri = s.talerWithdrawUri,
                 exchangeBaseUrl = s.exchangeBaseUrl,
@@ -171,33 +175,33 @@ class WithdrawManager(
     }
 
     @UiThread
-    fun acceptWithdrawal() {
+    fun acceptWithdrawal() = scope.launch {
         val status = withdrawStatus.value as ReceivedDetails
+        val operation = if (status.talerWithdrawUri == null) {
+            "acceptManualWithdrawal"
+        } else {
+            "acceptBankIntegratedWithdrawal"
+        }
+        withdrawStatus.value = WithdrawStatus.Withdrawing
 
-        val operation = if (status.talerWithdrawUri == null)
-            "acceptManualWithdrawal" else "acceptBankIntegratedWithdrawal"
-        val args = JSONObject().apply {
+        api.request<Unit>(operation) {
             put("exchangeBaseUrl", status.exchangeBaseUrl)
             if (status.talerWithdrawUri == null) {
                 put("amount", status.amountRaw)
             } else {
                 put("talerWithdrawUri", status.talerWithdrawUri)
             }
-        }
-        withdrawStatus.value = WithdrawStatus.Withdrawing
-        walletBackendApi.sendRequest(operation, args) { isError, result ->
-            if (isError) {
-                handleError(operation, result)
-                return@sendRequest
-            }
+        }.onError {
+            handleError(operation, it)
+        }.onSuccess {
             withdrawStatus.value = 
WithdrawStatus.Success(status.amountRaw.currency)
         }
     }
 
     @UiThread
-    private fun handleError(operation: String, result: JSONObject) {
-        Log.e(TAG, "Error $operation ${result.toString(2)}")
-        withdrawStatus.value = WithdrawStatus.Error(getErrorString(result))
+    private fun handleError(operation: String, error: WalletErrorInfo) {
+        Log.e(TAG, "Error $operation $error")
+        withdrawStatus.value = WithdrawStatus.Error(error.userFacingMsg)
     }
 
 }
diff --git 
a/wallet/src/test/java/net/taler/wallet/backend/WalletResponseTest.kt 
b/wallet/src/test/java/net/taler/wallet/backend/WalletResponseTest.kt
new file mode 100644
index 0000000..b7d7c68
--- /dev/null
+++ b/wallet/src/test/java/net/taler/wallet/backend/WalletResponseTest.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.wallet.backend
+
+import junit.framework.Assert.assertEquals
+import kotlinx.serialization.UnstableDefault
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.JsonConfiguration
+import net.taler.wallet.balances.BalanceResponse
+import org.junit.Test
+
+@UnstableDefault
+class WalletResponseTest {
+
+    private val json = Json(JsonConfiguration(ignoreUnknownKeys = true))
+
+    @Test
+    fun testBalanceResponse() {
+        val serializer = 
WalletResponse.Success.serializer(BalanceResponse.serializer())
+        val response = json.parse(
+            serializer, """
+            {
+              "type": "response",
+              "operation": "getBalances",
+              "id": 2,
+              "result": {
+                "balances": [
+                  {
+                    "available": "TESTKUDOS:15.8",
+                    "pendingIncoming": "TESTKUDOS:0",
+                    "pendingOutgoing": "TESTKUDOS:0",
+                    "hasPendingTransactions": false,
+                    "requiresUserInput": false
+                  }
+                ]
+              }
+            }
+        """.trimIndent()
+        )
+        assertEquals(1, response.result.balances.size)
+    }
+}

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]