How to use variables in chef resource http_request - ruby

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 :).

Related

Ruby GET NET HTTP request does not work with AUTHORIZATION and ACCEPT when passed in a header

I've been using the code below to call a third party API . This code works fine (i've changed the url and the credentials but the structure of the code is the same) :
require 'base64'
require 'httparty'
require 'json'
######################################################################
# Get the token first
######################################################################
consumer_key = "my_key"
consumer_secret = "my_secret"
credentials = Base64.encode64("#{consumer_key}:#{consumer_secret}").gsub("\n", '')
url = "https://mysite/token"
body = "grant_type=client_credentials"
headers = {
"Authorization" => "Basic #{credentials}",
"Content-Type" => "application/x-www-form-urlencoded;charset=UTF-8"
}
r = HTTParty.post(url, body: body, headers: headers)
bearer_token = JSON.parse(r.body)['access_token']
######################################################################
# Use the token in a call as authorisation header
######################################################################
api_url = "https://apisite/the_value_i_am_looking_for_in_the_api"
url = URI.parse(api_url)
req = Net::HTTP.new(url.host, url.port)
req.use_ssl = true
# If we are just passing a key that doesn't need to be in the token format
headers = {
'Authorization' => "Bearer #{bearer_token}"
}
# Get the response back (he data is in the response body: resp.body )
resp = req.get(url, headers)
My issue is that the API providers have changed their API so you now need to pass an "accept" into the call via the header. I used POSTMAN to make the call, added the accept to the header and was able to get it working without issue. So far so good.
I then changed my ruby code to extend the headers section to include the accept, using the code below:
headers = {
'Authorization' => "Bearer #{bearer_token}",
'Accept' => 'application/vnd.bluebadge-api.v1+json'
}
I've not added an accept to a header before so I may have gotten the syntax wrong.
However, this returns an unauthorised 401 response code:
#<Net::HTTPUnauthorized 401 Unauthorized readbody=true>
I thought I might have the credentials wrong so remove the accept, try again and this changes to a 406 response code:
#<Net::HTTPNotAcceptable 406 Not Acceptable readbody=true>
If I examine the response I get the message I would expect that the accept header is not the supported version. So I know the credentials are correct (and the fact they match the postman credentials which works):
"{\"apiVersion\": \"1\",\"context\": null,\"id\": null,\"method\": null,\"error\": {\"code\": null,\"message\": null,\"reason\": \"Accept header version is not a currently supported version.\",\"errors\": null}}\n"
So I know all my credentials are correct because I've copied them into the postman request which works with no errors. The value for the accept header is correct because I copied that from a working postman request too.
I am at a loss for why this wouldn't work.
I've looked through the NET HTTP library and cant find anything to help me there. I've seen a couple of posts elsewhere which I've tried and they haven't worked either.
I appreciate any help in trying to solve this.
Found the problem. I was using the credentials from the production environment to get the token then trying to query the test environment API. In my defence they look very similar (only 3 characters different). I think I had a case of the code blindness.
The code I posted does work when I put the correct URL for the environments.
I also found that I could use this:
uri = URI.parse("https://myapi/some_text")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri.request_uri)
request["Authorization"] = "Bearer #{bearer_token}"
request["Accept"] = "application/vnd.bluebadge-api.v1+json"
response = http.request(request)
Or using HTTParty like this:
response = HTTParty.get('https://myapi/some_text', {
headers: {"Authorization" => "Bearer #{bearer_token}", "Accept" => "application/vnd.bluebadge-api.v1+json" }
})
I would prefer the format of my orginal code or the HTTparty code because it is easy to see from the code that you're passing headers. Hopefully this will help others to double check their authorization credentials...

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

angular2 - http post request parameters prevent encoding the special characters

