diff --git a/addon/adapters/firebase.js b/addon/adapters/firebase.js index ef15427c..3027471d 100644 --- a/addon/adapters/firebase.js +++ b/addon/adapters/firebase.js @@ -415,13 +415,15 @@ export default DS.Adapter.extend(Waitable, { var serializedRecord = snapshot.serialize({ includeId: (lastPiece !== snapshot.id) // record has no firebase `key` in path }); + const serializer = store.serializerFor(typeClass.modelName); return new Promise((resolve, reject) => { var relationshipsToSave = []; // first we remove all relationships data from the serialized record, we backup the // removed data so that we can save it at a later stage. snapshot.record.eachRelationship((key, relationship) => { - const data = serializedRecord[key]; + const relationshipKey = serializer.keyForRelationship(key); + const data = serializedRecord[relationshipKey]; const isEmbedded = this.isRelationshipEmbedded(store, typeClass.modelName, relationship); const hasMany = relationship.kind === 'hasMany'; if (hasMany || isEmbedded) { @@ -433,7 +435,7 @@ export default DS.Adapter.extend(Waitable, { hasMany:hasMany }); } - delete serializedRecord[key]; + delete serializedRecord[relationshipKey]; } }); var reportError = (errors) => { @@ -544,7 +546,8 @@ export default DS.Adapter.extend(Waitable, { * version of the record */ _saveHasManyRecord(store, typeClass, relationship, parentRef, id) { - var ref = this._getRelationshipRef(parentRef, relationship.key, id); + const serializer = store.serializerFor(typeClass.modelName); + var ref = this._getRelationshipRef(parentRef, serializer.keyForRelationship(relationship.key), id); var record = store.peekRecord(relationship.type, id); var isEmbedded = this.isRelationshipEmbedded(store, typeClass.modelName, relationship); if (isEmbedded) { diff --git a/addon/serializers/firebase.js b/addon/serializers/firebase.js index e839bc66..68250f9b 100644 --- a/addon/serializers/firebase.js +++ b/addon/serializers/firebase.js @@ -35,7 +35,6 @@ export default DS.JSONSerializer.extend(DS.EmbeddedRecordsMixin, { */ extractRelationships(modelClass, payload) { this.normalizeRelationships(modelClass, payload); - return this._super(modelClass, payload); }, @@ -93,17 +92,19 @@ export default DS.JSONSerializer.extend(DS.EmbeddedRecordsMixin, { */ normalizeRelationships(modelClass, payload) { modelClass.eachRelationship((key, meta) => { - if (meta.kind === 'hasMany') { - if (payload.hasOwnProperty(key)) { + let relationshipKey = this.keyForRelationship(key, meta.kind, 'deserialize'); + if (meta.kind === 'hasMany') { + if (payload.hasOwnProperty(relationshipKey)) { + let relationshipPayload = payload[relationshipKey]; // embedded if (this.hasDeserializeRecordsOption(key)) { - if (typeof payload[key] === 'object' && !Ember.isArray(payload[key])) { - payload[key] = Object.keys(payload[key]).map((id) => { - return assign({ id: id }, payload[key][id]); + if (typeof relationshipPayload === 'object' && !Ember.isArray(relationshipPayload)) { + relationshipPayload = Object.keys(relationshipPayload).map((id) => { + return assign({ id: id }, relationshipPayload[id]); }); - } else if (Ember.isArray(payload[key])) { - payload[key] = this._addNumericIdsToEmbeddedArray(payload[key]); + } else if (Ember.isArray(relationshipPayload)) { + relationshipPayload = this._addNumericIdsToEmbeddedArray(relationshipPayload); } else { throw new Error(`${modelClass.toString()} relationship ${meta.kind}('${meta.type}') must contain embedded records with an \`id\`. Example: { "${key}": { "${meta.type}_1": { "id": "${meta.type}_1" } } } instead got: ${JSON.stringify(payload[key])}`); } @@ -111,15 +112,16 @@ export default DS.JSONSerializer.extend(DS.EmbeddedRecordsMixin, { // normalized else { - if (typeof payload[key] === 'object' && !Ember.isArray(payload[key])) { - payload[key] = Object.keys(payload[key]); - } else if (Ember.isArray(payload[key])) { - payload[key] = this._convertBooleanArrayToIds(payload[key]); + if (typeof relationshipPayload === 'object' && !Ember.isArray(relationshipPayload)) { + relationshipPayload = Object.keys(relationshipPayload); + } else if (Ember.isArray(relationshipPayload)) { + relationshipPayload = this._convertBooleanArrayToIds(relationshipPayload); } else { throw new Error(`${modelClass.toString()} relationship ${meta.kind}('${meta.type}') must be a key/value map. Example: { "${key}": { "${meta.type}_1": true } } instead got: ${JSON.stringify(payload[key])}`); } } + payload[relationshipKey] = relationshipPayload; } // hasMany property is not present @@ -127,14 +129,14 @@ export default DS.JSONSerializer.extend(DS.EmbeddedRecordsMixin, { // (i.e. it will never send `comments: null`) so we need to // force the empty relationship else { - payload[key] = []; + payload[relationshipKey] = []; } } if (meta.kind === 'belongsTo') { - if (!payload.hasOwnProperty(key)) { + if (!payload.hasOwnProperty(relationshipKey)) { // server wont send property if it was made null elsewhere - payload[key] = null; + payload[relationshipKey] = null; } } }); diff --git a/changelog.txt b/changelog.txt index e69de29b..8ae5a570 100644 --- a/changelog.txt +++ b/changelog.txt @@ -0,0 +1 @@ +fixed - EmberFire now respects the serializer's `keyForRelationship` method. diff --git a/tests/integration/updating-records-test.js b/tests/integration/updating-records-test.js index 5a3b9a24..f890ba9c 100644 --- a/tests/integration/updating-records-test.js +++ b/tests/integration/updating-records-test.js @@ -305,7 +305,6 @@ describe('Integration: FirebaseAdapter - Updating records', function() { parentId = parentRecord.get('id'); embeddedId = embeddedRecord.get('id'); parentRecord.get('children').addObject(embeddedRecord); - // debugger; parentRecord.save().then(function() { reference.once('value', function(snapshot) { parentData = snapshot.val().treeNodes[parentId]; @@ -515,6 +514,63 @@ describe('Integration: FirebaseAdapter - Updating records', function() { }); // embedded belongsTo records + it('respects keyForRelationship on belongsTo', function(done) { + const reference = firebaseTestRef.child('nonStandardKeys'); + adapter._ref = reference; + store.serializerFor('application').keyForRelationship = function(key) { + return Ember.String.capitalize(key); + }; + + Ember.run(function() { + let user = store.createRecord('user', { + firstName: 'John' + }); + + let newPost = store.createRecord('post', { + title: 'New Post', + user: user + }); + + newPost.save().then(() => { + reference.once('value', fbSnapshot => { + const postData = fbSnapshot.val().posts[newPost.get('id')]; + expect(postData.User).to.equal(user.get('id')); + done(); + }); + }); + }); + }); + + it('respects keyForRelationship on hasMany', function(done) { + const reference = firebaseTestRef.child('nonStandardKeys'); + adapter._ref = reference; + store.serializerFor('application').keyForRelationship = function(key) { + return Ember.String.capitalize(key); + }; + + Ember.run(function() { + let post = store.createRecord('post', { + title: 'New Post' + }); + + let comment = store.createRecord('comment', { + body: 'A comment' + }); + + post.save().then(() => { + return comment.save(); + }).then(() => { + post.set('comments', [comment]); + return post.save(); + }).then(() => { + reference.once('value', fbSnapshot => { + const postData = fbSnapshot.val().posts[post.get('id')]; + expect(postData.Comments).to.have.all.keys([comment.get('id')]); + done(); + }); + }); + }); + }); }); }); diff --git a/tests/unit/serializers/firebase-test.js b/tests/unit/serializers/firebase-test.js index 7c12c018..a600611d 100644 --- a/tests/unit/serializers/firebase-test.js +++ b/tests/unit/serializers/firebase-test.js @@ -1,4 +1,5 @@ /* jshint expr:true */ +import Ember from 'ember'; import { expect } from 'chai'; import { describeModule, @@ -251,6 +252,47 @@ describeModule( }); }); + it('respects keyForRelationship in belongsTo', function() { + let serializer = this.subject(); + serializer.keyForRelationship = function(key) { + return Ember.String.capitalize(key); + }; + + let { data } = serializer.normalize(Post, { + id: 'post_1', + published: 1395162147646, + User: 'aputinski' + }); + + expect(data.relationships.user).to.deep.equal({ + data: { type: 'user', id: 'aputinski' } + }); + }); + + it('respects keyForRelationship in hasMany', function() { + let serializer = this.subject(); + serializer.keyForRelationship = function(key) { + return Ember.String.capitalize(key); + }; + + let { data } = serializer.normalize(Post, { + id: 'post_1', + published: 1395162147646, + body: 'This is the first FireBlog post!', + title: 'Post 1', + Comments: { + comment_1: true, + comment_2: true + }, + }); + + expect(data.relationships.comments).to.deep.equal({ + data: [ + { type: 'comment', id: 'comment_1' }, + { type: 'comment', id: 'comment_2' } + ] + }); + }); }); // normalized relationships describe('invalid data', function () {