Responding with "*ok*" to a json callback in rails - ruby-on-rails

I have been struggling with this issue for a while now. I am trying to interface with the Blockchain API using rails. The API notifies my app of a specific event by means of a callback and I have to answer to this callback by responding “*ok*”. I am having a hard time creating a valid JSON response that consists only of “*ok*”. I can send an empty string format.json { render :json => {} } or something like format.json { render :json => {:ok => “*ok*”} } but can’t figure out how to send just the required “*ok*”.
http://blockchain.info/api/api_receive
Expected Response
In order to acknowledge successful processing of the callback the server should respond with the text "ok". If the server responds with anything else the callback will be resent again every new block (approximately every 10 minutes) up to 1000 times (1 week).
Thank you!

"*ok*"
This is invalid JSON. If you need to respond with plaintext, don't send a JSON response.
format.text { render text: “*ok*” }
or if the server isn't making a text/plaintext GET request, just respond without a respond_to block.
render text: "*ok*" and return

Related

How to create async action in Ruby on Rails

I have a page that needs parameters received by a request from a third-party service. Unfortunately, the request takes a long time and the server crashes with a 504 error.
def show
start_time = Time.now
#project = Project.find(params[:id])
file = File.new(project.rvt_schema, 'rb')
rvt_params = ForgeHandler.instance.get_urn_token_params(file, "#{#project.id.to_s}.rvt")
#urn = rvt_params[:urn]
#token = rvt_params[:token]
end_time = Time.now
end
The most time inside the method is taken by request:
# Translate previously uploaded file to SVF format
def translate_to_svf(object_id,access_token)
base_64_urn = Base64.strict_encode64(object_id)
response = RestClient.post("#{API_URL}/modelderivative/v2/designdata/job",
{
input: {
urn: base_64_urn
},
output: {
formats: [
{
type: "svf",
views: [
"3d"
]
}
]
}
}.to_json,
{ Authorization: "Bearer #{access_token}", content_type:'application/json' })
return response
end
Which status is checked in cycle by another method:
def verify_job_complete(base_64_urn,access_token)
is_complete = false
while(!is_complete)
response = RestClient.get("#{API_URL}/modelderivative/v2/designdata/#{base_64_urn}/manifest",
{ Authorization: "Bearer #{access_token}"} )
json = JSON.parse(response.body)
if(json["progress"]=="complete")
is_complete = true
puts("***** Finished translating your file to SVF - status: #{json['status']}, progress: #{json['progress']} ")
else
puts("***** Haven't finished translating your file to SVF - status: #{json['status']}, progress: #{json['progress']} ")
sleep 5
end
end
I would like to implement asynchronous parameter loading. So I want to load data after losing control of the controller but but initializing the beginning of data loading from remote request in it. Tell me how best to implement this.
Or another way that would remove the error "Gateway timeout".
While this might be more of a question for the ruby-on-rails community, let me answer from the Autodesk Forge standpoint:
First of all, you should never wait for the Model Derivative job to complete when handling a request to your server. If the design file is complex enough, the translation could take up to hours, so this should definitely be handled asynchronously.
One option is to poll the status of the translation by requesting "derivative manifest" using the GET :urn/manifest endpoint.
Another option is to setup a Forge Webhook to get notified when the extraction.finished event is triggered.
It's probably easier to offload asynchronous stuff to a worker and save a reference to the user that needs to know about it. If you couple it with something like StimulusReflex you can render the result once it's finished. Another option might be the Render Async gem.

How to return error messages in rails

