Skip to content

Commit 7bfa7eb

Browse files
authored
Merge pull request #1056 from cerebris/0_9_1_cherrypick
Cherrypicks some features
2 parents 13f6423 + fb3570c commit 7bfa7eb

10 files changed

Lines changed: 138 additions & 86 deletions

File tree

lib/jsonapi/exceptions.rb

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -364,24 +364,21 @@ def errors
364364
end
365365
end
366366

367-
class ParametersNotAllowed < Error
368-
attr_accessor :params
367+
class ParameterNotAllowed < Error
368+
attr_accessor :param
369369

370-
def initialize(params, error_object_overrides = {})
371-
@params = params
370+
def initialize(param, error_object_overrides = {})
371+
@param = param
372372
super(error_object_overrides)
373373
end
374374

375375
def errors
376-
params.collect do |param|
377-
create_error_object(code: JSONAPI::PARAM_NOT_ALLOWED,
378-
status: :bad_request,
379-
title: I18n.translate('jsonapi-resources.exceptions.parameters_not_allowed.title',
380-
default: 'Param not allowed'),
381-
detail: I18n.translate('jsonapi-resources.exceptions.parameters_not_allowed.detail',
382-
default: "#{param} is not allowed.", param: param))
383-
384-
end
376+
[create_error_object(code: JSONAPI::PARAM_NOT_ALLOWED,
377+
status: :bad_request,
378+
title: I18n.translate('jsonapi-resources.exceptions.parameter_not_allowed.title',
379+
default: 'Param not allowed'),
380+
detail: I18n.translate('jsonapi-resources.exceptions.parameter_not_allowed.detail',
381+
default: "#{param} is not allowed.", param: param))]
385382
end
386383
end
387384

lib/jsonapi/include_directives.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def get_related(current_path)
5252
current_relationship = current_resource_klass._relationships[fragment]
5353
current_resource_klass = current_relationship.try(:resource_klass)
5454
else
55-
warn "[RELATIONSHIP NOT FOUND] Relationship could not be found for #{current_path}."
55+
raise JSONAPI::Exceptions::InvalidInclude.new(current_resource_klass, current_path)
5656
end
5757

5858
include_in_join = @force_eager_load || !current_relationship || current_relationship.eager_load_on_include

lib/jsonapi/request_parser.rb

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -170,25 +170,23 @@ def parse_fields(fields)
170170
end
171171
type_resource = Resource.resource_for(@resource_klass.module_path + underscored_type.to_s)
172172
rescue NameError
173-
@errors.concat(JSONAPI::Exceptions::InvalidResource.new(type).errors)
174-
rescue JSONAPI::Exceptions::InvalidResource => e
175-
@errors.concat(e.errors)
173+
fail JSONAPI::Exceptions::InvalidResource.new(type)
176174
end
177175

178176
if type_resource.nil?
179-
@errors.concat(JSONAPI::Exceptions::InvalidResource.new(type).errors)
177+
fail JSONAPI::Exceptions::InvalidResource.new(type)
180178
else
181179
unless values.nil?
182180
valid_fields = type_resource.fields.collect { |key| format_key(key) }
183181
values.each do |field|
184182
if valid_fields.include?(field)
185183
extracted_fields[type].push unformat_key(field)
186184
else
187-
@errors.concat(JSONAPI::Exceptions::InvalidField.new(type, field).errors)
185+
fail JSONAPI::Exceptions::InvalidField.new(type, field)
188186
end
189187
end
190188
else
191-
@errors.concat(JSONAPI::Exceptions::InvalidField.new(type, 'nil').errors)
189+
fail JSONAPI::Exceptions::InvalidField.new(type, 'nil')
192190
end
193191
end
194192
end
@@ -205,16 +203,15 @@ def check_include(resource_klass, include_parts)
205203
check_include(Resource.resource_for(resource_klass.module_path + relationship.class_name.to_s.underscore), include_parts.last.partition('.'))
206204
end
207205
else
208-
@errors.concat(JSONAPI::Exceptions::InvalidInclude.new(format_key(resource_klass._type),
209-
include_parts.first).errors)
206+
fail JSONAPI::Exceptions::InvalidInclude.new(format_key(resource_klass._type), include_parts.first)
210207
end
211208
end
212209

