Ember - Cli , Rails API JSON API FORMAT , AMS 0.10.0 rc Not Working


#1

I am stuck with the JSON API , AMS, RAILS combination…

I covered ember with some books like “Rock and Roll with ember js” , "Ember-cli -101" But here is a scenario where i need to put some has_many / belongs_to relationship with some data…

In My Ember Application

models/customer.js

export default DS.Model.extend({
  name: DS.attr('string'),
  contactname: DS.attr('string'),
  contactno: DS.attr('string'),
  othcontactno: DS.attr('string'),
  othrefdetails: DS.attr('string'),
  projects: DS.hasMany('project'),
});

models/project.js

export default DS.Model.extend({
  name: DS.attr('string'),
  customer: DS.belongsTo('customer' , {async:true}),
  agent: DS.belongsTo('agent' , {async:true})
});

adapters/application.js

export default DS.JSONAPIAdapter.extend({
  host: 'http://localhost:3000',
  shouldReloadRecord(store, snapshot) {
    return false;
  },
});

serializers/applicaiton.js

 export default DS.JSONAPISerializer.extend({
 });

And in my Rails api

Rails.application.routes.draw do
  resources :enquiries
  resources :agents
  resources :customers
  resources :projects
end

class Project < ActiveRecord::Base
   belongs_to :customer 
   #belongs_to :agent
   #has_many :enquiries
   accepts_nested_attributes_for :customer
end


class Customer < ActiveRecord::Base
   has_many :projects
   accepts_nested_attributes_for :projects
end



class ProjectSerializer < ActiveModel::Serializer
  attributes :id, :name
   belongs_to :customer
   #has_one :agent
   #has_many :enquiries
end


class CustomerSerializer < ActiveModel::Serializer
  attributes :id, :name, :contactname, :contactno, :othcontactno, :othrefdetails
    has_many :projects
end

initializers/json_api.rb

ActiveModel::Serializer.config.adapter = ActiveModel::Serializer::Adapter::JsonApi

Gem File (I am using 0.10.0 rc3 due to some bugs in 0.9 version)

gem 'active_model_serializers' , '~> 0.10.0.rc3'
 # gem 'active_model_serializers' , '~> 0.9.3'

I am getting a json response for localhost:3000/customers From The rails api like this

{"data":[{"id":"6","type":"customers","attributes":{"name":"Talhaqqq","contactname":"Mohammed Talha","contactno":"9744878171","othcontactno":"+9104735253585","othrefdetails":"nil"},"relationships":{"projects":{"data":[{"id":"1","type":"projects"}]}}},{"id":"10","type":"customers","attributes":{"name":"Ameer Ali qq","contactname":"Ameer Mohammed Ali","contactno":"234234","othcontactno":"439870987","othrefdetails":"othr ref detaila"},"relationships":{"projects":{"data":[]}}},{"id":"67","type":"customers","attributes":{"name":"Signlab qqq","contactname":"Sigblab contact","contactno":"2340987","othcontactno":"sjfdla98","othrefdetails":"s098-"},"relationships":{"projects":{"data":[{"id":"2","type":"projects"}]}}},{"id":"68","type":"customers","attributes":{"name":"Anwar Nazar","contactname":"Anwar","contactno":"2+87+968","othcontactno":"354+641+9","othrefdetails":"31651651"},"relationships":{"projects":{"data":[]}}},{"id":"69","type":"customers","attributes":{"name":"Cust name qqq","contactname":"asdfda","contactno":"sdfsdf","othcontactno":"sadfafd","othrefdetails":"asfdgadfg"},"relationships":{"projects":{"data":[]}}},{"id":"70","type":"customers","attributes":{"name":"dsasva","contactname":"a","contactno":"sdfvsfva","othcontactno":"asdv","othrefdetails":"vafasdfv"},"relationships":{"projects":{"data":[]}}},{"id":"71","type":"customers","attributes":{"name":"egrweg","contactname":"aevg","contactno":"asdgr","othcontactno":"aergarg","othrefdetails":"saerg"},"relationships":{"projects":{"data":[]}}}]}

