jsonapi-resources 0.7.0 → 0.7.1.beta1

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.
@@ -1,8 +1,13 @@
1
1
  Description:
2
2
  Generator for JSONAPI Resources
3
3
 
4
- Example:
4
+ Examples:
5
5
  rails generate jsonapi:resource Post
6
6
 
7
7
  This will create:
8
8
  app/resources/post_resource.rb
9
+
10
+ rails generate jsonapi:controller Post
11
+
12
+ This will create:
13
+ app/controllers/posts_controller.rb
@@ -0,0 +1,14 @@
1
+ module Jsonapi
2
+ class ControllerGenerator < Rails::Generators::NamedBase
3
+ source_root File.expand_path('../templates', __FILE__)
4
+
5
+ def create_resource
6
+ template_file = File.join(
7
+ 'app/controllers',
8
+ class_path,
9
+ "#{file_name.pluralize}_controller.rb"
10
+ )
11
+ template 'jsonapi_controller.rb', template_file
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,4 @@
1
+ <% module_namespacing do -%>
2
+ class <%= class_name.pluralize %>Controller < JSONAPI::ResourceController
3
+ end
4
+ <% end -%>
@@ -18,7 +18,7 @@ class ActiveRecordOperationsProcessor < JSONAPI::OperationsProcessor
18
18
  # Catch errors that should be handled before JSONAPI::Exceptions::Error
19
19
  # and other unprocessed exceptions
20
20
  def process_operation(operation)
21
- with_default_handling do
21
+ with_default_handling do
22
22
  begin
23
23
  operation.apply
24
24
  rescue ActiveRecord::DeleteRestrictionError => e
@@ -26,9 +26,10 @@ class ActiveRecordOperationsProcessor < JSONAPI::OperationsProcessor
26
26
  return JSONAPI::ErrorsOperationResult.new(record_locked_error.errors[0].code, record_locked_error.errors)
27
27
 
28
28
  rescue ActiveRecord::RecordNotFound
29
- record_not_found = JSONAPI::Exceptions::RecordNotFound.new(operation.associated_key)
29
+ key = operation.respond_to?(:associated_key) ? operation.associated_key : :unavailable
30
+ record_not_found = JSONAPI::Exceptions::RecordNotFound.new(key)
30
31
  return JSONAPI::ErrorsOperationResult.new(record_not_found.errors[0].code, record_not_found.errors)
31
32
  end
32
- end
33
+ end
33
34
  end
34
35
  end
@@ -179,9 +179,13 @@ module JSONAPI
179
179
  when JSONAPI::Exceptions::Error
180
180
  render_errors(e.errors)
181
181
  else
182
- internal_server_error = JSONAPI::Exceptions::InternalServerError.new(e)
183
- Rails.logger.error { "Internal Server Error: #{e.message} #{e.backtrace.join("\n")}" }
184
- render_errors(internal_server_error.errors)
182
+ if JSONAPI.configuration.exception_class_whitelist.any? { |k| e.class.ancestors.include?(k) }
183
+ fail e
184
+ else
185
+ internal_server_error = JSONAPI::Exceptions::InternalServerError.new(e)
186
+ Rails.logger.error { "Internal Server Error: #{e.message} #{e.backtrace.join("\n")}" }
187
+ render_errors(internal_server_error.errors)
188
+ end
185
189
  end
186
190
  end
187
191
 
@@ -1,30 +1,30 @@
1
1
  module JSONAPI
