@ember-data/store 4.0.0-beta.3 → 4.0.2
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/identifiers/cache.ts +3 -5
- package/addon/-private/identifiers/utils/uuid-v4.ts +0 -13
- package/addon/-private/index.ts +0 -1
- package/addon/-private/system/core-store.ts +21 -23
- package/addon/-private/system/ds-model-store.ts +2 -2
- package/addon/-private/system/fetch-manager.ts +10 -6
- package/addon/-private/system/model/internal-model.ts +42 -27
- package/addon/-private/system/model/states.js +7 -1
- package/addon/-private/system/record-array-manager.js +2 -3
- package/addon/-private/system/record-arrays/adapter-populated-record-array.js +2 -3
- package/addon/-private/system/record-notification-manager.ts +14 -6
- package/addon/-private/system/references/{belongs-to.js → belongs-to.ts} +95 -16
- package/addon/-private/system/references/{has-many.js → has-many.ts} +141 -45
- package/addon/-private/system/references/record.ts +52 -7
- package/addon/-private/system/references/reference.ts +1 -1
- package/addon/-private/system/request-cache.ts +6 -7
- package/addon/-private/system/snapshot.ts +1 -2
- package/addon/-private/system/store/finders.js +4 -11
- package/addon/-private/ts-interfaces/ds-model.ts +1 -0
- package/addon/-private/ts-interfaces/fetch-manager.ts +4 -0
- package/addon/-private/ts-interfaces/identifier.ts +2 -3
- package/index.js +3 -0
- package/package.json +9 -8
- package/addon/-private/utils/symbol.ts +0 -33
|
@@ -1,11 +1,20 @@
|
|
|
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';
|
|
9
|
+
import type { BelongsToRelationship } from '@ember-data/record-data/-private';
|
|
6
10
|
import { assertPolymorphicType } from '@ember-data/store/-debug';
|
|
7
11
|
|
|
12
|
+
import { SingleResourceDocument } from '../../ts-interfaces/ember-data-json-api';
|
|
13
|
+
import { StableRecordIdentifier } from '../../ts-interfaces/identifier';
|
|
14
|
+
import CoreStore from '../core-store';
|
|
15
|
+
import { NotificationType, unsubscribe } from '../record-notification-manager';
|
|
8
16
|
import { internalModelFactoryFor, peekRecordIdentifier, recordIdentifierFor } from '../store/internal-model-factory';
|
|
17
|
+
import RecordReference from './record';
|
|
9
18
|
import Reference from './reference';
|
|
10
19
|
|
|
11
20
|
/**
|
|
@@ -22,17 +31,81 @@ import Reference from './reference';
|
|
|
22
31
|
@extends Reference
|
|
23
32
|
*/
|
|
24
33
|
export default class BelongsToReference extends Reference {
|
|
25
|
-
|
|
26
|
-
|
|
34
|
+
declare key: string;
|
|
35
|
+
declare belongsToRelationship: BelongsToRelationship;
|
|
36
|
+
declare type: string;
|
|
37
|
+
declare parent: RecordReference;
|
|
38
|
+
declare parentIdentifier: StableRecordIdentifier;
|
|
39
|
+
|
|
40
|
+
// unsubscribe tokens given to us by the notification manager
|
|
41
|
+
#token!: Object;
|
|
42
|
+
#relatedToken: Object | null = null;
|
|
43
|
+
|
|
44
|
+
@tracked _ref = 0;
|
|
45
|
+
|
|
46
|
+
constructor(
|
|
47
|
+
store: CoreStore,
|
|
48
|
+
parentIdentifier: StableRecordIdentifier,
|
|
49
|
+
belongsToRelationship: BelongsToRelationship,
|
|
50
|
+
key: string
|
|
51
|
+
) {
|
|
52
|
+
super(store, parentIdentifier);
|
|
27
53
|
this.key = key;
|
|
28
54
|
this.belongsToRelationship = belongsToRelationship;
|
|
29
55
|
this.type = belongsToRelationship.definition.type;
|
|
30
|
-
|
|
31
|
-
this.
|
|
56
|
+
const parent = internalModelFactoryFor(store).peek(parentIdentifier);
|
|
57
|
+
this.parent = parent!.recordReference;
|
|
58
|
+
this.parentIdentifier = parentIdentifier;
|
|
59
|
+
|
|
60
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
61
|
+
this.#token = store._notificationManager.subscribe(
|
|
62
|
+
parentIdentifier,
|
|
63
|
+
(_: StableRecordIdentifier, bucket: NotificationType, notifiedKey?: string) => {
|
|
64
|
+
if ((bucket === 'relationships' || bucket === 'property') && notifiedKey === key) {
|
|
65
|
+
this._ref++;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
}
|
|
32
70
|
|
|
33
71
|
// TODO inverse
|
|
34
72
|
}
|
|
35
73
|
|
|
74
|
+
destroy() {
|
|
75
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
76
|
+
unsubscribe(this.#token);
|
|
77
|
+
if (this.#relatedToken) {
|
|
78
|
+
unsubscribe(this.#relatedToken);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@cached
|
|
84
|
+
@dependentKeyCompat
|
|
85
|
+
get _relatedIdentifier(): StableRecordIdentifier | null {
|
|
86
|
+
this._ref; // consume the tracked prop
|
|
87
|
+
if (this.#relatedToken) {
|
|
88
|
+
unsubscribe(this.#relatedToken);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let resource = this._resource();
|
|
92
|
+
if (resource && resource.data) {
|
|
93
|
+
const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource.data);
|
|
94
|
+
this.#relatedToken = this.store._notificationManager.subscribe(
|
|
95
|
+
identifier,
|
|
96
|
+
(_: StableRecordIdentifier, bucket: NotificationType, notifiedKey?: string) => {
|
|
97
|
+
if (bucket === 'identity' || ((bucket === 'attributes' || bucket === 'property') && notifiedKey === 'id')) {
|
|
98
|
+
this._ref++;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
return identifier;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
36
109
|
/**
|
|
37
110
|
The `id` of the record that this reference refers to. Together, the
|
|
38
111
|
`type()` and `id()` methods form a composite key for the identity
|
|
@@ -73,13 +146,18 @@ export default class BelongsToReference extends Reference {
|
|
|
73
146
|
@public
|
|
74
147
|
@return {String} The id of the record in this belongsTo relationship.
|
|
75
148
|
*/
|
|
76
|
-
id() {
|
|
77
|
-
|
|
149
|
+
id(): string | null {
|
|
150
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
151
|
+
return this._relatedIdentifier?.id || null;
|
|
152
|
+
}
|
|
78
153
|
let resource = this._resource();
|
|
79
154
|
if (resource && resource.data) {
|
|
80
|
-
|
|
155
|
+
const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource.data);
|
|
156
|
+
|
|
157
|
+
return identifier.id;
|
|
81
158
|
}
|
|
82
|
-
|
|
159
|
+
|
|
160
|
+
return null;
|
|
83
161
|
}
|
|
84
162
|
|
|
85
163
|
_resource() {
|
|
@@ -132,10 +210,10 @@ export default class BelongsToReference extends Reference {
|
|
|
132
210
|
@param {Object|Promise} objectOrPromise a promise that resolves to a JSONAPI document object describing the new value of this relationship.
|
|
133
211
|
@return {Promise<record>} A promise that resolves with the new value in this belongs-to relationship.
|
|
134
212
|
*/
|
|
135
|
-
push(objectOrPromise) {
|
|
213
|
+
async push(objectOrPromise: Object | SingleResourceDocument): Promise<Object> {
|
|
136
214
|
// TODO deprecate thenable support
|
|
137
215
|
return resolve(objectOrPromise).then((data) => {
|
|
138
|
-
let record;
|
|
216
|
+
let record: Object;
|
|
139
217
|
|
|
140
218
|
if (DEPRECATE_BELONGS_TO_REFERENCE_PUSH && peekRecordIdentifier(data)) {
|
|
141
219
|
deprecate('Pushing a record into a BelongsToReference is deprecated', false, {
|
|
@@ -147,15 +225,16 @@ export default class BelongsToReference extends Reference {
|
|
|
147
225
|
enabled: '3.16',
|
|
148
226
|
},
|
|
149
227
|
});
|
|
150
|
-
record = data;
|
|
228
|
+
record = data as Object;
|
|
151
229
|
} else {
|
|
152
|
-
record = this.store.push(data);
|
|
230
|
+
record = this.store.push(data as SingleResourceDocument);
|
|
153
231
|
}
|
|
154
232
|
|
|
233
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
155
234
|
assertPolymorphicType(
|
|
156
235
|
this.belongsToRelationship.identifier,
|
|
157
236
|
this.belongsToRelationship.definition,
|
|
158
|
-
record
|
|
237
|
+
recordIdentifierFor(record),
|
|
159
238
|
this.store
|
|
160
239
|
);
|
|
161
240
|
|
|
@@ -223,7 +302,7 @@ export default class BelongsToReference extends Reference {
|
|
|
223
302
|
@public
|
|
224
303
|
@return {Model} the record in this relationship
|
|
225
304
|
*/
|
|
226
|
-
value() {
|
|
305
|
+
value(): Object | null {
|
|
227
306
|
let resource = this._resource();
|
|
228
307
|
if (resource && resource.data) {
|
|
229
308
|
let inverseInternalModel = this.store._internalModelForResource(resource.data);
|
|
@@ -299,7 +378,7 @@ export default class BelongsToReference extends Reference {
|
|
|
299
378
|
*/
|
|
300
379
|
load(options) {
|
|
301
380
|
let parentInternalModel = internalModelFactoryFor(this.store).peek(this.parentIdentifier);
|
|
302
|
-
return parentInternalModel
|
|
381
|
+
return parentInternalModel!.getBelongsTo(this.key, options);
|
|
303
382
|
}
|
|
304
383
|
|
|
305
384
|
/**
|
|
@@ -354,7 +433,7 @@ export default class BelongsToReference extends Reference {
|
|
|
354
433
|
*/
|
|
355
434
|
reload(options) {
|
|
356
435
|
let parentInternalModel = internalModelFactoryFor(this.store).peek(this.parentIdentifier);
|
|
357
|
-
return parentInternalModel
|
|
436
|
+
return parentInternalModel!.reloadBelongsTo(this.key, options).then((internalModel) => {
|
|
358
437
|
return this.value();
|
|
359
438
|
});
|
|
360
439
|
}
|
|
@@ -1,10 +1,23 @@
|
|
|
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';
|
|
8
|
+
import type { ManyRelationship } from '@ember-data/record-data/-private';
|
|
5
9
|
import { assertPolymorphicType } from '@ember-data/store/-debug';
|
|
6
10
|
|
|
11
|
+
import {
|
|
12
|
+
CollectionResourceDocument,
|
|
13
|
+
ExistingResourceObject,
|
|
14
|
+
SingleResourceDocument,
|
|
15
|
+
} from '../../ts-interfaces/ember-data-json-api';
|
|
16
|
+
import { StableRecordIdentifier } from '../../ts-interfaces/identifier';
|
|
17
|
+
import CoreStore from '../core-store';
|
|
18
|
+
import { NotificationType, unsubscribe } from '../record-notification-manager';
|
|
7
19
|
import { internalModelFactoryFor, recordIdentifierFor } from '../store/internal-model-factory';
|
|
20
|
+
import RecordReference from './record';
|
|
8
21
|
import Reference, { internalModelForReference } from './reference';
|
|
9
22
|
|
|
10
23
|
/**
|
|
@@ -20,17 +33,88 @@ import Reference, { internalModelForReference } from './reference';
|
|
|
20
33
|
@extends Reference
|
|
21
34
|
*/
|
|
22
35
|
export default class HasManyReference extends Reference {
|
|
23
|
-
|
|
24
|
-
|
|
36
|
+
declare key: string;
|
|
37
|
+
declare hasManyRelationship: ManyRelationship;
|
|
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
|
+
#relatedTokenMap!: Map<StableRecordIdentifier, Object>;
|
|
45
|
+
|
|
46
|
+
@tracked _ref = 0;
|
|
47
|
+
|
|
48
|
+
constructor(
|
|
49
|
+
store: CoreStore,
|
|
50
|
+
parentIdentifier: StableRecordIdentifier,
|
|
51
|
+
hasManyRelationship: ManyRelationship,
|
|
52
|
+
key: string
|
|
53
|
+
) {
|
|
54
|
+
super(store, parentIdentifier);
|
|
25
55
|
this.key = key;
|
|
26
56
|
this.hasManyRelationship = hasManyRelationship;
|
|
27
57
|
this.type = hasManyRelationship.definition.type;
|
|
28
58
|
|
|
29
|
-
this.parent = internalModelFactoryFor(store).peek(
|
|
59
|
+
this.parent = internalModelFactoryFor(store).peek(parentIdentifier)!.recordReference;
|
|
30
60
|
|
|
61
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
62
|
+
this.#token = store._notificationManager.subscribe(
|
|
63
|
+
parentIdentifier,
|
|
64
|
+
(_: StableRecordIdentifier, bucket: NotificationType, notifiedKey?: string) => {
|
|
65
|
+
if ((bucket === 'relationships' || bucket === 'property') && notifiedKey === key) {
|
|
66
|
+
this._ref++;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
this.#relatedTokenMap = new Map();
|
|
71
|
+
}
|
|
31
72
|
// TODO inverse
|
|
32
73
|
}
|
|
33
74
|
|
|
75
|
+
destroy() {
|
|
76
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
77
|
+
unsubscribe(this.#token);
|
|
78
|
+
this.#relatedTokenMap.forEach((token) => {
|
|
79
|
+
unsubscribe(token);
|
|
80
|
+
});
|
|
81
|
+
this.#relatedTokenMap.clear();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@cached
|
|
86
|
+
@dependentKeyCompat
|
|
87
|
+
get _relatedIdentifiers(): StableRecordIdentifier[] {
|
|
88
|
+
this._ref; // consume the tracked prop
|
|
89
|
+
|
|
90
|
+
let resource = this._resource();
|
|
91
|
+
|
|
92
|
+
this.#relatedTokenMap.forEach((token) => {
|
|
93
|
+
unsubscribe(token);
|
|
94
|
+
});
|
|
95
|
+
this.#relatedTokenMap.clear();
|
|
96
|
+
|
|
97
|
+
if (resource && resource.data) {
|
|
98
|
+
return resource.data.map((resourceIdentifier) => {
|
|
99
|
+
const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
|
|
100
|
+
const token = this.store._notificationManager.subscribe(
|
|
101
|
+
identifier,
|
|
102
|
+
(_: StableRecordIdentifier, bucket: NotificationType, notifiedKey?: string) => {
|
|
103
|
+
if (bucket === 'identity' || ((bucket === 'attributes' || bucket === 'property') && notifiedKey === 'id')) {
|
|
104
|
+
this._ref++;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
this.#relatedTokenMap.set(identifier, token);
|
|
110
|
+
|
|
111
|
+
return identifier;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
|
|
34
118
|
_resource() {
|
|
35
119
|
return this.recordData.getHasMany(this.key);
|
|
36
120
|
}
|
|
@@ -77,7 +161,7 @@ export default class HasManyReference extends Reference {
|
|
|
77
161
|
@public
|
|
78
162
|
@return {String} The name of the remote type. This should either be `link` or `ids`
|
|
79
163
|
*/
|
|
80
|
-
remoteType() {
|
|
164
|
+
remoteType(): 'link' | 'ids' {
|
|
81
165
|
let value = this._resource();
|
|
82
166
|
if (value && value.links && value.links.related) {
|
|
83
167
|
return 'link';
|
|
@@ -121,15 +205,22 @@ export default class HasManyReference extends Reference {
|
|
|
121
205
|
@public
|
|
122
206
|
@return {Array} The ids in this has-many relationship
|
|
123
207
|
*/
|
|
124
|
-
ids() {
|
|
208
|
+
ids(): Array<string | null> {
|
|
209
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
210
|
+
return this._relatedIdentifiers.map((identifier) => identifier.id);
|
|
211
|
+
}
|
|
212
|
+
|
|
125
213
|
let resource = this._resource();
|
|
126
214
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
215
|
+
if (resource && resource.data) {
|
|
216
|
+
return resource.data.map((resourceIdentifier) => {
|
|
217
|
+
const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
|
|
218
|
+
|
|
219
|
+
return identifier.id;
|
|
220
|
+
});
|
|
130
221
|
}
|
|
131
222
|
|
|
132
|
-
return
|
|
223
|
+
return [];
|
|
133
224
|
}
|
|
134
225
|
|
|
135
226
|
/**
|
|
@@ -177,45 +268,50 @@ export default class HasManyReference extends Reference {
|
|
|
177
268
|
@param {Array|Promise} objectOrPromise a promise that resolves to a JSONAPI document object describing the new value of this relationship.
|
|
178
269
|
@return {ManyArray}
|
|
179
270
|
*/
|
|
180
|
-
push(
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
271
|
+
async push(
|
|
272
|
+
objectOrPromise: ExistingResourceObject[] | CollectionResourceDocument | { data: SingleResourceDocument[] }
|
|
273
|
+
): Promise<any> {
|
|
274
|
+
const payload = await resolve(objectOrPromise);
|
|
275
|
+
let array: Array<ExistingResourceObject | SingleResourceDocument>;
|
|
276
|
+
|
|
277
|
+
if (!Array.isArray(payload) && typeof payload === 'object' && Array.isArray(payload.data)) {
|
|
278
|
+
array = payload.data;
|
|
279
|
+
} else {
|
|
280
|
+
array = payload as ExistingResourceObject[];
|
|
281
|
+
}
|
|
187
282
|
|
|
188
|
-
|
|
283
|
+
const internalModel = internalModelForReference(this)!;
|
|
284
|
+
const { store } = this;
|
|
189
285
|
|
|
190
|
-
|
|
191
|
-
|
|
286
|
+
let identifiers = array.map((obj) => {
|
|
287
|
+
let record;
|
|
288
|
+
if ('data' in obj) {
|
|
289
|
+
// TODO deprecate pushing non-valid JSON:API here
|
|
290
|
+
record = store.push(obj);
|
|
291
|
+
} else {
|
|
292
|
+
record = store.push({ data: obj });
|
|
293
|
+
}
|
|
192
294
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
return recordIdentifierFor(record);
|
|
203
|
-
});
|
|
295
|
+
if (DEBUG) {
|
|
296
|
+
let relationshipMeta = this.hasManyRelationship.definition;
|
|
297
|
+
let identifier = this.hasManyRelationship.identifier;
|
|
298
|
+
assertPolymorphicType(identifier, relationshipMeta, recordIdentifierFor(record), store);
|
|
299
|
+
}
|
|
300
|
+
return recordIdentifierFor(record);
|
|
301
|
+
});
|
|
204
302
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
});
|
|
303
|
+
const { graph, identifier } = this.hasManyRelationship;
|
|
304
|
+
store._backburner.join(() => {
|
|
305
|
+
graph.push({
|
|
306
|
+
op: 'replaceRelatedRecords',
|
|
307
|
+
record: identifier,
|
|
308
|
+
field: this.key,
|
|
309
|
+
value: identifiers,
|
|
213
310
|
});
|
|
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
311
|
});
|
|
312
|
+
|
|
313
|
+
// TODO IGOR it seems wrong that we were returning the many array here
|
|
314
|
+
return internalModel.getHasMany(this.key);
|
|
219
315
|
}
|
|
220
316
|
|
|
221
317
|
_isLoaded() {
|
|
@@ -275,7 +371,7 @@ export default class HasManyReference extends Reference {
|
|
|
275
371
|
@return {ManyArray}
|
|
276
372
|
*/
|
|
277
373
|
value() {
|
|
278
|
-
let internalModel = internalModelForReference(this)
|
|
374
|
+
let internalModel = internalModelForReference(this)!;
|
|
279
375
|
if (this._isLoaded()) {
|
|
280
376
|
return internalModel.getManyArray(this.key);
|
|
281
377
|
}
|
|
@@ -348,7 +444,7 @@ export default class HasManyReference extends Reference {
|
|
|
348
444
|
this has-many relationship.
|
|
349
445
|
*/
|
|
350
446
|
load(options) {
|
|
351
|
-
let internalModel = internalModelForReference(this)
|
|
447
|
+
let internalModel = internalModelForReference(this)!;
|
|
352
448
|
return internalModel.getHasMany(this.key, options);
|
|
353
449
|
}
|
|
354
450
|
|
|
@@ -403,7 +499,7 @@ export default class HasManyReference extends Reference {
|
|
|
403
499
|
@return {Promise} a promise that resolves with the ManyArray in this has-many relationship.
|
|
404
500
|
*/
|
|
405
501
|
reload(options) {
|
|
406
|
-
let internalModel = internalModelForReference(this)
|
|
502
|
+
let internalModel = internalModelForReference(this)!;
|
|
407
503
|
return internalModel.reloadHasMany(this.key, options);
|
|
408
504
|
}
|
|
409
505
|
}
|
|
@@ -1,8 +1,15 @@
|
|
|
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
|
+
|
|
3
8
|
import type { SingleResourceDocument } from '../../ts-interfaces/ember-data-json-api';
|
|
4
9
|
import type { StableRecordIdentifier } from '../../ts-interfaces/identifier';
|
|
5
10
|
import type { RecordInstance } from '../../ts-interfaces/record-instance';
|
|
11
|
+
import type CoreStore from '../core-store';
|
|
12
|
+
import { NotificationType, unsubscribe } from '../record-notification-manager';
|
|
6
13
|
import Reference, { internalModelForReference, REFERENCE_CACHE } from './reference';
|
|
7
14
|
|
|
8
15
|
/**
|
|
@@ -18,11 +25,39 @@ import Reference, { internalModelForReference, REFERENCE_CACHE } from './referen
|
|
|
18
25
|
@extends Reference
|
|
19
26
|
*/
|
|
20
27
|
export default class RecordReference extends Reference {
|
|
28
|
+
// unsubscribe token given to us by the notification manager
|
|
29
|
+
#token!: Object;
|
|
30
|
+
|
|
31
|
+
@tracked _ref = 0;
|
|
32
|
+
|
|
33
|
+
constructor(public store: CoreStore, identifier: StableRecordIdentifier) {
|
|
34
|
+
super(store, identifier);
|
|
35
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
36
|
+
this.#token = store._notificationManager.subscribe(
|
|
37
|
+
identifier,
|
|
38
|
+
(_: StableRecordIdentifier, bucket: NotificationType, notifiedKey?: string) => {
|
|
39
|
+
if (bucket === 'identity' || ((bucket === 'attributes' || bucket === 'property') && notifiedKey === 'id')) {
|
|
40
|
+
this._ref++;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
destroy() {
|
|
48
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
49
|
+
unsubscribe(this.#token);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
21
53
|
public get type(): string {
|
|
22
54
|
return this.identifier().type;
|
|
23
55
|
}
|
|
24
56
|
|
|
57
|
+
@cached
|
|
58
|
+
@dependentKeyCompat
|
|
25
59
|
private get _id(): string | null {
|
|
60
|
+
this._ref; // consume the tracked prop
|
|
26
61
|
let identifier = this.identifier();
|
|
27
62
|
if (identifier) {
|
|
28
63
|
return identifier.id;
|
|
@@ -50,7 +85,15 @@ export default class RecordReference extends Reference {
|
|
|
50
85
|
@return {String} The id of the record.
|
|
51
86
|
*/
|
|
52
87
|
id() {
|
|
53
|
-
|
|
88
|
+
if (CUSTOM_MODEL_CLASS) {
|
|
89
|
+
return this._id;
|
|
90
|
+
}
|
|
91
|
+
let identifier = this.identifier();
|
|
92
|
+
if (identifier) {
|
|
93
|
+
return identifier.id;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return null;
|
|
54
97
|
}
|
|
55
98
|
|
|
56
99
|
/**
|
|
@@ -92,7 +135,7 @@ export default class RecordReference extends Reference {
|
|
|
92
135
|
@public
|
|
93
136
|
@return {String} 'identity'
|
|
94
137
|
*/
|
|
95
|
-
remoteType(): '
|
|
138
|
+
remoteType(): 'identity' {
|
|
96
139
|
return 'identity';
|
|
97
140
|
}
|
|
98
141
|
|
|
@@ -159,7 +202,7 @@ export default class RecordReference extends Reference {
|
|
|
159
202
|
@return {Model} the record for this RecordReference
|
|
160
203
|
*/
|
|
161
204
|
value(): RecordInstance | null {
|
|
162
|
-
if (this.
|
|
205
|
+
if (this.id() !== null) {
|
|
163
206
|
let internalModel = internalModelForReference(this);
|
|
164
207
|
if (internalModel && internalModel.currentState.isLoaded) {
|
|
165
208
|
return internalModel.getRecord();
|
|
@@ -186,8 +229,9 @@ export default class RecordReference extends Reference {
|
|
|
186
229
|
@return {Promise<record>} the record for this RecordReference
|
|
187
230
|
*/
|
|
188
231
|
load() {
|
|
189
|
-
|
|
190
|
-
|
|
232
|
+
const id = this.id();
|
|
233
|
+
if (id !== null) {
|
|
234
|
+
return this.store.findRecord(this.type, id);
|
|
191
235
|
}
|
|
192
236
|
throw new Error(`Unable to fetch record of type ${this.type} without an id`);
|
|
193
237
|
}
|
|
@@ -210,8 +254,9 @@ export default class RecordReference extends Reference {
|
|
|
210
254
|
@return {Promise<record>} the record for this RecordReference
|
|
211
255
|
*/
|
|
212
256
|
reload() {
|
|
213
|
-
|
|
214
|
-
|
|
257
|
+
const id = this.id();
|
|
258
|
+
if (id !== null) {
|
|
259
|
+
return this.store.findRecord(this.type, id, { reload: true });
|
|
215
260
|
}
|
|
216
261
|
throw new Error(`Unable to fetch record of type ${this.type} without an id`);
|
|
217
262
|
}
|
|
@@ -98,7 +98,7 @@ abstract class Reference {
|
|
|
98
98
|
@public
|
|
99
99
|
@return {String} The name of the remote type. This should either be "link" or "ids"
|
|
100
100
|
*/
|
|
101
|
-
remoteType(): 'link' | 'id' | 'identity' {
|
|
101
|
+
remoteType(): 'link' | 'id' | 'ids' | 'identity' {
|
|
102
102
|
let value = this._resource();
|
|
103
103
|
if (isResourceIdentiferWithRelatedLinks(value)) {
|
|
104
104
|
return 'link';
|
|
@@ -7,10 +7,9 @@ import type {
|
|
|
7
7
|
} from '../ts-interfaces/fetch-manager';
|
|
8
8
|
import { RequestStateEnum } from '../ts-interfaces/fetch-manager';
|
|
9
9
|
import type { RecordIdentifier } from '../ts-interfaces/identifier';
|
|
10
|
-
import { addSymbol, symbol } from '../utils/symbol';
|
|
11
10
|
|
|
12
|
-
const Touching: unique symbol =
|
|
13
|
-
export const RequestPromise: unique symbol =
|
|
11
|
+
const Touching: unique symbol = Symbol('touching');
|
|
12
|
+
export const RequestPromise: unique symbol = Symbol('promise');
|
|
14
13
|
|
|
15
14
|
interface InternalRequest extends RequestState {
|
|
16
15
|
[Touching]: RecordIdentifier[];
|
|
@@ -41,8 +40,8 @@ export default class RequestCache {
|
|
|
41
40
|
request: queryRequest,
|
|
42
41
|
type,
|
|
43
42
|
} as InternalRequest;
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
request[Touching] = [query.recordIdentifier];
|
|
44
|
+
request[RequestPromise] = promise;
|
|
46
45
|
this._pending[lid].push(request);
|
|
47
46
|
this._triggerSubscriptions(request);
|
|
48
47
|
promise.then(
|
|
@@ -54,7 +53,7 @@ export default class RequestCache {
|
|
|
54
53
|
type,
|
|
55
54
|
response: { data: result },
|
|
56
55
|
} as InternalRequest;
|
|
57
|
-
|
|
56
|
+
finalizedRequest[Touching] = request[Touching];
|
|
58
57
|
this._addDone(finalizedRequest);
|
|
59
58
|
this._triggerSubscriptions(finalizedRequest);
|
|
60
59
|
},
|
|
@@ -66,7 +65,7 @@ export default class RequestCache {
|
|
|
66
65
|
type,
|
|
67
66
|
response: { data: error && error.error },
|
|
68
67
|
} as InternalRequest;
|
|
69
|
-
|
|
68
|
+
finalizedRequest[Touching] = request[Touching];
|
|
70
69
|
this._addDone(finalizedRequest);
|
|
71
70
|
this._triggerSubscriptions(finalizedRequest);
|
|
72
71
|
}
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { assert } from '@ember/debug';
|
|
5
5
|
import { get } from '@ember/object';
|
|
6
|
-
import { assign } from '@ember/polyfills';
|
|
7
6
|
|
|
8
7
|
import { CUSTOM_MODEL_CLASS } from '@ember-data/canary-features';
|
|
9
8
|
import { HAS_RECORD_DATA_PACKAGE } from '@ember-data/private-build-infra';
|
|
@@ -234,7 +233,7 @@ export default class Snapshot implements Snapshot {
|
|
|
234
233
|
@public
|
|
235
234
|
*/
|
|
236
235
|
attributes(): Dict<unknown> {
|
|
237
|
-
return
|
|
236
|
+
return { ...this._attributes };
|
|
238
237
|
}
|
|
239
238
|
|
|
240
239
|
/**
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { assert, deprecate, warn } from '@ember/debug';
|
|
2
|
-
import { assign } from '@ember/polyfills';
|
|
3
2
|
import { DEBUG } from '@glimmer/env';
|
|
4
3
|
|
|
5
4
|
import { Promise } from 'rsvp';
|
|
@@ -274,18 +273,12 @@ function fixRelationshipData(relationshipData, relationshipKind, { id, modelName
|
|
|
274
273
|
if (relationshipKind === 'hasMany') {
|
|
275
274
|
payload = relationshipData || [];
|
|
276
275
|
if (relationshipData) {
|
|
277
|
-
// IE11 does not support array.find
|
|
278
276
|
// these arrays could be massive so this is better than filter
|
|
279
277
|
// Note: this is potentially problematic if type/id are not in the
|
|
280
278
|
// same state of normalization.
|
|
281
|
-
let found =
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
if (v.type === parentRelationshipData.type && v.id === parentRelationshipData.id) {
|
|
285
|
-
found = true;
|
|
286
|
-
break;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
279
|
+
let found = relationshipData.find((v) => {
|
|
280
|
+
return v.type === parentRelationshipData.type && v.id === parentRelationshipData.id;
|
|
281
|
+
});
|
|
289
282
|
if (!found) {
|
|
290
283
|
payload.push(parentRelationshipData);
|
|
291
284
|
}
|
|
@@ -294,7 +287,7 @@ function fixRelationshipData(relationshipData, relationshipKind, { id, modelName
|
|
|
294
287
|
}
|
|
295
288
|
} else {
|
|
296
289
|
payload = relationshipData || {};
|
|
297
|
-
assign(payload, parentRelationshipData);
|
|
290
|
+
Object.assign(payload, parentRelationshipData);
|
|
298
291
|
}
|
|
299
292
|
|
|
300
293
|
return payload;
|