Skip to content

Commit

Permalink
Expressions/migrations2 (#81281)
Browse files Browse the repository at this point in the history
  • Loading branch information
ppisljar authored Oct 30, 2020
1 parent 4f71770 commit 076bb73
Show file tree
Hide file tree
Showing 20 changed files with 296 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export declare class Executor<Context extends Record<string, unknown> = Record<s
| [getType(name)](./kibana-plugin-plugins-expressions-public.executor.gettype.md) | | |
| [getTypes()](./kibana-plugin-plugins-expressions-public.executor.gettypes.md) | | |
| [inject(ast, references)](./kibana-plugin-plugins-expressions-public.executor.inject.md) | | |
| [migrate(ast, version)](./kibana-plugin-plugins-expressions-public.executor.migrate.md) | | |
| [migrateToLatest(ast, version)](./kibana-plugin-plugins-expressions-public.executor.migratetolatest.md) | | |
| [registerFunction(functionDefinition)](./kibana-plugin-plugins-expressions-public.executor.registerfunction.md) | | |
| [registerType(typeDefinition)](./kibana-plugin-plugins-expressions-public.executor.registertype.md) | | |
| [run(ast, input, params)](./kibana-plugin-plugins-expressions-public.executor.run.md) | | Execute expression and return result. |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) &gt; [Executor](./kibana-plugin-plugins-expressions-public.executor.md) &gt; [migrate](./kibana-plugin-plugins-expressions-public.executor.migrate.md)

## Executor.migrate() method

<b>Signature:</b>

```typescript
migrate(ast: SerializableState, version: string): ExpressionAstExpression;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| ast | <code>SerializableState</code> | |
| version | <code>string</code> | |

<b>Returns:</b>

`ExpressionAstExpression`

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) &gt; [Executor](./kibana-plugin-plugins-expressions-public.executor.md) &gt; [migrateToLatest](./kibana-plugin-plugins-expressions-public.executor.migratetolatest.md)

## Executor.migrateToLatest() method

<b>Signature:</b>

```typescript
migrateToLatest(ast: unknown, version: string): ExpressionAstExpression;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| ast | <code>unknown</code> | |
| version | <code>string</code> | |

<b>Returns:</b>

`ExpressionAstExpression`

Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export declare class ExpressionFunction implements PersistableState<ExpressionAs
| [help](./kibana-plugin-plugins-expressions-public.expressionfunction.help.md) | | <code>string</code> | A short help text. |
| [inject](./kibana-plugin-plugins-expressions-public.expressionfunction.inject.md) | | <code>(state: ExpressionAstFunction['arguments'], references: SavedObjectReference[]) =&gt; ExpressionAstFunction['arguments']</code> | |
| [inputTypes](./kibana-plugin-plugins-expressions-public.expressionfunction.inputtypes.md) | | <code>string[] &#124; undefined</code> | Type of inputs that this function supports. |
| [migrations](./kibana-plugin-plugins-expressions-public.expressionfunction.migrations.md) | | <code>{</code><br/><code> [key: string]: (state: SerializableState) =&gt; SerializableState;</code><br/><code> }</code> | |
| [name](./kibana-plugin-plugins-expressions-public.expressionfunction.name.md) | | <code>string</code> | Name of function |
| [telemetry](./kibana-plugin-plugins-expressions-public.expressionfunction.telemetry.md) | | <code>(state: ExpressionAstFunction['arguments'], telemetryData: Record&lt;string, any&gt;) =&gt; Record&lt;string, any&gt;</code> | |
| [type](./kibana-plugin-plugins-expressions-public.expressionfunction.type.md) | | <code>string</code> | Return type of function. This SHOULD be supplied. We use it for UI and autocomplete hinting. We may also use it for optimizations in the future. |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) &gt; [ExpressionFunction](./kibana-plugin-plugins-expressions-public.expressionfunction.md) &gt; [migrations](./kibana-plugin-plugins-expressions-public.expressionfunction.migrations.md)

## ExpressionFunction.migrations property

<b>Signature:</b>

