From 049572cfcbf8a18411e6ca4d4aefcc31ef930810 Mon Sep 17 00:00:00 2001 From: Corey Robertson Date: Fri, 24 Apr 2020 10:39:54 -0400 Subject: [PATCH] [Canvas] Misc NP Stuff (#63703) * Timelion function -> np * embeddable renderer -> np i18n context * ui_metric -> np * Fix timelion issue Co-authored-by: Elastic Machine --- .../renderers/embeddable/embeddable.tsx | 3 +- .../canvas/i18n/functions/dict/timelion.ts | 4 +- .../plugins/canvas/public/application.tsx | 5 + .../plugins/canvas/public/functions/index.ts | 15 +- .../canvas/public/functions/timelion.ts | 173 +++++++++--------- x-pack/legacy/plugins/canvas/public/legacy.ts | 2 + .../plugins/canvas/public/lib/ui_metric.ts | 21 ++- .../legacy/plugins/canvas/public/plugin.tsx | 15 +- 8 files changed, 141 insertions(+), 97 deletions(-) diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx index a1096d50c16535..ee08dfb87e1c13 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx @@ -6,7 +6,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { I18nContext } from 'ui/i18n'; import { CoreStart } from '../../../../../../../src/core/public'; import { StartDeps } from '../../plugin'; import { @@ -30,6 +29,8 @@ const embeddablesRegistry: { } = {}; const renderEmbeddableFactory = (core: CoreStart, plugins: StartDeps) => { + const I18nContext = core.i18n.Context; + return (embeddableObject: IEmbeddable, domNode: HTMLElement) => { return (
> = { +export const help: FunctionHelp>> = { help: i18n.translate('xpack.canvas.functions.timelionHelpText', { defaultMessage: 'Use Timelion to extract one or more timeseries from many sources.', }), diff --git a/x-pack/legacy/plugins/canvas/public/application.tsx b/x-pack/legacy/plugins/canvas/public/application.tsx index f75b3b427c41b5..7ebfdda743a97b 100644 --- a/x-pack/legacy/plugins/canvas/public/application.tsx +++ b/x-pack/legacy/plugins/canvas/public/application.tsx @@ -30,6 +30,7 @@ import { VALUE_CLICK_TRIGGER, ActionByType } from '../../../../../src/plugins/ui /* eslint-disable */ import { ACTION_VALUE_CLICK } from '../../../../../src/plugins/data/public/actions/value_click_action'; /* eslint-enable */ +import { init as initStatsReporter } from './lib/ui_metric'; import { CapabilitiesStrings } from '../i18n'; const { ReadOnlyBadge: strings } = CapabilitiesStrings; @@ -121,6 +122,10 @@ export const initializeCanvas = async ( startPlugins.uiActions.attachAction(VALUE_CLICK_TRIGGER, emptyAction); } + if (setupPlugins.usageCollection) { + initStatsReporter(setupPlugins.usageCollection.reportUiStats); + } + return canvasStore; }; diff --git a/x-pack/legacy/plugins/canvas/public/functions/index.ts b/x-pack/legacy/plugins/canvas/public/functions/index.ts index 27fb7d83274a45..5e098d8f175c51 100644 --- a/x-pack/legacy/plugins/canvas/public/functions/index.ts +++ b/x-pack/legacy/plugins/canvas/public/functions/index.ts @@ -4,16 +4,23 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ExpressionsSetup } from 'src/plugins/expressions/public'; import { asset } from './asset'; import { filtersFunctionFactory } from './filters'; -import { timelion } from './timelion'; +import { timelionFunctionFactory } from './timelion'; import { toFunctionFactory } from './to'; +import { CanvasSetupDeps, CoreSetup } from '../plugin'; export interface InitializeArguments { - typesRegistry: ExpressionsSetup['__LEGACY']['types']; + prependBasePath: CoreSetup['http']['basePath']['prepend']; + typesRegistry: CanvasSetupDeps['expressions']['__LEGACY']['types']; + timefilter: CanvasSetupDeps['data']['query']['timefilter']['timefilter']; } export function initFunctions(initialize: InitializeArguments) { - return [asset, filtersFunctionFactory(initialize), timelion, toFunctionFactory(initialize)]; + return [ + asset, + filtersFunctionFactory(initialize), + timelionFunctionFactory(initialize), + toFunctionFactory(initialize), + ]; } diff --git a/x-pack/legacy/plugins/canvas/public/functions/timelion.ts b/x-pack/legacy/plugins/canvas/public/functions/timelion.ts index ae87e858cf796c..e59d798108945a 100644 --- a/x-pack/legacy/plugins/canvas/public/functions/timelion.ts +++ b/x-pack/legacy/plugins/canvas/public/functions/timelion.ts @@ -6,8 +6,6 @@ import { flatten } from 'lodash'; import moment from 'moment-timezone'; -import chrome from 'ui/chrome'; -import { npStart } from 'ui/new_platform'; import { TimeRange } from 'src/plugins/data/common'; import { ExpressionFunctionDefinition, DatatableRow } from 'src/plugins/expressions/public'; import { fetch } from '../../common/lib/fetch'; @@ -15,6 +13,7 @@ import { fetch } from '../../common/lib/fetch'; import { buildBoolArray } from '../../server/lib/build_bool_array'; import { Datatable, Filter } from '../../types'; import { getFunctionHelp } from '../../i18n'; +import { InitializeArguments } from './'; interface Arguments { query: string; @@ -30,13 +29,17 @@ interface Arguments { * @param timeRange time range to parse * @param timeZone time zone to do the parsing in */ -function parseDateMath(timeRange: TimeRange, timeZone: string) { +function parseDateMath( + timeRange: TimeRange, + timeZone: string, + timefilter: InitializeArguments['timefilter'] +) { // the datemath plugin always parses dates by using the current default moment time zone. // to use the configured time zone, we are switching just for the bounds calculation. const defaultTimezone = moment().zoneName(); moment.tz.setDefault(timeZone); - const parsedRange = npStart.plugins.data.query.timefilter.timefilter.calculateBounds(timeRange); + const parsedRange = timefilter.calculateBounds(timeRange); // reset default moment timezone moment.tz.setDefault(defaultTimezone); @@ -44,96 +47,100 @@ function parseDateMath(timeRange: TimeRange, timeZone: string) { return parsedRange; } -export function timelion(): ExpressionFunctionDefinition< +type TimelionFunction = ExpressionFunctionDefinition< 'timelion', Filter, Arguments, Promise -> { - const { help, args: argHelp } = getFunctionHelp().timelion; +>; - return { - name: 'timelion', - type: 'datatable', - inputTypes: ['filter'], - help, - args: { - query: { - types: ['string'], - aliases: ['_', 'q'], - help: argHelp.query, - default: '".es(*)"', - }, - interval: { - types: ['string'], - help: argHelp.interval, - default: 'auto', - }, - from: { - types: ['string'], - help: argHelp.from, - default: 'now-1y', - }, - to: { - types: ['string'], - help: argHelp.to, - default: 'now', - }, - timezone: { - types: ['string'], - help: argHelp.timezone, - default: 'UTC', +export function timelionFunctionFactory(initialize: InitializeArguments): () => TimelionFunction { + return () => { + const { help, args: argHelp } = getFunctionHelp().timelion; + + return { + name: 'timelion', + type: 'datatable', + inputTypes: ['filter'], + help, + args: { + query: { + types: ['string'], + aliases: ['_', 'q'], + help: argHelp.query, + default: '".es(*)"', + }, + interval: { + types: ['string'], + help: argHelp.interval, + default: 'auto', + }, + from: { + types: ['string'], + help: argHelp.from, + default: 'now-1y', + }, + to: { + types: ['string'], + help: argHelp.to, + default: 'now', + }, + timezone: { + types: ['string'], + help: argHelp.timezone, + default: 'UTC', + }, }, - }, - fn: (input, args): Promise => { - // Timelion requires a time range. Use the time range from the timefilter element in the - // workpad, if it exists. Otherwise fall back on the function args. - const timeFilter = input.and.find(and => and.type === 'time'); - const range = timeFilter - ? { min: timeFilter.from, max: timeFilter.to } - : parseDateMath({ from: args.from, to: args.to }, args.timezone); + fn: (input, args): Promise => { + // Timelion requires a time range. Use the time range from the timefilter element in the + // workpad, if it exists. Otherwise fall back on the function args. + const timeFilter = input.and.find(and => and.type === 'time'); + const range = timeFilter + ? { min: timeFilter.from, max: timeFilter.to } + : parseDateMath({ from: args.from, to: args.to }, args.timezone, initialize.timefilter); - const body = { - extended: { - es: { - filter: { - bool: { - must: buildBoolArray(input.and), + const body = { + extended: { + es: { + filter: { + bool: { + must: buildBoolArray(input.and), + }, }, }, }, - }, - sheet: [args.query], - time: { - from: range.min, - to: range.max, - interval: args.interval, - timezone: args.timezone, - }, - }; + sheet: [args.query], + time: { + from: range.min, + to: range.max, + interval: args.interval, + timezone: args.timezone, + }, + }; - return fetch(chrome.addBasePath(`/api/timelion/run`), { - method: 'POST', - responseType: 'json', - data: body, - }).then(resp => { - const seriesList = resp.data.sheet[0].list; - const rows = flatten( - seriesList.map((series: { data: any[]; label: string }) => - series.data.map(row => ({ '@timestamp': row[0], value: row[1], label: series.label })) - ) - ) as DatatableRow[]; + return fetch(initialize.prependBasePath(`/api/timelion/run`), { + method: 'POST', + responseType: 'json', + data: body, + }).then(resp => { + const seriesList = resp.data.sheet[0].list; + const rows = flatten( + seriesList.map((series: { data: any[]; label: string }) => + series.data.map(row => ({ '@timestamp': row[0], value: row[1], label: series.label })) + ) + ) as DatatableRow[]; - return { - type: 'datatable', - columns: [ - { name: '@timestamp', type: 'date' }, - { name: 'value', type: 'number' }, - { name: 'label', type: 'string' }, - ], - rows, - }; - }); - }, + return { + type: 'datatable', + columns: [ + { name: '@timestamp', type: 'date' }, + { name: 'value', type: 'number' }, + { name: 'label', type: 'string' }, + ], + rows, + }; + }); + }, + }; }; } diff --git a/x-pack/legacy/plugins/canvas/public/legacy.ts b/x-pack/legacy/plugins/canvas/public/legacy.ts index 4af7c9b2bd0576..5bb628909c32e2 100644 --- a/x-pack/legacy/plugins/canvas/public/legacy.ts +++ b/x-pack/legacy/plugins/canvas/public/legacy.ts @@ -21,8 +21,10 @@ const shimCoreStart = { }; const shimSetupPlugins: CanvasSetupDeps = { + data: npSetup.plugins.data, expressions: npSetup.plugins.expressions, home: npSetup.plugins.home, + usageCollection: npSetup.plugins.usageCollection, }; const shimStartPlugins: CanvasStartDeps = { ...npStart.plugins, diff --git a/x-pack/legacy/plugins/canvas/public/lib/ui_metric.ts b/x-pack/legacy/plugins/canvas/public/lib/ui_metric.ts index 33976a147df46c..2a1a4b88b7264d 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/ui_metric.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/ui_metric.ts @@ -4,10 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - createUiStatsReporter, - METRIC_TYPE, -} from '../../../../../../src/legacy/core_plugins/ui_metric/public'; +import { UiStatsMetricType, METRIC_TYPE } from '@kbn/analytics'; +import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; -export const trackCanvasUiMetric = createUiStatsReporter('canvas'); export { METRIC_TYPE }; + +export let reportUiStats: UsageCollectionSetup['reportUiStats'] | undefined; + +export function init(_reportUiStats: UsageCollectionSetup['reportUiStats']): void { + reportUiStats = _reportUiStats; +} + +export function trackCanvasUiMetric(metricType: UiStatsMetricType, name: string | string[]) { + if (!reportUiStats) { + return; + } + + reportUiStats('canvas', metricType, name); +} diff --git a/x-pack/legacy/plugins/canvas/public/plugin.tsx b/x-pack/legacy/plugins/canvas/public/plugin.tsx index 3ea3ce625ca719..36ce1974be2724 100644 --- a/x-pack/legacy/plugins/canvas/public/plugin.tsx +++ b/x-pack/legacy/plugins/canvas/public/plugin.tsx @@ -10,8 +10,10 @@ import { HomePublicPluginSetup } from '../../../../../src/plugins/home/public'; import { initLoadingIndicator } from './lib/loading_indicator'; import { featureCatalogueEntry } from './feature_catalogue_entry'; import { ExpressionsSetup, ExpressionsStart } from '../../../../../src/plugins/expressions/public'; +import { DataPublicPluginSetup } from '../../../../../src/plugins/data/public'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; import { EmbeddableStart } from '../../../../../src/plugins/embeddable/public'; +import { UsageCollectionSetup } from '../../../../../src/plugins/usage_collection/public'; import { Start as InspectorStart } from '../../../../../src/plugins/inspector/public'; // @ts-ignore untyped local import { argTypeSpecs } from './expression_types/arg_types'; @@ -20,7 +22,7 @@ import { legacyRegistries } from './legacy_plugin_support'; import { getPluginApi, CanvasApi } from './plugin_api'; import { initFunctions } from './functions'; import { CanvasSrcPlugin } from '../canvas_plugin_src/plugin'; -export { CoreStart }; +export { CoreStart, CoreSetup }; /** * These are the private interfaces for the services your plugin depends on. @@ -28,14 +30,17 @@ export { CoreStart }; */ // This interface will be built out as we require other plugins for setup export interface CanvasSetupDeps { + data: DataPublicPluginSetup; expressions: ExpressionsSetup; home: HomePublicPluginSetup; + usageCollection?: UsageCollectionSetup; } export interface CanvasStartDeps { embeddable: EmbeddableStart; expressions: ExpressionsStart; inspector: InspectorStart; + uiActions: UiActionsStart; __LEGACY: { absoluteToParsedUrl: (url: string, basePath: string) => any; @@ -94,7 +99,13 @@ export class CanvasPlugin canvasApi.addTypes(legacyRegistries.types.getOriginalFns()); // Register core canvas stuff - canvasApi.addFunctions(initFunctions({ typesRegistry: plugins.expressions.__LEGACY.types })); + canvasApi.addFunctions( + initFunctions({ + timefilter: plugins.data.query.timefilter.timefilter, + prependBasePath: core.http.basePath.prepend, + typesRegistry: plugins.expressions.__LEGACY.types, + }) + ); canvasApi.addArgumentUIs(argTypeSpecs); canvasApi.addTransitions(transitions);