stub_request when request body is not predictable - ruby-on-rails

I am stubbing an http request with stub_request. This http request is basically a slack notification, that contains some random string (e.g. a timestamp.)
So, I can not just reuse the snippet, rspec spits out to me, because body differs on every execution. Is there any possibility to stub a request with, say, pattern, or I am stuck to hook e.g. the Slack#ping?
The dried code, jic:
mutation
class MyMutation < Mutations::Command
def run
slack.ping "#{rand (1..1000)}"
end
end
spec
describe MyMutation do
# ??? stub_request ???
it 'succeeded' do
expect(MyMutation.new.run.outcome).to be_success
end
end
Thanks.
UPD stub request:
stub_request(:post, "https://hooks.slack.com/services/SECRETS").
with(:body => {"payload"=>"{SLACK_RELATED_PROPS,\"text\":\"MY_RANDOM_HERE\"}"},
:headers => {'Accept'=>'*/*', MORE_HEADERS}).
to_return(:status => 200, :body => "", :headers => {})

You need to use partial hash matching:
stub_request(:post, "https://hooks.slack.com/services/SECRETS").
with(:body => hash_including("payload"=>"{SLACK_RELATED_PROPS}"),
:headers => {'Accept'=>'*/*', MORE_HEADERS}).
to_return(:status => 200, :body => "", :headers => {})
I'd also recommend to provide SLACK_RELATED_PROPS as a hash, not as a json-encoded string. Just choose some values from there that you really care about, and strip everything else, like your random-generated value.
You can view more features in docs, such as regex matching or even dynamic evaluating on request object.

Related

How to make a post request with httparty [duplicate]

I would like for a user within my ruby on rails app to be able to submit a ticket to my external ticket management system, squishlist.com. They have an api and instructions as follows. You need to authenticate and get a token and then submit the ticket with the token. From squishlist.
# get the token
https://api.squishlist.com/auth/?cfg=testcorp&user_key=privatekey&api_key=TEST-KEY-12345
=> {"token": "authtoken",
"expires": "2010-06-16 13:31:56"}
# and then the ticket with the token
https://api.squishlist.com/rest/?cfg=testcorp&token=authtoken&method=squish.issue.submit&prj=demo
POST data: {'issue_type': 1, 'subject': 'Hello, world.', 4: 'Open', 5: 10}
For testing purposes, I created a controller, route and view (page) for testing. On my controller I have the following
require 'httparty'
require 'json'
class SubmitticketController < ApplicationController
def submit_a_ticket
#cfg = 'xxxsupport'
#user_key = '4787fsdbbfbfsdbhbfad5aba91129a3f1ed1b743321f7b'
#api_key = 'MrUser411'
#project = 'excelm-manoke'
#url_new_string = 'https://api.squishlist.com/auth/?cfg='+#cfg+'&user_key='+#user_key+'&api_key='+#api_key
# https://api.squishlist.com/auth/?cfg=xxxsupport&user_key=4787fsdbbfbfsdbhbfad5aba91129a3f1ed1b743321f7b&api_key=MrUser411 - this is what is created by #url_new_string
response = HTTParty.get(#url_new_string.to_str) #submit the string to get the token
#parsed_and_a_hash = JSON.parse(response)
#token = #parsed_and_a_hash["token"]
#make a new string with the token
#urlstring_to_post = 'https://api.squishlist.com/rest/?cfg='+#cfg+'&token='+#token+'&method=squish.issue.submit&prj='+#project
#submit and get a result
#result = HTTParty.post(#urlstring_to_post.to_str, :body => {:subject => 'This is the screen name', :issue_type => 'Application Problem', :status => 'Open', :priority => 'Normal', :description => 'This is the description for the problem'})
end
end
And then I have a page that I go to to see the result of the controllers actions and it has the following code.
<p><%= #result %></p>
I know that it is working in general because of responses I have received along the way. My json is different from the example because of fields I have defined in squishlist. Can anyone help me out on this issue?
I guess the real problem is that I can't really see what the json looks like and if it is even close to match. I really don't know much about json. Should I be using something that might be easy. Should I be using ajax to submit this. Any help is greatly appreciated. I love the community here.
I solved this by adding .to_json and some heading information
#result = HTTParty.post(#urlstring_to_post.to_str,
:body => { :subject => 'This is the screen name',
:issue_type => 'Application Problem',
:status => 'Open',
:priority => 'Normal',
:description => 'This is the description for the problem'
}.to_json,
:headers => { 'Content-Type' => 'application/json' } )
The :query_string_normalizer option is also available, which will override the default normalizer HashConversions.to_params(query)
query_string_normalizer: ->(query){query.to_json}