```typescript
migrations: {
[key: string]: (state: SerializableState) => SerializableState;
};
```
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export declare class ExpressionsService implements PersistableState<ExpressionAs
| [getType](./kibana-plugin-plugins-expressions-public.expressionsservice.gettype.md) | | <code>ExpressionsServiceStart['getType']</code> | |
| [getTypes](./kibana-plugin-plugins-expressions-public.expressionsservice.gettypes.md) | | <code>() =&gt; ReturnType&lt;Executor['getTypes']&gt;</code> | Returns POJO map of all registered expression types, where keys are names of the types and values are <code>ExpressionType</code> instances. |
| [inject](./kibana-plugin-plugins-expressions-public.expressionsservice.inject.md) | | <code>(state: ExpressionAstExpression, references: SavedObjectReference[]) =&gt; ExpressionAstExpression</code> | Injects saved object references into expression AST |
| [migrate](./kibana-plugin-plugins-expressions-public.expressionsservice.migrate.md) | | <code>(state: SerializableState, version: string) =&gt; ExpressionAstExpression</code> | Injects saved object references into expression AST |
| [migrateToLatest](./kibana-plugin-plugins-expressions-public.expressionsservice.migratetolatest.md) | | <code>(state: unknown, version: string) =&gt; ExpressionAstExpression</code> | Injects saved object references into expression AST |
| [registerFunction](./kibana-plugin-plugins-expressions-public.expressionsservice.registerfunction.md) | | <code>(functionDefinition: AnyExpressionFunctionDefinition &#124; (() =&gt; AnyExpressionFunctionDefinition)) =&gt; void</code> | Register an expression function, which will be possible to execute as part of the expression pipeline.<!-- -->Below we register a function which simply sleeps for given number of milliseconds to delay the execution and outputs its input as-is.
```ts
expressions.registerFunction({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) &gt; [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) &gt; [migrate](./kibana-plugin-plugins-expressions-public.expressionsservice.migrate.md)

## ExpressionsService.migrate property

Injects saved object references into expression AST

<b>Signature:</b>

```typescript
readonly migrate: (state: SerializableState, version: string) => ExpressionAstExpression;
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) &gt; [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) &gt; [migrateToLatest](./kibana-plugin-plugins-expressions-public.expressionsservice.migratetolatest.md)

## ExpressionsService.migrateToLatest property

Injects saved object references into expression AST

<b>Signature:</b>

```typescript
readonly migrateToLatest: (state: unknown, version: string) => ExpressionAstExpression;
```
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export declare class Executor<Context extends Record<string, unknown> = Record<s
| [getType(name)](./kibana-plugin-plugins-expressions-server.executor.gettype.md) | | |
| [getTypes()](./kibana-plugin-plugins-expressions-server.executor.gettypes.md) | | |
| [inject(ast, references)](./kibana-plugin-plugins-expressions-server.executor.inject.md) | | |
| [migrate(ast, version)](./kibana-plugin-plugins-expressions-server.executor.migrate.md) | | |
| [migrateToLatest(ast, version)](./kibana-plugin-plugins-expressions-server.executor.migratetolatest.md) | | |
| [registerFunction(functionDefinition)](./kibana-plugin-plugins-expressions-server.executor.registerfunction.md) | | |
| [registerType(typeDefinition)](./kibana-plugin-plugins-expressions-server.executor.registertype.md) | | |
| [run(ast, input, params)](./kibana-plugin-plugins-expressions-server.executor.run.md) | | Execute expression and return result. |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) &gt; [Executor](./kibana-plugin-plugins-expressions-server.executor.md) &gt; [migrate](./kibana-plugin-plugins-expressions-server.executor.migrate.md)

## Executor.migrate() method

<b>Signature:</b>

```typescript
migrate(ast: SerializableState, version: string): ExpressionAstExpression;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| ast | <code>SerializableState</code> | |
| version | <code>string</code> | |

<b>Returns:</b>

`ExpressionAstExpression`

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) &gt; [Executor](./kibana-plugin-plugins-expressions-server.executor.md) &gt; [migrateToLatest](./kibana-plugin-plugins-expressions-server.executor.migratetolatest.md)

## Executor.migrateToLatest() method

<b>Signature:</b>

```typescript
migrateToLatest(ast: unknown, version: string): ExpressionAstExpression;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| ast | <code>unknown</code> | |
| version | <code>string</code> | |

<b>Returns:</b>

