@ember-data/store 4.8.0-alpha.1 → 4.8.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addon/-private/core-store.ts +13 -7
- package/addon/-private/fetch-manager.ts +25 -49
- package/addon/-private/index.ts +23 -3
- package/addon/-private/model/internal-model.ts +59 -61
- package/addon/-private/normalize-model-name.ts +1 -3
- package/addon/-private/record-data-store-wrapper.ts +1 -5
- package/addon/-private/request-cache.ts +1 -1
- package/package.json +4 -4
- package/addon/-private/errors-utils.js +0 -146
|
@@ -1788,7 +1788,10 @@ class Store extends Service {
|
|
|
1788
1788
|
if (DEBUG) {
|
|
1789
1789
|
assertDestroyingStore(this, 'recordWasInvalid');
|
|
1790
1790
|
}
|
|
1791
|
-
|
|
1791
|
+
error = error || new Error(`unknown invalid error`);
|
|
1792
|
+
error = typeof error === 'string' ? new Error(error) : error;
|
|
1793
|
+
error._parsedErrors = parsedErrors;
|
|
1794
|
+
internalModel.adapterDidInvalidate(error);
|
|
1792
1795
|
}
|
|
1793
1796
|
assert(`store.recordWasInvalid has been removed`);
|
|
1794
1797
|
}
|
|
@@ -2182,7 +2185,8 @@ class Store extends Service {
|
|
|
2182
2185
|
|
|
2183
2186
|
//We first make sure the primary data has been updated
|
|
2184
2187
|
//TODO try to move notification to the user to the end of the runloop
|
|
2185
|
-
internalModel.
|
|
2188
|
+
internalModel._recordData.didCommit(data);
|
|
2189
|
+
this.recordArrayManager.recordDidChange(internalModel.identifier);
|
|
2186
2190
|
|
|
2187
2191
|
if (payload && payload.included) {
|
|
2188
2192
|
this._push({ data: null, included: payload.included });
|
|
@@ -2191,12 +2195,14 @@ class Store extends Service {
|
|
|
2191
2195
|
return record;
|
|
2192
2196
|
},
|
|
2193
2197
|
(e) => {
|
|
2194
|
-
|
|
2195
|
-
|
|
2198
|
+
let err = e;
|
|
2199
|
+
if (!e) {
|
|
2200
|
+
err = new Error(`Unknown Error Occurred During Request`);
|
|
2201
|
+
} else if (typeof e === 'string') {
|
|
2202
|
+
err = new Error(e);
|
|
2196
2203
|
}
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
throw error;
|
|
2204
|
+
internalModel.adapterDidInvalidate(err);
|
|
2205
|
+
throw err;
|
|
2200
2206
|
}
|
|
2201
2207
|
);
|
|
2202
2208
|
}
|
|
@@ -22,7 +22,6 @@ import type { Dict } from '@ember-data/types/q/utils';
|
|
|
22
22
|
import coerceId from './coerce-id';
|
|
23
23
|
import { _bind, _guard, _objectIsAlive, guardDestroyedStore } from './common';
|
|
24
24
|
import type Store from './core-store';
|
|
25
|
-
import { errorsArrayToHash } from './errors-utils';
|
|
26
25
|
import ShimModelClass from './model/shim-model-class';
|
|
27
26
|
import RequestCache from './request-cache';
|
|
28
27
|
import { normalizeResponseHelper } from './serializer-response';
|
|
@@ -170,24 +169,7 @@ export default class FetchManager {
|
|
|
170
169
|
if (adapterPayload) {
|
|
171
170
|
return normalizeResponseHelper(serializer, store, modelClass, adapterPayload, snapshot.id, operation);
|
|
172
171
|
}
|
|
173
|
-
}
|
|
174
|
-
function (error) {
|
|
175
|
-
if (error && error.isAdapterError === true && error.code === 'InvalidError') {
|
|
176
|
-
let parsedErrors = error.errors;
|
|
177
|
-
|
|
178
|
-
// TODO deprecate extractErrors being called and/or make it part of the public interface
|
|
179
|
-
if (serializer && typeof serializer.extractErrors === 'function') {
|
|
180
|
-
parsedErrors = serializer.extractErrors(store, modelClass, error, snapshot.id);
|
|
181
|
-
} else {
|
|
182
|
-
parsedErrors = errorsArrayToHash(error.errors);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
throw { error, parsedErrors };
|
|
186
|
-
} else {
|
|
187
|
-
throw { error };
|
|
188
|
-
}
|
|
189
|
-
},
|
|
190
|
-
label
|
|
172
|
+
}
|
|
191
173
|
);
|
|
192
174
|
resolver.resolve(promise);
|
|
193
175
|
}
|
|
@@ -326,38 +308,32 @@ export default class FetchManager {
|
|
|
326
308
|
}),
|
|
327
309
|
this._store,
|
|
328
310
|
label
|
|
329
|
-
).then(
|
|
330
|
-
(
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
);
|
|
311
|
+
).then((adapterPayload) => {
|
|
312
|
+
assert(
|
|
313
|
+
`You made a 'findRecord' request for a '${modelName}' with id '${id}', but the adapter's response did not have any data`,
|
|
314
|
+
!!payloadIsNotBlank(adapterPayload)
|
|
315
|
+
);
|
|
316
|
+
let serializer = this._store.serializerFor(modelName);
|
|
317
|
+
let payload = normalizeResponseHelper(serializer, this._store, klass, adapterPayload, id, 'findRecord');
|
|
318
|
+
assert(
|
|
319
|
+
`Ember Data expected the primary data returned from a 'findRecord' response to be an object but instead it found an array.`,
|
|
320
|
+
!Array.isArray(payload.data)
|
|
321
|
+
);
|
|
322
|
+
assert(
|
|
323
|
+
`The 'findRecord' request for ${modelName}:${id} resolved indicating success but contained no primary data. To indicate a 404 not found you should either reject the promise returned by the adapter's findRecord method or throw a NotFoundError.`,
|
|
324
|
+
'data' in payload && payload.data !== null && typeof payload.data === 'object'
|
|
325
|
+
);
|
|
345
326
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
327
|
+
warn(
|
|
328
|
+
`You requested a record of type '${modelName}' with id '${id}' but the adapter returned a payload with primary data having an id of '${payload.data.id}'. Use 'store.findRecord()' when the requested id is the same as the one returned by the adapter. In other cases use 'store.queryRecord()' instead.`,
|
|
329
|
+
coerceId(payload.data.id) === coerceId(id),
|
|
330
|
+
{
|
|
331
|
+
id: 'ds.store.findRecord.id-mismatch',
|
|
332
|
+
}
|
|
333
|
+
);
|
|
353
334
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
(error) => {
|
|
357
|
-
throw error;
|
|
358
|
-
},
|
|
359
|
-
`DS: Extract payload of '${modelName}'`
|
|
360
|
-
);
|
|
335
|
+
return payload;
|
|
336
|
+
});
|
|
361
337
|
|
|
362
338
|
fetchItem.resolver.resolve(promise);
|
|
363
339
|
}
|
package/addon/-private/index.ts
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
@module @ember-data/store
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { assert, deprecate } from '@ember/debug';
|
|
6
|
+
|
|
7
|
+
import { DEPRECATE_HELPERS } from '@ember-data/private-build-infra/deprecations';
|
|
8
|
+
|
|
9
|
+
import _normalize from './normalize-model-name';
|
|
10
|
+
|
|
5
11
|
export { default as Store, storeFor } from './core-store';
|
|
6
12
|
|
|
7
13
|
export { recordIdentifierFor } from './internal-model-factory';
|
|
@@ -14,10 +20,24 @@ export {
|
|
|
14
20
|
setIdentifierResetMethod,
|
|
15
21
|
} from './identifier-cache';
|
|
16
22
|
|
|
17
|
-
export
|
|
18
|
-
|
|
23
|
+
export function normalizeModelName(modelName: string) {
|
|
24
|
+
if (DEPRECATE_HELPERS) {
|
|
25
|
+
deprecate(
|
|
26
|
+
`the helper function normalizeModelName is deprecated. You should use model names that are already normalized, or use string helpers of your own. This function is primarily an alias for dasherize from @ember/string.`,
|
|
27
|
+
false,
|
|
28
|
+
{
|
|
29
|
+
id: 'ember-data:deprecate-normalize-modelname-helper',
|
|
30
|
+
for: 'ember-data',
|
|
31
|
+
until: '5.0',
|
|
32
|
+
since: { available: '4.8', enabled: '4.8' },
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
return _normalize(modelName);
|
|
36
|
+
}
|
|
37
|
+
assert(`normalizeModelName support has been removed`);
|
|
38
|
+
}
|
|
19
39
|
|
|
20
|
-
export {
|
|
40
|
+
export { default as coerceId } from './coerce-id';
|
|
21
41
|
|
|
22
42
|
// `ember-data-model-fragments` relies on `InternalModel`
|
|
23
43
|
export { default as InternalModel } from './model/internal-model';
|
|
@@ -5,13 +5,14 @@ import { DEBUG } from '@glimmer/env';
|
|
|
5
5
|
import { HAS_MODEL_PACKAGE } from '@ember-data/private-build-infra';
|
|
6
6
|
import type { DSModel } from '@ember-data/types/q/ds-model';
|
|
7
7
|
import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
8
|
+
import type { MinimumSerializerInterface } from '@ember-data/types/q/minimum-serializer-interface';
|
|
8
9
|
import type { ChangedAttributesHash, RecordData } from '@ember-data/types/q/record-data';
|
|
9
10
|
import type { JsonApiResource, JsonApiValidationError } from '@ember-data/types/q/record-data-json-api';
|
|
10
11
|
import type { RecordInstance } from '@ember-data/types/q/record-instance';
|
|
11
12
|
|
|
12
13
|
import type Store from '../core-store';
|
|
13
|
-
import { errorsHashToArray } from '../errors-utils';
|
|
14
14
|
import { internalModelFactoryFor } from '../internal-model-factory';
|
|
15
|
+
import type ShimModelClass from './shim-model-class';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
@module @ember-data/store
|
|
@@ -27,6 +28,11 @@ function isDSModel(record: RecordInstance | null): record is DSModel {
|
|
|
27
28
|
);
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
type AdapterErrors = Error & { errors?: unknown[]; isAdapterError?: true; code?: string };
|
|
32
|
+
type SerializerWithParseErrors = MinimumSerializerInterface & {
|
|
33
|
+
extractErrors?(store: Store, modelClass: ShimModelClass, error: AdapterErrors, recordId: string | null): any;
|
|
34
|
+
};
|
|
35
|
+
|
|
30
36
|
export default class InternalModel {
|
|
31
37
|
declare _id: string | null;
|
|
32
38
|
declare modelName: string;
|
|
@@ -91,7 +97,10 @@ export default class InternalModel {
|
|
|
91
97
|
let newIdentifier = { type: this.identifier.type, lid: this.identifier.lid, id: value };
|
|
92
98
|
// TODO potentially this needs to handle merged result
|
|
93
99
|
this.store.identifierCache.updateRecordIdentifier(this.identifier, newIdentifier);
|
|
94
|
-
this.
|
|
100
|
+
if (this.hasRecord) {
|
|
101
|
+
// TODO this should likely *mostly* be the a different bucket
|
|
102
|
+
this.store._notificationManager.notify(this.identifier, 'property', 'id');
|
|
103
|
+
}
|
|
95
104
|
}
|
|
96
105
|
}
|
|
97
106
|
|
|
@@ -392,16 +401,6 @@ export default class InternalModel {
|
|
|
392
401
|
}
|
|
393
402
|
}
|
|
394
403
|
|
|
395
|
-
notifyPropertyChange(key: string) {
|
|
396
|
-
if (this.hasRecord) {
|
|
397
|
-
// TODO this should likely *mostly* be the `attributes` bucket
|
|
398
|
-
// but it seems for local mutations we rely on computed updating
|
|
399
|
-
// iteself when set. As we design our own thing we may need to change
|
|
400
|
-
// that.
|
|
401
|
-
this.store._notificationManager.notify(this.identifier, 'property', key);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
404
|
notifyStateChange(key?: string) {
|
|
406
405
|
if (this.hasRecord) {
|
|
407
406
|
this.store._notificationManager.notify(this.identifier, 'state');
|
|
@@ -533,52 +532,26 @@ export default class InternalModel {
|
|
|
533
532
|
this._isUpdatingId = false;
|
|
534
533
|
}
|
|
535
534
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
hasErrors(): boolean {
|
|
549
|
-
// TODO add assertion forcing consuming RecordData's to implement getErrors
|
|
550
|
-
if (this._recordData.getErrors) {
|
|
551
|
-
return this._recordData.getErrors(this.identifier).length > 0;
|
|
552
|
-
} else {
|
|
553
|
-
let record = this.store._instanceCache.peek({ identifier: this.identifier, bucket: 'record' });
|
|
554
|
-
// we can't have errors if we never tried loading
|
|
555
|
-
if (!record) {
|
|
556
|
-
return false;
|
|
535
|
+
// FOR USE DURING COMMIT PROCESS
|
|
536
|
+
adapterDidInvalidate(error: Error & { errors?: JsonApiValidationError[]; isAdapterError?: true; code?: string }) {
|
|
537
|
+
if (error && error.isAdapterError === true && error.code === 'InvalidError') {
|
|
538
|
+
let serializer = this.store.serializerFor(this.modelName) as SerializerWithParseErrors;
|
|
539
|
+
|
|
540
|
+
// TODO @deprecate extractErrors being called
|
|
541
|
+
// TODO remove extractErrors from the default serializers.
|
|
542
|
+
if (serializer && typeof serializer.extractErrors === 'function') {
|
|
543
|
+
let errorsHash = serializer.extractErrors(this.store, this.store.modelFor(this.modelName), error, this.id);
|
|
544
|
+
error.errors = errorsHashToArray(errorsHash);
|
|
557
545
|
}
|
|
558
|
-
let errors = (record as DSModel).errors;
|
|
559
|
-
return errors.length > 0;
|
|
560
546
|
}
|
|
561
|
-
}
|
|
562
547
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
let attribute;
|
|
569
|
-
if (error && parsedErrors) {
|
|
570
|
-
// TODO add assertion forcing consuming RecordData's to implement getErrors
|
|
571
|
-
if (!this._recordData.getErrors) {
|
|
572
|
-
let record = this.store._instanceCache.getRecord(this.identifier) as DSModel;
|
|
573
|
-
let errors = record.errors;
|
|
574
|
-
for (attribute in parsedErrors) {
|
|
575
|
-
if (Object.prototype.hasOwnProperty.call(parsedErrors, attribute)) {
|
|
576
|
-
errors.add(attribute, parsedErrors[attribute]);
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
}
|
|
548
|
+
if (error.errors) {
|
|
549
|
+
assert(
|
|
550
|
+
`Expected the RecordData implementation for ${this.identifier} to have a getErrors(identifier) method for retreiving errors.`,
|
|
551
|
+
typeof this._recordData.getErrors === 'function'
|
|
552
|
+
);
|
|
580
553
|
|
|
581
|
-
let jsonApiErrors: JsonApiValidationError[] =
|
|
554
|
+
let jsonApiErrors: JsonApiValidationError[] = error.errors;
|
|
582
555
|
if (jsonApiErrors.length === 0) {
|
|
583
556
|
jsonApiErrors = [{ title: 'Invalid Error', detail: '', source: { pointer: '/data' } }];
|
|
584
557
|
}
|
|
@@ -588,15 +561,40 @@ export default class InternalModel {
|
|
|
588
561
|
}
|
|
589
562
|
}
|
|
590
563
|
|
|
591
|
-
|
|
592
|
-
this.
|
|
564
|
+
toString() {
|
|
565
|
+
return `<${this.modelName}:${this.id}>`;
|
|
593
566
|
}
|
|
567
|
+
}
|
|
594
568
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
569
|
+
function makeArray(value) {
|
|
570
|
+
return Array.isArray(value) ? value : [value];
|
|
571
|
+
}
|
|
598
572
|
|
|
599
|
-
|
|
600
|
-
|
|
573
|
+
const PRIMARY_ATTRIBUTE_KEY = 'base';
|
|
574
|
+
|
|
575
|
+
function errorsHashToArray(errors): JsonApiValidationError[] {
|
|
576
|
+
const out: JsonApiValidationError[] = [];
|
|
577
|
+
|
|
578
|
+
if (errors) {
|
|
579
|
+
Object.keys(errors).forEach((key) => {
|
|
580
|
+
let messages = makeArray(errors[key]);
|
|
581
|
+
for (let i = 0; i < messages.length; i++) {
|
|
582
|
+
let title = 'Invalid Attribute';
|
|
583
|
+
let pointer = `/data/attributes/${key}`;
|
|
584
|
+
if (key === PRIMARY_ATTRIBUTE_KEY) {
|
|
585
|
+
title = 'Invalid Document';
|
|
586
|
+
pointer = `/data`;
|
|
587
|
+
}
|
|
588
|
+
out.push({
|
|
589
|
+
title: title,
|
|
590
|
+
detail: messages[i],
|
|
591
|
+
source: {
|
|
592
|
+
pointer: pointer,
|
|
593
|
+
},
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
});
|
|
601
597
|
}
|
|
598
|
+
|
|
599
|
+
return out;
|
|
602
600
|
}
|
|
@@ -4,9 +4,6 @@ import { dasherize } from '@ember/string';
|
|
|
4
4
|
@module @ember-data/store
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
// All modelNames are dasherized internally. Changing this function may
|
|
8
|
-
// require changes to other normalization hooks (such as typeForRoot).
|
|
9
|
-
|
|
10
7
|
/**
|
|
11
8
|
This method normalizes a modelName into the format Ember Data uses
|
|
12
9
|
internally by dasherizing it.
|
|
@@ -14,6 +11,7 @@ import { dasherize } from '@ember/string';
|
|
|
14
11
|
@method normalizeModelName
|
|
15
12
|
@static
|
|
16
13
|
@public
|
|
14
|
+
@deprecated
|
|
17
15
|
@for @ember-data/store
|
|
18
16
|
@param {String} modelName
|
|
19
17
|
@return {String} normalizedModelName
|
|
@@ -75,11 +75,7 @@ export default class RecordDataStoreWrapper implements StoreWrapper {
|
|
|
75
75
|
const resource = constructResource(type, id, lid);
|
|
76
76
|
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (internalModel) {
|
|
81
|
-
internalModel.notifyErrorsChange();
|
|
82
|
-
}
|
|
78
|
+
this._store._notificationManager.notify(identifier, 'errors');
|
|
83
79
|
}
|
|
84
80
|
|
|
85
81
|
_flushNotifications(): void {
|
|
@@ -62,7 +62,7 @@ export default class RequestCache {
|
|
|
62
62
|
state: 'rejected',
|
|
63
63
|
request: queryRequest,
|
|
64
64
|
type,
|
|
65
|
-
response: { data: error
|
|
65
|
+
response: { data: error },
|
|
66
66
|
} as InternalRequest;
|
|
67
67
|
finalizedRequest[Touching] = request[Touching];
|
|
68
68
|
this._addDone(finalizedRequest);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ember-data/store",
|
|
3
|
-
"version": "4.8.0-alpha.
|
|
3
|
+
"version": "4.8.0-alpha.2",
|
|
4
4
|
"description": "The default blueprint for ember-cli addons.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ember-addon"
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"start": "ember serve"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@ember-data/canary-features": "4.8.0-alpha.
|
|
21
|
-
"@ember-data/private-build-infra": "4.8.0-alpha.
|
|
20
|
+
"@ember-data/canary-features": "4.8.0-alpha.2",
|
|
21
|
+
"@ember-data/private-build-infra": "4.8.0-alpha.2",
|
|
22
22
|
"@ember/string": "^3.0.0",
|
|
23
23
|
"@embroider/macros": "^1.8.3",
|
|
24
24
|
"@glimmer/tracking": "^1.1.2",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"ember-cli-typescript": "^5.1.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@ember-data/unpublished-test-infra": "4.8.0-alpha.
|
|
32
|
+
"@ember-data/unpublished-test-infra": "4.8.0-alpha.2",
|
|
33
33
|
"@ember/optional-features": "^2.0.0",
|
|
34
34
|
"@ember/test-helpers": "~2.7.0",
|
|
35
35
|
"@types/ember": "^4.0.0",
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
@module @ember-data/adapter/error
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const SOURCE_POINTER_REGEXP = /^\/?data\/(attributes|relationships)\/(.*)/;
|
|
6
|
-
const SOURCE_POINTER_PRIMARY_REGEXP = /^\/?data/;
|
|
7
|
-
const PRIMARY_ATTRIBUTE_KEY = 'base';
|
|
8
|
-
|
|
9
|
-
function makeArray(value) {
|
|
10
|
-
return Array.isArray(value) ? value : [value];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
Convert an hash of errors into an array with errors in JSON-API format.
|
|
15
|
-
```javascript
|
|
16
|
-
import DS from 'ember-data';
|
|
17
|
-
|
|
18
|
-
const { errorsHashToArray } = DS;
|
|
19
|
-
|
|
20
|
-
let errors = {
|
|
21
|
-
base: 'Invalid attributes on saving this record',
|
|
22
|
-
name: 'Must be present',
|
|
23
|
-
age: ['Must be present', 'Must be a number']
|
|
24
|
-
};
|
|
25
|
-
let errorsArray = errorsHashToArray(errors);
|
|
26
|
-
// [
|
|
27
|
-
// {
|
|
28
|
-
// title: "Invalid Document",
|
|
29
|
-
// detail: "Invalid attributes on saving this record",
|
|
30
|
-
// source: { pointer: "/data" }
|
|
31
|
-
// },
|
|
32
|
-
// {
|
|
33
|
-
// title: "Invalid Attribute",
|
|
34
|
-
// detail: "Must be present",
|
|
35
|
-
// source: { pointer: "/data/attributes/name" }
|
|
36
|
-
// },
|
|
37
|
-
// {
|
|
38
|
-
// title: "Invalid Attribute",
|
|
39
|
-
// detail: "Must be present",
|
|
40
|
-
// source: { pointer: "/data/attributes/age" }
|
|
41
|
-
// },
|
|
42
|
-
// {
|
|
43
|
-
// title: "Invalid Attribute",
|
|
44
|
-
// detail: "Must be a number",
|
|
45
|
-
// source: { pointer: "/data/attributes/age" }
|
|
46
|
-
// }
|
|
47
|
-
// ]
|
|
48
|
-
```
|
|
49
|
-
@method errorsHashToArray
|
|
50
|
-
@for @ember-data/adapter/error
|
|
51
|
-
@static
|
|
52
|
-
@public
|
|
53
|
-
@param {Object} errors hash with errors as properties
|
|
54
|
-
@return {Array} array of errors in JSON-API format
|
|
55
|
-
*/
|
|
56
|
-
export function errorsHashToArray(errors) {
|
|
57
|
-
let out = [];
|
|
58
|
-
|
|
59
|
-
if (errors) {
|
|
60
|
-
Object.keys(errors).forEach((key) => {
|
|
61
|
-
let messages = makeArray(errors[key]);
|
|
62
|
-
for (let i = 0; i < messages.length; i++) {
|
|
63
|
-
let title = 'Invalid Attribute';
|
|
64
|
-
let pointer = `/data/attributes/${key}`;
|
|
65
|
-
if (key === PRIMARY_ATTRIBUTE_KEY) {
|
|
66
|
-
title = 'Invalid Document';
|
|
67
|
-
pointer = `/data`;
|
|
68
|
-
}
|
|
69
|
-
out.push({
|
|
70
|
-
title: title,
|
|
71
|
-
detail: messages[i],
|
|
72
|
-
source: {
|
|
73
|
-
pointer: pointer,
|
|
74
|
-
},
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return out;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
Convert an array of errors in JSON-API format into an object.
|
|
85
|
-
|
|
86
|
-
```javascript
|
|
87
|
-
import DS from 'ember-data';
|
|
88
|
-
|
|
89
|
-
const { errorsArrayToHash } = DS;
|
|
90
|
-
|
|
91
|
-
let errorsArray = [
|
|
92
|
-
{
|
|
93
|
-
title: 'Invalid Attribute',
|
|
94
|
-
detail: 'Must be present',
|
|
95
|
-
source: { pointer: '/data/attributes/name' }
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
title: 'Invalid Attribute',
|
|
99
|
-
detail: 'Must be present',
|
|
100
|
-
source: { pointer: '/data/attributes/age' }
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
title: 'Invalid Attribute',
|
|
104
|
-
detail: 'Must be a number',
|
|
105
|
-
source: { pointer: '/data/attributes/age' }
|
|
106
|
-
}
|
|
107
|
-
];
|
|
108
|
-
|
|
109
|
-
let errors = errorsArrayToHash(errorsArray);
|
|
110
|
-
// {
|
|
111
|
-
// "name": ["Must be present"],
|
|
112
|
-
// "age": ["Must be present", "must be a number"]
|
|
113
|
-
// }
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
@method errorsArrayToHash
|
|
117
|
-
@static
|
|
118
|
-
@for @ember-data/adapter/error
|
|
119
|
-
@public
|
|
120
|
-
@param {Array} errors array of errors in JSON-API format
|
|
121
|
-
@return {Object}
|
|
122
|
-
*/
|
|
123
|
-
export function errorsArrayToHash(errors) {
|
|
124
|
-
let out = {};
|
|
125
|
-
|
|
126
|
-
if (errors) {
|
|
127
|
-
errors.forEach((error) => {
|
|
128
|
-
if (error.source && error.source.pointer) {
|
|
129
|
-
let key = error.source.pointer.match(SOURCE_POINTER_REGEXP);
|
|
130
|
-
|
|
131
|
-
if (key) {
|
|
132
|
-
key = key[2];
|
|
133
|
-
} else if (error.source.pointer.search(SOURCE_POINTER_PRIMARY_REGEXP) !== -1) {
|
|
134
|
-
key = PRIMARY_ATTRIBUTE_KEY;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (key) {
|
|
138
|
-
out[key] = out[key] || [];
|
|
139
|
-
out[key].push(error.detail || error.title);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return out;
|
|
146
|
-
}
|