I have a bit of code that checks the response code of a list of URL's and presents them back - I am having trouble with a few of URL's that are hanging which causes the application not load at all. How can I make the request to give up after 30 seconds and check the next URL marking the skipped URL as failure.
below is my current code;
(Model/status.rb)
require "net/http"
require "uri"
class Status
def initialize(url)
#url = url
end
def get_status
response.code
end
def active?
["200","203","302"].include?(get_status) ? true : false
end
private
def lookup
URI.parse(#url)
end
def http
Net::HTTP.new(lookup.host, lookup.port)
end
def request
Net::HTTP::Get.new(lookup.request_uri)
end
def response
http.request(request)
end
end
(controllers/welcome_controller.rb)
class WelcomeController < ApplicationController
def index
#syndication = [
["http://v1.syndication.u01.example.uk/organisations?apikey=bbccdd", "U.INT 01"],
["http://v1.syndication.u02.example.uk/organisations?apikey=bbccdd", "U.INT 02"],
].collect { |url| logger.info("Boom #{url[0]}"); ["#{url[1]} (#{url[0]})", Status.new(url[0]).active?] }
end
end
Got the answer..
adding the following to my "def get_status"
def get_status
begin
response.code
rescue Exception => e
Rails.logger.info("Error #{e}")
end
end
This logged the error and the went to the next URL
Related
I am trying to pass the test cases for pages 404 & 500. But I am having a lot of problems
1) Firstly I have a page 500.html.erb inside app/views/errors/ which is not getting called.
2) If I run the below test my system freeze and I need to restart my system
3) If I comment this line expect{get "/errors/foo"}.to raise_exception(ActionController::RoutingError). So in my controller Action name page 500 gets passed as params but still, my system get freeze
Can anyone help me to fix this issue
errors_spec.rb
require "spec_helper"
describe "Errors" do
before do
allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new("production"))
end
it "renders the 500 page" do
get "/errors/500"
expect(response.status).to eq(500)
end
it "renders the 404 page" do
get "/errors/404"
expect(response.status).to eq(404)
end
it "raises an exception if the page doesn't exist" do
expect {get "/errors/foo"}.to raise_exception(ActionController::RoutingError)
end
end
errors_controller.rb
class ErrorsController < ApplicationController
skip_before_filter :authenticate_user!
EXTERNAL_ERRORS = ['sso']
VALID_ERRORS = ['404', '403', '500', 'maintenance'] + EXTERNAL_ERRORS
def show
status = external_error? ? 500 : 200
render page , status: status
end
def blocked
end
private
def page
if VALID_ERRORS.include?(params[:id])
params[:id]
else
raise(ActionController::RoutingError.new("/errors/#{params[:id]} not found"))
end
end
def external_error?
EXTERNAL_ERRORS.include?(params[:id])
end
end
In your code, you are setting status 200 when /errors/500 gets called.
def show
# external_error? returns false.
status = external_error? ? 500 : 200
render page , status: status # Status is 200.
end
Use debuggers like pry or byebug to check status. There is not any problem in your test case. Try this.
class ErrorsController < ApplicationController
skip_before_filter :authenticate_user!
EXTERNAL_ERRORS = ['sso']
VALID_ERRORS = ['404', '403', '500', 'maintenance'] + EXTERNAL_ERRORS
def show
status = error_500? ? 500 : 200
render page , status: status
end
def blocked
end
private
def page
if VALID_ERRORS.include?(params[:id])
params[:id]
else
raise(ActionController::RoutingError.new("/errors/#{params[:id]} not found"))
end
end
def external_error?
EXTERNAL_ERRORS.include?(params[:id])
end
def error_500?
['500'].include?(params[:id]) || external_error?
end
end
I 'm trying to build a Rails API client. There is an api where I can receive my data as json, which works great so far.
Now I am trying to do some timeout handling but I don't know how. I mean literally. How should I even use timeout handling?
I saw something in a tutorial which I translated for my used gem "net/http" but I cannot imagine that this has even any effect.
Here is my controller code:
require 'net/http'
class OverviewController < ApplicationController
def api_key
ENV["API_KEY"]
end
def handle_timeouts
begin
yield
rescue Net::OpenTimeout, Net::ReadTimeout
{}
end
end
def index
handle_timeouts do
url = "https://example.com/api/#{ api_key }"
uri = URI(url)
response = Net::HTTP.get(uri)
#url_debug = url
#my_hash = response
end
end
end
I have a class which is responsible for dealing with some response from payments gateway.
Let's say:
class PaymentReceiver
def initialize(gateway_response)
#gateway_response = gateway_response
end
def handle_response
if #gateway_response['NC_STATUS'] != '0'
if order
order.fail_payment
else
raise 'LackOfProperOrder'
# Log lack of proper order
end
end
end
private
def order
#order ||= Order.where(id: #gateway_response['orderID']).unpaid.first
end
end
In payload from payment I've NC_STATUS
which is responsible for information if payment succeed and orderID which refers to Order ActiveRecord class byid`.
I would like to test behavior(in rspec):
If PaymentReceiver receives response where NC_STATUS != 0 sends fail_payment to specific Order object referred by orderID.
How you would approach to testing this ? I assume that also design could be bad ...
You have to make refactorization to remove SRP and DIR principles violations.
Something below I'd say:
class PaymentReceiver
def initialize(response)
#response = response
end
def handle_response
if #response.success?
#response.order.pay
else
#response.order.fail_payment
end
end
end
# it wraps output paramteres only !
class PaymentResponse
def initialize(response)
#response = response
end
def order
# maybe we can check if order exists
#order ||= Order.find(#response['orderID'].to_i)
end
def success?
#response['NCSTATUS'] == '0'
end
end
p = PaymentReceiver.new(PaymentResponse({'NCSTATUS' => '0' }))
p.handle_response
Then testing everything is easy.
I keep getting a 422 error when testing stripe's webhook for customer.subscription.deleted
I placed this in my config routes
post 'stripewebhooks/receive'
here is my controller
class StripewebhooksController < ApplicationController
Stripe::api_key = ENV['STRIPE_SECRET_KEY']
require 'json'
def receive
data_json = JSON.parse request.body.read
p data_json['data']['object']['customer']
if data_json[:type] == "customer.subscription.deleted"
cancel_subscription(data_event)
end
end
def cancel_subscription(data_event)
#subscription = Subscription.find_by_stripe_customer_token(data['data']['object']['customer'])
#subscription.update_attribute(:subscription_status, "inactive")
end
end
I am unclear on what is suppose to go in the parenthesis after
def cancel_subscription
I am not sure that I am suppose to put data_event or what this means.
When you get a post data from stripe, you need to return a 200 status code from your application.
try this
def receive
data_json = JSON.parse request.body.read
p data_json['data']['object']['customer']
if data_json[:type] == "customer.subscription.deleted"
# Why did you send data_event? send the parsed data_json as parameter
cancel_subscription(data_json)
end
# Return a 200 status code
render :text => '{}', :status => :ok
end
Rails 4 adds an exception ActionDispatch::ParamsParser::ParseError exception but since its in the middleware stack it appears it can't be rescued in the normal controller environment. In a json API application I want respond with a standard error format.
This gist shows a strategy for inserting middleware to intercept and respond. Following this pattern I have:
application.rb:
module Traphos
class Application < Rails::Application
....
config.middleware.insert_before ActionDispatch::ParamsParser, "JSONParseError"
end
end
And the middleware is:
class JSONParseError
def initialize(app)
#app = app
end
def call(env)
begin
#app.call(env)
rescue ActionDispatch::ParamsParser::ParseError => e
[422, {}, ['Parse Error']]
end
end
end
If I run my test without the middleware I get (spec):
Failures:
1) Photo update attributes with non-parseable json
Failure/Error: patch update_url, {:description => description}, "CONTENT_TYPE" => content_type, "HTTP_ACCEPT" => accepts, "HTTP_AUTHORIZATION" => #auth
ActionDispatch::ParamsParser::ParseError:
399: unexpected token at 'description=Test+New+Description]'
Which is exactly what I would expect (ParseError that I can't rescue_from).
Now with the only change to add in the middleware above:
2) Photo update attributes with non-parseable json
Failure/Error: response.status.should eql(422)
expected: 422
got: 200
And the log shows that the standard controller action is being executed and returning a normal response (although since it didn't receive any parameters it didn't update anything).
My questions:
How can rescue from ParseError and return a custom response. Feels like I'm on the right track but not quite there.
I can't work out why, when the exception is raised and rescued, that the controller action still proceeds.
Help much appreciated, --Kip
Turns out that further up the middleware stack, ActionDispatch::ShowExceptions can be configured with an exceptions app.
module Traphos
class Application < Rails::Application
# For the exceptions app
require "#{config.root}/lib/exceptions/public_exceptions"
config.exceptions_app = Traphos::PublicExceptions.new(Rails.public_path)
end
end
Based heavily on the Rails provided one I am now using:
module Traphos
class PublicExceptions
attr_accessor :public_path
def initialize(public_path)
#public_path = public_path
end
def call(env)
exception = env["action_dispatch.exception"]
status = code_from_exception(env["PATH_INFO"][1..-1], exception)
request = ActionDispatch::Request.new(env)
content_type = request.formats.first
body = {:status => { :code => status, :exception => exception.class.name, :message => exception.message }}
render(status, content_type, body)
end
private
def render(status, content_type, body)
format = content_type && "to_#{content_type.to_sym}"
if format && body.respond_to?(format)
render_format(status, content_type, body.public_send(format))
else
render_html(status)
end
end
def render_format(status, content_type, body)
[status, {'Content-Type' => "#{content_type}; charset=#{ActionDispatch::Response.default_charset}",
'Content-Length' => body.bytesize.to_s}, [body]]
end
def render_html(status)
found = false
path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale
path = "#{public_path}/#{status}.html" unless path && (found = File.exist?(path))
if found || File.exist?(path)
render_format(status, 'text/html', File.read(path))
else
[404, { "X-Cascade" => "pass" }, []]
end
end
def code_from_exception(status, exception)
case exception
when ActionDispatch::ParamsParser::ParseError
"422"
else
status
end
end
end
end
To use it in a test environment requires setting config variables (otherwise you get the standard exception handling in development and test). So to test I have (edited to just have the key parts):
describe Photo, :type => :api do
context 'update' do
it 'attributes with non-parseable json' do
Rails.application.config.consider_all_requests_local = false
Rails.application.config.action_dispatch.show_exceptions = true
patch update_url, {:description => description}
response.status.should eql(422)
result = JSON.parse(response.body)
result['status']['exception'].should match(/ParseError/)
Rails.application.config.consider_all_requests_local = true
Rails.application.config.action_dispatch.show_exceptions = false
end
end
end
Which performs as I need in a public API way and is adaptable for any other exceptions I may choose to customise.
This article (also from 2013) thoughtbot covers also this topic. They put their response inside this middleware service only if you requested json
if env['HTTP_ACCEPT'] =~ /application\/json/
error_output = "There was a problem in the JSON you submitted: #{error}"
return [
400, { "Content-Type" => "application/json" },
[ { status: 400, error: error_output }.to_json ]
]
else
raise error
end