0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-13 22:41:32 -05:00
ghost/core/server/api/utils.js
Hannah Wolfe 51ac3f6532 Refactor to using pipeline for the API
refs #2758

- Post, Tag & User API methods are refactored to use pipeline
- Each functional code block is a named task function
- Each function takes options, manipulates it, and returns options back
- Tasks like permissions can reject if they don't pass, causing the pipeline to fail
- Tasks like validating and converting options might be abstracted out into utils - the same for each endpoint
- Tasks like the data call can be extremely complex if needs be (like for some user endpoints)
- Option validation is mostly factored out to utils
- Option conversion is factored out to utils
- API utils have 100% test coverage
- Minor updates to inline docs, more to do here
2015-06-28 22:52:31 +01:00

102 lines
3.4 KiB
JavaScript

// # API Utils
// Shared helpers for working with the API
var Promise = require('bluebird'),
_ = require('lodash'),
path = require('path'),
errors = require('../errors'),
utils;
utils = {
validate: function validate(docName, attrs) {
return function doValidate() {
var object, options;
if (arguments.length === 2) {
object = arguments[0];
options = _.clone(arguments[1]) || {};
} else if (arguments.length === 1) {
options = _.clone(arguments[0]) || {};
} else {
options = {};
}
if (attrs) {
options.data = _.pick(options, attrs);
options = _.omit(options, attrs);
}
if (object) {
return utils.checkObject(object, docName, options.id).then(function (data) {
options.data = data;
return Promise.resolve(options);
});
}
return Promise.resolve(options);
};
},
prepareInclude: function prepareInclude(include, allowedIncludes) {
include = include || '';
include = _.intersection(include.split(','), allowedIncludes);
return include;
},
/**
* @param {Array} allowedIncludes
* @returns {Function} doConversion
*/
convertOptions: function convertOptions(allowedIncludes) {
/**
* Convert our options from API-style to Model-style
* @param {Object} options
* @returns {Object} options
*/
return function doConversion(options) {
if (options.include) {
options.include = utils.prepareInclude(options.include, allowedIncludes);
}
return options;
};
},
/**
* ### Check Object
* Check an object passed to the API is in the correct format
*
* @param {Object} object
* @param {String} docName
* @returns {Promise(Object)} resolves to the original object if it checks out
*/
checkObject: function (object, docName, editId) {
if (_.isEmpty(object) || _.isEmpty(object[docName]) || _.isEmpty(object[docName][0])) {
return errors.logAndRejectError(new errors.BadRequestError('No root key (\'' + docName + '\') provided.'));
}
// convert author property to author_id to match the name in the database
if (docName === 'posts') {
if (object.posts[0].hasOwnProperty('author')) {
object.posts[0].author_id = object.posts[0].author;
delete object.posts[0].author;
}
}
if (editId && object[docName][0].id && parseInt(editId, 10) !== parseInt(object[docName][0].id, 10)) {
return errors.logAndRejectError(new errors.BadRequestError('Invalid id provided.'));
}
return Promise.resolve(object);
},
checkFileExists: function (options, filename) {
return !!(options[filename] && options[filename].type && options[filename].path);
},
checkFileIsValid: function (file, types, extensions) {
var type = file.type,
ext = path.extname(file.name).toLowerCase();
if (_.contains(types, type) && _.contains(extensions, ext)) {
return true;
}
return false;
}
};
module.exports = utils;