Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JSONAPI] Using EmbeddedRecordsMixin puts the embedded records in the wrong place #3720

Closed
jagthedrummer opened this issue Sep 2, 2015 · 16 comments

Comments

@jagthedrummer
Copy link
Contributor

I'm trying to send a Product and a collection of Screenshots to the server in one request.

My product serializer looks like this:

// app/serializers/product.js
import DS from 'ember-data';
export default DS.JSONAPISerializer.extend(DS.EmbeddedRecordsMixin,{
  attrs: {
    screenshots: { embedded: 'always' }
  }
});

When I save a Product that has one Screenshot in the collection I'm getting this payload:

{
  "data"=>{
    "attributes"=>{
      "name"=>"embedded save test",
      "price"=>"42",
    },
    "relationships"=>{
      "category"=>{
        "data"=>{"type"=>"categories", "id"=>"1"}
      }
    },
    "screenshots"=>[{
      "data"=>{
        "attributes"=>{
          "name"=>"testing the screenshots",
          "description"=>"heloo there",
          "cloudinary-image-id"=>"g3ljvsgc1j0jskrgmexd"
        },
        "relationships"=>{
          "product"=>{"data"=>{"type"=>"products", "id"=>nil}}
        },
        "type"=>"screenshots"
      }
    }],
    "type"=>"products"
  }
}

The screenshots object is part of the root data object instead of being in the relationships object along with category.

@wecc
Copy link
Contributor

wecc commented Sep 2, 2015

I would say that the EmbeddedRecordsMixin does not support JSONAPISerializer right now as the JSON API spec itself doesn't mention anything about embedding resources (are they relationships? keep them as complex attributes?). It shouldn't be too hard to fix but it's currently very untested.

@fivetanley
Copy link
Member

I would say that if you're using JSONAPI you shouldn't be using embedded records. You should be able to move those records to included

@csantero
Copy link
Contributor

csantero commented Sep 7, 2015

JSON API does not define any semantics for using included in a POST/PATCH. The way I've dealt with saving multiple related records in one request is to turn the related records into complex attributes on the primary record. It's ugly, but I haven't found any better way to do it.

There is discussion about how to support this use case and it's milestoned for the next version of the spec, but nothing concrete yet.

@MiracleBlue
Copy link

Darn, so this is what I have been dealing with all morning. Glad to have found this thread. I'll switch to just not using embedded records for the time being and side-load it in included.

@vladimir-e
Copy link

I ran into same problem today.
If you use JSONAPISerializer and you need to save a record and update its hasMany relationships in one request, DS.EmbeddedRecordsMixin is still okay, if you really need to make it work.

UPD: changed the code below after looking at PR #3848

Here's a patch I ended up creating:

I created mixin/embedded-jsonapi-records.js that adds another type of serialization ids-and-type which is json-api compliant:

import Ember from 'ember';
import {pluralize} from 'ember-inflector';

export default Ember.Mixin.create({

  // add json-api compliant serialization type
  hasSerializeIdsAndTypeOption: function(attr) {
    var option = this.attrsOption(attr);
    return option && option.serialize === 'ids-and-type';
  },

  serializeHasMany: function(snapshot, json, relationship) {
    var attr = relationship.key;
    if (this.noSerializeOptionSpecified(attr)) {
      this._super(snapshot, json, relationship);
      return;
    }
    var includeIds = this.hasSerializeIdsOption(attr);
    var includeIdsAndType = this.hasSerializeIdsAndTypeOption(attr);
    var includeRecords = this.hasSerializeRecordsOption(attr);
    if (includeIdsAndType){
      let serializedKey = this.keyForRelationship(attr, relationship.kind, 'serialize'),
          serializedRecords = snapshot.hasMany(attr, { ids: true }).map(function(id) {
            return { id: id, type: pluralize(relationship.type)};
          });
      if (!json['relationships']) {
        json['relationships'] = {};
      }
      json['relationships'][serializedKey] = { data: serializedRecords };
    } else if (includeIds) {
      let serializedKey = this.keyForRelationship(attr, relationship.kind, 'serialize');
      json[serializedKey] = snapshot.hasMany(attr, { ids: true });
    } else if (includeRecords) {
      this._serializeEmbeddedHasMany(snapshot, json, relationship);
    }
  },

});

