jsonapi-resources 0.7.0 → 0.7.1.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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