2
- VALIDATION_ERROR = 100
3
- INVALID_RESOURCE = 101
4
- FILTER_NOT_ALLOWED = 102
5
- INVALID_FIELD_VALUE = 103
6
- INVALID_FIELD = 104
7
- PARAM_NOT_ALLOWED = 105
8
- PARAM_MISSING = 106
9
- INVALID_FILTER_VALUE = 107
10
- COUNT_MISMATCH = 108
11
- KEY_ORDER_MISMATCH = 109
12
- KEY_NOT_INCLUDED_IN_URL = 110
13
- INVALID_INCLUDE = 112
14
- RELATION_EXISTS = 113
15
- INVALID_SORT_CRITERIA = 114
16
- INVALID_LINKS_OBJECT = 115
17
- TYPE_MISMATCH = 116
18
- INVALID_PAGE_OBJECT = 117
19
- INVALID_PAGE_VALUE = 118
20
- INVALID_FIELD_FORMAT = 119
21
- INVALID_FILTERS_SYNTAX = 120
22
- SAVE_FAILED = 121
23
- FORBIDDEN = 403
24
- RECORD_NOT_FOUND = 404
25
- UNSUPPORTED_MEDIA_TYPE = 415
26
- LOCKED = 423
27
- INTERNAL_SERVER_ERROR = 500
2
+ VALIDATION_ERROR = '100'
3
+ INVALID_RESOURCE = '101'
4
+ FILTER_NOT_ALLOWED = '102'
5
+ INVALID_FIELD_VALUE = '103'
6
+ INVALID_FIELD = '104'
7
+ PARAM_NOT_ALLOWED = '105'
8
+ PARAM_MISSING = '106'
9
+ INVALID_FILTER_VALUE = '107'
10
+ COUNT_MISMATCH = '108'
11
+ KEY_ORDER_MISMATCH = '109'
12
+ KEY_NOT_INCLUDED_IN_URL = '110'
13
+ INVALID_INCLUDE = '112'
14
+ RELATION_EXISTS = '113'
15
+ INVALID_SORT_CRITERIA = '114'
16
+ INVALID_LINKS_OBJECT = '115'
17
+ TYPE_MISMATCH = '116'
18
+ INVALID_PAGE_OBJECT = '117'
19
+ INVALID_PAGE_VALUE = '118'
20
+ INVALID_FIELD_FORMAT = '119'
21
+ INVALID_FILTERS_SYNTAX = '120'
22
+ SAVE_FAILED = '121'
23
+ FORBIDDEN = '403'
24
+ RECORD_NOT_FOUND = '404'
25
+ UNSUPPORTED_MEDIA_TYPE = '415'
26
+ LOCKED = '423'
27
+ INTERNAL_SERVER_ERROR = '500'
28
28
 
29
29
  TEXT_ERRORS =
30
30
  { VALIDATION_ERROR => 'VALIDATION_ERROR',
@@ -18,8 +18,10 @@ module JSONAPI
18
18
 
19
19
  [JSONAPI::Error.new(code: JSONAPI::INTERNAL_SERVER_ERROR,
20
20
  status: :internal_server_error,
21
- title: 'Internal Server Error',
22
- detail: 'Internal Server Error',
21
+ title: I18n.t('jsonapi-resources.exceptions.internal_server_error.title',
22
+ default: 'Internal Server Error'),
23
+ detail: I18n.t('jsonapi-resources.exceptions.internal_server_error.detail',
24
+ default: 'Internal Server Error'),
23
25
  meta: meta)]
24
26
  end
25
27
  end
@@ -33,8 +35,10 @@ module JSONAPI
33
35
  def errors
34
36
  [JSONAPI::Error.new(code: JSONAPI::INVALID_RESOURCE,
35
37
  status: :bad_request,
36
- title: 'Invalid resource',
37
- detail: "#{resource} is not a valid resource.")]
38
+ title: I18n.t('jsonapi-resources.exceptions.invalid_resource.title',
39
+ default: 'Invalid resource'),
40
+ detail: I18n.t('jsonapi-resources.exceptions.invalid_resource.detail',
41
+ default: "#{resource} is not a valid resource.", resource: resource))]
38
42
  end
39
43
  end
40
44
 
@@ -47,8 +51,10 @@ module JSONAPI
47
51
  def errors
48
52
  [JSONAPI::Error.new(code: JSONAPI::RECORD_NOT_FOUND,
49
53
  status: :not_found,
50
- title: 'Record not found',
51
- detail: "The record identified by #{id} could not be found.")]
54
+ title: I18n.translate('jsonapi-resources.exceptions.record_not_found.title',
55
+ default: 'Record not found'),
56
+ detail: I18n.translate('jsonapi-resources.exceptions.record_not_found.detail',
57
+ default: "The record identified by #{id} could not be found.", id: id))]
52
58
  end
53
59
  end
54
60
 
@@ -61,8 +67,12 @@ module JSONAPI
61
67
  def errors
