@ember-data/store 4.2.0-alpha.1 → 4.2.0-alpha.5
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 +130 -391
- 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/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,
|
|
@@ -68,7 +62,6 @@ import constructResource from '../utils/construct-resource';
|
|
|
68
62
|
import promiseRecord from '../utils/promise-record';
|
|
69
63
|
import edBackburner from './backburner';
|
|
70
64
|
import coerceId, { ensureStringId } from './coerce-id';
|
|
71
|
-
import { errorsArrayToHash } from './errors-utils';
|
|
72
65
|
import FetchManager, { SaveOp } from './fetch-manager';
|
|
73
66
|
import type InternalModel from './model/internal-model';
|
|
74
67
|
import {
|
|
@@ -86,8 +79,7 @@ import NotificationManager from './record-notification-manager';
|
|
|
86
79
|
import type { BelongsToReference, HasManyReference } from './references';
|
|
87
80
|
import { RecordReference } from './references';
|
|
88
81
|
import type RequestCache from './request-cache';
|
|
89
|
-
import type { default as Snapshot
|
|
90
|
-
import { _bind, _guard, _objectIsAlive, guardDestroyedStore } from './store/common';
|
|
82
|
+
import type { default as Snapshot } from './snapshot';
|
|
91
83
|
import { _find, _findAll, _findBelongsTo, _findHasMany, _findMany, _query, _queryRecord } from './store/finders';
|
|
92
84
|
import {
|
|
93
85
|
internalModelFactoryFor,
|
|
@@ -96,7 +88,6 @@ import {
|
|
|
96
88
|
setRecordIdentifier,
|
|
97
89
|
} from './store/internal-model-factory';
|
|
98
90
|
import RecordDataStoreWrapper from './store/record-data-store-wrapper';
|
|
99
|
-
import { normalizeResponseHelper } from './store/serializer-response';
|
|
100
91
|
|
|
101
92
|
type RecordDataConstruct = typeof RecordDataClass;
|
|
102
93
|
let _RecordData: RecordDataConstruct | undefined;
|
|
@@ -310,13 +301,9 @@ abstract class CoreStore extends Service {
|
|
|
310
301
|
constructor() {
|
|
311
302
|
super(...arguments);
|
|
312
303
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
if (CUSTOM_MODEL_CLASS) {
|
|
317
|
-
this._notificationManager = new NotificationManager(this);
|
|
318
|
-
this.__recordDataFor = this.__recordDataFor.bind(this);
|
|
319
|
-
}
|
|
304
|
+
this._fetchManager = new FetchManager(this);
|
|
305
|
+
this._notificationManager = new NotificationManager(this);
|
|
306
|
+
this.__recordDataFor = this.__recordDataFor.bind(this);
|
|
320
307
|
|
|
321
308
|
if (DEBUG) {
|
|
322
309
|
if (HAS_EMBER_DATA_PACKAGE && HAS_SERIALIZER_PACKAGE) {
|
|
@@ -421,10 +408,7 @@ abstract class CoreStore extends Service {
|
|
|
421
408
|
}
|
|
422
409
|
|
|
423
410
|
getRequestStateService(): RequestCache {
|
|
424
|
-
|
|
425
|
-
return this._fetchManager.requestCache;
|
|
426
|
-
}
|
|
427
|
-
assert('RequestService is not available unless the feature flag is on and running on a canary build', false);
|
|
411
|
+
return this._fetchManager.requestCache;
|
|
428
412
|
}
|
|
429
413
|
|
|
430
414
|
/**
|
|
@@ -448,55 +432,51 @@ abstract class CoreStore extends Service {
|
|
|
448
432
|
identifier: StableRecordIdentifier,
|
|
449
433
|
properties?: { [key: string]: any }
|
|
450
434
|
) {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
);
|
|
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
|
+
);
|
|
458
441
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
442
|
+
if ('id' in properties) {
|
|
443
|
+
internalModel.setId(properties.id);
|
|
444
|
+
}
|
|
462
445
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
for (let i = 0; i < keys.length; i++) {
|
|
471
|
-
let prop = keys[i];
|
|
472
|
-
let def = defs[prop];
|
|
473
|
-
|
|
474
|
-
if (def !== undefined) {
|
|
475
|
-
if (def.kind === 'hasMany') {
|
|
476
|
-
if (DEBUG) {
|
|
477
|
-
assertRecordsPassedToHasMany(properties[prop]);
|
|
478
|
-
}
|
|
479
|
-
relationshipValue = extractRecordDatasFromRecords(properties[prop]);
|
|
480
|
-
} else {
|
|
481
|
-
relationshipValue = extractRecordDataFromRecord(properties[prop]);
|
|
482
|
-
}
|
|
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;
|
|
483
452
|
|
|
484
|
-
|
|
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]);
|
|
485
465
|
}
|
|
466
|
+
|
|
467
|
+
properties[prop] = relationshipValue;
|
|
486
468
|
}
|
|
487
469
|
}
|
|
488
470
|
}
|
|
489
|
-
|
|
490
|
-
// TODO guard against initRecordOptions no being there
|
|
491
|
-
let createOptions = recordData._initRecordCreateOptions(properties);
|
|
492
|
-
//TODO Igor pass a wrapper instead of RD
|
|
493
|
-
let record = this.instantiateRecord(identifier, createOptions, this.__recordDataFor, this._notificationManager);
|
|
494
|
-
setRecordIdentifier(record, identifier);
|
|
495
|
-
//recordToInternalModelMap.set(record, internalModel);
|
|
496
|
-
return record;
|
|
497
471
|
}
|
|
498
472
|
|
|
499
|
-
|
|
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;
|
|
500
480
|
}
|
|
501
481
|
|
|
502
482
|
abstract instantiateRecord(
|
|
@@ -534,10 +514,7 @@ abstract class CoreStore extends Service {
|
|
|
534
514
|
}
|
|
535
515
|
|
|
536
516
|
getSchemaDefinitionService(): SchemaDefinitionService {
|
|
537
|
-
|
|
538
|
-
return this._schemaDefinitionService;
|
|
539
|
-
}
|
|
540
|
-
assert('need to enable CUSTOM_MODEL_CLASS feature flag in order to access SchemaDefinitionService');
|
|
517
|
+
return this._schemaDefinitionService;
|
|
541
518
|
}
|
|
542
519
|
|
|
543
520
|
// TODO Double check this return value is correct
|
|
@@ -715,16 +692,12 @@ abstract class CoreStore extends Service {
|
|
|
715
692
|
assertDestroyingStore(this, 'deleteRecord');
|
|
716
693
|
}
|
|
717
694
|
this._backburner.join(() => {
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
internalModel.deleteRecord();
|
|
724
|
-
}
|
|
695
|
+
let identifier = peekRecordIdentifier(record);
|
|
696
|
+
if (identifier) {
|
|
697
|
+
let internalModel = internalModelFactoryFor(this).peek(identifier);
|
|
698
|
+
if (internalModel) {
|
|
699
|
+
internalModel.deleteRecord();
|
|
725
700
|
}
|
|
726
|
-
} else {
|
|
727
|
-
record.deleteRecord();
|
|
728
701
|
}
|
|
729
702
|
});
|
|
730
703
|
}
|
|
@@ -749,16 +722,12 @@ abstract class CoreStore extends Service {
|
|
|
749
722
|
if (DEBUG) {
|
|
750
723
|
assertDestroyingStore(this, 'unloadRecord');
|
|
751
724
|
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
internalModel.unloadRecord();
|
|
758
|
-
}
|
|
725
|
+
let identifier = peekRecordIdentifier(record);
|
|
726
|
+
if (identifier) {
|
|
727
|
+
let internalModel = internalModelFactoryFor(this).peek(identifier);
|
|
728
|
+
if (internalModel) {
|
|
729
|
+
internalModel.unloadRecord();
|
|
759
730
|
}
|
|
760
|
-
} else {
|
|
761
|
-
record.unloadRecord();
|
|
762
731
|
}
|
|
763
732
|
}
|
|
764
733
|
|
|
@@ -1267,19 +1236,12 @@ abstract class CoreStore extends Service {
|
|
|
1267
1236
|
return this._scheduleFetch(internalModel, options);
|
|
1268
1237
|
}
|
|
1269
1238
|
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
if (
|
|
1273
|
-
return internalModel
|
|
1274
|
-
}
|
|
1275
|
-
} else {
|
|
1276
|
-
if (internalModel.currentState.isLoading) {
|
|
1277
|
-
let pendingRequest = this._fetchManager.getPendingFetch(internalModel.identifier, options);
|
|
1278
|
-
if (pendingRequest) {
|
|
1279
|
-
return pendingRequest.then(() => Promise.resolve(internalModel));
|
|
1280
|
-
}
|
|
1281
|
-
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));
|
|
1282
1243
|
}
|
|
1244
|
+
return this._scheduleFetch(internalModel, options);
|
|
1283
1245
|
}
|
|
1284
1246
|
|
|
1285
1247
|
return Promise.resolve(internalModel);
|
|
@@ -1385,74 +1347,12 @@ abstract class CoreStore extends Service {
|
|
|
1385
1347
|
}
|
|
1386
1348
|
|
|
1387
1349
|
_scheduleFetch(internalModel: InternalModel, options): RSVP.Promise<InternalModel> {
|
|
1388
|
-
|
|
1389
|
-
return this._scheduleFetchThroughFetchManager(internalModel, options);
|
|
1390
|
-
} else {
|
|
1391
|
-
if (internalModel._promiseProxy) {
|
|
1392
|
-
return internalModel._promiseProxy;
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
assertIdentifierHasId(internalModel.identifier);
|
|
1396
|
-
|
|
1397
|
-
let { id, modelName } = internalModel;
|
|
1398
|
-
let resolver = defer<InternalModel>(`Fetching ${modelName}' with id: ${id}`);
|
|
1399
|
-
let pendingFetchItem: PendingFetchItem = {
|
|
1400
|
-
internalModel,
|
|
1401
|
-
resolver,
|
|
1402
|
-
options,
|
|
1403
|
-
};
|
|
1404
|
-
|
|
1405
|
-
if (DEBUG) {
|
|
1406
|
-
if (this.generateStackTracesForTrackedRequests === true) {
|
|
1407
|
-
let trace;
|
|
1408
|
-
|
|
1409
|
-
try {
|
|
1410
|
-
throw new Error(`Trace Origin for scheduled fetch for ${modelName}:${id}.`);
|
|
1411
|
-
} catch (e) {
|
|
1412
|
-
trace = e;
|
|
1413
|
-
}
|
|
1414
|
-
|
|
1415
|
-
// enable folks to discover the origin of this findRecord call when
|
|
1416
|
-
// debugging. Ideally we would have a tracked queue for requests with
|
|
1417
|
-
// labels or local IDs that could be used to merge this trace with
|
|
1418
|
-
// the trace made available when we detect an async leak
|
|
1419
|
-
pendingFetchItem.trace = trace;
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
|
-
|
|
1423
|
-
let promise = resolver.promise;
|
|
1424
|
-
|
|
1425
|
-
internalModel.send('loadingData', promise);
|
|
1426
|
-
if (this._pendingFetch.size === 0) {
|
|
1427
|
-
emberBackburner.schedule('actions', this, this.flushAllPendingFetches);
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
let fetches = this._pendingFetch;
|
|
1431
|
-
let pending = fetches.get(modelName);
|
|
1432
|
-
|
|
1433
|
-
if (pending === undefined) {
|
|
1434
|
-
pending = [];
|
|
1435
|
-
fetches.set(modelName, pending);
|
|
1436
|
-
}
|
|
1437
|
-
|
|
1438
|
-
pending.push(pendingFetchItem);
|
|
1439
|
-
|
|
1440
|
-
return promise;
|
|
1441
|
-
}
|
|
1350
|
+
return this._scheduleFetchThroughFetchManager(internalModel, options);
|
|
1442
1351
|
}
|
|
1443
1352
|
|
|
1444
1353
|
flushAllPendingFetches() {
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
//assert here
|
|
1448
|
-
} else {
|
|
1449
|
-
if (this.isDestroyed || this.isDestroying) {
|
|
1450
|
-
return;
|
|
1451
|
-
}
|
|
1452
|
-
|
|
1453
|
-
this._pendingFetch.forEach(this._flushPendingFetchForType, this);
|
|
1454
|
-
this._pendingFetch.clear();
|
|
1455
|
-
}
|
|
1354
|
+
return;
|
|
1355
|
+
//assert here
|
|
1456
1356
|
}
|
|
1457
1357
|
|
|
1458
1358
|
_flushPendingFetchForType(pendingFetchItems: PendingFetchItem[], modelName: string) {
|
|
@@ -1768,9 +1668,7 @@ abstract class CoreStore extends Service {
|
|
|
1768
1668
|
@return {Promise} promise
|
|
1769
1669
|
*/
|
|
1770
1670
|
_reloadRecord(internalModel, options): RSVP.Promise<InternalModel> {
|
|
1771
|
-
|
|
1772
|
-
options.isReloading = true;
|
|
1773
|
-
}
|
|
1671
|
+
options.isReloading = true;
|
|
1774
1672
|
let { id, modelName } = internalModel;
|
|
1775
1673
|
let adapter = this.adapterFor(modelName);
|
|
1776
1674
|
|
|
@@ -2017,17 +1915,9 @@ abstract class CoreStore extends Service {
|
|
|
2017
1915
|
|
|
2018
1916
|
if (internalModel) {
|
|
2019
1917
|
// short circuit if we are already loading
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
return pendingRequest.then(() => internalModel.getRecord());
|
|
2024
|
-
}
|
|
2025
|
-
} else {
|
|
2026
|
-
if (internalModel.currentState.isLoading) {
|
|
2027
|
-
return internalModel._promiseProxy.then(() => {
|
|
2028
|
-
return internalModel.getRecord();
|
|
2029
|
-
});
|
|
2030
|
-
}
|
|
1918
|
+
let pendingRequest = this._fetchManager.getPendingFetch(internalModel.identifier, options);
|
|
1919
|
+
if (pendingRequest) {
|
|
1920
|
+
return pendingRequest.then(() => internalModel.getRecord());
|
|
2031
1921
|
}
|
|
2032
1922
|
}
|
|
2033
1923
|
|
|
@@ -2660,33 +2550,32 @@ abstract class CoreStore extends Service {
|
|
|
2660
2550
|
@param {Object} options
|
|
2661
2551
|
*/
|
|
2662
2552
|
scheduleSave(internalModel: InternalModel, resolver: RSVP.Deferred<void>, options): void | RSVP.Promise<void> {
|
|
2663
|
-
if (
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
}
|
|
2553
|
+
if (internalModel._isRecordFullyDeleted()) {
|
|
2554
|
+
resolver.resolve();
|
|
2555
|
+
return resolver.promise;
|
|
2556
|
+
}
|
|
2668
2557
|
|
|
2669
|
-
|
|
2558
|
+
internalModel.adapterWillCommit();
|
|
2670
2559
|
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2560
|
+
if (!options) {
|
|
2561
|
+
options = {};
|
|
2562
|
+
}
|
|
2563
|
+
let recordData = internalModel._recordData;
|
|
2564
|
+
let operation: 'createRecord' | 'deleteRecord' | 'updateRecord' = 'updateRecord';
|
|
2676
2565
|
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
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
|
+
}
|
|
2683
2572
|
|
|
2684
|
-
|
|
2573
|
+
options[SaveOp] = operation;
|
|
2685
2574
|
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2575
|
+
let fetchManagerPromise = this._fetchManager.scheduleSave(internalModel.identifier, options);
|
|
2576
|
+
let promise = fetchManagerPromise.then(
|
|
2577
|
+
(payload) => {
|
|
2578
|
+
/*
|
|
2690
2579
|
Note to future spelunkers hoping to optimize.
|
|
2691
2580
|
We rely on this `run` to create a run loop if needed
|
|
2692
2581
|
that `store._push` and `store.didSaveRecord` will both share.
|
|
@@ -2695,40 +2584,25 @@ abstract class CoreStore extends Service {
|
|
|
2695
2584
|
have an outer run loop available still from the first
|
|
2696
2585
|
call to `store._push`;
|
|
2697
2586
|
*/
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
}
|
|
2704
|
-
});
|
|
2705
|
-
},
|
|
2706
|
-
(e) => {
|
|
2707
|
-
if (typeof e === 'string') {
|
|
2708
|
-
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 });
|
|
2709
2592
|
}
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2593
|
+
});
|
|
2594
|
+
},
|
|
2595
|
+
(e) => {
|
|
2596
|
+
if (typeof e === 'string') {
|
|
2597
|
+
throw e;
|
|
2713
2598
|
}
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
} else {
|
|
2718
|
-
if (internalModel._isRecordFullyDeleted()) {
|
|
2719
|
-
resolver.resolve();
|
|
2720
|
-
return;
|
|
2599
|
+
const { error, parsedErrors } = e;
|
|
2600
|
+
this.recordWasInvalid(internalModel, parsedErrors, error);
|
|
2601
|
+
throw error;
|
|
2721
2602
|
}
|
|
2603
|
+
);
|
|
2722
2604
|
|
|
2723
|
-
|
|
2724
|
-
internalModel.adapterWillCommit();
|
|
2725
|
-
this._pendingSave.push({
|
|
2726
|
-
snapshot: snapshot,
|
|
2727
|
-
resolver: resolver,
|
|
2728
|
-
});
|
|
2729
|
-
|
|
2730
|
-
emberBackburner.scheduleOnce('actions', this, this.flushPendingSave);
|
|
2731
|
-
}
|
|
2605
|
+
return promise;
|
|
2732
2606
|
}
|
|
2733
2607
|
|
|
2734
2608
|
/**
|
|
@@ -2739,47 +2613,8 @@ abstract class CoreStore extends Service {
|
|
|
2739
2613
|
@private
|
|
2740
2614
|
*/
|
|
2741
2615
|
flushPendingSave() {
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
return;
|
|
2745
|
-
}
|
|
2746
|
-
let pending = this._pendingSave.slice();
|
|
2747
|
-
this._pendingSave = [];
|
|
2748
|
-
|
|
2749
|
-
for (let i = 0, j = pending.length; i < j; i++) {
|
|
2750
|
-
let pendingItem = pending[i];
|
|
2751
|
-
let snapshot = pendingItem.snapshot;
|
|
2752
|
-
let resolver = pendingItem.resolver;
|
|
2753
|
-
// TODO We have to cast due to our reliance on this private property
|
|
2754
|
-
// this will be refactored away once we change our pending API to be identifier based
|
|
2755
|
-
let internalModel = (snapshot as unknown as PrivateSnapshot)._internalModel;
|
|
2756
|
-
let adapter = this.adapterFor(internalModel.modelName);
|
|
2757
|
-
let operation;
|
|
2758
|
-
|
|
2759
|
-
if (RECORD_DATA_STATE) {
|
|
2760
|
-
// TODO move this out of internalModel
|
|
2761
|
-
if (internalModel.isNew()) {
|
|
2762
|
-
operation = 'createRecord';
|
|
2763
|
-
} else if (internalModel.isDeleted()) {
|
|
2764
|
-
operation = 'deleteRecord';
|
|
2765
|
-
} else {
|
|
2766
|
-
operation = 'updateRecord';
|
|
2767
|
-
}
|
|
2768
|
-
} else {
|
|
2769
|
-
if (internalModel.currentState.stateName === 'root.deleted.saved') {
|
|
2770
|
-
resolver.resolve();
|
|
2771
|
-
continue;
|
|
2772
|
-
} else if (internalModel.isNew()) {
|
|
2773
|
-
operation = 'createRecord';
|
|
2774
|
-
} else if (internalModel.isDeleted()) {
|
|
2775
|
-
operation = 'deleteRecord';
|
|
2776
|
-
} else {
|
|
2777
|
-
operation = 'updateRecord';
|
|
2778
|
-
}
|
|
2779
|
-
}
|
|
2780
|
-
|
|
2781
|
-
resolver.resolve(_commit(adapter, this, operation, snapshot));
|
|
2782
|
-
}
|
|
2616
|
+
// assert here
|
|
2617
|
+
return;
|
|
2783
2618
|
}
|
|
2784
2619
|
|
|
2785
2620
|
/**
|
|
@@ -2837,11 +2672,7 @@ abstract class CoreStore extends Service {
|
|
|
2837
2672
|
if (DEBUG) {
|
|
2838
2673
|
assertDestroyingStore(this, 'recordWasInvalid');
|
|
2839
2674
|
}
|
|
2840
|
-
|
|
2841
|
-
internalModel.adapterDidInvalidate(parsedErrors, error);
|
|
2842
|
-
} else {
|
|
2843
|
-
internalModel.adapterDidInvalidate(parsedErrors);
|
|
2844
|
-
}
|
|
2675
|
+
internalModel.adapterDidInvalidate(parsedErrors, error);
|
|
2845
2676
|
}
|
|
2846
2677
|
|
|
2847
2678
|
/**
|
|
@@ -3166,30 +2997,17 @@ abstract class CoreStore extends Service {
|
|
|
3166
2997
|
|
|
3167
2998
|
if (ENV.DS_WARN_ON_UNKNOWN_KEYS) {
|
|
3168
2999
|
let unknownAttributes, unknownRelationships;
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
});
|
|
3181
|
-
} else {
|
|
3182
|
-
let modelClass = this.modelFor(modelName);
|
|
3183
|
-
// Check unknown attributes
|
|
3184
|
-
unknownAttributes = Object.keys(data.attributes || {}).filter((key) => {
|
|
3185
|
-
return !get(modelClass, 'fields').has(key);
|
|
3186
|
-
});
|
|
3187
|
-
|
|
3188
|
-
// Check unknown relationships
|
|
3189
|
-
unknownRelationships = Object.keys(data.relationships || {}).filter((key) => {
|
|
3190
|
-
return !get(modelClass, 'fields').has(key);
|
|
3191
|
-
});
|
|
3192
|
-
}
|
|
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
|
+
});
|
|
3193
3011
|
let unknownAttributesMessage = `The payload for '${modelName}' contains these unknown attributes: ${unknownAttributes}. Make sure they've been defined in your model.`;
|
|
3194
3012
|
warn(unknownAttributesMessage, unknownAttributes.length === 0, {
|
|
3195
3013
|
id: 'ds.store.unknown-keys-in-payload',
|
|
@@ -3319,38 +3137,26 @@ abstract class CoreStore extends Service {
|
|
|
3319
3137
|
}
|
|
3320
3138
|
|
|
3321
3139
|
serializeRecord(record: RecordInstance, options?: Dict<unknown>): unknown {
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
return internalModel!.createSnapshot(options).serialize(options);
|
|
3327
|
-
}
|
|
3328
|
-
|
|
3329
|
-
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);
|
|
3330
3144
|
}
|
|
3331
3145
|
|
|
3332
3146
|
saveRecord(record: RecordInstance, options?: Dict<unknown>): RSVP.Promise<RecordInstance> {
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
return (internalModel!.save(options) as RSVP.Promise<void>).then(() => record);
|
|
3340
|
-
}
|
|
3341
|
-
|
|
3342
|
-
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);
|
|
3343
3153
|
}
|
|
3344
3154
|
|
|
3345
3155
|
relationshipReferenceFor(identifier: RecordIdentifier, key: string): BelongsToReference | HasManyReference {
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
return internalModel!.referenceFor(null, key);
|
|
3351
|
-
}
|
|
3352
|
-
|
|
3353
|
-
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);
|
|
3354
3160
|
}
|
|
3355
3161
|
|
|
3356
3162
|
/**
|
|
@@ -3833,73 +3639,6 @@ if (DEPRECATE_DEFAULT_ADAPTER) {
|
|
|
3833
3639
|
|
|
3834
3640
|
export default CoreStore;
|
|
3835
3641
|
|
|
3836
|
-
function _commit(adapter, store, operation, snapshot) {
|
|
3837
|
-
let internalModel = snapshot._internalModel;
|
|
3838
|
-
let modelName = snapshot.modelName;
|
|
3839
|
-
let modelClass = store.modelFor(modelName);
|
|
3840
|
-
assert(`You tried to update a record but you have no adapter (for ${modelName})`, adapter);
|
|
3841
|
-
assert(
|
|
3842
|
-
`You tried to update a record but your adapter (for ${modelName}) does not implement '${operation}'`,
|
|
3843
|
-
typeof adapter[operation] === 'function'
|
|
3844
|
-
);
|
|
3845
|
-
|
|
3846
|
-
let promise = Promise.resolve().then(() => adapter[operation](store, modelClass, snapshot));
|
|
3847
|
-
let serializer = store.serializerFor(modelName);
|
|
3848
|
-
let label = `DS: Extract and notify about ${operation} completion of ${internalModel}`;
|
|
3849
|
-
|
|
3850
|
-
promise = guardDestroyedStore(promise, store, label);
|
|
3851
|
-
promise = _guard(promise, _bind(_objectIsAlive, internalModel));
|
|
3852
|
-
|
|
3853
|
-
return promise.then(
|
|
3854
|
-
(adapterPayload) => {
|
|
3855
|
-
/*
|
|
3856
|
-
Note to future spelunkers hoping to optimize.
|
|
3857
|
-
We rely on this `run` to create a run loop if needed
|
|
3858
|
-
that `store._push` and `store.didSaveRecord` will both share.
|
|
3859
|
-
|
|
3860
|
-
We use `join` because it is often the case that we
|
|
3861
|
-
have an outer run loop available still from the first
|
|
3862
|
-
call to `store._push`;
|
|
3863
|
-
*/
|
|
3864
|
-
store._backburner.join(() => {
|
|
3865
|
-
let payload, data, sideloaded;
|
|
3866
|
-
if (adapterPayload) {
|
|
3867
|
-
payload = normalizeResponseHelper(serializer, store, modelClass, adapterPayload, snapshot.id, operation);
|
|
3868
|
-
if (payload.included) {
|
|
3869
|
-
sideloaded = payload.included;
|
|
3870
|
-
}
|
|
3871
|
-
data = payload.data;
|
|
3872
|
-
}
|
|
3873
|
-
store.didSaveRecord(internalModel, { data }, operation);
|
|
3874
|
-
// seems risky, but if the tests pass might be fine?
|
|
3875
|
-
if (sideloaded) {
|
|
3876
|
-
store._push({ data: null, included: sideloaded });
|
|
3877
|
-
}
|
|
3878
|
-
});
|
|
3879
|
-
|
|
3880
|
-
return internalModel;
|
|
3881
|
-
},
|
|
3882
|
-
function (error) {
|
|
3883
|
-
if (error && error.isAdapterError === true && error.code === 'InvalidError') {
|
|
3884
|
-
let parsedErrors;
|
|
3885
|
-
|
|
3886
|
-
if (typeof serializer.extractErrors === 'function') {
|
|
3887
|
-
parsedErrors = serializer.extractErrors(store, modelClass, error, snapshot.id);
|
|
3888
|
-
} else {
|
|
3889
|
-
parsedErrors = errorsArrayToHash(error.errors);
|
|
3890
|
-
}
|
|
3891
|
-
|
|
3892
|
-
store.recordWasInvalid(internalModel, parsedErrors, error);
|
|
3893
|
-
} else {
|
|
3894
|
-
store.recordWasError(internalModel, error);
|
|
3895
|
-
}
|
|
3896
|
-
|
|
3897
|
-
throw error;
|
|
3898
|
-
},
|
|
3899
|
-
label
|
|
3900
|
-
);
|
|
3901
|
-
}
|
|
3902
|
-
|
|
3903
3642
|
let assertDestroyingStore: Function;
|
|
3904
3643
|
let assertDestroyedStoreOnly: Function;
|
|
3905
3644
|
|