WalletResponse.kt (5217B)
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 kotlinx.serialization.KSerializer 20 import kotlinx.serialization.SerialName 21 import kotlinx.serialization.Serializable 22 import kotlinx.serialization.builtins.MapSerializer 23 import kotlinx.serialization.builtins.serializer 24 import kotlinx.serialization.descriptors.SerialDescriptor 25 import kotlinx.serialization.encoding.Decoder 26 import kotlinx.serialization.encoding.Encoder 27 import kotlinx.serialization.json.JsonDecoder 28 import kotlinx.serialization.json.JsonElement 29 import kotlinx.serialization.json.JsonObject 30 import kotlinx.serialization.json.JsonPrimitive 31 import kotlinx.serialization.json.buildJsonObject 32 import kotlinx.serialization.json.jsonObject 33 import kotlinx.serialization.json.jsonPrimitive 34 35 @Serializable 36 sealed class WalletResponse<T> { 37 @Serializable 38 @SerialName("response") 39 data class Success<T>( 40 val result: T, 41 ) : WalletResponse<T>() 42 43 @Serializable 44 @SerialName("error") 45 data class Error<T>( 46 val error: TalerErrorInfo, 47 ) : WalletResponse<T>() 48 49 fun onSuccess(block: (result: T) -> Unit): WalletResponse<T> { 50 if (this is Success) block(this.result) 51 return this 52 } 53 54 fun onError(block: (result: TalerErrorInfo) -> Unit): WalletResponse<T> { 55 if (this is Error) block(this.error) 56 return this 57 } 58 } 59 60 @Serializable(with = TalerErrorInfoSerializer::class) 61 data class TalerErrorInfo( 62 // Numeric error code defined defined in the 63 // GANA gnu-taler-error-codes registry. 64 val code: TalerErrorCode, 65 66 // English description of the error code. 67 val hint: String? = null, 68 69 // English diagnostic message that can give details 70 // for the instance of the error. 71 val message: String? = null, 72 73 // Error extra details 74 val extra: Map<String, JsonElement> = mapOf(), 75 ) { 76 val userFacingMsg: String 77 get() { 78 return StringBuilder().apply { 79 // If there's a hint in errorResponse, use it. 80 if (extra.containsKey("errorResponse")) { 81 val errorResponse = extra["errorResponse"]!!.jsonObject 82 if (errorResponse.containsKey("hint")) { 83 val hint = errorResponse["hint"]!!.jsonPrimitive.content 84 append(hint) 85 } 86 } else { 87 // Otherwise, use the standard ones 88 hint?.let { append(it) } 89 message?.let { append(" ").append(it) } 90 } 91 }.toString() 92 } 93 94 fun getStringExtra(key: String): String? = 95 extra[key]?.jsonPrimitive?.content 96 97 companion object { 98 fun makeCustomError( 99 message: String, 100 hint: String? = null, 101 code: TalerErrorCode = TalerErrorCode.UNKNOWN, 102 ) = TalerErrorInfo( 103 message = message, 104 hint = hint, 105 code = code, 106 ) 107 } 108 } 109 110 class TalerErrorInfoSerializer : KSerializer<TalerErrorInfo> { 111 private val stringToJsonElementSerializer = MapSerializer(String.serializer(), JsonElement.serializer()) 112 113 override val descriptor: SerialDescriptor 114 get() = stringToJsonElementSerializer.descriptor 115 116 override fun deserialize(decoder: Decoder): TalerErrorInfo { 117 // Decoder -> JsonInput 118 require(decoder is JsonDecoder) 119 val json = decoder.json 120 val filtersMap = decoder.decodeSerializableValue(stringToJsonElementSerializer) 121 122 val code = filtersMap["code"]?.let { 123 json.decodeFromJsonElement(TalerErrorCode.serializer(), it) 124 } ?: TalerErrorCode.UNKNOWN 125 val hint = filtersMap["hint"]?.let { 126 json.decodeFromJsonElement(String.serializer(), it) 127 } 128 val message = filtersMap["message"]?.let { 129 json.decodeFromJsonElement(String.serializer(), it) 130 } 131 132 val knownKeys = setOf("code", "hint", "message") 133 val unknownFilters = filtersMap.filter { (key, _) -> !knownKeys.contains(key) } 134 135 return TalerErrorInfo(code, hint, message, unknownFilters) 136 } 137 138 override fun serialize(encoder: Encoder, value: TalerErrorInfo) { 139 encoder.encodeSerializableValue(JsonObject.serializer(), buildJsonObject { 140 put("code", JsonPrimitive(value.code.code)) 141 put("hint", JsonPrimitive(value.hint)) 142 put("message", JsonPrimitive(value.message)) 143 value.extra.forEach { (key, value) -> put(key, value) } 144 }) 145 } 146 }