`ExpressionAstExpression`

Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export declare class ExpressionFunction implements PersistableState<ExpressionAs
| [help](./kibana-plugin-plugins-expressions-server.expressionfunction.help.md) | | <code>string</code> | A short help text. |
| [inject](./kibana-plugin-plugins-expressions-server.expressionfunction.inject.md) | | <code>(state: ExpressionAstFunction['arguments'], references: SavedObjectReference[]) =&gt; ExpressionAstFunction['arguments']</code> | |
| [inputTypes](./kibana-plugin-plugins-expressions-server.expressionfunction.inputtypes.md) | | <code>string[] &#124; undefined</code> | Type of inputs that this function supports. |
| [migrations](./kibana-plugin-plugins-expressions-server.expressionfunction.migrations.md) | | <code>{</code><br/><code> [key: string]: (state: SerializableState) =&gt; SerializableState;</code><br/><code> }</code> | |
| [name](./kibana-plugin-plugins-expressions-server.expressionfunction.name.md) | | <code>string</code> | Name of function |
| [telemetry](./kibana-plugin-plugins-expressions-server.expressionfunction.telemetry.md) | | <code>(state: ExpressionAstFunction['arguments'], telemetryData: Record&lt;string, any&gt;) =&gt; Record&lt;string, any&gt;</code> | |
| [type](./kibana-plugin-plugins-expressions-server.expressionfunction.type.md) | | <code>string</code> | Return type of function. This SHOULD be supplied. We use it for UI and autocomplete hinting. We may also use it for optimizations in the future. |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) &gt; [ExpressionFunction](./kibana-plugin-plugins-expressions-server.expressionfunction.md) &gt; [migrations](./kibana-plugin-plugins-expressions-server.expressionfunction.migrations.md)

## ExpressionFunction.migrations property

<b>Signature:</b>

```typescript
migrations: {
[key: string]: (state: SerializableState) => SerializableState;
};
```
31 changes: 31 additions & 0 deletions src/plugins/expressions/common/executor/executor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import * as expressionTypes from '../expression_types';
import * as expressionFunctions from '../expression_functions';
import { Execution } from '../execution';
import { ExpressionAstFunction, parseExpression } from '../ast';
import { MigrateFunction } from '../../../kibana_utils/common/persistable_state';

