Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUGFIX CHORE] fix native-class use by eliminating use of Ember.Error #5753

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 129 additions & 44 deletions addon/-private/adapters/errors.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import { makeArray } from '@ember/array';
import { isPresent } from '@ember/utils';
import EmberError from '@ember/error';
import { assert } from '@ember/debug';
import { DEBUG } from '@glimmer/env';

const SOURCE_POINTER_REGEXP = /^\/?data\/(attributes|relationships)\/(.*)/;
const SOURCE_POINTER_PRIMARY_REGEXP = /^\/?data/;
const PRIMARY_ATTRIBUTE_KEY = 'base';

function ExtendBuiltin(klass) {
function ExtendableBuiltin() {
klass.apply(this, arguments);
}

ExtendableBuiltin.prototype = Object.create(klass.prototype);
ExtendableBuiltin.prototype.constructor = ExtendableBuiltin;
return ExtendableBuiltin;
}

/**
A `DS.AdapterError` is used by an adapter to signal that an error occurred
during a request to an external API. It indicates a generic error, and
Expand Down Expand Up @@ -74,39 +84,40 @@ const PRIMARY_ATTRIBUTE_KEY = 'base';
@class AdapterError
@namespace DS
*/
export function AdapterError(errors, message = 'Adapter operation failed') {
this.isAdapterError = true;
EmberError.call(this, message);

this.errors = errors || [
{
title: 'Adapter Error',
detail: message,
},
];
}

function extendFn(ErrorClass) {
return function({ message: defaultMessage } = {}) {
return extend(ErrorClass, defaultMessage);
};
}

