From 55f6655e85c086ba75f86bacffe2886f07ba43c1 Mon Sep 17 00:00:00 2001 From: Gibran Chevalley Date: Fri, 25 Apr 2025 17:02:43 +0200 Subject: [PATCH 01/11] feat: Translate no network internal exceptions inside translateError() --- .../infomaniak/lib/core/api/ApiController.kt | 31 +++++++++++++++---- .../infomaniak/lib/core/models/ApiResponse.kt | 3 +- .../infomaniak/lib/core/utils/ApiErrorCode.kt | 21 ++++++++++--- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt index 91552c5b5..31590c5bc 100644 --- a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt +++ b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt @@ -1,6 +1,6 @@ /* * Infomaniak Core - Android - * Copyright (C) 2022-2024 Infomaniak Network SA + * Copyright (C) 2022-2025 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 @@ -32,6 +32,7 @@ import com.infomaniak.lib.core.models.ApiResponseStatus.ERROR import com.infomaniak.lib.core.networking.HttpClient import com.infomaniak.lib.core.networking.HttpUtils import com.infomaniak.lib.core.utils.CustomDateTypeAdapter +import com.infomaniak.lib.core.utils.ErrorCode import com.infomaniak.lib.core.utils.isNetworkException import com.infomaniak.lib.core.utils.isSerializationException import com.infomaniak.lib.login.ApiToken @@ -245,11 +246,19 @@ object ApiController { inline fun createInternetErrorResponse( noNetwork: Boolean = false, noinline buildErrorResult: ((apiError: ApiError?, translatedErrorRes: Int) -> T)?, - ) = createErrorResponse( - translatedError = if (noNetwork) R.string.noConnection else R.string.connectionError, - apiError = ApiError(exception = NetworkException()), - buildErrorResult = buildErrorResult, - ) + ): T { + val internalNetworkError = if (noNetwork) { + TranslatedInternalErrorCode.NoConnection + } else { + TranslatedInternalErrorCode.ConnectionError + } + + return createErrorResponse( + translatedError = internalNetworkError.translateRes, + apiError = internalNetworkError.toApiError(NetworkException()), + buildErrorResult = buildErrorResult, + ) + } fun createApiError(useKotlinxSerialization: Boolean, bodyResponse: String, exception: Exception) = ApiError( contextJson = if (useKotlinxSerialization) bodyResponse.bodyResponseToJson() else null, @@ -275,4 +284,14 @@ object ApiController { enum class ApiMethod { GET, PUT, POST, DELETE, PATCH } + + enum class TranslatedInternalErrorCode( + override val code: String, + @StringRes override val translateRes: Int, + ) : ErrorCode.Translated { + NoConnection("no_connection", R.string.noConnection), + ConnectionError("connection_error", R.string.connectionError), + } + + fun ErrorCode.toApiError(exception: Exception): ApiError = ApiError(code = code, exception = exception) } diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/models/ApiResponse.kt b/Legacy/src/main/java/com/infomaniak/lib/core/models/ApiResponse.kt index 5e9ce8c65..45083fc0f 100644 --- a/Legacy/src/main/java/com/infomaniak/lib/core/models/ApiResponse.kt +++ b/Legacy/src/main/java/com/infomaniak/lib/core/models/ApiResponse.kt @@ -1,6 +1,6 @@ /* * Infomaniak Core - Android - * Copyright (C) 2022-2024 Infomaniak Network SA + * Copyright (C) 2022-2025 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 @@ -34,6 +34,7 @@ open class ApiResponse( @SerializedName("response_at") val responseAt: Long = 0, val total: Int = 0, + @Deprecated("(╯°□°)╯︵ ┻━┻") // TODO var translatedError: Int = 0, @SerialName("items_per_page") @SerializedName("items_per_page") diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt b/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt index 68e76be54..8851977bc 100644 --- a/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt +++ b/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt @@ -1,6 +1,6 @@ /* * Infomaniak Core - Android - * Copyright (C) 2023-2024 Infomaniak Network SA + * Copyright (C) 2023-2025 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 @@ -20,9 +20,20 @@ package com.infomaniak.lib.core.utils import androidx.annotation.StringRes import com.infomaniak.lib.core.InfomaniakCore import com.infomaniak.lib.core.R +import com.infomaniak.lib.core.api.ApiController.TranslatedInternalErrorCode import com.infomaniak.lib.core.models.ApiResponse -data class ApiErrorCode(val code: String, @StringRes val translateRes: Int) { +interface ErrorCode { + val code: String + + interface Translated : ErrorCode { + override val code: String + @get:StringRes + val translateRes: Int + } +} + +data class ApiErrorCode(override val code: String, @StringRes override val translateRes: Int) : ErrorCode.Translated { companion object { @@ -33,12 +44,14 @@ data class ApiErrorCode(val code: String, @StringRes val translateRes: Int) { @StringRes fun ApiResponse.translateError(): Int = formatError().translateRes - fun ApiResponse.formatError(): ApiErrorCode { + fun ApiResponse.formatError(): ErrorCode.Translated { val errorCode = error?.code return if (errorCode == null) { defaultApiErrorCode } else { - InfomaniakCore.apiErrorCodes?.firstOrNull { it.code.equals(errorCode, ignoreCase = true) } ?: defaultApiErrorCode + InfomaniakCore.apiErrorCodes?.firstOrNull { it.code.equals(errorCode, ignoreCase = true) } + ?: TranslatedInternalErrorCode.entries.firstOrNull { it.code.equals(errorCode, ignoreCase = true) } + ?: defaultApiErrorCode } } } From 649186cdfe9692d07bf1fc7260abd6ccd3efe449 Mon Sep 17 00:00:00 2001 From: Gibran Chevalley Date: Mon, 28 Apr 2025 14:11:08 +0200 Subject: [PATCH 02/11] feat: Also translate missing server errors --- .../infomaniak/lib/core/api/ApiController.kt | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt index 31590c5bc..643fb837d 100644 --- a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt +++ b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt @@ -192,8 +192,12 @@ object ApiController { scope.setExtra("bodyResponse", bodyResponse) } createErrorResponse( - apiError = createApiError(useKotlinxSerialization, bodyResponse, ServerErrorException(bodyResponse)), - translatedError = R.string.serverError, + apiError = TranslatedInternalErrorCode.ServerError.toApiError( + useKotlinxSerialization, + bodyResponse, + ServerErrorException(bodyResponse), + ), + translatedError = TranslatedInternalErrorCode.ServerError.translateRes, buildErrorResult = buildErrorResult, ) } @@ -260,7 +264,13 @@ object ApiController { ) } - fun createApiError(useKotlinxSerialization: Boolean, bodyResponse: String, exception: Exception) = ApiError( + fun createApiError( + useKotlinxSerialization: Boolean, + bodyResponse: String, + exception: Exception, + code: String? = null, + ) = ApiError( + code = code, contextJson = if (useKotlinxSerialization) bodyResponse.bodyResponseToJson() else null, contextGson = when { useKotlinxSerialization -> null @@ -291,7 +301,14 @@ object ApiController { ) : ErrorCode.Translated { NoConnection("no_connection", R.string.noConnection), ConnectionError("connection_error", R.string.connectionError), + ServerError("server_error", R.string.serverError), } fun ErrorCode.toApiError(exception: Exception): ApiError = ApiError(code = code, exception = exception) + + fun ErrorCode.toApiError( + useKotlinxSerialization: Boolean, + bodyResponse: String, + exception: Exception, + ): ApiError = createApiError(useKotlinxSerialization, bodyResponse, exception, code) } From b1b1df816f44c711dc782feca7d90a3116a94423 Mon Sep 17 00:00:00 2001 From: Gibran Chevalley Date: Mon, 28 Apr 2025 14:15:23 +0200 Subject: [PATCH 03/11] feat: Define UnknownError --- .../infomaniak/lib/core/api/ApiController.kt | 17 +++++++++++++---- .../infomaniak/lib/core/utils/ApiErrorCode.kt | 11 ++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt index 643fb837d..024e54e12 100644 --- a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt +++ b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt @@ -207,7 +207,11 @@ object ApiController { } } catch (refreshTokenException: RefreshTokenException) { refreshTokenException.printStackTrace() - return createErrorResponse(translatedError = R.string.anErrorHasOccurred, buildErrorResult = buildErrorResult) + return createErrorResponse( + translatedError = TranslatedInternalErrorCode.UnknownError.translateRes, + apiError = TranslatedInternalErrorCode.UnknownError.toApiError(), + buildErrorResult = buildErrorResult, + ) } catch (exception: Exception) { exception.printStackTrace() @@ -222,8 +226,12 @@ object ApiController { } createErrorResponse( - translatedError = R.string.anErrorHasOccurred, - apiError = createApiError(useKotlinxSerialization, bodyResponse, exception = exception), + translatedError = TranslatedInternalErrorCode.UnknownError.translateRes, + apiError = TranslatedInternalErrorCode.UnknownError.toApiError( + useKotlinxSerialization, + bodyResponse, + exception, + ), buildErrorResult = buildErrorResult, ) } @@ -302,9 +310,10 @@ object ApiController { NoConnection("no_connection", R.string.noConnection), ConnectionError("connection_error", R.string.connectionError), ServerError("server_error", R.string.serverError), + UnknownError("an_error_has_occurred", R.string.anErrorHasOccurred), } - fun ErrorCode.toApiError(exception: Exception): ApiError = ApiError(code = code, exception = exception) + fun ErrorCode.toApiError(exception: Exception? = null): ApiError = ApiError(code = code, exception = exception) fun ErrorCode.toApiError( useKotlinxSerialization: Boolean, diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt b/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt index 8851977bc..906456454 100644 --- a/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt +++ b/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt @@ -19,7 +19,6 @@ package com.infomaniak.lib.core.utils import androidx.annotation.StringRes import com.infomaniak.lib.core.InfomaniakCore -import com.infomaniak.lib.core.R import com.infomaniak.lib.core.api.ApiController.TranslatedInternalErrorCode import com.infomaniak.lib.core.models.ApiResponse @@ -34,24 +33,18 @@ interface ErrorCode { } data class ApiErrorCode(override val code: String, @StringRes override val translateRes: Int) : ErrorCode.Translated { - companion object { - - const val AN_ERROR_HAS_OCCURRED = "an_error_has_occured" - - private val defaultApiErrorCode = ApiErrorCode(AN_ERROR_HAS_OCCURRED, R.string.anErrorHasOccurred) - @StringRes fun ApiResponse.translateError(): Int = formatError().translateRes fun ApiResponse.formatError(): ErrorCode.Translated { val errorCode = error?.code return if (errorCode == null) { - defaultApiErrorCode + TranslatedInternalErrorCode.UnknownError } else { InfomaniakCore.apiErrorCodes?.firstOrNull { it.code.equals(errorCode, ignoreCase = true) } ?: TranslatedInternalErrorCode.entries.firstOrNull { it.code.equals(errorCode, ignoreCase = true) } - ?: defaultApiErrorCode + ?: TranslatedInternalErrorCode.UnknownError } } } From 49bf958c195edc97e85396bab141ebe6700508f1 Mon Sep 17 00:00:00 2001 From: Gibran Chevalley Date: Mon, 28 Apr 2025 15:47:04 +0200 Subject: [PATCH 04/11] feat: Factorize internal error code access inside createErrorResponse --- .../infomaniak/lib/core/api/ApiController.kt | 85 +++++++------------ .../infomaniak/lib/core/utils/ApiErrorCode.kt | 1 - 2 files changed, 32 insertions(+), 54 deletions(-) diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt index 024e54e12..cfc518a9b 100644 --- a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt +++ b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt @@ -192,12 +192,8 @@ object ApiController { scope.setExtra("bodyResponse", bodyResponse) } createErrorResponse( - apiError = TranslatedInternalErrorCode.ServerError.toApiError( - useKotlinxSerialization, - bodyResponse, - ServerErrorException(bodyResponse), - ), - translatedError = TranslatedInternalErrorCode.ServerError.translateRes, + TranslatedInternalErrorCode.ServerError, + InternalErrorPayload(ServerErrorException(bodyResponse), useKotlinxSerialization, bodyResponse), buildErrorResult = buildErrorResult, ) } @@ -207,11 +203,7 @@ object ApiController { } } catch (refreshTokenException: RefreshTokenException) { refreshTokenException.printStackTrace() - return createErrorResponse( - translatedError = TranslatedInternalErrorCode.UnknownError.translateRes, - apiError = TranslatedInternalErrorCode.UnknownError.toApiError(), - buildErrorResult = buildErrorResult, - ) + return createErrorResponse(TranslatedInternalErrorCode.UnknownError, buildErrorResult = buildErrorResult) } catch (exception: Exception) { exception.printStackTrace() @@ -226,12 +218,8 @@ object ApiController { } createErrorResponse( - translatedError = TranslatedInternalErrorCode.UnknownError.translateRes, - apiError = TranslatedInternalErrorCode.UnknownError.toApiError( - useKotlinxSerialization, - bodyResponse, - exception, - ), + TranslatedInternalErrorCode.UnknownError, + InternalErrorPayload(exception, useKotlinxSerialization, bodyResponse), buildErrorResult = buildErrorResult, ) } @@ -258,40 +246,20 @@ object ApiController { inline fun createInternetErrorResponse( noNetwork: Boolean = false, noinline buildErrorResult: ((apiError: ApiError?, translatedErrorRes: Int) -> T)?, - ): T { - val internalNetworkError = if (noNetwork) { - TranslatedInternalErrorCode.NoConnection - } else { - TranslatedInternalErrorCode.ConnectionError - } - - return createErrorResponse( - translatedError = internalNetworkError.translateRes, - apiError = internalNetworkError.toApiError(NetworkException()), - buildErrorResult = buildErrorResult, - ) - } - - fun createApiError( - useKotlinxSerialization: Boolean, - bodyResponse: String, - exception: Exception, - code: String? = null, - ) = ApiError( - code = code, - contextJson = if (useKotlinxSerialization) bodyResponse.bodyResponseToJson() else null, - contextGson = when { - useKotlinxSerialization -> null - else -> runCatching { JsonParser.parseString(bodyResponse).asJsonObject }.getOrDefault(null) - }, - exception = exception + ): T = createErrorResponse( + if (noNetwork) TranslatedInternalErrorCode.NoConnection else TranslatedInternalErrorCode.ConnectionError, + InternalErrorPayload(NetworkException()), + buildErrorResult = buildErrorResult, ) inline fun createErrorResponse( - @StringRes translatedError: Int, - apiError: ApiError? = null, + internalErrorCode: TranslatedInternalErrorCode, + payload: InternalErrorPayload? = null, noinline buildErrorResult: ((apiError: ApiError?, translatedErrorRes: Int) -> T)?, ): T { + val apiError = internalErrorCode.toApiError(payload) + val translatedError = internalErrorCode.translateRes + return buildErrorResult?.invoke(apiError, translatedError) ?: ApiResponse(result = ERROR, error = apiError, translatedError = translatedError) as T } @@ -303,6 +271,12 @@ object ApiController { GET, PUT, POST, DELETE, PATCH } + data class InternalErrorPayload( + val exception: Exception? = null, + val useKotlinxSerialization: Boolean? = null, + val bodyResponse: String? = null, + ) + enum class TranslatedInternalErrorCode( override val code: String, @StringRes override val translateRes: Int, @@ -313,11 +287,16 @@ object ApiController { UnknownError("an_error_has_occurred", R.string.anErrorHasOccurred), } - fun ErrorCode.toApiError(exception: Exception? = null): ApiError = ApiError(code = code, exception = exception) - - fun ErrorCode.toApiError( - useKotlinxSerialization: Boolean, - bodyResponse: String, - exception: Exception, - ): ApiError = createApiError(useKotlinxSerialization, bodyResponse, exception, code) + fun ErrorCode.toApiError(payload: InternalErrorPayload?): ApiError { + val useKotlinxSerialization = payload?.useKotlinxSerialization + return ApiError( + code = code, + contextJson = if (useKotlinxSerialization == true) payload.bodyResponse?.bodyResponseToJson() else null, + contextGson = when { + useKotlinxSerialization == true -> null + else -> runCatching { payload?.let { JsonParser.parseString(it.bodyResponse).asJsonObject } }.getOrDefault(null) + }, + exception = payload?.exception + ) + } } diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt b/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt index 906456454..8e60d9731 100644 --- a/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt +++ b/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt @@ -26,7 +26,6 @@ interface ErrorCode { val code: String interface Translated : ErrorCode { - override val code: String @get:StringRes val translateRes: Int } From 8bc0787fcaee435c7988e27f64e707fe363ec170 Mon Sep 17 00:00:00 2001 From: Gibran Chevalley Date: Mon, 28 Apr 2025 16:01:46 +0200 Subject: [PATCH 05/11] feat: Remove server error translation to the user --- .../src/main/java/com/infomaniak/lib/core/api/ApiController.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt index cfc518a9b..78bcb9216 100644 --- a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt +++ b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt @@ -192,7 +192,7 @@ object ApiController { scope.setExtra("bodyResponse", bodyResponse) } createErrorResponse( - TranslatedInternalErrorCode.ServerError, + TranslatedInternalErrorCode.UnknownError, InternalErrorPayload(ServerErrorException(bodyResponse), useKotlinxSerialization, bodyResponse), buildErrorResult = buildErrorResult, ) @@ -283,7 +283,6 @@ object ApiController { ) : ErrorCode.Translated { NoConnection("no_connection", R.string.noConnection), ConnectionError("connection_error", R.string.connectionError), - ServerError("server_error", R.string.serverError), UnknownError("an_error_has_occurred", R.string.anErrorHasOccurred), } From dac064d71e2f58227c60d8231a32def84ae6a0cf Mon Sep 17 00:00:00 2001 From: Gibran Chevalley Date: Mon, 28 Apr 2025 16:24:07 +0200 Subject: [PATCH 06/11] docs: Add deprecated explaination to the translatedError field --- .../main/java/com/infomaniak/lib/core/models/ApiResponse.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/models/ApiResponse.kt b/Legacy/src/main/java/com/infomaniak/lib/core/models/ApiResponse.kt index 45083fc0f..89775d24a 100644 --- a/Legacy/src/main/java/com/infomaniak/lib/core/models/ApiResponse.kt +++ b/Legacy/src/main/java/com/infomaniak/lib/core/models/ApiResponse.kt @@ -34,7 +34,11 @@ open class ApiResponse( @SerializedName("response_at") val responseAt: Long = 0, val total: Int = 0, - @Deprecated("(╯°□°)╯︵ ┻━┻") // TODO + @Deprecated( + "translatedError doesn't take into account project specific translated errors specified through " + + "InfomaniakCore.apiErrorCodes. Use translateError() instead", + ReplaceWith("translateError()", "com.infomaniak.lib.core.utils.ApiErrorCode.Companion.translateError"), + ) var translatedError: Int = 0, @SerialName("items_per_page") @SerializedName("items_per_page") From aa1b9fd6e5797c99e6dda11208e989266a9e8061 Mon Sep 17 00:00:00 2001 From: Gibran Chevalley Date: Tue, 29 Apr 2025 09:12:45 +0200 Subject: [PATCH 07/11] feat: Extract InternalTranslatedErrorCode to make it reusable and add a core error for mail --- .../infomaniak/lib/core/api/ApiController.kt | 22 ++++--------- .../core/api/InternalTranslatedErrorCode.kt | 32 +++++++++++++++++++ .../infomaniak/lib/core/utils/ApiErrorCode.kt | 8 ++--- 3 files changed, 42 insertions(+), 20 deletions(-) create mode 100644 Legacy/src/main/java/com/infomaniak/lib/core/api/InternalTranslatedErrorCode.kt diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt index 78bcb9216..23430b00b 100644 --- a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt +++ b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt @@ -17,7 +17,6 @@ */ package com.infomaniak.lib.core.api -import androidx.annotation.StringRes import com.google.gson.Gson import com.google.gson.GsonBuilder import com.google.gson.JsonParser @@ -192,7 +191,7 @@ object ApiController { scope.setExtra("bodyResponse", bodyResponse) } createErrorResponse( - TranslatedInternalErrorCode.UnknownError, + InternalTranslatedErrorCode.UnknownError, InternalErrorPayload(ServerErrorException(bodyResponse), useKotlinxSerialization, bodyResponse), buildErrorResult = buildErrorResult, ) @@ -203,7 +202,7 @@ object ApiController { } } catch (refreshTokenException: RefreshTokenException) { refreshTokenException.printStackTrace() - return createErrorResponse(TranslatedInternalErrorCode.UnknownError, buildErrorResult = buildErrorResult) + return createErrorResponse(InternalTranslatedErrorCode.UnknownError, buildErrorResult = buildErrorResult) } catch (exception: Exception) { exception.printStackTrace() @@ -218,7 +217,7 @@ object ApiController { } createErrorResponse( - TranslatedInternalErrorCode.UnknownError, + InternalTranslatedErrorCode.UnknownError, InternalErrorPayload(exception, useKotlinxSerialization, bodyResponse), buildErrorResult = buildErrorResult, ) @@ -247,13 +246,13 @@ object ApiController { noNetwork: Boolean = false, noinline buildErrorResult: ((apiError: ApiError?, translatedErrorRes: Int) -> T)?, ): T = createErrorResponse( - if (noNetwork) TranslatedInternalErrorCode.NoConnection else TranslatedInternalErrorCode.ConnectionError, + if (noNetwork) InternalTranslatedErrorCode.NoConnection else InternalTranslatedErrorCode.ConnectionError, InternalErrorPayload(NetworkException()), buildErrorResult = buildErrorResult, ) inline fun createErrorResponse( - internalErrorCode: TranslatedInternalErrorCode, + internalErrorCode: InternalTranslatedErrorCode, payload: InternalErrorPayload? = null, noinline buildErrorResult: ((apiError: ApiError?, translatedErrorRes: Int) -> T)?, ): T { @@ -277,16 +276,7 @@ object ApiController { val bodyResponse: String? = null, ) - enum class TranslatedInternalErrorCode( - override val code: String, - @StringRes override val translateRes: Int, - ) : ErrorCode.Translated { - NoConnection("no_connection", R.string.noConnection), - ConnectionError("connection_error", R.string.connectionError), - UnknownError("an_error_has_occurred", R.string.anErrorHasOccurred), - } - - fun ErrorCode.toApiError(payload: InternalErrorPayload?): ApiError { + fun ErrorCode.toApiError(payload: InternalErrorPayload? = null): ApiError { val useKotlinxSerialization = payload?.useKotlinxSerialization return ApiError( code = code, diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/api/InternalTranslatedErrorCode.kt b/Legacy/src/main/java/com/infomaniak/lib/core/api/InternalTranslatedErrorCode.kt new file mode 100644 index 000000000..af2c11820 --- /dev/null +++ b/Legacy/src/main/java/com/infomaniak/lib/core/api/InternalTranslatedErrorCode.kt @@ -0,0 +1,32 @@ +/* + * Infomaniak Core - Android + * Copyright (C) 2025 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.lib.core.api + +import androidx.annotation.StringRes +import com.infomaniak.lib.core.R +import com.infomaniak.lib.core.utils.ErrorCode + +enum class InternalTranslatedErrorCode( + override val code: String, + @StringRes override val translateRes: Int, +) : ErrorCode.Translated { + NoConnection("no_connection", R.string.noConnection), + ConnectionError("connection_error", R.string.connectionError), + UnknownError("an_error_has_occurred", R.string.anErrorHasOccurred), + UserAlreadyPresent("user_already_present", R.string.errorUserAlreadyPresent), +} diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt b/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt index 8e60d9731..0a8e8e596 100644 --- a/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt +++ b/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt @@ -19,7 +19,7 @@ package com.infomaniak.lib.core.utils import androidx.annotation.StringRes import com.infomaniak.lib.core.InfomaniakCore -import com.infomaniak.lib.core.api.ApiController.TranslatedInternalErrorCode +import com.infomaniak.lib.core.api.InternalTranslatedErrorCode import com.infomaniak.lib.core.models.ApiResponse interface ErrorCode { @@ -39,11 +39,11 @@ data class ApiErrorCode(override val code: String, @StringRes override val trans fun ApiResponse.formatError(): ErrorCode.Translated { val errorCode = error?.code return if (errorCode == null) { - TranslatedInternalErrorCode.UnknownError + InternalTranslatedErrorCode.UnknownError } else { InfomaniakCore.apiErrorCodes?.firstOrNull { it.code.equals(errorCode, ignoreCase = true) } - ?: TranslatedInternalErrorCode.entries.firstOrNull { it.code.equals(errorCode, ignoreCase = true) } - ?: TranslatedInternalErrorCode.UnknownError + ?: InternalTranslatedErrorCode.entries.firstOrNull { it.code.equals(errorCode, ignoreCase = true) } + ?: InternalTranslatedErrorCode.UnknownError } } } From 4a27297b5cf9a813fd00f9db6c376078c1f7493b Mon Sep 17 00:00:00 2001 From: Gibran Chevalley Date: Tue, 29 Apr 2025 09:25:17 +0200 Subject: [PATCH 08/11] fix: Add missing error field to createApiResponse() method --- .../main/java/com/infomaniak/lib/core/api/ApiController.kt | 4 ++-- .../main/java/com/infomaniak/lib/core/models/ApiResponse.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt index 23430b00b..124e91220 100644 --- a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt +++ b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt @@ -23,7 +23,6 @@ import com.google.gson.JsonParser import com.google.gson.reflect.TypeToken import com.infomaniak.lib.core.BuildConfig.LOGIN_ENDPOINT_URL import com.infomaniak.lib.core.InfomaniakCore -import com.infomaniak.lib.core.R import com.infomaniak.lib.core.auth.TokenInterceptorListener import com.infomaniak.lib.core.models.ApiError import com.infomaniak.lib.core.models.ApiResponse @@ -232,7 +231,8 @@ object ApiController { } if (apiResponse is ApiResponse<*> && apiResponse.result == ERROR) { - apiResponse.translatedError = R.string.anErrorHasOccurred + apiResponse.translatedError = InternalTranslatedErrorCode.UnknownError.translateRes + apiResponse.error = InternalTranslatedErrorCode.UnknownError.toApiError() } return apiResponse diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/models/ApiResponse.kt b/Legacy/src/main/java/com/infomaniak/lib/core/models/ApiResponse.kt index 89775d24a..487c90a9d 100644 --- a/Legacy/src/main/java/com/infomaniak/lib/core/models/ApiResponse.kt +++ b/Legacy/src/main/java/com/infomaniak/lib/core/models/ApiResponse.kt @@ -27,7 +27,7 @@ open class ApiResponse( val result: ApiResponseStatus = ApiResponseStatus.UNKNOWN, val data: @RawValue T? = null, val uri: String? = null, - val error: ApiError? = null, + var error: ApiError? = null, val page: Int = 0, val pages: Int = 0, @SerialName("response_at") From 455710fcba00a1038525749da3fbf91f984cb7bd Mon Sep 17 00:00:00 2001 From: Gibran Chevalley Date: Thu, 1 May 2025 13:44:03 +0200 Subject: [PATCH 09/11] chore: Add deprecation lint supress when translatedError is used for good reasons --- .../src/main/java/com/infomaniak/lib/core/api/ApiController.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt index 124e91220..7f607fea2 100644 --- a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt +++ b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt @@ -231,6 +231,7 @@ object ApiController { } if (apiResponse is ApiResponse<*> && apiResponse.result == ERROR) { + @Suppress("DEPRECATION") apiResponse.translatedError = InternalTranslatedErrorCode.UnknownError.translateRes apiResponse.error = InternalTranslatedErrorCode.UnknownError.toApiError() } From 2736e8e1e9c67b54f303d6330f50ef1efe906aa1 Mon Sep 17 00:00:00 2001 From: Gibran Chevalley Date: Thu, 1 May 2025 13:44:30 +0200 Subject: [PATCH 10/11] refactor: Merge ErrorCore.Translate into ErrorCodeTranslate --- .../com/infomaniak/lib/core/api/ApiController.kt | 4 ++-- .../lib/core/api/InternalTranslatedErrorCode.kt | 4 ++-- .../com/infomaniak/lib/core/utils/ApiErrorCode.kt | 13 +++++-------- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt index 7f607fea2..b5e1cf104 100644 --- a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt +++ b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt @@ -30,7 +30,7 @@ import com.infomaniak.lib.core.models.ApiResponseStatus.ERROR import com.infomaniak.lib.core.networking.HttpClient import com.infomaniak.lib.core.networking.HttpUtils import com.infomaniak.lib.core.utils.CustomDateTypeAdapter -import com.infomaniak.lib.core.utils.ErrorCode +import com.infomaniak.lib.core.utils.ErrorCodeTranslated import com.infomaniak.lib.core.utils.isNetworkException import com.infomaniak.lib.core.utils.isSerializationException import com.infomaniak.lib.login.ApiToken @@ -277,7 +277,7 @@ object ApiController { val bodyResponse: String? = null, ) - fun ErrorCode.toApiError(payload: InternalErrorPayload? = null): ApiError { + fun ErrorCodeTranslated.toApiError(payload: InternalErrorPayload? = null): ApiError { val useKotlinxSerialization = payload?.useKotlinxSerialization return ApiError( code = code, diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/api/InternalTranslatedErrorCode.kt b/Legacy/src/main/java/com/infomaniak/lib/core/api/InternalTranslatedErrorCode.kt index af2c11820..fb3d9d0a3 100644 --- a/Legacy/src/main/java/com/infomaniak/lib/core/api/InternalTranslatedErrorCode.kt +++ b/Legacy/src/main/java/com/infomaniak/lib/core/api/InternalTranslatedErrorCode.kt @@ -19,12 +19,12 @@ package com.infomaniak.lib.core.api import androidx.annotation.StringRes import com.infomaniak.lib.core.R -import com.infomaniak.lib.core.utils.ErrorCode +import com.infomaniak.lib.core.utils.ErrorCodeTranslated enum class InternalTranslatedErrorCode( override val code: String, @StringRes override val translateRes: Int, -) : ErrorCode.Translated { +) : ErrorCodeTranslated { NoConnection("no_connection", R.string.noConnection), ConnectionError("connection_error", R.string.connectionError), UnknownError("an_error_has_occurred", R.string.anErrorHasOccurred), diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt b/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt index 0a8e8e596..cc96d8b37 100644 --- a/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt +++ b/Legacy/src/main/java/com/infomaniak/lib/core/utils/ApiErrorCode.kt @@ -22,21 +22,18 @@ import com.infomaniak.lib.core.InfomaniakCore import com.infomaniak.lib.core.api.InternalTranslatedErrorCode import com.infomaniak.lib.core.models.ApiResponse -interface ErrorCode { +interface ErrorCodeTranslated { val code: String - - interface Translated : ErrorCode { - @get:StringRes - val translateRes: Int - } + @get:StringRes + val translateRes: Int } -data class ApiErrorCode(override val code: String, @StringRes override val translateRes: Int) : ErrorCode.Translated { +data class ApiErrorCode(override val code: String, @StringRes override val translateRes: Int) : ErrorCodeTranslated { companion object { @StringRes fun ApiResponse.translateError(): Int = formatError().translateRes - fun ApiResponse.formatError(): ErrorCode.Translated { + fun ApiResponse.formatError(): ErrorCodeTranslated { val errorCode = error?.code return if (errorCode == null) { InternalTranslatedErrorCode.UnknownError From 14ea13af409dc4fb4529f846a582fceefa53f33e Mon Sep 17 00:00:00 2001 From: Gibran Chevalley Date: Fri, 2 May 2025 09:09:07 +0200 Subject: [PATCH 11/11] refactor: Hide toApiError(payload) signature as best as we can and only expose toApiError() --- .../com/infomaniak/lib/core/api/ApiController.kt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt index b5e1cf104..184a32963 100644 --- a/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt +++ b/Legacy/src/main/java/com/infomaniak/lib/core/api/ApiController.kt @@ -257,7 +257,7 @@ object ApiController { payload: InternalErrorPayload? = null, noinline buildErrorResult: ((apiError: ApiError?, translatedErrorRes: Int) -> T)?, ): T { - val apiError = internalErrorCode.toApiError(payload) + val apiError = createDetailedApiError(internalErrorCode, payload) val translatedError = internalErrorCode.translateRes return buildErrorResult?.invoke(apiError, translatedError) @@ -277,14 +277,18 @@ object ApiController { val bodyResponse: String? = null, ) - fun ErrorCodeTranslated.toApiError(payload: InternalErrorPayload? = null): ApiError { + fun ErrorCodeTranslated.toApiError(): ApiError = createDetailedApiError(this, null) + + fun createDetailedApiError(errorCode: ErrorCodeTranslated, payload: InternalErrorPayload? = null): ApiError { val useKotlinxSerialization = payload?.useKotlinxSerialization return ApiError( - code = code, + code = errorCode.code, contextJson = if (useKotlinxSerialization == true) payload.bodyResponse?.bodyResponseToJson() else null, contextGson = when { useKotlinxSerialization == true -> null - else -> runCatching { payload?.let { JsonParser.parseString(it.bodyResponse).asJsonObject } }.getOrDefault(null) + else -> errorCode.runCatching { + payload?.let { JsonParser.parseString(it.bodyResponse).asJsonObject } + }.getOrDefault(null) }, exception = payload?.exception )