WalletBackendApi.kt (5063B)
1 /* 2 * This file is part of GNU Taler 3 * (C) 2020 Taler Systems S.A. 4 * 5 * GNU Taler is free software; you can redistribute it and/or modify it under the 6 * terms of the GNU General Public License as published by the Free Software 7 * Foundation; either version 3, or (at your option) any later version. 8 * 9 * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY 10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License along with 14 * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 package net.taler.wallet.backend 18 19 import android.app.Application 20 import kotlinx.coroutines.DelicateCoroutinesApi 21 import kotlinx.coroutines.Dispatchers 22 import kotlinx.coroutines.GlobalScope 23 import kotlinx.coroutines.launch 24 import kotlinx.coroutines.withContext 25 import kotlinx.serialization.KSerializer 26 import kotlinx.serialization.encodeToString 27 import kotlinx.serialization.json.JsonObject 28 import kotlinx.serialization.json.decodeFromJsonElement 29 import net.taler.wallet.backend.TalerErrorCode.NONE 30 import org.json.JSONObject 31 import java.io.File 32 33 private const val WALLET_DB = "talerwalletdb.sqlite3" 34 35 @OptIn(DelicateCoroutinesApi::class) 36 class WalletBackendApi( 37 private val app: Application, 38 private val initialConfig: WalletRunConfig, 39 private val versionReceiver: VersionReceiver, 40 notificationReceiver: NotificationReceiver, 41 ) { 42 43 private val backendManager = BackendManager(notificationReceiver) 44 45 fun startWallet() { 46 backendManager.run() 47 GlobalScope.launch(Dispatchers.IO) { 48 sendInitMessage() 49 } 50 } 51 52 fun stopWallet() { 53 GlobalScope.launch(Dispatchers.IO) { 54 sendRequest("shutdown") 55 backendManager.destroy() 56 } 57 } 58 59 private suspend fun sendInitMessage() { 60 val db = if (File(app.filesDir, "talerwalletdb.sql").isFile) { 61 // can be removed after a reasonable migration period (2024-02-02) 62 "${app.filesDir}/talerwalletdb.sql" 63 } else { 64 "${app.filesDir}/${WALLET_DB}" 65 } 66 67 request("init", InitResponse.serializer()) { 68 put("persistentStoragePath", db) 69 put("logLevel", "INFO") 70 put("config", JSONObject(BackendManager.json.encodeToString(initialConfig))) 71 }.onSuccess { response -> 72 versionReceiver.onVersionReceived(response.versionInfo) 73 }.onError { error -> 74 // TODO expose this to the UI as it can happen when using an older DB version 75 error("Error on init message: $error") 76 } 77 } 78 79 suspend fun setWalletConfig(config: WalletRunConfig): WalletResponse<InitResponse> { 80 return request("initWallet", InitResponse.serializer()) { 81 put("config", JSONObject(BackendManager.json.encodeToString(config))) 82 } 83 } 84 85 suspend fun sendRequest(operation: String, args: JSONObject? = null): ApiResponse { 86 return backendManager.send(operation, args) 87 } 88 89 suspend inline fun <reified T> request( 90 operation: String, 91 serializer: KSerializer<T>? = null, 92 noinline args: (JSONObject.() -> JSONObject)? = null, 93 ): WalletResponse<T> = withContext(Dispatchers.Default) { 94 val json = BackendManager.json 95 try { 96 when (val response = sendRequest(operation, args?.invoke(JSONObject()))) { 97 is ApiResponse.Response -> { 98 val t: T = serializer?.let { 99 json.decodeFromJsonElement(serializer, response.result) 100 } ?: Unit as T 101 WalletResponse.Success(t) 102 } 103 104 is ApiResponse.Error -> { 105 val error: TalerErrorInfo = json.decodeFromJsonElement(response.error) 106 WalletResponse.Error(error) 107 } 108 } 109 } catch (e: Exception) { 110 val info = TalerErrorInfo(NONE, "", e.toString()) 111 WalletResponse.Error(info) 112 } 113 } 114 115 // Returns raw JSON response instead of serialized object 116 suspend inline fun rawRequest( 117 operation: String, 118 noinline args: (JSONObject.() -> JSONObject)? = null, 119 ): WalletResponse<JsonObject> = withContext(Dispatchers.Default) { 120 val json = BackendManager.json 121 try { 122 when (val response = sendRequest(operation, args?.invoke(JSONObject()))) { 123 is ApiResponse.Response -> { 124 WalletResponse.Success(response.result) 125 } 126 127 is ApiResponse.Error -> { 128 val error: TalerErrorInfo = json.decodeFromJsonElement(response.error) 129 WalletResponse.Error(error) 130 } 131 } 132 } catch (e: Exception) { 133 val info = TalerErrorInfo(NONE, "", e.toString()) 134 WalletResponse.Error(info) 135 } 136 } 137 }