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

feat: use objection error types in error-handler #131

Merged
merged 9 commits into from
Nov 21, 2020
Merged
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
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,6 @@ you now have full CRUD for your new todos service!
and to [seed](http://knexjs.org/#Seeds) a table with mock data.

## Error handling

As of version 4.8.0, `feathers-objection` only throws [Feathers Errors](https://docs.feathersjs.com/api/errors.html)
with the message.
On the server, the original error can be retrieved through a secure symbol via `error[require('feathers-objection').ERROR]`.
Expand All @@ -845,6 +844,10 @@ try {
}
```

As of version 7.0.0, `feathers-objection` has normalized errors accross all databases supported by Objection, and makes a best-effort attempt to provide reasonable error messages that can be returned directly to the client.

If these error messages do not work for your needs, the original error is still available using the symbol described above.

## Migrating to `feathers-objection` v2

`feathers-objection` 2.0.0 comes with important security and usability updates
Expand Down Expand Up @@ -887,6 +890,17 @@ The following breaking changes have been introduced:
- `patch` method now enforce `params.query` with upsert
- NotFound error will be thrown when `get` & `update` methods are called with different values in `id` & `params.query.id`

## Migrating to `feathers-objection` v7

`feathers-objection` 7.0.0 comes with improved error handling.

The following breaking changes have been introduced:

- All Databases will return the same types of errors based on the underlying Objection error
- SQL Driver error text is no longer used as the Feathers error message
- Objection errors are mapped more accurately to Feathers errors, e.g.
- Objection's `UniqueViolationError` -> Feathers' `Conflict` error type

## License

Copyright © 2020
Expand Down
151 changes: 59 additions & 92 deletions src/error-handler.js
Original file line number Diff line number Diff line change
@@ -1,108 +1,75 @@
import errors from '@feathersjs/errors';
import {
ValidationError,
NotFoundError,
DBError,
ConstraintViolationError,
UniqueViolationError,
NotNullViolationError,
ForeignKeyViolationError,
CheckViolationError,
DataError
} from 'objection';

const ERROR = Symbol('feathers-knex/error');

export default function errorHandler (error) {
const { message } = error.nativeError || error;
let feathersError = error;
let feathersError;

if (error.code === 'SQLITE_ERROR') {
switch (error.errno) {
case 1:
case 8:
case 18:
case 19:
case 20:
feathersError = new errors.BadRequest(message);
if (error instanceof errors.FeathersError) {
feathersError = error;
} else if (error instanceof ValidationError) {
switch (error.type) {
case 'ModelValidation':
feathersError = new errors.BadRequest(message, error.data);
break;
case 2:
feathersError = new errors.Unavailable(message);
case 'RelationExpression':
feathersError = new errors.BadRequest('Invalid Relation Expression');
break;
case 3:
case 23:
feathersError = new errors.Forbidden(message);
case 'UnallowedRelation':
feathersError = new errors.BadRequest('Unallowed Relation Expression');
break;
case 12:
feathersError = new errors.NotFound(message);
case 'InvalidGraph':
feathersError = new errors.BadRequest('Invalid Relation Graph');
break;
default:
feathersError = new errors.GeneralError(message);
break;
}
} else if (error.statusCode) { // Objection validation error
switch (error.statusCode) {
case 400:
feathersError = new errors.BadRequest(message);
break;

case 401:
feathersError = new errors.NotAuthenticated(message);
break;

case 402:
feathersError = new errors.PaymentError(message);
break;

case 403:
feathersError = new errors.Forbidden(message);
break;

case 404:
feathersError = new errors.NotFound(message);
break;

case 405:
feathersError = new errors.MethodNotAllowed(message);
break;

case 406:
feathersError = new errors.NotAcceptable(message);
break;

case 408:
feathersError = new errors.Timeout(message);
break;

case 409:
feathersError = new errors.Conflict(message);
break;

case 422:
feathersError = new errors.Unprocessable(message);
break;

case 501:
feathersError = new errors.NotImplemented(message);
break;

case 503:
feathersError = new errors.Unavailable(message);
break;

case 500:
default:
feathersError = new errors.GeneralError(message);
}
} else if (typeof error.code === 'string') { // Postgres error code - TODO: Properly detect postgres error
const pgerror = error.code.substring(0, 2);

switch (pgerror) {
case '28':
case '42':
feathersError = new errors.Forbidden(message);
break;

case '20':
case '21':
case '22':
case '23':
feathersError = new errors.BadRequest(message);
break;

default:
feathersError = new errors.GeneralError(message);
feathersError = new errors.BadRequest('Unknown Validation Error');
}
} else if (!(error instanceof errors.FeathersError)) {
} else if (error instanceof NotFoundError) {
feathersError = new errors.NotFound(message);
} else if (error instanceof UniqueViolationError) {
feathersError = new errors.Conflict(`${error.columns.join(', ')} must be unique`, {
columns: error.columns,
table: error.table,
constraint: error.constraint
});
} else if (error instanceof NotNullViolationError) {
feathersError = new errors.BadRequest(`${error.column} must not be null`, {
column: error.column,
table: error.table
});
} else if (error instanceof ForeignKeyViolationError) {
feathersError = new errors.Conflict('Foreign Key Violation', {
table: error.table,
constraint: error.constraint
});
} else if (error instanceof CheckViolationError) {
feathersError = new errors.BadRequest('Check Violation', {
table: error.table,
constraint: error.constraint
});
} else if (error instanceof ConstraintViolationError) {
feathersError = new errors.Conflict('Constraint Violation', {
columns: error.columns,
table: error.table,
constraint: error.constraint
});
} else if (error instanceof DataError) {
feathersError = new errors.BadRequest('Invalid Data');
} else if (error instanceof DBError) {
feathersError = new errors.GeneralError('Unknown Database Error');
} else {
feathersError = new errors.GeneralError(message);
}

Expand Down
Loading