[expressions] changes fork to use namespacing (#125957)

This commit is contained in:
Peter Pisljar 2022-03-15 14:32:56 +01:00 committed by GitHub
parent 8ef8e65ae1
commit 8ee46c2f31
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 299 additions and 92 deletions

View file

@ -7,7 +7,8 @@
*/
import React from 'react';
import ReactDOM from 'react-dom';
import type { ExpressionsService, ExpressionsServiceSetup } from 'src/plugins/expressions';
import type { ExpressionsServiceSetup } from 'src/plugins/expressions';
import { ExpressionsServiceFork } from 'src/plugins/expressions/common/service/expressions_fork';
import { AppMountParameters, AppNavLinkStatus, CoreSetup, Plugin } from '../../../src/core/public';
import type { DeveloperExamplesSetup } from '../../developer_examples/public';
import { App, ExpressionsContext } from './app';
@ -19,13 +20,15 @@ interface SetupDeps {
}
export class PartialResultsExamplePlugin implements Plugin<void, void, SetupDeps> {
private expressions?: ExpressionsService;
private expressions?: ExpressionsServiceFork;
setup({ application }: CoreSetup, { expressions, developerExamples }: SetupDeps) {
this.expressions = expressions.fork();
this.expressions.registerFunction(countEvent);
this.expressions.registerFunction(getEvents);
this.expressions.registerFunction(pluck);
this.expressions = expressions.fork('test');
const expressionsSetup = this.expressions.setup();
expressionsSetup.registerFunction(countEvent);
expressionsSetup.registerFunction(getEvents);
expressionsSetup.registerFunction(pluck);
const expressionsStart = this.expressions.start();
application.register({
id: 'partialResultsExample',
@ -33,7 +36,7 @@ export class PartialResultsExamplePlugin implements Plugin<void, void, SetupDeps
navLinkStatus: AppNavLinkStatus.hidden,
mount: async ({ element }: AppMountParameters) => {
ReactDOM.render(
<ExpressionsContext.Provider value={this.expressions}>
<ExpressionsContext.Provider value={expressionsStart}>
<App />
</ExpressionsContext.Provider>,
element

View file

@ -300,7 +300,11 @@ export class Execution<
...(chainArr.map((link) =>
switchMap((currentInput) => {
const { function: fnName, arguments: fnArgs } = link;
const fn = getByAlias(this.state.get().functions, fnName);
const fn = getByAlias(
this.state.get().functions,
fnName,
this.execution.params.namespace
);
if (!fn) {
throw createError({

View file

@ -20,7 +20,7 @@ import { ExpressionType } from '../expression_types/expression_type';
import { AnyExpressionTypeDefinition } from '../expression_types/types';
import { ExpressionAstExpression, ExpressionAstFunction } from '../ast';
import { ExpressionValueError, typeSpecs } from '../expression_types/specs';
import { getByAlias } from '../util';
import { ALL_NAMESPACES, getByAlias } from '../util';
import { SavedObjectReference } from '../../../../core/types';
import {
MigrateFunctionsObject,
@ -146,15 +146,17 @@ export class Executor<Context extends Record<string, unknown> = Record<string, u
this.container.transitions.addFunction(fn);
}
public getFunction(name: string): ExpressionFunction | undefined {
return this.container.get().functions[name] ?? this.parent?.getFunction(name);
public getFunction(name: string, namespace?: string): ExpressionFunction | undefined {
const fn = this.container.get().functions[name];
if (!fn?.namespace || fn.namespace === namespace) return fn;
}
public getFunctions(): Record<string, ExpressionFunction> {
return {
...(this.parent?.getFunctions() ?? {}),
...this.container.get().functions,
};
public getFunctions(namespace?: string): Record<string, ExpressionFunction> {
const fns = Object.entries(this.container.get().functions);
const filtered = fns.filter(
([key, value]) => !value.namespace || value.namespace === namespace
);
return Object.fromEntries(filtered);
}
public registerType(
@ -222,9 +224,10 @@ export class Executor<Context extends Record<string, unknown> = Record<string, u
ast: ExpressionAstExpression,
action: (fn: ExpressionFunction, link: ExpressionAstFunction) => void
) {
const functions = this.container.get().functions;
for (const link of ast.chain) {
const { function: fnName, arguments: fnArgs } = link;
const fn = getByAlias(this.getFunctions(), fnName);
const fn = getByAlias(functions, fnName, ALL_NAMESPACES);
if (fn) {
// if any of arguments are expressions we should migrate those first
@ -249,12 +252,13 @@ export class Executor<Context extends Record<string, unknown> = Record<string, u
) => ExpressionAstFunction | ExpressionAstExpression
): ExpressionAstExpression {
let additionalFunctions = 0;
const functions = this.container.get().functions;
return (
ast.chain.reduce<ExpressionAstExpression>(
(newAst: ExpressionAstExpression, funcAst: ExpressionAstFunction, index: number) => {
const realIndex = index + additionalFunctions;
const { function: fnName, arguments: fnArgs } = funcAst;
const fn = getByAlias(this.getFunctions(), fnName);
const fn = getByAlias(functions, fnName, ALL_NAMESPACES);
if (!fn) {
return newAst;
}
@ -331,7 +335,7 @@ export class Executor<Context extends Record<string, unknown> = Record<string, u
public getAllMigrations() {
const uniqueVersions = new Set(
Object.values(this.getFunctions())
Object.values(this.container.get().functions)
.map((fn) => Object.keys(fn.migrations))
.flat(1)
);

View file

@ -24,6 +24,8 @@ export class ExpressionFunction implements PersistableState<ExpressionAstFunctio
*/
name: string;
namespace?: string;
/**
* Aliases that can be used instead of `name`.
*/
@ -90,9 +92,11 @@ export class ExpressionFunction implements PersistableState<ExpressionAstFunctio
inject,
extract,
migrations,
namespace,
} = functionDefinition;
this.name = name;
this.namespace = namespace;
this.type = type;
this.aliases = aliases || [];
this.fn = fn as ExpressionFunction['fn'];

View file

@ -44,6 +44,8 @@ export interface ExpressionFunctionDefinition<
*/
disabled?: boolean;
namespace?: string;
/**
* Name of type of value this function outputs.
*/

View file

@ -10,6 +10,7 @@ import { ExpressionRenderDefinition } from './types';
export class ExpressionRenderer<Config = unknown> {
public readonly name: string;
public readonly namespace?: string;
public readonly displayName: string;
public readonly help: string;
public readonly validate: () => void | Error;
@ -17,9 +18,10 @@ export class ExpressionRenderer<Config = unknown> {
public readonly render: ExpressionRenderDefinition<Config>['render'];
constructor(config: ExpressionRenderDefinition<Config>) {
const { name, displayName, help, validate, reuseDomNode, render } = config;
const { name, displayName, help, validate, reuseDomNode, render, namespace } = config;
this.name = name;
this.namespace = namespace;
this.displayName = displayName || name;
this.help = help || '';
this.validate = validate || (() => {});

View file

@ -15,6 +15,7 @@ export interface ExpressionRenderDefinition<Config = unknown> {
* function that is used to create the `type: render` object.
*/
name: string;
namespace?: string;
/**
* A user friendly name of the renderer as will be displayed to user in UI.

View file

@ -12,7 +12,7 @@ import { getType } from './get_type';
export class ExpressionType {
name: string;
namespace?: string;
/**
* A short help text.
*/
@ -33,9 +33,10 @@ export class ExpressionType {
private readonly definition: AnyExpressionTypeDefinition;
constructor(definition: AnyExpressionTypeDefinition) {
const { name, help, deserialize, serialize, validate } = definition;
const { name, help, deserialize, serialize, validate, namespace } = definition;
this.name = name;
this.namespace = namespace;
this.help = help || '';
this.validate = validate || (() => {});

View file

@ -32,6 +32,7 @@ export interface ExpressionTypeDefinition<
SerializedType = undefined
> {
name: Name;
namespace?: string;
validate?(type: unknown): void | Error;
serialize?(type: Value): SerializedType;
deserialize?(type: SerializedType): Value;

View file

@ -0,0 +1,140 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import {
ExpressionExecutionParams,
ExpressionsService,
ExpressionsServiceSetup,
ExpressionsServiceStart,
} from '.';
import { ExpressionAstExpression } from '../ast';
import { AnyExpressionFunctionDefinition } from '../expression_functions';
import { AnyExpressionTypeDefinition } from '../expression_types';
import { AnyExpressionRenderDefinition } from '../expression_renderers';
export interface ExpressionServiceFork {
setup(): ExpressionsServiceSetup;
start(): ExpressionsServiceStart;
}
/**
* `ExpressionsService` class is used for multiple purposes:
*
* 1. It implements the same Expressions service that can be used on both:
* (1) server-side and (2) browser-side.
* 2. It implements the same Expressions service that users can fork/clone,
* thus have their own instance of the Expressions plugin.
* 3. `ExpressionsService` defines the public contracts of *setup* and *start*
* Kibana Platform life-cycles for ease-of-use on server-side and browser-side.
* 4. `ExpressionsService` creates a bound version of all exported contract functions.
* 5. Functions are bound the way there are:
*
* ```ts
* registerFunction = (...args: Parameters<Executor['registerFunction']>
* ): ReturnType<Executor['registerFunction']> => this.executor.registerFunction(...args);
* ```
*
* so that JSDoc appears in developers IDE when they use those `plugins.expressions.registerFunction(`.
*/
export class ExpressionsServiceFork implements ExpressionServiceFork {
/**
* @note Workaround since the expressions service is frozen.
*/
constructor(private namespace: string, private expressionsService: ExpressionsService) {
this.registerFunction = this.registerFunction.bind(this);
this.registerRenderer = this.registerRenderer.bind(this);
this.registerType = this.registerType.bind(this);
this.run = this.run.bind(this);
this.execute = this.execute.bind(this);
this.getFunction = this.getFunction.bind(this);
this.getFunctions = this.getFunctions.bind(this);
}
protected registerFunction(
definition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)
) {
if (typeof definition === 'function') definition = definition();
return this.expressionsService.registerFunction({
...definition,
namespace: this.namespace,
});
}
protected registerRenderer(
definition: AnyExpressionRenderDefinition | (() => AnyExpressionRenderDefinition)
) {
if (typeof definition === 'function') definition = definition();
return this.expressionsService.registerRenderer({
...definition,
namespace: this.namespace,
});
}
protected registerType(
definition: AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition)
) {
if (typeof definition === 'function') definition = definition();
return this.expressionsService.registerType({ ...definition, namespace: this.namespace });
}
protected run<Input, Output>(
ast: string | ExpressionAstExpression,
input: Input,
params?: ExpressionExecutionParams
) {
return this.expressionsService.run<Input, Output>(ast, input, {
...params,
namespace: this.namespace,
});
}
protected execute<Input = unknown, Output = unknown>(
ast: string | ExpressionAstExpression,
input: Input,
params?: ExpressionExecutionParams
) {
return this.expressionsService.execute<Input, Output>(ast, input, {
...params,
namespace: this.namespace,
});
}
protected getFunction(name: string) {
return this.expressionsService.getFunction(name, this.namespace);
}
protected getFunctions() {
return this.expressionsService.getFunctions(this.namespace);
}
/**
* Returns Kibana Platform *setup* life-cycle contract. Useful to return the
* same contract on server-side and browser-side.
*/
public setup(): ExpressionsServiceSetup {
return {
...this.expressionsService,
registerFunction: this.registerFunction,
registerRenderer: this.registerRenderer,
registerType: this.registerType,
};
}
/**
* Returns Kibana Platform *start* life-cycle contract. Useful to return the
* same contract on server-side and browser-side.
*/
public start(): ExpressionsServiceStart {
return {
...this.expressionsService,
run: this.run,
execute: this.execute,
getFunction: this.getFunction,
getFunctions: this.getFunctions,
};
}
}

View file

@ -7,6 +7,7 @@
*/
import { ExpressionsService } from './expressions_services';
import { ExpressionsServiceFork } from './expressions_fork';
describe('ExpressionsService', () => {
test('can instantiate', () => {
@ -58,36 +59,31 @@ describe('ExpressionsService', () => {
describe('.fork()', () => {
test('returns a new ExpressionsService instance', () => {
const service = new ExpressionsService();
const fork = service.fork();
const fork = service.fork('test');
expect(fork).not.toBe(service);
expect(fork).toBeInstanceOf(ExpressionsService);
expect(fork).toBeInstanceOf(ExpressionsServiceFork);
});
test('fork keeps all types of the origin service', () => {
const service = new ExpressionsService();
const fork = service.fork();
const fork = service.fork('test');
const forkStart = fork.start();
expect(fork.getTypes()).toEqual(service.getTypes());
expect(forkStart.getTypes()).toEqual(service.getTypes());
});
test('fork keeps all functions of the origin service', () => {
const service = new ExpressionsService();
const fork = service.fork();
const fork = service.fork('test');
const forkStart = fork.start();
expect(fork.getFunctions()).toEqual(service.getFunctions());
});
test('fork keeps context of the origin service', () => {
const service = new ExpressionsService();
const fork = service.fork();
expect(fork.executor.state.context).toEqual(service.executor.state.context);
expect(forkStart.getFunctions()).toEqual(service.getFunctions());
});
test('newly registered functions in origin are also available in fork', () => {
const service = new ExpressionsService();
const fork = service.fork();
const fork = service.fork('test');
service.registerFunction({
name: '__test__',
@ -96,29 +92,35 @@ describe('ExpressionsService', () => {
fn: () => {},
});
expect(fork.getFunctions()).toEqual(service.getFunctions());
const forkStart = fork.start();
expect(forkStart.getFunctions()).toEqual(service.getFunctions());
});
test('newly registered functions in fork are NOT available in origin', () => {
const service = new ExpressionsService();
const fork = service.fork();
const fork = service.fork('test');
const forkSetup = fork.setup();
fork.registerFunction({
forkSetup.registerFunction({
name: '__test__',
args: {},
help: '',
fn: () => {},
});
expect(Object.values(fork.getFunctions())).toHaveLength(
const forkStart = fork.start();
expect(Object.values(forkStart.getFunctions())).toHaveLength(
Object.values(service.getFunctions()).length + 1
);
});
test('fork can execute an expression with newly registered function', async () => {
const service = new ExpressionsService();
const fork = service.fork();
fork.start();
const fork = service.fork('test');
service.start();
const forkStart = fork.start();
service.registerFunction({
name: '__test__',
@ -129,7 +131,7 @@ describe('ExpressionsService', () => {
},
});
const { result } = await fork.run('__test__', null).toPromise();
const { result } = await forkStart.run('__test__', null).toPromise();
expect(result).toBe('123');
});
@ -138,7 +140,7 @@ describe('ExpressionsService', () => {
const service = new ExpressionsService();
service.start();
expect(() => service.fork()).toThrow();
expect(() => service.fork('test')).toThrow();
});
});

View file

@ -19,7 +19,11 @@ import { ExecutionContract, ExecutionResult } from '../execution';
import { AnyExpressionTypeDefinition, ExpressionValueError } from '../expression_types';
import { AnyExpressionFunctionDefinition } from '../expression_functions';
import { SavedObjectReference } from '../../../../core/types';
import { PersistableStateService, VersionedState } from '../../../kibana_utils/common';
import {
MigrateFunctionsObject,
PersistableStateService,
VersionedState,
} from '../../../kibana_utils/common';
import { Adapters } from '../../../inspector/common/adapters';
import {
clog,
@ -36,6 +40,7 @@ import {
math,
mathColumn,
} from '../expression_functions';
import { ExpressionsServiceFork } from './expressions_fork';
/**
* The public contract that `ExpressionsService` provides to other plugins
@ -49,14 +54,14 @@ export interface ExpressionsServiceSetup {
* service - do not mutate that object.
* @deprecated Use start contract instead.
*/
getFunction(name: string): ReturnType<Executor['getFunction']>;
getFunction(name: string, namespace?: string): ReturnType<Executor['getFunction']>;
/**
* Returns POJO map of all registered expression functions, where keys are
* names of the functions and values are `ExpressionFunction` instances.
* @deprecated Use start contract instead.
*/
getFunctions(): ReturnType<Executor['getFunctions']>;
getFunctions(namespace?: string): ReturnType<Executor['getFunctions']>;
/**
* Returns POJO map of all registered expression types, where keys are
@ -75,7 +80,7 @@ export interface ExpressionsServiceSetup {
* service.
* @param name A fork name that can be used to get fork instance later.
*/
fork(name?: string): ExpressionsService;
fork(namespace: string): ExpressionsServiceFork;
/**
* Register an expression function, which will be possible to execute as
@ -120,6 +125,8 @@ export interface ExpressionsServiceSetup {
registerRenderer(
definition: AnyExpressionRenderDefinition | (() => AnyExpressionRenderDefinition)
): void;
getAllMigrations(): MigrateFunctionsObject;
}
export interface ExpressionExecutionParams {
@ -148,6 +155,8 @@ export interface ExpressionExecutionParams {
inspectorAdapters?: Adapters;
executionContext?: KibanaExecutionContext;
namespace?: string;
}
/**
@ -161,13 +170,13 @@ export interface ExpressionsServiceStart {
* instance is an internal representation of the function in Expressions
* service - do not mutate that object.
*/
getFunction(name: string): ReturnType<Executor['getFunction']>;
getFunction(name: string, namespace?: string): ReturnType<Executor['getFunction']>;
/**
* Returns POJO map of all registered expression functions, where keys are
* names of the functions and values are `ExpressionFunction` instances.
*/
getFunctions(): ReturnType<Executor['getFunctions']>;
getFunctions(namespace?: string): ReturnType<Executor['getFunctions']>;
/**
* Get a registered `ExpressionRenderer` by its name, which was registered
@ -238,6 +247,23 @@ export interface ExpressionsServiceStart {
input: Input,
params?: ExpressionExecutionParams
): ExecutionContract<Input, Output>;
extract(state: ExpressionAstExpression): {
state: ExpressionAstExpression;
references: SavedObjectReference[];
};
inject(
state: ExpressionAstExpression,
references: SavedObjectReference[]
): ExpressionAstExpression;
telemetry(
state: ExpressionAstExpression,
telemetryData: Record<string, unknown>
): Record<string, unknown>;
getAllMigrations(): MigrateFunctionsObject;
}
export interface ExpressionServiceParams {
@ -274,9 +300,6 @@ export class ExpressionsService
* @note Workaround since the expressions service is frozen.
*/
private static started = new WeakSet<ExpressionsService>();
private children = new Map<string, ExpressionsService>();
private parent?: ExpressionsService;
public readonly executor: Executor;
public readonly renderers: ExpressionRendererRegistry;
@ -289,7 +312,7 @@ export class ExpressionsService
}
private isStarted(): boolean {
return !!(ExpressionsService.started.has(this) || this.parent?.isStarted());
return ExpressionsService.started.has(this);
}
private assertSetup() {
@ -304,11 +327,11 @@ export class ExpressionsService
}
}
public readonly getFunction: ExpressionsServiceStart['getFunction'] = (name) =>
this.executor.getFunction(name);
public readonly getFunction: ExpressionsServiceStart['getFunction'] = (name, namespace) =>
this.executor.getFunction(name, namespace);
public readonly getFunctions: ExpressionsServiceStart['getFunctions'] = () =>
this.executor.getFunctions();
public readonly getFunctions: ExpressionsServiceStart['getFunctions'] = (namespace) =>
this.executor.getFunctions(namespace);
public readonly getRenderer: ExpressionsServiceStart['getRenderer'] = (name) => {
this.assertStart();
@ -342,16 +365,7 @@ export class ExpressionsService
public readonly fork: ExpressionsServiceSetup['fork'] = (name) => {
this.assertSetup();
const executor = this.executor.fork();
const renderers = this.renderers;
const fork = new (this.constructor as typeof ExpressionsService)({ executor, renderers });
fork.parent = this;
if (name) {
this.children.set(name, fork);
}
const fork = new ExpressionsServiceFork(name, this);
return fork;
};

View file

@ -11,16 +11,33 @@
* the given object/array for a case-insensitive match, which could be either the
* `name` itself, or something under the `aliases` property.
*/
export function getByAlias<T extends { name?: string; aliases?: string[] }>(
export const ALL_NAMESPACES = '*';
interface Node {
name?: string;
aliases?: string[];
namespace?: string;
}
export function getByAlias<T extends Node>(
node: T[] | Record<string, T>,
nodeName: string
nodeName: string,
nodeNamespace?: string
): T | undefined {
const lowerCaseName = nodeName.toLowerCase();
return Object.values(node).find(({ name, aliases }) => {
return Object.values(node).find(({ name, aliases, namespace }) => {
if (!name) return false;
if (name.toLowerCase() === lowerCaseName) return true;
if (name.toLowerCase() === lowerCaseName) {
if (!namespace || nodeNamespace === ALL_NAMESPACES || namespace === nodeNamespace) {
return true;
}
}
return (aliases || []).some((alias) => {
return alias.toLowerCase() === lowerCaseName;
return (
alias.toLowerCase() === lowerCaseName &&
(!namespace || nodeNamespace === ALL_NAMESPACES || namespace === nodeNamespace)
);
});
});
}

View file

@ -23,6 +23,7 @@ const createSetupContract = (): Setup => {
registerFunction: jest.fn(),
registerRenderer: jest.fn(),
registerType: jest.fn(),
getAllMigrations: jest.fn(),
};
return setupContract;
};
@ -40,6 +41,10 @@ const createStartContract = (): Start => {
render: jest.fn(),
ReactExpressionRenderer: jest.fn((props) => <></>),
run: jest.fn(),
telemetry: jest.fn(),
extract: jest.fn(),
inject: jest.fn(),
getAllMigrations: jest.fn(),
};
};

View file

@ -8,7 +8,7 @@
import { expressionsPluginMock } from './mocks';
import { add } from '../common/test_helpers/expression_functions/add';
import { ExpressionsService } from './services';
import { ExpressionsServiceFork } from '../common/service/expressions_fork';
describe('ExpressionsPublicPlugin', () => {
test('can instantiate from mocks', async () => {
@ -19,9 +19,9 @@ describe('ExpressionsPublicPlugin', () => {
describe('setup contract', () => {
test('.fork() method returns ExpressionsService', async () => {
const { setup } = await expressionsPluginMock.createPlugin();
const fork = setup.fork();
const fork = setup.fork('test');
expect(fork).toBeInstanceOf(ExpressionsService);
expect(fork).toBeInstanceOf(ExpressionsServiceFork);
});
describe('.registerFunction()', () => {

View file

@ -21,6 +21,7 @@ const createSetupContract = (): Setup => ({
registerFunction: jest.fn(),
registerRenderer: jest.fn(),
registerType: jest.fn(),
getAllMigrations: jest.fn(),
});
const createStartContract = (): Start =>
@ -30,6 +31,10 @@ const createStartContract = (): Start =>
getRenderer: jest.fn(),
getType: jest.fn(),
run: jest.fn(),
telemetry: jest.fn(),
extract: jest.fn(),
inject: jest.fn(),
getAllMigrations: jest.fn(),
} as unknown as Start);
const createPlugin = async () => {

View file

@ -52,7 +52,7 @@ export class ExpressionsService {
context?: ExpressionExecutionParams
): Promise<ExpressionValue> {
return await this.expressions
.execute(ast, input, context)
.execute(ast, input, { ...context, namespace: 'canvas' })
.getData()
.pipe(pluck('result'))
.toPromise();

View file

@ -29,12 +29,13 @@ export const expressionsServiceFactory: CanvasExpressionsServiceFactory = (
const placeholder = {} as any;
const expressionsPlugin = plugin(placeholder);
const setup = expressionsPlugin.setup(placeholder);
const expressionsService = setup.fork();
const fork = setup.fork('canvas');
const expressionsService = fork.setup();
functionDefinitions.forEach((fn) => expressionsService.registerFunction(fn));
renderFunctions.forEach((fn) => {
expressionsService.registerRenderer(fn as unknown as AnyExpressionRenderDefinition);
setup.registerRenderer(fn as unknown as AnyExpressionRenderDefinition);
});
return new ExpressionsService(expressionsService, requiredServices);
return new ExpressionsService(fork.start(), requiredServices);
};

View file

@ -52,9 +52,9 @@ export class CanvasPlugin implements Plugin {
}
public setup(coreSetup: CoreSetup<PluginsStart>, plugins: PluginsSetup) {
const expressionsFork = plugins.expressions.fork();
setupInterpreter(expressionsFork, {
const expressionsFork = plugins.expressions.fork('canvas');
const expressionsSetup = expressionsFork.setup();
setupInterpreter(expressionsSetup, {
embeddablePersistableStateService: {
extract: plugins.embeddable.extract,
inject: plugins.embeddable.inject,
@ -62,7 +62,7 @@ export class CanvasPlugin implements Plugin {
},
});
const deps: CanvasSavedObjectTypeMigrationsDeps = { expressions: expressionsFork };
const deps: CanvasSavedObjectTypeMigrationsDeps = { expressions: expressionsSetup };
coreSetup.uiSettings.register(getUISettings());
coreSetup.savedObjects.registerType(customElementType(deps));
coreSetup.savedObjects.registerType(workpadTypeFactory(deps));
@ -70,7 +70,8 @@ export class CanvasPlugin implements Plugin {
plugins.features.registerKibanaFeature(getCanvasFeature(plugins));
const contextProvider = createWorkpadRouteContext({ expressions: expressionsFork });
const expressionsStart = expressionsFork.start();
const contextProvider = createWorkpadRouteContext({ expressions: expressionsStart });
coreSetup.http.registerRouteHandlerContext<CanvasRouteHandlerContext, 'canvas'>(
'canvas',
contextProvider
@ -80,7 +81,7 @@ export class CanvasPlugin implements Plugin {
initRoutes({
router: canvasRouter,
expressions: expressionsFork,
expressions: expressionsSetup,
bfetch: plugins.bfetch,
logger: this.logger,
});

View file

@ -23,7 +23,7 @@ export function initializeGetFunctionsRoute(deps: RouteInitializerDeps) {
validate: false,
},
async (context, request, response) => {
const functions = expressions.getFunctions();
const functions = expressions.getFunctions('canvas');
const body = JSON.stringify(functions);
return response.ok({
body,
@ -39,7 +39,7 @@ export function initializeBatchFunctionsRoute(deps: RouteInitializerDeps) {
const { functionName, args, context } = fnCall;
const { deserialize } = serializeProvider(expressions.getTypes());
const fnDef = expressions.getFunctions()[functionName];
const fnDef = expressions.getFunctions('canvas')[functionName];
if (!fnDef) throw new Error(`Function "${functionName}" could not be found.`);
const deserialized = deserialize(context);

View file

@ -4,8 +4,8 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { ExpressionsService } from 'src/plugins/expressions/public';
import { ExpressionsServiceSetup } from 'src/plugins/expressions/public';
export interface CanvasSavedObjectTypeMigrationsDeps {
expressions: ExpressionsService;
expressions: ExpressionsServiceSetup;
}

View file

@ -11,7 +11,7 @@ import {
SavedObject,
SavedObjectsResolveResponse,
} from 'kibana/server';
import { ExpressionsService } from 'src/plugins/expressions';
import { ExpressionsServiceStart } from 'src/plugins/expressions';
import { WorkpadAttributes } from './routes/workpad/workpad_attributes';
import { CANVAS_TYPE } from '../common/lib/constants';
import { injectReferences, extractReferences } from './saved_objects/workpad_references';
@ -34,7 +34,7 @@ export interface CanvasRouteHandlerContext extends RequestHandlerContext {
}
interface Deps {
expressions: ExpressionsService;
expressions: ExpressionsServiceStart;
}
export const createWorkpadRouteContext: (