I have implemented my own object creation logic by overriding the create action in a JSONAPI::ResourceController controller.
After successful creation, I want to render the created object representation.
How to render this automatically generated JSON API response, using the jsonapi-resources gem?
Calling the super method does also trigger the default resource creation logic, so this does not work out for me.
class Api::V1::TransactionsController < JSONAPI::ResourceController
def create
#transaction = Transaction.create_from_api_request(request.headers, params)
# render automatic generated JSON API response (object representation)
end
end
You could do something like this:
class UsersController < JSONAPI::ResourceController
def create
user = create_user_from(request_params)
render json: serialize_user(user)
end
def serialize_user(user)
JSONAPI::ResourceSerializer
.new(UserResource)
.serialize_to_hash(UserResource.new(user, nil))
end
end
this way you will get a json response that is compliant with Jsonapi standards
render json: JSON.pretty_generate( JSON.parse #transaction )
def render_json
result =
begin
block_given? ? { success: true, data: yield } : { success: true }
rescue => e
json_error_response(e)
end
render json: result.to_json
end
def json_error_response(e)
Rails.logger.error(e.message)
response = { success: false, errors: e.message }
render json: response.to_json
end
render_json { values }
Related
I've got Rails 5 app with dry-monads on board. Monads are used to create the Appointment object inside create action in AppointmentsController. They return Success or Failure in the last step with below structure:
# services/appointments/create.rb
(...)
def call
Success(appointment_params: appointment_params)
(...)
.bind(method(:save_appointment))
end
private
def save_appointment(appointment)
if appointment.save
Success(appointment)
else
Failure(failure_appointments: appointment, appointments_errors: appointment.errors.full_messages)
end
end
After each action (success or failure) I want to send an email and display the corresponding json in AppointmentsController:
class Api::AppointmentsController < ApplicationController
def create
succeeded_appointments = []
failure_appointments = []
appointments_errors = []
batch_create_appointments_params[:_json].each do |appointment_params|
appointment = ::Appointments::Create.new(appointment_params).call
if appointment.success?
succeeded_appointments << appointment.value!
else
failure_appointments << appointment.failure[:failure_appointments] &&
appointments_errors << appointment.failure[:appointments_errors]
end
end
if failure_appointments.any?
AppointmentMailer.failed_mail(email, failure_appointments.size, appointments_errors).deliver_now
render json: {
error: appointments_errors.join(', '),
}, status: :bad_request
elsif succeeded_appointments.any?
AppointmentMailer.success_mail(email, succeeded_appointments.size).deliver_now
render json: {
success: succeeded_appointments.map do |appointment|
appointment.as_json(include: %i[car customer work_orders])
end,
}
end
end
I wonder if there is a better, faster way to record these errors than declaring 3 different empty arrays (succeeded_appointments, failure_appointments, appointments_errors) like at the beginning of create action? so far the create action looks heavy.
Create a separate service object for bulk creation:
# services/appointments/bulk_create.rb
class Appointments::BulkCreate
def initialize(bulk_params)
#bulk_params = bulk_params
end
def call
if failed_results.any?
AppointmentMailer.failed_mail(email, failed_results_errors.size, failed_results_errors).deliver_now
Failure(failed_results_errors.join(', '))
else
AppointmentMailer.success_mail(email, success_appointments.size).deliver_now
Success(success_appointments)
end
end
private
attr_reader :bulk_params
def failed_results
results.select(&:failure?)
end
def success_results
results.select(&:success?)
end
def success_appointments
#success_appointments ||= success_results.map do |appointment|
appointment.as_json(include: %i[car customer work_orders])
end
end
def failed_results_errors
#failed_results_errors ||= failed_results.map do |failed_result|
failed_result.failure[:appointments_errors]
end
end
def results
#results ||= bulk_params.map do |appointment_params|
::Appointments::Create.new(appointment_params).call
end
end
end
Then your controller will look like this:
class Api::AppointmentsController < ApplicationController
def create
result = ::Appointments::BulkCreate.new(batch_create_appointments_params[:_json]).call
if result.success?
render json: { success: result.value! }, status: :ok
else
render json: { error: result.failure }, status: :bad_request
end
end
end
I have the following controller:
class Api::V1::FeedbacksController < ApplicationController
before_action :authenticate_user!
def create
#feedback = current_user.feedbacks.create(
feedback_type: params[:selectedType],
message: params[:message]
)
json_response(#feedback)
end
private
def json_response(object, status = :ok)
render json: object, status: status
end
end
Feedback.rb
validates :message, presence: true, length: { in: 1..1000 }
This works great when message is between 1 to 1000 in length. If the controller is submitted more than 1000 characters, the controller is still respond back but without the error.
What is the right way in Rails 5 to have the controller return an error if the create method above fails?
The usual rails way is to test the return value of .save:
def create
#feedback = current_user.feedbacks.new(
feedback_type: params[:selectedType],
message: params[:message]
)
if #feedback.save
json_response(#feedback)
else
json_response(#feedback.errors, :some_other_status)
# you could also send #feedback directly and then in your JSON response handler
# to test if the json contains values in the object.errors array
end
end
private
def json_response(object, status = :ok)
render json: object, status: status
end
You can use this doc to find the right statuts code to return https://cloud.google.com/storage/docs/json_api/v1/status-codes
Hey everyone I am having an issue setting up my app. It uses the shopify API and essentially what it does is grab some data via a view and sends it to the controller but I am having issues passing it to another method in the controller to use the API to save the data.
Here is my code :
Controller
class BuilderController < ShopifyApp::AuthenticatedController
def index
urlval = request.fullpath
#urlCheck = urlval.split('/').last
end
def show
url = request.fullpath
#urlID = url.split('/').last
#customers = ShopifyAPI::Customer.search(query: "id:"+ #urlID)
#need to get a way to retrieve the ajax call info here to pass into the update
end
def updateCustomer(notes)
#customers.each do |cus|
cus.note = notes
cus.save()
end
end
def new
notes = params[:notes]
updateCustomer(notes)
render json: notes
end
end
View
<button id="test">TEST</button>
<script>
var butt = document.getElementById('test');
butt.addEventListener("click",function(){
$.ajax({
url: "/builder/new",
type: "GET",
data: {
"notes": [
"test",
"test2"
]
},
success: function(data,text,xhr) {
console.log(text);
console.log(xhr);
console.log(data);
alert('successfully');
},
error: function(data,error){
console.log(data);
console.log(error);
alert("help");
}
});
});
</script>
Rather than a fully separate method, have you looked into the
respond_to method? http://api.rubyonrails.org/classes/ActionController/MimeResponds.html#method-i-respond_to
You could do (assuming html is the primary request type, change if it isn't):
def index
respond_to do |format|
format.html { actions }
format.json { actions }
end
end
This the method we use to accommodate different request types within the same action. Please let me know if I've misinterpreted your question.
you can use this
update_all(updates) public
Updates all records with details given if
they match a set of conditions supplied, limits and order can also be
supplied. This method constructs a single SQL UPDATE statement and
sends it straight to the database. It does not instantiate the
involved models and it does not trigger Active Record callbacks or
validations.
http://apidock.com/rails/v4.0.2/ActiveRecord/Relation/update_all
def new
notes = params[:notes]
#customer.update_all({note: notes})
respond_to do |format|
format.html {}
format.json { json: #customer.json }
end
end
I have this method in my controller:
# GET /bios/1
# GET /bios/1.json
def show
if member_session?
#member = MemberPresenter.new(#bio.member)
# I need something here to add a flag to the json response to signal this is a member session.
else
#member = MemberPresenter.new(#bio.member)
end
end
I need to modify the json response to return something like:
{ member: #member, member_session: true }
Thanks in advance!
You can use json param for render functions:
render json: { member: #member, member_session: true }
But it's not the best way to render JSON in rails. I'd recommend you try to use https://github.com/rails-api/active_model_serializers
I'm not sure if you specifically want to return json all the time but here's an alternative to rendering other formats as well:
respond_to do |format|
format.html
format.json { render json: { member: #member, flag: #member.status } }
end
For small and simple objects, doing this is fine, but if you had to drag the associations along, you have the choice of using a serializer, or you could override the to_json method to something like this.
# member.rb
def as_json(options = {})
options = options.merge(
except: [
:updated_at,
:created_at,
],
include: { # Getting associations here
address: {
only: [:street, :zip_code],
include: {
neighbors: { only: :name }
}
}
}
)
super.as_json(options)
end
And finally within the controller, render json: #member.to_json and it will pull all the associations you want with it. This is the lazy man's way of serializing aka what I do :)
I'm using active model serializer for my API to serialize data models.
class Api::V1::UsersController < Api::V1::ApiController
include ::ActionController::Serialization
def create
user = User.new(user_params)
if user.save
return render json: user, status: :ok, root: :data
end
render_error(user.errors)
end
private
def user_params
params.require(:user).permit(:email, :password)
end
def render_error(errors, status = :unprocessable_entity)
meta = { count: errors.messages.count }
render json: errors, status: status, meta: meta, root: :data
end
end
When user parameters are valid and it is saved to DB, the API returns with data as root. For example:
{
"data": {
"id": 11
}
}
However, when parameters are not valid and the user object is not saved to DB, it returns without data as root. Example:
{
"email": [
"has already been taken"
]
}
I'm not sure what I'm missing, but I just want the API to return data as root for also failed scenario. Btw, the user serializer only includes id attribute.
You can specify it in the JSON:
def render_error(errors, status = :unprocessable_entity)
meta = { count: errors.messages.count }
render json: { data: errors }, status: status, meta: meta
end