Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: 💡 separate server-side function reg from executor #44531

Merged
merged 6 commits into from
Sep 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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