Parse Json Request from CURL in Rails - ruby-on-rails

I need to parse Json data from curl request.Need to split Mac,Parameter,datatype,value from below curl request and pass those data's as input for set method(For each Mac separately).Can anyone please guide how to split?
curl -k -s -H "Content-Type: application/json" -d '{"Data": {"Mac":"10.43.33.34","Parameter":"Device.wifi","datatype":"string","value":"5Ghz"},{"Mac":"15.23.43.48","Parameter":"Device.wifi","datatype":"string","value":"2.4GHZ"}}' http://test:3000/api/executions_api_set/
Set API
def show
client = SetClient.new
versionResponse = client.set_req(mac,parameter,datatype,value)
if versionResponse.code == "200"
value = JSON.parse(versionResponse.body)
render json: {Mac: mac,Response:value}, status: :ok
else
render json: {Mac: mac,Parameter: parameter,status: 'Failed',responsecode:versionResponse.code}, status: :ok
end
end
end
end
end
Updated Code
def create
value = ''
client = SetClient.new
params["Data"].each do |mac_attributes|
#mac_address, #Parameter, #dataType, #value = mac_attributes.values_at("Mac", "Parameter", "datatype", "value")
#versionResponse = client.set_req_api(#mac_address,#Parameter,#dataType,#value)
puts "versionResponse.status_code #{#versionResponse.code}"
end
if #versionResponse.code == "200"
value = JSON.parse(#versionResponse.body)
render json: {Mac_address: #mac_address,Response:value}, status: :ok
else
render json: {Mac_address: #mac_address,Parameter:#Parameter,status: 'Failed',responsecode:#versionResponse.code}, status: :ok
end
end
end
end

I'm not entirely sure what you're trying to do, but if you want the controller to call a method for each JSON object in the array, it would look something like:
def show
params["Data"].each do |hash_of_mac_attributes|
mac, parameter, datatype, value = hash_of_mac_attributes.values_at("Mac", "Parameter", "datatype", "value")
method_you_want_to_call(mac, parameter, datatype, value)
end
This is assuming that rails is correctly detecting and parsing the JSON body into the params hash for you. If it's not, that's a separate issue that can be addressed.
UPDATE
In order to return the result from each call to the external service we need to store each call in an array and render the array in the response. Something like the following:
def show
json_array = params["Data"].map do |hash_of_mac_attributes|
mac, parameter, datatype, value = hash_of_mac_attributes.values_at("Mac", "Parameter", "datatype", "value")
response = client.set_req_api(mac, parameter, datatype, value)
build_json_for_mac_lookup(response: response, mac: mac, parameter: parameter)
end
render json: json_array
end
def build_json_for_mac_lookup(arguments:, mac:, parameter:)
json_body = {Mac_address: mac}
if response.code == "200"
json_body.merge(Response: JSON.parse(response.body))
else
json_body.merge(Parameter: parameter, status: 'Failed', responsecode: response.code)
end
end

Related

Rails test that method is called from the controller

I have a controller function that calls a service and I want to test that the service is called with the right arguments.
def send_for_signature
client = Client.find(params[:client_id])
external_documents = params[:document_ids].map{|id| ExternalDocument.find(id)}
service = EsignGenieSendByTemplate.new(client: client, external_documents: external_documents, form_values: params[:form_values])
result = service.process
if result["result"] == "success"
head 200
else
render json: result["error_description"], status: :unprocessable_entity
end
end
How would I write my test to ensure that EsignGenieSendByTemplate.new(client: client, external_documents: external_documents, form_values: params[:form_values]) is called correctly?
I would start by adding a factory method to the service:
class EsignGenieSendByTemplate
# ...
def self.process(**kwargs)
new(**kwargs).process
end
end
This kind of code is boilerplate in almost any kind of service object and provides a better API between the service object and its consumers (like the controller).
This method should be covered by an example in your service spec.
describe '.process' do
let(:options) do
{ client: 'A', external_documents: 'B', form_values: 'C' }
end
it "forwards its arguments" do
expect(described_class).to recieve(:new).with(**options)
EsignGenieSendByTemplate.process(**options)
end
it "calls process on the instance" do
dbl = instance_double('EsignGenieSendByTemplate')
allow(described_class).to recieve(:new).and_return(dbl)
expect(dbl).to recieve(:process)
EsignGenieSendByTemplate.process(**options)
end
end
Your controller should just call the factory method instead of instanciating EsignGenieSendByTemplate:
def send_for_signature
client = Client.find(params[:client_id])
# Just pass an array to .find instead of looping - this create a single
# db query instead of n+1
external_documents = ExternalDocument.find(params[:document_ids])
result = EsignGenieSendByTemplate.process(
client: client,
external_documents: external_documents,
form_values: params[:form_values]
)
if result["result"] == "success"
head 200
else
render json: result["error_description"], status: :unprocessable_entity
end
end
This better API between the controller and service lets you set an expectation on the EsignGenieSendByTemplate class instead so you don't have to monkey around with expect_any_instance or stubbing the .new method.
it 'requests the signature' do
expect(EsignGenieSendByTemplate).to receive(:process).with(client: 'A', external_documents: 'B', form_values: 'C')
get :send_for_signature, params: { ... }
end
What you need is something called expecting messages.
I usually write something like this:
it 'requests the signature' do
expect(EsignGenieSendByTemplate).to receive(:new).with(client: 'A', external_documents: 'B', form_values: 'C')
get :send_for_signature, params: { ... }
expect(response.status).to have_http_status(:success)
end

How can I update serialized data in a Rails/MariaDB backend?

Currently, I have some QueuedImages that become GalleryImages once certain conditions are met.
#image = GalleryImage.new(image_params.except(:tags))
#image.user = current_user
#queued = QueuedImage.new(user: current_user)
#queued.image = #image
Before saving this method, transform the images into JSON:
def image_to_json
self.image_data = #image.to_json(include: :plain_images)
end
I'm trying to update the serialized hash/JSON like this:
def update
#queued = QueuedImage.find(params[:id])
hash_as_string = #queued.image_data
i_data = JSON.parse(hash_as_string.gsub('=>', ':'))
i_data["title"] = params["title"]
i_data["description"] = params["description"]
i_data["folder_id"] = params["folder_id"]
i_data["nsfw"] = params["nsfw"]
i_data["tags"] = params["tags"]
#queued.image_data = i_data
if #queued.save
render json: #queued, scope: current_user
else
render json: #queued.errors.as_json(full_messages: true), scope: current_user, status: :unprocessable_entity
end
end
Which throws me a parse error, what am I doing wrong?
How can I properly update the serialized string?
The thing is that when it transforms from a QueuedImage to a GalleryImage, another method takes that data and evaluates it and creates the new GalleryImage, so it has to be in the exact format, and I believe what I'm doing in the update method is wrong, even though I save it as a string in the DB.

Rails how to return single JSON object instead of array of JSON objects?

I am limiting to 1, so I thought it would simple return an object in this case the same as .find_by_email
Code:
# GET /users/:identified/type/:social_type
# Returns a single record - so limit 1
def find
#user = User.where("identified = ? AND social_type = ?", params[:identified], params[:social_type]).limit(1)
if not #user.empty?
render json: #user.as_json, status: :created
else
render json: #user, status: :not_found
end
end
Current Response:
[{"id":7,"voy_num":null,"voy_pin":null}]
How can ensure I return a single JSON object?
To get the single object, use first with where like this:
#user = User.where("identified = ? AND social_type = ?", params[:identified], params[:social_type]).first

Why Rails deserializes snake case data structures but serializes camel case data structures?

It is one of my first Ruby on Rails project and it is weird for me to send JSON with properties written in snake case on my requests and receive JSON with properties written in camel case on my responses.
Here is an example of request payload:
{
"end_point":"test"
}
And here is an example of response payload:
{
"endPoint":"test"
}
Here is the code that consumes and returns the alike data structures above:
def create
def create
#api = interactor.create(params[:organization_id], api_params)
respond_to do |format|
format.json { render :show, status: :created, location: api_url(#api) }
end
end
Use String#camelize method from ActiveSupport(which comes with Rails) like so:
attributes_hash = { "test_data" => "test" }
json_hash = Hash[attributes_hash.map {|k, v| [k.camelize(:lower), v] }]
#=> {"testData"=>"test"}
Now, you can convert it to JSON string:
p json_hash.to_json #=> "{\"testData\":\"test\"}"
UPDATE: You can override serializable_hash method in your model class(since you're not clear with what exactly interceptor is in your code, I assume you'd need to put this in the class whose object you're instantiating to send the data as JSON) like so -
def serializable_hash(options = nil)
options ||= {}
if options[:camelize]
Hash[attributes.map {|k, v| [k.camelize(:lower), v] }]
else
super
end
end
Now, you can do: #api.to_json(:camelize => true) and it'll convert attributes to camecase except the first character, i.e.: endPoint.

looping from database and no implicit conversion of Symbol into Integer

I encounter a strange problem when trying to alter values from a Hash. I have the following setup:
def index
data, total = Role.with_filtering(params, current_holding_company)
data.each do |total_user|
total_user = { total_user: RoleUser.where(role_id: data[:id]).group(:user_id).count.to_s }
data[:total_user] = total_user
end
respond_to do |format|
format.json { render json: { data: data, total_count: total }.to_json, status: 200 }
end
end
When I execute this code I get: "TypeError: no implicit conversion of Symbol into Integer" . What am I doing wrong?
data is output from database, so my goal is i want to add total_user in every record with add new key and value into data
This might not completely solves your issue but hopefully will guide you to the correct path.
In ruby the error TypeError: no implicit conversion of Symbol into Integer usually happens when you miss treat an array!
lets suppose you have
numbers = [1,2,3]
#now
numbers[0]
# > 1
numbers[2]
# > 3
#But
numbers[:1]
# throws TypeError: no implicit conversion of Symbol into Integer
So, you must be passing a symbol on an array [] operator by mistake
One way to debug this kind of issues is by checking class type of your objects, something like this in the consol
data.class
# > Array (for example)
try this....
def index
data, total = Role.with_filtering(params, current_holding_company)
data.each do |total_user|
total_user = { "total_user" => RoleUser.where(role_id: data[:id]).group(:user_id).count.to_s }
data[:total_user] = total_user
end
respond_to do |format|
format.json { render json: { data: data, total_count: total }.to_json, status: 200 }
end
end

Resources