@ember-data/store 4.1.0 → 4.2.0-alpha.3
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 +135 -495
- package/addon/-private/system/ds-model-store.ts +12 -63
- package/addon/-private/system/model/internal-model.ts +90 -342
- package/addon/-private/system/model/states.js +2 -14
- package/addon/-private/system/record-array-manager.js +3 -30
- package/addon/-private/system/references/belongs-to.ts +11 -26
- package/addon/-private/system/references/has-many.ts +14 -33
- package/addon/-private/system/references/record.ts +9 -23
- package/addon/-private/system/references/reference.ts +3 -25
- package/addon/-private/system/snapshot.ts +22 -48
- package/addon/-private/system/store/finders.js +2 -58
- package/addon/-private/system/store/record-data-store-wrapper.ts +17 -24
- package/package.json +8 -8
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { getOwner } from '@ember/application';
|
|
5
5
|
import { A } from '@ember/array';
|
|
6
6
|
import { assert, deprecate, inspect, warn } from '@ember/debug';
|
|
7
|
-
import { computed, defineProperty,
|
|
7
|
+
import { computed, defineProperty, set } from '@ember/object';
|
|
8
8
|
import { _backburner as emberBackburner } from '@ember/runloop';
|
|
9
9
|
import type { Backburner } from '@ember/runloop/-private/backburner';
|
|
10
10
|
import Service from '@ember/service';
|
|
@@ -14,14 +14,8 @@ import { DEBUG } from '@glimmer/env';
|
|
|
14
14
|
import Ember from 'ember';
|
|
15
15
|
|
|
16
16
|
import require from 'require';
|
|
17
|
-
import { all, default as RSVP,
|
|
17
|
+
import { all, default as RSVP, Promise, resolve } from 'rsvp';
|
|
18
18
|
|
|
19
|
-
import {
|
|
20
|
-
CUSTOM_MODEL_CLASS,
|
|
21
|
-
RECORD_DATA_ERRORS,
|
|
22
|
-
RECORD_DATA_STATE,
|
|
23
|
-
REQUEST_SERVICE,
|
|
24
|
-
} from '@ember-data/canary-features';
|
|
25
19
|
import {
|
|
26
20
|
HAS_ADAPTER_PACKAGE,
|
|
27
21
|
HAS_EMBER_DATA_PACKAGE,
|
|
@@ -30,7 +24,6 @@ import {
|
|
|
30
24
|
} from '@ember-data/private-build-infra';
|
|
31
25
|
import {
|
|
32
26
|
DEPRECATE_DEFAULT_ADAPTER,
|
|
33
|
-
DEPRECATE_DEFAULT_SERIALIZER,
|
|
34
27
|
DEPRECATE_LEGACY_TEST_REGISTRATIONS,
|
|
35
28
|
} from '@ember-data/private-build-infra/deprecations';
|
|
36
29
|
import type {
|
|
@@ -69,7 +62,6 @@ import constructResource from '../utils/construct-resource';
|
|
|
69
62
|
import promiseRecord from '../utils/promise-record';
|
|
70
63
|
import edBackburner from './backburner';
|
|
71
64
|
import coerceId, { ensureStringId } from './coerce-id';
|
|
72
|
-
import { errorsArrayToHash } from './errors-utils';
|
|
73
65
|
import FetchManager, { SaveOp } from './fetch-manager';
|
|
74
66
|
import type InternalModel from './model/internal-model';
|
|
75
67
|
import {
|
|
@@ -87,8 +79,7 @@ import NotificationManager from './record-notification-manager';
|
|
|
87
79
|
import type { BelongsToReference, HasManyReference } from './references';
|
|
88
80
|
import { RecordReference } from './references';
|
|
89
81
|
import type RequestCache from './request-cache';
|
|
90
|
-
import type { default as Snapshot
|
|
91
|
-
import { _bind, _guard, _objectIsAlive, guardDestroyedStore } from './store/common';
|
|
82
|
+
import type { default as Snapshot } from './snapshot';
|
|
92
83
|
import { _find, _findAll, _findBelongsTo, _findHasMany, _findMany, _query, _queryRecord } from './store/finders';
|
|
93
84
|
import {
|
|
94
85
|
internalModelFactoryFor,
|
|
@@ -97,7 +88,6 @@ import {
|
|
|
97
88
|
setRecordIdentifier,
|
|
98
89
|
} from './store/internal-model-factory';
|
|
99
90
|
import RecordDataStoreWrapper from './store/record-data-store-wrapper';
|
|
100
|
-
import { normalizeResponseHelper } from './store/serializer-response';
|
|
101
91
|
|
|
102
92
|
type RecordDataConstruct = typeof RecordDataClass;
|
|
103
93
|
let _RecordData: RecordDataConstruct | undefined;
|
|
@@ -311,13 +301,9 @@ abstract class CoreStore extends Service {
|
|
|
311
301
|
constructor() {
|
|
312
302
|
super(...arguments);
|
|
313
303
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
if (CUSTOM_MODEL_CLASS) {
|
|
318
|
-
this._notificationManager = new NotificationManager(this);
|
|
319
|
-
this.__recordDataFor = this.__recordDataFor.bind(this);
|
|
320
|
-
}
|
|
304
|
+
this._fetchManager = new FetchManager(this);
|
|
305
|
+
this._notificationManager = new NotificationManager(this);
|
|
306
|
+
this.__recordDataFor = this.__recordDataFor.bind(this);
|
|
321
307
|
|
|
322
308
|
if (DEBUG) {
|
|
323
309
|
if (HAS_EMBER_DATA_PACKAGE && HAS_SERIALIZER_PACKAGE) {
|
|
@@ -422,10 +408,7 @@ abstract class CoreStore extends Service {
|
|
|
422
408
|
}
|
|
423
409
|
|
|
424
410
|
getRequestStateService(): RequestCache {
|
|
425
|
-
|
|
426
|
-
return this._fetchManager.requestCache;
|
|
427
|
-
}
|
|
428
|
-
assert('RequestService is not available unless the feature flag is on and running on a canary build', false);
|
|
411
|
+
return this._fetchManager.requestCache;
|
|
429
412
|
}
|
|
430
413
|
|
|
431
414
|
/**
|
|
@@ -449,55 +432,51 @@ abstract class CoreStore extends Service {
|
|
|
449
432
|
identifier: StableRecordIdentifier,
|
|
450
433
|
properties?: { [key: string]: any }
|
|
451
434
|
) {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
);
|
|
435
|
+
// assert here
|
|
436
|
+
if (properties !== undefined) {
|
|
437
|
+
assert(
|
|
438
|
+
`You passed '${properties}' as properties for record creation instead of an object.`,
|
|
439
|
+
typeof properties === 'object' && properties !== null
|
|
440
|
+
);
|
|
459
441
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
442
|
+
if ('id' in properties) {
|
|
443
|
+
internalModel.setId(properties.id);
|
|
444
|
+
}
|
|
463
445
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
if (def !== undefined) {
|
|
476
|
-
if (def.kind === 'hasMany') {
|
|
477
|
-
if (DEBUG) {
|
|
478
|
-
assertRecordsPassedToHasMany(properties[prop]);
|
|
479
|
-
}
|
|
480
|
-
relationshipValue = extractRecordDatasFromRecords(properties[prop]);
|
|
481
|
-
} else {
|
|
482
|
-
relationshipValue = extractRecordDataFromRecord(properties[prop]);
|
|
483
|
-
}
|
|
446
|
+
// convert relationship Records to RecordDatas before passing to RecordData
|
|
447
|
+
let defs = this._relationshipsDefinitionFor(modelName);
|
|
448
|
+
|
|
449
|
+
if (defs !== null) {
|
|
450
|
+
let keys = Object.keys(properties);
|
|
451
|
+
let relationshipValue;
|
|
452
|
+
|
|
453
|
+
for (let i = 0; i < keys.length; i++) {
|
|
454
|
+
let prop = keys[i];
|
|
455
|
+
let def = defs[prop];
|
|
484
456
|
|
|
485
|
-
|
|
457
|
+
if (def !== undefined) {
|
|
458
|
+
if (def.kind === 'hasMany') {
|
|
459
|
+
if (DEBUG) {
|
|
460
|
+
assertRecordsPassedToHasMany(properties[prop]);
|
|
461
|
+
}
|
|
462
|
+
relationshipValue = extractRecordDatasFromRecords(properties[prop]);
|
|
463
|
+
} else {
|
|
464
|
+
relationshipValue = extractRecordDataFromRecord(properties[prop]);
|
|
486
465
|
}
|
|
466
|
+
|
|
467
|
+
properties[prop] = relationshipValue;
|
|
487
468
|
}
|
|
488
469
|
}
|
|
489
470
|
}
|
|
490
|
-
|
|
491
|
-
// TODO guard against initRecordOptions no being there
|
|
492
|
-
let createOptions = recordData._initRecordCreateOptions(properties);
|
|
493
|
-
//TODO Igor pass a wrapper instead of RD
|
|
494
|
-
let record = this.instantiateRecord(identifier, createOptions, this.__recordDataFor, this._notificationManager);
|
|
495
|
-
setRecordIdentifier(record, identifier);
|
|
496
|
-
//recordToInternalModelMap.set(record, internalModel);
|
|
497
|
-
return record;
|
|
498
471
|
}
|
|
499
472
|
|
|
500
|
-
|
|
473
|
+
// TODO guard against initRecordOptions no being there
|
|
474
|
+
let createOptions = recordData._initRecordCreateOptions(properties);
|
|
475
|
+
//TODO Igor pass a wrapper instead of RD
|
|
476
|
+
let record = this.instantiateRecord(identifier, createOptions, this.__recordDataFor, this._notificationManager);
|
|
477
|
+
setRecordIdentifier(record, identifier);
|
|
478
|
+
//recordToInternalModelMap.set(record, internalModel);
|
|
479
|
+
return record;
|
|
501
480
|
}
|
|
502
481
|
|
|
503
482
|
abstract instantiateRecord(
|
|
@@ -535,10 +514,7 @@ abstract class CoreStore extends Service {
|
|
|
535
514
|
}
|
|
536
515
|
|
|
537
516
|
getSchemaDefinitionService(): SchemaDefinitionService {
|
|
538
|
-
|
|
539
|
-
return this._schemaDefinitionService;
|
|
540
|
-
}
|
|
541
|
-
assert('need to enable CUSTOM_MODEL_CLASS feature flag in order to access SchemaDefinitionService');
|
|
517
|
+
return this._schemaDefinitionService;
|
|
542
518
|
}
|
|
543
519
|
|
|
544
520
|
// TODO Double check this return value is correct
|
|
@@ -716,31 +692,12 @@ abstract class CoreStore extends Service {
|
|
|
716
692
|
assertDestroyingStore(this, 'deleteRecord');
|
|
717
693
|
}
|
|
718
694
|
this._backburner.join(() => {
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
internalModel.deleteRecord();
|
|
725
|
-
}
|
|
726
|
-
} else {
|
|
727
|
-
deprecate(
|
|
728
|
-
`You passed a non ember-data managed record ${record} to store.deleteRecord. Ember Data store is not meant to manage non store records. This is not supported and will be removed`,
|
|
729
|
-
false,
|
|
730
|
-
{
|
|
731
|
-
id: 'ember-data:delete-record-non-store',
|
|
732
|
-
until: '4.0',
|
|
733
|
-
for: '@ember-data/store',
|
|
734
|
-
since: {
|
|
735
|
-
available: '3.28',
|
|
736
|
-
enabled: '3.28',
|
|
737
|
-
},
|
|
738
|
-
}
|
|
739
|
-
);
|
|
740
|
-
record.deleteRecord();
|
|
695
|
+
let identifier = peekRecordIdentifier(record);
|
|
696
|
+
if (identifier) {
|
|
697
|
+
let internalModel = internalModelFactoryFor(this).peek(identifier);
|
|
698
|
+
if (internalModel) {
|
|
699
|
+
internalModel.deleteRecord();
|
|
741
700
|
}
|
|
742
|
-
} else {
|
|
743
|
-
record.deleteRecord();
|
|
744
701
|
}
|
|
745
702
|
});
|
|
746
703
|
}
|
|
@@ -765,31 +722,12 @@ abstract class CoreStore extends Service {
|
|
|
765
722
|
if (DEBUG) {
|
|
766
723
|
assertDestroyingStore(this, 'unloadRecord');
|
|
767
724
|
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
internalModel.unloadRecord();
|
|
774
|
-
}
|
|
775
|
-
} else {
|
|
776
|
-
deprecate(
|
|
777
|
-
`You passed a non ember-data managed record ${record} to store.unloadRecord. Ember Data store is not meant to manage non store records. This is not supported and will be removed`,
|
|
778
|
-
false,
|
|
779
|
-
{
|
|
780
|
-
id: 'ember-data:unload-record-non-store',
|
|
781
|
-
until: '4.0',
|
|
782
|
-
for: '@ember-data/store',
|
|
783
|
-
since: {
|
|
784
|
-
available: '3.28',
|
|
785
|
-
enabled: '3.28',
|
|
786
|
-
},
|
|
787
|
-
}
|
|
788
|
-
);
|
|
789
|
-
record.unloadRecord();
|
|
725
|
+
let identifier = peekRecordIdentifier(record);
|
|
726
|
+
if (identifier) {
|
|
727
|
+
let internalModel = internalModelFactoryFor(this).peek(identifier);
|
|
728
|
+
if (internalModel) {
|
|
729
|
+
internalModel.unloadRecord();
|
|
790
730
|
}
|
|
791
|
-
} else {
|
|
792
|
-
record.unloadRecord();
|
|
793
731
|
}
|
|
794
732
|
}
|
|
795
733
|
|
|
@@ -1298,19 +1236,12 @@ abstract class CoreStore extends Service {
|
|
|
1298
1236
|
return this._scheduleFetch(internalModel, options);
|
|
1299
1237
|
}
|
|
1300
1238
|
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
if (
|
|
1304
|
-
return internalModel
|
|
1305
|
-
}
|
|
1306
|
-
} else {
|
|
1307
|
-
if (internalModel.currentState.isLoading) {
|
|
1308
|
-
let pendingRequest = this._fetchManager.getPendingFetch(internalModel.identifier, options);
|
|
1309
|
-
if (pendingRequest) {
|
|
1310
|
-
return pendingRequest.then(() => Promise.resolve(internalModel));
|
|
1311
|
-
}
|
|
1312
|
-
return this._scheduleFetch(internalModel, options);
|
|
1239
|
+
if (internalModel.currentState.isLoading) {
|
|
1240
|
+
let pendingRequest = this._fetchManager.getPendingFetch(internalModel.identifier, options);
|
|
1241
|
+
if (pendingRequest) {
|
|
1242
|
+
return pendingRequest.then(() => Promise.resolve(internalModel));
|
|
1313
1243
|
}
|
|
1244
|
+
return this._scheduleFetch(internalModel, options);
|
|
1314
1245
|
}
|
|
1315
1246
|
|
|
1316
1247
|
return Promise.resolve(internalModel);
|
|
@@ -1416,74 +1347,12 @@ abstract class CoreStore extends Service {
|
|
|
1416
1347
|
}
|
|
1417
1348
|
|
|
1418
1349
|
_scheduleFetch(internalModel: InternalModel, options): RSVP.Promise<InternalModel> {
|
|
1419
|
-
|
|
1420
|
-
return this._scheduleFetchThroughFetchManager(internalModel, options);
|
|
1421
|
-
} else {
|
|
1422
|
-
if (internalModel._promiseProxy) {
|
|
1423
|
-
return internalModel._promiseProxy;
|
|
1424
|
-
}
|
|
1425
|
-
|
|
1426
|
-
assertIdentifierHasId(internalModel.identifier);
|
|
1427
|
-
|
|
1428
|
-
let { id, modelName } = internalModel;
|
|
1429
|
-
let resolver = defer<InternalModel>(`Fetching ${modelName}' with id: ${id}`);
|
|
1430
|
-
let pendingFetchItem: PendingFetchItem = {
|
|
1431
|
-
internalModel,
|
|
1432
|
-
resolver,
|
|
1433
|
-
options,
|
|
1434
|
-
};
|
|
1435
|
-
|
|
1436
|
-
if (DEBUG) {
|
|
1437
|
-
if (this.generateStackTracesForTrackedRequests === true) {
|
|
1438
|
-
let trace;
|
|
1439
|
-
|
|
1440
|
-
try {
|
|
1441
|
-
throw new Error(`Trace Origin for scheduled fetch for ${modelName}:${id}.`);
|
|
1442
|
-
} catch (e) {
|
|
1443
|
-
trace = e;
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
// enable folks to discover the origin of this findRecord call when
|
|
1447
|
-
// debugging. Ideally we would have a tracked queue for requests with
|
|
1448
|
-
// labels or local IDs that could be used to merge this trace with
|
|
1449
|
-
// the trace made available when we detect an async leak
|
|
1450
|
-
pendingFetchItem.trace = trace;
|
|
1451
|
-
}
|
|
1452
|
-
}
|
|
1453
|
-
|
|
1454
|
-
let promise = resolver.promise;
|
|
1455
|
-
|
|
1456
|
-
internalModel.send('loadingData', promise);
|
|
1457
|
-
if (this._pendingFetch.size === 0) {
|
|
1458
|
-
emberBackburner.schedule('actions', this, this.flushAllPendingFetches);
|
|
1459
|
-
}
|
|
1460
|
-
|
|
1461
|
-
let fetches = this._pendingFetch;
|
|
1462
|
-
let pending = fetches.get(modelName);
|
|
1463
|
-
|
|
1464
|
-
if (pending === undefined) {
|
|
1465
|
-
pending = [];
|
|
1466
|
-
fetches.set(modelName, pending);
|
|
1467
|
-
}
|
|
1468
|
-
|
|
1469
|
-
pending.push(pendingFetchItem);
|
|
1470
|
-
|
|
1471
|
-
return promise;
|
|
1472
|
-
}
|
|
1350
|
+
return this._scheduleFetchThroughFetchManager(internalModel, options);
|
|
1473
1351
|
}
|
|
1474
1352
|
|
|
1475
1353
|
flushAllPendingFetches() {
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
//assert here
|
|
1479
|
-
} else {
|
|
1480
|
-
if (this.isDestroyed || this.isDestroying) {
|
|
1481
|
-
return;
|
|
1482
|
-
}
|
|
1483
|
-
|
|
1484
|
-
this._pendingFetch.forEach(this._flushPendingFetchForType, this);
|
|
1485
|
-
this._pendingFetch.clear();
|
|
1486
|
-
}
|
|
1354
|
+
return;
|
|
1355
|
+
//assert here
|
|
1487
1356
|
}
|
|
1488
1357
|
|
|
1489
1358
|
_flushPendingFetchForType(pendingFetchItems: PendingFetchItem[], modelName: string) {
|
|
@@ -1799,9 +1668,7 @@ abstract class CoreStore extends Service {
|
|
|
1799
1668
|
@return {Promise} promise
|
|
1800
1669
|
*/
|
|
1801
1670
|
_reloadRecord(internalModel, options): RSVP.Promise<InternalModel> {
|
|
1802
|
-
|
|
1803
|
-
options.isReloading = true;
|
|
1804
|
-
}
|
|
1671
|
+
options.isReloading = true;
|
|
1805
1672
|
let { id, modelName } = internalModel;
|
|
1806
1673
|
let adapter = this.adapterFor(modelName);
|
|
1807
1674
|
|
|
@@ -2048,17 +1915,9 @@ abstract class CoreStore extends Service {
|
|
|
2048
1915
|
|
|
2049
1916
|
if (internalModel) {
|
|
2050
1917
|
// short circuit if we are already loading
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
return pendingRequest.then(() => internalModel.getRecord());
|
|
2055
|
-
}
|
|
2056
|
-
} else {
|
|
2057
|
-
if (internalModel.currentState.isLoading) {
|
|
2058
|
-
return internalModel._promiseProxy.then(() => {
|
|
2059
|
-
return internalModel.getRecord();
|
|
2060
|
-
});
|
|
2061
|
-
}
|
|
1918
|
+
let pendingRequest = this._fetchManager.getPendingFetch(internalModel.identifier, options);
|
|
1919
|
+
if (pendingRequest) {
|
|
1920
|
+
return pendingRequest.then(() => internalModel.getRecord());
|
|
2062
1921
|
}
|
|
2063
1922
|
}
|
|
2064
1923
|
|
|
@@ -2691,33 +2550,32 @@ abstract class CoreStore extends Service {
|
|
|
2691
2550
|
@param {Object} options
|
|
2692
2551
|
*/
|
|
2693
2552
|
scheduleSave(internalModel: InternalModel, resolver: RSVP.Deferred<void>, options): void | RSVP.Promise<void> {
|
|
2694
|
-
if (
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
}
|
|
2553
|
+
if (internalModel._isRecordFullyDeleted()) {
|
|
2554
|
+
resolver.resolve();
|
|
2555
|
+
return resolver.promise;
|
|
2556
|
+
}
|
|
2699
2557
|
|
|
2700
|
-
|
|
2558
|
+
internalModel.adapterWillCommit();
|
|
2701
2559
|
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
// TODO handle missing isNew
|
|
2709
|
-
if (recordData.isNew && recordData.isNew()) {
|
|
2710
|
-
operation = 'createRecord';
|
|
2711
|
-
} else if (recordData.isDeleted && recordData.isDeleted()) {
|
|
2712
|
-
operation = 'deleteRecord';
|
|
2713
|
-
}
|
|
2560
|
+
if (!options) {
|
|
2561
|
+
options = {};
|
|
2562
|
+
}
|
|
2563
|
+
let recordData = internalModel._recordData;
|
|
2564
|
+
let operation: 'createRecord' | 'deleteRecord' | 'updateRecord' = 'updateRecord';
|
|
2714
2565
|
|
|
2715
|
-
|
|
2566
|
+
// TODO handle missing isNew
|
|
2567
|
+
if (recordData.isNew && recordData.isNew()) {
|
|
2568
|
+
operation = 'createRecord';
|
|
2569
|
+
} else if (recordData.isDeleted && recordData.isDeleted()) {
|
|
2570
|
+
operation = 'deleteRecord';
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2573
|
+
options[SaveOp] = operation;
|
|
2716
2574
|
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2575
|
+
let fetchManagerPromise = this._fetchManager.scheduleSave(internalModel.identifier, options);
|
|
2576
|
+
let promise = fetchManagerPromise.then(
|
|
2577
|
+
(payload) => {
|
|
2578
|
+
/*
|
|
2721
2579
|
Note to future spelunkers hoping to optimize.
|
|
2722
2580
|
We rely on this `run` to create a run loop if needed
|
|
2723
2581
|
that `store._push` and `store.didSaveRecord` will both share.
|
|
@@ -2726,40 +2584,25 @@ abstract class CoreStore extends Service {
|
|
|
2726
2584
|
have an outer run loop available still from the first
|
|
2727
2585
|
call to `store._push`;
|
|
2728
2586
|
*/
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
}
|
|
2735
|
-
});
|
|
2736
|
-
},
|
|
2737
|
-
(e) => {
|
|
2738
|
-
if (typeof e === 'string') {
|
|
2739
|
-
throw e;
|
|
2587
|
+
this._backburner.join(() => {
|
|
2588
|
+
let data = payload && payload.data;
|
|
2589
|
+
this.didSaveRecord(internalModel, { data }, operation);
|
|
2590
|
+
if (payload && payload.included) {
|
|
2591
|
+
this._push({ data: null, included: payload.included });
|
|
2740
2592
|
}
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2593
|
+
});
|
|
2594
|
+
},
|
|
2595
|
+
(e) => {
|
|
2596
|
+
if (typeof e === 'string') {
|
|
2597
|
+
throw e;
|
|
2744
2598
|
}
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
} else {
|
|
2749
|
-
if (internalModel._isRecordFullyDeleted()) {
|
|
2750
|
-
resolver.resolve();
|
|
2751
|
-
return;
|
|
2599
|
+
const { error, parsedErrors } = e;
|
|
2600
|
+
this.recordWasInvalid(internalModel, parsedErrors, error);
|
|
2601
|
+
throw error;
|
|
2752
2602
|
}
|
|
2603
|
+
);
|
|
2753
2604
|
|
|
2754
|
-
|
|
2755
|
-
internalModel.adapterWillCommit();
|
|
2756
|
-
this._pendingSave.push({
|
|
2757
|
-
snapshot: snapshot,
|
|
2758
|
-
resolver: resolver,
|
|
2759
|
-
});
|
|
2760
|
-
|
|
2761
|
-
emberBackburner.scheduleOnce('actions', this, this.flushPendingSave);
|
|
2762
|
-
}
|
|
2605
|
+
return promise;
|
|
2763
2606
|
}
|
|
2764
2607
|
|
|
2765
2608
|
/**
|
|
@@ -2770,47 +2613,8 @@ abstract class CoreStore extends Service {
|
|
|
2770
2613
|
@private
|
|
2771
2614
|
*/
|
|
2772
2615
|
flushPendingSave() {
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
return;
|
|
2776
|
-
}
|
|
2777
|
-
let pending = this._pendingSave.slice();
|
|
2778
|
-
this._pendingSave = [];
|
|
2779
|
-
|
|
2780
|
-
for (let i = 0, j = pending.length; i < j; i++) {
|
|
2781
|
-
let pendingItem = pending[i];
|
|
2782
|
-
let snapshot = pendingItem.snapshot;
|
|
2783
|
-
let resolver = pendingItem.resolver;
|
|
2784
|
-
// TODO We have to cast due to our reliance on this private property
|
|
2785
|
-
// this will be refactored away once we change our pending API to be identifier based
|
|
2786
|
-
let internalModel = (snapshot as unknown as PrivateSnapshot)._internalModel;
|
|
2787
|
-
let adapter = this.adapterFor(internalModel.modelName);
|
|
2788
|
-
let operation;
|
|
2789
|
-
|
|
2790
|
-
if (RECORD_DATA_STATE) {
|
|
2791
|
-
// TODO move this out of internalModel
|
|
2792
|
-
if (internalModel.isNew()) {
|
|
2793
|
-
operation = 'createRecord';
|
|
2794
|
-
} else if (internalModel.isDeleted()) {
|
|
2795
|
-
operation = 'deleteRecord';
|
|
2796
|
-
} else {
|
|
2797
|
-
operation = 'updateRecord';
|
|
2798
|
-
}
|
|
2799
|
-
} else {
|
|
2800
|
-
if (internalModel.currentState.stateName === 'root.deleted.saved') {
|
|
2801
|
-
resolver.resolve();
|
|
2802
|
-
continue;
|
|
2803
|
-
} else if (internalModel.isNew()) {
|
|
2804
|
-
operation = 'createRecord';
|
|
2805
|
-
} else if (internalModel.isDeleted()) {
|
|
2806
|
-
operation = 'deleteRecord';
|
|
2807
|
-
} else {
|
|
2808
|
-
operation = 'updateRecord';
|
|
2809
|
-
}
|
|
2810
|
-
}
|
|
2811
|
-
|
|
2812
|
-
resolver.resolve(_commit(adapter, this, operation, snapshot));
|
|
2813
|
-
}
|
|
2616
|
+
// assert here
|
|
2617
|
+
return;
|
|
2814
2618
|
}
|
|
2815
2619
|
|
|
2816
2620
|
/**
|
|
@@ -2868,11 +2672,7 @@ abstract class CoreStore extends Service {
|
|
|
2868
2672
|
if (DEBUG) {
|
|
2869
2673
|
assertDestroyingStore(this, 'recordWasInvalid');
|
|
2870
2674
|
}
|
|
2871
|
-
|
|
2872
|
-
internalModel.adapterDidInvalidate(parsedErrors, error);
|
|
2873
|
-
} else {
|
|
2874
|
-
internalModel.adapterDidInvalidate(parsedErrors);
|
|
2875
|
-
}
|
|
2675
|
+
internalModel.adapterDidInvalidate(parsedErrors, error);
|
|
2876
2676
|
}
|
|
2877
2677
|
|
|
2878
2678
|
/**
|
|
@@ -3197,30 +2997,17 @@ abstract class CoreStore extends Service {
|
|
|
3197
2997
|
|
|
3198
2998
|
if (ENV.DS_WARN_ON_UNKNOWN_KEYS) {
|
|
3199
2999
|
let unknownAttributes, unknownRelationships;
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
});
|
|
3212
|
-
} else {
|
|
3213
|
-
let modelClass = this.modelFor(modelName);
|
|
3214
|
-
// Check unknown attributes
|
|
3215
|
-
unknownAttributes = Object.keys(data.attributes || {}).filter((key) => {
|
|
3216
|
-
return !get(modelClass, 'fields').has(key);
|
|
3217
|
-
});
|
|
3218
|
-
|
|
3219
|
-
// Check unknown relationships
|
|
3220
|
-
unknownRelationships = Object.keys(data.relationships || {}).filter((key) => {
|
|
3221
|
-
return !get(modelClass, 'fields').has(key);
|
|
3222
|
-
});
|
|
3223
|
-
}
|
|
3000
|
+
let relationships = this.getSchemaDefinitionService().relationshipsDefinitionFor(modelName);
|
|
3001
|
+
let attributes = this.getSchemaDefinitionService().attributesDefinitionFor(modelName);
|
|
3002
|
+
// Check unknown attributes
|
|
3003
|
+
unknownAttributes = Object.keys(data.attributes || {}).filter((key) => {
|
|
3004
|
+
return !attributes[key];
|
|
3005
|
+
});
|
|
3006
|
+
|
|
3007
|
+
// Check unknown relationships
|
|
3008
|
+
unknownRelationships = Object.keys(data.relationships || {}).filter((key) => {
|
|
3009
|
+
return !relationships[key];
|
|
3010
|
+
});
|
|
3224
3011
|
let unknownAttributesMessage = `The payload for '${modelName}' contains these unknown attributes: ${unknownAttributes}. Make sure they've been defined in your model.`;
|
|
3225
3012
|
warn(unknownAttributesMessage, unknownAttributes.length === 0, {
|
|
3226
3013
|
id: 'ds.store.unknown-keys-in-payload',
|
|
@@ -3350,38 +3137,26 @@ abstract class CoreStore extends Service {
|
|
|
3350
3137
|
}
|
|
3351
3138
|
|
|
3352
3139
|
serializeRecord(record: RecordInstance, options?: Dict<unknown>): unknown {
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
return internalModel!.createSnapshot(options).serialize(options);
|
|
3358
|
-
}
|
|
3359
|
-
|
|
3360
|
-
assert('serializeRecord is only available when CUSTOM_MODEL_CLASS ff is on', false);
|
|
3140
|
+
let identifier = recordIdentifierFor(record);
|
|
3141
|
+
let internalModel = internalModelFactoryFor(this).peek(identifier);
|
|
3142
|
+
// TODO we used to check if the record was destroyed here
|
|
3143
|
+
return internalModel!.createSnapshot(options).serialize(options);
|
|
3361
3144
|
}
|
|
3362
3145
|
|
|
3363
3146
|
saveRecord(record: RecordInstance, options?: Dict<unknown>): RSVP.Promise<RecordInstance> {
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
return (internalModel!.save(options) as RSVP.Promise<void>).then(() => record);
|
|
3371
|
-
}
|
|
3372
|
-
|
|
3373
|
-
assert('saveRecord is only available when CUSTOM_MODEL_CLASS ff is on');
|
|
3147
|
+
let identifier = recordIdentifierFor(record);
|
|
3148
|
+
let internalModel = internalModelFactoryFor(this).peek(identifier);
|
|
3149
|
+
// TODO we used to check if the record was destroyed here
|
|
3150
|
+
// Casting can be removed once REQUEST_SERVICE ff is turned on
|
|
3151
|
+
// because a `Record` is provided there will always be a matching internalModel
|
|
3152
|
+
return (internalModel!.save(options) as RSVP.Promise<void>).then(() => record);
|
|
3374
3153
|
}
|
|
3375
3154
|
|
|
3376
3155
|
relationshipReferenceFor(identifier: RecordIdentifier, key: string): BelongsToReference | HasManyReference {
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
return internalModel!.referenceFor(null, key);
|
|
3382
|
-
}
|
|
3383
|
-
|
|
3384
|
-
assert('relationshipReferenceFor is only available when CUSTOM_MODEL_CLASS ff is on', false);
|
|
3156
|
+
let stableIdentifier = identifierCacheFor(this).getOrCreateRecordIdentifier(identifier);
|
|
3157
|
+
let internalModel = internalModelFactoryFor(this).peek(stableIdentifier);
|
|
3158
|
+
// TODO we used to check if the record was destroyed here
|
|
3159
|
+
return internalModel!.referenceFor(null, key);
|
|
3385
3160
|
}
|
|
3386
3161
|
|
|
3387
3162
|
/**
|
|
@@ -3635,10 +3410,6 @@ abstract class CoreStore extends Service {
|
|
|
3635
3410
|
for an `App.ApplicationSerializer` (the default serializer for
|
|
3636
3411
|
your entire application).
|
|
3637
3412
|
|
|
3638
|
-
if no `App.ApplicationSerializer` is found, it will attempt
|
|
3639
|
-
to get the `defaultSerializer` from the `PersonAdapter`
|
|
3640
|
-
(`adapterFor('person')`).
|
|
3641
|
-
|
|
3642
3413
|
If a serializer cannot be found on the adapter, it will fall back
|
|
3643
3414
|
to an instance of `JSONSerializer`.
|
|
3644
3415
|
|
|
@@ -3706,31 +3477,6 @@ abstract class CoreStore extends Service {
|
|
|
3706
3477
|
}
|
|
3707
3478
|
|
|
3708
3479
|
let serializerName;
|
|
3709
|
-
if (DEPRECATE_DEFAULT_SERIALIZER) {
|
|
3710
|
-
// no model specific serializer or application serializer, check for the `defaultSerializer`
|
|
3711
|
-
// property defined on the adapter
|
|
3712
|
-
let adapter = this.adapterFor(modelName);
|
|
3713
|
-
serializerName = get(adapter, 'defaultSerializer');
|
|
3714
|
-
|
|
3715
|
-
deprecate(
|
|
3716
|
-
`store.serializerFor("${modelName}") resolved the "${serializerName}" serializer via the deprecated \`adapter.defaultSerializer\` property.\n\n\tPreviously, if no application or type-specific serializer was specified, the store would attempt to lookup a serializer via the \`defaultSerializer\` property on the type's adapter. This behavior is deprecated in favor of explicitly defining a type-specific serializer or application serializer`,
|
|
3717
|
-
!serializerName,
|
|
3718
|
-
{
|
|
3719
|
-
id: 'ember-data:default-serializer',
|
|
3720
|
-
until: '4.0',
|
|
3721
|
-
url: 'https://deprecations.emberjs.com/ember-data/v3.x/#toc_ember-data-default-serializers',
|
|
3722
|
-
for: '@ember-data/store',
|
|
3723
|
-
since: {
|
|
3724
|
-
available: '3.15',
|
|
3725
|
-
enabled: '3.15',
|
|
3726
|
-
},
|
|
3727
|
-
}
|
|
3728
|
-
);
|
|
3729
|
-
|
|
3730
|
-
serializer = serializerName
|
|
3731
|
-
? _serializerCache[serializerName] || owner.lookup(`serializer:${serializerName}`)
|
|
3732
|
-
: undefined;
|
|
3733
|
-
}
|
|
3734
3480
|
|
|
3735
3481
|
if (DEPRECATE_LEGACY_TEST_REGISTRATIONS) {
|
|
3736
3482
|
// in production this is handled by the re-export
|
|
@@ -3761,49 +3507,10 @@ abstract class CoreStore extends Service {
|
|
|
3761
3507
|
}
|
|
3762
3508
|
}
|
|
3763
3509
|
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
if (DEBUG && HAS_EMBER_DATA_PACKAGE && HAS_SERIALIZER_PACKAGE && serializer === undefined) {
|
|
3769
|
-
const JSONSerializer = require('@ember-data/serializer/json').default;
|
|
3770
|
-
owner.register('serializer:-default', JSONSerializer);
|
|
3771
|
-
serializer = owner.lookup('serializer:-default');
|
|
3772
|
-
|
|
3773
|
-
serializer && deprecateTestRegistration('serializer', '-default');
|
|
3774
|
-
}
|
|
3775
|
-
|
|
3776
|
-
deprecate(
|
|
3777
|
-
`store.serializerFor("${modelName}") resolved the "-default" serializer via the deprecated "-default" lookup fallback.\n\n\tPreviously, when no type-specific serializer, application serializer, or adapter.defaultSerializer had been defined by the app, the "-default" serializer would be used which defaulted to the \`JSONSerializer\`. This behavior is deprecated in favor of explicitly defining an application or type-specific serializer`,
|
|
3778
|
-
!serializer,
|
|
3779
|
-
{
|
|
3780
|
-
id: 'ember-data:default-serializer',
|
|
3781
|
-
until: '4.0',
|
|
3782
|
-
url: 'https://deprecations.emberjs.com/ember-data/v3.x/#toc_ember-data-default-serializers',
|
|
3783
|
-
for: '@ember-data/store',
|
|
3784
|
-
since: {
|
|
3785
|
-
available: '3.15',
|
|
3786
|
-
enabled: '3.15',
|
|
3787
|
-
},
|
|
3788
|
-
}
|
|
3789
|
-
);
|
|
3790
|
-
|
|
3791
|
-
assert(
|
|
3792
|
-
`No serializer was found for '${modelName}' and no 'application' serializer was found as a fallback`,
|
|
3793
|
-
serializer !== undefined
|
|
3794
|
-
);
|
|
3795
|
-
|
|
3796
|
-
set(serializer, 'store', this);
|
|
3797
|
-
_serializerCache[normalizedModelName] = serializer;
|
|
3798
|
-
_serializerCache['-default'] = serializer;
|
|
3799
|
-
|
|
3800
|
-
return serializer;
|
|
3801
|
-
} else {
|
|
3802
|
-
assert(
|
|
3803
|
-
`No serializer was found for '${modelName}' and no 'application' serializer was found as a fallback`,
|
|
3804
|
-
serializer !== undefined
|
|
3805
|
-
);
|
|
3806
|
-
}
|
|
3510
|
+
assert(
|
|
3511
|
+
`No serializer was found for '${modelName}' and no 'application' serializer was found as a fallback`,
|
|
3512
|
+
serializer !== undefined
|
|
3513
|
+
);
|
|
3807
3514
|
}
|
|
3808
3515
|
|
|
3809
3516
|
destroy() {
|
|
@@ -3932,73 +3639,6 @@ if (DEPRECATE_DEFAULT_ADAPTER) {
|
|
|
3932
3639
|
|
|
3933
3640
|
export default CoreStore;
|
|
3934
3641
|
|
|
3935
|
-
function _commit(adapter, store, operation, snapshot) {
|
|
3936
|
-
let internalModel = snapshot._internalModel;
|
|
3937
|
-
let modelName = snapshot.modelName;
|
|
3938
|
-
let modelClass = store.modelFor(modelName);
|
|
3939
|
-
assert(`You tried to update a record but you have no adapter (for ${modelName})`, adapter);
|
|
3940
|
-
assert(
|
|
3941
|
-
`You tried to update a record but your adapter (for ${modelName}) does not implement '${operation}'`,
|
|
3942
|
-
typeof adapter[operation] === 'function'
|
|
3943
|
-
);
|
|
3944
|
-
|
|
3945
|
-
let promise = Promise.resolve().then(() => adapter[operation](store, modelClass, snapshot));
|
|
3946
|
-
let serializer = store.serializerFor(modelName);
|
|
3947
|
-
let label = `DS: Extract and notify about ${operation} completion of ${internalModel}`;
|
|
3948
|
-
|
|
3949
|
-
promise = guardDestroyedStore(promise, store, label);
|
|
3950
|
-
promise = _guard(promise, _bind(_objectIsAlive, internalModel));
|
|
3951
|
-
|
|
3952
|
-
return promise.then(
|
|
3953
|
-
(adapterPayload) => {
|
|
3954
|
-
/*
|
|
3955
|
-
Note to future spelunkers hoping to optimize.
|
|
3956
|
-
We rely on this `run` to create a run loop if needed
|
|
3957
|
-
that `store._push` and `store.didSaveRecord` will both share.
|
|
3958
|
-
|
|
3959
|
-
We use `join` because it is often the case that we
|
|
3960
|
-
have an outer run loop available still from the first
|
|
3961
|
-
call to `store._push`;
|
|
3962
|
-
*/
|
|
3963
|
-
store._backburner.join(() => {
|
|
3964
|
-
let payload, data, sideloaded;
|
|
3965
|
-
if (adapterPayload) {
|
|
3966
|
-
payload = normalizeResponseHelper(serializer, store, modelClass, adapterPayload, snapshot.id, operation);
|
|
3967
|
-
if (payload.included) {
|
|
3968
|
-
sideloaded = payload.included;
|
|
3969
|
-
}
|
|
3970
|
-
data = payload.data;
|
|
3971
|
-
}
|
|
3972
|
-
store.didSaveRecord(internalModel, { data }, operation);
|
|
3973
|
-
// seems risky, but if the tests pass might be fine?
|
|
3974
|
-
if (sideloaded) {
|
|
3975
|
-
store._push({ data: null, included: sideloaded });
|
|
3976
|
-
}
|
|
3977
|
-
});
|
|
3978
|
-
|
|
3979
|
-
return internalModel;
|
|
3980
|
-
},
|
|
3981
|
-
function (error) {
|
|
3982
|
-
if (error && error.isAdapterError === true && error.code === 'InvalidError') {
|
|
3983
|
-
let parsedErrors;
|
|
3984
|
-
|
|
3985
|
-
if (typeof serializer.extractErrors === 'function') {
|
|
3986
|
-
parsedErrors = serializer.extractErrors(store, modelClass, error, snapshot.id);
|
|
3987
|
-
} else {
|
|
3988
|
-
parsedErrors = errorsArrayToHash(error.errors);
|
|
3989
|
-
}
|
|
3990
|
-
|
|
3991
|
-
store.recordWasInvalid(internalModel, parsedErrors, error);
|
|
3992
|
-
} else {
|
|
3993
|
-
store.recordWasError(internalModel, error);
|
|
3994
|
-
}
|
|
3995
|
-
|
|
3996
|
-
throw error;
|
|
3997
|
-
},
|
|
3998
|
-
label
|
|
3999
|
-
);
|
|
4000
|
-
}
|
|
4001
|
-
|
|
4002
3642
|
let assertDestroyingStore: Function;
|
|
4003
3643
|
let assertDestroyedStoreOnly: Function;
|
|
4004
3644
|
|