bug_bunny 3.1.2 → 3.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +25 -1
- data/lib/bug_bunny/exception.rb +45 -21
- data/lib/bug_bunny/middleware/raise_error.rb +68 -18
- data/lib/bug_bunny/resource.rb +34 -41
- data/lib/bug_bunny/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4aab7949b09bfbf861d4a9ff736bc3bef832fdb3c8a302924544783bdbc9e1bf
|
|
4
|
+
data.tar.gz: 12b73641a812ec4ce72d2a5ad8992a1f2b91c4b0b11ce181aac02fe3a4912751
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2f4475f1754c1de91be6576d4fabb23d025b1f3735d4d76d3123415e71b907d2bfc307dd8e8f4a921ad5a11b2524cd85a1ee17ea3fb1d42f6e15c62ddef01425
|
|
7
|
+
data.tar.gz: c3973288d2d394121491ffb3c7fa809b8841c9fdc3fc87486fb807b62b6f948e48298fd9f4b696132c9b86d448dad82bd4cfa909c39384efc2ab1b7729c15f31
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
|
+
## [3.1.3] - 2026-02-19
|
|
3
|
+
|
|
4
|
+
### 🏗️ Architectural Refactoring (Middleware Standardization)
|
|
5
|
+
* **Centralized Error Handling:** Refactored `BugBunny::Resource` to completely delegate HTTP status evaluation to the `RaiseError` middleware. The ORM now operates strictly on a "Happy Path" mentality, rescuing semantic exceptions (`NotFound`, `UnprocessableEntity`) natively.
|
|
6
|
+
* **Middleware Injection Enforcement:** `BugBunny::Resource` now explicitly guarantees that `BugBunny::Middleware::RaiseError` and `BugBunny::Middleware::JsonResponse` are the core of the stack, ensuring consistent data parsing and error raising before any custom user middlewares are executed.
|
|
7
|
+
|
|
8
|
+
### ✨ New Features & Improvements
|
|
9
|
+
* **Smart Validation Errors:** `BugBunny::UnprocessableEntity` (422) is now intelligent. It automatically parses the remote worker's response payload, gracefully handling string fallbacks or extracting the standard Rails `{ errors: ... }` convention to accurately populate local object validations.
|
|
10
|
+
* **HTTP 409 Conflict Support:** Added native support for `409 Conflict` mapping it to the new `BugBunny::Conflict` exception. Ideal for handling state collisions in distributed systems.
|
|
11
|
+
* **Global Error Formatting:** Moved `format_error_message` directly into the `RaiseError` middleware. Now, even manual `BugBunny::Client` requests will benefit from clean, structured exception messages (e.g., `"Internal Server Error - undefined method"`) optimized for APMs like Sentry or Datadog.
|
|
12
|
+
|
|
2
13
|
## [3.1.2] - 2026-02-19
|
|
3
14
|
|
|
4
15
|
### 🐛 Bug Fixes
|
data/README.md
CHANGED
|
@@ -188,7 +188,7 @@ Manager::Service.with(
|
|
|
188
188
|
Intercepta peticiones de ida y respuestas de vuelta en la arquitectura del cliente.
|
|
189
189
|
|
|
190
190
|
**Middlewares Incluidos (Built-ins)**
|
|
191
|
-
Si usas `BugBunny::Resource
|
|
191
|
+
Si usas `BugBunny::Resource`, el manejo de JSON y de errores ya está integrado automáticamente. Pero si utilizas el cliente manual (`BugBunny::Client`), puedes inyectar los middlewares incluidos para no tener que parsear respuestas manualmente:
|
|
192
192
|
|
|
193
193
|
* `BugBunny::Middleware::JsonResponse`: Parsea automáticamente el cuerpo de la respuesta de JSON a un Hash de Ruby.
|
|
194
194
|
* `BugBunny::Middleware::RaiseError`: Evalúa el código de estado (`status`) de la respuesta y lanza excepciones nativas (`BugBunny::NotFound`, `BugBunny::UnprocessableEntity`, `BugBunny::InternalServerError`, etc.).
|
|
@@ -220,6 +220,30 @@ class Manager::Service < BugBunny::Resource
|
|
|
220
220
|
end
|
|
221
221
|
```
|
|
222
222
|
|
|
223
|
+
**Personalización Avanzada de Errores**
|
|
224
|
+
Si en tu aplicación necesitas mapear códigos HTTP de negocio (ej. `402 Payment Required`) a excepciones personalizadas, la forma más limpia es usar `Module#prepend` sobre el middleware nativo en un inicializador. De esta forma inyectas tus reglas sin perder el comportamiento por defecto para los demás errores:
|
|
225
|
+
|
|
226
|
+
```ruby
|
|
227
|
+
# config/initializers/bug_bunny_custom_errors.rb
|
|
228
|
+
module CustomBugBunnyErrors
|
|
229
|
+
def on_complete(response)
|
|
230
|
+
status = response['status'].to_i
|
|
231
|
+
|
|
232
|
+
# 1. Reglas específicas de tu negocio
|
|
233
|
+
if status == 402
|
|
234
|
+
raise MyApp::PaymentRequiredError, response['body']['message']
|
|
235
|
+
elsif status == 403 && response['body']['reason'] == 'ip_blocked'
|
|
236
|
+
raise MyApp::IpBlockedError, response['body']['detail']
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# 2. Delegar el resto de los errores (404, 422, 500) al middleware original
|
|
240
|
+
super(response)
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
BugBunny::Middleware::RaiseError.prepend(CustomBugBunnyErrors)
|
|
245
|
+
```
|
|
246
|
+
|
|
223
247
|
---
|
|
224
248
|
|
|
225
249
|
## 📡 Modo Servidor: Controladores
|
data/lib/bug_bunny/exception.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require 'json'
|
|
3
4
|
|
|
4
5
|
module BugBunny
|
|
@@ -11,10 +12,12 @@ module BugBunny
|
|
|
11
12
|
class CommunicationError < Error; end
|
|
12
13
|
|
|
13
14
|
# Error lanzado cuando ocurren un acceso no permitido a controladores.
|
|
14
|
-
#
|
|
15
|
+
# Protege contra vulnerabilidades de RCE validando la herencia de las clases enrutadas.
|
|
15
16
|
class SecurityError < Error; end
|
|
16
17
|
|
|
17
|
-
#
|
|
18
|
+
# ==========================================
|
|
19
|
+
# Categoría: Errores del Cliente (4xx)
|
|
20
|
+
# ==========================================
|
|
18
21
|
|
|
19
22
|
# Clase base para errores causados por una petición incorrecta del cliente.
|
|
20
23
|
# Corresponde a códigos de estado 400-499.
|
|
@@ -36,7 +39,13 @@ module BugBunny
|
|
|
36
39
|
# El servidor tardó demasiado en responder o el cliente agotó su tiempo de espera (RPC timeout).
|
|
37
40
|
class RequestTimeout < ClientError; end
|
|
38
41
|
|
|
39
|
-
#
|
|
42
|
+
# Error 409: Conflict.
|
|
43
|
+
# implica que la petición es técnicamente válida, pero choca con reglas de negocio o datos existentes
|
|
44
|
+
class Conflict < ClientError; end
|
|
45
|
+
|
|
46
|
+
# ==========================================
|
|
47
|
+
# Categoría: Errores del Servidor (5xx)
|
|
48
|
+
# ==========================================
|
|
40
49
|
|
|
41
50
|
# Clase base para errores causados por fallos en el servidor remoto.
|
|
42
51
|
# Corresponde a códigos de estado 500-599.
|
|
@@ -46,44 +55,59 @@ module BugBunny
|
|
|
46
55
|
# Ocurrió un error inesperado en el worker/servidor remoto al procesar el mensaje.
|
|
47
56
|
class InternalServerError < ServerError; end
|
|
48
57
|
|
|
49
|
-
#
|
|
58
|
+
# ==========================================
|
|
59
|
+
# Categoría: Errores de Validación (422)
|
|
60
|
+
# ==========================================
|
|
50
61
|
|
|
51
62
|
# Error 422: Unprocessable Entity.
|
|
52
63
|
# Indica que la solicitud fue bien formada pero contenía errores semánticos,
|
|
53
64
|
# típicamente fallos de validación en el modelo remoto (ActiveRecord).
|
|
54
65
|
#
|
|
55
|
-
# Esta excepción es
|
|
56
|
-
# para exponer los mensajes de error de forma estructurada
|
|
66
|
+
# Esta excepción es "inteligente": intenta parsear automáticamente el cuerpo
|
|
67
|
+
# de la respuesta para extraer y exponer los mensajes de error de forma estructurada,
|
|
68
|
+
# buscando por convención la clave `errors`.
|
|
57
69
|
class UnprocessableEntity < ClientError
|
|
58
|
-
# @return [Hash, Array] Los mensajes de error
|
|
70
|
+
# @return [Hash, Array, String] Los mensajes de error listos para ser iterados.
|
|
59
71
|
attr_reader :error_messages
|
|
60
72
|
|
|
61
|
-
# @return [String] El cuerpo crudo de la respuesta original.
|
|
73
|
+
# @return [String, Hash] El cuerpo crudo de la respuesta original.
|
|
62
74
|
attr_reader :raw_response
|
|
63
75
|
|
|
64
76
|
# Inicializa la excepción procesando el cuerpo de la respuesta.
|
|
65
77
|
#
|
|
66
|
-
# @param response_body [String, Hash] El cuerpo de la respuesta fallida.
|
|
78
|
+
# @param response_body [String, Hash] El cuerpo de la respuesta fallida (ej. `{ "errors": { "name": ["blank"] } }`).
|
|
67
79
|
def initialize(response_body)
|
|
68
80
|
@raw_response = response_body
|
|
69
|
-
@error_messages =
|
|
81
|
+
@error_messages = extract_errors(response_body)
|
|
70
82
|
super('Validation failed on remote service')
|
|
71
83
|
end
|
|
72
84
|
|
|
73
85
|
private
|
|
74
86
|
|
|
75
|
-
# Intenta convertir el cuerpo de la respuesta a una estructura Ruby
|
|
76
|
-
# Si el cuerpo no es JSON
|
|
87
|
+
# Intenta convertir el cuerpo de la respuesta a una estructura Ruby y extrae la clave 'errors'.
|
|
88
|
+
# Si el cuerpo no sigue la convención o no es JSON, hace un graceful fallback devolviendo
|
|
89
|
+
# el payload completo.
|
|
77
90
|
#
|
|
78
|
-
# @param body [String, Hash] El cuerpo a
|
|
79
|
-
# @return [Object]
|
|
91
|
+
# @param body [String, Hash] El cuerpo a procesar.
|
|
92
|
+
# @return [Object] Los errores aislados o el cuerpo original.
|
|
80
93
|
# @api private
|
|
81
|
-
def
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
94
|
+
def extract_errors(body)
|
|
95
|
+
parsed = if body.is_a?(String)
|
|
96
|
+
begin
|
|
97
|
+
JSON.parse(body)
|
|
98
|
+
rescue JSON::ParserError
|
|
99
|
+
body # Si no es JSON, devolvemos el string tal cual
|
|
100
|
+
end
|
|
101
|
+
else
|
|
102
|
+
body
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
if parsed.is_a?(Hash)
|
|
106
|
+
# Extraemos inteligentemente la clave 'errors' si existe (convención típica de Rails)
|
|
107
|
+
parsed['errors'] || parsed[:errors] || parsed
|
|
108
|
+
else
|
|
109
|
+
parsed
|
|
110
|
+
end
|
|
87
111
|
end
|
|
88
112
|
end
|
|
89
113
|
end
|
|
@@ -1,22 +1,33 @@
|
|
|
1
|
-
# lib/bug_bunny/middleware/raise_error.rb
|
|
2
1
|
# frozen_string_literal: true
|
|
3
2
|
|
|
4
|
-
require_relative '
|
|
3
|
+
require_relative 'base'
|
|
5
4
|
|
|
6
5
|
module BugBunny
|
|
7
6
|
module Middleware
|
|
8
7
|
# Middleware que inspecciona el status de la respuesta y lanza excepciones
|
|
9
8
|
# si se encuentran errores (4xx o 5xx).
|
|
10
9
|
#
|
|
10
|
+
# Extrae inteligentemente el mensaje de error del cuerpo de la respuesta
|
|
11
|
+
# para que las excepciones tengan trazas claras y legibles, evitando el
|
|
12
|
+
# output crudo de Hashes en Ruby (`{ "error" => ... }`).
|
|
13
|
+
#
|
|
11
14
|
# @see BugBunny::Middleware::Base
|
|
12
15
|
class RaiseError < BugBunny::Middleware::Base
|
|
13
16
|
# Hook de ciclo de vida: Ejecutado después de recibir la respuesta.
|
|
14
17
|
#
|
|
15
|
-
# Verifica el código de estado
|
|
18
|
+
# Verifica el código de estado (status) de la respuesta. Si cae en el rango
|
|
19
|
+
# de éxito (2xx), permite que el flujo continúe. Si es un error, lo formatea
|
|
20
|
+
# y lanza la excepción semántica correspondiente.
|
|
16
21
|
#
|
|
17
22
|
# @param response [Hash] El hash de respuesta conteniendo 'status' y 'body'.
|
|
18
|
-
# @raise [BugBunny::
|
|
19
|
-
# @raise [BugBunny::
|
|
23
|
+
# @raise [BugBunny::BadRequest] Si el status es 400.
|
|
24
|
+
# @raise [BugBunny::NotFound] Si el status es 404.
|
|
25
|
+
# @raise [BugBunny::NotAcceptable] Si el status es 406.
|
|
26
|
+
# @raise [BugBunny::RequestTimeout] Si el status es 408.
|
|
27
|
+
# @raise [BugBunny::Conflict] Si el status es 409.
|
|
28
|
+
# @raise [BugBunny::UnprocessableEntity] Si el status es 422.
|
|
29
|
+
# @raise [BugBunny::InternalServerError] Si el status es 500..599.
|
|
30
|
+
# @raise [BugBunny::ClientError, BugBunny::ServerError] Para códigos no mapeados.
|
|
20
31
|
# @return [void]
|
|
21
32
|
def on_complete(response)
|
|
22
33
|
status = response['status'].to_i
|
|
@@ -24,25 +35,64 @@ module BugBunny
|
|
|
24
35
|
|
|
25
36
|
case status
|
|
26
37
|
when 200..299
|
|
27
|
-
|
|
28
|
-
when 400
|
|
29
|
-
|
|
30
|
-
when
|
|
31
|
-
|
|
32
|
-
when
|
|
33
|
-
|
|
38
|
+
return # Flujo normal (Success)
|
|
39
|
+
when 400
|
|
40
|
+
raise BugBunny::BadRequest, format_error_message(body)
|
|
41
|
+
when 404
|
|
42
|
+
raise BugBunny::NotFound
|
|
43
|
+
when 406
|
|
44
|
+
raise BugBunny::NotAcceptable
|
|
45
|
+
when 408
|
|
46
|
+
raise BugBunny::RequestTimeout
|
|
47
|
+
when 409
|
|
48
|
+
raise BugBunny::Conflict, format_error_message(body)
|
|
49
|
+
when 422
|
|
50
|
+
# Pasamos el body crudo; UnprocessableEntity lo procesará en exception.rb
|
|
51
|
+
raise BugBunny::UnprocessableEntity, body
|
|
52
|
+
when 500..599
|
|
53
|
+
raise BugBunny::InternalServerError, format_error_message(body)
|
|
34
54
|
else
|
|
35
|
-
handle_unknown_error(status)
|
|
55
|
+
handle_unknown_error(status, body)
|
|
36
56
|
end
|
|
37
57
|
end
|
|
38
58
|
|
|
39
59
|
private
|
|
40
60
|
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
|
|
45
|
-
|
|
61
|
+
# Formatea el cuerpo de la respuesta de error para que sea legible en las excepciones.
|
|
62
|
+
#
|
|
63
|
+
# Prioriza la convención `{ "error": "...", "detail": "..." }`. Si la respuesta no
|
|
64
|
+
# sigue esta convención, convierte el Hash completo a un JSON string para mantenerlo legible.
|
|
65
|
+
#
|
|
66
|
+
# @param body [Hash, String, nil] El cuerpo de la respuesta.
|
|
67
|
+
# @return [String] Un mensaje de error limpio y estructurado.
|
|
68
|
+
def format_error_message(body)
|
|
69
|
+
return "Unknown Error" if body.nil? || (body.respond_to?(:empty?) && body.empty?)
|
|
70
|
+
return body if body.is_a?(String)
|
|
71
|
+
|
|
72
|
+
# Si el worker devolvió un JSON con una key 'error' (nuestra convención en Controller)
|
|
73
|
+
if body.is_a?(Hash) && body['error']
|
|
74
|
+
detail = body['detail'] ? " - #{body['detail']}" : ""
|
|
75
|
+
"#{body['error']}#{detail}"
|
|
76
|
+
else
|
|
77
|
+
# Fallback: Convertir todo el Hash a JSON string para que se vea claro en Sentry/Logs
|
|
78
|
+
body.to_json
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Maneja códigos de error genéricos no mapeados explícitamente en el `case`.
|
|
83
|
+
#
|
|
84
|
+
# @param status [Integer] El código de estado HTTP (ej. 418, 502).
|
|
85
|
+
# @param body [Object] El cuerpo crudo de la respuesta.
|
|
86
|
+
# @raise [BugBunny::ServerError] Si es 5xx.
|
|
87
|
+
# @raise [BugBunny::ClientError] Si es 4xx.
|
|
88
|
+
def handle_unknown_error(status, body)
|
|
89
|
+
msg = format_error_message(body)
|
|
90
|
+
|
|
91
|
+
if status >= 500
|
|
92
|
+
raise BugBunny::ServerError, "Server Error (#{status}): #{msg}"
|
|
93
|
+
elsif status >= 400
|
|
94
|
+
raise BugBunny::ClientError, "Client Error (#{status}): #{msg}"
|
|
95
|
+
end
|
|
46
96
|
end
|
|
47
97
|
end
|
|
48
98
|
end
|
data/lib/bug_bunny/resource.rb
CHANGED
|
@@ -15,7 +15,7 @@ module BugBunny
|
|
|
15
15
|
# 3. **Específico:** Definidos en la clase del recurso o vía `with`.
|
|
16
16
|
#
|
|
17
17
|
# @author Gabriel
|
|
18
|
-
# @since 3.1.
|
|
18
|
+
# @since 3.1.2
|
|
19
19
|
class Resource
|
|
20
20
|
include ActiveModel::API
|
|
21
21
|
include ActiveModel::Attributes
|
|
@@ -100,13 +100,22 @@ module BugBunny
|
|
|
100
100
|
stack
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
-
# Instancia el cliente inyectando los middlewares
|
|
103
|
+
# Instancia el cliente inyectando los middlewares núcleo y personalizados.
|
|
104
|
+
# Integra automáticamente `RaiseError` y `JsonResponse` para que el ORM trabaje
|
|
105
|
+
# puramente con datos parseados o atrape excepciones sin validar HTTP Status manuales.
|
|
106
|
+
#
|
|
104
107
|
# @return [BugBunny::Client]
|
|
105
108
|
def bug_bunny_client
|
|
106
109
|
pool = connection_pool
|
|
107
110
|
raise BugBunny::Error, "Connection pool missing for #{name}" unless pool
|
|
108
|
-
|
|
109
|
-
|
|
111
|
+
|
|
112
|
+
BugBunny::Client.new(pool: pool) do |stack|
|
|
113
|
+
# 1. Middlewares Core (Siempre presentes para el Resource)
|
|
114
|
+
stack.use BugBunny::Middleware::RaiseError
|
|
115
|
+
stack.use BugBunny::Middleware::JsonResponse
|
|
116
|
+
|
|
117
|
+
# 2. Middlewares Personalizados del Usuario
|
|
118
|
+
resolve_middleware_stack.each { |block| block.call(stack) }
|
|
110
119
|
end
|
|
111
120
|
end
|
|
112
121
|
|
|
@@ -164,6 +173,8 @@ module BugBunny
|
|
|
164
173
|
# @!group Acciones CRUD RESTful
|
|
165
174
|
|
|
166
175
|
# Realiza una búsqueda filtrada (GET).
|
|
176
|
+
# Mapea un posible 404 a un array vacío.
|
|
177
|
+
#
|
|
167
178
|
# @param filters [Hash]
|
|
168
179
|
# @return [Array<BugBunny::Resource>]
|
|
169
180
|
def where(filters = {})
|
|
@@ -188,6 +199,8 @@ module BugBunny
|
|
|
188
199
|
inst.send(:clear_changes_information)
|
|
189
200
|
inst
|
|
190
201
|
end
|
|
202
|
+
rescue BugBunny::NotFound
|
|
203
|
+
[]
|
|
191
204
|
end
|
|
192
205
|
|
|
193
206
|
# Devuelve todos los registros.
|
|
@@ -195,6 +208,8 @@ module BugBunny
|
|
|
195
208
|
def all; where({}); end
|
|
196
209
|
|
|
197
210
|
# Busca un registro por ID (GET).
|
|
211
|
+
# Mapea un 404 (NotFound) devolviendo un objeto nulo.
|
|
212
|
+
#
|
|
198
213
|
# @param id [String, Integer]
|
|
199
214
|
# @return [BugBunny::Resource, nil]
|
|
200
215
|
def find(id)
|
|
@@ -211,12 +226,14 @@ module BugBunny
|
|
|
211
226
|
queue_options: current_queue_options
|
|
212
227
|
)
|
|
213
228
|
|
|
214
|
-
return nil
|
|
215
|
-
|
|
229
|
+
return nil unless response && response['body'].is_a?(Hash)
|
|
230
|
+
|
|
216
231
|
instance = new(response['body'])
|
|
217
232
|
instance.persisted = true
|
|
218
233
|
instance.send(:clear_changes_information)
|
|
219
234
|
instance
|
|
235
|
+
rescue BugBunny::NotFound
|
|
236
|
+
nil
|
|
220
237
|
end
|
|
221
238
|
|
|
222
239
|
# Crea una nueva instancia y la persiste.
|
|
@@ -350,7 +367,9 @@ module BugBunny
|
|
|
350
367
|
# @!group Persistencia
|
|
351
368
|
|
|
352
369
|
# Guarda el recurso en el servidor remoto vía AMQP (POST o PUT).
|
|
353
|
-
#
|
|
370
|
+
# Asume el Happy Path; el middleware se encarga de interceptar y lanzar excepciones.
|
|
371
|
+
#
|
|
372
|
+
# @return [Boolean] Retorna true si tuvo éxito, false si falló la validación.
|
|
354
373
|
def save
|
|
355
374
|
return false unless valid?
|
|
356
375
|
|
|
@@ -364,6 +383,7 @@ module BugBunny
|
|
|
364
383
|
path = is_new ? self.class.resource_name : "#{self.class.resource_name}/#{id}"
|
|
365
384
|
method = is_new ? :post : :put
|
|
366
385
|
|
|
386
|
+
# Si el middleware de errores no lanza excepción, asumimos un éxito (200..299)
|
|
367
387
|
response = bug_bunny_client.request(
|
|
368
388
|
path,
|
|
369
389
|
method: method,
|
|
@@ -375,7 +395,10 @@ module BugBunny
|
|
|
375
395
|
body: wrapped_payload
|
|
376
396
|
)
|
|
377
397
|
|
|
378
|
-
|
|
398
|
+
assign_attributes(response['body'])
|
|
399
|
+
self.persisted = true
|
|
400
|
+
clear_changes_information
|
|
401
|
+
true
|
|
379
402
|
end
|
|
380
403
|
rescue BugBunny::UnprocessableEntity => e
|
|
381
404
|
load_remote_rabbit_errors(e.error_messages)
|
|
@@ -383,6 +406,7 @@ module BugBunny
|
|
|
383
406
|
end
|
|
384
407
|
|
|
385
408
|
# Elimina el recurso del servidor remoto (DELETE).
|
|
409
|
+
#
|
|
386
410
|
# @return [Boolean]
|
|
387
411
|
def destroy
|
|
388
412
|
return false unless persisted?
|
|
@@ -409,40 +433,9 @@ module BugBunny
|
|
|
409
433
|
|
|
410
434
|
private
|
|
411
435
|
|
|
412
|
-
#
|
|
413
|
-
def handle_save_response(response)
|
|
414
|
-
if response['status'] == 422
|
|
415
|
-
raise BugBunny::UnprocessableEntity.new(response['body']['errors'] || response['body'])
|
|
416
|
-
elsif response['status'] >= 500
|
|
417
|
-
raise BugBunny::InternalServerError, format_error_message(response['body'])
|
|
418
|
-
elsif response['status'] >= 400
|
|
419
|
-
raise BugBunny::ClientError, format_error_message(response['body'])
|
|
420
|
-
end
|
|
421
|
-
|
|
422
|
-
assign_attributes(response['body'])
|
|
423
|
-
self.persisted = true
|
|
424
|
-
clear_changes_information
|
|
425
|
-
true
|
|
426
|
-
end
|
|
427
|
-
|
|
428
|
-
# Formatea el cuerpo de la respuesta de error para que sea legible en las excepciones
|
|
429
|
-
def format_error_message(body)
|
|
430
|
-
return "Unknown Error" if body.nil?
|
|
431
|
-
return body if body.is_a?(String)
|
|
432
|
-
|
|
433
|
-
# Si el worker devolvió un JSON con una key 'error' (nuestra convención en Controller), la priorizamos
|
|
434
|
-
if body.is_a?(Hash) && body['error']
|
|
435
|
-
detail = body['detail'] ? " - #{body['detail']}" : ""
|
|
436
|
-
"#{body['error']}#{detail}"
|
|
437
|
-
else
|
|
438
|
-
# Fallback: Convertir todo el Hash a JSON string para que se vea claro en Sentry/Logs
|
|
439
|
-
body.to_json
|
|
440
|
-
end
|
|
441
|
-
end
|
|
442
|
-
|
|
443
|
-
# Carga errores remotos en el objeto local.
|
|
436
|
+
# Carga errores remotos en el objeto local (utilizado al recibir 422).
|
|
444
437
|
def load_remote_rabbit_errors(errors_hash)
|
|
445
|
-
return if errors_hash.nil?
|
|
438
|
+
return if errors_hash.nil? || errors_hash.empty?
|
|
446
439
|
if errors_hash.is_a?(String)
|
|
447
440
|
errors.add(:base, errors_hash)
|
|
448
441
|
else
|
data/lib/bug_bunny/version.rb
CHANGED