Merge pull request #13 from feathersjs/es6

complete rewrite. Closes #11 and #12.
This commit is contained in:
Eric Kryski 2015-12-12 01:02:00 -07:00 committed by David Luecke
parent 93cbfda170
commit 1acdbacc1a
19 changed files with 778 additions and 486 deletions

7
packages/errors/.babelrc Normal file
View File

@ -0,0 +1,7 @@
{
"plugins": [
"transform-object-assign",
"add-module-exports"
],
"presets": [ "es2015" ]
}

View File

@ -0,0 +1,13 @@
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

View File

@ -1,21 +1,30 @@
{
"globals": {
"it": true,
"describe": true,
"before": true,
"after": true,
"exports": true
},
"curly": true,
"eqeqeq": true,
"immed": true,
"latedef": "nofunc",
"newcap": true,
"noarg": true,
"sub": true,
"undef": true,
"unused": true,
"boss": true,
"eqnull": true,
"node": true
"node": true,
"esnext": true,
"bitwise": true,
"camelcase": true,
"curly": true,
"eqeqeq": true,
"immed": true,
"indent": 2,
"latedef": "nofunc",
"newcap": false,
"noarg": true,
"quotmark": "single",
"regexp": true,
"undef": true,
"unused": true,
"strict": false,
"trailing": true,
"smarttabs": true,
"white": false,
"node": true,
"globals": {
"it": true,
"describe": true,
"before": true,
"beforeEach": true,
"after": true,
"afterEach": true
}
}

View File

@ -0,0 +1,8 @@
./.editorconfig
./.jshintrc
./.travis.yml
./.babelrc
.idea/
src/
test/
!lib/

View File

@ -1,3 +1,4 @@
language: node_js
node_js:
- "0.10"
- 'node'
- 'iojs'

View File

@ -1,44 +0,0 @@
'use strict';
module.exports = function (grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
release: {},
watch: {
scripts: {
files: ['.jshintrc', 'lib/**/*.js', 'Gruntfile.js', 'test/**/*.js'],
tasks: ['jshint','simplemocha'],
options: {
spawn: true
}
}
},
jshint: {
options: {
jshintrc: '.jshintrc'
},
lib: ['lib/**/*.js', 'Gruntfile.js'],
test: 'test/**/*.js',
examples: 'examples/**/*.js'
},
simplemocha: {
all: {
options: {
reporter: 'spec',
clearRequireCache: true
},
src: ['test/**/*.test.js']
}
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-release');
grunt.loadNpmTasks('grunt-simple-mocha');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('test', ['jshint', 'simplemocha']);
grunt.registerTask('default', ['jshint', 'simplemocha', 'watch']);
};

22
packages/errors/LICENSE Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Feathers
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:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,22 +0,0 @@
Copyright (c) 2014 Eric Kryski
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:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,34 +1,12 @@
# feathers-errors [![Build Status](https://travis-ci.org/feathersjs/feathers-errors.svg?branch=master)](https://travis-ci.org/feathersjs/feathers-errors)
# feathers-errors
> Error handling mixin for Feathers apps.
[![Build Status](https://travis-ci.org/feathersjs/feathers-errors.png?branch=master)](https://travis-ci.org/feathersjs/feathers-errors)
> Common error types for feathers apps
## Getting Started
Feathers errors come with feathers by default. So typically you don't need to install it at all. However you can also use `feathers-errors` with express directly as well. In that case you install the module with: `npm install feathers-errors --save`
#### With Feathers
```js
var feathers = require('feathers');
var memory = require('feathers-memory');
var app = feathers()
.use('/users', memory)
.configure(feathers.errors());
```
#### With Express
```js
var app = require('express');
var errors = require('feathers-errors');
var app = express()
.use(errors.fourOhFour)
.use(errors.handler);
```
**Pro Tip:** Just like express middleware, **order matters**. So your error handling should typically be configured last.
Feathers errors come with feathers by default. So typically you don't need to install it at all.
## Documentation
@ -53,69 +31,23 @@ var app = express()
#### Usage:
```js
var feathers = require('feathers');
var app = feathers();
import errors from 'feathers-errors';
var userService = {
find: function(params, callback) {
let userService = {
find(params, callback) {
// If you were to create an error yourself.
callback(new this.app.errors.NotFound('User does not exist'));
callback(new errors.NotFound('User does not exist'));
// You can also simply do something like this if you
// just want to fire back a simple 500 error with your
// custom message.
//
// callback('A generic server error');
},
setup: function(app){
this.app = app;
}
};
app.use('/users', userService)
.configure(feathers.errors());
```
#### 404 Handling:
We have conveniently created a basic 404 middleware already for you. If you want to override it, do this:
```js
var feathers = require('feathers');
var app = feathers();
app.use('/users', userService)
.configure(feathers.errors({
fourOhFour: function(req, res, next){
// Handle your 404's some special way
}
}));
```
#### Custom Error Handling:
We already have an error handler that gets added to the middleware stack when you call `feathers.errors()`. However, if you want customize how you handle errors you can do so like this:
```js
var feathers = require('feathers');
var app = feathers();
app.use('/users', userService)
.configure(feathers.errors({
handler: function(req, res, next){
// Handle your errors the way you want
}
}));
```
## Examples
See [examples directory](https://github.com/feathersjs/feathers-errors/tree/master/examples).
## Contributing
In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](http://gruntjs.com/).
## Release History
__0.2.0__
@ -144,5 +76,7 @@ __0.1.0__
- Initial release
## License
Copyright (c) 2014 [Eric Kryski](https://github.com/ekryski)
Licensed under the [MIT license](https://github.com/feathersjs/feathers-errors/blob/master/LICENSE-MIT).
Copyright (c) 2015 Feathers Contributors
Licensed under the [MIT license](LICENSE).

View File

@ -1,25 +0,0 @@
var feathers = require('feathers');
var app = feathers();
var userService = {
find: function(params, callback) {
callback(new this.app.errors.NotFound('User does not exist'));
// You can also simply do something like this if you
// just want to fire back a 500 error.
//
// callback('A generic server error');
},
setup: function(app){
this.app = app;
}
};
app.use('/users', userService)
.configure(feathers.errors());
app.listen(8080);
console.log('App listening on 127.0.0.1:8080');

View File

@ -1,156 +0,0 @@
/*
* feathers-errors
* https://github.com/feathersjs/feathers-errors
*
* Copyright (c) 2014 Eric Kryski
* Licensed under the MIT license.
*/
'use strict';
var _ = require('lodash');
var fs = require('fs');
var path = require('path');
var errors = require('./error-types');
var html = fs.readFileSync(path.resolve(__dirname, '..', 'public/error.html')).toString();
/**
* Escape the given string of `html`.
*
* @param {String} html
* @return {String}
* @api private
*/
var escapeHTML = function(html){
return String(html)
.replace(/&(?!\w+;)/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
};
/**
* Method for a 404 middleware
* See http://expressjs.com/guide.html#error-handling
* @param {Error} err - An error
* @param {Object} req - the request object
* @param {Object} res - the response object
* @param {Function} next - callback to call for next step in middleware chain
*/
var fourOhFour = function(req, res, next) {
next(new errors.NotFound('Page not found.'));
};
/**
* The error handler middleware.
* See http://expressjs.com/guide.html#error-handling
* @param {Error} err - An error
* @param {Object} req - the request object
* @param {Object} res - the response object
* @param {Function} next - callback to call for next step in middleware chain
*
*/
/* jshint unused:false */
var handler = function(err, req, res, next) {
if (typeof err === 'string') {
err = new errors.GeneralError(err);
}
else if (!(err instanceof errors.AbstractError)) {
var oldError = err;
err = new errors.GeneralError(oldError.message);
if (oldError.stack) {
err.stack = oldError.stack;
}
}
// Don't show stack trace if it is a 404 error
if (err.code === 404) {
err.stack = null;
}
var statusCode = typeof err.code === 'number' ? err.code : 500;
res.status(statusCode);
if (req.app.logger && typeof req.app.logger.error === 'function') {
req.app.logger.error(err.code + ' - ' + req.url, err.stack || err);
}
else if (typeof req.app.error === 'function') {
req.app.error(err.code + ' - ' + req.url, err.stack || err);
}
res.format({
'text/html': function(){
// If we have a rendering engine don't show the
// default feathers error page.
// TODO (EK): We should let people specify this instead
// of making a shitty assumption.
if (req.app.get('view engine') !== undefined) {
if (err.code === 404) {
return res.redirect('/404');
}
return res.redirect('/500');
}
var stack = (err.stack || '')
.split('\n')
.slice(1)
.map(function(v) {
return '<li>' + v + '</li>';
})
.join('');
var errorPage = html
.replace('{stack}', stack)
.replace('{title}', err.message)
.replace('{statusCode}', err.code)
.replace(/\{error\}/g, escapeHTML(err.toString().replace(/\n/g, '<br/>')));
res.send(errorPage);
},
'application/json': function(){
res.json({
'code': err.code,
'name': err.name,
'message': err.message,
'errors': err.errors || {}
});
},
'text/plain': function(){
res.send(err.message);
}
});
};
exports = module.exports = function (config) {
if (typeof config === 'function') {
handler = config;
config = {};
}
config = _.defaults({}, config, {
handler: handler,
fourOhFour: fourOhFour
});
return function () {
var app = this;
// Enable the errors Plugin
app.enable('feathers errors');
// Set the error handlers
app.use(config.fourOhFour).use(config.handler);
// Set the available errors on the app for convenience
app.errors = errors;
};
};
exports.fourOhFour = fourOhFour;
exports.handler = handler;
exports.types = errors;

View File

@ -0,0 +1,302 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.types = exports.errors = undefined;
var _debug = require('debug');
var _debug2 = _interopRequireDefault(_debug);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _typeof(obj) { return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // import util from 'util';
var debug = (0, _debug2.default)('feathers-errors');
var AbstractError = (function (_Error) {
_inherits(AbstractError, _Error);
function AbstractError(msg, name, code, className, data) {
_classCallCheck(this, AbstractError);
msg = msg || 'Error';
var errors = undefined;
var message = undefined;
if (msg instanceof Error) {
message = msg.message || 'Error';
// NOTE (EK): This is typically to handle errors
// that are thrown from other modules. For example,
// Mongoose validations can return multiple errors.
if (msg.errors) {
errors = msg.errors;
}
}
// Support plain old objects
else if ((typeof msg === 'undefined' ? 'undefined' : _typeof(msg)) === 'object') {
message = msg.message || 'Error';
errors = msg.errors ? msg.errors : msg;
}
// message is just a string
else {
message = msg;
}
// NOTE (EK): Babel doesn't support this so
// we have to pass in the class name manually.
// this.name = this.constructor.name;
var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(AbstractError).call(this, message));
_this.name = name;
_this.message = message;
_this.code = code;
_this.className = className;
_this.data = data;
_this.errors = errors;
Error.captureStackTrace(_this, _this.name);
debug(_this.name + '(' + _this.code + '): ' + _this.message);
return _this;
}
return AbstractError;
})(Error);
var BadRequest = (function (_AbstractError) {
_inherits(BadRequest, _AbstractError);
function BadRequest(message, data) {
_classCallCheck(this, BadRequest);
return _possibleConstructorReturn(this, Object.getPrototypeOf(BadRequest).call(this, message, 'BadRequest', 400, 'bad-request', data));
}
return BadRequest;
})(AbstractError);
var NotAuthenticated = (function (_AbstractError2) {
_inherits(NotAuthenticated, _AbstractError2);
function NotAuthenticated(message, data) {
_classCallCheck(this, NotAuthenticated);
return _possibleConstructorReturn(this, Object.getPrototypeOf(NotAuthenticated).call(this, message, 'NotAuthenticated', 401, 'not-authenticated', data));
}
return NotAuthenticated;
})(AbstractError);
var PaymentError = (function (_AbstractError3) {
_inherits(PaymentError, _AbstractError3);
function PaymentError(message, data) {
_classCallCheck(this, PaymentError);
return _possibleConstructorReturn(this, Object.getPrototypeOf(PaymentError).call(this, message, 'PaymentError', 402, 'payment-error', data));
}
return PaymentError;
})(AbstractError);
var Forbidden = (function (_AbstractError4) {
_inherits(Forbidden, _AbstractError4);
function Forbidden(message, data) {
_classCallCheck(this, Forbidden);
return _possibleConstructorReturn(this, Object.getPrototypeOf(Forbidden).call(this, message, 'Forbidden', 403, 'forbidden', data));
}
return Forbidden;
})(AbstractError);
var NotFound = (function (_AbstractError5) {
_inherits(NotFound, _AbstractError5);
function NotFound(message, data) {
_classCallCheck(this, NotFound);
return _possibleConstructorReturn(this, Object.getPrototypeOf(NotFound).call(this, message, 'NotFound', 404, 'not-found', data));
}
return NotFound;
})(AbstractError);
var MethodNotAllowed = (function (_AbstractError6) {
_inherits(MethodNotAllowed, _AbstractError6);
function MethodNotAllowed(message, data) {
_classCallCheck(this, MethodNotAllowed);
return _possibleConstructorReturn(this, Object.getPrototypeOf(MethodNotAllowed).call(this, message, 'MethodNotAllowed', 405, 'method-not-allowed', data));
}
return MethodNotAllowed;
})(AbstractError);
var NotAcceptable = (function (_AbstractError7) {
_inherits(NotAcceptable, _AbstractError7);
function NotAcceptable(message, data) {
_classCallCheck(this, NotAcceptable);
return _possibleConstructorReturn(this, Object.getPrototypeOf(NotAcceptable).call(this, message, 'NotAcceptable', 406, 'not-acceptable', data));
}
return NotAcceptable;
})(AbstractError);
var Timeout = (function (_AbstractError8) {
_inherits(Timeout, _AbstractError8);
function Timeout(message, data) {
_classCallCheck(this, Timeout);
return _possibleConstructorReturn(this, Object.getPrototypeOf(Timeout).call(this, message, 'Timeout', 408, 'timeout', data));
}
return Timeout;
})(AbstractError);
var Conflict = (function (_AbstractError9) {
_inherits(Conflict, _AbstractError9);
function Conflict(message, data) {
_classCallCheck(this, Conflict);
return _possibleConstructorReturn(this, Object.getPrototypeOf(Conflict).call(this, message, 'Conflict', 409, 'conflict', data));
}
return Conflict;
})(AbstractError);
var Unprocessable = (function (_AbstractError10) {
_inherits(Unprocessable, _AbstractError10);
function Unprocessable(message, data) {
_classCallCheck(this, Unprocessable);
return _possibleConstructorReturn(this, Object.getPrototypeOf(Unprocessable).call(this, message, 'Unprocessable', 422, 'unprocessable', data));
}
return Unprocessable;
})(AbstractError);
var GeneralError = (function (_AbstractError11) {
_inherits(GeneralError, _AbstractError11);
function GeneralError(message, data) {
_classCallCheck(this, GeneralError);
return _possibleConstructorReturn(this, Object.getPrototypeOf(GeneralError).call(this, message, 'GeneralError', 500, 'general-error', data));
}
return GeneralError;
})(AbstractError);
var NotImplemented = (function (_AbstractError12) {
_inherits(NotImplemented, _AbstractError12);
function NotImplemented(message, data) {
_classCallCheck(this, NotImplemented);
return _possibleConstructorReturn(this, Object.getPrototypeOf(NotImplemented).call(this, message, 'NotImplemented', 501, 'not-implemented', data));
}
return NotImplemented;
})(AbstractError);
var Unavailable = (function (_AbstractError13) {
_inherits(Unavailable, _AbstractError13);
function Unavailable(message, data) {
_classCallCheck(this, Unavailable);
return _possibleConstructorReturn(this, Object.getPrototypeOf(Unavailable).call(this, message, 'Unavailable', 503, 'unavailable', data));
}
return Unavailable;
})(AbstractError);
var errors = {
BadRequest: BadRequest,
NotAuthenticated: NotAuthenticated,
PaymentError: PaymentError,
Forbidden: Forbidden,
NotFound: NotFound,
MethodNotAllowed: MethodNotAllowed,
NotAcceptable: NotAcceptable,
Timeout: Timeout,
Conflict: Conflict,
Unprocessable: Unprocessable,
GeneralError: GeneralError,
NotImplemented: NotImplemented,
Unavailable: Unavailable
};
var types = errors;
// // Abstract Error
// let AbstractError = (msg, constr, data) => {
// Error.captureStackTrace(this, constr || this);
// if (msg instanceof Error) {
// this.message = msg.message;
// if (msg.errors) {
// this.errors = msg.errors;
// }
// }
// else {
// this.message = msg || 'Error';
// }
// this.data = data;
// };
// util.inherits(AbstractError, Error);
// AbstractError.prototype.name = 'Abstract Error';
// let createError = (errorName, code, className) => {
// let errorFn = (msg, data) => {
// debug(`Feathers Error: ${errorName}(${code}): ${msg}`);
// errorFn.super_.call(this, msg, this.constructor, data);
// };
// util.inherits(errorFn, AbstractError);
// errorFn.prototype.name = errorName;
// errorFn.prototype.code = code;
// errorFn.prototype.className = className;
// errors[errorName] = errorFn;
// };
// createError('BadRequest', 400, 'bad-request');
// createError('NotAuthenticated', 401, 'not-authenticated');
// createError('PaymentError', 402, 'payment-error');
// createError('Forbidden', 403, 'forbidden');
// createError('NotFound', 404, 'not-found');
// createError('MethodNotAllowed', 405, 'method-not-allowed');
// createError('NotAcceptable', 406, 'not-acceptable');
// createError('Timeout', 408, 'timeout');
// createError('Conflict', 409, 'conflict');
// createError('Unprocessable', 422, 'unprocessable');
// createError('GeneralError', 500, 'error');
// createError('NotImplemented', 501, 'not-implemented');
// createError('Unavailable', 503, 'unavailable');
exports.errors = errors;
exports.types = types;

View File

@ -0,0 +1,49 @@
'use strict';
var util = require('util');
// Abstract Error
var AbstractError = exports.AbstractError = function (msg, constr, data) {
Error.captureStackTrace(this, constr || this);
if (msg instanceof Error) {
this.message = msg.message;
if (msg.errors) {
this.errors = msg.errors;
}
} else {
this.message = msg || 'Error';
}
this.data = data;
};
util.inherits(AbstractError, Error);
AbstractError.prototype.name = 'Abstract Error';
function createError(errorName, code, className) {
var errorFn = exports[errorName] = function (msg, data) {
errorFn.super_.call(this, msg, this.constructor, data);
};
util.inherits(errorFn, AbstractError);
errorFn.prototype.name = errorName;
errorFn.prototype.code = code;
errorFn.prototype.className = className;
}
createError('BadRequest', 400, 'bad-request');
createError('NotAuthenticated', 401, 'not-authenticated');
createError('PaymentError', 402, 'payment-error');
createError('Forbidden', 403, 'forbidden');
createError('NotFound', 404, 'not-found');
createError('MethodNotAllowed', 405, 'method-not-allowed');
createError('NotAcceptable', 406, 'not-acceptable');
createError('Timeout', 408, 'timeout');
createError('Conflict', 409, 'conflict');
createError('Unprocessable', 422, 'unprocessable');
createError('GeneralError', 500, 'error');
createError('NotImplemented', 501, 'not-implemented');
createError('Unavailable', 503, 'unavailable');

View File

@ -1,57 +1,57 @@
{
"name": "feathers-errors",
"description": "Convenient error handling for Feathers services.",
"description": "Common error types for feathers apps",
"version": "0.2.5",
"homepage": "https://github.com/feathersjs/feathers-errors",
"author": {
"name": "Eric Kryski",
"email": "e.kryski@gmail.com"
},
"contributors": [
{
"name": "David Luecke",
"email": "daff@neyeon.com"
}
"main": "lib/",
"keywords": [
"feathers",
"feathers-plugin",
"feathers-errors"
],
"license": "MIT",
"repository": {
"type": "git",
"url": "git://github.com/feathersjs/feathers-errors.git"
},
"author": {
"name": "Feathers contributors",
"email": "hello@feathersjs.com",
"url": "https://feathersjs.com"
},
"contributors": [],
"bugs": {
"url": "https://github.com/feathersjs/feathers-errors/issues"
},
"licenses": [
{
"type": "MIT",
"url": "https://github.com/feathersjs/feathers-errors/blob/master/LICENSE-MIT"
}
],
"main": "lib/errors",
"engines": {
"node": ">= 0.10.0"
"node": ">= 0.12.0"
},
"scripts": {
"test": "grunt test",
"start": "grunt"
"prepublish": "npm run compile",
"publish": "git push origin && git push origin --tags",
"release:patch": "npm version patch && npm publish",
"release:minor": "npm version minor && npm publish",
"release:major": "npm version major && npm publish",
"compile": "rm -rf lib/ && babel -d lib/ src/",
"watch": "babel --watch -d lib/ src/",
"jshint": "jshint src/. test/. --config",
"mocha": "mocha test/ --compilers js:babel-core/register",
"test": "npm run compile && npm run jshint && npm run mocha"
},
"directories": {
"lib": "lib"
},
"dependencies": {
"debug": "^2.2.0"
},
"devDependencies": {
"grunt-cli": "~0.1.7",
"grunt": "~0.4.1",
"grunt-release": "~0.5.1",
"grunt-contrib-jshint": "~0.x",
"grunt-simple-mocha": "~0.4.0",
"grunt-contrib-watch": "~0.5.3",
"feathers": ">= 0.3.1",
"supertest": "~0.9.0",
"socket.io-client": "^0.9.16"
},
"keywords": [
"feathers-plugin",
"feathers",
"errors",
"error handling"
],
"dependencies": {
"lodash": "^2.4.1"
"babel-cli": "^6.3.17",
"babel-core": "^6.3.17",
"babel-plugin-add-module-exports": "^0.1.1",
"babel-plugin-transform-object-assign": "^6.3.13",
"babel-preset-es2015": "^6.3.13",
"feathers": "^1.2.0",
"jshint": "^2.8.0",
"mocha": "^2.3.4"
}
}

View File

@ -1,50 +0,0 @@
<html>
<head>
<meta charset='utf-8'>
<title>{error}</title>
<style>
* {
margin: 0;
padding: 0;
outline: 0;
}
body {
padding: 80px 100px;
font: 13px "Helvetica Neue", "Lucida Grande", "Arial";
background: #ECE9E9 -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ECE9E9));
background: #ECE9E9 -moz-linear-gradient(top, #fff, #ECE9E9);
background-repeat: no-repeat;
color: #555;
-webkit-font-smoothing: antialiased;
}
h1, h2 {
font-size: 22px;
color: #343434;
}
h1 em, h2 em {
padding: 0 5px;
font-weight: normal;
}
h1 {
font-size: 60px;
}
h2 {
margin-top: 10px;
}
ul li {
list-style: none;
}
#stacktrace {
margin-left: 60px;
}
</style>
</head>
<body>
<div id="wrapper">
<h1>{title}</h1>
<h2><em>{statusCode}</em> {error}</h2>
<ul id="stacktrace">{stack}</ul>
</div>
</body>
</html>

View File

@ -0,0 +1,197 @@
// import util from 'util';
import makeDebug from 'debug';
const debug = makeDebug('feathers-errors');
class AbstractError extends Error {
constructor(msg, name, code, className, data) {
msg = msg || 'Error';
let errors;
let message;
if (msg instanceof Error) {
message = msg.message || 'Error';
// NOTE (EK): This is typically to handle errors
// that are thrown from other modules. For example,
// Mongoose validations can return multiple errors.
if (msg.errors) {
errors = msg.errors;
}
}
// Support plain old objects
else if (typeof msg === 'object') {
message = msg.message || 'Error';
errors = msg.errors ? msg.errors : msg;
}
// message is just a string
else {
message = msg;
}
super(message);
// NOTE (EK): Babel doesn't support this so
// we have to pass in the class name manually.
// this.name = this.constructor.name;
this.name = name;
this.message = message;
this.code = code;
this.className = className;
this.data = data;
this.errors = errors;
Error.captureStackTrace(this, this.name);
debug(`${this.name}(${this.code}): ${this.message}`);
}
}
class BadRequest extends AbstractError {
constructor(message, data) {
super(message, 'BadRequest', 400, 'bad-request', data);
}
}
class NotAuthenticated extends AbstractError {
constructor(message, data) {
super(message, 'NotAuthenticated', 401, 'not-authenticated', data);
}
}
class PaymentError extends AbstractError {
constructor(message, data) {
super(message, 'PaymentError', 402, 'payment-error', data);
}
}
class Forbidden extends AbstractError {
constructor(message, data) {
super(message, 'Forbidden', 403, 'forbidden', data);
}
}
class NotFound extends AbstractError {
constructor(message, data) {
super(message, 'NotFound', 404, 'not-found', data);
}
}
class MethodNotAllowed extends AbstractError {
constructor(message, data) {
super(message, 'MethodNotAllowed', 405, 'method-not-allowed', data);
}
}
class NotAcceptable extends AbstractError {
constructor(message, data) {
super(message, 'NotAcceptable', 406, 'not-acceptable', data);
}
}
class Timeout extends AbstractError {
constructor(message, data) {
super(message, 'Timeout', 408, 'timeout', data);
}
}
class Conflict extends AbstractError {
constructor(message, data) {
super(message, 'Conflict', 409, 'conflict', data);
}
}
class Unprocessable extends AbstractError {
constructor(message, data) {
super(message, 'Unprocessable', 422, 'unprocessable', data);
}
}
class GeneralError extends AbstractError {
constructor(message, data) {
super(message, 'GeneralError', 500, 'general-error', data);
}
}
class NotImplemented extends AbstractError {
constructor(message, data) {
super(message, 'NotImplemented', 501, 'not-implemented', data);
}
}
class Unavailable extends AbstractError {
constructor(message, data) {
super(message, 'Unavailable', 503, 'unavailable', data);
}
}
var errors = {
BadRequest,
NotAuthenticated,
PaymentError,
Forbidden,
NotFound,
MethodNotAllowed,
NotAcceptable,
Timeout,
Conflict,
Unprocessable,
GeneralError,
NotImplemented,
Unavailable
};
var types = errors;
// // Abstract Error
// let AbstractError = (msg, constr, data) => {
// Error.captureStackTrace(this, constr || this);
// if (msg instanceof Error) {
// this.message = msg.message;
// if (msg.errors) {
// this.errors = msg.errors;
// }
// }
// else {
// this.message = msg || 'Error';
// }
// this.data = data;
// };
// util.inherits(AbstractError, Error);
// AbstractError.prototype.name = 'Abstract Error';
// let createError = (errorName, code, className) => {
// let errorFn = (msg, data) => {
// debug(`Feathers Error: ${errorName}(${code}): ${msg}`);
// errorFn.super_.call(this, msg, this.constructor, data);
// };
// util.inherits(errorFn, AbstractError);
// errorFn.prototype.name = errorName;
// errorFn.prototype.code = code;
// errorFn.prototype.className = className;
// errors[errorName] = errorFn;
// };
// createError('BadRequest', 400, 'bad-request');
// createError('NotAuthenticated', 401, 'not-authenticated');
// createError('PaymentError', 402, 'payment-error');
// createError('Forbidden', 403, 'forbidden');
// createError('NotFound', 404, 'not-found');
// createError('MethodNotAllowed', 405, 'method-not-allowed');
// createError('NotAcceptable', 406, 'not-acceptable');
// createError('Timeout', 408, 'timeout');
// createError('Conflict', 409, 'conflict');
// createError('Unprocessable', 422, 'unprocessable');
// createError('GeneralError', 500, 'error');
// createError('NotImplemented', 501, 'not-implemented');
// createError('Unavailable', 503, 'unavailable');
export { errors, types };

View File

@ -1,52 +0,0 @@
'use strict';
/* jshint undef:false */
var assert = require('assert');
var feathers = require('feathers');
var errors = require('../lib/errors');
var app;
describe('Feathers errors', function () {
beforeEach(function() {
app = feathers().configure(errors());
});
it('exposes the api properly', function () {
assert.equal(typeof errors.types, 'object', 'exposes error types');
assert.equal(typeof errors.handler, 'function', 'exposes error handler');
assert.equal(typeof errors.fourOhFour, 'function', 'exposes 404 handler');
});
it('initializes errors object', function () {
assert.equal(typeof app.errors, 'object', 'errors got added to the app');
});
it('can create app errors', function () {
var error = new app.errors.GeneralError('foo');
assert.equal(error.code, 500 ,'a general error was created with the correct code');
assert.equal(error.message, 'foo' ,'a general error was created with the correct message');
});
it('app has all the available errors', function () {
assert.notEqual(typeof app.errors.BadRequest, 'undefined', 'has BadRequest');
assert.notEqual(typeof app.errors.NotAuthenticated, 'undefined', 'has NotAuthenticated');
assert.notEqual(typeof app.errors.PaymentError, 'undefined', 'has PaymentError');
assert.notEqual(typeof app.errors.Forbidden, 'undefined', 'has Forbidden');
assert.notEqual(typeof app.errors.NotFound, 'undefined', 'has NotFound');
assert.notEqual(typeof app.errors.MethodNotAllowed, 'undefined', 'has MethodNotAllowed');
assert.notEqual(typeof app.errors.NotAcceptable, 'undefined', 'has NotAcceptable');
assert.notEqual(typeof app.errors.Timeout, 'undefined', 'has Timeout');
assert.notEqual(typeof app.errors.Conflict, 'undefined', 'has Conflict');
assert.notEqual(typeof app.errors.Unprocessable, 'undefined', 'has Unprocessable');
assert.notEqual(typeof app.errors.GeneralError, 'undefined', 'has GeneralError');
assert.notEqual(typeof app.errors.NotImplemented, 'undefined', 'has NotImplemented');
assert.notEqual(typeof app.errors.Unavailable, 'undefined', 'has Unavailable');
});
it.skip('error handler catches errors', function () {
// TODO (EK)
});
});

View File

@ -0,0 +1,99 @@
import assert from 'assert';
import { types, errors } from '../src';
describe('feathers-errors', () => {
it('is CommonJS compatible', () => {
assert.equal(typeof require('../lib'), 'object');
});
describe('error types', () => {
it('Bad Request', () => {
assert.notEqual(typeof errors.BadRequest, 'undefined', 'has BadRequest');
});
it('Not Authenticated', () => {
assert.notEqual(typeof errors.NotAuthenticated, 'undefined', 'has NotAuthenticated');
});
it('Payment Error', () => {
assert.notEqual(typeof errors.PaymentError, 'undefined', 'has PaymentError');
});
it('Forbidden', () => {
assert.notEqual(typeof errors.Forbidden, 'undefined', 'has Forbidden');
});
it('Not Found', () => {
assert.notEqual(typeof errors.NotFound, 'undefined', 'has NotFound');
});
it('Method Not Allowed', () => {
assert.notEqual(typeof errors.MethodNotAllowed, 'undefined', 'has MethodNotAllowed');
});
it('Not Acceptable', () => {
assert.notEqual(typeof errors.NotAcceptable, 'undefined', 'has NotAcceptable');
});
it('Timeout', () => {
assert.notEqual(typeof errors.Timeout, 'undefined', 'has Timeout');
});
it('Conflict', () => {
assert.notEqual(typeof errors.Conflict, 'undefined', 'has Conflict');
});
it('Unprocessable', () => {
assert.notEqual(typeof errors.Unprocessable, 'undefined', 'has Unprocessable');
});
it('General Error', () => {
assert.notEqual(typeof errors.GeneralError, 'undefined', 'has GeneralError');
});
it('Not Implemented', () => {
assert.notEqual(typeof errors.NotImplemented, 'undefined', 'has NotImplemented');
});
it('Unavailable', () => {
assert.notEqual(typeof errors.Unavailable, 'undefined', 'has Unavailable');
});
});
it('exposes errors via types for backwards compatibility', () => {
assert.notEqual(typeof types.BadRequest, 'undefined', 'has BadRequest');
});
describe('successful error creation', () => {
it('default error', () => {
var error = new errors.GeneralError();
assert.equal(error.code, 500);
assert.equal(error.message, 'Error');
assert.equal(error.className, 'general-error');
});
it('with custom message', () => {
var error = new errors.BadRequest('Invalid Password');
assert.equal(error.code, 400);
assert.equal(error.message, 'Invalid Password');
});
it('with error object', () => {
var error = new errors.BadRequest(new Error('Invalid Password'));
assert.equal(error.code, 400);
assert.equal(error.message, 'Invalid Password');
});
it('with multiple errors', () => {
var data = {
email: 'Email Taken',
password: 'Invalid Password'
};
var error = new errors.BadRequest(data);
assert.equal(error.code, 400);
assert.equal(error.message, 'Error');
assert.equal(error.errors, data);
});
});
});