Skip to content

Commit

Permalink
refactor: πŸ’‘ separate server-side function reg from executor (#44531) (#…
Browse files Browse the repository at this point in the history
…46775)

* refactor: πŸ’‘ separate server-side function reg from executor

* refactor: πŸ’‘ separate Canvas functionality of Interpreter

* refactor: πŸ’‘ simplify, move server fn init into Canavas

* fix: πŸ› adjust Expressions service usage after master merge
  • Loading branch information
streamich authored Sep 27, 2019
1 parent e7e0723 commit 4bda107
Show file tree
Hide file tree
Showing 13 changed files with 114 additions and 171 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* 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.
*/

/**
* This file needs to be deleted by 8.0 release. It is here to load available
* server side functions and create a wrappers around them on client side, to
* execute them from client side. This functionality is used only by Canvas
* and all server side functions are in Canvas plugin.
*
* In 8.0 there will be no server-side functions, plugins will register only
* client side functions and if they need those to execute something on the
* server side, it should be respective function's internal implementation detail.
*/

import { get, identity } from 'lodash';
// @ts-ignore
import { npSetup } from 'ui/new_platform';
import { FUNCTIONS_URL } from './consts';
import { ajaxStream } from './ajax_stream';
import { batchedFetch } from './batched_fetch';

export function getType(node: any) {
if (node == null) return 'null';
if (typeof node === 'object') {
if (!node.type) throw new Error('Objects must have a type property');
return node.type;
}
return typeof node;
}

export function serializeProvider(types: any) {
return {
serialize: provider('serialize'),
deserialize: provider('deserialize'),
};

function provider(key: any) {
return (context: any) => {
const type = getType(context);
const typeDef = types[type];
const fn: any = get(typeDef, key) || identity;
return fn(context);
};
}
}

let cached: Promise<void> | null = null;

export const loadLegacyServerFunctionWrappers = async () => {
if (!cached) {
cached = (async () => {
const serverFunctionList = await npSetup.core.http.get(FUNCTIONS_URL);
const types = npSetup.plugins.expressions.__LEGACY.types.toJS();
const { serialize } = serializeProvider(types);
const batch = batchedFetch({
ajaxStream: ajaxStream(
npSetup.core.injectedMetadata.getKibanaVersion(),
npSetup.core.injectedMetadata.getBasePath()
),
serialize,
});

// For every sever-side function, register a client-side
// function that matches its definition, but which simply
// calls the server-side function endpoint.
Object.keys(serverFunctionList).forEach(functionName => {
const fn = () => ({
...serverFunctionList[functionName],
fn: (context: any, args: any) => batch({ functionName, args, context }),
});
npSetup.plugins.expressions.registerFunction(fn);
});
})();
}

return cached;
};
28 changes: 4 additions & 24 deletions src/legacy/core_plugins/interpreter/public/interpreter.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,12 @@

import 'uiExports/interpreter';
import { register, registryFactory } from '@kbn/interpreter/common';
import { npSetup } from 'ui/new_platform';
import { initializeInterpreter } from './lib/interpreter';
import { registries } from './registries';

import { ajaxStream } from './lib/ajax_stream';
import { functions } from './functions';
import { visualization } from './renderers/visualization';
import { typeSpecs } from '../../../../plugins/expressions/common';

const { http } = npSetup.core;
const KIBANA_VERSION = npSetup.core.injectedMetadata.getKibanaVersion();
const KIBANA_BASE_PATH = npSetup.core.injectedMetadata.getBasePath();

// Expose kbnInterpreter.register(specs) and kbnInterpreter.registries() globally so that plugins
// can register without a transpile step.
global.kbnInterpreter = Object.assign(global.kbnInterpreter || {}, registryFactory(registries));
Expand All @@ -42,26 +35,13 @@ register(registries, {
renderers: [visualization],
});

let _resolve;
let _interpreterPromise;

const initialize = async () => {
initializeInterpreter({
http,
ajaxStream: ajaxStream(KIBANA_VERSION, KIBANA_BASE_PATH),
typesRegistry: registries.types,
functionsRegistry: registries.browserFunctions,
}).then(interpreter => {
_resolve({ interpreter });
});
};
let interpreterPromise;

