API error "info" key is dynamic
Описание
Рассмотрим, например, вариант ошибки из модуля Auth.
{
"success": false,
"message": "Invalid captcha",
"error_type": "authorization.captcha"
"info": {
"site_key": "6Le7R8IUAAAAAMCwR4b...."
}
}
Можно обратить внимание, что в этом случае по ключу info
содержаться данные в формате [String: String]
.
Теперь посмотрим какой вид имеет этот же ключ у модуля Invitations.
{
"success": false,
"error_type": "validation",
"message": "Validation error",
"info": {
"users.0.email": [
"The email field is required."
]
}
}
Тут уже в info
данные в формате [String: [String]]
.
Проблема
Если мы попытаемся сделать абстракцию для ошибок апи, то у нас по ключу info
должно содержаться значение типа [String: Any]
. А такой тип не парсится декодером JSON, потому что Any
- неопределенный тип и не может быть однзначно распакован.
Получается я должен для каждого модуля/запроса делать отдельную структуру ошибок?
Сейчас она у меня имеет вид:
struct CattrAPIErrorContainer: ResponseContainer {
let success: Bool
let message: String
let errorType: CattrAPIErrorCode
var info: [String:String]? = nil
}
И только лишь из-за одного неоднозначного поля мне нужно либо игнорировать этот ключ совсем, либо под каждый модуль создавать кучу таких структур. Можно конечно как-то исхитриться и попробовать обыграть это через дженерик, но если инфо отличается даже в рамках одного модуля (что скорее всего так), то это невозможно. Это поле придется тогда игнорировать.
Решение
На мой взгляд поле info
выполняет две функции, которые можно разделить.
- Для детального описания проблемы можно использовать отдельный ключ
details
типаString
. - В случае если мы хотим указать на источник проблем с валадацией, так же задать отдельный опциональный ключ (например
source
), который будет являться объектом только с двумя свойствами (parameter: String
иdescription: String
). Таким образом все ключи будут жестко детерминированы и тут уже можно абстрагироваться от ошибки. В итоге абстрактный объект ошибки имеет вид:
{
message: string,
error_type: string,
details: string,
source: {
parameter: string,
description: string
} (optional)
}
Пример ответа:
{
"message": "Validation error",
"error_type": "core.validation",
"details": "One of the parameters has malformed data. Please see source key.",
"source": {
"parameter": "users.0.email",
"description": "The email field is required."
}
}
Или без ключа source
:
{
"message": "Not authorized",
"error_type": "authorization.unauthorized",
"details": "Please provide valid token with 'Authorized' header."
}