Skip to content

Commit

Permalink
[BUGFIX release] Disable polymorphic deserialization when a model exp…
Browse files Browse the repository at this point in the history
…ects a type attribute

b7f7b7a introduced a bug where if there is a type key in a payload that is part
of an array, it would be used for polymorphic deserialization even when the
model expects an attribute that is named "type".

Interestingly, for such payloads, `arrayHash` passed into `normalizeArray()` in
rest-serializer.js contains Ember.Object instances as opposed to plain objects.
This causes the code to throw, since `hash.type` would be a computed property
in that case. This is probably because of extending from JSONAPISerializer.
  • Loading branch information
Alan authored and bmac committed Sep 18, 2015
1 parent aeccf38 commit 69dca0c
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 5 deletions.
9 changes: 6 additions & 3 deletions packages/ember-data/lib/serializers/rest-serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {singularize} from "ember-inflector/lib/system/string";
import coerceId from "ember-data/system/coerce-id";

var camelize = Ember.String.camelize;
var get = Ember.get;

/**
Normally, applications will use the `RESTSerializer` by implementing
Expand Down Expand Up @@ -148,8 +149,10 @@ var RESTSerializer = JSONSerializer.extend({
let modelClass = store.modelFor(modelName);
let serializer = store.serializerFor(modelName);

const primaryHasTypeAttribute = get(modelClass, 'attributes').get('type');
/*jshint loopfunc:true*/
arrayHash.forEach((hash) => {
let { data, included } = this._normalizePolymorphicRecord(store, hash, prop, modelClass, serializer);
let { data, included } = this._normalizePolymorphicRecord(store, hash, prop, modelClass, serializer, primaryHasTypeAttribute);
documentHash.data.push(data);
if (included) {
documentHash.included.push(...included);
Expand All @@ -159,10 +162,10 @@ var RESTSerializer = JSONSerializer.extend({
return documentHash;
},

_normalizePolymorphicRecord(store, hash, prop, primaryModelClass, primarySerializer) {
_normalizePolymorphicRecord(store, hash, prop, primaryModelClass, primarySerializer, primaryHasTypeAttribute) {
let serializer, modelClass;
// Support polymorphic records in async relationships
if (hash.type && store._hasModelFor(this.modelNameFromPayloadKey(hash.type))) {
if (!primaryHasTypeAttribute && hash.type && store._hasModelFor(this.modelNameFromPayloadKey(hash.type))) {
serializer = store.serializerFor(hash.type);
modelClass = store.modelFor(hash.type);
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
var HomePlanet, league, SuperVillain, EvilMinion, YellowMinion, DoomsdayDevice, Comment, env;
var HomePlanet, league, SuperVillain, EvilMinion, YellowMinion, DoomsdayDevice, Comment, Basket, env;
var run = Ember.run;

module("integration/serializer/rest - RESTSerializer", {
Expand Down Expand Up @@ -29,20 +29,26 @@ module("integration/serializer/rest - RESTSerializer", {
root: DS.attr('boolean'),
children: DS.hasMany('comment', { inverse: null, async: false })
});
Basket = DS.Model.extend({
type: DS.attr('string'),
size: DS.attr('number')
});
env = setupStore({
superVillain: SuperVillain,
homePlanet: HomePlanet,
evilMinion: EvilMinion,
yellowMinion: YellowMinion,
doomsdayDevice: DoomsdayDevice,
comment: Comment
comment: Comment,
basket: Basket
});
env.store.modelFor('super-villain');
env.store.modelFor('home-planet');
env.store.modelFor('evil-minion');
env.store.modelFor('yellow-minion');
env.store.modelFor('doomsday-device');
env.store.modelFor('comment');
env.store.modelFor('basket');
},

teardown: function() {
Expand Down Expand Up @@ -588,3 +594,28 @@ test("normalizeResponse can load secondary records of the same type without affe
}]
});
});

test("don't polymorphically deserialize base on the type key in payload when a type attribute exist", function() {
env.registry.register('serializer:application', DS.RESTSerializer.extend({
isNewSerializerAPI: true
}));

run(function() {
env.restSerializer.normalizeArrayResponse(env.store, Basket, {
basket: [
env.store.createRecord('Basket', { type: 'bamboo', size: 10, id: '1' }),
env.store.createRecord('Basket', { type: 'yellowMinion', size: 10, id: '65536' })
]
});
});

const normalRecord = env.store.peekRecord('basket', '1');
ok(normalRecord, "payload with type that doesn't exist");
strictEqual(normalRecord.get('type'), 'bamboo');
strictEqual(normalRecord.get('size'), 10);

const clashingRecord = env.store.peekRecord('basket', '65536');
ok(clashingRecord, 'payload with type that matches another model name');
strictEqual(clashingRecord.get('type'), 'yellowMinion');
strictEqual(clashingRecord.get('size'), 10);
});

0 comments on commit 69dca0c

Please sign in to comment.