diff --git a/examples/partial_results_example/public/plugin.tsx b/examples/partial_results_example/public/plugin.tsx index aa209a87918e7..9599a0d6f590c 100755 --- a/examples/partial_results_example/public/plugin.tsx +++ b/examples/partial_results_example/public/plugin.tsx @@ -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 { - 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 { ReactDOM.render( - + , element diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts index 7cc7fa771d8c8..d3e2324b276a2 100644 --- a/src/plugins/expressions/common/execution/execution.ts +++ b/src/plugins/expressions/common/execution/execution.ts @@ -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({ diff --git a/src/plugins/expressions/common/executor/executor.ts b/src/plugins/expressions/common/executor/executor.ts index 722083e1c739a..e9398f1d17c58 100644 --- a/src/plugins/expressions/common/executor/executor.ts +++ b/src/plugins/expressions/common/executor/executor.ts @@ -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 = Record { - return { - ...(this.parent?.getFunctions() ?? {}), - ...this.container.get().functions, - }; + public getFunctions(namespace?: string): Record { + 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 = Record 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 = Record ExpressionAstFunction | ExpressionAstExpression ): ExpressionAstExpression { let additionalFunctions = 0; + const functions = this.container.get().functions; return ( ast.chain.reduce( (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 = Record Object.keys(fn.migrations)) .flat(1) ); diff --git a/src/plugins/expressions/common/expression_functions/expression_function.ts b/src/plugins/expressions/common/expression_functions/expression_function.ts index 4ae51ff018f1b..951a5aab37961 100644 --- a/src/plugins/expressions/common/expression_functions/expression_function.ts +++ b/src/plugins/expressions/common/expression_functions/expression_function.ts @@ -24,6 +24,8 @@ export class ExpressionFunction implements PersistableState { 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 { public readonly render: ExpressionRenderDefinition['render']; constructor(config: ExpressionRenderDefinition) { - 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 || (() => {}); diff --git a/src/plugins/expressions/common/expression_renderers/types.ts b/src/plugins/expressions/common/expression_renderers/types.ts index 6c889a81a1f80..1e40ba2a65fff 100644 --- a/src/plugins/expressions/common/expression_renderers/types.ts +++ b/src/plugins/expressions/common/expression_renderers/types.ts @@ -15,6 +15,7 @@ export interface ExpressionRenderDefinition { * 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. diff --git a/src/plugins/expressions/common/expression_types/expression_type.ts b/src/plugins/expressions/common/expression_types/expression_type.ts index bf8a96a4ab1e6..59afd5e94a7b8 100644 --- a/src/plugins/expressions/common/expression_types/expression_type.ts +++ b/src/plugins/expressions/common/expression_types/expression_type.ts @@ -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 || (() => {}); diff --git a/src/plugins/expressions/common/expression_types/types.ts b/src/plugins/expressions/common/expression_types/types.ts index 15ec82e40314d..753ff047c8779 100644 --- a/src/plugins/expressions/common/expression_types/types.ts +++ b/src/plugins/expressions/common/expression_types/types.ts @@ -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; diff --git a/src/plugins/expressions/common/service/expressions_fork.ts b/src/plugins/expressions/common/service/expressions_fork.ts new file mode 100644 index 0000000000000..b1fb81add183e --- /dev/null +++ b/src/plugins/expressions/common/service/expressions_fork.ts @@ -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 + * ): ReturnType => 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( + ast: string | ExpressionAstExpression, + input: Input, + params?: ExpressionExecutionParams + ) { + return this.expressionsService.run(ast, input, { + ...params, + namespace: this.namespace, + }); + } + + protected execute( + ast: string | ExpressionAstExpression, + input: Input, + params?: ExpressionExecutionParams + ) { + return this.expressionsService.execute(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, + }; + } +} diff --git a/src/plugins/expressions/common/service/expressions_services.test.ts b/src/plugins/expressions/common/service/expressions_services.test.ts index 620917dc64d4d..8e83cd8e55651 100644 --- a/src/plugins/expressions/common/service/expressions_services.test.ts +++ b/src/plugins/expressions/common/service/expressions_services.test.ts @@ -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(); }); }); diff --git a/src/plugins/expressions/common/service/expressions_services.ts b/src/plugins/expressions/common/service/expressions_services.ts index 416edbea61dfe..f15bb49e4ecf9 100644 --- a/src/plugins/expressions/common/service/expressions_services.ts +++ b/src/plugins/expressions/common/service/expressions_services.ts @@ -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; + getFunction(name: string, namespace?: string): ReturnType; /** * 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; + getFunctions(namespace?: string): ReturnType; /** * 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; + getFunction(name: string, namespace?: string): ReturnType; /** * Returns POJO map of all registered expression functions, where keys are * names of the functions and values are `ExpressionFunction` instances. */ - getFunctions(): ReturnType; + getFunctions(namespace?: string): ReturnType; /** * Get a registered `ExpressionRenderer` by its name, which was registered @@ -238,6 +247,23 @@ export interface ExpressionsServiceStart { input: Input, params?: ExpressionExecutionParams ): ExecutionContract; + + extract(state: ExpressionAstExpression): { + state: ExpressionAstExpression; + references: SavedObjectReference[]; + }; + + inject( + state: ExpressionAstExpression, + references: SavedObjectReference[] + ): ExpressionAstExpression; + + telemetry( + state: ExpressionAstExpression, + telemetryData: Record + ): Record; + + 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(); - private children = new Map(); - 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; }; diff --git a/src/plugins/expressions/common/util/get_by_alias.ts b/src/plugins/expressions/common/util/get_by_alias.ts index 6c6371cccc80f..dec630d27936e 100644 --- a/src/plugins/expressions/common/util/get_by_alias.ts +++ b/src/plugins/expressions/common/util/get_by_alias.ts @@ -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( + +export const ALL_NAMESPACES = '*'; + +interface Node { + name?: string; + aliases?: string[]; + namespace?: string; +} + +export function getByAlias( node: T[] | Record, - 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) + ); }); }); } diff --git a/src/plugins/expressions/public/mocks.tsx b/src/plugins/expressions/public/mocks.tsx index 353ae2908b54c..bf5daab911efe 100644 --- a/src/plugins/expressions/public/mocks.tsx +++ b/src/plugins/expressions/public/mocks.tsx @@ -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(), }; }; diff --git a/src/plugins/expressions/public/plugin.test.ts b/src/plugins/expressions/public/plugin.test.ts index 61ff0d8b54033..f3bab4e2732e4 100644 --- a/src/plugins/expressions/public/plugin.test.ts +++ b/src/plugins/expressions/public/plugin.test.ts @@ -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()', () => { diff --git a/src/plugins/expressions/server/mocks.ts b/src/plugins/expressions/server/mocks.ts index bf36ab3c5daa9..cb6392e04fc9e 100644 --- a/src/plugins/expressions/server/mocks.ts +++ b/src/plugins/expressions/server/mocks.ts @@ -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 () => { diff --git a/x-pack/plugins/canvas/public/services/kibana/expressions.ts b/x-pack/plugins/canvas/public/services/kibana/expressions.ts index ea329b63863f8..6174c56aa2b71 100644 --- a/x-pack/plugins/canvas/public/services/kibana/expressions.ts +++ b/x-pack/plugins/canvas/public/services/kibana/expressions.ts @@ -52,7 +52,7 @@ export class ExpressionsService { context?: ExpressionExecutionParams ): Promise { return await this.expressions - .execute(ast, input, context) + .execute(ast, input, { ...context, namespace: 'canvas' }) .getData() .pipe(pluck('result')) .toPromise(); diff --git a/x-pack/plugins/canvas/public/services/stubs/expressions.ts b/x-pack/plugins/canvas/public/services/stubs/expressions.ts index 405f2ebe0ba91..776b553b75d55 100644 --- a/x-pack/plugins/canvas/public/services/stubs/expressions.ts +++ b/x-pack/plugins/canvas/public/services/stubs/expressions.ts @@ -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); }; diff --git a/x-pack/plugins/canvas/server/plugin.ts b/x-pack/plugins/canvas/server/plugin.ts index 172d1e8dd8bf1..de4d452508cf2 100644 --- a/x-pack/plugins/canvas/server/plugin.ts +++ b/x-pack/plugins/canvas/server/plugin.ts @@ -52,9 +52,9 @@ export class CanvasPlugin implements Plugin { } public setup(coreSetup: CoreSetup, 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( 'canvas', contextProvider @@ -80,7 +81,7 @@ export class CanvasPlugin implements Plugin { initRoutes({ router: canvasRouter, - expressions: expressionsFork, + expressions: expressionsSetup, bfetch: plugins.bfetch, logger: this.logger, }); diff --git a/x-pack/plugins/canvas/server/routes/functions/functions.ts b/x-pack/plugins/canvas/server/routes/functions/functions.ts index ed1559034c3a6..47b35a4f6c17b 100644 --- a/x-pack/plugins/canvas/server/routes/functions/functions.ts +++ b/x-pack/plugins/canvas/server/routes/functions/functions.ts @@ -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); diff --git a/x-pack/plugins/canvas/server/saved_objects/migrations/types.ts b/x-pack/plugins/canvas/server/saved_objects/migrations/types.ts index 18ce0bd88cb69..0b7c26d69f7eb 100644 --- a/x-pack/plugins/canvas/server/saved_objects/migrations/types.ts +++ b/x-pack/plugins/canvas/server/saved_objects/migrations/types.ts @@ -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; } diff --git a/x-pack/plugins/canvas/server/workpad_route_context.ts b/x-pack/plugins/canvas/server/workpad_route_context.ts index d7c818b786e32..2e4cad231dd84 100644 --- a/x-pack/plugins/canvas/server/workpad_route_context.ts +++ b/x-pack/plugins/canvas/server/workpad_route_context.ts @@ -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: (