I'm learning rails and confused about some basics. Here is my API method:
def itunes_app_create
begin
app = Spaceship::Tunes::Application.create!(name: params[:itunes_app_name],
primary_language: "English",
version: params[:itunes_app_version],
sku: params[:itunes_app_sku],
bundle_id: params[:itunes_app_bundle_id],
team_id: params[:itunes_team_id])
render json: app.to_json, status: 200
rescue
render json: app.errors.full_messages.to_json, status: 200
end
end
My app.errors.full_messages.to_json line fails because well, I just made that up from something I saw. How do I return a message of what caused the method to fail?
Not sure if it matters, app is not part of my model. I just need to call this from my client app and then send back the result.
As a side question, what status should I return with the error in this case?
You should return the errors which occurred (if any) while creating the object. Your object is an instance of Spaceship::Tunes::Application, so you should be searching for whether this class defines any instance methods which return validation errors.
I am not familiar with this class, but after a quick look into it's documentation I do not see that it even has any validations in create method.
So you can just return a custom error message if by any chance the object creation failed.
As to
what status should I return with the error in this case?
Take a look into the list of status codes and pick suitable one (I think 400 (Bad Request) would do).
You could do something like this
def itunes_app_create
begin
app = Spaceship::Tunes::Application.create!(name: params[:itunes_app_name],
primary_language: "English",
version: params[:itunes_app_version],
sku: params[:itunes_app_sku],
bundle_id: params[:itunes_app_bundle_id],
team_id: params[:itunes_team_id])
render json: app.to_json, status: 200
rescue => e
render json: {error: e, status: 500}.to_json
end
end
But if you are building out a full api you might want to come up with your own error codes and a convention on when you will use which of the http errors codes that Andrey so politely included. Also its not a good practice to just blindly catch all error types because it is behavior concealing and it will generalize the behavior of your app in difference scenarios. In my example since you are return the error message it give you a little bit a visibility.
any time you are urged to write something like this
rescue => e
write something like this instead
rescue SyntaxError => e
Be very deliberate with your error handling, see this question for why

Redirect and then render