I am trying to set a customer for a project…

I tried it manually in one of the routes actions… (I dont know the best way to do it in ember )… Both of the tutorial books is not giving me a best way to set the relationship data

let project = this.store.peekRecord('project', 3);
let customer = this.store.peekRecord('customer', 6);
project.set('customer', customer ); 
project.save();

And i am getting this on rails

  Parameters: {"data"=>{"id"=>"3", "attributes"=>{"name"=>"Xyy project"}, "relationships"=>{"customer"=>{"data"=>{"type"=>"customers", "id"=>"6"}}}, "type"=>"projects"}, "id"=>"3"}

I tried to permit the relationships, attributes in Projects_controller

params.require(:data).permit!
#  params.require(:data)
# .permit(:id ,   :attributes => [:name] , :relationships => [:customer => [:data] =>[:type] ] , :customer => [:data] )

But nothing worked well … always given unpermitted parameter… and i ended up permitting all params using

params.require(:data).permit!

And finally , When i did the update using this function

  def update
    @project = Project.find(params[:id])
    @customer = Customer.find(6)
    @project.update_attributes(:customer => params[:customer])
    if @project.update(project_params)

      head :no_content
    else
      render json: @project.errors, status: :unprocessable_entity
    end
  end 

I got this response in rails customer_id = NULL…

Started PATCH "/projects/1" for 127.0.0.1 at 2015-12-09 09:32:42 +0530
Processing by ProjectsController#update as JSON
  Parameters: {"data"=>{"id"=>"1", "attributes"=>{"name"=>"Lulu Mall Project qw "}, "relationships"=>{"customer"=>{"data"=>{"type"=>"customers", "id"=>"67"}}}, "type"=>"projects"}, "id"=>"1"}
  Project Load (1.0ms)  SELECT  `projects`.* FROM `projects` WHERE `projects`.`id` = 1 LIMIT 1
  CACHE (0.0ms)  SELECT  `projects`.* FROM `projects` WHERE `projects`.`id` = 1 LIMIT 1  [["id", "1"]]
  Customer Load (0.9ms)  SELECT  `customers`.* FROM `customers` WHERE `customers`.`id` = 6 LIMIT 1
   (0.3ms)  BEGIN
  SQL (1.1ms)  UPDATE `projects` SET `customer_id` = NULL, `updated_at` = '2015-12-09 04:02:42' WHERE `projects`.`id` = 1
   (50.4ms)  COMMIT

{"data"=>{"id"=>"1", "attributes"=>{"name"=>"Lulu Mall Project qw "}, "relationships"=>{"customer"=>{"data"=>{"type"=>"customers", "id"=>"67"}}}, "type"=>"projects"}, "controller"=>"projects", "action"=>"update", "id"=>"1"}
   (0.8ms)  BEGIN

I know i am totally confused, I tried to use AMS 0.9 version without the JSON API Standard , but ended up in stack level too deep error and come other problems.

But using the AMS 0.10.0 rc version and standard JSON API Format, i couldn’t find any tutorials or answers for these questions…

  1. How to set relationship data in ember ?
  2. The best json format from API (standart json / json with embed ids )?
  3. How to allow strong parametes in Rails (Especially nested with relationships{id, type} etc… With STANDARD JSON API FORMAT)?
  4. Update relationship (update attributes ) in Rails with the json api request from ember?
  5. Is it best to go with the JSON API FORMAT , and AMS …AND JSON API ADAPTER /SERIALIZER…

It feels some thing like an eclipse should happen with these things to work all together…


#2

having the exact same issue. Any recommendations/solutions?


#3

If you can, shift to https://github.com/cerebris/jsonapi-resources its far more compliant with JSON-API


#4

You shouldn’t be mixing AMS on the backend and JSONAPI on the front end. You definitely want to go with JSON API format. That is the convention that ember-data currently defaults to and expects. It’s also the standard going forward for Ember.


