@ember-data/store 4.2.0-alpha.2 → 4.2.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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, set } from '@ember/object';
7
+ import { computed, defineProperty, get, 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,8 +14,14 @@ 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, Promise, resolve } from 'rsvp';
17
+ import { all, default as RSVP, defer, 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';
19
25
  import {
20
26
  HAS_ADAPTER_PACKAGE,
21
27
  HAS_EMBER_DATA_PACKAGE,
@@ -24,6 +30,7 @@ import {
24
30
  } from '@ember-data/private-build-infra';
25
31
  import {
26
32
  DEPRECATE_DEFAULT_ADAPTER,
33
+ DEPRECATE_DEFAULT_SERIALIZER,
27
34
  DEPRECATE_LEGACY_TEST_REGISTRATIONS,
28
35
  } from '@ember-data/private-build-infra/deprecations';
29
36
  import type {
@@ -62,6 +69,7 @@ import constructResource from '../utils/construct-resource';
62
69
  import promiseRecord from '../utils/promise-record';
63
70
  import edBackburner from './backburner';
64
71
  import coerceId, { ensureStringId } from './coerce-id';
72
+ import { errorsArrayToHash } from './errors-utils';
65
73
  import FetchManager, { SaveOp } from './fetch-manager';
66
74
  import type InternalModel from './model/internal-model';
67
75
  import {
@@ -79,7 +87,8 @@ import NotificationManager from './record-notification-manager';
79
87
  import type { BelongsToReference, HasManyReference } from './references';
80
88
  import { RecordReference } from './references';
81
89
  import type RequestCache from './request-cache';
82
- import type { default as Snapshot } from './snapshot';
90
+ import type { default as Snapshot, PrivateSnapshot } from './snapshot';
91
+ import { _bind, _guard, _objectIsAlive, guardDestroyedStore } from './store/common';
83
92
  import { _find, _findAll, _findBelongsTo, _findHasMany, _findMany, _query, _queryRecord } from './store/finders';
84
93
  import {
85
94
  internalModelFactoryFor,
@@ -88,6 +97,7 @@ import {
88
97
  setRecordIdentifier,
89
98
  } from './store/internal-model-factory';
90
99
  import RecordDataStoreWrapper from './store/record-data-store-wrapper';
100
+ import { normalizeResponseHelper } from './store/serializer-response';
91
101
 
92
102
  type RecordDataConstruct = typeof RecordDataClass;
93
103
  let _RecordData: RecordDataConstruct | undefined;
@@ -301,9 +311,13 @@ abstract class CoreStore extends Service {
301
311
  constructor() {
302
312
  super(...arguments);
303
313
 
304
- this._fetchManager = new FetchManager(this);
305
- this._notificationManager = new NotificationManager(this);
306
- this.__recordDataFor = this.__recordDataFor.bind(this);
314
+ if (REQUEST_SERVICE) {
315
+ this._fetchManager = new FetchManager(this);
316
+ }
317
+ if (CUSTOM_MODEL_CLASS) {
318
+ this._notificationManager = new NotificationManager(this);
319
+ this.__recordDataFor = this.__recordDataFor.bind(this);
320
+ }
307
321
 
308
322
  if (DEBUG) {
309
323
  if (HAS_EMBER_DATA_PACKAGE && HAS_SERIALIZER_PACKAGE) {
@@ -408,7 +422,10 @@ abstract class CoreStore extends Service {
408
422
  }
409
423
 
410
424
  getRequestStateService(): RequestCache {
411
- return this._fetchManager.requestCache;
425
+ if (REQUEST_SERVICE) {
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);
412
429
  }
413
430
 
414
431
  /**
@@ -432,51 +449,55 @@ abstract class CoreStore extends Service {
432
449
  identifier: StableRecordIdentifier,
433
450
  properties?: { [key: string]: any }
434
451
  ) {
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
- );
441
-
442
- if ('id' in properties) {
443
- internalModel.setId(properties.id);
444
- }
445
-
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
+ if (CUSTOM_MODEL_CLASS) {
453
+ // assert here
454
+ if (properties !== undefined) {
455
+ assert(
456
+ `You passed '${properties}' as properties for record creation instead of an object.`,
457
+ typeof properties === 'object' && properties !== null
458
+ );
452
459
 
453
- for (let i = 0; i < keys.length; i++) {
454
- let prop = keys[i];
455
- let def = defs[prop];
460
+ if ('id' in properties) {
461
+ internalModel.setId(properties.id);
462
+ }
456
463
 
457
- if (def !== undefined) {
458
- if (def.kind === 'hasMany') {
459
- if (DEBUG) {
460
- assertRecordsPassedToHasMany(properties[prop]);
464
+ // convert relationship Records to RecordDatas before passing to RecordData
465
+ let defs = this._relationshipsDefinitionFor(modelName);
466
+
467
+ if (defs !== null) {
468
+ let keys = Object.keys(properties);
469
+ let relationshipValue;
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]);
461
483
  }
462
- relationshipValue = extractRecordDatasFromRecords(properties[prop]);
463
- } else {
464
- relationshipValue = extractRecordDataFromRecord(properties[prop]);
465
- }
466
484
 
467
- properties[prop] = relationshipValue;
485
+ properties[prop] = relationshipValue;
486
+ }
468
487
  }
469
488
  }
470
489
  }
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;
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
+ assert('should not be here, custom model class ff error', false);
480
501
  }
481
502
 
482
503
  abstract instantiateRecord(
@@ -514,7 +535,10 @@ abstract class CoreStore extends Service {
514
535
  }
515
536
 
516
537
  getSchemaDefinitionService(): SchemaDefinitionService {
517
- return this._schemaDefinitionService;
538
+ if (CUSTOM_MODEL_CLASS) {
539
+ return this._schemaDefinitionService;
540
+ }
541
+ assert('need to enable CUSTOM_MODEL_CLASS feature flag in order to access SchemaDefinitionService');
518
542
  }
519
543
 
520
544
  // TODO Double check this return value is correct
@@ -692,12 +716,16 @@ abstract class CoreStore extends Service {
692
716
  assertDestroyingStore(this, 'deleteRecord');
693
717
  }
694
718
  this._backburner.join(() => {
695
- let identifier = peekRecordIdentifier(record);
696
- if (identifier) {
697
- let internalModel = internalModelFactoryFor(this).peek(identifier);
698
- if (internalModel) {
699
- internalModel.deleteRecord();
719
+ if (CUSTOM_MODEL_CLASS) {
720
+ let identifier = peekRecordIdentifier(record);
721
+ if (identifier) {
722
+ let internalModel = internalModelFactoryFor(this).peek(identifier);
723
+ if (internalModel) {
724
+ internalModel.deleteRecord();
725
+ }
700
726
  }
727
+ } else {
728
+ record.deleteRecord();
701
729
  }
702
730
  });
703
731
  }
@@ -722,12 +750,16 @@ abstract class CoreStore extends Service {
722
750
  if (DEBUG) {
723
751
  assertDestroyingStore(this, 'unloadRecord');
724
752
  }
725
- let identifier = peekRecordIdentifier(record);
726
- if (identifier) {
727
- let internalModel = internalModelFactoryFor(this).peek(identifier);
728
- if (internalModel) {
729
- internalModel.unloadRecord();
753
+ if (CUSTOM_MODEL_CLASS) {
754
+ let identifier = peekRecordIdentifier(record);
755
+ if (identifier) {
756
+ let internalModel = internalModelFactoryFor(this).peek(identifier);
757
+ if (internalModel) {
758
+ internalModel.unloadRecord();
759
+ }
730
760
  }
761
+ } else {
762
+ record.unloadRecord();
731
763
  }
732
764
  }
733
765
 
@@ -1216,7 +1248,7 @@ abstract class CoreStore extends Service {
1216
1248
  return Promise.resolve(internalModel);
1217
1249
  }
1218
1250
 
1219
- _findByInternalModel(internalModel: InternalModel, options: FindOptions = {}) {
1251
+ _findByInternalModel(internalModel, options: { preload?: any } = {}) {
1220
1252
  if (options.preload) {
1221
1253
  this._backburner.join(() => {
1222
1254
  internalModel.preloadData(options.preload);
@@ -1231,17 +1263,24 @@ abstract class CoreStore extends Service {
1231
1263
  );
1232
1264
  }
1233
1265
 
1234
- _findEmptyInternalModel(internalModel: InternalModel, options: FindOptions) {
1266
+ _findEmptyInternalModel(internalModel, options) {
1235
1267
  if (internalModel.currentState.isEmpty) {
1236
1268
  return this._scheduleFetch(internalModel, options);
1237
1269
  }
1238
1270
 
1239
- if (internalModel.currentState.isLoading) {
1240
- let pendingRequest = this._fetchManager.getPendingFetch(internalModel.identifier, options);
1241
- if (pendingRequest) {
1242
- return pendingRequest.then(() => Promise.resolve(internalModel));
1271
+ //TODO double check about reloading
1272
+ if (!REQUEST_SERVICE) {
1273
+ if (internalModel.currentState.isLoading) {
1274
+ return internalModel._promiseProxy;
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);
1243
1283
  }
1244
- return this._scheduleFetch(internalModel, options);
1245
1284
  }
1246
1285
 
1247
1286
  return Promise.resolve(internalModel);
@@ -1347,12 +1386,74 @@ abstract class CoreStore extends Service {
1347
1386
  }
1348
1387
 
1349
1388
  _scheduleFetch(internalModel: InternalModel, options): RSVP.Promise<InternalModel> {
1350
- return this._scheduleFetchThroughFetchManager(internalModel, options);
1389
+ if (REQUEST_SERVICE) {
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
+ }
1351
1443
  }
1352
1444
 
1353
1445
  flushAllPendingFetches() {
1354
- return;
1355
- //assert here
1446
+ if (REQUEST_SERVICE) {
1447
+ return;
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
+ }
1356
1457
  }
1357
1458
 
1358
1459
  _flushPendingFetchForType(pendingFetchItems: PendingFetchItem[], modelName: string) {
@@ -1668,7 +1769,9 @@ abstract class CoreStore extends Service {
1668
1769
  @return {Promise} promise
1669
1770
  */
1670
1771
  _reloadRecord(internalModel, options): RSVP.Promise<InternalModel> {
1671
- options.isReloading = true;
1772
+ if (REQUEST_SERVICE) {
1773
+ options.isReloading = true;
1774
+ }
1672
1775
  let { id, modelName } = internalModel;
1673
1776
  let adapter = this.adapterFor(modelName);
1674
1777
 
@@ -1915,9 +2018,17 @@ abstract class CoreStore extends Service {
1915
2018
 
1916
2019
  if (internalModel) {
1917
2020
  // short circuit if we are already loading
1918
- let pendingRequest = this._fetchManager.getPendingFetch(internalModel.identifier, options);
1919
- if (pendingRequest) {
1920
- return pendingRequest.then(() => internalModel.getRecord());
2021
+ if (REQUEST_SERVICE) {
2022
+ let pendingRequest = this._fetchManager.getPendingFetch(internalModel.identifier);
2023
+ if (pendingRequest) {
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
+ }
1921
2032
  }
1922
2033
  }
1923
2034
 
@@ -1940,10 +2051,6 @@ abstract class CoreStore extends Service {
1940
2051
  return resolve(null);
1941
2052
  }
1942
2053
 
1943
- if (!internalModel) {
1944
- assert(`No InternalModel found for ${resource.lid}`, internalModel);
1945
- }
1946
-
1947
2054
  return this._findByInternalModel(internalModel, options);
1948
2055
  }
1949
2056
 
@@ -2550,32 +2657,33 @@ abstract class CoreStore extends Service {
2550
2657
  @param {Object} options
2551
2658
  */
2552
2659
  scheduleSave(internalModel: InternalModel, resolver: RSVP.Deferred<void>, options): void | RSVP.Promise<void> {
2553
- if (internalModel._isRecordFullyDeleted()) {
2554
- resolver.resolve();
2555
- return resolver.promise;
2556
- }
2660
+ if (REQUEST_SERVICE) {
2661
+ if (internalModel._isRecordFullyDeleted()) {
2662
+ resolver.resolve();
2663
+ return resolver.promise;
2664
+ }
2557
2665
 
2558
- internalModel.adapterWillCommit();
2666
+ internalModel.adapterWillCommit();
2559
2667
 
2560
- if (!options) {
2561
- options = {};
2562
- }
2563
- let recordData = internalModel._recordData;
2564
- let operation: 'createRecord' | 'deleteRecord' | 'updateRecord' = 'updateRecord';
2668
+ if (!options) {
2669
+ options = {};
2670
+ }
2671
+ let recordData = internalModel._recordData;
2672
+ let operation: 'createRecord' | 'deleteRecord' | 'updateRecord' = 'updateRecord';
2565
2673
 
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
- }
2674
+ // TODO handle missing isNew
2675
+ if (recordData.isNew && recordData.isNew()) {
2676
+ operation = 'createRecord';
2677
+ } else if (recordData.isDeleted && recordData.isDeleted()) {
2678
+ operation = 'deleteRecord';
2679
+ }
2572
2680
 
2573
- options[SaveOp] = operation;
2681
+ options[SaveOp] = operation;
2574
2682
 
2575
- let fetchManagerPromise = this._fetchManager.scheduleSave(internalModel.identifier, options);
2576
- let promise = fetchManagerPromise.then(
2577
- (payload) => {
2578
- /*
2683
+ let fetchManagerPromise = this._fetchManager.scheduleSave(internalModel.identifier, options);
2684
+ let promise = fetchManagerPromise.then(
2685
+ (payload) => {
2686
+ /*
2579
2687
  Note to future spelunkers hoping to optimize.
2580
2688
  We rely on this `run` to create a run loop if needed
2581
2689
  that `store._push` and `store.didSaveRecord` will both share.
@@ -2584,25 +2692,40 @@ abstract class CoreStore extends Service {
2584
2692
  have an outer run loop available still from the first
2585
2693
  call to `store._push`;
2586
2694
  */
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 });
2695
+ this._backburner.join(() => {
2696
+ let data = payload && payload.data;
2697
+ this.didSaveRecord(internalModel, { data }, operation);
2698
+ if (payload && payload.included) {
2699
+ this._push({ data: null, included: payload.included });
2700
+ }
2701
+ });
2702
+ },
2703
+ (e) => {
2704
+ if (typeof e === 'string') {
2705
+ throw e;
2592
2706
  }
2593
- });
2594
- },
2595
- (e) => {
2596
- if (typeof e === 'string') {
2597
- throw e;
2707
+ const { error, parsedErrors } = e;
2708
+ this.recordWasInvalid(internalModel, parsedErrors, error);
2709
+ throw error;
2598
2710
  }
2599
- const { error, parsedErrors } = e;
2600
- this.recordWasInvalid(internalModel, parsedErrors, error);
2601
- throw error;
2711
+ );
2712
+
2713
+ return promise;
2714
+ } else {
2715
+ if (internalModel._isRecordFullyDeleted()) {
2716
+ resolver.resolve();
2717
+ return;
2602
2718
  }
2603
- );
2604
2719
 
2605
- return promise;
2720
+ let snapshot = internalModel.createSnapshot(options);
2721
+ internalModel.adapterWillCommit();
2722
+ this._pendingSave.push({
2723
+ snapshot: snapshot,
2724
+ resolver: resolver,
2725
+ });
2726
+
2727
+ emberBackburner.scheduleOnce('actions', this, this.flushPendingSave);
2728
+ }
2606
2729
  }
2607
2730
 
2608
2731
  /**
@@ -2613,8 +2736,47 @@ abstract class CoreStore extends Service {
2613
2736
  @private
2614
2737
  */
2615
2738
  flushPendingSave() {
2616
- // assert here
2617
- return;
2739
+ if (REQUEST_SERVICE) {
2740
+ // assert here
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
+ }
2618
2780
  }
2619
2781
 
2620
2782
  /**
@@ -2672,7 +2834,11 @@ abstract class CoreStore extends Service {
2672
2834
  if (DEBUG) {
2673
2835
  assertDestroyingStore(this, 'recordWasInvalid');
2674
2836
  }
2675
- internalModel.adapterDidInvalidate(parsedErrors, error);
2837
+ if (RECORD_DATA_ERRORS) {
2838
+ internalModel.adapterDidInvalidate(parsedErrors, error);
2839
+ } else {
2840
+ internalModel.adapterDidInvalidate(parsedErrors);
2841
+ }
2676
2842
  }
2677
2843
 
2678
2844
  /**
@@ -2997,17 +3163,30 @@ abstract class CoreStore extends Service {
2997
3163
 
2998
3164
  if (ENV.DS_WARN_ON_UNKNOWN_KEYS) {
2999
3165
  let unknownAttributes, unknownRelationships;
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
- });
3166
+ if (CUSTOM_MODEL_CLASS) {
3167
+ let relationships = this.getSchemaDefinitionService().relationshipsDefinitionFor(modelName);
3168
+ let attributes = this.getSchemaDefinitionService().attributesDefinitionFor(modelName);
3169
+ // Check unknown attributes
3170
+ unknownAttributes = Object.keys(data.attributes || {}).filter((key) => {
3171
+ return !attributes[key];
3172
+ });
3173
+
3174
+ // Check unknown relationships
3175
+ unknownRelationships = Object.keys(data.relationships || {}).filter((key) => {
3176
+ return !relationships[key];
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
+ }
3011
3190
  let unknownAttributesMessage = `The payload for '${modelName}' contains these unknown attributes: ${unknownAttributes}. Make sure they've been defined in your model.`;
3012
3191
  warn(unknownAttributesMessage, unknownAttributes.length === 0, {
3013
3192
  id: 'ds.store.unknown-keys-in-payload',
@@ -3137,26 +3316,38 @@ abstract class CoreStore extends Service {
3137
3316
  }
3138
3317
 
3139
3318
  serializeRecord(record: RecordInstance, options?: Dict<unknown>): unknown {
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);
3319
+ if (CUSTOM_MODEL_CLASS) {
3320
+ let identifier = recordIdentifierFor(record);
3321
+ let internalModel = internalModelFactoryFor(this).peek(identifier);
3322
+ // TODO we used to check if the record was destroyed here
3323
+ return internalModel!.createSnapshot(options).serialize(options);
3324
+ }
3325
+
3326
+ assert('serializeRecord is only available when CUSTOM_MODEL_CLASS ff is on', false);
3144
3327
  }
3145
3328
 
3146
3329
  saveRecord(record: RecordInstance, options?: Dict<unknown>): RSVP.Promise<RecordInstance> {
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);
3330
+ if (CUSTOM_MODEL_CLASS) {
3331
+ let identifier = recordIdentifierFor(record);
3332
+ let internalModel = internalModelFactoryFor(this).peek(identifier);
3333
+ // TODO we used to check if the record was destroyed here
3334
+ // Casting can be removed once REQUEST_SERVICE ff is turned on
3335
+ // because a `Record` is provided there will always be a matching internalModel
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');
3153
3340
  }
3154
3341
 
3155
3342
  relationshipReferenceFor(identifier: RecordIdentifier, key: string): BelongsToReference | HasManyReference {
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);
3343
+ if (CUSTOM_MODEL_CLASS) {
3344
+ let stableIdentifier = identifierCacheFor(this).getOrCreateRecordIdentifier(identifier);
3345
+ let internalModel = internalModelFactoryFor(this).peek(stableIdentifier);
3346
+ // TODO we used to check if the record was destroyed here
3347
+ return internalModel!.referenceFor(null, key);
3348
+ }
3349
+
3350
+ assert('relationshipReferenceFor is only available when CUSTOM_MODEL_CLASS ff is on', false);
3160
3351
  }
3161
3352
 
3162
3353
  /**
@@ -3410,6 +3601,10 @@ abstract class CoreStore extends Service {
3410
3601
  for an `App.ApplicationSerializer` (the default serializer for
3411
3602
  your entire application).
3412
3603
 
3604
+ if no `App.ApplicationSerializer` is found, it will attempt
3605
+ to get the `defaultSerializer` from the `PersonAdapter`
3606
+ (`adapterFor('person')`).
3607
+
3413
3608
  If a serializer cannot be found on the adapter, it will fall back
3414
3609
  to an instance of `JSONSerializer`.
3415
3610
 
@@ -3477,6 +3672,31 @@ abstract class CoreStore extends Service {
3477
3672
  }
3478
3673
 
3479
3674
  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
+ }
3480
3700
 
3481
3701
  if (DEPRECATE_LEGACY_TEST_REGISTRATIONS) {
3482
3702
  // in production this is handled by the re-export
@@ -3507,10 +3727,49 @@ abstract class CoreStore extends Service {
3507
3727
  }
3508
3728
  }
3509
3729
 
3510
- assert(
3511
- `No serializer was found for '${modelName}' and no 'application' serializer was found as a fallback`,
3512
- serializer !== undefined
3513
- );
3730
+ if (DEPRECATE_DEFAULT_SERIALIZER) {
3731
+ // final fallback, no model specific serializer, no application serializer, no
3732
+ // `serializer` property on store: use the convenience JSONSerializer
3733
+ serializer = _serializerCache['-default'] || owner.lookup('serializer:-default');
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
+ }
3514
3773
  }
3515
3774
 
3516
3775
  destroy() {
@@ -3639,6 +3898,73 @@ if (DEPRECATE_DEFAULT_ADAPTER) {
3639
3898
 
3640
3899
  export default CoreStore;
3641
3900
 
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
+
3642
3968
  let assertDestroyingStore: Function;
3643
3969
  let assertDestroyedStoreOnly: Function;
3644
3970