Ruby's Faraday - Send optional parameters in get method - ruby

I have an endpoint with multiple optional parameters.
def get_customers(params=nil)
if params.nil?
customer_url = "#{#url}/customers"
# call api
response = connection.get(customer_url)
else
# I do not know how to write this part
end
end
Could you please help me in order to write a call to and endpoint with optional parameters. The params argument is a hash (key, pair value). The query can have 8 parameters. I do not know how to concatenate the params to the url. I am stack in this section. I am a rookie at ruby and faraday.
Thanks in advance

You don't have to concatenate params with the url on your own. Faraday can accept a hash of params (or nil). You can pass them to the get method like so:
response = connection.get(customer_url, params)
Have into the "GET, HEAD, DELETE, TRACE" section of the documentation for more examples.
Side note: you don't event have to concatenate url and the path. You can pass them as separate arguments.

Related

Using parsed response in separate GET call

I'm new to Ruby and API, so my apologies if this is super simple...
I need to have script that will first POST to initiate the creation of an export file, and then have a GET call to retrieve the file. The GET call needs to use part of the POST json response.
I'm using the httparty gem.
I think I need to create a variable that equals the parsed json, and then make that variable part of the GET call, but I'm not clear on how to do that.
Help is appreciated.
require 'httparty'
url = 'https://api.somewhere.org'
response = HTTParty.post(url)
puts response.parse_response
json response:
export_files"=>
{"id"=> #####,
"export_id"=> #####,
"status"=>"Queued"}}
In my GET call I need to use the export_id number in the url.
HTTParty.get('https://api.somewhere.org/export_id/####')
As described in the comments but a bit more verbose and skeleton for error:
require 'httparty'
require 'json'
url = 'https://api.somewhere.org'
response = HTTParty.post(url)
if hash = JSON.parse(response.body)
if export_id = hash[:export_files][:export_id]
post = HTTParty.post("https://api.somewhere.org/export_id/#{export_id}")
end
else
# handle error
end

How can I access the raw request body in ruby CGI scripts?

In a ruby script that I run as a CGI program, I need to access the body of a HTTP POST request. The request body contains JSON data:
{"data":"a"}
I want to take the whole body and parse it with JSON.parse to process it. What's the canonical way to do this? The Ruby docs don't mention the request body.
I only found a hint in a blog post that
CGI tries to parse the request body as form parameters so a blob of JSON awkwardly ends up as the one and only parameter key.
This approach seems to work
puts cgi.params.keys.first # prints {"data":"a"}
but fails as soon as the value for data is a base64 encoded string that contains an = for padding: Using this body
{"data":"a="}
results in the following output (characters missing at the end):
puts cgi.params.keys.first # prints {"data":"a
What's the correct approach to solve this?
As you might already know, when parameters and their values are urlencoded they are delimited with an =: name=Theo&language=ruby and so on.
This is why the name of the first parameter stops at the character before the =. The approach of using the first key, as describe in that blog post, isn't really reliable.
Instead, in a CGI script you can read the request body directly from stdin e.g.
request_body = $stdin.read
Note, when you instantiate a CGI object it will read in everything from stdin and attempt to parse it into the params hash.
This means that if you'd still like to use the cgi library for building your response you'll need to read from stdin earlier in the code, before creating the CGI object. e.g.
# minimal example that just outputs the request body
require 'cgi'
request_body = $stdin.read
cgi = CGI.new
cgi.out("status" => "OK", "type" => "text/plain", "connection" => "close") do
request_body
end
Apparently there is no easy solution for this in Ruby.
But there are two ways you can achieve this.
Redefine CGI::parse(params) method.
This method in CGI module is responsible for parsing both POST and GET parameters into params hash. You can redefine this method in your code so that it add an extra parameter called RAW_DATA in params hash.
def CGI::parse(query)
params = {}
query.split(/[&;]/).each do |pairs |
key, value = pairs.split('=', 2).collect {
| v | CGI::unescape(v)
}
next unless key
params[key] || = []
params[key].push(value) if value
end
#Add RAW_DATA to params
params[:RAW_DATA] = query
params.default = [].freeze
params
end
Use $stdin.read() before creating CGI instance.
But this may prevent you from making use of other CGI features.
So you may replace $stdin temporarily with a StringIO object.
require 'cgi'
require 'stringio'
raw_data = $stdin.read()
real_stdin = $stdin
$stdin = StringIO.new(raw_data)
STDIN = $stdin
cgi = CGI.new
#Your CGI code here
#........
$stdin = real_stdin
STDIN = $stdin

