hati-jsonapi-error 0.1.0
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +391 -0
- data/hati-jsonapi-error.gemspec +35 -0
- data/lib/hati_jsonapi_error/api_error/base_error.rb +65 -0
- data/lib/hati_jsonapi_error/api_error/error_const.rb +53 -0
- data/lib/hati_jsonapi_error/api_error/links.rb +22 -0
- data/lib/hati_jsonapi_error/api_error/source.rb +24 -0
- data/lib/hati_jsonapi_error/config.rb +42 -0
- data/lib/hati_jsonapi_error/errors/helpers_handle_error.rb +12 -0
- data/lib/hati_jsonapi_error/errors/helpers_render_error.rb +12 -0
- data/lib/hati_jsonapi_error/errors/not_defined_error_class_error.rb +12 -0
- data/lib/hati_jsonapi_error/errors/not_loaded_error.rb +12 -0
- data/lib/hati_jsonapi_error/helpers.rb +78 -0
- data/lib/hati_jsonapi_error/kigen.rb +70 -0
- data/lib/hati_jsonapi_error/poro_serializer.rb +32 -0
- data/lib/hati_jsonapi_error/registry.rb +55 -0
- data/lib/hati_jsonapi_error/resolver.rb +27 -0
- data/lib/hati_jsonapi_error/version.rb +5 -0
- data/lib/hati_jsonapi_error.rb +23 -0
- metadata +67 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f5c0d7b15ad75fc5e3cc6671e4468ad84d34414d7806f5fb08f7f27d03cdad0f
|
|
4
|
+
data.tar.gz: 2dfc99d96da5c7f1f65023d7120755deac9ecb42a62f5bc5b49fe36c584c77ff
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 2d320265c9f28b71b6421fe04e6341e7d877c53ac99ff7c2667e3fb2f96d4d24cc094db3509b3ee2fcc851aa053632ed422730da3e89c620cf011751d6946f60
|
|
7
|
+
data.tar.gz: d5e54f2fd110a5ab0e0e13abd51b1d54bc9cf8f0b7060332cff16c1ceeff447cac7616621b4b2b89262ae1bdc8047bcddfdf492d23b94ff27a06a5e41faabc56
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 hackico.ai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
# Hati JSON:API Error
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/rb/hati-jsonapi-error)
|
|
4
|
+
[](https://ruby-lang.org)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://github.com/hackico-ai/ruby-hati-jsonapi-error)
|
|
7
|
+
|
|
8
|
+
> **Production-ready JSON:API-compliant error responses for professional Web APIs**
|
|
9
|
+
|
|
10
|
+
Transform inconsistent error handling into standardized, traceable responses. Built for Ruby applications requiring enterprise-grade error management.
|
|
11
|
+
|
|
12
|
+
## Why Standardized Error Handling Matters
|
|
13
|
+
|
|
14
|
+
### The Problem: Inconsistent Error Responses
|
|
15
|
+
|
|
16
|
+
Different controllers returning different error formats creates maintenance nightmares:
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
# Three different error formats in one application
|
|
20
|
+
class UsersController
|
|
21
|
+
def show
|
|
22
|
+
render json: { error: "User not found" }, status: 404
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class OrdersController
|
|
27
|
+
def create
|
|
28
|
+
render json: { message: "Validation failed", details: errors }, status: 422
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class PaymentsController
|
|
33
|
+
def process
|
|
34
|
+
render json: { errors: errors, error_code: "INVALID", status: "failure" }, status: 400
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
This forces frontend developers to handle multiple error formats:
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
// Unmaintainable error handling
|
|
43
|
+
if (data.error) {
|
|
44
|
+
showError(data.error); // Users format
|
|
45
|
+
} else if (data.message && data.details) {
|
|
46
|
+
showError(`${data.message}: ${data.details.join(", ")}`); // Orders format
|
|
47
|
+
} else if (data.errors && data.error_code) {
|
|
48
|
+
showError(`${data.error_code}: ${data.errors.join(", ")}`); // Payments format
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### The Impact
|
|
53
|
+
|
|
54
|
+
- **API Documentation**: Each endpoint needs custom error documentation
|
|
55
|
+
- **Error Tracking**: Different structures break centralized logging
|
|
56
|
+
- **Client SDKs**: Cannot provide consistent error handling
|
|
57
|
+
- **Testing**: Each format requires separate test cases
|
|
58
|
+
- **Team Coordination**: New developers must learn multiple patterns
|
|
59
|
+
|
|
60
|
+
### The Solution: JSON:API Standard
|
|
61
|
+
|
|
62
|
+
**One format across all endpoints:**
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
raise HatiJsonapiError::UnprocessableEntity.new(
|
|
66
|
+
detail: "Email address is required",
|
|
67
|
+
source: { pointer: "/data/attributes/email" }
|
|
68
|
+
)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Always produces standardized output:**
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"errors": [
|
|
76
|
+
{
|
|
77
|
+
"status": 422,
|
|
78
|
+
"code": "unprocessable_entity",
|
|
79
|
+
"title": "Validation Failed",
|
|
80
|
+
"detail": "Email address is required",
|
|
81
|
+
"source": { "pointer": "/data/attributes/email" }
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## ✨ Features
|
|
88
|
+
|
|
89
|
+
- **JSON:API Compliant** - Follows the official [JSON:API error specification](https://jsonapi.org/format/#errors)
|
|
90
|
+
- **Auto-Generated Error Classes** - Dynamic HTTP status code error classes (400-511)
|
|
91
|
+
- **Rich Error Context** - Support for `id`, `code`, `title`, `detail`, `status`, `meta`, `links`, `source`
|
|
92
|
+
- **Error Registry** - Map custom exceptions to standardized responses
|
|
93
|
+
- **Controller Integration** - Helper methods for Rails, Sinatra, and other frameworks
|
|
94
|
+
- **100% Test Coverage** - Comprehensive RSpec test suite
|
|
95
|
+
- **Zero Dependencies** - Lightweight and fast
|
|
96
|
+
- **Production Ready** - Thread-safe and memory efficient
|
|
97
|
+
|
|
98
|
+
## Installation
|
|
99
|
+
|
|
100
|
+
```ruby
|
|
101
|
+
# Gemfile
|
|
102
|
+
gem 'hati-jsonapi-error'
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
bundle install
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Quick Start
|
|
110
|
+
|
|
111
|
+
### 1. Configuration
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
# config/initializers/hati_jsonapi_error.rb
|
|
115
|
+
HatiJsonapiError::Config.configure do |config|
|
|
116
|
+
config.load_errors!
|
|
117
|
+
|
|
118
|
+
config.map_errors = {
|
|
119
|
+
ActiveRecord::RecordNotFound => :not_found,
|
|
120
|
+
ActiveRecord::RecordInvalid => :unprocessable_entity,
|
|
121
|
+
ArgumentError => :bad_request
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
config.use_unexpected = HatiJsonapiError::InternalServerError
|
|
125
|
+
end
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 2. Basic Usage
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
# Simple error raising
|
|
132
|
+
raise HatiJsonapiError::NotFound.new
|
|
133
|
+
raise HatiJsonapiError::BadRequest.new
|
|
134
|
+
raise HatiJsonapiError::Unauthorized.new
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Usage Examples
|
|
138
|
+
|
|
139
|
+
### Basic Error Handling
|
|
140
|
+
|
|
141
|
+
**Access errors multiple ways:**
|
|
142
|
+
|
|
143
|
+
```ruby
|
|
144
|
+
# By class name
|
|
145
|
+
raise HatiJsonapiError::NotFound.new
|
|
146
|
+
|
|
147
|
+
# By status code
|
|
148
|
+
api_err = HatiJsonapiError::Helpers::ApiErr
|
|
149
|
+
raise api_err[404]
|
|
150
|
+
|
|
151
|
+
# By error code
|
|
152
|
+
raise api_err[:not_found]
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Rich Error Context
|
|
156
|
+
|
|
157
|
+
**Add debugging information:**
|
|
158
|
+
|
|
159
|
+
```ruby
|
|
160
|
+
HatiJsonapiError::NotFound.new(
|
|
161
|
+
id: 'user_lookup_failed',
|
|
162
|
+
detail: 'User with email john@example.com was not found',
|
|
163
|
+
source: { pointer: '/data/attributes/email' },
|
|
164
|
+
meta: {
|
|
165
|
+
searched_email: 'john@example.com',
|
|
166
|
+
suggestion: 'Verify the email address is correct'
|
|
167
|
+
}
|
|
168
|
+
)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Multiple Validation Errors
|
|
172
|
+
|
|
173
|
+
**Collect and return multiple errors:**
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
errors = []
|
|
177
|
+
errors << HatiJsonapiError::UnprocessableEntity.new(
|
|
178
|
+
detail: "Email format is invalid",
|
|
179
|
+
source: { pointer: '/data/attributes/email' }
|
|
180
|
+
)
|
|
181
|
+
errors << HatiJsonapiError::UnprocessableEntity.new(
|
|
182
|
+
detail: "Password too short",
|
|
183
|
+
source: { pointer: '/data/attributes/password' }
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
resolver = HatiJsonapiError::Resolver.new(errors)
|
|
187
|
+
render json: resolver.to_json, status: resolver.status
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Controller Integration
|
|
191
|
+
|
|
192
|
+
```ruby
|
|
193
|
+
class ApiController < ApplicationController
|
|
194
|
+
include HatiJsonapiError::Helpers
|
|
195
|
+
|
|
196
|
+
rescue_from StandardError, with: :handle_error
|
|
197
|
+
|
|
198
|
+
def show
|
|
199
|
+
# ActiveRecord::RecordNotFound automatically mapped to JSON:API NotFound
|
|
200
|
+
user = User.find(params[:id])
|
|
201
|
+
render json: user
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def create
|
|
205
|
+
user = User.new(user_params)
|
|
206
|
+
|
|
207
|
+
unless user.save
|
|
208
|
+
validation_error = HatiJsonapiError::UnprocessableEntity.new(
|
|
209
|
+
detail: user.errors.full_messages.join(', '),
|
|
210
|
+
source: { pointer: '/data/attributes' },
|
|
211
|
+
meta: { validation_errors: user.errors.messages }
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
return render_error(validation_error)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
render json: user, status: :created
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Custom Error Classes
|
|
223
|
+
|
|
224
|
+
**Domain-specific errors:**
|
|
225
|
+
|
|
226
|
+
```ruby
|
|
227
|
+
class PaymentRequiredError < HatiJsonapiError::PaymentRequired
|
|
228
|
+
def initialize(amount:, currency: 'USD')
|
|
229
|
+
super(
|
|
230
|
+
detail: "Payment of #{amount} #{currency} required",
|
|
231
|
+
meta: {
|
|
232
|
+
required_amount: amount,
|
|
233
|
+
currency: currency,
|
|
234
|
+
payment_methods: ['credit_card', 'paypal']
|
|
235
|
+
},
|
|
236
|
+
links: {
|
|
237
|
+
payment_page: "https://app.com/billing/upgrade?amount=#{amount}"
|
|
238
|
+
}
|
|
239
|
+
)
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Usage
|
|
244
|
+
raise PaymentRequiredError.new(amount: 29.99)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Functional Programming Integration
|
|
248
|
+
|
|
249
|
+
Perfect for functional programming patterns with [hati-operation gem](https://github.com/hackico-ai/ruby-hati-operation):
|
|
250
|
+
|
|
251
|
+
```ruby
|
|
252
|
+
require 'hati_operation'
|
|
253
|
+
|
|
254
|
+
class Api::User::CreateOperation < Hati::Operation
|
|
255
|
+
ApiErr = HatiJsonapiError::Helpers::ApiErr
|
|
256
|
+
|
|
257
|
+
def call(params)
|
|
258
|
+
user_params = step validate_params(params), err: ApiErr[422]
|
|
259
|
+
user = step create_user(user_params), err: ApiErr[409]
|
|
260
|
+
profile = step create_profile(user), err: ApiErr[503]
|
|
261
|
+
|
|
262
|
+
Success(profile)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
private
|
|
266
|
+
|
|
267
|
+
def validate_params(params)
|
|
268
|
+
return Failure('Invalid parameters') unless params[:name]
|
|
269
|
+
Success(params)
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Configuration
|
|
275
|
+
|
|
276
|
+
### Error Mapping
|
|
277
|
+
|
|
278
|
+
```ruby
|
|
279
|
+
HatiJsonapiError::Config.configure do |config|
|
|
280
|
+
config.map_errors = {
|
|
281
|
+
# Rails exceptions
|
|
282
|
+
ActiveRecord::RecordNotFound => :not_found,
|
|
283
|
+
ActiveRecord::RecordInvalid => :unprocessable_entity,
|
|
284
|
+
|
|
285
|
+
# Custom exceptions
|
|
286
|
+
AuthenticationError => :unauthorized,
|
|
287
|
+
RateLimitError => :too_many_requests,
|
|
288
|
+
|
|
289
|
+
# Infrastructure exceptions
|
|
290
|
+
Redis::TimeoutError => :service_unavailable,
|
|
291
|
+
Net::ReadTimeout => :gateway_timeout
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
config.use_unexpected = HatiJsonapiError::InternalServerError
|
|
295
|
+
end
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Available Error Classes
|
|
299
|
+
|
|
300
|
+
**Quick Reference - Most Common:**
|
|
301
|
+
|
|
302
|
+
| Status | Class | Code |
|
|
303
|
+
| ------ | --------------------- | ----------------------- |
|
|
304
|
+
| 400 | `BadRequest` | `bad_request` |
|
|
305
|
+
| 401 | `Unauthorized` | `unauthorized` |
|
|
306
|
+
| 403 | `Forbidden` | `forbidden` |
|
|
307
|
+
| 404 | `NotFound` | `not_found` |
|
|
308
|
+
| 422 | `UnprocessableEntity` | `unprocessable_entity` |
|
|
309
|
+
| 429 | `TooManyRequests` | `too_many_requests` |
|
|
310
|
+
| 500 | `InternalServerError` | `internal_server_error` |
|
|
311
|
+
| 502 | `BadGateway` | `bad_gateway` |
|
|
312
|
+
| 503 | `ServiceUnavailable` | `service_unavailable` |
|
|
313
|
+
|
|
314
|
+
**[Complete list of all 39 HTTP status codes →](HTTP_STATUS_CODES.md)**
|
|
315
|
+
|
|
316
|
+
## Testing
|
|
317
|
+
|
|
318
|
+
### RSpec Integration
|
|
319
|
+
|
|
320
|
+
```ruby
|
|
321
|
+
# Shared examples for JSON:API compliance
|
|
322
|
+
RSpec.shared_examples 'JSON:API error response' do |expected_status, expected_code|
|
|
323
|
+
it 'returns proper JSON:API error format' do
|
|
324
|
+
json = JSON.parse(response.body)
|
|
325
|
+
|
|
326
|
+
expect(response).to have_http_status(expected_status)
|
|
327
|
+
expect(json['errors'].first['status']).to eq(expected_status)
|
|
328
|
+
expect(json['errors'].first['code']).to eq(expected_code)
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
# Usage in specs
|
|
333
|
+
describe 'GET #show' do
|
|
334
|
+
context 'when user not found' do
|
|
335
|
+
subject { get :show, params: { id: 'nonexistent' } }
|
|
336
|
+
include_examples 'JSON:API error response', 404, 'not_found'
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Unit Testing
|
|
342
|
+
|
|
343
|
+
```ruby
|
|
344
|
+
RSpec.describe HatiJsonapiError::NotFound do
|
|
345
|
+
it 'has correct default attributes' do
|
|
346
|
+
error = described_class.new
|
|
347
|
+
|
|
348
|
+
expect(error.status).to eq(404)
|
|
349
|
+
expect(error.code).to eq(:not_found)
|
|
350
|
+
expect(error.to_h[:title]).to eq('Not Found')
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## Benefits
|
|
356
|
+
|
|
357
|
+
**For Development Teams:**
|
|
358
|
+
|
|
359
|
+
- Reduced development time with single error pattern
|
|
360
|
+
- Easier onboarding for new developers
|
|
361
|
+
- Better testing with standardized structure
|
|
362
|
+
- Improved debugging with consistent error tracking
|
|
363
|
+
|
|
364
|
+
**For Frontend/Mobile Teams:**
|
|
365
|
+
|
|
366
|
+
- One error parser for entire API
|
|
367
|
+
- Rich error context for better user experience
|
|
368
|
+
- Easier SDK development
|
|
369
|
+
|
|
370
|
+
**For Operations:**
|
|
371
|
+
|
|
372
|
+
- Centralized monitoring and alerting
|
|
373
|
+
- Consistent error analysis
|
|
374
|
+
- Simplified documentation
|
|
375
|
+
|
|
376
|
+
## Contributing
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
git clone https://github.com/hackico-ai/ruby-hati-jsonapi-error.git
|
|
380
|
+
cd ruby-hati-jsonapi-error
|
|
381
|
+
bundle install
|
|
382
|
+
bundle exec rspec
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## License
|
|
386
|
+
|
|
387
|
+
MIT License - see [LICENSE](LICENSE) file.
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
**Professional error handling for professional APIs**
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
+
|
|
6
|
+
require 'hati_jsonapi_error/version'
|
|
7
|
+
|
|
8
|
+
Gem::Specification.new do |spec|
|
|
9
|
+
spec.name = 'hati-jsonapi-error'
|
|
10
|
+
spec.version = HatiJsonapiError::VERSION
|
|
11
|
+
spec.authors = ['Marie Giy']
|
|
12
|
+
spec.email = %w[giy.mariya@gmail.com]
|
|
13
|
+
spec.license = 'MIT'
|
|
14
|
+
|
|
15
|
+
spec.summary = 'Standardized JSON: API-compliant error responses made easy for your Web API.'
|
|
16
|
+
spec.description = 'hati-jsonapi-error is a Ruby gem for Standardized JSON Error.'
|
|
17
|
+
spec.homepage = "https://github.com/hackico-ai/#{spec.name}"
|
|
18
|
+
|
|
19
|
+
spec.required_ruby_version = '>= 3.0.0'
|
|
20
|
+
|
|
21
|
+
spec.files = Dir['CHANGELOG.md', 'LICENSE', 'README.md', 'hati-jsonapi-error.gemspec', 'lib/**/*']
|
|
22
|
+
spec.bindir = 'bin'
|
|
23
|
+
spec.executables = []
|
|
24
|
+
spec.require_paths = ['lib']
|
|
25
|
+
|
|
26
|
+
spec.metadata['repo_homepage'] = spec.homepage
|
|
27
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
|
28
|
+
|
|
29
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
30
|
+
spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
31
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
|
32
|
+
spec.metadata['bug_tracker_uri'] = "#{spec.homepage}/issues"
|
|
33
|
+
|
|
34
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
35
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HatiJsonapiError
|
|
4
|
+
# This is the base error class for all errors in the HatiJsonapiError gem.
|
|
5
|
+
class BaseError < ::StandardError
|
|
6
|
+
STR = ''
|
|
7
|
+
OBJ = {}.freeze
|
|
8
|
+
|
|
9
|
+
attr_accessor :id, :code, :title, :detail, :status, :meta, :links, :source
|
|
10
|
+
|
|
11
|
+
def initialize(**attrs)
|
|
12
|
+
@id = attrs[:id] || STR
|
|
13
|
+
@code = attrs[:code] || STR
|
|
14
|
+
@title = attrs[:title] || STR
|
|
15
|
+
@detail = attrs[:detail] || STR
|
|
16
|
+
@status = attrs[:status] || STR
|
|
17
|
+
|
|
18
|
+
@links = build_links(attrs[:links])
|
|
19
|
+
@source = build_source(attrs[:source])
|
|
20
|
+
@meta = attrs[:meta] || OBJ
|
|
21
|
+
|
|
22
|
+
super(error_message)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# NOTE: used in lib/hati_jsonapi_error/payload_adapter.rb
|
|
26
|
+
def to_h
|
|
27
|
+
{
|
|
28
|
+
id: id,
|
|
29
|
+
links: links.to_h,
|
|
30
|
+
status: status,
|
|
31
|
+
code: code,
|
|
32
|
+
title: title,
|
|
33
|
+
detail: detail,
|
|
34
|
+
source: source.to_h,
|
|
35
|
+
meta: meta
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def to_s
|
|
40
|
+
to_h.to_s
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def serializable_hash
|
|
44
|
+
to_h
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def to_json(*_args)
|
|
48
|
+
serializable_hash.to_json
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def build_links(links_attrs)
|
|
54
|
+
links_attrs ? Links.new(**links_attrs) : OBJ
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def build_source(source_attrs)
|
|
58
|
+
source_attrs ? Source.new(**source_attrs) : OBJ
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def error_message
|
|
62
|
+
@detail.empty? ? @title : @detail
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HatiJsonapiError
|
|
4
|
+
# rubocop:disable Layout/LineLength
|
|
5
|
+
CLIENT = {
|
|
6
|
+
400 => { name: 'BadRequest', code: :bad_request, message: 'Bad Request' },
|
|
7
|
+
401 => { name: 'Unauthorized', code: :unauthorized, message: 'Unauthorized' },
|
|
8
|
+
402 => { name: 'PaymentRequired', code: :payment_required, message: 'Payment Required' },
|
|
9
|
+
403 => { name: 'Forbidden', code: :forbidden, message: 'Forbidden' },
|
|
10
|
+
404 => { name: 'NotFound', code: :not_found, message: 'Not Found' },
|
|
11
|
+
405 => { name: 'MethodNotAllowed', code: :method_not_allowed, message: 'Method Not Allowed' },
|
|
12
|
+
406 => { name: 'NotAcceptable', code: :not_acceptable, message: 'Not Acceptable' },
|
|
13
|
+
407 => { name: 'ProxyAuthenticationRequired', code: :proxy_authentication_required, message: 'Proxy Authentication Required' },
|
|
14
|
+
408 => { name: 'RequestTimeout', code: :request_timeout, message: 'Request Timeout' },
|
|
15
|
+
409 => { name: 'Conflict', code: :conflict, message: 'Conflict' },
|
|
16
|
+
410 => { name: 'Gone', code: :gone, message: 'Gone' },
|
|
17
|
+
411 => { name: 'LengthRequired', code: :length_required, message: 'Length Required' },
|
|
18
|
+
412 => { name: 'PreconditionFailed', code: :precondition_failed, message: 'Precondition Failed' },
|
|
19
|
+
413 => { name: 'RequestEntityTooLarge', code: :request_entity_too_large, message: 'Request Entity Too Large' },
|
|
20
|
+
414 => { name: 'RequestUriTooLong', code: :request_uri_too_long, message: 'Request Uri Too Long' },
|
|
21
|
+
415 => { name: 'UnsupportedMediaType', code: :unsupported_media_type, message: 'Unsupported Media Type' },
|
|
22
|
+
416 => { name: 'RequestedRangeNotSatisfiable', code: :requested_range_not_satisfiable, message: 'Requested Range Not Satisfiable' },
|
|
23
|
+
417 => { name: 'ExpectationFailed', code: :expectation_failed, message: 'Expectation Failed' },
|
|
24
|
+
421 => { name: 'MisdirectedRequest', code: :misdirected_request, message: 'Misdirected Request' },
|
|
25
|
+
422 => { name: 'UnprocessableEntity', code: :unprocessable_entity, message: 'Unprocessable Entity' },
|
|
26
|
+
423 => { name: 'Locked', code: :locked, message: 'Locked' },
|
|
27
|
+
424 => { name: 'FailedDependency', code: :failed_dependency, message: 'Failed Dependency' },
|
|
28
|
+
425 => { name: 'TooEarly', code: :too_early, message: 'Too Early' },
|
|
29
|
+
426 => { name: 'UpgradeRequired', code: :upgrade_required, message: 'Upgrade Required' },
|
|
30
|
+
428 => { name: 'PreconditionRequired', code: :precondition_required, message: 'Precondition Required' },
|
|
31
|
+
429 => { name: 'TooManyRequests', code: :too_many_requests, message: 'Too Many Requests' },
|
|
32
|
+
431 => { name: 'RequestHeaderFieldsTooLarge', code: :request_header_fields_too_large, message: 'Request Header Fields Too Large' },
|
|
33
|
+
451 => { name: 'UnavailableForLegalReasons', code: :unavailable_for_legal_reasons, message: 'Unavailable for Legal Reasons' }
|
|
34
|
+
}.freeze
|
|
35
|
+
|
|
36
|
+
SERVER = {
|
|
37
|
+
500 => { name: 'InternalServerError', code: :internal_server_error, message: 'Internal Server Error' },
|
|
38
|
+
501 => { name: 'NotImplemented', code: :not_implemented, message: 'Not Implemented' },
|
|
39
|
+
502 => { name: 'BadGateway', code: :bad_gateway, message: 'Bad Gateway' },
|
|
40
|
+
503 => { name: 'ServiceUnavailable', code: :service_unavailable, message: 'Service Unavailable' },
|
|
41
|
+
504 => { name: 'GatewayTimeout', code: :gateway_timeout, message: 'Gateway Timeout' },
|
|
42
|
+
505 => { name: 'HttpVersionNotSupported', code: :http_version_not_supported, message: 'HTTP Version Not Supported' },
|
|
43
|
+
506 => { name: 'VariantAlsoNegotiates', code: :variant_also_negotiates, message: 'Variant Also Negotiates' },
|
|
44
|
+
507 => { name: 'InsufficientStorage', code: :insufficient_storage, message: 'Insufficient Storage' },
|
|
45
|
+
508 => { name: 'LoopDetected', code: :loop_detected, message: 'Loop Detected' },
|
|
46
|
+
509 => { name: 'BandwidthLimitExceeded', code: :bandwidth_limit_exceeded, message: 'Bandwidth Limit Exceeded' },
|
|
47
|
+
510 => { name: 'NotExtended', code: :not_extended, message: 'Not Extended' },
|
|
48
|
+
511 => { name: 'NetworkAuthenticationRequired', code: :network_authentication_required, message: 'Network Authentication Required' }
|
|
49
|
+
}.freeze
|
|
50
|
+
# rubocop:enable Layout/LineLength
|
|
51
|
+
|
|
52
|
+
STATUS_MAP = CLIENT.merge(SERVER)
|
|
53
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HatiJsonapiError
|
|
4
|
+
# This class is used to build the links object for the error response.
|
|
5
|
+
class Links
|
|
6
|
+
STR = ''
|
|
7
|
+
|
|
8
|
+
attr_accessor :about, :type
|
|
9
|
+
|
|
10
|
+
def initialize(about: STR, type: STR)
|
|
11
|
+
@about = about
|
|
12
|
+
@type = type
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_h
|
|
16
|
+
{
|
|
17
|
+
about: about,
|
|
18
|
+
type: type
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HatiJsonapiError
|
|
4
|
+
# This class is used to build the source object for the error response.
|
|
5
|
+
class Source
|
|
6
|
+
STR = ''
|
|
7
|
+
|
|
8
|
+
attr_accessor :pointer, :parameter, :header
|
|
9
|
+
|
|
10
|
+
def initialize(pointer: STR, parameter: STR, header: STR)
|
|
11
|
+
@pointer = pointer
|
|
12
|
+
@parameter = parameter
|
|
13
|
+
@header = header
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_h
|
|
17
|
+
{
|
|
18
|
+
pointer: pointer,
|
|
19
|
+
parameter: parameter,
|
|
20
|
+
header: header
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module HatiJsonapiError
|
|
2
|
+
class Config
|
|
3
|
+
# HatiJsonapiError::Config.configure do |config|
|
|
4
|
+
# config.load_error!
|
|
5
|
+
# config.map_errors = {
|
|
6
|
+
# ActiveRecord::RecordNotFound => ApiError::NotFound,
|
|
7
|
+
# ActiveRecord::RecordInvalid => ApiError::UnprocessableEntity
|
|
8
|
+
# ActiveRecord::RecordNotUnique => :conflict
|
|
9
|
+
# ActiveRecord::Unauthorized => 401
|
|
10
|
+
# }
|
|
11
|
+
# config.use_unexpected = InternalServerError
|
|
12
|
+
# end
|
|
13
|
+
|
|
14
|
+
# TODO: preload rails rescue responses
|
|
15
|
+
# - what to do about order ???order of loading is important
|
|
16
|
+
# - what to do about rails app?
|
|
17
|
+
# - what to do about rails app not loaded?
|
|
18
|
+
# - what to do about rails app not loaded?
|
|
19
|
+
class << self
|
|
20
|
+
def configure
|
|
21
|
+
yield self if block_given?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def use_unexpected=(fallback_error)
|
|
25
|
+
HatiJsonapiError::Registry.fallback = fallback_error
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def map_errors=(error_map)
|
|
29
|
+
HatiJsonapiError::Registry.error_map = error_map
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def error_map
|
|
33
|
+
HatiJsonapiError::Registry.error_map
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# TODO: check if double defintion of errors
|
|
37
|
+
def load_errors!
|
|
38
|
+
HatiJsonapiError::Kigen.load_errors!
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HatiJsonapiError
|
|
4
|
+
# class ApiController < ApplicationController
|
|
5
|
+
# rescue_from ::StandardError, with: ->(e) { handle_error(e) }
|
|
6
|
+
# end
|
|
7
|
+
|
|
8
|
+
# This module contains helper methods for rendering errors in a JSON API format.
|
|
9
|
+
module Helpers
|
|
10
|
+
HatiErrs = HatiJsonapiError::Errors
|
|
11
|
+
|
|
12
|
+
def render_error(error, status: nil, short: false)
|
|
13
|
+
error_instance = error.is_a?(Class) ? error.new : error
|
|
14
|
+
|
|
15
|
+
unless error_instance.class <= HatiJsonapiError::BaseError
|
|
16
|
+
msg = "Supported only explicit type of HatiJsonapiError::BaseError, got: #{error_instance.class.name}"
|
|
17
|
+
raise HatiErrs::HelpersRenderError, msg
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
resolver = HatiJsonapiError::Resolver.new(error_instance)
|
|
21
|
+
|
|
22
|
+
unless defined?(render)
|
|
23
|
+
msg = 'Render not defined'
|
|
24
|
+
raise HatiErrs::HelpersRenderError, msg
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
render json: resolver.to_json(short: short), status: status || resolver.status
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# with_original: oneOf: [false, true, :full_trace]
|
|
31
|
+
def handle_error(error, with_original: false)
|
|
32
|
+
error_class = error if error.class <= HatiJsonapiError::BaseError
|
|
33
|
+
error_class ||= HatiJsonapiError::Registry.lookup_error(error)
|
|
34
|
+
|
|
35
|
+
unless error_class
|
|
36
|
+
msg = 'Used handle_error but no mapping of default error set'
|
|
37
|
+
raise HatiErrs::HelpersHandleError, msg
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Fix: if error_class is already an instance, use it directly, otherwise create new instance
|
|
41
|
+
api_err = error_class.is_a?(Class) ? error_class.new : error_class
|
|
42
|
+
if with_original
|
|
43
|
+
api_err.meta = {
|
|
44
|
+
original_error: error.class,
|
|
45
|
+
trace: error.backtrace[0],
|
|
46
|
+
message: error.message
|
|
47
|
+
}
|
|
48
|
+
api_err.meta.merge!(backtrace: error.backtrace.join("\n")) if with_original == :full_trace
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
render_error(api_err)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# shorthand for API errors
|
|
55
|
+
# raise ApiErr[404] # => ApiError::NotFound
|
|
56
|
+
# raise ApiErr[:not_found] # => ApiError::NotFound
|
|
57
|
+
class ApiErr
|
|
58
|
+
class << self
|
|
59
|
+
def [](error)
|
|
60
|
+
call(error)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def call(error)
|
|
64
|
+
raise HatiErrs::NotLoadedError unless HatiJsonapiError::Kigen.loaded?
|
|
65
|
+
|
|
66
|
+
err = HatiJsonapiError::Kigen.fetch_err(error)
|
|
67
|
+
|
|
68
|
+
unless err
|
|
69
|
+
msg = "Error #{error} not defined on load_errors!. Check kigen.rb and api_error/error_const.rb"
|
|
70
|
+
raise HatiErrs::NotDefinedErrorClassError, msg
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
err
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HatiJsonapiError
|
|
4
|
+
# This class is used to load all errors from STATUS_MAP in api_error/error_const.rb
|
|
5
|
+
class Kigen
|
|
6
|
+
class << self
|
|
7
|
+
# loads all errors from STATUS_MAP in api_error/error_const.rb
|
|
8
|
+
# HatiJsonapiError::NotFound
|
|
9
|
+
# HatiJsonapiError::BadRequest
|
|
10
|
+
# HatiJsonapiError::Unauthorized
|
|
11
|
+
# HatiJsonapiError::Forbidden
|
|
12
|
+
# etc.
|
|
13
|
+
def load_errors!
|
|
14
|
+
return if loaded?
|
|
15
|
+
|
|
16
|
+
HatiJsonapiError::STATUS_MAP.each do |status, value|
|
|
17
|
+
next if HatiJsonapiError.const_defined?(value[:name])
|
|
18
|
+
|
|
19
|
+
err_klass = create_error_class(status, value)
|
|
20
|
+
|
|
21
|
+
status_klass_map[status] = err_klass
|
|
22
|
+
code_klass_map[value[:code]] = err_klass
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@loaded = true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# HatiJsonapiError::Kigen.fetch_err(400) # => HatiJsonapiError::BadRequest
|
|
29
|
+
# HatiJsonapiError::Kigen.fetch_err(:bad_request)
|
|
30
|
+
def fetch_err(err)
|
|
31
|
+
return unless loaded?
|
|
32
|
+
|
|
33
|
+
status_klass_map[err] || code_klass_map[err]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# HatiJsonapiError::Kigen[400] # => HatiJsonapiError::BadRequest
|
|
37
|
+
def [](err)
|
|
38
|
+
fetch_err(err)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def loaded?
|
|
42
|
+
@loaded
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def status_klass_map
|
|
46
|
+
@status_klass_map ||= {}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def code_klass_map
|
|
50
|
+
@code_klass_map ||= {}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def create_error_class(status, value)
|
|
56
|
+
HatiJsonapiError.const_set(value[:name], Class.new(HatiJsonapiError::BaseError) do
|
|
57
|
+
define_method :initialize do |**attrs|
|
|
58
|
+
defaults = {
|
|
59
|
+
code: value[:code],
|
|
60
|
+
message: value[:message],
|
|
61
|
+
title: value[:message],
|
|
62
|
+
status: status
|
|
63
|
+
}
|
|
64
|
+
super(**defaults.merge(attrs))
|
|
65
|
+
end
|
|
66
|
+
end)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HatiJsonapiError
|
|
4
|
+
# This class is used to serialize errors to a JSON API format.
|
|
5
|
+
class PoroSerializer
|
|
6
|
+
SHORT_KEYS = %i[status title detail source].freeze
|
|
7
|
+
|
|
8
|
+
def initialize(error)
|
|
9
|
+
@errors = normalized_errors(error)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def serialize_to_json(short: false)
|
|
13
|
+
serializable_hash(short: short).to_json
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def serializable_hash(short: false)
|
|
17
|
+
if short
|
|
18
|
+
{ errors: errors.map { |error| error.to_h.slice(*SHORT_KEYS) } }
|
|
19
|
+
else
|
|
20
|
+
{ errors: errors.map(&:to_h) }
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
attr_reader :errors
|
|
27
|
+
|
|
28
|
+
def normalized_errors(error)
|
|
29
|
+
error.is_a?(Array) ? error : [error]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HatiJsonapiError
|
|
4
|
+
# This class is used to register errors and provide a fallback error.
|
|
5
|
+
class Registry
|
|
6
|
+
class << self
|
|
7
|
+
def fallback=(err)
|
|
8
|
+
@fallback = loaded_error?(err) ? err : fetch_error(err)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def fallback
|
|
12
|
+
@fallback ||= nil
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Base.loaded? # => true
|
|
16
|
+
# Registry.error_map = {
|
|
17
|
+
# ActiveRecord::RecordNotFound => :not_found,
|
|
18
|
+
# ActiveRecord::RecordInvalid => 422
|
|
19
|
+
# }
|
|
20
|
+
def error_map=(error_map)
|
|
21
|
+
error_map.each do |error, mapped_error|
|
|
22
|
+
next if loaded_error?(mapped_error)
|
|
23
|
+
|
|
24
|
+
error_map[error] = fetch_error(mapped_error)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
@error_map = error_map
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def error_map
|
|
31
|
+
@error_map ||= {}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def lookup_error(error)
|
|
35
|
+
error_map[error.class] || fallback
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def loaded_error?(error)
|
|
41
|
+
error.is_a?(Class) && error <= HatiJsonapiError::BaseError
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def fetch_error(error)
|
|
45
|
+
err = HatiJsonapiError::Kigen.fetch_err(error)
|
|
46
|
+
unless err
|
|
47
|
+
msg = "Error #{error} definition not found in lib/hati_jsonapi_error/api_error/error_const.rb"
|
|
48
|
+
raise HatiJsonapiError::Errors::NotDefinedErrorClassError, msg
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
err
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HatiJsonapiError
|
|
4
|
+
# This class is used to resolve errors and serialize them to a JSON API format.
|
|
5
|
+
class Resolver
|
|
6
|
+
attr_reader :errors, :serializer
|
|
7
|
+
|
|
8
|
+
def initialize(api_error, serializer: PoroSerializer)
|
|
9
|
+
@errors = error_arr(api_error)
|
|
10
|
+
@serializer = serializer.new(errors)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def status
|
|
14
|
+
errors.first.status
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_json(*_args)
|
|
18
|
+
serializer.serialize_to_json
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def error_arr(api_error)
|
|
24
|
+
api_error.is_a?(Array) ? api_error : [api_error]
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'hati_jsonapi_error/version'
|
|
4
|
+
|
|
5
|
+
# errors
|
|
6
|
+
require 'hati_jsonapi_error/errors/helpers_handle_error'
|
|
7
|
+
require 'hati_jsonapi_error/errors/helpers_render_error'
|
|
8
|
+
require 'hati_jsonapi_error/errors/not_defined_error_class_error'
|
|
9
|
+
require 'hati_jsonapi_error/errors/not_loaded_error'
|
|
10
|
+
|
|
11
|
+
# api_error/*
|
|
12
|
+
require 'hati_jsonapi_error/api_error/base_error'
|
|
13
|
+
require 'hati_jsonapi_error/api_error/error_const'
|
|
14
|
+
require 'hati_jsonapi_error/api_error/links'
|
|
15
|
+
require 'hati_jsonapi_error/api_error/source'
|
|
16
|
+
|
|
17
|
+
# logic
|
|
18
|
+
require 'hati_jsonapi_error/config'
|
|
19
|
+
require 'hati_jsonapi_error/kigen'
|
|
20
|
+
require 'hati_jsonapi_error/helpers'
|
|
21
|
+
require 'hati_jsonapi_error/poro_serializer'
|
|
22
|
+
require 'hati_jsonapi_error/registry'
|
|
23
|
+
require 'hati_jsonapi_error/resolver'
|
metadata
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: hati-jsonapi-error
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Marie Giy
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: hati-jsonapi-error is a Ruby gem for Standardized JSON Error.
|
|
13
|
+
email:
|
|
14
|
+
- giy.mariya@gmail.com
|
|
15
|
+
executables: []
|
|
16
|
+
extensions: []
|
|
17
|
+
extra_rdoc_files: []
|
|
18
|
+
files:
|
|
19
|
+
- LICENSE
|
|
20
|
+
- README.md
|
|
21
|
+
- hati-jsonapi-error.gemspec
|
|
22
|
+
- lib/hati_jsonapi_error.rb
|
|
23
|
+
- lib/hati_jsonapi_error/api_error/base_error.rb
|
|
24
|
+
- lib/hati_jsonapi_error/api_error/error_const.rb
|
|
25
|
+
- lib/hati_jsonapi_error/api_error/links.rb
|
|
26
|
+
- lib/hati_jsonapi_error/api_error/source.rb
|
|
27
|
+
- lib/hati_jsonapi_error/config.rb
|
|
28
|
+
- lib/hati_jsonapi_error/errors/helpers_handle_error.rb
|
|
29
|
+
- lib/hati_jsonapi_error/errors/helpers_render_error.rb
|
|
30
|
+
- lib/hati_jsonapi_error/errors/not_defined_error_class_error.rb
|
|
31
|
+
- lib/hati_jsonapi_error/errors/not_loaded_error.rb
|
|
32
|
+
- lib/hati_jsonapi_error/helpers.rb
|
|
33
|
+
- lib/hati_jsonapi_error/kigen.rb
|
|
34
|
+
- lib/hati_jsonapi_error/poro_serializer.rb
|
|
35
|
+
- lib/hati_jsonapi_error/registry.rb
|
|
36
|
+
- lib/hati_jsonapi_error/resolver.rb
|
|
37
|
+
- lib/hati_jsonapi_error/version.rb
|
|
38
|
+
homepage: https://github.com/hackico-ai/hati-jsonapi-error
|
|
39
|
+
licenses:
|
|
40
|
+
- MIT
|
|
41
|
+
metadata:
|
|
42
|
+
repo_homepage: https://github.com/hackico-ai/hati-jsonapi-error
|
|
43
|
+
allowed_push_host: https://rubygems.org
|
|
44
|
+
homepage_uri: https://github.com/hackico-ai/hati-jsonapi-error
|
|
45
|
+
changelog_uri: https://github.com/hackico-ai/hati-jsonapi-error/blob/main/CHANGELOG.md
|
|
46
|
+
source_code_uri: https://github.com/hackico-ai/hati-jsonapi-error
|
|
47
|
+
bug_tracker_uri: https://github.com/hackico-ai/hati-jsonapi-error/issues
|
|
48
|
+
rubygems_mfa_required: 'true'
|
|
49
|
+
rdoc_options: []
|
|
50
|
+
require_paths:
|
|
51
|
+
- lib
|
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
53
|
+
requirements:
|
|
54
|
+
- - ">="
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: 3.0.0
|
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
requirements: []
|
|
63
|
+
rubygems_version: 3.6.9
|
|
64
|
+
specification_version: 4
|
|
65
|
+
summary: 'Standardized JSON: API-compliant error responses made easy for your Web
|
|
66
|
+
API.'
|
|
67
|
+
test_files: []
|