I'm trying to up receive updates on my Trello model when a change occurs, which I'm using their webhooks for. The problem is that one of the parameter's name is "action", which seems to be overwritten by Rails depending on the value in the Routes.rb. Is there any way to avoid this or do I just have to live with it?
Routes.rb
match "/trello" => "trello_updates#index", via: [:get,:post]
Webhook reponse
Parameters: {"model"=>{...},"action"=>"index"}
You can write a middleware in initializers and update the params coming from trello webhooks. like below -
class TrelloWebhooks
def initialize(app)
#app = app
end
def call(env)
request = Rack::Request.new(env)
trello_action = request.params['action']
request.update_param('trello_action', trello_action)
status, headers, response = #app.call(env)
[status, headers, response]
end
end
Rails.application.config.middleware.use 'TrelloWebhooks'
I had to modify the code from Vishnu, which is the accepted answer to make it work with a post request, so if you have a post request, you need to fetch the params out from the body of the response instead:
class TrelloWebhooks
def initialize(app)
#app = app
end
def call(env)
request = Rack::Request.new(env)
body = JSON.parse(request.body.string)
trello_action = body["action"]
request.update_param('trello_action', trello_action)
status, headers, response = #app.call(env)
[status, headers, response]
end
end
Rails.application.config.middleware.use 'TrelloWebhooks'
Related
I'm currently working on a Rails application where I am trying to submit a form to the FormStack API. The request look as follows.
This is what the requests looks like:
POST /api/v2/form/12345/submission.json HTTP/1.1
Host: www.formstack.com
Authorization: Bearer YOUR_APP_OAUTH_TOKEN
Accept: application/json
Content-Type: application/json
field_12345=Example&field_12346=Answer
I'm trying to implement that using Httparty on the library I created to make the requests to this API service.
module FormStack
class Form
include HTTParty
attr_reader :form_id
base_uri "https://www.formstack.com/api/v2"
def initialize
#access_token = ENV.fetch('FORMSTACK_ACCESS_TOKEN')
#form_id = ENV.fetch('FORMSTACK_FORM_ID')
end
def create_form
self.class.get(relative_uri, headers: headers)
end
def submission
self.class.post(create_submission_uri, headers: headers, query: query)
end
private
def relative_uri
"/form/#{#form_id}/field.json"
end
def create_submission_uri
"form/#{#form_id}/submission.json"
end
def headers
{
"Accept" => "application/json",
"Content-Type" => "application/json",
"Authorization" => "Bearer #{#access_token}"
}
end
def query
{
"field_66563890" => "blah",
"field_66563757" => "something"
}
end
end
end
controller
class FormsController < ApplicationController
def display_form
#form = FormStack::Form.new().create_form
end
def create
#form.submission
redirect_to 'localhost:3000'
end
end
This are the routes
get '/forms/display_form', to: 'forms#display_form'
post '/forms/submit', to: "forms#create"
First of all, I've got a couple general ruby things for you:
When you call FormStack::Form.new().create_form you actually don't need the () after .new -- ruby knows to call the method with no arguments even if you exclude the parens.
I'm not quite sure how you're calling FormsController::display_form from FormsController::create, but for now I'll just assume that you're using magic.
Anyways, on to my answer. As your error message states, the error is related to you calling submission on something which does not have a submission method. With that knowledge, we can look at what Object you're calling submission on in this line:
#form.submission
It looks like you're calling submission on #form. Well, let's go and look at where you declare #form:
#form = FormStack::Form.new().create_form
Let's break that declaration down into its parts. First, with FormStack::Form.new(), you're creating a new instance of FormStack::Form. So far so good. FormStack::Form has a submission method defined on it. But then, you call create_form on it. So, let's look at what create_form does:
def create_form
self.class.get(relative_uri, headers: headers)
end
create_form calls a method provided by HTTParty, get. The get method returns a HTTParty::Response Object. So, let's parse through the line where you set #form again. Broken down, what you're doing is this:
#form = FormStack::Form # This line sets the variable to a constant
#form = #form.new # This line sets the variable to be an instance of FormStack::Form
#form = #form.create_form # This line sets #form to be an instance of HTTParty::Reponse
As you can see, at the end we've set #form to an instance of HTTParty::Reponse instead of FormStack::Form, and since there's not submission method for HTTParty::Response that's why you get the error.
Based on this exploration, we can see that the fix would be to set #form to a FormStack::Form object instead, which we can do by changing the display_form action to be:
def display_form
#form = FormStack::Form.new
#form.create_form
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 Rails API which accepts only JSON as input. If I fail to include a header of Content-Type: application/json, then request.headers['Content-Type'] defaults to application/x-www-form-urlencoded and the params do not get parsed properly. The whole json body becomes a key in the params. The result is a 422, which is confusing to API users.
How can I change this to default to parsing as json if no Content-Type header is supplied?
Lots of other questions answer how to do this with the response format. To change this default, you can specify it in the controller with:
request.format = :json
Or in a route namespace with something like:
namespace :api, defaults: {format: :json} do
This, however, changes the default response format and does not change the default request format. What I need to do is to change the default request format for parsing parameters.
Here is my admittedly terrible solution derived from the suggestion in Micael Nussbaumer's answer. I'd love it if some Rubyists could magically turn this ugly hack into a pithy one liner.
module Api
class BaseApiController < ActionController::API
private
# This is an ugly hack needed to make it default to json if you do not
# specify a Content-Type. If you see this and know of a better way please
# say so!
def params
if !#params
if request.headers["Content-Type"]=="application/x-www-form-urlencoded"
body_string = request.body.read
begin
hash = JSON.parse(body_string)
#params = ActionController::Parameters.new(hash)
rescue
# do nothing
end
end
if !#params
#params = super
end
end
#params
end
...
end
I've solved it with middleware this way for Rails API (rails new my_project --api)
config:
# config/application.rb
# ...
require './lib/middleware/consider_all_request_json_middleware'
# ...
module MyApplication
# ...
class Application < Rails::Application
# ...
config.middleware.insert_before(ActionDispatch::Static,ConsiderAllRequestJsonMiddleware)
# ...
middleware:
# lib/middleware/consider_all_request_json_middleware.rb
class ConsiderAllRequestJsonMiddleware
def initialize app
#app = app
end
def call(env)
if env["CONTENT_TYPE"] == 'application/x-www-form-urlencoded'
env["CONTENT_TYPE"] = 'application/json'
end
#app.call(env)
end
end
original: https://blog.eq8.eu/til/content-type-applicationjson-by-default-in-rails-5.html
parsed = JSON.parse(json_body) unless request.headers["Content-Type"] == 'application/json'
I have existing functionality, where specs calls HTTP PUT method of a controller, which in-turn calls model's method to get some JSON data via. API.
class SomeModel
def GetData()
uri = URI.parse('http://api.mydomain.com/getmydata/123/ohmyurl')
Net::HTTP.start(uri.host, uri.port, :read_timeout => 200) do |http|
request = Net::HTTP::Get.new(uri.request_uri)
response = http.request(request)
unless response.body == "null"
return JSON.parse(response.body)
end
#......
end
end
end
class SomeController < ApplicationController
def update
#...
#model.GetData()
#...
end
end
#......specs............
put :update
I need to mock the API in SomeModel. So far I have tried:
#......specs............
before do
stub_request(:get, /api.mydomain.com/)
.with(headers: {'Accept'=>'application/json' })
.to_return(status: 200, body: {id: 1, secondParam: '324'}.to_json, headers: {})
end
#.......
put :update
result = JSON.load response.body
expect(result['secondParam']).to eq("324")
Which is not able to mock the API call and actual API is called.
kindly ignore syntax errors
After user push save button, I need to render new page and render_to_string preview of this page in same time. To store it into DB.
So i got DoubleRenderError exception.
I try to stub #performed?
But Layouts purging after first render. Any ideas?
Thank you for answers!
I've successfully used both render_to_string and render on the same request.
I think you need to make sure you call render_to_string first. YMMV
I would probably do this using rack middleware.
class ResponseLoggerMiddleware
def initialize(app)
#app = app
end
def call(env)
status, headers, response = #app.call(env)
... save your response to the database ...
[status, headers, response]
end
end
You can install it like this:
# environment.rb
Rails::Initializer.run do |config|
...
config.middleware.use ResponseLoggerMiddleware
end