mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
* Add partial result flag to the execution result * Update expressions plugin run method to return observable * Update data getter in the execution contract to return observable * Update the expression loader to take into account the partial results flag
This commit is contained in:
parent
e601457bba
commit
c73e13d744
43 changed files with 370 additions and 281 deletions
|
@ -7,7 +7,7 @@
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
interpret<T>(ast: ExpressionAstNode, input: T): Observable<unknown>;
|
||||
interpret<T>(ast: ExpressionAstNode, input: T): Observable<ExecutionResult<unknown>>;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
@ -19,5 +19,5 @@ interpret<T>(ast: ExpressionAstNode, input: T): Observable<unknown>;
|
|||
|
||||
<b>Returns:</b>
|
||||
|
||||
`Observable<unknown>`
|
||||
`Observable<ExecutionResult<unknown>>`
|
||||
|
||||
|
|
|
@ -26,8 +26,8 @@ export declare class Execution<Input = unknown, Output = unknown, InspectorAdapt
|
|||
| [expression](./kibana-plugin-plugins-expressions-public.execution.expression.md) | | <code>string</code> | |
|
||||
| [input](./kibana-plugin-plugins-expressions-public.execution.input.md) | | <code>Input</code> | Initial input of the execution.<!-- -->N.B. It is initialized to <code>null</code> rather than <code>undefined</code> for legacy reasons, because in legacy interpreter it was set to <code>null</code> by default. |
|
||||
| [inspectorAdapters](./kibana-plugin-plugins-expressions-public.execution.inspectoradapters.md) | | <code>InspectorAdapters</code> | |
|
||||
| [result](./kibana-plugin-plugins-expressions-public.execution.result.md) | | <code>Observable<Output | ExpressionValueError></code> | Future that tracks result or error of this execution. |
|
||||
| [state](./kibana-plugin-plugins-expressions-public.execution.state.md) | | <code>ExecutionContainer<Output | ExpressionValueError></code> | Dynamic state of the execution. |
|
||||
| [result](./kibana-plugin-plugins-expressions-public.execution.result.md) | | <code>Observable<ExecutionResult<Output | ExpressionValueError>></code> | Future that tracks result or error of this execution. |
|
||||
| [state](./kibana-plugin-plugins-expressions-public.execution.state.md) | | <code>ExecutionContainer<ExecutionResult<Output | ExpressionValueError>></code> | Dynamic state of the execution. |
|
||||
|
||||
## Methods
|
||||
|
||||
|
|
|
@ -9,5 +9,5 @@ Future that tracks result or error of this execution.
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
readonly result: Observable<Output | ExpressionValueError>;
|
||||
readonly result: Observable<ExecutionResult<Output | ExpressionValueError>>;
|
||||
```
|
||||
|
|
|
@ -11,7 +11,7 @@ N.B. `input` is initialized to `null` rather than `undefined` for legacy reasons
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
start(input?: Input): Observable<Output | ExpressionValueError>;
|
||||
start(input?: Input): Observable<ExecutionResult<Output | ExpressionValueError>>;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
@ -22,5 +22,5 @@ start(input?: Input): Observable<Output | ExpressionValueError>;
|
|||
|
||||
<b>Returns:</b>
|
||||
|
||||
`Observable<Output | ExpressionValueError>`
|
||||
`Observable<ExecutionResult<Output | ExpressionValueError>>`
|
||||
|
||||
|
|
|
@ -9,5 +9,5 @@ Dynamic state of the execution.
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
readonly state: ExecutionContainer<Output | ExpressionValueError>;
|
||||
readonly state: ExecutionContainer<ExecutionResult<Output | ExpressionValueError>>;
|
||||
```
|
||||
|
|
|
@ -9,5 +9,5 @@ Returns the final output of expression, if any error happens still wraps that er
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
getData: () => Promise<Output | ExpressionValueError>;
|
||||
getData: () => Observable<ExecutionResult<Output | ExpressionValueError>>;
|
||||
```
|
||||
|
|
|
@ -25,7 +25,7 @@ export declare class ExecutionContract<Input = unknown, Output = unknown, Inspec
|
|||
| [cancel](./kibana-plugin-plugins-expressions-public.executioncontract.cancel.md) | | <code>() => void</code> | Cancel the execution of the expression. This will set abort signal (available in execution context) to aborted state, letting expression functions to stop their execution. |
|
||||
| [execution](./kibana-plugin-plugins-expressions-public.executioncontract.execution.md) | | <code>Execution<Input, Output, InspectorAdapters></code> | |
|
||||
| [getAst](./kibana-plugin-plugins-expressions-public.executioncontract.getast.md) | | <code>() => ExpressionAstExpression</code> | Get AST used to execute the expression. |
|
||||
| [getData](./kibana-plugin-plugins-expressions-public.executioncontract.getdata.md) | | <code>() => Promise<Output | ExpressionValueError></code> | Returns the final output of expression, if any error happens still wraps that error into <code>ExpressionValueError</code> type and returns that. This function never throws. |
|
||||
| [getData](./kibana-plugin-plugins-expressions-public.executioncontract.getdata.md) | | <code>() => Observable<ExecutionResult<Output | ExpressionValueError>></code> | Returns the final output of expression, if any error happens still wraps that error into <code>ExpressionValueError</code> type and returns that. This function never throws. |
|
||||
| [getExpression](./kibana-plugin-plugins-expressions-public.executioncontract.getexpression.md) | | <code>() => string</code> | Get string representation of the expression. Returns the original string if execution was started from a string. If execution was started from an AST this method returns a string generated from AST. |
|
||||
| [inspect](./kibana-plugin-plugins-expressions-public.executioncontract.inspect.md) | | <code>() => InspectorAdapters</code> | Get Inspector adapters provided to all functions of expression through execution context. |
|
||||
| [isPending](./kibana-plugin-plugins-expressions-public.executioncontract.ispending.md) | | <code>boolean</code> | |
|
||||
|
|
|
@ -9,7 +9,7 @@ Execute expression and return result.
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
run<Input, Output>(ast: string | ExpressionAstExpression, input: Input, params?: ExpressionExecutionParams): Observable<Output | ExpressionValueError>;
|
||||
run<Input, Output>(ast: string | ExpressionAstExpression, input: Input, params?: ExpressionExecutionParams): Observable<ExecutionResult<Output | ExpressionValueError>>;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
@ -22,5 +22,5 @@ run<Input, Output>(ast: string | ExpressionAstExpression, input: Input, params?:
|
|||
|
||||
<b>Returns:</b>
|
||||
|
||||
`Observable<Output | ExpressionValueError>`
|
||||
`Observable<ExecutionResult<Output | ExpressionValueError>>`
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ export interface ExpressionsServiceStart
|
|||
| [getFunction](./kibana-plugin-plugins-expressions-public.expressionsservicestart.getfunction.md) | <code>(name: string) => ReturnType<Executor['getFunction']></code> | Get a registered <code>ExpressionFunction</code> by its name, which was registered using the <code>registerFunction</code> method. The returned <code>ExpressionFunction</code> instance is an internal representation of the function in Expressions service - do not mutate that object. |
|
||||
| [getRenderer](./kibana-plugin-plugins-expressions-public.expressionsservicestart.getrenderer.md) | <code>(name: string) => ReturnType<ExpressionRendererRegistry['get']></code> | Get a registered <code>ExpressionRenderer</code> by its name, which was registered using the <code>registerRenderer</code> method. The returned <code>ExpressionRenderer</code> instance is an internal representation of the renderer in Expressions service - do not mutate that object. |
|
||||
| [getType](./kibana-plugin-plugins-expressions-public.expressionsservicestart.gettype.md) | <code>(name: string) => ReturnType<Executor['getType']></code> | Get a registered <code>ExpressionType</code> by its name, which was registered using the <code>registerType</code> method. The returned <code>ExpressionType</code> instance is an internal representation of the type in Expressions service - do not mutate that object. |
|
||||
| [run](./kibana-plugin-plugins-expressions-public.expressionsservicestart.run.md) | <code><Input, Output>(ast: string | ExpressionAstExpression, input: Input, params?: ExpressionExecutionParams) => Promise<Output></code> | Executes expression string or a parsed expression AST and immediately returns the result.<!-- -->Below example will execute <code>sleep 100 | clog</code> expression with <code>123</code> initial input to the first function.
|
||||
| [run](./kibana-plugin-plugins-expressions-public.expressionsservicestart.run.md) | <code><Input, Output>(ast: string | ExpressionAstExpression, input: Input, params?: ExpressionExecutionParams) => Observable<ExecutionResult<Output | ExpressionValueError>></code> | Executes expression string or a parsed expression AST and immediately returns the result.<!-- -->Below example will execute <code>sleep 100 | clog</code> expression with <code>123</code> initial input to the first function.
|
||||
```ts
|
||||
expressions.run('sleep 100 | clog', 123);
|
||||
|
||||
|
|
|
@ -24,5 +24,5 @@ expressions.run('...', null, { elasticsearchClient });
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
run: <Input, Output>(ast: string | ExpressionAstExpression, input: Input, params?: ExpressionExecutionParams) => Promise<Output>;
|
||||
run: <Input, Output>(ast: string | ExpressionAstExpression, input: Input, params?: ExpressionExecutionParams) => Observable<ExecutionResult<Output | ExpressionValueError>>;
|
||||
```
|
||||
|
|
|
@ -22,6 +22,7 @@ export interface IExpressionLoaderParams
|
|||
| [hasCompatibleActions](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.hascompatibleactions.md) | <code>ExpressionRenderHandlerParams['hasCompatibleActions']</code> | |
|
||||
| [inspectorAdapters](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.inspectoradapters.md) | <code>Adapters</code> | |
|
||||
| [onRenderError](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.onrendererror.md) | <code>RenderErrorHandlerFnType</code> | |
|
||||
| [partial](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.partial.md) | <code>boolean</code> | |
|
||||
| [renderMode](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.rendermode.md) | <code>RenderMode</code> | |
|
||||
| [searchContext](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.searchcontext.md) | <code>SerializableState</code> | |
|
||||
| [searchSessionId](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.searchsessionid.md) | <code>string</code> | |
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IExpressionLoaderParams](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md) > [partial](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.partial.md)
|
||||
|
||||
## IExpressionLoaderParams.partial property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
partial?: boolean;
|
||||
```
|
|
@ -18,7 +18,7 @@ export interface ReactExpressionRendererProps extends IExpressionLoaderParams
|
|||
| [dataAttrs](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.dataattrs.md) | <code>string[]</code> | |
|
||||
| [debounce](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.debounce.md) | <code>number</code> | |
|
||||
| [expression](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.expression.md) | <code>string | ExpressionAstExpression</code> | |
|
||||
| [onData$](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.ondata_.md) | <code><TData, TInspectorAdapters>(data: TData, adapters?: TInspectorAdapters) => void</code> | |
|
||||
| [onData$](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.ondata_.md) | <code><TData, TInspectorAdapters>(data: TData, adapters?: TInspectorAdapters, partial?: boolean) => void</code> | |
|
||||
| [onEvent](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.onevent.md) | <code>(event: ExpressionRendererEvent) => void</code> | |
|
||||
| [padding](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.padding.md) | <code>'xs' | 's' | 'm' | 'l' | 'xl'</code> | |
|
||||
| [reload$](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.reload_.md) | <code>Observable<unknown></code> | An observable which can be used to re-run the expression without destroying the component |
|
||||
|
|
|
@ -7,5 +7,5 @@
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
onData$?: <TData, TInspectorAdapters>(data: TData, adapters?: TInspectorAdapters) => void;
|
||||
onData$?: <TData, TInspectorAdapters>(data: TData, adapters?: TInspectorAdapters, partial?: boolean) => void;
|
||||
```
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
interpret<T>(ast: ExpressionAstNode, input: T): Observable<unknown>;
|
||||
interpret<T>(ast: ExpressionAstNode, input: T): Observable<ExecutionResult<unknown>>;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
@ -19,5 +19,5 @@ interpret<T>(ast: ExpressionAstNode, input: T): Observable<unknown>;
|
|||
|
||||
<b>Returns:</b>
|
||||
|
||||
`Observable<unknown>`
|
||||
`Observable<ExecutionResult<unknown>>`
|
||||
|
||||
|
|
|
@ -26,8 +26,8 @@ export declare class Execution<Input = unknown, Output = unknown, InspectorAdapt
|
|||
| [expression](./kibana-plugin-plugins-expressions-server.execution.expression.md) | | <code>string</code> | |
|
||||
| [input](./kibana-plugin-plugins-expressions-server.execution.input.md) | | <code>Input</code> | Initial input of the execution.<!-- -->N.B. It is initialized to <code>null</code> rather than <code>undefined</code> for legacy reasons, because in legacy interpreter it was set to <code>null</code> by default. |
|
||||
| [inspectorAdapters](./kibana-plugin-plugins-expressions-server.execution.inspectoradapters.md) | | <code>InspectorAdapters</code> | |
|
||||
| [result](./kibana-plugin-plugins-expressions-server.execution.result.md) | | <code>Observable<Output | ExpressionValueError></code> | Future that tracks result or error of this execution. |
|
||||
| [state](./kibana-plugin-plugins-expressions-server.execution.state.md) | | <code>ExecutionContainer<Output | ExpressionValueError></code> | Dynamic state of the execution. |
|
||||
| [result](./kibana-plugin-plugins-expressions-server.execution.result.md) | | <code>Observable<ExecutionResult<Output | ExpressionValueError>></code> | Future that tracks result or error of this execution. |
|
||||
| [state](./kibana-plugin-plugins-expressions-server.execution.state.md) | | <code>ExecutionContainer<ExecutionResult<Output | ExpressionValueError>></code> | Dynamic state of the execution. |
|
||||
|
||||
## Methods
|
||||
|
||||
|
|
|
@ -9,5 +9,5 @@ Future that tracks result or error of this execution.
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
readonly result: Observable<Output | ExpressionValueError>;
|
||||
readonly result: Observable<ExecutionResult<Output | ExpressionValueError>>;
|
||||
```
|
||||
|
|
|
@ -11,7 +11,7 @@ N.B. `input` is initialized to `null` rather than `undefined` for legacy reasons
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
start(input?: Input): Observable<Output | ExpressionValueError>;
|
||||
start(input?: Input): Observable<ExecutionResult<Output | ExpressionValueError>>;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
@ -22,5 +22,5 @@ start(input?: Input): Observable<Output | ExpressionValueError>;
|
|||
|
||||
<b>Returns:</b>
|
||||
|
||||
`Observable<Output | ExpressionValueError>`
|
||||
`Observable<ExecutionResult<Output | ExpressionValueError>>`
|
||||
|
||||
|
|
|
@ -9,5 +9,5 @@ Dynamic state of the execution.
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
readonly state: ExecutionContainer<Output | ExpressionValueError>;
|
||||
readonly state: ExecutionContainer<ExecutionResult<Output | ExpressionValueError>>;
|
||||
```
|
||||
|
|
|
@ -9,7 +9,7 @@ Execute expression and return result.
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
run<Input, Output>(ast: string | ExpressionAstExpression, input: Input, params?: ExpressionExecutionParams): Observable<Output | ExpressionValueError>;
|
||||
run<Input, Output>(ast: string | ExpressionAstExpression, input: Input, params?: ExpressionExecutionParams): Observable<ExecutionResult<Output | ExpressionValueError>>;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
@ -22,5 +22,5 @@ run<Input, Output>(ast: string | ExpressionAstExpression, input: Input, params?:
|
|||
|
||||
<b>Returns:</b>
|
||||
|
||||
`Observable<Output | ExpressionValueError>`
|
||||
`Observable<ExecutionResult<Output | ExpressionValueError>>`
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import { pluck } from 'rxjs/operators';
|
||||
import {
|
||||
EuiCodeBlock,
|
||||
EuiFlexItem,
|
||||
|
@ -35,7 +36,7 @@ interface Props {
|
|||
|
||||
export function RunExpressionsExample({ expressions, inspector }: Props) {
|
||||
const [expression, updateExpression] = useState('markdown "## expressions explorer"');
|
||||
const [result, updateResult] = useState({});
|
||||
const [result, updateResult] = useState<unknown>({});
|
||||
|
||||
const expressionChanged = (value: string) => {
|
||||
updateExpression(value);
|
||||
|
@ -49,17 +50,13 @@ export function RunExpressionsExample({ expressions, inspector }: Props) {
|
|||
);
|
||||
|
||||
useEffect(() => {
|
||||
const runExpression = async () => {
|
||||
const execution = expressions.execute(expression, null, {
|
||||
debug: true,
|
||||
inspectorAdapters,
|
||||
});
|
||||
const execution = expressions.execute(expression, null, {
|
||||
debug: true,
|
||||
inspectorAdapters,
|
||||
});
|
||||
const subscription = execution.getData().pipe(pluck('result')).subscribe(updateResult);
|
||||
|
||||
const data: any = await execution.getData();
|
||||
updateResult(data);
|
||||
};
|
||||
|
||||
runExpression();
|
||||
return () => subscription.unsubscribe();
|
||||
}, [expression, expressions, inspectorAdapters]);
|
||||
|
||||
return (
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { first } from 'rxjs/operators';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import { Execution } from './execution';
|
||||
import { parseExpression } from '../ast';
|
||||
|
@ -40,9 +39,9 @@ describe('Execution abortion tests', () => {
|
|||
execution.start();
|
||||
execution.cancel();
|
||||
|
||||
const result = await execution.result.pipe(first()).toPromise();
|
||||
const result = await execution.result.toPromise();
|
||||
|
||||
expect(result).toMatchObject({
|
||||
expect(result).toHaveProperty('result', {
|
||||
type: 'error',
|
||||
error: {
|
||||
message: 'The expression was aborted.',
|
||||
|
@ -58,9 +57,9 @@ describe('Execution abortion tests', () => {
|
|||
jest.advanceTimersByTime(100);
|
||||
execution.cancel();
|
||||
|
||||
const result = await execution.result.pipe(first()).toPromise();
|
||||
const result = await execution.result.toPromise();
|
||||
|
||||
expect(result).toMatchObject({
|
||||
expect(result).toHaveProperty('result', {
|
||||
type: 'error',
|
||||
error: {
|
||||
message: 'The expression was aborted.',
|
||||
|
@ -76,7 +75,7 @@ describe('Execution abortion tests', () => {
|
|||
|
||||
execution.start();
|
||||
|
||||
const result = await execution.result.pipe(first()).toPromise();
|
||||
const { result } = await execution.result.toPromise();
|
||||
|
||||
execution.cancel();
|
||||
|
||||
|
@ -136,7 +135,7 @@ describe('Execution abortion tests', () => {
|
|||
await waitFor(() => expect(started).toHaveBeenCalledTimes(1));
|
||||
|
||||
execution.cancel();
|
||||
const result = await execution.result.pipe(first()).toPromise();
|
||||
const { result } = await execution.result.toPromise();
|
||||
expect(result).toMatchObject({
|
||||
type: 'error',
|
||||
error: {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import { of } from 'rxjs';
|
||||
import { first, scan } from 'rxjs/operators';
|
||||
import { scan } from 'rxjs/operators';
|
||||
import { TestScheduler } from 'rxjs/testing';
|
||||
import { Execution } from './execution';
|
||||
import { parseExpression, ExpressionAstExpression } from '../ast';
|
||||
|
@ -45,7 +45,7 @@ const run = async (
|
|||
) => {
|
||||
const execution = createExecution(expression, context);
|
||||
execution.start(input);
|
||||
return await execution.result.pipe(first()).toPromise();
|
||||
return await execution.result.toPromise();
|
||||
};
|
||||
|
||||
let testScheduler: TestScheduler;
|
||||
|
@ -84,7 +84,7 @@ describe('Execution', () => {
|
|||
/* eslint-enable no-console */
|
||||
|
||||
execution.start(123);
|
||||
const result = await execution.result.pipe(first()).toPromise();
|
||||
const { result } = await execution.result.toPromise();
|
||||
|
||||
expect(result).toBe(123);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
|
@ -102,7 +102,7 @@ describe('Execution', () => {
|
|||
value: -1,
|
||||
});
|
||||
|
||||
const result = await execution.result.pipe(first()).toPromise();
|
||||
const { result } = await execution.result.toPromise();
|
||||
|
||||
expect(result).toEqual({
|
||||
type: 'num',
|
||||
|
@ -117,7 +117,7 @@ describe('Execution', () => {
|
|||
value: 0,
|
||||
});
|
||||
|
||||
const result = await execution.result.pipe(first()).toPromise();
|
||||
const { result } = await execution.result.toPromise();
|
||||
|
||||
expect(result).toEqual({
|
||||
type: 'num',
|
||||
|
@ -131,7 +131,7 @@ describe('Execution', () => {
|
|||
|
||||
// Below 1 is cast to { type: 'num', value: 1 }.
|
||||
execution.start(1);
|
||||
const result = await execution.result.pipe(first()).toPromise();
|
||||
const { result } = await execution.result.toPromise();
|
||||
|
||||
expect(result).toEqual({
|
||||
type: 'num',
|
||||
|
@ -143,7 +143,7 @@ describe('Execution', () => {
|
|||
const execution = createExecution('add val=1');
|
||||
|
||||
execution.start(Promise.resolve(1));
|
||||
const result = await execution.result.pipe(first()).toPromise();
|
||||
const { result } = await execution.result.toPromise();
|
||||
|
||||
expect(result).toEqual({
|
||||
type: 'num',
|
||||
|
@ -155,7 +155,7 @@ describe('Execution', () => {
|
|||
const execution = createExecution('add val=1');
|
||||
|
||||
execution.start(of(1));
|
||||
const result = await execution.result.pipe(first()).toPromise();
|
||||
const { result } = await execution.result.toPromise();
|
||||
|
||||
expect(result).toEqual({
|
||||
type: 'num',
|
||||
|
@ -167,14 +167,14 @@ describe('Execution', () => {
|
|||
const execution = createExecution('add val=1');
|
||||
|
||||
testScheduler.run(({ cold, expectObservable }) => {
|
||||
const input = cold(' -a--b-c-', { a: 1, b: 2, c: 3 });
|
||||
const input = cold(' -a--b-c|', { a: 1, b: 2, c: 3 });
|
||||
const subscription = ' ---^---!';
|
||||
const expected = ' ---ab-c-';
|
||||
const expected = ' ---ab-c|';
|
||||
|
||||
expectObservable(execution.start(input), subscription).toBe(expected, {
|
||||
a: { type: 'num', value: 2 },
|
||||
b: { type: 'num', value: 3 },
|
||||
c: { type: 'num', value: 4 },
|
||||
a: { partial: false, result: { type: 'num', value: 2 } },
|
||||
b: { partial: false, result: { type: 'num', value: 3 } },
|
||||
c: { partial: false, result: { type: 'num', value: 4 } },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -187,21 +187,21 @@ describe('Execution', () => {
|
|||
const expected = ' -a-#';
|
||||
|
||||
expectObservable(execution.start(input)).toBe(expected, {
|
||||
a: { type: 'num', value: 2 },
|
||||
a: { partial: false, result: { type: 'num', value: 2 } },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('does not complete when input completes', () => {
|
||||
test('completes when input completes', () => {
|
||||
const execution = createExecution('add val=1');
|
||||
|
||||
testScheduler.run(({ cold, expectObservable }) => {
|
||||
const input = cold('-a-b|', { a: 1, b: 2 });
|
||||
const expected = ' -a-b-';
|
||||
const expected = ' -a-b|';
|
||||
|
||||
expectObservable(execution.start(input)).toBe(expected, {
|
||||
a: { type: 'num', value: 2 },
|
||||
b: { type: 'num', value: 3 },
|
||||
a: expect.objectContaining({ result: { type: 'num', value: 2 } }),
|
||||
b: expect.objectContaining({ result: { type: 'num', value: 3 } }),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -216,9 +216,9 @@ describe('Execution', () => {
|
|||
const input = items.pipe(scan((result, value) => [...result, value], new Array<number>()));
|
||||
|
||||
expectObservable(execution.start(input), subscription).toBe(expected, {
|
||||
a: { type: 'num', value: 1 },
|
||||
b: { type: 'num', value: 3 },
|
||||
c: { type: 'num', value: 6 },
|
||||
a: { partial: false, result: { type: 'num', value: 1 } },
|
||||
b: { partial: false, result: { type: 'num', value: 3 } },
|
||||
c: { partial: false, result: { type: 'num', value: 6 } },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -263,44 +263,51 @@ describe('Execution', () => {
|
|||
describe('execution context', () => {
|
||||
test('context.variables is an object', async () => {
|
||||
const { result } = (await run('introspectContext key="variables"')) as any;
|
||||
expect(typeof result).toBe('object');
|
||||
|
||||
expect(result).toHaveProperty('result', expect.any(Object));
|
||||
});
|
||||
|
||||
test('context.types is an object', async () => {
|
||||
const { result } = (await run('introspectContext key="types"')) as any;
|
||||
expect(typeof result).toBe('object');
|
||||
|
||||
expect(result).toHaveProperty('result', expect.any(Object));
|
||||
});
|
||||
|
||||
test('context.abortSignal is an object', async () => {
|
||||
const { result } = (await run('introspectContext key="abortSignal"')) as any;
|
||||
expect(typeof result).toBe('object');
|
||||
|
||||
expect(result).toHaveProperty('result', expect.any(Object));
|
||||
});
|
||||
|
||||
test('context.inspectorAdapters is an object', async () => {
|
||||
const { result } = (await run('introspectContext key="inspectorAdapters"')) as any;
|
||||
expect(typeof result).toBe('object');
|
||||
|
||||
expect(result).toHaveProperty('result', expect.any(Object));
|
||||
});
|
||||
|
||||
test('context.getKibanaRequest is a function if provided', async () => {
|
||||
const { result } = (await run('introspectContext key="getKibanaRequest"', {
|
||||
kibanaRequest: {},
|
||||
})) as any;
|
||||
expect(typeof result).toBe('function');
|
||||
|
||||
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;
|
||||
expect(typeof result).toBe('undefined');
|
||||
|
||||
expect(result).toHaveProperty('result', undefined);
|
||||
});
|
||||
|
||||
test('unknown context key is undefined', async () => {
|
||||
const { result } = (await run('introspectContext key="foo"')) as any;
|
||||
expect(typeof result).toBe('undefined');
|
||||
|
||||
expect(result).toHaveProperty('result', undefined);
|
||||
});
|
||||
|
||||
test('can set context variables', async () => {
|
||||
const variables = { foo: 'bar' };
|
||||
const result = await run('var name="foo"', { variables });
|
||||
const { result } = await run('var name="foo"', { variables });
|
||||
expect(result).toBe('bar');
|
||||
});
|
||||
});
|
||||
|
@ -308,10 +315,13 @@ 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;
|
||||
expect(result).toMatchObject({
|
||||
tables: expect.any(Object),
|
||||
requests: expect.any(Object),
|
||||
});
|
||||
expect(result).toHaveProperty(
|
||||
'result',
|
||||
expect.objectContaining({
|
||||
tables: expect.any(Object),
|
||||
requests: expect.any(Object),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('can set custom inspector adapters', async () => {
|
||||
|
@ -319,7 +329,7 @@ describe('Execution', () => {
|
|||
const { result } = (await run('introspectContext key="inspectorAdapters"', {
|
||||
inspectorAdapters,
|
||||
})) as any;
|
||||
expect(result).toBe(inspectorAdapters);
|
||||
expect(result).toHaveProperty('result', inspectorAdapters);
|
||||
});
|
||||
|
||||
test('can access custom inspector adapters on Execution object', async () => {
|
||||
|
@ -335,8 +345,7 @@ describe('Execution', () => {
|
|||
test('context has abortSignal object', async () => {
|
||||
const { result } = (await run('introspectContext key="abortSignal"')) as any;
|
||||
|
||||
expect(typeof result).toBe('object');
|
||||
expect((result as AbortSignal).aborted).toBe(false);
|
||||
expect(result).toHaveProperty('result.aborted', false);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -348,7 +357,7 @@ describe('Execution', () => {
|
|||
value: 0,
|
||||
});
|
||||
|
||||
const result = await execution.result.pipe(first()).toPromise();
|
||||
const { result } = await execution.result.toPromise();
|
||||
|
||||
expect(result).toEqual({
|
||||
type: 'num',
|
||||
|
@ -357,8 +366,8 @@ describe('Execution', () => {
|
|||
});
|
||||
|
||||
test('can execute async functions', async () => {
|
||||
const res = await run('sleep 10 | sleep 10');
|
||||
expect(res).toBe(null);
|
||||
const { result } = await run('sleep 10 | sleep 10');
|
||||
expect(result).toBe(null);
|
||||
});
|
||||
|
||||
test('result is undefined until execution completes', async () => {
|
||||
|
@ -374,7 +383,7 @@ describe('Execution', () => {
|
|||
|
||||
jest.advanceTimersByTime(10);
|
||||
await new Promise(process.nextTick);
|
||||
expect(execution.state.get().result).toBe(null);
|
||||
expect(execution.state.get().result).toHaveProperty('result', null);
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
@ -382,7 +391,7 @@ describe('Execution', () => {
|
|||
test('handles functions returning observables', () => {
|
||||
testScheduler.run(({ cold, expectObservable }) => {
|
||||
const arg = cold(' -a-b-c|', { a: 1, b: 2, c: 3 });
|
||||
const expected = ' -a-b-c-';
|
||||
const expected = ' -a-b-c|';
|
||||
const observable: ExpressionFunctionDefinition<'observable', any, {}, any> = {
|
||||
name: 'observable',
|
||||
args: {},
|
||||
|
@ -394,14 +403,18 @@ describe('Execution', () => {
|
|||
|
||||
const result = executor.run('observable', null, {});
|
||||
|
||||
expectObservable(result).toBe(expected, { a: 1, b: 2, c: 3 });
|
||||
expectObservable(result).toBe(expected, {
|
||||
a: { result: 1, partial: true },
|
||||
b: { result: 2, partial: true },
|
||||
c: { result: 3, partial: false },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when function throws', () => {
|
||||
test('error is reported in output object', async () => {
|
||||
const result = await run('error "foobar"');
|
||||
const { result } = await run('error "foobar"');
|
||||
|
||||
expect(result).toMatchObject({
|
||||
type: 'error',
|
||||
|
@ -409,7 +422,7 @@ describe('Execution', () => {
|
|||
});
|
||||
|
||||
test('error message is prefixed with function name', async () => {
|
||||
const result = await run('error "foobar"');
|
||||
const { result } = await run('error "foobar"');
|
||||
|
||||
expect(result).toMatchObject({
|
||||
error: {
|
||||
|
@ -419,7 +432,7 @@ describe('Execution', () => {
|
|||
});
|
||||
|
||||
test('returns error of the first function that throws', async () => {
|
||||
const result = await run('error "foo" | error "bar"');
|
||||
const { result } = await run('error "foo" | error "bar"');
|
||||
|
||||
expect(result).toMatchObject({
|
||||
error: {
|
||||
|
@ -432,15 +445,18 @@ describe('Execution', () => {
|
|||
const execution = await createExecution('error "foo"');
|
||||
execution.start(null);
|
||||
|
||||
const result = await execution.result.pipe(first()).toPromise();
|
||||
const { result } = await execution.result.toPromise();
|
||||
|
||||
expect(result).toMatchObject({
|
||||
type: 'error',
|
||||
});
|
||||
expect(execution.state.get().state).toBe('result');
|
||||
expect(execution.state.get().result).toMatchObject({
|
||||
type: 'error',
|
||||
});
|
||||
expect(execution.state.get().result).toHaveProperty(
|
||||
'result',
|
||||
expect.objectContaining({
|
||||
type: 'error',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('does not execute remaining functions in pipeline', async () => {
|
||||
|
@ -453,7 +469,7 @@ describe('Execution', () => {
|
|||
const executor = createUnitTestExecutor();
|
||||
executor.registerFunction(spy);
|
||||
|
||||
await executor.run('error "..." | spy', null).pipe(first()).toPromise();
|
||||
await executor.run('error "..." | spy', null).toPromise();
|
||||
|
||||
expect(spy.fn).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
@ -483,21 +499,21 @@ describe('Execution', () => {
|
|||
test('execution state is "result" when execution successfully completes', async () => {
|
||||
const execution = createExecution('sleep 1');
|
||||
execution.start(null);
|
||||
await execution.result.pipe(first()).toPromise();
|
||||
await execution.result.toPromise();
|
||||
expect(execution.state.get().state).toBe('result');
|
||||
});
|
||||
|
||||
test('execution state is "result" when execution successfully completes - 2', async () => {
|
||||
const execution = createExecution('var foo');
|
||||
execution.start(null);
|
||||
await execution.result.pipe(first()).toPromise();
|
||||
await execution.result.toPromise();
|
||||
expect(execution.state.get().state).toBe('result');
|
||||
});
|
||||
});
|
||||
|
||||
describe('sub-expressions', () => {
|
||||
test('executes sub-expressions', async () => {
|
||||
const result = await run('add val={add 5 | access "value"}', {}, null);
|
||||
const { result } = await run('add val={add 5 | access "value"}', {}, null);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
type: 'num',
|
||||
|
@ -506,7 +522,7 @@ describe('Execution', () => {
|
|||
});
|
||||
|
||||
test('can use global variables', async () => {
|
||||
const result = await run(
|
||||
const { result } = await run(
|
||||
'add val={var foo}',
|
||||
{
|
||||
variables: {
|
||||
|
@ -523,7 +539,7 @@ describe('Execution', () => {
|
|||
});
|
||||
|
||||
test('can modify global variables', async () => {
|
||||
const result = await run(
|
||||
const { result } = await run(
|
||||
'add val={var_set name=foo value=66 | var bar} | var foo',
|
||||
{
|
||||
variables: {
|
||||
|
@ -547,18 +563,20 @@ describe('Execution', () => {
|
|||
const executor = createUnitTestExecutor();
|
||||
executor.registerFunction(observable);
|
||||
|
||||
expect(
|
||||
executor.run('add val={observable}', 1, {}).pipe(first()).toPromise()
|
||||
).resolves.toEqual({
|
||||
type: 'num',
|
||||
value: 2,
|
||||
});
|
||||
expect(executor.run('add val={observable}', 1, {}).toPromise()).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
result: {
|
||||
type: 'num',
|
||||
value: 2,
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('supports observables in arguments emitting multiple values', () => {
|
||||
testScheduler.run(({ cold, expectObservable }) => {
|
||||
const arg = cold('-a-b-c-', { a: 1, b: 2, c: 3 });
|
||||
const expected = '-a-b-c-';
|
||||
const arg = cold('-a-b-c|', { a: 1, b: 2, c: 3 });
|
||||
const expected = '-a-b-c|';
|
||||
const observable = {
|
||||
name: 'observable',
|
||||
args: {},
|
||||
|
@ -571,18 +589,18 @@ describe('Execution', () => {
|
|||
const result = executor.run('add val={observable}', 1, {});
|
||||
|
||||
expectObservable(result).toBe(expected, {
|
||||
a: { type: 'num', value: 2 },
|
||||
b: { type: 'num', value: 3 },
|
||||
c: { type: 'num', value: 4 },
|
||||
a: { partial: true, result: { type: 'num', value: 2 } },
|
||||
b: { partial: true, result: { type: 'num', value: 3 } },
|
||||
c: { partial: false, result: { type: 'num', value: 4 } },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('combines multiple observables in arguments', () => {
|
||||
testScheduler.run(({ cold, expectObservable }) => {
|
||||
const arg1 = cold('--ab-c-', { a: 0, b: 2, c: 4 });
|
||||
const arg2 = cold('-a--bc-', { a: 1, b: 3, c: 5 });
|
||||
const expected = ' --abc(de)-';
|
||||
const arg1 = cold('--ab-c---|', { a: 0, b: 2, c: 4 });
|
||||
const arg2 = cold('-a--bc---|', { a: 1, b: 3, c: 5 });
|
||||
const expected = ' --abc(de)|';
|
||||
const observable1 = {
|
||||
name: 'observable1',
|
||||
args: {},
|
||||
|
@ -612,32 +630,11 @@ describe('Execution', () => {
|
|||
const result = executor.run('max val1={observable1} val2={observable2}', {});
|
||||
|
||||
expectObservable(result).toBe(expected, {
|
||||
a: { type: 'num', value: 1 },
|
||||
b: { type: 'num', value: 2 },
|
||||
c: { type: 'num', value: 3 },
|
||||
d: { type: 'num', value: 4 },
|
||||
e: { type: 'num', value: 5 },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('does not complete when an argument completes', () => {
|
||||
testScheduler.run(({ cold, expectObservable }) => {
|
||||
const arg = cold('-a|', { a: 1 });
|
||||
const expected = '-a-';
|
||||
const observable = {
|
||||
name: 'observable',
|
||||
args: {},
|
||||
help: '',
|
||||
fn: () => arg,
|
||||
};
|
||||
const executor = createUnitTestExecutor();
|
||||
executor.registerFunction(observable);
|
||||
|
||||
const result = executor.run('add val={observable}', 1, {});
|
||||
|
||||
expectObservable(result).toBe(expected, {
|
||||
a: { type: 'num', value: 2 },
|
||||
a: { partial: true, result: { type: 'num', value: 1 } },
|
||||
b: { partial: true, result: { type: 'num', value: 2 } },
|
||||
c: { partial: true, result: { type: 'num', value: 3 } },
|
||||
d: { partial: true, result: { type: 'num', value: 4 } },
|
||||
e: { partial: false, result: { type: 'num', value: 5 } },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -645,7 +642,7 @@ describe('Execution', () => {
|
|||
test('handles error in observable arguments', () => {
|
||||
testScheduler.run(({ cold, expectObservable }) => {
|
||||
const arg = cold('-a-#', { a: 1 }, new Error('some error'));
|
||||
const expected = '-a-b';
|
||||
const expected = '-a-(b|)';
|
||||
const observable = {
|
||||
name: 'observable',
|
||||
args: {},
|
||||
|
@ -658,13 +655,15 @@ describe('Execution', () => {
|
|||
const result = executor.run('add val={observable}', 1, {});
|
||||
|
||||
expectObservable(result).toBe(expected, {
|
||||
a: { type: 'num', value: 2 },
|
||||
b: {
|
||||
error: expect.objectContaining({
|
||||
message: '[add] > [observable] > some error',
|
||||
}),
|
||||
type: 'error',
|
||||
},
|
||||
a: expect.objectContaining({ result: { type: 'num', value: 2 } }),
|
||||
b: expect.objectContaining({
|
||||
result: {
|
||||
error: expect.objectContaining({
|
||||
message: '[add] > [observable] > some error',
|
||||
}),
|
||||
type: 'error',
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -685,7 +684,7 @@ describe('Execution', () => {
|
|||
};
|
||||
const executor = createUnitTestExecutor();
|
||||
executor.registerFunction(requiredArg);
|
||||
const result = await executor.run('requiredArg', null, {}).pipe(first()).toPromise();
|
||||
const { result } = await executor.run('requiredArg', null, {}).toPromise();
|
||||
|
||||
expect(result).toMatchObject({
|
||||
type: 'error',
|
||||
|
@ -696,7 +695,7 @@ describe('Execution', () => {
|
|||
});
|
||||
|
||||
test('when required argument is missing and has alias, returns error', async () => {
|
||||
const result = await run('var_set', {});
|
||||
const { result } = await run('var_set', {});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
type: 'error',
|
||||
|
@ -711,7 +710,7 @@ describe('Execution', () => {
|
|||
test('can execute expression in debug mode', async () => {
|
||||
const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true);
|
||||
execution.start(-1);
|
||||
const result = await execution.result.pipe(first()).toPromise();
|
||||
const { result } = await execution.result.toPromise();
|
||||
|
||||
expect(result).toEqual({
|
||||
type: 'num',
|
||||
|
@ -726,7 +725,7 @@ describe('Execution', () => {
|
|||
true
|
||||
);
|
||||
execution.start(0);
|
||||
const result = await execution.result.pipe(first()).toPromise();
|
||||
const { result } = await execution.result.toPromise();
|
||||
|
||||
expect(result).toEqual({
|
||||
type: 'num',
|
||||
|
@ -738,7 +737,7 @@ describe('Execution', () => {
|
|||
test('sets "success" flag on all functions to true', async () => {
|
||||
const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true);
|
||||
execution.start(-1);
|
||||
await execution.result.pipe(first()).toPromise();
|
||||
await execution.result.toPromise();
|
||||
|
||||
for (const node of execution.state.get().ast.chain) {
|
||||
expect(node.debug?.success).toBe(true);
|
||||
|
@ -748,7 +747,7 @@ describe('Execution', () => {
|
|||
test('stores "fn" reference to the function', async () => {
|
||||
const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true);
|
||||
execution.start(-1);
|
||||
await execution.result.pipe(first()).toPromise();
|
||||
await execution.result.toPromise();
|
||||
|
||||
for (const node of execution.state.get().ast.chain) {
|
||||
expect(node.debug?.fn).toBe('add');
|
||||
|
@ -758,7 +757,7 @@ describe('Execution', () => {
|
|||
test('saves duration it took to execute each function', async () => {
|
||||
const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true);
|
||||
execution.start(-1);
|
||||
await execution.result.pipe(first()).toPromise();
|
||||
await execution.result.toPromise();
|
||||
|
||||
for (const node of execution.state.get().ast.chain) {
|
||||
expect(typeof node.debug?.duration).toBe('number');
|
||||
|
@ -770,7 +769,7 @@ describe('Execution', () => {
|
|||
test('adds .debug field in expression AST on each executed function', async () => {
|
||||
const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true);
|
||||
execution.start(-1);
|
||||
await execution.result.pipe(first()).toPromise();
|
||||
await execution.result.toPromise();
|
||||
|
||||
for (const node of execution.state.get().ast.chain) {
|
||||
expect(typeof node.debug).toBe('object');
|
||||
|
@ -781,7 +780,7 @@ describe('Execution', () => {
|
|||
test('stores input of each function', async () => {
|
||||
const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true);
|
||||
execution.start(-1);
|
||||
await execution.result.pipe(first()).toPromise();
|
||||
await execution.result.toPromise();
|
||||
|
||||
const { chain } = execution.state.get().ast;
|
||||
|
||||
|
@ -799,7 +798,7 @@ describe('Execution', () => {
|
|||
test('stores output of each function', async () => {
|
||||
const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true);
|
||||
execution.start(-1);
|
||||
await execution.result.pipe(first()).toPromise();
|
||||
await execution.result.toPromise();
|
||||
|
||||
const { chain } = execution.state.get().ast;
|
||||
|
||||
|
@ -824,7 +823,7 @@ describe('Execution', () => {
|
|||
true
|
||||
);
|
||||
execution.start(-1);
|
||||
await execution.result.pipe(first()).toPromise();
|
||||
await execution.result.toPromise();
|
||||
|
||||
const { chain } = execution.state.get().ast;
|
||||
|
||||
|
@ -847,7 +846,7 @@ describe('Execution', () => {
|
|||
true
|
||||
);
|
||||
execution.start(0);
|
||||
await execution.result.pipe(first()).toPromise();
|
||||
await execution.result.toPromise();
|
||||
|
||||
const { chain } = execution.state.get().ast.chain[0].arguments
|
||||
.val[0] as ExpressionAstExpression;
|
||||
|
@ -882,7 +881,7 @@ describe('Execution', () => {
|
|||
params: { debug: true },
|
||||
});
|
||||
execution.start(0);
|
||||
await execution.result.pipe(first()).toPromise();
|
||||
await execution.result.toPromise();
|
||||
|
||||
const node1 = execution.state.get().ast.chain[0];
|
||||
const node2 = execution.state.get().ast.chain[1];
|
||||
|
@ -900,7 +899,7 @@ describe('Execution', () => {
|
|||
params: { debug: true },
|
||||
});
|
||||
execution.start(0);
|
||||
await execution.result.pipe(first()).toPromise();
|
||||
await execution.result.toPromise();
|
||||
|
||||
const node2 = execution.state.get().ast.chain[1];
|
||||
|
||||
|
@ -921,7 +920,7 @@ describe('Execution', () => {
|
|||
params: { debug: true },
|
||||
});
|
||||
execution.start(0);
|
||||
await execution.result.pipe(first()).toPromise();
|
||||
await execution.result.toPromise();
|
||||
|
||||
const node2 = execution.state.get().ast.chain[1];
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import {
|
|||
Observable,
|
||||
ReplaySubject,
|
||||
} from 'rxjs';
|
||||
import { catchError, finalize, map, shareReplay, switchMap, tap } from 'rxjs/operators';
|
||||
import { catchError, finalize, map, pluck, shareReplay, switchMap, tap } from 'rxjs/operators';
|
||||
import { Executor } from '../executor';
|
||||
import { createExecutionContainer, ExecutionContainer } from './container';
|
||||
import { createError } from '../util';
|
||||
|
@ -45,6 +45,21 @@ import { ExpressionExecutionParams } from '../service';
|
|||
import { TablesAdapter } from '../util/tables_adapter';
|
||||
import { ExpressionsInspectorAdapter } from '../util/expressions_inspector_adapter';
|
||||
|
||||
/**
|
||||
* The result returned after an expression function execution.
|
||||
*/
|
||||
export interface ExecutionResult<Output> {
|
||||
/**
|
||||
* Partial result flag.
|
||||
*/
|
||||
partial: boolean;
|
||||
|
||||
/**
|
||||
* The expression function result.
|
||||
*/
|
||||
result: Output;
|
||||
}
|
||||
|
||||
/**
|
||||
* AbortController is not available in Node until v15, so we
|
||||
* need to temporarily mock it for plugins using expressions
|
||||
|
@ -91,7 +106,7 @@ export class Execution<
|
|||
/**
|
||||
* Dynamic state of the execution.
|
||||
*/
|
||||
public readonly state: ExecutionContainer<Output | ExpressionValueError>;
|
||||
public readonly state: ExecutionContainer<ExecutionResult<Output | ExpressionValueError>>;
|
||||
|
||||
/**
|
||||
* Initial input of the execution.
|
||||
|
@ -137,7 +152,7 @@ export class Execution<
|
|||
/**
|
||||
* Future that tracks result or error of this execution.
|
||||
*/
|
||||
public readonly result: Observable<Output | ExpressionValueError>;
|
||||
public readonly result: Observable<ExecutionResult<Output | ExpressionValueError>>;
|
||||
|
||||
/**
|
||||
* Keeping track of any child executions
|
||||
|
@ -174,7 +189,7 @@ export class Execution<
|
|||
this.expression = execution.expression || formatExpression(execution.ast!);
|
||||
const ast = execution.ast || parseExpression(this.expression);
|
||||
|
||||
this.state = createExecutionContainer<Output | ExpressionValueError>({
|
||||
this.state = createExecutionContainer({
|
||||
...executor.state.get(),
|
||||
state: 'not-started',
|
||||
ast,
|
||||
|
@ -201,12 +216,40 @@ export class Execution<
|
|||
};
|
||||
|
||||
this.result = this.input$.pipe(
|
||||
switchMap((input) => this.race(this.invokeChain(this.state.get().ast.chain, input))),
|
||||
switchMap((input) =>
|
||||
this.race(this.invokeChain(this.state.get().ast.chain, input)).pipe(
|
||||
(source) =>
|
||||
new Observable<ExecutionResult<Output>>((subscriber) => {
|
||||
let latest: ExecutionResult<Output> | undefined;
|
||||
|
||||
subscriber.add(
|
||||
source.subscribe({
|
||||
next: (result) => {
|
||||
latest = { result, partial: true };
|
||||
subscriber.next(latest);
|
||||
},
|
||||
error: (error) => subscriber.error(error),
|
||||
complete: () => {
|
||||
if (latest) {
|
||||
latest.partial = false;
|
||||
}
|
||||
|
||||
subscriber.complete();
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
subscriber.add(() => {
|
||||
latest = undefined;
|
||||
});
|
||||
})
|
||||
)
|
||||
),
|
||||
catchError((error) => {
|
||||
if (this.abortController.signal.aborted) {
|
||||
this.childExecutions.forEach((childExecution) => childExecution.cancel());
|
||||
|
||||
return of(createAbortErrorValue());
|
||||
return of({ result: createAbortErrorValue(), partial: false });
|
||||
}
|
||||
|
||||
return throwError(error);
|
||||
|
@ -236,25 +279,20 @@ 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): Observable<Output | ExpressionValueError> {
|
||||
public start(
|
||||
input: Input = null as any
|
||||
): Observable<ExecutionResult<Output | ExpressionValueError>> {
|
||||
if (this.hasStarted) throw new Error('Execution already started.');
|
||||
this.hasStarted = true;
|
||||
this.input = input;
|
||||
this.state.transitions.start();
|
||||
|
||||
if (isObservable<Input>(input)) {
|
||||
// `input$` should never complete
|
||||
input.subscribe(
|
||||
(value) => this.input$.next(value),
|
||||
(error) => this.input$.error(error)
|
||||
);
|
||||
input.subscribe(this.input$);
|
||||
} else if (isPromise(input)) {
|
||||
input.then(
|
||||
(value) => this.input$.next(value),
|
||||
(error) => this.input$.error(error)
|
||||
);
|
||||
from(input).subscribe(this.input$);
|
||||
} else {
|
||||
this.input$.next(input);
|
||||
of(input).subscribe(this.input$);
|
||||
}
|
||||
|
||||
return this.result;
|
||||
|
@ -439,6 +477,7 @@ export class Execution<
|
|||
const resolveArgFns = mapValues(dealiasedArgAsts, (asts, argName) =>
|
||||
asts.map((item) => (subInput = input) =>
|
||||
this.interpret(item, subInput).pipe(
|
||||
pluck('result'),
|
||||
map((output) => {
|
||||
if (isExpressionValueError(output)) {
|
||||
throw output.error;
|
||||
|
@ -486,7 +525,7 @@ export class Execution<
|
|||
});
|
||||
}
|
||||
|
||||
public interpret<T>(ast: ExpressionAstNode, input: T): Observable<unknown> {
|
||||
public interpret<T>(ast: ExpressionAstNode, input: T): Observable<ExecutionResult<unknown>> {
|
||||
switch (getType(ast)) {
|
||||
case 'expression':
|
||||
const execution = this.execution.executor.createExecution(
|
||||
|
@ -494,12 +533,13 @@ export class Execution<
|
|||
this.execution.params
|
||||
);
|
||||
this.childExecutions.push(execution);
|
||||
|
||||
return execution.start(input);
|
||||
case 'string':
|
||||
case 'number':
|
||||
case 'null':
|
||||
case 'boolean':
|
||||
return of(ast);
|
||||
return of({ result: ast, partial: false });
|
||||
default:
|
||||
return throwError(new Error(`Unknown AST object: ${JSON.stringify(ast)}`));
|
||||
}
|
||||
|
|
|
@ -66,26 +66,23 @@ describe('ExecutionContract', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('can get error result of the expression execution', async () => {
|
||||
test('can get error result of the expression execution', () => {
|
||||
const execution = createExecution('foo bar=123');
|
||||
const contract = new ExecutionContract(execution);
|
||||
execution.start();
|
||||
|
||||
const result = await contract.getData();
|
||||
|
||||
expect(result).toMatchObject({
|
||||
type: 'error',
|
||||
});
|
||||
expect(contract.getData().toPromise()).resolves.toHaveProperty(
|
||||
'result',
|
||||
expect.objectContaining({ type: 'error' })
|
||||
);
|
||||
});
|
||||
|
||||
test('can get result of the expression execution', async () => {
|
||||
test('can get result of the expression execution', () => {
|
||||
const execution = createExecution('var_set name="foo" value="bar" | var name="foo"');
|
||||
const contract = new ExecutionContract(execution);
|
||||
execution.start();
|
||||
|
||||
const result = await contract.getData();
|
||||
|
||||
expect(result).toBe('bar');
|
||||
expect(contract.getData().toPromise()).resolves.toHaveProperty('result', 'bar');
|
||||
});
|
||||
|
||||
describe('isPending', () => {
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { of } from 'rxjs';
|
||||
import { catchError, take } from 'rxjs/operators';
|
||||
import { Execution } from './execution';
|
||||
import { of, Observable } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { Execution, ExecutionResult } from './execution';
|
||||
import { ExpressionValueError } from '../expression_types/specs';
|
||||
import { ExpressionAstExpression } from '../ast';
|
||||
|
||||
|
@ -39,22 +39,22 @@ export class ExecutionContract<Input = unknown, Output = unknown, InspectorAdapt
|
|||
* wraps that error into `ExpressionValueError` type and returns that.
|
||||
* This function never throws.
|
||||
*/
|
||||
getData = async (): Promise<Output | ExpressionValueError> => {
|
||||
return this.execution.result
|
||||
.pipe(
|
||||
take(1),
|
||||
catchError(({ name, message, stack }) =>
|
||||
of({
|
||||
getData = (): Observable<ExecutionResult<Output | ExpressionValueError>> => {
|
||||
return this.execution.result.pipe(
|
||||
catchError(({ name, message, stack }) =>
|
||||
of({
|
||||
partial: false,
|
||||
result: {
|
||||
type: 'error',
|
||||
error: {
|
||||
name,
|
||||
message,
|
||||
stack,
|
||||
},
|
||||
} as ExpressionValueError)
|
||||
)
|
||||
} as ExpressionValueError,
|
||||
})
|
||||
)
|
||||
.toPromise();
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,7 +13,7 @@ import { Observable } from 'rxjs';
|
|||
import { ExecutorState, ExecutorContainer } from './container';
|
||||
import { createExecutorContainer } from './container';
|
||||
import { AnyExpressionFunctionDefinition, ExpressionFunction } from '../expression_functions';
|
||||
import { Execution, ExecutionParams } from '../execution/execution';
|
||||
import { Execution, ExecutionParams, ExecutionResult } from '../execution/execution';
|
||||
import { IRegistry } from '../types';
|
||||
import { ExpressionType } from '../expression_types/expression_type';
|
||||
import { AnyExpressionTypeDefinition } from '../expression_types/types';
|
||||
|
@ -160,7 +160,7 @@ export class Executor<Context extends Record<string, unknown> = Record<string, u
|
|||
ast: string | ExpressionAstExpression,
|
||||
input: Input,
|
||||
params: ExpressionExecutionParams = {}
|
||||
): Observable<Output | ExpressionValueError> {
|
||||
): Observable<ExecutionResult<Output | ExpressionValueError>> {
|
||||
return this.createExecution<Input, Output>(ast, params).start(input);
|
||||
}
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ describe('expression_functions', () => {
|
|||
|
||||
it('sets the variables', async () => {
|
||||
const vars = {};
|
||||
const result = await executor
|
||||
const { result } = await executor
|
||||
.run('var_set name=test1 name=test2 value=1', 2, { variables: vars })
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
|
|
@ -114,7 +114,7 @@ describe('ExpressionsService', () => {
|
|||
},
|
||||
});
|
||||
|
||||
const result = await fork.run('__test__', null);
|
||||
const { result } = await fork.run('__test__', null).toPromise();
|
||||
|
||||
expect(result).toBe('123');
|
||||
});
|
||||
|
|
|
@ -6,15 +6,15 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { take } from 'rxjs/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import type { KibanaRequest } from 'src/core/server';
|
||||
|
||||
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 { ExecutionContract, ExecutionResult } from '../execution';
|
||||
import { AnyExpressionTypeDefinition, ExpressionValueError } from '../expression_types';
|
||||
import { AnyExpressionFunctionDefinition } from '../expression_functions';
|
||||
import { SavedObjectReference } from '../../../../core/types';
|
||||
import { PersistableStateService, SerializableState } from '../../../kibana_utils/common';
|
||||
|
@ -136,7 +136,7 @@ export interface ExpressionsServiceStart {
|
|||
ast: string | ExpressionAstExpression,
|
||||
input: Input,
|
||||
params?: ExpressionExecutionParams
|
||||
) => Promise<Output>;
|
||||
) => Observable<ExecutionResult<Output | ExpressionValueError>>;
|
||||
|
||||
/**
|
||||
* Starts expression execution and immediately returns `ExecutionContract`
|
||||
|
@ -243,7 +243,7 @@ export class ExpressionsService implements PersistableStateService<ExpressionAst
|
|||
): void => this.renderers.register(definition);
|
||||
|
||||
public readonly run: ExpressionsServiceStart['run'] = (ast, input, params) =>
|
||||
this.executor.run(ast, input, params).pipe(take(1)).toPromise<any>();
|
||||
this.executor.run(ast, input, params);
|
||||
|
||||
public readonly getFunction: ExpressionsServiceStart['getFunction'] = (name) =>
|
||||
this.executor.getFunction(name);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { of } from 'rxjs';
|
||||
import { first, skip, toArray } from 'rxjs/operators';
|
||||
import { loader, ExpressionLoader } from './loader';
|
||||
import { Observable } from 'rxjs';
|
||||
|
@ -111,8 +112,29 @@ describe('ExpressionLoader', () => {
|
|||
|
||||
it('emits on $data when data is available', async () => {
|
||||
const expressionLoader = new ExpressionLoader(element, 'var foo', { variables: { foo: 123 } });
|
||||
const response = await expressionLoader.data$.pipe(first()).toPromise();
|
||||
expect(response).toBe(123);
|
||||
const { result } = await expressionLoader.data$.pipe(first()).toPromise();
|
||||
expect(result).toBe(123);
|
||||
});
|
||||
|
||||
it('ignores partial results by default', async () => {
|
||||
const expressionLoader = new ExpressionLoader(element, 'var foo', {
|
||||
variables: { foo: of(1, 2) },
|
||||
});
|
||||
const { result, partial } = await expressionLoader.data$.pipe(first()).toPromise();
|
||||
|
||||
expect(partial).toBe(false);
|
||||
expect(result).toBe(2);
|
||||
});
|
||||
|
||||
it('emits partial results if enabled', async () => {
|
||||
const expressionLoader = new ExpressionLoader(element, 'var foo', {
|
||||
variables: { foo: of(1, 2) },
|
||||
partial: true,
|
||||
});
|
||||
const { result, partial } = await expressionLoader.data$.pipe(first()).toPromise();
|
||||
|
||||
expect(partial).toBe(true);
|
||||
expect(result).toBe(1);
|
||||
});
|
||||
|
||||
it('emits on loading$ on initial load and on updates', async () => {
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
import { BehaviorSubject, Observable, Subject, Subscription } from 'rxjs';
|
||||
import { filter, map, delay } from 'rxjs/operators';
|
||||
import { defaults } from 'lodash';
|
||||
import { UnwrapObservable } from '@kbn/utility-types';
|
||||
import { Adapters } from '../../inspector/public';
|
||||
import { IExpressionLoaderParams } from './types';
|
||||
import { ExpressionAstExpression } from '../common';
|
||||
|
@ -20,7 +21,7 @@ import { getExpressionsService } from './services';
|
|||
type Data = any;
|
||||
|
||||
export class ExpressionLoader {
|
||||
data$: Observable<Data>;
|
||||
data$: ReturnType<ExecutionContract['getData']>;
|
||||
update$: ExpressionRenderHandler['update$'];
|
||||
render$: ExpressionRenderHandler['render$'];
|
||||
events$: ExpressionRenderHandler['events$'];
|
||||
|
@ -28,10 +29,11 @@ export class ExpressionLoader {
|
|||
|
||||
private execution: ExecutionContract | undefined;
|
||||
private renderHandler: ExpressionRenderHandler;
|
||||
private dataSubject: Subject<Data>;
|
||||
private dataSubject: Subject<UnwrapObservable<ExpressionLoader['data$']>>;
|
||||
private loadingSubject: Subject<boolean>;
|
||||
private data: Data;
|
||||
private params: IExpressionLoaderParams = {};
|
||||
private subscription?: Subscription;
|
||||
|
||||
constructor(
|
||||
element: HTMLElement,
|
||||
|
@ -67,8 +69,8 @@ export class ExpressionLoader {
|
|||
}
|
||||
});
|
||||
|
||||
this.data$.subscribe((data) => {
|
||||
this.render(data);
|
||||
this.data$.subscribe(({ result }) => {
|
||||
this.render(result);
|
||||
});
|
||||
|
||||
this.render$.subscribe(() => {
|
||||
|
@ -87,27 +89,20 @@ export class ExpressionLoader {
|
|||
this.dataSubject.complete();
|
||||
this.loadingSubject.complete();
|
||||
this.renderHandler.destroy();
|
||||
if (this.execution) {
|
||||
this.execution.cancel();
|
||||
}
|
||||
this.cancel();
|
||||
this.subscription?.unsubscribe();
|
||||
}
|
||||
|
||||
cancel() {
|
||||
if (this.execution) {
|
||||
this.execution.cancel();
|
||||
}
|
||||
this.execution?.cancel();
|
||||
}
|
||||
|
||||
getExpression(): string | undefined {
|
||||
if (this.execution) {
|
||||
return this.execution.getExpression();
|
||||
}
|
||||
return this.execution?.getExpression();
|
||||
}
|
||||
|
||||
getAst(): ExpressionAstExpression | undefined {
|
||||
if (this.execution) {
|
||||
return this.execution.getAst();
|
||||
}
|
||||
return this.execution?.getAst();
|
||||
}
|
||||
|
||||
getElement(): HTMLElement {
|
||||
|
@ -115,27 +110,25 @@ export class ExpressionLoader {
|
|||
}
|
||||
|
||||
inspect(): Adapters | undefined {
|
||||
return this.execution ? (this.execution.inspect() as Adapters) : undefined;
|
||||
return this.execution?.inspect() as Adapters;
|
||||
}
|
||||
|
||||
async update(
|
||||
expression?: string | ExpressionAstExpression,
|
||||
params?: IExpressionLoaderParams
|
||||
): Promise<void> {
|
||||
update(expression?: string | ExpressionAstExpression, params?: IExpressionLoaderParams): void {
|
||||
this.setParams(params);
|
||||
|
||||
this.loadingSubject.next(true);
|
||||
if (expression) {
|
||||
await this.loadData(expression, this.params);
|
||||
this.loadData(expression, this.params);
|
||||
} else if (this.data) {
|
||||
this.render(this.data);
|
||||
}
|
||||
}
|
||||
|
||||
private loadData = async (
|
||||
private loadData = (
|
||||
expression: string | ExpressionAstExpression,
|
||||
params: IExpressionLoaderParams
|
||||
): Promise<void> => {
|
||||
) => {
|
||||
this.subscription?.unsubscribe();
|
||||
if (this.execution && this.execution.isPending) {
|
||||
this.execution.cancel();
|
||||
}
|
||||
|
@ -148,13 +141,13 @@ export class ExpressionLoader {
|
|||
debug: params.debug,
|
||||
syncColors: params.syncColors,
|
||||
});
|
||||
|
||||
const prevDataHandler = this.execution;
|
||||
const data = await prevDataHandler.getData();
|
||||
if (this.execution !== prevDataHandler) {
|
||||
return;
|
||||
}
|
||||
this.dataSubject.next(data);
|
||||
this.subscription = this.execution
|
||||
.getData()
|
||||
.pipe(
|
||||
delay(0), // delaying until the next tick since we execute the expression in the constructor
|
||||
filter(({ partial }) => params.partial || !partial)
|
||||
)
|
||||
.subscribe((value) => this.dataSubject.next(value));
|
||||
};
|
||||
|
||||
private render(data: Data): void {
|
||||
|
@ -184,6 +177,7 @@ export class ExpressionLoader {
|
|||
}
|
||||
this.params.syncColors = params.syncColors;
|
||||
this.params.debug = Boolean(params.debug);
|
||||
this.params.partial = Boolean(params.partial);
|
||||
|
||||
this.params.inspectorAdapters = (params.inspectorAdapters ||
|
||||
this.execution?.inspect()) as Adapters;
|
||||
|
|
|
@ -36,8 +36,10 @@ describe('ExpressionsPublicPlugin', () => {
|
|||
describe('.run()', () => {
|
||||
test('can execute simple expression', async () => {
|
||||
const { setup } = await expressionsPluginMock.createPlugin();
|
||||
const bar = await setup.run('var_set name="foo" value="bar" | var name="foo"', null);
|
||||
expect(bar).toBe('bar');
|
||||
const { result } = await setup
|
||||
.run('var_set name="foo" value="bar" | var name="foo"', null)
|
||||
.toPromise();
|
||||
expect(result).toBe('bar');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -112,16 +112,17 @@ export class Execution<Input = unknown, Output = unknown, InspectorAdapters exte
|
|||
// (undocumented)
|
||||
get inspectorAdapters(): InspectorAdapters;
|
||||
// (undocumented)
|
||||
interpret<T>(ast: ExpressionAstNode, input: T): Observable<unknown>;
|
||||
interpret<T>(ast: ExpressionAstNode, input: T): Observable<ExecutionResult<unknown>>;
|
||||
// (undocumented)
|
||||
invokeChain(chainArr: ExpressionAstFunction[], input: unknown): Observable<any>;
|
||||
// (undocumented)
|
||||
invokeFunction(fn: ExpressionFunction, input: unknown, args: Record<string, unknown>): Observable<any>;
|
||||
// (undocumented)
|
||||
resolveArgs(fnDef: ExpressionFunction, input: unknown, argAsts: any): Observable<any>;
|
||||
readonly result: Observable<Output | ExpressionValueError>;
|
||||
start(input?: Input): Observable<Output | ExpressionValueError>;
|
||||
readonly state: ExecutionContainer<Output | ExpressionValueError>;
|
||||
readonly result: Observable<ExecutionResult<Output | ExpressionValueError>>;
|
||||
start(input?: Input): Observable<ExecutionResult<Output | ExpressionValueError>>;
|
||||
// Warning: (ae-forgotten-export) The symbol "ExecutionResult" needs to be exported by the entry point index.d.ts
|
||||
readonly state: ExecutionContainer<ExecutionResult<Output | ExpressionValueError>>;
|
||||
}
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "StateContainer" needs to be exported by the entry point index.d.ts
|
||||
|
@ -155,7 +156,7 @@ export class ExecutionContract<Input = unknown, Output = unknown, InspectorAdapt
|
|||
// (undocumented)
|
||||
protected readonly execution: Execution<Input, Output, InspectorAdapters>;
|
||||
getAst: () => ExpressionAstExpression;
|
||||
getData: () => Promise<Output | ExpressionValueError>;
|
||||
getData: () => Observable<ExecutionResult<Output | ExpressionValueError>>;
|
||||
getExpression: () => string;
|
||||
inspect: () => InspectorAdapters;
|
||||
// (undocumented)
|
||||
|
@ -230,7 +231,7 @@ export class Executor<Context extends Record<string, unknown> = Record<string, u
|
|||
registerFunction(functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)): void;
|
||||
// (undocumented)
|
||||
registerType(typeDefinition: AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition)): void;
|
||||
run<Input, Output>(ast: string | ExpressionAstExpression, input: Input, params?: ExpressionExecutionParams): Observable<Output | ExpressionValueError>;
|
||||
run<Input, Output>(ast: string | ExpressionAstExpression, input: Input, params?: ExpressionExecutionParams): Observable<ExecutionResult<Output | ExpressionValueError>>;
|
||||
// (undocumented)
|
||||
readonly state: ExecutorContainer<Context>;
|
||||
// (undocumented)
|
||||
|
@ -637,7 +638,7 @@ export interface ExpressionsServiceStart {
|
|||
getFunction: (name: string) => ReturnType<Executor['getFunction']>;
|
||||
getRenderer: (name: string) => ReturnType<ExpressionRendererRegistry['get']>;
|
||||
getType: (name: string) => ReturnType<Executor['getType']>;
|
||||
run: <Input, Output>(ast: string | ExpressionAstExpression, input: Input, params?: ExpressionExecutionParams) => Promise<Output>;
|
||||
run: <Input, Output>(ast: string | ExpressionAstExpression, input: Input, params?: ExpressionExecutionParams) => Observable<ExecutionResult<Output | ExpressionValueError>>;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ExpressionsSetup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
|
@ -907,6 +908,8 @@ export interface IExpressionLoaderParams {
|
|||
//
|
||||
// (undocumented)
|
||||
onRenderError?: RenderErrorHandlerFnType;
|
||||
// (undocumented)
|
||||
partial?: boolean;
|
||||
// Warning: (ae-forgotten-export) The symbol "RenderMode" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
|
@ -1073,7 +1076,7 @@ export interface ReactExpressionRendererProps extends IExpressionLoaderParams {
|
|||
// (undocumented)
|
||||
expression: string | ExpressionAstExpression;
|
||||
// (undocumented)
|
||||
onData$?: <TData, TInspectorAdapters>(data: TData, adapters?: TInspectorAdapters) => void;
|
||||
onData$?: <TData, TInspectorAdapters>(data: TData, adapters?: TInspectorAdapters, partial?: boolean) => void;
|
||||
// (undocumented)
|
||||
onEvent?: (event: ExpressionRendererEvent) => void;
|
||||
// (undocumented)
|
||||
|
|
|
@ -255,7 +255,7 @@ describe('ExpressionRenderer', () => {
|
|||
const dataSubject = new Subject();
|
||||
const data$ = dataSubject.asObservable().pipe(share());
|
||||
|
||||
const newData = {};
|
||||
const result = {};
|
||||
const inspectData = {};
|
||||
const onData$ = jest.fn();
|
||||
|
||||
|
@ -275,11 +275,11 @@ describe('ExpressionRenderer', () => {
|
|||
expect(onData$).toHaveBeenCalledTimes(0);
|
||||
|
||||
act(() => {
|
||||
dataSubject.next(newData);
|
||||
dataSubject.next({ result });
|
||||
});
|
||||
|
||||
expect(onData$).toHaveBeenCalledTimes(1);
|
||||
expect(onData$.mock.calls[0][0]).toBe(newData);
|
||||
expect(onData$.mock.calls[0][0]).toBe(result);
|
||||
expect(onData$.mock.calls[0][1]).toBe(inspectData);
|
||||
});
|
||||
|
||||
|
|
|
@ -30,7 +30,11 @@ export interface ReactExpressionRendererProps extends IExpressionLoaderParams {
|
|||
) => React.ReactElement | React.ReactElement[];
|
||||
padding?: 'xs' | 's' | 'm' | 'l' | 'xl';
|
||||
onEvent?: (event: ExpressionRendererEvent) => void;
|
||||
onData$?: <TData, TInspectorAdapters>(data: TData, adapters?: TInspectorAdapters) => void;
|
||||
onData$?: <TData, TInspectorAdapters>(
|
||||
data: TData,
|
||||
adapters?: TInspectorAdapters,
|
||||
partial?: boolean
|
||||
) => void;
|
||||
/**
|
||||
* An observable which can be used to re-run the expression without destroying the component
|
||||
*/
|
||||
|
@ -135,8 +139,8 @@ export const ReactExpressionRenderer = ({
|
|||
}
|
||||
if (onData$) {
|
||||
subs.push(
|
||||
expressionLoaderRef.current.data$.subscribe((newData) => {
|
||||
onData$(newData, expressionLoaderRef.current?.inspect());
|
||||
expressionLoaderRef.current.data$.subscribe(({ partial, result }) => {
|
||||
onData$(result, expressionLoaderRef.current?.inspect(), partial);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ export interface IExpressionLoaderParams {
|
|||
renderMode?: RenderMode;
|
||||
syncColors?: boolean;
|
||||
hasCompatibleActions?: ExpressionRenderHandlerParams['hasCompatibleActions'];
|
||||
partial?: boolean;
|
||||
}
|
||||
|
||||
export interface ExpressionRenderError extends Error {
|
||||
|
|
|
@ -28,8 +28,10 @@ describe('ExpressionsServerPlugin', () => {
|
|||
describe('.run()', () => {
|
||||
test('can execute simple expression', async () => {
|
||||
const { setup } = await expressionsPluginMock.createPlugin();
|
||||
const bar = await setup.run('var_set name="foo" value="bar" | var name="foo"', null);
|
||||
expect(bar).toBe('bar');
|
||||
const { result } = await setup
|
||||
.run('var_set name="foo" value="bar" | var name="foo"', null)
|
||||
.toPromise();
|
||||
expect(result).toBe('bar');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -110,16 +110,17 @@ export class Execution<Input = unknown, Output = unknown, InspectorAdapters exte
|
|||
// (undocumented)
|
||||
get inspectorAdapters(): InspectorAdapters;
|
||||
// (undocumented)
|
||||
interpret<T>(ast: ExpressionAstNode, input: T): Observable<unknown>;
|
||||
interpret<T>(ast: ExpressionAstNode, input: T): Observable<ExecutionResult<unknown>>;
|
||||
// (undocumented)
|
||||
invokeChain(chainArr: ExpressionAstFunction[], input: unknown): Observable<any>;
|
||||
// (undocumented)
|
||||
invokeFunction(fn: ExpressionFunction, input: unknown, args: Record<string, unknown>): Observable<any>;
|
||||
// (undocumented)
|
||||
resolveArgs(fnDef: ExpressionFunction, input: unknown, argAsts: any): Observable<any>;
|
||||
readonly result: Observable<Output | ExpressionValueError>;
|
||||
start(input?: Input): Observable<Output | ExpressionValueError>;
|
||||
readonly state: ExecutionContainer<Output | ExpressionValueError>;
|
||||
readonly result: Observable<ExecutionResult<Output | ExpressionValueError>>;
|
||||
start(input?: Input): Observable<ExecutionResult<Output | ExpressionValueError>>;
|
||||
// Warning: (ae-forgotten-export) The symbol "ExecutionResult" needs to be exported by the entry point index.d.ts
|
||||
readonly state: ExecutionContainer<ExecutionResult<Output | ExpressionValueError>>;
|
||||
}
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "StateContainer" needs to be exported by the entry point index.d.ts
|
||||
|
@ -212,7 +213,7 @@ export class Executor<Context extends Record<string, unknown> = Record<string, u
|
|||
registerFunction(functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)): void;
|
||||
// (undocumented)
|
||||
registerType(typeDefinition: AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition)): void;
|
||||
run<Input, Output>(ast: string | ExpressionAstExpression, input: Input, params?: ExpressionExecutionParams): Observable<Output | ExpressionValueError>;
|
||||
run<Input, Output>(ast: string | ExpressionAstExpression, input: Input, params?: ExpressionExecutionParams): Observable<ExecutionResult<Output | ExpressionValueError>>;
|
||||
// (undocumented)
|
||||
readonly state: ExecutorContainer<Context>;
|
||||
// (undocumented)
|
||||
|
|
|
@ -10,6 +10,7 @@ import PropTypes from 'prop-types';
|
|||
import React, { Component } from 'react';
|
||||
import { keys, EuiFlexGroup, EuiFlexItem, EuiButton, EuiText, EuiSwitch } from '@elastic/eui';
|
||||
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
|
||||
import { pluck } from 'rxjs/operators';
|
||||
|
||||
const MIN_CHART_HEIGHT = 300;
|
||||
|
||||
|
@ -55,7 +56,9 @@ class VisEditorVisualizationUI extends Component {
|
|||
await this._handler.render(this._visEl.current);
|
||||
this.props.eventEmitter.emit('embeddableRendered');
|
||||
|
||||
this._subscription = this._handler.handler.data$.subscribe((data) => onDataChange(data.value));
|
||||
this._subscription = this._handler.handler.data$
|
||||
.pipe(pluck('result'))
|
||||
.subscribe((data) => onDataChange(data.value));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,7 +9,7 @@ import './main.scss';
|
|||
|
||||
import React from 'react';
|
||||
import { EuiPage, EuiPageBody, EuiPageContent, EuiPageContentHeader } from '@elastic/eui';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { first, pluck } from 'rxjs/operators';
|
||||
import {
|
||||
IInterpreterRenderHandlers,
|
||||
ExpressionValue,
|
||||
|
@ -59,7 +59,9 @@ class Main extends React.Component<{}, State> {
|
|||
inspectorAdapters: adapters,
|
||||
searchContext: initialContext as any,
|
||||
})
|
||||
.getData();
|
||||
.getData()
|
||||
.pipe(pluck('result'))
|
||||
.toPromise();
|
||||
};
|
||||
|
||||
let lastRenderHandler: ExpressionRenderHandler;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { pluck } from 'rxjs/operators';
|
||||
import { CoreSetup, Plugin, HttpResponsePayload } from '../../../../../src/core/server';
|
||||
import { PluginStart as DataPluginStart } from '../../../../../src/plugins/data/server';
|
||||
import { ExpressionsServerStart } from '../../../../../src/plugins/expressions/server';
|
||||
|
@ -32,13 +33,12 @@ export class TestPlugin implements Plugin<TestPluginSetup, TestPluginStart, {},
|
|||
},
|
||||
async (context, req, res) => {
|
||||
const [, { expressions }] = await core.getStartServices();
|
||||
const output = await expressions.run<unknown, HttpResponsePayload>(
|
||||
req.body.expression,
|
||||
req.body.input,
|
||||
{
|
||||
const output = await expressions
|
||||
.run<unknown, HttpResponsePayload>(req.body.expression, req.body.input, {
|
||||
kibanaRequest: req,
|
||||
}
|
||||
);
|
||||
})
|
||||
.pipe(pluck('result'))
|
||||
.toPromise();
|
||||
return res.ok({ body: output });
|
||||
}
|
||||
);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import { fromExpression, getType } from '@kbn/interpreter/common';
|
||||
import { pluck } from 'rxjs/operators';
|
||||
import { ExpressionValue, ExpressionAstExpression } from 'src/plugins/expressions/public';
|
||||
import { pluginServices, expressionsService } from '../services';
|
||||
|
||||
|
@ -21,7 +22,12 @@ export async function interpretAst(
|
|||
variables: Record<string, any>
|
||||
): Promise<ExpressionValue> {
|
||||
const context = { variables };
|
||||
return await expressionsService.getService().execute(ast, null, context).getData();
|
||||
return await expressionsService
|
||||
.getService()
|
||||
.execute(ast, null, context)
|
||||
.getData()
|
||||
.pipe(pluck('result'))
|
||||
.toPromise();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -43,7 +49,12 @@ export async function runInterpreter(
|
|||
const context = { variables };
|
||||
|
||||
try {
|
||||
const renderable = await expressionsService.getService().execute(ast, input, context).getData();
|
||||
const renderable = await expressionsService
|
||||
.getService()
|
||||
.execute(ast, input, context)
|
||||
.getData()
|
||||
.pipe(pluck('result'))
|
||||
.toPromise();
|
||||
|
||||
if (getType(renderable) === 'render') {
|
||||
return renderable;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue