@ember-data/store 4.2.0-alpha.8 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addon/-private/system/core-store.ts +466 -140
- package/addon/-private/system/ds-model-store.ts +63 -12
- package/addon/-private/system/fetch-manager.ts +4 -9
- package/addon/-private/system/model/internal-model.ts +342 -90
- package/addon/-private/system/model/states.js +14 -2
- package/addon/-private/system/record-array-manager.js +30 -3
- package/addon/-private/system/references/belongs-to.ts +26 -11
- package/addon/-private/system/references/has-many.ts +33 -14
- package/addon/-private/system/references/record.ts +23 -9
- package/addon/-private/system/snapshot.ts +48 -22
- package/addon/-private/system/store/finders.js +58 -2
- package/addon/-private/system/store/record-data-store-wrapper.ts +24 -17
- package/addon/-private/ts-interfaces/fetch-manager.ts +0 -4
- package/package.json +10 -10
|
@@ -1,12 +1,20 @@
|
|
|
1
|
+
import { getOwner, setOwner } from '@ember/application';
|
|
1
2
|
import { A, default as EmberArray } from '@ember/array';
|
|
2
3
|
import { assert, inspect } from '@ember/debug';
|
|
3
4
|
import EmberError from '@ember/error';
|
|
4
|
-
import { get } from '@ember/object';
|
|
5
|
+
import { get, set } from '@ember/object';
|
|
5
6
|
import { _backburner as emberBackburner, cancel, run } from '@ember/runloop';
|
|
6
7
|
import { DEBUG } from '@glimmer/env';
|
|
7
8
|
|
|
8
9
|
import RSVP, { Promise } from 'rsvp';
|
|
9
10
|
|
|
11
|
+
import {
|
|
12
|
+
CUSTOM_MODEL_CLASS,
|
|
13
|
+
RECORD_DATA_ERRORS,
|
|
14
|
+
RECORD_DATA_STATE,
|
|
15
|
+
REMOVE_RECORD_ARRAY_MANAGER_LEGACY_COMPAT,
|
|
16
|
+
REQUEST_SERVICE,
|
|
17
|
+
} from '@ember-data/canary-features';
|
|
10
18
|
import { HAS_MODEL_PACKAGE, HAS_RECORD_DATA_PACKAGE } from '@ember-data/private-build-infra';
|
|
11
19
|
import type {
|
|
12
20
|
BelongsToRelationship,
|
|
@@ -23,13 +31,15 @@ import type { JsonApiResource, JsonApiValidationError } from '../../ts-interface
|
|
|
23
31
|
import type { RecordInstance } from '../../ts-interfaces/record-instance';
|
|
24
32
|
import type { FindOptions } from '../../ts-interfaces/store';
|
|
25
33
|
import type { ConfidentDict } from '../../ts-interfaces/utils';
|
|
34
|
+
import coerceId from '../coerce-id';
|
|
26
35
|
import type CoreStore from '../core-store';
|
|
27
36
|
import type Store from '../ds-model-store';
|
|
28
37
|
import { errorsHashToArray } from '../errors-utils';
|
|
38
|
+
import { recordArraysForIdentifier } from '../record-array-manager';
|
|
29
39
|
import recordDataFor from '../record-data-for';
|
|
30
40
|
import { BelongsToReference, HasManyReference, RecordReference } from '../references';
|
|
31
41
|
import Snapshot from '../snapshot';
|
|
32
|
-
import { internalModelFactoryFor } from '../store/internal-model-factory';
|
|
42
|
+
import { internalModelFactoryFor, setRecordIdentifier } from '../store/internal-model-factory';
|
|
33
43
|
import RootState from './states';
|
|
34
44
|
|
|
35
45
|
// move to TS hacks module that we can delete when this is no longer a necessary recast
|
|
@@ -62,6 +72,11 @@ if (HAS_MODEL_PACKAGE) {
|
|
|
62
72
|
};
|
|
63
73
|
}
|
|
64
74
|
|
|
75
|
+
// TODO this should be integrated with the code removal so we can use it together with the if condition
|
|
76
|
+
// and not alongside it
|
|
77
|
+
function isNotCustomModelClass(store: CoreStore | Store): store is Store {
|
|
78
|
+
return !CUSTOM_MODEL_CLASS;
|
|
79
|
+
}
|
|
65
80
|
interface BelongsToMetaWrapper {
|
|
66
81
|
key: string;
|
|
67
82
|
store: CoreStore;
|
|
@@ -218,40 +233,60 @@ export default class InternalModel {
|
|
|
218
233
|
return true;
|
|
219
234
|
}
|
|
220
235
|
|
|
221
|
-
if (
|
|
222
|
-
|
|
236
|
+
if (RECORD_DATA_STATE) {
|
|
237
|
+
if (this.currentState.isLoading) {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
223
240
|
}
|
|
224
241
|
|
|
225
|
-
let isRecordFullyDeleted
|
|
242
|
+
let isRecordFullyDeleted;
|
|
243
|
+
if (RECORD_DATA_STATE) {
|
|
244
|
+
isRecordFullyDeleted = this._isRecordFullyDeleted();
|
|
245
|
+
} else {
|
|
246
|
+
isRecordFullyDeleted = this.currentState.stateName === 'root.deleted.saved';
|
|
247
|
+
}
|
|
226
248
|
return this._isDematerializing || this.hasScheduledDestroy() || this.isDestroyed || isRecordFullyDeleted;
|
|
227
249
|
}
|
|
228
250
|
|
|
229
251
|
_isRecordFullyDeleted(): boolean {
|
|
230
|
-
if (
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
252
|
+
if (RECORD_DATA_STATE) {
|
|
253
|
+
if (this._recordData.isDeletionCommitted && this._recordData.isDeletionCommitted()) {
|
|
254
|
+
return true;
|
|
255
|
+
} else if (
|
|
256
|
+
this._recordData.isNew &&
|
|
257
|
+
this._recordData.isDeleted &&
|
|
258
|
+
this._recordData.isNew() &&
|
|
259
|
+
this._recordData.isDeleted()
|
|
260
|
+
) {
|
|
261
|
+
return true;
|
|
262
|
+
} else {
|
|
263
|
+
return this.currentState.stateName === 'root.deleted.saved';
|
|
264
|
+
}
|
|
239
265
|
} else {
|
|
240
|
-
|
|
266
|
+
// assert here
|
|
267
|
+
return false;
|
|
241
268
|
}
|
|
242
269
|
}
|
|
243
270
|
|
|
244
271
|
isDeleted() {
|
|
245
|
-
if (
|
|
246
|
-
|
|
272
|
+
if (RECORD_DATA_STATE) {
|
|
273
|
+
if (this._recordData.isDeleted) {
|
|
274
|
+
return this._recordData.isDeleted();
|
|
275
|
+
} else {
|
|
276
|
+
return this.currentState.isDeleted;
|
|
277
|
+
}
|
|
247
278
|
} else {
|
|
248
279
|
return this.currentState.isDeleted;
|
|
249
280
|
}
|
|
250
281
|
}
|
|
251
282
|
|
|
252
283
|
isNew() {
|
|
253
|
-
if (
|
|
254
|
-
|
|
284
|
+
if (RECORD_DATA_STATE) {
|
|
285
|
+
if (this._recordData.isNew) {
|
|
286
|
+
return this._recordData.isNew();
|
|
287
|
+
} else {
|
|
288
|
+
return this.currentState.isNew;
|
|
289
|
+
}
|
|
255
290
|
} else {
|
|
256
291
|
return this.currentState.isNew;
|
|
257
292
|
}
|
|
@@ -261,7 +296,73 @@ export default class InternalModel {
|
|
|
261
296
|
if (!this._record && !this._isDematerializing) {
|
|
262
297
|
let { store } = this;
|
|
263
298
|
|
|
264
|
-
|
|
299
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
300
|
+
this._record = store._instantiateRecord(this, this.modelName, this._recordData, this.identifier, properties);
|
|
301
|
+
} else {
|
|
302
|
+
if (isNotCustomModelClass(store)) {
|
|
303
|
+
// lookupFactory should really return an object that creates
|
|
304
|
+
// instances with the injections applied
|
|
305
|
+
let createOptions: any = {
|
|
306
|
+
store,
|
|
307
|
+
_internalModel: this,
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
if (!REQUEST_SERVICE) {
|
|
311
|
+
createOptions.isError = this.isError;
|
|
312
|
+
createOptions.adapterError = this.error;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (properties !== undefined) {
|
|
316
|
+
assert(
|
|
317
|
+
`You passed '${properties}' as properties for record creation instead of an object.`,
|
|
318
|
+
typeof properties === 'object' && properties !== null
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
if ('id' in properties) {
|
|
322
|
+
const id = coerceId(properties.id);
|
|
323
|
+
|
|
324
|
+
if (id !== null) {
|
|
325
|
+
this.setId(id);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// convert relationship Records to RecordDatas before passing to RecordData
|
|
330
|
+
let defs = store._relationshipsDefinitionFor(this.modelName);
|
|
331
|
+
|
|
332
|
+
if (defs !== null) {
|
|
333
|
+
let keys = Object.keys(properties);
|
|
334
|
+
let relationshipValue;
|
|
335
|
+
|
|
336
|
+
for (let i = 0; i < keys.length; i++) {
|
|
337
|
+
let prop = keys[i];
|
|
338
|
+
let def = defs[prop];
|
|
339
|
+
|
|
340
|
+
if (def !== undefined) {
|
|
341
|
+
if (def.kind === 'hasMany') {
|
|
342
|
+
if (DEBUG) {
|
|
343
|
+
assertRecordsPassedToHasMany(properties[prop]);
|
|
344
|
+
}
|
|
345
|
+
relationshipValue = extractRecordDatasFromRecords(properties[prop]);
|
|
346
|
+
} else {
|
|
347
|
+
relationshipValue = extractRecordDataFromRecord(properties[prop]);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
properties[prop] = relationshipValue;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
let additionalCreateOptions = this._recordData._initRecordCreateOptions(properties);
|
|
357
|
+
Object.assign(createOptions, additionalCreateOptions);
|
|
358
|
+
|
|
359
|
+
// ensure that `getOwner(this)` works inside a model instance
|
|
360
|
+
setOwner(createOptions, getOwner(store));
|
|
361
|
+
|
|
362
|
+
this._record = store._modelFactoryFor(this.modelName).create(createOptions);
|
|
363
|
+
setRecordIdentifier(this._record, this.identifier);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
265
366
|
this._triggerDeferredTriggers();
|
|
266
367
|
}
|
|
267
368
|
|
|
@@ -276,8 +377,15 @@ export default class InternalModel {
|
|
|
276
377
|
this._doNotDestroy = false;
|
|
277
378
|
// this has to occur before the internal model is removed
|
|
278
379
|
// for legacy compat.
|
|
380
|
+
if (!REMOVE_RECORD_ARRAY_MANAGER_LEGACY_COMPAT) {
|
|
381
|
+
this.store.recordArrayManager.recordDidChange(this.identifier);
|
|
382
|
+
}
|
|
279
383
|
if (this._record) {
|
|
280
|
-
|
|
384
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
385
|
+
this.store.teardownRecord(this._record);
|
|
386
|
+
} else {
|
|
387
|
+
this._record.destroy();
|
|
388
|
+
}
|
|
281
389
|
}
|
|
282
390
|
|
|
283
391
|
// move to an empty never-loaded state
|
|
@@ -301,15 +409,19 @@ export default class InternalModel {
|
|
|
301
409
|
this.error = null;
|
|
302
410
|
this._previousState = this.currentState;
|
|
303
411
|
this.currentState = RootState.empty;
|
|
304
|
-
|
|
412
|
+
if (REMOVE_RECORD_ARRAY_MANAGER_LEGACY_COMPAT) {
|
|
413
|
+
this.store.recordArrayManager.recordDidChange(this.identifier);
|
|
414
|
+
}
|
|
305
415
|
}
|
|
306
416
|
|
|
307
417
|
deleteRecord() {
|
|
308
418
|
run(() => {
|
|
309
419
|
const backburner = this.store._backburner;
|
|
310
420
|
backburner.run(() => {
|
|
311
|
-
if (
|
|
312
|
-
this._recordData.setIsDeleted
|
|
421
|
+
if (RECORD_DATA_STATE) {
|
|
422
|
+
if (this._recordData.setIsDeleted) {
|
|
423
|
+
this._recordData.setIsDeleted(true);
|
|
424
|
+
}
|
|
313
425
|
}
|
|
314
426
|
|
|
315
427
|
if (this.isNew()) {
|
|
@@ -332,26 +444,50 @@ export default class InternalModel {
|
|
|
332
444
|
let promiseLabel = 'DS: Model#save ' + this;
|
|
333
445
|
let resolver = RSVP.defer<void>(promiseLabel);
|
|
334
446
|
|
|
335
|
-
|
|
336
|
-
|
|
447
|
+
if (REQUEST_SERVICE) {
|
|
448
|
+
// Casting to promise to narrow due to the feature flag paths inside scheduleSave
|
|
449
|
+
return this.store.scheduleSave(this, resolver, options) as Promise<void>;
|
|
450
|
+
} else {
|
|
451
|
+
this.store.scheduleSave(this, resolver, options);
|
|
452
|
+
return resolver.promise;
|
|
453
|
+
}
|
|
337
454
|
}
|
|
338
455
|
|
|
339
456
|
reload(options) {
|
|
340
|
-
if (
|
|
341
|
-
options
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
457
|
+
if (REQUEST_SERVICE) {
|
|
458
|
+
if (!options) {
|
|
459
|
+
options = {};
|
|
460
|
+
}
|
|
461
|
+
let internalModel = this;
|
|
462
|
+
|
|
463
|
+
return internalModel.store._reloadRecord(internalModel, options).then(
|
|
464
|
+
function () {
|
|
465
|
+
//TODO NOW seems like we shouldn't need to do this
|
|
466
|
+
return internalModel;
|
|
467
|
+
},
|
|
468
|
+
function (error) {
|
|
469
|
+
throw error;
|
|
470
|
+
},
|
|
471
|
+
'DS: Model#reload complete, update flags'
|
|
472
|
+
);
|
|
473
|
+
} else {
|
|
474
|
+
let internalModel = this;
|
|
475
|
+
let promiseLabel = 'DS: Model#reload of ' + this;
|
|
476
|
+
|
|
477
|
+
return new Promise(function (resolve) {
|
|
478
|
+
internalModel.send('reloadRecord', { resolve, options });
|
|
479
|
+
}, promiseLabel).then(
|
|
480
|
+
function () {
|
|
481
|
+
internalModel.didCleanError();
|
|
482
|
+
return internalModel;
|
|
483
|
+
},
|
|
484
|
+
function (error) {
|
|
485
|
+
internalModel.didError(error);
|
|
486
|
+
throw error;
|
|
487
|
+
},
|
|
488
|
+
'DS: Model#reload complete, update flags'
|
|
489
|
+
);
|
|
490
|
+
}
|
|
355
491
|
}
|
|
356
492
|
|
|
357
493
|
/*
|
|
@@ -439,8 +575,6 @@ export default class InternalModel {
|
|
|
439
575
|
let identifier =
|
|
440
576
|
resource && resource.data ? identifierCacheFor(this.store).getOrCreateRecordIdentifier(resource.data) : null;
|
|
441
577
|
let relationshipMeta = this.store._relationshipMetaFor(this.modelName, null, key);
|
|
442
|
-
if (!relationshipMeta) return;
|
|
443
|
-
|
|
444
578
|
let store = this.store;
|
|
445
579
|
let parentInternalModel = this;
|
|
446
580
|
let async = relationshipMeta.options.async;
|
|
@@ -731,17 +865,31 @@ export default class InternalModel {
|
|
|
731
865
|
}
|
|
732
866
|
|
|
733
867
|
hasChangedAttributes() {
|
|
734
|
-
if (
|
|
735
|
-
|
|
736
|
-
|
|
868
|
+
if (REQUEST_SERVICE) {
|
|
869
|
+
if (!this.__recordData) {
|
|
870
|
+
// no need to calculate changed attributes when calling `findRecord`
|
|
871
|
+
return false;
|
|
872
|
+
}
|
|
873
|
+
} else {
|
|
874
|
+
if (this.currentState.isLoading) {
|
|
875
|
+
// no need to calculate changed attributes when calling `findRecord`
|
|
876
|
+
return false;
|
|
877
|
+
}
|
|
737
878
|
}
|
|
738
879
|
return this._recordData.hasChangedAttributes();
|
|
739
880
|
}
|
|
740
881
|
|
|
741
882
|
changedAttributes() {
|
|
742
|
-
if (
|
|
743
|
-
|
|
744
|
-
|
|
883
|
+
if (REQUEST_SERVICE) {
|
|
884
|
+
if (!this.__recordData) {
|
|
885
|
+
// no need to calculate changed attributes when calling `findRecord`
|
|
886
|
+
return {};
|
|
887
|
+
}
|
|
888
|
+
} else {
|
|
889
|
+
if (this.currentState.isLoading) {
|
|
890
|
+
// no need to calculate changed attributes when calling `findRecord`
|
|
891
|
+
return {};
|
|
892
|
+
}
|
|
745
893
|
}
|
|
746
894
|
return this._recordData.changedAttributes();
|
|
747
895
|
}
|
|
@@ -776,29 +924,64 @@ export default class InternalModel {
|
|
|
776
924
|
return;
|
|
777
925
|
}
|
|
778
926
|
|
|
779
|
-
|
|
927
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
928
|
+
this.store._notificationManager.notify(this.identifier, 'relationships', key);
|
|
929
|
+
} else {
|
|
930
|
+
if (manyArray) {
|
|
931
|
+
manyArray.notify();
|
|
932
|
+
|
|
933
|
+
//We need to notifyPropertyChange in the adding case because we need to make sure
|
|
934
|
+
//we fetch the newly added record in case it is unloaded
|
|
935
|
+
//TODO(Igor): Consider whether we could do this only if the record state is unloaded
|
|
936
|
+
if (manyArray.isAsync) {
|
|
937
|
+
this._record.notifyPropertyChange(key);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
780
941
|
}
|
|
781
942
|
}
|
|
782
943
|
|
|
783
944
|
notifyBelongsToChange(key: string) {
|
|
784
945
|
if (this.hasRecord) {
|
|
785
|
-
|
|
946
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
947
|
+
this.store._notificationManager.notify(this.identifier, 'relationships', key);
|
|
948
|
+
} else {
|
|
949
|
+
this._record.notifyPropertyChange(key, this._record);
|
|
950
|
+
}
|
|
786
951
|
}
|
|
787
952
|
}
|
|
788
953
|
|
|
789
954
|
notifyPropertyChange(key) {
|
|
790
955
|
if (this.hasRecord) {
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
956
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
957
|
+
// TODO this should likely *mostly* be the `attributes` bucket
|
|
958
|
+
// but it seems for local mutations we rely on computed updating
|
|
959
|
+
// iteself when set. As we design our own thing we may need to change
|
|
960
|
+
// that.
|
|
961
|
+
this.store._notificationManager.notify(this.identifier, 'property', key);
|
|
962
|
+
} else {
|
|
963
|
+
if (key === 'currentState') {
|
|
964
|
+
set(this._record, 'currentState', this.currentState);
|
|
965
|
+
} else {
|
|
966
|
+
this._record.notifyPropertyChange(key);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
796
969
|
}
|
|
797
970
|
}
|
|
798
971
|
|
|
799
972
|
notifyStateChange(key?) {
|
|
973
|
+
assert('Cannot notify state change if Record Data State flag is not on', !!RECORD_DATA_STATE);
|
|
800
974
|
if (this.hasRecord) {
|
|
801
|
-
|
|
975
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
976
|
+
this.store._notificationManager.notify(this.identifier, 'state');
|
|
977
|
+
} else {
|
|
978
|
+
if (!key || key === 'isNew') {
|
|
979
|
+
(this.getRecord() as DSModel).notifyPropertyChange('isNew');
|
|
980
|
+
}
|
|
981
|
+
if (!key || key === 'isDeleted') {
|
|
982
|
+
(this.getRecord() as DSModel).notifyPropertyChange('isDeleted');
|
|
983
|
+
}
|
|
984
|
+
}
|
|
802
985
|
}
|
|
803
986
|
if (!key || key === 'isDeletionCommitted') {
|
|
804
987
|
this.store.recordArrayManager.recordDidChange(this.identifier);
|
|
@@ -874,13 +1057,17 @@ export default class InternalModel {
|
|
|
874
1057
|
}
|
|
875
1058
|
|
|
876
1059
|
this.currentState = state;
|
|
877
|
-
if (
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
1060
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
1061
|
+
if (this.hasRecord && typeof this._record.notifyPropertyChange === 'function') {
|
|
1062
|
+
// TODO refactor Model to have all flags pull from the notification manager
|
|
1063
|
+
// and for currentState.stateName to be constructed from flag state.
|
|
1064
|
+
// Probably just port this work from ember-m3
|
|
1065
|
+
// After that we can eliminate this.
|
|
1066
|
+
this.notifyStateChange('currentState');
|
|
1067
|
+
// this._record.notifyPropertyChange('currentState');
|
|
1068
|
+
}
|
|
1069
|
+
} else {
|
|
1070
|
+
this.notifyPropertyChange('currentState');
|
|
884
1071
|
}
|
|
885
1072
|
|
|
886
1073
|
for (i = 0, l = setups.length; i < l; i++) {
|
|
@@ -1031,14 +1218,42 @@ export default class InternalModel {
|
|
|
1031
1218
|
}
|
|
1032
1219
|
|
|
1033
1220
|
if (didChange && this.hasRecord) {
|
|
1034
|
-
|
|
1221
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
1222
|
+
this.store._notificationManager.notify(this.identifier, 'identity');
|
|
1223
|
+
} else {
|
|
1224
|
+
this.notifyPropertyChange('id');
|
|
1225
|
+
}
|
|
1035
1226
|
}
|
|
1036
1227
|
this._isUpdatingId = false;
|
|
1037
1228
|
}
|
|
1038
1229
|
|
|
1039
|
-
didError() {
|
|
1230
|
+
didError(error) {
|
|
1231
|
+
if (!REQUEST_SERVICE) {
|
|
1232
|
+
this.error = error;
|
|
1233
|
+
this.isError = true;
|
|
1234
|
+
|
|
1235
|
+
if (this.hasRecord) {
|
|
1236
|
+
this._record.setProperties({
|
|
1237
|
+
isError: true,
|
|
1238
|
+
adapterError: error,
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1040
1243
|
|
|
1041
|
-
didCleanError() {
|
|
1244
|
+
didCleanError() {
|
|
1245
|
+
if (!REQUEST_SERVICE) {
|
|
1246
|
+
this.error = null;
|
|
1247
|
+
this.isError = false;
|
|
1248
|
+
|
|
1249
|
+
if (this.hasRecord) {
|
|
1250
|
+
this._record.setProperties({
|
|
1251
|
+
isError: false,
|
|
1252
|
+
adapterError: null,
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1042
1257
|
|
|
1043
1258
|
/*
|
|
1044
1259
|
If the adapter did not return a hash in response to a commit,
|
|
@@ -1048,19 +1263,29 @@ export default class InternalModel {
|
|
|
1048
1263
|
adapterDidCommit(data) {
|
|
1049
1264
|
this.didCleanError();
|
|
1050
1265
|
|
|
1051
|
-
this._recordData.didCommit(data);
|
|
1266
|
+
let changedKeys = this._recordData.didCommit(data);
|
|
1267
|
+
|
|
1052
1268
|
this.send('didCommit');
|
|
1053
1269
|
this.store.recordArrayManager.recordDidChange(this.identifier);
|
|
1054
1270
|
|
|
1055
1271
|
if (!data) {
|
|
1056
1272
|
return;
|
|
1057
1273
|
}
|
|
1058
|
-
|
|
1274
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
1275
|
+
this.store._notificationManager.notify(this.identifier, 'attributes');
|
|
1276
|
+
} else {
|
|
1277
|
+
this._record._notifyProperties(changedKeys);
|
|
1278
|
+
}
|
|
1059
1279
|
}
|
|
1060
1280
|
|
|
1061
1281
|
hasErrors() {
|
|
1062
|
-
if (
|
|
1063
|
-
|
|
1282
|
+
if (RECORD_DATA_ERRORS) {
|
|
1283
|
+
if (this._recordData.getErrors) {
|
|
1284
|
+
return this._recordData.getErrors(this.identifier).length > 0;
|
|
1285
|
+
} else {
|
|
1286
|
+
let errors = (this.getRecord() as DSModel).errors;
|
|
1287
|
+
return errors.length > 0;
|
|
1288
|
+
}
|
|
1064
1289
|
} else {
|
|
1065
1290
|
let errors = (this.getRecord() as DSModel).errors;
|
|
1066
1291
|
return errors.length > 0;
|
|
@@ -1069,37 +1294,54 @@ export default class InternalModel {
|
|
|
1069
1294
|
|
|
1070
1295
|
// FOR USE DURING COMMIT PROCESS
|
|
1071
1296
|
adapterDidInvalidate(parsedErrors, error) {
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
if (
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
(
|
|
1297
|
+
if (RECORD_DATA_ERRORS) {
|
|
1298
|
+
// TODO @runspired this should be handled by RecordState
|
|
1299
|
+
// and errors should be dirtied but lazily fetch if at
|
|
1300
|
+
// all possible. We should only notify errors here.
|
|
1301
|
+
let attribute;
|
|
1302
|
+
if (error && parsedErrors) {
|
|
1303
|
+
if (!this._recordData.getErrors) {
|
|
1304
|
+
for (attribute in parsedErrors) {
|
|
1305
|
+
if (hasOwnProperty.call(parsedErrors, attribute)) {
|
|
1306
|
+
(this.getRecord() as DSModel).errors._add(attribute, parsedErrors[attribute]);
|
|
1307
|
+
}
|
|
1081
1308
|
}
|
|
1082
1309
|
}
|
|
1083
|
-
}
|
|
1084
1310
|
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1311
|
+
let jsonApiErrors: JsonApiValidationError[] = errorsHashToArray(parsedErrors);
|
|
1312
|
+
this.send('becameInvalid');
|
|
1313
|
+
if (jsonApiErrors.length === 0) {
|
|
1314
|
+
jsonApiErrors = [{ title: 'Invalid Error', detail: '', source: { pointer: '/data' } }];
|
|
1315
|
+
}
|
|
1316
|
+
this._recordData.commitWasRejected(this.identifier, jsonApiErrors);
|
|
1317
|
+
} else {
|
|
1318
|
+
this.send('becameError');
|
|
1319
|
+
this._recordData.commitWasRejected(this.identifier);
|
|
1089
1320
|
}
|
|
1090
|
-
this._recordData.commitWasRejected(this.identifier, jsonApiErrors);
|
|
1091
1321
|
} else {
|
|
1092
|
-
|
|
1093
|
-
|
|
1322
|
+
let attribute;
|
|
1323
|
+
|
|
1324
|
+
for (attribute in parsedErrors) {
|
|
1325
|
+
if (hasOwnProperty.call(parsedErrors, attribute)) {
|
|
1326
|
+
(this.getRecord() as DSModel).errors._add(attribute, parsedErrors[attribute]);
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
this.send('becameInvalid');
|
|
1331
|
+
|
|
1332
|
+
this._recordData.commitWasRejected();
|
|
1094
1333
|
}
|
|
1095
1334
|
}
|
|
1096
1335
|
|
|
1097
1336
|
notifyErrorsChange() {
|
|
1098
|
-
|
|
1337
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
1338
|
+
this.store._notificationManager.notify(this.identifier, 'errors');
|
|
1339
|
+
}
|
|
1099
1340
|
}
|
|
1100
1341
|
|
|
1101
|
-
adapterDidError() {
|
|
1342
|
+
adapterDidError(error) {
|
|
1102
1343
|
this.send('becameError');
|
|
1344
|
+
this.didError(error);
|
|
1103
1345
|
|
|
1104
1346
|
this._recordData.commitWasRejected();
|
|
1105
1347
|
}
|
|
@@ -1146,6 +1388,16 @@ export default class InternalModel {
|
|
|
1146
1388
|
}
|
|
1147
1389
|
}
|
|
1148
1390
|
|
|
1391
|
+
// in production code, this is only accesssed in `record-array-manager`
|
|
1392
|
+
// if REMOVE_RECORD_ARRAY_MANAGER_LEGACY_COMPAT is also false
|
|
1393
|
+
if (!REMOVE_RECORD_ARRAY_MANAGER_LEGACY_COMPAT) {
|
|
1394
|
+
Object.defineProperty(InternalModel.prototype, '_recordArrays', {
|
|
1395
|
+
get() {
|
|
1396
|
+
return recordArraysForIdentifier(this.identifier);
|
|
1397
|
+
},
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1149
1401
|
function handleCompletedRelationshipRequest(internalModel, key, relationship, value, error) {
|
|
1150
1402
|
delete internalModel._relationshipPromisesCache[key];
|
|
1151
1403
|
relationship.state.shouldForceReload = false;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { assert } from '@ember/debug';
|
|
5
5
|
|
|
6
|
+
import { CUSTOM_MODEL_CLASS, REQUEST_SERVICE } from '@ember-data/canary-features';
|
|
6
7
|
/*
|
|
7
8
|
This file encapsulates the various states that a record can transition
|
|
8
9
|
through during its lifecycle.
|
|
@@ -431,7 +432,11 @@ createdState.uncommitted.rollback = function (internalModel) {
|
|
|
431
432
|
|
|
432
433
|
createdState.uncommitted.pushedData = function (internalModel) {
|
|
433
434
|
// TODO @runspired consider where to do this once we kill off state machine
|
|
434
|
-
|
|
435
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
436
|
+
internalModel.store._notificationManager.notify(internalModel.identifier, 'identity');
|
|
437
|
+
} else {
|
|
438
|
+
internalModel.notifyPropertyChange('id');
|
|
439
|
+
}
|
|
435
440
|
internalModel.transitionTo('loaded.updated.uncommitted');
|
|
436
441
|
internalModel.triggerLater('didLoad');
|
|
437
442
|
};
|
|
@@ -495,6 +500,9 @@ const RootState = {
|
|
|
495
500
|
|
|
496
501
|
// EVENTS
|
|
497
502
|
loadingData(internalModel, promise) {
|
|
503
|
+
if (!REQUEST_SERVICE) {
|
|
504
|
+
internalModel._promiseProxy = promise;
|
|
505
|
+
}
|
|
498
506
|
internalModel.transitionTo('loading');
|
|
499
507
|
},
|
|
500
508
|
|
|
@@ -585,7 +593,11 @@ const RootState = {
|
|
|
585
593
|
internalModel.transitionTo('updated.inFlight');
|
|
586
594
|
},
|
|
587
595
|
|
|
588
|
-
reloadRecord() {
|
|
596
|
+
reloadRecord(internalModel, { resolve, options }) {
|
|
597
|
+
if (!REQUEST_SERVICE) {
|
|
598
|
+
resolve(internalModel.store._reloadRecord(internalModel, options));
|
|
599
|
+
}
|
|
600
|
+
},
|
|
589
601
|
|
|
590
602
|
deleteRecord(internalModel) {
|
|
591
603
|
internalModel.transitionTo('deleted.uncommitted');
|