From 247061d953097d11295faa88368a36da6c3b4880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Molini=C3=A9?= Date: Thu, 20 Jul 2023 16:10:46 +0200 Subject: [PATCH] changedAttributes still return the dirty attributes until the record has been fully committed (#476) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * changedAttributes still return the dirty attributes until the record has been fully committed * review fixes --------- Co-authored-by: Vincent MoliniƩ --- addon/record-data.js | 15 ++++-- tests/dummy/app/models/component.js | 1 + tests/dummy/app/serializers/component.js | 20 ++++++- tests/unit/serialize_test.js | 67 ++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 5 deletions(-) diff --git a/addon/record-data.js b/addon/record-data.js index 4353b012..cff5cea0 100644 --- a/addon/record-data.js +++ b/addon/record-data.js @@ -618,9 +618,7 @@ export default class FragmentRecordData extends RecordData { } hasChangedFragments() { - return ( - this.__fragments !== null && Object.keys(this.__fragments).length > 0 - ); + return Object.keys(this._fragmentsOrInFlight).length > 0; } isFragmentDirty(key) { @@ -669,7 +667,9 @@ export default class FragmentRecordData extends RecordData { changedFragments() { const diffData = Object.create(null); - for (const [key, newFragment] of Object.entries(this._fragments)) { + for (const [key, newFragment] of Object.entries( + this._fragmentsOrInFlight + )) { const behavior = this._fragmentBehavior[key]; const oldFragment = key in this._inFlightFragments @@ -1053,6 +1053,13 @@ export default class FragmentRecordData extends RecordData { this.__inFlightFragments = v; } + get _fragmentsOrInFlight() { + return this.__inFlightFragments && + Object.keys(this.__inFlightFragments).length > 0 + ? this.__inFlightFragments + : this.__fragments || {}; + } + /** * Fragment array instances * diff --git a/tests/dummy/app/models/component.js b/tests/dummy/app/models/component.js index 58bdc860..31ce5bdf 100644 --- a/tests/dummy/app/models/component.js +++ b/tests/dummy/app/models/component.js @@ -2,6 +2,7 @@ import Model, { attr } from '@ember-data/model'; import { fragment } from 'ember-data-model-fragments/attributes'; export default class Component extends Model { + @attr('string') name; @attr('string') type; @fragment('component-options', { polymorphic: true, diff --git a/tests/dummy/app/serializers/component.js b/tests/dummy/app/serializers/component.js index 33d31a20..40797a43 100644 --- a/tests/dummy/app/serializers/component.js +++ b/tests/dummy/app/serializers/component.js @@ -1,3 +1,21 @@ import JSONAPISerializer from '@ember-data/serializer/json-api'; -export default class extends JSONAPISerializer {} +export default class extends JSONAPISerializer { + serialize(snapshot, ...args) { + const data = super.serialize(snapshot, ...args); + const { record } = snapshot; + + if (data.data?.attributes) { + // NOTICE: Remove all the unchanged attributes in the payload. + const changedAttributes = Object.keys(record.changedAttributes()); + + Object.keys(data.data.attributes).forEach((attributeName) => { + if (!changedAttributes.includes(attributeName)) { + delete data.data.attributes[attributeName]; + } + }); + } + + return data; + } +} diff --git a/tests/unit/serialize_test.js b/tests/unit/serialize_test.js index 5ba1e008..75fc833e 100644 --- a/tests/unit/serialize_test.js +++ b/tests/unit/serialize_test.js @@ -4,6 +4,7 @@ import { setupApplicationTest } from '../helpers'; import JSONSerializer from '@ember-data/serializer/json'; import Person from 'dummy/models/person'; import { fragmentArray, array } from 'ember-data-model-fragments/attributes'; +import Pretender from 'pretender'; // eslint-disable-next-line ember/use-ember-data-rfc-395-imports import DS from 'ember-data'; let store, owner; @@ -319,4 +320,70 @@ module('unit - Serialization', function (hooks) { 'fragment property values are normalized' ); }); + + module('when saving the record', function (saveHooks) { + let server; + + saveHooks.beforeEach(function () { + server = new Pretender(); + }); + + saveHooks.afterEach(function () { + server.shutdown(); + }); + + test('changedAttributes should have the same result when serialized as before the save is called', async function (assert) { + assert.expect(3); + + store.pushPayload('component', { + data: { + type: 'components', + id: 1, + attributes: { + name: 'mine', + type: 'text', + options: { + fontFamily: 'roman', + fontSize: 12, + }, + }, + }, + }); + const component = store.peekRecord('component', 1); + + component.options.fontFamily = 'sans-serif'; + + assert.deepEqual(component.changedAttributes(), { + options: [ + { + fontFamily: 'roman', + fontSize: 12, + }, + { + fontFamily: 'sans-serif', + fontSize: 12, + }, + ], + }); + + server.put('/components/1', (request) => { + assert.deepEqual(JSON.parse(request.requestBody), { + data: { + type: 'components', + attributes: { + options: { + fontFamily: 'sans-serif', + fontSize: 12, + }, + }, + }, + }); + return [204, { 'Content-Type': 'application/json' }, '{}']; + }); + + await component.save(); + + assert.deepEqual(component.changedAttributes(), {}); + }); + }); });