/**
* @overview provides methods for fetching reports via CloudHealth's API
* @see {@link https://github.com/CloudHealth/cht_api_guide#reporting-api}
* @author Ben Watson <ben.watson@coxautoinc.com>
*/
var utils = require('../utils/chapi');
var async = require('async');
/**
* @class Report
* @memberof module:cox-chapi
* @param {string} [api_key]
*/
function Report(api_key) {
this.set_api_key(api_key);
}
/**
* sets the api key to use when making calls to CloudHealth's API
* @function module:cox-chapi.Report#set_api_key
* @param {string} api_key
*/
Report.prototype.set_api_key = function set_api_key(api_key) {
this._api_key = api_key;
}
/**
* creates and returns an options object that can be given to utils.send_request
* @private
* @param {string} method - the method to use (ie. GET, POST, etc.)
* @param {string} [path] - a string to append to the path field of options
* @param {string[]} [params] - an array of parameters to add to the url in the
* form "key=value"
* @return {object} an options object
*/
Report.prototype._options = function _options(method, path, params) {
return utils._options('/olap_reports', method, path, params, this._api_key);
};
/**
* gets a list of topics (or reports for a topic if a topic id is specified)
* @function module:cox-chapi.Report#list
* @param {object} [flags] - an optional object specifying the following options
* @param {boolean} [flags.nest] - if true, each topic will have a "reports"
* field containing the result of calling this function for that topic
* @param {boolean} [flags.all] - same as nest
* @param {string} [topic] - the topic to list subtopics for
* @param {arrayCallback} cb - called with an array of objects, each containing
* a name and id field for a topic
*/
Report.prototype.list = function list(flags, topic, cb) {
if (typeof flags === 'function') {
cb = flags;
topic = '';
flags = undefined;
}
else if (typeof topic === 'function') {
cb = topic;
topic = '';
if (typeof flags === 'string') {
topic = '/' + flags;
flags = undefined;
}
}
else {
topic = '/' + topic;
}
this._list(flags, topic, cb);
};
/**
* Helper for #list
* @private
*/
Report.prototype._list = function _list(flags, topic, cb) {
if (flags && (flags.nest || flags.all)) {
this._nest(cb);
}
else {
var options = this._options('GET', topic);
utils.send_request(options, null, this._list_cb.bind(this, flags, topic, cb));
}
};
/**
* Helper callback for #list
* @private
*/
Report.prototype._list_cb = function _list_cb(flags, topic, cb, err, result) {
if (err) return cb(err, result);
var topics = this._format_list(result);
cb(null, topics);
};
/**
* gets a list of topics, each having an array of reports named "reports"
* @private
* @function module:cox-chapi.Report#_nest
* @param {arrayCallback} cb - called with the list of topics
*/
Report.prototype._nest = function _nest(cb) {
this.list((err, topics) => {
if (err) return cb(err);
var list_calls = [];
for (var i=0; i<topics.length; i++) {
var topic = topics[i];
list_calls.push(((index, topic, list_cb) => {
this.list(topic.id, (err, reports) => {
if (err) return list_cb(err);
list_cb(null, {index: index, reports: reports});
});
}).bind(this, i, topic));
}
async.parallel(list_calls, (err, reports) => {
if (err) return cb(err);
for (var report of reports) {
topics[report.index].reports = report.reports;
}
cb(null, topics);
});
});
};
/**
* Formats the result of calling "list"
* @private
* @function module:cox-chapi.Report#_format_list
* @param {object} result - the result of calling "list"
* @return {array} an array of topics extracted from the result
*/
Report.prototype._format_list = function _format_list(result) {
var links = result.links;
var keys = Object.keys(links);
var topics = [];
for (var i=0; i<keys.length; ++i) {
var key = keys[i];
var topic = {
name: key,
}
var match = links[key].href.match(/report_id=([0-9]+)/);
if (match) {
topic.id = match[1];
}
else {
topic.id = links[key].href.match(/olap_reports\/(.+)$/)[1];
}
topics.push(topic);
}
return topics;
};
/**
* gets data for the report with the given id
* @function module:cox-chapi.Report#get
* @param {string} id - the id of the report
* @param {objectCallback} cb - yields the report data
*/
Report.prototype.get = function get(id, cb) {
if ((''+id).match(/^[0-9]+$/)) {
id = `custom/${id}`;
}
var options = this._options('GET', '/' + id);
utils.send_request(options, null, cb);
};
/**
* lists the possible dimensions for generating a report under a base
* @function module:cox-chapi.Report#dimensions
* @param {object} [flags] - an object containing flags
* @param {boolean} [flags.short] - specifies to yield only the name and label
* of dimensions and measures
* @param {string} base - the category/sub-category for the report to show in
* the form "category/sub-category" (ie. "usage/instance" or "cost/history")
* @param {objectCallback} cb - yields an object containing dimensions (used
* for x-axis or category) and measures (used for y-axis)
*/
Report.prototype.dimensions = function dimensions(flags, base, cb) {
if (typeof flags !== 'object') {
cb = base;
base = flags;
flags = undefined;
}
this._dimensions(flags, base, cb);
};
/**
* Helper for #dimensions
* @private
*/
Report.prototype._dimensions = function _dimensions(flags, base, cb) {
var options = this._options('GET', '/' + base + '/new');
utils.send_request(options, null, this._dimensions_cb.bind(this, flags, base, cb));
};
/**
* Helper callback for #dimensions
* @private
*/
Report.prototype._dimensions_cb = function _dimensions_cb(flags, base, cb, err, result) {
if (err) return cb(err, result);
if (flags && flags.short) {
result.dimensions = result.dimensions.map((dimension) =>
({label: dimension.label, name: dimension.name})
);
result.measures = result.measures.map((measure) =>
({label: measure.label, name: measure.name})
);
}
cb(null, result);
};
/**
* returns data for a custom report built from the parameters you specify
* @function module:cox-chapi.Report#generate
* @param {string} base - the category/sub-category for the report to show in
* the form "category/sub-category" (ie. "usage/instance" or "cost/history")
* @param {string} x - the id of the dimension to use for the x-axis
* @param {string} y - the id of the measure to use for the y-axis
* @param {string} category - the id of the dimension to use for categorizing
* the report's data
* @param {stirng} [interval] - the time interval by which to break up the data
* (only use if either the x or category dimensions are "time")
* @param {objectCallback} cb - yields an object containing data for the report
* as well as data about the report (including dimensions, filters, interval,
* measures, report name, status, and the last time the report was updated)
*/
Report.prototype.generate = function generate(base, x, y, category, interval, cb) {
if (typeof interval === 'function') {
cb = interval;
interval = undefined;
}
this._generate(base, x, y, category, interval, cb);
};
/**
* Helper for #generate
* @private
*/
Report.prototype._generate = function _generate(base, x, y, category, interval, cb) {
if (interval && interval !== '') {
interval = 'interval=' + interval;
}
else {
interval = '';
}
var options = this._options('GET', '/' + base, ['dimensions[]=' + x, 'dimensions[]' + category, 'measures[]=' + y, interval]);
utils.send_request(options, null, cb);
};
module.exports = Report;