From 2f5306ea425349264d6137a6ef3bb0b647174e35 Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Wed, 4 Sep 2019 14:09:38 +0200 Subject: [PATCH] expression service (#42337) --- .../kbn-interpreter/src/common/index.d.ts | 2 +- .../kbn-interpreter/src/common/lib/ast.d.ts | 1 + .../expressions/expression_renderer.tsx | 39 ++- .../public/expressions/expression_runner.ts | 75 ----- .../expressions/expressions_service.test.tsx | 276 ------------------ .../public/expressions/expressions_service.ts | 125 +++----- .../data/public/expressions/index.ts | 3 +- .../data/public/expressions/lib/_types.ts | 86 ++++++ .../public/expressions/lib/execute.test.ts | 94 ++++++ .../data/public/expressions/lib/execute.ts | 115 ++++++++ .../public/expressions/lib/loader.test.ts | 118 ++++++++ .../data/public/expressions/lib/loader.ts | 130 +++++++++ .../public/expressions/lib/render.test.ts | 88 ++++++ .../data/public/expressions/lib/render.ts | 99 +++++++ .../data/public/expressions/services.ts | 41 +++ src/legacy/core_plugins/data/public/index.ts | 6 +- src/legacy/core_plugins/data/public/legacy.ts | 13 +- src/legacy/core_plugins/data/public/plugin.ts | 30 +- 18 files changed, 867 insertions(+), 474 deletions(-) delete mode 100644 src/legacy/core_plugins/data/public/expressions/expression_runner.ts delete mode 100644 src/legacy/core_plugins/data/public/expressions/expressions_service.test.tsx create mode 100644 src/legacy/core_plugins/data/public/expressions/lib/_types.ts create mode 100644 src/legacy/core_plugins/data/public/expressions/lib/execute.test.ts create mode 100644 src/legacy/core_plugins/data/public/expressions/lib/execute.ts create mode 100644 src/legacy/core_plugins/data/public/expressions/lib/loader.test.ts create mode 100644 src/legacy/core_plugins/data/public/expressions/lib/loader.ts create mode 100644 src/legacy/core_plugins/data/public/expressions/lib/render.test.ts create mode 100644 src/legacy/core_plugins/data/public/expressions/lib/render.ts create mode 100644 src/legacy/core_plugins/data/public/expressions/services.ts diff --git a/packages/kbn-interpreter/src/common/index.d.ts b/packages/kbn-interpreter/src/common/index.d.ts index a8917b7a65df..7201ccbb3563 100644 --- a/packages/kbn-interpreter/src/common/index.d.ts +++ b/packages/kbn-interpreter/src/common/index.d.ts @@ -19,4 +19,4 @@ export { Registry } from './lib/registry'; -export { fromExpression, Ast } from './lib/ast'; +export { fromExpression, toExpression, Ast } from './lib/ast'; diff --git a/packages/kbn-interpreter/src/common/lib/ast.d.ts b/packages/kbn-interpreter/src/common/lib/ast.d.ts index 2b0328bda939..a4ee23535946 100644 --- a/packages/kbn-interpreter/src/common/lib/ast.d.ts +++ b/packages/kbn-interpreter/src/common/lib/ast.d.ts @@ -20,3 +20,4 @@ export type Ast = unknown; export declare function fromExpression(expression: string): Ast; +export declare function toExpression(astObj: Ast, type?: string): string; diff --git a/src/legacy/core_plugins/data/public/expressions/expression_renderer.tsx b/src/legacy/core_plugins/data/public/expressions/expression_renderer.tsx index 310a3b11a845..e5358acc1c05 100644 --- a/src/legacy/core_plugins/data/public/expressions/expression_renderer.tsx +++ b/src/legacy/core_plugins/data/public/expressions/expression_renderer.tsx @@ -19,47 +19,60 @@ import { useRef, useEffect } from 'react'; import React from 'react'; -import { Ast } from '@kbn/interpreter/common'; -import { ExpressionRunnerOptions, ExpressionRunner } from './expression_runner'; -import { Result } from './expressions_service'; +import { ExpressionAST, IExpressionLoaderParams, IInterpreterResult } from './lib/_types'; +import { IExpressionLoader, ExpressionLoader } from './lib/loader'; // Accept all options of the runner as props except for the // dom element which is provided by the component itself -export type ExpressionRendererProps = Pick< - ExpressionRunnerOptions, - Exclude -> & { - expression: string | Ast; +export interface ExpressionRendererProps extends IExpressionLoaderParams { + className: 'string'; + expression: string | ExpressionAST; /** * If an element is specified, but the response of the expression run can't be rendered * because it isn't a valid response or the specified renderer isn't available, * this callback is called with the given result. */ - onRenderFailure?: (result: Result) => void; -}; + onRenderFailure?: (result: IInterpreterResult) => void; +} export type ExpressionRenderer = React.FC; -export const createRenderer = (run: ExpressionRunner): ExpressionRenderer => ({ +export const createRenderer = (loader: IExpressionLoader): ExpressionRenderer => ({ + className, expression, onRenderFailure, ...options }: ExpressionRendererProps) => { const mountpoint: React.MutableRefObject = useRef(null); + const handlerRef: React.MutableRefObject = useRef(null); + useEffect(() => { if (mountpoint.current) { - run(expression, { ...options, element: mountpoint.current }).catch(result => { + if (!handlerRef.current) { + handlerRef.current = loader(mountpoint.current, expression, options); + } else { + handlerRef.current.update(expression, options); + } + handlerRef.current.data$.toPromise().catch(result => { if (onRenderFailure) { onRenderFailure(result); } }); } - }, [expression, mountpoint.current]); + }, [ + expression, + options.searchContext, + options.context, + options.variables, + options.disableCaching, + mountpoint.current, + ]); return (
{ mountpoint.current = el; }} diff --git a/src/legacy/core_plugins/data/public/expressions/expression_runner.ts b/src/legacy/core_plugins/data/public/expressions/expression_runner.ts deleted file mode 100644 index 86a36c9409a4..000000000000 --- a/src/legacy/core_plugins/data/public/expressions/expression_runner.ts +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { Ast, fromExpression } from '@kbn/interpreter/common'; - -import { RequestAdapter, DataAdapter } from '../../../../../plugins/inspector/public'; -import { RenderFunctionsRegistry, Interpreter, Result } from './expressions_service'; - -export interface ExpressionRunnerOptions { - // TODO use the real types here once they are ready - context?: object; - getInitialContext?: () => object; - element?: Element; -} - -export type ExpressionRunner = ( - expression: string | Ast, - options: ExpressionRunnerOptions -) => Promise; - -export const createRunFn = ( - renderersRegistry: RenderFunctionsRegistry, - interpreterPromise: Promise -): ExpressionRunner => async (expressionOrAst, { element, context, getInitialContext }) => { - // TODO: make interpreter initialization synchronous to avoid this - const interpreter = await interpreterPromise; - const ast = - typeof expressionOrAst === 'string' ? fromExpression(expressionOrAst) : expressionOrAst; - - const response = await interpreter.interpretAst(ast, context || { type: 'null' }, { - getInitialContext: getInitialContext || (() => ({})), - inspectorAdapters: { - // TODO connect real adapters - requests: new RequestAdapter(), - data: new DataAdapter(), - }, - }); - - if (response.type === 'error') { - throw response; - } - - if (element) { - if (response.type === 'render' && response.as && renderersRegistry.get(response.as) !== null) { - renderersRegistry.get(response.as).render(element, response.value, { - onDestroy: fn => { - // TODO implement - }, - done: () => { - // TODO implement - }, - }); - } else { - throw response; - } - } - - return response; -}; diff --git a/src/legacy/core_plugins/data/public/expressions/expressions_service.test.tsx b/src/legacy/core_plugins/data/public/expressions/expressions_service.test.tsx deleted file mode 100644 index 83313e5a2b2f..000000000000 --- a/src/legacy/core_plugins/data/public/expressions/expressions_service.test.tsx +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { fromExpression, Ast } from '@kbn/interpreter/common'; - -import { - ExpressionsService, - RenderFunctionsRegistry, - RenderFunction, - Interpreter, - ExpressionsServiceDependencies, - Result, - ExpressionsSetup, -} from './expressions_service'; -import { mount } from 'enzyme'; -import React from 'react'; - -const waitForInterpreterRun = async () => { - // Wait for two ticks with empty callback queues - // This makes sure the runFn promise and actual interpretAst - // promise have been resolved and processed - await new Promise(resolve => setTimeout(resolve)); - await new Promise(resolve => setTimeout(resolve)); -}; - -const RENDERER_ID = 'mockId'; - -describe('expressions_service', () => { - let interpretAstMock: jest.Mocked['interpretAst']; - let interpreterMock: jest.Mocked; - let renderFunctionMock: jest.Mocked; - let setupPluginsMock: ExpressionsServiceDependencies; - const expressionResult: Result = { type: 'render', as: RENDERER_ID, value: {} }; - - let api: ExpressionsSetup; - let testExpression: string; - let testAst: Ast; - - beforeEach(() => { - interpretAstMock = jest.fn((..._) => Promise.resolve(expressionResult)); - interpreterMock = { interpretAst: interpretAstMock }; - renderFunctionMock = ({ - render: jest.fn(), - } as unknown) as jest.Mocked; - setupPluginsMock = { - interpreter: { - getInterpreter: () => Promise.resolve({ interpreter: interpreterMock }), - renderersRegistry: ({ - get: (id: string) => (id === RENDERER_ID ? renderFunctionMock : null), - } as unknown) as RenderFunctionsRegistry, - }, - }; - api = new ExpressionsService().setup(setupPluginsMock); - testExpression = 'test | expression'; - testAst = fromExpression(testExpression); - }); - - describe('expression_runner', () => { - it('should return run function', () => { - expect(typeof api.run).toBe('function'); - }); - - it('should call the interpreter with parsed expression', async () => { - await api.run(testExpression, { element: document.createElement('div') }); - expect(interpreterMock.interpretAst).toHaveBeenCalledWith( - testAst, - expect.anything(), - expect.anything() - ); - }); - - it('should call the interpreter with given context and getInitialContext functions', async () => { - const getInitialContext = () => ({}); - const context = {}; - - await api.run(testExpression, { getInitialContext, context }); - const interpretCall = interpreterMock.interpretAst.mock.calls[0]; - - expect(interpretCall[1]).toBe(context); - expect(interpretCall[2].getInitialContext).toBe(getInitialContext); - }); - - it('should call the interpreter with passed in ast', async () => { - await api.run(testAst, { element: document.createElement('div') }); - expect(interpreterMock.interpretAst).toHaveBeenCalledWith( - testAst, - expect.anything(), - expect.anything() - ); - }); - - it('should return the result of the interpreter run', async () => { - const response = await api.run(testAst, {}); - expect(response).toBe(expressionResult); - }); - - it('should reject the promise if the response is not renderable but an element is passed', async () => { - const unexpectedResult = { type: 'datatable', value: {} }; - interpretAstMock.mockReturnValue(Promise.resolve(unexpectedResult)); - expect( - api.run(testAst, { - element: document.createElement('div'), - }) - ).rejects.toBe(unexpectedResult); - }); - - it('should reject the promise if the renderer is not known', async () => { - const unexpectedResult = { type: 'render', as: 'unknown_id' }; - interpretAstMock.mockReturnValue(Promise.resolve(unexpectedResult)); - expect( - api.run(testAst, { - element: document.createElement('div'), - }) - ).rejects.toBe(unexpectedResult); - }); - - it('should not reject the promise on unknown renderer if the runner is not rendering', async () => { - const unexpectedResult = { type: 'render', as: 'unknown_id' }; - interpretAstMock.mockReturnValue(Promise.resolve(unexpectedResult)); - expect(api.run(testAst, {})).resolves.toBe(unexpectedResult); - }); - - it('should reject the promise if the response is an error', async () => { - const errorResult = { type: 'error', error: {} }; - interpretAstMock.mockReturnValue(Promise.resolve(errorResult)); - expect(api.run(testAst, {})).rejects.toBe(errorResult); - }); - - it('should reject the promise if there are syntax errors', async () => { - expect(api.run('|||', {})).rejects.toBeInstanceOf(Error); - }); - - it('should call the render function with the result and element', async () => { - const element = document.createElement('div'); - - await api.run(testAst, { element }); - expect(renderFunctionMock.render).toHaveBeenCalledWith( - element, - expressionResult.value, - expect.anything() - ); - expect(interpreterMock.interpretAst).toHaveBeenCalledWith( - testAst, - expect.anything(), - expect.anything() - ); - }); - }); - - describe('expression_renderer', () => { - it('should call interpreter and render function when called through react component', async () => { - const ExpressionRenderer = api.ExpressionRenderer; - - mount(); - - await waitForInterpreterRun(); - - expect(renderFunctionMock.render).toHaveBeenCalledWith( - expect.any(Element), - expressionResult.value, - expect.anything() - ); - expect(interpreterMock.interpretAst).toHaveBeenCalledWith( - testAst, - expect.anything(), - expect.anything() - ); - }); - - it('should call the interpreter with given context and getInitialContext functions', async () => { - const getInitialContext = () => ({}); - const context = {}; - - const ExpressionRenderer = api.ExpressionRenderer; - - mount( - - ); - - await waitForInterpreterRun(); - - const interpretCall = interpreterMock.interpretAst.mock.calls[0]; - - expect(interpretCall[1]).toBe(context); - expect(interpretCall[2].getInitialContext).toBe(getInitialContext); - }); - - it('should call interpreter and render function again if expression changes', async () => { - const ExpressionRenderer = api.ExpressionRenderer; - - const instance = mount(); - - await waitForInterpreterRun(); - - expect(renderFunctionMock.render).toHaveBeenCalledWith( - expect.any(Element), - expressionResult.value, - expect.anything() - ); - expect(interpreterMock.interpretAst).toHaveBeenCalledWith( - testAst, - expect.anything(), - expect.anything() - ); - - instance.setProps({ expression: 'supertest | expression ' }); - - await waitForInterpreterRun(); - - expect(renderFunctionMock.render).toHaveBeenCalledTimes(2); - expect(interpreterMock.interpretAst).toHaveBeenCalledTimes(2); - }); - - it('should not call interpreter and render function again if expression does not change', async () => { - const ast = fromExpression(testExpression); - - const ExpressionRenderer = api.ExpressionRenderer; - - const instance = mount(); - - await waitForInterpreterRun(); - - expect(renderFunctionMock.render).toHaveBeenCalledWith( - expect.any(Element), - expressionResult.value, - expect.anything() - ); - expect(interpreterMock.interpretAst).toHaveBeenCalledWith( - ast, - expect.anything(), - expect.anything() - ); - - instance.update(); - - await waitForInterpreterRun(); - - expect(renderFunctionMock.render).toHaveBeenCalledTimes(1); - expect(interpreterMock.interpretAst).toHaveBeenCalledTimes(1); - }); - - it('should call onRenderFailure if the result can not be rendered', async () => { - const errorResult = { type: 'error', error: {} }; - interpretAstMock.mockReturnValue(Promise.resolve(errorResult)); - const renderFailureSpy = jest.fn(); - - const ExpressionRenderer = api.ExpressionRenderer; - - mount(); - - await waitForInterpreterRun(); - - expect(renderFailureSpy).toHaveBeenCalledWith(errorResult); - }); - }); -}); diff --git a/src/legacy/core_plugins/data/public/expressions/expressions_service.ts b/src/legacy/core_plugins/data/public/expressions/expressions_service.ts index fc4cd853afe1..a00512f04c1a 100644 --- a/src/legacy/core_plugins/data/public/expressions/expressions_service.ts +++ b/src/legacy/core_plugins/data/public/expressions/expressions_service.ts @@ -17,107 +17,51 @@ * under the License. */ -import { Ast } from '@kbn/interpreter/common'; +import { npSetup } from 'ui/new_platform'; +// @ts-ignore -// TODO: -// this type import and the types below them should be switched to the types of -// the interpreter plugin itself once they are ready -import { Registry } from '@kbn/interpreter/common'; -import { Adapters } from 'src/plugins/inspector/public'; -import { Filter } from '@kbn/es-query'; -import { TimeRange } from 'src/plugins/data/public'; +import { setInspector, setInterpreter } from './services'; +import { execute } from './lib/execute'; +import { loader } from './lib/loader'; +import { render } from './lib/render'; import { createRenderer } from './expression_renderer'; -import { createRunFn } from './expression_runner'; -import { Query } from '../query'; -export interface InitialContextObject { - timeRange?: TimeRange; - filters?: Filter[]; - query?: Query; +import { Start as IInspector } from '../../../../../plugins/inspector/public'; + +export interface ExpressionsServiceStartDependencies { + inspector: IInspector; } - -export type getInitialContextFunction = () => InitialContextObject; - -export interface Handlers { - getInitialContext: getInitialContextFunction; - inspectorAdapters?: Adapters; - abortSignal?: AbortSignal; -} - -type Context = object; -export interface Result { - type: string; - as?: string; - value?: unknown; - error?: unknown; -} - -interface RenderHandlers { - done: () => void; - onDestroy: (fn: () => void) => void; -} - -export interface RenderFunction { - name: string; - displayName: string; - help: string; - validate: () => void; - reuseDomNode: boolean; - render: (domNode: Element, data: unknown, handlers: RenderHandlers) => void; -} - -export type RenderFunctionsRegistry = Registry; - -export interface Interpreter { - interpretAst(ast: Ast, context: Context, handlers: Handlers): Promise; -} - -type InterpreterGetter = () => Promise<{ interpreter: Interpreter }>; - -export interface ExpressionsServiceDependencies { - interpreter: { - renderersRegistry: RenderFunctionsRegistry; - getInterpreter: InterpreterGetter; - }; -} - /** * Expressions Service * @internal */ export class ExpressionsService { - public setup({ - interpreter: { renderersRegistry, getInterpreter }, - }: ExpressionsServiceDependencies) { - const run = createRunFn( - renderersRegistry, - getInterpreter().then(({ interpreter }) => interpreter) - ); + public setup() { + // eslint-disable-next-line + const { getInterpreter } = require('../../../interpreter/public/interpreter'); + getInterpreter() + .then(setInterpreter) + .catch((e: Error) => { + throw new Error('interpreter is not initialized'); + }); return { - /** - * **experimential** This API is experimential and might be removed in the future - * without notice - * - * Executes the given expression string or ast and renders the result into the - * given DOM element. - * - * - * @param expressionOrAst - * @param element - */ - run, - /** - * **experimential** This API is experimential and might be removed in the future - * without notice - * - * Component which executes and renders the given expression in a div element. - * The expression is re-executed on updating the props. - * - * This is a React bridge of the `run` method - * @param props - */ - ExpressionRenderer: createRenderer(run), + registerType: npSetup.plugins.data.expressions.registerType, + registerFunction: npSetup.plugins.data.expressions.registerFunction, + registerRenderer: npSetup.plugins.data.expressions.registerRenderer, + }; + } + + public start({ inspector }: ExpressionsServiceStartDependencies) { + const ExpressionRenderer = createRenderer(loader); + setInspector(inspector); + + return { + execute, + render, + loader, + + ExpressionRenderer, }; } @@ -128,3 +72,4 @@ export class ExpressionsService { /** @public */ export type ExpressionsSetup = ReturnType; +export type ExpressionsStart = ReturnType; diff --git a/src/legacy/core_plugins/data/public/expressions/index.ts b/src/legacy/core_plugins/data/public/expressions/index.ts index fceefce44f81..ae51804a85fb 100644 --- a/src/legacy/core_plugins/data/public/expressions/index.ts +++ b/src/legacy/core_plugins/data/public/expressions/index.ts @@ -17,6 +17,5 @@ * under the License. */ -export { ExpressionsService, ExpressionsSetup } from './expressions_service'; +export { ExpressionsService, ExpressionsSetup, ExpressionsStart } from './expressions_service'; export { ExpressionRenderer, ExpressionRendererProps } from './expression_renderer'; -export { ExpressionRunner } from './expression_runner'; diff --git a/src/legacy/core_plugins/data/public/expressions/lib/_types.ts b/src/legacy/core_plugins/data/public/expressions/lib/_types.ts new file mode 100644 index 000000000000..b3185fda2c17 --- /dev/null +++ b/src/legacy/core_plugins/data/public/expressions/lib/_types.ts @@ -0,0 +1,86 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { TimeRange } from 'src/plugins/data/public'; +import { Filter } from '@kbn/es-query'; +import { Adapters } from '../../../../../ui/public/inspector'; +import { Query } from '../../query'; +import { ExpressionAST } from '../../../../../../plugins/data/common/expressions/types'; + +export { ExpressionAST, TimeRange, Adapters, Filter, Query }; + +export type RenderId = number; +export type Data = any; +export type event = any; +export type Context = object; + +export interface SearchContext { + type: 'kibana_context'; + filters?: Filter[]; + query?: Query; + timeRange?: TimeRange; +} + +export type IGetInitialContext = () => SearchContext | Context; + +export interface IExpressionLoaderParams { + searchContext?: SearchContext; + context?: Context; + variables?: Record; + disableCaching?: boolean; + customFunctions?: []; + customRenderers?: []; +} + +export interface IInterpreterHandlers { + getInitialContext: IGetInitialContext; + inspectorAdapters?: Adapters; +} + +export interface IInterpreterResult { + type: string; + as?: string; + value?: unknown; + error?: unknown; +} + +export interface IInterpreterRenderHandlers { + done: () => void; + onDestroy: (fn: () => void) => void; + reload: () => void; + update: (params: any) => void; + event: (event: event) => void; +} + +export interface IInterpreterRenderFunction { + name: string; + displayName: string; + help: string; + validate: () => void; + reuseDomNode: boolean; + render: (domNode: Element, data: unknown, handlers: IInterpreterRenderHandlers) => void; +} + +export interface IInterpreter { + interpretAst( + ast: ExpressionAST, + context: Context, + handlers: IInterpreterHandlers + ): Promise; +} diff --git a/src/legacy/core_plugins/data/public/expressions/lib/execute.test.ts b/src/legacy/core_plugins/data/public/expressions/lib/execute.test.ts new file mode 100644 index 000000000000..978d0c834ca8 --- /dev/null +++ b/src/legacy/core_plugins/data/public/expressions/lib/execute.test.ts @@ -0,0 +1,94 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { execute, ExpressionDataHandler } from './execute'; +import { fromExpression } from '@kbn/interpreter/common'; +import { ExpressionAST } from '../../../../../../plugins/data/common/expressions/types'; + +jest.mock('../services', () => ({ + getInterpreter: () => { + return { + interpretAst: async (expression: ExpressionAST) => { + return {}; + }, + }; + }, +})); + +describe('execute helper function', () => { + it('returns ExpressionDataHandler instance', () => { + const response = execute(''); + expect(response).toBeInstanceOf(ExpressionDataHandler); + }); +}); + +describe('ExpressionDataHandler', () => { + const expressionString = ''; + + describe('constructor', () => { + it('accepts expression string', () => { + const expressionDataHandler = new ExpressionDataHandler(expressionString, {}); + expect(expressionDataHandler.getExpression()).toEqual(expressionString); + }); + + it('accepts expression AST', () => { + const expressionAST = fromExpression(expressionString) as ExpressionAST; + const expressionDataHandler = new ExpressionDataHandler(expressionAST, {}); + expect(expressionDataHandler.getExpression()).toEqual(expressionString); + expect(expressionDataHandler.getAst()).toEqual(expressionAST); + }); + + it('allows passing in context', () => { + const expressionDataHandler = new ExpressionDataHandler(expressionString, { + context: { test: 'hello' }, + }); + expect(expressionDataHandler.getExpression()).toEqual(expressionString); + }); + + it('allows passing in search context', () => { + const expressionDataHandler = new ExpressionDataHandler(expressionString, { + searchContext: { type: 'kibana_context', filters: [] }, + }); + expect(expressionDataHandler.getExpression()).toEqual(expressionString); + }); + }); + + describe('getData()', () => { + it('returns a promise', () => { + const expressionDataHandler = new ExpressionDataHandler(expressionString, {}); + expect(expressionDataHandler.getData()).toBeInstanceOf(Promise); + }); + + it('promise resolves with data', async () => { + const expressionDataHandler = new ExpressionDataHandler(expressionString, {}); + expect(await expressionDataHandler.getData()).toEqual({}); + }); + }); + + it('cancel() aborts request', () => { + const expressionDataHandler = new ExpressionDataHandler(expressionString, {}); + expressionDataHandler.cancel(); + }); + + it('inspect() returns correct inspector adapters', () => { + const expressionDataHandler = new ExpressionDataHandler(expressionString, {}); + expect(expressionDataHandler.inspect()).toHaveProperty('requests'); + expect(expressionDataHandler.inspect()).toHaveProperty('data'); + }); +}); diff --git a/src/legacy/core_plugins/data/public/expressions/lib/execute.ts b/src/legacy/core_plugins/data/public/expressions/lib/execute.ts new file mode 100644 index 000000000000..4cc1cead3e03 --- /dev/null +++ b/src/legacy/core_plugins/data/public/expressions/lib/execute.ts @@ -0,0 +1,115 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { fromExpression } from '@kbn/interpreter/target/common'; +import { DataAdapter, RequestAdapter, Adapters } from '../../../../../../plugins/inspector/public'; +import { getInterpreter } from '../services'; +import { ExpressionAST, IExpressionLoaderParams, IInterpreterResult } from './_types'; + +/** + * The search context describes a specific context (filters, time range and query) + * that will be applied to the expression for execution. Not every expression will + * be effected by that. You have to use special functions + * that will pick up this search context and forward it to following functions that + * understand it. + */ + +export class ExpressionDataHandler { + private abortController: AbortController; + private expression: string; + private ast: ExpressionAST; + + private inspectorAdapters: Adapters; + private promise: Promise; + + constructor(expression: string | ExpressionAST, params: IExpressionLoaderParams) { + if (typeof expression === 'string') { + this.expression = expression; + this.ast = fromExpression(expression) as ExpressionAST; + } else { + this.ast = expression; + this.expression = ''; + } + + this.abortController = new AbortController(); + this.inspectorAdapters = this.getActiveInspectorAdapters(); + + const getInitialContext = () => ({ + type: 'kibana_context', + ...params.searchContext, + }); + + const defaultContext = { type: 'null' }; + + const interpreter = getInterpreter(); + this.promise = interpreter.interpretAst(this.ast, params.context || defaultContext, { + getInitialContext, + inspectorAdapters: this.inspectorAdapters, + }); + } + + cancel = () => { + this.abortController.abort(); + }; + + getData = async () => { + return await this.promise; + }; + + getExpression = () => { + return this.expression; + }; + + getAst = () => { + return this.ast; + }; + + inspect = () => { + return this.inspectorAdapters; + }; + + /** + * Returns an object of all inspectors for this vis object. + * This must only be called after this.type has properly be initialized, + * since we need to read out data from the the vis type to check which + * inspectors are available. + */ + private getActiveInspectorAdapters = (): Adapters => { + const adapters: Adapters = {}; + + // Add the requests inspector adapters if the vis type explicitly requested it via + // inspectorAdapters.requests: true in its definition or if it's using the courier + // request handler, since that will automatically log its requests. + adapters.requests = new RequestAdapter(); + + // Add the data inspector adapter if the vis type requested it or if the + // vis is using courier, since we know that courier supports logging + // its data. + adapters.data = new DataAdapter(); + + return adapters; + }; +} + +export function execute( + expression: string | ExpressionAST, + params: IExpressionLoaderParams = {} +): ExpressionDataHandler { + return new ExpressionDataHandler(expression, params); +} diff --git a/src/legacy/core_plugins/data/public/expressions/lib/loader.test.ts b/src/legacy/core_plugins/data/public/expressions/lib/loader.test.ts new file mode 100644 index 000000000000..ee9efd9d3373 --- /dev/null +++ b/src/legacy/core_plugins/data/public/expressions/lib/loader.test.ts @@ -0,0 +1,118 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { first } from 'rxjs/operators'; +import { loader, ExpressionLoader } from './loader'; +import { fromExpression } from '@kbn/interpreter/common'; +import { IInterpreterRenderHandlers } from './_types'; +import { Observable } from 'rxjs'; +import { ExpressionAST } from '../../../../../../plugins/data/common/expressions/types'; + +const element: HTMLElement = null as any; + +jest.mock('../services', () => ({ + getInterpreter: () => { + return { + interpretAst: async (expression: ExpressionAST) => { + return { type: 'render', as: 'test' }; + }, + }; + }, +})); + +jest.mock('../../../../interpreter/public/registries', () => { + const _registry: Record = {}; + _registry.test = { + render: (el: HTMLElement, value: any, handlers: IInterpreterRenderHandlers) => { + handlers.done(); + }, + }; + return { + renderersRegistry: { + get: (id: string) => { + return _registry[id]; + }, + }, + }; +}); + +describe('execute helper function', () => { + it('returns ExpressionDataHandler instance', () => { + const response = loader(element, '', {}); + expect(response).toBeInstanceOf(ExpressionLoader); + }); +}); + +describe('ExpressionDataHandler', () => { + const expressionString = ''; + + describe('constructor', () => { + it('accepts expression string', () => { + const expressionDataHandler = new ExpressionLoader(element, expressionString, {}); + expect(expressionDataHandler.getExpression()).toEqual(expressionString); + }); + + it('accepts expression AST', () => { + const expressionAST = fromExpression(expressionString) as ExpressionAST; + const expressionDataHandler = new ExpressionLoader(element, expressionAST, {}); + expect(expressionDataHandler.getExpression()).toEqual(expressionString); + expect(expressionDataHandler.getAst()).toEqual(expressionAST); + }); + + it('creates observables', () => { + const expressionLoader = new ExpressionLoader(element, expressionString, {}); + expect(expressionLoader.events$).toBeInstanceOf(Observable); + expect(expressionLoader.render$).toBeInstanceOf(Observable); + expect(expressionLoader.update$).toBeInstanceOf(Observable); + expect(expressionLoader.data$).toBeInstanceOf(Observable); + }); + }); + + it('emits on $data when data is available', async () => { + const expressionLoader = new ExpressionLoader(element, expressionString, {}); + const response = await expressionLoader.data$.pipe(first()).toPromise(); + expect(response).toEqual({ type: 'render', as: 'test' }); + }); + + it('emits on render$ when rendering is done', async () => { + const expressionLoader = new ExpressionLoader(element, expressionString, {}); + const response = await expressionLoader.render$.pipe(first()).toPromise(); + expect(response).toBe(1); + }); + + it('allows updating configuration', async () => { + const expressionLoader = new ExpressionLoader(element, expressionString, {}); + let response = await expressionLoader.render$.pipe(first()).toPromise(); + expect(response).toBe(1); + expressionLoader.update('', {}); + response = await expressionLoader.render$.pipe(first()).toPromise(); + expect(response).toBe(2); + }); + + it('cancel() aborts request', () => { + const expressionDataHandler = new ExpressionLoader(element, expressionString, {}); + expressionDataHandler.cancel(); + }); + + it('inspect() returns correct inspector adapters', () => { + const expressionDataHandler = new ExpressionLoader(element, expressionString, {}); + expect(expressionDataHandler.inspect()).toHaveProperty('data'); + expect(expressionDataHandler.inspect()).toHaveProperty('requests'); + }); +}); diff --git a/src/legacy/core_plugins/data/public/expressions/lib/loader.ts b/src/legacy/core_plugins/data/public/expressions/lib/loader.ts new file mode 100644 index 000000000000..e3353ab676be --- /dev/null +++ b/src/legacy/core_plugins/data/public/expressions/lib/loader.ts @@ -0,0 +1,130 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Observable, Subject } from 'rxjs'; +import { first, share } from 'rxjs/operators'; +import { Adapters, InspectorSession } from '../../../../../../plugins/inspector/public'; +import { execute, ExpressionDataHandler } from './execute'; +import { ExpressionRenderHandler } from './render'; +import { RenderId, Data, IExpressionLoaderParams, ExpressionAST } from './_types'; +import { getInspector } from '../services'; + +export class ExpressionLoader { + data$: Observable; + update$: Observable; + render$: Observable; + events$: Observable; + + private dataHandler: ExpressionDataHandler; + private renderHandler: ExpressionRenderHandler; + private dataSubject: Subject; + private data: Data; + + constructor( + element: HTMLElement, + expression: string | ExpressionAST, + params: IExpressionLoaderParams + ) { + this.dataSubject = new Subject(); + this.data$ = this.dataSubject.asObservable().pipe(share()); + + this.renderHandler = new ExpressionRenderHandler(element); + this.render$ = this.renderHandler.render$; + this.update$ = this.renderHandler.update$; + this.events$ = this.renderHandler.events$; + + this.update$.subscribe(({ newExpression, newParams }) => { + this.update(newExpression, newParams); + }); + + this.data$.subscribe(data => { + this.render(data); + }); + + this.execute(expression, params); + // @ts-ignore + this.dataHandler = this.dataHandler; + } + + destroy() {} + + cancel() { + this.dataHandler.cancel(); + } + + getExpression(): string { + return this.dataHandler.getExpression(); + } + + getAst(): ExpressionAST { + return this.dataHandler.getAst(); + } + + getElement(): HTMLElement { + return this.renderHandler.getElement(); + } + + openInspector(title: string): InspectorSession { + return getInspector().open(this.inspect(), { + title, + }); + } + + inspect(): Adapters { + return this.dataHandler.inspect(); + } + + update(expression: string | ExpressionAST, params: IExpressionLoaderParams): Promise { + const promise = this.render$.pipe(first()).toPromise(); + + if (expression !== null) { + this.execute(expression, params); + } else { + this.render(this.data); + } + return promise; + } + + private execute = async ( + expression: string | ExpressionAST, + params: IExpressionLoaderParams + ): Promise => { + if (this.dataHandler) { + this.dataHandler.cancel(); + } + this.dataHandler = execute(expression, params); + const data = await this.dataHandler.getData(); + this.dataSubject.next(data); + return data; + }; + + private async render(data: Data): Promise { + return this.renderHandler.render(data); + } +} + +export type IExpressionLoader = ( + element: HTMLElement, + expression: string | ExpressionAST, + params: IExpressionLoaderParams +) => ExpressionLoader; + +export const loader: IExpressionLoader = (element, expression, params) => { + return new ExpressionLoader(element, expression, params); +}; diff --git a/src/legacy/core_plugins/data/public/expressions/lib/render.test.ts b/src/legacy/core_plugins/data/public/expressions/lib/render.test.ts new file mode 100644 index 000000000000..1bb2f8d6554b --- /dev/null +++ b/src/legacy/core_plugins/data/public/expressions/lib/render.test.ts @@ -0,0 +1,88 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { render, ExpressionRenderHandler } from './render'; +import { Observable } from 'rxjs'; +import { IInterpreterRenderHandlers } from './_types'; + +const element: HTMLElement = null as any; + +jest.mock('../../../../interpreter/public/registries', () => { + const _registry: Record = {}; + _registry.test = { + render: (el: HTMLElement, value: any, handlers: IInterpreterRenderHandlers) => { + handlers.done(); + }, + }; + return { + renderersRegistry: { + get: (id: string) => { + return _registry[id]; + }, + }, + }; +}); + +describe('render helper function', () => { + it('returns ExpressionRenderHandler instance', () => { + const response = render(element, {}); + expect(response).toBeInstanceOf(ExpressionRenderHandler); + }); +}); + +describe('ExpressionRenderHandler', () => { + const data = { type: 'render', as: 'test' }; + + it('constructor creates observers', () => { + const expressionRenderHandler = new ExpressionRenderHandler(element); + expect(expressionRenderHandler.events$).toBeInstanceOf(Observable); + expect(expressionRenderHandler.render$).toBeInstanceOf(Observable); + expect(expressionRenderHandler.update$).toBeInstanceOf(Observable); + }); + + it('getElement returns the element', () => { + const expressionRenderHandler = new ExpressionRenderHandler(element); + expect(expressionRenderHandler.getElement()).toBe(element); + }); + + describe('render()', () => { + it('throws if invalid data is provided', async () => { + const expressionRenderHandler = new ExpressionRenderHandler(element); + await expect(expressionRenderHandler.render({})).rejects.toThrow(); + }); + + it('throws if renderer does not exist', async () => { + const expressionRenderHandler = new ExpressionRenderHandler(element); + await expect( + expressionRenderHandler.render({ type: 'render', as: 'something' }) + ).rejects.toThrow(); + }); + + it('returns a promise', () => { + const expressionRenderHandler = new ExpressionRenderHandler(element); + expect(expressionRenderHandler.render(data)).toBeInstanceOf(Promise); + }); + + it('resolves a promise once rendering is complete', async () => { + const expressionRenderHandler = new ExpressionRenderHandler(element); + const response = await expressionRenderHandler.render(data); + expect(response).toBe(1); + }); + }); +}); diff --git a/src/legacy/core_plugins/data/public/expressions/lib/render.ts b/src/legacy/core_plugins/data/public/expressions/lib/render.ts new file mode 100644 index 000000000000..250fa14c9de1 --- /dev/null +++ b/src/legacy/core_plugins/data/public/expressions/lib/render.ts @@ -0,0 +1,99 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Observable } from 'rxjs'; +import * as Rx from 'rxjs'; +import { share, first } from 'rxjs/operators'; +import { renderersRegistry } from '../../../../interpreter/public/registries'; +import { event, RenderId, Data, IInterpreterRenderHandlers } from './_types'; + +export class ExpressionRenderHandler { + render$: Observable; + update$: Observable; + events$: Observable; + + private element: HTMLElement; + private destroyFn?: any; + private renderCount: number = 0; + private handlers: IInterpreterRenderHandlers; + + constructor(element: HTMLElement) { + this.element = element; + + const eventsSubject = new Rx.Subject(); + this.events$ = eventsSubject.asObservable().pipe(share()); + + const renderSubject = new Rx.Subject(); + this.render$ = renderSubject.asObservable().pipe(share()); + + const updateSubject = new Rx.Subject(); + this.update$ = updateSubject.asObservable().pipe(share()); + + this.handlers = { + onDestroy: (fn: any) => { + this.destroyFn = fn; + }, + done: () => { + this.renderCount++; + renderSubject.next(this.renderCount); + }, + reload: () => { + updateSubject.next(null); + }, + update: params => { + updateSubject.next(params); + }, + event: data => { + eventsSubject.next(data); + }, + }; + } + + render = async (data: Data) => { + if (data.type !== 'render' || !data.as) { + throw new Error('invalid data provided to expression renderer'); + } + + if (!renderersRegistry.get(data.as)) { + throw new Error(`invalid renderer id '${data.as}'`); + } + + const promise = this.render$.pipe(first()).toPromise(); + + renderersRegistry.get(data.as).render(this.element, data.value, this.handlers); + + return promise; + }; + + destroy = () => { + if (this.destroyFn) { + this.destroyFn(); + } + }; + + getElement = () => { + return this.element; + }; +} + +export function render(element: HTMLElement, data: Data): ExpressionRenderHandler { + const handler = new ExpressionRenderHandler(element); + handler.render(data); + return handler; +} diff --git a/src/legacy/core_plugins/data/public/expressions/services.ts b/src/legacy/core_plugins/data/public/expressions/services.ts new file mode 100644 index 000000000000..73f627f8dc88 --- /dev/null +++ b/src/legacy/core_plugins/data/public/expressions/services.ts @@ -0,0 +1,41 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { IInterpreter } from './lib/_types'; +import { Start as IInspector } from '../../../../../plugins/inspector/public'; + +let interpreter: IInterpreter | undefined; +let inspector: IInspector; + +export const getInterpreter = (): IInterpreter => { + if (!interpreter) throw new Error('interpreter was not set'); + return interpreter; +}; + +export const setInterpreter = (inspectorInstance: IInterpreter) => { + interpreter = inspectorInstance; +}; + +export const getInspector = (): IInspector => { + return inspector; +}; + +export const setInspector = (inspectorInstance: IInspector) => { + inspector = inspectorInstance; +}; diff --git a/src/legacy/core_plugins/data/public/index.ts b/src/legacy/core_plugins/data/public/index.ts index 4e1e83d71afe..8bf13a24f177 100644 --- a/src/legacy/core_plugins/data/public/index.ts +++ b/src/legacy/core_plugins/data/public/index.ts @@ -18,7 +18,7 @@ */ // /// Define plugin function -import { DataPlugin as Plugin, DataSetup } from './plugin'; +import { DataPlugin as Plugin, DataSetup, DataStart } from './plugin'; export function plugin() { return new Plugin(); @@ -28,7 +28,9 @@ export function plugin() { /** @public types */ export type DataSetup = DataSetup; -export { ExpressionRenderer, ExpressionRendererProps, ExpressionRunner } from './expressions'; +export type DataStart = DataStart; + +export { ExpressionRenderer, ExpressionRendererProps } from './expressions'; export { FilterBar, ApplyFiltersPopover } from './filter'; export { Field, diff --git a/src/legacy/core_plugins/data/public/legacy.ts b/src/legacy/core_plugins/data/public/legacy.ts index 14350a628419..3d86a200eab9 100644 --- a/src/legacy/core_plugins/data/public/legacy.ts +++ b/src/legacy/core_plugins/data/public/legacy.ts @@ -34,9 +34,7 @@ * data that will eventually be injected by the new platform. */ -import { npSetup } from 'ui/new_platform'; -// @ts-ignore -import { renderersRegistry } from 'plugins/interpreter/registries'; +import { npSetup, npStart } from 'ui/new_platform'; // @ts-ignore import { getInterpreter } from 'plugins/interpreter/interpreter'; import { LegacyDependenciesPlugin } from './shim/legacy_dependencies_plugin'; @@ -47,8 +45,9 @@ const legacyPlugin = new LegacyDependenciesPlugin(); export const setup = dataPlugin.setup(npSetup.core, { __LEGACY: legacyPlugin.setup(), - interpreter: { - renderersRegistry, - getInterpreter, - }, + inspector: npSetup.plugins.inspector, +}); + +export const start = dataPlugin.start(npStart.core, { + inspector: npStart.plugins.inspector, }); diff --git a/src/legacy/core_plugins/data/public/plugin.ts b/src/legacy/core_plugins/data/public/plugin.ts index a38e55e8139e..b62c8106bc16 100644 --- a/src/legacy/core_plugins/data/public/plugin.ts +++ b/src/legacy/core_plugins/data/public/plugin.ts @@ -18,12 +18,16 @@ */ import { CoreSetup, CoreStart, Plugin } from '../../../../core/public'; -import { ExpressionsService, ExpressionsSetup } from './expressions'; +import { ExpressionsService, ExpressionsSetup, ExpressionsStart } from './expressions'; import { SearchService, SearchSetup } from './search'; import { QueryService, QuerySetup } from './query'; import { FilterService, FilterSetup } from './filter'; import { IndexPatternsService, IndexPatternsSetup } from './index_patterns'; import { LegacyDependenciesPluginSetup } from './shim/legacy_dependencies_plugin'; +import { + Start as InspectorStart, + Setup as InspectorSetup, +} from '../../../../plugins/inspector/public'; /** * Interface for any dependencies on other plugins' `setup` contracts. @@ -32,7 +36,11 @@ import { LegacyDependenciesPluginSetup } from './shim/legacy_dependencies_plugin */ export interface DataPluginSetupDependencies { __LEGACY: LegacyDependenciesPluginSetup; - interpreter: any; + inspector: InspectorSetup; +} + +export interface DataPluginStartDependencies { + inspector: InspectorStart; } /** @@ -48,6 +56,10 @@ export interface DataSetup { search: SearchSetup; } +export interface DataStart { + expressions: ExpressionsStart; +} + /** * Data Plugin - public * @@ -59,7 +71,7 @@ export interface DataSetup { * in the setup/start interfaces. The remaining items exported here are either types, * or static code. */ -export class DataPlugin implements Plugin { +export class DataPlugin implements Plugin { // Exposed services, sorted alphabetically private readonly expressions: ExpressionsService = new ExpressionsService(); private readonly filter: FilterService = new FilterService(); @@ -67,7 +79,7 @@ export class DataPlugin implements Plugin