Lens client side shim cleanup (#56976) (#57562)

This commit is contained in:
Joe Reuter 2020-02-13 15:36:28 +01:00 committed by GitHub
parent 8ca0cef8a9
commit b8b275f5ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
152 changed files with 834 additions and 1730 deletions

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export * from './plugin';
export * from './app';

View file

@ -0,0 +1,32 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { CoreSetup } from 'src/core/public';
import { datatableVisualization } from './visualization';
import { ExpressionsSetup } from '../../../../../../src/plugins/expressions/public';
import { datatable, datatableColumns, getDatatableRenderer } from './expression';
import { FormatFactory } from '../legacy_imports';
import { EditorFrameSetup } from '../types';
export interface DatatableVisualizationPluginSetupPlugins {
expressions: ExpressionsSetup;
formatFactory: FormatFactory;
editorFrame: EditorFrameSetup;
}
export class DatatableVisualization {
constructor() {}
setup(
_core: CoreSetup | null,
{ expressions, formatFactory, editorFrame }: DatatableVisualizationPluginSetupPlugins
) {
expressions.registerFunction(() => datatableColumns);
expressions.registerFunction(() => datatable);
expressions.registerRenderer(() => getDatatableRenderer(formatFactory));
editorFrame.registerVisualization(datatableVisualization);
}
}

View file

@ -5,7 +5,7 @@
*/
import React from 'react';
import { createMockDatasource } from '../editor_frame_plugin/mocks';
import { createMockDatasource } from '../editor_frame_service/mocks';
import {
DatatableVisualizationState,
datatableVisualization,

View file

@ -1,49 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { npSetup } from 'ui/new_platform';
import { CoreSetup } from 'src/core/public';
import { getFormat, FormatFactory } from 'ui/visualize/loader/pipeline_helpers/utilities';
import { datatableVisualization } from './visualization';
import { ExpressionsSetup } from '../../../../../../src/plugins/expressions/public';
import { datatable, datatableColumns, getDatatableRenderer } from './expression';
export interface DatatableVisualizationPluginSetupPlugins {
expressions: ExpressionsSetup;
// TODO this is a simulated NP plugin.
// Once field formatters are actually migrated, the actual shim can be used
fieldFormat: {
formatFactory: FormatFactory;
};
}
class DatatableVisualizationPlugin {
constructor() {}
setup(
_core: CoreSetup | null,
{ expressions, fieldFormat }: DatatableVisualizationPluginSetupPlugins
) {
expressions.registerFunction(() => datatableColumns);
expressions.registerFunction(() => datatable);
expressions.registerRenderer(() => getDatatableRenderer(fieldFormat.formatFactory));
return datatableVisualization;
}
stop() {}
}
const plugin = new DatatableVisualizationPlugin();
export const datatableVisualizationSetup = () =>
plugin.setup(npSetup.core, {
expressions: npSetup.plugins.expressions,
fieldFormat: {
formatFactory: getFormat,
},
});
export const datatableVisualizationStop = () => plugin.stop();

View file

@ -1,7 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export * from './plugin';

View file

@ -4,10 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
import _ from 'lodash';
import { Chrome } from 'ui/chrome';
import { capabilities } from 'ui/capabilities';
import {
Capabilities,
HttpSetup,
RecursiveReadonly,
SavedObjectsClientContract,
} from 'kibana/public';
import { i18n } from '@kbn/i18n';
import { IndexPatternsContract, IndexPattern } from '../../../../../../../src/plugins/data/public';
import { ReactExpressionRendererType } from '../../../../../../../src/plugins/expressions/public';
@ -24,14 +26,12 @@ import { getEditPath } from '../../../../../../plugins/lens/common';
export class EmbeddableFactory extends AbstractEmbeddableFactory {
type = DOC_TYPE;
private chrome: Chrome;
private indexPatternService: IndexPatternsContract;
private expressionRenderer: ReactExpressionRendererType;
constructor(
chrome: Chrome,
expressionRenderer: ReactExpressionRendererType,
indexPatternService: IndexPatternsContract
private coreHttp: HttpSetup,
private capabilities: RecursiveReadonly<Capabilities>,
private savedObjectsClient: SavedObjectsClientContract,
private expressionRenderer: ReactExpressionRendererType,
private indexPatternService: IndexPatternsContract
) {
super({
savedObjectMetaData: {
@ -42,13 +42,10 @@ export class EmbeddableFactory extends AbstractEmbeddableFactory {
getIconForSavedObject: () => 'lensApp',
},
});
this.chrome = chrome;
this.expressionRenderer = expressionRenderer;
this.indexPatternService = indexPatternService;
}
public isEditable() {
return capabilities.get().visualize.save as boolean;
return this.capabilities.visualize.save as boolean;
}
canCreateNew() {
@ -66,7 +63,7 @@ export class EmbeddableFactory extends AbstractEmbeddableFactory {
input: Partial<EmbeddableInput> & { id: string },
parent?: IContainer
) {
const store = new SavedObjectIndexStore(this.chrome.getSavedObjectsClient());
const store = new SavedObjectIndexStore(this.savedObjectsClient);
const savedVis = await store.load(savedObjectId);
const promises = savedVis.state.datasourceMetaData.filterableIndexPatterns.map(
@ -91,7 +88,7 @@ export class EmbeddableFactory extends AbstractEmbeddableFactory {
this.expressionRenderer,
{
savedVis,
editUrl: this.chrome.addBasePath(getEditPath(savedObjectId)),
editUrl: this.coreHttp.basePath.prepend(getEditPath(savedObjectId)),
editable: this.isEditable(),
indexPatterns,
},

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export * from './plugin';
export * from './service';

View file

@ -11,7 +11,7 @@ import {
KibanaDatatable,
} from 'src/plugins/expressions/public';
import { LensMultiTable } from '../types';
import { toAbsoluteDates } from '../indexpattern_plugin/auto_date';
import { toAbsoluteDates } from '../indexpattern_datasource/auto_date';
interface MergeTables {
layerIds: string[];

View file

@ -10,12 +10,10 @@ import {
ExpressionsSetup,
ExpressionsStart,
} from '../../../../../../src/plugins/expressions/public';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { embeddablePluginMock } from '../../../../../../src/plugins/embeddable/public/mocks';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { expressionsPluginMock } from '../../../../../../src/plugins/expressions/public/mocks';
import { DatasourcePublicAPI, FramePublicAPI, Datasource, Visualization } from '../types';
import { EditorFrameSetupPlugins, EditorFrameStartPlugins } from './plugin';
import { EditorFrameSetupPlugins, EditorFrameStartPlugins } from './service';
export function createMockVisualization(): jest.Mocked<Visualization> {
return {
@ -108,9 +106,6 @@ export function createMockSetupDependencies() {
data: {},
embeddable: embeddablePluginMock.createSetupContract(),
expressions: expressionsPluginMock.createSetupContract(),
chrome: {
getSavedObjectsClient: () => {},
},
} as unknown) as MockedSetupDependencies;
}

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EditorFramePlugin } from './plugin';
import { EditorFrameService } from './service';
import { coreMock } from 'src/core/public/mocks';
import {
MockedSetupDependencies,
@ -25,14 +25,14 @@ jest.mock('./embeddable/embeddable_factory', () => ({
EmbeddableFactory: class Mock {},
}));
describe('editor_frame plugin', () => {
let pluginInstance: EditorFramePlugin;
describe('editor_frame service', () => {
let pluginInstance: EditorFrameService;
let mountpoint: Element;
let pluginSetupDependencies: MockedSetupDependencies;
let pluginStartDependencies: MockedStartDependencies;
beforeEach(() => {
pluginInstance = new EditorFramePlugin();
pluginInstance = new EditorFrameService();
mountpoint = document.createElement('div');
pluginSetupDependencies = createMockSetupDependencies();
pluginStartDependencies = createMockStartDependencies();
@ -42,26 +42,28 @@ describe('editor_frame plugin', () => {
mountpoint.remove();
});
it('should create an editor frame instance which mounts and unmounts', () => {
expect(() => {
pluginInstance.setup(coreMock.createSetup(), pluginSetupDependencies);
const publicAPI = pluginInstance.start(coreMock.createStart(), pluginStartDependencies);
const instance = publicAPI.createInstance({});
instance.mount(mountpoint, {
onError: jest.fn(),
onChange: jest.fn(),
dateRange: { fromDate: '', toDate: '' },
query: { query: '', language: 'lucene' },
filters: [],
});
instance.unmount();
}).not.toThrowError();
it('should create an editor frame instance which mounts and unmounts', async () => {
await expect(
(async () => {
pluginInstance.setup(coreMock.createSetup(), pluginSetupDependencies);
const publicAPI = pluginInstance.start(coreMock.createStart(), pluginStartDependencies);
const instance = await publicAPI.createInstance({});
instance.mount(mountpoint, {
onError: jest.fn(),
onChange: jest.fn(),
dateRange: { fromDate: '', toDate: '' },
query: { query: '', language: 'lucene' },
filters: [],
});
instance.unmount();
})()
).resolves.toBeUndefined();
});
it('should not have child nodes after unmount', () => {
it('should not have child nodes after unmount', async () => {
pluginInstance.setup(coreMock.createSetup(), pluginSetupDependencies);
const publicAPI = pluginInstance.start(coreMock.createStart(), pluginStartDependencies);
const instance = publicAPI.createInstance({});
const instance = await publicAPI.createInstance({});
instance.mount(mountpoint, {
onError: jest.fn(),
onChange: jest.fn(),

View file

@ -8,8 +8,6 @@ import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { I18nProvider } from '@kbn/i18n/react';
import { CoreSetup, CoreStart } from 'src/core/public';
import chrome, { Chrome } from 'ui/chrome';
import { npSetup, npStart } from 'ui/new_platform';
import {
ExpressionsSetup,
ExpressionsStart,
@ -44,24 +42,35 @@ export interface EditorFrameStartPlugins {
data: DataPublicPluginStart;
embeddable: IEmbeddableStart;
expressions: ExpressionsStart;
chrome: Chrome;
}
export class EditorFramePlugin {
async function collectAsyncDefinitions<T extends { id: string }>(
definitions: Array<T | Promise<T>>
) {
const resolvedDefinitions = await Promise.all(definitions);
const definitionMap: Record<string, T> = {};
resolvedDefinitions.forEach(definition => {
definitionMap[definition.id] = definition;
});
return definitionMap;
}
export class EditorFrameService {
constructor() {}
private readonly datasources: Record<string, Datasource> = {};
private readonly visualizations: Record<string, Visualization> = {};
private readonly datasources: Array<Datasource | Promise<Datasource>> = [];
private readonly visualizations: Array<Visualization | Promise<Visualization>> = [];
public setup(core: CoreSetup, plugins: EditorFrameSetupPlugins): EditorFrameSetup {
plugins.expressions.registerFunction(() => mergeTables);
return {
registerDatasource: datasource => {
this.datasources[datasource.id] = datasource as Datasource<unknown, unknown>;
this.datasources.push(datasource as Datasource<unknown, unknown>);
},
registerVisualization: visualization => {
this.visualizations[visualization.id] = visualization as Visualization<unknown, unknown>;
this.visualizations.push(visualization as Visualization<unknown, unknown>);
},
};
}
@ -70,27 +79,34 @@ export class EditorFramePlugin {
plugins.embeddable.registerEmbeddableFactory(
'lens',
new EmbeddableFactory(
plugins.chrome,
core.http,
core.application.capabilities,
core.savedObjects.client,
plugins.expressions.ReactExpressionRenderer,
plugins.data.indexPatterns
)
);
const createInstance = (): EditorFrameInstance => {
const createInstance = async (): Promise<EditorFrameInstance> => {
let domElement: Element;
const [resolvedDatasources, resolvedVisualizations] = await Promise.all([
collectAsyncDefinitions(this.datasources),
collectAsyncDefinitions(this.visualizations),
]);
return {
mount: (element, { doc, onError, dateRange, query, filters, savedQuery, onChange }) => {
domElement = element;
const firstDatasourceId = Object.keys(this.datasources)[0];
const firstVisualizationId = Object.keys(this.visualizations)[0];
const firstDatasourceId = Object.keys(resolvedDatasources)[0];
const firstVisualizationId = Object.keys(resolvedVisualizations)[0];
render(
<I18nProvider>
<EditorFrame
data-test-subj="lnsEditorFrame"
onError={onError}
datasourceMap={this.datasources}
visualizationMap={this.visualizations}
datasourceMap={resolvedDatasources}
visualizationMap={resolvedVisualizations}
initialDatasourceId={getActiveDatasourceIdFromDoc(doc) || firstDatasourceId || null}
initialVisualizationId={
(doc && doc.visualizationType) || firstVisualizationId || null
@ -120,27 +136,4 @@ export class EditorFramePlugin {
createInstance,
};
}
public stop() {
return {};
}
}
const editorFrame = new EditorFramePlugin();
export const editorFrameSetup = () =>
editorFrame.setup(npSetup.core, {
data: npSetup.plugins.data,
embeddable: npSetup.plugins.embeddable,
expressions: npSetup.plugins.expressions,
});
export const editorFrameStart = () =>
editorFrame.start(npStart.core, {
data: npStart.plugins.data,
embeddable: npStart.plugins.embeddable,
expressions: npStart.plugins.expressions,
chrome,
});
export const editorFrameStop = () => editorFrame.stop();

View file

@ -7,9 +7,9 @@
@import './config_panel';
@import './app_plugin/index';
@import './datatable_visualization_plugin/index';
@import 'datatable_visualization/index';
@import './drag_drop/index';
@import './editor_frame_plugin/index';
@import './indexpattern_plugin/index';
@import './xy_visualization_plugin/index';
@import './metric_visualization_plugin/index';
@import 'editor_frame_service/index';
@import 'indexpattern_datasource/index';
@import 'xy_visualization/index';
@import 'metric_visualization/index';

View file

@ -4,4 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { LensPlugin } from './plugin';
export * from './types';
export const plugin = () => new LensPlugin();

View file

@ -0,0 +1,49 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { CoreSetup } from 'src/core/public';
import { Storage } from '../../../../../../src/plugins/kibana_utils/public';
import { getIndexPatternDatasource } from './indexpattern';
import { renameColumns } from './rename_columns';
import { autoDate } from './auto_date';
import { ExpressionsSetup } from '../../../../../../src/plugins/expressions/public';
import {
DataPublicPluginSetup,
DataPublicPluginStart,
} from '../../../../../../src/plugins/data/public';
import { Datasource, EditorFrameSetup } from '../types';
export interface IndexPatternDatasourceSetupPlugins {
expressions: ExpressionsSetup;
data: DataPublicPluginSetup;
editorFrame: EditorFrameSetup;
}
export interface IndexPatternDatasourceStartPlugins {
data: DataPublicPluginStart;
}
export class IndexPatternDatasource {
constructor() {}
setup(
core: CoreSetup<IndexPatternDatasourceStartPlugins>,
{ expressions, editorFrame }: IndexPatternDatasourceSetupPlugins
) {
expressions.registerFunction(renameColumns);
expressions.registerFunction(autoDate);
editorFrame.registerDatasource(
core.getStartServices().then(([coreStart, { data }]) =>
getIndexPatternDatasource({
core: coreStart,
storage: new Storage(localStorage),
data,
})
) as Promise<Datasource>
);
}
}

View file

@ -4,9 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import chromeMock from 'ui/chrome';
import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
import { SavedObjectsClientContract } from 'kibana/public';
import { getIndexPatternDatasource, IndexPatternColumn, uniqueLabels } from './indexpattern';
import { DatasourcePublicAPI, Operation, Datasource } from '../types';
import { coreMock } from 'src/core/public/mocks';
@ -15,8 +13,6 @@ import { IndexPatternPersistedState, IndexPatternPrivateState } from './types';
jest.mock('./loader');
jest.mock('../id_generator');
// chrome, notify, storage are used by ./plugin
jest.mock('ui/chrome');
// Contains old and new platform data plugins, used for interpreter and filter ratio
jest.mock('ui/new_platform');
@ -142,10 +138,8 @@ describe('IndexPattern Data Source', () => {
beforeEach(() => {
indexPatternDatasource = getIndexPatternDatasource({
chrome: chromeMock,
storage: {} as IStorageWrapper,
core: coreMock.createStart(),
savedObjectsClient: {} as SavedObjectsClientContract,
data: pluginsMock.createStart().data,
});

View file

@ -8,7 +8,7 @@ import _ from 'lodash';
import React from 'react';
import { render } from 'react-dom';
import { I18nProvider } from '@kbn/i18n/react';
import { CoreStart, SavedObjectsClientContract } from 'src/core/public';
import { CoreStart } from 'src/core/public';
import { i18n } from '@kbn/i18n';
import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
import {
@ -21,7 +21,6 @@ import {
import { loadInitialState, changeIndexPattern, changeLayerIndexPattern } from './loader';
import { toExpression } from './to_expression';
import { IndexPatternDimensionPanel } from './dimension_panel';
import { IndexPatternDatasourceSetupPlugins } from './plugin';
import { IndexPatternDataPanel } from './datapanel';
import {
getDatasourceSuggestionsForField,
@ -90,20 +89,16 @@ export function uniqueLabels(layers: Record<string, IndexPatternLayer>) {
}
export function getIndexPatternDatasource({
chrome,
core,
storage,
savedObjectsClient,
data,
}: Pick<IndexPatternDatasourceSetupPlugins, 'chrome'> & {
// Core start is being required here because it contains the savedObject client
// In the new platform, this plugin wouldn't be initialized until after setup
}: {
core: CoreStart;
storage: IStorageWrapper;
savedObjectsClient: SavedObjectsClientContract;
data: ReturnType<DataPlugin['start']>;
}) {
const uiSettings = chrome.getUiSettingsClient();
const savedObjectsClient = core.savedObjects.client;
const uiSettings = core.uiSettings;
const onIndexPatternLoadError = (err: Error) =>
core.notifications.toasts.addError(err, {
title: i18n.translate('xpack.lens.indexPattern.indexPatternLoadError', {
@ -118,7 +113,7 @@ export function getIndexPatternDatasource({
async initialize(state?: IndexPatternPersistedState) {
return loadInitialState({
state,
savedObjectsClient,
savedObjectsClient: await savedObjectsClient,
defaultIndexPatternId: core.uiSettings.get('defaultIndex'),
});
},

View file

@ -5,7 +5,6 @@
*/
import _ from 'lodash';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { SavedObjectsClientContract, SavedObjectAttributes, HttpSetup } from 'src/core/public';
import { SimpleSavedObject } from 'src/core/public';
import { StateSetter } from '../types';

Some files were not shown because too many files have changed in this diff Show more