jsonapi-resources 0.4.2 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -2
- data/README.md +103 -71
- data/Rakefile +2 -2
- data/jsonapi-resources.gemspec +2 -2
- data/lib/jsonapi-resources.rb +0 -1
- data/lib/jsonapi/active_record_operations_processor.rb +10 -2
- data/lib/jsonapi/acts_as_resource_controller.rb +26 -24
- data/lib/jsonapi/association.rb +50 -15
- data/lib/jsonapi/callbacks.rb +1 -2
- data/lib/jsonapi/configuration.rb +8 -24
- data/lib/jsonapi/error.rb +1 -2
- data/lib/jsonapi/error_codes.rb +3 -1
- data/lib/jsonapi/exceptions.rb +59 -47
- data/lib/jsonapi/include_directives.rb +11 -11
- data/lib/jsonapi/mime_types.rb +2 -2
- data/lib/jsonapi/operation.rb +28 -11
- data/lib/jsonapi/operations_processor.rb +16 -5
- data/lib/jsonapi/paginator.rb +19 -19
- data/lib/jsonapi/request.rb +175 -196
- data/lib/jsonapi/resource.rb +158 -105
- data/lib/jsonapi/resource_serializer.rb +37 -26
- data/lib/jsonapi/resources/version.rb +2 -2
- data/lib/jsonapi/response_document.rb +5 -4
- data/lib/jsonapi/routing_ext.rb +24 -19
- data/test/controllers/controller_test.rb +261 -31
- data/test/fixtures/active_record.rb +206 -8
- data/test/fixtures/book_comments.yml +2 -1
- data/test/fixtures/books.yml +1 -0
- data/test/fixtures/documents.yml +3 -0
- data/test/fixtures/people.yml +8 -1
- data/test/fixtures/pictures.yml +15 -0
- data/test/fixtures/products.yml +3 -0
- data/test/fixtures/vehicles.yml +8 -0
- data/test/helpers/{hash_helpers.rb → assertions.rb} +6 -1
- data/test/integration/requests/request_test.rb +14 -3
- data/test/integration/routes/routes_test.rb +47 -0
- data/test/test_helper.rb +27 -4
- data/test/unit/serializer/include_directives_test.rb +5 -0
- data/test/unit/serializer/polymorphic_serializer_test.rb +384 -0
- data/test/unit/serializer/serializer_test.rb +19 -1
- metadata +14 -4
data/Rakefile
CHANGED
data/jsonapi-resources.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = JSONAPI::Resources::VERSION
|
9
9
|
spec.authors = ['Dan Gebhardt', 'Larry Gebhardt']
|
10
10
|
spec.email = ['dan@cerebris.com', 'larry@cerebris.com']
|
11
|
-
spec.summary =
|
12
|
-
spec.description =
|
11
|
+
spec.summary = 'Easily support JSON API in Rails.'
|
12
|
+
spec.description = 'A resource-centric approach to implementing the controllers, routes, and serializers needed to support the JSON API spec.'
|
13
13
|
spec.homepage = 'https://github.com/cerebris/jsonapi-resources'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
data/lib/jsonapi-resources.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
class ActiveRecordOperationsProcessor < JSONAPI::OperationsProcessor
|
2
|
-
|
3
2
|
private
|
3
|
+
|
4
4
|
def transaction
|
5
5
|
if @transactional
|
6
6
|
ActiveRecord::Base.transaction do
|
@@ -12,7 +12,7 @@ class ActiveRecordOperationsProcessor < JSONAPI::OperationsProcessor
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def rollback
|
15
|
-
|
15
|
+
fail ActiveRecord::Rollback if @transactional
|
16
16
|
end
|
17
17
|
|
18
18
|
def process_operation(operation)
|
@@ -24,5 +24,13 @@ class ActiveRecordOperationsProcessor < JSONAPI::OperationsProcessor
|
|
24
24
|
rescue ActiveRecord::RecordNotFound
|
25
25
|
record_not_found = JSONAPI::Exceptions::RecordNotFound.new(operation.associated_key)
|
26
26
|
return JSONAPI::ErrorsOperationResult.new(record_not_found.errors[0].code, record_not_found.errors)
|
27
|
+
|
28
|
+
rescue JSONAPI::Exceptions::Error => e
|
29
|
+
raise e
|
30
|
+
|
31
|
+
rescue => e
|
32
|
+
internal_server_error = JSONAPI::Exceptions::InternalServerError.new(e)
|
33
|
+
Rails.logger.error { "Internal Server Error: #{e.message} #{e.backtrace.join("\n")}" }
|
34
|
+
return JSONAPI::ErrorsOperationResult.new(internal_server_error.errors[0].code, internal_server_error.errors)
|
27
35
|
end
|
28
36
|
end
|
@@ -6,7 +6,7 @@ module JSONAPI
|
|
6
6
|
|
7
7
|
included do
|
8
8
|
before_filter :ensure_correct_media_type, only: [:create, :update, :create_association, :update_association]
|
9
|
-
|
9
|
+
append_before_filter :setup_request
|
10
10
|
after_filter :setup_response
|
11
11
|
end
|
12
12
|
|
@@ -60,6 +60,7 @@ module JSONAPI
|
|
60
60
|
end
|
61
61
|
|
62
62
|
private
|
63
|
+
|
63
64
|
def resource_klass
|
64
65
|
@resource_klass ||= resource_klass_name.safe_constantize
|
65
66
|
end
|
@@ -78,17 +79,14 @@ module JSONAPI
|
|
78
79
|
|
79
80
|
def ensure_correct_media_type
|
80
81
|
unless request.content_type == JSONAPI::MEDIA_TYPE
|
81
|
-
|
82
|
+
fail JSONAPI::Exceptions::UnsupportedMediaTypeError.new(request.content_type)
|
82
83
|
end
|
83
84
|
rescue => e
|
84
85
|
handle_exceptions(e)
|
85
86
|
end
|
86
87
|
|
87
88
|
def setup_request
|
88
|
-
@request = JSONAPI::Request.new(params,
|
89
|
-
context: context,
|
90
|
-
key_formatter: key_formatter
|
91
|
-
})
|
89
|
+
@request = JSONAPI::Request.new(params, context: context, key_formatter: key_formatter)
|
92
90
|
render_errors(@request.errors) unless @request.errors.empty?
|
93
91
|
rescue => e
|
94
92
|
handle_exceptions(e)
|
@@ -105,6 +103,11 @@ module JSONAPI
|
|
105
103
|
{}
|
106
104
|
end
|
107
105
|
|
106
|
+
# override to set scope_id
|
107
|
+
def scope_id
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
|
108
111
|
# Control by setting in an initializer:
|
109
112
|
# JSONAPI.configuration.json_key_format = :camelized_key
|
110
113
|
# JSONAPI.configuration.route = :camelized_route
|
@@ -128,7 +131,7 @@ module JSONAPI
|
|
128
131
|
end
|
129
132
|
|
130
133
|
def render_errors(errors)
|
131
|
-
operation_results = JSONAPI::OperationResults.new
|
134
|
+
operation_results = JSONAPI::OperationResults.new
|
132
135
|
result = JSONAPI::ErrorsOperationResult.new(errors[0].status, errors)
|
133
136
|
operation_results.add_result(result)
|
134
137
|
|
@@ -143,18 +146,17 @@ module JSONAPI
|
|
143
146
|
def create_response_document(operation_results)
|
144
147
|
JSONAPI::ResponseDocument.new(
|
145
148
|
operation_results,
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
}
|
149
|
+
primary_resource_klass: resource_klass,
|
150
|
+
include_directives: @request ? @request.include_directives : nil,
|
151
|
+
fields: @request ? @request.fields : nil,
|
152
|
+
base_url: base_url,
|
153
|
+
key_formatter: key_formatter,
|
154
|
+
route_formatter: route_formatter,
|
155
|
+
base_meta: base_response_meta,
|
156
|
+
base_links: base_response_links,
|
157
|
+
resource_serializer_klass: resource_serializer_klass,
|
158
|
+
request: @request,
|
159
|
+
scope_id: scope_id
|
158
160
|
)
|
159
161
|
end
|
160
162
|
|
@@ -169,11 +171,11 @@ module JSONAPI
|
|
169
171
|
# Note: Be sure to either call super(e) or handle JSONAPI::Exceptions::Error and raise unhandled exceptions
|
170
172
|
def handle_exceptions(e)
|
171
173
|
case e
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
174
|
+
when JSONAPI::Exceptions::Error
|
175
|
+
render_errors(e.errors)
|
176
|
+
else # raise all other exceptions
|
177
|
+
# :nocov:
|
178
|
+
fail e
|
177
179
|
# :nocov:
|
178
180
|
end
|
179
181
|
end
|
data/lib/jsonapi/association.rb
CHANGED
@@ -1,34 +1,69 @@
|
|
1
1
|
module JSONAPI
|
2
2
|
class Association
|
3
|
-
attr_reader :acts_as_set, :foreign_key, :type, :options, :name,
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
@
|
8
|
-
@
|
9
|
-
@
|
10
|
-
@
|
3
|
+
attr_reader :acts_as_set, :foreign_key, :type, :options, :name,
|
4
|
+
:class_name, :polymorphic
|
5
|
+
|
6
|
+
def initialize(name, options = {})
|
7
|
+
@name = name.to_s
|
8
|
+
@options = options
|
9
|
+
@acts_as_set = options.fetch(:acts_as_set, false) == true
|
10
|
+
@foreign_key = options[:foreign_key] ? options[:foreign_key].to_sym : nil
|
11
|
+
@module_path = options[:module_path] || ''
|
12
|
+
@relation_name = options.fetch(:relation_name, @name)
|
13
|
+
@polymorphic = options.fetch(:polymorphic, false) == true
|
11
14
|
end
|
12
15
|
|
16
|
+
alias_method :polymorphic?, :polymorphic
|
17
|
+
|
13
18
|
def primary_key
|
14
|
-
@primary_key ||=
|
19
|
+
@primary_key ||= resource_klass._primary_key
|
20
|
+
end
|
21
|
+
|
22
|
+
def resource_klass
|
23
|
+
@resource_klass ||= Resource.resource_for(@module_path + @class_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def relation_name(options = {})
|
27
|
+
case @relation_name
|
28
|
+
when Symbol
|
29
|
+
# :nocov:
|
30
|
+
@relation_name
|
31
|
+
# :nocov:
|
32
|
+
when String
|
33
|
+
@relation_name.to_sym
|
34
|
+
when Proc
|
35
|
+
@relation_name.call(options)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def type_for_source(source)
|
40
|
+
if polymorphic?
|
41
|
+
resource = source.public_send(name)
|
42
|
+
resource.class._type if resource
|
43
|
+
else
|
44
|
+
type
|
45
|
+
end
|
15
46
|
end
|
16
47
|
|
17
48
|
class HasOne < Association
|
18
|
-
def initialize(name, options={})
|
49
|
+
def initialize(name, options = {})
|
19
50
|
super
|
20
|
-
@class_name = options.fetch(:class_name, name.to_s.
|
51
|
+
@class_name = options.fetch(:class_name, name.to_s.camelize)
|
21
52
|
@type = class_name.underscore.pluralize.to_sym
|
22
|
-
@foreign_key ||=
|
53
|
+
@foreign_key ||= "#{name}_id".to_sym
|
54
|
+
end
|
55
|
+
|
56
|
+
def polymorphic_type
|
57
|
+
"#{type.to_s.singularize}_type" if polymorphic?
|
23
58
|
end
|
24
59
|
end
|
25
60
|
|
26
61
|
class HasMany < Association
|
27
|
-
def initialize(name, options={})
|
62
|
+
def initialize(name, options = {})
|
28
63
|
super
|
29
|
-
@class_name = options.fetch(:class_name, name.to_s.
|
64
|
+
@class_name = options.fetch(:class_name, name.to_s.camelize.singularize)
|
30
65
|
@type = class_name.underscore.pluralize.to_sym
|
31
|
-
@foreign_key
|
66
|
+
@foreign_key ||= "#{name.to_s.singularize}_ids".to_sym
|
32
67
|
end
|
33
68
|
end
|
34
69
|
end
|
data/lib/jsonapi/callbacks.rb
CHANGED
@@ -62,37 +62,21 @@ module JSONAPI
|
|
62
62
|
@operations_processor = JSONAPI::OperationsProcessor.operations_processor_for(@operations_processor_name)
|
63
63
|
end
|
64
64
|
|
65
|
-
|
66
|
-
@allowed_request_params = allowed_request_params
|
67
|
-
end
|
65
|
+
attr_writer :allowed_request_params
|
68
66
|
|
69
|
-
|
70
|
-
@default_paginator = default_paginator
|
71
|
-
end
|
67
|
+
attr_writer :default_paginator
|
72
68
|
|
73
|
-
|
74
|
-
@default_page_size = default_page_size
|
75
|
-
end
|
69
|
+
attr_writer :default_page_size
|
76
70
|
|
77
|
-
|
78
|
-
@maximum_page_size = maximum_page_size
|
79
|
-
end
|
71
|
+
attr_writer :maximum_page_size
|
80
72
|
|
81
|
-
|
82
|
-
@use_text_errors = use_text_errors
|
83
|
-
end
|
73
|
+
attr_writer :use_text_errors
|
84
74
|
|
85
|
-
|
86
|
-
@top_level_links_include_pagination = top_level_links_include_pagination
|
87
|
-
end
|
75
|
+
attr_writer :top_level_links_include_pagination
|
88
76
|
|
89
|
-
|
90
|
-
@top_level_meta_include_record_count = top_level_meta_include_record_count
|
91
|
-
end
|
77
|
+
attr_writer :top_level_meta_include_record_count
|
92
78
|
|
93
|
-
|
94
|
-
@top_level_meta_record_count_key = top_level_meta_record_count_key
|
95
|
-
end
|
79
|
+
attr_writer :top_level_meta_record_count_key
|
96
80
|
end
|
97
81
|
|
98
82
|
class << self
|
data/lib/jsonapi/error.rb
CHANGED
data/lib/jsonapi/error_codes.rb
CHANGED
@@ -24,6 +24,7 @@ module JSONAPI
|
|
24
24
|
RECORD_NOT_FOUND = 404
|
25
25
|
UNSUPPORTED_MEDIA_TYPE = 415
|
26
26
|
LOCKED = 423
|
27
|
+
INTERNAL_SERVER_ERROR = 500
|
27
28
|
|
28
29
|
TEXT_ERRORS =
|
29
30
|
{ VALIDATION_ERROR => 'VALIDATION_ERROR',
|
@@ -50,6 +51,7 @@ module JSONAPI
|
|
50
51
|
FORBIDDEN => 'FORBIDDEN',
|
51
52
|
RECORD_NOT_FOUND => 'RECORD_NOT_FOUND',
|
52
53
|
UNSUPPORTED_MEDIA_TYPE => 'UNSUPPORTED_MEDIA_TYPE',
|
53
|
-
LOCKED => 'LOCKED'
|
54
|
+
LOCKED => 'LOCKED',
|
55
|
+
INTERNAL_SERVER_ERROR => 'INTERNAL_SERVER_ERROR'
|
54
56
|
}
|
55
57
|
end
|
data/lib/jsonapi/exceptions.rb
CHANGED
@@ -2,6 +2,21 @@ module JSONAPI
|
|
2
2
|
module Exceptions
|
3
3
|
class Error < RuntimeError; end
|
4
4
|
|
5
|
+
class InternalServerError < Error
|
6
|
+
attr_accessor :exception
|
7
|
+
|
8
|
+
def initialize(exception)
|
9
|
+
@exception = exception
|
10
|
+
end
|
11
|
+
|
12
|
+
def errors
|
13
|
+
[JSONAPI::Error.new(code: JSONAPI::INTERNAL_SERVER_ERROR,
|
14
|
+
status: 500,
|
15
|
+
title: 'Internal Server Error',
|
16
|
+
detail: 'Internal Server Error')]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
5
20
|
class InvalidResource < Error
|
6
21
|
attr_accessor :resource
|
7
22
|
def initialize(resource)
|
@@ -10,9 +25,9 @@ module JSONAPI
|
|
10
25
|
|
11
26
|
def errors
|
12
27
|
[JSONAPI::Error.new(code: JSONAPI::INVALID_RESOURCE,
|
13
|
-
|
14
|
-
|
15
|
-
|
28
|
+
status: :bad_request,
|
29
|
+
title: 'Invalid resource',
|
30
|
+
detail: "#{resource} is not a valid resource.")]
|
16
31
|
end
|
17
32
|
end
|
18
33
|
|
@@ -24,9 +39,9 @@ module JSONAPI
|
|
24
39
|
|
25
40
|
def errors
|
26
41
|
[JSONAPI::Error.new(code: JSONAPI::RECORD_NOT_FOUND,
|
27
|
-
|
28
|
-
|
29
|
-
|
42
|
+
status: :not_found,
|
43
|
+
title: 'Record not found',
|
44
|
+
detail: "The record identified by #{id} could not be found.")]
|
30
45
|
end
|
31
46
|
end
|
32
47
|
|
@@ -38,9 +53,9 @@ module JSONAPI
|
|
38
53
|
|
39
54
|
def errors
|
40
55
|
[JSONAPI::Error.new(code: JSONAPI::UNSUPPORTED_MEDIA_TYPE,
|
41
|
-
|
42
|
-
|
43
|
-
|
56
|
+
status: :unsupported_media_type,
|
57
|
+
title: 'Unsupported media type',
|
58
|
+
detail: "All requests that create or update resources must use the '#{JSONAPI::MEDIA_TYPE}' Content-Type. This request specified '#{media_type}.'")]
|
44
59
|
end
|
45
60
|
end
|
46
61
|
|
@@ -75,9 +90,9 @@ module JSONAPI
|
|
75
90
|
|
76
91
|
def errors
|
77
92
|
[JSONAPI::Error.new(code: JSONAPI::INVALID_FILTERS_SYNTAX,
|
78
|
-
|
79
|
-
|
80
|
-
|
93
|
+
status: :bad_request,
|
94
|
+
title: 'Invalid filters syntax',
|
95
|
+
detail: "#{filters} is not a valid syntax for filtering.")]
|
81
96
|
end
|
82
97
|
end
|
83
98
|
|
@@ -89,9 +104,9 @@ module JSONAPI
|
|
89
104
|
|
90
105
|
def errors
|
91
106
|
[JSONAPI::Error.new(code: JSONAPI::FILTER_NOT_ALLOWED,
|
92
|
-
|
93
|
-
|
94
|
-
|
107
|
+
status: :bad_request,
|
108
|
+
title: 'Filter not allowed',
|
109
|
+
detail: "#{filter} is not allowed.")]
|
95
110
|
end
|
96
111
|
end
|
97
112
|
|
@@ -104,9 +119,9 @@ module JSONAPI
|
|
104
119
|
|
105
120
|
def errors
|
106
121
|
[JSONAPI::Error.new(code: JSONAPI::INVALID_FILTER_VALUE,
|
107
|
-
|
108
|
-
|
109
|
-
|
122
|
+
status: :bad_request,
|
123
|
+
title: 'Invalid filter value',
|
124
|
+
detail: "#{value} is not a valid value for #{filter}.")]
|
110
125
|
end
|
111
126
|
end
|
112
127
|
|
@@ -119,9 +134,9 @@ module JSONAPI
|
|
119
134
|
|
120
135
|
def errors
|
121
136
|
[JSONAPI::Error.new(code: JSONAPI::INVALID_FIELD_VALUE,
|
122
|
-
|
123
|
-
|
124
|
-
|
137
|
+
status: :bad_request,
|
138
|
+
title: 'Invalid field value',
|
139
|
+
detail: "#{value} is not a valid value for #{field}.")]
|
125
140
|
end
|
126
141
|
end
|
127
142
|
|
@@ -166,9 +181,9 @@ module JSONAPI
|
|
166
181
|
|
167
182
|
def errors
|
168
183
|
[JSONAPI::Error.new(code: JSONAPI::INVALID_FIELD,
|
169
|
-
|
170
|
-
|
171
|
-
|
184
|
+
status: :bad_request,
|
185
|
+
title: 'Invalid field',
|
186
|
+
detail: "#{field} is not a valid field for #{type}.")]
|
172
187
|
end
|
173
188
|
end
|
174
189
|
|
@@ -209,13 +224,12 @@ module JSONAPI
|
|
209
224
|
end
|
210
225
|
|
211
226
|
def errors
|
212
|
-
|
213
|
-
|
227
|
+
params.collect do |param|
|
228
|
+
JSONAPI::Error.new(code: JSONAPI::PARAM_NOT_ALLOWED,
|
214
229
|
status: :bad_request,
|
215
230
|
title: 'Param not allowed',
|
216
231
|
detail: "#{param} is not allowed.")
|
217
|
-
|
218
|
-
|
232
|
+
end
|
219
233
|
end
|
220
234
|
end
|
221
235
|
|
@@ -227,18 +241,18 @@ module JSONAPI
|
|
227
241
|
|
228
242
|
def errors
|
229
243
|
[JSONAPI::Error.new(code: JSONAPI::PARAM_MISSING,
|
230
|
-
|
231
|
-
|
232
|
-
|
244
|
+
status: :bad_request,
|
245
|
+
title: 'Missing Parameter',
|
246
|
+
detail: "The required parameter, #{param}, is missing.")]
|
233
247
|
end
|
234
248
|
end
|
235
249
|
|
236
250
|
class CountMismatch < Error
|
237
251
|
def errors
|
238
252
|
[JSONAPI::Error.new(code: JSONAPI::COUNT_MISMATCH,
|
239
|
-
|
240
|
-
|
241
|
-
|
253
|
+
status: :bad_request,
|
254
|
+
title: 'Count to key mismatch',
|
255
|
+
detail: 'The resource collection does not contain the same number of objects as the number of keys.')]
|
242
256
|
end
|
243
257
|
end
|
244
258
|
|
@@ -250,18 +264,18 @@ module JSONAPI
|
|
250
264
|
|
251
265
|
def errors
|
252
266
|
[JSONAPI::Error.new(code: JSONAPI::KEY_NOT_INCLUDED_IN_URL,
|
253
|
-
|
254
|
-
|
255
|
-
|
267
|
+
status: :bad_request,
|
268
|
+
title: 'Key is not included in URL',
|
269
|
+
detail: "The URL does not support the key #{key}")]
|
256
270
|
end
|
257
271
|
end
|
258
272
|
|
259
273
|
class MissingKey < Error
|
260
274
|
def errors
|
261
275
|
[JSONAPI::Error.new(code: JSONAPI::KEY_ORDER_MISMATCH,
|
262
|
-
|
263
|
-
|
264
|
-
|
276
|
+
status: :bad_request,
|
277
|
+
title: 'A key is required',
|
278
|
+
detail: 'The resource object does not contain a key.')]
|
265
279
|
end
|
266
280
|
end
|
267
281
|
|
@@ -273,9 +287,9 @@ module JSONAPI
|
|
273
287
|
|
274
288
|
def errors
|
275
289
|
[JSONAPI::Error.new(code: JSONAPI::LOCKED,
|
276
|
-
|
277
|
-
|
278
|
-
|
290
|
+
status: :locked,
|
291
|
+
title: 'Locked resource',
|
292
|
+
detail: "#{message}")]
|
279
293
|
end
|
280
294
|
end
|
281
295
|
|
@@ -323,7 +337,6 @@ module JSONAPI
|
|
323
337
|
end
|
324
338
|
end
|
325
339
|
|
326
|
-
|
327
340
|
class PageParametersNotAllowed < Error
|
328
341
|
attr_accessor :params
|
329
342
|
def initialize(params)
|
@@ -331,13 +344,12 @@ module JSONAPI
|
|
331
344
|
end
|
332
345
|
|
333
346
|
def errors
|
334
|
-
params.collect
|
347
|
+
params.collect do |param|
|
335
348
|
JSONAPI::Error.new(code: JSONAPI::PARAM_NOT_ALLOWED,
|
336
349
|
status: :bad_request,
|
337
350
|
title: 'Page parameter not allowed',
|
338
351
|
detail: "#{param} is not an allowed page parameter.")
|
339
|
-
|
340
|
-
|
352
|
+
end
|
341
353
|
end
|
342
354
|
end
|
343
355
|
|