UNPKG

kibana-with-account

Version:

kibana-with-account is develop based kibana project

373 lines (323 loc) 10.7 kB
define(function (require) { return function IntervalHelperProvider(Private, timefilter, config) { var _ = require('lodash'); var moment = require('moment'); var dateMath = require('ui/utils/dateMath'); var parseInterval = require('ui/utils/parse_interval'); var calcAuto = Private(require('ui/time_buckets/calc_auto_interval')); var calcEsInterval = Private(require('ui/time_buckets/calc_es_interval')); var tzOffset = moment().format('Z'); function isValidMoment(m) { return m && ('isValid' in m) && m.isValid(); } /** * Helper class for wrapping the concept of an "Interval", * which describes a timespan that will seperate moments. * * @param {state} object - one of "" * @param {[type]} display [description] */ function TimeBuckets() { return TimeBuckets.__cached__(this); } /**** * PUBLIC API ****/ /** * Set the bounds that these buckets are expected to cover. * This is required to support interval "auto" as well * as interval scaling. * * @param {object} input - an object with properties min and max, * representing the edges for the time span * we should cover * * @returns {undefined} */ TimeBuckets.prototype.setBounds = function (input) { if (!input) return this.clearBounds(); var bounds; if (_.isPlainObject(input)) { // accept the response from timefilter.getActiveBounds() bounds = [input.min, input.max]; } else { bounds = _.isArray(input) ? input : []; } var moments = _(bounds) .map(_.ary(moment, 1)) .sortBy(Number); var valid = moments.size() === 2 && moments.every(isValidMoment); if (!valid) { this.clearBounds(); throw new Error('invalid bounds set: ' + input); } this._lb = moments.shift(); this._ub = moments.pop(); if (this.getDuration().asSeconds() < 0) { throw new TypeError('Intervals must be positive'); } }; /** * Clear the stored bounds * * @return {undefined} */ TimeBuckets.prototype.clearBounds = function () { this._lb = this._ub = null; }; /** * Check to see if we have received bounds yet * * @return {Boolean} */ TimeBuckets.prototype.hasBounds = function () { return isValidMoment(this._ub) && isValidMoment(this._lb); }; /** * Return the current bounds, if we have any. * * THIS DOES NOT CLONE THE BOUNDS, so editting them * may have unexpected side-effects. Always * call bounds.min.clone() before editting * * @return {object|undefined} - If bounds are not defined, this * returns undefined, else it returns the bounds * for these buckets. This object has two props, * min and max. Each property will be a moment() * object * */ TimeBuckets.prototype.getBounds = function () { if (!this.hasBounds()) return; return { min: this._lb, max: this._ub }; }; /** * Get a moment duration object representing * the distance between the bounds, if the bounds * are set. * * @return {moment.duration|undefined} */ TimeBuckets.prototype.getDuration = function () { if (!this.hasBounds()) return; return moment.duration(this._ub - this._lb, 'ms'); }; /** * Update the interval at which buckets should be * generated. * * Input can be one of the following: * - Any object from src/ui/agg_types/buckets/_interval_options.js * - "auto" * - Pass a valid moment unit * - a moment.duration object. * * @param {object|string|moment.duration} input - see desc */ TimeBuckets.prototype.setInterval = function (input) { var interval = input; // selection object -> val if (_.isObject(input)) { interval = input.val; } if (!interval || interval === 'auto') { this._i = 'auto'; return; } if (_.isString(interval)) { input = interval; interval = parseInterval(interval); if (+interval === 0) { interval = null; } } // if the value wasn't converted to a duration, and isn't // already a duration, we have a problem if (!moment.isDuration(interval)) { throw new TypeError('"' + input + '" is not a valid interval.'); } this._i = interval; }; /** * Get the interval for the buckets. If the * number of buckets created by the interval set * is larger than config:histogram:maxBars then the * interval will be scaled up. If the number of buckets * created is less than one, the interval is scaled back. * * The interval object returned is a moment.duration * object that has been decorated with the following * properties. * * interval.description: a text description of the interval. * designed to be used list "field per {{ desc }}". * - "minute" * - "10 days" * - "3 years" * * interval.expr: the elasticsearch expression that creates this * interval. If the interval does not properly form an elasticsearch * expression it will be forced into one. * * interval.scaled: the interval was adjusted to * accomidate the maxBars setting. * * interval.scale: the numer that y-values should be * multiplied by * * interval.scaleDescription: a description that reflects * the values which will be produced by using the * interval.scale. * * * @return {[type]} [description] */ TimeBuckets.prototype.getInterval = function () { var self = this; var duration = self.getDuration(); return decorateInterval(maybeScaleInterval(readInterval())); // either pull the interval from state or calculate the auto-interval function readInterval() { var interval = self._i; if (moment.isDuration(interval)) return interval; return calcAuto.near(config.get('histogram:barTarget'), duration); } // check to see if the interval should be scaled, and scale it if so function maybeScaleInterval(interval) { if (!self.hasBounds()) return interval; var maxLength = config.get('histogram:maxBars'); var approxLen = duration / interval; var scaled; if (approxLen > maxLength) { scaled = calcAuto.lessThan(maxLength, duration); } else { return interval; } if (+scaled === +interval) return interval; decorateInterval(interval); return _.assign(scaled, { preScaled: interval, scale: interval / scaled, scaled: true }); } // append some TimeBuckets specific props to the interval function decorateInterval(interval) { var esInterval = calcEsInterval(interval); interval.esValue = esInterval.value; interval.esUnit = esInterval.unit; interval.expression = esInterval.expression; interval.overflow = duration > interval ? moment.duration(interval - duration) : false; var prettyUnits = moment.normalizeUnits(esInterval.unit); if (esInterval.value === 1) { interval.description = prettyUnits; } else { interval.description = esInterval.value + ' ' + prettyUnits + 's'; } return interval; } }; /** * Get a date format string that will represent dates that * progress at our interval. * * Since our interval can be as small as 1ms, the default * date format is usually way too much. with `dateFormat:scaled` * users can modify how dates are formatted within series * produced by TimeBuckets * * @return {string} */ TimeBuckets.prototype.getScaledDateFormat = function () { var interval = this.getInterval(); var rules = config.get('dateFormat:scaled'); for (var i = rules.length - 1; i >= 0; i--) { var rule = rules[i]; if (!rule[0] || interval >= moment.duration(rule[0])) { return rule[1]; } } return config.get('dateFormat'); }; TimeBuckets.__cached__ = function (self) { var cache = {}; var sameMoment = same(moment.isMoment); var sameDuration = same(moment.isDuration); var desc = { __cached__: { value: self }, }; var breakers = { setBounds: 'bounds', clearBounds: 'bounds', setInterval: 'interval' }; var resources = { bounds: { setup: function () { return [self._lb, self._ub]; }, changes: function (prev) { return !sameMoment(prev[0], self._lb) || !sameMoment(prev[1], self._ub); } }, interval: { setup: function () { return self._i; }, changes: function (prev) { return !sameDuration(prev, this._i); } } }; function cachedGetter(prop) { return { value: function cachedGetter() { if (cache.hasOwnProperty(prop)) { return cache[prop]; } return cache[prop] = self[prop](); } }; } function cacheBreaker(prop) { var resource = resources[breakers[prop]]; var setup = resource.setup; var changes = resource.changes; var deps = resource.deps; var fn = self[prop]; return { value: function cacheBreaker(input) { var prev = setup.call(self); var ret = fn.apply(self, arguments); if (changes.call(self, prev)) { cache = {}; } return ret; } }; } function same(checkType) { return function (a, b) { if (a === b) return true; if (checkType(a) === checkType(b)) return +a === +b; return false; }; } _.forOwn(TimeBuckets.prototype, function (fn, prop) { if (prop[0] === '_') return; if (breakers.hasOwnProperty(prop)) { desc[prop] = cacheBreaker(prop); } else { desc[prop] = cachedGetter(prop); } }); return Object.create(self, desc); }; return TimeBuckets; }; });