Angular.js Rails Resource Association Update - ruby-on-rails

I am working on an angular app w/ rails using Angular Rails Resource. When a user updates a photo, I want to query an association and update an attribute to true.
I have read through the Angular Rails Resource docs but I don't see much info on querying associations. Would this be an angular thing, or a rails thing?
Here is my angular code:
$scope.uploadHeadshot = ->
Upload.upload(
url: window.apiUrl + '/personas/' + $routeParams.unique_code + '/' + $routeParams.slug
method: 'PUT'
data: persona: headshot_attributes:
image: $scope.headshot
crop_x: $scope.cropAttributes.cropImageLeft
crop_y: $scope.cropAttributes.cropImageTop
crop_w: $scope.cropAttributes.cropImageWidth
crop_h: $scope.cropAttributes.cropImageHeight
# right here
badge_attributes: completed: true).then ((response) ->
$mdDialog.hide response.data
resizeImage(false)
return
)
rails code
def update
#persona = Persona.friendly.find params[:id]
#persona.assign_attributes persona_params
return unprocessable_entity 'Invalid parameters', #persona.errors unless #persona.save
render json: #persona, include: %w(modules owner badges)
end
and in the persona_params I have the attrribute
badge_attributes: [:id, :completed]
I also have a attr on badge of badge_type so from the angular side I could get the current user, do a badge.where(badge_type: 'badge type').update(completed: true) but I'm not sure how to do that from angular. Or would this be a rails thing where you could do something like this from #update
if params[:badge_attributes]
Badge.where(badge_type: 'badge type').first.update(completed: true)
end
What I have posted doesn't do anything, but it doesnt break the photo upload from happening, and I can see the badge_attribute params being sent. Does anyone know how I could accomplish this?

I was able to get this solved by doing the heavy lifting with rails.
if params[:badge_attributes]
badge = Badge.where(persona_id: current_persona.id, badge_type: params[:badge_attributes][:badge_type]).first
badge.update_attributes(completed: true)
end

Related

rails 5 ForbiddenAttributesError on bulk operations