Sinatra: params hash cannot be merged

I want to merge a hash with default parameters and the actual parameters given in a request. When I call this seemingly innocent script:
#!/usr/bin/env ruby
require 'sinatra'
get '/' do
defaults = { 'p1' => 'default1', 'p2' => 'default2' }
# params = request.params
params = defaults.merge(params)
params
end
with curl http://localhost:4567?p0=request then it crashes with
Listening on localhost:4567, CTRL+C to stop
2016-06-17 11:10:34 - TypeError - no implicit conversion of nil into Hash:
sinatrabug:8:in `merge'
sinatrabug:8:in `block in <main>'
When I access the Rack request.params directly it works. I looked into the Sinatra sources but I couldn't figure it out.
So I have a solution for my actual problem. But I don't know why it works.
My question is: Why can I assign param to a parameter, why is the class Hash but in defaults.merge params it throws an exception?
Any idea?
This is caused by the way Ruby handles local variables and setter methods (i.e. methods that end in =) with the same name. When Ruby reaches the line
params = defaults.merge(params)
it assumes you want to create a new local variable named params, rather than use the method. The initial value of this variable will be nil, and this is the value that the merge method sees.
If you want to refer to the method, you need to refer to it as self.params=. This is for any object that has such a method, not just Sinatra.
A better solution, to avoid this confusion altogether, might be to use a different name. Something like:
get '/' do
defaults = { 'p1' => 'default1', 'p2' => 'default2' }
normalized_params = defaults.merge(params)
normalized_params.inspect
end
Your code is throwing an error because params is nil when you make this call defaults.merge(params). I assume you are trying to merge defaults with request.params, which should contain the parameters from your GET.
Change this line
params = defaults.merge(params)
to this
params = defaults.merge(request.params)
I found this in rack gem
http://www.rubydoc.info/gems/rack/Rack/Request#params-instance_method
It seems you can retrieve GET and POST data by params method but you can't write in it. You have to use update_param and delete_param instead.

Sinatra URL Matching with question mark?

Is there a way to match urls with Sinatra using question mark?
get '/:id?inspect' do
# ...
end
get '/:id?last' do
# ...
end
get '/:id' do
# ...
end
I tried escaping the question mark \?, regex etc, but none of those worked.
I don't want to retrieve the value of inspect or last. I only want to know if they were supplied in the url.
Is that possible?
You can’t directly do what you want. When describing the route, Sinatra treats ? as defining an optional parameter, and doesn’t provide a way to escape it.
In a url a ? separates the path from the query string, and Sinatra only uses the path when matching routes. The contents of the query string are parsed and available in params, and the raw string is available as request.query_string.
Your url scheme seems rather unusual, but one possibility if you want to avoid checking the query_string in the actual route could be to create a custom condition to apply to the routes, and check in there:
set(:query) { |val| condition { request.query_string == val } }
get '/:id', :query => 'inspect' do
# ...
end
get '/:id', :query => 'last' do
# ...
end
get '/:id' do
# ...
end
A standard route is not defined by query parameters and should not be. Why don't you use a if construct on the params in the get /:id route?
I also suggest that when you want to set a query parameter in the request you set it like this: /:id?inspect=true (provide a dummy value)

Sinatra: access to optional parameter

In code like this,
get '/posts.?:format?' do
# ...
end
How can I get the format that was requested?
Look into the params hash as in a no-optional param situation:
params[:format]

Resources