62
68
  [JSONAPI::Error.new(code: JSONAPI::UNSUPPORTED_MEDIA_TYPE,
63
69
  status: :unsupported_media_type,
64
- title: 'Unsupported media type',
65
- detail: "All requests that create or update resources must use the '#{JSONAPI::MEDIA_TYPE}' Content-Type. This request specified '#{media_type}.'")]
70
+ title: I18n.translate('jsonapi-resources.exceptions.unsupported_media_type.title',
71
+ default: 'Unsupported media type'),
72
+ detail: I18n.translate('jsonapi-resources.exceptions.unsupported_media_type.detail',
73
+ default: "All requests that create or update must use the '#{JSONAPI::MEDIA_TYPE}' Content-Type. This request specified '#{media_type}'.",
74
+ needed_media_type: JSONAPI::MEDIA_TYPE,
75
+ media_type: media_type))]
66
76
  end
67
77
  end
68
78
 
@@ -75,8 +85,11 @@ module JSONAPI
75
85
  def errors
76
86
  [JSONAPI::Error.new(code: JSONAPI::RELATION_EXISTS,
77
87
  status: :bad_request,
78
- title: 'Relation exists',
79
- detail: "The relation to #{id} already exists.")]
88
+ title: I18n.translate('jsonapi-resources.exceptions.has_many_relation.title',
89
+ default: 'Relation exists'),
90
+ detail: I18n.translate('jsonapi-resources.exceptions.has_many_relation.detail',
91
+ default: "The relation to #{id} already exists.",
92
+ id: id))]
80
93
  end
81
94
  end
82
95
 
@@ -84,8 +97,10 @@ module JSONAPI
84
97
  def errors
85
98
  [JSONAPI::Error.new(code: JSONAPI::FORBIDDEN,
86
99
  status: :forbidden,
87
- title: 'Complete replacement forbidden',
88
- detail: 'Complete replacement forbidden for this relationship')]
100
+ title: I18n.translate('jsonapi-resources.exceptions.to_many_set_replacement_forbidden.title',
101
+ default: 'Complete replacement forbidden'),
102
+ detail: I18n.translate('jsonapi-resources.exceptions.to_many_set_replacement_forbidden.detail',
103
+ default: 'Complete replacement forbidden for this relationship'))]
89
104
  end
90
105
  end
91
106
 
@@ -98,8 +113,11 @@ module JSONAPI
98
113
  def errors
99
114
  [JSONAPI::Error.new(code: JSONAPI::INVALID_FILTERS_SYNTAX,
100
115
  status: :bad_request,
101
- title: 'Invalid filters syntax',
102
- detail: "#{filters} is not a valid syntax for filtering.")]
116
+ title: I18n.translate('jsonapi-resources.exceptions.invalid_filter_syntax.title',
117
+ default: 'Invalid filters syntax'),
118
+ detail: I18n.translate('jsonapi-resources.exceptions.invalid_filter_syntax.detail',
119
+ default: "#{filters} is not a valid syntax for filtering.",
120
+ filters: filters))]
103
121
  end
104
122
  end
105
123
 
@@ -112,8 +130,10 @@ module JSONAPI
112
130
  def errors
113
131
  [JSONAPI::Error.new(code: JSONAPI::FILTER_NOT_ALLOWED,
114
132
  status: :bad_request,
115
- title: 'Filter not allowed',
116
- detail: "#{filter} is not allowed.")]
133
+ title: I18n.translate('jsonapi-resources.exceptions.filter_not_allowed.title',
134
+ default: 'Filter not allowed'),
135
+ detail: I18n.translate('jsonapi-resources.exceptions.filter_not_allowed.detail',
136
+ default: "#{filter} is not allowed.", filter: filter))]
117
137
  end
118
138
  end
119
139
 
@@ -127,8 +147,11 @@ module JSONAPI
127
147
  def errors
128
148
  [JSONAPI::Error.new(code: JSONAPI::INVALID_FILTER_VALUE,
129
149
  status: :bad_request,
130
- title: 'Invalid filter value',
131
- detail: "#{value} is not a valid value for #{filter}.")]
150
+ title: I18n.translate('jsonapi-resources.exceptions.invalid_filter_value.title',
151
+ default: 'Invalid filter value'),
152
+ detail: I18n.translate('jsonapi-resources.exceptions.invalid_filter_value.detail',
153
+ default: "#{value} is not a valid value for #{filter}.",
154
+ value: value, filter: filter))]
132
155
  end
133
156
  end
134
157
 
@@ -142,8 +165,11 @@ module JSONAPI
142
165
  def errors