i try to bulk operation in my rails controller this is my script
def update_by_user
user_skill_selected = UserSkillSelected.create(params[:user_skill_selected][:users])
# check through array if all is valid
if user_skill_selected.all? {|item| item.valid?}
render json: {json_status: save_success}
else
render json: {json_status: save_failed}
end
end
and this is my user_skill_selected_params
def user_skill_selected_params
params.require(:user_skill_selected).permit(:user_id, :subskill_id, :skill_id, :users => [])
end
unfortunately i get an error in my log, the log said
"exception": "#<ActiveModel::ForbiddenAttributesError:ActiveModel::ForbiddenAttributesError>",
after that i try to bulk operations from rails console with using create method with the array value and its work
can anyone solve this... :(
sorry for the bad english
This can be confusing. Your code is passing in params[:user_skill_selected][:users] to the model create method, instead of your user_skill_selected_params strong parameters, which is why you're seeing that error.
Change this line:
user_skill_selected = UserSkillSelected.create(params[:user_skill_selected][:users])
To this:
user_skill_selected = UserSkillSelected.create(user_skill_selected_params)
And it should eliminate this error.

How to create Post request to Rails API using Postman?

I am new to Postman. I have a Rails server running on the background. I am trying to mock a POST request, but it is not being accepted.
Let's say the model is called manufacturer_organization.rb. Inside, it requires 3 parameters: organization_id (uuid data type), manufacturer_id (integer data type), and account_number (string data type). manufacturer_organization belongs_to organization and it also belongs_to :manufacturer (vice versa; manufacturer and organization has_many manufacturer_organization)
Inside manufacturer_organizations_controller.rb, I have a create method:
def create
#manufacturer_organization = ManufacturerOrganization.new(manufacturer_organization_params)
if #manufacturer_organization.save
puts "success!"
render json: #manufacturer_organization
else
puts "Sorry, something went wrong"
end
end
I can confirm that I have sufficient authorization; when I perform a GET request I got the right JSON response. I am using rails serializer and I have setup serializer for this model as well. Route is also setup using resources :manufacturer_organizations. My gut feeling says the way I am using postman is wrong.
Here is the screenshot of Postman app. I have the right address on address bar, and I am performing a POST request. I have the three params under key-value.
After I Send it, under my Rails Server log I see:
Started POST "/manufacturer_organizations" for 127.0.0.1 at 2017-04-13 16:56:44 -0700
Processing by ManufacturerOrganizationsController#create as */*
Parameters: {"organization_id"=>"fb20ddc9-a3ee-47c3-bdd2-f710541-ff89c", "manufacturer_id"=>"1", "account_number"=>"A rand
om account number test"}
...
  (0.4ms)  BEGIN
   (0.3ms)  ROLLBACK
Sorry, something went wrong
I can do ManufacturerOrganization.new(organization_id: Organization.last.id, manufacturer_id: Manufacturer.last.id, and account_number: "random test account number") just fine inside rails console.
How can I submit a POST request from postman to add a new manufacturer_organization?
Edit:
def manufacturer_organization_params
api_params.permit(:organization_id, :manufacturer_id, :account_number)
end
whereas inside application_controller.rb
def api_params
#api_params ||= ActionController::Parameters.new(ActiveModelSerializers::Deserialization.jsonapi_parse(params))
end
Edit2:
I added error.full_messages and this is what I got:
Manufacturer can't be blank
Organization can't be blank
Account number can't be blank
Why are they blank?
You can pass the data using params or within the body request.
The best way to do this is using the body, because you can send files and the request becomes more clean without the params.
To send data in the body, you must pass the model name and attribute in the "key" field, and the value in the "value" field, like this:
I don't understand what you do to your params. There is a reason the ActiveModelSerializers::Deserialization is namespaced in the "Model" namespace. It shouldn't be used to serialize or de-serialize internet params, but instead it's for serializing/de-serializing model instances.
If parameters arrive in the correct format ActionController::Base from which AplicationController and thus ManufacturerOrganizationsController inherit will de-serialize them for you. The Rails query parameter format looks as follows:
name=something #=> params[:name] = 'something'
names[]=something1&names[]=something2 #=> params[:names] = ['something1', 'something2']
instance[id]=1&instance[name]=foo #=> params[:instance] = {id: '1', name: 'foo'}
This can also be stacked and is used for nested resources by Rails. Example:
instance[title]=some&instance[nested][name]=thing&instance[nested][ids][]=1&instance[nested][ids][]=2
#=> params[:instance] = {title: 'some', nested: {name: 'thing', ids: ['1', '2']}}
Having said that let's get to your example. First of al let us throw away those manual building of params and stick to the convention:
class ManufacturerOrganizationsController
# ...
private
def manufacturer_organization_params
# arriving params should look like this:
#
#=> params = {
# manufacturer_organization: {
# organization_id: 'fb20ddc9-a3ee-47c3-bdd2-f710541-ff89c',
# organization_id: '1',
# account_number: 'A random account number test'
# }
# }
#
# The method #require raises an exception if the provided key
# is not present or has a blank value (with exception of false).
# If the key is found and has a value present than that value is
# returned.
#
params.require(:manufacturer_organization)
.permit(:organization_id, :manufacturer_id, :account_number)
end
end
With that out of the way let's send the correct formatted params:
+--------------------------------------------+---------------------------------------+
| Key | Value |
|--------------------------------------------|---------------------------------------|
| manufacturer_organization[organization_id] | fb20ddc9-a3ee-47c3-bdd2-f710541-ff89c |
| manufacturer_organization[manufacturer_id] | 1 |
| manufacturer_organization[account_number] | A random account number test |
+--------------------------------------------+---------------------------------------+
Those 2 things combined should let you create your resource successfully.
The key thing you should take from this is that params is not a string containing al the params that should be de-serialized. It already should be de-serialized, if it's not than you might have send your parameters wrong.
Ruby on Rails and Postman - Post request.
Hello, this is an example that I developed with Postman and Rails API.
Postman.
I can't add images but this what you have to add in postman Key = Value
Change to Post Request and send.
book[name] = 'Harry Potter'
book[author] = J.K. Rowling
Ruby on Rails 7.
Rails maintains the same code.
def create
#book = Book.new(book_params)
if #book.save
render json: #book, status: :created, location: api_v1_books_url(#book)
else
render json: #book.errors, status: :unprocessable_entity
end
end
def book_params
debugger
params.require(:book).permit(:name, :author, :price)
end
I hope this helps.

POST method/Create action works for some methods but not others, Feedzirra

Ok I am making a JQuery .post request to a server to insert some data. It only works sometimes depending on what methods are in the Rails create method.
Here are the specifics. I have a Rails app with Backbone.js on the frontend. In my frontend code I make this .post request
$.post('/publications');
Seems simple enough. I have a Publication model as well as
resources :publications
in the router. Now in my Publications controller I augmented the create method to the following:
def create
feed = Feedzirra::Feed.fetch_and_parse(publication_params[:url])
params = {:name => feed.title}
#publication = Publication.new(params)
respond_to do |format|
if #publication.save
format.html { redirect_to #publication, notice: 'Publication was successfully created.' }
format.json { render action: 'show', status: :created, location: #publication }
else
format.html { render action: 'new' }
format.json { render json: #publication.errors, status: :unprocessable_entity }
end
end
end
Feedzirra is a gem that parses RSS feeds. When I make the POST request like this I get a 500 (Internal Server Error) and this message from my server logs
NoMethodError (undefined method `title' for {}:Hash):
app/controllers/publications_controller.rb:28:in `create'
Rendered /Users/ericabt1/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/rescues/_source.erb (40.3ms)
Rendered /Users/ericabt1/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.1ms)
Rendered /Users/ericabt1/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (1.0ms)
Rendered /Users/ericabt1/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (54.7ms)
What is confusing me is that if I choose a different method than 'title' like 'entries' or 'nil?' the POST request works just fine. I know that 'title' is indeed a method because when I go into Rails console and create a test Feedzirra object and look at the various methods available I see that 'title' is one of them.
Why does my POST request work for some of these method but not others?!?!?!?!
* UPDATE ****
After taking krabbi and Alexander Zolotko's advice I started playing with what FeedZirra is returning. It looks like the line
feed = Feedzirra::Feed.fetch_and_parse(publication_params[:url])
is returning an empty hash.
Now when I run the same line in the rails console and hardcode a url in there it returns the proper hash and I am able to grab the title and other values. So it looks like the issue lies with
publication_params[:url]
Still working on this and open to suggestions:)
* UPDATE PART TWO *
I thought the problem was that there was no url column in the Publication Model. So I made the proper migration. So here is the schema:
create_table "publications", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.string "url"
end
Back to the controller I have at the bottom:
private
# Use callbacks to share common setup or constraints between actions.
def set_publication
#publication = Publication.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def publication_params
params.permit(:name, :url)
end
publication_params[:url] is still returning nil. I have also tried the line:
params.require(:publication).permit(:url, :name)
which just gives me 400 Bad request error
As Alexander Zolotko points out Feedzirra::Feed.fetch_and_parse seems to return a hash.
Try for example
params = { :name => feed[:title] }
Assuming frontend is correct and REST resource publications is properly set up in Rails backend.
Controller should for example look like:
def create
# requesting the feed from passed url
feed = Feedzirra::Feed.fetch_and_parse(publication_params[:url])
# mapping feed keys to publication attributes
feed_to_publication_hash = { :name => feed[:title], anything else }
# instantiating new publication
#publication = Publication.new(publication_params.merge(feed_to_publication_hash))
...
end
private
def publication_params
params.require(:publication).permit(:url, everything else you need)
end
I am quite sure that even if that works it is not good practice. Usually you strife for Object Orientation, that is in this case one purpose per action.
But I don't know right now how to refactor fetching the feed and perhaps mapping the hash. Maybe it is a controller concern or something like that.
After reviewing the Feedzirra::Feed.fetch_and_parse the only option I see is that your publication_params[:url] contains something other than String. In that case fetch_and_parse returns a Hash of results (even if only one url was passed). Could you please check whether publication_params[:url].is_a?(String) is true.

Ember PUT request using JSON is giving a weird error

(If the next is too much to read for you, please read the last line). I'm using Ember and Rails backend from a while, but I'm still a bit stick with transactions or what other way is used to send actions to the backend. I have Users and Events. My Users can participate in one or several Events, and for each Event several Users can attend. I'm maintaining this relationship using the Mongoid macro has_and_belongs_to_many in this way:
Event
# ...
has_and_belongs_to_many :attendings, class_name: "User" , inverse_of: :attendances
# ...
User
# ...
has_and_belongs_to_many :attendances, class_name: "Event" , inverse_of: :attendings
# ...
In my template I'm showing a list of all the Events stored in my backend and a button "Attend" if the User is not already attending, and "Not attend" it the User is attending. For attending, I've tried to make the PUT request using transactions, making the next steps in my Ember Event controller:
Event controller (Ember), actionAttending method using transactions:
actionAttending: function() {
var userId = this.get('controllers.app.model.id');
this.transaction = this.get('store').transaction();
this.get('attendings').pushObject(App.User.find(userId));
this.transaction.add(this.get('model'));
this.transaction.commit();
}
When I try this, my Event JSON request is containing all the parameters and relations but attendings attribute. So I decided to try using a jQuery request:
Event controller (Ember), actionAttending method using jQuery ajax:
// ...
var eId = this.get('id');
this.get('attendings').pushObject(App.User.find(userId));
var eAttendings = this.get('attendings');
var url = "/events/" + eId + ".json";
var data = { event: { id: eId, attendings: eAttendings } };
$.ajax({
type: "PUT",
url: url,
data: data,
dataType: "JSON"
});
// ...
Well, this is pretty much working, except for the line where I declare eAttendings, where Ember complains about init function is not called, or somehing similar. After googling a bit I found a "solution", which is convert to array, so the line changed like this:
var eAttendings = this.get('attendings').toArray();
My nice error now is:
TypeError: fullName is undefined
var nameParts = fullName.split(":"),
This is raised in Firebug before my ajax request is launched. I don't know what's the exact meaning or how to fix it...
Anyway, I wanted to try if my ajax request is working, so I tried it in curl:
curl --request PUT localhost:3000/events/521b97ef5ef9095ba211bf70 --data "id=521b97ef5ef9095ba211bf70&attendings=521b7eda99027121d1533015"
The answer is:
{"errors":{"attendings":["is invalid"]}}
And the bakend is returning a 422 Unproccesable entity answer... I've got no validations on my model on this field, so I don't know what's going on here either... My update action on Rails Event controller is like this:
def update
e = Event.find(params[:id])
u = User.find(params[:attendings])
if params[:attendings]
e.attendings << u
e.save
respond_with e, api_template: :general_event, status: :no_content
end end
Last detail: I'm using Ember 1.0.0.rc6, jQuery 1.10.2 and gems acts_as_api and active_model_serializers in Rails.
So, to summarize, I want to get working just my PUT request in Ember to add Users attending to an Event through the has_many_and_belongs_to macro (using transactions, jQuery, or something else)

Ember remove record on invalid server validation

I am trying to create a record and on the server side I am using rails. The rails validations are failing and I am returning a 422 status code but when I delete it in the becameInvalid callback, it doesn't get removed from the template. It just shows a blank entry.
When it is waiting for the server to load it is just showing the name, which is expected.
Ember Model code
App.Job = DS.Model.extend({
name : DS.attr("string"),
user : DS.belongsTo("App.User", {embedded : "load"}),
plans : DS.hasMany("App.Plan", {embedded : "load"}),
shares : DS.hasMany("App.Share", {embedded : "load"}),
becameInvalid : function(data){
this.deleteRecord();
}
});
Ember controller call
PlanSource.Job.createRecord({"name" : name});
job.save();
Rails create method
def create
if can? :create, Job
#job = Job.new(name: params["job"]["name"], user_id: current_user.id)
if !#job.save
render :json => {job: #job}, status: :unprocessable_entity
return
end
if
render :json => {:job => #job}, include: [:plans, :user, :shares => {except: :token, include: [:user, :job]}]
else
render_no_permission
end
else
render_no_permission
end
end
My question is what is the best way to handle server side validation errors. I don't want to try to resubmit the record, I just want to delete it. I was looking for something to make Ember wait for server response but found nothing.
This method isn't working because it causes undefined errors down the model pipeline after deleting.
My question is what is the best way to handle server side validation errors.
Not sure there is one best way. Depends on what you want to happen in your UI. Typically you will want to let the user know that the record was not saved and present some information what went wrong.
I don't want to try to resubmit the record, I just want to delete it.
OK. If the record is new, delete does not really make sense but probably what you want to do is rollback the transaction? Try this.transaction.rollback() or this.rollback(). For example:
App.Job = DS.Model.extend({
name : DS.attr("string"),
user : DS.belongsTo("App.User", {embedded : "load"}),
plans : DS.hasMany("App.Plan", {embedded : "load"}),
shares : DS.hasMany("App.Share", {embedded : "load"}),
becameInvalid : function(data){
this.transaction.rollback();
}
});
See: How to deleteRecord when it was never committed on the backend?
I was looking for something to make Ember wait for server response but found nothing.
model.save() returns a promise. That means you can add success/failure handlers like this:
PlanSource.Job.createRecord({"name" : name});
var success = function(model) {
alert('ok');
};
var failure = function(model) {
alert('fail');
};
job.save().then(success, failure);

Resources