Ghost/core/server/data/validation/index.js
Harry Wolff be37070fb6 This aims to speed up both the ghost application and tests by
migration from usage of config() to just an object of config.

no relevant issue

- Change 'loadConfig' task to 'ensureConfig' to more accurately reflect
what it is actually doing.  Its sole purpose is to make sure a `config.js`
 file exists, and as such the name now reflects that purpose.

- Update config/index.js to export the ghostConfig object directly
so that it can be accessed from other modules

- Update all references of config(). to config.
This was a blind global find all and replace, treat it as such.

- Fixes to tests to support new config access method

- Allow each test to still work when invoked invidually
2014-07-22 22:37:44 -04:00

170 lines
6.2 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var schema = require('../schema').tables,
_ = require('lodash'),
validator = require('validator'),
when = require('when'),
errors = require('../../errors'),
config = require('../../config'),
requireTree = require('../../require-tree').readAll,
validateSchema,
validateSettings,
validateActiveTheme,
validate,
availableThemes;
// Provide a few custom validators
//
validator.extend('empty', function (str) {
return _.isEmpty(str);
});
validator.extend('notContains', function (str, badString) {
return !_.contains(str, badString);
});
validator.extend('isEmptyOrURL', function (str) {
return (_.isEmpty(str) || validator.isURL(str, { protocols: ['http', 'https'], require_protocol: true }));
});
// Validation validation against schema attributes
// values are checked against the validation objects
// form schema.js
validateSchema = function (tableName, model) {
var columns = _.keys(schema[tableName]),
validationErrors = [];
_.each(columns, function (columnKey) {
var message = '';
// check nullable
if (model.hasOwnProperty(columnKey) && schema[tableName][columnKey].hasOwnProperty('nullable')
&& schema[tableName][columnKey].nullable !== true) {
if (validator.isNull(model[columnKey]) || validator.empty(model[columnKey])) {
message = 'Value in [' + tableName + '.' + columnKey + '] cannot be blank.';
validationErrors.push(new errors.ValidationError(message, tableName + '.' + columnKey));
}
}
// TODO: check if mandatory values should be enforced
if (model[columnKey] !== null && model[columnKey] !== undefined) {
// check length
if (schema[tableName][columnKey].hasOwnProperty('maxlength')) {
if (!validator.isLength(model[columnKey], 0, schema[tableName][columnKey].maxlength)) {
message = 'Value in [' + tableName + '.' + columnKey + '] exceeds maximum length of '
+ schema[tableName][columnKey].maxlength + ' characters.';
validationErrors.push(new errors.ValidationError(message, tableName + '.' + columnKey));
}
}
//check validations objects
if (schema[tableName][columnKey].hasOwnProperty('validations')) {
validationErrors = 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 not an integer.';
validationErrors.push(new errors.ValidationError(message, tableName + '.' + columnKey));
}
}
}
});
if (validationErrors.length !== 0) {
return when.reject(validationErrors);
}
return when.resolve();
};
// Validation for settings
// settings are checked against the validation objects
// form default-settings.json
validateSettings = function (defaultSettings, model) {
var values = model.toJSON(),
validationErrors = [],
matchingDefault = defaultSettings[values.key];
if (matchingDefault && matchingDefault.validations) {
validationErrors = validationErrors.concat(validate(values.value, values.key, matchingDefault.validations));
}
if (validationErrors.length !== 0) {
return when.reject(validationErrors);
}
return when.resolve();
};
// A Promise that will resolve to an object with a property for each installed theme.
// This is necessary because certain configuration data is only available while Ghost
// is running and at times the validations are used when it's not (e.g. tests)
availableThemes = requireTree(config.paths.themePath);
validateActiveTheme = function (themeName) {
// If Ghost is running and its availableThemes collection exists
// give it priority.
if (config.paths.availableThemes && Object.keys(config.paths.availableThemes).length > 0) {
availableThemes = when(config.paths.availableThemes);
}
return availableThemes.then(function (themes) {
if (!themes.hasOwnProperty(themeName)) {
return when.reject(new errors.ValidationError(themeName + ' cannot be activated because it is not currently installed.', 'activeTheme'));
}
return when.resolve();
});
};
// Validate default settings using the validator module.
// Each validation's key is a method name and its value is an array of options
//
// eg:
// validations: { isURL: true, isLength: [20, 40] }
//
// will validate that a setting's length is a URL between 20 and 40 chars.
//
// If you pass a boolean as the value, it will specify the "good" result. By default
// the "good" result is assumed to be true.
//
// eg:
// validations: { isNull: false } // means the "good" result would
// // fail the `isNull` check, so
// // not null.
//
// available validators: https://github.com/chriso/validator.js#validators
validate = function (value, key, validations) {
var validationErrors = [];
_.each(validations, function (validationOptions, validationName) {
var goodResult = true;
if (_.isBoolean(validationOptions)) {
goodResult = validationOptions;
validationOptions = [];
} else if (!_.isArray(validationOptions)) {
validationOptions = [validationOptions];
}
validationOptions.unshift(value);
// equivalent of validator.isSomething(option1, option2)
if (validator[validationName].apply(validator, validationOptions) !== goodResult) {
validationErrors.push(new errors.ValidationError('Validation (' + validationName + ') failed for ' + key, key));
}
validationOptions.shift();
}, this);
return validationErrors;
};
module.exports = {
validateSchema: validateSchema,
validateSettings: validateSettings,
validateActiveTheme: validateActiveTheme
};