How to test REST API request (rails) - ruby-on-rails

I have REST API, for example: I already create REST users, for creating user end point is go to "localhost:3000/users" POST method.
So pattern of param should be like this :
:user => {:name => "apple", :address => "heaven"}
My question is :
How to create pattern above, then I test it to endpoint POST method ?
I already test with postman client, but failed because my pattern incorrect (miss root : user)
Thanks

A controller test will catch the type of error you're describing, and is more efficient than an end-to-end test. Example in RSpec:
it 'creates a new user' do
post :create, user: {name: 'apple', address: 'heaven'}, format: 'json'
assert_response :success
expect(response).to render_template(:create)
expect(assigns(:user).name).to eq('apple')
expect(assigns(:user).address).to eq('heaven')
end
I have used curl (in code) to test APIs end-to-end. The Faraday gem would probably work as well.
With the curl request, you'll need to pass the -X POST parameter and a -d parameter for the data. -d can have the following formats:
-d "user[name]=apple" -d "user[address]=heaven"
-d {"user": {"name": "apple", "address": "heaven"}}
You can also pass headers with -H.
Altogether:
curl http://your_url -X POST -H "Content-type: application/json" -d {"user": {"name": "apple", "address": "heaven"}}

Related

Proper forming of API requests

The following command in the console:
curl 'https://3b803ce956aa.ngrok.io/api/v1/send' -i --header "Accept: application/json" --header "Content-Type: application/json" -H 'Accept-Charset: utf-8' -H 'Authorization: Token token="GeUPm5xoxQFXR0ijJOZ6W6xr2ME1wZUWiaB3PLg9uZ8uGhFFDE7YnqCjFQwcCs0zgbtHjIiuc2jxo4I5"' -d '{"server_id":1,"building_id":1}'
but also with a version
--data '{"server_id":1,"building_id":1}'
is being received by the API controller. The routes are defined as:
namespace :api do
namespace :v1 do
post 'send', to: 'messages#send', defaults: { format: :json }
However, the handling of the requests is failing, in both cases, as it receives and returns:
Processing by Api::V1::MessagesController#send as JSON
Parameters: {"server_id"=>1, "building_id"=>1, "message"=>{"server_id"=>1, "building_id"=>1}}
Completed 500 Internal Server Error in 0ms (ActiveRecord: 0.0ms)
ArgumentError (wrong number of arguments (given 1, expected 0)):
where the controller action
def send
#request = JSON.parse(request.body.read)
#messagelog = Messagelog.create(server_id: params[:server_id], building_id: params[:building_id]
[...]
Two oddities appear:
the fact that the parameters are repeated with the contents of the parameters as part of the message
the action expects 0 arguments
I do not understand why the parameters get interpreted twice (this is a sub-question..). How can the API controller action be construed to accept the parameters?
By default Rails wraps JSON request parameters into a hash guessing its name from controller class. You can read details here.
So if you don't need this "guessed" hash in your params just remove :json from :format array in config\initializers\wrap_parameters.rb. Or you can use fine-grained control at the controller level as described above.

No route matches?