143
166
  [JSONAPI::Error.new(code: JSONAPI::INVALID_FIELD_VALUE,
144
167
  status: :bad_request,
145
- title: 'Invalid field value',
146
- detail: "#{value} is not a valid value for #{field}.")]
168
+ title: I18n.translate('jsonapi-resources.exceptions.invalid_field_value.title',
169
+ default: 'Invalid field value'),
170
+ detail: I18n.translate('jsonapi-resources.exceptions.invalid_field_value.detail',
171
+ default: "#{value} is not a valid value for #{field}.",
172
+ value: value, field: field))]
147
173
  end
148
174
  end
149
175
 
@@ -151,8 +177,10 @@ module JSONAPI
151
177
  def errors
152
178
  [JSONAPI::Error.new(code: JSONAPI::INVALID_FIELD_FORMAT,
153
179
  status: :bad_request,
154
- title: 'Invalid field format',
155
- detail: 'Fields must specify a type.')]
180
+ title: I18n.translate('jsonapi-resources.exceptions.invalid_field_format.title',
181
+ default: 'Invalid field format'),
182
+ detail: I18n.translate('jsonapi-resources.exceptions.invalid_field_format.detail',
183
+ default: 'Fields must specify a type.'))]
156
184
  end
157
185
  end
158
186
 
@@ -160,8 +188,10 @@ module JSONAPI
160
188
  def errors
161
189
  [JSONAPI::Error.new(code: JSONAPI::INVALID_LINKS_OBJECT,
162
190
  status: :bad_request,
163
- title: 'Invalid Links Object',
164
- detail: 'Data is not a valid Links Object.')]
191
+ title: I18n.translate('jsonapi-resources.exceptions.invalid_links_object.title',
192
+ default: 'Invalid Links Object'),
193
+ detail: I18n.translate('jsonapi-resources.exceptions.invalid_links_object.detail',
194
+ default: 'Data is not a valid Links Object.'))]
165
195
  end
166
196
  end
167
197
 
@@ -174,8 +204,10 @@ module JSONAPI
174
204
  def errors
175
205
  [JSONAPI::Error.new(code: JSONAPI::TYPE_MISMATCH,
176
206
  status: :bad_request,
177
- title: 'Type Mismatch',
178
- detail: "#{type} is not a valid type for this operation.")]
207
+ title: I18n.translate('jsonapi-resources.exceptions.type_mismatch.title',
208
+ default: 'Type Mismatch'),
209
+ detail: I18n.translate('jsonapi-resources.exceptions.type_mismatch.detail',
210
+ default: "#{type} is not a valid type for this operation.", type: type))]
179
211
  end
180
212
  end
181
213
 
@@ -189,8 +221,11 @@ module JSONAPI
189
221
  def errors
190
222
  [JSONAPI::Error.new(code: JSONAPI::INVALID_FIELD,
191
223
  status: :bad_request,
192
- title: 'Invalid field',
193
- detail: "#{field} is not a valid field for #{type}.")]
224
+ title: I18n.translate('jsonapi-resources.exceptions.invalid_field.title',
225
+ default: 'Invalid field'),
226
+ detail: I18n.translate('jsonapi-resources.exceptions.invalid_field.detail',
227
+ default: "#{field} is not a valid field for #{type}.",
228
+ field: field, type: type))]
194
229
  end
195
230
  end
196
231
 
@@ -204,8 +239,11 @@ module JSONAPI
204
239
  def errors
205
240
  [JSONAPI::Error.new(code: JSONAPI::INVALID_INCLUDE,
206
241
  status: :bad_request,
207
- title: 'Invalid field',
208
- detail: "#{relationship} is not a valid relationship of #{resource}")]
242
+ title: I18n.translate('jsonapi-resources.exceptions.invalid_include.title',
243
+ default: 'Invalid field'),
244
+ detail: I18n.translate('jsonapi-resources.exceptions.invalid_include.detail',
245
+ default: "#{relationship} is not a valid relationship of #{resource}",
246
+ relationship: relationship, resource: resource))]
209
247
  end
210
248
  end
211
249
 
@@ -219,8 +257,11 @@ module JSONAPI
219
257
  def errors
