mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Timelion] Communicate the index pattern to the dashboard (#90623)
* [Timelion] Communicate the index pattern to the dashboard Closes #86418 * update types / limits.yml * Update timelion_vis_type.tsx * fix typo * remove extra await Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
35eef9c4d8
commit
aa468c1d56
9 changed files with 102 additions and 61 deletions
|
@ -22,7 +22,7 @@ snapshots.js
|
|||
/src/core/lib/kbn_internal_native_observable
|
||||
/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/broken
|
||||
/src/plugins/data/common/es_query/kuery/ast/_generated_/**
|
||||
/src/plugins/vis_type_timelion/public/_generated_/**
|
||||
/src/plugins/vis_type_timelion/common/_generated_/**
|
||||
/x-pack/legacy/plugins/**/__tests__/fixtures/**
|
||||
/x-pack/plugins/apm/e2e/tmp/*
|
||||
/x-pack/plugins/canvas/canvas_plugin
|
||||
|
|
|
@ -91,7 +91,7 @@ pageLoadAssetSize:
|
|||
visTypeMetric: 42790
|
||||
visTypeTable: 94934
|
||||
visTypeTagcloud: 37575
|
||||
visTypeTimelion: 51933
|
||||
visTypeTimelion: 68883
|
||||
visTypeTimeseries: 155203
|
||||
visTypeVega: 153573
|
||||
visTypeVislib: 242838
|
||||
|
|
50
src/plugins/vis_type_timelion/common/parser.ts
Normal file
50
src/plugins/vis_type_timelion/common/parser.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
// @ts-ignore
|
||||
import { parse } from './_generated_/chain';
|
||||
|
||||
export interface ExpressionLocation {
|
||||
min: number;
|
||||
max: number;
|
||||
}
|
||||
|
||||
interface ExpressionItem {
|
||||
name: string;
|
||||
function: string;
|
||||
location: ExpressionLocation;
|
||||
text: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface TimelionExpressionArgument extends ExpressionItem {
|
||||
value: {
|
||||
location: ExpressionLocation;
|
||||
type: string;
|
||||
value: string;
|
||||
text: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface TimelionExpressionFunction extends ExpressionItem {
|
||||
arguments: TimelionExpressionArgument[];
|
||||
}
|
||||
|
||||
export interface TimelionExpressionChain {
|
||||
chain: TimelionExpressionFunction[];
|
||||
type: 'chain';
|
||||
}
|
||||
|
||||
export interface ParsedExpression {
|
||||
args: TimelionExpressionArgument[];
|
||||
functions: TimelionExpressionFunction[];
|
||||
tree: TimelionExpressionChain[];
|
||||
variables: Record<string, any>;
|
||||
}
|
||||
|
||||
export const parseTimelionExpression = (input: string): ParsedExpression => parse(input);
|
|
@ -6,16 +6,17 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { get, startsWith } from 'lodash';
|
||||
import { startsWith } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { monaco } from '@kbn/monaco';
|
||||
import {
|
||||
parseTimelionExpression,
|
||||
ParsedExpression,
|
||||
TimelionExpressionArgument,
|
||||
ExpressionLocation,
|
||||
} from '../../common/parser';
|
||||
|
||||
import { Parser } from 'pegjs';
|
||||
|
||||
// @ts-ignore
|
||||
import { parse } from '../_generated_/chain';
|
||||
|
||||
import { ArgValueSuggestions, FunctionArg, Location } from '../helpers/arg_value_suggestions';
|
||||
import { ArgValueSuggestions } from '../helpers/arg_value_suggestions';
|
||||
import { ITimelionFunction, TimelionFunctionArgs } from '../../common/types';
|
||||
|
||||
export enum SUGGESTION_TYPE {
|
||||
|
@ -24,13 +25,13 @@ export enum SUGGESTION_TYPE {
|
|||
FUNCTIONS = 'functions',
|
||||
}
|
||||
|
||||
function inLocation(cursorPosition: number, location: Location) {
|
||||
function inLocation(cursorPosition: number, location: ExpressionLocation) {
|
||||
return cursorPosition >= location.min && cursorPosition <= location.max;
|
||||
}
|
||||
|
||||
function getArgumentsHelp(
|
||||
functionHelp: ITimelionFunction | undefined,
|
||||
functionArgs: FunctionArg[] = []
|
||||
functionArgs: TimelionExpressionArgument[] = []
|
||||
) {
|
||||
if (!functionHelp) {
|
||||
return [];
|
||||
|
@ -45,14 +46,12 @@ function getArgumentsHelp(
|
|||
}
|
||||
|
||||
async function extractSuggestionsFromParsedResult(
|
||||
result: ReturnType<Parser['parse']>,
|
||||
result: ParsedExpression,
|
||||
cursorPosition: number,
|
||||
functionList: ITimelionFunction[],
|
||||
argValueSuggestions: ArgValueSuggestions
|
||||
) {
|
||||
const activeFunc = result.functions.find(({ location }: { location: Location }) =>
|
||||
inLocation(cursorPosition, location)
|
||||
);
|
||||
const activeFunc = result.functions.find(({ location }) => inLocation(cursorPosition, location));
|
||||
|
||||
if (!activeFunc) {
|
||||
return;
|
||||
|
@ -72,7 +71,7 @@ async function extractSuggestionsFromParsedResult(
|
|||
}
|
||||
|
||||
// return argument value suggestions when cursor is inside argument value
|
||||
const activeArg = activeFunc.arguments.find((argument: FunctionArg) => {
|
||||
const activeArg = activeFunc.arguments.find((argument) => {
|
||||
return inLocation(cursorPosition, argument.location);
|
||||
});
|
||||
if (
|
||||
|
@ -112,7 +111,7 @@ async function extractSuggestionsFromParsedResult(
|
|||
// return argument suggestions
|
||||
const argsHelp = getArgumentsHelp(functionHelp, activeFunc.arguments);
|
||||
const argumentSuggestions = argsHelp.filter((arg) => {
|
||||
if (get(activeArg, 'type') === 'namedArg') {
|
||||
if (activeArg?.type === 'namedArg') {
|
||||
return startsWith(arg.name, activeArg.name);
|
||||
} else if (activeArg) {
|
||||
return startsWith(arg.name, activeArg.text);
|
||||
|
@ -129,7 +128,7 @@ export async function suggest(
|
|||
argValueSuggestions: ArgValueSuggestions
|
||||
) {
|
||||
try {
|
||||
const result = await parse(expression);
|
||||
const result = parseTimelionExpression(expression);
|
||||
|
||||
return await extractSuggestionsFromParsedResult(
|
||||
result,
|
||||
|
|
|
@ -9,37 +9,19 @@
|
|||
import { get } from 'lodash';
|
||||
import { getIndexPatterns } from './plugin_services';
|
||||
import { TimelionFunctionArgs } from '../../common/types';
|
||||
import { TimelionExpressionFunction, TimelionExpressionArgument } from '../../common/parser';
|
||||
import {
|
||||
IndexPatternField,
|
||||
indexPatterns as indexPatternsUtils,
|
||||
KBN_FIELD_TYPES,
|
||||
} from '../../../data/public';
|
||||
|
||||
export interface Location {
|
||||
min: number;
|
||||
max: number;
|
||||
}
|
||||
|
||||
export interface FunctionArg {
|
||||
function: string;
|
||||
location: Location;
|
||||
name: string;
|
||||
text: string;
|
||||
type: string;
|
||||
value: {
|
||||
location: Location;
|
||||
text: string;
|
||||
type: string;
|
||||
value: string;
|
||||
};
|
||||
}
|
||||
|
||||
const isRuntimeField = (field: IndexPatternField) => Boolean(field.runtimeField);
|
||||
|
||||
export function getArgValueSuggestions() {
|
||||
const indexPatterns = getIndexPatterns();
|
||||
|
||||
async function getIndexPattern(functionArgs: FunctionArg[]) {
|
||||
async function getIndexPattern(functionArgs: TimelionExpressionFunction[]) {
|
||||
const indexPatternArg = functionArgs.find(({ name }) => name === 'index');
|
||||
if (!indexPatternArg) {
|
||||
// index argument not provided
|
||||
|
@ -61,7 +43,7 @@ export function getArgValueSuggestions() {
|
|||
|
||||
// Argument value suggestion handlers requiring custom client side code
|
||||
// Could not put with function definition since functions are defined on server
|
||||
const customHandlers = {
|
||||
const customHandlers: Record<string, any> = {
|
||||
es: {
|
||||
async index(partial: string) {
|
||||
const search = partial ? `${partial}*` : '*';
|
||||
|
@ -71,7 +53,7 @@ export function getArgValueSuggestions() {
|
|||
name: title,
|
||||
}));
|
||||
},
|
||||
async metric(partial: string, functionArgs: FunctionArg[]) {
|
||||
async metric(partial: string, functionArgs: TimelionExpressionFunction[]) {
|
||||
if (!partial || !partial.includes(':')) {
|
||||
return [
|
||||
{ name: 'avg:' },
|
||||
|
@ -101,7 +83,7 @@ export function getArgValueSuggestions() {
|
|||
)
|
||||
.map((field) => ({ name: `${valueSplit[0]}:${field.name}`, help: field.type }));
|
||||
},
|
||||
async split(partial: string, functionArgs: FunctionArg[]) {
|
||||
async split(partial: string, functionArgs: TimelionExpressionFunction[]) {
|
||||
const indexPattern = await getIndexPattern(functionArgs);
|
||||
if (!indexPattern) {
|
||||
return [];
|
||||
|
@ -125,7 +107,7 @@ export function getArgValueSuggestions() {
|
|||
)
|
||||
.map((field) => ({ name: field.name, help: field.type }));
|
||||
},
|
||||
async timefield(partial: string, functionArgs: FunctionArg[]) {
|
||||
async timefield(partial: string, functionArgs: TimelionExpressionFunction[]) {
|
||||
const indexPattern = await getIndexPattern(functionArgs);
|
||||
if (!indexPattern) {
|
||||
return [];
|
||||
|
@ -150,10 +132,7 @@ export function getArgValueSuggestions() {
|
|||
* @param {string} argName - user provided argument name
|
||||
* @return {boolean} true when dynamic suggestion handler provided for function argument
|
||||
*/
|
||||
hasDynamicSuggestionsForArgument: <T extends keyof typeof customHandlers>(
|
||||
functionName: T,
|
||||
argName: keyof typeof customHandlers[T]
|
||||
) => {
|
||||
hasDynamicSuggestionsForArgument: (functionName: string, argName: string) => {
|
||||
return customHandlers[functionName] && customHandlers[functionName][argName];
|
||||
},
|
||||
|
||||
|
@ -164,10 +143,10 @@ export function getArgValueSuggestions() {
|
|||
* @param {string} partial - user provided argument value
|
||||
* @return {array} array of dynamic suggestions matching partial
|
||||
*/
|
||||
getDynamicSuggestionsForArgument: async <T extends keyof typeof customHandlers>(
|
||||
functionName: T,
|
||||
argName: keyof typeof customHandlers[T],
|
||||
functionArgs: FunctionArg[],
|
||||
getDynamicSuggestionsForArgument: async (
|
||||
functionName: string,
|
||||
argName: string,
|
||||
functionArgs: TimelionExpressionArgument[],
|
||||
partialInput = ''
|
||||
) => {
|
||||
// @ts-ignore
|
||||
|
|
|
@ -13,8 +13,11 @@ import { DefaultEditorSize } from '../../vis_default_editor/public';
|
|||
import { TimelionOptionsProps } from './timelion_options';
|
||||
import { TimelionVisDependencies } from './plugin';
|
||||
import { toExpressionAst } from './to_ast';
|
||||
import { getIndexPatterns } from './helpers/plugin_services';
|
||||
|
||||
import { VIS_EVENT_TO_TRIGGER } from '../../visualizations/public';
|
||||
import { parseTimelionExpression } from '../common/parser';
|
||||
|
||||
import { VIS_EVENT_TO_TRIGGER, VisParams } from '../../visualizations/public';
|
||||
|
||||
const TimelionOptions = lazy(() => import('./timelion_options'));
|
||||
|
||||
|
@ -47,6 +50,21 @@ export function getTimelionVisDefinition(dependencies: TimelionVisDependencies)
|
|||
getSupportedTriggers: () => {
|
||||
return [VIS_EVENT_TO_TRIGGER.applyFilter];
|
||||
},
|
||||
getUsedIndexPattern: (params: VisParams) => {
|
||||
try {
|
||||
const args = parseTimelionExpression(params.expression)?.args ?? [];
|
||||
const indexArg = args.find(
|
||||
({ type, name, function: fn }) => type === 'namedArg' && fn === 'es' && name === 'index'
|
||||
);
|
||||
|
||||
if (indexArg?.value.text) {
|
||||
return getIndexPatterns().find(indexArg.value.text);
|
||||
}
|
||||
} catch {
|
||||
// timelion expression is invalid
|
||||
}
|
||||
return [];
|
||||
},
|
||||
options: {
|
||||
showIndexSelection: false,
|
||||
showQueryBar: false,
|
||||
|
|
|
@ -7,17 +7,12 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import _ from 'lodash';
|
||||
const grammar = fs.readFileSync(path.resolve(__dirname, '../../../common/chain.peg'), 'utf8');
|
||||
import PEG from 'pegjs';
|
||||
const Parser = PEG.generate(grammar);
|
||||
import { parseTimelionExpression } from '../../../common/parser';
|
||||
|
||||
export default function parseSheet(sheet) {
|
||||
return _.map(sheet, function (plot) {
|
||||
return sheet.map(function (plot) {
|
||||
try {
|
||||
return Parser.parse(plot).tree;
|
||||
return parseTimelionExpression(plot).tree;
|
||||
} catch (e) {
|
||||
if (e.expected) {
|
||||
throw new Error(
|
||||
|
|
|
@ -15,7 +15,7 @@ module.exports = {
|
|||
},
|
||||
},
|
||||
timelion_chain: {
|
||||
src: 'src/plugins/vis_type_timelion/public/chain.peg',
|
||||
dest: 'src/plugins/vis_type_timelion/public/_generated_/chain.js',
|
||||
src: 'src/plugins/vis_type_timelion/common/chain.peg',
|
||||
dest: 'src/plugins/vis_type_timelion/common/_generated_/chain.js',
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue