Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.x] [Timelion] Communicate the index pattern to the dashboard (#90623) #91112

Merged
merged 1 commit into from
Feb 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/kbn-optimizer/limits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ pageLoadAssetSize:
visTypeMetric: 42790
visTypeTable: 95078
visTypeTagcloud: 37575
visTypeTimelion: 51933
visTypeTimelion: 68883
visTypeTimeseries: 155347
visTypeVega: 153861
visTypeVislib: 242982
Expand Down
50 changes: 50 additions & 0 deletions src/plugins/vis_type_timelion/common/parser.ts
Original file line number Diff line number Diff line change
@@ -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);
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 [];
Expand All @@ -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;
Expand All @@ -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 (
Expand Down Expand Up @@ -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);
Expand All @@ -129,7 +128,7 @@ export async function suggest(
argValueSuggestions: ArgValueSuggestions
) {
try {
const result = await parse(expression);
const result = parseTimelionExpression(expression);

return await extractSuggestionsFromParsedResult(
result,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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}*` : '*';
Expand All @@ -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:' },
Expand Down Expand Up @@ -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 [];
Expand All @@ -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 [];
Expand All @@ -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];
},

Expand All @@ -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
Expand Down
20 changes: 19 additions & 1 deletion src/plugins/vis_type_timelion/public/timelion_vis_type.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'));

Expand Down Expand Up @@ -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,
Expand Down
11 changes: 3 additions & 8 deletions src/plugins/vis_type_timelion/server/handlers/lib/parse_sheet.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
4 changes: 2 additions & 2 deletions tasks/config/peg.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
};