220
258
  [JSONAPI::Error.new(code: JSONAPI::INVALID_SORT_CRITERIA,
221
259
  status: :bad_request,
222
- title: 'Invalid sort criteria',
223
- detail: "#{sort_criteria} is not a valid sort criteria for #{resource}")]
260
+ title: I18n.translate('jsonapi-resources.exceptions.invalid_sort_criteria.title',
261
+ default: 'Invalid sort criteria'),
262
+ detail: I18n.translate('jsonapi-resources.exceptions.invalid_sort_criteria.detail',
263
+ default: "#{sort_criteria} is not a valid sort criteria for #{resource}",
264
+ sort_criteria: sort_criteria, resource: resource))]
224
265
  end
225
266
  end
226
267
 
@@ -234,8 +275,11 @@ module JSONAPI
234
275
  params.collect do |param|
235
276
  JSONAPI::Error.new(code: JSONAPI::PARAM_NOT_ALLOWED,
236
277
  status: :bad_request,
237
- title: 'Param not allowed',
238
- detail: "#{param} is not allowed.")
278
+ title: I18n.translate('jsonapi-resources.exceptions.parameters_not_allowed.title',
279
+ default: 'Param not allowed'),
280
+ detail: I18n.translate('jsonapi-resources.exceptions.parameters_not_allowed.detail',
281
+ default: "#{param} is not allowed.", param: param))
282
+
239
283
  end
240
284
  end
241
285
  end
@@ -249,8 +293,10 @@ module JSONAPI
249
293
  def errors
250
294
  [JSONAPI::Error.new(code: JSONAPI::PARAM_MISSING,
251
295
  status: :bad_request,
252
- title: 'Missing Parameter',
253
- detail: "The required parameter, #{param}, is missing.")]
296
+ title: I18n.translate('jsonapi-resources.exceptions.parameter_missing.title',
297
+ default: 'Missing Parameter'),
298
+ detail: I18n.translate('jsonapi-resources.exceptions.parameter_missing.detail',
299
+ default: "The required parameter, #{param}, is missing.", param: param))]
254
300
  end
255
301
  end
256
302
 
@@ -258,8 +304,10 @@ module JSONAPI
258
304
  def errors
259
305
  [JSONAPI::Error.new(code: JSONAPI::COUNT_MISMATCH,
260
306
  status: :bad_request,
261
- title: 'Count to key mismatch',
262
- detail: 'The resource collection does not contain the same number of objects as the number of keys.')]
307
+ title: I18n.translate('jsonapi-resources.exceptions.count_mismatch.title',
308
+ default: 'Count to key mismatch'),
309
+ detail: I18n.translate('jsonapi-resources.exceptions.count_mismatch.detail',
310
+ default: 'The resource collection does not contain the same number of objects as the number of keys.'))]
263
311
  end
264
312
  end
265
313
 
@@ -272,8 +320,11 @@ module JSONAPI
272
320
  def errors
273
321
  [JSONAPI::Error.new(code: JSONAPI::KEY_NOT_INCLUDED_IN_URL,
274
322
  status: :bad_request,
275
- title: 'Key is not included in URL',
276
- detail: "The URL does not support the key #{key}")]
323
+ title: I18n.translate('jsonapi-resources.exceptions.key_not_included_in_url.title',
324
+ default: 'Key is not included in URL'),
325
+ detail: I18n.translate('jsonapi-resources.exceptions.key_not_included_in_url.detail',
326
+ default: "The URL does not support the key #{key}",
327
+ key: key))]
277
328
  end
278
329
  end
279
330
 
@@ -281,8 +332,10 @@ module JSONAPI
281
332
  def errors
282
333
  [JSONAPI::Error.new(code: JSONAPI::KEY_ORDER_MISMATCH,
283
334
  status: :bad_request,
284
- title: 'A key is required',
285
- detail: 'The resource object does not contain a key.')]
335
+ title: I18n.translate('jsonapi-resources.exceptions.missing_key.title',
336
+ default: 'A key is required'),
337
+ detail: I18n.translate('jsonapi-resources.exceptions.missing_key.detail',
338
+ default: 'The resource object does not contain a key.'))]
286
339
  end
287
340
  end
288
341
 
@@ -295,16 +348,18 @@ module JSONAPI
295
348
  def errors
296
349
  [JSONAPI::Error.new(code: JSONAPI::LOCKED,
297
350
  status: :locked,
298
- title: 'Locked resource',
351
+ title: I18n.translate('jsonapi-resources.exceptions.record_locked.title',
352
+ default: 'Locked resource'),
299
353
  detail: "#{message}")]
