@ember-data/store 3.28.7 → 3.28.10
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/ds-model-store.ts +2 -2
- package/addon/-private/system/model/internal-model.ts +27 -13
- package/addon/-private/system/model/states.js +7 -1
- package/addon/-private/system/record-array-manager.js +1 -1
- package/addon/-private/system/record-notification-manager.ts +14 -6
- package/addon/-private/system/references/{belongs-to.js → belongs-to.ts} +97 -16
- package/addon/-private/system/references/{has-many.js → has-many.ts} +142 -45
- package/addon/-private/system/references/record.ts +54 -7
- package/addon/-private/system/references/reference.ts +1 -1
- package/addon/-private/ts-interfaces/ds-model.ts +1 -0
- package/index.js +3 -0
- package/package.json +5 -4
|
@@ -2,7 +2,6 @@ import { getOwner, setOwner } from '@ember/application';
|
|
|
2
2
|
import { assert, deprecate } from '@ember/debug';
|
|
3
3
|
import EmberError from '@ember/error';
|
|
4
4
|
import { get } from '@ember/object';
|
|
5
|
-
import { assign } from '@ember/polyfills';
|
|
6
5
|
import { isPresent } from '@ember/utils';
|
|
7
6
|
import { DEBUG } from '@glimmer/env';
|
|
8
7
|
|
|
@@ -39,9 +38,10 @@ class Store extends CoreStore {
|
|
|
39
38
|
let createOptions: any = {
|
|
40
39
|
store: this,
|
|
41
40
|
_internalModel: internalModel,
|
|
41
|
+
// TODO deprecate allowing unknown args setting
|
|
42
|
+
_createProps: createRecordArgs,
|
|
42
43
|
container: null,
|
|
43
44
|
};
|
|
44
|
-
assign(createOptions, createRecordArgs);
|
|
45
45
|
|
|
46
46
|
// ensure that `getOwner(this)` works inside a model instance
|
|
47
47
|
setOwner(createOptions, getOwner(this));
|
|
@@ -28,6 +28,8 @@ import Snapshot from '../snapshot';
|
|
|
28
28
|
import { internalModelFactoryFor, setRecordIdentifier } from '../store/internal-model-factory';
|
|
29
29
|
import RootState from './states';
|
|
30
30
|
|
|
31
|
+
type DSModel = import('../../ts-interfaces/ds-model').DSModel;
|
|
32
|
+
|
|
31
33
|
type BelongsToRelationship = import('@ember-data/record-data/-private').BelongsToRelationship;
|
|
32
34
|
type ManyRelationship = import('@ember-data/record-data/-private').ManyRelationship;
|
|
33
35
|
|
|
@@ -130,7 +132,7 @@ export default class InternalModel {
|
|
|
130
132
|
declare _deferredTriggers: any;
|
|
131
133
|
declare __recordArrays: any;
|
|
132
134
|
declare references: any;
|
|
133
|
-
declare _recordReference:
|
|
135
|
+
declare _recordReference: RecordReference;
|
|
134
136
|
declare _manyArrayCache: ConfidentDict<ManyArray>;
|
|
135
137
|
|
|
136
138
|
declare _relationshipPromisesCache: ConfidentDict<RSVP.Promise<any>>;
|
|
@@ -202,7 +204,7 @@ export default class InternalModel {
|
|
|
202
204
|
}
|
|
203
205
|
}
|
|
204
206
|
|
|
205
|
-
get recordReference() {
|
|
207
|
+
get recordReference(): RecordReference {
|
|
206
208
|
if (this._recordReference === null) {
|
|
207
209
|
this._recordReference = new RecordReference(this.store, this.identifier);
|
|
208
210
|
}
|
|
@@ -294,7 +296,7 @@ export default class InternalModel {
|
|
|
294
296
|
}
|
|
295
297
|
}
|
|
296
298
|
|
|
297
|
-
getRecord(properties?) {
|
|
299
|
+
getRecord(properties?): Object {
|
|
298
300
|
if (!this._record && !this._isDematerializing) {
|
|
299
301
|
let { store } = this;
|
|
300
302
|
|
|
@@ -616,7 +618,7 @@ export default class InternalModel {
|
|
|
616
618
|
"' with id " +
|
|
617
619
|
parentInternalModel.id +
|
|
618
620
|
' but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`belongsTo({ async: true })`)',
|
|
619
|
-
toReturn === null || !toReturn.
|
|
621
|
+
toReturn === null || !(toReturn as DSModel).isEmpty
|
|
620
622
|
);
|
|
621
623
|
return toReturn;
|
|
622
624
|
}
|
|
@@ -675,7 +677,7 @@ export default class InternalModel {
|
|
|
675
677
|
assert(`hasMany only works with the @ember-data/record-data package`);
|
|
676
678
|
}
|
|
677
679
|
|
|
678
|
-
getHasMany(key: string, options) {
|
|
680
|
+
getHasMany(key: string, options?) {
|
|
679
681
|
if (HAS_RECORD_DATA_PACKAGE) {
|
|
680
682
|
const graphFor = require('@ember-data/record-data/-private').graphFor;
|
|
681
683
|
const relationship = graphFor(this.store).get(this.identifier, key);
|
|
@@ -793,11 +795,22 @@ export default class InternalModel {
|
|
|
793
795
|
!this._record || this._record.get('isDestroyed') || this._record.get('isDestroying')
|
|
794
796
|
);
|
|
795
797
|
this.isDestroying = true;
|
|
798
|
+
if (this._recordReference) {
|
|
799
|
+
this._recordReference.destroy();
|
|
800
|
+
}
|
|
801
|
+
this._recordReference = null;
|
|
796
802
|
let cache = this._manyArrayCache;
|
|
797
803
|
Object.keys(cache).forEach((key) => {
|
|
798
804
|
cache[key].destroy();
|
|
799
805
|
delete cache[key];
|
|
800
806
|
});
|
|
807
|
+
if (this.references) {
|
|
808
|
+
cache = this.references;
|
|
809
|
+
Object.keys(cache).forEach((key) => {
|
|
810
|
+
cache[key].destroy();
|
|
811
|
+
delete cache[key];
|
|
812
|
+
});
|
|
813
|
+
}
|
|
801
814
|
|
|
802
815
|
internalModelFactoryFor(this.store).remove(this);
|
|
803
816
|
this._isDestroyed = true;
|
|
@@ -806,6 +819,7 @@ export default class InternalModel {
|
|
|
806
819
|
setupData(data) {
|
|
807
820
|
let changedKeys = this._recordData.pushData(data, this.hasRecord);
|
|
808
821
|
if (this.hasRecord) {
|
|
822
|
+
// TODO @runspired should this be going through the notification manager?
|
|
809
823
|
this._record._notifyProperties(changedKeys);
|
|
810
824
|
}
|
|
811
825
|
this.send('pushedData');
|
|
@@ -966,10 +980,10 @@ export default class InternalModel {
|
|
|
966
980
|
this.store._notificationManager.notify(this.identifier, 'state');
|
|
967
981
|
} else {
|
|
968
982
|
if (!key || key === 'isNew') {
|
|
969
|
-
this.getRecord().notifyPropertyChange('isNew');
|
|
983
|
+
(this.getRecord() as DSModel).notifyPropertyChange('isNew');
|
|
970
984
|
}
|
|
971
985
|
if (!key || key === 'isDeleted') {
|
|
972
|
-
this.getRecord().notifyPropertyChange('isDeleted');
|
|
986
|
+
(this.getRecord() as DSModel).notifyPropertyChange('isDeleted');
|
|
973
987
|
}
|
|
974
988
|
}
|
|
975
989
|
}
|
|
@@ -1273,12 +1287,12 @@ export default class InternalModel {
|
|
|
1273
1287
|
if (this._recordData.getErrors) {
|
|
1274
1288
|
return this._recordData.getErrors(this.identifier).length > 0;
|
|
1275
1289
|
} else {
|
|
1276
|
-
let errors =
|
|
1277
|
-
return errors.
|
|
1290
|
+
let errors = (this.getRecord() as DSModel).errors;
|
|
1291
|
+
return errors.length > 0;
|
|
1278
1292
|
}
|
|
1279
1293
|
} else {
|
|
1280
|
-
let errors =
|
|
1281
|
-
return errors.
|
|
1294
|
+
let errors = (this.getRecord() as DSModel).errors;
|
|
1295
|
+
return errors.length > 0;
|
|
1282
1296
|
}
|
|
1283
1297
|
}
|
|
1284
1298
|
|
|
@@ -1293,7 +1307,7 @@ export default class InternalModel {
|
|
|
1293
1307
|
if (!this._recordData.getErrors) {
|
|
1294
1308
|
for (attribute in parsedErrors) {
|
|
1295
1309
|
if (hasOwnProperty.call(parsedErrors, attribute)) {
|
|
1296
|
-
this.getRecord().errors._add(attribute, parsedErrors[attribute]);
|
|
1310
|
+
(this.getRecord() as DSModel).errors._add(attribute, parsedErrors[attribute]);
|
|
1297
1311
|
}
|
|
1298
1312
|
}
|
|
1299
1313
|
}
|
|
@@ -1313,7 +1327,7 @@ export default class InternalModel {
|
|
|
1313
1327
|
|
|
1314
1328
|
for (attribute in parsedErrors) {
|
|
1315
1329
|
if (hasOwnProperty.call(parsedErrors, attribute)) {
|
|
1316
|
-
this.getRecord().errors._add(attribute, parsedErrors[attribute]);
|
|
1330
|
+
(this.getRecord() as DSModel).errors._add(attribute, parsedErrors[attribute]);
|
|
1317
1331
|
}
|
|
1318
1332
|
}
|
|
1319
1333
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { assert } from '@ember/debug';
|
|
5
5
|
|
|
6
|
-
import { REQUEST_SERVICE } from '@ember-data/canary-features';
|
|
6
|
+
import { CUSTOM_MODEL_CLASS, REQUEST_SERVICE } from '@ember-data/canary-features';
|
|
7
7
|
/*
|
|
8
8
|
This file encapsulates the various states that a record can transition
|
|
9
9
|
through during its lifecycle.
|
|
@@ -431,6 +431,12 @@ createdState.uncommitted.rollback = function (internalModel) {
|
|
|
431
431
|
};
|
|
432
432
|
|
|
433
433
|
createdState.uncommitted.pushedData = function (internalModel) {
|
|
434
|
+
// TODO @runspired consider where to do this once we kill off state machine
|
|
435
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
436
|
+
internalModel.store._notificationManager.notify(internalModel.identifier, 'identity');
|
|
437
|
+
} else {
|
|
438
|
+
internalModel.notifyPropertyChange('id');
|
|
439
|
+
}
|
|
434
440
|
internalModel.transitionTo('loaded.updated.uncommitted');
|
|
435
441
|
internalModel.triggerLater('didLoad');
|
|
436
442
|
};
|
|
@@ -27,7 +27,7 @@ export function recordArraysForIdentifier(identifierOrInternalModel) {
|
|
|
27
27
|
return RecordArraysCache.get(identifierOrInternalModel);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
const pendingForIdentifier = new Set(
|
|
30
|
+
const pendingForIdentifier = new Set();
|
|
31
31
|
const IMDematerializing = new WeakMap();
|
|
32
32
|
|
|
33
33
|
const getIdentifier = function getIdentifier(identifierOrInternalModel) {
|
|
@@ -6,7 +6,7 @@ type StableRecordIdentifier = import('../ts-interfaces/identifier').StableRecord
|
|
|
6
6
|
|
|
7
7
|
type UnsubscribeToken = Object;
|
|
8
8
|
|
|
9
|
-
const Cache = new WeakMap<StableRecordIdentifier, NotificationCallback
|
|
9
|
+
const Cache = new WeakMap<StableRecordIdentifier, Map<UnsubscribeToken, NotificationCallback>>();
|
|
10
10
|
const Tokens = new WeakMap<UnsubscribeToken, StableRecordIdentifier>();
|
|
11
11
|
|
|
12
12
|
export type NotificationType =
|
|
@@ -31,7 +31,8 @@ export function unsubscribe(token: UnsubscribeToken) {
|
|
|
31
31
|
throw new Error('Passed unknown unsubscribe token to unsubscribe');
|
|
32
32
|
}
|
|
33
33
|
Tokens.delete(token);
|
|
34
|
-
Cache.
|
|
34
|
+
const map = Cache.get(identifier);
|
|
35
|
+
map?.delete(token);
|
|
35
36
|
}
|
|
36
37
|
/*
|
|
37
38
|
Currently only support a single callback per identifier
|
|
@@ -41,8 +42,13 @@ export default class NotificationManager {
|
|
|
41
42
|
|
|
42
43
|
subscribe(identifier: RecordIdentifier, callback: NotificationCallback): UnsubscribeToken {
|
|
43
44
|
let stableIdentifier = identifierCacheFor(this.store).getOrCreateRecordIdentifier(identifier);
|
|
44
|
-
Cache.
|
|
45
|
+
let map = Cache.get(stableIdentifier);
|
|
46
|
+
if (map === undefined) {
|
|
47
|
+
map = new Map();
|
|
48
|
+
Cache.set(stableIdentifier, map);
|
|
49
|
+
}
|
|
45
50
|
let unsubToken = {};
|
|
51
|
+
map.set(unsubToken, callback);
|
|
46
52
|
Tokens.set(unsubToken, stableIdentifier);
|
|
47
53
|
return unsubToken;
|
|
48
54
|
}
|
|
@@ -51,11 +57,13 @@ export default class NotificationManager {
|
|
|
51
57
|
notify(identifier: RecordIdentifier, value: 'errors' | 'meta' | 'identity' | 'unload' | 'state'): boolean;
|
|
52
58
|
notify(identifier: RecordIdentifier, value: NotificationType, key?: string): boolean {
|
|
53
59
|
let stableIdentifier = identifierCacheFor(this.store).getOrCreateRecordIdentifier(identifier);
|
|
54
|
-
let
|
|
55
|
-
if (!
|
|
60
|
+
let callbackMap = Cache.get(stableIdentifier);
|
|
61
|
+
if (!callbackMap || !callbackMap.size) {
|
|
56
62
|
return false;
|
|
57
63
|
}
|
|
58
|
-
|
|
64
|
+
callbackMap.forEach((cb) => {
|
|
65
|
+
cb(stableIdentifier, value, key);
|
|
66
|
+
});
|
|
59
67
|
return true;
|
|
60
68
|
}
|
|
61
69
|
}
|
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
import { deprecate } from '@ember/debug';
|
|
2
|
+
import { dependentKeyCompat } from '@ember/object/compat';
|
|
3
|
+
import { cached, tracked } from '@glimmer/tracking';
|
|
2
4
|
|
|
3
5
|
import { resolve } from 'rsvp';
|
|
4
6
|
|
|
7
|
+
import { CUSTOM_MODEL_CLASS } from '@ember-data/canary-features';
|
|
5
8
|
import { DEPRECATE_BELONGS_TO_REFERENCE_PUSH } from '@ember-data/private-build-infra/deprecations';
|
|
6
9
|
import { assertPolymorphicType } from '@ember-data/store/-debug';
|
|
7
10
|
|
|
11
|
+
import { unsubscribe } from '../record-notification-manager';
|
|
8
12
|
import { internalModelFactoryFor, peekRecordIdentifier, recordIdentifierFor } from '../store/internal-model-factory';
|
|
9
13
|
import Reference from './reference';
|
|
10
14
|
|
|
15
|
+
type RecordReference = import('./record').default;
|
|
16
|
+
type NotificationType = import('../record-notification-manager').NotificationType;
|
|
17
|
+
type CoreStore = import('../core-store').default;
|
|
18
|
+
type StableRecordIdentifier = import('../../ts-interfaces/identifier').StableRecordIdentifier;
|
|
19
|
+
type SingleResourceDocument = import('../../ts-interfaces/ember-data-json-api').SingleResourceDocument;
|
|
20
|
+
|
|
21
|
+
type BelongsToRelationship = import('@ember-data/record-data/-private').BelongsToRelationship;
|
|
11
22
|
/**
|
|
12
23
|
@module @ember-data/store
|
|
13
24
|
*/
|
|
@@ -22,17 +33,81 @@ import Reference from './reference';
|
|
|
22
33
|
@extends Reference
|
|
23
34
|
*/
|
|
24
35
|
export default class BelongsToReference extends Reference {
|
|
25
|
-
|
|
26
|
-
|
|
36
|
+
declare key: string;
|
|
37
|
+
declare belongsToRelationship: BelongsToRelationship;
|
|
38
|
+
declare type: string;
|
|
39
|
+
declare parent: RecordReference;
|
|
40
|
+
declare parentIdentifier: StableRecordIdentifier;
|
|
41
|
+
|
|
42
|
+
// unsubscribe tokens given to us by the notification manager
|
|
43
|
+
#token!: Object;
|
|
44
|
+
#relatedToken: Object | null = null;
|
|
45
|
+
|
|
46
|
+
@tracked _ref = 0;
|
|
47
|
+
|
|
48
|
+
constructor(
|
|
49
|
+
store: CoreStore,
|
|
50
|
+
parentIdentifier: StableRecordIdentifier,
|
|
51
|
+
belongsToRelationship: BelongsToRelationship,
|
|
52
|
+
key: string
|
|
53
|
+
) {
|
|
54
|
+
super(store, parentIdentifier);
|
|
27
55
|
this.key = key;
|
|
28
56
|
this.belongsToRelationship = belongsToRelationship;
|
|
29
57
|
this.type = belongsToRelationship.definition.type;
|
|
30
|
-
|
|
31
|
-
this.
|
|
58
|
+
const parent = internalModelFactoryFor(store).peek(parentIdentifier);
|
|
59
|
+
this.parent = parent!.recordReference;
|
|
60
|
+
this.parentIdentifier = parentIdentifier;
|
|
61
|
+
|
|
62
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
63
|
+
this.#token = store._notificationManager.subscribe(
|
|
64
|
+
parentIdentifier,
|
|
65
|
+
(_: StableRecordIdentifier, bucket: NotificationType, notifiedKey?: string) => {
|
|
66
|
+
if ((bucket === 'relationships' || bucket === 'property') && notifiedKey === key) {
|
|
67
|
+
this._ref++;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
}
|
|
32
72
|
|
|
33
73
|
// TODO inverse
|
|
34
74
|
}
|
|
35
75
|
|
|
76
|
+
destroy() {
|
|
77
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
78
|
+
unsubscribe(this.#token);
|
|
79
|
+
if (this.#relatedToken) {
|
|
80
|
+
unsubscribe(this.#relatedToken);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@cached
|
|
86
|
+
@dependentKeyCompat
|
|
87
|
+
get _relatedIdentifier(): StableRecordIdentifier | null {
|
|
88
|
+
this._ref; // consume the tracked prop
|
|
89
|
+
if (this.#relatedToken) {
|
|
90
|
+
unsubscribe(this.#relatedToken);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let resource = this._resource();
|
|
94
|
+
if (resource && resource.data) {
|
|
95
|
+
const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource.data);
|
|
96
|
+
this.#relatedToken = this.store._notificationManager.subscribe(
|
|
97
|
+
identifier,
|
|
98
|
+
(_: StableRecordIdentifier, bucket: NotificationType, notifiedKey?: string) => {
|
|
99
|
+
if (bucket === 'identity' || ((bucket === 'attributes' || bucket === 'property') && notifiedKey === 'id')) {
|
|
100
|
+
this._ref++;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
return identifier;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
36
111
|
/**
|
|
37
112
|
The `id` of the record that this reference refers to. Together, the
|
|
38
113
|
`type()` and `id()` methods form a composite key for the identity
|
|
@@ -73,13 +148,18 @@ export default class BelongsToReference extends Reference {
|
|
|
73
148
|
@public
|
|
74
149
|
@return {String} The id of the record in this belongsTo relationship.
|
|
75
150
|
*/
|
|
76
|
-
id() {
|
|
77
|
-
|
|
151
|
+
id(): string | null {
|
|
152
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
153
|
+
return this._relatedIdentifier?.id || null;
|
|
154
|
+
}
|
|
78
155
|
let resource = this._resource();
|
|
79
156
|
if (resource && resource.data) {
|
|
80
|
-
|
|
157
|
+
const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource.data);
|
|
158
|
+
|
|
159
|
+
return identifier.id;
|
|
81
160
|
}
|
|
82
|
-
|
|
161
|
+
|
|
162
|
+
return null;
|
|
83
163
|
}
|
|
84
164
|
|
|
85
165
|
_resource() {
|
|
@@ -132,10 +212,10 @@ export default class BelongsToReference extends Reference {
|
|
|
132
212
|
@param {Object|Promise} objectOrPromise a promise that resolves to a JSONAPI document object describing the new value of this relationship.
|
|
133
213
|
@return {Promise<record>} A promise that resolves with the new value in this belongs-to relationship.
|
|
134
214
|
*/
|
|
135
|
-
push(objectOrPromise) {
|
|
215
|
+
async push(objectOrPromise: Object | SingleResourceDocument): Promise<Object> {
|
|
136
216
|
// TODO deprecate thenable support
|
|
137
217
|
return resolve(objectOrPromise).then((data) => {
|
|
138
|
-
let record;
|
|
218
|
+
let record: Object;
|
|
139
219
|
|
|
140
220
|
if (DEPRECATE_BELONGS_TO_REFERENCE_PUSH && peekRecordIdentifier(data)) {
|
|
141
221
|
deprecate('Pushing a record into a BelongsToReference is deprecated', false, {
|
|
@@ -147,15 +227,16 @@ export default class BelongsToReference extends Reference {
|
|
|
147
227
|
enabled: '3.16',
|
|
148
228
|
},
|
|
149
229
|
});
|
|
150
|
-
record = data;
|
|
230
|
+
record = data as Object;
|
|
151
231
|
} else {
|
|
152
|
-
record = this.store.push(data);
|
|
232
|
+
record = this.store.push(data as SingleResourceDocument);
|
|
153
233
|
}
|
|
154
234
|
|
|
235
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
155
236
|
assertPolymorphicType(
|
|
156
237
|
this.belongsToRelationship.identifier,
|
|
157
238
|
this.belongsToRelationship.definition,
|
|
158
|
-
record
|
|
239
|
+
recordIdentifierFor(record),
|
|
159
240
|
this.store
|
|
160
241
|
);
|
|
161
242
|
|
|
@@ -223,7 +304,7 @@ export default class BelongsToReference extends Reference {
|
|
|
223
304
|
@public
|
|
224
305
|
@return {Model} the record in this relationship
|
|
225
306
|
*/
|
|
226
|
-
value() {
|
|
307
|
+
value(): Object | null {
|
|
227
308
|
let resource = this._resource();
|
|
228
309
|
if (resource && resource.data) {
|
|
229
310
|
let inverseInternalModel = this.store._internalModelForResource(resource.data);
|
|
@@ -299,7 +380,7 @@ export default class BelongsToReference extends Reference {
|
|
|
299
380
|
*/
|
|
300
381
|
load(options) {
|
|
301
382
|
let parentInternalModel = internalModelFactoryFor(this.store).peek(this.parentIdentifier);
|
|
302
|
-
return parentInternalModel
|
|
383
|
+
return parentInternalModel!.getBelongsTo(this.key, options);
|
|
303
384
|
}
|
|
304
385
|
|
|
305
386
|
/**
|
|
@@ -354,7 +435,7 @@ export default class BelongsToReference extends Reference {
|
|
|
354
435
|
*/
|
|
355
436
|
reload(options) {
|
|
356
437
|
let parentInternalModel = internalModelFactoryFor(this.store).peek(this.parentIdentifier);
|
|
357
|
-
return parentInternalModel
|
|
438
|
+
return parentInternalModel!.reloadBelongsTo(this.key, options).then((internalModel) => {
|
|
358
439
|
return this.value();
|
|
359
440
|
});
|
|
360
441
|
}
|
|
@@ -1,12 +1,26 @@
|
|
|
1
|
+
import { dependentKeyCompat } from '@ember/object/compat';
|
|
1
2
|
import { DEBUG } from '@glimmer/env';
|
|
3
|
+
import { cached, tracked } from '@glimmer/tracking';
|
|
2
4
|
|
|
3
5
|
import { resolve } from 'rsvp';
|
|
4
6
|
|
|
7
|
+
import { CUSTOM_MODEL_CLASS } from '@ember-data/canary-features';
|
|
5
8
|
import { assertPolymorphicType } from '@ember-data/store/-debug';
|
|
6
9
|
|
|
10
|
+
import { unsubscribe } from '../record-notification-manager';
|
|
7
11
|
import { internalModelFactoryFor, recordIdentifierFor } from '../store/internal-model-factory';
|
|
8
12
|
import Reference, { internalModelForReference } from './reference';
|
|
9
13
|
|
|
14
|
+
type RecordReference = import('./record').default;
|
|
15
|
+
type NotificationType = import('../record-notification-manager').NotificationType;
|
|
16
|
+
type CoreStore = import('../core-store').default;
|
|
17
|
+
type StableRecordIdentifier = import('../../ts-interfaces/identifier').StableRecordIdentifier;
|
|
18
|
+
type CollectionResourceDocument = import('../../ts-interfaces/ember-data-json-api').CollectionResourceDocument;
|
|
19
|
+
type ExistingResourceObject = import('../../ts-interfaces/ember-data-json-api').ExistingResourceObject;
|
|
20
|
+
type SingleResourceDocument = import('../../ts-interfaces/ember-data-json-api').SingleResourceDocument;
|
|
21
|
+
|
|
22
|
+
type ManyRelationship = import('@ember-data/record-data/-private').ManyRelationship;
|
|
23
|
+
|
|
10
24
|
/**
|
|
11
25
|
@module @ember-data/store
|
|
12
26
|
*/
|
|
@@ -20,17 +34,88 @@ import Reference, { internalModelForReference } from './reference';
|
|
|
20
34
|
@extends Reference
|
|
21
35
|
*/
|
|
22
36
|
export default class HasManyReference extends Reference {
|
|
23
|
-
|
|
24
|
-
|
|
37
|
+
declare key: string;
|
|
38
|
+
declare hasManyRelationship: ManyRelationship;
|
|
39
|
+
declare type: string;
|
|
40
|
+
declare parent: RecordReference;
|
|
41
|
+
declare parentIdentifier: StableRecordIdentifier;
|
|
42
|
+
|
|
43
|
+
// unsubscribe tokens given to us by the notification manager
|
|
44
|
+
#token!: Object;
|
|
45
|
+
#relatedTokenMap!: Map<StableRecordIdentifier, Object>;
|
|
46
|
+
|
|
47
|
+
@tracked _ref = 0;
|
|
48
|
+
|
|
49
|
+
constructor(
|
|
50
|
+
store: CoreStore,
|
|
51
|
+
parentIdentifier: StableRecordIdentifier,
|
|
52
|
+
hasManyRelationship: ManyRelationship,
|
|
53
|
+
key: string
|
|
54
|
+
) {
|
|
55
|
+
super(store, parentIdentifier);
|
|
25
56
|
this.key = key;
|
|
26
57
|
this.hasManyRelationship = hasManyRelationship;
|
|
27
58
|
this.type = hasManyRelationship.definition.type;
|
|
28
59
|
|
|
29
|
-
this.parent = internalModelFactoryFor(store).peek(
|
|
60
|
+
this.parent = internalModelFactoryFor(store).peek(parentIdentifier)!.recordReference;
|
|
30
61
|
|
|
62
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
63
|
+
this.#token = store._notificationManager.subscribe(
|
|
64
|
+
parentIdentifier,
|
|
65
|
+
(_: StableRecordIdentifier, bucket: NotificationType, notifiedKey?: string) => {
|
|
66
|
+
if ((bucket === 'relationships' || bucket === 'property') && notifiedKey === key) {
|
|
67
|
+
this._ref++;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
this.#relatedTokenMap = new Map();
|
|
72
|
+
}
|
|
31
73
|
// TODO inverse
|
|
32
74
|
}
|
|
33
75
|
|
|
76
|
+
destroy() {
|
|
77
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
78
|
+
unsubscribe(this.#token);
|
|
79
|
+
this.#relatedTokenMap.forEach((token) => {
|
|
80
|
+
unsubscribe(token);
|
|
81
|
+
});
|
|
82
|
+
this.#relatedTokenMap.clear();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@cached
|
|
87
|
+
@dependentKeyCompat
|
|
88
|
+
get _relatedIdentifiers(): StableRecordIdentifier[] {
|
|
89
|
+
this._ref; // consume the tracked prop
|
|
90
|
+
|
|
91
|
+
let resource = this._resource();
|
|
92
|
+
|
|
93
|
+
this.#relatedTokenMap.forEach((token) => {
|
|
94
|
+
unsubscribe(token);
|
|
95
|
+
});
|
|
96
|
+
this.#relatedTokenMap.clear();
|
|
97
|
+
|
|
98
|
+
if (resource && resource.data) {
|
|
99
|
+
return resource.data.map((resourceIdentifier) => {
|
|
100
|
+
const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
|
|
101
|
+
const token = this.store._notificationManager.subscribe(
|
|
102
|
+
identifier,
|
|
103
|
+
(_: StableRecordIdentifier, bucket: NotificationType, notifiedKey?: string) => {
|
|
104
|
+
if (bucket === 'identity' || ((bucket === 'attributes' || bucket === 'property') && notifiedKey === 'id')) {
|
|
105
|
+
this._ref++;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
this.#relatedTokenMap.set(identifier, token);
|
|
111
|
+
|
|
112
|
+
return identifier;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
|
|
34
119
|
_resource() {
|
|
35
120
|
return this.recordData.getHasMany(this.key);
|
|
36
121
|
}
|
|
@@ -77,7 +162,7 @@ export default class HasManyReference extends Reference {
|
|
|
77
162
|
@public
|
|
78
163
|
@return {String} The name of the remote type. This should either be `link` or `ids`
|
|
79
164
|
*/
|
|
80
|
-
remoteType() {
|
|
165
|
+
remoteType(): 'link' | 'ids' {
|
|
81
166
|
let value = this._resource();
|
|
82
167
|
if (value && value.links && value.links.related) {
|
|
83
168
|
return 'link';
|
|
@@ -121,15 +206,22 @@ export default class HasManyReference extends Reference {
|
|
|
121
206
|
@public
|
|
122
207
|
@return {Array} The ids in this has-many relationship
|
|
123
208
|
*/
|
|
124
|
-
ids() {
|
|
209
|
+
ids(): Array<string | null> {
|
|
210
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
211
|
+
return this._relatedIdentifiers.map((identifier) => identifier.id);
|
|
212
|
+
}
|
|
213
|
+
|
|
125
214
|
let resource = this._resource();
|
|
126
215
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
216
|
+
if (resource && resource.data) {
|
|
217
|
+
return resource.data.map((resourceIdentifier) => {
|
|
218
|
+
const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
|
|
219
|
+
|
|
220
|
+
return identifier.id;
|
|
221
|
+
});
|
|
130
222
|
}
|
|
131
223
|
|
|
132
|
-
return
|
|
224
|
+
return [];
|
|
133
225
|
}
|
|
134
226
|
|
|
135
227
|
/**
|
|
@@ -177,45 +269,50 @@ export default class HasManyReference extends Reference {
|
|
|
177
269
|
@param {Array|Promise} objectOrPromise a promise that resolves to a JSONAPI document object describing the new value of this relationship.
|
|
178
270
|
@return {ManyArray}
|
|
179
271
|
*/
|
|
180
|
-
push(
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
272
|
+
async push(
|
|
273
|
+
objectOrPromise: ExistingResourceObject[] | CollectionResourceDocument | { data: SingleResourceDocument[] }
|
|
274
|
+
): Promise<any> {
|
|
275
|
+
const payload = await resolve(objectOrPromise);
|
|
276
|
+
let array: Array<ExistingResourceObject | SingleResourceDocument>;
|
|
277
|
+
|
|
278
|
+
if (!Array.isArray(payload) && typeof payload === 'object' && Array.isArray(payload.data)) {
|
|
279
|
+
array = payload.data;
|
|
280
|
+
} else {
|
|
281
|
+
array = payload as ExistingResourceObject[];
|
|
282
|
+
}
|
|
187
283
|
|
|
188
|
-
|
|
284
|
+
const internalModel = internalModelForReference(this)!;
|
|
285
|
+
const { store } = this;
|
|
189
286
|
|
|
190
|
-
|
|
191
|
-
|
|
287
|
+
let identifiers = array.map((obj) => {
|
|
288
|
+
let record;
|
|
289
|
+
if ('data' in obj) {
|
|
290
|
+
// TODO deprecate pushing non-valid JSON:API here
|
|
291
|
+
record = store.push(obj);
|
|
292
|
+
} else {
|
|
293
|
+
record = store.push({ data: obj });
|
|
294
|
+
}
|
|
192
295
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
return recordIdentifierFor(record);
|
|
203
|
-
});
|
|
296
|
+
if (DEBUG) {
|
|
297
|
+
let relationshipMeta = this.hasManyRelationship.definition;
|
|
298
|
+
let identifier = this.hasManyRelationship.identifier;
|
|
299
|
+
assertPolymorphicType(identifier, relationshipMeta, recordIdentifierFor(record), store);
|
|
300
|
+
}
|
|
301
|
+
return recordIdentifierFor(record);
|
|
302
|
+
});
|
|
204
303
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
});
|
|
304
|
+
const { graph, identifier } = this.hasManyRelationship;
|
|
305
|
+
store._backburner.join(() => {
|
|
306
|
+
graph.push({
|
|
307
|
+
op: 'replaceRelatedRecords',
|
|
308
|
+
record: identifier,
|
|
309
|
+
field: this.key,
|
|
310
|
+
value: identifiers,
|
|
213
311
|
});
|
|
214
|
-
|
|
215
|
-
return internalModel.getHasMany(this.key);
|
|
216
|
-
// TODO IGOR it seems wrong that we were returning the many array here
|
|
217
|
-
//return this.hasManyRelationship.manyArray;
|
|
218
312
|
});
|
|
313
|
+
|
|
314
|
+
// TODO IGOR it seems wrong that we were returning the many array here
|
|
315
|
+
return internalModel.getHasMany(this.key);
|
|
219
316
|
}
|
|
220
317
|
|
|
221
318
|
_isLoaded() {
|
|
@@ -275,7 +372,7 @@ export default class HasManyReference extends Reference {
|
|
|
275
372
|
@return {ManyArray}
|
|
276
373
|
*/
|
|
277
374
|
value() {
|
|
278
|
-
let internalModel = internalModelForReference(this)
|
|
375
|
+
let internalModel = internalModelForReference(this)!;
|
|
279
376
|
if (this._isLoaded()) {
|
|
280
377
|
return internalModel.getManyArray(this.key);
|
|
281
378
|
}
|
|
@@ -348,7 +445,7 @@ export default class HasManyReference extends Reference {
|
|
|
348
445
|
this has-many relationship.
|
|
349
446
|
*/
|
|
350
447
|
load(options) {
|
|
351
|
-
let internalModel = internalModelForReference(this)
|
|
448
|
+
let internalModel = internalModelForReference(this)!;
|
|
352
449
|
return internalModel.getHasMany(this.key, options);
|
|
353
450
|
}
|
|
354
451
|
|
|
@@ -403,7 +500,7 @@ export default class HasManyReference extends Reference {
|
|
|
403
500
|
@return {Promise} a promise that resolves with the ManyArray in this has-many relationship.
|
|
404
501
|
*/
|
|
405
502
|
reload(options) {
|
|
406
|
-
let internalModel = internalModelForReference(this)
|
|
503
|
+
let internalModel = internalModelForReference(this)!;
|
|
407
504
|
return internalModel.reloadHasMany(this.key, options);
|
|
408
505
|
}
|
|
409
506
|
}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
import { dependentKeyCompat } from '@ember/object/compat';
|
|
2
|
+
import { cached, tracked } from '@glimmer/tracking';
|
|
3
|
+
|
|
1
4
|
import RSVP, { resolve } from 'rsvp';
|
|
2
5
|
|
|
6
|
+
import { CUSTOM_MODEL_CLASS } from '@ember-data/canary-features';
|
|
7
|
+
|
|
8
|
+
import { unsubscribe } from '../record-notification-manager';
|
|
3
9
|
import Reference, { internalModelForReference, REFERENCE_CACHE } from './reference';
|
|
4
10
|
|
|
11
|
+
type NotificationType = import('../record-notification-manager').NotificationType;
|
|
12
|
+
|
|
13
|
+
type CoreStore = import('../core-store').default;
|
|
5
14
|
type SingleResourceDocument = import('../../ts-interfaces/ember-data-json-api').SingleResourceDocument;
|
|
6
15
|
type RecordInstance = import('../../ts-interfaces/record-instance').RecordInstance;
|
|
7
16
|
type StableRecordIdentifier = import('../../ts-interfaces/identifier').StableRecordIdentifier;
|
|
@@ -19,11 +28,39 @@ type StableRecordIdentifier = import('../../ts-interfaces/identifier').StableRec
|
|
|
19
28
|
@extends Reference
|
|
20
29
|
*/
|
|
21
30
|
export default class RecordReference extends Reference {
|
|
31
|
+
// unsubscribe token given to us by the notification manager
|
|
32
|
+
#token!: Object;
|
|
33
|
+
|
|
34
|
+
@tracked _ref = 0;
|
|
35
|
+
|
|
36
|
+
constructor(public store: CoreStore, identifier: StableRecordIdentifier) {
|
|
37
|
+
super(store, identifier);
|
|
38
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
39
|
+
this.#token = store._notificationManager.subscribe(
|
|
40
|
+
identifier,
|
|
41
|
+
(_: StableRecordIdentifier, bucket: NotificationType, notifiedKey?: string) => {
|
|
42
|
+
if (bucket === 'identity' || ((bucket === 'attributes' || bucket === 'property') && notifiedKey === 'id')) {
|
|
43
|
+
this._ref++;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
destroy() {
|
|
51
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
52
|
+
unsubscribe(this.#token);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
22
56
|
public get type(): string {
|
|
23
57
|
return this.identifier().type;
|
|
24
58
|
}
|
|
25
59
|
|
|
60
|
+
@cached
|
|
61
|
+
@dependentKeyCompat
|
|
26
62
|
private get _id(): string | null {
|
|
63
|
+
this._ref; // consume the tracked prop
|
|
27
64
|
let identifier = this.identifier();
|
|
28
65
|
if (identifier) {
|
|
29
66
|
return identifier.id;
|
|
@@ -51,7 +88,15 @@ export default class RecordReference extends Reference {
|
|
|
51
88
|
@return {String} The id of the record.
|
|
52
89
|
*/
|
|
53
90
|
id() {
|
|
54
|
-
|
|
91
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
92
|
+
return this._id;
|
|
93
|
+
}
|
|
94
|
+
let identifier = this.identifier();
|
|
95
|
+
if (identifier) {
|
|
96
|
+
return identifier.id;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return null;
|
|
55
100
|
}
|
|
56
101
|
|
|
57
102
|
/**
|
|
@@ -93,7 +138,7 @@ export default class RecordReference extends Reference {
|
|
|
93
138
|
@public
|
|
94
139
|
@return {String} 'identity'
|
|
95
140
|
*/
|
|
96
|
-
remoteType(): '
|
|
141
|
+
remoteType(): 'identity' {
|
|
97
142
|
return 'identity';
|
|
98
143
|
}
|
|
99
144
|
|
|
@@ -160,7 +205,7 @@ export default class RecordReference extends Reference {
|
|
|
160
205
|
@return {Model} the record for this RecordReference
|
|
161
206
|
*/
|
|
162
207
|
value(): RecordInstance | null {
|
|
163
|
-
if (this.
|
|
208
|
+
if (this.id() !== null) {
|
|
164
209
|
let internalModel = internalModelForReference(this);
|
|
165
210
|
if (internalModel && internalModel.currentState.isLoaded) {
|
|
166
211
|
return internalModel.getRecord();
|
|
@@ -187,8 +232,9 @@ export default class RecordReference extends Reference {
|
|
|
187
232
|
@return {Promise<record>} the record for this RecordReference
|
|
188
233
|
*/
|
|
189
234
|
load() {
|
|
190
|
-
|
|
191
|
-
|
|
235
|
+
const id = this.id();
|
|
236
|
+
if (id !== null) {
|
|
237
|
+
return this.store.findRecord(this.type, id);
|
|
192
238
|
}
|
|
193
239
|
throw new Error(`Unable to fetch record of type ${this.type} without an id`);
|
|
194
240
|
}
|
|
@@ -211,8 +257,9 @@ export default class RecordReference extends Reference {
|
|
|
211
257
|
@return {Promise<record>} the record for this RecordReference
|
|
212
258
|
*/
|
|
213
259
|
reload() {
|
|
214
|
-
|
|
215
|
-
|
|
260
|
+
const id = this.id();
|
|
261
|
+
if (id !== null) {
|
|
262
|
+
return this.store.findRecord(this.type, id, { reload: true });
|
|
216
263
|
}
|
|
217
264
|
throw new Error(`Unable to fetch record of type ${this.type} without an id`);
|
|
218
265
|
}
|
|
@@ -100,7 +100,7 @@ abstract class Reference {
|
|
|
100
100
|
@public
|
|
101
101
|
@return {String} The name of the remote type. This should either be "link" or "ids"
|
|
102
102
|
*/
|
|
103
|
-
remoteType(): 'link' | 'id' | 'identity' {
|
|
103
|
+
remoteType(): 'link' | 'id' | 'ids' | 'identity' {
|
|
104
104
|
let value = this._resource();
|
|
105
105
|
if (isResourceIdentiferWithRelatedLinks(value)) {
|
|
106
106
|
return 'link';
|
package/index.js
CHANGED
|
@@ -10,6 +10,8 @@ module.exports = Object.assign({}, addonBaseConfig, {
|
|
|
10
10
|
shouldRollupPrivate: true,
|
|
11
11
|
externalDependenciesForPrivateModule() {
|
|
12
12
|
return [
|
|
13
|
+
'ember-cached-decorator-polyfill',
|
|
14
|
+
|
|
13
15
|
'@ember-data/canary-features',
|
|
14
16
|
'@ember-data/store/-debug',
|
|
15
17
|
|
|
@@ -23,6 +25,7 @@ module.exports = Object.assign({}, addonBaseConfig, {
|
|
|
23
25
|
'@ember/object/evented',
|
|
24
26
|
'@ember/object/internals',
|
|
25
27
|
'@ember/object/mixin',
|
|
28
|
+
'@ember/object/compat',
|
|
26
29
|
'@ember/object/promise-proxy-mixin',
|
|
27
30
|
'@ember/object/proxy',
|
|
28
31
|
'@ember/polyfills',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ember-data/store",
|
|
3
|
-
"version": "3.28.
|
|
3
|
+
"version": "3.28.10",
|
|
4
4
|
"description": "The default blueprint for ember-cli addons.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ember-addon"
|
|
@@ -17,16 +17,17 @@
|
|
|
17
17
|
"start": "ember serve"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@ember-data/canary-features": "3.28.
|
|
21
|
-
"@ember-data/private-build-infra": "3.28.
|
|
20
|
+
"@ember-data/canary-features": "3.28.10",
|
|
21
|
+
"@ember-data/private-build-infra": "3.28.10",
|
|
22
22
|
"@ember/string": "^3.0.0",
|
|
23
23
|
"@glimmer/tracking": "^1.0.4",
|
|
24
|
+
"ember-cached-decorator-polyfill": "^0.1.4",
|
|
24
25
|
"ember-cli-babel": "^7.26.6",
|
|
25
26
|
"ember-cli-path-utils": "^1.0.0",
|
|
26
27
|
"ember-cli-typescript": "^4.1.0"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
|
-
"@ember-data/unpublished-test-infra": "3.28.
|
|
30
|
+
"@ember-data/unpublished-test-infra": "3.28.10",
|
|
30
31
|
"@ember/optional-features": "^2.0.0",
|
|
31
32
|
"@ember/test-helpers": "^2.2.5",
|
|
32
33
|
"@types/ember": "^3.16.5",
|