Skip to content

Commit

Permalink
cleaning up expression service types
Browse files Browse the repository at this point in the history
  • Loading branch information
ppisljar committed Oct 15, 2020
1 parent b125472 commit 0754cd7
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 80 deletions.
58 changes: 24 additions & 34 deletions src/plugins/expressions/common/execution/execution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/

import { keys, last, mapValues, reduce, zipObject } from 'lodash';
import { Executor, ExpressionExecOptions } from '../executor';
import { Executor } from '../executor';
import { createExecutionContainer, ExecutionContainer } from './container';
import { createError } from '../util';
import { Defer, now } from '../../../kibana_utils/common';
Expand All @@ -38,27 +38,19 @@ import { getType, ExpressionValue } from '../expression_types';
import { ArgumentType, ExpressionFunction } from '../expression_functions';
import { getByAlias } from '../util/get_by_alias';
import { ExecutionContract } from './execution_contract';
import { ExpressionExecutionParams } from '../service';

const createAbortErrorValue = () =>
createError({
message: 'The expression was aborted.',
name: 'AbortError',
});

export interface ExecutionParams<
ExtraContext extends Record<string, unknown> = Record<string, unknown>
> {
export interface ExecutionParams {
executor: Executor<any>;
ast?: ExpressionAstExpression;
expression?: string;
context?: ExtraContext;

/**
* Whether to execute expression in *debug mode*. In *debug mode* inputs and
* outputs as well as all resolved arguments and time it took to execute each
* function are saved and are available for introspection.
*/
debug?: boolean;
params?: ExpressionExecutionParams;
}

const createDefaultInspectorAdapters = (): DefaultInspectorAdapters => ({
Expand All @@ -67,11 +59,10 @@ const createDefaultInspectorAdapters = (): DefaultInspectorAdapters => ({
});

export class Execution<
ExtraContext extends Record<string, unknown> = Record<string, unknown>,
Input = unknown,
Output = unknown,
InspectorAdapters extends Adapters = ExtraContext['inspectorAdapters'] extends object
? ExtraContext['inspectorAdapters']
InspectorAdapters extends Adapters = ExpressionExecutionParams['inspectorAdapters'] extends object
? ExpressionExecutionParams['inspectorAdapters']
: DefaultInspectorAdapters
> {
/**
Expand All @@ -91,7 +82,7 @@ export class Execution<
* Execution context - object that allows to do side-effects. Context is passed
* to every function.
*/
public readonly context: ExecutionContext<Input, InspectorAdapters> & ExtraContext;
public readonly context: ExecutionContext<Input, InspectorAdapters>;

/**
* AbortController to cancel this Execution.
Expand Down Expand Up @@ -125,11 +116,10 @@ export class Execution<
* can return to other plugins for their consumption.
*/
public readonly contract: ExecutionContract<
ExtraContext,
Input,
Output,
InspectorAdapters
> = new ExecutionContract<ExtraContext, Input, Output, InspectorAdapters>(this);
> = new ExecutionContract<Input, Output, InspectorAdapters>(this);

public readonly expression: string;

Expand All @@ -141,17 +131,17 @@ export class Execution<
return this.context.inspectorAdapters;
}

constructor(public readonly params: ExecutionParams<ExtraContext>) {
const { executor } = params;
constructor(public readonly execution: ExecutionParams) {
const { executor } = execution;

if (!params.ast && !params.expression) {
if (!execution.ast && !execution.expression) {
throw new TypeError('Execution params should contain at least .ast or .expression key.');
} else if (params.ast && params.expression) {
} else if (execution.ast && execution.expression) {
throw new TypeError('Execution params cannot contain both .ast and .expression key.');
}

this.expression = params.expression || formatExpression(params.ast!);
const ast = params.ast || parseExpression(this.expression);
this.expression = execution.expression || formatExpression(execution.ast!);
const ast = execution.ast || parseExpression(this.expression);

this.state = createExecutionContainer<Output | ExpressionValueError>({
...executor.state.get(),
Expand All @@ -161,12 +151,12 @@ export class Execution<

this.context = {
getInitialInput: () => this.input,
getSessionId: () => execution.params?.sessionId,
variables: {},
types: executor.getTypes(),
abortSignal: this.abortController.signal,
...(params.context || ({} as ExtraContext)),
inspectorAdapters: (params.context && params.context.inspectorAdapters
? params.context.inspectorAdapters
inspectorAdapters: (execution.params?.inspectorAdapters
? execution.params.inspectorAdapters
: createDefaultInspectorAdapters()) as InspectorAdapters,
};
}
Expand Down Expand Up @@ -228,10 +218,10 @@ export class Execution<
// actually have a `then` function which would be treated as a `Promise`.
const { resolvedArgs } = await this.race(this.resolveArgs(fn, input, fnArgs));
args = resolvedArgs;
timeStart = this.params.debug ? now() : 0;
timeStart = this.execution.params.debug ? now() : 0;
const output = await this.race(this.invokeFunction(fn, input, resolvedArgs));

if (this.params.debug) {
if (this.execution.params.debug) {
const timeEnd: number = now();
(link as ExpressionAstFunction).debug = {
success: true,
Expand All @@ -246,11 +236,11 @@ export class Execution<
if (getType(output) === 'error') return output;
input = output;
} catch (rawError) {
const timeEnd: number = this.params.debug ? now() : 0;
const timeEnd: number = this.execution.params.debug ? now() : 0;
const error = createError(rawError) as ExpressionValueError;
error.error.message = `[${fnName}] > ${error.error.message}`;

if (this.params.debug) {
if (this.execution.params.debug) {
(link as ExpressionAstFunction).debug = {
success: false,
fn: fn.name,
Expand Down Expand Up @@ -384,7 +374,7 @@ export class Execution<
return asts.map((item: ExpressionAstExpression) => {
return async (subInput = input) => {
const output = await this.interpret(item, subInput, {
debug: this.params.debug,
debug: this.execution.params.debug,
});
if (isExpressionValueError(output)) throw output.error;
const casted = this.cast(output, argDefs[argName as any].types);
Expand Down Expand Up @@ -420,11 +410,11 @@ export class Execution<
public async interpret<T>(
ast: ExpressionAstNode,
input: T,
options?: ExpressionExecOptions
options?: ExpressionExecutionParams
): Promise<unknown> {
switch (getType(ast)) {
case 'expression':
const execution = this.params.executor.createExecution(
const execution = this.execution.executor.createExecution(
ast as ExpressionAstExpression,
this.context,
options
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,15 @@ import { ExpressionAstExpression } from '../ast';
* `ExecutionContract` is a wrapper around `Execution` class. It provides the
* same functionality but does not expose Expressions plugin internals.
*/
export class ExecutionContract<
ExtraContext extends Record<string, unknown> = Record<string, unknown>,
Input = unknown,
Output = unknown,
InspectorAdapters = unknown
> {
export class ExecutionContract<Input = unknown, Output = unknown, InspectorAdapters = unknown> {
public get isPending(): boolean {
const state = this.execution.state.get().state;
const finished = state === 'error' || state === 'result';
return !finished;
}

constructor(
protected readonly execution: Execution<ExtraContext, Input, Output, InspectorAdapters>
protected readonly execution: Execution<Input, Output, InspectorAdapters>
) {}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/expressions/common/execution/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ export interface ExecutionContext<Input = unknown, InspectorAdapters extends Ada
*/
search?: ExecutionContextSearch;

getSessionId: () => string | undefined;

/**
* Allows to fetch saved objects from ElasticSearch. In browser `getSavedObject`
* function is provided automatically by the Expressions plugin. On the server
Expand Down
36 changes: 11 additions & 25 deletions src/plugins/expressions/common/executor/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,7 @@ import { AnyExpressionTypeDefinition } from '../expression_types/types';
import { ExpressionAstExpression } from '../ast';
import { typeSpecs } from '../expression_types/specs';
import { functionSpecs } from '../expression_functions/specs';

export interface ExpressionExecOptions {
/**
* Whether to execute expression in *debug mode*. In *debug mode* inputs and
* outputs as well as all resolved arguments and time it took to execute each
* function are saved and are available for introspection.
*/
debug?: boolean;
}
import { ExpressionExecutionParams } from '../service';

export class TypesRegistry implements IRegistry<ExpressionType> {
constructor(private readonly executor: Executor<any>) {}
Expand Down Expand Up @@ -171,28 +163,22 @@ export class Executor<Context extends Record<string, unknown> = Record<string, u
return (await execution.result) as Output;
}

public createExecution<
ExtraContext extends Record<string, unknown> = Record<string, unknown>,
Input = unknown,
Output = unknown
>(
public createExecution<Input = unknown, Output = unknown>(
ast: string | ExpressionAstExpression,
context: ExtraContext = {} as ExtraContext,
{ debug }: ExpressionExecOptions = {} as ExpressionExecOptions
): Execution<Context & ExtraContext, Input, Output> {
const params: ExecutionParams<Context & ExtraContext> = {
params: ExpressionExecutionParams = {}
): Execution<Input, Output> {
const executionParams: ExecutionParams = {
executor: this,
context: {
params: {
...params,
...this.context,
...context,
} as Context & ExtraContext,
debug,
},
};

if (typeof ast === 'string') params.expression = ast;
else params.ast = ast;
if (typeof ast === 'string') executionParams.expression = ast;
else executionParams.ast = ast;

const execution = new Execution<Context & ExtraContext, Input, Output>(params);
const execution = new Execution<Input, Output>(executionParams);

return execution;
}
Expand Down
43 changes: 29 additions & 14 deletions src/plugins/expressions/common/service/expressions_services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import { Executor } from '../executor';
import { AnyExpressionRenderDefinition, ExpressionRendererRegistry } from '../expression_renderers';
import { ExpressionAstExpression } from '../ast';
import { ExecutionContract } from '../execution/execution_contract';
import { AnyExpressionTypeDefinition } from '../expression_types';
import { AnyExpressionTypeDefinition, ExpressionValue } from '../expression_types';
import { AnyExpressionFunctionDefinition } from '../expression_functions';
import { Adapters } from '../../../inspector/common/adapters';
import { ExecutionContextSearch } from '../execution';

/**
* The public contract that `ExpressionsService` provides to other plugins
Expand All @@ -43,6 +45,23 @@ export type ExpressionsServiceSetup = Pick<
| 'fork'
>;

export interface ExpressionExecutionParams {
searchContext?: ExecutionContextSearch;

variables?: Record<string, any>;

/**
* Whether to execute expression in *debug mode*. In *debug mode* inputs and
* outputs as well as all resolved arguments and time it took to execute each
* function are saved and are available for introspection.
*/
debug?: boolean;

sessionId?: string;

inspectorAdapters?: Adapters;
}

/**
* The public contract that `ExpressionsService` provides to other plugins
* in Kibana Platform in *start* life-cycle.
Expand Down Expand Up @@ -96,27 +115,23 @@ export interface ExpressionsServiceStart {
* expressions.run('...', null, { elasticsearchClient });
* ```
*/
run: <Input, Output, ExtraContext extends Record<string, unknown> = Record<string, unknown>>(
run: <Input, Output>(
ast: string | ExpressionAstExpression,
input: Input,
context?: ExtraContext
params?: ExpressionExecutionParams
) => Promise<Output>;

/**
* Starts expression execution and immediately returns `ExecutionContract`
* instance that tracks the progress of the execution and can be used to
* interact with the execution.
*/
execute: <
Input = unknown,
Output = unknown,
ExtraContext extends Record<string, unknown> = Record<string, unknown>
>(
execute: <Input = unknown, Output = unknown>(
ast: string | ExpressionAstExpression,
// This any is for legacy reasons.
input: Input,
context?: ExtraContext
) => ExecutionContract<ExtraContext, Input, Output>;
params?: ExpressionExecutionParams
) => ExecutionContract<Input, Output>;

/**
* Create a new instance of `ExpressionsService`. The new instance inherits
Expand Down Expand Up @@ -210,8 +225,8 @@ export class ExpressionsService {
definition: AnyExpressionRenderDefinition | (() => AnyExpressionRenderDefinition)
): void => this.renderers.register(definition);

public readonly run: ExpressionsServiceStart['run'] = (ast, input, context) =>
this.executor.run(ast, input, context);
public readonly run: ExpressionsServiceStart['run'] = (ast, input, params) =>
this.executor.run(ast, input, params);

public readonly getFunction: ExpressionsServiceStart['getFunction'] = (name) =>
this.executor.getFunction(name);
Expand Down Expand Up @@ -242,8 +257,8 @@ export class ExpressionsService {
*/
public readonly getTypes = (): ReturnType<Executor['getTypes']> => this.executor.getTypes();

public readonly execute: ExpressionsServiceStart['execute'] = ((ast, input, context) => {
const execution = this.executor.createExecution(ast, context);
public readonly execute: ExpressionsServiceStart['execute'] = ((ast, input, params) => {
const execution = this.executor.createExecution(ast, params);
execution.start(input);
return execution.contract;
}) as ExpressionsServiceStart['execute'];
Expand Down
4 changes: 4 additions & 0 deletions src/plugins/expressions/public/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export class ExpressionLoader {
search: params.searchContext,
variables: params.variables || {},
inspectorAdapters: params.inspectorAdapters,
sessionId: params.sessionId,
});

const prevDataHandler = this.execution;
Expand Down Expand Up @@ -181,6 +182,9 @@ export class ExpressionLoader {
if (params.variables && this.params) {
this.params.variables = params.variables;
}
if (params.sessionId && this.params) {
this.params.sessionId = params.sessionId;
}

this.params.inspectorAdapters = (params.inspectorAdapters ||
this.execution?.inspect()) as Adapters;
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/expressions/public/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export interface IExpressionLoaderParams {
uiState?: unknown;
inspectorAdapters?: Adapters;
onRenderError?: RenderErrorHandlerFnType;
sessionId?: string;
debug: boolean;
}

export interface ExpressionRenderError extends Error {
Expand Down

0 comments on commit 0754cd7

Please sign in to comment.