Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add possibility to override status handling #324

Merged
merged 1 commit into from
Jan 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
* remove type from changes on initialize
* ensure that query builder doesn't propagate query values to resource attributes via #build method

- [#324](https://github.com/JsonApiClient/json_api_client/pull/324) - add possibility to override status handling
* add status_handlers to JsonApiClient::Resource.connection_options

## 1.8.0

- [#316](https://github.com/JsonApiClient/json_api_client/pull/316) - Allow custom error messages
Expand Down
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,44 @@ module MyApi
end
```

##### Custom status handler

You can change handling of response status using `connection_options`. For example you can override 400 status handling.
By default it raises `JsonApiClient::Errors::ClientError` but you can skip exception if you want to process errors from the server.
You need to provide a `proc` which should call `throw(:handled)` default handler for this status should be skipped.
```ruby
class ApiBadRequestHandler
def self.call(_env)
# do not raise exception
end
end

class CustomUnauthorizedError < StandardError
attr_reader :env

def initialize(env)
@env = env
super('not authorized')
end
end

MyApi::Base.connection_options[:status_handlers] = {
400 => ApiBadRequestHandler,
401 => ->(env) { raise CustomUnauthorizedError, env }
}

module MyApi
class User < Base
# will use the customized status_handlers
end
end

user = MyApi::User.create(name: 'foo')
# server responds with { errors: [ { detail: 'bad request' } ] }
user.errors.messages # { base: ['bad request'] }
# on 401 it will raise CustomUnauthorizedError instead of JsonApiClient::Errors::NotAuthorized
```

##### Specifying an HTTP Proxy

All resources have a class method ```connection_options``` used to pass options to the JsonApiClient::Connection initializer.
Expand Down
4 changes: 3 additions & 1 deletion lib/json_api_client/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ def initialize(options = {})
site = options.fetch(:site)
connection_options = options.slice(:proxy, :ssl, :request, :headers, :params)
adapter_options = Array(options.fetch(:adapter, Faraday.default_adapter))
status_middleware_options = {}
status_middleware_options[:custom_handlers] = options[:status_handlers] if options[:status_handlers].present?
@faraday = Faraday.new(site, connection_options) do |builder|
builder.request :json
builder.use Middleware::JsonRequest
builder.use Middleware::Status
builder.use Middleware::Status, status_middleware_options
builder.use Middleware::ParseJson
builder.adapter(*adapter_options)
end
Expand Down
14 changes: 13 additions & 1 deletion lib/json_api_client/middleware/status.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
module JsonApiClient
module Middleware
class Status < Faraday::Middleware
def initialize(app, options)
super(app)
@options = options
end

def call(environment)
@app.call(environment).on_complete do |env|
handle_status(env[:status], env)
Expand All @@ -15,9 +20,16 @@ def call(environment)
raise Errors::ConnectionError.new environment, e.to_s
end

protected
private

def custom_handler_for(code)
@options.fetch(:custom_handlers, {})[code]
end

def handle_status(code, env)
custom_handler = custom_handler_for(code)
return custom_handler.call(env) if custom_handler.present?

case code
when 200..399
when 401
Expand Down
24 changes: 24 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,30 @@ class Comment < TestResource
class User < TestResource
end

class ApiBadRequestHandler
def self.call(_env)
# do not raise exception
end
end

class CustomUnauthorizedError < StandardError
attr_reader :env

def initialize(env)
@env = env
super('not authorized')
end
end

class UserWithCustomStatusHandler < TestResource
self.connection_options = {
status_handlers: {
400 => ApiBadRequestHandler,
401 => ->(env) { raise CustomUnauthorizedError, env }
}
}
end

class UserPreference < TestResource
self.primary_key = :user_id
end
Expand Down
79 changes: 79 additions & 0 deletions test/unit/status_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,85 @@ def test_server_responding_with_408_status
end
end

def test_server_responding_with_400_status
stub_request(:get, "http://example.com/users/1")
.to_return(headers: {content_type: "application/vnd.api+json"}, body: {
meta: {
status: 400,
message: "Bad Request"
}
}.to_json)

assert_raises JsonApiClient::Errors::ClientError do
User.find(1)
end
end

def test_server_responding_with_401_status
stub_request(:get, "http://example.com/users/1")
.to_return(headers: {content_type: "application/vnd.api+json"}, body: {
meta: {
status: 401,
message: "Not Authorized"
}
}.to_json)

assert_raises JsonApiClient::Errors::NotAuthorized do
User.find(1)
end
end

def test_server_responding_with_400_status_in_meta_with_custom_status_handler
stub_request(:get, "http://example.com/user_with_custom_status_handlers/1")
.to_return(headers: {content_type: "application/vnd.api+json"}, body: {
meta: {
status: 400,
message: "Bad Request"
}
}.to_json)

UserWithCustomStatusHandler.find(1)
end

def test_server_responding_with_401_status_in_meta_with_custom_status_handler
stub_request(:get, "http://example.com/user_with_custom_status_handlers/1")
.to_return(headers: {content_type: "application/vnd.api+json"}, body: {
meta: {
status: 401,
message: "Not Authorized"
}
}.to_json)

assert_raises CustomUnauthorizedError do
UserWithCustomStatusHandler.find(1)
end
end

def test_server_responding_with_400_status_with_custom_status_handler
stub_request(:post, "http://example.com/user_with_custom_status_handlers")
.with(headers: { content_type: 'application/vnd.api+json', accept: 'application/vnd.api+json' }, body: {
data: {
type: 'user_with_custom_status_handlers',
attributes: {
name: 'foo'
}
}
}.to_json)
.to_return(status: 400, headers: { content_type: "application/vnd.api+json" }, body: {
errors: [
{
status: '400',
detail: 'Bad Request'
}
]
}.to_json)

user = UserWithCustomStatusHandler.create(name: 'foo')
refute user.persisted?
expected_errors = { base: ['Bad Request'] }
assert_equal expected_errors, user.errors.messages
end

def test_server_responding_with_422_status
stub_request(:get, "http://example.com/users/1")
.to_return(headers: {content_type: "application/vnd.api+json"}, body: {
Expand Down