Calling public class method within string - ruby

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" })

Related

How to use variables in chef resource http_request

I am using http_request resource in a chef recipe to make an http request which requires proxy user and password. I am facing problem substituting the variables defined in attributes or in fact any variables
e.g. following code works fine, where username and password are hard coded.
http_request 'get-info' do
url "http://host:8080/v123/orgs/abc"
headers({ 'AUTHORIZATION' => "Basic #{ Base64.encode64('user1:pwd123')}",
'Content-Type' => 'application/json' } )
message ( "{ } " )
action :get
end
But if I use use variables instead of hard coded credentials like following
u_name=node['mychef']['username']
pwd=node['mychef']['password']
http_request 'get-info' do
url "http://host:8080/v123/orgs/abc"
headers({ 'AUTHORIZATION' => "Basic #{ Base64.encode64('#{u_name}:#{pwd}')}",
'Content-Type' => 'application/json' } )
message ( "{ } " )
action :get
end
then I get following error
================================================================================
Error executing action `get` on resource 'http_request[get-info]'
================================================================================
Net::HTTPServerException
------------------------
401 "Unauthorized"
Certainly the credentials are not getting read properly. Appreciate help on how to substitute variables in chef resource http_request.
I have resolved my question in the meantime. If I build the Base64 encoding separately in advance like following, then I can easily substitute the value in http_request
sys_admin_creds = Base64.encode64("#{node['mychef']['username']}:#{node['mychef']['password']}")
And then I can substitute value like following
headers({ 'AUTHORIZATION' => "Basic #{sys_admin_creds}",
'Content-Type' => 'application/json' } )
I think there might be other ways to solve this. When I directly substitute values in headers of http_request, I think the number of quotes and double quotes get unmanageable. But I am sure that can be fixed too. But for now I will go ahead with my solution. If anyone have better solution, still post it. So we can always improve our community fellows :).

HTTParty headers strange behavior

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.

Ruby request headers but also a ?tag=tag

I am trying to send an API call to consul using a ruby script and I have a command that should be close to what I need but I can't get the rest to work.
RestClient::Request.execute(method: :get, url: path, timeout: 10, headers: {params: {"tag": tag, "X-Consul-Token" => encode_auth_token}})
Pretty much I need the call to be like if I did
curl --header "X-Consul-Token: <my token>" <my path>?tag=tag where the last tag in ?tag=tag is a variable and the token is a variable too.
You are extremely close, right now this does not work for you because the "X-Consul-Token" => 'AUTH_TOKEN' is being seen as a url parameter and not a header.
req.url
#=> "http://example.com?tag=tag&X-Consul-Token=AUTH_TOKEN"
req.headers
#=> {}
To fix this you just need to have params and the other header tags separated:
req= RestClient::Request.new(
method: :get,
url: 'http://example.com/',
timeout: 10,
headers: {params: {tag: 'tag'},
"X-Consul-Token" => 'AUTH_TOKEN'}) #notice outside of params
req.url
#=> "http://example.com?tag=tag"
req.headers
#=> {"X-Consul-Token"=>"AUTH_TOKEN"}

Correct use of the Rack::Response-Object in Server-Answer

i have a question on how to make proper of use the Rack::Response-Object in a server-response.
In line 711 the method Rack::Lint#each (https://github.com/rack/rack/blob/master/lib/rack/lint.rb#L711) asserts among others that part.kind_of? String such that assert("Body yielded non-string value #{part.inspect}")
The most basic response is a triplet like
[status, headers, body]
I got an error message, when I put the Rack::Response-Object in the triplet like that:
[status, headers, rs]
where rs is the Response-Object.
I got it to work with
[status, headers, st.body]
however, as it then passes the above-mentioned assertion !
My question is only if this makes correct use of Rack::Response, or if not, what are then the proper ways of using it in a Server-Response.
Thank you very much
Rack::Response is simply an interface that is new'd up with 3 arguments, the body, the status code and the headers.
The body argument can either respond to #to_str or it must implement #each and whatever is yielded to #each must respond to #to_s.
So as a straightforward answer consider this to be valid:
response = Rack::Response.new(
['a','series', 'of', 'strings'],
200,
{ 'Content-Type' => 'application/json' }
)
This is valid as well
response = Rack::Response.new(
'{}',
200,
{ 'Content-Type' => 'application/json' }
)
Or if you have a custom class that say is proxying a response from an API, this would also suffice:
class ApiClient
def initialize
#response = get_something
end
def each
#response.chars.each { |el| yield el }
end
private
def get_something
"a string of responses"
end
end
response = Rack::Response.new(
ApiClient.new,
200,
{ 'Content-Type' => 'application/json' }
)
When you're ready to return your response object back to the caller, you simply called #finish on your Rack::Response instance. This is also aliased as #to_a and #to_ary.

Sending an array of string in a request made with rest-client gem

I am creating a gem to implement the Calendly API and I need to make a request like
curl --header "X-TOKEN: <your_token>"
--data "url=https://blah.foo/bar&events[]=invitee.created"
https://calendly.com/api/v1/hooks
to create a webhook.
As you may see, events is passed as an array of strings and this is indicated by the [].
I am trying to use RestClient::Request#execute to send this request, but I don't know how to pass this array of strings in this case.
I tried
RestClient::Request.execute(method: :post,
url: #uri,
params: { url: url,
events: "invitee.#{type}"
},
headers: { "X-TOKEN": "#{#api_key}" })
but then I am not sending an array as the API expects.
I also tried
RestClient::Request.execute(method: :post,
url: #uri,
params: { url: url,
'events[]': "invitee.#{type}" },
headers: { "X-TOKEN": "#{#api_key}" })
but it didn't work either. I got a bad request error and nothing else.
How should I build my request in this case?
Posted for visibility (previously answered in comment)
RestClient will appropriately serialize a native ruby Array for you meaning
events: ["invitee.#{type}"]
will be serialized into events[]=invitee.created where type == 'created'. This will work for an Array with multiple values too.
events: ["more","than","one"]
will be converted to events[]=more&events[]=than&events[]=one.
Finally it also handles an other data types inside an Array such as another Hash
events: [{first_name: 'Zaphod', last_name: 'Beeblebrox'}]
will become events[][first_name]=Zaphod&events[][last_name]=Beeblebrox

Resources