Rails API carrierwave cannot upload file send from API client - ruby-on-rails

Im using unirest to create a client API in Node JS that will send a data to rails API. I have to change the file into base64 encoded string like this:
unirest.post('http://localhost:3000/api/v1/image_uploaders')
.headers({'Content-Type': 'multipart/form-data'})
.field({
"product_id": 12,
"variant_id": 1,
"image": fs.readFileSync(path).toString('base64')
}) // Form field
.end(function (response) {
console.log(response.body);
});
in Rails side this is what handle the request:
def create
variant = Variant.where(id: params[:variant_id]).first
if variant
product_image = ProductImage.new
product_image.image = StringIO.new(Base64.decode64(params[:image]))
product_image.product_id = params[:product_id]
product_image.variant_id = params[:variant_id]
if product_image.save
render json: true, status: :ok
else
render json: false, status: :bad_request
end
else
render json: false, status: :bad_request
end
end
I didn't get the file uploaded. Any idea? thanks.
UPDATE
I got this error message:
undefined method `unpack'
for #<ActionDispatch::Http::UploadedFile:0x007fe47ac26f80>
which point to this line:
product_image.image = StringIO.new(Base64.decode64(params[:image]))

You can use the following gem to use base64 encoded string with carrierwave:
https://github.com/lebedev-yury/carrierwave-base64
You would just need to change the mount_uploader to mount_base64_uploader in your ProductImage class, and then you can just assign the base64 encoded string to your image field:
product_image = ProductImage.new(params)

Solved it.
I change the:
"image": fs.readFileSync(path).toString('base64')
to:
"image": fs.createReadStream(path)
with that there is no need to decode the file. Thanks everyone

Related

Parse Json Request from CURL in 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

Paperclip Unpermitted parameter: image

I am trying to get Paperclip to upload an image to s3 from my festival model on form submit but am receiving the Unpermitted parameter: image. error
I have checked the strong params, the model content validation and read through the paperclip documents with no avail.
I think I have narrowed the problem down to my post request to the DB cannot handle the File object that gets assigned to festival.image, but can't figure out how I would represent this in the post request.
I am capturing the data in rails using react on rails on the front end with Rails as the backend. I was following along with this sample code https://github.com/carlbaron/react-file-upload-demo
I also use React-dropzone to capture the uploaded file and it adds the preview attribute for the image preview.
Been stuck on this for some time now, any help greatly appreciated!
Beginning of the post request printed to console
Processing by FestivalsController#create as JSON
Parameters: {"festival"=>{"fest_name"=>"Test Festival", "image"=>{"preview"=>"blob:http://localhost:5000/76b95cb5-45bf-46a9-ba7b-f5b9ad127521"}}}
| Unpermitted parameter: image
Festival object printed to the console
Post Request to the DB via axios
postFestival(festival) {
let config = {
responseType: 'json',
processData: false,
contentType: false,
headers: ReactOnRails.authenticityHeaders(),
};
let str = JSON.stringify(festival);
console.log("ENTITY IS " + str);
//returns
//ENTITY IS {"fest_name":"Test Festival","image":{"preview":"blob:http://localhost:5000/76b95cb5-45bf-46a9-ba7b-f5b9ad127521"}}
return(
request.post('/festivals/create', {festival}, config)
);
},
Festival.rb
class Festival < ApplicationRecord
has_attached_file :image, default_url: "/assets/ASOT-COVER.png"
validates_attachment :image,
content_type: { content_type: ["image/jpeg", "image/gif", "image/png"] }
end
Festivals Controller
def create
#festival = Festival.create(festival_params)
puts "festival.image =" + #festival.image.inspect
#returns = festival.image =#<Paperclip::Attachment:0x007fc288868bf0 #name=:image, #name_string="image", #instance=#
if #festival.save
puts "Festival SAved = + " + #festival.inspect
#returns the festival object saved to the DB minus the image param
else
respond_to do |format|
format.json { render json: #festival.errors, status: :unprocessable_entity}
puts "ERROR = " + #festival.errors.inspect
end
end
private
def festival_params
params.require(:festival).permit(:fest_name, :fest_organizer, :fest_location,
:fest_date, :fest_url, :fest_venue, :fest_description,
:image)
end
end
As the image parameter in your request is a hash "image"=>{"preview"=>"blob:http://localhost:5000/76b95cb5-45bf-46a9-ba7b-f5b9ad127521"}, you will need to modify your festival_params method like this:
def festival_params
params.require(:festival).permit(:fest_name, :fest_organizer, :fest_location,
:fest_date, :fest_url, :fest_venue, :fest_description,
{ image: :preview })
end
Let me know if it solves the error.

Rails Render JSON Object instead of entire array

In my controller I have the present code that results in a JSON array, even though there is only one result:
def getSharedSpecial
#result = Campaign.find_by_sql("SELECT
id
,name
,image
,ad_caption
,ad_details
FROM campaigns
WHERE id = " + params[:shared_campagin_id].to_s + "
LIMIT 1;")
respond_to do |format|
format.html
format.json { render json: { special_shared: #result }}
end
end
returns:
"special_shared":[
{
"id":41,
"name":"tester the way",
"image":{
"url":"/uploads/campaign/image/41/Gilded_pic.jpg"
},
"ad_caption":"yfftitu6",
"ad_details":"jku"
}
]
}
As can be seen given the [], this is a JSON array.
How can I create just an object and not an entire array?
The problem is that find_by_sql always returns an array even though you are only looking for a single record. There is no need to use find_by_sql and you've opened yourself to SQL injection attacks by doing so, so just write the finder the traditional way:
#result = Campaign.select(:id, :name, :image, :ad_caption, :ad_details).find(params[:shared_campagin_id])

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.

Geojson data format output properly in a rails app

i m trying to implement a service that displays data in geojson format.
I hit my database with this :
sql =
"select ST_AsGeoJSON(boundaries)
from cluster_shapes
where category = '#{params[:category]}'
and area_id =
(select id
from areas
where name = '#{params[:city]}' )"
connection = ActiveRecord::Base.connection
#clusters = ActiveRecord::Base.connection.execute(sql)
respond_to do |format|
format.json { render json: #clusters }
end
And I get ugly results that is not valid geojson
[
{
st_asgeojson: "{"type":"Polygon","coordinates":[[[23.819537,38.039409],[23.81892,38.04068],...}"
,
st_asgeojson: "{"type":"Polygon","coordinates":[[[23.919537,38.039409],[22.81892,38.04068],...}"
,
...
If I remove the ugly part st_asgeojson and make it look like this , it is valid geojson.
{"type":"Polygon","coordinates":[[[23.819537,38.039409],[23.81892,38.04068],[23.820912,38.040237],[23.8209752169298,38.0400047630785],[23.8209519774174,38.0399755885062],[23.8205266,38.0394558],[23.819537,38.039409]]]}
So the question is how do i get rid of "st_asgeojson" ,or am i doing it wrong ?
PS:A model exist, if query is modified the result is pretty the same.
It seems like what you want is simply nested inside your #clusters json, so you can simply parse it out...
instead of:
format.json { render json: #clusters }
you should change it to
format.json { render json: #clusters[0].st_asgeojson }
Or if you are expecting more than one result to be returned from you sql query, try
format.json { render json: #clusters.map {|x| x.st_asgeojson }}

Resources