@ember-data/store 4.2.0-alpha.0 → 4.2.0-alpha.4
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 +141 -467
- package/addon/-private/system/ds-model-store.ts +14 -64
- package/addon/-private/system/fetch-manager.ts +9 -4
- package/addon/-private/system/model/internal-model.ts +117 -349
- package/addon/-private/system/model/states.js +3 -9
- package/addon/-private/system/record-array-manager.js +3 -30
- package/addon/-private/system/record-notification-manager.ts +14 -6
- package/addon/-private/system/references/{belongs-to.js → belongs-to.ts} +83 -19
- package/addon/-private/system/references/{has-many.js → has-many.ts} +126 -49
- package/addon/-private/system/references/record.ts +37 -6
- package/addon/-private/system/references/reference.ts +1 -1
- 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/addon/-private/ts-interfaces/ds-model.ts +1 -0
- package/addon/-private/ts-interfaces/fetch-manager.ts +4 -0
- package/index.js +3 -0
- package/package.json +10 -9
|
@@ -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
|
-
for (let i = 0; i < keys.length; i++) {
|
|
472
|
-
let prop = keys[i];
|
|
473
|
-
let def = defs[prop];
|
|
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;
|
|
484
452
|
|
|
485
|
-
|
|
453
|
+
for (let i = 0; i < keys.length; i++) {
|
|
454
|
+
let prop = keys[i];
|
|
455
|
+
let def = defs[prop];
|
|
456
|
+
|
|
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,16 +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
|
-
}
|
|
695
|
+
let identifier = peekRecordIdentifier(record);
|
|
696
|
+
if (identifier) {
|
|
697
|
+
let internalModel = internalModelFactoryFor(this).peek(identifier);
|
|
698
|
+
if (internalModel) {
|
|
699
|
+
internalModel.deleteRecord();
|
|
726
700
|
}
|
|
727
|
-
} else {
|
|
728
|
-
record.deleteRecord();
|
|
729
701
|
}
|
|
730
702
|
});
|
|
731
703
|
}
|
|
@@ -750,16 +722,12 @@ abstract class CoreStore extends Service {
|
|
|
750
722
|
if (DEBUG) {
|
|
751
723
|
assertDestroyingStore(this, 'unloadRecord');
|
|
752
724
|
}
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
internalModel.unloadRecord();
|
|
759
|
-
}
|
|
725
|
+
let identifier = peekRecordIdentifier(record);
|
|
726
|
+
if (identifier) {
|
|
727
|
+
let internalModel = internalModelFactoryFor(this).peek(identifier);
|
|
728
|
+
if (internalModel) {
|
|
729
|
+
internalModel.unloadRecord();
|
|
760
730
|
}
|
|
761
|
-
} else {
|
|
762
|
-
record.unloadRecord();
|
|
763
731
|
}
|
|
764
732
|
}
|
|
765
733
|
|
|
@@ -1248,7 +1216,7 @@ abstract class CoreStore extends Service {
|
|
|
1248
1216
|
return Promise.resolve(internalModel);
|
|
1249
1217
|
}
|
|
1250
1218
|
|
|
1251
|
-
_findByInternalModel(internalModel, options:
|
|
1219
|
+
_findByInternalModel(internalModel: InternalModel, options: FindOptions = {}) {
|
|
1252
1220
|
if (options.preload) {
|
|
1253
1221
|
this._backburner.join(() => {
|
|
1254
1222
|
internalModel.preloadData(options.preload);
|
|
@@ -1263,24 +1231,17 @@ abstract class CoreStore extends Service {
|
|
|
1263
1231
|
);
|
|
1264
1232
|
}
|
|
1265
1233
|
|
|
1266
|
-
_findEmptyInternalModel(internalModel, options) {
|
|
1234
|
+
_findEmptyInternalModel(internalModel: InternalModel, options: FindOptions) {
|
|
1267
1235
|
if (internalModel.currentState.isEmpty) {
|
|
1268
1236
|
return this._scheduleFetch(internalModel, options);
|
|
1269
1237
|
}
|
|
1270
1238
|
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
if (
|
|
1274
|
-
return internalModel
|
|
1275
|
-
}
|
|
1276
|
-
} else {
|
|
1277
|
-
if (internalModel.currentState.isLoading) {
|
|
1278
|
-
let pending = this._fetchManager.getPendingFetch(internalModel.identifier);
|
|
1279
|
-
if (pending) {
|
|
1280
|
-
return pending.then(() => Promise.resolve(internalModel));
|
|
1281
|
-
}
|
|
1282
|
-
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));
|
|
1283
1243
|
}
|
|
1244
|
+
return this._scheduleFetch(internalModel, options);
|
|
1284
1245
|
}
|
|
1285
1246
|
|
|
1286
1247
|
return Promise.resolve(internalModel);
|
|
@@ -1386,74 +1347,12 @@ abstract class CoreStore extends Service {
|
|
|
1386
1347
|
}
|
|
1387
1348
|
|
|
1388
1349
|
_scheduleFetch(internalModel: InternalModel, options): RSVP.Promise<InternalModel> {
|
|
1389
|
-
|
|
1390
|
-
return this._scheduleFetchThroughFetchManager(internalModel, options);
|
|
1391
|
-
} else {
|
|
1392
|
-
if (internalModel._promiseProxy) {
|
|
1393
|
-
return internalModel._promiseProxy;
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
|
-
assertIdentifierHasId(internalModel.identifier);
|
|
1397
|
-
|
|
1398
|
-
let { id, modelName } = internalModel;
|
|
1399
|
-
let resolver = defer<InternalModel>(`Fetching ${modelName}' with id: ${id}`);
|
|
1400
|
-
let pendingFetchItem: PendingFetchItem = {
|
|
1401
|
-
internalModel,
|
|
1402
|
-
resolver,
|
|
1403
|
-
options,
|
|
1404
|
-
};
|
|
1405
|
-
|
|
1406
|
-
if (DEBUG) {
|
|
1407
|
-
if (this.generateStackTracesForTrackedRequests === true) {
|
|
1408
|
-
let trace;
|
|
1409
|
-
|
|
1410
|
-
try {
|
|
1411
|
-
throw new Error(`Trace Origin for scheduled fetch for ${modelName}:${id}.`);
|
|
1412
|
-
} catch (e) {
|
|
1413
|
-
trace = e;
|
|
1414
|
-
}
|
|
1415
|
-
|
|
1416
|
-
// enable folks to discover the origin of this findRecord call when
|
|
1417
|
-
// debugging. Ideally we would have a tracked queue for requests with
|
|
1418
|
-
// labels or local IDs that could be used to merge this trace with
|
|
1419
|
-
// the trace made available when we detect an async leak
|
|
1420
|
-
pendingFetchItem.trace = trace;
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1423
|
-
|
|
1424
|
-
let promise = resolver.promise;
|
|
1425
|
-
|
|
1426
|
-
internalModel.send('loadingData', promise);
|
|
1427
|
-
if (this._pendingFetch.size === 0) {
|
|
1428
|
-
emberBackburner.schedule('actions', this, this.flushAllPendingFetches);
|
|
1429
|
-
}
|
|
1430
|
-
|
|
1431
|
-
let fetches = this._pendingFetch;
|
|
1432
|
-
let pending = fetches.get(modelName);
|
|
1433
|
-
|
|
1434
|
-
if (pending === undefined) {
|
|
1435
|
-
pending = [];
|
|
1436
|
-
fetches.set(modelName, pending);
|
|
1437
|
-
}
|
|
1438
|
-
|
|
1439
|
-
pending.push(pendingFetchItem);
|
|
1440
|
-
|
|
1441
|
-
return promise;
|
|
1442
|
-
}
|
|
1350
|
+
return this._scheduleFetchThroughFetchManager(internalModel, options);
|
|
1443
1351
|
}
|
|
1444
1352
|
|
|
1445
1353
|
flushAllPendingFetches() {
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
//assert here
|
|
1449
|
-
} else {
|
|
1450
|
-
if (this.isDestroyed || this.isDestroying) {
|
|
1451
|
-
return;
|
|
1452
|
-
}
|
|
1453
|
-
|
|
1454
|
-
this._pendingFetch.forEach(this._flushPendingFetchForType, this);
|
|
1455
|
-
this._pendingFetch.clear();
|
|
1456
|
-
}
|
|
1354
|
+
return;
|
|
1355
|
+
//assert here
|
|
1457
1356
|
}
|
|
1458
1357
|
|
|
1459
1358
|
_flushPendingFetchForType(pendingFetchItems: PendingFetchItem[], modelName: string) {
|
|
@@ -1729,7 +1628,7 @@ abstract class CoreStore extends Service {
|
|
|
1729
1628
|
if (arguments.length === 1 && isMaybeIdentifier(identifier)) {
|
|
1730
1629
|
let stableIdentifier = identifierCacheFor(this).peekRecordIdentifier(identifier);
|
|
1731
1630
|
if (stableIdentifier) {
|
|
1732
|
-
return internalModelFactoryFor(this).peek(stableIdentifier)?.getRecord();
|
|
1631
|
+
return internalModelFactoryFor(this).peek(stableIdentifier)?.getRecord() || null;
|
|
1733
1632
|
}
|
|
1734
1633
|
return null;
|
|
1735
1634
|
}
|
|
@@ -1769,9 +1668,7 @@ abstract class CoreStore extends Service {
|
|
|
1769
1668
|
@return {Promise} promise
|
|
1770
1669
|
*/
|
|
1771
1670
|
_reloadRecord(internalModel, options): RSVP.Promise<InternalModel> {
|
|
1772
|
-
|
|
1773
|
-
options.isReloading = true;
|
|
1774
|
-
}
|
|
1671
|
+
options.isReloading = true;
|
|
1775
1672
|
let { id, modelName } = internalModel;
|
|
1776
1673
|
let adapter = this.adapterFor(modelName);
|
|
1777
1674
|
|
|
@@ -2018,17 +1915,9 @@ abstract class CoreStore extends Service {
|
|
|
2018
1915
|
|
|
2019
1916
|
if (internalModel) {
|
|
2020
1917
|
// short circuit if we are already loading
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
return pendingRequest.then(() => internalModel.getRecord());
|
|
2025
|
-
}
|
|
2026
|
-
} else {
|
|
2027
|
-
if (internalModel.currentState.isLoading) {
|
|
2028
|
-
return internalModel._promiseProxy.then(() => {
|
|
2029
|
-
return internalModel.getRecord();
|
|
2030
|
-
});
|
|
2031
|
-
}
|
|
1918
|
+
let pendingRequest = this._fetchManager.getPendingFetch(internalModel.identifier, options);
|
|
1919
|
+
if (pendingRequest) {
|
|
1920
|
+
return pendingRequest.then(() => internalModel.getRecord());
|
|
2032
1921
|
}
|
|
2033
1922
|
}
|
|
2034
1923
|
|
|
@@ -2051,6 +1940,10 @@ abstract class CoreStore extends Service {
|
|
|
2051
1940
|
return resolve(null);
|
|
2052
1941
|
}
|
|
2053
1942
|
|
|
1943
|
+
if (!internalModel) {
|
|
1944
|
+
assert(`No InternalModel found for ${resource.lid}`, internalModel);
|
|
1945
|
+
}
|
|
1946
|
+
|
|
2054
1947
|
return this._findByInternalModel(internalModel, options);
|
|
2055
1948
|
}
|
|
2056
1949
|
|
|
@@ -2657,33 +2550,32 @@ abstract class CoreStore extends Service {
|
|
|
2657
2550
|
@param {Object} options
|
|
2658
2551
|
*/
|
|
2659
2552
|
scheduleSave(internalModel: InternalModel, resolver: RSVP.Deferred<void>, options): void | RSVP.Promise<void> {
|
|
2660
|
-
if (
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
}
|
|
2553
|
+
if (internalModel._isRecordFullyDeleted()) {
|
|
2554
|
+
resolver.resolve();
|
|
2555
|
+
return resolver.promise;
|
|
2556
|
+
}
|
|
2665
2557
|
|
|
2666
|
-
|
|
2558
|
+
internalModel.adapterWillCommit();
|
|
2667
2559
|
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2560
|
+
if (!options) {
|
|
2561
|
+
options = {};
|
|
2562
|
+
}
|
|
2563
|
+
let recordData = internalModel._recordData;
|
|
2564
|
+
let operation: 'createRecord' | 'deleteRecord' | 'updateRecord' = 'updateRecord';
|
|
2673
2565
|
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
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
|
+
}
|
|
2680
2572
|
|
|
2681
|
-
|
|
2573
|
+
options[SaveOp] = operation;
|
|
2682
2574
|
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2575
|
+
let fetchManagerPromise = this._fetchManager.scheduleSave(internalModel.identifier, options);
|
|
2576
|
+
let promise = fetchManagerPromise.then(
|
|
2577
|
+
(payload) => {
|
|
2578
|
+
/*
|
|
2687
2579
|
Note to future spelunkers hoping to optimize.
|
|
2688
2580
|
We rely on this `run` to create a run loop if needed
|
|
2689
2581
|
that `store._push` and `store.didSaveRecord` will both share.
|
|
@@ -2692,40 +2584,25 @@ abstract class CoreStore extends Service {
|
|
|
2692
2584
|
have an outer run loop available still from the first
|
|
2693
2585
|
call to `store._push`;
|
|
2694
2586
|
*/
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
}
|
|
2701
|
-
});
|
|
2702
|
-
},
|
|
2703
|
-
(e) => {
|
|
2704
|
-
if (typeof e === 'string') {
|
|
2705
|
-
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 });
|
|
2706
2592
|
}
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2593
|
+
});
|
|
2594
|
+
},
|
|
2595
|
+
(e) => {
|
|
2596
|
+
if (typeof e === 'string') {
|
|
2597
|
+
throw e;
|
|
2710
2598
|
}
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
} else {
|
|
2715
|
-
if (internalModel._isRecordFullyDeleted()) {
|
|
2716
|
-
resolver.resolve();
|
|
2717
|
-
return;
|
|
2599
|
+
const { error, parsedErrors } = e;
|
|
2600
|
+
this.recordWasInvalid(internalModel, parsedErrors, error);
|
|
2601
|
+
throw error;
|
|
2718
2602
|
}
|
|
2603
|
+
);
|
|
2719
2604
|
|
|
2720
|
-
|
|
2721
|
-
internalModel.adapterWillCommit();
|
|
2722
|
-
this._pendingSave.push({
|
|
2723
|
-
snapshot: snapshot,
|
|
2724
|
-
resolver: resolver,
|
|
2725
|
-
});
|
|
2726
|
-
|
|
2727
|
-
emberBackburner.scheduleOnce('actions', this, this.flushPendingSave);
|
|
2728
|
-
}
|
|
2605
|
+
return promise;
|
|
2729
2606
|
}
|
|
2730
2607
|
|
|
2731
2608
|
/**
|
|
@@ -2736,47 +2613,8 @@ abstract class CoreStore extends Service {
|
|
|
2736
2613
|
@private
|
|
2737
2614
|
*/
|
|
2738
2615
|
flushPendingSave() {
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
return;
|
|
2742
|
-
}
|
|
2743
|
-
let pending = this._pendingSave.slice();
|
|
2744
|
-
this._pendingSave = [];
|
|
2745
|
-
|
|
2746
|
-
for (let i = 0, j = pending.length; i < j; i++) {
|
|
2747
|
-
let pendingItem = pending[i];
|
|
2748
|
-
let snapshot = pendingItem.snapshot;
|
|
2749
|
-
let resolver = pendingItem.resolver;
|
|
2750
|
-
// TODO We have to cast due to our reliance on this private property
|
|
2751
|
-
// this will be refactored away once we change our pending API to be identifier based
|
|
2752
|
-
let internalModel = (snapshot as unknown as PrivateSnapshot)._internalModel;
|
|
2753
|
-
let adapter = this.adapterFor(internalModel.modelName);
|
|
2754
|
-
let operation;
|
|
2755
|
-
|
|
2756
|
-
if (RECORD_DATA_STATE) {
|
|
2757
|
-
// TODO move this out of internalModel
|
|
2758
|
-
if (internalModel.isNew()) {
|
|
2759
|
-
operation = 'createRecord';
|
|
2760
|
-
} else if (internalModel.isDeleted()) {
|
|
2761
|
-
operation = 'deleteRecord';
|
|
2762
|
-
} else {
|
|
2763
|
-
operation = 'updateRecord';
|
|
2764
|
-
}
|
|
2765
|
-
} else {
|
|
2766
|
-
if (internalModel.currentState.stateName === 'root.deleted.saved') {
|
|
2767
|
-
resolver.resolve();
|
|
2768
|
-
continue;
|
|
2769
|
-
} else if (internalModel.isNew()) {
|
|
2770
|
-
operation = 'createRecord';
|
|
2771
|
-
} else if (internalModel.isDeleted()) {
|
|
2772
|
-
operation = 'deleteRecord';
|
|
2773
|
-
} else {
|
|
2774
|
-
operation = 'updateRecord';
|
|
2775
|
-
}
|
|
2776
|
-
}
|
|
2777
|
-
|
|
2778
|
-
resolver.resolve(_commit(adapter, this, operation, snapshot));
|
|
2779
|
-
}
|
|
2616
|
+
// assert here
|
|
2617
|
+
return;
|
|
2780
2618
|
}
|
|
2781
2619
|
|
|
2782
2620
|
/**
|
|
@@ -2834,11 +2672,7 @@ abstract class CoreStore extends Service {
|
|
|
2834
2672
|
if (DEBUG) {
|
|
2835
2673
|
assertDestroyingStore(this, 'recordWasInvalid');
|
|
2836
2674
|
}
|
|
2837
|
-
|
|
2838
|
-
internalModel.adapterDidInvalidate(parsedErrors, error);
|
|
2839
|
-
} else {
|
|
2840
|
-
internalModel.adapterDidInvalidate(parsedErrors);
|
|
2841
|
-
}
|
|
2675
|
+
internalModel.adapterDidInvalidate(parsedErrors, error);
|
|
2842
2676
|
}
|
|
2843
2677
|
|
|
2844
2678
|
/**
|
|
@@ -3163,30 +2997,17 @@ abstract class CoreStore extends Service {
|
|
|
3163
2997
|
|
|
3164
2998
|
if (ENV.DS_WARN_ON_UNKNOWN_KEYS) {
|
|
3165
2999
|
let unknownAttributes, unknownRelationships;
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
});
|
|
3178
|
-
} else {
|
|
3179
|
-
let modelClass = this.modelFor(modelName);
|
|
3180
|
-
// Check unknown attributes
|
|
3181
|
-
unknownAttributes = Object.keys(data.attributes || {}).filter((key) => {
|
|
3182
|
-
return !get(modelClass, 'fields').has(key);
|
|
3183
|
-
});
|
|
3184
|
-
|
|
3185
|
-
// Check unknown relationships
|
|
3186
|
-
unknownRelationships = Object.keys(data.relationships || {}).filter((key) => {
|
|
3187
|
-
return !get(modelClass, 'fields').has(key);
|
|
3188
|
-
});
|
|
3189
|
-
}
|
|
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
|
+
});
|
|
3190
3011
|
let unknownAttributesMessage = `The payload for '${modelName}' contains these unknown attributes: ${unknownAttributes}. Make sure they've been defined in your model.`;
|
|
3191
3012
|
warn(unknownAttributesMessage, unknownAttributes.length === 0, {
|
|
3192
3013
|
id: 'ds.store.unknown-keys-in-payload',
|
|
@@ -3316,38 +3137,26 @@ abstract class CoreStore extends Service {
|
|
|
3316
3137
|
}
|
|
3317
3138
|
|
|
3318
3139
|
serializeRecord(record: RecordInstance, options?: Dict<unknown>): unknown {
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
return internalModel!.createSnapshot(options).serialize(options);
|
|
3324
|
-
}
|
|
3325
|
-
|
|
3326
|
-
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);
|
|
3327
3144
|
}
|
|
3328
3145
|
|
|
3329
3146
|
saveRecord(record: RecordInstance, options?: Dict<unknown>): RSVP.Promise<RecordInstance> {
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
return (internalModel!.save(options) as RSVP.Promise<void>).then(() => record);
|
|
3337
|
-
}
|
|
3338
|
-
|
|
3339
|
-
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);
|
|
3340
3153
|
}
|
|
3341
3154
|
|
|
3342
3155
|
relationshipReferenceFor(identifier: RecordIdentifier, key: string): BelongsToReference | HasManyReference {
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
return internalModel!.referenceFor(null, key);
|
|
3348
|
-
}
|
|
3349
|
-
|
|
3350
|
-
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);
|
|
3351
3160
|
}
|
|
3352
3161
|
|
|
3353
3162
|
/**
|
|
@@ -3601,10 +3410,6 @@ abstract class CoreStore extends Service {
|
|
|
3601
3410
|
for an `App.ApplicationSerializer` (the default serializer for
|
|
3602
3411
|
your entire application).
|
|
3603
3412
|
|
|
3604
|
-
if no `App.ApplicationSerializer` is found, it will attempt
|
|
3605
|
-
to get the `defaultSerializer` from the `PersonAdapter`
|
|
3606
|
-
(`adapterFor('person')`).
|
|
3607
|
-
|
|
3608
3413
|
If a serializer cannot be found on the adapter, it will fall back
|
|
3609
3414
|
to an instance of `JSONSerializer`.
|
|
3610
3415
|
|
|
@@ -3672,31 +3477,6 @@ abstract class CoreStore extends Service {
|
|
|
3672
3477
|
}
|
|
3673
3478
|
|
|
3674
3479
|
let serializerName;
|
|
3675
|
-
if (DEPRECATE_DEFAULT_SERIALIZER) {
|
|
3676
|
-
// no model specific serializer or application serializer, check for the `defaultSerializer`
|
|
3677
|
-
// property defined on the adapter
|
|
3678
|
-
let adapter = this.adapterFor(modelName);
|
|
3679
|
-
serializerName = get(adapter, 'defaultSerializer');
|
|
3680
|
-
|
|
3681
|
-
deprecate(
|
|
3682
|
-
`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`,
|
|
3683
|
-
!serializerName,
|
|
3684
|
-
{
|
|
3685
|
-
id: 'ember-data:default-serializer',
|
|
3686
|
-
until: '4.0',
|
|
3687
|
-
url: 'https://deprecations.emberjs.com/ember-data/v3.x/#toc_ember-data-default-serializers',
|
|
3688
|
-
for: '@ember-data/store',
|
|
3689
|
-
since: {
|
|
3690
|
-
available: '3.15',
|
|
3691
|
-
enabled: '3.15',
|
|
3692
|
-
},
|
|
3693
|
-
}
|
|
3694
|
-
);
|
|
3695
|
-
|
|
3696
|
-
serializer = serializerName
|
|
3697
|
-
? _serializerCache[serializerName] || owner.lookup(`serializer:${serializerName}`)
|
|
3698
|
-
: undefined;
|
|
3699
|
-
}
|
|
3700
3480
|
|
|
3701
3481
|
if (DEPRECATE_LEGACY_TEST_REGISTRATIONS) {
|
|
3702
3482
|
// in production this is handled by the re-export
|
|
@@ -3727,49 +3507,10 @@ abstract class CoreStore extends Service {
|
|
|
3727
3507
|
}
|
|
3728
3508
|
}
|
|
3729
3509
|
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
if (DEBUG && HAS_EMBER_DATA_PACKAGE && HAS_SERIALIZER_PACKAGE && serializer === undefined) {
|
|
3735
|
-
const JSONSerializer = require('@ember-data/serializer/json').default;
|
|
3736
|
-
owner.register('serializer:-default', JSONSerializer);
|
|
3737
|
-
serializer = owner.lookup('serializer:-default');
|
|
3738
|
-
|
|
3739
|
-
serializer && deprecateTestRegistration('serializer', '-default');
|
|
3740
|
-
}
|
|
3741
|
-
|
|
3742
|
-
deprecate(
|
|
3743
|
-
`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`,
|
|
3744
|
-
!serializer,
|
|
3745
|
-
{
|
|
3746
|
-
id: 'ember-data:default-serializer',
|
|
3747
|
-
until: '4.0',
|
|
3748
|
-
url: 'https://deprecations.emberjs.com/ember-data/v3.x/#toc_ember-data-default-serializers',
|
|
3749
|
-
for: '@ember-data/store',
|
|
3750
|
-
since: {
|
|
3751
|
-
available: '3.15',
|
|
3752
|
-
enabled: '3.15',
|
|
3753
|
-
},
|
|
3754
|
-
}
|
|
3755
|
-
);
|
|
3756
|
-
|
|
3757
|
-
assert(
|
|
3758
|
-
`No serializer was found for '${modelName}' and no 'application' serializer was found as a fallback`,
|
|
3759
|
-
serializer !== undefined
|
|
3760
|
-
);
|
|
3761
|
-
|
|
3762
|
-
set(serializer, 'store', this);
|
|
3763
|
-
_serializerCache[normalizedModelName] = serializer;
|
|
3764
|
-
_serializerCache['-default'] = serializer;
|
|
3765
|
-
|
|
3766
|
-
return serializer;
|
|
3767
|
-
} else {
|
|
3768
|
-
assert(
|
|
3769
|
-
`No serializer was found for '${modelName}' and no 'application' serializer was found as a fallback`,
|
|
3770
|
-
serializer !== undefined
|
|
3771
|
-
);
|
|
3772
|
-
}
|
|
3510
|
+
assert(
|
|
3511
|
+
`No serializer was found for '${modelName}' and no 'application' serializer was found as a fallback`,
|
|
3512
|
+
serializer !== undefined
|
|
3513
|
+
);
|
|
3773
3514
|
}
|
|
3774
3515
|
|
|
3775
3516
|
destroy() {
|
|
@@ -3898,73 +3639,6 @@ if (DEPRECATE_DEFAULT_ADAPTER) {
|
|
|
3898
3639
|
|
|
3899
3640
|
export default CoreStore;
|
|
3900
3641
|
|
|
3901
|
-
function _commit(adapter, store, operation, snapshot) {
|
|
3902
|
-
let internalModel = snapshot._internalModel;
|
|
3903
|
-
let modelName = snapshot.modelName;
|
|
3904
|
-
let modelClass = store.modelFor(modelName);
|
|
3905
|
-
assert(`You tried to update a record but you have no adapter (for ${modelName})`, adapter);
|
|
3906
|
-
assert(
|
|
3907
|
-
`You tried to update a record but your adapter (for ${modelName}) does not implement '${operation}'`,
|
|
3908
|
-
typeof adapter[operation] === 'function'
|
|
3909
|
-
);
|
|
3910
|
-
|
|
3911
|
-
let promise = Promise.resolve().then(() => adapter[operation](store, modelClass, snapshot));
|
|
3912
|
-
let serializer = store.serializerFor(modelName);
|
|
3913
|
-
let label = `DS: Extract and notify about ${operation} completion of ${internalModel}`;
|
|
3914
|
-
|
|
3915
|
-
promise = guardDestroyedStore(promise, store, label);
|
|
3916
|
-
promise = _guard(promise, _bind(_objectIsAlive, internalModel));
|
|
3917
|
-
|
|
3918
|
-
return promise.then(
|
|
3919
|
-
(adapterPayload) => {
|
|
3920
|
-
/*
|
|
3921
|
-
Note to future spelunkers hoping to optimize.
|
|
3922
|
-
We rely on this `run` to create a run loop if needed
|
|
3923
|
-
that `store._push` and `store.didSaveRecord` will both share.
|
|
3924
|
-
|
|
3925
|
-
We use `join` because it is often the case that we
|
|
3926
|
-
have an outer run loop available still from the first
|
|
3927
|
-
call to `store._push`;
|
|
3928
|
-
*/
|
|
3929
|
-
store._backburner.join(() => {
|
|
3930
|
-
let payload, data, sideloaded;
|
|
3931
|
-
if (adapterPayload) {
|
|
3932
|
-
payload = normalizeResponseHelper(serializer, store, modelClass, adapterPayload, snapshot.id, operation);
|
|
3933
|
-
if (payload.included) {
|
|
3934
|
-
sideloaded = payload.included;
|
|
3935
|
-
}
|
|
3936
|
-
data = payload.data;
|
|
3937
|
-
}
|
|
3938
|
-
store.didSaveRecord(internalModel, { data }, operation);
|
|
3939
|
-
// seems risky, but if the tests pass might be fine?
|
|
3940
|
-
if (sideloaded) {
|
|
3941
|
-
store._push({ data: null, included: sideloaded });
|
|
3942
|
-
}
|
|
3943
|
-
});
|
|
3944
|
-
|
|
3945
|
-
return internalModel;
|
|
3946
|
-
},
|
|
3947
|
-
function (error) {
|
|
3948
|
-
if (error && error.isAdapterError === true && error.code === 'InvalidError') {
|
|
3949
|
-
let parsedErrors;
|
|
3950
|
-
|
|
3951
|
-
if (typeof serializer.extractErrors === 'function') {
|
|
3952
|
-
parsedErrors = serializer.extractErrors(store, modelClass, error, snapshot.id);
|
|
3953
|
-
} else {
|
|
3954
|
-
parsedErrors = errorsArrayToHash(error.errors);
|
|
3955
|
-
}
|
|
3956
|
-
|
|
3957
|
-
store.recordWasInvalid(internalModel, parsedErrors, error);
|
|
3958
|
-
} else {
|
|
3959
|
-
store.recordWasError(internalModel, error);
|
|
3960
|
-
}
|
|
3961
|
-
|
|
3962
|
-
throw error;
|
|
3963
|
-
},
|
|
3964
|
-
label
|
|
3965
|
-
);
|
|
3966
|
-
}
|
|
3967
|
-
|
|
3968
3642
|
let assertDestroyingStore: Function;
|
|
3969
3643
|
let assertDestroyedStoreOnly: Function;
|
|
3970
3644
|
|