i am trying to make a HTTP post request. When using the below code, the request parameters are null in the server side.
let url = apiURL;
let body = { 'tenantId': tenantId, 'vsLoginToken': vsLoginToken, 'branchId': branchId };
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this._http.post(url, body, options).map(res => res.json()).
catch(error => Observable.throw(error.json().error || 'Server error'));
If i remove the 'options' parameter in the HTTP post method i am getting the request parameters values. But the parameter vsLoginToken has some special characters. The original vsLoginToken value is like 'ax6a+kao+c'. But when i get this value in the server side it is like 'ax6a kao c'. As you can see the '+' characters are replaced by spaces and i want to prevent this encoding. How can i prevent this encoding? (i don't want to use URLSearchParam QueryEncoder) Any other answers will be great. Thanks

MultiJson::LoadError: 795: unexpected token when trying to parse incoming body request

I'm losing my sanity trying to parse an incoming request on a Sinatra app.
This is my spec
payload = File.read("./spec/support/fixtures/payload.json")
post "/api/v1/verify_payload", { :payload => payload }
last_response.body.must_equal payload
where is simply spec/support/fixtures/payload.json
{"ref":"refs/heads/master"}
My route looks like
post '/verify_payload' do
params = MultiJson.load(request.body.read, symbolize_keys: true)
params[:payload]
end
And running the spec I get the following error:
MultiJson::LoadError: 795: unexpected token at 'payload=%7B%22ref%22%3A%22refs%2Fheads%2Fmaster%22%7D'
I have tried to parse the body request in different ways without luck.
How can I make the request valid JSON?
THANKS
If you want to send a JSON-encoded POST body, you have to set the Content-Type header to application/json. With Rack::Test, you should be able to do this:
post "/api/v1/verify_payload", payload, 'CONTENT_TYPE' => 'application/json'
Alternatively:
header 'Content-Type' => 'application/json'
post '/api/v1/verify_payload'
More info here: http://www.sinatrarb.com/testing.html
The problem it is that you are passing a ruby hash, that is not well formated, you should pass a json object.
Something like this, should work:
post "/api/v1/verify_payload", { :payload => payload.to_json }

Ruby - Uploading a file using RestClient post

I have a cURL that I am trying to translate into Ruby.
the cURL is this:
curl -i -k -H "Accept: application/json" -H "Authorization: token" -H "Content-Type: image/jpeg" -H "Content-Length: 44062" --data-binary "gullfoss.jpg" http://www.someurl.com/objects
My Ruby code is this:
image = File.read('uploads/images/gullfoss.jpg')
result = RestClient.post('http://www.someurl.com/objects',image,{'upload' => image, 'Accept' => 'application/json', 'Authorization' => 'token', 'Content-Type' => 'image/jpeg', 'Content-Length' => '44062'})
The Ruby code is giving me back a 400 Bad Request. It's not a problem with Authorization or the other headers. I think the problem lies with translating --data-binary to Ruby.
Any help appreciated.
Thanks,
Adrian
There are a few things which might cause the problem.
You want to use an actual File object with RestClient, so use File.open; it returns a file object.
You are including the image object twice in your request; the actual content is not made clear from your question. You are mixing payload elements with headers, for instance. Based on the signature of RestClient.post, which takes arguments url, payload, headers={}, your request should take one of the two following forms:
Is the endpoint expecting named parameters? If so then use the following:
Technique 1: the payload includes a field or parameter named 'upload' and the value is set to the image object
result = RestClient.post(
'http://www.someurl.com/objects',
{ 'upload' => image },
'Accept' => 'application/json',
'Authorization' => 'token',
'Content-Type' => 'image/jpeg',
)
Is the endpoint expecting a simple file upload? If so then use:
Technique 2: the image file object is sent as the sole payload
result = RestClient.post(
'http://www.someurl.com/objects',
image,
'Accept' => 'application/json',
'Authorization' => 'token',
'Content-Type' => 'image/jpeg',
)
Note that I didn't include the 'Content-Length' header; this is not usually required when using RestClient because it inserts it for you when processing files.
There is another way to send payloads with RestClient explained here.
Make sure to read through the official docs on github as well.
I am also new to RestClient so if I'm wrong in this case don't be too harsh; I will correct any misconceptions of mine immediately. Hope this helps!

Resources