Okay, so real quick, I am using a file upload plugin http://plugins.krajee.com/file-input to upload my images. The plugin expects some sort of response from the server, and i want to send back an empty json object.
But when the images are uploaded, I also need to redirect immediately to another place so people can sort of make changes to the order.
Rails says I can't use render and redirect, but says i can redirect and return.
How do i redirect and return the empty json object??
def create
if !params[:images].nil?
package = Package.first
#photos = Array.new
#order = current_user.orders.new
#order.save
#order.order_items.each{|d| d.delete} #Stupid hack to prevent creation of fake order items. Don't know what is causing this yet
params["images"].each do |i|
photo = current_user.photos.create
photo.write(i.original_filename, i.read)
photo.save
#order.order_items.create(photo_id: photo.id, size_id: package.size_id, material_id: package.material_id)
end
redirect_to edit_order_path(#order) and return
else
flash[:danger] = "Please select at least one photo to upload"
redirect_to upload_photos_path
end
end
If the upload plugin you're using is expecting a JSON response and you would like to redirect after a successful upload, then you'll need to do it client side.
If you're not using Rails 4 or Turbolinks, you can simply redirect via window.location.replace. From your Rails code it looks like you're batch uploading in which case you'll want to assign a callback to the filebatchuploadsuccess event as per the docs
Example:
$('#fileinputid').on('filebatchuploadsuccess', function(event, data, previewId, index) {
// files have been successfully uploaded, redirect
window.location.replace( '/your_path_here' );
});
If you are using Turbolinks, the above code will be exactly the same except that instead of window.location.replace, you can use Turbolinks.visit
Example:
$('#fileinputid').on('filebatchuploadsuccess', function(event, data, previewId, index) {
// files have been successfully uploaded, redirect
Turbolinks.visit( '/your_path_here' );
});

Why does my Net::HTTP.post_form timeout?

In my rails app controller I am posting to the api of the app on the same machine. I have build this out to handle the posting the data to the url:
url = "http://172.16.155.165:3000/api/jobs"
params = {
:input => "original/video.h264",
:output => "new/video.mp4",
:preset => 'h264'
}
jobResults = Net::HTTP.post_form(URI.parse(url), params)
This works great when I run this code through rails console but when I use it in my controller it gives me this error after loading for a minute or so:
Timeout::Error in SeminarsController#create
Timeout::Error
Once the timeout happens the data is actually posted and the api does what it should. It is like it is hanging until it times out then posts the data. The controller never goes beyond this step though. It should write the response body to a file with jobResults.body which would work fine if it didn't time out. If I write this into rails console it outputs the response immediately. The api will never take a whole minute to respond.
Am I doing something to cause this to happen? How can I make it work right?
edit:
This is the code for create in app/controllers/api/jobs_controller.rb:
def create
job = Job.from_api(params, :callback_url => lambda { |job| api_job_url(job) })
if job.valid?
response.headers["X-State-Changes-Location"] = api_state_changes_url(job)
response.headers["X-Notifications-Location"] = api_notifications_url(job)
respond_with job, :location => api_job_url(job) do |format|
format.html { redirect_to jobs_path }
end
else
respond_with job do |format|
format.html { #job = job; render "/jobs/new"}
end
end
end
Yes. Ideally you should remove the long running process (yes this is long running process) into background job. Remember that when many users start updating the videos, this process will show down for many reasons (like bandwidth, API acceptance rate etc). Rake::Timeout always pops out if the process passes the threshold. It is actually designed to abort requests that are taking too long to respond. And, it is not raised in console.
How can I make it work right?
Move it to the background job. Or you can explictly increase the rake timeout interval by doing something like this
# config/initializers/timeout.rb
Rack::Timeout.timeout = 30 # seconds
But i suggest not to do this. This rake-timeout helps in debugging. Mainly people use in heroku with newrelic.

Looking for paradigm to use for generic error handling in Angular from a JSON response from Rails

I'm building an app which is architected as a Rails server app providing RESTful api's to the client. The Rails server uses RABL. The client is an Angular JS client performing standard $http calls (gets, puts, etc).
Occasionally my Rails server will produce an error (let's say validation error attached to the object) or even no error in which case I would want to display something to the user - either the errror e.g., "The record did not save because..." or "The record was updated successfully".
I'm trying to map out a pattern on both the Rails side and the Angular/client side to handle this.
As for Rails:
I can certainly pass back a node in each of my RABL files to contain error arrays
I can also return different RABL by checking in the controller before returning
Most suggest using http codes (which makes sense) as per here (although there doesn't seem to be a consistent usages of the codes for something like a validation error).
As for Angular:
I suppose I can write a response interceptor but not sure how that would fully get flushed out.
I guess I'm hoping that I don't have to reinvent the wheel here and someone can point me to a pattern that's currently used and suggested (and localized).
I went ahead and implemented what I thought needed to be done. Thanks for digger69 for some help with this.
On the Rails side, I went with using an http status code. As per here I agreed with using a 400 http status code for error validation.
In my controllers I now have something like the following:
def create
my_obj = MyObj.build_with_params(params)
if my_obj.save
respond_with(my_obj) # regular RABL response
else
respond_with_errors(my_obj.errors)
end
end
In my application_controller.rb I defined a common method respond_with_errors
# respond back to the client an http 400 status plus the errors array
def respond_with_errors(errors)
render :json => {:errors => errors}, :status => :bad_request
end
Note that the :bad_request symbol is already defined for Rails as per here
On the client side I needed to intercept http calls (not only for validation but for authentication failures too (and probably more). Here is an example of my code in Angular (thanks to this post for the help with that):
var interceptor = ['$rootScope', '$q', function (scope, $q) {
function success(response) {
return response;
}
function error(response) {
var status = response.status;
if (status == 401) { // unauthorized - redirect to login again
window.location = "/";
} else if (status == 400) { // validation error display errors
alert(JSON.stringify(response.data.errors)); // here really we need to format this but just showing as alert.
} else {
// otherwise reject other status codes
return $q.reject(response);
}
}
return function (promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(interceptor);
I now can be consistent with my rails code and deal with success returns from http calls on the client. I'm sure I have some more to do, but I think this gives a localized solution.
Use an HTTP response interceptor. I am currently using that successfully in an application.
http://docs.angularjs.org/api/ng.$http
From the documentation:
$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
return function(promise) {
return promise.then(function(response) {
// do something on success
}, function(response) {
// do something on error
if (canRecover(response)) {
return responseOrNewPromise
}
return $q.reject(response);
});
}
});
$httpProvider.responseInterceptors.push('myHttpInterceptor');
In my case I created a feedback service, which displays either success or error messages globally. An other option would be to broadcast the responses on the rootscope.

Resources