mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Expressions] Remove the any
type usages (#113477)
* Update ESLint config to disallow usage of the any type * Remove the any type usages from the expressions plugin * Update plugins using expressions according to the updated public API
This commit is contained in:
parent
fed0dc6563
commit
0d9825d03c
66 changed files with 415 additions and 296 deletions
|
@ -47,11 +47,11 @@ export function ActionsExpressionsExample({ expressions, actions }: Props) {
|
|||
};
|
||||
|
||||
const handleEvents = (event: any) => {
|
||||
if (event.id !== 'NAVIGATE') return;
|
||||
if (event.name !== 'NAVIGATE') return;
|
||||
// enrich event context with some extra data
|
||||
event.baseUrl = 'http://www.google.com';
|
||||
|
||||
actions.executeTriggerActions(NAVIGATE_TRIGGER_ID, event.value);
|
||||
actions.executeTriggerActions(NAVIGATE_TRIGGER_ID, event.data);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -50,7 +50,7 @@ export function ActionsExpressionsExample2({ expressions, actions }: Props) {
|
|||
};
|
||||
|
||||
const handleEvents = (event: any) => {
|
||||
updateVariables({ color: event.value.href === 'http://www.google.com' ? 'red' : 'blue' });
|
||||
updateVariables({ color: event.data.href === 'http://www.google.com' ? 'red' : 'blue' });
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -18,8 +18,8 @@ export const buttonRenderer: ExpressionRenderDefinition<any> = {
|
|||
render(domNode, config, handlers) {
|
||||
const buttonClick = () => {
|
||||
handlers.event({
|
||||
id: 'NAVIGATE',
|
||||
value: {
|
||||
name: 'NAVIGATE',
|
||||
data: {
|
||||
href: config.href,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -12,6 +12,8 @@ import { functionWrapper } from '../../../../expressions/common/expression_funct
|
|||
import { ExpressionValueVisDimension } from '../../../../visualizations/public';
|
||||
import { Datatable } from '../../../../expressions/common/expression_types/specs';
|
||||
|
||||
type Arguments = Parameters<ReturnType<typeof tagcloudFunction>['fn']>[1];
|
||||
|
||||
describe('interpreter/functions#tagcloud', () => {
|
||||
const fn = functionWrapper(tagcloudFunction());
|
||||
const column1 = 'Count';
|
||||
|
@ -26,7 +28,7 @@ describe('interpreter/functions#tagcloud', () => {
|
|||
{ [column1]: 0, [column2]: 'US' },
|
||||
{ [column1]: 10, [column2]: 'UK' },
|
||||
],
|
||||
};
|
||||
} as unknown as Datatable;
|
||||
const visConfig = {
|
||||
scale: 'linear',
|
||||
orientation: 'single',
|
||||
|
@ -73,12 +75,12 @@ describe('interpreter/functions#tagcloud', () => {
|
|||
};
|
||||
|
||||
it('returns an object with the correct structure for number accessors', () => {
|
||||
const actual = fn(context, { ...visConfig, ...numberAccessors }, undefined);
|
||||
const actual = fn(context, { ...visConfig, ...numberAccessors } as Arguments, undefined);
|
||||
expect(actual).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('returns an object with the correct structure for string accessors', () => {
|
||||
const actual = fn(context, { ...visConfig, ...stringAccessors }, undefined);
|
||||
const actual = fn(context, { ...visConfig, ...stringAccessors } as Arguments, undefined);
|
||||
expect(actual).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
@ -93,7 +95,7 @@ describe('interpreter/functions#tagcloud', () => {
|
|||
},
|
||||
},
|
||||
};
|
||||
await fn(context, { ...visConfig, ...numberAccessors }, handlers as any);
|
||||
await fn(context, { ...visConfig, ...numberAccessors } as Arguments, handlers as any);
|
||||
|
||||
expect(loggedTable!).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"rules": {
|
||||
"@typescript-eslint/consistent-type-definitions": 0
|
||||
"@typescript-eslint/consistent-type-definitions": 0,
|
||||
"@typescript-eslint/no-explicit-any": ["error", { "ignoreRestArgs": true }]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,13 +32,13 @@ import { parse } from './parse';
|
|||
* @param val Value you want to check.
|
||||
* @return boolean
|
||||
*/
|
||||
export function isExpressionAstBuilder(val: any): val is ExpressionAstExpressionBuilder {
|
||||
return val?.type === 'expression_builder';
|
||||
export function isExpressionAstBuilder(val: unknown): val is ExpressionAstExpressionBuilder {
|
||||
return (val as Record<string, unknown> | undefined)?.type === 'expression_builder';
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function isExpressionAst(val: any): val is ExpressionAstExpression {
|
||||
return val?.type === 'expression';
|
||||
export function isExpressionAst(val: unknown): val is ExpressionAstExpression {
|
||||
return (val as Record<string, unknown> | undefined)?.type === 'expression';
|
||||
}
|
||||
|
||||
export interface ExpressionAstExpressionBuilder {
|
||||
|
|
|
@ -64,7 +64,7 @@ export type ExpressionAstFunctionDebug = {
|
|||
/**
|
||||
* Raw error that was thrown by the function, if any.
|
||||
*/
|
||||
rawError?: any | Error;
|
||||
rawError?: any | Error; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
|
||||
/**
|
||||
* Time in milliseconds it took to execute the function. Duration can be
|
||||
|
|
|
@ -90,7 +90,7 @@ describe('Execution abortion tests', () => {
|
|||
const completed = jest.fn();
|
||||
const aborted = jest.fn();
|
||||
|
||||
const defer: ExpressionFunctionDefinition<'defer', any, { time: number }, any> = {
|
||||
const defer: ExpressionFunctionDefinition<'defer', unknown, { time: number }, unknown> = {
|
||||
name: 'defer',
|
||||
args: {
|
||||
time: {
|
||||
|
|
|
@ -17,7 +17,7 @@ import { ExecutionContract } from './execution_contract';
|
|||
|
||||
beforeAll(() => {
|
||||
if (typeof performance === 'undefined') {
|
||||
(global as any).performance = { now: Date.now };
|
||||
global.performance = { now: Date.now } as typeof performance;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -41,7 +41,7 @@ const createExecution = (
|
|||
const run = async (
|
||||
expression: string = 'foo bar=123',
|
||||
context?: Record<string, unknown>,
|
||||
input: any = null
|
||||
input: unknown = null
|
||||
) => {
|
||||
const execution = createExecution(expression, context);
|
||||
execution.start(input);
|
||||
|
@ -262,45 +262,45 @@ describe('Execution', () => {
|
|||
|
||||
describe('execution context', () => {
|
||||
test('context.variables is an object', async () => {
|
||||
const { result } = (await run('introspectContext key="variables"')) as any;
|
||||
const { result } = await run('introspectContext key="variables"');
|
||||
|
||||
expect(result).toHaveProperty('result', expect.any(Object));
|
||||
});
|
||||
|
||||
test('context.types is an object', async () => {
|
||||
const { result } = (await run('introspectContext key="types"')) as any;
|
||||
const { result } = await run('introspectContext key="types"');
|
||||
|
||||
expect(result).toHaveProperty('result', expect.any(Object));
|
||||
});
|
||||
|
||||
test('context.abortSignal is an object', async () => {
|
||||
const { result } = (await run('introspectContext key="abortSignal"')) as any;
|
||||
const { result } = await run('introspectContext key="abortSignal"');
|
||||
|
||||
expect(result).toHaveProperty('result', expect.any(Object));
|
||||
});
|
||||
|
||||
test('context.inspectorAdapters is an object', async () => {
|
||||
const { result } = (await run('introspectContext key="inspectorAdapters"')) as any;
|
||||
const { result } = await run('introspectContext key="inspectorAdapters"');
|
||||
|
||||
expect(result).toHaveProperty('result', expect.any(Object));
|
||||
});
|
||||
|
||||
test('context.getKibanaRequest is a function if provided', async () => {
|
||||
const { result } = (await run('introspectContext key="getKibanaRequest"', {
|
||||
const { result } = await run('introspectContext key="getKibanaRequest"', {
|
||||
kibanaRequest: {},
|
||||
})) as any;
|
||||
});
|
||||
|
||||
expect(result).toHaveProperty('result', expect.any(Function));
|
||||
});
|
||||
|
||||
test('context.getKibanaRequest is undefined if not provided', async () => {
|
||||
const { result } = (await run('introspectContext key="getKibanaRequest"')) as any;
|
||||
const { result } = await run('introspectContext key="getKibanaRequest"');
|
||||
|
||||
expect(result).toHaveProperty('result', undefined);
|
||||
});
|
||||
|
||||
test('unknown context key is undefined', async () => {
|
||||
const { result } = (await run('introspectContext key="foo"')) as any;
|
||||
const { result } = await run('introspectContext key="foo"');
|
||||
|
||||
expect(result).toHaveProperty('result', undefined);
|
||||
});
|
||||
|
@ -314,7 +314,7 @@ describe('Execution', () => {
|
|||
|
||||
describe('inspector adapters', () => {
|
||||
test('by default, "tables" and "requests" inspector adapters are available', async () => {
|
||||
const { result } = (await run('introspectContext key="inspectorAdapters"')) as any;
|
||||
const { result } = await run('introspectContext key="inspectorAdapters"');
|
||||
expect(result).toHaveProperty(
|
||||
'result',
|
||||
expect.objectContaining({
|
||||
|
@ -326,9 +326,9 @@ describe('Execution', () => {
|
|||
|
||||
test('can set custom inspector adapters', async () => {
|
||||
const inspectorAdapters = {};
|
||||
const { result } = (await run('introspectContext key="inspectorAdapters"', {
|
||||
const { result } = await run('introspectContext key="inspectorAdapters"', {
|
||||
inspectorAdapters,
|
||||
})) as any;
|
||||
});
|
||||
expect(result).toHaveProperty('result', inspectorAdapters);
|
||||
});
|
||||
|
||||
|
@ -351,7 +351,7 @@ describe('Execution', () => {
|
|||
|
||||
describe('expression abortion', () => {
|
||||
test('context has abortSignal object', async () => {
|
||||
const { result } = (await run('introspectContext key="abortSignal"')) as any;
|
||||
const { result } = await run('introspectContext key="abortSignal"');
|
||||
|
||||
expect(result).toHaveProperty('result.aborted', false);
|
||||
});
|
||||
|
@ -400,7 +400,7 @@ describe('Execution', () => {
|
|||
testScheduler.run(({ cold, expectObservable }) => {
|
||||
const arg = cold(' -a-b-c|', { a: 1, b: 2, c: 3 });
|
||||
const expected = ' -a-b-c|';
|
||||
const observable: ExpressionFunctionDefinition<'observable', any, {}, any> = {
|
||||
const observable: ExpressionFunctionDefinition<'observable', unknown, {}, unknown> = {
|
||||
name: 'observable',
|
||||
args: {},
|
||||
help: '',
|
||||
|
@ -468,7 +468,7 @@ describe('Execution', () => {
|
|||
});
|
||||
|
||||
test('does not execute remaining functions in pipeline', async () => {
|
||||
const spy: ExpressionFunctionDefinition<'spy', any, {}, any> = {
|
||||
const spy: ExpressionFunctionDefinition<'spy', unknown, {}, unknown> = {
|
||||
name: 'spy',
|
||||
args: {},
|
||||
help: '',
|
||||
|
@ -621,7 +621,12 @@ describe('Execution', () => {
|
|||
help: '',
|
||||
fn: () => arg2,
|
||||
};
|
||||
const max: ExpressionFunctionDefinition<'max', any, { val1: number; val2: number }, any> = {
|
||||
const max: ExpressionFunctionDefinition<
|
||||
'max',
|
||||
unknown,
|
||||
{ val1: number; val2: number },
|
||||
unknown
|
||||
> = {
|
||||
name: 'max',
|
||||
args: {
|
||||
val1: { help: '', types: ['number'] },
|
||||
|
@ -679,7 +684,12 @@ describe('Execution', () => {
|
|||
|
||||
describe('when arguments are missing', () => {
|
||||
it('when required argument is missing and has not alias, returns error', async () => {
|
||||
const requiredArg: ExpressionFunctionDefinition<'requiredArg', any, { arg: any }, any> = {
|
||||
const requiredArg: ExpressionFunctionDefinition<
|
||||
'requiredArg',
|
||||
unknown,
|
||||
{ arg: unknown },
|
||||
unknown
|
||||
> = {
|
||||
name: 'requiredArg',
|
||||
args: {
|
||||
arg: {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { isPromise } from '@kbn/std';
|
||||
import { ObservableLike, UnwrapObservable, UnwrapPromiseOrReturn } from '@kbn/utility-types';
|
||||
import { keys, last, mapValues, reduce, zipObject } from 'lodash';
|
||||
import {
|
||||
combineLatest,
|
||||
|
@ -44,6 +45,18 @@ import { ExecutionContract } from './execution_contract';
|
|||
import { ExpressionExecutionParams } from '../service';
|
||||
import { createDefaultInspectorAdapters } from '../util/create_default_inspector_adapters';
|
||||
|
||||
type UnwrapReturnType<Function extends (...args: any[]) => unknown> =
|
||||
ReturnType<Function> extends ObservableLike<unknown>
|
||||
? UnwrapObservable<ReturnType<Function>>
|
||||
: UnwrapPromiseOrReturn<ReturnType<Function>>;
|
||||
|
||||
// type ArgumentsOf<Function extends ExpressionFunction> = Function extends ExpressionFunction<
|
||||
// unknown,
|
||||
// infer Arguments
|
||||
// >
|
||||
// ? Arguments
|
||||
// : never;
|
||||
|
||||
/**
|
||||
* The result returned after an expression function execution.
|
||||
*/
|
||||
|
@ -83,7 +96,7 @@ const createAbortErrorValue = () =>
|
|||
});
|
||||
|
||||
export interface ExecutionParams {
|
||||
executor: Executor<any>;
|
||||
executor: Executor;
|
||||
ast?: ExpressionAstExpression;
|
||||
expression?: string;
|
||||
params: ExpressionExecutionParams;
|
||||
|
@ -107,7 +120,7 @@ export class Execution<
|
|||
* N.B. It is initialized to `null` rather than `undefined` for legacy reasons,
|
||||
* because in legacy interpreter it was set to `null` by default.
|
||||
*/
|
||||
public input: Input = null as any;
|
||||
public input = null as unknown as Input;
|
||||
|
||||
/**
|
||||
* Input of the started execution.
|
||||
|
@ -186,13 +199,13 @@ export class Execution<
|
|||
});
|
||||
|
||||
const inspectorAdapters =
|
||||
execution.params.inspectorAdapters || createDefaultInspectorAdapters();
|
||||
(execution.params.inspectorAdapters as InspectorAdapters) || createDefaultInspectorAdapters();
|
||||
|
||||
this.context = {
|
||||
getSearchContext: () => this.execution.params.searchContext || {},
|
||||
getSearchSessionId: () => execution.params.searchSessionId,
|
||||
getKibanaRequest: execution.params.kibanaRequest
|
||||
? () => execution.params.kibanaRequest
|
||||
? () => execution.params.kibanaRequest!
|
||||
: undefined,
|
||||
variables: execution.params.variables || {},
|
||||
types: executor.getTypes(),
|
||||
|
@ -201,14 +214,14 @@ export class Execution<
|
|||
logDatatable: (name: string, datatable: Datatable) => {
|
||||
inspectorAdapters.tables[name] = datatable;
|
||||
},
|
||||
isSyncColorsEnabled: () => execution.params.syncColors,
|
||||
...(execution.params as any).extraContext,
|
||||
isSyncColorsEnabled: () => execution.params.syncColors!,
|
||||
...execution.params.extraContext,
|
||||
getExecutionContext: () => execution.params.executionContext,
|
||||
};
|
||||
|
||||
this.result = this.input$.pipe(
|
||||
switchMap((input) =>
|
||||
this.race(this.invokeChain(this.state.get().ast.chain, input)).pipe(
|
||||
this.race(this.invokeChain<Output>(this.state.get().ast.chain, input)).pipe(
|
||||
(source) =>
|
||||
new Observable<ExecutionResult<Output>>((subscriber) => {
|
||||
let latest: ExecutionResult<Output> | undefined;
|
||||
|
@ -270,8 +283,8 @@ export class Execution<
|
|||
* N.B. `input` is initialized to `null` rather than `undefined` for legacy reasons,
|
||||
* because in legacy interpreter it was set to `null` by default.
|
||||
*/
|
||||
public start(
|
||||
input: Input = null as any,
|
||||
start(
|
||||
input = null as unknown as Input,
|
||||
isSubExpression?: boolean
|
||||
): Observable<ExecutionResult<Output | ExpressionValueError>> {
|
||||
if (this.hasStarted) throw new Error('Execution already started.');
|
||||
|
@ -294,7 +307,10 @@ export class Execution<
|
|||
return this.result;
|
||||
}
|
||||
|
||||
invokeChain(chainArr: ExpressionAstFunction[], input: unknown): Observable<any> {
|
||||
invokeChain<ChainOutput = unknown>(
|
||||
chainArr: ExpressionAstFunction[],
|
||||
input: unknown
|
||||
): Observable<ChainOutput> {
|
||||
return of(input).pipe(
|
||||
...(chainArr.map((link) =>
|
||||
switchMap((currentInput) => {
|
||||
|
@ -364,19 +380,24 @@ export class Execution<
|
|||
})
|
||||
) as Parameters<Observable<unknown>['pipe']>),
|
||||
catchError((error) => of(error))
|
||||
);
|
||||
) as Observable<ChainOutput>;
|
||||
}
|
||||
|
||||
invokeFunction(
|
||||
fn: ExpressionFunction,
|
||||
invokeFunction<Fn extends ExpressionFunction>(
|
||||
fn: Fn,
|
||||
input: unknown,
|
||||
args: Record<string, unknown>
|
||||
): Observable<any> {
|
||||
): Observable<UnwrapReturnType<Fn['fn']>> {
|
||||
return of(input).pipe(
|
||||
map((currentInput) => this.cast(currentInput, fn.inputTypes)),
|
||||
switchMap((normalizedInput) => this.race(of(fn.fn(normalizedInput, args, this.context)))),
|
||||
switchMap((fnResult: any) =>
|
||||
isObservable(fnResult) ? fnResult : from(isPromise(fnResult) ? fnResult : [fnResult])
|
||||
switchMap(
|
||||
(fnResult) =>
|
||||
(isObservable(fnResult)
|
||||
? fnResult
|
||||
: from(isPromise(fnResult) ? fnResult : [fnResult])) as Observable<
|
||||
UnwrapReturnType<Fn['fn']>
|
||||
>
|
||||
),
|
||||
map((output) => {
|
||||
// Validate that the function returned the type it said it would.
|
||||
|
@ -405,39 +426,49 @@ export class Execution<
|
|||
);
|
||||
}
|
||||
|
||||
public cast(value: any, toTypeNames?: string[]) {
|
||||
public cast<Type = unknown>(value: unknown, toTypeNames?: string[]): Type {
|
||||
// If you don't give us anything to cast to, you'll get your input back
|
||||
if (!toTypeNames || toTypeNames.length === 0) return value;
|
||||
if (!toTypeNames?.length) {
|
||||
return value as Type;
|
||||
}
|
||||
|
||||
// No need to cast if node is already one of the valid types
|
||||
const fromTypeName = getType(value);
|
||||
if (toTypeNames.includes(fromTypeName)) return value;
|
||||
if (toTypeNames.includes(fromTypeName)) {
|
||||
return value as Type;
|
||||
}
|
||||
|
||||
const { types } = this.state.get();
|
||||
const fromTypeDef = types[fromTypeName];
|
||||
|
||||
for (const toTypeName of toTypeNames) {
|
||||
// First check if the current type can cast to this type
|
||||
if (fromTypeDef && fromTypeDef.castsTo(toTypeName)) {
|
||||
if (fromTypeDef?.castsTo(toTypeName)) {
|
||||
return fromTypeDef.to(value, toTypeName, types);
|
||||
}
|
||||
|
||||
// If that isn't possible, check if this type can cast from the current type
|
||||
const toTypeDef = types[toTypeName];
|
||||
if (toTypeDef && toTypeDef.castsFrom(fromTypeName)) return toTypeDef.from(value, types);
|
||||
if (toTypeDef?.castsFrom(fromTypeName)) {
|
||||
return toTypeDef.from(value, types);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Can not cast '${fromTypeName}' to any of '${toTypeNames.join(', ')}'`);
|
||||
}
|
||||
|
||||
// Processes the multi-valued AST argument values into arguments that can be passed to the function
|
||||
resolveArgs(fnDef: ExpressionFunction, input: unknown, argAsts: any): Observable<any> {
|
||||
resolveArgs<Fn extends ExpressionFunction>(
|
||||
fnDef: Fn,
|
||||
input: unknown,
|
||||
argAsts: Record<string, ExpressionAstArgument[]>
|
||||
): Observable<Record<string, unknown>> {
|
||||
return defer(() => {
|
||||
const { args: argDefs } = fnDef;
|
||||
|
||||
// Use the non-alias name from the argument definition
|
||||
const dealiasedArgAsts = reduce(
|
||||
argAsts as Record<string, ExpressionAstArgument>,
|
||||
argAsts,
|
||||
(acc, argAst, argName) => {
|
||||
const argDef = getByAlias(argDefs, argName);
|
||||
if (!argDef) {
|
||||
|
@ -452,7 +483,7 @@ export class Execution<
|
|||
// Check for missing required arguments.
|
||||
for (const { aliases, default: argDefault, name, required } of Object.values(argDefs)) {
|
||||
if (!(name in dealiasedArgAsts) && typeof argDefault !== 'undefined') {
|
||||
dealiasedArgAsts[name] = [parse(argDefault, 'argument')];
|
||||
dealiasedArgAsts[name] = [parse(argDefault as string, 'argument')];
|
||||
}
|
||||
|
||||
if (!required || name in dealiasedArgAsts) {
|
||||
|
@ -490,7 +521,7 @@ export class Execution<
|
|||
const argNames = keys(resolveArgFns);
|
||||
|
||||
if (!argNames.length) {
|
||||
return from([[]]);
|
||||
return from([{}]);
|
||||
}
|
||||
|
||||
const resolvedArgValuesObservable = combineLatest(
|
||||
|
@ -523,7 +554,7 @@ export class Execution<
|
|||
});
|
||||
}
|
||||
|
||||
public interpret<T>(ast: ExpressionAstNode, input: T): Observable<ExecutionResult<unknown>> {
|
||||
interpret<T>(ast: ExpressionAstNode, input: T): Observable<ExecutionResult<unknown>> {
|
||||
switch (getType(ast)) {
|
||||
case 'expression':
|
||||
const execution = this.execution.executor.createExecution(
|
||||
|
|
|
@ -11,7 +11,7 @@ import type { SerializableRecord } from '@kbn/utility-types';
|
|||
import type { KibanaRequest } from 'src/core/server';
|
||||
import type { KibanaExecutionContext } from 'src/core/public';
|
||||
|
||||
import { ExpressionType } from '../expression_types';
|
||||
import { Datatable, ExpressionType } from '../expression_types';
|
||||
import { Adapters, RequestAdapter } from '../../../inspector/common';
|
||||
import { TablesAdapter } from '../util/tables_adapter';
|
||||
|
||||
|
@ -69,6 +69,11 @@ export interface ExecutionContext<
|
|||
* Contains the meta-data about the source of the expression.
|
||||
*/
|
||||
getExecutionContext: () => KibanaExecutionContext | undefined;
|
||||
|
||||
/**
|
||||
* Logs datatable.
|
||||
*/
|
||||
logDatatable?(name: string, datatable: Datatable): void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,7 +19,7 @@ export interface ExecutorState<Context extends Record<string, unknown> = Record<
|
|||
context: Context;
|
||||
}
|
||||
|
||||
export const defaultState: ExecutorState<any> = {
|
||||
export const defaultState: ExecutorState = {
|
||||
functions: {},
|
||||
types: {},
|
||||
context: {},
|
||||
|
@ -61,7 +61,7 @@ export type ExecutorContainer<Context extends Record<string, unknown> = Record<s
|
|||
export const createExecutorContainer = <
|
||||
Context extends Record<string, unknown> = Record<string, unknown>
|
||||
>(
|
||||
state: ExecutorState<Context> = defaultState
|
||||
state = defaultState as ExecutorState<Context>
|
||||
): ExecutorContainer<Context> => {
|
||||
const container = createStateContainer<
|
||||
ExecutorState<Context>,
|
||||
|
|
|
@ -8,20 +8,14 @@
|
|||
|
||||
import { Executor } from './executor';
|
||||
import { parseExpression } from '../ast';
|
||||
import { Execution } from '../execution/execution';
|
||||
|
||||
// eslint-disable-next-line
|
||||
const { __getArgs } = require('../execution/execution');
|
||||
jest.mock('../execution/execution', () => ({
|
||||
Execution: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../execution/execution', () => {
|
||||
const mockedModule = {
|
||||
args: undefined,
|
||||
__getArgs: () => mockedModule.args,
|
||||
Execution: function ExecutionMock(...args: any) {
|
||||
mockedModule.args = args;
|
||||
},
|
||||
};
|
||||
|
||||
return mockedModule;
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('Executor mocked execution tests', () => {
|
||||
|
@ -31,7 +25,9 @@ describe('Executor mocked execution tests', () => {
|
|||
const executor = new Executor();
|
||||
executor.createExecution('foo bar="baz"');
|
||||
|
||||
expect(__getArgs()[0].expression).toBe('foo bar="baz"');
|
||||
expect(Execution).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ expression: 'foo bar="baz"' })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -41,7 +37,9 @@ describe('Executor mocked execution tests', () => {
|
|||
const ast = parseExpression('foo bar="baz"');
|
||||
executor.createExecution(ast);
|
||||
|
||||
expect(__getArgs()[0].expression).toBe(undefined);
|
||||
expect(Execution).toHaveBeenCalledWith(
|
||||
expect.not.objectContaining({ expression: expect.anything() })
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -145,7 +145,7 @@ describe('Executor', () => {
|
|||
executor.extendContext({ foo });
|
||||
const execution = executor.createExecution('foo bar="baz"');
|
||||
|
||||
expect((execution.context as any).foo).toBe(foo);
|
||||
expect(execution.context).toHaveProperty('foo', foo);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -175,10 +175,10 @@ describe('Executor', () => {
|
|||
migrations: {
|
||||
'7.10.0': ((state: ExpressionAstFunction, version: string): ExpressionAstFunction => {
|
||||
return migrateFn(state, version);
|
||||
}) as any as MigrateFunction,
|
||||
}) as unknown as MigrateFunction,
|
||||
'7.10.1': ((state: ExpressionAstFunction, version: string): ExpressionAstFunction => {
|
||||
return migrateFn(state, version);
|
||||
}) as any as MigrateFunction,
|
||||
}) as unknown as MigrateFunction,
|
||||
},
|
||||
fn: jest.fn(),
|
||||
};
|
||||
|
|
|
@ -40,7 +40,7 @@ export interface ExpressionExecOptions {
|
|||
}
|
||||
|
||||
export class TypesRegistry implements IRegistry<ExpressionType> {
|
||||
constructor(private readonly executor: Executor<any>) {}
|
||||
constructor(private readonly executor: Executor) {}
|
||||
|
||||
public register(
|
||||
typeDefinition: AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition)
|
||||
|
@ -62,7 +62,7 @@ export class TypesRegistry implements IRegistry<ExpressionType> {
|
|||
}
|
||||
|
||||
export class FunctionsRegistry implements IRegistry<ExpressionFunction> {
|
||||
constructor(private readonly executor: Executor<any>) {}
|
||||
constructor(private readonly executor: Executor) {}
|
||||
|
||||
public register(
|
||||
functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)
|
||||
|
@ -100,12 +100,12 @@ export class Executor<Context extends Record<string, unknown> = Record<string, u
|
|||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public readonly functions = new FunctionsRegistry(this);
|
||||
public readonly functions = new FunctionsRegistry(this as Executor);
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public readonly types = new TypesRegistry(this);
|
||||
public readonly types = new TypesRegistry(this as Executor);
|
||||
|
||||
protected parent?: Executor<Context>;
|
||||
|
||||
|
@ -207,15 +207,15 @@ export class Executor<Context extends Record<string, unknown> = Record<string, u
|
|||
ast: string | ExpressionAstExpression,
|
||||
params: ExpressionExecutionParams = {}
|
||||
): Execution<Input, Output> {
|
||||
const executionParams: ExecutionParams = {
|
||||
const executionParams = {
|
||||
executor: this,
|
||||
params: {
|
||||
...params,
|
||||
// for canvas we are passing this in,
|
||||
// canvas should be refactored to not pass any extra context in
|
||||
extraContext: this.context,
|
||||
} as any,
|
||||
};
|
||||
},
|
||||
} as ExecutionParams;
|
||||
|
||||
if (typeof ast === 'string') executionParams.expression = ast;
|
||||
else executionParams.ast = ast;
|
||||
|
@ -273,7 +273,7 @@ export class Executor<Context extends Record<string, unknown> = Record<string, u
|
|||
return { state: newAst, references: allReferences };
|
||||
}
|
||||
|
||||
public telemetry(ast: ExpressionAstExpression, telemetryData: Record<string, any>) {
|
||||
public telemetry(ast: ExpressionAstExpression, telemetryData: Record<string, unknown>) {
|
||||
this.walkAst(cloneDeep(ast), (fn, link) => {
|
||||
telemetryData = fn.telemetry(link.arguments, telemetryData);
|
||||
});
|
||||
|
|
|
@ -24,9 +24,9 @@ export type ArgumentType<T> =
|
|||
* representation of the type.
|
||||
*/
|
||||
// prettier-ignore
|
||||
type ArrayTypeToArgumentString<T> =
|
||||
T extends Array<infer ElementType> ? TypeString<ElementType> :
|
||||
T extends null ? 'null' :
|
||||
type ArrayTypeToArgumentString<T> =
|
||||
T extends Array<infer ElementType> ? TypeString<ElementType> :
|
||||
T extends null ? 'null' :
|
||||
never;
|
||||
|
||||
/**
|
||||
|
@ -34,9 +34,9 @@ type ArrayTypeToArgumentString<T> =
|
|||
* string-based representation of the return type.
|
||||
*/
|
||||
// prettier-ignore
|
||||
type UnresolvedTypeToArgumentString<T> =
|
||||
T extends (...args: any) => infer ElementType ? TypeString<ElementType> :
|
||||
T extends null ? 'null' :
|
||||
type UnresolvedTypeToArgumentString<T> =
|
||||
T extends (...args: any[]) => infer ElementType ? TypeString<ElementType> :
|
||||
T extends null ? 'null' :
|
||||
never;
|
||||
|
||||
/**
|
||||
|
@ -44,10 +44,10 @@ type UnresolvedTypeToArgumentString<T> =
|
|||
* string-based representation of the return type.
|
||||
*/
|
||||
// prettier-ignore
|
||||
type UnresolvedArrayTypeToArgumentString<T> =
|
||||
T extends Array<(...args: any) => infer ElementType> ? TypeString<ElementType> :
|
||||
T extends (...args: any) => infer ElementType ? ArrayTypeToArgumentString<ElementType> :
|
||||
T extends null ? 'null' :
|
||||
type UnresolvedArrayTypeToArgumentString<T> =
|
||||
T extends Array<(...args: any[]) => infer ElementType> ? TypeString<ElementType> :
|
||||
T extends (...args: any[]) => infer ElementType ? ArrayTypeToArgumentString<ElementType> :
|
||||
T extends null ? 'null' :
|
||||
never;
|
||||
|
||||
/** A type containing properties common to all Function Arguments. */
|
||||
|
|
|
@ -36,7 +36,11 @@ export class ExpressionFunction implements PersistableState<ExpressionAstFunctio
|
|||
/**
|
||||
* Function to run function (context, args)
|
||||
*/
|
||||
fn: (input: ExpressionValue, params: Record<string, any>, handlers: object) => ExpressionValue;
|
||||
fn: (
|
||||
input: ExpressionValue,
|
||||
params: Record<string, unknown>,
|
||||
handlers: object
|
||||
) => ExpressionValue;
|
||||
|
||||
/**
|
||||
* A short help text.
|
||||
|
@ -56,8 +60,8 @@ export class ExpressionFunction implements PersistableState<ExpressionAstFunctio
|
|||
disabled: boolean;
|
||||
telemetry: (
|
||||
state: ExpressionAstFunction['arguments'],
|
||||
telemetryData: Record<string, any>
|
||||
) => Record<string, any>;
|
||||
telemetryData: Record<string, unknown>
|
||||
) => Record<string, unknown>;
|
||||
extract: (state: ExpressionAstFunction['arguments']) => {
|
||||
state: ExpressionAstFunction['arguments'];
|
||||
references: SavedObjectReference[];
|
||||
|
@ -100,13 +104,12 @@ export class ExpressionFunction implements PersistableState<ExpressionAstFunctio
|
|||
this.migrations = migrations || {};
|
||||
|
||||
for (const [key, arg] of Object.entries(args || {})) {
|
||||
this.args[key] = new ExpressionFunctionParameter(key, arg);
|
||||
this.args[key as keyof typeof args] = new ExpressionFunctionParameter(key, arg);
|
||||
}
|
||||
}
|
||||
|
||||
accepts = (type: string): boolean => {
|
||||
// If you don't tell us input types, we'll assume you don't care what you get.
|
||||
if (!this.inputTypes) return true;
|
||||
return this.inputTypes.indexOf(type) > -1;
|
||||
return this.inputTypes?.includes(type) ?? true;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,20 +6,21 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { KnownTypeToString } from '../types';
|
||||
import { ArgumentType } from './arguments';
|
||||
|
||||
export class ExpressionFunctionParameter {
|
||||
export class ExpressionFunctionParameter<T = unknown> {
|
||||
name: string;
|
||||
required: boolean;
|
||||
help: string;
|
||||
types: string[];
|
||||
default: any;
|
||||
types: ArgumentType<T>['types'];
|
||||
default?: ArgumentType<T>['default'];
|
||||
aliases: string[];
|
||||
multi: boolean;
|
||||
resolve: boolean;
|
||||
options: any[];
|
||||
options: T[];
|
||||
|
||||
constructor(name: string, arg: ArgumentType<any>) {
|
||||
constructor(name: string, arg: ArgumentType<T>) {
|
||||
const { required, help, types, aliases, multi, resolve, options } = arg;
|
||||
|
||||
if (name === '_') {
|
||||
|
@ -38,7 +39,6 @@ export class ExpressionFunctionParameter {
|
|||
}
|
||||
|
||||
accepts(type: string) {
|
||||
if (!this.types.length) return true;
|
||||
return this.types.indexOf(type) > -1;
|
||||
return !this.types?.length || this.types.includes(type as KnownTypeToString<T>);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ describe('ExpressionFunctionParameter', () => {
|
|||
const param = new ExpressionFunctionParameter('foo', {
|
||||
help: 'bar',
|
||||
types: ['baz', 'quux'],
|
||||
});
|
||||
} as ConstructorParameters<typeof ExpressionFunctionParameter>[1]);
|
||||
|
||||
expect(param.accepts('baz')).toBe(true);
|
||||
expect(param.accepts('quux')).toBe(true);
|
||||
|
|
|
@ -11,9 +11,9 @@ import { ExpressionFunctionDefinition } from '../types';
|
|||
import { Datatable, DatatableColumn } from '../../expression_types';
|
||||
|
||||
export interface CreateTableArguments {
|
||||
ids: string[];
|
||||
names: string[] | null;
|
||||
rowCount: number;
|
||||
ids?: string[];
|
||||
names?: string[] | null;
|
||||
rowCount?: number;
|
||||
}
|
||||
|
||||
export const createTable: ExpressionFunctionDefinition<
|
||||
|
|
|
@ -30,7 +30,7 @@ const inlineStyle = (obj: Record<string, string | number>) => {
|
|||
return styles.join(';');
|
||||
};
|
||||
|
||||
interface Arguments {
|
||||
export interface FontArguments {
|
||||
align?: TextAlignment;
|
||||
color?: string;
|
||||
family?: FontFamily;
|
||||
|
@ -41,7 +41,12 @@ interface Arguments {
|
|||
weight?: FontWeight;
|
||||
}
|
||||
|
||||
export type ExpressionFunctionFont = ExpressionFunctionDefinition<'font', null, Arguments, Style>;
|
||||
export type ExpressionFunctionFont = ExpressionFunctionDefinition<
|
||||
'font',
|
||||
null,
|
||||
FontArguments,
|
||||
Style
|
||||
>;
|
||||
|
||||
export const font: ExpressionFunctionFont = {
|
||||
name: 'font',
|
||||
|
|
|
@ -110,7 +110,7 @@ export const mapColumn: ExpressionFunctionDefinition<
|
|||
map((rows) => {
|
||||
let type: DatatableColumnType = 'null';
|
||||
for (const row of rows) {
|
||||
const rowType = getType(row[id]);
|
||||
const rowType = getType(row[id]) as DatatableColumnType;
|
||||
if (rowType !== 'null') {
|
||||
type = rowType;
|
||||
break;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { map, zipObject } from 'lodash';
|
||||
import { map, zipObject, isString } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { evaluate } from '@kbn/tinymath';
|
||||
import { ExpressionFunctionDefinition } from '../types';
|
||||
|
@ -23,19 +23,18 @@ const TINYMATH = '`TinyMath`';
|
|||
const TINYMATH_URL =
|
||||
'https://www.elastic.co/guide/en/kibana/current/canvas-tinymath-functions.html';
|
||||
|
||||
const isString = (val: any): boolean => typeof val === 'string';
|
||||
|
||||
function pivotObjectArray<
|
||||
RowType extends { [key: string]: any },
|
||||
ReturnColumns extends string | number | symbol = keyof RowType
|
||||
>(rows: RowType[], columns?: string[]): Record<string, ReturnColumns[]> {
|
||||
RowType extends { [key: string]: unknown },
|
||||
ReturnColumns extends keyof RowType & string
|
||||
>(rows: RowType[], columns?: ReturnColumns[]) {
|
||||
const columnNames = columns || Object.keys(rows[0]);
|
||||
if (!columnNames.every(isString)) {
|
||||
throw new Error('Columns should be an array of strings');
|
||||
}
|
||||
|
||||
const columnValues = map(columnNames, (name) => map(rows, name));
|
||||
return zipObject(columnNames, columnValues);
|
||||
|
||||
return zipObject(columnNames, columnValues) as { [K in ReturnColumns]: Array<RowType[K]> };
|
||||
}
|
||||
|
||||
export const errors = {
|
||||
|
|
|
@ -107,7 +107,7 @@ export const mathColumn: ExpressionFunctionDefinition<
|
|||
let type: DatatableColumnType = 'null';
|
||||
if (newRows.length) {
|
||||
for (const row of newRows) {
|
||||
const rowType = getType(row[args.id]);
|
||||
const rowType = getType(row[args.id]) as DatatableColumnType;
|
||||
if (rowType !== 'null') {
|
||||
type = rowType;
|
||||
break;
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
*/
|
||||
|
||||
import { openSans } from '../../../fonts';
|
||||
import { font } from '../font';
|
||||
import { FontWeight, TextAlignment } from '../../../types';
|
||||
import { font, FontArguments } from '../font';
|
||||
import { functionWrapper } from './utils';
|
||||
|
||||
describe('font', () => {
|
||||
|
@ -22,7 +23,7 @@ describe('font', () => {
|
|||
size: 14,
|
||||
underline: false,
|
||||
weight: 'normal',
|
||||
};
|
||||
} as unknown as FontArguments;
|
||||
|
||||
describe('default output', () => {
|
||||
const result = fn(null, args);
|
||||
|
@ -63,7 +64,7 @@ describe('font', () => {
|
|||
|
||||
describe('family', () => {
|
||||
it('sets font family', () => {
|
||||
const result = fn(null, { ...args, family: 'Optima, serif' });
|
||||
const result = fn(null, { ...args, family: 'Optima, serif' } as unknown as FontArguments);
|
||||
expect(result.spec.fontFamily).toBe('Optima, serif');
|
||||
expect(result.css).toContain('font-family:Optima, serif');
|
||||
});
|
||||
|
@ -79,29 +80,29 @@ describe('font', () => {
|
|||
|
||||
describe('weight', () => {
|
||||
it('sets font weight', () => {
|
||||
let result = fn(null, { ...args, weight: 'normal' });
|
||||
let result = fn(null, { ...args, weight: FontWeight.NORMAL });
|
||||
expect(result.spec.fontWeight).toBe('normal');
|
||||
expect(result.css).toContain('font-weight:normal');
|
||||
|
||||
result = fn(null, { ...args, weight: 'bold' });
|
||||
result = fn(null, { ...args, weight: FontWeight.BOLD });
|
||||
expect(result.spec.fontWeight).toBe('bold');
|
||||
expect(result.css).toContain('font-weight:bold');
|
||||
|
||||
result = fn(null, { ...args, weight: 'bolder' });
|
||||
result = fn(null, { ...args, weight: FontWeight.BOLDER });
|
||||
expect(result.spec.fontWeight).toBe('bolder');
|
||||
expect(result.css).toContain('font-weight:bolder');
|
||||
|
||||
result = fn(null, { ...args, weight: 'lighter' });
|
||||
result = fn(null, { ...args, weight: FontWeight.LIGHTER });
|
||||
expect(result.spec.fontWeight).toBe('lighter');
|
||||
expect(result.css).toContain('font-weight:lighter');
|
||||
|
||||
result = fn(null, { ...args, weight: '400' });
|
||||
result = fn(null, { ...args, weight: FontWeight.FOUR });
|
||||
expect(result.spec.fontWeight).toBe('400');
|
||||
expect(result.css).toContain('font-weight:400');
|
||||
});
|
||||
|
||||
it('throws when provided an invalid weight', () => {
|
||||
expect(() => fn(null, { ...args, weight: 'foo' })).toThrow();
|
||||
expect(() => fn(null, { ...args, weight: 'foo' as FontWeight })).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -131,25 +132,25 @@ describe('font', () => {
|
|||
|
||||
describe('align', () => {
|
||||
it('sets text alignment', () => {
|
||||
let result = fn(null, { ...args, align: 'left' });
|
||||
let result = fn(null, { ...args, align: TextAlignment.LEFT });
|
||||
expect(result.spec.textAlign).toBe('left');
|
||||
expect(result.css).toContain('text-align:left');
|
||||
|
||||
result = fn(null, { ...args, align: 'center' });
|
||||
result = fn(null, { ...args, align: TextAlignment.CENTER });
|
||||
expect(result.spec.textAlign).toBe('center');
|
||||
expect(result.css).toContain('text-align:center');
|
||||
|
||||
result = fn(null, { ...args, align: 'right' });
|
||||
result = fn(null, { ...args, align: TextAlignment.RIGHT });
|
||||
expect(result.spec.textAlign).toBe('right');
|
||||
expect(result.css).toContain('text-align:right');
|
||||
|
||||
result = fn(null, { ...args, align: 'justify' });
|
||||
result = fn(null, { ...args, align: TextAlignment.JUSTIFY });
|
||||
expect(result.spec.textAlign).toBe('justify');
|
||||
expect(result.css).toContain('text-align:justify');
|
||||
});
|
||||
|
||||
it('throws when provided an invalid alignment', () => {
|
||||
expect(() => fn(null, { ...args, align: 'foo' })).toThrow();
|
||||
expect(() => fn(null, { ...args, align: 'foo' as TextAlignment })).toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,19 +6,19 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { errors, math } from '../math';
|
||||
import { errors, math, MathArguments, MathInput } from '../math';
|
||||
import { emptyTable, functionWrapper, testTable } from './utils';
|
||||
|
||||
describe('math', () => {
|
||||
const fn = functionWrapper<unknown>(math);
|
||||
const fn = functionWrapper(math);
|
||||
|
||||
it('evaluates math expressions without reference to context', () => {
|
||||
expect(fn(null, { expression: '10.5345' })).toBe(10.5345);
|
||||
expect(fn(null, { expression: '123 + 456' })).toBe(579);
|
||||
expect(fn(null, { expression: '100 - 46' })).toBe(54);
|
||||
expect(fn(null as unknown as MathInput, { expression: '10.5345' })).toBe(10.5345);
|
||||
expect(fn(null as unknown as MathInput, { expression: '123 + 456' })).toBe(579);
|
||||
expect(fn(null as unknown as MathInput, { expression: '100 - 46' })).toBe(54);
|
||||
expect(fn(1, { expression: '100 / 5' })).toBe(20);
|
||||
expect(fn('foo', { expression: '100 / 5' })).toBe(20);
|
||||
expect(fn(true, { expression: '100 / 5' })).toBe(20);
|
||||
expect(fn('foo' as unknown as MathInput, { expression: '100 / 5' })).toBe(20);
|
||||
expect(fn(true as unknown as MathInput, { expression: '100 / 5' })).toBe(20);
|
||||
expect(fn(testTable, { expression: '100 * 5' })).toBe(500);
|
||||
expect(fn(emptyTable, { expression: '100 * 5' })).toBe(500);
|
||||
});
|
||||
|
@ -54,7 +54,7 @@ describe('math', () => {
|
|||
describe('args', () => {
|
||||
describe('expression', () => {
|
||||
it('sets the math expression to be evaluted', () => {
|
||||
expect(fn(null, { expression: '10' })).toBe(10);
|
||||
expect(fn(null as unknown as MathInput, { expression: '10' })).toBe(10);
|
||||
expect(fn(23.23, { expression: 'floor(value)' })).toBe(23);
|
||||
expect(fn(testTable, { expression: 'count(price)' })).toBe(9);
|
||||
expect(fn(testTable, { expression: 'count(name)' })).toBe(9);
|
||||
|
@ -99,11 +99,11 @@ describe('math', () => {
|
|||
it('throws when missing expression', () => {
|
||||
expect(() => fn(testTable)).toThrow(new RegExp(errors.emptyExpression().message));
|
||||
|
||||
expect(() => fn(testTable, { expession: '' })).toThrow(
|
||||
expect(() => fn(testTable, { expession: '' } as unknown as MathArguments)).toThrow(
|
||||
new RegExp(errors.emptyExpression().message)
|
||||
);
|
||||
|
||||
expect(() => fn(testTable, { expession: ' ' })).toThrow(
|
||||
expect(() => fn(testTable, { expession: ' ' } as unknown as MathArguments)).toThrow(
|
||||
new RegExp(errors.emptyExpression().message)
|
||||
);
|
||||
});
|
||||
|
|
|
@ -26,14 +26,14 @@ describe('expression_functions', () => {
|
|||
};
|
||||
|
||||
context = {
|
||||
getSearchContext: () => ({} as any),
|
||||
getSearchContext: () => ({}),
|
||||
getSearchSessionId: () => undefined,
|
||||
getExecutionContext: () => undefined,
|
||||
types: {},
|
||||
variables: { theme: themeProps },
|
||||
abortSignal: {} as any,
|
||||
inspectorAdapters: {} as any,
|
||||
};
|
||||
abortSignal: {},
|
||||
inspectorAdapters: {},
|
||||
} as unknown as typeof context;
|
||||
});
|
||||
|
||||
it('returns the selected variable', () => {
|
||||
|
|
|
@ -10,13 +10,15 @@ jest.mock('../../../../common');
|
|||
|
||||
import { IUiSettingsClient } from 'src/core/public';
|
||||
import { getUiSettingFn } from '../ui_setting';
|
||||
import { functionWrapper } from './utils';
|
||||
|
||||
describe('uiSetting', () => {
|
||||
describe('fn', () => {
|
||||
let getStartDependencies: jest.MockedFunction<
|
||||
Parameters<typeof getUiSettingFn>[0]['getStartDependencies']
|
||||
>;
|
||||
let uiSetting: ReturnType<typeof getUiSettingFn>;
|
||||
const uiSettingWrapper = () => functionWrapper(getUiSettingFn({ getStartDependencies }));
|
||||
let uiSetting: ReturnType<typeof uiSettingWrapper>;
|
||||
let uiSettings: jest.Mocked<IUiSettingsClient>;
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -27,13 +29,13 @@ describe('uiSetting', () => {
|
|||
uiSettings,
|
||||
})) as unknown as typeof getStartDependencies;
|
||||
|
||||
uiSetting = getUiSettingFn({ getStartDependencies });
|
||||
uiSetting = uiSettingWrapper();
|
||||
});
|
||||
|
||||
it('should return a value', () => {
|
||||
uiSettings.get.mockReturnValueOnce('value');
|
||||
|
||||
expect(uiSetting.fn(null, { parameter: 'something' }, {} as any)).resolves.toEqual({
|
||||
expect(uiSetting(null, { parameter: 'something' })).resolves.toEqual({
|
||||
type: 'ui_setting',
|
||||
key: 'something',
|
||||
value: 'value',
|
||||
|
@ -41,7 +43,7 @@ describe('uiSetting', () => {
|
|||
});
|
||||
|
||||
it('should pass a default value', async () => {
|
||||
await uiSetting.fn(null, { parameter: 'something', default: 'default' }, {} as any);
|
||||
await uiSetting(null, { parameter: 'something', default: 'default' });
|
||||
|
||||
expect(uiSettings.get).toHaveBeenCalledWith('something', 'default');
|
||||
});
|
||||
|
@ -51,16 +53,16 @@ describe('uiSetting', () => {
|
|||
throw new Error();
|
||||
});
|
||||
|
||||
expect(uiSetting.fn(null, { parameter: 'something' }, {} as any)).rejects.toEqual(
|
||||
expect(uiSetting(null, { parameter: 'something' })).rejects.toEqual(
|
||||
new Error('Invalid parameter "something".')
|
||||
);
|
||||
});
|
||||
|
||||
it('should get a request instance on the server-side', async () => {
|
||||
const request = {};
|
||||
await uiSetting.fn(null, { parameter: 'something' }, {
|
||||
await uiSetting(null, { parameter: 'something' }, {
|
||||
getKibanaRequest: () => request,
|
||||
} as any);
|
||||
} as Parameters<typeof uiSetting>[2]);
|
||||
|
||||
const [[getKibanaRequest]] = getStartDependencies.mock.calls;
|
||||
|
||||
|
@ -68,7 +70,7 @@ describe('uiSetting', () => {
|
|||
});
|
||||
|
||||
it('should throw an error if request is not provided on the server-side', async () => {
|
||||
await uiSetting.fn(null, { parameter: 'something' }, {} as any);
|
||||
await uiSetting(null, { parameter: 'something' });
|
||||
|
||||
const [[getKibanaRequest]] = getStartDependencies.mock.calls;
|
||||
|
||||
|
|
|
@ -15,13 +15,15 @@ import { Datatable } from '../../../expression_types';
|
|||
* Takes a function spec and passes in default args,
|
||||
* overriding with any provided args.
|
||||
*/
|
||||
export const functionWrapper = <ContextType = object | null>(
|
||||
spec: AnyExpressionFunctionDefinition
|
||||
export const functionWrapper = <
|
||||
ExpressionFunctionDefinition extends AnyExpressionFunctionDefinition
|
||||
>(
|
||||
spec: ExpressionFunctionDefinition
|
||||
) => {
|
||||
const defaultArgs = mapValues(spec.args, (argSpec) => argSpec.default);
|
||||
return (
|
||||
context: ContextType,
|
||||
args: Record<string, any> = {},
|
||||
context?: Parameters<ExpressionFunctionDefinition['fn']>[0] | null,
|
||||
args: Parameters<ExpressionFunctionDefinition['fn']>[1] = {},
|
||||
handlers: ExecutionContext = {} as ExecutionContext
|
||||
) => spec.fn(context, { ...defaultArgs, ...args }, handlers);
|
||||
};
|
||||
|
|
|
@ -24,9 +24,9 @@ describe('expression_functions', () => {
|
|||
getExecutionContext: () => undefined,
|
||||
types: {},
|
||||
variables: { test: 1 },
|
||||
abortSignal: {} as any,
|
||||
inspectorAdapters: {} as any,
|
||||
};
|
||||
abortSignal: {},
|
||||
inspectorAdapters: {},
|
||||
} as unknown as typeof context;
|
||||
});
|
||||
|
||||
it('returns the selected variable', () => {
|
||||
|
|
|
@ -17,7 +17,7 @@ describe('expression_functions', () => {
|
|||
const fn = functionWrapper(variableSet);
|
||||
let input: Partial<ReturnType<ExecutionContext['getSearchContext']>>;
|
||||
let context: ExecutionContext;
|
||||
let variables: Record<string, any>;
|
||||
let variables: Record<string, unknown>;
|
||||
|
||||
beforeEach(() => {
|
||||
input = { timeRange: { from: '0', to: '1' } };
|
||||
|
@ -27,9 +27,9 @@ describe('expression_functions', () => {
|
|||
getExecutionContext: () => undefined,
|
||||
types: {},
|
||||
variables: { test: 1 },
|
||||
abortSignal: {} as any,
|
||||
inspectorAdapters: {} as any,
|
||||
};
|
||||
abortSignal: {},
|
||||
inspectorAdapters: {},
|
||||
} as unknown as typeof context;
|
||||
|
||||
variables = context.variables;
|
||||
});
|
||||
|
|
|
@ -12,10 +12,10 @@ import { ExpressionFunctionDefinition } from '../types';
|
|||
|
||||
interface Arguments {
|
||||
variable: string;
|
||||
default: string | number | boolean;
|
||||
default?: string | number | boolean;
|
||||
}
|
||||
|
||||
type Output = any;
|
||||
type Output = unknown;
|
||||
|
||||
export type ExpressionFunctionTheme = ExpressionFunctionDefinition<
|
||||
'theme',
|
||||
|
|
|
@ -36,7 +36,8 @@ export const variable: ExpressionFunctionVar = {
|
|||
},
|
||||
},
|
||||
fn(input, args, context) {
|
||||
const variables: Record<string, any> = context.variables;
|
||||
const { variables } = context;
|
||||
|
||||
return variables[args.name];
|
||||
},
|
||||
};
|
||||
|
|
|
@ -7,11 +7,12 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { Serializable } from '@kbn/utility-types';
|
||||
import { ExpressionFunctionDefinition } from '../types';
|
||||
|
||||
interface Arguments {
|
||||
name: string[];
|
||||
value: any[];
|
||||
value: Serializable[];
|
||||
}
|
||||
|
||||
export type ExpressionFunctionVarSet = ExpressionFunctionDefinition<
|
||||
|
@ -46,10 +47,11 @@ export const variableSet: ExpressionFunctionVarSet = {
|
|||
},
|
||||
},
|
||||
fn(input, args, context) {
|
||||
const variables: Record<string, any> = context.variables;
|
||||
const { variables } = context;
|
||||
args.name.forEach((name, i) => {
|
||||
variables[name] = args.value[i] === undefined ? input : args.value[i];
|
||||
});
|
||||
|
||||
return input;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -30,7 +30,7 @@ import { PersistableStateDefinition } from '../../../kibana_utils/common';
|
|||
export interface ExpressionFunctionDefinition<
|
||||
Name extends string,
|
||||
Input,
|
||||
Arguments extends Record<string, any>,
|
||||
Arguments extends Record<keyof unknown, unknown>,
|
||||
Output,
|
||||
Context extends ExecutionContext = ExecutionContext
|
||||
> extends PersistableStateDefinition<ExpressionAstFunction['arguments']> {
|
||||
|
@ -99,12 +99,14 @@ export interface ExpressionFunctionDefinition<
|
|||
/**
|
||||
* Type to capture every possible expression function definition.
|
||||
*/
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
export type AnyExpressionFunctionDefinition = ExpressionFunctionDefinition<
|
||||
string,
|
||||
any,
|
||||
Record<string, any>,
|
||||
any
|
||||
>;
|
||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||
|
||||
/**
|
||||
* A mapping of `ExpressionFunctionDefinition`s for functions which the
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { ExpressionAstExpression } from '../ast';
|
||||
|
||||
export interface ExpressionRenderDefinition<Config = unknown> {
|
||||
/**
|
||||
* Technical name of the renderer, used as ID to identify renderer in
|
||||
|
@ -46,6 +48,7 @@ export interface ExpressionRenderDefinition<Config = unknown> {
|
|||
) => void | Promise<void>;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type AnyExpressionRenderDefinition = ExpressionRenderDefinition<any>;
|
||||
|
||||
/**
|
||||
|
@ -59,24 +62,34 @@ export type AnyExpressionRenderDefinition = ExpressionRenderDefinition<any>;
|
|||
*/
|
||||
export type RenderMode = 'edit' | 'preview' | 'view';
|
||||
|
||||
export interface IInterpreterRenderUpdateParams<Params = unknown> {
|
||||
newExpression?: string | ExpressionAstExpression;
|
||||
newParams: Params;
|
||||
}
|
||||
|
||||
export interface IInterpreterRenderEvent<Context = unknown> {
|
||||
name: string;
|
||||
data?: Context;
|
||||
}
|
||||
|
||||
export interface IInterpreterRenderHandlers {
|
||||
/**
|
||||
* Done increments the number of rendering successes
|
||||
*/
|
||||
done: () => void;
|
||||
onDestroy: (fn: () => void) => void;
|
||||
reload: () => void;
|
||||
update: (params: any) => void;
|
||||
event: (event: any) => void;
|
||||
hasCompatibleActions?: (event: any) => Promise<boolean>;
|
||||
getRenderMode: () => RenderMode;
|
||||
done(): void;
|
||||
onDestroy(fn: () => void): void;
|
||||
reload(): void;
|
||||
update(params: IInterpreterRenderUpdateParams): void;
|
||||
event(event: IInterpreterRenderEvent): void;
|
||||
hasCompatibleActions?(event: IInterpreterRenderEvent): Promise<boolean>;
|
||||
getRenderMode(): RenderMode;
|
||||
|
||||
/**
|
||||
* The chart is rendered in a non-interactive environment and should not provide any affordances for interaction like brushing.
|
||||
*/
|
||||
isInteractive: () => boolean;
|
||||
isInteractive(): boolean;
|
||||
|
||||
isSyncColorsEnabled: () => boolean;
|
||||
isSyncColorsEnabled(): boolean;
|
||||
/**
|
||||
* This uiState interface is actually `PersistedState` from the visualizations plugin,
|
||||
* but expressions cannot know about vis or it creates a mess of circular dependencies.
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { Serializable } from '@kbn/utility-types';
|
||||
import { AnyExpressionTypeDefinition, ExpressionValue, ExpressionValueConverter } from './types';
|
||||
import { getType } from './get_type';
|
||||
|
||||
|
@ -20,15 +21,15 @@ export class ExpressionType {
|
|||
/**
|
||||
* Type validation, useful for checking function output.
|
||||
*/
|
||||
validate: (type: any) => void | Error;
|
||||
validate: (type: unknown) => void | Error;
|
||||
|
||||
create: unknown;
|
||||
|
||||
/**
|
||||
* Optional serialization (used when passing context around client/server).
|
||||
*/
|
||||
serialize?: (value: ExpressionValue) => any;
|
||||
deserialize?: (serialized: any) => ExpressionValue;
|
||||
serialize?: (value: Serializable) => unknown;
|
||||
deserialize?: (serialized: unknown[]) => Serializable;
|
||||
|
||||
constructor(private readonly definition: AnyExpressionTypeDefinition) {
|
||||
const { name, help, deserialize, serialize, validate } = definition;
|
||||
|
@ -38,7 +39,7 @@ export class ExpressionType {
|
|||
this.validate = validate || (() => {});
|
||||
|
||||
// Optional
|
||||
this.create = (definition as any).create;
|
||||
this.create = (definition as unknown as Record<'create', unknown>).create;
|
||||
|
||||
this.serialize = serialize;
|
||||
this.deserialize = deserialize;
|
||||
|
|
|
@ -6,14 +6,24 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export function getType(node: any) {
|
||||
if (node == null) return 'null';
|
||||
export function getType(node: unknown): string {
|
||||
if (node == null) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
if (Array.isArray(node)) {
|
||||
throw new Error('Unexpected array value encountered.');
|
||||
}
|
||||
if (typeof node === 'object') {
|
||||
if (!node.type) throw new Error('Objects must have a type property');
|
||||
return node.type;
|
||||
|
||||
if (typeof node !== 'object') {
|
||||
return typeof node;
|
||||
}
|
||||
return typeof node;
|
||||
|
||||
const { type } = node as Record<string, unknown>;
|
||||
|
||||
if (!type) {
|
||||
throw new Error('Objects must have a type property');
|
||||
}
|
||||
|
||||
return type as string;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import type { SerializableRecord } from '@kbn/utility-types';
|
||||
import { map, pick, zipObject } from 'lodash';
|
||||
|
||||
import { ExpressionTypeDefinition } from '../types';
|
||||
import { ExpressionTypeDefinition, ExpressionValueBoxed } from '../types';
|
||||
import { PointSeries, PointSeriesColumn } from './pointseries';
|
||||
import { ExpressionValueRender } from './render';
|
||||
import { SerializedFieldFormat } from '../../types';
|
||||
|
@ -21,7 +21,7 @@ const name = 'datatable';
|
|||
* @param datatable
|
||||
*/
|
||||
export const isDatatable = (datatable: unknown): datatable is Datatable =>
|
||||
!!datatable && typeof datatable === 'object' && (datatable as any).type === 'datatable';
|
||||
(datatable as ExpressionValueBoxed | undefined)?.type === 'datatable';
|
||||
|
||||
/**
|
||||
* This type represents the `type` of any `DatatableColumn` in a `Datatable`.
|
||||
|
@ -48,6 +48,7 @@ export type DatatableColumnType =
|
|||
/**
|
||||
* This type represents a row in a `Datatable`.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type DatatableRow = Record<string, any>;
|
||||
|
||||
/**
|
||||
|
@ -112,7 +113,7 @@ interface RenderedDatatable {
|
|||
|
||||
export const datatable: ExpressionTypeDefinition<typeof name, Datatable, SerializedDatatable> = {
|
||||
name,
|
||||
validate: (table) => {
|
||||
validate: (table: Record<string, unknown>) => {
|
||||
// TODO: Check columns types. Only string, boolean, number, date, allowed for now.
|
||||
if (!table.columns) {
|
||||
throw new Error('datatable must have a columns array, even if it is empty');
|
||||
|
|
|
@ -22,7 +22,7 @@ export type ExpressionValueError = ExpressionValueBoxed<
|
|||
}
|
||||
>;
|
||||
|
||||
export const isExpressionValueError = (value: any): value is ExpressionValueError =>
|
||||
export const isExpressionValueError = (value: unknown): value is ExpressionValueError =>
|
||||
getType(value) === 'error';
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import { ExpressionTypeDefinition, ExpressionValueBoxed } from '../types';
|
||||
import { Datatable } from './datatable';
|
||||
import { Datatable, DatatableRow } from './datatable';
|
||||
import { ExpressionValueRender } from './render';
|
||||
|
||||
const name = 'pointseries';
|
||||
|
@ -31,7 +31,7 @@ export interface PointSeriesColumn {
|
|||
*/
|
||||
export type PointSeriesColumns = Record<PointSeriesColumnName, PointSeriesColumn> | {};
|
||||
|
||||
export type PointSeriesRow = Record<string, any>;
|
||||
export type PointSeriesRow = DatatableRow;
|
||||
|
||||
/**
|
||||
* A `PointSeries` is a unique structure that represents dots on a chart.
|
||||
|
|
|
@ -11,7 +11,7 @@ import { ExpressionValueRender } from './render';
|
|||
|
||||
const name = 'shape';
|
||||
|
||||
export const shape: ExpressionTypeDefinition<typeof name, ExpressionValueRender<any>> = {
|
||||
export const shape: ExpressionTypeDefinition<typeof name, ExpressionValueRender<unknown>> = {
|
||||
name: 'shape',
|
||||
to: {
|
||||
render: (input) => {
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { ExpressionType } from './expression_type';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type ExpressionValueUnboxed = any;
|
||||
|
||||
export type ExpressionValueBoxed<Type extends string = string, Value extends object = object> = {
|
||||
|
@ -16,7 +19,7 @@ export type ExpressionValue = ExpressionValueUnboxed | ExpressionValueBoxed;
|
|||
|
||||
export type ExpressionValueConverter<I extends ExpressionValue, O extends ExpressionValue> = (
|
||||
input: I,
|
||||
availableTypes: Record<string, any>
|
||||
availableTypes: Record<string, ExpressionType>
|
||||
) => O;
|
||||
|
||||
/**
|
||||
|
@ -29,18 +32,19 @@ export interface ExpressionTypeDefinition<
|
|||
SerializedType = undefined
|
||||
> {
|
||||
name: Name;
|
||||
validate?: (type: any) => void | Error;
|
||||
serialize?: (type: Value) => SerializedType;
|
||||
deserialize?: (type: SerializedType) => Value;
|
||||
validate?(type: unknown): void | Error;
|
||||
serialize?(type: Value): SerializedType;
|
||||
deserialize?(type: SerializedType): Value;
|
||||
// TODO: Update typings for the `availableTypes` parameter once interfaces for this
|
||||
// have been added elsewhere in the interpreter.
|
||||
from?: {
|
||||
[type: string]: ExpressionValueConverter<any, Value>;
|
||||
[type: string]: ExpressionValueConverter<ExpressionValue, Value>;
|
||||
};
|
||||
to?: {
|
||||
[type: string]: ExpressionValueConverter<Value, any>;
|
||||
[type: string]: ExpressionValueConverter<Value, ExpressionValue>;
|
||||
};
|
||||
help?: string;
|
||||
}
|
||||
|
||||
export type AnyExpressionTypeDefinition = ExpressionTypeDefinition<any, any, any>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type AnyExpressionTypeDefinition = ExpressionTypeDefinition<string, any, any>;
|
||||
|
|
|
@ -11,7 +11,7 @@ import { ExecutionContext } from './execution/types';
|
|||
export const createMockExecutionContext = <ExtraContext extends object = object>(
|
||||
extraContext: ExtraContext = {} as ExtraContext
|
||||
): ExecutionContext & ExtraContext => {
|
||||
const executionContext: ExecutionContext = {
|
||||
const executionContext = {
|
||||
getSearchContext: jest.fn(),
|
||||
getSearchSessionId: jest.fn(),
|
||||
getExecutionContext: jest.fn(),
|
||||
|
@ -25,10 +25,10 @@ export const createMockExecutionContext = <ExtraContext extends object = object>
|
|||
removeEventListener: jest.fn(),
|
||||
},
|
||||
inspectorAdapters: {
|
||||
requests: {} as any,
|
||||
data: {} as any,
|
||||
requests: {},
|
||||
data: {},
|
||||
},
|
||||
};
|
||||
} as unknown as ExecutionContext;
|
||||
|
||||
return {
|
||||
...executionContext,
|
||||
|
|
|
@ -125,7 +125,7 @@ export interface ExpressionsServiceSetup {
|
|||
export interface ExpressionExecutionParams {
|
||||
searchContext?: SerializableRecord;
|
||||
|
||||
variables?: Record<string, any>;
|
||||
variables?: Record<string, unknown>;
|
||||
|
||||
/**
|
||||
* Whether to execute expression in *debug mode*. In *debug mode* inputs and
|
||||
|
@ -148,6 +148,8 @@ export interface ExpressionExecutionParams {
|
|||
inspectorAdapters?: Adapters;
|
||||
|
||||
executionContext?: KibanaExecutionContext;
|
||||
|
||||
extraContext?: object;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -375,7 +377,7 @@ export class ExpressionsService
|
|||
*/
|
||||
public readonly telemetry = (
|
||||
state: ExpressionAstExpression,
|
||||
telemetryData: Record<string, any> = {}
|
||||
telemetryData: Record<string, unknown> = {}
|
||||
) => {
|
||||
return this.executor.telemetry(state, telemetryData);
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import { ExpressionFunctionDefinition } from '../../expression_functions';
|
||||
|
||||
export const access: ExpressionFunctionDefinition<'access', any, { key: string }, any> = {
|
||||
export const access: ExpressionFunctionDefinition<'access', unknown, { key: string }, unknown> = {
|
||||
name: 'access',
|
||||
help: 'Access key on input object or return the input, if it is not an object',
|
||||
args: {
|
||||
|
@ -19,6 +19,10 @@ export const access: ExpressionFunctionDefinition<'access', any, { key: string }
|
|||
},
|
||||
},
|
||||
fn: (input, { key }, context) => {
|
||||
return !input ? input : typeof input === 'object' ? input[key] : input;
|
||||
return !input
|
||||
? input
|
||||
: typeof input === 'object'
|
||||
? (input as Record<string, unknown>)[key]
|
||||
: input;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -26,11 +26,11 @@ export const add: ExpressionFunctionDefinition<
|
|||
types: ['null', 'number', 'string'],
|
||||
},
|
||||
},
|
||||
fn: ({ value: value1 }, { val: input2 }, context) => {
|
||||
fn: ({ value: value1 }, { val: input2 }) => {
|
||||
const value2 = !input2
|
||||
? 0
|
||||
: typeof input2 === 'object'
|
||||
? (input2 as any).value
|
||||
? (input2 as ExpressionValueNum).value
|
||||
: Number(input2);
|
||||
|
||||
return {
|
||||
|
|
|
@ -10,9 +10,9 @@ import { ExpressionFunctionDefinition } from '../../expression_functions';
|
|||
|
||||
export const introspectContext: ExpressionFunctionDefinition<
|
||||
'introspectContext',
|
||||
any,
|
||||
unknown,
|
||||
{ key: string },
|
||||
any
|
||||
unknown
|
||||
> = {
|
||||
name: 'introspectContext',
|
||||
args: {
|
||||
|
@ -25,7 +25,7 @@ export const introspectContext: ExpressionFunctionDefinition<
|
|||
fn: (input, args, context) => {
|
||||
return {
|
||||
type: 'any',
|
||||
result: (context as any)[args.key],
|
||||
result: context[args.key as keyof typeof context],
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import { ExpressionFunctionDefinition } from '../../expression_functions';
|
||||
|
||||
export const sleep: ExpressionFunctionDefinition<'sleep', any, { time: number }, any> = {
|
||||
export const sleep: ExpressionFunctionDefinition<'sleep', unknown, { time: number }, unknown> = {
|
||||
name: 'sleep',
|
||||
args: {
|
||||
time: {
|
||||
|
|
|
@ -37,7 +37,7 @@ export type KnownTypeToString<T> =
|
|||
* `someArgument: Promise<boolean | string>` results in `types: ['boolean', 'string']`
|
||||
*/
|
||||
export type TypeString<T> = KnownTypeToString<
|
||||
T extends ObservableLike<any> ? UnwrapObservable<T> : UnwrapPromiseOrReturn<T>
|
||||
T extends ObservableLike<unknown> ? UnwrapObservable<T> : UnwrapPromiseOrReturn<T>
|
||||
>;
|
||||
|
||||
/**
|
||||
|
@ -52,6 +52,7 @@ export type UnmappedTypeStrings = 'date' | 'filter';
|
|||
* Is used to carry information about how to format data in
|
||||
* a data table as part of the column definition.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export interface SerializedFieldFormat<TParams = Record<string, any>> {
|
||||
id?: string;
|
||||
params?: TParams;
|
||||
|
|
|
@ -7,16 +7,17 @@
|
|||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { ExpressionAstNode } from '..';
|
||||
|
||||
export class ExpressionsInspectorAdapter extends EventEmitter {
|
||||
private _ast: any = {};
|
||||
private _ast = {} as ExpressionAstNode;
|
||||
|
||||
public logAST(ast: any): void {
|
||||
logAST(ast: ExpressionAstNode): void {
|
||||
this._ast = ast;
|
||||
this.emit('change', this._ast);
|
||||
}
|
||||
|
||||
public get ast() {
|
||||
public get ast(): ExpressionAstNode {
|
||||
return this._ast;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ export const createMockContext = () => {
|
|||
getSearchSessionId: () => undefined,
|
||||
types: {},
|
||||
variables: {},
|
||||
abortSignal: {} as any,
|
||||
inspectorAdapters: {} as any,
|
||||
abortSignal: {},
|
||||
inspectorAdapters: {},
|
||||
} as ExecutionContext;
|
||||
};
|
||||
|
|
|
@ -16,12 +16,14 @@ import {
|
|||
IInterpreterRenderHandlers,
|
||||
RenderMode,
|
||||
AnyExpressionFunctionDefinition,
|
||||
ExpressionsService,
|
||||
ExecutionContract,
|
||||
} from '../common';
|
||||
|
||||
// eslint-disable-next-line
|
||||
const { __getLastExecution, __getLastRenderMode } = require('./services');
|
||||
|
||||
const element: HTMLElement = null as any;
|
||||
const element = null as unknown as HTMLElement;
|
||||
|
||||
let testScheduler: TestScheduler;
|
||||
|
||||
|
@ -36,8 +38,9 @@ jest.mock('./services', () => {
|
|||
},
|
||||
};
|
||||
|
||||
// eslint-disable-next-line
|
||||
const service = new (require('../common/service/expressions_services').ExpressionsService as any)();
|
||||
const service: ExpressionsService =
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
new (require('../common/service/expressions_services').ExpressionsService)();
|
||||
|
||||
const testFn: AnyExpressionFunctionDefinition = {
|
||||
fn: () => ({ type: 'render', as: 'test' }),
|
||||
|
@ -54,9 +57,9 @@ jest.mock('./services', () => {
|
|||
|
||||
service.start();
|
||||
|
||||
let execution: ExecutionContract;
|
||||
const moduleMock = {
|
||||
__execution: undefined,
|
||||
__getLastExecution: () => moduleMock.__execution,
|
||||
__getLastExecution: () => execution,
|
||||
__getLastRenderMode: () => renderMode,
|
||||
getRenderersRegistry: () => ({
|
||||
get: (id: string) => renderers[id],
|
||||
|
@ -72,13 +75,14 @@ jest.mock('./services', () => {
|
|||
};
|
||||
|
||||
const execute = service.execute;
|
||||
service.execute = (...args: any) => {
|
||||
const execution = execute(...args);
|
||||
|
||||
jest.spyOn(service, 'execute').mockImplementation((...args) => {
|
||||
execution = execute(...args);
|
||||
jest.spyOn(execution, 'getData');
|
||||
jest.spyOn(execution, 'cancel');
|
||||
moduleMock.__execution = execution;
|
||||
|
||||
return execution;
|
||||
};
|
||||
});
|
||||
|
||||
return moduleMock;
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import { BehaviorSubject, Observable, Subject, Subscription, asyncScheduler, identity } from 'rxjs';
|
||||
import { filter, map, delay, throttleTime } from 'rxjs/operators';
|
||||
import { defaults } from 'lodash';
|
||||
import { UnwrapObservable } from '@kbn/utility-types';
|
||||
import { SerializableRecord, UnwrapObservable } from '@kbn/utility-types';
|
||||
import { Adapters } from '../../inspector/public';
|
||||
import { IExpressionLoaderParams } from './types';
|
||||
import { ExpressionAstExpression } from '../common';
|
||||
|
@ -18,7 +18,7 @@ import { ExecutionContract } from '../common/execution/execution_contract';
|
|||
import { ExpressionRenderHandler } from './render';
|
||||
import { getExpressionsService } from './services';
|
||||
|
||||
type Data = any;
|
||||
type Data = unknown;
|
||||
|
||||
export class ExpressionLoader {
|
||||
data$: ReturnType<ExecutionContract['getData']>;
|
||||
|
@ -156,7 +156,7 @@ export class ExpressionLoader {
|
|||
};
|
||||
|
||||
private render(data: Data): void {
|
||||
this.renderHandler.render(data, this.params.uiState);
|
||||
this.renderHandler.render(data as SerializableRecord, this.params.uiState);
|
||||
}
|
||||
|
||||
private setParams(params?: IExpressionLoaderParams) {
|
||||
|
@ -169,7 +169,7 @@ export class ExpressionLoader {
|
|||
{},
|
||||
params.searchContext,
|
||||
this.params.searchContext || {}
|
||||
) as any;
|
||||
);
|
||||
}
|
||||
if (params.uiState && this.params) {
|
||||
this.params.uiState = params.uiState;
|
||||
|
|
|
@ -14,6 +14,7 @@ import { ReactExpressionRenderer } from './react_expression_renderer';
|
|||
import { ExpressionLoader } from './loader';
|
||||
import { mount } from 'enzyme';
|
||||
import { EuiProgress } from '@elastic/eui';
|
||||
import { IInterpreterRenderHandlers } from '../common';
|
||||
import { RenderErrorHandlerFnType } from './types';
|
||||
import { ExpressionRendererEvent } from './render';
|
||||
|
||||
|
@ -234,7 +235,7 @@ describe('ExpressionRenderer', () => {
|
|||
done: () => {
|
||||
renderSubject.next(1);
|
||||
},
|
||||
} as any);
|
||||
} as IInterpreterRenderHandlers);
|
||||
});
|
||||
|
||||
instance.update();
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import { ExpressionRenderHandler, render } from './render';
|
||||
import { Observable } from 'rxjs';
|
||||
import { SerializableRecord } from '@kbn/utility-types';
|
||||
import { ExpressionRenderError } from './types';
|
||||
import { getRenderersRegistry } from './services';
|
||||
import { first, take, toArray } from 'rxjs/operators';
|
||||
|
@ -79,11 +80,11 @@ describe('ExpressionRenderHandler', () => {
|
|||
|
||||
it('in case of error render$ should emit when error renderer is finished', async () => {
|
||||
const expressionRenderHandler = new ExpressionRenderHandler(element);
|
||||
expressionRenderHandler.render(false);
|
||||
expressionRenderHandler.render(false as unknown as SerializableRecord);
|
||||
const promise1 = expressionRenderHandler.render$.pipe(first()).toPromise();
|
||||
await expect(promise1).resolves.toEqual(1);
|
||||
|
||||
expressionRenderHandler.render(false);
|
||||
expressionRenderHandler.render(false as unknown as SerializableRecord);
|
||||
const promise2 = expressionRenderHandler.render$.pipe(first()).toPromise();
|
||||
await expect(promise2).resolves.toEqual(2);
|
||||
});
|
||||
|
@ -92,7 +93,7 @@ describe('ExpressionRenderHandler', () => {
|
|||
const expressionRenderHandler = new ExpressionRenderHandler(element, {
|
||||
onRenderError: mockMockErrorRenderFunction,
|
||||
});
|
||||
await expressionRenderHandler.render(false);
|
||||
await expressionRenderHandler.render(false as unknown as SerializableRecord);
|
||||
expect(getHandledError()!.message).toEqual(
|
||||
`invalid data provided to the expression renderer`
|
||||
);
|
||||
|
@ -122,7 +123,8 @@ describe('ExpressionRenderHandler', () => {
|
|||
get: () => ({
|
||||
render: (domNode: HTMLElement, config: unknown, handlers: IInterpreterRenderHandlers) => {
|
||||
handlers.hasCompatibleActions!({
|
||||
foo: 'bar',
|
||||
name: 'something',
|
||||
data: 'bar',
|
||||
});
|
||||
},
|
||||
}),
|
||||
|
@ -136,7 +138,8 @@ describe('ExpressionRenderHandler', () => {
|
|||
await expressionRenderHandler.render({ type: 'render', as: 'something' });
|
||||
expect(hasCompatibleActions).toHaveBeenCalledTimes(1);
|
||||
expect(hasCompatibleActions.mock.calls[0][0]).toEqual({
|
||||
foo: 'bar',
|
||||
name: 'something',
|
||||
data: 'bar',
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -156,7 +159,7 @@ describe('ExpressionRenderHandler', () => {
|
|||
it('default renderer should use notification service', async () => {
|
||||
const expressionRenderHandler = new ExpressionRenderHandler(element);
|
||||
const promise1 = expressionRenderHandler.render$.pipe(first()).toPromise();
|
||||
expressionRenderHandler.render(false);
|
||||
expressionRenderHandler.render(false as unknown as SerializableRecord);
|
||||
await expect(promise1).resolves.toEqual(1);
|
||||
expect(mockNotificationService.toasts.addError).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
|
@ -175,7 +178,7 @@ describe('ExpressionRenderHandler', () => {
|
|||
const expressionRenderHandler1 = new ExpressionRenderHandler(element, {
|
||||
onRenderError: mockMockErrorRenderFunction,
|
||||
});
|
||||
expressionRenderHandler1.render(false);
|
||||
expressionRenderHandler1.render(false as unknown as SerializableRecord);
|
||||
const renderPromiseAfterRender = expressionRenderHandler1.render$.pipe(first()).toPromise();
|
||||
await expect(renderPromiseAfterRender).resolves.toEqual(1);
|
||||
expect(getHandledError()!.message).toEqual(
|
||||
|
@ -188,7 +191,7 @@ describe('ExpressionRenderHandler', () => {
|
|||
onRenderError: mockMockErrorRenderFunction,
|
||||
});
|
||||
const renderPromiseBeforeRender = expressionRenderHandler2.render$.pipe(first()).toPromise();
|
||||
expressionRenderHandler2.render(false);
|
||||
expressionRenderHandler2.render(false as unknown as SerializableRecord);
|
||||
await expect(renderPromiseBeforeRender).resolves.toEqual(1);
|
||||
expect(getHandledError()!.message).toEqual(
|
||||
'invalid data provided to the expression renderer'
|
||||
|
@ -199,9 +202,9 @@ describe('ExpressionRenderHandler', () => {
|
|||
// that observables will emit previous result if subscription happens after render
|
||||
it('should emit previous render and error results', async () => {
|
||||
const expressionRenderHandler = new ExpressionRenderHandler(element);
|
||||
expressionRenderHandler.render(false);
|
||||
expressionRenderHandler.render(false as unknown as SerializableRecord);
|
||||
const renderPromise = expressionRenderHandler.render$.pipe(take(2), toArray()).toPromise();
|
||||
expressionRenderHandler.render(false);
|
||||
expressionRenderHandler.render(false as unknown as SerializableRecord);
|
||||
await expect(renderPromise).resolves.toEqual([1, 2]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,13 +9,20 @@
|
|||
import * as Rx from 'rxjs';
|
||||
import { Observable } from 'rxjs';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { isNumber } from 'lodash';
|
||||
import { SerializableRecord } from '@kbn/utility-types';
|
||||
import { ExpressionRenderError, RenderErrorHandlerFnType, IExpressionLoaderParams } from './types';
|
||||
import { renderErrorHandler as defaultRenderErrorHandler } from './render_error_handler';
|
||||
import { IInterpreterRenderHandlers, ExpressionAstExpression, RenderMode } from '../common';
|
||||
import {
|
||||
IInterpreterRenderHandlers,
|
||||
IInterpreterRenderEvent,
|
||||
IInterpreterRenderUpdateParams,
|
||||
RenderMode,
|
||||
} from '../common';
|
||||
|
||||
import { getRenderersRegistry } from './services';
|
||||
|
||||
export type IExpressionRendererExtraHandlers = Record<string, any>;
|
||||
export type IExpressionRendererExtraHandlers = Record<string, unknown>;
|
||||
|
||||
export interface ExpressionRenderHandlerParams {
|
||||
onRenderError?: RenderErrorHandlerFnType;
|
||||
|
@ -25,15 +32,10 @@ export interface ExpressionRenderHandlerParams {
|
|||
hasCompatibleActions?: (event: ExpressionRendererEvent) => Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface ExpressionRendererEvent {
|
||||
name: string;
|
||||
data: any;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type ExpressionRendererEvent = IInterpreterRenderEvent<any>;
|
||||
|
||||
interface UpdateValue {
|
||||
newExpression?: string | ExpressionAstExpression;
|
||||
newParams: IExpressionLoaderParams;
|
||||
}
|
||||
type UpdateValue = IInterpreterRenderUpdateParams<IExpressionLoaderParams>;
|
||||
|
||||
export class ExpressionRenderHandler {
|
||||
render$: Observable<number>;
|
||||
|
@ -41,7 +43,7 @@ export class ExpressionRenderHandler {
|
|||
events$: Observable<ExpressionRendererEvent>;
|
||||
|
||||
private element: HTMLElement;
|
||||
private destroyFn?: any;
|
||||
private destroyFn?: Function;
|
||||
private renderCount: number = 0;
|
||||
private renderSubject: Rx.BehaviorSubject<number | null>;
|
||||
private eventsSubject: Rx.Subject<unknown>;
|
||||
|
@ -66,16 +68,14 @@ export class ExpressionRenderHandler {
|
|||
|
||||
this.onRenderError = onRenderError || defaultRenderErrorHandler;
|
||||
|
||||
this.renderSubject = new Rx.BehaviorSubject(null as any | null);
|
||||
this.render$ = this.renderSubject
|
||||
.asObservable()
|
||||
.pipe(filter((_) => _ !== null)) as Observable<any>;
|
||||
this.renderSubject = new Rx.BehaviorSubject<number | null>(null);
|
||||
this.render$ = this.renderSubject.asObservable().pipe(filter(isNumber));
|
||||
|
||||
this.updateSubject = new Rx.Subject();
|
||||
this.update$ = this.updateSubject.asObservable();
|
||||
|
||||
this.handlers = {
|
||||
onDestroy: (fn: any) => {
|
||||
onDestroy: (fn: Function) => {
|
||||
this.destroyFn = fn;
|
||||
},
|
||||
done: () => {
|
||||
|
@ -104,14 +104,14 @@ export class ExpressionRenderHandler {
|
|||
};
|
||||
}
|
||||
|
||||
render = async (value: any, uiState?: any) => {
|
||||
render = async (value: SerializableRecord, uiState?: unknown) => {
|
||||
if (!value || typeof value !== 'object') {
|
||||
return this.handleRenderError(new Error('invalid data provided to the expression renderer'));
|
||||
}
|
||||
|
||||
if (value.type !== 'render' || !value.as) {
|
||||
if (value.type === 'error') {
|
||||
return this.handleRenderError(value.error);
|
||||
return this.handleRenderError(value.error as unknown as ExpressionRenderError);
|
||||
} else {
|
||||
return this.handleRenderError(
|
||||
new Error('invalid data provided to the expression renderer')
|
||||
|
@ -119,20 +119,20 @@ export class ExpressionRenderHandler {
|
|||
}
|
||||
}
|
||||
|
||||
if (!getRenderersRegistry().get(value.as)) {
|
||||
if (!getRenderersRegistry().get(value.as as string)) {
|
||||
return this.handleRenderError(new Error(`invalid renderer id '${value.as}'`));
|
||||
}
|
||||
|
||||
try {
|
||||
// Rendering is asynchronous, completed by handlers.done()
|
||||
await getRenderersRegistry()
|
||||
.get(value.as)!
|
||||
.get(value.as as string)!
|
||||
.render(this.element, value.value, {
|
||||
...this.handlers,
|
||||
uiState,
|
||||
} as any);
|
||||
});
|
||||
} catch (e) {
|
||||
return this.handleRenderError(e);
|
||||
return this.handleRenderError(e as ExpressionRenderError);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -156,10 +156,10 @@ export class ExpressionRenderHandler {
|
|||
|
||||
export function render(
|
||||
element: HTMLElement,
|
||||
data: any,
|
||||
data: unknown,
|
||||
options?: ExpressionRenderHandlerParams
|
||||
): ExpressionRenderHandler {
|
||||
const handler = new ExpressionRenderHandler(element, options);
|
||||
handler.render(data);
|
||||
handler.render(data as SerializableRecord);
|
||||
return handler;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ export interface ExpressionInterpreter {
|
|||
export interface IExpressionLoaderParams {
|
||||
searchContext?: SerializableRecord;
|
||||
context?: ExpressionValue;
|
||||
variables?: Record<string, any>;
|
||||
variables?: Record<string, unknown>;
|
||||
// Enables debug tracking on each expression in the AST
|
||||
debug?: boolean;
|
||||
disableCaching?: boolean;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import { functionWrapper } from '../../expressions/common/expression_functions/specs/tests/utils';
|
||||
import { createMarkdownVisFn } from './markdown_fn';
|
||||
import { Arguments } from './types';
|
||||
|
||||
describe('interpreter/functions#markdown', () => {
|
||||
const fn = functionWrapper(createMarkdownVisFn());
|
||||
|
@ -15,7 +16,7 @@ describe('interpreter/functions#markdown', () => {
|
|||
font: { spec: { fontSize: 12 } },
|
||||
openLinksInNewTab: true,
|
||||
markdown: '## hello _markdown_',
|
||||
};
|
||||
} as unknown as Arguments;
|
||||
|
||||
it('returns an object with the correct structure', async () => {
|
||||
const actual = await fn(null, args, undefined);
|
||||
|
|
|
@ -10,13 +10,15 @@ import { createMetricVisFn } from './metric_vis_fn';
|
|||
import { functionWrapper } from '../../../expressions/common/expression_functions/specs/tests/utils';
|
||||
import { Datatable } from '../../../expressions/common/expression_types/specs';
|
||||
|
||||
type Arguments = Parameters<ReturnType<typeof createMetricVisFn>['fn']>[1];
|
||||
|
||||
describe('interpreter/functions#metric', () => {
|
||||
const fn = functionWrapper(createMetricVisFn());
|
||||
const context = {
|
||||
type: 'datatable',
|
||||
rows: [{ 'col-0-1': 0 }],
|
||||
columns: [{ id: 'col-0-1', name: 'Count' }],
|
||||
};
|
||||
} as unknown as Datatable;
|
||||
const args = {
|
||||
percentageMode: false,
|
||||
useRanges: false,
|
||||
|
@ -50,7 +52,7 @@ describe('interpreter/functions#metric', () => {
|
|||
aggType: 'count',
|
||||
},
|
||||
],
|
||||
};
|
||||
} as unknown as Arguments;
|
||||
|
||||
it('returns an object with the correct structure', () => {
|
||||
const actual = fn(context, args, undefined);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import { functionWrapper } from '../../../expressions/common/expression_functions/specs/tests/utils';
|
||||
import { createPieVisFn } from './pie_fn';
|
||||
import { PieVisConfig } from './types';
|
||||
import { Datatable } from '../../../expressions/common/expression_types/specs';
|
||||
|
||||
describe('interpreter/functions#pie', () => {
|
||||
|
@ -16,7 +17,7 @@ describe('interpreter/functions#pie', () => {
|
|||
type: 'datatable',
|
||||
rows: [{ 'col-0-1': 0 }],
|
||||
columns: [{ id: 'col-0-1', name: 'Count' }],
|
||||
};
|
||||
} as unknown as Datatable;
|
||||
const visConfig = {
|
||||
addTooltip: true,
|
||||
addLegend: true,
|
||||
|
@ -43,7 +44,7 @@ describe('interpreter/functions#pie', () => {
|
|||
params: {},
|
||||
aggType: 'count',
|
||||
},
|
||||
};
|
||||
} as unknown as PieVisConfig;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import { createTableVisFn } from './table_vis_fn';
|
||||
import { tableVisResponseHandler } from './utils';
|
||||
import { TableVisConfig } from './types';
|
||||
|
||||
import { functionWrapper } from '../../../expressions/common/expression_functions/specs/tests/utils';
|
||||
import { Datatable } from '../../../expressions/common/expression_types/specs';
|
||||
|
@ -24,7 +25,7 @@ describe('interpreter/functions#table', () => {
|
|||
type: 'datatable',
|
||||
rows: [{ 'col-0-1': 0 }],
|
||||
columns: [{ id: 'col-0-1', name: 'Count' }],
|
||||
};
|
||||
} as unknown as Datatable;
|
||||
const visConfig = {
|
||||
title: 'My Chart title',
|
||||
perPage: 10,
|
||||
|
@ -52,7 +53,7 @@ describe('interpreter/functions#table', () => {
|
|||
},
|
||||
],
|
||||
buckets: [],
|
||||
};
|
||||
} as unknown as TableVisConfig;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { Datatable } from 'src/plugins/expressions';
|
||||
import { functionWrapper } from '../../../expressions/common/expression_functions/specs/tests/utils';
|
||||
import { createPieVisFn } from './pie_fn';
|
||||
// @ts-ignore
|
||||
|
@ -34,7 +35,7 @@ describe('interpreter/functions#pie', () => {
|
|||
type: 'datatable',
|
||||
rows: [{ 'col-0-1': 0 }],
|
||||
columns: [{ id: 'col-0-1', name: 'Count' }],
|
||||
};
|
||||
} as unknown as Datatable;
|
||||
const visConfig = {
|
||||
type: 'pie',
|
||||
addTooltip: true,
|
||||
|
|
|
@ -5,7 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Datatable, ExpressionFunctionDefinition, getType } from '../../../types';
|
||||
import {
|
||||
Datatable,
|
||||
DatatableColumnType,
|
||||
ExpressionFunctionDefinition,
|
||||
getType,
|
||||
} from '../../../types';
|
||||
import { getFunctionHelp } from '../../../i18n';
|
||||
|
||||
interface Arguments {
|
||||
|
@ -30,14 +35,14 @@ export function asFn(): ExpressionFunctionDefinition<'as', Input, Arguments, Dat
|
|||
default: 'value',
|
||||
},
|
||||
},
|
||||
fn: (input, args) => {
|
||||
fn: (input, args): Datatable => {
|
||||
return {
|
||||
type: 'datatable',
|
||||
columns: [
|
||||
{
|
||||
id: args.name,
|
||||
name: args.name,
|
||||
meta: { type: getType(input) },
|
||||
meta: { type: getType(input) as DatatableColumnType },
|
||||
},
|
||||
],
|
||||
rows: [
|
||||
|
|
|
@ -156,7 +156,7 @@ ${examplesBlock}
|
|||
*Returns:* ${output ? wrapInBackTicks(output) : 'Depends on your input and arguments'}\n\n`;
|
||||
};
|
||||
|
||||
const getArgsTable = (args: { [key: string]: ExpressionFunctionParameter }) => {
|
||||
const getArgsTable = (args: { [key: string]: ExpressionFunctionParameter<any> }) => {
|
||||
if (!args || Object.keys(args).length === 0) {
|
||||
return 'None';
|
||||
}
|
||||
|
|
|
@ -7,11 +7,10 @@
|
|||
|
||||
import { Datatable, DatatableColumn } from 'src/plugins/expressions/public';
|
||||
import { functionWrapper } from 'src/plugins/expressions/common/expression_functions/specs/tests/utils';
|
||||
import { FormatColumnArgs, formatColumn } from './index';
|
||||
import { formatColumn } from './index';
|
||||
|
||||
describe('format_column', () => {
|
||||
const fn: (input: Datatable, args: FormatColumnArgs) => Promise<Datatable> =
|
||||
functionWrapper(formatColumn);
|
||||
const fn = functionWrapper(formatColumn);
|
||||
|
||||
let datatable: Datatable;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue