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

Upgrade to @feathersjs/adapter-commons and latest common service features #181

Merged
merged 5 commits into from
Dec 28, 2018
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ node_modules
.lock-wscript

dist/
*.sqlite
*.sqlite*
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
language: node_js
node_js:
- 10
- 6
- 'node'
- 8
sudo: false
install: npm install
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ __Options:__
- `id` (*optional*, default: `'id'`) - The name of the id field property.
- `events` (*optional*) - A list of [custom service events](https://docs.feathersjs.com/api/events.html#custom-events) sent by this service
- `paginate` (*optional*) - A [pagination object](https://docs.feathersjs.com/api/databases/common.html#pagination) containing a `default` and `max` page size
- `multi` (*optional*) - Allow `create` with arrays and `update` and `remove` with `id` `null` to change multiple items. Can be `true` for all methods or an array of allowed methods (e.g. `[ 'remove', 'create' ]`)
- `whitelist` (*optional*) - A list of additional query parameters to allow (e..g `[ '$regex', '$geoNear' ]`). Default is the supported `operators`

### `adapter.createQuery(query)`

Expand Down Expand Up @@ -281,8 +283,24 @@ app.service('mesages').hooks({
});
```

### Error handling

As of version 4.0.0 `feathers-knex` 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-knex').ERROR]`

```js
const { ERROR } = require('feathers-knex');

try {
await sequelizeService.doSomething();
} catch(error) {
// error is a FeathersError with just the message
// Safely retrieve the Knex error
const knexError = error[ERROR];
}
```

## License

Copyright (c) 2016
Copyright (c) 2019

Licensed under the [MIT license](LICENSE).
58 changes: 27 additions & 31 deletions lib/error-handler.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,25 @@
const errors = require('@feathersjs/errors');
const ERROR = Symbol('feathers-knex/error');

module.exports = function errorHandler (error) {
const { message } = error;
let feathersError = error;

// TODO (EK): Map PG, Oracle, etc. errors

// NOTE: SQLState values from
// https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-error-sqlstates.html

if (error.sqlState && error.sqlState.length) {
// remove SQLSTATE marker (#) and pad/truncate SQLSTATE to 5 chars
let sqlState = ('00000' + error.sqlState.replace('#', '')).slice(-5);
const sqlState = ('00000' + error.sqlState.replace('#', '')).slice(-5);

switch (sqlState.slice(0, 2)) {
case '02':
feathersError = new errors.NotFound(error);
feathersError = new errors.NotFound(message);
break;
case '28':
feathersError = new errors.Forbidden(error);
feathersError = new errors.Forbidden(message);
break;
case '08':
case '0A':
case '0K':
feathersError = new errors.Unavailable(error);
feathersError = new errors.Unavailable(message);
break;
case '20':
case '21':
Expand All @@ -33,66 +30,65 @@ module.exports = function errorHandler (error) {
case '40':
case '42':
case '70':
feathersError = new errors.BadRequest(error);
feathersError = new errors.BadRequest(message);
break;
default:
feathersError = new errors.GeneralError(error);
feathersError = new errors.GeneralError(message);
}
}

// NOTE (EK): Error codes taken from
// https://www.sqlite.org/c3ref/c_abort.html

if (error.code === 'SQLITE_ERROR') {
} else if (error.code === 'SQLITE_ERROR') {
// NOTE (EK): Error codes taken from
// https://www.sqlite.org/c3ref/c_abort.html
switch (error.errno) {
case 1:
case 8:
case 18:
case 19:
case 20:
feathersError = new errors.BadRequest(error);
feathersError = new errors.BadRequest(message);
break;
case 2:
feathersError = new errors.Unavailable(error);
feathersError = new errors.Unavailable(message);
break;
case 3:
case 23:
feathersError = new errors.Forbidden(error);
feathersError = new errors.Forbidden(message);
break;
case 12:
feathersError = new errors.NotFound(error);
feathersError = new errors.NotFound(message);
break;
default:
feathersError = new errors.GeneralError(error);
feathersError = new errors.GeneralError(message);
break;
}
}

// NOTE: Error codes taken from
// https://www.postgresql.org/docs/9.6/static/errcodes-appendix.html
if (typeof error.code === 'string' && error.severity && error.routine) {
} else if (typeof error.code === 'string' && error.severity && error.routine) {
// NOTE: Error codes taken from
// https://www.postgresql.org/docs/9.6/static/errcodes-appendix.html
// Omit query information
const messages = error.message.split('-');
error.message = messages[messages.length - 1];

switch (error.code.slice(0, 2)) {
case '22':
case '23':
feathersError = new errors.BadRequest(error);
feathersError = new errors.BadRequest(message);
break;
case '28':
feathersError = new errors.Forbidden(error);
feathersError = new errors.Forbidden(message);
break;
case '3D':
case '3F':
case '42':
feathersError = new errors.Unprocessable(error);
feathersError = new errors.Unprocessable(message);
break;
default:
feathersError = new errors.GeneralError(error);
feathersError = new errors.GeneralError(message);
break;
}
} else if (!(error instanceof errors.FeathersError)) {
feathersError = new errors.GeneralError(message);
}

feathersError[ERROR] = error;

throw feathersError;
};
14 changes: 6 additions & 8 deletions lib/hooks.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
const debug = require('debug')('feathers-knex-transaction');

const RollbackReason = function(error) {
const RollbackReason = function (error) {
this.error = error;
};

const start = (options) => {
const start = () => {
return hook => new Promise(resolve => {
if (!hook.service.Model || typeof hook.service.Model.transaction !== 'function') {
return resolve(hook);
Expand Down Expand Up @@ -33,10 +33,10 @@ const start = (options) => {
});
};

const end = (options) => {
const end = () => {
return hook => {
if (hook.params.transaction) {
const { promise, trx, id, count } = hook.params.transaction;
const { trx, id, count } = hook.params.transaction;

if (count > 0) {
hook.params.transaction.count -= 1;
Expand All @@ -46,21 +46,19 @@ const end = (options) => {
hook.params.transaction = undefined;

return trx.commit()
.then(() => promise)
.then(() => debug('finished transaction %s with success', id))
.then(() => hook);
}
return hook;
};
};

const rollback = (options) => {
const rollback = () => {
return hook => {
if (hook.params.transaction) {
const { promise, trx, id } = hook.params.transaction;
const { trx, id } = hook.params.transaction;
return trx.rollback(new RollbackReason(hook.error))
.then(() => debug('rolling back transaction %s', id))
.then(() => promise)
.then(() => hook);
}
return hook;
Expand Down
Loading