From a117f018edea21e869bb6038aae671ad21f056df Mon Sep 17 00:00:00 2001 From: Alan Date: Thu, 3 Sep 2015 14:31:38 -0400 Subject: [PATCH] [BUGFIX release-1-13] Disable polymorphic deserialization when a model expects 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. --- .../lib/serializers/rest-serializer.js | 7 ++-- .../serializers/rest-serializer-test.js | 35 +++++++++++++++++-- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/packages/ember-data/lib/serializers/rest-serializer.js b/packages/ember-data/lib/serializers/rest-serializer.js index b1bd3174d0a..56835c45f6b 100644 --- a/packages/ember-data/lib/serializers/rest-serializer.js +++ b/packages/ember-data/lib/serializers/rest-serializer.js @@ -228,9 +228,10 @@ var RESTSerializer = JSONSerializer.extend({ Ember.assert(`${this.toString()} has opted into the new serializer API and expects the ${serializer.toString()} it collaborates with to also support the new serializer API by setting its \`isNewSerializerAPI\` property to true.`, get(serializer, 'isNewSerializerAPI')); + const primaryHasTypeAttribute = get(modelClass, 'attributes').get('type'); /*jshint loopfunc:true*/ forEach.call(arrayHash, (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); @@ -240,10 +241,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 { diff --git a/packages/ember-data/tests/integration/serializers/rest-serializer-test.js b/packages/ember-data/tests/integration/serializers/rest-serializer-test.js index 8a553b52f29..4b47f5c8945 100644 --- a/packages/ember-data/tests/integration/serializers/rest-serializer-test.js +++ b/packages/ember-data/tests/integration/serializers/rest-serializer-test.js @@ -1,5 +1,5 @@ var get = Ember.get; -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", { @@ -30,13 +30,18 @@ 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'); @@ -44,6 +49,7 @@ module("integration/serializer/rest - RESTSerializer", { env.store.modelFor('yellow-minion'); env.store.modelFor('doomsday-device'); env.store.modelFor('comment'); + env.store.modelFor('basket'); }, teardown: function() { @@ -513,3 +519,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); +});