213210
def parse_include_directives(raw_include)
214211
return unless raw_include
215212

216213
unless JSONAPI.configuration.allow_include
217-
fail JSONAPI::Exceptions::ParametersNotAllowed.new([:include])
214+
fail JSONAPI::Exceptions::ParameterNotAllowed.new(:include)
218215
end
219216

220217
included_resources = []
@@ -226,19 +223,24 @@ def parse_include_directives(raw_include)
226223

227224
return if included_resources.empty?
228225

229-
result = included_resources.compact.map do |included_resource|
230-
check_include(@resource_klass, included_resource.partition('.'))
231-
unformat_key(included_resource).to_s
232-
end
226+
begin
227+
result = included_resources.compact.map do |included_resource|
228+
check_include(@resource_klass, included_resource.partition('.'))
229+
unformat_key(included_resource).to_s
230+
end
233231

234-
@include_directives = JSONAPI::IncludeDirectives.new(@resource_klass, result)
232+
@include_directives = JSONAPI::IncludeDirectives.new(@resource_klass, result)
233+
rescue JSONAPI::Exceptions::InvalidInclude => e
234+
@errors.concat(e.errors)
235+
@include_directives = {}
236+
end
235237
end
236238

237239
def parse_filters(filters)
238240
return unless filters
239241

240242
unless JSONAPI.configuration.allow_filter
241-
fail JSONAPI::Exceptions::ParametersNotAllowed.new([:filter])
243+
fail JSONAPI::Exceptions::ParameterNotAllowed.new(:filter)
242244
end
243245

244246
unless filters.class.method_defined?(:each)
@@ -251,7 +253,7 @@ def parse_filters(filters)
251253
if @resource_klass._allowed_filter?(filter)
252254
@filters[filter] = value
253255
else
254-
@errors.concat(JSONAPI::Exceptions::FilterNotAllowed.new(filter).errors)
256+
fail JSONAPI::Exceptions::FilterNotAllowed.new(filter)
255257
end
256258
end
257259
end
@@ -267,7 +269,7 @@ def parse_sort_criteria(sort_criteria)
267269
return unless sort_criteria.present?
268270

269271
unless JSONAPI.configuration.allow_sort
270-
fail JSONAPI::Exceptions::ParametersNotAllowed.new([:sort])
272+
fail JSONAPI::Exceptions::ParameterNotAllowed.new(:sort)
271273
end
272274

273275
sorts = []
@@ -296,9 +298,8 @@ def check_sort_criteria(resource_klass, sort_criteria)
296298
sort_field = sort_criteria[:field]
297299
sortable_fields = resource_klass.sortable_fields(context)
298300

299-
unless sortable_fields.include? sort_field.to_sym
300-
@errors.concat(JSONAPI::Exceptions::InvalidSortCriteria
301-
.new(format_key(resource_klass._type), sort_field).errors)
301+
unless sortable_fields.include?sort_field.to_sym
302+
fail JSONAPI::Exceptions::InvalidSortCriteria.new(format_key(resource_klass._type), sort_field)
302303
end
303304
end
304305

@@ -473,7 +474,7 @@ def parse_to_one_relationship(link_value, relationship)
473474

