@ember-data/model 4.9.0-alpha.6 → 4.9.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/LICENSE.md +3 -1
- package/addon/-private/belongs-to.js +58 -47
- package/addon/-private/debug/assert-polymorphic-type.js +71 -0
- package/addon/-private/deprecated-promise-proxy.ts +29 -7
- package/addon/-private/has-many.js +53 -47
- package/addon/-private/legacy-data-fetch.js +36 -33
- package/addon/-private/legacy-relationships-support.ts +31 -27
- package/addon/-private/many-array.ts +23 -21
- package/addon/-private/model.js +55 -48
- package/addon/-private/record-state.ts +6 -7
- package/addon/-private/references/belongs-to.ts +39 -27
- package/addon/-private/references/has-many.ts +37 -20
- package/addon/-private/relationship-meta.ts +4 -2
- package/addon/index.ts +0 -1
- package/blueprints/model-test/mocha-files/__root__/__path__/__test__.js +4 -3
- package/blueprints/model-test/mocha-rfc-232-files/__root__/__path__/__test__.js +3 -2
- package/blueprints/model-test/qunit-files/__root__/__path__/__test__.js +3 -2
- package/index.js +1 -1
- package/package.json +34 -44
- package/config/environment.js +0 -5
package/LICENSE.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
The MIT License (MIT)
|
|
2
2
|
|
|
3
|
-
Copyright (
|
|
3
|
+
Copyright (C) 2017-2022 Ember.js contributors
|
|
4
|
+
Portions Copyright (C) 2011-2017 Tilde, Inc. and contributors.
|
|
5
|
+
Portions Copyright (C) 2011 LivingSocial Inc.
|
|
4
6
|
|
|
5
7
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
8
|
|
|
@@ -13,8 +13,10 @@ import { lookupLegacySupport } from './model';
|
|
|
13
13
|
import { computedMacroWithOptionalParams } from './util';
|
|
14
14
|
|
|
15
15
|
function normalizeType(type) {
|
|
16
|
-
if (DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE
|
|
17
|
-
|
|
16
|
+
if (DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE) {
|
|
17
|
+
if (!type) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
return dasherize(type);
|
|
@@ -127,60 +129,69 @@ function normalizeType(type) {
|
|
|
127
129
|
function belongsTo(modelName, options) {
|
|
128
130
|
let opts = options;
|
|
129
131
|
let userEnteredModelName = modelName;
|
|
130
|
-
if (DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (typeof modelName === 'object') {
|
|
139
|
-
opts = modelName;
|
|
140
|
-
userEnteredModelName = undefined;
|
|
141
|
-
} else {
|
|
142
|
-
opts = options;
|
|
143
|
-
userEnteredModelName = modelName;
|
|
144
|
-
}
|
|
132
|
+
if (DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE) {
|
|
133
|
+
if (typeof modelName !== 'string' || !modelName.length) {
|
|
134
|
+
deprecate('belongsTo() must specify the string type of the related resource as the first parameter', false, {
|
|
135
|
+
id: 'ember-data:deprecate-non-strict-relationships',
|
|
136
|
+
for: 'ember-data',
|
|
137
|
+
until: '5.0',
|
|
138
|
+
since: { enabled: '4.7', available: '4.7' },
|
|
139
|
+
});
|
|
145
140
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
141
|
+
if (typeof modelName === 'object') {
|
|
142
|
+
opts = modelName;
|
|
143
|
+
userEnteredModelName = undefined;
|
|
144
|
+
} else {
|
|
145
|
+
opts = options;
|
|
146
|
+
userEnteredModelName = modelName;
|
|
147
|
+
}
|
|
153
148
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
149
|
+
assert(
|
|
150
|
+
'The first argument to belongsTo must be a string representing a model type key, not an instance of ' +
|
|
151
|
+
typeof userEnteredModelName +
|
|
152
|
+
". E.g., to define a relation to the Person model, use belongsTo('person')",
|
|
153
|
+
typeof userEnteredModelName === 'string' || typeof userEnteredModelName === 'undefined'
|
|
154
|
+
);
|
|
158
155
|
}
|
|
159
|
-
deprecate('belongsTo(<type>, <options>) must specify options.async as either `true` or `false`.', false, {
|
|
160
|
-
id: 'ember-data:deprecate-non-strict-relationships',
|
|
161
|
-
for: 'ember-data',
|
|
162
|
-
until: '5.0',
|
|
163
|
-
since: { enabled: '4.7', available: '4.7' },
|
|
164
|
-
});
|
|
165
|
-
} else {
|
|
166
|
-
assert(`Expected belongsTo options.async to be a boolean`, opts && typeof opts.async === 'boolean');
|
|
167
156
|
}
|
|
168
157
|
|
|
169
|
-
if (
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
'belongsTo(<type>, <options>) must specify options.
|
|
176
|
-
false,
|
|
177
|
-
{
|
|
158
|
+
if (DEPRECATE_RELATIONSHIPS_WITHOUT_ASYNC) {
|
|
159
|
+
if (!opts || typeof opts.async !== 'boolean') {
|
|
160
|
+
opts = opts || {};
|
|
161
|
+
if (!('async' in opts)) {
|
|
162
|
+
opts.async = true;
|
|
163
|
+
}
|
|
164
|
+
deprecate('belongsTo(<type>, <options>) must specify options.async as either `true` or `false`.', false, {
|
|
178
165
|
id: 'ember-data:deprecate-non-strict-relationships',
|
|
179
166
|
for: 'ember-data',
|
|
180
167
|
until: '5.0',
|
|
181
168
|
since: { enabled: '4.7', available: '4.7' },
|
|
182
|
-
}
|
|
183
|
-
|
|
169
|
+
});
|
|
170
|
+
} else {
|
|
171
|
+
assert(`Expected belongsTo options.async to be a boolean`, opts && typeof opts.async === 'boolean');
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
assert(`Expected belongsTo options.async to be a boolean`, opts && typeof opts.async === 'boolean');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE) {
|
|
178
|
+
if (opts.inverse !== null && (typeof opts.inverse !== 'string' || opts.inverse.length === 0)) {
|
|
179
|
+
deprecate(
|
|
180
|
+
'belongsTo(<type>, <options>) must specify options.inverse as either `null` or string type of the related resource.',
|
|
181
|
+
false,
|
|
182
|
+
{
|
|
183
|
+
id: 'ember-data:deprecate-non-strict-relationships',
|
|
184
|
+
for: 'ember-data',
|
|
185
|
+
until: '5.0',
|
|
186
|
+
since: { enabled: '4.7', available: '4.7' },
|
|
187
|
+
}
|
|
188
|
+
);
|
|
189
|
+
} else {
|
|
190
|
+
assert(
|
|
191
|
+
`Expected belongsTo options.inverse to be either null or the string type of the related resource.`,
|
|
192
|
+
opts.inverse === null || (typeof opts.inverse === 'string' && opts.inverse.length > 0)
|
|
193
|
+
);
|
|
194
|
+
}
|
|
184
195
|
} else {
|
|
185
196
|
assert(
|
|
186
197
|
`Expected belongsTo options.inverse to be either null or the string type of the related resource.`,
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { assert } from '@ember/debug';
|
|
2
|
+
import { DEBUG } from '@glimmer/env';
|
|
3
|
+
|
|
4
|
+
import { DEPRECATE_NON_EXPLICIT_POLYMORPHISM } from '@ember-data/private-build-infra/deprecations';
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
Assert that `addedRecord` has a valid type so it can be added to the
|
|
8
|
+
relationship of the `record`.
|
|
9
|
+
|
|
10
|
+
The assert basically checks if the `addedRecord` can be added to the
|
|
11
|
+
relationship (specified via `relationshipMeta`) of the `record`.
|
|
12
|
+
|
|
13
|
+
This utility should only be used internally, as both record parameters must
|
|
14
|
+
be stable record identifiers and the `relationshipMeta` needs to be the meta
|
|
15
|
+
information about the relationship, retrieved via
|
|
16
|
+
`record.relationshipFor(key)`.
|
|
17
|
+
*/
|
|
18
|
+
let assertPolymorphicType;
|
|
19
|
+
|
|
20
|
+
if (DEBUG) {
|
|
21
|
+
let checkPolymorphic = function checkPolymorphic(modelClass, addedModelClass) {
|
|
22
|
+
if (modelClass.__isMixin) {
|
|
23
|
+
return (
|
|
24
|
+
modelClass.__mixin.detect(addedModelClass.PrototypeMixin) ||
|
|
25
|
+
// handle native class extension e.g. `class Post extends Model.extend(Commentable) {}`
|
|
26
|
+
modelClass.__mixin.detect(Object.getPrototypeOf(addedModelClass).PrototypeMixin)
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return addedModelClass.prototype instanceof modelClass || modelClass.detect(addedModelClass);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
assertPolymorphicType = function assertPolymorphicType(parentIdentifier, parentDefinition, addedIdentifier, store) {
|
|
34
|
+
let asserted = false;
|
|
35
|
+
|
|
36
|
+
if (parentDefinition.inverseIsImplicit) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (parentDefinition.isPolymorphic) {
|
|
40
|
+
let meta = store.getSchemaDefinitionService().relationshipsDefinitionFor(addedIdentifier)[
|
|
41
|
+
parentDefinition.inverseKey
|
|
42
|
+
];
|
|
43
|
+
if (meta?.options?.as) {
|
|
44
|
+
asserted = true;
|
|
45
|
+
assert(
|
|
46
|
+
`The schema for the relationship '${parentDefinition.inverseKey}' on '${addedIdentifier.type}' type does not implement '${parentDefinition.type}' and thus cannot be assigned to the '${parentDefinition.key}' relationship in '${parentIdentifier.type}'. The definition should specify 'as: "${parentDefinition.type}"' in options.`,
|
|
47
|
+
meta.options.as === parentDefinition.type
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (DEPRECATE_NON_EXPLICIT_POLYMORPHISM) {
|
|
53
|
+
if (!asserted) {
|
|
54
|
+
store = store._store ? store._store : store; // allow usage with storeWrapper
|
|
55
|
+
let addedModelName = addedIdentifier.type;
|
|
56
|
+
let parentModelName = parentIdentifier.type;
|
|
57
|
+
let key = parentDefinition.key;
|
|
58
|
+
let relationshipModelName = parentDefinition.type;
|
|
59
|
+
let relationshipClass = store.modelFor(relationshipModelName);
|
|
60
|
+
let addedClass = store.modelFor(addedModelName);
|
|
61
|
+
|
|
62
|
+
let assertionMessage = `The '${addedModelName}' type does not implement '${relationshipModelName}' and thus cannot be assigned to the '${key}' relationship in '${parentModelName}'. Make it a descendant of '${relationshipModelName}' or use a mixin of the same name.`;
|
|
63
|
+
let isPolymorphic = checkPolymorphic(relationshipClass, addedClass);
|
|
64
|
+
|
|
65
|
+
assert(assertionMessage, isPolymorphic);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export { assertPolymorphicType };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { deprecate } from '@ember/debug';
|
|
2
|
+
import { get } from '@ember/object';
|
|
3
|
+
import { DEBUG } from '@glimmer/env';
|
|
2
4
|
|
|
3
5
|
import { resolve } from 'rsvp';
|
|
4
6
|
|
|
@@ -12,18 +14,36 @@ function promiseObject<T>(promise: Promise<T>): PromiseObject<T> {
|
|
|
12
14
|
|
|
13
15
|
// constructor is accessed in some internals but not including it in the copyright for the deprecation
|
|
14
16
|
const ALLOWABLE_METHODS = ['constructor', 'then', 'catch', 'finally'];
|
|
17
|
+
const ALLOWABLE_PROPS = ['__ec_yieldable__', '__ec_cancel__'];
|
|
15
18
|
const PROXIED_OBJECT_PROPS = ['content', 'isPending', 'isSettled', 'isRejected', 'isFulfilled', 'promise', 'reason'];
|
|
16
19
|
|
|
20
|
+
const ProxySymbolString = String(Symbol.for('PROXY_CONTENT'));
|
|
21
|
+
|
|
17
22
|
export function deprecatedPromiseObject<T>(promise: Promise<T>): PromiseObject<T> {
|
|
18
23
|
const promiseObjectProxy: PromiseObject<T> = promiseObject(promise);
|
|
24
|
+
if (!DEBUG) {
|
|
25
|
+
return promiseObjectProxy;
|
|
26
|
+
}
|
|
19
27
|
const handler = {
|
|
20
|
-
get(target: object, prop: string, receiver
|
|
28
|
+
get(target: object, prop: string, receiver: object): unknown {
|
|
21
29
|
if (typeof prop === 'symbol') {
|
|
30
|
+
if (String(prop) === ProxySymbolString) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
22
33
|
return Reflect.get(target, prop, receiver);
|
|
23
34
|
}
|
|
35
|
+
|
|
36
|
+
if (prop === 'constructor') {
|
|
37
|
+
return target.constructor;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (ALLOWABLE_PROPS.includes(prop)) {
|
|
41
|
+
return target[prop];
|
|
42
|
+
}
|
|
43
|
+
|
|
24
44
|
if (!ALLOWABLE_METHODS.includes(prop)) {
|
|
25
45
|
deprecate(
|
|
26
|
-
`Accessing ${prop} is deprecated. The return type is being changed
|
|
46
|
+
`Accessing ${prop} is deprecated. The return type is being changed from PromiseObjectProxy to a Promise. The only available methods to access on this promise are .then, .catch and .finally`,
|
|
27
47
|
false,
|
|
28
48
|
{
|
|
29
49
|
id: 'ember-data:model-save-promise',
|
|
@@ -35,15 +55,17 @@ export function deprecatedPromiseObject<T>(promise: Promise<T>): PromiseObject<T
|
|
|
35
55
|
},
|
|
36
56
|
}
|
|
37
57
|
);
|
|
58
|
+
} else {
|
|
59
|
+
return (target[prop] as () => unknown).bind(target);
|
|
38
60
|
}
|
|
39
61
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return value.bind(target);
|
|
62
|
+
if (PROXIED_OBJECT_PROPS.includes(prop)) {
|
|
63
|
+
return target[prop];
|
|
43
64
|
}
|
|
44
65
|
|
|
45
|
-
|
|
46
|
-
|
|
66
|
+
const value: unknown = get(target, prop);
|
|
67
|
+
if (value && typeof value === 'function' && typeof value.bind === 'function') {
|
|
68
|
+
return value.bind(receiver);
|
|
47
69
|
}
|
|
48
70
|
|
|
49
71
|
return undefined;
|
|
@@ -19,8 +19,10 @@ import { lookupLegacySupport } from './model';
|
|
|
19
19
|
import { computedMacroWithOptionalParams } from './util';
|
|
20
20
|
|
|
21
21
|
function normalizeType(type) {
|
|
22
|
-
if (DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE
|
|
23
|
-
|
|
22
|
+
if (DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE) {
|
|
23
|
+
if (!type) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
return singularize(dasherize(type));
|
|
@@ -168,60 +170,64 @@ function normalizeType(type) {
|
|
|
168
170
|
@return {Ember.computed} relationship
|
|
169
171
|
*/
|
|
170
172
|
function hasMany(type, options) {
|
|
171
|
-
if (DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
173
|
+
if (DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE) {
|
|
174
|
+
if (typeof type !== 'string' || !type.length) {
|
|
175
|
+
deprecate(
|
|
176
|
+
'hasMany(<type>, <options>) must specify the string type of the related resource as the first parameter',
|
|
177
|
+
false,
|
|
178
|
+
{
|
|
179
|
+
id: 'ember-data:deprecate-non-strict-relationships',
|
|
180
|
+
for: 'ember-data',
|
|
181
|
+
until: '5.0',
|
|
182
|
+
since: { enabled: '4.7', available: '4.7' },
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
if (typeof type === 'object') {
|
|
186
|
+
options = type;
|
|
187
|
+
type = undefined;
|
|
180
188
|
}
|
|
181
|
-
);
|
|
182
|
-
if (typeof type === 'object') {
|
|
183
|
-
options = type;
|
|
184
|
-
type = undefined;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
assert(
|
|
188
|
-
`The first argument to hasMany must be a string representing a model type key, not an instance of ${inspect(
|
|
189
|
-
type
|
|
190
|
-
)}. E.g., to define a relation to the Comment model, use hasMany('comment')`,
|
|
191
|
-
typeof type === 'string' || typeof type === 'undefined'
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
189
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
190
|
+
assert(
|
|
191
|
+
`The first argument to hasMany must be a string representing a model type key, not an instance of ${inspect(
|
|
192
|
+
type
|
|
193
|
+
)}. E.g., to define a relation to the Comment model, use hasMany('comment')`,
|
|
194
|
+
typeof type === 'string' || typeof type === 'undefined'
|
|
195
|
+
);
|
|
199
196
|
}
|
|
200
|
-
deprecate('hasMany(<type>, <options>) must specify options.async as either `true` or `false`.', false, {
|
|
201
|
-
id: 'ember-data:deprecate-non-strict-relationships',
|
|
202
|
-
for: 'ember-data',
|
|
203
|
-
until: '5.0',
|
|
204
|
-
since: { enabled: '4.7', available: '4.7' },
|
|
205
|
-
});
|
|
206
|
-
} else {
|
|
207
|
-
assert(`Expected hasMany options.async to be a boolean`, options && typeof options.async === 'boolean');
|
|
208
197
|
}
|
|
209
198
|
|
|
210
|
-
if (
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
'hasMany(<type>, <options>) must specify options.
|
|
217
|
-
false,
|
|
218
|
-
{
|
|
199
|
+
if (DEPRECATE_RELATIONSHIPS_WITHOUT_ASYNC) {
|
|
200
|
+
if (!options || typeof options.async !== 'boolean') {
|
|
201
|
+
options = options || {};
|
|
202
|
+
if (!('async' in options)) {
|
|
203
|
+
options.async = true;
|
|
204
|
+
}
|
|
205
|
+
deprecate('hasMany(<type>, <options>) must specify options.async as either `true` or `false`.', false, {
|
|
219
206
|
id: 'ember-data:deprecate-non-strict-relationships',
|
|
220
207
|
for: 'ember-data',
|
|
221
208
|
until: '5.0',
|
|
222
209
|
since: { enabled: '4.7', available: '4.7' },
|
|
223
|
-
}
|
|
224
|
-
|
|
210
|
+
});
|
|
211
|
+
} else {
|
|
212
|
+
assert(`Expected hasMany options.async to be a boolean`, options && typeof options.async === 'boolean');
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
assert(`Expected hasMany options.async to be a boolean`, options && typeof options.async === 'boolean');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE) {
|
|
219
|
+
if (options.inverse !== null && (typeof options.inverse !== 'string' || options.inverse.length === 0)) {
|
|
220
|
+
deprecate(
|
|
221
|
+
'hasMany(<type>, <options>) must specify options.inverse as either `null` or string type of the related resource.',
|
|
222
|
+
false,
|
|
223
|
+
{
|
|
224
|
+
id: 'ember-data:deprecate-non-strict-relationships',
|
|
225
|
+
for: 'ember-data',
|
|
226
|
+
until: '5.0',
|
|
227
|
+
since: { enabled: '4.7', available: '4.7' },
|
|
228
|
+
}
|
|
229
|
+
);
|
|
230
|
+
}
|
|
225
231
|
}
|
|
226
232
|
|
|
227
233
|
// Metadata about relationships is stored on the meta of
|
|
@@ -195,36 +195,37 @@ function ensureRelationshipIsSetToParent(payload, parentIdentifier, store, paren
|
|
|
195
195
|
|
|
196
196
|
let relationshipData = relationships[inverseKey] && relationships[inverseKey].data;
|
|
197
197
|
|
|
198
|
-
if (
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
198
|
+
if (DEBUG) {
|
|
199
|
+
if (
|
|
200
|
+
typeof relationshipData !== 'undefined' &&
|
|
201
|
+
!relationshipDataPointsToParent(relationshipData, parentIdentifier)
|
|
202
|
+
) {
|
|
203
|
+
let inspect = function inspect(thing) {
|
|
204
|
+
return `'${JSON.stringify(thing)}'`;
|
|
205
|
+
};
|
|
206
|
+
let quotedType = inspect(type);
|
|
207
|
+
let quotedInverse = inspect(inverseKey);
|
|
208
|
+
let expected = inspect({
|
|
209
|
+
id: parentIdentifier.id,
|
|
210
|
+
type: parentIdentifier.type,
|
|
211
|
+
});
|
|
212
|
+
let expectedModel = `${parentIdentifier.type}:${parentIdentifier.id}`;
|
|
213
|
+
let got = inspect(relationshipData);
|
|
214
|
+
let prefix = typeof index === 'number' ? `data[${index}]` : `data`;
|
|
215
|
+
let path = `${prefix}.relationships.${inverseKey}.data`;
|
|
216
|
+
let other = relationshipData ? `<${relationshipData.type}:${relationshipData.id}>` : null;
|
|
217
|
+
let relationshipFetched = `${expectedModel}.${parentRelationship.kind}("${parentRelationship.name}")`;
|
|
218
|
+
let includedRecord = `<${type}:${id}>`;
|
|
219
|
+
let message = [
|
|
220
|
+
`Encountered mismatched relationship: Ember Data expected ${path} in the payload from ${relationshipFetched} to include ${expected} but got ${got} instead.\n`,
|
|
221
|
+
`The ${includedRecord} record loaded at ${prefix} in the payload specified ${other} as its ${quotedInverse}, but should have specified ${expectedModel} (the record the relationship is being loaded from) as its ${quotedInverse} instead.`,
|
|
222
|
+
`This could mean that the response for ${relationshipFetched} may have accidentally returned ${quotedType} records that aren't related to ${expectedModel} and could be related to a different ${parentIdentifier.type} record instead.`,
|
|
223
|
+
`Ember Data has corrected the ${includedRecord} record's ${quotedInverse} relationship to ${expectedModel} so that ${relationshipFetched} will include ${includedRecord}.`,
|
|
224
|
+
`Please update the response from the server or change your serializer to either ensure that the response for only includes ${quotedType} records that specify ${expectedModel} as their ${quotedInverse}, or omit the ${quotedInverse} relationship from the response.`,
|
|
225
|
+
].join('\n');
|
|
226
|
+
|
|
227
|
+
assert(message);
|
|
228
|
+
}
|
|
228
229
|
}
|
|
229
230
|
|
|
230
231
|
if (kind !== 'hasMany' || typeof relationshipData !== 'undefined') {
|
|
@@ -244,9 +245,11 @@ function inverseForRelationship(store, identifier, key) {
|
|
|
244
245
|
return null;
|
|
245
246
|
}
|
|
246
247
|
|
|
247
|
-
if (DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE
|
|
248
|
-
|
|
249
|
-
|
|
248
|
+
if (DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE) {
|
|
249
|
+
if (metaIsRelationshipDefinition(definition)) {
|
|
250
|
+
const modelClass = store.modelFor(identifier.type);
|
|
251
|
+
return definition._inverseKey(store, modelClass);
|
|
252
|
+
}
|
|
250
253
|
}
|
|
251
254
|
assert(
|
|
252
255
|
`Expected the relationship defintion to specify the inverse type or null.`,
|
|
@@ -389,13 +389,15 @@ export class LegacySupport {
|
|
|
389
389
|
const graph = graphFor(this.store);
|
|
390
390
|
const relationship = graph.get(this.identifier, name);
|
|
391
391
|
|
|
392
|
-
if (DEBUG
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
392
|
+
if (DEBUG) {
|
|
393
|
+
if (kind) {
|
|
394
|
+
let modelName = this.identifier.type;
|
|
395
|
+
let actualRelationshipKind = relationship.definition.kind;
|
|
396
|
+
assert(
|
|
397
|
+
`You tried to get the '${name}' relationship on a '${modelName}' via record.${kind}('${name}'), but the relationship is of kind '${actualRelationshipKind}'. Use record.${actualRelationshipKind}('${name}') instead.`,
|
|
398
|
+
actualRelationshipKind === kind
|
|
399
|
+
);
|
|
400
|
+
}
|
|
399
401
|
}
|
|
400
402
|
|
|
401
403
|
let relationshipKind = relationship.definition.kind;
|
|
@@ -705,26 +707,28 @@ function extractIdentifierFromRecord(recordOrPromiseRecord: PromiseProxyRecord |
|
|
|
705
707
|
return null;
|
|
706
708
|
}
|
|
707
709
|
|
|
708
|
-
if (DEPRECATE_PROMISE_PROXIES
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
710
|
+
if (DEPRECATE_PROMISE_PROXIES) {
|
|
711
|
+
if (isPromiseRecord(recordOrPromiseRecord)) {
|
|
712
|
+
let content = recordOrPromiseRecord.content;
|
|
713
|
+
assert(
|
|
714
|
+
'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.',
|
|
715
|
+
content !== undefined
|
|
716
|
+
);
|
|
717
|
+
deprecate(
|
|
718
|
+
`You passed in a PromiseProxy to a Relationship API that now expects a resolved value. await the value before setting it.`,
|
|
719
|
+
false,
|
|
720
|
+
{
|
|
721
|
+
id: 'ember-data:deprecate-promise-proxies',
|
|
722
|
+
until: '5.0',
|
|
723
|
+
since: {
|
|
724
|
+
enabled: '4.7',
|
|
725
|
+
available: '4.7',
|
|
726
|
+
},
|
|
727
|
+
for: 'ember-data',
|
|
728
|
+
}
|
|
729
|
+
);
|
|
730
|
+
return content ? recordIdentifierFor(content) : null;
|
|
731
|
+
}
|
|
728
732
|
}
|
|
729
733
|
|
|
730
734
|
return recordIdentifierFor(recordOrPromiseRecord);
|
|
@@ -367,27 +367,29 @@ function extractIdentifiersFromRecords(records: RecordInstance[]): StableRecordI
|
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
function extractIdentifierFromRecord(recordOrPromiseRecord: PromiseProxyRecord | RecordInstance) {
|
|
370
|
-
if (DEPRECATE_PROMISE_PROXIES
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
370
|
+
if (DEPRECATE_PROMISE_PROXIES) {
|
|
371
|
+
if (isPromiseRecord(recordOrPromiseRecord)) {
|
|
372
|
+
let content = recordOrPromiseRecord.content;
|
|
373
|
+
assert(
|
|
374
|
+
'You passed in a promise that did not originate from an EmberData relationship. You can only pass promises that come from a belongsTo relationship.',
|
|
375
|
+
content !== undefined && content !== null
|
|
376
|
+
);
|
|
377
|
+
deprecate(
|
|
378
|
+
`You passed in a PromiseProxy to a Relationship API that now expects a resolved value. await the value before setting it.`,
|
|
379
|
+
false,
|
|
380
|
+
{
|
|
381
|
+
id: 'ember-data:deprecate-promise-proxies',
|
|
382
|
+
until: '5.0',
|
|
383
|
+
since: {
|
|
384
|
+
enabled: '4.7',
|
|
385
|
+
available: '4.7',
|
|
386
|
+
},
|
|
387
|
+
for: 'ember-data',
|
|
388
|
+
}
|
|
389
|
+
);
|
|
390
|
+
assertRecordPassedToHasMany(content);
|
|
391
|
+
return recordIdentifierFor(content);
|
|
392
|
+
}
|
|
391
393
|
}
|
|
392
394
|
|
|
393
395
|
assertRecordPassedToHasMany(recordOrPromiseRecord);
|
package/addon/-private/model.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { assert, deprecate, warn } from '@ember/debug';
|
|
6
|
-
import EmberError from '@ember/error';
|
|
7
6
|
import EmberObject from '@ember/object';
|
|
8
7
|
import { dependentKeyCompat } from '@ember/object/compat';
|
|
9
8
|
import { run } from '@ember/runloop';
|
|
@@ -125,10 +124,12 @@ class Model extends EmberObject {
|
|
|
125
124
|
___private_notifications;
|
|
126
125
|
|
|
127
126
|
init(options = {}) {
|
|
128
|
-
if (DEBUG
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
127
|
+
if (DEBUG) {
|
|
128
|
+
if (!options._secretInit && !options._createProps) {
|
|
129
|
+
throw new Error(
|
|
130
|
+
'You should not call `create` on a model. Instead, call `store.createRecord` with the attributes you would like to set.'
|
|
131
|
+
);
|
|
132
|
+
}
|
|
132
133
|
}
|
|
133
134
|
const createProps = options._createProps;
|
|
134
135
|
const _secretInit = options._secretInit;
|
|
@@ -521,8 +522,10 @@ class Model extends EmberObject {
|
|
|
521
522
|
// when using legacy/classic ember classes. Basically: lazy in prod and eager in dev.
|
|
522
523
|
// so we do this to try to steer folks to the nicer "dont user currentState"
|
|
523
524
|
// error.
|
|
524
|
-
if (!DEBUG
|
|
525
|
-
this.___recordState
|
|
525
|
+
if (!DEBUG) {
|
|
526
|
+
if (!this.___recordState) {
|
|
527
|
+
this.___recordState = new RecordState(this);
|
|
528
|
+
}
|
|
526
529
|
}
|
|
527
530
|
return this.___recordState;
|
|
528
531
|
}
|
|
@@ -1489,56 +1492,60 @@ class Model extends EmberObject {
|
|
|
1489
1492
|
}
|
|
1490
1493
|
|
|
1491
1494
|
// ensure inverse is properly configured
|
|
1492
|
-
if (DEBUG
|
|
1493
|
-
if (
|
|
1494
|
-
if (
|
|
1495
|
-
|
|
1495
|
+
if (DEBUG) {
|
|
1496
|
+
if (isPolymorphic) {
|
|
1497
|
+
if (DEPRECATE_NON_EXPLICIT_POLYMORPHISM) {
|
|
1498
|
+
if (!inverseOptions.as) {
|
|
1499
|
+
deprecate(
|
|
1500
|
+
`Relationships that satisfy polymorphic relationships MUST define which abstract-type they are satisfying using 'as'. The field '${fieldOnInverse}' on type '${inverseSchema.modelName}' is misconfigured.`,
|
|
1501
|
+
false,
|
|
1502
|
+
{
|
|
1503
|
+
id: 'ember-data:non-explicit-relationships',
|
|
1504
|
+
since: { enabled: '4.7', available: '4.7' },
|
|
1505
|
+
until: '5.0',
|
|
1506
|
+
for: 'ember-data',
|
|
1507
|
+
}
|
|
1508
|
+
);
|
|
1509
|
+
}
|
|
1510
|
+
} else {
|
|
1511
|
+
assert(
|
|
1496
1512
|
`Relationships that satisfy polymorphic relationships MUST define which abstract-type they are satisfying using 'as'. The field '${fieldOnInverse}' on type '${inverseSchema.modelName}' is misconfigured.`,
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
for: 'ember-data',
|
|
1503
|
-
}
|
|
1513
|
+
inverseOptions.as
|
|
1514
|
+
);
|
|
1515
|
+
assert(
|
|
1516
|
+
`options.as should match the expected type of the polymorphic relationship. Expected field '${fieldOnInverse}' on type '${inverseSchema.modelName}' to specify '${relationship.type}' but found '${inverseOptions.as}'`,
|
|
1517
|
+
!!inverseOptions.as && relationship.type === inverseOptions.as
|
|
1504
1518
|
);
|
|
1505
1519
|
}
|
|
1506
|
-
} else {
|
|
1507
|
-
assert(
|
|
1508
|
-
`Relationships that satisfy polymorphic relationships MUST define which abstract-type they are satisfying using 'as'. The field '${fieldOnInverse}' on type '${inverseSchema.modelName}' is misconfigured.`,
|
|
1509
|
-
inverseOptions.as
|
|
1510
|
-
);
|
|
1511
|
-
assert(
|
|
1512
|
-
`options.as should match the expected type of the polymorphic relationship. Expected field '${fieldOnInverse}' on type '${inverseSchema.modelName}' to specify '${relationship.type}' but found '${inverseOptions.as}'`,
|
|
1513
|
-
!!inverseOptions.as && relationship.type === inverseOptions.as
|
|
1514
|
-
);
|
|
1515
1520
|
}
|
|
1516
1521
|
}
|
|
1517
1522
|
|
|
1518
1523
|
// ensure we are properly configured
|
|
1519
|
-
if (DEBUG
|
|
1520
|
-
if (
|
|
1521
|
-
if (
|
|
1522
|
-
|
|
1524
|
+
if (DEBUG) {
|
|
1525
|
+
if (inverseOptions.polymorphic) {
|
|
1526
|
+
if (DEPRECATE_NON_EXPLICIT_POLYMORPHISM) {
|
|
1527
|
+
if (!options.as) {
|
|
1528
|
+
deprecate(
|
|
1529
|
+
`Relationships that satisfy polymorphic relationships MUST define which abstract-type they are satisfying using 'as'. The field '${name}' on type '${this.modelName}' is misconfigured.`,
|
|
1530
|
+
false,
|
|
1531
|
+
{
|
|
1532
|
+
id: 'ember-data:non-explicit-relationships',
|
|
1533
|
+
since: { enabled: '4.7', available: '4.7' },
|
|
1534
|
+
until: '5.0',
|
|
1535
|
+
for: 'ember-data',
|
|
1536
|
+
}
|
|
1537
|
+
);
|
|
1538
|
+
}
|
|
1539
|
+
} else {
|
|
1540
|
+
assert(
|
|
1523
1541
|
`Relationships that satisfy polymorphic relationships MUST define which abstract-type they are satisfying using 'as'. The field '${name}' on type '${this.modelName}' is misconfigured.`,
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
for: 'ember-data',
|
|
1530
|
-
}
|
|
1542
|
+
options.as
|
|
1543
|
+
);
|
|
1544
|
+
assert(
|
|
1545
|
+
`options.as should match the expected type of the polymorphic relationship. Expected field '${name}' on type '${this.modelName}' to specify '${inverseRelationship.type}' but found '${options.as}'`,
|
|
1546
|
+
!!options.as && inverseRelationship.type === options.as
|
|
1531
1547
|
);
|
|
1532
1548
|
}
|
|
1533
|
-
} else {
|
|
1534
|
-
assert(
|
|
1535
|
-
`Relationships that satisfy polymorphic relationships MUST define which abstract-type they are satisfying using 'as'. The field '${name}' on type '${this.modelName}' is misconfigured.`,
|
|
1536
|
-
options.as
|
|
1537
|
-
);
|
|
1538
|
-
assert(
|
|
1539
|
-
`options.as should match the expected type of the polymorphic relationship. Expected field '${name}' on type '${this.modelName}' to specify '${inverseRelationship.type}' but found '${options.as}'`,
|
|
1540
|
-
!!options.as && inverseRelationship.type === options.as
|
|
1541
|
-
);
|
|
1542
1549
|
}
|
|
1543
1550
|
}
|
|
1544
1551
|
|
|
@@ -2469,7 +2476,7 @@ if (DEBUG) {
|
|
|
2469
2476
|
let idDesc = lookupDescriptor(this, 'id');
|
|
2470
2477
|
|
|
2471
2478
|
if (idDesc.get !== ID_DESCRIPTOR.get) {
|
|
2472
|
-
throw new
|
|
2479
|
+
throw new Error(
|
|
2473
2480
|
`You may not set 'id' as an attribute on your model. Please remove any lines that look like: \`id: attr('<type>')\` from ${this.constructor.toString()}`
|
|
2474
2481
|
);
|
|
2475
2482
|
}
|
|
@@ -8,6 +8,7 @@ import { storeFor } from '@ember-data/store';
|
|
|
8
8
|
import { recordIdentifierFor } from '@ember-data/store/-private';
|
|
9
9
|
import type { NotificationType } from '@ember-data/store/-private/managers/record-notification-manager';
|
|
10
10
|
import type RequestCache from '@ember-data/store/-private/network/request-cache';
|
|
11
|
+
import { addToTransaction, subscribe } from '@ember-data/tracking/-private';
|
|
11
12
|
import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
12
13
|
import type { RecordData } from '@ember-data/types/q/record-data';
|
|
13
14
|
|
|
@@ -35,21 +36,19 @@ class Tag {
|
|
|
35
36
|
declare rev: number;
|
|
36
37
|
declare isDirty: boolean;
|
|
37
38
|
declare value: any;
|
|
39
|
+
declare t: boolean;
|
|
38
40
|
|
|
39
41
|
constructor() {
|
|
40
42
|
this.rev = 1;
|
|
41
43
|
this.isDirty = true;
|
|
42
44
|
this.value = undefined;
|
|
45
|
+
this.t = false;
|
|
43
46
|
}
|
|
44
|
-
@tracked
|
|
45
|
-
|
|
46
|
-
subscribe() {
|
|
47
|
-
this._tracking;
|
|
48
|
-
}
|
|
47
|
+
@tracked ref = null;
|
|
49
48
|
|
|
50
49
|
notify() {
|
|
51
50
|
this.isDirty = true;
|
|
52
|
-
this
|
|
51
|
+
addToTransaction(this);
|
|
53
52
|
this.rev++;
|
|
54
53
|
}
|
|
55
54
|
consume(v) {
|
|
@@ -86,7 +85,7 @@ export function tagged(_target, key, desc) {
|
|
|
86
85
|
const setter = desc.set;
|
|
87
86
|
desc.get = function () {
|
|
88
87
|
let tag = getTag(this, key);
|
|
89
|
-
|
|
88
|
+
subscribe(tag);
|
|
90
89
|
|
|
91
90
|
if (tag.isDirty) {
|
|
92
91
|
tag.consume(getter.call(this));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { deprecate } from '@ember/debug';
|
|
2
2
|
import { dependentKeyCompat } from '@ember/object/compat';
|
|
3
|
+
import { DEBUG } from '@glimmer/env';
|
|
3
4
|
import { cached, tracked } from '@glimmer/tracking';
|
|
4
5
|
|
|
5
6
|
import type { Object as JSONObject, Value as JSONValue } from 'json-typescript';
|
|
@@ -9,7 +10,6 @@ import { DEPRECATE_PROMISE_PROXIES } from '@ember-data/private-build-infra/depre
|
|
|
9
10
|
import type { Graph } from '@ember-data/record-data/-private/graph/graph';
|
|
10
11
|
import type BelongsToRelationship from '@ember-data/record-data/-private/relationships/state/belongs-to';
|
|
11
12
|
import type Store from '@ember-data/store';
|
|
12
|
-
import { assertPolymorphicType } from '@ember-data/store/-debug';
|
|
13
13
|
import { recordIdentifierFor } from '@ember-data/store/-private';
|
|
14
14
|
import type { NotificationType } from '@ember-data/store/-private/managers/record-notification-manager';
|
|
15
15
|
import type {
|
|
@@ -22,6 +22,7 @@ import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
|
22
22
|
import type { RecordInstance } from '@ember-data/types/q/record-instance';
|
|
23
23
|
import type { Dict } from '@ember-data/types/q/utils';
|
|
24
24
|
|
|
25
|
+
import { assertPolymorphicType } from '../debug/assert-polymorphic-type';
|
|
25
26
|
import type { LegacySupport } from '../legacy-relationships-support';
|
|
26
27
|
import { LEGACY_SUPPORT } from '../model';
|
|
27
28
|
|
|
@@ -101,9 +102,16 @@ export default class BelongsToReference {
|
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
104
|
|
|
105
|
+
/**
|
|
106
|
+
* The identifier of the record that this reference refers to.
|
|
107
|
+
* `null` if no related record is known.
|
|
108
|
+
*
|
|
109
|
+
* @property {StableRecordIdentifier | null} identifier
|
|
110
|
+
* @public
|
|
111
|
+
*/
|
|
104
112
|
@cached
|
|
105
113
|
@dependentKeyCompat
|
|
106
|
-
get
|
|
114
|
+
get identifier(): StableRecordIdentifier | null {
|
|
107
115
|
this._ref; // consume the tracked prop
|
|
108
116
|
if (this.___relatedToken) {
|
|
109
117
|
this.store._notificationManager.unsubscribe(this.___relatedToken);
|
|
@@ -165,11 +173,11 @@ export default class BelongsToReference {
|
|
|
165
173
|
```
|
|
166
174
|
|
|
167
175
|
@method id
|
|
168
|
-
|
|
176
|
+
@public
|
|
169
177
|
@return {String} The id of the record in this belongsTo relationship.
|
|
170
178
|
*/
|
|
171
179
|
id(): string | null {
|
|
172
|
-
return this.
|
|
180
|
+
return this.identifier?.id || null;
|
|
173
181
|
}
|
|
174
182
|
|
|
175
183
|
/**
|
|
@@ -388,33 +396,37 @@ export default class BelongsToReference {
|
|
|
388
396
|
*/
|
|
389
397
|
async push(data: SingleResourceDocument | Promise<SingleResourceDocument>): Promise<RecordInstance> {
|
|
390
398
|
let jsonApiDoc: SingleResourceDocument = data as SingleResourceDocument;
|
|
391
|
-
if (DEPRECATE_PROMISE_PROXIES
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
399
|
+
if (DEPRECATE_PROMISE_PROXIES) {
|
|
400
|
+
if ((data as { then: unknown }).then) {
|
|
401
|
+
jsonApiDoc = await resolve(data);
|
|
402
|
+
if (jsonApiDoc !== data) {
|
|
403
|
+
deprecate(
|
|
404
|
+
`You passed in a Promise to a Reference API that now expects a resolved value. await the value before setting it.`,
|
|
405
|
+
false,
|
|
406
|
+
{
|
|
407
|
+
id: 'ember-data:deprecate-promise-proxies',
|
|
408
|
+
until: '5.0',
|
|
409
|
+
since: {
|
|
410
|
+
enabled: '4.7',
|
|
411
|
+
available: '4.7',
|
|
412
|
+
},
|
|
413
|
+
for: 'ember-data',
|
|
414
|
+
}
|
|
415
|
+
);
|
|
416
|
+
}
|
|
407
417
|
}
|
|
408
418
|
}
|
|
409
419
|
let record = this.store.push(jsonApiDoc);
|
|
410
420
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
421
|
+
if (DEBUG) {
|
|
422
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
423
|
+
assertPolymorphicType(
|
|
424
|
+
this.belongsToRelationship.identifier,
|
|
425
|
+
this.belongsToRelationship.definition,
|
|
426
|
+
recordIdentifierFor(record),
|
|
427
|
+
this.store
|
|
428
|
+
);
|
|
429
|
+
}
|
|
418
430
|
|
|
419
431
|
const { identifier } = this.belongsToRelationship;
|
|
420
432
|
this.store._join(() => {
|
|
@@ -13,7 +13,6 @@ import type { Graph } from '@ember-data/record-data/-private/graph/graph';
|
|
|
13
13
|
import type ManyRelationship from '@ember-data/record-data/-private/relationships/state/has-many';
|
|
14
14
|
import type Store from '@ember-data/store';
|
|
15
15
|
import { recordIdentifierFor } from '@ember-data/store';
|
|
16
|
-
import { assertPolymorphicType } from '@ember-data/store/-debug';
|
|
17
16
|
import type { NotificationType } from '@ember-data/store/-private/managers/record-notification-manager';
|
|
18
17
|
import type {
|
|
19
18
|
CollectionResourceDocument,
|
|
@@ -28,6 +27,7 @@ import type { RecordInstance } from '@ember-data/types/q/record-instance';
|
|
|
28
27
|
import type { FindOptions } from '@ember-data/types/q/store';
|
|
29
28
|
import type { Dict } from '@ember-data/types/q/utils';
|
|
30
29
|
|
|
30
|
+
import { assertPolymorphicType } from '../debug/assert-polymorphic-type';
|
|
31
31
|
import type { LegacySupport } from '../legacy-relationships-support';
|
|
32
32
|
import { LEGACY_SUPPORT } from '../model';
|
|
33
33
|
|
|
@@ -102,9 +102,15 @@ export default class HasManyReference {
|
|
|
102
102
|
this.___relatedTokenMap.clear();
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
/**
|
|
106
|
+
* An array of identifiers for the records that this reference refers to.
|
|
107
|
+
*
|
|
108
|
+
* @property {StableRecordIdentifier[]} identifiers
|
|
109
|
+
* @public
|
|
110
|
+
*/
|
|
105
111
|
@cached
|
|
106
112
|
@dependentKeyCompat
|
|
107
|
-
get
|
|
113
|
+
get identifiers(): StableRecordIdentifier[] {
|
|
108
114
|
this._ref; // consume the tracked prop
|
|
109
115
|
|
|
110
116
|
let resource = this._resource();
|
|
@@ -236,7 +242,7 @@ export default class HasManyReference {
|
|
|
236
242
|
@return {Array} The ids in this has-many relationship
|
|
237
243
|
*/
|
|
238
244
|
ids(): Array<string | null> {
|
|
239
|
-
return this.
|
|
245
|
+
return this.identifiers.map((identifier) => identifier.id);
|
|
240
246
|
}
|
|
241
247
|
|
|
242
248
|
/**
|
|
@@ -400,22 +406,24 @@ export default class HasManyReference {
|
|
|
400
406
|
objectOrPromise: ExistingResourceObject[] | CollectionResourceDocument | { data: SingleResourceDocument[] }
|
|
401
407
|
): Promise<ManyArray> {
|
|
402
408
|
let payload = objectOrPromise;
|
|
403
|
-
if (DEPRECATE_PROMISE_PROXIES
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
409
|
+
if (DEPRECATE_PROMISE_PROXIES) {
|
|
410
|
+
if ((objectOrPromise as unknown as { then: unknown }).then) {
|
|
411
|
+
payload = await resolve(objectOrPromise);
|
|
412
|
+
if (payload !== objectOrPromise) {
|
|
413
|
+
deprecate(
|
|
414
|
+
`You passed in a Promise to a Reference API that now expects a resolved value. await the value before setting it.`,
|
|
415
|
+
false,
|
|
416
|
+
{
|
|
417
|
+
id: 'ember-data:deprecate-promise-proxies',
|
|
418
|
+
until: '5.0',
|
|
419
|
+
since: {
|
|
420
|
+
enabled: '4.7',
|
|
421
|
+
available: '4.7',
|
|
422
|
+
},
|
|
423
|
+
for: 'ember-data',
|
|
424
|
+
}
|
|
425
|
+
);
|
|
426
|
+
}
|
|
419
427
|
}
|
|
420
428
|
}
|
|
421
429
|
let array: Array<ExistingResourceObject | SingleResourceDocument>;
|
|
@@ -519,7 +527,16 @@ export default class HasManyReference {
|
|
|
519
527
|
this.___identifier
|
|
520
528
|
)!;
|
|
521
529
|
|
|
522
|
-
|
|
530
|
+
const loaded = this._isLoaded();
|
|
531
|
+
|
|
532
|
+
if (!loaded) {
|
|
533
|
+
// subscribe to changes
|
|
534
|
+
// for when we are not loaded yet
|
|
535
|
+
this._ref;
|
|
536
|
+
return null;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
return support.getManyArray(this.key);
|
|
523
540
|
}
|
|
524
541
|
|
|
525
542
|
/**
|
|
@@ -77,8 +77,10 @@ class RelationshipDefinition implements RelationshipSchema {
|
|
|
77
77
|
inverse = modelClass.inverseFor(this.key, store);
|
|
78
78
|
}
|
|
79
79
|
// TODO make this error again for the non-polymorphic case
|
|
80
|
-
if (DEBUG
|
|
81
|
-
|
|
80
|
+
if (DEBUG) {
|
|
81
|
+
if (!this.options.polymorphic) {
|
|
82
|
+
modelClass.typeForRelationship(this.key, store);
|
|
83
|
+
}
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
if (inverse) {
|
package/addon/index.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { expect } from 'chai';
|
|
2
2
|
import { describe, it } from 'mocha';
|
|
3
|
+
|
|
3
4
|
import { setupModelTest } from 'ember-mocha';
|
|
4
5
|
|
|
5
|
-
describe('<%= friendlyTestDescription %>', function() {
|
|
6
|
+
describe('<%= friendlyTestDescription %>', function () {
|
|
6
7
|
setupModelTest('<%= dasherizedModuleName %>', {
|
|
7
8
|
// Specify the other units that are required for this test.
|
|
8
|
-
<%= typeof needs !== 'undefined' ? needs : ''
|
|
9
|
+
<%= typeof needs !== 'undefined' ? needs : '' %>,
|
|
9
10
|
});
|
|
10
11
|
|
|
11
12
|
// Replace this with your real tests.
|
|
12
|
-
it('exists', function() {
|
|
13
|
+
it('exists', function () {
|
|
13
14
|
let model = this.subject();
|
|
14
15
|
// var store = this.store();
|
|
15
16
|
expect(model).to.be.ok;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { expect } from 'chai';
|
|
2
2
|
import { describe, it } from 'mocha';
|
|
3
|
+
|
|
3
4
|
import { setupTest } from '<%= modulePrefix %>/tests/helpers';
|
|
4
5
|
|
|
5
|
-
describe('<%= friendlyTestDescription %>', function() {
|
|
6
|
+
describe('<%= friendlyTestDescription %>', function () {
|
|
6
7
|
setupTest();
|
|
7
8
|
|
|
8
9
|
// Replace this with your real tests.
|
|
9
|
-
it('exists', function() {
|
|
10
|
+
it('exists', function () {
|
|
10
11
|
let store = this.owner.lookup('service:store');
|
|
11
12
|
let model = store.createRecord('<%= dasherizedModuleName %>', {});
|
|
12
13
|
expect(model).to.be.ok;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { module, test } from 'qunit';
|
|
2
|
+
|
|
2
3
|
import { setupTest } from '<%= modulePrefix %>/tests/helpers';
|
|
3
4
|
|
|
4
|
-
module('<%= friendlyTestDescription %>', function(hooks) {
|
|
5
|
+
module('<%= friendlyTestDescription %>', function (hooks) {
|
|
5
6
|
setupTest(hooks);
|
|
6
7
|
|
|
7
8
|
// Replace this with your real tests.
|
|
8
|
-
test('it exists', function(assert) {
|
|
9
|
+
test('it exists', function (assert) {
|
|
9
10
|
let store = this.owner.lookup('service:store');
|
|
10
11
|
let model = store.createRecord('<%= dasherizedModuleName %>', {});
|
|
11
12
|
assert.ok(model);
|
package/index.js
CHANGED
|
@@ -15,10 +15,10 @@ module.exports = Object.assign({}, addonBaseConfig, {
|
|
|
15
15
|
'@ember-data/canary-features',
|
|
16
16
|
'@ember-data/store',
|
|
17
17
|
'@ember-data/store/-private',
|
|
18
|
-
'@ember-data/store/-debug',
|
|
19
18
|
'@embroider/macros',
|
|
20
19
|
'@ember/string',
|
|
21
20
|
'@embroider/macros/es-compat',
|
|
21
|
+
'@ember-data/tracking/-private',
|
|
22
22
|
|
|
23
23
|
'@ember/object/proxy',
|
|
24
24
|
'@ember/object/promise-proxy-mixin',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ember-data/model",
|
|
3
|
-
"version": "4.9.0-
|
|
3
|
+
"version": "4.9.0-beta.0",
|
|
4
4
|
"description": "A presentation layer for apps built with @ember-data/store",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ember-addon"
|
|
@@ -13,62 +13,52 @@
|
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"author": "",
|
|
15
15
|
"directories": {},
|
|
16
|
-
"scripts": {
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
"
|
|
16
|
+
"scripts": {},
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"@ember-data/record-data": "workspace:4.9.0-alpha.6",
|
|
19
|
+
"@ember-data/store": "workspace:4.9.0-alpha.6",
|
|
20
|
+
"@ember-data/tracking": "workspace:4.9.0-alpha.6",
|
|
21
|
+
"@ember/string": "^3.0.0",
|
|
22
|
+
"ember-inflector": "^4.0.2"
|
|
23
|
+
},
|
|
24
|
+
"peerDependenciesMeta": {
|
|
25
|
+
"@ember-data/record-data": {
|
|
26
|
+
"optional": true
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"dependenciesMeta": {
|
|
30
|
+
"@ember-data/canary-features": {
|
|
31
|
+
"injected": true
|
|
32
|
+
},
|
|
33
|
+
"@ember-data/private-build-infra": {
|
|
34
|
+
"injected": true
|
|
35
|
+
}
|
|
20
36
|
},
|
|
21
37
|
"dependencies": {
|
|
22
|
-
"@ember-data/canary-features": "4.9.0-
|
|
23
|
-
"@ember-data/private-build-infra": "4.9.0-
|
|
24
|
-
"@ember-data/store": "4.9.0-alpha.6",
|
|
38
|
+
"@ember-data/canary-features": "workspace:4.9.0-beta.0",
|
|
39
|
+
"@ember-data/private-build-infra": "workspace:4.9.0-beta.0",
|
|
25
40
|
"@ember/edition-utils": "^1.2.0",
|
|
26
|
-
"@
|
|
27
|
-
"
|
|
28
|
-
"ember-
|
|
29
|
-
"ember-cached-decorator-polyfill": "^0.1.4",
|
|
41
|
+
"@embroider/macros": "^1.9.0",
|
|
42
|
+
"ember-auto-import": "^2.4.3",
|
|
43
|
+
"ember-cached-decorator-polyfill": "^1.0.1",
|
|
30
44
|
"ember-cli-babel": "^7.26.11",
|
|
31
45
|
"ember-cli-string-utils": "^1.1.0",
|
|
32
46
|
"ember-cli-test-info": "^1.0.0",
|
|
33
|
-
"ember-cli-typescript": "^5.1.1",
|
|
34
47
|
"ember-compatibility-helpers": "^1.2.6",
|
|
35
|
-
"inflection": "~1.13.
|
|
48
|
+
"inflection": "~1.13.4"
|
|
36
49
|
},
|
|
37
50
|
"devDependencies": {
|
|
38
|
-
"@
|
|
39
|
-
"@
|
|
40
|
-
"
|
|
41
|
-
"broccoli-asset-rev": "^3.0.0",
|
|
42
|
-
"ember-cli": "~4.7.0",
|
|
43
|
-
"ember-cli-blueprint-test-helpers": "^0.19.2",
|
|
44
|
-
"ember-cli-dependency-checker": "^3.3.1",
|
|
45
|
-
"ember-cli-htmlbars": "^6.1.1",
|
|
46
|
-
"ember-cli-inject-live-reload": "^2.1.0",
|
|
47
|
-
"ember-cli-sri": "^2.1.1",
|
|
48
|
-
"ember-cli-terser": "~4.0.2",
|
|
49
|
-
"ember-disable-prototype-extensions": "^1.1.3",
|
|
50
|
-
"ember-export-application-global": "^2.0.1",
|
|
51
|
-
"ember-load-initializers": "^2.1.2",
|
|
52
|
-
"ember-maybe-import-regenerator": "^1.0.0",
|
|
53
|
-
"ember-qunit": "^5.1.5",
|
|
54
|
-
"ember-resolver": "^8.0.3",
|
|
55
|
-
"ember-source": "~4.7.0",
|
|
56
|
-
"ember-source-channel-url": "^3.0.0",
|
|
57
|
-
"ember-try": "^2.0.0",
|
|
58
|
-
"loader.js": "^4.7.0",
|
|
59
|
-
"qunit": "^2.19.1",
|
|
60
|
-
"qunit-dom": "^2.0.0",
|
|
61
|
-
"silent-error": "^1.1.1",
|
|
51
|
+
"@babel/core": "^7.19.6",
|
|
52
|
+
"@glimmer/component": "^1.1.2",
|
|
53
|
+
"ember-source": "~4.8.0",
|
|
62
54
|
"webpack": "^5.74.0"
|
|
63
55
|
},
|
|
64
56
|
"engines": {
|
|
65
57
|
"node": "^14.8.0 || 16.* || >= 18.*"
|
|
66
58
|
},
|
|
67
|
-
"ember-addon": {
|
|
68
|
-
"configPath": "tests/dummy/config"
|
|
69
|
-
},
|
|
59
|
+
"ember-addon": {},
|
|
70
60
|
"volta": {
|
|
71
|
-
"
|
|
72
|
-
|
|
73
|
-
|
|
61
|
+
"extends": "../../package.json"
|
|
62
|
+
},
|
|
63
|
+
"packageManager": "pnpm@7.14.1"
|
|
74
64
|
}
|