From fd0f5a5028888210e68482245d03ad207bc2c6ca Mon Sep 17 00:00:00 2001 From: Sebastian Gierlinger Date: Fri, 9 May 2014 12:11:29 +0200 Subject: [PATCH] Add distinct error classes closes #2690 - added new error classes - moved errorhandling.js to /errors/index.js - changed API errors to use new classes - updated tests --- core/bootstrap.js | 2 +- core/server/api/db.js | 20 +++++----- core/server/api/index.js | 39 +++---------------- core/server/api/mail.js | 7 ++-- core/server/api/notifications.js | 6 ++- core/server/api/posts.js | 21 +++++----- core/server/api/settings.js | 17 +++----- core/server/api/users.js | 13 ++++--- core/server/apps/index.js | 2 +- core/server/controllers/admin.js | 2 +- core/server/controllers/frontend.js | 7 ++-- core/server/data/migration/index.js | 2 +- core/server/data/validation/index.js | 23 +++++------ core/server/errors/badrequesterror.js | 15 +++++++ core/server/errors/emailerror.js | 15 +++++++ .../{errorHandling.js => errors/index.js} | 28 +++++++++---- core/server/errors/internalservererror.js | 15 +++++++ core/server/errors/nopermissionerror.js | 15 +++++++ core/server/errors/notfounderror.js | 15 +++++++ core/server/errors/requesttoolargeerror.js | 15 +++++++ core/server/errors/unauthorizederror.js | 15 +++++++ core/server/errors/validationerror.js | 22 +++++++++++ core/server/helpers/index.js | 2 +- core/server/helpers/template.js | 2 +- core/server/index.js | 2 +- core/server/middleware/ghost-busboy.js | 5 ++- core/server/middleware/index.js | 2 +- core/server/models/post.js | 2 +- core/server/models/settings.js | 6 +-- core/server/models/user.js | 2 +- core/server/permissions/effective.js | 2 +- core/server/storage/index.js | 2 +- core/server/storage/localfilesystem.js | 2 +- core/server/update-check.js | 2 +- core/server/xmlrpc.js | 2 +- core/test/integration/api/api_db_spec.js | 20 +++++----- .../model/model_permissions_spec.js | 2 +- .../integration/model/model_users_spec.js | 2 +- core/test/unit/errorHandling_spec.js | 2 +- core/test/unit/export_spec.js | 2 +- core/test/unit/import_spec.js | 2 +- core/test/unit/permissions_spec.js | 2 +- index.js | 2 +- 43 files changed, 249 insertions(+), 134 deletions(-) create mode 100644 core/server/errors/badrequesterror.js create mode 100644 core/server/errors/emailerror.js rename core/server/{errorHandling.js => errors/index.js} (85%) create mode 100644 core/server/errors/internalservererror.js create mode 100644 core/server/errors/nopermissionerror.js create mode 100644 core/server/errors/notfounderror.js create mode 100644 core/server/errors/requesttoolargeerror.js create mode 100644 core/server/errors/unauthorizederror.js create mode 100644 core/server/errors/validationerror.js diff --git a/core/bootstrap.js b/core/bootstrap.js index 6fcbb35ebf..9ce1e8ee41 100644 --- a/core/bootstrap.js +++ b/core/bootstrap.js @@ -7,7 +7,7 @@ var fs = require('fs'), url = require('url'), when = require('when'), - errors = require('./server/errorHandling'), + errors = require('./server/errors'), config = require('./server/config'), appRoot = config().paths.appRoot, diff --git a/core/server/api/db.js b/core/server/api/db.js index 8217f3c3ed..79db045e5c 100644 --- a/core/server/api/db.js +++ b/core/server/api/db.js @@ -6,7 +6,7 @@ var dataExport = require('../data/export'), nodefn = require('when/node/function'), _ = require('lodash'), validation = require('../data/validation'), - errors = require('../../server/errorHandling'), + errors = require('../../server/errors'), canThis = require('../permissions').canThis, api = {}, db; @@ -23,10 +23,10 @@ db = { return dataExport().then(function (exportedData) { return when.resolve({ db: [exportedData] }); }).otherwise(function (error) { - return when.reject({type: 'InternalServerError', message: error.message || error}); + return when.reject(new errors.InternalServerError(error.message || error)); }); }, function () { - return when.reject({type: 'NoPermission', message: 'You do not have permission to export data. (no rights)'}); + return when.reject(new errors.NoPermissionError('You do not have permission to export data. (no rights)')); }); }, 'importContent': function (options) { @@ -43,7 +43,7 @@ db = { * - If there is no path * - If the name doesn't have json in it */ - return when.reject({type: 'InternalServerError', message: 'Please select a .json file to import.'}); + return when.reject(new errors.InternalServerError('Please select a .json file to import.')); } return api.settings.read.call({ internal: true }, { key: 'databaseVersion' }).then(function (response) { @@ -65,11 +65,11 @@ db = { importData = JSON.parse(fileContents); } catch (e) { errors.logError(e, "API DB import content", "check that the import file is valid JSON."); - return when.reject(new Error("Failed to parse the import JSON file")); + return when.reject(new errors.BadRequest("Failed to parse the import JSON file")); } if (!importData.meta || !importData.meta.version) { - return when.reject(new Error("Import data does not specify version")); + return when.reject(new errors.ValidationError("Import data does not specify version", 'meta.version')); } _.each(_.keys(importData.data), function (tableName) { @@ -94,13 +94,13 @@ db = { }).then(function () { return when.resolve({ db: [] }); }).otherwise(function importFailure(error) { - return when.reject({type: 'InternalServerError', message: error.message || error}); + return when.reject(new errors.InternalServerError(error.message || error)); }).finally(function () { // Unlink the file after import return nodefn.call(fs.unlink, options.importfile.path); }); }, function () { - return when.reject({type: 'NoPermission', message: 'You do not have permission to export data. (no rights)'}); + return when.reject(new errors.NoPermissionError('You do not have permission to export data. (no rights)')); }); }, 'deleteAllContent': function () { @@ -111,10 +111,10 @@ db = { .then(function () { return when.resolve({ db: [] }); }, function (error) { - return when.reject({message: error.message || error}); + return when.reject(new errors.InternalServerError(error.message || error)); }); }, function () { - return when.reject({type: 'NoPermission', message: 'You do not have permission to export data. (no rights)'}); + return when.reject(new errors.NoPermissionError('You do not have permission to export data. (no rights)')); }); } }; diff --git a/core/server/api/index.js b/core/server/api/index.js index 1e6a093761..e5038c1f81 100644 --- a/core/server/api/index.js +++ b/core/server/api/index.js @@ -12,34 +12,7 @@ var _ = require('lodash'), tags = require('./tags'), mail = require('./mail'), requestHandler, - init, - - errorTypes = { - BadRequest: { - code: 400 - }, - Unauthorized: { - code: 401 - }, - NoPermission: { - code: 403 - }, - NotFound: { - code: 404 - }, - RequestEntityTooLarge: { - code: 413 - }, - ValidationError: { - code: 422 - }, - EmailError: { - code: 500 - }, - InternalServerError: { - code: 500 - } - }; + init; // ## Request Handlers @@ -169,14 +142,14 @@ requestHandler = function (apiMethod) { error = [].concat(error); } - _.each(error, function (erroritem) { + _.each(error, function (errorItem) { var errorContent = {}; //TODO: add logic to set the correct status code - errorCode = errorTypes[erroritem.type].code || 500; - - errorContent['message'] = _.isString(erroritem) ? erroritem : (_.isObject(erroritem) ? erroritem.message : 'Unknown API Error'); - errorContent['type'] = erroritem.type || 'InternalServerError'; + errorCode = errorItem.code || 500; + + errorContent['message'] = _.isString(errorItem) ? errorItem : (_.isObject(errorItem) ? errorItem.message : 'Unknown API Error'); + errorContent['type'] = errorItem.type || 'InternalServerError'; errors.push(errorContent); }); diff --git a/core/server/api/mail.js b/core/server/api/mail.js index 2c86fec490..8ca0af072c 100644 --- a/core/server/api/mail.js +++ b/core/server/api/mail.js @@ -1,5 +1,6 @@ -var when = require("when"), - config = require('../config'), +var when = require("when"), + config = require('../config'), + errors = require('../errors'), mail; @@ -22,7 +23,7 @@ mail = { return when.resolve({message: data.message }); }) .otherwise(function (error) { - return when.reject({type: 'EmailError', message: error.message }); + return when.reject(new errors.EmailError(error.message)); }); }, diff --git a/core/server/api/notifications.js b/core/server/api/notifications.js index 03689884b1..6a5360d299 100644 --- a/core/server/api/notifications.js +++ b/core/server/api/notifications.js @@ -1,5 +1,7 @@ var when = require('when'), _ = require('lodash'), + errors = require('../errors'), + // Holds the persistent notifications notificationsStore = [], // Holds the last used id @@ -22,11 +24,11 @@ notifications = { }); if (notification && !notification.dismissable) { - return when.reject({type: 'NoPermission', message: 'You do not have permission to dismiss this notification.'}); + return when.reject(new errors.NoPermissionError('You do not have permission to dismiss this notification.')); } if (!notification) { - return when.reject({type: 'NoPermission', message: 'Notification does not exist.'}); + return when.reject(new errors.NotFoundError('Notification does not exist.')); } notificationsStore = _.reject(notificationsStore, function (element) { diff --git a/core/server/api/posts.js b/core/server/api/posts.js index fc66634b69..fc85251db4 100644 --- a/core/server/api/posts.js +++ b/core/server/api/posts.js @@ -2,13 +2,14 @@ var when = require('when'), _ = require('lodash'), dataProvider = require('../models'), canThis = require('../permissions').canThis, + errors = require('../errors'), - posts, - allowedIncludes = ['created_by', 'updated_by', 'published_by', 'author', 'tags', 'fields']; + allowedIncludes = ['created_by', 'updated_by', 'published_by', 'author', 'tags', 'fields'], + posts; function checkPostData(postData) { if (_.isEmpty(postData) || _.isEmpty(postData.posts) || _.isEmpty(postData.posts[0])) { - return when.reject({type: 'BadRequest', message: 'No root key (\'posts\') provided.'}); + return when.reject(new errors.BadRequestError('No root key (\'posts\') provided.')); } return when.resolve(postData); } @@ -69,7 +70,7 @@ posts = { return { posts: [ result.toJSON() ]}; } - return when.reject({type: 'NotFound', message: 'Post not found.'}); + return when.reject(new errors.NotFoundError('Post not found.')); }); }, @@ -100,10 +101,10 @@ posts = { return { posts: [ post ]}; } - return when.reject({type: 'NotFound', message: 'Post not found.'}); + return when.reject(new errors.NotFoundError('Post not found.')); }); }, function () { - return when.reject({type: 'NoPermission', message: 'You do not have permission to edit this post.'}); + return when.reject(new errors.NoPermissionError('You do not have permission to edit this post.')); }); }, @@ -131,7 +132,7 @@ posts = { return { posts: [ post ]}; }); }, function () { - return when.reject({type: 'NoPermission', message: 'You do not have permission to add posts.'}); + return when.reject(new errors.NoPermissionError('You do not have permission to add posts.')); }); }, @@ -156,7 +157,7 @@ posts = { }); }); }, function () { - return when.reject({type: 'NoPermission', message: 'You do not have permission to remove posts.'}); + return when.reject(new errors.NoPermissionError('You do not have permission to remove posts.')); }); }, @@ -169,10 +170,10 @@ posts = { if (slug) { return slug; } - return when.reject({type: 'InternalServerError', message: 'Could not generate slug'}); + return when.reject(new errors.InternalServerError('Could not generate slug')); }); }, function () { - return when.reject({type: 'NoPermission', message: 'You do not have permission.'}); + return when.reject(new errors.NoPermissionError('You do not have permission.')); }); } diff --git a/core/server/api/settings.js b/core/server/api/settings.js index 899b4a32de..16ec0e975d 100644 --- a/core/server/api/settings.js +++ b/core/server/api/settings.js @@ -3,6 +3,7 @@ var _ = require('lodash'), when = require('when'), config = require('../config'), canThis = require('../permissions').canThis, + errors = require('../errors'), settings, settingsFilter, updateSettingsCache, @@ -176,11 +177,11 @@ settings = { result = {}; if (!setting) { - return when.reject({type: 'NotFound', message: 'Unable to find setting: ' + options.key}); + return when.reject(new errors.NotFoundError('Unable to find setting: ' + options.key)); } if (!self.internal && setting.type === 'core') { - return when.reject({type: 'NoPermission', message: 'Attempted to access core setting on external request' }); + return when.reject(new errors.NoPermissionError('Attempted to access core setting on external request')); } result[options.key] = setting; @@ -200,11 +201,11 @@ settings = { var setting = settingsCache[settingInfo.key]; if (!setting) { - return when.reject({type: 'NotFound', message: 'Unable to find setting: ' + settingInfo.key}); + return when.reject(new errors.NotFoundError('Unable to find setting: ' + settingInfo.key)); } if (!self.internal && setting.type === 'core') { - return when.reject({type: 'NoPermission', message: 'Attempted to access core setting on external request' }); + return when.reject(new errors.NoPermissionError('Attempted to access core setting on external request')); } return canThis(self).edit.setting(settingInfo.key); @@ -239,14 +240,6 @@ settings = { return settingsResult(readResult, type); }); }).catch(function (error) { - // In case something goes wrong it is most likely because of an invalid key - // or because of a badly formatted request. - - // Check for actual javascript error, not api error - if (error instanceof Error) { - return when.reject({type: 'BadRequest', message: error.message}); - } - // Pass along API error return when.reject(error); }); diff --git a/core/server/api/users.js b/core/server/api/users.js index 9b75c14cc2..f4585d08d1 100644 --- a/core/server/api/users.js +++ b/core/server/api/users.js @@ -3,13 +3,14 @@ var when = require('when'), dataProvider = require('../models'), settings = require('./settings'), canThis = require('../permissions').canThis, + errors = require('../errors'), ONE_DAY = 86400000, users; function checkUserData(userData) { if (_.isEmpty(userData) || _.isEmpty(userData.users) || _.isEmpty(userData.users[0])) { - return when.reject({code: 400, message: 'No root key (\'users\') provided.'}); + return when.reject(new errors.BadRequestError('No root key (\'users\') provided.')); } return when.resolve(userData); } @@ -25,7 +26,7 @@ users = { return { users: result.toJSON() }; }); }, function () { - return when.reject({type: 'NoPermission', message: 'You do not have permission to browse users.'}); + return when.reject(new errors.NoPermissionError('You do not have permission to browse users.')); }); }, @@ -42,7 +43,7 @@ users = { return { users: [result.toJSON()] }; } - return when.reject({type: 'NotFound', message: 'User not found.'}); + return when.reject(new errors.NotFoundError('User not found.')); }); }, @@ -58,10 +59,10 @@ users = { if (result) { return { users: [result.toJSON()]}; } - return when.reject({type: 'NotFound', message: 'User not found.'}); + return when.reject(new errors.NotFoundError('User not found.')); }); }, function () { - return when.reject({type: 'NoPermission', message: 'You do not have permission to edit this users.'}); + return when.reject(new errors.NoPermissionError('You do not have permission to edit this users.')); }); }, @@ -84,7 +85,7 @@ users = { } }); }, function () { - return when.reject({type: 'NoPermission', message: 'You do not have permission to add a users.'}); + return when.reject(new errors.NoPermissionError('You do not have permission to add a users.')); }); }, diff --git a/core/server/apps/index.js b/core/server/apps/index.js index a815d2dbfa..82013eee9b 100644 --- a/core/server/apps/index.js +++ b/core/server/apps/index.js @@ -1,7 +1,7 @@ var _ = require('lodash'), when = require('when'), - errors = require('../errorHandling'), + errors = require('../errors'), api = require('../api'), loader = require('./loader'), // Holds the available apps diff --git a/core/server/controllers/admin.js b/core/server/controllers/admin.js index 9244e123f0..79258e2d20 100644 --- a/core/server/controllers/admin.js +++ b/core/server/controllers/admin.js @@ -4,7 +4,7 @@ var config = require('../config'), when = require('when'), api = require('../api'), mailer = require('../mail'), - errors = require('../errorHandling'), + errors = require('../errors'), storage = require('../storage'), updateCheck = require('../update-check'), diff --git a/core/server/controllers/frontend.js b/core/server/controllers/frontend.js index 284d17c571..9ff45a4d05 100644 --- a/core/server/controllers/frontend.js +++ b/core/server/controllers/frontend.js @@ -15,6 +15,7 @@ var moment = require('moment'), config = require('../config'), filters = require('../../server/filters'), template = require('../helpers/template'), + errors = require('../errors'), frontendControllers, // Cache static post permalink regex @@ -161,7 +162,7 @@ frontendControllers = { // If there are still no matches then return. if (staticPostPermalink.match(path) === false) { // Reject promise chain with type 'NotFound' - return when.reject({type: 'NotFound'}); + return when.reject(new errors.NotFoundError()); } permalink = staticPostPermalink; @@ -192,7 +193,7 @@ frontendControllers = { return res.redirect(config().paths.subdir + '/ghost/editor/' + post.id + '/'); } else if (params.edit !== undefined) { // reject with type: 'NotFound' - return when.reject({type: 'NotFound'}); + return when.reject(new errors.NotFoundError()); } setReqCtx(req, post); @@ -254,7 +255,7 @@ frontendControllers = { // If we've thrown an error message // of type: 'NotFound' then we found // no path match. - if (err.type === 'NotFound') { + if (err.type === 'NotFoundError') { return next(); } diff --git a/core/server/data/migration/index.js b/core/server/data/migration/index.js index 95eae4494c..d9e9df8102 100644 --- a/core/server/data/migration/index.js +++ b/core/server/data/migration/index.js @@ -1,6 +1,6 @@ var _ = require('lodash'), when = require('when'), - errors = require('../../errorHandling'), + errors = require('../../errors'), client = require('../../models/base').client, knex = require('../../models/base').knex, sequence = require('when/sequence'), diff --git a/core/server/data/validation/index.js b/core/server/data/validation/index.js index 1948e342aa..cbbc586385 100644 --- a/core/server/data/validation/index.js +++ b/core/server/data/validation/index.js @@ -2,6 +2,7 @@ var schema = require('../schema').tables, _ = require('lodash'), validator = require('validator'), when = require('when'), + errors = require('../../errors'), validateSchema, validateSettings, @@ -22,7 +23,7 @@ validator.extend('notContains', function (str, badString) { // form schema.js validateSchema = function (tableName, model) { var columns = _.keys(schema[tableName]), - errors = []; + validationErrors = []; _.each(columns, function (columnKey) { var message = ''; @@ -31,7 +32,7 @@ validateSchema = function (tableName, model) { && schema[tableName][columnKey].nullable !== true) { if (validator.isNull(model[columnKey]) || validator.empty(model[columnKey])) { message = 'Value in [' + tableName + '.' + columnKey + '] cannot be blank.'; - errors.push({type: 'ValidationError', property: tableName + '.' + columnKey, message: message}); + validationErrors.push(new errors.ValidationError(message, tableName + '.' + columnKey)); } } // TODO: check if mandatory values should be enforced @@ -41,27 +42,27 @@ validateSchema = function (tableName, model) { if (!validator.isLength(model[columnKey], 0, schema[tableName][columnKey].maxlength)) { message = 'Value in [' + tableName + '.' + columnKey + '] exceeds maximum length of ' + schema[tableName][columnKey].maxlength + ' characters.'; - errors.push({type: 'ValidationError', property: tableName + '.' + columnKey, message: message}); + validationErrors.push(new errors.ValidationError(message, tableName + '.' + columnKey)); } } //check validations objects if (schema[tableName][columnKey].hasOwnProperty('validations')) { - errors.concat(validate(model[columnKey], columnKey, schema[tableName][columnKey].validations)); + validationErrors.concat(validate(model[columnKey], columnKey, schema[tableName][columnKey].validations)); } //check type if (schema[tableName][columnKey].hasOwnProperty('type')) { if (schema[tableName][columnKey].type === 'integer' && !validator.isInt(model[columnKey])) { message = 'Value in [' + tableName + '.' + columnKey + '] is no valid integer.'; - errors.push({type: 'ValidationError', property: tableName + '.' + columnKey, message: message}); + validationErrors.push(new errors.ValidationError(message, tableName + '.' + columnKey)); } } } }); - if (errors.length !== 0) { - return when.reject(errors); + if (validationErrors.length !== 0) { + return when.reject(validationErrors); } }; @@ -95,7 +96,7 @@ validateSettings = function (defaultSettings, model) { // // available validators: https://github.com/chriso/validator.js#validators validate = function (value, key, validations) { - var errors = []; + var validationErrors = []; _.each(validations, function (validationOptions, validationName) { var goodResult = true; @@ -110,14 +111,14 @@ validate = function (value, key, validations) { // equivalent of validator.isSomething(option1, option2) if (validator[validationName].apply(validator, validationOptions) !== goodResult) { - errors.push({type: 'ValidationError', property: key, message: 'Settings validation (' + validationName + ') failed for ' + key}); + validationErrors.push(new errors.ValidationError('Settings validation (' + validationName + ') failed for ' + key, key)); } validationOptions.shift(); }, this); - if (errors.length !== 0) { - return when.reject(errors); + if (validationErrors.length !== 0) { + return when.reject(validationErrors); } }; diff --git a/core/server/errors/badrequesterror.js b/core/server/errors/badrequesterror.js new file mode 100644 index 0000000000..c37a133eda --- /dev/null +++ b/core/server/errors/badrequesterror.js @@ -0,0 +1,15 @@ +// # Bad request error +// Custom error class with status code and type prefilled. + +function BadRequestError(message) { + this.message = message; + this.stack = new Error().stack; + this.code = 400; + this.type = this.name; +} + +BadRequestError.prototype = Object.create(Error.prototype); +BadRequestError.prototype.name = "BadRequestError"; + + +module.exports = BadRequestError; \ No newline at end of file diff --git a/core/server/errors/emailerror.js b/core/server/errors/emailerror.js new file mode 100644 index 0000000000..48910e7fa3 --- /dev/null +++ b/core/server/errors/emailerror.js @@ -0,0 +1,15 @@ +// # Email error +// Custom error class with status code and type prefilled. + +function EmailError(message) { + this.message = message; + this.stack = new Error().stack; + this.code = 500; + this.type = this.name; +} + +EmailError.prototype = Object.create(Error.prototype); +EmailError.prototype.name = "EmailError"; + + +module.exports = EmailError; \ No newline at end of file diff --git a/core/server/errorHandling.js b/core/server/errors/index.js similarity index 85% rename from core/server/errorHandling.js rename to core/server/errors/index.js index f7294605a8..30c0fc5af5 100644 --- a/core/server/errorHandling.js +++ b/core/server/errors/index.js @@ -1,10 +1,17 @@ /*jslint regexp: true */ -var _ = require('lodash'), - colors = require('colors'), - config = require('./config'), - path = require('path'), - when = require('when'), - hbs = require('express-hbs'), +var _ = require('lodash'), + colors = require('colors'), + config = require('../config'), + path = require('path'), + when = require('when'), + hbs = require('express-hbs'), + NotFoundError = require('./notfounderror'), + BadRequestError = require('./badrequesterror'), + InternalServerError = require('./internalservererror'), + NoPermissionError = require('./nopermissionerror'), + RequestEntityTooLargeError = require('./requesttoolargeerror'), + UnauthorizedError = require('./unauthorizederror'), + ValidationError = require('./validationerror'), errors, // Paths for views @@ -242,4 +249,11 @@ _.each([ errors[funcName] = errors[funcName].bind(errors); }); -module.exports = errors; +module.exports = errors; +module.exports.NotFoundError = NotFoundError; +module.exports.BadRequestError = BadRequestError; +module.exports.InternalServerError = InternalServerError; +module.exports.NoPermissionError = NoPermissionError; +module.exports.UnauthorizedError = UnauthorizedError; +module.exports.ValidationError = ValidationError; +module.exports.RequestEntityTooLargeError = RequestEntityTooLargeError; diff --git a/core/server/errors/internalservererror.js b/core/server/errors/internalservererror.js new file mode 100644 index 0000000000..bb4cfd9a03 --- /dev/null +++ b/core/server/errors/internalservererror.js @@ -0,0 +1,15 @@ +// # Internal Server Error +// Custom error class with status code and type prefilled. + +function InternalServerError(message) { + this.message = message; + this.stack = new Error().stack; + this.code = 500; + this.type = this.name; +} + +InternalServerError.prototype = Object.create(Error.prototype); +InternalServerError.prototype.name = "InternalServerError"; + + +module.exports = InternalServerError; \ No newline at end of file diff --git a/core/server/errors/nopermissionerror.js b/core/server/errors/nopermissionerror.js new file mode 100644 index 0000000000..05096510d9 --- /dev/null +++ b/core/server/errors/nopermissionerror.js @@ -0,0 +1,15 @@ +// # No Permission Error +// Custom error class with status code and type prefilled. + +function NoPermissionError(message) { + this.message = message; + this.stack = new Error().stack; + this.code = 403; + this.type = this.name; +} + +NoPermissionError.prototype = Object.create(Error.prototype); +NoPermissionError.prototype.name = "NoPermissionError"; + + +module.exports = NoPermissionError; \ No newline at end of file diff --git a/core/server/errors/notfounderror.js b/core/server/errors/notfounderror.js new file mode 100644 index 0000000000..b0d11cd4c1 --- /dev/null +++ b/core/server/errors/notfounderror.js @@ -0,0 +1,15 @@ +// # Not found error +// Custom error class with status code and type prefilled. + +function NotFoundError(message) { + this.message = message; + this.stack = new Error().stack; + this.code = 404; + this.type = this.name; +} + +NotFoundError.prototype = Object.create(Error.prototype); +NotFoundError.prototype.name = "NotFoundError"; + + +module.exports = NotFoundError; \ No newline at end of file diff --git a/core/server/errors/requesttoolargeerror.js b/core/server/errors/requesttoolargeerror.js new file mode 100644 index 0000000000..34cdf1db37 --- /dev/null +++ b/core/server/errors/requesttoolargeerror.js @@ -0,0 +1,15 @@ +// # Request Entity Too Large Error +// Custom error class with status code and type prefilled. + +function RequestEntityTooLargeError(message) { + this.message = message; + this.stack = new Error().stack; + this.code = 413; + this.type = this.name; +} + +RequestEntityTooLargeError.prototype = Object.create(Error.prototype); +RequestEntityTooLargeError.prototype.name = "RequestEntityTooLargeError"; + + +module.exports = RequestEntityTooLargeError; \ No newline at end of file diff --git a/core/server/errors/unauthorizederror.js b/core/server/errors/unauthorizederror.js new file mode 100644 index 0000000000..a928f77493 --- /dev/null +++ b/core/server/errors/unauthorizederror.js @@ -0,0 +1,15 @@ +// # Unauthorized error +// Custom error class with status code and type prefilled. + +function UnauthorizedError(message) { + this.message = message; + this.stack = new Error().stack; + this.code = 404; + this.type = this.name; +} + +UnauthorizedError.prototype = Object.create(Error.prototype); +UnauthorizedError.prototype.name = "UnauthorizedError"; + + +module.exports = UnauthorizedError; \ No newline at end of file diff --git a/core/server/errors/validationerror.js b/core/server/errors/validationerror.js new file mode 100644 index 0000000000..c6d88eea9a --- /dev/null +++ b/core/server/errors/validationerror.js @@ -0,0 +1,22 @@ +// # Validation Error +// Custom error class with status code and type prefilled. + +function ValidationError(message) { + return new ValidationError(message, null); +} + +function ValidationError(message, offendingProperty) { + this.message = message; + this.stack = new Error().stack; + this.code = 422; + if (offendingProperty) { + this.property = offendingProperty; + } + this.type = this.name; +} + +ValidationError.prototype = Object.create(Error.prototype); +ValidationError.prototype.name = "ValidationError"; + + +module.exports = ValidationError; \ No newline at end of file diff --git a/core/server/helpers/index.js b/core/server/helpers/index.js index 848788c238..a96018e5ef 100644 --- a/core/server/helpers/index.js +++ b/core/server/helpers/index.js @@ -7,7 +7,7 @@ var downsize = require('downsize'), api = require('../api'), config = require('../config'), - errors = require('../errorHandling'), + errors = require('../errors'), filters = require('../filters'), template = require('./template'), schema = require('../data/schema').checks, diff --git a/core/server/helpers/template.js b/core/server/helpers/template.js index 622ebab03c..008a18be5b 100644 --- a/core/server/helpers/template.js +++ b/core/server/helpers/template.js @@ -1,6 +1,6 @@ var templates = {}, hbs = require('express-hbs'), - errors = require('../errorHandling'); + errors = require('../errors'); // ## Template utils diff --git a/core/server/index.js b/core/server/index.js index f88d6964c3..3cd9c7650d 100644 --- a/core/server/index.js +++ b/core/server/index.js @@ -12,7 +12,7 @@ var crypto = require('crypto'), api = require('./api'), config = require('./config'), - errors = require('./errorHandling'), + errors = require('./errors'), helpers = require('./helpers'), mailer = require('./mail'), middleware = require('./middleware'), diff --git a/core/server/middleware/ghost-busboy.js b/core/server/middleware/ghost-busboy.js index 1f28b561d8..c5240cc964 100644 --- a/core/server/middleware/ghost-busboy.js +++ b/core/server/middleware/ghost-busboy.js @@ -2,7 +2,8 @@ var BusBoy = require('busboy'), fs = require('fs-extra'), path = require('path'), os = require('os'), - crypto = require('crypto'); + crypto = require('crypto'), + errors = require('../errors'); // ### ghostBusboy // Process multipart file streams @@ -55,7 +56,7 @@ function ghostBusBoy(req, res, next) { busboy.on('limit', function () { hasError = true; - res.send(413, {type: 'RequestEntityTooLarge', message: 'File size limit breached.'}); + res.send(413, new errors.RequestEntityTooLargeError('File size limit breached.')); }); busboy.on('error', function (error) { diff --git a/core/server/middleware/index.js b/core/server/middleware/index.js index c18fd9253d..c0eb3787ad 100644 --- a/core/server/middleware/index.js +++ b/core/server/middleware/index.js @@ -5,7 +5,7 @@ var api = require('../api'), BSStore = require('../bookshelf-session'), config = require('../config'), - errors = require('../errorHandling'), + errors = require('../errors'), express = require('express'), fs = require('fs'), hbs = require('express-hbs'), diff --git a/core/server/models/post.js b/core/server/models/post.js index 51ca73bd04..68db3cc500 100644 --- a/core/server/models/post.js +++ b/core/server/models/post.js @@ -2,7 +2,7 @@ var _ = require('lodash'), uuid = require('node-uuid'), when = require('when'), - errors = require('../errorHandling'), + errors = require('../errors'), Showdown = require('showdown'), ghostgfm = require('../../shared/lib/showdown/extensions/ghostgfm'), converter = new Showdown.converter({extensions: [ghostgfm]}), diff --git a/core/server/models/settings.js b/core/server/models/settings.js index 5c52478e47..53319ea9f2 100644 --- a/core/server/models/settings.js +++ b/core/server/models/settings.js @@ -2,7 +2,7 @@ var Settings, ghostBookshelf = require('./base'), uuid = require('node-uuid'), _ = require('lodash'), - errors = require('../errorHandling'), + errors = require('../errors'), when = require('when'), validation = require('../data/validation'), @@ -100,7 +100,7 @@ Settings = ghostBookshelf.Model.extend({ // Accept an array of models as input if (item.toJSON) { item = item.toJSON(); } if (!(_.isString(item.key) && item.key.length > 0)) { - return when.reject({type: 'ValidationError', message: 'Setting key cannot be empty.'}); + return when.reject(new errors.ValidationError('Setting key cannot be empty.')); } item = self.filterData(item); @@ -111,7 +111,7 @@ Settings = ghostBookshelf.Model.extend({ return setting.save({value: item.value}, options); } - return when.reject({type: 'NotFound', message: 'Unable to find setting to update: ' + item.key}); + return when.reject(new errors.NotFoundError('Unable to find setting to update: ' + item.key)); }, errors.logAndThrowError); }); diff --git a/core/server/models/user.js b/core/server/models/user.js index 25e59e0c0c..7dec00fad4 100644 --- a/core/server/models/user.js +++ b/core/server/models/user.js @@ -1,6 +1,6 @@ var _ = require('lodash'), when = require('when'), - errors = require('../errorHandling'), + errors = require('../errors'), nodefn = require('when/node/function'), bcrypt = require('bcryptjs'), Posts = require('./post').Posts, diff --git a/core/server/permissions/effective.js b/core/server/permissions/effective.js index dba9d06b94..d4c4db61dc 100644 --- a/core/server/permissions/effective.js +++ b/core/server/permissions/effective.js @@ -1,6 +1,6 @@ var _ = require('lodash'), Models = require('../models'), - errors = require('../errorHandling'), + errors = require('../errors'), User = Models.User, App = Models.App; diff --git a/core/server/storage/index.js b/core/server/storage/index.js index 26500240c3..e585264828 100644 --- a/core/server/storage/index.js +++ b/core/server/storage/index.js @@ -1,4 +1,4 @@ -var errors = require('../errorHandling'), +var errors = require('../errors'), storage; function get_storage() { diff --git a/core/server/storage/localfilesystem.js b/core/server/storage/localfilesystem.js index d57c7db012..d6de2e14de 100644 --- a/core/server/storage/localfilesystem.js +++ b/core/server/storage/localfilesystem.js @@ -7,7 +7,7 @@ var _ = require('lodash'), nodefn = require('when/node/function'), path = require('path'), when = require('when'), - errors = require('../errorHandling'), + errors = require('../errors'), config = require('../config'), baseStore = require('./base'), diff --git a/core/server/update-check.js b/core/server/update-check.js index ff82a793b4..d0f0cf4d1b 100644 --- a/core/server/update-check.js +++ b/core/server/update-check.js @@ -30,7 +30,7 @@ var crypto = require('crypto'), api = require('./api'), config = require('./config'), - errors = require('./errorHandling'), + errors = require('./errors'), packageInfo = require('../../package.json'), allowedCheckEnvironments = ['development', 'production'], diff --git a/core/server/xmlrpc.js b/core/server/xmlrpc.js index 2703896651..98637866ee 100644 --- a/core/server/xmlrpc.js +++ b/core/server/xmlrpc.js @@ -1,6 +1,6 @@ var _ = require('lodash'), config = require('./config'), - errors = require('./errorHandling'), + errors = require('./errors'), http = require('http'), xml = require('xml'), pingList; diff --git a/core/test/integration/api/api_db_spec.js b/core/test/integration/api/api_db_spec.js index 467d0b0df3..51d43b03f3 100644 --- a/core/test/integration/api/api_db_spec.js +++ b/core/test/integration/api/api_db_spec.js @@ -55,7 +55,7 @@ describe('DB API', function () { done(); }); }).catch(function (error) { - done('error', error); + done(error); }); }); @@ -65,17 +65,17 @@ describe('DB API', function () { }).then(function (){ done(new Error("Delete all content is not denied for editor.")); }, function (error) { - error.type.should.eql('NoPermission'); + error.type.should.eql('NoPermissionError'); return dbAPI.deleteAllContent.call({user: 3}); }).then(function (){ done(new Error("Delete all content is not denied for author.")); }, function (error) { - error.type.should.eql('NoPermission'); + error.type.should.eql('NoPermissionError'); return dbAPI.deleteAllContent(); }).then(function (){ done(new Error("Delete all content is not denied without authentication.")); }).catch(function (error) { - error.type.should.eql('NoPermission'); + error.type.should.eql('NoPermissionError'); done(); }); }); @@ -86,17 +86,17 @@ describe('DB API', function () { }).then(function (){ done(new Error("Export content is not denied for editor.")); }, function (error) { - error.type.should.eql('NoPermission'); + error.type.should.eql('NoPermissionError'); return dbAPI.exportContent.call({user: 3}); }).then(function (){ done(new Error("Export content is not denied for author.")); }, function (error) { - error.type.should.eql('NoPermission'); + error.type.should.eql('NoPermissionError'); return dbAPI.exportContent(); }).then(function (){ done(new Error("Export content is not denied without authentication.")); }).catch(function (error) { - error.type.should.eql('NoPermission'); + error.type.should.eql('NoPermissionError'); done(); }); }); @@ -107,17 +107,17 @@ describe('DB API', function () { }).then(function (result){ done(new Error("Import content is not denied for editor.")); }, function (error) { - error.type.should.eql('NoPermission'); + error.type.should.eql('NoPermissionError'); return dbAPI.importContent.call({user: 3}); }).then(function (result){ done(new Error("Import content is not denied for author.")); }, function (error) { - error.type.should.eql('NoPermission'); + error.type.should.eql('NoPermissionError'); return dbAPI.importContent(); }).then(function (result){ done(new Error("Import content is not denied without authentication.")); }).catch(function (error) { - error.type.should.eql('NoPermission'); + error.type.should.eql('NoPermissionError'); done(); }); }); diff --git a/core/test/integration/model/model_permissions_spec.js b/core/test/integration/model/model_permissions_spec.js index f601b476e4..370a3e571d 100644 --- a/core/test/integration/model/model_permissions_spec.js +++ b/core/test/integration/model/model_permissions_spec.js @@ -1,7 +1,7 @@ /*globals describe, it, before, beforeEach, afterEach */ var testUtils = require('../../utils'), should = require('should'), - errors = require('../../../server/errorHandling'), + errors = require('../../../server/errors'), // Stuff we are testing Models = require('../../../server/models'); diff --git a/core/test/integration/model/model_users_spec.js b/core/test/integration/model/model_users_spec.js index f1f4601bec..fbca93a2f2 100644 --- a/core/test/integration/model/model_users_spec.js +++ b/core/test/integration/model/model_users_spec.js @@ -3,7 +3,7 @@ var testUtils = require('../../utils'), should = require('should'), when = require('when'), _ = require('lodash'), - errors = require('../../../server/errorHandling'), + errors = require('../../../server/errors'), sinon = require('sinon'), uuid = require('node-uuid'), diff --git a/core/test/unit/errorHandling_spec.js b/core/test/unit/errorHandling_spec.js index 4c815a3bc1..585f38bae7 100644 --- a/core/test/unit/errorHandling_spec.js +++ b/core/test/unit/errorHandling_spec.js @@ -8,7 +8,7 @@ var testUtils = require('../utils'), // Stuff we are testing colors = require('colors'), - errors = rewire('../../server/errorHandling'), + errors = rewire('../../server/errors'), // storing current environment currentEnv = process.env.NODE_ENV; diff --git a/core/test/unit/export_spec.js b/core/test/unit/export_spec.js index 4d3f1aa3d8..f486abec4d 100644 --- a/core/test/unit/export_spec.js +++ b/core/test/unit/export_spec.js @@ -4,7 +4,7 @@ var testUtils = require('../utils'), sinon = require('sinon'), when = require('when'), _ = require("lodash"), - errors = require('../../server/errorHandling'), + errors = require('../../server/errors'), // Stuff we are testing migration = require('../../server/data/migration'), diff --git a/core/test/unit/import_spec.js b/core/test/unit/import_spec.js index f8c095d28a..1a3dd9c5c3 100644 --- a/core/test/unit/import_spec.js +++ b/core/test/unit/import_spec.js @@ -5,7 +5,7 @@ var testUtils = require('../utils'), when = require('when'), assert = require('assert'), _ = require("lodash"), - errors = require('../../server/errorHandling'), + errors = require('../../server/errors'), // Stuff we are testing knex = require("../../server/models/base").knex, diff --git a/core/test/unit/permissions_spec.js b/core/test/unit/permissions_spec.js index 2ee860482b..a6a5dbd3d0 100644 --- a/core/test/unit/permissions_spec.js +++ b/core/test/unit/permissions_spec.js @@ -4,7 +4,7 @@ var testUtils = require('../utils'), sinon = require('sinon'), when = require('when'), _ = require("lodash"), - errors = require('../../server/errorHandling'), + errors = require('../../server/errors'), // Stuff we are testing permissions = require('../../server/permissions'), diff --git a/index.js b/index.js index 6cfca38bc0..270f69b7cf 100644 --- a/index.js +++ b/index.js @@ -3,7 +3,7 @@ // When run from command line. var ghost = require('./core'), - errors = require('./core/server/errorHandling'); + errors = require('./core/server/errors'); ghost().otherwise(function (err) { errors.logErrorAndExit(err, err.context, err.help);