474475
unless links_object[:id].nil?
475476
resource = self.resource_klass || Resource
476-
relationship_resource = resource.resource_for(unformat_key(links_object[:type]).to_s)
477+
relationship_resource = resource.resource_for(unformat_key(relationship.options[:class_name] || links_object[:type]).to_s)
477478
relationship_id = relationship_resource.verify_key(links_object[:id], @context)
478479
if relationship.polymorphic?
479480
{ id: relationship_id, type: unformat_key(links_object[:type].to_s) }
@@ -528,45 +529,52 @@ def verify_permitted_params(params, allowed_fields)
528529
when 'relationships'
529530
value.keys.each do |links_key|
530531
unless formatted_allowed_fields.include?(links_key.to_sym)
531-
params_not_allowed.push(links_key)
532-
unless JSONAPI.configuration.raise_if_parameters_not_allowed
532+
if JSONAPI.configuration.raise_if_parameters_not_allowed
533+
fail JSONAPI::Exceptions::ParameterNotAllowed.new(links_key)
534+
else
535+
params_not_allowed.push(links_key)
533536
value.delete links_key
534537
end
535538
end
536539
end
537540
when 'attributes'
538541
value.each do |attr_key, attr_value|
539542
unless formatted_allowed_fields.include?(attr_key.to_sym)
540-
params_not_allowed.push(attr_key)
541-
unless JSONAPI.configuration.raise_if_parameters_not_allowed
543+
if JSONAPI.configuration.raise_if_parameters_not_allowed
544+
fail JSONAPI::Exceptions::ParameterNotAllowed.new(attr_key)
545+
else
546+
params_not_allowed.push(attr_key)
542547
value.delete attr_key
543548
end
544549
end
545550
end
546551
when 'type'
547552
when 'id'
548553
unless formatted_allowed_fields.include?(:id)
549-
params_not_allowed.push(:id)
550-
unless JSONAPI.configuration.raise_if_parameters_not_allowed
554+
if JSONAPI.configuration.raise_if_parameters_not_allowed
555+
fail JSONAPI::Exceptions::ParameterNotAllowed.new(:id)
556+
else
557+
params_not_allowed.push(:id)
551558
params.delete :id
552559
end
553560
end
554561
else
555-
params_not_allowed.push(key)
562+
if JSONAPI.configuration.raise_if_parameters_not_allowed
563+
fail JSONAPI::Exceptions::ParameterNotAllowed.new(key)
564+
else
565+
params_not_allowed.push(key)
566+
params.delete key
567+
end
556568
end
557569
end
558570

559571
if params_not_allowed.length > 0
560-
if JSONAPI.configuration.raise_if_parameters_not_allowed
561-
fail JSONAPI::Exceptions::ParametersNotAllowed.new(params_not_allowed)
562-
else
563-
params_not_allowed_warnings = params_not_allowed.map do |key|
564-
JSONAPI::Warning.new(code: JSONAPI::PARAM_NOT_ALLOWED,
565-
title: 'Param not allowed',
566-
detail: "#{key} is not allowed.")
567-
end
568-
self.warnings.concat(params_not_allowed_warnings)
572+
params_not_allowed_warnings = params_not_allowed.map do |param|
573+
JSONAPI::Warning.new(code: JSONAPI::PARAM_NOT_ALLOWED,
574+
title: 'Param not allowed',
575+
detail: "#{param} is not allowed.")
569576
end
577+
self.warnings.concat(params_not_allowed_warnings)
570578
end
571579
end
572580

lib/jsonapi/resource.rb

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ def rebuild_relationships(relationships)
453453

454454
def resource_for(type)
455455
type = type.underscore
456-
type_with_module = type.include?('/') ? type : module_path + type
456+
type_with_module = type.start_with?(module_path) ? type : module_path + type
457457

458458
resource_name = _resource_name_from_type(type_with_module)
459459
resource = resource_name.safe_constantize if resource_name
@@ -507,10 +507,12 @@ def attributes(*attrs)
507507
end
508508
end
509509

510-
def attribute(attr, options = {})
510+
def attribute(attribute_name, options = {})
511+
attr = attribute_name.to_sym
512+
511513
check_reserved_attribute_name(attr)
512514

513-
if (attr.to_sym == :id) && (options[:format].nil?)
515+
if (attr == :id) && (options[:format].nil?)
514516
ActiveSupport::Deprecation.warn('Id without format is no longer supported. Please remove ids from attributes, or specify a format.')
515517
end
516518

