diff --git a/packages/ember-data/lib/serializers/embedded-records-mixin.js b/packages/ember-data/lib/serializers/embedded-records-mixin.js index d61102408ae..3a0dcb91686 100644 --- a/packages/ember-data/lib/serializers/embedded-records-mixin.js +++ b/packages/ember-data/lib/serializers/embedded-records-mixin.js @@ -1,3 +1,5 @@ +var get = Ember.get; +var set = Ember.set; var forEach = Ember.EnumerableUtils.forEach; var camelize = Ember.String.camelize; @@ -121,8 +123,13 @@ var EmbeddedRecordsMixin = Ember.Mixin.create({ @return {Object} the normalized hash **/ normalize: function(typeClass, hash, prop) { + if (Ember.FEATURES.isEnabled('ds-new-serializer-api') && this.get('isNewSerializerAPI')) { + let normalizedHash = this._super(...arguments); + return _newNormalize.call(this, typeClass, normalizedHash, prop); + } + var normalizedHash = this._super(typeClass, hash, prop); - return extractEmbeddedRecords(this, this.store, typeClass, normalizedHash); + return this._extractEmbeddedRecords(this, this.store, typeClass, normalizedHash); }, keyForRelationship: function(key, typeClass, method) { @@ -393,63 +400,124 @@ var EmbeddedRecordsMixin = Ember.Mixin.create({ attrsOption: function(attr) { var attrs = this.get('attrs'); return attrs && (attrs[camelize(attr)] || attrs[attr]); - } -}); + }, -// chooses a relationship kind to branch which function is used to update payload -// does not change payload if attr is not embedded -function extractEmbeddedRecords(serializer, store, typeClass, partial) { + /** + @method _extractEmbeddedRecords + @private + */ + _extractEmbeddedRecords: function(serializer, store, typeClass, partial) { + if (Ember.FEATURES.isEnabled('ds-new-serializer-api') && this.get('isNewSerializerAPI')) { + return _newExtractEmbeddedRecords.apply(this, arguments); + } - typeClass.eachRelationship(function(key, relationship) { - if (serializer.hasDeserializeRecordsOption(key)) { - var embeddedTypeClass = store.modelFor(relationship.type); - if (relationship.kind === "hasMany") { - if (relationship.options.polymorphic) { - extractEmbeddedHasManyPolymorphic(store, key, partial); - } else { - extractEmbeddedHasMany(store, key, embeddedTypeClass, partial); + typeClass.eachRelationship(function(key, relationship) { + if (serializer.hasDeserializeRecordsOption(key)) { + var embeddedTypeClass = store.modelFor(relationship.type); + if (relationship.kind === "hasMany") { + if (relationship.options.polymorphic) { + this._extractEmbeddedHasManyPolymorphic(store, key, partial); + } else { + this._extractEmbeddedHasMany(store, key, embeddedTypeClass, partial); + } } - } - if (relationship.kind === "belongsTo") { - if (relationship.options.polymorphic) { - extractEmbeddedBelongsToPolymorphic(store, key, partial); - } else { - extractEmbeddedBelongsTo(store, key, embeddedTypeClass, partial); + if (relationship.kind === "belongsTo") { + if (relationship.options.polymorphic) { + this._extractEmbeddedBelongsToPolymorphic(store, key, partial); + } else { + this._extractEmbeddedBelongsTo(store, key, embeddedTypeClass, partial); + } } } + }, this); + + return partial; + }, + + /** + @method _extractEmbeddedHasMany + @private + */ + _extractEmbeddedHasMany: function(store, key, embeddedTypeClass, hash) { + if (Ember.FEATURES.isEnabled('ds-new-serializer-api') && this.get('isNewSerializerAPI')) { + return _newExtractEmbeddedHasMany.apply(this, arguments); } - }); - return partial; -} + if (!hash[key]) { + return hash; + } + + var ids = []; -// handles embedding for `hasMany` relationship -function extractEmbeddedHasMany(store, key, embeddedTypeClass, hash) { - if (!hash[key]) { + var embeddedSerializer = store.serializerFor(embeddedTypeClass.modelName); + forEach(hash[key], function(data) { + var embeddedRecord = embeddedSerializer.normalize(embeddedTypeClass, data, null); + store.push(embeddedTypeClass.modelName, embeddedRecord); + ids.push(embeddedRecord.id); + }); + + hash[key] = ids; return hash; - } + }, - var ids = []; + /** + @method _extractEmbeddedHasManyPolymorphic + @private + */ + _extractEmbeddedHasManyPolymorphic: function(store, key, hash) { + if (!hash[key]) { + return hash; + } - var embeddedSerializer = store.serializerFor(embeddedTypeClass.modelName); - forEach(hash[key], function(data) { - var embeddedRecord = embeddedSerializer.normalize(embeddedTypeClass, data, null); - store.push(embeddedTypeClass.modelName, embeddedRecord); - ids.push(embeddedRecord.id); - }); + var ids = []; - hash[key] = ids; - return hash; -} + forEach(hash[key], function(data) { + var modelName = data.type; + var embeddedSerializer = store.serializerFor(modelName); + var embeddedTypeClass = store.modelFor(modelName); + // var primaryKey = embeddedSerializer.get('primaryKey'); -function extractEmbeddedHasManyPolymorphic(store, key, hash) { - if (!hash[key]) { + var embeddedRecord = embeddedSerializer.normalize(embeddedTypeClass, data, null); + store.push(embeddedTypeClass.modelName, embeddedRecord); + ids.push({ id: embeddedRecord.id, type: modelName }); + }); + + hash[key] = ids; return hash; - } + }, + + /** + @method _extractEmbeddedBelongsTo + @private + */ + _extractEmbeddedBelongsTo: function(store, key, embeddedTypeClass, hash) { + if (Ember.FEATURES.isEnabled('ds-new-serializer-api') && this.get('isNewSerializerAPI')) { + return _newExtractEmbeddedBelongsTo.apply(this, arguments); + } + + if (!hash[key]) { + return hash; + } + + var embeddedSerializer = store.serializerFor(embeddedTypeClass.modelName); + var embeddedRecord = embeddedSerializer.normalize(embeddedTypeClass, hash[key], null); + store.push(embeddedTypeClass.modelName, embeddedRecord); - var ids = []; + hash[key] = embeddedRecord.id; + //TODO Need to add a reference to the parent later so relationship works between both `belongsTo` records + return hash; + }, - forEach(hash[key], function(data) { + /** + @method _extractEmbeddedBelongsToPolymorphic + @private + */ + _extractEmbeddedBelongsToPolymorphic: function(store, key, hash) { + if (!hash[key]) { + return hash; + } + + var data = hash[key]; var modelName = data.type; var embeddedSerializer = store.serializerFor(modelName); var embeddedTypeClass = store.modelFor(modelName); @@ -457,43 +525,103 @@ function extractEmbeddedHasManyPolymorphic(store, key, hash) { var embeddedRecord = embeddedSerializer.normalize(embeddedTypeClass, data, null); store.push(embeddedTypeClass.modelName, embeddedRecord); - ids.push({ id: embeddedRecord.id, type: modelName }); - }); - hash[key] = ids; - return hash; -} - -function extractEmbeddedBelongsTo(store, key, embeddedTypeClass, hash) { - if (!hash[key]) { + hash[key] = embeddedRecord.id; + hash[key + 'Type'] = modelName; return hash; + }, + + /** + @method _normalizeEmbeddedRelationship + @private + */ + _normalizeEmbeddedRelationship: function(store, relationshipMeta, relationshipHash) { + let modelName = relationshipMeta.type; + if (relationshipMeta.options.polymorphic) { + modelName = relationshipHash.type; + } + let modelClass = store.modelFor(modelName); + let serializer = store.serializerFor(modelName); + + return serializer.normalize(modelClass, relationshipHash, null); } - var embeddedSerializer = store.serializerFor(embeddedTypeClass.modelName); - var embeddedRecord = embeddedSerializer.normalize(embeddedTypeClass, hash[key], null); - store.push(embeddedTypeClass.modelName, embeddedRecord); +}); - hash[key] = embeddedRecord.id; - //TODO Need to add a reference to the parent later so relationship works between both `belongsTo` records - return hash; +export default EmbeddedRecordsMixin; + +/** + This is only to be used temporarily during the transition from the old + serializer API to the new one. + + @method _newNormalize + @param {DS.Model} modelClass + @param {Object} hash + @return {Object} + @private +*/ +function _newNormalize(typeClass, hash, prop) { + return this._extractEmbeddedRecords(this, this.store, typeClass, hash); } -function extractEmbeddedBelongsToPolymorphic(store, key, hash) { - if (!hash[key]) { - return hash; +/** + @method _newExtractEmbeddedRecords + @private +*/ +function _newExtractEmbeddedRecords(serializer, store, typeClass, partial) { + typeClass.eachRelationship((key, relationship) => { + if (serializer.hasDeserializeRecordsOption(key)) { + if (relationship.kind === "hasMany") { + this._extractEmbeddedHasMany(store, key, partial, relationship); + } + if (relationship.kind === "belongsTo") { + this._extractEmbeddedBelongsTo(store, key, partial, relationship); + } + } + }, this); + return partial; +} + +/** + @method _newExtractEmbeddedHasMany + @private +*/ +function _newExtractEmbeddedHasMany(store, key, hash, relationshipMeta) { + let relationshipHash = get(hash, `data.relationships.${key}.data`); + if (!relationshipHash) { + return; } - var data = hash[key]; - var modelName = data.type; - var embeddedSerializer = store.serializerFor(modelName); - var embeddedTypeClass = store.modelFor(modelName); + let hasMany = relationshipHash.map(item => { + let { data, included } = this._normalizeEmbeddedRelationship(store, relationshipMeta, item); + hash.included = hash.included || []; + hash.included.push(data); + hash.included.push(...included); - var embeddedRecord = embeddedSerializer.normalize(embeddedTypeClass, data, null); - store.push(embeddedTypeClass.modelName, embeddedRecord); + return { id: data.id, type: data.type }; + }); - hash[key] = embeddedRecord.id; - hash[key + 'Type'] = modelName; - return hash; + let relationship = { data: hasMany }; + set(hash, `data.relationships.${key}`, relationship); } -export default EmbeddedRecordsMixin; +/** + @method _newExtractEmbeddedBelongsTo + @private +*/ +function _newExtractEmbeddedBelongsTo(store, key, hash, relationshipMeta) { + let relationshipHash = get(hash, `data.relationships.${key}.data`); + if (!relationshipHash) { + return; + } + + let { data, included } = this._normalizeEmbeddedRelationship(store, relationshipMeta, relationshipHash); + hash.included = hash.included || []; + hash.included.push(data); + hash.included.push(...included); + + let belongsTo = { id: data.id, type: data.type }; + let relationship = { data: belongsTo }; + + set(hash, `data.relationships.${key}`, relationship); +} diff --git a/packages/ember-data/tests/integration/serializers/embedded-records-mixin-new-test.js b/packages/ember-data/tests/integration/serializers/embedded-records-mixin-new-test.js new file mode 100644 index 00000000000..808862be58c --- /dev/null +++ b/packages/ember-data/tests/integration/serializers/embedded-records-mixin-new-test.js @@ -0,0 +1,1200 @@ +var HomePlanet, SuperVillain, EvilMinion, SecretLab, SecretWeapon, BatCave, Comment, env; +var run = Ember.run; +var LightSaber; +var TestSerializer; + +module("integration/embedded_records_mixin - EmbeddedRecordsMixin (new API)", { + setup: function() { + SuperVillain = DS.Model.extend({ + firstName: DS.attr('string'), + lastName: DS.attr('string'), + homePlanet: DS.belongsTo("homePlanet", { inverse: 'villains' }), + secretLab: DS.belongsTo("secretLab"), + secretWeapons: DS.hasMany("secretWeapon"), + evilMinions: DS.hasMany("evilMinion") + }); + HomePlanet = DS.Model.extend({ + name: DS.attr('string'), + villains: DS.hasMany('superVillain', { inverse: 'homePlanet' }) + }); + SecretLab = DS.Model.extend({ + minionCapacity: DS.attr('number'), + vicinity: DS.attr('string'), + superVillain: DS.belongsTo('superVillain') + }); + BatCave = SecretLab.extend({ + infiltrated: DS.attr('boolean') + }); + SecretWeapon = DS.Model.extend({ + name: DS.attr('string'), + superVillain: DS.belongsTo('superVillain') + }); + LightSaber = SecretWeapon.extend({ + color: DS.attr('string') + }); + EvilMinion = DS.Model.extend({ + superVillain: DS.belongsTo('superVillain'), + name: DS.attr('string') + }); + Comment = DS.Model.extend({ + body: DS.attr('string'), + root: DS.attr('boolean'), + children: DS.hasMany('comment', { inverse: null }) + }); + TestSerializer = DS.RESTSerializer.extend({ + isNewSerializerAPI: true + }); + env = setupStore({ + superVillain: SuperVillain, + homePlanet: HomePlanet, + secretLab: SecretLab, + batCave: BatCave, + secretWeapon: SecretWeapon, + lightSaber: LightSaber, + evilMinion: EvilMinion, + comment: Comment + }); + env.store.modelFor('superVillain'); + env.store.modelFor('homePlanet'); + env.store.modelFor('secretLab'); + env.store.modelFor('batCave'); + env.store.modelFor('secretWeapon'); + env.store.modelFor('lightSaber'); + env.store.modelFor('evilMinion'); + env.store.modelFor('comment'); + + env.registry.register('serializer:application', TestSerializer.extend(DS.EmbeddedRecordsMixin)); + }, + + teardown: function() { + run(env.store, 'destroy'); + } +}); + +if (Ember.FEATURES.isEnabled('ds-new-serializer-api')) { + + test("extractSingle with embedded objects", function() { + env.registry.register('adapter:super-villain', DS.RESTAdapter); + env.registry.register('serializer:super-villain', TestSerializer.extend()); + env.registry.register('serializer:home-planet', TestSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + villains: { embedded: 'always' } + } + })); + + var serializer = env.container.lookup("serializer:home-planet"); + var json_hash = { + homePlanet: { + id: "1", + name: "Umber", + villains: [{ + id: "2", + firstName: "Tom", + lastName: "Dale" + }] + } + }; + var json; + + run(function() { + json = serializer.normalizeResponse(env.store, HomePlanet, json_hash, '1', 'find'); + }); + + deepEqual(json, { + "data": { + "id": "1", + "type": "home-planet", + "attributes": { + "name": "Umber" + }, + "relationships": { + "villains": { + "data": [ + { "id": "2", "type": "super-villain" } + ] + } + } + }, + "included": [ + { + "id": "2", + "type": "super-villain", + "attributes": { + "firstName": "Tom", + "lastName": "Dale" + }, + "relationships": {} + } + ] + }); + }); + + test("extractSingle with embedded objects inside embedded objects", function() { + env.registry.register('adapter:super-villain', DS.RESTAdapter); + env.registry.register('serializer:home-planet', TestSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + villains: { embedded: 'always' } + } + })); + env.registry.register('serializer:super-villain', TestSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + evilMinions: { embedded: 'always' } + } + })); + env.registry.register('serializer:evil-minion', TestSerializer); + + var serializer = env.container.lookup("serializer:home-planet"); + var json_hash = { + homePlanet: { + id: "1", + name: "Umber", + villains: [{ + id: "2", + firstName: "Tom", + lastName: "Dale", + evilMinions: [{ + id: "3", + name: "Alex" + }] + }] + } + }; + var json; + + run(function() { + json = serializer.normalizeResponse(env.store, HomePlanet, json_hash, '1', 'find'); + }); + + deepEqual(json, { + "data": { + "id": "1", + "type": "home-planet", + "attributes": { + "name": "Umber" + }, + "relationships": { + "villains": { + "data": [ + { "id": "2", "type": "super-villain" } + ] + } + } + }, + "included": [{ + "id": "2", + "type": "super-villain", + "attributes": { + "firstName": "Tom", + "lastName": "Dale" + }, + "relationships": { + "evilMinions": { + "data": [ + { "id": "3", "type": "evil-minion" } + ] + } + } + }, { + "id": "3", + "type": "evil-minion", + "attributes": { + "name": "Alex" + }, + "relationships": {} + }] + }); + }); + + test("extractSingle with embedded objects of same type", function() { + env.registry.register('adapter:comment', DS.RESTAdapter); + env.registry.register('serializer:comment', TestSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + children: { embedded: 'always' } + } + })); + + var serializer = env.container.lookup("serializer:comment"); + var json_hash = { + comment: { + id: "1", + body: "Hello", + root: true, + children: [{ + id: "2", + body: "World", + root: false + }, + { + id: "3", + body: "Foo", + root: false + }] + } + }; + var json; + run(function() { + json = serializer.normalizeResponse(env.store, Comment, json_hash, '1', 'find'); + }); + + deepEqual(json, { + "data": { + "id": "1", + "type": "comment", + "attributes": { + "body": "Hello", + "root": true + }, + "relationships": { + "children": { + "data": [ + { "id": "2", "type": "comment" }, + { "id": "3", "type": "comment" } + ] + } + } + }, + "included": [{ + "id": "2", + "type": "comment", + "attributes": { + "body": "World", + "root": false + }, + "relationships": {} + }, { + "id": "3", + "type": "comment", + "attributes": { + "body": "Foo", + "root": false + }, + "relationships": {} + }] + }, "Primary record was correct"); + }); + + test("extractSingle with embedded objects inside embedded objects of same type", function() { + env.registry.register('adapter:comment', DS.RESTAdapter); + env.registry.register('serializer:comment', TestSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + children: { embedded: 'always' } + } + })); + + var serializer = env.container.lookup("serializer:comment"); + var json_hash = { + comment: { + id: "1", + body: "Hello", + root: true, + children: [{ + id: "2", + body: "World", + root: false, + children: [{ + id: "4", + body: "Another", + root: false + }] + }, + { + id: "3", + body: "Foo", + root: false + }] + } + }; + var json; + run(function() { + json = serializer.normalizeResponse(env.store, Comment, json_hash, '1', 'find'); + }); + + deepEqual(json, { + "data": { + "id": "1", + "type": "comment", + "attributes": { + "body": "Hello", + "root": true + }, + "relationships": { + "children": { + "data": [ + { "id": "2", "type": "comment" }, + { "id": "3", "type": "comment" } + ] + } + } + }, + "included": [{ + "id": "2", + "type": "comment", + "attributes": { + "body": "World", + "root": false + }, + "relationships": { + "children": { + "data": [ + { "id": "4", "type": "comment" } + ] + } + } + }, { + "id": "4", + "type": "comment", + "attributes": { + "body": "Another", + "root": false + }, + "relationships": {} + }, { + "id": "3", + "type": "comment", + "attributes": { + "body": "Foo", + "root": false + }, + "relationships": {} + }] + }, "Primary record was correct"); + }); + + test("extractSingle with embedded objects of same type, but from separate attributes", function() { + HomePlanet.reopen({ + reformedVillains: DS.hasMany('superVillain', { inverse: null }) + }); + + env.registry.register('adapter:home-planet', DS.RESTAdapter); + env.registry.register('serializer:home-planet', TestSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + villains: { embedded: 'always' }, + reformedVillains: { embedded: 'always' } + } + })); + env.registry.register('serializer:super-villain', TestSerializer); + + var serializer = env.container.lookup("serializer:home-planet"); + var json_hash = { + homePlanet: { + id: "1", + name: "Earth", + villains: [{ + id: "1", + firstName: "Tom" + }, { + id: "3", + firstName: "Yehuda" + }], + reformedVillains: [{ + id: "2", + firstName: "Alex" + },{ + id: "4", + firstName: "Erik" + }] + } + }; + var json; + run(function() { + json = serializer.normalizeResponse(env.store, HomePlanet, json_hash, '1', 'find'); + }); + + deepEqual(json, { + "data": { + "id": "1", + "type": "home-planet", + "attributes": { + "name": "Earth" + }, + "relationships": { + "villains": { + "data": [ + { "id": "1", "type": "super-villain" }, + { "id": "3", "type": "super-villain" } + ] + }, + "reformedVillains": { + "data": [ + { "id": "2", "type": "super-villain" }, + { "id": "4", "type": "super-villain" } + ] + } + } + }, + "included": [{ + "id": "1", + "type": "super-villain", + "attributes": { + "firstName": "Tom" + }, + "relationships": {} + }, { + "id": "3", + "type": "super-villain", + "attributes": { + "firstName": "Yehuda" + }, + "relationships": {} + }, { + "id": "2", + "type": "super-villain", + "attributes": { + "firstName": "Alex" + }, + "relationships": {} + }, { + "id": "4", + "type": "super-villain", + "attributes": { + "firstName": "Erik" + }, + "relationships": {} + }] + }, "Primary hash was correct"); + }); + + test("extractArray with embedded objects", function() { + env.registry.register('adapter:super-villain', DS.RESTAdapter); + env.registry.register('serializer:home-planet', TestSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + villains: { embedded: 'always' } + } + })); + env.registry.register('serializer:super-villain', TestSerializer); + + var serializer = env.container.lookup("serializer:home-planet"); + + var json_hash = { + homePlanets: [{ + id: "1", + name: "Umber", + villains: [{ + id: "1", + firstName: "Tom", + lastName: "Dale" + }] + }] + }; + var array; + + run(function() { + array = serializer.normalizeResponse(env.store, HomePlanet, json_hash, null, 'findAll'); + }); + + deepEqual(array, { + "data": [{ + "id": "1", + "type": "home-planet", + "attributes": { + "name": "Umber" + }, + "relationships": { + "villains": { + "data": [ + { "id": "1", "type": "super-villain" } + ] + } + } + }], + "included": [{ + "id": "1", + "type": "super-villain", + "attributes": { + "firstName": "Tom", + "lastName": "Dale" + }, + "relationships": {} + }] + }); + }); + + test("extractArray with embedded objects with custom primary key", function() { + expect(1); + env.registry.register('adapter:super-villain', DS.RESTAdapter); + env.registry.register('serializer:super-villain', TestSerializer.extend({ + primaryKey: 'villain_id' + })); + env.registry.register('serializer:home-planet', TestSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + villains: { embedded: 'always' } + } + })); + + var serializer = env.container.lookup("serializer:home-planet"); + + var json_hash = { + homePlanets: [{ + id: "1", + name: "Umber", + villains: [{ + villain_id: "2", + firstName: "Alex", + lastName: "Baizeau" + }] + }] + }; + var array; + + run(function() { + array = serializer.normalizeResponse(env.store, HomePlanet, json_hash, null, 'findAll'); + }); + + deepEqual(array, { + "data": [{ + "id": "1", + "type": "home-planet", + "attributes": { + "name": "Umber" + }, + "relationships": { + "villains": { + "data": [ + { "id": "2", "type": "super-villain" } + ] + } + } + }], + "included": [{ + "id": "2", + "type": "super-villain", + "attributes": { + "firstName": "Alex", + "lastName": "Baizeau" + }, + "relationships": {} + }] + }); + }); + + test("extractArray with embedded objects with identical relationship and attribute key ", function() { + expect(1); + env.registry.register('adapter:super-villain', DS.RESTAdapter); + env.registry.register('serializer:home-planet', TestSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + villains: { embedded: 'always' } + }, + //Makes the keyForRelationship and keyForAttribute collide. + keyForRelationship: function(key, type) { + return this.keyForAttribute(key, type); + } + })); + env.registry.register('serializer:super-villain', TestSerializer); + + var serializer = env.container.lookup("serializer:home-planet"); + + var json_hash = { + homePlanets: [{ + id: "1", + name: "Umber", + villains: [{ + id: "1", + firstName: "Alex", + lastName: "Baizeau" + }] + }] + }; + var array; + + run(function() { + array = serializer.normalizeResponse(env.store, HomePlanet, json_hash, null, 'findAll'); + }); + + deepEqual(array, { + "data": [{ + "id": "1", + "type": "home-planet", + "attributes": { + "name": "Umber" + }, + "relationships": { + "villains": { + "data": [ + { "id": "1", "type": "super-villain" } + ] + } + } + }], + "included": [{ + "id": "1", + "type": "super-villain", + "attributes": { + "firstName": "Alex", + "lastName": "Baizeau" + }, + "relationships": {} + }] + }); + }); + test("extractArray with embedded objects of same type as primary type", function() { + env.registry.register('adapter:comment', DS.RESTAdapter); + env.registry.register('serializer:comment', TestSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + children: { embedded: 'always' } + } + })); + + var serializer = env.container.lookup("serializer:comment"); + + var json_hash = { + comments: [{ + id: "1", + body: "Hello", + root: true, + children: [{ + id: "2", + body: "World", + root: false + }, + { + id: "3", + body: "Foo", + root: false + }] + }] + }; + var array; + + run(function() { + array = serializer.normalizeResponse(env.store, Comment, json_hash, null, 'findAll'); + }); + + deepEqual(array, { + "data": [{ + "id": "1", + "type": "comment", + "attributes": { + "body": "Hello", + "root": true + }, + "relationships": { + "children": { + "data": [ + { "id": "2", "type": "comment" }, + { "id": "3", "type": "comment" } + ] + } + } + }], + "included": [{ + "id": "2", + "type": "comment", + "attributes": { + "body": "World", + "root": false + }, + "relationships": {} + }, { + "id": "3", + "type": "comment", + "attributes": { + "body": "Foo", + "root": false + }, + "relationships": {} + }] + }, "Primary array is correct"); + }); + + test("extractArray with embedded objects of same type, but from separate attributes", function() { + HomePlanet.reopen({ + reformedVillains: DS.hasMany('superVillain') + }); + + env.registry.register('adapter:home-planet', DS.RESTAdapter); + env.registry.register('serializer:home-planet', TestSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + villains: { embedded: 'always' }, + reformedVillains: { embedded: 'always' } + } + })); + env.registry.register('serializer:super-villain', TestSerializer); + + var serializer = env.container.lookup("serializer:home-planet"); + var json_hash = { + homePlanets: [{ + id: "1", + name: "Earth", + villains: [{ + id: "1", + firstName: "Tom" + },{ + id: "3", + firstName: "Yehuda" + }], + reformedVillains: [{ + id: "2", + firstName: "Alex" + },{ + id: "4", + firstName: "Erik" + }] + },{ + id: "2", + name: "Mars", + villains: [{ + id: "1", + firstName: "Tom" + },{ + id: "3", + firstName: "Yehuda" + }], + reformedVillains: [{ + id: "5", + firstName: "Peter" + },{ + id: "6", + firstName: "Trek" + }] + }] + }; + + var json; + run(function() { + json = serializer.normalizeResponse(env.store, HomePlanet, json_hash, null, 'findAll'); + }); + + deepEqual(json, { + "data": [{ + "id": "1", + "type": "home-planet", + "attributes": { + "name": "Earth" + }, + "relationships": { + "reformedVillains": { + "data": [ + { "id": "2", "type": "super-villain" }, + { "id": "4", "type": "super-villain" } + ] + }, + "villains": { + "data": [ + { "id": "1", "type": "super-villain" }, + { "id": "3", "type": "super-villain" } + ] + } + } + }, { + "id": "2", + "type": "home-planet", + "attributes": { + "name": "Mars" + }, + "relationships": { + "reformedVillains": { + "data": [ + { "id": "5", "type": "super-villain" }, + { "id": "6", "type": "super-villain" } + ] + }, + "villains": { + "data": [ + { "id": "1", "type": "super-villain" }, + { "id": "3", "type": "super-villain" } + ] + } + } + }], + "included": [{ + "id": "1", + "type": "super-villain", + "attributes": { + "firstName": "Tom" + }, + "relationships": {} + }, { + "id": "3", + "type": "super-villain", + "attributes": { + "firstName": "Yehuda" + }, + "relationships": {} + }, { + "id": "2", + "type": "super-villain", + "attributes": { + "firstName": "Alex" + }, + "relationships": {} + }, { + "id": "4", + "type": "super-villain", + "attributes": { + "firstName": "Erik" + }, + "relationships": {} + }, { + "id": "1", + "type": "super-villain", + "attributes": { + "firstName": "Tom" + }, + "relationships": {} + }, { + "id": "3", + "type": "super-villain", + "attributes": { + "firstName": "Yehuda" + }, + "relationships": {} + }, { + "id": "5", + "type": "super-villain", + "attributes": { + "firstName": "Peter" + }, + "relationships": {} + }, { + "id": "6", + "type": "super-villain", + "attributes": { + "firstName": "Trek" + }, + "relationships": {} + }] + }, "Primary array was correct"); + }); + + test("extractSingle with embedded object (belongsTo relationship)", function() { + env.registry.register('adapter:super-villain', DS.RESTAdapter); + env.registry.register('serializer:super-villain', TestSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + secretLab: { embedded: 'always' } + } + })); + //env.registry.register('serializer:secret-lab', TestSerializer); + + var serializer = env.container.lookup("serializer:super-villain"); + + var json_hash = { + superVillain: { + id: "1", + firstName: "Tom", + lastName: "Dale", + homePlanet: "123", + evilMinions: ["1", "2", "3"], + secretLab: { + minionCapacity: 5000, + vicinity: "California, USA", + id: "101" + }, + secretWeapons: [] + } + }; + var json; + + run(function() { + json = serializer.normalizeResponse(env.store, SuperVillain, json_hash, '1', 'find'); + }); + + deepEqual(json, { + "data": { + "id": "1", + "type": "super-villain", + "attributes": { + "firstName": "Tom", + "lastName": "Dale" + }, + "relationships": { + "evilMinions": { + "data": [ + { "id": "1", "type": "evil-minion" }, + { "id": "2", "type": "evil-minion" }, + { "id": "3", "type": "evil-minion" } + ] + }, + "homePlanet": { + "data": { "id": "123", "type": "home-planet" } + }, + "secretLab": { + "data": { "id": "101", "type": "secret-lab" } + }, + "secretWeapons": { + "data": [] + } + } + }, + "included": [{ + "id": "101", + "type": "secret-lab", + "attributes": { + "minionCapacity": 5000, + "vicinity": "California, USA" + }, + "relationships": {} + }] + }); + }); + + test("extractSingle with multiply-nested belongsTo", function() { + env.registry.register('adapter:evil-minion', DS.RESTAdapter); + env.registry.register('serializer:evil-minion', TestSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + superVillain: { embedded: 'always' } + } + })); + env.registry.register('serializer:super-villain', TestSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + homePlanet: { embedded: 'always' } + } + })); + + var serializer = env.container.lookup("serializer:evil-minion"); + var json_hash = { + evilMinion: { + id: "1", + name: "Alex", + superVillain: { + id: "1", + firstName: "Tom", + lastName: "Dale", + evilMinions: ["1"], + homePlanet: { + id: "1", + name: "Umber", + villains: ["1"] + } + } + } + }; + var json; + + run(function() { + json = serializer.normalizeResponse(env.store, EvilMinion, json_hash, '1', 'find'); + }); + + deepEqual(json, { + "data": { + "id": "1", + "type": "evil-minion", + "attributes": { + "name": "Alex" + }, + "relationships": { + "superVillain": { + "data": { "id": "1", "type": "super-villain" } + } + } + }, + "included": [{ + "id": "1", + "type": "super-villain", + "attributes": { + "firstName": "Tom", + "lastName": "Dale" + }, + "relationships": { + "evilMinions": { + "data": [ + { "id": "1", "type": "evil-minion" } + ] + }, + "homePlanet": { + "data": { "id": "1", "type": "home-planet" } + } + } + }, { + "id": "1", + "type": "home-planet", + "attributes": { + "name": "Umber" + }, + "relationships": { + "villains": { + "data": [ + { "id": "1", "type": "super-villain" } + ] + } + } + }] + }, "Primary hash was correct"); + }); + + test("extractSingle with polymorphic hasMany", function() { + SuperVillain.reopen({ + secretWeapons: DS.hasMany("secretWeapon", { polymorphic: true }) + }); + + env.registry.register('adapter:super-villain', DS.RESTAdapter); + env.registry.register('serializer:super-villain', TestSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + secretWeapons: { embedded: 'always' } + } + })); + var serializer = env.container.lookup("serializer:super-villain"); + + var json_hash = { + superVillain: { + id: "1", + firstName: "Tom", + lastName: "Dale", + secretWeapons: [ + { + id: "1", + type: "LightSaber", + name: "Tom's LightSaber", + color: "Red" + }, + { + id: "1", + type: "SecretWeapon", + name: "The Death Star" + } + ] + } + }; + var json; + + run(function() { + json = serializer.normalizeResponse(env.store, SuperVillain, json_hash, '1', 'findAll'); + }); + + deepEqual(json, { + "data": { + "id": "1", + "type": "super-villain", + "attributes": { + "firstName": "Tom", + "lastName": "Dale" + }, + "relationships": { + "secretWeapons": { + "data": [ + { "id": "1", "type": "light-saber" }, + { "id": "1", "type": "secret-weapon" } + ] + } + } + }, + "included": [{ + "id": "1", + "type": "light-saber", + "attributes": { + "color": "Red", + "name": "Tom's LightSaber" + }, + "relationships": {} + }, { + "id": "1", + "type": "secret-weapon", + "attributes": { + "name": "The Death Star" + }, + "relationships": {} + }] + }, "Primary hash was correct"); + }); + + test("extractSingle with polymorphic belongsTo", function() { + SuperVillain.reopen({ + secretLab: DS.belongsTo("secretLab", { polymorphic: true }) + }); + + env.registry.register('adapter:super-villain', DS.RESTAdapter); + env.registry.register('serializer:super-villain', TestSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + secretLab: { embedded: 'always' } + } + })); + var serializer = env.container.lookup("serializer:super-villain"); + + var json_hash = { + superVillain: { + id: "1", + firstName: "Tom", + lastName: "Dale", + secretLab: { + id: "1", + type: "bat-cave", + infiltrated: true + } + } + }; + + var json; + + run(function() { + json = serializer.normalizeResponse(env.store, SuperVillain, json_hash, '1', 'find'); + }); + + deepEqual(json, { + "data": { + "id": "1", + "type": "super-villain", + "attributes": { + "firstName": "Tom", + "lastName": "Dale" + }, + "relationships": { + "secretLab": { + "data": { "id": "1", "type": "bat-cave" } + } + } + }, + "included": [{ + "id": "1", + "type": "bat-cave", + "attributes": { + "infiltrated": true + }, + "relationships": {} + }] + }, "Primary has was correct"); + }); + + test("normalize with custom belongsTo primary key", function() { + env.registry.register('adapter:evil-minion', DS.RESTAdapter); + env.registry.register('serializer:evil-minion', TestSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + superVillain: { embedded: 'always' } + } + })); + env.registry.register('serializer:super-villain', TestSerializer.extend({ + primaryKey: 'custom' + })); + + var serializer = env.container.lookup("serializer:evil-minion"); + var json_hash = { + evil_minion: { + id: "1", + name: "Alex", + superVillain: { + custom: "1", + firstName: "Tom", + lastName: "Dale" + } + } + }; + var json; + + run(function() { + json = serializer.normalizeResponse(env.store, EvilMinion, json_hash, '1', 'find'); + }); + + deepEqual(json, { + "data": { + "id": "1", + "type": "evil-minion", + "attributes": { + "name": "Alex" + }, + "relationships": { + "superVillain": { + "data": { "id": "1", "type": "super-villain" } + } + } + }, + "included": [{ + "id": "1", + "type": "super-villain", + "attributes": { + "firstName": "Tom", + "lastName": "Dale" + }, + "relationships": {} + }] + }, "Primary hash was correct"); + }); + +}