Parsing Data from httpparty response

I am returning quite a lot of JSON from a http party request
In my Rails controller I got this data:
#data = #request.parsed_response["InfoResponses"]
and in my html.erb I displayed the #data on screen and it is as below:
[{"AdditionalResponse"=>nil, "AddressResponse"=>nil, "HighResResponse"=>nil, "LowResResponse"=>nil, "OtherResponse"=>nil, "Error"=>nil, "CarResponse"=>{"Values"=>[{"FieldDescription"=>"Colour", "FieldName"=>"COLOUR", "Value"=>BLUE}, {"FieldDescription"=>"Engine Size", "FieldName"=>"ENGINESIZE", "Value"=>1400}, {"FieldDescription"=>"Number Doors", "FieldName"=>"NUMBERDOORS", "Value"=>4}, {"FieldDescription"=>"Fuel", "FieldName"=>"FUEL", "Value"=>GAS}, {"FieldDescription"=>"Wheel Size", "FieldName"=>"WHEELSIZE", "Value"=>17}, {"FieldDescription"=>"CD Player", "FieldName"=>"CDPLAYER", "Value"=>Y}],
"Error"=>nil, "Id"=>"2"}, "Id"=>"2", "DensityResponse"=>nil, "RiskIndexResponse"=>nil, "FinanceResponse"=>{"Value"=>Available, "Error"=>nil, "Id"=>"2"}},
{"AdditionalResponse"=>nil, "AddressResponse"=>nil, "HighResResponse"=>nil, "LowResResponse"=>nil, "OtherResponse"=>nil, "Error"=>nil, "CarResponse"=>{"Values"=>[{"FieldDescription"=>"Colour", "FieldName"=>"COLOUR", "Value"=>RED}, {"FieldDescription"=>"Engine Size", "FieldName"=>"ENGINESIZE", "Value"=>1400}, {"FieldDescription"=>"Number Doors", "FieldName"=>"NUMBERDOORS", "Value"=>4}, {"FieldDescription"=>"Fuel", "FieldName"=>"FUEL", "Value"=>GAS}, {"FieldDescription"=>"Wheel Size", "FieldName"=>"WHEELSIZE", "Value"=>19}, {"FieldDescription"=>"CD Player", "FieldName"=>"CDPLAYER", "Value"=>Y}],
"Error"=>nil, "Id"=>"1"}, "Id"=>"1", "DensityResponse"=>nil, "RiskIndexResponse"=>nil, "FinanceResponse"=>{"Value"=>Available, "Error"=>nil, "Id"=>"1"}}]
On screen I only want to Display the CarResponse Details - i.e all the Fields such as Colout, EngineSize, etc and their values and the only other details I want to extract are the FinanceResponse the Value and the Id.
What is the best way to extract these details - I have shown 2 here but my JSON response could include the same response as above repeated.
EDIT - My HttpParty code is as below
def self.GetInforFromASPWebAPI
#request_body = [{:Id => '1',
:A => '1',
:B => '1',
:C=> '1'}]
post('localhost:50544/api/MyController/GetInfo', :body => #request_body.to_json,
:headers => {'Content-Type' => 'application/json'})
end
This is in a Helper class - then in my controller I was doing something like the below:
#response = ASPWebAPIClass.GetInforFromASPWebAPI
The contents of the response was even more data so I used what I had done before to strip some of it down to the parsed_response.
You should have a full class representing the API. Your methods would then get what you're interested in, rather than passing an entire response back to the Rails controller forcing the controller to know how to parse the API's response. Put all the API knowledge into the API class in accordance with the Single Responsibility Principle. Your code will be cleaner as a result.
Example:
class CarAPI
include HTTParty
base_uri 'localhost:50544/api/MyController'
# any other initialization stuff
def initialize
#options = {
:body => #request_body.to_json,
:headers => {'Content-Type' => 'application/json'}
}
end
def car_info
response = self.class.post '/getinfo'
# Create a simple Ruby hash with key/value pairs
cars = {}
response['InfoResponses'][0]['CarResponses']['Values'].each do |field|
cars[field['FieldName']] = field['Value']
end
cars
end
end
This is a bit simplified; you'll want to iterate over all the responses (replace the [0] with a loop/map).

