@ember-data/model 4.8.0-alpha.4 → 4.8.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addon/-private/belongs-to.js +4 -4
- package/addon/-private/deprecated-promise-proxy.ts +54 -0
- package/addon/-private/has-many.js +7 -5
- package/addon/-private/legacy-relationships-support.ts +182 -118
- package/addon/-private/many-array.ts +211 -249
- package/addon/-private/model.js +147 -73
- package/addon/-private/promise-belongs-to.ts +1 -1
- package/addon/-private/promise-many-array.ts +35 -13
- package/addon/-private/promise-proxy-base.js +4 -0
- package/addon/-private/record-state.ts +1 -1
- package/addon/-private/references/belongs-to.ts +35 -13
- package/addon/-private/references/has-many.ts +40 -17
- package/addon/-private/relationship-meta.ts +3 -1
- package/index.js +2 -0
- package/package.json +6 -6
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE,
|
|
10
10
|
} from '@ember-data/private-build-infra/deprecations';
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { lookupLegacySupport } from './model';
|
|
13
13
|
import { computedMacroWithOptionalParams } from './util';
|
|
14
14
|
|
|
15
15
|
function normalizeType(type) {
|
|
@@ -205,7 +205,7 @@ function belongsTo(modelName, options) {
|
|
|
205
205
|
if (this.isDestroying || this.isDestroyed) {
|
|
206
206
|
return null;
|
|
207
207
|
}
|
|
208
|
-
const support =
|
|
208
|
+
const support = lookupLegacySupport(this);
|
|
209
209
|
|
|
210
210
|
if (DEBUG) {
|
|
211
211
|
if (['currentState'].indexOf(key) !== -1) {
|
|
@@ -237,7 +237,7 @@ function belongsTo(modelName, options) {
|
|
|
237
237
|
return support.getBelongsTo(key);
|
|
238
238
|
},
|
|
239
239
|
set(key, value) {
|
|
240
|
-
const support =
|
|
240
|
+
const support = lookupLegacySupport(this);
|
|
241
241
|
if (DEBUG) {
|
|
242
242
|
if (['currentState'].indexOf(key) !== -1) {
|
|
243
243
|
throw new Error(
|
|
@@ -245,7 +245,7 @@ function belongsTo(modelName, options) {
|
|
|
245
245
|
);
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
|
-
this.store.
|
|
248
|
+
this.store._join(() => {
|
|
249
249
|
support.setDirtyBelongsTo(key, value);
|
|
250
250
|
});
|
|
251
251
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { deprecate } from '@ember/debug';
|
|
2
|
+
|
|
3
|
+
import { resolve } from 'rsvp';
|
|
4
|
+
|
|
5
|
+
import { PromiseObject } from './promise-proxy-base';
|
|
6
|
+
|
|
7
|
+
function promiseObject<T>(promise: Promise<T>): PromiseObject<T> {
|
|
8
|
+
return PromiseObject.create({
|
|
9
|
+
promise: resolve(promise),
|
|
10
|
+
}) as PromiseObject<T>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// constructor is accessed in some internals but not including it in the copyright for the deprecation
|
|
14
|
+
const ALLOWABLE_METHODS = ['constructor', 'then', 'catch', 'finally'];
|
|
15
|
+
const PROXIED_OBJECT_PROPS = ['content', 'isPending', 'isSettled', 'isRejected', 'isFulfilled', 'promise', 'reason'];
|
|
16
|
+
|
|
17
|
+
export function deprecatedPromiseObject<T>(promise: Promise<T>): PromiseObject<T> {
|
|
18
|
+
const promiseObjectProxy: PromiseObject<T> = promiseObject(promise);
|
|
19
|
+
const handler = {
|
|
20
|
+
get(target: object, prop: string, receiver?: object): unknown {
|
|
21
|
+
if (typeof prop === 'symbol') {
|
|
22
|
+
return Reflect.get(target, prop, receiver);
|
|
23
|
+
}
|
|
24
|
+
if (!ALLOWABLE_METHODS.includes(prop)) {
|
|
25
|
+
deprecate(
|
|
26
|
+
`Accessing ${prop} is deprecated. The return type is being changed fomr PromiseObjectProxy to a Promise. The only available methods to access on this promise are .then, .catch and .finally`,
|
|
27
|
+
false,
|
|
28
|
+
{
|
|
29
|
+
id: 'ember-data:model-save-promise',
|
|
30
|
+
until: '5.0',
|
|
31
|
+
for: '@ember-data/store',
|
|
32
|
+
since: {
|
|
33
|
+
available: '4.4',
|
|
34
|
+
enabled: '4.4',
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const value: unknown = target[prop];
|
|
41
|
+
if (value && typeof value === 'function' && typeof value.bind === 'function') {
|
|
42
|
+
return value.bind(target);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (PROXIED_OBJECT_PROPS.includes(prop)) {
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return undefined;
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
return new Proxy(promiseObjectProxy, handler);
|
|
54
|
+
}
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE,
|
|
16
16
|
} from '@ember-data/private-build-infra/deprecations';
|
|
17
17
|
|
|
18
|
-
import {
|
|
18
|
+
import { lookupLegacySupport } from './model';
|
|
19
19
|
import { computedMacroWithOptionalParams } from './util';
|
|
20
20
|
|
|
21
21
|
function normalizeType(type) {
|
|
@@ -249,7 +249,7 @@ function hasMany(type, options) {
|
|
|
249
249
|
if (this.isDestroying || this.isDestroyed) {
|
|
250
250
|
return A();
|
|
251
251
|
}
|
|
252
|
-
return
|
|
252
|
+
return lookupLegacySupport(this).getHasMany(key);
|
|
253
253
|
},
|
|
254
254
|
set(key, records) {
|
|
255
255
|
if (DEBUG) {
|
|
@@ -259,9 +259,11 @@ function hasMany(type, options) {
|
|
|
259
259
|
);
|
|
260
260
|
}
|
|
261
261
|
}
|
|
262
|
-
const support =
|
|
263
|
-
|
|
264
|
-
|
|
262
|
+
const support = lookupLegacySupport(this);
|
|
263
|
+
const manyArray = support.getManyArray(key);
|
|
264
|
+
assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
|
|
265
|
+
this.store._join(() => {
|
|
266
|
+
manyArray.splice(0, manyArray.length, ...records);
|
|
265
267
|
});
|
|
266
268
|
|
|
267
269
|
return support.getHasMany(key);
|
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
import { assert } from '@ember/debug';
|
|
1
|
+
import { assert, deprecate } from '@ember/debug';
|
|
2
2
|
import { DEBUG } from '@glimmer/env';
|
|
3
3
|
|
|
4
4
|
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
|
|
8
|
+
import { DEPRECATE_PROMISE_PROXIES } from '@ember-data/private-build-infra/deprecations';
|
|
9
9
|
import type { UpgradedMeta } from '@ember-data/record-data/-private/graph/-edge-definition';
|
|
10
|
-
import
|
|
10
|
+
import type { LocalRelationshipOperation } from '@ember-data/record-data/-private/graph/-operations';
|
|
11
|
+
import type { ImplicitRelationship } from '@ember-data/record-data/-private/graph/index';
|
|
12
|
+
import type BelongsToRelationship from '@ember-data/record-data/-private/relationships/state/belongs-to';
|
|
13
|
+
import type ManyRelationship from '@ember-data/record-data/-private/relationships/state/has-many';
|
|
11
14
|
import type Store from '@ember-data/store';
|
|
12
|
-
import { recordIdentifierFor, storeFor } from '@ember-data/store/-private';
|
|
13
|
-
import {
|
|
15
|
+
import { fastPush, isStableIdentifier, recordIdentifierFor, SOURCE, storeFor } from '@ember-data/store/-private';
|
|
16
|
+
import type { NonSingletonRecordDataManager } from '@ember-data/store/-private/managers/record-data-manager';
|
|
14
17
|
import type { DSModel } from '@ember-data/types/q/ds-model';
|
|
15
|
-
import {
|
|
16
|
-
CollectionResourceRelationship,
|
|
17
|
-
ResourceIdentifierObject,
|
|
18
|
-
SingleResourceRelationship,
|
|
19
|
-
} from '@ember-data/types/q/ember-data-json-api';
|
|
18
|
+
import { CollectionResourceRelationship, SingleResourceRelationship } from '@ember-data/types/q/ember-data-json-api';
|
|
20
19
|
import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
21
20
|
import type { RecordData } from '@ember-data/types/q/record-data';
|
|
22
21
|
import type { JsonApiRelationship } from '@ember-data/types/q/record-data-json-api';
|
|
@@ -26,8 +25,7 @@ import type { Dict } from '@ember-data/types/q/utils';
|
|
|
26
25
|
|
|
27
26
|
import { _findBelongsTo, _findHasMany } from './legacy-data-fetch';
|
|
28
27
|
import { assertIdentifierHasId } from './legacy-data-utils';
|
|
29
|
-
import
|
|
30
|
-
import ManyArray from './many-array';
|
|
28
|
+
import RelatedCollection from './many-array';
|
|
31
29
|
import type { BelongsToProxyCreateArgs, BelongsToProxyMeta } from './promise-belongs-to';
|
|
32
30
|
import PromiseBelongsTo from './promise-belongs-to';
|
|
33
31
|
import type { HasManyProxyCreateArgs } from './promise-many-array';
|
|
@@ -35,7 +33,6 @@ import PromiseManyArray from './promise-many-array';
|
|
|
35
33
|
import BelongsToReference from './references/belongs-to';
|
|
36
34
|
import HasManyReference from './references/has-many';
|
|
37
35
|
|
|
38
|
-
type ManyArrayFactory = { create(args: ManyArrayCreateArgs): ManyArray };
|
|
39
36
|
type PromiseBelongsToFactory = { create(args: BelongsToProxyCreateArgs): PromiseBelongsTo };
|
|
40
37
|
|
|
41
38
|
export class LegacySupport {
|
|
@@ -44,8 +41,8 @@ export class LegacySupport {
|
|
|
44
41
|
declare recordData: RecordData;
|
|
45
42
|
declare references: Dict<BelongsToReference | HasManyReference>;
|
|
46
43
|
declare identifier: StableRecordIdentifier;
|
|
47
|
-
declare _manyArrayCache: Dict<
|
|
48
|
-
declare _relationshipPromisesCache: Dict<Promise<
|
|
44
|
+
declare _manyArrayCache: Dict<RelatedCollection>;
|
|
45
|
+
declare _relationshipPromisesCache: Dict<Promise<RelatedCollection | RecordInstance>>;
|
|
49
46
|
declare _relationshipProxyCache: Dict<PromiseManyArray | PromiseBelongsTo>;
|
|
50
47
|
|
|
51
48
|
declare isDestroying: boolean;
|
|
@@ -57,12 +54,38 @@ export class LegacySupport {
|
|
|
57
54
|
this.identifier = recordIdentifierFor(record);
|
|
58
55
|
this.recordData = this.store._instanceCache.getRecordData(this.identifier);
|
|
59
56
|
|
|
60
|
-
this._manyArrayCache = Object.create(null) as Dict<
|
|
61
|
-
this._relationshipPromisesCache = Object.create(null) as Dict<Promise<
|
|
57
|
+
this._manyArrayCache = Object.create(null) as Dict<RelatedCollection>;
|
|
58
|
+
this._relationshipPromisesCache = Object.create(null) as Dict<Promise<RelatedCollection | RecordInstance>>;
|
|
62
59
|
this._relationshipProxyCache = Object.create(null) as Dict<PromiseManyArray | PromiseBelongsTo>;
|
|
63
60
|
this.references = Object.create(null) as Dict<BelongsToReference>;
|
|
64
61
|
}
|
|
65
62
|
|
|
63
|
+
_syncArray(array: RelatedCollection) {
|
|
64
|
+
// It’s possible the parent side of the relationship may have been destroyed by this point
|
|
65
|
+
if (this.isDestroyed || this.isDestroying) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const currentState = array[SOURCE];
|
|
69
|
+
const identifier = this.identifier;
|
|
70
|
+
|
|
71
|
+
let [identifiers, jsonApi] = this._getCurrentState(identifier, array.key);
|
|
72
|
+
|
|
73
|
+
if (jsonApi.meta) {
|
|
74
|
+
array.meta = jsonApi.meta;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (jsonApi.links) {
|
|
78
|
+
array.links = jsonApi.links;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
currentState.length = 0;
|
|
82
|
+
fastPush(currentState, identifiers);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
updateCache(operation: LocalRelationshipOperation): void {
|
|
86
|
+
this.recordData.update(operation);
|
|
87
|
+
}
|
|
88
|
+
|
|
66
89
|
_findBelongsTo(
|
|
67
90
|
key: string,
|
|
68
91
|
resource: SingleResourceRelationship,
|
|
@@ -103,8 +126,8 @@ export class LegacySupport {
|
|
|
103
126
|
getBelongsTo(key: string, options?: FindOptions): PromiseBelongsTo | RecordInstance | null {
|
|
104
127
|
const { identifier, recordData } = this;
|
|
105
128
|
let resource = recordData.getRelationship(this.identifier, key) as SingleResourceRelationship;
|
|
106
|
-
let relatedIdentifier =
|
|
107
|
-
|
|
129
|
+
let relatedIdentifier = resource && resource.data ? resource.data : null;
|
|
130
|
+
assert(`Expected a stable identifier`, !relatedIdentifier || isStableIdentifier(relatedIdentifier));
|
|
108
131
|
|
|
109
132
|
const store = this.store;
|
|
110
133
|
const graphFor = (
|
|
@@ -127,10 +150,11 @@ export class LegacySupport {
|
|
|
127
150
|
}
|
|
128
151
|
|
|
129
152
|
let promise = this._findBelongsTo(key, resource, relationship, options);
|
|
153
|
+
const isLoaded = relatedIdentifier && store._instanceCache.recordIsLoaded(relatedIdentifier);
|
|
130
154
|
|
|
131
155
|
return this._updatePromiseProxyFor('belongsTo', key, {
|
|
132
156
|
promise,
|
|
133
|
-
content:
|
|
157
|
+
content: isLoaded ? store._instanceCache.getRecord(relatedIdentifier!) : null,
|
|
134
158
|
_belongsToState,
|
|
135
159
|
});
|
|
136
160
|
} else {
|
|
@@ -150,53 +174,100 @@ export class LegacySupport {
|
|
|
150
174
|
}
|
|
151
175
|
|
|
152
176
|
setDirtyBelongsTo(key: string, value: RecordInstance | null) {
|
|
153
|
-
return this.recordData.
|
|
177
|
+
return this.recordData.update(
|
|
178
|
+
{
|
|
179
|
+
op: 'replaceRelatedRecord',
|
|
180
|
+
record: this.identifier,
|
|
181
|
+
field: key,
|
|
182
|
+
value: extractIdentifierFromRecord(value),
|
|
183
|
+
},
|
|
184
|
+
// @ts-expect-error
|
|
185
|
+
true
|
|
186
|
+
);
|
|
154
187
|
}
|
|
155
188
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
189
|
+
_getCurrentState(
|
|
190
|
+
identifier: StableRecordIdentifier,
|
|
191
|
+
field: string
|
|
192
|
+
): [StableRecordIdentifier[], CollectionResourceRelationship] {
|
|
193
|
+
let jsonApi = (this.recordData as NonSingletonRecordDataManager).getRelationship(
|
|
194
|
+
identifier,
|
|
195
|
+
field,
|
|
196
|
+
true
|
|
197
|
+
) as CollectionResourceRelationship;
|
|
198
|
+
const cache = this.store._instanceCache;
|
|
199
|
+
let identifiers: StableRecordIdentifier[] = [];
|
|
200
|
+
if (jsonApi.data) {
|
|
201
|
+
for (let i = 0; i < jsonApi.data.length; i++) {
|
|
202
|
+
const identifier = jsonApi.data[i];
|
|
203
|
+
assert(`Expected a stable identifier`, isStableIdentifier(identifier));
|
|
204
|
+
if (cache.recordIsLoaded(identifier, true)) {
|
|
205
|
+
identifiers.push(identifier);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
164
208
|
}
|
|
165
209
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
});
|
|
179
|
-
this._manyArrayCache[key] = manyArray;
|
|
180
|
-
}
|
|
210
|
+
return [identifiers, jsonApi];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
getManyArray(key: string, definition?: UpgradedMeta): RelatedCollection {
|
|
214
|
+
if (HAS_RECORD_DATA_PACKAGE) {
|
|
215
|
+
let manyArray: RelatedCollection | undefined = this._manyArrayCache[key];
|
|
216
|
+
if (!definition) {
|
|
217
|
+
const graphFor = (
|
|
218
|
+
importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
|
|
219
|
+
).graphFor;
|
|
220
|
+
definition = graphFor(this.store).get(this.identifier, key).definition;
|
|
221
|
+
}
|
|
181
222
|
|
|
182
|
-
|
|
223
|
+
if (!manyArray) {
|
|
224
|
+
const [identifiers, doc] = this._getCurrentState(this.identifier, key);
|
|
225
|
+
|
|
226
|
+
manyArray = new RelatedCollection({
|
|
227
|
+
store: this.store,
|
|
228
|
+
type: definition.type,
|
|
229
|
+
identifier: this.identifier,
|
|
230
|
+
recordData: this.recordData,
|
|
231
|
+
identifiers,
|
|
232
|
+
key,
|
|
233
|
+
meta: doc.meta || null,
|
|
234
|
+
links: doc.links || null,
|
|
235
|
+
isPolymorphic: definition.isPolymorphic,
|
|
236
|
+
isAsync: definition.isAsync,
|
|
237
|
+
_inverseIsAsync: definition.inverseIsAsync,
|
|
238
|
+
manager: this,
|
|
239
|
+
isLoaded: !definition.isAsync,
|
|
240
|
+
allowMutation: true,
|
|
241
|
+
});
|
|
242
|
+
this._manyArrayCache[key] = manyArray;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return manyArray;
|
|
246
|
+
}
|
|
247
|
+
assert('hasMany only works with the @ember-data/record-data package');
|
|
183
248
|
}
|
|
184
249
|
|
|
185
250
|
fetchAsyncHasMany(
|
|
186
251
|
key: string,
|
|
187
252
|
relationship: ManyRelationship,
|
|
188
|
-
manyArray:
|
|
253
|
+
manyArray: RelatedCollection,
|
|
189
254
|
options?: FindOptions
|
|
190
|
-
): Promise<
|
|
255
|
+
): Promise<RelatedCollection> {
|
|
191
256
|
if (HAS_RECORD_DATA_PACKAGE) {
|
|
192
|
-
let loadingPromise = this._relationshipPromisesCache[key] as Promise<
|
|
257
|
+
let loadingPromise = this._relationshipPromisesCache[key] as Promise<RelatedCollection> | undefined;
|
|
193
258
|
if (loadingPromise) {
|
|
194
259
|
return loadingPromise;
|
|
195
260
|
}
|
|
196
261
|
|
|
197
262
|
const jsonApi = this.recordData.getRelationship(this.identifier, key) as CollectionResourceRelationship;
|
|
263
|
+
const promise = this._findHasManyByJsonApiResource(jsonApi, this.identifier, relationship, options);
|
|
198
264
|
|
|
199
|
-
|
|
265
|
+
if (!promise) {
|
|
266
|
+
manyArray.isLoaded = true;
|
|
267
|
+
return resolve(manyArray);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
loadingPromise = promise.then(
|
|
200
271
|
() => handleCompletedRelationshipRequest(this, key, relationship, manyArray),
|
|
201
272
|
(e: Error) => handleCompletedRelationshipRequest(this, key, relationship, manyArray, e)
|
|
202
273
|
);
|
|
@@ -232,7 +303,7 @@ export class LegacySupport {
|
|
|
232
303
|
assert(`hasMany only works with the @ember-data/record-data package`);
|
|
233
304
|
}
|
|
234
305
|
|
|
235
|
-
getHasMany(key: string, options?: FindOptions): PromiseManyArray |
|
|
306
|
+
getHasMany(key: string, options?: FindOptions): PromiseManyArray | RelatedCollection {
|
|
236
307
|
if (HAS_RECORD_DATA_PACKAGE) {
|
|
237
308
|
const graphFor = (
|
|
238
309
|
importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
|
|
@@ -263,11 +334,6 @@ export class LegacySupport {
|
|
|
263
334
|
assert(`hasMany only works with the @ember-data/record-data package`);
|
|
264
335
|
}
|
|
265
336
|
|
|
266
|
-
setDirtyHasMany(key: string, records: RecordInstance[]) {
|
|
267
|
-
assertRecordsPassedToHasMany(records);
|
|
268
|
-
return this.recordData.setHasMany(this.identifier, key, extractIdentifiersFromRecords(records));
|
|
269
|
-
}
|
|
270
|
-
|
|
271
337
|
_updatePromiseProxyFor(kind: 'hasMany', key: string, args: HasManyProxyCreateArgs): PromiseManyArray;
|
|
272
338
|
_updatePromiseProxyFor(kind: 'belongsTo', key: string, args: BelongsToProxyCreateArgs): PromiseBelongsTo;
|
|
273
339
|
_updatePromiseProxyFor(
|
|
@@ -320,7 +386,8 @@ export class LegacySupport {
|
|
|
320
386
|
const graphFor = (
|
|
321
387
|
importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
|
|
322
388
|
).graphFor;
|
|
323
|
-
const
|
|
389
|
+
const graph = graphFor(this.store);
|
|
390
|
+
const relationship = graph.get(this.identifier, name);
|
|
324
391
|
|
|
325
392
|
if (DEBUG && kind) {
|
|
326
393
|
let modelName = this.identifier.type;
|
|
@@ -334,9 +401,15 @@ export class LegacySupport {
|
|
|
334
401
|
let relationshipKind = relationship.definition.kind;
|
|
335
402
|
|
|
336
403
|
if (relationshipKind === 'belongsTo') {
|
|
337
|
-
reference = new BelongsToReference(
|
|
404
|
+
reference = new BelongsToReference(
|
|
405
|
+
this.store,
|
|
406
|
+
graph,
|
|
407
|
+
this.identifier,
|
|
408
|
+
relationship as BelongsToRelationship,
|
|
409
|
+
name
|
|
410
|
+
);
|
|
338
411
|
} else if (relationshipKind === 'hasMany') {
|
|
339
|
-
reference = new HasManyReference(this.store, this.identifier, relationship as ManyRelationship, name);
|
|
412
|
+
reference = new HasManyReference(this.store, graph, this.identifier, relationship as ManyRelationship, name);
|
|
340
413
|
}
|
|
341
414
|
|
|
342
415
|
this.references[name] = reference;
|
|
@@ -350,10 +423,10 @@ export class LegacySupport {
|
|
|
350
423
|
parentIdentifier: StableRecordIdentifier,
|
|
351
424
|
relationship: ManyRelationship,
|
|
352
425
|
options: FindOptions = {}
|
|
353
|
-
): Promise<void | unknown[]> {
|
|
426
|
+
): Promise<void | unknown[]> | void {
|
|
354
427
|
if (HAS_RECORD_DATA_PACKAGE) {
|
|
355
428
|
if (!resource) {
|
|
356
|
-
return
|
|
429
|
+
return;
|
|
357
430
|
}
|
|
358
431
|
const { definition, state } = relationship;
|
|
359
432
|
const adapter = this.store.adapterFor(definition.type);
|
|
@@ -403,11 +476,19 @@ export class LegacySupport {
|
|
|
403
476
|
|
|
404
477
|
// fetch using data, pulling from local cache if possible
|
|
405
478
|
if (!shouldForceReload && !isStale && (preferLocalCache || hasLocalPartialData)) {
|
|
479
|
+
if (allInverseRecordsAreLoaded) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
406
482
|
assert(`Expected collection to be an array`, Array.isArray(resource.data));
|
|
483
|
+
if (allInverseRecordsAreLoaded) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
407
486
|
let finds = new Array(resource.data.length);
|
|
487
|
+
let cache = this.store._instanceCache;
|
|
408
488
|
for (let i = 0; i < resource.data.length; i++) {
|
|
409
|
-
|
|
410
|
-
|
|
489
|
+
const identifier = resource.data[i];
|
|
490
|
+
assert(`expected a stable identifier`, isStableIdentifier(identifier));
|
|
491
|
+
finds[i] = cache._fetchDataIfNeededForIdentifier(identifier, options);
|
|
411
492
|
}
|
|
412
493
|
|
|
413
494
|
return all(finds);
|
|
@@ -417,8 +498,9 @@ export class LegacySupport {
|
|
|
417
498
|
|
|
418
499
|
// fetch by data
|
|
419
500
|
if (hasData || hasLocalPartialData) {
|
|
420
|
-
|
|
421
|
-
|
|
501
|
+
const identifiers = resource.data;
|
|
502
|
+
assert(`Expected collection to be an array`, Array.isArray(identifiers));
|
|
503
|
+
assert(`Expected stable identifiers`, identifiers.every(isStableIdentifier));
|
|
422
504
|
let fetches = new Array(identifiers.length);
|
|
423
505
|
const manager = this.store._fetchManager;
|
|
424
506
|
|
|
@@ -433,7 +515,7 @@ export class LegacySupport {
|
|
|
433
515
|
|
|
434
516
|
// we were explicitly told we have no data and no links.
|
|
435
517
|
// TODO if the relationshipIsStale, should we hit the adapter anyway?
|
|
436
|
-
return
|
|
518
|
+
return;
|
|
437
519
|
}
|
|
438
520
|
assert(`hasMany only works with the @ember-data/record-data package`);
|
|
439
521
|
}
|
|
@@ -448,7 +530,8 @@ export class LegacySupport {
|
|
|
448
530
|
return resolve(null);
|
|
449
531
|
}
|
|
450
532
|
|
|
451
|
-
const identifier = resource.data ?
|
|
533
|
+
const identifier = resource.data ? resource.data : null;
|
|
534
|
+
assert(`Expected a stable identifier`, !identifier || isStableIdentifier(identifier));
|
|
452
535
|
|
|
453
536
|
let { isStale, hasDematerializedInverse, hasReceivedData, isEmpty, shouldForceReload } = relationship.state;
|
|
454
537
|
|
|
@@ -549,8 +632,8 @@ function handleCompletedRelationshipRequest(
|
|
|
549
632
|
recordExt: LegacySupport,
|
|
550
633
|
key: string,
|
|
551
634
|
relationship: ManyRelationship,
|
|
552
|
-
value:
|
|
553
|
-
):
|
|
635
|
+
value: RelatedCollection
|
|
636
|
+
): RelatedCollection;
|
|
554
637
|
function handleCompletedRelationshipRequest(
|
|
555
638
|
recordExt: LegacySupport,
|
|
556
639
|
key: string,
|
|
@@ -562,16 +645,16 @@ function handleCompletedRelationshipRequest(
|
|
|
562
645
|
recordExt: LegacySupport,
|
|
563
646
|
key: string,
|
|
564
647
|
relationship: ManyRelationship,
|
|
565
|
-
value:
|
|
648
|
+
value: RelatedCollection,
|
|
566
649
|
error: Error
|
|
567
650
|
): never;
|
|
568
651
|
function handleCompletedRelationshipRequest(
|
|
569
652
|
recordExt: LegacySupport,
|
|
570
653
|
key: string,
|
|
571
654
|
relationship: BelongsToRelationship | ManyRelationship,
|
|
572
|
-
value:
|
|
655
|
+
value: RelatedCollection | StableRecordIdentifier | null,
|
|
573
656
|
error?: Error
|
|
574
|
-
):
|
|
657
|
+
): RelatedCollection | RecordInstance | null {
|
|
575
658
|
delete recordExt._relationshipPromisesCache[key];
|
|
576
659
|
relationship.state.shouldForceReload = false;
|
|
577
660
|
const isHasMany = relationship.definition.kind === 'hasMany';
|
|
@@ -579,7 +662,7 @@ function handleCompletedRelationshipRequest(
|
|
|
579
662
|
if (isHasMany) {
|
|
580
663
|
// we don't notify the record property here to avoid refetch
|
|
581
664
|
// only the many array
|
|
582
|
-
(value as
|
|
665
|
+
(value as RelatedCollection).notify();
|
|
583
666
|
}
|
|
584
667
|
|
|
585
668
|
if (error) {
|
|
@@ -603,7 +686,7 @@ function handleCompletedRelationshipRequest(
|
|
|
603
686
|
}
|
|
604
687
|
|
|
605
688
|
if (isHasMany) {
|
|
606
|
-
(value as
|
|
689
|
+
(value as RelatedCollection).isLoaded = true;
|
|
607
690
|
}
|
|
608
691
|
|
|
609
692
|
relationship.state.hasFailedLoadAttempt = false;
|
|
@@ -611,33 +694,10 @@ function handleCompletedRelationshipRequest(
|
|
|
611
694
|
relationship.state.isStale = false;
|
|
612
695
|
|
|
613
696
|
return isHasMany || !value
|
|
614
|
-
? (value as
|
|
697
|
+
? (value as RelatedCollection | null)
|
|
615
698
|
: recordExt.store.peekRecord(value as StableRecordIdentifier);
|
|
616
699
|
}
|
|
617
700
|
|
|
618
|
-
function assertRecordsPassedToHasMany(records: RecordInstance[]) {
|
|
619
|
-
assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
|
|
620
|
-
assert(
|
|
621
|
-
`All elements of a hasMany relationship must be instances of Model, you passed ${records
|
|
622
|
-
.map((r) => `${typeof r}`)
|
|
623
|
-
.join(', ')}`,
|
|
624
|
-
(function () {
|
|
625
|
-
return records.every((record) => {
|
|
626
|
-
try {
|
|
627
|
-
recordIdentifierFor(record);
|
|
628
|
-
return true;
|
|
629
|
-
} catch {
|
|
630
|
-
return false;
|
|
631
|
-
}
|
|
632
|
-
});
|
|
633
|
-
})()
|
|
634
|
-
);
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
function extractIdentifiersFromRecords(records: RecordInstance[]): StableRecordIdentifier[] {
|
|
638
|
-
return records.map(extractIdentifierFromRecord) as StableRecordIdentifier[];
|
|
639
|
-
}
|
|
640
|
-
|
|
641
701
|
type PromiseProxyRecord = { then(): void; content: RecordInstance | null | undefined };
|
|
642
702
|
|
|
643
703
|
function extractIdentifierFromRecord(recordOrPromiseRecord: PromiseProxyRecord | RecordInstance | null) {
|
|
@@ -645,12 +705,25 @@ function extractIdentifierFromRecord(recordOrPromiseRecord: PromiseProxyRecord |
|
|
|
645
705
|
return null;
|
|
646
706
|
}
|
|
647
707
|
|
|
648
|
-
if (isPromiseRecord(recordOrPromiseRecord)) {
|
|
708
|
+
if (DEPRECATE_PROMISE_PROXIES && isPromiseRecord(recordOrPromiseRecord)) {
|
|
649
709
|
let content = recordOrPromiseRecord.content;
|
|
650
710
|
assert(
|
|
651
711
|
'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.',
|
|
652
712
|
content !== undefined
|
|
653
713
|
);
|
|
714
|
+
deprecate(
|
|
715
|
+
`You passed in a PromiseProxy to a Relationship API that now expects a resolved value. await the value before setting it.`,
|
|
716
|
+
false,
|
|
717
|
+
{
|
|
718
|
+
id: 'ember-data:deprecate-promise-proxies',
|
|
719
|
+
until: '5.0',
|
|
720
|
+
since: {
|
|
721
|
+
enabled: '4.8',
|
|
722
|
+
available: '4.8',
|
|
723
|
+
},
|
|
724
|
+
for: 'ember-data',
|
|
725
|
+
}
|
|
726
|
+
);
|
|
654
727
|
return content ? recordIdentifierFor(content) : null;
|
|
655
728
|
}
|
|
656
729
|
|
|
@@ -662,7 +735,7 @@ function isPromiseRecord(record: PromiseProxyRecord | RecordInstance): record is
|
|
|
662
735
|
}
|
|
663
736
|
|
|
664
737
|
function anyUnloaded(store: Store, relationship: ManyRelationship) {
|
|
665
|
-
let state = relationship.
|
|
738
|
+
let state = relationship.localState;
|
|
666
739
|
const cache = store._instanceCache;
|
|
667
740
|
const unloaded = state.find((s) => {
|
|
668
741
|
let isLoaded = cache.recordIsLoaded(s, true);
|
|
@@ -673,30 +746,21 @@ function anyUnloaded(store: Store, relationship: ManyRelationship) {
|
|
|
673
746
|
}
|
|
674
747
|
|
|
675
748
|
function areAllInverseRecordsLoaded(store: Store, resource: JsonApiRelationship): boolean {
|
|
676
|
-
const
|
|
749
|
+
const instanceCache = store._instanceCache;
|
|
750
|
+
const identifiers = resource.data;
|
|
677
751
|
|
|
678
|
-
if (Array.isArray(
|
|
752
|
+
if (Array.isArray(identifiers)) {
|
|
753
|
+
assert(`Expected stable identifiers`, identifiers.every(isStableIdentifier));
|
|
679
754
|
// treat as collection
|
|
680
755
|
// check for unloaded records
|
|
681
|
-
|
|
682
|
-
return hasEmptyModel || isEmpty(store, cache, resourceIdentifier);
|
|
683
|
-
}, false);
|
|
684
|
-
|
|
685
|
-
return !hasEmptyRecords;
|
|
686
|
-
} else {
|
|
687
|
-
// treat as single resource
|
|
688
|
-
if (!resource.data) {
|
|
689
|
-
return true;
|
|
690
|
-
} else {
|
|
691
|
-
return !isEmpty(store, cache, resource.data);
|
|
692
|
-
}
|
|
756
|
+
return identifiers.every((identifier: StableRecordIdentifier) => instanceCache.recordIsLoaded(identifier));
|
|
693
757
|
}
|
|
694
|
-
}
|
|
695
758
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
759
|
+
// treat as single resource
|
|
760
|
+
if (!identifiers) return true;
|
|
761
|
+
|
|
762
|
+
assert(`Expected stable identifiers`, isStableIdentifier(identifiers));
|
|
763
|
+
return instanceCache.recordIsLoaded(identifiers);
|
|
700
764
|
}
|
|
701
765
|
|
|
702
766
|
function isBelongsTo(
|