@@ -1041,7 +1043,8 @@ def _add_relationship(klass, *attrs)
10411043
options = attrs.extract_options!
10421044
options[:parent_resource] = self
10431045

1044-
attrs.each do |relationship_name|
1046+
attrs.each do |name|
1047+
relationship_name = name.to_sym
10451048
check_reserved_relationship_name(relationship_name)
10461049
check_duplicate_relationship_name(relationship_name)
10471050

locales/en.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ en:
5555
invalid_sort_criteria:
5656
title: 'Invalid sort criteria'
5757
detail: "%{sort_criteria} is not a valid sort criteria for %{resource}"
58-
parameters_not_allowed:
58+
parameter_not_allowed:
5959
title: 'Param not allowed'
6060
detail: "%{param} is not allowed."
6161
parameter_missing:

test/controllers/controller_test.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1803,7 +1803,6 @@ def test_update_unpermitted_attributes
18031803
}
18041804

18051805
assert_response :bad_request
1806-
assert_match /author is not allowed./, response.body
18071806
assert_match /subject is not allowed./, response.body
18081807
end
18091808

@@ -2058,7 +2057,6 @@ def test_expense_entries_show_bad_include_missing_relationship
20582057
assert_cacheable_get :show, params: {id: 1, include: 'isoCurrencies,employees'}
20592058
assert_response :bad_request
20602059
assert_match /isoCurrencies is not a valid relationship of expenseEntries/, json_response['errors'][0]['detail']
2061-
assert_match /employees is not a valid relationship of expenseEntries/, json_response['errors'][1]['detail']
20622060
end
20632061

20642062
def test_expense_entries_show_bad_include_missing_sub_relationship
@@ -2067,6 +2065,18 @@ def test_expense_entries_show_bad_include_missing_sub_relationship
20672065
assert_match /post is not a valid relationship of people/, json_response['errors'][0]['detail']
20682066
end
20692067

2068+
def test_invalid_include
2069+
assert_cacheable_get :index, params: {include: 'invalid../../../../'}
2070+
assert_response :bad_request
2071+
assert_match /invalid is not a valid relationship of expenseEntries/, json_response['errors'][0]['detail']
2072+
end
2073+
2074+
def test_invalid_include_long_garbage_string
2075+
assert_cacheable_get :index, params: {include: 'invalid.foo.bar.dfsdfs,dfsdfs.sdfwe.ewrerw.erwrewrew'}
2076+
assert_response :bad_request
2077+
assert_match /invalid is not a valid relationship of expenseEntries/, json_response['errors'][0]['detail']
2078+
end
2079+
20702080
def test_expense_entries_show_fields
20712081
assert_cacheable_get :show, params: {id: 1, include: 'isoCurrency,employee', 'fields' => {'expenseEntries' => 'transactionDate'}}
20722082
assert_response :success

test/fixtures/active_record.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,12 +1462,12 @@ class PersonResource < PersonResource; end
14621462
class PostResource < PostResource; end
14631463

14641464
class BookResource < JSONAPI::Resource
1465-
attribute :title
1465+
attribute "title"
14661466
attributes :isbn, :banned
14671467

1468-
has_many :authors
1468+
has_many "authors"
14691469

1470-
has_many :book_comments, relation_name: -> (options = {}) {
1470+
has_many "book_comments", relation_name: -> (options = {}) {
14711471
context = options[:context]
14721472
current_user = context ? context[:current_user] : nil
14731473

@@ -1478,7 +1478,7 @@ class BookResource < JSONAPI::Resource
14781478
end
14791479
}, reflect: true
14801480

1481-
has_many :aliased_comments, class_name: 'BookComments', relation_name: :approved_book_comments
1481+
has_many "aliased_comments", class_name: 'BookComments', relation_name: :approved_book_comments
14821482

14831483
filters :book_comments
14841484
filter :banned, apply: :apply_filter_banned

0 commit comments

Comments
 (0)