POST JSON to API using Rails and HTTParty

I would like for a user within my ruby on rails app to be able to submit a ticket to my external ticket management system, squishlist.com. They have an api and instructions as follows. You need to authenticate and get a token and then submit the ticket with the token. From squishlist.
# get the token
https://api.squishlist.com/auth/?cfg=testcorp&user_key=privatekey&api_key=TEST-KEY-12345
=> {"token": "authtoken",
"expires": "2010-06-16 13:31:56"}
# and then the ticket with the token
https://api.squishlist.com/rest/?cfg=testcorp&token=authtoken&method=squish.issue.submit&prj=demo
POST data: {'issue_type': 1, 'subject': 'Hello, world.', 4: 'Open', 5: 10}
For testing purposes, I created a controller, route and view (page) for testing. On my controller I have the following
require 'httparty'
require 'json'
class SubmitticketController < ApplicationController
def submit_a_ticket
#cfg = 'xxxsupport'
#user_key = '4787fsdbbfbfsdbhbfad5aba91129a3f1ed1b743321f7b'
#api_key = 'MrUser411'
#project = 'excelm-manoke'
#url_new_string = 'https://api.squishlist.com/auth/?cfg='+#cfg+'&user_key='+#user_key+'&api_key='+#api_key
# https://api.squishlist.com/auth/?cfg=xxxsupport&user_key=4787fsdbbfbfsdbhbfad5aba91129a3f1ed1b743321f7b&api_key=MrUser411 - this is what is created by #url_new_string
response = HTTParty.get(#url_new_string.to_str) #submit the string to get the token
#parsed_and_a_hash = JSON.parse(response)
#token = #parsed_and_a_hash["token"]
#make a new string with the token
#urlstring_to_post = 'https://api.squishlist.com/rest/?cfg='+#cfg+'&token='+#token+'&method=squish.issue.submit&prj='+#project
#submit and get a result
#result = HTTParty.post(#urlstring_to_post.to_str, :body => {:subject => 'This is the screen name', :issue_type => 'Application Problem', :status => 'Open', :priority => 'Normal', :description => 'This is the description for the problem'})
end
end
And then I have a page that I go to to see the result of the controllers actions and it has the following code.
<p><%= #result %></p>
I know that it is working in general because of responses I have received along the way. My json is different from the example because of fields I have defined in squishlist. Can anyone help me out on this issue?
I guess the real problem is that I can't really see what the json looks like and if it is even close to match. I really don't know much about json. Should I be using something that might be easy. Should I be using ajax to submit this. Any help is greatly appreciated. I love the community here.
I solved this by adding .to_json and some heading information
#result = HTTParty.post(#urlstring_to_post.to_str,
:body => { :subject => 'This is the screen name',
:issue_type => 'Application Problem',
:status => 'Open',
:priority => 'Normal',
:description => 'This is the description for the problem'
}.to_json,
:headers => { 'Content-Type' => 'application/json' } )
The :query_string_normalizer option is also available, which will override the default normalizer HashConversions.to_params(query)
query_string_normalizer: ->(query){query.to_json}

How do I pass this param properly?

I want to make an API request with a text param, with information I currently have in params[:brand][:tag_list] which seems to be saved as a single comma-delimited string. What's the correct way to make the API request?
Controller code:
current_user.tag(#brand, :with => params[:brand][:tag_list], :on => :tags)
url = "http://www.viralheat.com/api/sentiment/review.json"
#sentiment_response = url.to_uri.get(
:api_key => 'MY_KEY',
:text => :tag_list ).deserialize #This is what I'm currently using and is wrong
Response codes from log:
<- (GET 49996946161 2204098100) http://www.viralheat.com:80/api/sentiment/review.json?api_key=MY_KEY&text=tag_list
-> (GET 49996946161 2204098100) 200 OK (62 bytes 3.09s)
Looking up the docs for viralheat, it looks like their api accepts exactly two parameters: api_key, and text. Assuming params[:brand][:tag_list] a comma-delimited string, you can form your request like so:
current_user.tag(#brand, :with => params[:brand][:tag_list], :on => :tags)
url = "http://www.viralheat.com/api/sentiment/review.json"
#sentiment_response = url.to_uri.get(
:api_key => 'MY_KEY',
:text => params[:brand][:tag_list].split(',').join('&') ).deserialize
This should create the url:
http://www.viralheat.com/api/sentiment/review.json?api_key=MY_KEY&text=cat%26dog%26​mouse
params[:brand][:tag_list].split(',') breaks your string into an array, and join('&') turns it back into a string, but this time delimited by ampersands (which seems to be what you want, based on what you said in a comment on your original post). Your uri.get method should escape the ampersands in the uri, which is why you see the %26s in the final url. This is correct.