Example:

app/serializers/client.js

import DS from 'ember-data';
import EmbeddedJSONapiRecordsMixin from '../mixins/embedded-jsonapi-records';

export default DS.JSONAPISerializer.extend(DS.EmbeddedRecordsMixin, EmbeddedJSONapiRecordsMixin, {
  attrs: {
    pets: {
      serialize: 'ids-and-type',
      deserialize: 'records'
    }
  }
});

@bmac
Copy link
Member

bmac commented Mar 22, 2016

I have opened a pr so Ember Data will warn in the future when JSONAPISerializer and EmbeddedRecordsMixin are combined. #4259

I don't think JSONAPISerializer and EmbeddedRecordsMixin will work together out of the box until JSON API has a more detailed specification on how it supports embedded records. json-api/json-api#795

@michaeljbishop
Copy link

Do we necessarily need something more from JSON-API at this point for this to work? Why couldn't the payload in the above example look like this?

{
  "data"=>{
    "attributes"=>{
      "name"=>"embedded save test",
      "price"=>"42",
      "screenshots"=>[{
        "name"=>"testing the screenshots",
        "description"=>"heloo there",
        "cloudinary-image-id"=>"g3ljvsgc1j0jskrgmexd"
      }],
    },
    "relationships"=>{
      "category"=>{
        "data"=>{"type"=>"categories", "id"=>"1"}
      }
    },
    "type"=>"products"
  }
}

In other words, if the value of the "attribute" is an embedded record, why don't we serialize it as a JSON blob and submit (and retrieve) that as the value?

@taras
Copy link

taras commented Aug 18, 2016

@bmac is it possible to suppress this error for those who're using JSONAPI & EmbeddedRecordsMixin intentionally?

@danilovaz
Copy link

anything about it? this is very frustrating. I use JSONAPISERIALIZER. I have a User model containing many Tags. What can I do when I need to make a Patch request, which can be editing the user name and selected Tags ids?

@fivetanley
Copy link
Member

@taras I don't think we'll allow disabling the warning until JSONAPI takes a stance on this. The JSONAPI adapter/serializer's goals are to only support stuff in the spec or in a spec extension. A better route for now might be to fork or copy the EmbeddedRecordsMixin into your app and make the changes you need there for now.

I know this is kind of not the greatest response in the world, and I apologize. EmbeddedRecordsMixin has in the past been pretty complex to support across a variety of APIs, so we want to reduce the maintenance burden until something is standardized so everyone has an idea of what their server should be sending.

@fivetanley
Copy link
Member

We should come up with something in the short term so at least IDs can be serialized to the server.

@sandstrom
Copy link
Contributor

FWIW here is the discussion on embedded records in json-api:

json-api/json-api#795 (comment)

@taras
Copy link

taras commented Sep 13, 2016

I know this is kind of not the greatest response in the world, and I apologize.

No worries, it makes sense.

Would it make sense to pull EmbeddedRecordsMixin mixing into an addon? It can stay in Ember Data and people who want to use it can use it without the warning can use the addon. It'll make their opt-in explicit.

@pixelhandler
Copy link
Contributor

@jagthedrummer Just saw this, json-api/json-api#1089 seems that there are still a lot of question to resolve before the JSON API adapter/serializer can support embedded records.

@taras since the EmbeddedRecordsMixin originally supported the ActiveModel adapter/serializer it makes sense to extract as an addon.

@sandstrom
Copy link
Contributor

This PR may have solved this issue: #7126

(I haven't tested myself yet though, saw it only a few minutes ago)

@runspired
Copy link
Contributor

Closing as both identifiers resolved much of the original issues faced here and because serializers are an antiquated pattern that are optional today and don't have a lasting future (see emberjs/rfcs#860)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests