Rails JSON API tests don't return json when exception is thrown - ruby-on-rails

I'm writing a test to check for invalid DELETE requests to a Rails API using Rspec.
This is what I have:
context 'invalid id number' do
it 'returns success: false' do
xhr :delete, :destroy, id: 999999999999999999
expect(JSON.parse(response.body)['success']).to be_false
end
end
Postgres throws some kind of integer overflow exception (as it should), but in my spec I can't look at the JSON object because it's never formed. How can I make it return { success : false } instead of a blank string? How do I force the JSON object to return despite the exception?
When I use pry to look at the json object, I get this error: JSON::ParserError: A JSON text must at least contain two octets! because response.body evaluates to the empty string ""
Whoa, almost forgot to include the controller code.
def destroy
if (site == ::MyModel.find(params[:id]))
site.destroy
render :json => {success: true}
else
render :json => {success: false}
end

There are two issues here:
Depending on your database an id of "999999999999999999" is probably outside of an integer type. I recommend reducing it to below signed integer limit, like 9999.
You are trying to find a non-existent record and its raising a record_not_found exception. I recommend changing your destroy method to:
def destroy
site = ::MyModel.find_by(id:params[:id])
if (site.present?)
render :json => {success: false}
else
site.first.destroy
render :json => {success: true}
end
end
EDIT
#rafb3 is correct find_by and present? is a better choice.

By the sounds of it you will need some kind of rescue statement for the exception:
rescue ArithmeticException => ex
# We need to complete the contract and return json here
#response = { success: false }
end
If you want to learn more about contracts check out this link
Remember to stay away from returning objects in failure responses, as if you send back something like site in this case and site does not exist or the database connection is not there your exception response code may have its own exception!
Also try and stay away from rescue Exception => e explanation here: Why is it a bad style to `rescue Exception => e` in Ruby?
TLDR: Your request always expects a json response so in all places even failure it should return one.

Related

Couldn't find Post with 'id'=params[:id]

If I don’t have row with id=params[:id] how can i check it, since
when I write
def show
#post=Post.find(params[:id])
if #post.nil?
#post={
title:"No such post",
post:"No such post"
}
end
end
I get error.
From the fine manual:
find(*args)
Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]). If no record can be found for all of the listed ids, then RecordNotFound will be raised.
So if find can't find anything with the id you're looking for, it raises an ActiveRecord::RecordNotFound exception rather than return nil like you want it to. That exception ends up being handled deep inside Rails and gets converted to a 404.
You could trap that exception yourself:
def show
#post = Post.find(params[:id])
rescue ActiveRecord::RecordNotFound
#post = {
title: "No such post",
post: "No such post"
}
end
Note that you'd only trap the specific exception you're expecting to see, a bare rescue is almost always a mistake because it can hide bugs.
You could also use find_by:
find_by(*args)
[...]
If no record is found, returns nil.
like this:
def show
#post = Post.find_by(:id => params[:id])
if #post.nil?
#post = {
title: "No such post",
post: "No such post"
}
end
end
Exceptions are meant for handling errors and other exceptional conditions, they're not meant to be used for normal flow control. I'd probably use find_by for this sort of thing; it seems that you're expecting the occasional missing record so it a missing record isn't really an error or an unexpected condition.
show controller is expected to show existing elements only. When an element (Post instance) does not exist, find throws an exception. As #Michal suggested in the comments, usually non-existing entities are being handled with 404 response, or like.
For the time, being, though, you might cheat Rails with:
#post = Post.find(params[:id]) rescue {
title: "No such post",
post: "No such post"
}
This is not a production solution, of course, but it might help during learning phase.

Printing error when using PARAMS in Rails

For my API in RAILS I have programmed a code that basically does the following.
class Api::V1::NameController < ApplicationController
skip_before_filter :verify_authenticity_token
def index
end
def create
# Loading data
data_1_W = params[:data1]
data_2_W = params[:data2]
while len > i
# -Here I do some calculations with data_1_W and data_2_W.
# Its not important to show the code here
end
# -Organizing outputs to obtain only one JSON-
# Its not important to show the code here
# Finally HTTP responses
if check_error == 1
render status: 200, json: {
message: "Succesful data calculation",
data_output: response_hash
}.to_json
end
end
end
To test that everything is working I use the cURL command. I notice that loading the data could be a problem and therefore the code would break.
I want to tell the user that it was an error loading the data for some reason (HTTP response), but I don't know where to put it. If I put and else under my success status it would not print it because the code breaks just starting (instead of sending the correct name - d '#data.json' of the data in cURL I send -d '#dat.json').
The data I am loading is a JSON data {"data1":[{"name1":"value1"},{"name2":number2}...],"data2":[{"name1":"value1"},{"name2":number2...}]}. (This data has 70080 rows with 2 columns if we see it as a table, which I divided into two in my CODE for calculations purposes data_1_W and data_2_W)
Could anyone help me where to put it? more or less like this:
render status: 500, json: {
message: "Error loading the data",
}.to_json
Put it in a rescue block around the code that throws the error.
E.g.
def func
# code that raises exception
rescue SomeException => e
# render 422
end
Since you are working in Rails I'd recommend going the rails way. This means that I would create some kind of service and initialize it in the create action.
Now, within the service you do all you funky stuff (which also allows you to clean this controller and make i look prettier) and the moment a condition is not fulfilled in that service return false. So...
# controllers/api/v1/name_controller.rb
...
def create
meaningful_variable_name = YourFunkyService.new(args)
if meaningful_variable_name.perform # since you are in create then I assume you're creating some kind of resource
#do something
else
render json: {
error: "Your error",
status: error_code, # I don't think you want to return 500. Since you're the one handling it
}
end
end
# services/api/v1/your_funky_service.rb
class Api::V1::YourFunkyService
def initiliaze(params)
#params = params
end
def perfom #call it save if you wish
....
return false if check_error == 1
end
end

Rails 4 custom json errors

I am using Rails 4 i am trying to set an api and i have a services controller where i def some methods like this:
def articles_stores
#article = Store.find(params[:id])
if #article.nil?
render :json => {:error_msg => "Record not found",:error_code => 404,:success => false}
else
render json: {article: #article.as_json({except: [:updated_at,:created_at]}),success: true}
end
end
But for some reason it is not rendering the error the else part works fine y also have all the necessary routes
Any help will be appreciated
#article.nil? will never be true if the article does not exist: Store.find(params[:id]) will already raise an exception if the record does not exist, and this than gets handled by rails automatically as a 404. If you want to return nil, use something like this:
Store.where(id: 10).first
# old, deprecated way:
Store.find_by_id(10)
Also see here.

Rails: validation error codes in JSON

So, I am writing Rails web application which has JSON API for mobile apps. For example, it sends POST JSON request to example.com/api/orders to create order.
{id: 1, order: { product_name: "Pizza", price: 10000}}
In case of validation errors I can response with HTTP 422 error code and order.errors.full_messages in json. But it seems better for me to have specific error code in JSON response. Unfortunately, it seems like Rails does not provide ability to set error code for validation error. How to solve this problem?
You can pass a custom status code by using the status option when rendering the response.
def create
#order = ...
if #order.save
render json: #order
else
render json: { message: "Validation failed", errors: #order.errors }, status: 400
end
end
I usually tend to return HTTP 400 on validation errors. The message is a readable status response, the errors are also attached.
This is a respons example
{
message: "Validation failed",
errors: [
...
]
}
You can also embed additional attributes.
I was after something similar, so what I did was extend String eg
class ErrorCodeString < String
def init(value, error_code)
#error_code = error_code
super(value)
end
def error_code
#error_code
end
end
Then in a custom validation (this won't work on standard validation) I'd do
errors.add(:email, ErrorCodeString.new('cannot be blank', 50)
Now when you return your JSON you can check to see if the error value is an ErrorCodeString and add the error_code value to the output. As ErrorString inherits String, you shouldn't be breaking anything else along the way.
Rails 5 has error.details that can be used for exactly that.
In the model
errors.add(:price, 1023, message: "Transaction value #{price} is above limit (#{maximum_price}).")
In the controller
format.json { render json: #order.errors.details, status: :unprocessable_entity }
error details can be anything, eg. you could also use :above_limit instead of 1023.
The API response body will then look like
pp JSON.parse(response)
{"price"=>[{"error"=>1023}]}
This feature has been backported to Rails 4, see also http://blog.bigbinary.com/2016/05/03/rails-5-adds-a-way-to-get-information-about-types-of-failed-validations.html
Also: Is there a way to return error code in addition to error message in rails active record validation?

Rails Parse Active record validation error

I have the following in my controller:
def create
equipment = Equipment.create(:name => params[:name])
errors = equipment.errors.messages
puts "equipment errors: " + errors.inspect
respond_to do |format|
format.json { render :json => #equipment }
end
end
The response from the 'puts' is:
equipment errors: {:name=>["has already been taken"]}
How do I parse the error and get the message?
Thanks
equipment.errors.full_messages.join(", ")
This should give you "Name has already been taken". It concatenates all the errors.
Its just a hash. Access the message with
errors[:name].first
This gets the value of the :name key from the hash, which is an array with one element, and then returns the value of the first element of the array, which is the error message.
With more errors, use Array functions to access all of them and display them appropriately.

Resources