Ghost/core/server/helpers/get.js

159 lines
4.0 KiB
JavaScript
Raw Normal View History

// # Get Helper
// Usage: `{{#get "posts" limit="5"}}`, `{{#get "tags" limit="all"}}`
// Fetches data from the API
var proxy = require('./proxy'),
_ = require('lodash'),
Promise = require('bluebird'),
jsonpath = require('jsonpath'),
logging = proxy.logging,
i18n = proxy.i18n,
createFrame = proxy.hbs.handlebars.createFrame,
api = proxy.api,
labs = proxy.labs,
resources,
pathAliases,
get;
// Endpoints that the helper is able to access
resources = ['posts', 'tags', 'users'];
// Short forms of paths which we should understand
pathAliases = {
'post.tags': 'post.tags[*].slug',
'post.author': 'post.author.slug'
};
/**
* ## Is Browse
* Is this a Browse request or a Read request?
* @param {Object} resource
* @param {Object} options
* @returns {boolean}
*/
function isBrowse(resource, options) {
var browse = true;
if (options.id || options.slug) {
browse = false;
}
return browse;
}
/**
* ## Resolve Paths
* Find and resolve path strings
*
* @param {Object} data
* @param {String} value
* @returns {String}
*/
function resolvePaths(data, value) {
var regex = /\{\{(.*?)\}\}/g;
value = value.replace(regex, function (match, path) {
var result;
// Handle aliases
path = pathAliases[path] ? pathAliases[path] : path;
// Handle Handlebars .[] style arrays
path = path.replace(/\.\[/g, '[');
// Do the query, and convert from array to string
result = jsonpath.query(data, path).join(',');
return result;
});
return value;
}
/**
* ## Parse Options
* Ensure options passed in make sense
*
* @param {Object} data
* @param {Object} options
* @returns {*}
*/
function parseOptions(data, options) {
if (_.isString(options.filter)) {
options.filter = resolvePaths(data, options.filter);
}
return options;
}
/**
* ## Get
* @param {Object} resource
* @param {Object} options
* @returns {Promise}
*/
get = function get(resource, options) {
options = options || {};
options.hash = options.hash || {};
options.data = options.data || {};
var self = this,
data = createFrame(options.data),
apiOptions = options.hash,
apiMethod;
if (!options.fn) {
data.error = i18n.t('warnings.helpers.get.mustBeCalledAsBlock', {helperName: 'get'});
🎨 configurable logging with bunyan (#7431) - 🛠 add bunyan and prettyjson, remove morgan - ✨ add logging module - GhostLogger class that handles setup of bunyan - PrettyStream for stdout - ✨ config for logging - @TODO: testing level fatal? - ✨ log each request via GhostLogger (express middleware) - @TODO: add errors to output - 🔥 remove errors.updateActiveTheme - we can read the value from config - 🔥 remove 15 helper functions in core/server/errors/index.js - all these functions get replaced by modules: 1. logging 2. error middleware handling for html/json 3. error creation (which will be part of PR #7477) - ✨ add express error handler for html/json - one true error handler for express responses - contains still some TODO's, but they are not high priority for first implementation/integration - this middleware only takes responsibility of either rendering html responses or return json error responses - 🎨 use new express error handler in middleware/index - 404 and 500 handling - 🎨 return error instead of error message in permissions/index.js - the rule for error handling should be: if you call a unit, this unit should return a custom Ghost error - 🎨 wrap serve static module - rule: if you call a module/unit, you should always wrap this error - it's always the same rule - so the caller never has to worry about what comes back - it's always a clear error instance - in this case: we return our notfounderror if serve static does not find the resource - this avoid having checks everywhere - 🎨 replace usages of errors/index.js functions and adapt tests - use logging.error, logging.warn - make tests green - remove some usages of logging and throwing api errors -> because when a request is involved, logging happens automatically - 🐛 return errorDetails to Ghost-Admin - errorDetails is used for Theme error handling - 🎨 use 500er error for theme is missing error in theme-handler - 🎨 extend file rotation to 1w
2016-10-04 18:33:43 +03:00
logging.warn(data.error);
return Promise.resolve();
}
if (!_.includes(resources, resource)) {
data.error = i18n.t('warnings.helpers.get.invalidResource');
🎨 configurable logging with bunyan (#7431) - 🛠 add bunyan and prettyjson, remove morgan - ✨ add logging module - GhostLogger class that handles setup of bunyan - PrettyStream for stdout - ✨ config for logging - @TODO: testing level fatal? - ✨ log each request via GhostLogger (express middleware) - @TODO: add errors to output - 🔥 remove errors.updateActiveTheme - we can read the value from config - 🔥 remove 15 helper functions in core/server/errors/index.js - all these functions get replaced by modules: 1. logging 2. error middleware handling for html/json 3. error creation (which will be part of PR #7477) - ✨ add express error handler for html/json - one true error handler for express responses - contains still some TODO's, but they are not high priority for first implementation/integration - this middleware only takes responsibility of either rendering html responses or return json error responses - 🎨 use new express error handler in middleware/index - 404 and 500 handling - 🎨 return error instead of error message in permissions/index.js - the rule for error handling should be: if you call a unit, this unit should return a custom Ghost error - 🎨 wrap serve static module - rule: if you call a module/unit, you should always wrap this error - it's always the same rule - so the caller never has to worry about what comes back - it's always a clear error instance - in this case: we return our notfounderror if serve static does not find the resource - this avoid having checks everywhere - 🎨 replace usages of errors/index.js functions and adapt tests - use logging.error, logging.warn - make tests green - remove some usages of logging and throwing api errors -> because when a request is involved, logging happens automatically - 🐛 return errorDetails to Ghost-Admin - errorDetails is used for Theme error handling - 🎨 use 500er error for theme is missing error in theme-handler - 🎨 extend file rotation to 1w
2016-10-04 18:33:43 +03:00
logging.warn(data.error);
return Promise.resolve(options.inverse(self, {data: data}));
}
// Determine if this is a read or browse
apiMethod = isBrowse(resource, apiOptions) ? api[resource].browse : api[resource].read;
// Parse the options we're going to pass to the API
apiOptions = parseOptions(this, apiOptions);
return apiMethod(apiOptions).then(function success(result) {
var blockParams;
// block params allows the theme developer to name the data using something like
// `{{#get "posts" as |result pageInfo|}}`
blockParams = [result[resource]];
if (result.meta && result.meta.pagination) {
result.pagination = result.meta.pagination;
blockParams.push(result.meta.pagination);
}
// Call the main template function
return options.fn(result, {
data: data,
blockParams: blockParams
});
}).catch(function error(err) {
logging.error(err);
data.error = err.message;
return options.inverse(self, {data: data});
});
};
module.exports = function getLabsWrapper() {
var self = this,
args = arguments;
return labs.enabledHelper({
flagKey: 'publicAPI',
flagName: 'Public API',
helperName: 'get',
helpUrl: 'https://help.ghost.org/hc/en-us/articles/115000301672-Public-API-Beta',
async: true
}, function executeHelper() {
return get.apply(self, args);
});
};