taler-android

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

Response.kt (2788B)


      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.cashier
     18 
     19 import android.content.Context
     20 import android.util.Log
     21 import io.ktor.client.call.body
     22 import io.ktor.client.plugins.ResponseException
     23 import io.ktor.http.HttpStatusCode
     24 import kotlinx.serialization.Serializable
     25 import net.taler.lib.android.isOnline
     26 import java.net.UnknownHostException
     27 
     28 class Response<out T> private constructor(
     29     private val value: Any?
     30 ) {
     31     companion object {
     32         suspend fun <T> response(request: suspend () -> T): Response<T> {
     33             return try {
     34                 Response(request())
     35             } catch (e: Throwable) {
     36                 Log.e("HttpClient", "Error getting request", e)
     37                 Response(getFailure(e))
     38             }
     39         }
     40 
     41         private suspend fun getFailure(e: Throwable): Failure = when (e) {
     42             is ResponseException -> Failure(e, getExceptionString(e), e.response.status)
     43             else -> Failure(e, e.toString())
     44         }
     45 
     46         private suspend fun getExceptionString(e: ResponseException): String {
     47             val response = e.response
     48             return try {
     49                 val error: Error = response.body()
     50                 "Error ${error.code}: ${error.hint}"
     51             } catch (ex: Exception) {
     52                 "Status code: ${response.status.value}"
     53             }
     54         }
     55     }
     56 
     57     val isFailure: Boolean get() = value is Failure
     58 
     59     suspend fun onSuccess(block: suspend (result: T) -> Unit): Response<T> {
     60         @Suppress("UNCHECKED_CAST")
     61         if (!isFailure) block(value as T)
     62         return this
     63     }
     64 
     65     suspend fun onError(block: suspend (failure: Failure) -> Unit): Response<T> {
     66         if (value is Failure) block(value)
     67         return this
     68     }
     69 
     70     data class Failure(
     71         val exception: Throwable,
     72         val msg: String,
     73         val statusCode: HttpStatusCode? = null
     74     ) {
     75         fun isOffline(context: Context): Boolean {
     76             return exception is UnknownHostException && !context.isOnline()
     77         }
     78     }
     79 
     80     @Serializable
     81     private class Error(
     82         val code: Int?,
     83         val hint: String?
     84     )
     85 }