How to post JSON data in rails 3 functional test

I plan to use JSON data in both request and response in my project and having some problems in testing.
After searching for a while, I find the following code which uses curl to post JSON data:
curl -H "Content-Type:application/json" -H "Accept:application/json" \
-d '{ "foo" : "bar" }' localhost:3000/api/new
In the controller I can access the JSON data simply using params[:foo] which is really easy. But for functional testing, I only find post and xhr (alias for xml_http_request).
How can I write functional test in rails to achieve the same effect as using curl? Or should I do test in other ways?
Here's what I've tried. I find the implementation for xhr in action_controller/test_case.rb, and tried to add jhr method simply changing 'Conetent-Type' and 'HTTP_ACCEPT'. (Added in test/test_helpers.rb.)
def json_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
#request.env['Content-Type'] = 'Application/json'
#request.env['HTTP_ACCEPT'] ||= [Mime::JSON, Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
__send__(request_method, action, parameters, session, flash).tap do
#request.env.delete 'Content-Type'
#request.env.delete 'HTTP_ACCEPT'
end
end
alias jhr :json_http_request
I used this in the same way as xhr, but it does not work. I inspected the #response object and sees the body is " ".
I also find one similar question on Stack Overflow but it's for rails 2 and the answer for posting raw data does not work in rails 3.
As of Rails 5, the way to do this is:
post new_widget_url, as: :json, params: { foo: "bar" }
This will also set the Content-type header correctly (to application/json).
I found that this does exactly what I want – post JSON to a controller's action.
post :create, {:format => 'json', :user => { :email => "test#test.com", :password => "foobar"}}
Just specify appropriate content type:
post :index, '{"foo":"bar", "bool":true}', "CONTENT_TYPE" => 'application/json'
Json data should go as a string, not as a Hash.
Looking at stack trace running a test you can acquire more control on request preparation:
ActionDispatch::Integration::RequestHelpers.post => ActionDispatch::Integration::Session.process =>
Rack::Test::Session.env_for
Specifying :format does not work because request go as 'application/x-www-form-urlencoded' and json isn't parsed properly processing a request body.
Assuming you have a controller named api, a method named new, and you're in the test for the api controller:
#request.env["RAW_POST_DATA"] = '{ "foo" : "bar" }'
post :new
did the trick for me.
Here is a snippet that let me post json data to test my own app. rails 3
port = Rails.env.production? ? 80 : 3000
uri = URI.parse( Rails.application.routes.url_helpers.books_url(:host => request.host, :port => port, :format => :json) )
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
request.content_type = 'application/json'
request.body = #json_data
response = http.request( request )
#result = response.body
Hope this helps others
As #taro suggests in a comment above, the syntax that works for me in functional and integration tests is:
post :create, {param1: 'value1', param2: 'value2', format: 'json'}
(The curly braces aren't always necessary, but sometimes it doesn't work if they're missing, so I always add them.)
Here's what params and request.format look like for a post of that sort:
params:
{"param1"=>"value1", "param2"=>"value2", "format"=>"json", "controller"=>"things", "action"=>"create"}
request.format:
application/json
The best answer I can come up with to this is you don't
Whether or not it was intentional it s maybe good that rails doesn't implement this for you.
In functional tests you really want to just test your controller and not rails method of deserialization or even that routing and mime detection are all setup correctly, those all fall under an IntegrationTest.
So for your controllers, don't pass JSON just pass your params hash like you normally would. Maybe adding :format as an argument as well if you need to check that and respond differently.
If you want to test the full stack move to an IntegrationTest

Resources