mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-13 22:41:32 -05:00
51ac3f6532
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
102 lines
3.4 KiB
JavaScript
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;
|