[expressions] Remove legacy APIs. (#75517)

This commit is contained in:
Luke Elmers 2020-08-20 10:08:31 -06:00 committed by GitHub
parent afd90b04df
commit 94beed1b79
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 27 additions and 504 deletions

View file

@ -221,17 +221,6 @@ export const npSetup = {
registerFunction: sinon.fake(),
registerRenderer: sinon.fake(),
registerType: sinon.fake(),
__LEGACY: {
renderers: {
register: () => undefined,
get: () => null,
},
getExecutor: () => ({
interpreter: {
interpretAst: () => {},
},
}),
},
},
data: {
autocomplete: {

View file

@ -3,9 +3,6 @@
"version": "kibana",
"server": true,
"ui": true,
"requiredPlugins": [
"bfetch"
],
"extraPublicDirs": ["common", "common/fonts"],
"requiredBundles": [
"kibanaUtils",

View file

@ -20,7 +20,7 @@
import { first, skip, toArray } from 'rxjs/operators';
import { loader, ExpressionLoader } from './loader';
import { Observable } from 'rxjs';
import { ExpressionAstExpression, parseExpression, IInterpreterRenderHandlers } from '../common';
import { parseExpression, IInterpreterRenderHandlers } from '../common';
// eslint-disable-next-line
const { __getLastExecution } = require('./services');
@ -42,13 +42,6 @@ jest.mock('./services', () => {
const moduleMock = {
__execution: undefined,
__getLastExecution: () => moduleMock.__execution,
getInterpreter: () => {
return {
interpretAst: async (expression: ExpressionAstExpression) => {
return { type: 'render', as: 'test' };
},
};
},
getRenderersRegistry: () => ({
get: (id: string) => renderers[id],
}),

View file

@ -21,7 +21,6 @@ import React from 'react';
import { ExpressionsSetup, ExpressionsStart, plugin as pluginInitializer } from '.';
import { coreMock } from '../../../core/public/mocks';
import { bfetchPluginMock } from '../../bfetch/public/mocks';
export type Setup = jest.Mocked<ExpressionsSetup>;
export type Start = jest.Mocked<ExpressionsStart>;
@ -39,23 +38,6 @@ const createSetupContract = (): Setup => {
registerRenderer: jest.fn(),
registerType: jest.fn(),
run: jest.fn(),
__LEGACY: {
functions: {
register: () => {},
} as any,
renderers: {
register: () => {},
} as any,
types: {
register: () => {},
} as any,
getExecutor: () => ({
interpreter: {
interpretAst: (() => {}) as any,
},
}),
loadLegacyServerFunctionWrappers: () => Promise.resolve(),
},
};
return setupContract;
};
@ -84,9 +66,7 @@ const createPlugin = async () => {
const coreSetup = coreMock.createSetup();
const coreStart = coreMock.createStart();
const plugin = pluginInitializer(pluginInitializerContext);
const setup = await plugin.setup(coreSetup, {
bfetch: bfetchPluginMock.createSetupContract(),
});
const setup = await plugin.setup(coreSetup);
return {
pluginInitializerContext,
@ -94,10 +74,7 @@ const createPlugin = async () => {
coreStart,
plugin,
setup,
doStart: async () =>
await plugin.start(coreStart, {
bfetch: bfetchPluginMock.createStartContract(),
}),
doStart: async () => await plugin.start(coreStart),
};
};

View file

@ -17,75 +17,19 @@
* under the License.
*/
import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../core/public';
import { ExpressionExecutor } from './types';
import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public';
import {
ExpressionRendererRegistry,
FunctionsRegistry,
serializeProvider,
TypesRegistry,
ExpressionsService,
ExpressionsServiceSetup,
ExpressionsServiceStart,
ExecutionContext,
} from '../common';
import { BfetchPublicSetup, BfetchPublicStart } from '../../bfetch/public';
import {
setCoreStart,
setInterpreter,
setRenderersRegistry,
setNotifications,
setExpressionsService,
} from './services';
import { setRenderersRegistry, setNotifications, setExpressionsService } from './services';
import { ReactExpressionRenderer } from './react_expression_renderer';
import { ExpressionLoader, loader } from './loader';
import { render, ExpressionRenderHandler } from './render';
export interface ExpressionsSetupDeps {
bfetch: BfetchPublicSetup;
}
export interface ExpressionsStartDeps {
bfetch: BfetchPublicStart;
}
export interface ExpressionsSetup extends ExpressionsServiceSetup {
/**
* @todo Get rid of these `__LEGACY` APIs.
*
* `__LEGACY` APIs are used by Canvas. It should be possible to stop
* using all of them (except `loadLegacyServerFunctionWrappers`) and use
* Kibana Platform plugin contracts instead.
*/
__LEGACY: {
/**
* Use `registerType` and `getTypes` instead.
*/
types: TypesRegistry;
/**
* Use `registerFunction` and `getFunctions` instead.
*/
functions: FunctionsRegistry;
/**
* Use `registerRenderer` and `getRenderers`, and `getRenderer` instead.
*/
renderers: ExpressionRendererRegistry;
/**
* Use `run` function instead.
*/
getExecutor: () => ExpressionExecutor;
/**
* This function is used by Canvas to load server-side function and create
* browser-side "wrapper" for each one. This function can be removed once
* we enable expressions on server-side: https://github.com/elastic/kibana/issues/46906
*/
loadLegacyServerFunctionWrappers: () => Promise<void>;
};
}
export type ExpressionsSetup = ExpressionsServiceSetup;
export interface ExpressionsStart extends ExpressionsServiceStart {
ExpressionLoader: typeof ExpressionLoader;
@ -95,9 +39,7 @@ export interface ExpressionsStart extends ExpressionsServiceStart {
render: typeof render;
}
export class ExpressionsPublicPlugin
implements
Plugin<ExpressionsSetup, ExpressionsStart, ExpressionsSetupDeps, ExpressionsStartDeps> {
export class ExpressionsPublicPlugin implements Plugin<ExpressionsSetup, ExpressionsStart> {
private readonly expressions: ExpressionsService = new ExpressionsService();
constructor(initializerContext: PluginInitializerContext) {}
@ -116,68 +58,21 @@ export class ExpressionsPublicPlugin
});
}
public setup(core: CoreSetup, { bfetch }: ExpressionsSetupDeps): ExpressionsSetup {
public setup(core: CoreSetup): ExpressionsSetup {
this.configureExecutor(core);
const { expressions } = this;
const { executor, renderers } = expressions;
const { renderers } = expressions;
setRenderersRegistry(renderers);
setExpressionsService(this.expressions);
setExpressionsService(expressions);
const expressionsSetup = expressions.setup();
// This is legacy. Should go away when we get rid of __LEGACY.
const getExecutor = (): ExpressionExecutor => {
return { interpreter: { interpretAst: expressionsSetup.run } };
};
setInterpreter(getExecutor().interpreter);
let cached: Promise<void> | null = null;
const loadLegacyServerFunctionWrappers = async () => {
if (!cached) {
cached = (async () => {
const serverFunctionList = await core.http.get(`/api/interpreter/fns`);
const batchedFunction = bfetch.batchedFunction({ url: `/api/interpreter/fns` });
const { serialize } = serializeProvider(executor.getTypes());
// 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) => {
if (expressionsSetup.getFunction(functionName)) {
return;
}
const fn = () => ({
...serverFunctionList[functionName],
fn: (input: any, args: any) => {
return batchedFunction({ functionName, args, context: serialize(input) });
},
});
expressionsSetup.registerFunction(fn);
});
})();
}
return cached;
};
const setup: ExpressionsSetup = {
...expressionsSetup,
__LEGACY: {
types: executor.types,
functions: executor.functions,
renderers,
getExecutor,
loadLegacyServerFunctionWrappers,
},
};
const setup = expressions.setup();
return Object.freeze(setup);
}
public start(core: CoreStart, { bfetch }: ExpressionsStartDeps): ExpressionsStart {
setCoreStart(core);
public start(core: CoreStart): ExpressionsStart {
setNotifications(core.notifications);
const { expressions } = this;

View file

@ -18,22 +18,15 @@
*/
import { NotificationsStart } from 'kibana/public';
import { createKibanaUtilsCore, createGetterSetter } from '../../kibana_utils/public';
import { ExpressionInterpreter } from './types';
import { ExpressionsSetup } from './plugin';
import { ExpressionsService } from '../common';
import { createGetterSetter } from '../../kibana_utils/public';
import { ExpressionsService, ExpressionRendererRegistry } from '../common';
export const { getCoreStart, setCoreStart } = createKibanaUtilsCore();
export const [getInterpreter, setInterpreter] = createGetterSetter<ExpressionInterpreter>(
'Interpreter'
);
export const [getNotifications, setNotifications] = createGetterSetter<NotificationsStart>(
'Notifications'
);
export const [getRenderersRegistry, setRenderersRegistry] = createGetterSetter<
ExpressionsSetup['__LEGACY']['renderers']
ExpressionRendererRegistry
>('Renderers registry');
export const [getExpressionsService, setExpressionsService] = createGetterSetter<

View file

@ -17,7 +17,7 @@
* under the License.
*/
import { PluginInitializerContext } from '../../../core/server';
import { PluginInitializerContext } from 'src/core/server';
import { ExpressionsServerPlugin } from './plugin';
export { ExpressionsServerSetup, ExpressionsServerStart } from './plugin';

View file

@ -1,134 +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.
*/
/* eslint-disable max-classes-per-file */
// TODO: Remove this file once https://github.com/elastic/kibana/issues/46906 is complete.
// @ts-ignore
import { register, registryFactory, Registry, Fn } from '@kbn/interpreter/common';
import Boom from 'boom';
import { schema } from '@kbn/config-schema';
import { CoreSetup, Logger, LegacyAPICaller } from 'src/core/server';
import { ExpressionsServerSetupDependencies } from './plugin';
import { typeSpecs, ExpressionType } from '../common';
import { serializeProvider } from '../common';
export class TypesRegistry extends Registry<any, any> {
wrapper(obj: any) {
return new (ExpressionType as any)(obj);
}
}
export class FunctionsRegistry extends Registry<any, any> {
wrapper(obj: any) {
return new Fn(obj);
}
}
export const registries = {
types: new TypesRegistry(),
serverFunctions: new FunctionsRegistry(),
};
export interface LegacyInterpreterServerApi {
registries(): typeof registries;
register(specs: Record<keyof typeof registries, any[]>): typeof registries;
}
export const createLegacyServerInterpreterApi = (): LegacyInterpreterServerApi => {
const api = registryFactory(registries);
register(registries, {
types: typeSpecs,
});
return api;
};
export const createLegacyServerEndpoints = (
api: LegacyInterpreterServerApi,
logger: Logger,
core: CoreSetup,
plugins: ExpressionsServerSetupDependencies
) => {
const router = core.http.createRouter();
/**
* Register the endpoint that returns the list of server-only functions.
*/
router.get(
{
path: `/api/interpreter/fns`,
validate: {
body: schema.any(),
},
},
async (context, request, response) => {
const functions = api.registries().serverFunctions.toJS();
const body = JSON.stringify(functions);
return response.ok({
body,
});
}
);
/**
* Run a single Canvas function.
*
* @param {*} server - The Kibana server object
* @param {*} handlers - The Canvas handlers
* @param {*} fnCall - Describes the function being run `{ functionName, args, context }`
*/
async function runFunction(
handlers: { environment: string; elasticsearchClient: LegacyAPICaller },
fnCall: any
) {
const { functionName, args, context } = fnCall;
const { deserialize } = serializeProvider(registries.types.toJS());
const fnDef = registries.serverFunctions.toJS()[functionName];
if (!fnDef) throw Boom.notFound(`Function "${functionName}" could not be found.`);
const deserialized = deserialize(context);
const result = fnDef.fn(deserialized, args, handlers);
return result;
}
/**
* Register an endpoint that executes a batch of functions, and streams the
* results back using ND-JSON.
*/
plugins.bfetch.addBatchProcessingRoute(`/api/interpreter/fns`, (request) => {
return {
onBatchItem: async (fnCall: any) => {
const [coreStart] = await core.getStartServices();
const handlers = {
environment: 'server',
elasticsearchClient: coreStart.elasticsearch.legacy.client.asScoped(request)
.callAsCurrentUser,
};
const result = await runFunction(handlers, fnCall);
if (typeof result === 'undefined') {
throw new Error(`Function ${fnCall.functionName} did not return anything.`);
}
return result;
},
};
});
};

View file

@ -20,7 +20,6 @@
import { ExpressionsServerSetup, ExpressionsServerStart } from '.';
import { plugin as pluginInitializer } from '.';
import { coreMock } from '../../../core/server/mocks';
import { bfetchPluginMock } from '../../bfetch/server/mocks';
export type Setup = jest.Mocked<ExpressionsServerSetup>;
export type Start = jest.Mocked<ExpressionsServerStart>;
@ -38,10 +37,6 @@ const createSetupContract = (): Setup => {
registerRenderer: jest.fn(),
registerType: jest.fn(),
run: jest.fn(),
__LEGACY: {
register: jest.fn(),
registries: jest.fn(),
},
};
return setupContract;
};
@ -67,9 +62,7 @@ const createPlugin = async () => {
const coreSetup = coreMock.createSetup();
const coreStart = coreMock.createStart();
const plugin = pluginInitializer(pluginInitializerContext);
const setup = await plugin.setup(coreSetup, {
bfetch: bfetchPluginMock.createSetupContract(),
});
const setup = await plugin.setup(coreSetup);
return {
pluginInitializerContext,
@ -77,10 +70,7 @@ const createPlugin = async () => {
coreStart,
plugin,
setup,
doStart: async () =>
await plugin.start(coreStart, {
bfetch: bfetchPluginMock.createStartContract(),
}),
doStart: async () => await plugin.start(coreStart),
};
};

View file

@ -17,68 +17,30 @@
* under the License.
*/
import { CoreStart, PluginInitializerContext, CoreSetup, Plugin } from 'src/core/server';
import { BfetchServerSetup, BfetchServerStart } from '../../bfetch/server';
import {
LegacyInterpreterServerApi,
createLegacyServerInterpreterApi,
createLegacyServerEndpoints,
} from './legacy';
import { CoreStart, CoreSetup, Plugin, PluginInitializerContext } from 'src/core/server';
import { ExpressionsService, ExpressionsServiceSetup, ExpressionsServiceStart } from '../common';
export interface ExpressionsServerSetupDependencies {
bfetch: BfetchServerSetup;
}
export interface ExpressionsServerStartDependencies {
bfetch: BfetchServerStart;
}
export interface ExpressionsServerSetup extends ExpressionsServiceSetup {
__LEGACY: LegacyInterpreterServerApi;
}
export type ExpressionsServerSetup = ExpressionsServiceSetup;
export type ExpressionsServerStart = ExpressionsServiceStart;
export class ExpressionsServerPlugin
implements
Plugin<
ExpressionsServerSetup,
ExpressionsServerStart,
ExpressionsServerSetupDependencies,
ExpressionsServerStartDependencies
> {
implements Plugin<ExpressionsServerSetup, ExpressionsServerStart> {
readonly expressions: ExpressionsService = new ExpressionsService();
constructor(private readonly initializerContext: PluginInitializerContext) {}
constructor(initializerContext: PluginInitializerContext) {}
public setup(
core: CoreSetup,
plugins: ExpressionsServerSetupDependencies
): ExpressionsServerSetup {
const logger = this.initializerContext.logger.get();
const { expressions } = this;
const { executor } = expressions;
executor.extendContext({
public setup(core: CoreSetup): ExpressionsServerSetup {
this.expressions.executor.extendContext({
environment: 'server',
});
const legacyApi = createLegacyServerInterpreterApi();
createLegacyServerEndpoints(legacyApi, logger, core, plugins);
const setup = {
...this.expressions.setup(),
__LEGACY: legacyApi,
};
const setup = this.expressions.setup();
return Object.freeze(setup);
}
public start(
core: CoreStart,
plugins: ExpressionsServerStartDependencies
): ExpressionsServerStart {
public start(core: CoreStart): ExpressionsServerStart {
const start = this.expressions.start();
return Object.freeze(start);

View file

@ -1,39 +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 { createKibanaUtilsCore } from './create_kibana_utils_core';
import { CoreStart } from 'kibana/public';
describe('createKibanaUtilsCore', () => {
it('should allows to work with multiple instances', () => {
const core1 = {} as CoreStart;
const core2 = {} as CoreStart;
const { setCoreStart: setCoreStart1, getCoreStart: getCoreStart1 } = createKibanaUtilsCore();
const { setCoreStart: setCoreStart2, getCoreStart: getCoreStart2 } = createKibanaUtilsCore();
setCoreStart1(core1);
setCoreStart2(core2);
expect(getCoreStart1()).toBe(core1);
expect(getCoreStart2()).toBe(core2);
expect(getCoreStart1() !== getCoreStart2()).toBeTruthy();
});
});

View file

@ -1,39 +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 { createGetterSetter, Get, Set } from '../../common';
import { CoreStart } from '../../../../core/public';
import { KUSavedObjectClient, createSavedObjectsClient } from './saved_objects_client';
interface Return {
getCoreStart: Get<CoreStart>;
setCoreStart: Set<CoreStart>;
savedObjects: KUSavedObjectClient;
}
export const createKibanaUtilsCore = (): Return => {
const [getCoreStart, setCoreStart] = createGetterSetter<CoreStart>('CoreStart');
const savedObjects = createSavedObjectsClient(getCoreStart);
return {
getCoreStart,
setCoreStart,
savedObjects,
};
};

View file

@ -17,5 +17,4 @@
* under the License.
*/
export * from './create_kibana_utils_core';
export * from './create_start_service_getter';

View file

@ -1,35 +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 { CoreStart } from '../../../../core/public';
import { Get } from '../../common';
type CoreSavedObjectClient = CoreStart['savedObjects']['client'];
export interface KUSavedObjectClient {
get: CoreSavedObjectClient['get'];
}
export const createSavedObjectsClient = (getCoreStart: Get<CoreStart>) => {
const savedObjectsClient: KUSavedObjectClient = {
get: (...args) => getCoreStart().savedObjects.client.get(...args),
};
return savedObjectsClient;
};

View file

@ -1,23 +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 { createGetterSetter } from '../../common';
import { CoreStart } from '../../../../core/public';
export const [getCoreStart, setCoreStart] = createGetterSetter<CoreStart>('CoreStart');

View file

@ -12,9 +12,7 @@ import { renderFunctions } from '../../../canvas_plugin_src/renderers/core';
const placeholder = {} as any;
const expressionsPlugin = plugin(placeholder);
const setup = expressionsPlugin.setup(placeholder, {
inspector: {},
} as any);
const setup = expressionsPlugin.setup(placeholder);
export const expressionsService: ExpressionsService = setup.fork();