From ed2c0c873437fa6127d590ac8b956ea75960c000 Mon Sep 17 00:00:00 2001 From: Benjamin Vadon Date: Wed, 22 Apr 2026 13:18:23 +0200 Subject: [PATCH 1/4] chore: Add isError method --- .../java/com/infomaniak/core/network/models/ApiResponse.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Network/Models/src/main/java/com/infomaniak/core/network/models/ApiResponse.kt b/Network/Models/src/main/java/com/infomaniak/core/network/models/ApiResponse.kt index 1286134c8..1bfe70cfd 100644 --- a/Network/Models/src/main/java/com/infomaniak/core/network/models/ApiResponse.kt +++ b/Network/Models/src/main/java/com/infomaniak/core/network/models/ApiResponse.kt @@ -1,6 +1,6 @@ /* * Infomaniak Core - Android - * Copyright (C) 2022-2025 Infomaniak Network SA + * Copyright (C) 2022-2026 Infomaniak Network SA * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -52,4 +52,6 @@ open class ApiResponse( var headers: ResponseHeaders = ResponseHeaders() fun isSuccess() = result == ApiResponseStatus.SUCCESS + + fun isError() = result == ApiResponseStatus.ERROR } From f99509c26b53998af84cc561e8e604f9d4286930 Mon Sep 17 00:00:00 2001 From: Benjamin Vadon Date: Wed, 22 Apr 2026 15:12:22 +0200 Subject: [PATCH 2/4] feat: Add ApiResponse extension methods to simplify success and failure data retrieving Add onXXX method to apply code when success or error happens with strong typing (not null) Add mapXXX method to transform response content --- .../utils/apiResponse/ApiErrorException.kt | 22 +++++++ .../utils/apiResponse/ApiResponseException.kt | 22 +++++++ .../utils/apiResponse/ApiResponseExt.kt | 58 +++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 Network/src/main/kotlin/com/infomaniak/core/network/utils/apiResponse/ApiErrorException.kt create mode 100644 Network/src/main/kotlin/com/infomaniak/core/network/utils/apiResponse/ApiResponseException.kt create mode 100644 Network/src/main/kotlin/com/infomaniak/core/network/utils/apiResponse/ApiResponseExt.kt diff --git a/Network/src/main/kotlin/com/infomaniak/core/network/utils/apiResponse/ApiErrorException.kt b/Network/src/main/kotlin/com/infomaniak/core/network/utils/apiResponse/ApiErrorException.kt new file mode 100644 index 000000000..7c6290192 --- /dev/null +++ b/Network/src/main/kotlin/com/infomaniak/core/network/utils/apiResponse/ApiErrorException.kt @@ -0,0 +1,22 @@ +/* + * Infomaniak Core - Android + * Copyright (C) 2026 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.infomaniak.core.network.utils.apiResponse + +import com.infomaniak.core.network.models.ApiResponse + +class ApiErrorException(response: ApiResponse<*>) : Exception("Error is null but required for an error $response") diff --git a/Network/src/main/kotlin/com/infomaniak/core/network/utils/apiResponse/ApiResponseException.kt b/Network/src/main/kotlin/com/infomaniak/core/network/utils/apiResponse/ApiResponseException.kt new file mode 100644 index 000000000..1fe20af3e --- /dev/null +++ b/Network/src/main/kotlin/com/infomaniak/core/network/utils/apiResponse/ApiResponseException.kt @@ -0,0 +1,22 @@ +/* + * Infomaniak Core - Android + * Copyright (C) 2026 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.infomaniak.core.network.utils.apiResponse + +import com.infomaniak.core.network.models.ApiResponse + +class ApiResponseException(response: ApiResponse<*>) : Exception("Data is null but required for a success $response") diff --git a/Network/src/main/kotlin/com/infomaniak/core/network/utils/apiResponse/ApiResponseExt.kt b/Network/src/main/kotlin/com/infomaniak/core/network/utils/apiResponse/ApiResponseExt.kt new file mode 100644 index 000000000..cb64cf1bc --- /dev/null +++ b/Network/src/main/kotlin/com/infomaniak/core/network/utils/apiResponse/ApiResponseExt.kt @@ -0,0 +1,58 @@ +/* + * Infomaniak Core - Android + * Copyright (C) 2026 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.infomaniak.core.network.utils.apiResponse + +import com.infomaniak.core.network.models.ApiError +import com.infomaniak.core.network.models.ApiResponse +import io.sentry.Sentry + +inline fun ApiResponse.onSuccess(block: T.() -> Unit): ApiResponse = apply { + if (isSuccess()) { + data?.run(block) ?: Sentry.captureException(ApiResponseException(this)) + } +} + +inline fun ApiResponse.onError(block: ApiError.() -> Unit): ApiResponse = apply { + if (isError()) { + error?.run(block) ?: Sentry.captureException(ApiErrorException(this)) + } +} + +inline fun ApiResponse.on( + onSuccess: T.() -> Unit, + onError: ApiError.() -> Unit +): ApiResponse = onSuccess(onSuccess).onError(onError) + +inline fun ApiResponse.mapSuccess(block: T.() -> R): R? { + return takeIf { isSuccess() } + ?.runCatching { data?.run(block) ?: throw ApiResponseException(this) } + ?.onFailure(Sentry::captureException) + ?.getOrNull() +} + +inline fun ApiResponse.mapError(block: ApiError.() -> R): R? { + return takeIf { isError() } + ?.runCatching { error?.run(block) ?: throw ApiErrorException(this) } + ?.onFailure(Sentry::captureException) + ?.getOrNull() +} + +inline fun ApiResponse.map( + onSuccess: T.() -> R, + onError: ApiError.() -> R +): R? = mapSuccess(onSuccess) ?: mapError(onError) From 2f71fd2c0880d5b9722d18ef1b18cca7bacc7f38 Mon Sep 17 00:00:00 2001 From: Benjamin Vadon Date: Thu, 23 Apr 2026 13:01:52 +0200 Subject: [PATCH 3/4] chore: Add a toString to ApiResponse --- .../java/com/infomaniak/core/network/models/ApiResponse.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Network/Models/src/main/java/com/infomaniak/core/network/models/ApiResponse.kt b/Network/Models/src/main/java/com/infomaniak/core/network/models/ApiResponse.kt index 1bfe70cfd..4ce29493a 100644 --- a/Network/Models/src/main/java/com/infomaniak/core/network/models/ApiResponse.kt +++ b/Network/Models/src/main/java/com/infomaniak/core/network/models/ApiResponse.kt @@ -54,4 +54,8 @@ open class ApiResponse( fun isSuccess() = result == ApiResponseStatus.SUCCESS fun isError() = result == ApiResponseStatus.ERROR + + override fun toString(): String { + return "ApiResponse(result=$result, data=$data, uri=$uri, error=$error, responseAt=$responseAt )" + } } From 65d5bcae009a90b74354fa6b730e1d7561212dde Mon Sep 17 00:00:00 2001 From: Benjamin Vadon Date: Thu, 23 Apr 2026 13:02:57 +0200 Subject: [PATCH 4/4] feat: Remove map method as they need more reflection on real usage and structure --- .../utils/apiResponse/ApiResponseExt.kt | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/Network/src/main/kotlin/com/infomaniak/core/network/utils/apiResponse/ApiResponseExt.kt b/Network/src/main/kotlin/com/infomaniak/core/network/utils/apiResponse/ApiResponseExt.kt index cb64cf1bc..3b52b764a 100644 --- a/Network/src/main/kotlin/com/infomaniak/core/network/utils/apiResponse/ApiResponseExt.kt +++ b/Network/src/main/kotlin/com/infomaniak/core/network/utils/apiResponse/ApiResponseExt.kt @@ -37,22 +37,3 @@ inline fun ApiResponse.on( onSuccess: T.() -> Unit, onError: ApiError.() -> Unit ): ApiResponse = onSuccess(onSuccess).onError(onError) - -inline fun ApiResponse.mapSuccess(block: T.() -> R): R? { - return takeIf { isSuccess() } - ?.runCatching { data?.run(block) ?: throw ApiResponseException(this) } - ?.onFailure(Sentry::captureException) - ?.getOrNull() -} - -inline fun ApiResponse.mapError(block: ApiError.() -> R): R? { - return takeIf { isError() } - ?.runCatching { error?.run(block) ?: throw ApiErrorException(this) } - ?.onFailure(Sentry::captureException) - ?.getOrNull() -} - -inline fun ApiResponse.map( - onSuccess: T.() -> R, - onError: ApiError.() -> R -): R? = mapSuccess(onSuccess) ?: mapError(onError)