I have spec to test for internal server error case.
context "when network error" do
it "returns internal_server_error with network error description" do
allow(Authorer::AuthorizationClient).to receive(:post).and_raise(EOFError)
if request_method == "get"
get url, params: params, headers: #header
else
#header["Content-Type"] = "application/vnd.api+json"
post url, params: params, as: :json, headers: #header
end
reload_response_body
expect(response).to have_http_status(:internal_server_error)
end
end
CONTEXT: Authorer::AuthorizationClient is our client code to access some third-party server, and sometimes we face EOFError then we need to handle it by rescuing it and raise again to client for internal server error.
something like this
# code
....
begin
Authorer::AuthorizationClient.evaluate_permission(id) # this one hits third-party
rescue EOFError
raise StandardError, "Network Error"
end
the test was success and green. but there is error log and traces
[test] [2022-08-15 11:22:52.397] [id.test] [E] {"message":null,"exception":"Network Error","extra":null,"context":null}
/app/service/authorer/authorization_client.rb:29:in `rescue in authorize_access'
/app/service/authorer/authorization_client.rb:21:in `rescue in authorize_user'
........
How to omit this error log?
thanks in advance
Related
I am using appsignal gem to track if there is an error processing in my app.
This case i do call external API using faraday.
def truck_information(req_params)
response = #conn.post('truck/info') do |req|
req.headers['Content-Type'] = 'application/json'
req.body = req_params
end
return JSON.parse(response.body) if response_successful?(response)
response_error(response)
end
def response_successful?(response)
response.status == 200
end
def response_error(response)
err = NctError, "Code: #{response.status}, response: #{response.body}"
Appsignal.set_error(err)
raise NctError, I18n.t('error_messages.ppob.server_error')
end
my truck_information is used to call external api. and if success i will parse it to json. but if error i will call response_error method parser to create custom class error (NctError) and i want to send to appsignal to show the error without breaking the application process.
But when i was tested it, it doesn't send to appsignal. How to do send error to appsignal, even if it doesn't crash a request? because i need to track the error.
Thank you
You could try Appsignal.send_error
Appsignal.send_error(err)
If above doesn't work either, then set_error and send_error may only work with Exception:
def response_error(response)
raise NctError, I18n.t('error_messages.ppob.server_error')
rescue => e
Appsignal.send_error(e) do |transaction|
transaction.params = { code: response.status, response: response.body }
end
end
Within an engine w/ an API, when querying the API an exception is thrown, but the server response is not using the response specified, and instead rendering a default debug response (in production).
I can confirm that the exception is caught by the controller:
Started GET "/api_v2/admin/submissions?system_id=123&spt_id=123" for
127.0.0.1 at 2019-03-15 10:04:37 -0400
Processing by ApiV2::Admin::SubmissionsController#show as JSON
Parameters: {"system_id"=>"123", "spt_id"=>"123"}
[3, 12] in /....../emp_api_v2/app/controllers/emp_api_v2/application_controller.rb
3:
4: before_action :doorkeeper_authorize!
5:
6: rescue_from ::ActiveRecord::RecordNotFound do |e|
7: byebug
=> 8: response(:standard_error, 500, "Hello World")
9: end
10:
11: def doorkeeper_unauthorized_render_options(error: nil)
12: { json: { message: "Not authorized" }, status: 403 }
(byebug) cont
Completed 500 Internal Server Error in 5220ms (ActiveRecord: 0.0ms)
ActiveRecord::RecordNotFound - Couldn't find Emp::System with 'id'=123:
The server is expected to respond with 500 Server Error not the debug error stacktrace.
Why are there two responses, even though the controller catches the exception and runs a response method.
NOTE: This happens in dev and prod ! Server responds with 500 first (my catch response) but then with a stacktrace and 404 (As this is the source of the error and correct exception). It breaks my tests as the past response was 500. I was not able to revert my server to the old behavior by: reinstalling ruby, reinstalling rails + all gems + rolling back all changes throughout the repo. This behavior seems to be externally set by a ENV variable or something else.
I'd be grateful for any insight.
Edit: The (api) app controller looks like this:
module ApiV2
class ApplicationController < ActionController::API
before_action :doorkeeper_authorize!
rescue_from ::ActiveRecord::RecordNotFound do |e|
response(:standard_error, 500, "Hello World")
end
def doorkeeper_unauthorized_render_options(error: nil)
{ json: { message: "Not authorized" }, status: 403 }
end
end
end
Edit 2: I was able to get the correct response by wrapping the call in a rescue block. That code will result in a lot of begin/rescue blocks though as each of them has a specific error message.
begin
system = Emp::System.find(sys_id)
rescue
render json: { status: 500, content: "Specific Error msg" } and return
end
Originally i had a method as follows:
def handle_exception(message, &block)
begin
block.call
rescue Exception => e
render json: { message: message }, status: 500 and return
end
end
This will result in double render error as it's not returning from the top-level method but from the block.
I am using RestClient to make a network call in the ruby class. I am getting a SocketError whenever I am not connected to the internet. I have added a rescue block to catch the exception still I am not able to do so.
the error message is:
SocketError (Failed to open TCP connection to api.something.com:443 (getaddrinfo: Name or service not known))
module MyProject
class Client
def get_object(url, params={})
response = RestClient.get(url, {params: params})
rescue SocketError => e
puts "In Socket errror"
rescue => e
puts (e.class.inspect)
end
end
end
The broad rescue gets called and print SocketError, but why the previous rescue SocketError is not triggered!
Do you see something that I am missing?
There are a couple of exceptions you'll need to rescue in case you want to fail gracefully.
require 'rest-client'
def get_object(url, params={})
response = RestClient.get(url, {params: params})
rescue RestClient::ResourceNotFound => e
p e.class
rescue SocketError => e
p e.class
rescue Errno::ECONNREFUSED => e
p e.class
end
get_object('https://www.google.com/missing')
get_object('https://dasdkajsdlaadsasd.com')
get_object('dasdkajsdlaadsasd.com')
get_object('invalid')
get_object('127.0.0.1')
Because you can have problems regarding the uri being a 404 destination, or be a domain that doesn't existing, an IP, and so on.
As you can see above, you can have different scenarios that you may encounter when dealing with connecting to a remote uri. The code above rescue from the most common scenarios.
The first one the URL is missing, which is handled by RestClient itself.
The the following three below are invalid domains, which will fail with SocketError (basically a DNS error in this case).
Finally in the last call we try to connect to an IP that has no server running on it - therefore it throws an ERRNO::ECONNREFUSED
I am using a Rails gem to send requests to an api using RestClient. I need to rescue a 401 error code. I saw the following on RestClient documentation:
> RestClient.get('http://my-rest-service.com/resource'){ |response,
> request, result, &block| case response.code when 200
> p "It worked !"
> response when 423
> raise SomeCustomExceptionIfYouWant else
> response.return!(request, result, &block) end }
I have attempted to implement a similar case statement:
case response.code
when 200
JSON.parse(response.body)
when 401
raise AuthenicationError, "Unauthorized"
else
raise RestClient::ExceptionWithResponse
end
It captures the 200 case fine but ignores the 401 case and goes straight to the else. Any suggestions on raising an exception for 401 on a response that's coming back through RestClient?
I can't tell you and why I'm sure the rest-client repo can tell you :) ... but using RestClient::Request.new then executing the api call with a block works for me.
I think it probably has to do with the fact that the RestClient has built in exceptions.
request = RestClient::Request.new(
method: :get,
url: 'https://my-rest-service.com/resource.json')
response = request.execute {|response| response}
case response.code
when 200
puts "Good"
when 401
puts "Bad"
raise Exception
end
It captures the 200 case fine but ignores the 401 case and goes straight to the else.
I rather suspect it does not go to the else, actually; you'd still get a RestClient::ExceptionWithResponse raised even if you took out the else clause completely, because that's what RestClient.get does when it gets an error response such as in the 400 or 500 range. From the README:
for result codes between 200 and 207, a RestClient::Response will be returned
for result codes 301, 302 or 307, the redirection will be followed if the request is a GET or a HEAD
for result code 303, the redirection will be followed and the request transformed into a GET
for other cases, a RestClient::ExceptionWithResponse holding the Response will be raised; a specific exception class will be thrown for known error codes
call .response on the exception to get the server's response
You are using wrong HTTP code. Unauthorized is actually 401, not 410.
If you catch the exception from Request.execute in a rescue block, note that you can also get the response body form the exception, example:
def request(method, url, params = {})
resp = RestClient::Request.execute(
method: method,
url: url,
timeout: 30,
accept: :json,
payload: params.to_json,
headers: {
content_type: :json,
}
)
JSON.parse(resp.body)
rescue => e
{ error: e.message, body: JSON.parse(e.response.body) } # <-------------
end
I have an Rspec test that makes a POST request and sends a header with it because authentication is required:
it 'creates a client for an organization if none exists' do
VCR.use_cassette('create_client') do
post "/organizations/#{#organization.id}/clients", nil, { 'TOKEN' => #token }
expect(last_response.status).to be(201)
expect(json_response).to be_a(Hash)
expect(json_response["organization_id"]).to eq(#organization.id)
expect(json_response.keys).to include('auth_token')
end
expect(#organization.client).to_not be_nil
end
This passes without problems when I run the tests on my local machine, but will fail on the CI server (in this case Codeship):
Failure/Error: post "/organizations/#{#organization.id}/clients", nil, { 'TOKEN' => #token }
MyModule::MyClass::Errors::InvalidOptionError:
bad URI(is not URI?):
When I remove the header part from the post request, the test will obviously fail because the token header is required, but the post request will go through without errors.
Any ideas why this is happening? I'd appreciate any input.
Turns out it was a problem with Elasticsearch on Codeship.