300
354
  end
301
355
  end
302
356
 
303
357
  class ValidationErrors < Error
304
- attr_reader :error_messages, :resource_relationships
358
+ attr_reader :error_messages, :error_metadata, :resource_relationships
305
359
 
306
360
  def initialize(resource)
307
361
  @error_messages = resource.model_error_messages
362
+ @error_metadata = resource.validation_error_metadata
308
363
  @resource_relationships = resource.class._relationships.keys
309
364
  @key_formatter = JSONAPI.configuration.key_formatter
310
365
  end
@@ -326,7 +381,13 @@ module JSONAPI
326
381
  status: :unprocessable_entity,
327
382
  title: message,
328
383
  detail: "#{format_key(attr_key)} - #{message}",
329
- source: { pointer: pointer(attr_key) })
384
+ source: { pointer: pointer(attr_key) },
385
+ meta: metadata_for(attr_key, message))
386
+ end
387
+
388
+ def metadata_for(attr_key, message)
389
+ return if error_metadata.nil?
390
+ error_metadata[attr_key] ? error_metadata[attr_key][message] : nil
330
391
  end
331
392
 
332
393
  def pointer(attr_or_relationship_name)
@@ -343,8 +404,10 @@ module JSONAPI
343
404
  def errors
344
405
  [JSONAPI::Error.new(code: JSONAPI::SAVE_FAILED,
345
406
  status: :unprocessable_entity,
346
- title: 'Save failed or was cancelled',
347
- detail: 'Save failed or was cancelled')]
407
+ title: I18n.translate('jsonapi-resources.exceptions.save_failed.title',
408
+ default: 'Save failed or was cancelled'),
409
+ detail: I18n.translate('jsonapi-resources.exceptions.save_failed.detail',
410
+ default: 'Save failed or was cancelled'))]
348
411
  end
349
412
  end
350
413
 
@@ -352,8 +415,10 @@ module JSONAPI
352
415
  def errors
353
416
  [JSONAPI::Error.new(code: JSONAPI::INVALID_PAGE_OBJECT,
354
417
  status: :bad_request,
355
- title: 'Invalid Page Object',
356
- detail: 'Invalid Page Object.')]
418
+ title: I18n.translate('jsonapi-resources.exceptions.invalid_page_object.title',
419
+ default: 'Invalid Page Object'),
420
+ detail: I18n.translate('jsonapi-resources.exceptions.invalid_page_object.detail',
421
+ default: 'Invalid Page Object.'))]
357
422
  end
358
423
  end
359
424
 
@@ -367,8 +432,11 @@ module JSONAPI
367
432
  params.collect do |param|
368
433
  JSONAPI::Error.new(code: JSONAPI::PARAM_NOT_ALLOWED,
369
434
  status: :bad_request,
370
- title: 'Page parameter not allowed',
371
- detail: "#{param} is not an allowed page parameter.")
435
+ title: I18n.translate('jsonapi-resources.exceptions.page_parameters_not_allowed.title',
436
+ default: 'Page parameter not allowed'),
437
+ detail: I18n.translate('jsonapi-resources.exceptions.page_parameters_not_allowed.detail',
438
+ default: "#{param} is not an allowed page parameter.",
439
+ param: param))
372
440
  end
373
441
  end
374
442
  end
@@ -378,13 +446,16 @@ module JSONAPI
378
446
  def initialize(page, value, msg = nil)
379
447
  @page = page
380
448
  @value = value
381
- @msg = msg || "#{value} is not a valid value for #{page} page parameter."
449
+ @msg = msg || I18n.translate('jsonapi-resources.exceptions.invalid_page_value.detail',
450
+ default: "#{value} is not a valid value for #{page} page parameter.",
451
+ value: value, page: page)
382
452
  end
383
453
 
384
454
  def errors
385
455
  [JSONAPI::Error.new(code: JSONAPI::INVALID_PAGE_VALUE,
386
456
  status: :bad_request,
387
- title: 'Invalid page value',
457
+ title: I18n.translate('jsonapi-resources.exceptions.invalid_page_value.title',
458
+ default: 'Invalid page value'),
388
459
  detail: @msg)]
389
460
  end
390
461
  end