Skip to content

Commit b36a49a

Browse files
Merge branch 'feature/bad-request-exception'
2 parents fa354bd + c0d3e73 commit b36a49a

File tree

7 files changed

+47
-19
lines changed

7 files changed

+47
-19
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## v0.3.0 - 2019-09-16
8+
### Changed
9+
- Raise `Jsonapi::InvalidAttributeError` when attribute is invalid.
10+
711
## v0.2.0 - 2019-08-01
812
### Fixed
913
- Adding condition when spliting filter_params to prevent issue.

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,25 @@ Or use negative sort `/contacts?sort=-firstname` to sort by firstname in `desc`
9595

9696
You can even combine multiple sort `/contacts?sort=lastname,-firstname`
9797

98+
99+
### Rescuing a Bad Request in Rails
100+
101+
Jsonapi::scope raises a `Jsonapi::InvalidAttributeError` you can [rescue_from](https://guides.rubyonrails.org/action_controller_overview.html#rescue-from) in your `ApplicationController`.
102+
103+
If you want to follow the specification, you **must** respond with a `400 Bad Request`.
104+
105+
```ruby
106+
class ApplicationController < ActionController::Base
107+
rescue_from Jsonapi::InvalidAttributeError, with: :json_api_bad_request
108+
109+
private
110+
111+
def json_api_bad_request(exception)
112+
render json: { error: exception.message }, status: :bad_request
113+
end
114+
end
115+
```
116+
98117
## Contributing
99118
Do not hesitate to contribute to the project by adapting or adding features ! Bug reports or pull requests are welcome.
100119

lib/jsonapi/exceptions.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# frozen_string_literal: true
2+
3+
module Jsonapi
4+
class Error < StandardError; end
5+
6+
class InvalidAttributeError < Error
7+
def initialize(message)
8+
super(message)
9+
end
10+
end
11+
end

lib/jsonapi/scopes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# frozen_string_literal: true
22

3+
require 'jsonapi/exceptions'
34
require 'jsonapi/scopes/filters'
45
require 'jsonapi/scopes/sorts'

lib/jsonapi/scopes/filters.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ def apply_filter(params)
2121
filtering_params.each do |key, value|
2222
value = value.to_s.split(',').reject(&:blank?) if value.include?(',')
2323

24-
records = records.public_send(key, value) if @filters.include?(key.to_sym)
24+
raise InvalidAttributeError, "#{key} is not valid as filter attribute." unless @filters.include?(key.to_sym)
25+
26+
records = records.public_send(key, value)
2527
end
2628

2729
records

lib/jsonapi/scopes/sorts.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,12 @@ def apply_sort(params = {}, options = { allowed: [], default: {} })
2828
default_order = default_order.transform_keys(&:to_sym)
2929

3030
ordered_fields = convert_to_ordered_hash(fields)
31-
filtered_fields = ordered_fields.select { |key, _| allowed_fields.include?(key) }
3231

33-
order = filtered_fields.presence || default_order
32+
ordered_fields.each do |field, _|
33+
raise InvalidAttributeError, "#{field} is not valid as sort attribute." unless allowed_fields.include?(field)
34+
end
35+
36+
order = ordered_fields.presence || default_order
3437

3538
self.order(order)
3639
end

test/dummy/spec/models/contact_spec.rb

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@
6363
}
6464
end
6565

66-
it 'does not filter by last_name' do
67-
expect(Contact.apply_filter(invalid_params)).to include(anakin, harry)
66+
it 'raises an exception' do
67+
expect { Contact.apply_filter(invalid_params) }.to raise_exception(Jsonapi::InvalidAttributeError, 'last_name is not valid as filter attribute.')
6868
end
6969
end
7070
end
@@ -91,28 +91,16 @@
9191
expect(Contact.apply_sort(valid_params).pluck(:id)).to eq(expected_ids)
9292
expect(Contact.apply_sort(valid_params).pluck(:id)).to_not eq(not_expected_ids)
9393
end
94-
95-
it 'sorts by allowed fields only' do
96-
valid_params = { sort: 'last_name' } # must be ignored
97-
expected_ids = Contact.order(last_name: :desc).pluck(:id) # default sort
98-
not_expected_ids = Contact.order(:last_name).pluck(:id) # params sort
99-
100-
expect(Contact.apply_sort(valid_params, allowed: [:first_name, :age]).pluck(:id)).to eq(expected_ids)
101-
expect(Contact.apply_sort(valid_params, allowed: [:first_name, :age]).pluck(:id)).to_not eq(not_expected_ids)
102-
end
10394
end
10495

10596
context 'with invalid params' do
10697
let(:anakin) { create(:contact, first_name: 'Anakin', last_name: 'Skywalker', age: 19) }
10798
let(:harry) { create(:contact, first_name: 'Harry', last_name: 'Potter', age: 13) }
10899

109-
it 'does not sort by age' do
100+
it 'raises an exception' do
110101
invalid_params = { sort: 'age' }
111-
expected_ids = Contact.pluck(:id)
112-
not_expected_ids = Contact.order(:age).pluck(:id)
113102

114-
expect(Contact.apply_sort(invalid_params).pluck(:id)).to eq(expected_ids)
115-
expect(Contact.apply_sort(invalid_params).pluck(:id)).to_not eq(not_expected_ids)
103+
expect { Contact.apply_sort(invalid_params) }.to raise_exception(Jsonapi::InvalidAttributeError, 'age is not valid as sort attribute.')
116104
end
117105
end
118106
end

0 commit comments

Comments
 (0)