taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

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 }