HTTParty headers strange behavior - ruby

Context :
I'm using HTTParty to do a POST request on an API that accept application/json
My body is something like that (as a hash)
{:contact_id =>"12345679",
:items=> [
{:contact_id=>"123456789", :quantity=>"1", :price=>"0"},
{:contact_id=>"112315475", :quantity=>"2", :price=>"2"}
]}
Issue :
When I write the following code, this is working :
HTTParty.post('https://some-url', body: content.to_json, headers: { "Content-Type" => "application/json" } )
But when change only the => symbol in headers, to : symbol, this is not working (the API respond that some parameters are missing)
HTTParty.post('https://some-url', body: content.to_json, headers: { "Content-Type": "application/json" } )
Question :
Why changing "Content-Type" => "application/json" to "Content-Type": "application/json" lead to errors ?
I think this is something with Ruby hash that I don't understand.
Edit :
I think my question is not a duplicate of Why is this string key in a hash converted to a symbol?
It's important for HTTParty consumers, to know that HTTParty do not accept symbols for headers but only strings.
For more information, see
Content-Type Does not send when I try to use a new Hash syntax on header and
Passing headers and query params in HTTparty
Thank you #anothermh

Hash keys in Ruby can be any object type. For example, they can be strings or they can be symbols. Using a colon (:) in your hash key tells Ruby that you are using a symbol. The only way to use a string (or other object type, like Integer) for your key is to use hash rockets (=>).
When you enter { "Content-Type": "application/json" }, Ruby will convert the string "Content-Type" to the symbol :"Content-Type". You can see this yourself in the console:
{ "Content-Type": "application/json" }
=> { :"Content-Type" => "application/json" }
When you use a hash rocket it does not get converted and remains a string:
{ "Content-Type" => "application/json" }
=> { "Content-Type" => "application/json" }
HTTParty does not work with symbolized keys.

Related

Users_not_found in HTTParty request to SlackAPI when curl works

