mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
* Allow setting offset relative to kibana time * remove changes to kibana.yml * use timerange instead of kibana_time, do not allow default value, better help text * throw error when zero is provided, round number to avoid decimals - which are not allowed * do not allow offsets larger than zero
This commit is contained in:
parent
73d3883188
commit
0ab5e56e87
3 changed files with 101 additions and 7 deletions
|
@ -20,7 +20,7 @@
|
|||
import loadFunctions from '../load_functions.js';
|
||||
const fitFunctions = loadFunctions('fit_functions');
|
||||
import TimelionFunction from './timelion_function';
|
||||
import offsetTime from '../offset_time';
|
||||
import { offsetTime, preprocessOffset } from '../offset_time';
|
||||
import _ from 'lodash';
|
||||
|
||||
|
||||
|
@ -41,7 +41,9 @@ export default class Datasource extends TimelionFunction {
|
|||
name: 'offset',
|
||||
types: ['string', 'null'],
|
||||
help: 'Offset the series retrieval by a date expression, ' +
|
||||
'e.g., -1M to make events from one month ago appear as if they are happening now'
|
||||
'e.g., -1M to make events from one month ago appear as if they are happening now. ' +
|
||||
'Offset the series relative to the charts overall time range, by using the value "timerange", ' +
|
||||
'e.g. "timerange:-2" will specify an offset that is twice the overall chart time range to the past.'
|
||||
});
|
||||
|
||||
config.args.push({
|
||||
|
@ -54,16 +56,18 @@ export default class Datasource extends TimelionFunction {
|
|||
const originalFunction = config.fn;
|
||||
config.fn = function (args, tlConfig) {
|
||||
const config = _.clone(tlConfig);
|
||||
if (args.byName.offset) {
|
||||
let offset = args.byName.offset;
|
||||
if (offset) {
|
||||
offset = preprocessOffset(offset, tlConfig.time.from, tlConfig.time.to);
|
||||
config.time = _.cloneDeep(tlConfig.time);
|
||||
config.time.from = offsetTime(config.time.from, args.byName.offset);
|
||||
config.time.to = offsetTime(config.time.to, args.byName.offset);
|
||||
config.time.from = offsetTime(config.time.from, offset);
|
||||
config.time.to = offsetTime(config.time.to, offset);
|
||||
}
|
||||
|
||||
return Promise.resolve(originalFunction(args, config)).then(function (seriesList) {
|
||||
seriesList.list = _.map(seriesList.list, function (series) {
|
||||
if (series.data.length === 0) throw new Error(name + '() returned no results');
|
||||
series.data = offsetSeries(series.data, args.byName.offset);
|
||||
series.data = offsetSeries(series.data, offset);
|
||||
series.fit = args.byName.fit || series.fit || 'nearest';
|
||||
return series;
|
||||
});
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
import moment from 'moment';
|
||||
|
||||
// usually reverse = false on the request, true on the response
|
||||
export default function offsetTime(milliseconds, offset, reverse) {
|
||||
export function offsetTime(milliseconds, offset, reverse) {
|
||||
if (!offset.match(/[-+][0-9]+[mshdwMy]/g)) {
|
||||
throw new Error ('Malformed `offset` at ' + offset);
|
||||
}
|
||||
|
@ -34,3 +34,37 @@ export default function offsetTime(milliseconds, offset, reverse) {
|
|||
const momentObj = moment(milliseconds)[mode](parts[1], parts[2]);
|
||||
return momentObj.valueOf();
|
||||
}
|
||||
|
||||
function timeRangeErrorMsg(offset) {
|
||||
return `Malformed timerange offset, expecting "timerange:<number>", received: ${offset}`;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate offset when parameter is requesting a relative offset based on requested time range.
|
||||
*
|
||||
* @param {string} offset - offset parameter value
|
||||
* @param {number} from - kibana global time 'from' in milliseconds
|
||||
* @param {number} to - kibana global time 'to' in milliseconds
|
||||
*/
|
||||
export function preprocessOffset(offset, from, to) {
|
||||
if (!offset.startsWith('timerange')) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
const parts = offset.split(':');
|
||||
if (parts.length === 1) {
|
||||
throw new Error(timeRangeErrorMsg(offset));
|
||||
}
|
||||
|
||||
const factor = parseFloat(parts[1]);
|
||||
if (isNaN(factor)) {
|
||||
throw new Error(timeRangeErrorMsg(offset));
|
||||
}
|
||||
if (factor >= 0) {
|
||||
throw new Error('Malformed timerange offset, factor must be negative number.');
|
||||
}
|
||||
|
||||
const deltaSeconds = (to - from) / 1000;
|
||||
const processedOffset = Math.round(deltaSeconds * factor);
|
||||
return `${processedOffset}s`;
|
||||
}
|
||||
|
|
56
src/core_plugins/timelion/server/lib/offset_time.test.js
Normal file
56
src/core_plugins/timelion/server/lib/offset_time.test.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from 'expect.js';
|
||||
import moment from 'moment';
|
||||
import { preprocessOffset } from './offset_time';
|
||||
|
||||
describe('offset', () => {
|
||||
|
||||
describe('preprocessOffset', () => {
|
||||
const from = moment('2018-01-01T00:00:00.000Z').valueOf();
|
||||
const to = moment('2018-01-01T00:15:00.000Z').valueOf();
|
||||
|
||||
test('throws error when no number is provided', () => {
|
||||
expect(() => preprocessOffset('timerange', from, to)).to.throwError();
|
||||
});
|
||||
|
||||
test('throws error when zero is provided', () => {
|
||||
expect(() => preprocessOffset('timerange:0', from, to)).to.throwError();
|
||||
});
|
||||
|
||||
test('throws error when factor is larger than zero', () => {
|
||||
expect(() => preprocessOffset('timerange:1', from, to)).to.throwError();
|
||||
});
|
||||
|
||||
test('throws error with malformed', () => {
|
||||
expect(() => preprocessOffset('timerange:notANumber', from, to)).to.throwError();
|
||||
});
|
||||
|
||||
test('does not modify offset when value is not requesting relative offset', () => {
|
||||
const offset = '-1d';
|
||||
expect(preprocessOffset(offset, from, to)).to.eql(offset);
|
||||
});
|
||||
|
||||
test('converts offset when value is requesting relative offset with multiplier', () => {
|
||||
const offset = 'timerange:-2';
|
||||
expect(preprocessOffset(offset, from, to)).to.eql('-1800s');
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue