Ruby rest client array of params - ruby-on-rails

I'm updating my rest-client gem from 1.8 to 2.0.
On 1.8 it sends an array of params on a get request as my-url?ids=1,2,3,4.
But on 2.0 it uses duplicated keys like my-url?ids=1&ids=2&ids=3. For reasons beyond the context of this question, our back end does NOT support the new multiple keys syntax (ok, it supports it, but we'll have to make a big refactor). So I'd like to know if there's a way to use the 2.0 client version and keep sending get array requests with only one key and separated by comma as before?

Based on the rest-client docs https://github.com/rest-client/rest-client#query-parameters it seems your only option would be to serialize the parameters yourself and add them to the URL as the query string.
If you don't like this behavior and want more control, just serialize params yourself (e.g. with URI.encode_www_form) and add the query string to the URL directly for GET parameters or pass the payload as a string for POST requests.
If you provide some sample code on how you're using the gem, we could help out a bit better with sample answers.

Ok yeah Leo Correa was right, so what I had to do is replace my old code
params = {
partner_key: #partner,
resources: ["front_end_config", "gui_settings"]
}
#response = JSON.parse( RestClient.get( "#{api_base_uri}/partner_config.json", params: params.merge({multipart:true}) ) )
with this new one, serializing and encoding by myself...
params = {
partner_key: #partner,
resources: '["front_end_config", "gui_settings"]'
}
params = URI.encode_www_form(params.merge({multipart:true}))
#response = JSON.parse( RestClient.get( "#{api_base_uri}/partner_config.json?#{params}" ) )
It's ugly as hell, but it worked for me. If there's some other idea on how to make it better, I'd appreciate.

Related

How to create requests params for json api resources in requests tests?

I'm creating a API with Rails 6 and JSON API Resource in order to learn more. I got stuck with requests tests. What I'm doing for now is using the gem json_matchers to test the response of my endpoint, but my real problem is to build the body of requests for: post, put and patch.
Hi, thanks for you attention.
Here is the deal, I started adding to my RSpec files all the json needed for the tests, but these files ended up getting too big. So I thought, how can I makes this to stay more lean?
Then I copy all the json in my tests to json files and importing when needed, for example:
# RSpec Helper
def request_json(json_name)
request_directory = "#{Dir.pwd}/spec/support/api/requests"
request_path = "#{request_directory}/#{json_name}.json"
File.read(request_path)
end
# Example of params for user request
let(:params) { request_json("user") }
I thoutgh it was a great idea, but then I ran into two problems: I need to create a file for every request test and... how I will modify same value of this json file at runtime? For example, I create a object using FactoryBot and now I need to use the ID of this object in my request for a update. How can I do that?
Using regex?
let(:params) do
json = request_json("user")
json.gsub(/\"id\": \"1\"/, "\"id\": \"#{id}\"")
end
Ok... It works, but I don't like it! I think that this can turn to be a real mess. Another options was to convert this to hash and then json again... Nope, nope, nope, don't like it either.
Now I'm trying to create something more dynamic using FactoryBot and Faker. For now, with the code below, I can pass a factory name and receive a perfect json body for a post.
def serializer_for(resource)
{
"data": {
"type": resource.to_s.tableize,
"attributes": create_attributes_for(resource),
}
}.to_json
end
def create_attributes_for(resource)
attributes = attributes_for(resource)
attributes.reduce({}) do |hash, element|
hash.update(standardize_json_key() => element.last.to_s)
end
end
def standardize_json_key(symbol)
symbol.to_s.gsub("_", "-")
end
But then I began to think about the challenges of this approach:
Post/Put/Patch with relationships?(has_one or has_many)
How I'll add the ID field for put/path?
Add how I'll add the upload files when needed?
So... returning to my question: How can I prepare the json body for the request using factory bot (using even traits) with all the concerns above?
If you have/know a better answer for this problem, share with, please. If not, I'll try to create a gem for this.
Appreciate your time, thanks!

How to send an array with a POST request, with a parameter name for the array that contains an empty set of square brackets []?

So, Rails normally handles parsing of incoming Arrays sent via HTTP Post requests (forms), like this:
"Normally Rails ignores duplicate parameter names. If the parameter
name contains an empty set of square brackets [] then they will be
accumulated in an array." - Rails Guides
But when using Net::HTTP.Post to send a Post request to a third party service (API), then it seems this convention of handling arrays in HTTP Post requests is not followed.
This code:
data = {:categories => [one, two, three]}
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
request.set_form_data(data)
response = http.request(request)
Then set_form_data will serialize the Array like this:
categories=one&categories=two&categories=three
And not like this (which I thought was the conventional Rails way):
categories[]=one&categories[]=two&categories[]=three
Why?
I can see that it has to do with the recent implementation of the URI.encode_www_form method that set_form_data uses. But what is the purpose deviating from the conventional Rails way?
And, more importantly, how do I easily modify this to send it in the latter way (without overloading a bunch of inherent Ruby/Rails methods)?
I found out that the solution was as easy as changing the table name:
data = {'categories[]' => [one, two, three]}
It even works if other elements of the data hash are :symbols.
I'd still be curious to find out why Rails makes this "hack" necessary when using the Net::HTTPHeader::set_form_data method, to get Rails' otherwise conventional way of handling arrays in the url parameters.

How to indicate empty array in HTTP request query params?

This is the way Rails accepts arrays in query parameters:
PUT /resource.json?sport_ids[]=133&sport_ids[]=64&sport_ids[]=71 ...
I tried to google this question but didn't find any explicit docs on it:
How to tell Rails that we want sport_ids to become empty (pass empty array of sport_ids via query parameters) ?
HTTP requests can have only variables on the url itself. That's a limitation feature of HTTP, not Rails.
Take a look at How Does Rack Parse Query Params? With Parse_nested_query to figure out how rails collects the variables into an array, it won't run out of the box in case of an empty array.
You can avoiding sending the params["sport_ids"] and patch your controller with:
params["sport_ids"] ||= []
The best practice to use put/post requests, is passing such data in the request body (json/xml) like:
{
"sport_ids": []
}
Or with data as:
//...
{
"sport_ids": [133, 64, 71]
}
//...
For more info about HTTP request steps, check Running a HTTP request with rails.
While #mohameddiaa27's answer has good advice on how to achieve that by passing such data in the request body as JSON I found that I cannot rely on it within my application: I found that it is not easy to combine such passing of JSON into request body within multipart forms where I want to pass user record (with user[sport_ids] in it) and user's avatar image (user[avatar]) field.
So I continued to investigate how to achieve that using default "query parameters in a request body of POST/PUT request" approach and found the reason why I was not able to reset my sport_ids on server-side: it was the lack of permission for that specific sport_ids field. Now I have the following permits (pseudocode):
current_user.update!(user_params)
where user_params is
user_attributes_to_permit = [
...
:sport_ids, # this is what was needed for just `user[sport_ids]` to work.
{ :sport_ids => [] } # this is needed for sport_ids non-empty arrays to work
...
]
params.require(:user).permit(user_attributes_to_permit)
so now I am able to reset the sport_ids array of my user by passing just user[sport_ids] (without '=' and value! i.e. ...&user[sport_ids]&...) within my query parameters.

Display incoming xml request in Rails

Hey guys,
Is there an easy way to display the complete xml request I'm getting posted to one of my methods?
Thanks a lot
If you want to see the actual XML that is being posted rather than a params hash try
xml_data = request.raw_post
Then to get a hash out of that you could do
hash_from_xml_data = Hash.from_xml(xml_data)
I'm inferring a bit, but you probably want request.body.
An easy, though slightly dirty, way to view it is to raise it as an error in your controller: raise request.body

Passing an attribute to a SOAP request using Savon

I'm consuming a simple SOAP web service to get a small piece of HTML to be included in a Rails site. Unfortunately, I'm not particularly familiar with SOAP.
I need to call the TopHtml() SOAP method on the endpoint below but I need to also pass an ID number like TopHtml(29).
I'm using the Savon gem and my code looks a little something like:
response = Savon::Client.new('http://www.xxxxxx.xxx/webservices/services.asmx?wsdl').top_html(29)
which works but returns the default response for when an ID number was not provided.
It seems that the ID number is not being passed. Does anyone know how to pass parameters to Savon SOAP requests?
Many thanks,
Tristan
try
response = Savon::Client.new('http://www.xxxxxx.xxx/webservices/services.asmx').top_html { |soap| soap.body = { :id => 29} }
In the interests of time, I ended up preparing the request XML myself which is less than ideal (and almost defeats the purpose of using Savon) but it's the only way I could have the request prepared properly. The XML was provided by the developers of the service.
client = Savon::Client.new 'http://www.xxxxxx.xxx/webservices/services.asmx?wsdl'
response = client.top_html do |soap|
soap.xml = ...long xml here...
end
Yuck but I'm not going to spend anymore time on it.

Resources