I'm trying to use GET request using httparty gem to have info about specific user from SlackAPI. From curl it works well
curl --data "token=SLACK_TOKEN&email=user#example.com" https://slack.com/api/users.lookupByEmail
But my code below seems to be broken because I received an error {"ok":false,"error":"users_not_found"}
module Slack
class GetUserId
def call
response = HTTParty.get("https://slack.com/api/users.lookupByEmail", payload: payload, headers: headers)
end
def headers
{
'Content-Type' => 'application/json',
'Authorization' => 'Bearer SLACK_TOKEN'
}
end
def payload
{
email: "user#example.com"
}.to_json
end
end
end
If you check their documentation, it seems that API do not accept JSON-form but rather "application/x-www-form-urlencoded.
So something like:
headers: {
'Authorization' => 'Bearer SLACK_TOKEN,
"Content-Type" => "application/x-www-form-urlencoded"
},
body: {
"token=SLACK_TOKEN”,
”email=user#example.com"
...
Reference: https://api.slack.com/methods/users.lookupByEmail

Calling public class method within string

I am trying to insert a public class method within the payload of a POST call. The code I've written:
RestClient::Request.execute(method: :post,
url: $url,
payload: '{"event" : "start_skill",
"uuid" : "RandomSecure.uuid"}',
headers: {"Content-Type" => "application/json"})
So what I am trying to do is for this call to generate a new UUID each time the call is made. The problem, obviously, is the fact that the entire payload is in single quotation marks (e.g. ' ). I've tried wrapping the UUID in single quotation marks, but that does not generate the same output. I've also tried #{, but that does not work either.
What else could I try for the UUID method to be called upon without changing the message?
There are a couple of ways you could go. You could use an interpolated string (string with double quotes "like this", and indeed use #{} within this string. However, you would need to manually escape every single " in your string which can become hassle.
The other method would be to make a hash with your desired json data, and call .to_json on it.(note: you need to add require 'json'.
{ event: "start_skill", uuid: RandomSecure.uuid }.to_json
Which would result in:
"{\"event\":\"start_skill\", \"udid\": \"some random udid\"}"
So to wrap it up:
# somewhere at the top of your file
require 'json'
payload = { event: "start_skill", uuid: RandomSecure.uuid }.to_json
RestClient::Request.execute(method: :post,
url: $url,
payload: payload,
headers: { "Content-Type" => "application/json" })

Ruby Typhoeus gem - Invalid Json in body?

I have the following code:
require 'Typhoeus'
url = Typhoeus::Request.new("https://fluidsurveys.com/api/v2/webhooks/subscribe/",
userpwd: "username_test:password_test",
method: :post,
body: {
'subscription_url' => 'http://glacial-spire-test.herokuapp.com/hooks/response_created_callback',
'event' => 'response_complete'
},
headers: { 'Content-Type' => "application/json"})
response = url.run.body
puts response
This returns a response code of 400 and an error:
Content-Type set to application/json but could not decode valid JSON in request body.
Here are the docs for the API call I am making: http://docs.fluidsurveys.com/api/webhooks.html
POST /api/v2/webhooks/subscribe/¶
Returns a status of 409 if a webhook with the subscription url already exists. Returns a
status of 201 if the webhook was successfully created. Requests must be sent as an
application/json-encoded dictionary with the required fields subscription_url and event
Sample request (ACCORDING TO DOCS):
{
"subscription_url": "http://fluidsurveys.com/api/v2/callback/",
"event": "response_complete",
"survey": 1,
"collector": 1
}
What am I doing wrong here? survey and collector are optional params, and I don't see an issue with the json in my body.
I am guessing you might need to convert the request body into JSON using a library like Oj (https://github.com/ohler55/oj). Using the Oj library:
requestBody = {
'subscription_url' => 'http://glacial-spire-test.herokuapp.com/hook/response_created_callback',
'event' => 'response_complete'
}
url = Typhoeus::Request.new("https://fluidsurveys.com/api/v2/webhooks/subscribe/",
userpwd: "username_test:password_test",
method: :post,
body: Oj.dump(requestBody, mode: :compat),
headers: { 'Content-Type' => "application/json"})
response = url.run.body
puts response
The critical line is:
body: Oj.dump(requestBody, mode: :compat)
If you need to load any JSON content to Ruby, just use Oj.load

What is Net::HTTP doing differently than executing curl in ruby?

I have methods that GETs a resource from an external Rails server via JSON. One uses the net/http library, the other executes curl. They respond differently.
If I use net/http
def get_via_nethttp(endpoint, user, password, id)
uri = URI.parse("#{endpoint}/items/#{id}.json")
http = Net::HTTP.new(uri.host, uri.port)
headers = {
'Content-Type' => 'application/json',
'Accept-Encoding' => 'gzip,deflate',
'Accept' => 'application/json'
}
request = Net::HTTP::Get.new(uri.request_uri, headers)
request.basic_auth(user, password)
http.request(request)
end
then the return code is ASCII-8Bit encoded. Here's a snippet:
\x1F\x8B\b\x00\x00\x00\x00\x00\x00\x03\xBDWmo\xE28\x10\xFE+Vv\xA5...
However, if I use curl
def get_via_curl(endpoint, user, password, id)
%x(curl -u #{user}:#{password} #{endpoint}/items/#{id}.json)
end
then a JSON string returns
"{\"id\":1234,\"title\":\"Foo\"}"
I've tried numerous ways to decode the output of net/http to no avail. In the end, I just want the JSON string, so I ended up going with curl. What's the difference that's causing this?
Setting in headers 'Accept-Encoding' => 'gzip,deflate' can result server sending you a chunked response. Just remove it.

Unable to decode request as valid JSON using RUBY

I am making the following API GET request, using ruby 1.9.3 and the httparty gem:
uri= HTTParty.post("www.surveys.com/api/v2/contacts",
:basic_auth => auth,
:headers => { 'ContentType' => 'application/json' },
:body => {
"custom_test" => test,
"name" => firstname,
"email" => emailaddress
}
)
The variables auth,test,firstname, and emailaddress are valid. This is the response I am receiving back from my request:
{
"code": "invalid_json",
"description": "Request sent with Content-Type: application/json but was unable to decode request body as valid json.",
"success": false
}
500
#<Net::HTTPInternalServerError:0x007fe4eb98bde0>
What is wrong with the way I am posting this JSON request?
EDIT: It's probably worth noting that the API allows you to define custom attributes to a contact, hence the "custom_test" attribute in the body.
Since you are receiving an internal server error (500) instead of a not accepted (406), most likely there is coding problem on the server, because an exception that he is not expecting is happening instead of delivery to you a nice error explaining what is wrong (and this would be my first guess).
But let's say it is a problem with the JSON communication. Maybe you have to specify that you are accepting json format?
Try
:headers => { 'ContentType' => 'application/json', 'Accept' => 'application/json' },
Accept header definition from w3:
The Accept request-header field can be used to specify certain media types which are acceptable for the response. Accept headers can be used to indicate that the request is specifically limited to a small set of desired types, as in the case of a request for an in-line image.
And content-type header definition:
The Content-Type entity-header field indicates the media type of the entity-body sent to the recipient or, in the case of the HEAD method, the media type that would have been sent had the request been a GET.
You're sending a regular HTTP query string while saying it's json.
From HTTParty manual: "body: The body of the request. If it‘s a Hash, it is converted into query-string format, otherwise it is sent as-is."
Try:
:body => JSON.generate({
"custom_test" => test,
"name" => firstname,
"email" => emailaddress
})
You need to require 'JSON'

Resources