describe('Executor', () => {
test('can instantiate', () => {
Expand Down Expand Up @@ -158,6 +159,7 @@ describe('Executor', () => {

const injectFn = jest.fn().mockImplementation((args, references) => args);
const extractFn = jest.fn().mockReturnValue({ args: {}, references: [] });
const migrateFn = jest.fn().mockImplementation((args) => args);

const fooFn = {
name: 'foo',
Expand All @@ -174,6 +176,14 @@ describe('Executor', () => {
inject: (state: ExpressionAstFunction['arguments']) => {
return injectFn(state);
},
migrations: {
'7.10.0': (((state: ExpressionAstFunction, version: string): ExpressionAstFunction => {
return migrateFn(state, version);
}) as any) as MigrateFunction,
'7.10.1': (((state: ExpressionAstFunction, version: string): ExpressionAstFunction => {
return migrateFn(state, version);
}) as any) as MigrateFunction,
},
fn: jest.fn(),
};
executor.registerFunction(fooFn);
Expand All @@ -194,5 +204,26 @@ describe('Executor', () => {
expect(extractFn).toBeCalledTimes(5);
});
});

describe('.migrate', () => {
test('calls migrate function for every expression function in expression', () => {
executor.migrate(
parseExpression('foo bar="baz" | foo bar={foo bar="baz" | foo bar={foo bar="baz"}}'),
'7.10.0'
);
expect(migrateFn).toBeCalledTimes(5);
});
});

describe('.migrateToLatest', () => {
test('calls extract function for every expression function in expression', () => {
migrateFn.mockClear();
executor.migrateToLatest(
parseExpression('foo bar="baz" | foo bar={foo bar="baz" | foo bar={foo bar="baz"}}'),
'7.10.0'
);
expect(migrateFn).toBeCalledTimes(10);
});
});
});
});
37 changes: 36 additions & 1 deletion src/plugins/expressions/common/executor/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { typeSpecs } from '../expression_types/specs';
import { functionSpecs } from '../expression_functions/specs';
import { getByAlias } from '../util';
import { SavedObjectReference } from '../../../../core/types';
import { PersistableState } from '../../../kibana_utils/common';
import { PersistableState, SerializableState } from '../../../kibana_utils/common';
import { ExpressionExecutionParams } from '../service';

export interface ExpressionExecOptions {
Expand Down Expand Up @@ -88,6 +88,20 @@ export class FunctionsRegistry implements IRegistry<ExpressionFunction> {
}
}

const semverGte = (semver1: string, semver2: string) => {
const regex = /^([0-9]+)\.([0-9]+)\.([0-9]+)$/;
const matches1 = regex.exec(semver1) as RegExpMatchArray;
const matches2 = regex.exec(semver2) as RegExpMatchArray;

const [, major1, minor1, patch1] = matches1;
const [, major2, minor2, patch2] = matches2;

return (
major1 > major2 ||
(major1 === major2 && (minor1 > minor2 || (minor1 === minor2 && patch1 >= patch2)))
);
};

export class Executor<Context extends Record<string, unknown> = Record<string, unknown>>
implements PersistableState<ExpressionAstExpression> {
static createWithDefaults<Ctx extends Record<string, unknown> = Record<string, unknown>>(
Expand Down Expand Up @@ -249,6 +263,27 @@ export class Executor<Context extends Record<string, unknown> = Record<string, u
return telemetryData;
}

public migrate(ast: SerializableState, version: string) {
return this.walkAst(cloneDeep(ast) as ExpressionAstExpression, (fn, link) => {
if (!fn.migrations[version]) return link;
const updatedAst = fn.migrations[version](link) as ExpressionAstFunction;
link.arguments = updatedAst.arguments;
link.type = updatedAst.type;
});
}

public migrateToLatest(ast: unknown, version: string) {
return this.walkAst(cloneDeep(ast) as ExpressionAstExpression, (fn, link) => {
for (const key of Object.keys(fn.migrations)) {
if (semverGte(key, version)) {
const updatedAst = fn.migrations[key](link) as ExpressionAstFunction;
link.arguments = updatedAst.arguments;
link.type = updatedAst.type;
}
}
});
}

public fork(): Executor<Context> {
const initialState = this.state.get();
const fork = new Executor<Context>(initialState);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { ExpressionValue } from '../expression_types/types';
import { ExecutionContext } from '../execution';
import { ExpressionAstFunction } from '../ast';
import { SavedObjectReference } from '../../../../core/types';
import { PersistableState } from '../../../kibana_utils/common';
import { PersistableState, SerializableState } from '../../../kibana_utils/common';

export class ExpressionFunction implements PersistableState<ExpressionAstFunction['arguments']> {
/**
Expand Down Expand Up @@ -76,6 +76,9 @@ export class ExpressionFunction implements PersistableState<ExpressionAstFunctio
state: ExpressionAstFunction['arguments'],
references: SavedObjectReference[]
) => ExpressionAstFunction['arguments'];
migrations: {
[key: string]: (state: SerializableState) => SerializableState;
};

constructor(functionDefinition: AnyExpressionFunctionDefinition) {
const {
Expand All @@ -91,6 +94,7 @@ export class ExpressionFunction implements PersistableState<ExpressionAstFunctio
telemetry,
inject,
extract,
migrations,
} = functionDefinition;

this.name = name;
Expand All @@ -104,6 +108,7 @@ export class ExpressionFunction implements PersistableState<ExpressionAstFunctio
this.telemetry = telemetry || ((s, c) => c);
this.inject = inject || identity;
this.extract = extract || ((s) => ({ state: s, references: [] }));
this.migrations = migrations || {};

for (const [key, arg] of Object.entries(args || {})) {
this.args[key] = new ExpressionFunctionParameter(key, arg);
Expand Down
22 changes: 21 additions & 1 deletion src/plugins/expressions/common/service/expressions_services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { ExecutionContract } from '../execution/execution_contract';
import { AnyExpressionTypeDefinition } from '../expression_types';
import { AnyExpressionFunctionDefinition } from '../expression_functions';
import { SavedObjectReference } from '../../../../core/types';
import { PersistableState } from '../../../kibana_utils/common';
import { PersistableState, SerializableState } from '../../../kibana_utils/common';
import { Adapters } from '../../../inspector/common/adapters';
import { ExecutionContextSearch } from '../execution';

Expand Down Expand Up @@ -303,6 +303,26 @@ export class ExpressionsService implements PersistableState<ExpressionAstExpress
return this.executor.inject(state, references);
};

/**
* Runs the migration (if it exists) for specified version. This will run a single migration step (ie from 7.10.0 to 7.10.1)
* @param state expression AST to update
* @param version defines which migration version to run
* @returns new migrated expression AST
*/
public readonly migrate = (state: SerializableState, version: string) => {
return this.executor.migrate(state, version);
};

/**
* Migrates expression to the latest version
* @param state expression AST to update
* @param version the version of kibana in which expression was created
* @returns migrated expression AST
*/
public readonly migrateToLatest = (state: unknown, version: string) => {
return this.executor.migrateToLatest(state, version);
};

/**
* Returns Kibana Platform *setup* life-cycle contract. Useful to return the
* same contract on server-side and browser-side.
Expand Down
Loading

0 comments on commit 076bb73

Please sign in to comment.