@ember-data/model 4.8.0-alpha.2 → 4.8.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addon/-private/attr.js +9 -24
- package/addon/-private/belongs-to.js +87 -25
- package/addon/-private/errors.ts +6 -6
- package/addon/-private/has-many.js +81 -21
- package/addon/-private/legacy-data-fetch.js +35 -13
- package/addon/-private/legacy-data-utils.ts +1 -1
- package/addon/-private/legacy-relationships-support.ts +138 -133
- package/addon/-private/many-array.ts +38 -26
- package/addon/-private/model.js +105 -99
- package/addon/-private/notify-changes.ts +2 -2
- package/addon/-private/promise-many-array.ts +170 -112
- package/addon/-private/record-state.ts +35 -33
- package/addon/-private/references/belongs-to.ts +44 -36
- package/addon/-private/references/has-many.ts +65 -54
- package/addon/-private/relationship-meta.ts +3 -23
- package/package.json +12 -8
|
@@ -5,25 +5,23 @@ import { importSync } from '@embroider/macros';
|
|
|
5
5
|
import { all, resolve } from 'rsvp';
|
|
6
6
|
|
|
7
7
|
import { HAS_RECORD_DATA_PACKAGE } from '@ember-data/private-build-infra';
|
|
8
|
-
import type {
|
|
9
|
-
BelongsToRelationship,
|
|
10
|
-
ManyRelationship,
|
|
11
|
-
RecordData as DefaultRecordData,
|
|
12
|
-
} from '@ember-data/record-data/-private';
|
|
13
8
|
import type { UpgradedMeta } from '@ember-data/record-data/-private/graph/-edge-definition';
|
|
14
|
-
import type {
|
|
9
|
+
import type { ImplicitRelationship } from '@ember-data/record-data/-private/graph/index';
|
|
10
|
+
import type BelongsToRelationship from '@ember-data/record-data/-private/relationships/state/belongs-to';
|
|
11
|
+
import type ManyRelationship from '@ember-data/record-data/-private/relationships/state/has-many';
|
|
15
12
|
import type Store from '@ember-data/store';
|
|
16
|
-
import
|
|
17
|
-
import {
|
|
18
|
-
import type { IdentifierCache } from '@ember-data/store/-private/identifier-cache';
|
|
13
|
+
import { recordIdentifierFor, storeFor } from '@ember-data/store/-private';
|
|
14
|
+
import { IdentifierCache } from '@ember-data/store/-private/caches/identifier-cache';
|
|
19
15
|
import type { DSModel } from '@ember-data/types/q/ds-model';
|
|
20
|
-
import
|
|
16
|
+
import {
|
|
17
|
+
CollectionResourceRelationship,
|
|
18
|
+
ResourceIdentifierObject,
|
|
19
|
+
SingleResourceRelationship,
|
|
20
|
+
} from '@ember-data/types/q/ember-data-json-api';
|
|
21
21
|
import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
22
22
|
import type { RecordData } from '@ember-data/types/q/record-data';
|
|
23
23
|
import type { JsonApiRelationship } from '@ember-data/types/q/record-data-json-api';
|
|
24
|
-
import type { RelationshipSchema } from '@ember-data/types/q/record-data-schemas';
|
|
25
24
|
import type { RecordInstance } from '@ember-data/types/q/record-instance';
|
|
26
|
-
import type { DefaultSingleResourceRelationship } from '@ember-data/types/q/relationship-record-data';
|
|
27
25
|
import type { FindOptions } from '@ember-data/types/q/store';
|
|
28
26
|
import type { Dict } from '@ember-data/types/q/utils';
|
|
29
27
|
|
|
@@ -44,7 +42,7 @@ type PromiseBelongsToFactory = { create(args: BelongsToProxyCreateArgs): Promise
|
|
|
44
42
|
export class LegacySupport {
|
|
45
43
|
declare record: DSModel;
|
|
46
44
|
declare store: Store;
|
|
47
|
-
declare recordData:
|
|
45
|
+
declare recordData: RecordData;
|
|
48
46
|
declare references: Dict<BelongsToReference | HasManyReference>;
|
|
49
47
|
declare identifier: StableRecordIdentifier;
|
|
50
48
|
declare _manyArrayCache: Dict<ManyArray>;
|
|
@@ -58,7 +56,7 @@ export class LegacySupport {
|
|
|
58
56
|
this.record = record;
|
|
59
57
|
this.store = storeFor(record)!;
|
|
60
58
|
this.identifier = recordIdentifierFor(record);
|
|
61
|
-
this.recordData = this.store._instanceCache.getRecordData(this.identifier)
|
|
59
|
+
this.recordData = this.store._instanceCache.getRecordData(this.identifier);
|
|
62
60
|
|
|
63
61
|
this._manyArrayCache = Object.create(null) as Dict<ManyArray>;
|
|
64
62
|
this._relationshipPromisesCache = Object.create(null) as Dict<Promise<ManyArray | RecordInstance>>;
|
|
@@ -68,16 +66,16 @@ export class LegacySupport {
|
|
|
68
66
|
|
|
69
67
|
_findBelongsTo(
|
|
70
68
|
key: string,
|
|
71
|
-
resource:
|
|
72
|
-
|
|
69
|
+
resource: SingleResourceRelationship,
|
|
70
|
+
relationship: BelongsToRelationship,
|
|
73
71
|
options?: FindOptions
|
|
74
72
|
): Promise<RecordInstance | null> {
|
|
75
73
|
// TODO @runspired follow up if parent isNew then we should not be attempting load here
|
|
76
74
|
// TODO @runspired follow up on whether this should be in the relationship requests cache
|
|
77
|
-
return this._findBelongsToByJsonApiResource(resource, this.identifier,
|
|
75
|
+
return this._findBelongsToByJsonApiResource(resource, this.identifier, relationship, options).then(
|
|
78
76
|
(identifier: StableRecordIdentifier | null) =>
|
|
79
|
-
handleCompletedRelationshipRequest(this, key,
|
|
80
|
-
(e: Error) => handleCompletedRelationshipRequest(this, key,
|
|
77
|
+
handleCompletedRelationshipRequest(this, key, relationship, identifier),
|
|
78
|
+
(e: Error) => handleCompletedRelationshipRequest(this, key, relationship, null, e)
|
|
81
79
|
);
|
|
82
80
|
}
|
|
83
81
|
|
|
@@ -87,15 +85,16 @@ export class LegacySupport {
|
|
|
87
85
|
return loadingPromise;
|
|
88
86
|
}
|
|
89
87
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
let
|
|
97
|
-
|
|
98
|
-
|
|
88
|
+
const graphFor = (
|
|
89
|
+
importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
|
|
90
|
+
).graphFor;
|
|
91
|
+
const relationship = graphFor(this.store).get(this.identifier, key);
|
|
92
|
+
assert(`Expected ${key} to be a belongs-to relationship`, isBelongsTo(relationship));
|
|
93
|
+
|
|
94
|
+
let resource = this.recordData.getRelationship(this.identifier, key) as SingleResourceRelationship;
|
|
95
|
+
relationship.state.hasFailedLoadAttempt = false;
|
|
96
|
+
relationship.state.shouldForceReload = true;
|
|
97
|
+
let promise = this._findBelongsTo(key, resource, relationship, options);
|
|
99
98
|
if (this._relationshipProxyCache[key]) {
|
|
100
99
|
return this._updatePromiseProxyFor('belongsTo', key, { promise });
|
|
101
100
|
}
|
|
@@ -104,28 +103,31 @@ export class LegacySupport {
|
|
|
104
103
|
|
|
105
104
|
getBelongsTo(key: string, options?: FindOptions): PromiseBelongsTo | RecordInstance | null {
|
|
106
105
|
const { identifier, recordData } = this;
|
|
107
|
-
let resource = recordData.
|
|
106
|
+
let resource = recordData.getRelationship(this.identifier, key) as SingleResourceRelationship;
|
|
108
107
|
let relatedIdentifier =
|
|
109
108
|
resource && resource.data ? this.store.identifierCache.getOrCreateRecordIdentifier(resource.data) : null;
|
|
110
|
-
let relationshipMeta = this.store.getSchemaDefinitionService().relationshipsDefinitionFor(identifier)[key];
|
|
111
|
-
assert(`Attempted to access a belongsTo relationship but no definition exists for it`, relationshipMeta);
|
|
112
109
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
110
|
+
const store = this.store;
|
|
111
|
+
const graphFor = (
|
|
112
|
+
importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
|
|
113
|
+
).graphFor;
|
|
114
|
+
const relationship = graphFor(store).get(this.identifier, key);
|
|
115
|
+
assert(`Expected ${key} to be a belongs-to relationship`, isBelongsTo(relationship));
|
|
116
|
+
|
|
117
|
+
let isAsync = relationship.definition.isAsync;
|
|
116
118
|
let _belongsToState: BelongsToProxyMeta = {
|
|
117
119
|
key,
|
|
118
120
|
store,
|
|
119
121
|
legacySupport: this,
|
|
120
|
-
modelName:
|
|
122
|
+
modelName: relationship.definition.type,
|
|
121
123
|
};
|
|
122
124
|
|
|
123
125
|
if (isAsync) {
|
|
124
|
-
if (
|
|
126
|
+
if (relationship.state.hasFailedLoadAttempt) {
|
|
125
127
|
return this._relationshipProxyCache[key] as PromiseBelongsTo;
|
|
126
128
|
}
|
|
127
129
|
|
|
128
|
-
let promise = this._findBelongsTo(key, resource,
|
|
130
|
+
let promise = this._findBelongsTo(key, resource, relationship, options);
|
|
129
131
|
|
|
130
132
|
return this._updatePromiseProxyFor('belongsTo', key, {
|
|
131
133
|
promise,
|
|
@@ -140,8 +142,8 @@ export class LegacySupport {
|
|
|
140
142
|
assert(
|
|
141
143
|
`You looked up the '${key}' relationship on a '${identifier.type}' with id ${
|
|
142
144
|
identifier.id || 'null'
|
|
143
|
-
} 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 })\`)`,
|
|
144
|
-
toReturn === null ||
|
|
145
|
+
} 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(<type>, { async: true, inverse: <inverse> })\`)`,
|
|
146
|
+
toReturn === null || store._instanceCache.recordIsLoaded(relatedIdentifier, true)
|
|
145
147
|
);
|
|
146
148
|
return toReturn;
|
|
147
149
|
}
|
|
@@ -149,7 +151,7 @@ export class LegacySupport {
|
|
|
149
151
|
}
|
|
150
152
|
|
|
151
153
|
setDirtyBelongsTo(key: string, value: RecordInstance | null) {
|
|
152
|
-
return this.recordData.
|
|
154
|
+
return this.recordData.setBelongsTo(this.identifier, key, extractIdentifierFromRecord(value));
|
|
153
155
|
}
|
|
154
156
|
|
|
155
157
|
getManyArray(key: string, definition?: UpgradedMeta): ManyArray {
|
|
@@ -166,6 +168,7 @@ export class LegacySupport {
|
|
|
166
168
|
manyArray = (ManyArray as unknown as ManyArrayFactory).create({
|
|
167
169
|
store: this.store,
|
|
168
170
|
type: this.store.modelFor(definition.type),
|
|
171
|
+
identifier: this.identifier,
|
|
169
172
|
recordData: this.recordData,
|
|
170
173
|
key,
|
|
171
174
|
isPolymorphic: definition.isPolymorphic,
|
|
@@ -192,7 +195,7 @@ export class LegacySupport {
|
|
|
192
195
|
return loadingPromise;
|
|
193
196
|
}
|
|
194
197
|
|
|
195
|
-
const jsonApi = this.recordData.
|
|
198
|
+
const jsonApi = this.recordData.getRelationship(this.identifier, key) as CollectionResourceRelationship;
|
|
196
199
|
|
|
197
200
|
loadingPromise = this._findHasManyByJsonApiResource(jsonApi, this.identifier, relationship, options).then(
|
|
198
201
|
() => handleCompletedRelationshipRequest(this, key, relationship, manyArray),
|
|
@@ -251,7 +254,7 @@ export class LegacySupport {
|
|
|
251
254
|
assert(
|
|
252
255
|
`You looked up the '${key}' relationship on a '${this.identifier.type}' with id ${
|
|
253
256
|
this.identifier.id || 'null'
|
|
254
|
-
} 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 ('hasMany({ async: true })')`,
|
|
257
|
+
} 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 ('hasMany(<type>, { async: true, inverse: <inverse> })')`,
|
|
255
258
|
!anyUnloaded(this.store, relationship)
|
|
256
259
|
);
|
|
257
260
|
|
|
@@ -263,7 +266,7 @@ export class LegacySupport {
|
|
|
263
266
|
|
|
264
267
|
setDirtyHasMany(key: string, records: RecordInstance[]) {
|
|
265
268
|
assertRecordsPassedToHasMany(records);
|
|
266
|
-
return this.recordData.
|
|
269
|
+
return this.recordData.setHasMany(this.identifier, key, extractIdentifiersFromRecords(records));
|
|
267
270
|
}
|
|
268
271
|
|
|
269
272
|
_updatePromiseProxyFor(kind: 'hasMany', key: string, args: HasManyProxyCreateArgs): PromiseManyArray;
|
|
@@ -318,7 +321,8 @@ export class LegacySupport {
|
|
|
318
321
|
const graphFor = (
|
|
319
322
|
importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
|
|
320
323
|
).graphFor;
|
|
321
|
-
const
|
|
324
|
+
const graph = graphFor(this.store);
|
|
325
|
+
const relationship = graph.get(this.identifier, name);
|
|
322
326
|
|
|
323
327
|
if (DEBUG && kind) {
|
|
324
328
|
let modelName = this.identifier.type;
|
|
@@ -332,9 +336,15 @@ export class LegacySupport {
|
|
|
332
336
|
let relationshipKind = relationship.definition.kind;
|
|
333
337
|
|
|
334
338
|
if (relationshipKind === 'belongsTo') {
|
|
335
|
-
reference = new BelongsToReference(
|
|
339
|
+
reference = new BelongsToReference(
|
|
340
|
+
this.store,
|
|
341
|
+
graph,
|
|
342
|
+
this.identifier,
|
|
343
|
+
relationship as BelongsToRelationship,
|
|
344
|
+
name
|
|
345
|
+
);
|
|
336
346
|
} else if (relationshipKind === 'hasMany') {
|
|
337
|
-
reference = new HasManyReference(this.store, this.identifier, relationship as ManyRelationship, name);
|
|
347
|
+
reference = new HasManyReference(this.store, graph, this.identifier, relationship as ManyRelationship, name);
|
|
338
348
|
}
|
|
339
349
|
|
|
340
350
|
this.references[name] = reference;
|
|
@@ -344,7 +354,7 @@ export class LegacySupport {
|
|
|
344
354
|
}
|
|
345
355
|
|
|
346
356
|
_findHasManyByJsonApiResource(
|
|
347
|
-
resource,
|
|
357
|
+
resource: CollectionResourceRelationship,
|
|
348
358
|
parentIdentifier: StableRecordIdentifier,
|
|
349
359
|
relationship: ManyRelationship,
|
|
350
360
|
options: FindOptions = {}
|
|
@@ -354,12 +364,10 @@ export class LegacySupport {
|
|
|
354
364
|
return resolve();
|
|
355
365
|
}
|
|
356
366
|
const { definition, state } = relationship;
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
let { isStale, hasDematerializedInverse, hasReceivedData, isEmpty, shouldForceReload } = state;
|
|
367
|
+
const adapter = this.store.adapterFor(definition.type);
|
|
368
|
+
const { isStale, hasDematerializedInverse, hasReceivedData, isEmpty, shouldForceReload } = state;
|
|
360
369
|
const allInverseRecordsAreLoaded = areAllInverseRecordsLoaded(this.store, resource);
|
|
361
|
-
|
|
362
|
-
let shouldFindViaLink =
|
|
370
|
+
const shouldFindViaLink =
|
|
363
371
|
resource.links &&
|
|
364
372
|
resource.links.related &&
|
|
365
373
|
(typeof adapter.findHasMany === 'function' || typeof resource.data === 'undefined') &&
|
|
@@ -369,9 +377,9 @@ export class LegacySupport {
|
|
|
369
377
|
if (shouldFindViaLink) {
|
|
370
378
|
// findHasMany, although not public, does not need to care about our upgrade relationship definitions
|
|
371
379
|
// and can stick with the public definition API for now.
|
|
372
|
-
const relationshipMeta = this.store
|
|
373
|
-
|
|
374
|
-
|
|
380
|
+
const relationshipMeta = this.store
|
|
381
|
+
.getSchemaDefinitionService()
|
|
382
|
+
.relationshipsDefinitionFor({ type: definition.inverseType })[definition.key];
|
|
375
383
|
let adapter = this.store.adapterFor(parentIdentifier.type);
|
|
376
384
|
|
|
377
385
|
/*
|
|
@@ -394,16 +402,16 @@ export class LegacySupport {
|
|
|
394
402
|
typeof adapter.findHasMany === 'function'
|
|
395
403
|
);
|
|
396
404
|
|
|
397
|
-
return _findHasMany(adapter, this.store, parentIdentifier, resource.links
|
|
405
|
+
return _findHasMany(adapter, this.store, parentIdentifier, resource.links!.related, relationshipMeta, options);
|
|
398
406
|
}
|
|
399
407
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
let hasLocalPartialData =
|
|
408
|
+
const preferLocalCache = hasReceivedData && !isEmpty;
|
|
409
|
+
const hasLocalPartialData =
|
|
403
410
|
hasDematerializedInverse || (isEmpty && Array.isArray(resource.data) && resource.data.length > 0);
|
|
404
411
|
|
|
405
412
|
// fetch using data, pulling from local cache if possible
|
|
406
413
|
if (!shouldForceReload && !isStale && (preferLocalCache || hasLocalPartialData)) {
|
|
414
|
+
assert(`Expected collection to be an array`, Array.isArray(resource.data));
|
|
407
415
|
let finds = new Array(resource.data.length);
|
|
408
416
|
for (let i = 0; i < resource.data.length; i++) {
|
|
409
417
|
let identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource.data[i]);
|
|
@@ -417,6 +425,7 @@ export class LegacySupport {
|
|
|
417
425
|
|
|
418
426
|
// fetch by data
|
|
419
427
|
if (hasData || hasLocalPartialData) {
|
|
428
|
+
assert(`Expected collection to be an array`, Array.isArray(resource.data));
|
|
420
429
|
let identifiers = resource.data.map((json) => this.store.identifierCache.getOrCreateRecordIdentifier(json));
|
|
421
430
|
let fetches = new Array(identifiers.length);
|
|
422
431
|
const manager = this.store._fetchManager;
|
|
@@ -438,43 +447,44 @@ export class LegacySupport {
|
|
|
438
447
|
}
|
|
439
448
|
|
|
440
449
|
_findBelongsToByJsonApiResource(
|
|
441
|
-
resource,
|
|
450
|
+
resource: SingleResourceRelationship,
|
|
442
451
|
parentIdentifier: StableRecordIdentifier,
|
|
443
|
-
|
|
452
|
+
relationship: BelongsToRelationship,
|
|
444
453
|
options: FindOptions = {}
|
|
445
454
|
): Promise<StableRecordIdentifier | null> {
|
|
446
455
|
if (!resource) {
|
|
447
456
|
return resolve(null);
|
|
448
457
|
}
|
|
449
458
|
|
|
450
|
-
const
|
|
459
|
+
const identifier = resource.data ? this.store.identifierCache.getOrCreateRecordIdentifier(resource.data) : null;
|
|
451
460
|
|
|
452
|
-
let { isStale, hasDematerializedInverse, hasReceivedData, isEmpty, shouldForceReload } =
|
|
453
|
-
.state as RelationshipState;
|
|
454
|
-
const allInverseRecordsAreLoaded = areAllInverseRecordsLoaded(this.store, resource);
|
|
461
|
+
let { isStale, hasDematerializedInverse, hasReceivedData, isEmpty, shouldForceReload } = relationship.state;
|
|
455
462
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
if (internalModel) {
|
|
462
|
-
// short circuit if we are already loading
|
|
463
|
-
let pendingRequest = this.store._fetchManager.getPendingFetch(internalModel.identifier, options);
|
|
464
|
-
if (pendingRequest) {
|
|
465
|
-
return pendingRequest;
|
|
466
|
-
}
|
|
463
|
+
// short circuit if we are already loading
|
|
464
|
+
let pendingRequest = identifier && this.store._fetchManager.getPendingFetch(identifier, options);
|
|
465
|
+
if (pendingRequest) {
|
|
466
|
+
return pendingRequest;
|
|
467
467
|
}
|
|
468
468
|
|
|
469
|
+
const allInverseRecordsAreLoaded = areAllInverseRecordsLoaded(this.store, resource);
|
|
470
|
+
const shouldFindViaLink =
|
|
471
|
+
resource.links?.related &&
|
|
472
|
+
(shouldForceReload || hasDematerializedInverse || isStale || (!allInverseRecordsAreLoaded && !isEmpty));
|
|
473
|
+
|
|
469
474
|
// fetch via link
|
|
470
475
|
if (shouldFindViaLink) {
|
|
471
|
-
|
|
476
|
+
const relationshipMeta = this.store.getSchemaDefinitionService().relationshipsDefinitionFor(this.identifier)[
|
|
477
|
+
relationship.definition.key
|
|
478
|
+
];
|
|
479
|
+
assert(`Attempted to access a belongsTo relationship but no definition exists for it`, relationshipMeta);
|
|
480
|
+
|
|
481
|
+
return _findBelongsTo(this.store, parentIdentifier, resource.links!.related, relationshipMeta, options);
|
|
472
482
|
}
|
|
473
483
|
|
|
474
484
|
let preferLocalCache = hasReceivedData && allInverseRecordsAreLoaded && !isEmpty;
|
|
475
485
|
let hasLocalPartialData = hasDematerializedInverse || (isEmpty && resource.data);
|
|
476
486
|
// null is explicit empty, undefined is "we don't know anything"
|
|
477
|
-
|
|
487
|
+
const localDataIsEmpty = resource.data === undefined || resource.data === null;
|
|
478
488
|
|
|
479
489
|
// fetch using data, pulling from local cache if possible
|
|
480
490
|
if (!shouldForceReload && !isStale && (preferLocalCache || hasLocalPartialData)) {
|
|
@@ -485,22 +495,21 @@ export class LegacySupport {
|
|
|
485
495
|
return resolve(null);
|
|
486
496
|
}
|
|
487
497
|
|
|
488
|
-
if (!
|
|
489
|
-
assert(`No
|
|
498
|
+
if (!identifier) {
|
|
499
|
+
assert(`No Information found for ${resource.data!.lid}`, identifier);
|
|
490
500
|
}
|
|
491
501
|
|
|
492
|
-
return this.store._instanceCache._fetchDataIfNeededForIdentifier(
|
|
502
|
+
return this.store._instanceCache._fetchDataIfNeededForIdentifier(identifier, options);
|
|
493
503
|
}
|
|
494
504
|
|
|
495
|
-
let resourceIsLocal = !localDataIsEmpty && resource.data
|
|
505
|
+
let resourceIsLocal = !localDataIsEmpty && resource.data!.id === null;
|
|
496
506
|
|
|
497
|
-
if (
|
|
498
|
-
return resolve(
|
|
507
|
+
if (identifier && resourceIsLocal) {
|
|
508
|
+
return resolve(identifier);
|
|
499
509
|
}
|
|
500
510
|
|
|
501
511
|
// fetch by data
|
|
502
|
-
if (
|
|
503
|
-
let identifier = internalModel.identifier;
|
|
512
|
+
if (identifier && !localDataIsEmpty) {
|
|
504
513
|
assertIdentifierHasId(identifier);
|
|
505
514
|
|
|
506
515
|
return this.store._fetchManager.scheduleFetch(identifier, options);
|
|
@@ -512,32 +521,28 @@ export class LegacySupport {
|
|
|
512
521
|
}
|
|
513
522
|
|
|
514
523
|
destroy() {
|
|
515
|
-
assert(
|
|
516
|
-
'Cannot destroy an internalModel while its record is materialized',
|
|
517
|
-
!this.record || this.record.isDestroyed || this.record.isDestroying
|
|
518
|
-
);
|
|
519
524
|
this.isDestroying = true;
|
|
520
525
|
|
|
521
|
-
|
|
526
|
+
let cache: Dict<{ destroy(): void }> = this._manyArrayCache;
|
|
527
|
+
this._manyArrayCache = Object.create(null);
|
|
522
528
|
Object.keys(cache).forEach((key) => {
|
|
523
529
|
cache[key]!.destroy();
|
|
524
|
-
delete cache[key];
|
|
525
530
|
});
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
531
|
+
|
|
532
|
+
cache = this._relationshipProxyCache;
|
|
533
|
+
this._relationshipProxyCache = Object.create(null);
|
|
534
|
+
Object.keys(cache).forEach((key) => {
|
|
535
|
+
const proxy = cache[key]!;
|
|
529
536
|
if (proxy.destroy) {
|
|
530
537
|
proxy.destroy();
|
|
531
538
|
}
|
|
532
|
-
delete this._relationshipProxyCache[key];
|
|
533
539
|
});
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}
|
|
540
|
+
|
|
541
|
+
cache = this.references;
|
|
542
|
+
this.references = Object.create(null);
|
|
543
|
+
Object.keys(cache).forEach((key) => {
|
|
544
|
+
cache[key]!.destroy();
|
|
545
|
+
});
|
|
541
546
|
this.isDestroyed = true;
|
|
542
547
|
}
|
|
543
548
|
}
|
|
@@ -625,32 +630,39 @@ function assertRecordsPassedToHasMany(records: RecordInstance[]) {
|
|
|
625
630
|
.map((r) => `${typeof r}`)
|
|
626
631
|
.join(', ')}`,
|
|
627
632
|
(function () {
|
|
628
|
-
return records.every((record) =>
|
|
633
|
+
return records.every((record) => {
|
|
634
|
+
try {
|
|
635
|
+
recordIdentifierFor(record);
|
|
636
|
+
return true;
|
|
637
|
+
} catch {
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
});
|
|
629
641
|
})()
|
|
630
642
|
);
|
|
631
643
|
}
|
|
632
644
|
|
|
633
|
-
function
|
|
634
|
-
return records.map(
|
|
645
|
+
function extractIdentifiersFromRecords(records: RecordInstance[]): StableRecordIdentifier[] {
|
|
646
|
+
return records.map(extractIdentifierFromRecord) as StableRecordIdentifier[];
|
|
635
647
|
}
|
|
636
648
|
|
|
637
|
-
type PromiseProxyRecord = { then(): void;
|
|
649
|
+
type PromiseProxyRecord = { then(): void; content: RecordInstance | null | undefined };
|
|
638
650
|
|
|
639
|
-
function
|
|
651
|
+
function extractIdentifierFromRecord(recordOrPromiseRecord: PromiseProxyRecord | RecordInstance | null) {
|
|
640
652
|
if (!recordOrPromiseRecord) {
|
|
641
653
|
return null;
|
|
642
654
|
}
|
|
643
655
|
|
|
644
656
|
if (isPromiseRecord(recordOrPromiseRecord)) {
|
|
645
|
-
let content = recordOrPromiseRecord.
|
|
657
|
+
let content = recordOrPromiseRecord.content;
|
|
646
658
|
assert(
|
|
647
659
|
'You passed in a promise that did not originate from an EmberData relationship. You can only pass promises that come from a belongsTo or hasMany relationship to the get call.',
|
|
648
660
|
content !== undefined
|
|
649
661
|
);
|
|
650
|
-
return content ?
|
|
662
|
+
return content ? recordIdentifierFor(content) : null;
|
|
651
663
|
}
|
|
652
664
|
|
|
653
|
-
return
|
|
665
|
+
return recordIdentifierFor(recordOrPromiseRecord);
|
|
654
666
|
}
|
|
655
667
|
|
|
656
668
|
function isPromiseRecord(record: PromiseProxyRecord | RecordInstance): record is PromiseProxyRecord {
|
|
@@ -658,25 +670,16 @@ function isPromiseRecord(record: PromiseProxyRecord | RecordInstance): record is
|
|
|
658
670
|
}
|
|
659
671
|
|
|
660
672
|
function anyUnloaded(store: Store, relationship: ManyRelationship) {
|
|
661
|
-
let state = relationship.
|
|
673
|
+
let state = relationship.localState;
|
|
674
|
+
const cache = store._instanceCache;
|
|
662
675
|
const unloaded = state.find((s) => {
|
|
663
|
-
let
|
|
664
|
-
return
|
|
676
|
+
let isLoaded = cache.recordIsLoaded(s, true);
|
|
677
|
+
return !isLoaded;
|
|
665
678
|
});
|
|
666
679
|
|
|
667
680
|
return unloaded || false;
|
|
668
681
|
}
|
|
669
682
|
|
|
670
|
-
/**
|
|
671
|
-
* Flag indicating whether all inverse records are available
|
|
672
|
-
*
|
|
673
|
-
* true if the inverse exists and is loaded (not empty)
|
|
674
|
-
* true if there is no inverse
|
|
675
|
-
* false if the inverse exists and is not loaded (empty)
|
|
676
|
-
*
|
|
677
|
-
* @internal
|
|
678
|
-
* @return {boolean}
|
|
679
|
-
*/
|
|
680
683
|
function areAllInverseRecordsLoaded(store: Store, resource: JsonApiRelationship): boolean {
|
|
681
684
|
const cache = store.identifierCache;
|
|
682
685
|
|
|
@@ -684,7 +687,7 @@ function areAllInverseRecordsLoaded(store: Store, resource: JsonApiRelationship)
|
|
|
684
687
|
// treat as collection
|
|
685
688
|
// check for unloaded records
|
|
686
689
|
let hasEmptyRecords = resource.data.reduce((hasEmptyModel, resourceIdentifier) => {
|
|
687
|
-
return hasEmptyModel ||
|
|
690
|
+
return hasEmptyModel || isEmpty(store, cache, resourceIdentifier);
|
|
688
691
|
}, false);
|
|
689
692
|
|
|
690
693
|
return !hasEmptyRecords;
|
|
@@ -693,17 +696,19 @@ function areAllInverseRecordsLoaded(store: Store, resource: JsonApiRelationship)
|
|
|
693
696
|
if (!resource.data) {
|
|
694
697
|
return true;
|
|
695
698
|
} else {
|
|
696
|
-
|
|
697
|
-
return !internalModel.isEmpty;
|
|
699
|
+
return !isEmpty(store, cache, resource.data);
|
|
698
700
|
}
|
|
699
701
|
}
|
|
700
702
|
}
|
|
701
703
|
|
|
702
|
-
function
|
|
703
|
-
store: Store,
|
|
704
|
-
cache: IdentifierCache,
|
|
705
|
-
resource: ResourceIdentifierObject
|
|
706
|
-
): InternalModel {
|
|
704
|
+
function isEmpty(store: Store, cache: IdentifierCache, resource: ResourceIdentifierObject): boolean {
|
|
707
705
|
const identifier = cache.getOrCreateRecordIdentifier(resource);
|
|
708
|
-
|
|
706
|
+
const recordData = store._instanceCache.__instances.recordData.get(identifier);
|
|
707
|
+
return !recordData || recordData.isEmpty(identifier);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
function isBelongsTo(
|
|
711
|
+
relationship: BelongsToRelationship | ImplicitRelationship | ManyRelationship
|
|
712
|
+
): relationship is BelongsToRelationship {
|
|
713
|
+
return relationship.definition.kind === 'belongsTo';
|
|
709
714
|
}
|
|
@@ -9,14 +9,15 @@ import EmberObject, { get } from '@ember/object';
|
|
|
9
9
|
import { all } from 'rsvp';
|
|
10
10
|
|
|
11
11
|
import type Store from '@ember-data/store';
|
|
12
|
-
import { PromiseArray,
|
|
13
|
-
import type
|
|
14
|
-
import type
|
|
12
|
+
import { PromiseArray, recordIdentifierFor } from '@ember-data/store/-private';
|
|
13
|
+
import type ShimModelClass from '@ember-data/store/-private/legacy-model-support/shim-model-class';
|
|
14
|
+
import type { NonSingletonRecordDataManager } from '@ember-data/store/-private/managers/record-data-manager';
|
|
15
|
+
import type { CreateRecordProperties } from '@ember-data/store/-private/store-service';
|
|
15
16
|
import type { DSModelSchema } from '@ember-data/types/q/ds-model';
|
|
16
|
-
import type { Links, PaginationLinks } from '@ember-data/types/q/ember-data-json-api';
|
|
17
|
+
import type { CollectionResourceRelationship, Links, PaginationLinks } from '@ember-data/types/q/ember-data-json-api';
|
|
17
18
|
import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
19
|
+
import type { RecordData } from '@ember-data/types/q/record-data';
|
|
18
20
|
import type { RecordInstance } from '@ember-data/types/q/record-instance';
|
|
19
|
-
import type { RelationshipRecordData } from '@ember-data/types/q/relationship-record-data';
|
|
20
21
|
import type { FindOptions } from '@ember-data/types/q/store';
|
|
21
22
|
import type { Dict } from '@ember-data/types/q/utils';
|
|
22
23
|
|
|
@@ -32,7 +33,8 @@ const MutableArrayWithObject = EmberObject.extend(MutableArray) as unknown as ne
|
|
|
32
33
|
export interface ManyArrayCreateArgs {
|
|
33
34
|
store: Store;
|
|
34
35
|
type: ShimModelClass;
|
|
35
|
-
|
|
36
|
+
identifier: StableRecordIdentifier;
|
|
37
|
+
recordData: RecordData;
|
|
36
38
|
key: string;
|
|
37
39
|
isPolymorphic: boolean;
|
|
38
40
|
isAsync: boolean;
|
|
@@ -98,7 +100,8 @@ export default class ManyArray extends MutableArrayWithObject<StableRecordIdenti
|
|
|
98
100
|
declare _meta: Dict<unknown> | null;
|
|
99
101
|
declare _links: Links | PaginationLinks | null;
|
|
100
102
|
declare currentState: StableRecordIdentifier[];
|
|
101
|
-
declare
|
|
103
|
+
declare identifier: StableRecordIdentifier;
|
|
104
|
+
declare recordData: RecordData;
|
|
102
105
|
declare legacySupport: LegacySupport;
|
|
103
106
|
declare store: Store;
|
|
104
107
|
declare key: string;
|
|
@@ -272,25 +275,22 @@ export default class ManyArray extends MutableArrayWithObject<StableRecordIdenti
|
|
|
272
275
|
|
|
273
276
|
replace(idx: number, amt: number, objects?: RecordInstance[]) {
|
|
274
277
|
assert(`Cannot push mutations to the cache while updating the relationship from cache`, !this._isUpdating);
|
|
275
|
-
|
|
276
|
-
|
|
278
|
+
assert(
|
|
279
|
+
'The third argument to replace needs to be an array.',
|
|
280
|
+
!objects || Array.isArray(objects) || EmberArray.detect(objects)
|
|
281
|
+
);
|
|
282
|
+
const { store, identifier } = this;
|
|
283
|
+
store._join(() => {
|
|
277
284
|
let identifiers: StableRecordIdentifier[];
|
|
278
285
|
if (amt > 0) {
|
|
279
286
|
identifiers = this.currentState.slice(idx, idx + amt);
|
|
280
|
-
this.recordData.removeFromHasMany(
|
|
281
|
-
this.key,
|
|
282
|
-
// TODO RecordData V2: recordData should take identifiers not RecordDatas
|
|
283
|
-
identifiers.map((identifier) => store._instanceCache.getRecordData(identifier))
|
|
284
|
-
);
|
|
287
|
+
this.recordData.removeFromHasMany(identifier, this.key, identifiers);
|
|
285
288
|
}
|
|
286
|
-
if (objects) {
|
|
287
|
-
assert(
|
|
288
|
-
'The third argument to replace needs to be an array.',
|
|
289
|
-
Array.isArray(objects) || EmberArray.detect(objects)
|
|
290
|
-
);
|
|
289
|
+
if (objects && objects.length > 0) {
|
|
291
290
|
this.recordData.addToHasMany(
|
|
291
|
+
identifier,
|
|
292
292
|
this.key,
|
|
293
|
-
objects.map((obj: RecordInstance) =>
|
|
293
|
+
objects.map((obj: RecordInstance) => recordIdentifierFor(obj)),
|
|
294
294
|
idx
|
|
295
295
|
);
|
|
296
296
|
}
|
|
@@ -305,17 +305,23 @@ export default class ManyArray extends MutableArrayWithObject<StableRecordIdenti
|
|
|
305
305
|
}
|
|
306
306
|
this._isDirty = false;
|
|
307
307
|
this._isUpdating = true;
|
|
308
|
-
|
|
308
|
+
const identifier = this.identifier;
|
|
309
|
+
|
|
310
|
+
let jsonApi = (this.recordData as NonSingletonRecordDataManager).getRelationship(
|
|
311
|
+
identifier,
|
|
312
|
+
this.key,
|
|
313
|
+
true
|
|
314
|
+
) as CollectionResourceRelationship;
|
|
315
|
+
const cache = this.store._instanceCache;
|
|
316
|
+
const idCache = this.store.identifierCache;
|
|
309
317
|
|
|
310
318
|
let identifiers: StableRecordIdentifier[] = [];
|
|
311
319
|
if (jsonApi.data) {
|
|
312
320
|
for (let i = 0; i < jsonApi.data.length; i++) {
|
|
313
|
-
|
|
314
|
-
let im = this.store._instanceCache._internalModelForResource(jsonApi.data[i]);
|
|
315
|
-
let shouldRemove = im._isDematerializing || im.isEmpty || !im.isLoaded;
|
|
321
|
+
const identifier = idCache.getOrCreateRecordIdentifier(jsonApi.data[i]);
|
|
316
322
|
|
|
317
|
-
if (
|
|
318
|
-
identifiers.push(
|
|
323
|
+
if (cache.recordIsLoaded(identifier, true)) {
|
|
324
|
+
identifiers.push(identifier);
|
|
319
325
|
}
|
|
320
326
|
}
|
|
321
327
|
}
|
|
@@ -348,6 +354,12 @@ export default class ManyArray extends MutableArrayWithObject<StableRecordIdenti
|
|
|
348
354
|
this._isUpdating = false;
|
|
349
355
|
}
|
|
350
356
|
|
|
357
|
+
destroy() {
|
|
358
|
+
this._length = 0;
|
|
359
|
+
this.currentState = [];
|
|
360
|
+
return super.destroy();
|
|
361
|
+
}
|
|
362
|
+
|
|
351
363
|
/**
|
|
352
364
|
Reloads all of the records in the manyArray. If the manyArray
|
|
353
365
|
holds a relationship that was originally fetched using a links url
|