I want to do something like this post but in the actual Rails app, not the testing.
I want to see if the HTTP request was successful or not. If it's not successful (aka 404 Not Found), then I want to render a different HTML. But I can't figure out the syntax to compare.
Currently, I have:
def videos
# get current_user's wistia_project_id & authorization token
#current_user = current_user
project_id = #current_user.wistia_project_id
auth_token = "blah"
request = "https://api.wistia.com/v1/projects/#{project_id}.json?api_password=#{auth_token}"
#response = HTTP.get(request).body
puts HTTP.get(request).status
# handle errors: not 200 OK
if !HTTP.get(request).status:
render "/errors.html.erb/"
end
# get embed code for each video using the hashed_id, put in list
#video_iframe_urls = JSON.parse(#response)['medias'].map do |p|
"https://fast.wistia.com/embed/iframe/#{p["hashed_id"]}?version=v1&controlsVisibleOnLoad=true&playerColor=aae3d8"
end
end
require 'net/http'
uri = URI("https://api.wistia.com/v1/projects/#{project_id}.json?api_password=#{auth_token}")
res = Net::HTTP.get_response(uri)
# Status
puts res.code # => '200'
puts res.message # => 'OK'
puts res.class.name # => 'HTTPOK'
# Body
puts res.body if res.response_body_permitted?
Related
I am trying to set up an example Twilio Rails project that calls a person. I am following the tutorial associated with this repo and have basically a carbon copy of the codebase. I'm getting an error that I think is from this line #validator = Twilio::Util::RequestValidator.new(##twilio_token).
Here's my twilio_controller.rb
class TwilioController < ApplicationController
# Before we allow the incoming request to connect, verify
# that it is a Twilio request
skip_before_action :verify_authenticity_token
before_action :authenticate_twilio_request, :only => [
:connect
]
##twilio_sid = ENV['TWILIO_ACCOUNT_SID']
##twilio_token = ENV['TWILIO_AUTH_TOKEN']
##twilio_number = ENV['TWILIO_NUMBER']
##api_host = ENV['TWILIO_HOST']
# Render home page
def index
render 'index'
end
def voice
response = Twilio::TwiML::Response.new do |r|
r.Say "Yay! You're on Rails!", voice: "alice"
r.Sms "Well done building your first Twilio on Rails 5 app!"
end
render :xml => response.to_xml
end
# Handle a POST from our web form and connect a call via REST API
def call
contact = Contact.new
contact.user_phone = params[:userPhone]
contact.sales_phone = params[:salesPhone]
# Validate contact
if contact.valid?
#client = Twilio::REST::Client.new ##twilio_sid, ##twilio_token
# Connect an outbound call to the number submitted
#call = #client.calls.create(
:from => ##twilio_number,
:to => contact.user_phone,
:url => "#{##api_host}/connect/#{contact.encoded_sales_phone}" # Fetch instructions from this URL when the call connects
)
# Let's respond to the ajax call with some positive reinforcement
#msg = { :message => 'Phone call incoming!', :status => 'ok' }
else
# Oops there was an error, lets return the validation errors
#msg = { :message => contact.errors.full_messages, :status => 'ok' }
end
respond_to do |format|
format.json { render :json => #msg }
end
end
# This URL contains instructions for the call that is connected with a lead
# that is using the web form.
def connect
# Our response to this request will be an XML document in the "TwiML"
# format. Our Ruby library provides a helper for generating one
# of these documents
response = Twilio::TwiML::Response.new do |r|
r.Say 'FUCK.', :voice => 'alice'
# r.Dial params[:sales_number]
end
render text: response.text
end
# Authenticate that all requests to our public-facing TwiML pages are
# coming from Twilio. Adapted from the example at
# http://twilio-ruby.readthedocs.org/en/latest/usage/validation.html
# Read more on Twilio Security at https://www.twilio.com/docs/security
private
def authenticate_twilio_request
twilio_signature = request.headers['HTTP_X_TWILIO_SIGNATURE']
# Helper from twilio-ruby to validate requests.
#validator = Twilio::Util::RequestValidator.new(##twilio_token)
# the POST variables attached to the request (eg "From", "To")
# Twilio requests only accept lowercase letters. So scrub here:
post_vars = params.reject {|k, v| k.downcase == k}
is_twilio_req = #validator.validate(request.url, post_vars, twilio_signature)
unless is_twilio_req
render :xml => (Twilio::TwiML::Response.new {|r| r.Hangup}).text, :status => :unauthorized
false
end
end
end
Error image:
I am using ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-darwin15] and Rails 5.1.0.
Your code is most likely failing at is_twilio_req = #validator.validate(request.url, post_vars, twilio_signature) because upon inspection of the gem's code, it is failing at sort below
data = url + params.sort.join
This is because in Rails 5.1, ActionController::Parameters no longer inherits from Hash, so Hash methods like sort (see Hash docs) will no longer work.
You will need to convert params into hash explicitly:
def authenticate_twilio_request
twilio_signature = request.headers['HTTP_X_TWILIO_SIGNATURE']
#validator = Twilio::Util::RequestValidator.new(##twilio_token)
# convert `params` which is an `ActionController::Parameters` object into `Hash`
# you will need `permit!` to strong-params-permit EVERYTHING so that they will be included in the converted `Hash` (you don't need to specifically whitelist specific parameters for now as the params are used by the Twilio gem)
params_hash = params.permit!.to_hash
post_vars = params_hash.reject {|k, v| k.downcase == k}
is_twilio_req = #validator.validate(request.url, post_vars, twilio_signature)
unless is_twilio_req
render :xml => (Twilio::TwiML::Response.new {|r| r.Hangup}).text, :status => :unauthorized
false
end
end
I am using a custom error controller. I would like to have a 200 response code when visiting the 500 page URL (otherwise my middleware that intercepts 500 response codes sends me an exception email whenever I want to show off my 500)
It would seem using the self.routes as exceptions_app will change the request.path to always be equal to the error number
Target
visit www.example.com/crashy_url # => Should show custom 500 error template with 500 response code
visit www.example.com/500 # => Should show custom 500 error template with 200 response code
Problem
visit www.example.com/crashy_url # => request.path is equal to `/500' so my controller sends a 200 response code
How can I extract the really visited URL with this ?
# config/application.rb
config.exceptions_app = self.routes
# routes
match '/500', to: 'errors#server_error', via: :all
# app/controllers/errors_controller.rb
def server_error
#status = 500
can_be_visited_with_ok_response_code
show
end
def can_be_visited_with_ok_response_code
# We want to hide hide the faulty status if the user only wanted to see how our beautiful /xxx page looks like
# BUT `action_dispatch/middleware/debug_exceptions` will change request.path to be equal to the error !!
# #status = 200 if request.path == "/#{#status}" # BAD request.path will always return response code
end
def show
respond_to do |format|
format.html { render 'show', status: #status }
format.all { head #status }
end
end
I love Ruby and did_you_mean... I was able to guess the correct method name: request.original_fullpath should be used instead to get the original URL entered by the user.
#status = 200 if request.original_fullpath == "/#{#status}"
Note that request.original_url can also give you the full path including the host name
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
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
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