#5

@talha I think your problem is the deserialization. I got it to work but definitely needed some playing around. FYI I’m using AMS version ‘0.10.0.rc4’. Here are the changes I had to make, using one of my controllers as an example.

def level_params
  ActiveModelSerializers::Deserialization.jsonapi_parse(params)
end

Might have to call params.to_h or params.to_unsafe_h. More info listed here: https://github.com/rails-api/active_model_serializers/blob/master/docs/general/deserialization.md

In general here are the settings I needed to make it work.

For AMS settings

//initializers/active_model_serializer.rb file

ActiveModel::Serializer.config.adapter = ActiveModel::Serializer::Adapter::JsonApi

For Mime Types

// initializers/mime_types.rb file

    api_mime_types = %W(
      application/vnd.api+json
      text/x-json
      application/json
    )
    Mime::Type.unregister :json
    Mime::Type.register 'application/json', :json, api_mime_types

Configure to accept Patch methods in my applicatin.rb I also configure that. An example of something I have:

  // in application.rb_ file
config.middleware.use Rack::Cors do
      allow do
        origins "localhost:4200"
        resource "*", headers: :any, methods: [:get, :post, :put, :patch, :delete, :options]
      end
    end
    config.active_record.raise_in_transactional_callbacks = true
  end

Example of one of my serializers:

// level_serializer.rb file

    class LevelSerializer < ActiveModel::Serializer
      belongs_to :feature
      has_many :options

      attributes :id, :name
    end

Needed to edit format of the errors so made an error serializer

// serializers/error_serializer.rb file

    module ErrorSerializer
      def ErrorSerializer.serialize(errors)
        return if errors.nil?
        json = {}
        new_hash = errors.to_hash(true).map do |k, v|
          v.map do |msg|
            { id: k, title: msg }
          end
        end.flatten
        json[:errors] = new_hash
        json
      end
    end

And this is how I use it in the controller as an example.

// controllers/levels_controller.rb file   

      def create
        level = Level.new(level_params)
        if level.save
          render json: level
        else
          render json: ErrorSerializer.serialize(level.errors), status: 422
        end
      end

Now for the Ember Side. Adapter:

// adapters/application.js file

    import Ember from 'ember';
    import DS from 'ember-data';
    import ENV from 'foundation-general/config/environment';
    import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin';

    export default DS.JSONAPIAdapter.extend(
      DataAdapterMixin, {

      host: ENV.API.host,
      namespace: ENV.API.namespace,
      authorizer: 'authorizer:devise',

      // allows the multiword paths in urls to be underscored
      pathForType(type) {
        let underscored = Ember.String.underscore(type);
        return Ember.String.pluralize(underscored);
      }
    });

Serializer:

// serializers/application.js file

import Ember from 'ember';
import DS from 'ember-data';
const underscore = Ember.String.underscore;

export default DS.JSONAPISerializer.extend({
  keyForAttribute: function(attr) {
    return Ember.String.underscore(attr);
  },

  keyForRelationship: function(rawKey) {
    return underscore(rawKey);
  }
});

#6

Thanks brother,

I figured out some temporary solution, with AMS version ‘0.10.0.rc4’ . But wasn’t able to use underscored versions yet. I also have many to many relationship problem, Couldn’t figure out how to do it in rails and ember side.

I logged my temporary solutions here.

Let me try your solution, I congratulate you for finding this out real quick, Genius


#7

Hello! Thank you for sharing your solution. It was very helpful. Did you manage to get the authentication working? If so, could you please share the combination of Ember, Rails tools you used? I managed to solve the registration but for sign in Devise 3.4 returns unauthorized despite receiving the properly parsed params.


#8

Rails 5 Worked for me

you could refer this readme for ember-simple-auth and devise configuration

a working repository here , https://github.com/x303/signlab-rails


#9

I’ll give it a try. Thank a lot!