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 }