function extend(ParentErrorClass, defaultMessage) {
let ErrorClass = function(errors, message) {
assert('`AdapterError` expects json-api formatted errors array.', Array.isArray(errors || []));
ParentErrorClass.call(this, errors, message || defaultMessage);
};
ErrorClass.prototype = Object.create(ParentErrorClass.prototype);
ErrorClass.extend = extendFn(ErrorClass);
export class AdapterError extends ExtendBuiltin(Error) {
constructor(errors, message = 'Adapter operation failed') {
super();
this.isAdapterError = true;

this.errors = errors || [
{
title: 'Adapter Error',
detail: message,
},
];

let error = Error.call(this, message);
this.stack = error.stack;
this.description = error.description;
this.fileName = error.fileName;
this.lineNumber = error.lineNumber;
this.message = error.message;
this.name = error.name;
this.number = error.number;
this.code = error.code;
}

return ErrorClass;
// TODO deprecate this in favor of user doing the native class extending
static extend({ message: defaultMessage } = {}) {
return class CustomError extends this {
constructor(errors, message = defaultMessage) {
super(errors, message);
}
};
}
}

AdapterError.prototype = Object.create(EmberError.prototype);

AdapterError.extend = extendFn(AdapterError);

/**
A `DS.InvalidError` is used by an adapter to signal the external API
was unable to process a request because the content was not
Expand Down Expand Up @@ -166,10 +177,17 @@ AdapterError.extend = extendFn(AdapterError);
@namespace DS
@extends AdapterError
*/
export const InvalidError = extend(
AdapterError,
'The adapter rejected the commit because it was invalid'
);
export class InvalidError extends AdapterError {
constructor(errors, message = 'The adapter rejected the commit because it was invalid') {
if (DEBUG) {
assert(
'`AdapterError` expects json-api formatted errors array.',
Array.isArray(errors || [])
);
}
super(errors, message);
}
}

/**
A `DS.TimeoutError` is used by an adapter to signal that a request
Expand Down Expand Up @@ -204,7 +222,17 @@ export const InvalidError = extend(
@namespace DS
@extends AdapterError
*/
export const TimeoutError = extend(AdapterError, 'The adapter operation timed out');
export class TimeoutError extends AdapterError {
constructor(errors, message = 'The adapter operation timed out') {
if (DEBUG) {
assert(
'`AdapterError` expects json-api formatted errors array.',
Array.isArray(errors || [])
);
}
super(errors, message);
}
}

/**
A `DS.AbortError` is used by an adapter to signal that a request to
Expand All @@ -216,7 +244,17 @@ export const TimeoutError = extend(AdapterError, 'The adapter operation timed ou
@namespace DS
@extends AdapterError
*/
export const AbortError = extend(AdapterError, 'The adapter operation was aborted');
export class AbortError extends AdapterError {
constructor(errors, message = 'The adapter operation was aborted') {
if (DEBUG) {
assert(
'`AdapterError` expects json-api formatted errors array.',
Array.isArray(errors || [])
);
}
super(errors, message);
}
}

/**
A `DS.UnauthorizedError` equates to a HTTP `401 Unauthorized` response
Expand Down Expand Up @@ -252,7 +290,17 @@ export const AbortError = extend(AdapterError, 'The adapter operation was aborte
@namespace DS
@extends AdapterError
*/
export const UnauthorizedError = extend(AdapterError, 'The adapter operation is unauthorized');
export class UnauthorizedError extends AdapterError {
constructor(errors, message = 'The adapter operation is unauthorized') {
if (DEBUG) {
assert(
'`AdapterError` expects json-api formatted errors array.',
Array.isArray(errors || [])
);
}
super(errors, message);
}
}

/**
A `DS.ForbiddenError` equates to a HTTP `403 Forbidden` response status.
Expand All @@ -265,7 +313,17 @@ export const UnauthorizedError = extend(AdapterError, 'The adapter operation is
@namespace DS
@extends AdapterError
*/
export const ForbiddenError = extend(AdapterError, 'The adapter operation is forbidden');
export class ForbiddenError extends AdapterError {
constructor(errors, message = 'The adapter operation is forbidden') {
if (DEBUG) {
assert(
'`AdapterError` expects json-api formatted errors array.',
Array.isArray(errors || [])
);
}
super(errors, message);
}
}

/**
A `DS.NotFoundError` equates to a HTTP `404 Not Found` response status.
Expand Down Expand Up @@ -304,7 +362,17 @@ export const ForbiddenError = extend(AdapterError, 'The adapter operation is for
@namespace DS
@extends AdapterError
*/
export const NotFoundError = extend(AdapterError, 'The adapter could not find the resource');
export class NotFoundError extends AdapterError {
constructor(errors, message = 'The adapter could not find the resource') {
if (DEBUG) {
assert(
'`AdapterError` expects json-api formatted errors array.',
Array.isArray(errors || [])
);
}
super(errors, message);
}
}

/**
A `DS.ConflictError` equates to a HTTP `409 Conflict` response status.
Expand All @@ -317,7 +385,17 @@ export const NotFoundError = extend(AdapterError, 'The adapter could not find th
@namespace DS
@extends AdapterError
*/
export const ConflictError = extend(AdapterError, 'The adapter operation failed due to a conflict');
export class ConflictError extends AdapterError {
constructor(errors, message = 'The adapter operation failed due to a conflict') {
if (DEBUG) {
assert(
'`AdapterError` expects json-api formatted errors array.',
Array.isArray(errors || [])
);
}
super(errors, message);
}
}

/**
A `DS.ServerError` equates to a HTTP `500 Internal Server Error` response
Expand All @@ -328,10 +406,17 @@ export const ConflictError = extend(AdapterError, 'The adapter operation failed
@namespace DS
@extends AdapterError
*/
export const ServerError = extend(
AdapterError,
'The adapter operation failed due to a server error'
);
export class ServerError extends AdapterError {
constructor(errors, message = 'The adapter operation failed due to a server error') {
if (DEBUG) {
assert(
'`AdapterError` expects json-api formatted errors array.',
Array.isArray(errors || [])
);
}
super(errors, message);
}
}

/**
Convert an hash of errors into an array with errors in JSON-API format.
Expand Down
5 changes: 2 additions & 3 deletions addon/-private/system/model/internal-model.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { set, get } from '@ember/object';
import EmberError from '@ember/error';
import { default as EmberArray, A } from '@ember/array';
import { setOwner, getOwner } from '@ember/application';
import { run } from '@ember/runloop';
Expand Down Expand Up @@ -708,7 +707,7 @@ export default class InternalModel {

setDirtyAttribute(key, value) {
if (this.isDeleted()) {
throw new EmberError(`Attempted to set '${key}' to '${value}' on the deleted record ${this}`);
throw new Error(`Attempted to set '${key}' to '${value}' on the deleted record ${this}`);
}

let currentValue = this.getAttributeValue(key);
Expand Down Expand Up @@ -973,7 +972,7 @@ export default class InternalModel {
errorMessage += 'Called with ' + inspect(context) + '.';
}

throw new EmberError(errorMessage);
throw new Error(errorMessage);
}

triggerLater(...args) {
Expand Down
5 changes: 2 additions & 3 deletions addon/-private/system/model/model.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { isNone } from '@ember/utils';
import EmberError from '@ember/error';
import Evented from '@ember/object/evented';
import EmberObject, { computed, get } from '@ember/object';
import { DEBUG } from '@glimmer/env';
Expand Down Expand Up @@ -1199,7 +1198,7 @@ if (DEBUG) {
this._super(...arguments);

if (!this._internalModel) {
throw new EmberError(
throw new Error(
'You should not call `create` on a model. Instead, call `store.createRecord` with the attributes you would like to set.'
);
}
Expand Down Expand Up @@ -1235,7 +1234,7 @@ if (DEBUG) {
let idDesc = lookupDescriptor(this, 'id');

if (idDesc.get !== ID_DESCRIPTOR.get) {
throw new EmberError(
throw new Error(
`You may not set 'id' as an attribute on your model. Please remove any lines that look like: \`id: DS.attr('<type>')\` from ${this.constructor.toString()}`
);
}
Expand Down
7 changes: 3 additions & 4 deletions addon/-private/system/snapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
@module ember-data
*/
import { inspect } from '@ember/debug';
import EmberError from '@ember/error';
import { get } from '@ember/object';
import { assign } from '@ember/polyfills';
import { relationshipStateFor } from './record-data-for';
Expand Down Expand Up @@ -132,7 +131,7 @@ export default class Snapshot {
if (keyName in this._attributes) {
return this._attributes[keyName];
}
throw new EmberError(
throw new Error(
"Model '" + inspect(this.record) + "' has no attribute named '" + keyName + "' defined."
);
}
Expand Down Expand Up @@ -232,7 +231,7 @@ export default class Snapshot {

let relationshipMeta = store._relationshipMetaFor(this.modelName, null, keyName);
if (!(relationshipMeta && relationshipMeta.kind === 'belongsTo')) {
throw new EmberError(
throw new Error(
"Model '" +
inspect(this.record) +
"' has no belongsTo relationship named '" +
Expand Down Expand Up @@ -314,7 +313,7 @@ export default class Snapshot {
let store = this._internalModel.store;
let relationshipMeta = store._relationshipMetaFor(this.modelName, null, keyName);
if (!(relationshipMeta && relationshipMeta.kind === 'hasMany')) {
throw new EmberError(
throw new Error(
"Model '" +
inspect(this.record) +
"' has no hasMany relationship named '" +
Expand Down
3 changes: 1 addition & 2 deletions addon/-private/system/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import { registerWaiter, unregisterWaiter } from '@ember/test';

import { A } from '@ember/array';
import EmberError from '@ember/error';
import { run as emberRunLoop } from '@ember/runloop';
import { set, get, computed } from '@ember/object';
import { getOwner } from '@ember/application';
Expand Down Expand Up @@ -2426,7 +2425,7 @@ const Store = Service.extend({
let factory = getModelFactory(this, this._modelFactoryCache, normalizedModelName);

if (factory === null) {
throw new EmberError(`No model was found for '${normalizedModelName}'`);
throw new Error(`No model was found for '${normalizedModelName}'`);
}

return factory;
Expand Down
11 changes: 0 additions & 11 deletions addon/index.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,9 @@
import { VERSION } from '@ember/version';
import EmberError from '@ember/error';

/**
Ember Data
@module ember-data
@main ember-data
*/

if (VERSION.match(/^1\.([0-9]|1[0-2])\./)) {
throw new EmberError(
'Ember Data requires at least Ember 1.13.0, but you have ' +
VERSION +
'. Please upgrade your version of Ember, then upgrade Ember Data.'
);
}

import {
Snapshot,
DebugAdapter,
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/relationships/inverse-relationships-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -631,9 +631,9 @@ testInDebug(

let env = setupStore({ user: User });

assert.expectAssertion(() => {
assert.throws(() => {
env.store.createRecord('user', { post: null });
}, /No model was found for/);
}, /No model was found for 'post'/);

// but don't error if the relationship is not used
env.store.createRecord('user', {});
Expand Down
3 changes: 0 additions & 3 deletions tests/unit/adapter-errors-test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import EmberError from '@ember/error';

import testInDebug from 'dummy/tests/helpers/test-in-debug';
import { module, test } from 'qunit';

Expand All @@ -11,7 +9,6 @@ test('DS.AdapterError', function(assert) {
let error = new DS.AdapterError();

assert.ok(error instanceof Error);
assert.ok(error instanceof EmberError);
assert.ok(error.isAdapterError);
assert.equal(error.message, 'Adapter operation failed');
});
Expand Down
Loading