From 9839064d7b920ce1e9974d0e4a2601a3f8982d5e Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 22 Oct 2019 15:39:47 +0200 Subject: [PATCH] Timelion: Move datemath parsing to the client (#47813) (#48889) --- .../components/local_nav/_local_title.scss | 1 - .../core_plugins/timelion/public/app.js | 8 +- .../core_plugins/timelion/public/legacy.ts | 2 + .../core_plugins/timelion/public/plugin.ts | 7 +- .../public/vis/timelion_request_handler.ts | 12 +- .../timelion/server/handlers/chain_runner.js | 6 +- .../server/handlers/lib/validate_time.js | 4 +- .../timelion/server/lib/date_math.js | 123 ------------------ .../server/series_functions/quandl.js | 2 - 9 files changed, 30 insertions(+), 135 deletions(-) delete mode 100644 src/legacy/core_plugins/timelion/server/lib/date_math.js diff --git a/packages/kbn-ui-framework/src/components/local_nav/_local_title.scss b/packages/kbn-ui-framework/src/components/local_nav/_local_title.scss index bb105c84a7727..5e1261cb35764 100644 --- a/packages/kbn-ui-framework/src/components/local_nav/_local_title.scss +++ b/packages/kbn-ui-framework/src/components/local_nav/_local_title.scss @@ -1,7 +1,6 @@ .kuiLocalTitle { display: flex; align-items: center; - height: 100%; padding: ($localNavSideSpacing * 1.5) $localNavSideSpacing; font-size: $kuiFontSize; font-weight: bold; diff --git a/src/legacy/core_plugins/timelion/public/app.js b/src/legacy/core_plugins/timelion/public/app.js index a1f4699a1282a..5bc5355d7c061 100644 --- a/src/legacy/core_plugins/timelion/public/app.js +++ b/src/legacy/core_plugins/timelion/public/app.js @@ -407,9 +407,15 @@ app.controller('timelion', function ( $scope.state.save(); $scope.running = true; + // parse the time range client side to make sure it behaves like other charts + const timeRangeBounds = timefilter.getBounds(); + const httpResult = $http.post('../api/timelion/run', { sheet: $scope.state.sheet, - time: _.extend(timefilter.getTime(), { + time: _.extend({ + from: timeRangeBounds.min, + to: timeRangeBounds.max, + }, { interval: $scope.state.interval, timezone: timezone }), diff --git a/src/legacy/core_plugins/timelion/public/legacy.ts b/src/legacy/core_plugins/timelion/public/legacy.ts index 9f057471e3466..77cd94279c879 100644 --- a/src/legacy/core_plugins/timelion/public/legacy.ts +++ b/src/legacy/core_plugins/timelion/public/legacy.ts @@ -21,11 +21,13 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; import { plugin } from '.'; import { setup as visualizations } from '../../visualizations/public/np_ready/public/legacy'; +import { setup as data } from '../../data/public/legacy'; import { TimelionPluginSetupDependencies } from './plugin'; import { LegacyDependenciesPlugin } from './shim'; const setupPlugins: Readonly = { visualizations, + data, expressions: npSetup.plugins.expressions, // Temporary solution diff --git a/src/legacy/core_plugins/timelion/public/plugin.ts b/src/legacy/core_plugins/timelion/public/plugin.ts index 61fba8eb8aa3c..6447e3bbc5f51 100644 --- a/src/legacy/core_plugins/timelion/public/plugin.ts +++ b/src/legacy/core_plugins/timelion/public/plugin.ts @@ -29,6 +29,8 @@ import { VisualizationsSetup } from '../../visualizations/public/np_ready/public import { getTimelionVisualizationConfig } from './timelion_vis_fn'; import { getTimelionVisualization } from './vis'; import { getTimeChart } from './panels/timechart/timechart'; +import { DataSetup } from '../../data/public'; +import { TimefilterSetup } from '../../data/public/timefilter'; import { Panel } from './panels/panel'; import { LegacyDependenciesPlugin, LegacyDependenciesPluginSetup } from './shim'; @@ -37,12 +39,14 @@ export interface TimelionVisualizationDependencies extends LegacyDependenciesPlu uiSettings: UiSettingsClientContract; http: HttpSetup; timelionPanels: Map; + timefilter: TimefilterSetup; } /** @internal */ export interface TimelionPluginSetupDependencies { expressions: ReturnType; visualizations: VisualizationsSetup; + data: DataSetup; // Temporary solution __LEGACY: LegacyDependenciesPlugin; @@ -58,13 +62,14 @@ export class TimelionPlugin implements Plugin, void> { public async setup( core: CoreSetup, - { __LEGACY, expressions, visualizations }: TimelionPluginSetupDependencies + { __LEGACY, expressions, visualizations, data }: TimelionPluginSetupDependencies ) { const timelionPanels: Map = new Map(); const dependencies: TimelionVisualizationDependencies = { uiSettings: core.uiSettings, http: core.http, + timefilter: data.timefilter, timelionPanels, ...(await __LEGACY.setup(core, timelionPanels)), }; diff --git a/src/legacy/core_plugins/timelion/public/vis/timelion_request_handler.ts b/src/legacy/core_plugins/timelion/public/vis/timelion_request_handler.ts index 2365b28f42af9..eb4fb3f397149 100644 --- a/src/legacy/core_plugins/timelion/public/vis/timelion_request_handler.ts +++ b/src/legacy/core_plugins/timelion/public/vis/timelion_request_handler.ts @@ -50,7 +50,7 @@ export interface TimelionSuccessResponse { } export function getTimelionRequestHandler(dependencies: TimelionVisualizationDependencies) { - const { uiSettings, http } = dependencies; + const { uiSettings, http, timefilter } = dependencies; const timezone = timezoneProvider(uiSettings)(); return async function({ @@ -77,6 +77,9 @@ export function getTimelionRequestHandler(dependencies: TimelionVisualizationDep const esQueryConfigs = getEsQueryConfig(uiSettings); + // parse the time range client side to make sure it behaves like other charts + const timeRangeBounds = timefilter.timefilter.calculateBounds(timeRange); + try { return await http.post('../api/timelion/run', { body: JSON.stringify({ @@ -86,7 +89,12 @@ export function getTimelionRequestHandler(dependencies: TimelionVisualizationDep filter: buildEsQuery(undefined, query, filters, esQueryConfigs), }, }, - time: { ...timeRange, interval: visParams.interval, timezone }, + time: { + from: timeRangeBounds.min, + to: timeRangeBounds.max, + interval: visParams.interval, + timezone, + }, }), }); } catch (e) { diff --git a/src/legacy/core_plugins/timelion/server/handlers/chain_runner.js b/src/legacy/core_plugins/timelion/server/handlers/chain_runner.js index 0860851f3a9b6..cf82a82fd2c12 100644 --- a/src/legacy/core_plugins/timelion/server/handlers/chain_runner.js +++ b/src/legacy/core_plugins/timelion/server/handlers/chain_runner.js @@ -21,9 +21,9 @@ import _ from 'lodash'; import Bluebird from 'bluebird'; import { i18n } from '@kbn/i18n'; +import moment from 'moment'; import parseSheet from './lib/parse_sheet.js'; -import parseDateMath from '../lib/date_math.js'; import repositionArguments from './lib/reposition_arguments.js'; import indexArguments from './lib/index_arguments.js'; import validateTime from './lib/validate_time.js'; @@ -191,8 +191,8 @@ export default function chainRunner(tlConfig) { validateTime(request.time, tlConfig); tlConfig.time = request.time; - tlConfig.time.to = parseDateMath(request.time.to, true).valueOf(); - tlConfig.time.from = parseDateMath(request.time.from).valueOf(); + tlConfig.time.to = moment(request.time.to).valueOf(); + tlConfig.time.from = moment(request.time.from).valueOf(); tlConfig.time.interval = calculateInterval( tlConfig.time.from, tlConfig.time.to, diff --git a/src/legacy/core_plugins/timelion/server/handlers/lib/validate_time.js b/src/legacy/core_plugins/timelion/server/handlers/lib/validate_time.js index 67068b42d08f4..8b1f8998557be 100644 --- a/src/legacy/core_plugins/timelion/server/handlers/lib/validate_time.js +++ b/src/legacy/core_plugins/timelion/server/handlers/lib/validate_time.js @@ -18,12 +18,12 @@ */ import { i18n } from '@kbn/i18n'; +import moment from 'moment'; -import parseDateMath from '../../lib/date_math.js'; import toMS from '../../lib/to_milliseconds.js'; export default function validateTime(time, tlConfig) { - const span = parseDateMath(time.to, true) - parseDateMath(time.from); + const span = moment.duration(moment(time.to).diff(moment(time.from))).asMilliseconds(); const interval = toMS(time.interval); const bucketCount = span / interval; const maxBuckets = tlConfig.settings['timelion:max_buckets']; diff --git a/src/legacy/core_plugins/timelion/server/lib/date_math.js b/src/legacy/core_plugins/timelion/server/lib/date_math.js deleted file mode 100644 index 5efd972d179c5..0000000000000 --- a/src/legacy/core_plugins/timelion/server/lib/date_math.js +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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 _ from 'lodash'; -import moment from 'moment'; - -const units = ['y', 'M', 'w', 'd', 'h', 'm', 's']; - -/* This is a simplified version of elasticsearch's date parser */ -export default function parse(text, roundUp) { - if (!text) { - return undefined; - } - if (moment.isMoment(text)) { - return text; - } - if (_.isDate(text)) { - return moment(text); - } - - let time; - let mathString = ''; - let index; - let parseString; - - if (text.substring(0, 3) === 'now') { - time = moment(); - mathString = text.substring('now'.length); - } else { - index = text.indexOf('||'); - if (index === -1) { - parseString = text; - mathString = ''; // nothing else - } else { - parseString = text.substring(0, index); - mathString = text.substring(index + 2); - } - // We're going to just require ISO8601 timestamps, k? - time = moment(parseString); - } - - if (!mathString.length) { - return time; - } - - return parseDateMath(mathString, time, roundUp); -} - -function parseDateMath(mathString, time, roundUp) { - const dateTime = time; - - for (let i = 0; i < mathString.length;) { - const c = mathString.charAt(i++); - let type; - let num; - - if (c === '/') { - type = 0; - } else if (c === '+') { - type = 1; - } else if (c === '-') { - type = 2; - } else { - return undefined; - } - - if (isNaN(mathString.charAt(i))) { - num = 1; - } else if (mathString.length === 2) { - num = mathString.charAt(i); - } else { - const numFrom = i; - while (!isNaN(mathString.charAt(i))) { - i++; - if (i > 10) { - return undefined; - } - } - num = parseInt(mathString.substring(numFrom, i), 10); - } - - if (type === 0) { - // rounding is only allowed on whole, single, units (eg M or 1M, not 0.5M or 2M) - if (num !== 1) { - return undefined; - } - } - const unit = mathString.charAt(i++); - - if (!_.contains(units, unit)) { - return undefined; - } else { - if (type === 0) { - if (roundUp) { - dateTime.endOf(unit); - } else { - dateTime.startOf(unit); - } - } else if (type === 1) { - dateTime.add(num, unit); - } else if (type === 2) { - dateTime.subtract(num, unit); - } - } - } - return dateTime; -} diff --git a/src/legacy/core_plugins/timelion/server/series_functions/quandl.js b/src/legacy/core_plugins/timelion/server/series_functions/quandl.js index 93ce3e61262bd..dc7c27b8abc1a 100644 --- a/src/legacy/core_plugins/timelion/server/series_functions/quandl.js +++ b/src/legacy/core_plugins/timelion/server/series_functions/quandl.js @@ -23,8 +23,6 @@ import fetch from 'node-fetch'; import moment from 'moment'; fetch.Promise = require('bluebird'); -//var parseDateMath = require('../utils/date_math.js'); - import Datasource from '../lib/classes/datasource'; export default new Datasource ('quandl', {