export const getInterpreter = async () => {
if (!_interpreterPromise) {
_interpreterPromise = new Promise(resolve => _resolve = resolve);
initialize();
if (!interpreterPromise) {
interpreterPromise = initializeInterpreter();
}
return await _interpreterPromise;
return await interpreterPromise;
};

export const interpretAst = async (...params) => {
Expand Down
16 changes: 6 additions & 10 deletions src/legacy/core_plugins/interpreter/public/interpreter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ jest.mock('@kbn/interpreter/common', () => ({
}));

const mockInterpreter = {
interpretAst: jest.fn(),
interpreter: {
interpretAst: jest.fn(),
}
};
jest.mock('./lib/interpreter', () => ({
initializeInterpreter: jest.fn().mockReturnValue(Promise.resolve(mockInterpreter)),
Expand Down Expand Up @@ -71,12 +73,6 @@ describe('interpreter/interpreter', () => {
it('initializes interpreter', async () => {
await getInterpreter();
expect(initializeInterpreter).toHaveBeenCalledTimes(1);
expect(initializeInterpreter.mock.calls[0][0]).toMatchObject({
ajaxStream: expect.any(Function),
http: expect.any(Object),
typesRegistry: expect.any(Function),
functionsRegistry: expect.any(Function),
});
});

it('only initializes interpreter once', async () => {
Expand Down Expand Up @@ -110,15 +106,15 @@ describe('interpreter/interpreter', () => {
it('calls interpreter.interpretAst with the provided params', async () => {
const params = [{}];
await interpretAst(...params);
expect(mockInterpreter.interpretAst).toHaveBeenCalledTimes(1);
expect(mockInterpreter.interpretAst).toHaveBeenCalledWith(...params);
expect(mockInterpreter.interpreter.interpretAst).toHaveBeenCalledTimes(1);
expect(mockInterpreter.interpreter.interpretAst).toHaveBeenCalledWith(...params);
});

it('calls interpreter.interpretAst each time', async () => {
const params = [{}];
await interpretAst(...params);
await interpretAst(...params);
expect(mockInterpreter.interpretAst).toHaveBeenCalledTimes(2);
expect(mockInterpreter.interpreter.interpretAst).toHaveBeenCalledTimes(2);
});
});

Expand Down
105 changes: 0 additions & 105 deletions src/legacy/core_plugins/interpreter/public/lib/interpreter.test.ts

This file was deleted.

37 changes: 6 additions & 31 deletions src/legacy/core_plugins/interpreter/public/lib/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,44 +17,19 @@
* under the License.
*/

import { interpreterProvider, serializeProvider } from '../../common';
import { interpreterProvider } from '../../common';
import { createHandlers } from './create_handlers';
import { batchedFetch } from './batched_fetch';
import { FUNCTIONS_URL } from './consts';
import { CoreStart } from '../../../../../core/public';

interface Config {
http: CoreStart['http'];
ajaxStream: any; // TODO: Import this from kibana_utils/ajax_stream
typesRegistry: any;
functionsRegistry: any;
}

export async function initializeInterpreter(config: Config) {
const { http, ajaxStream, typesRegistry, functionsRegistry } = config;
const serverFunctionList = await http.get(FUNCTIONS_URL);
const types = typesRegistry.toJS();
const { serialize } = serializeProvider(types);
const batch = batchedFetch({ ajaxStream, serialize });

// For every sever-side function, register a client-side
// function that matches its definition, but which simply
// calls the server-side function endpoint.
Object.keys(serverFunctionList).forEach(functionName => {
functionsRegistry.register(() => ({
...serverFunctionList[functionName],
fn: (context: any, args: any) => batch({ functionName, args, context }),
}));
});
import { registries } from '../registries';

export async function initializeInterpreter() {
const interpretAst = async (ast: any, context: any, handlers: any) => {
const interpretFn = await interpreterProvider({
types: typesRegistry.toJS(),
types: registries.types.toJS(),
handlers: { ...handlers, ...createHandlers() },
functions: functionsRegistry.toJS(),
functions: registries.browserFunctions.toJS(),
});
return interpretFn(ast, context);
};

return { interpretAst };
return { interpreter: { interpretAst } };
}
2 changes: 2 additions & 0 deletions x-pack/legacy/plugins/canvas/public/components/app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { connect } from 'react-redux';
import { compose, withProps } from 'recompose';
import { registries } from 'plugins/interpreter/registries';
import { getInterpreter } from 'plugins/interpreter/interpreter';
import { loadLegacyServerFunctionWrappers } from 'plugins/interpreter/canvas/load_legacy_server_function_wrappers';
import { getAppReady, getBasePath } from '../../state/selectors/app';
import { appReady, appError } from '../../state/actions/app';
import { elementsRegistry } from '../../lib/elements_registry';
Expand Down Expand Up @@ -71,6 +72,7 @@ register(registries, {
const mapDispatchToProps = dispatch => ({
setAppReady: () => async () => {
try {
await loadLegacyServerFunctionWrappers();
await getInterpreter();

// Register the expression language with the Monaco Editor
Expand Down
4 changes: 3 additions & 1 deletion x-pack/legacy/plugins/canvas/public/lib/run_interpreter.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import { fromExpression, getType } from '@kbn/interpreter/common';
import { interpretAst } from 'plugins/interpreter/interpreter';
import { loadLegacyServerFunctionWrappers } from 'plugins/interpreter/canvas/load_legacy_server_function_wrappers';
import { notify } from './notify';

/**
Expand All @@ -19,7 +20,8 @@ import { notify } from './notify';
* @returns {promise}
*/
export function runInterpreter(ast, context = null, options = {}) {
return interpretAst(ast, context)
return loadLegacyServerFunctionWrappers()
.then(() => interpretAst(ast, context))
.then(renderable => {
if (getType(renderable) === 'render') {
return renderable;
Expand Down

0 comments on commit 4bda107

Please sign in to comment.