I'm getting a "No route matches" exception in one of my tests (and using curl from the command line) one of my routes (POST /users/confirm). The curl's I've tried are as follows, neither of them work and receive the same exceptions outlined in the below notes:
curl -X POST -H "Content-Type: application/json; version=1" \
-d '{ user: { "token":"1deb36b4e6a7ba6d9203" } }' \
http://localhost:3000/appname/users/confirm
curl -X POST -H "Content-Type: application/json; version=1" \
-d '{ "token":"1deb36b4e6a7ba6d9203" }' \
http://localhost:3000/appname/users/confirm
My test is as follows. I have config.wrap_parameters = true in my /config/application.rb file...
Here is my User UsersController#confirm action along with my strong params. I params.require(:user).permit(:token) as opposed to simply params.permit(:token) because, as stated above, I have config.wrap_parameters = true in my /config/application.rb file...
This is my route entry...
Here is the output from rails routes (app name removed)...
I have config.wrap_parameters = true in my /config/application.rb file...
Oddly enough, if I change my params in my test to post :confirm, params: { token: #user.confirmation_token }, I get the following error instead:
At a loss. Any thoughts?
It turns out I didn't need :token in my route after all. Changed it to this, and all is well:
post '/appname/users/confirm/', to: 'users#confirm', as: 'appname_users_confirm'

curl not connecting, but I can visit it in my browser?

I am unsure what this is, but when I do:
curl -X POST -d '{"foo":"bar"}'
-H 'Content-Type: application/json'
-H 'Authorization: Token bdcaf10129ba8ad968c03c6083898bf8c426f749d4e9a14d58e4ef02bfe1c217'
'http://api.site.local.com:4000/internal/application/v1/users'
# New lines in above request are only there for readability.
curl: (7) Failed to connect to api.site.local.com port 4000: Connection refused
But if I go to:
http://api.site.local.com:4000/internal/application/v1/users
It works, I mean I get an error that there is no index action, but none the less the actual route exists and is working.
So why is the browser saying yes, but curl is saying no?
I mean my test:
it "should return 200 for a user being published" do
VCR.use_cassette 'publisher/publish_user_200' do
site = double(
'site',
:site_api_url => YAML.load(File.read('config/application.yml'))['APPLICATION_URL'].to_s,
:site_api_key => YAML.load(File.read('config/application.yml'))['APPLICATION_KEY'].to_s
)
expect(
BlackBird::PublishToSites::User.publish(
site,
{
first_name: 'adsadsad', user_name: 'sasdasdasdsa' ,
email: 'asdassad#sample.com', auth_token: 'asdsadasdasdsa'
}
).code
).to eql 200
end
returns a 0, meaning it could not connect. Am I doing something wrong I don't know about?
Note: The application key and url from the applications.yml file are the same as the values in the curl request.

Converting curl to ruby equivalent

I'm having trouble converting a curl to a ruby script. Basically I'm trying to consume the parse.com RESTful api. Here is the curl
curl -X POST \
-H "X-Parse-Application-Id: XXX" \
-H "X-Parse-REST-API-Key: YYY" \
-H "Content-Type: application/json" \
-d '{
"channels": [
"Giants",
"Mets"
],
"data": {
"alert": "test message"
}
}' \
https://api.parse.com/1/push
And this Is what I've been trying to do (using HttParty)
puts "hello world"
require 'httparty'
require 'json'
class Parse
include HTTParty
base_uri "api.parse.com:443"
headers "X-Parse-Application-Id" => "XXX", "X-Parse-REST-API-Key" => "YYY", "Content-Type" => "application/json"
end
option = {:channels => "Giants", :data => {:alert => "un mensaje de mierda"}}
puts Parse.post("/1/push", body: option.to_json)
)
I'm getting an error code 107 saying invalid json. Any suggestions will be appreciated.
I think you just need to serialise the ruby structure to JSON in the second param. The param should be the string to POST, not a Ruby struct.
I think this will work (only other possible problem I can see is whether you'll connect via https as the code is written):
data = {"channels": ['Giants'], "data": {alert: 'un mensaje '}}
puts Parse.post("/1/push", body: data.to_json)
. . . the JSON-like format in the Ruby data structure is not JSON,
foo: "bar"
is just another Ruby (1.9+) way of saying
:foo => "bar"

Ruby: HTTParty: can't format XML POST data correctly?

NOTE: "object" is a placeholder work, as I don't think I should be saying what the controller does specifically.
so, I have multiple ways of calling my apps API, the following works in the command line:
curl -H 'Content-Type: application/xml' -d '<object><name>Test API object</name><password>password</password><description>This is a test object</description></object>' "http://acme.example.dev/objects.xml?api_key=1234"
the above command generates the following request in the devlog:
Processing ObjectsController#create to xml (for 127.0.0.1 at 2011-07-07 09:17:51) [POST]
Parameters: {"format"=>"xml", "action"=>"create", "api_key"=>"1234", "controller"=>"objects",
"object"=>{"name"=>"Test API object", "description"=>"This is a test object", "password"=>"[FILTERED]"}}
Now, I'm trying to write tests for the actions using the API, to make sure the API works, as well as the controllers.
Here is my current (broken) httparty command:
response = post("create", :api_key => SharedTest.user_api_key, :xml => data, :format => "xml")
this command generates the following request in the testlog:
Processing ObjectsController#create to xml (for 0.0.0.0 at 2011-07-07 09:37:35) [POST]
Parameters: {
"xml"=>"<object><name><![CDATA[first post]]></name>
<description><![CDATA[Things are not as they used to be]]></description>
<password><![CDATA[WHEE]]></password>
</object>",
"format"=>"xml",
"api_key"=>"the_hatter_wants_to_have_tea1",
"action"=>"create",
"controller"=>"objects
So, as you can see, the command line command actually generates the object hash from the xml, whereas the httparty command ends up staying in xml, which causes problems for the create method, as it needs a hash.
Any ideas / proper documentation?
Current documentation says that post takes an url, and "options" and then never says what options are available
**EDIT:
as per #Casper's suggestion, my method now looks like this:
def post_through_api_to_url(url, data, api_key = SharedTest.user_api_key)
response = post("create", {
:query => {
:api_key => api_key
},
:headers => {
"Content-Type" => "application/xml"
},
:body => data
})
ap #request.env["REQUEST_URI"]
assert_response :success
return response
end
unfortunately, the assert_response fails, because the authentication via the api key fails.
looking at the very of of the request_uri, the api_key isn't being set properly... it shows:
api_key%5D=the_hatter_wants_to_have_tea1"
but it should just be equals, without the %5D (right square bracket)
I think this is how you're supposed to use it:
options = {
:query => {
:api_key => 1234
},
:headers => {
"Content-Type" => "application/xml"
},
:body => "<xmlcode>goes here</xmlcode>"
}
post("/create", options)
Forgive me for being basic about it but if you only want to send one variable as a parameter, why don't you do as Casper suggests, but just do:
post("/create?api_key=1234", options)
Or rather than testing HTTParty's peculiarities in accessing your API, perhaps write your tests using Rack::Test? Very rough example...
require "rack/test"
require "nokogiri"
class ObjectsTest < Test::Unit::TestCase
include Rack::Test::Methods
def app
MyApp.new
end
def create_an_object(o)
authorize "x", "1234" # or however you want to authenticate using query params
header 'Accept', 'text/xml'
header 'Content-Type', 'text/xml'
body o.to_xml
post "/create"
xml = Nokogiri::XML(last_response.body)
assert something_logic_about(xml)
end
end

Resources