{
- private showNoResultsMessage = memoizeLast(shouldShowNoResultsMessage);
-
- constructor(props: VisualizationProps) {
- super(props);
-
- props.vis.setUiState(props.uiState);
- }
-
- public render() {
- const { vis, visData, visParams, onInit, uiState, listenOnChange } = this.props;
-
- const noResults = this.showNoResultsMessage(vis, visData);
-
- return (
-
- {noResults ? (
-
- ) : (
-
- )}
-
- );
- }
-
- public shouldComponentUpdate(nextProps: VisualizationProps): boolean {
- if (nextProps.uiState !== this.props.uiState) {
- throw new Error('Changing uiState on is not supported!');
- }
- return true;
- }
-}
diff --git a/src/plugins/visualizations/public/components/visualization_chart.test.js b/src/plugins/visualizations/public/components/visualization_chart.test.js
deleted file mode 100644
index 98cc5bb8d0729..0000000000000
--- a/src/plugins/visualizations/public/components/visualization_chart.test.js
+++ /dev/null
@@ -1,56 +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
- * and the Server Side Public License, v 1; you may not use this file except in
- * compliance with, at your election, the Elastic License or the Server Side
- * Public License, v 1.
- */
-
-jest.useFakeTimers();
-
-import React from 'react';
-import { render, mount } from 'enzyme';
-import { VisualizationChart } from './visualization_chart';
-
-let renderPromise;
-
-class VisualizationStub {
- constructor(el, vis) {
- this.el = el;
- this.vis = vis;
- }
-
- render() {
- renderPromise = new Promise((resolve) => {
- this.el.textContent = this.vis.params.markdown;
- resolve();
- });
-
- return renderPromise;
- }
-}
-
-describe('', () => {
- const vis = {
- type: {
- title: 'Test Visualization',
- visualization: VisualizationStub,
- },
- params: {
- markdown:
- 'This is a test of the [markdown](http://daringfireball.net/projects/markdown) vis.',
- },
- };
-
- it('should render initial html', () => {
- const wrapper = render();
- expect(wrapper.text()).toBe('');
- });
-
- it('should render visualization', async () => {
- const wrapper = mount();
- jest.runAllTimers();
- await renderPromise;
- expect(wrapper.find('.visChart').text()).toMatch(/markdown/);
- });
-});
diff --git a/src/plugins/visualizations/public/components/visualization_chart.tsx b/src/plugins/visualizations/public/components/visualization_chart.tsx
deleted file mode 100644
index c6ad1c53f91b7..0000000000000
--- a/src/plugins/visualizations/public/components/visualization_chart.tsx
+++ /dev/null
@@ -1,134 +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
- * and the Server Side Public License, v 1; you may not use this file except in
- * compliance with, at your election, the Elastic License or the Server Side
- * Public License, v 1.
- */
-
-import React from 'react';
-import * as Rx from 'rxjs';
-import { debounceTime, filter, share, switchMap } from 'rxjs/operators';
-import { PersistedState } from '../../../../plugins/visualizations/public';
-import { VisualizationController } from '../types';
-import { ResizeChecker } from '../../../../plugins/kibana_utils/public';
-import { ExprVis } from '../expressions/vis';
-
-interface VisualizationChartProps {
- onInit?: () => void;
- uiState: PersistedState;
- vis: ExprVis;
- visData: any;
- visParams: any;
- listenOnChange: boolean;
-}
-
-class VisualizationChart extends React.Component {
- private resizeChecker?: ResizeChecker;
- private visualization?: VisualizationController;
- private chartDiv = React.createRef();
- private containerDiv = React.createRef();
- private renderSubject: Rx.Subject<{
- vis: ExprVis;
- visParams: any;
- visData: any;
- }>;
- private renderSubscription: Rx.Subscription;
-
- constructor(props: VisualizationChartProps) {
- super(props);
-
- this.renderSubject = new Rx.Subject();
- const render$ = this.renderSubject.asObservable().pipe(share());
-
- const success$ = render$.pipe(
- filter(({ vis, visData }) => vis && (!vis.type.requiresSearch || visData)),
- debounceTime(100),
- switchMap(async ({ vis, visData, visParams }) => {
- if (!this.visualization) {
- // This should never happen, since we only should trigger another rendering
- // after this component has mounted and thus the visualization implementation
- // has been initialized
- throw new Error('Visualization implementation was not initialized on first render.');
- }
-
- return this.visualization.render(visData, visParams);
- })
- );
-
- this.renderSubscription = success$.subscribe(() => {
- if (this.props.onInit) {
- this.props.onInit();
- }
- });
- }
-
- public render() {
- return (
-
- );
- }
-
- public componentDidMount() {
- if (!this.chartDiv.current || !this.containerDiv.current) {
- throw new Error('chartDiv and currentDiv reference should always be present.');
- }
-
- const { vis } = this.props;
- const Visualization = vis.type.visualization;
-
- if (!Visualization) {
- throw new Error(
- 'Tried to use VisualizationChart component with a vis without visualization property.'
- );
- }
-
- this.visualization = new Visualization(this.chartDiv.current, vis);
-
- // We know that containerDiv.current will never be null, since we will always
- // have rendered and the div is always rendered into the tree (i.e. not
- // inside any condition).
- this.resizeChecker = new ResizeChecker(this.containerDiv.current);
- this.resizeChecker.on('resize', () => this.startRenderVisualization());
-
- if (this.props.listenOnChange) {
- this.props.uiState.on('change', this.onUiStateChanged);
- }
-
- this.startRenderVisualization();
- }
-
- public componentDidUpdate() {
- this.startRenderVisualization();
- }
-
- public componentWillUnmount() {
- if (this.renderSubscription) {
- this.renderSubscription.unsubscribe();
- }
- if (this.resizeChecker) {
- this.resizeChecker.destroy();
- }
- if (this.visualization) {
- this.visualization.destroy();
- }
- }
-
- private onUiStateChanged = () => {
- this.startRenderVisualization();
- };
-
- private startRenderVisualization(): void {
- if (this.containerDiv.current && this.chartDiv.current) {
- this.renderSubject.next({
- vis: this.props.vis,
- visData: this.props.visData,
- visParams: this.props.visParams,
- });
- }
- }
-}
-
-export { VisualizationChart };
diff --git a/src/plugins/visualizations/public/components/visualization_requesterror.test.js b/src/plugins/visualizations/public/components/visualization_requesterror.test.js
deleted file mode 100644
index 8a183efe3faa6..0000000000000
--- a/src/plugins/visualizations/public/components/visualization_requesterror.test.js
+++ /dev/null
@@ -1,28 +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
- * and the Server Side Public License, v 1; you may not use this file except in
- * compliance with, at your election, the Elastic License or the Server Side
- * Public License, v 1.
- */
-
-import React from 'react';
-import { render } from 'enzyme';
-import { VisualizationRequestError } from './visualization_requesterror';
-
-describe('VisualizationRequestError', () => {
- it('should render according to snapshot', () => {
- const wrapper = render();
- expect(wrapper).toMatchSnapshot();
- });
-
- it('should set html when error is an object', () => {
- const wrapper = render();
- expect(wrapper.text()).toBe('Request error');
- });
-
- it('should set html when error is a string', () => {
- const wrapper = render();
- expect(wrapper.text()).toBe('Request error');
- });
-});
diff --git a/src/plugins/visualizations/public/components/visualization_requesterror.tsx b/src/plugins/visualizations/public/components/visualization_requesterror.tsx
deleted file mode 100644
index 3378875327959..0000000000000
--- a/src/plugins/visualizations/public/components/visualization_requesterror.tsx
+++ /dev/null
@@ -1,51 +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
- * and the Server Side Public License, v 1; you may not use this file except in
- * compliance with, at your election, the Elastic License or the Server Side
- * Public License, v 1.
- */
-
-import { EuiIcon, EuiSpacer, EuiText } from '@elastic/eui';
-import React from 'react';
-import { SearchError } from '../../../../plugins/data/public';
-
-interface VisualizationRequestErrorProps {
- onInit?: () => void;
- error: SearchError | string;
-}
-
-export class VisualizationRequestError extends React.Component {
- private containerDiv = React.createRef();
-
- public render() {
- const { error } = this.props;
- const errorMessage = typeof error === 'string' ? error : error.message;
-
- return (
-
-
-
-
-
-
- {errorMessage}
-
-
- );
- }
-
- public componentDidMount() {
- this.afterRender();
- }
-
- public componentDidUpdate() {
- this.afterRender();
- }
-
- private afterRender() {
- if (this.props.onInit) {
- this.props.onInit();
- }
- }
-}
diff --git a/src/plugins/visualizations/public/embeddable/_embeddables.scss b/src/plugins/visualizations/public/embeddable/_embeddables.scss
index f0ec8479489d2..2a4d5c14a46a5 100644
--- a/src/plugins/visualizations/public/embeddable/_embeddables.scss
+++ b/src/plugins/visualizations/public/embeddable/_embeddables.scss
@@ -8,10 +8,6 @@
@include euiScrollBar; /* 2 */
}
- .visualization .visChart__container {
- overflow: visible; /* 1 */
- }
-
.visLegend__toggle {
border-bottom-right-radius: 0;
border-top-left-radius: 0;
diff --git a/src/plugins/visualizations/public/embeddable/get_index_pattern.ts b/src/plugins/visualizations/public/embeddable/get_index_pattern.ts
deleted file mode 100644
index 65c6ebe75daf5..0000000000000
--- a/src/plugins/visualizations/public/embeddable/get_index_pattern.ts
+++ /dev/null
@@ -1,25 +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
- * and the Server Side Public License, v 1; you may not use this file except in
- * compliance with, at your election, the Elastic License or the Server Side
- * Public License, v 1.
- */
-
-import { VisSavedObject } from '../types';
-import type { IndexPattern } from '../../../../plugins/data/public';
-import { getIndexPatterns } from '../services';
-
-export async function getIndexPattern(
- savedVis: VisSavedObject
-): Promise {
- if (savedVis.visState.type !== 'metrics') {
- return savedVis.searchSource!.getField('index');
- }
-
- const indexPatternsClient = getIndexPatterns();
-
- return savedVis.visState.params.index_pattern
- ? (await indexPatternsClient.find(`"${savedVis.visState.params.index_pattern}"`))[0]
- : await indexPatternsClient.getDefault();
-}
diff --git a/src/plugins/visualizations/public/embeddable/to_ast.ts b/src/plugins/visualizations/public/embeddable/to_ast.ts
new file mode 100644
index 0000000000000..31a9db5b92d11
--- /dev/null
+++ b/src/plugins/visualizations/public/embeddable/to_ast.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * and the Server Side Public License, v 1; you may not use this file except in
+ * compliance with, at your election, the Elastic License or the Server Side
+ * Public License, v 1.
+ */
+
+import { ExpressionFunctionKibana, ExpressionFunctionKibanaContext } from '../../../data/public';
+import { buildExpression, buildExpressionFunction } from '../../../expressions/public';
+
+import { VisToExpressionAst } from '../types';
+
+/**
+ * Creates an ast expression for a visualization based on kibana context (query, filters, timerange)
+ * including a saved search if the visualization is based on it.
+ * The expression also includes particular visualization expression ast if presented.
+ *
+ * @internal
+ */
+export const toExpressionAst: VisToExpressionAst = async (vis, params) => {
+ const { savedSearchId, searchSource } = vis.data;
+ const query = searchSource?.getField('query');
+ const filters = searchSource?.getField('filter');
+
+ const kibana = buildExpressionFunction('kibana', {});
+ const kibanaContext = buildExpressionFunction('kibana_context', {
+ q: query && JSON.stringify(query),
+ filters: filters && JSON.stringify(filters),
+ savedSearchId,
+ });
+
+ const ast = buildExpression([kibana, kibanaContext]);
+ const expression = ast.toAst();
+
+ if (!vis.type.toExpressionAst) {
+ throw new Error('Visualization type definition should have toExpressionAst function defined');
+ }
+
+ const visExpressionAst = await vis.type.toExpressionAst(vis, params);
+ // expand the expression chain with a particular visualization expression chain, if it exists
+ expression.chain.push(...visExpressionAst.chain);
+
+ return expression;
+};
diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
index 88fc6e6a98c0d..45bd6a8a9d554 100644
--- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
+++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
@@ -32,8 +32,8 @@ import {
IExpressionLoaderParams,
ExpressionsStart,
ExpressionRenderError,
+ ExpressionAstExpression,
} from '../../../../plugins/expressions/public';
-import { buildPipeline } from '../legacy/build_pipeline';
import { Vis, SerializedVis } from '../vis';
import { getExpressions, getUiActions } from '../services';
import { VIS_EVENT_TO_TRIGGER } from './events';
@@ -41,6 +41,7 @@ import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory';
import { SavedObjectAttributes } from '../../../../core/types';
import { SavedVisualizationsLoader } from '../saved_visualizations';
import { VisSavedObject } from '../types';
+import { toExpressionAst } from './to_ast';
const getKeys = (o: T): Array => Object.keys(o) as Array;
@@ -94,7 +95,7 @@ export class VisualizeEmbeddable
private syncColors?: boolean;
private visCustomizations?: Pick;
private subscriptions: Subscription[] = [];
- private expression: string = '';
+ private expression?: ExpressionAstExpression;
private vis: Vis;
private domNode: any;
public readonly type = VISUALIZE_EMBEDDABLE_TYPE;
@@ -382,7 +383,7 @@ export class VisualizeEmbeddable
}
this.abortController = new AbortController();
const abortController = this.abortController;
- this.expression = await buildPipeline(this.vis, {
+ this.expression = await toExpressionAst(this.vis, {
timefilter: this.timefilter,
timeRange: this.timeRange,
abortSignal: this.abortController!.signal,
diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx
index 399e0e4b71532..6a57bf7b4477a 100644
--- a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx
+++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx
@@ -74,12 +74,12 @@ export class VisualizeEmbeddableFactory
type: 'visualization',
getIconForSavedObject: (savedObject) => {
return (
- getTypes().get(JSON.parse(savedObject.attributes.visState).type).icon || 'visualizeApp'
+ getTypes().get(JSON.parse(savedObject.attributes.visState).type)?.icon || 'visualizeApp'
);
},
getTooltipForSavedObject: (savedObject) => {
return `${savedObject.attributes.title} (${
- getTypes().get(JSON.parse(savedObject.attributes.visState).type).title
+ getTypes().get(JSON.parse(savedObject.attributes.visState).type)?.title
})`;
},
showSavedObject: (savedObject) => {
diff --git a/src/plugins/visualizations/public/expressions/vis.ts b/src/plugins/visualizations/public/expressions/vis.ts
deleted file mode 100644
index a3952d284805d..0000000000000
--- a/src/plugins/visualizations/public/expressions/vis.ts
+++ /dev/null
@@ -1,157 +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
- * and the Server Side Public License, v 1; you may not use this file except in
- * compliance with, at your election, the Elastic License or the Server Side
- * Public License, v 1.
- */
-
-/**
- * @name Vis
- *
- * @description This class consists of aggs, params, listeners, title, and type.
- * - Aggs: Instances of AggConfig.
- * - Params: The settings in the Options tab.
- *
- * Not to be confused with vislib/vis.js.
- */
-
-import { EventEmitter } from 'events';
-import _ from 'lodash';
-import { VisParams, PersistedState } from '../../../../plugins/visualizations/public';
-
-import { getTypes } from '../services';
-import { VisType } from '../vis_types';
-
-export interface ExprVisState {
- title?: string;
- type: VisType | string;
- params?: VisParams;
-}
-
-export interface ExprVisAPIEvents {
- filter: (data: any) => void;
- brush: (data: any) => void;
- applyFilter: (data: any) => void;
-}
-
-export interface ExprVisAPI {
- events: ExprVisAPIEvents;
-}
-
-export class ExprVis extends EventEmitter {
- public title: string = '';
- public type: VisType;
- public params: VisParams = {};
- public sessionState: Record = {};
- public API: ExprVisAPI;
- public eventsSubject: any;
- private uiState: PersistedState;
-
- constructor(visState: ExprVisState = { type: 'histogram' }) {
- super();
-
- this.type = this.getType(visState.type);
- this.uiState = new PersistedState();
- this.setState(visState);
-
- this.API = {
- events: {
- filter: (data: any) => {
- if (!this.eventsSubject) return;
- this.eventsSubject.next({
- name: 'filterBucket',
- data: data.data
- ? {
- data: data.data,
- negate: data.negate,
- }
- : { data: [data] },
- });
- },
- brush: (data: any) => {
- if (!this.eventsSubject) return;
- this.eventsSubject.next({ name: 'brush', data });
- },
- applyFilter: (data: any) => {
- if (!this.eventsSubject) return;
- this.eventsSubject.next({ name: 'applyFilter', data });
- },
- },
- };
- }
-
- private getType(type: string | VisType) {
- if (_.isString(type)) {
- const newType = getTypes().get(type);
- if (!newType) {
- throw new Error(`Invalid type "${type}"`);
- }
- return newType;
- } else {
- return type;
- }
- }
-
- setState(state: ExprVisState) {
- this.title = state.title || '';
- if (state.type) {
- this.type = this.getType(state.type);
- }
- this.params = _.defaultsDeep(
- {},
- _.cloneDeep(state.params || {}),
- _.cloneDeep(this.type.visConfig.defaults || {})
- );
- }
-
- getState() {
- return {
- title: this.title,
- type: this.type.name,
- params: _.cloneDeep(this.params),
- };
- }
-
- updateState() {
- this.emit('update');
- }
-
- forceReload() {
- this.emit('reload');
- }
-
- isHierarchical() {
- if (_.isFunction(this.type.hierarchicalData)) {
- return !!this.type.hierarchicalData(this);
- } else {
- return !!this.type.hierarchicalData;
- }
- }
-
- hasUiState() {
- return !!this.uiState;
- }
-
- getUiState() {
- return this.uiState;
- }
-
- setUiState(state: PersistedState) {
- this.uiState = state;
- }
-
- /**
- * Currently this is only used to extract map-specific information
- * (e.g. mapZoom, mapCenter).
- */
- uiStateVal(key: string, val: any) {
- if (this.hasUiState()) {
- if (_.isUndefined(val)) {
- return this.uiState.get(key);
- }
- return this.uiState.set(key, val);
- }
- return val;
- }
-}
diff --git a/src/plugins/visualizations/public/expressions/visualization_function.ts b/src/plugins/visualizations/public/expressions/visualization_function.ts
deleted file mode 100644
index 623fb303baccc..0000000000000
--- a/src/plugins/visualizations/public/expressions/visualization_function.ts
+++ /dev/null
@@ -1,144 +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
- * and the Server Side Public License, v 1; you may not use this file except in
- * compliance with, at your election, the Elastic License or the Server Side
- * Public License, v 1.
- */
-
-import { get } from 'lodash';
-import { i18n } from '@kbn/i18n';
-import { VisResponseValue, PersistedState } from '../../../../plugins/visualizations/public';
-import { ExpressionFunctionDefinition, Render } from '../../../../plugins/expressions/public';
-import { getTypes, getIndexPatterns, getFilterManager, getSearch } from '../services';
-
-interface Arguments {
- index?: string | null;
- metricsAtAllLevels?: boolean;
- partialRows?: boolean;
- type?: string;
- schemas?: string;
- visConfig?: string;
- uiState?: string;
- aggConfigs?: string;
-}
-
-export type ExpressionFunctionVisualization = ExpressionFunctionDefinition<
- 'visualization',
- any,
- Arguments,
- Promise>
->;
-
-export const visualization = (): ExpressionFunctionVisualization => ({
- name: 'visualization',
- type: 'render',
- help: i18n.translate('visualizations.functions.visualization.help', {
- defaultMessage: 'A simple visualization',
- }),
- args: {
- // TODO: Below `help` keys should be internationalized once this function
- // TODO: is moved to visualizations plugin.
- index: {
- types: ['string', 'null'],
- default: null,
- help: 'Index',
- },
- metricsAtAllLevels: {
- types: ['boolean'],
- default: false,
- help: 'Metrics levels',
- },
- partialRows: {
- types: ['boolean'],
- default: false,
- help: 'Partial rows',
- },
- type: {
- types: ['string'],
- default: '',
- help: 'Type',
- },
- schemas: {
- types: ['string'],
- default: '"{}"',
- help: 'Schemas',
- },
- visConfig: {
- types: ['string'],
- default: '"{}"',
- help: 'Visualization configuration',
- },
- uiState: {
- types: ['string'],
- default: '"{}"',
- help: 'User interface state',
- },
- aggConfigs: {
- types: ['string'],
- default: '"{}"',
- help: 'Aggregation configurations',
- },
- },
- async fn(input, args, { inspectorAdapters }) {
- const visConfigParams = args.visConfig ? JSON.parse(args.visConfig) : {};
- const schemas = args.schemas ? JSON.parse(args.schemas) : {};
- const visType = getTypes().get(args.type || 'histogram') as any;
- const indexPattern = args.index ? await getIndexPatterns().get(args.index) : null;
-
- const uiStateParams = args.uiState ? JSON.parse(args.uiState) : {};
- const uiState = new PersistedState(uiStateParams);
-
- const aggConfigsState = args.aggConfigs ? JSON.parse(args.aggConfigs) : [];
- const aggs = indexPattern
- ? getSearch().aggs.createAggConfigs(indexPattern, aggConfigsState)
- : undefined;
-
- if (typeof visType.requestHandler === 'function') {
- input = await visType.requestHandler({
- partialRows: args.partialRows,
- metricsAtAllLevels: args.metricsAtAllLevels,
- index: indexPattern,
- visParams: visConfigParams,
- timeRange: get(input, 'timeRange', null),
- query: get(input, 'query', null),
- filters: get(input, 'filters', null),
- uiState,
- inspectorAdapters,
- queryFilter: getFilterManager(),
- aggs,
- });
- }
-
- if (typeof visType.responseHandler === 'function') {
- if (input.columns) {
- // assign schemas to aggConfigs
- input.columns.forEach((column: any) => {
- if (column.aggConfig) {
- column.aggConfig.aggConfigs.schemas = visType.schemas.all;
- }
- });
-
- Object.keys(schemas).forEach((key) => {
- schemas[key].forEach((i: any) => {
- if (input.columns[i] && input.columns[i].aggConfig) {
- input.columns[i].aggConfig.schema = key;
- }
- });
- });
- }
-
- input = await visType.responseHandler(input, visConfigParams.dimensions);
- }
-
- return {
- type: 'render',
- as: 'visualization',
- value: {
- visData: input,
- visType: args.type || '',
- visConfig: visConfigParams,
- },
- };
- },
-});
diff --git a/src/plugins/visualizations/public/expressions/visualization_renderer.tsx b/src/plugins/visualizations/public/expressions/visualization_renderer.tsx
deleted file mode 100644
index 3547a5d7e20ce..0000000000000
--- a/src/plugins/visualizations/public/expressions/visualization_renderer.tsx
+++ /dev/null
@@ -1,51 +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
- * and the Server Side Public License, v 1; you may not use this file except in
- * compliance with, at your election, the Elastic License or the Server Side
- * Public License, v 1.
- */
-
-import React from 'react';
-import { render, unmountComponentAtNode } from 'react-dom';
-// @ts-ignore
-import { ExprVis } from './vis';
-import { Visualization } from '../components';
-import { VisParams } from '../types';
-
-export const visualization = () => ({
- name: 'visualization',
- displayName: 'visualization',
- reuseDomNode: true,
- render: async (domNode: HTMLElement, config: any, handlers: any) => {
- const { visData, visConfig, params } = config;
- const visType = config.visType || visConfig.type;
-
- const vis = new ExprVis({
- title: config.title,
- type: visType as string,
- params: visConfig as VisParams,
- });
-
- vis.eventsSubject = { next: handlers.event };
-
- const uiState = handlers.uiState || vis.getUiState();
-
- handlers.onDestroy(() => {
- unmountComponentAtNode(domNode);
- });
-
- const listenOnChange = params ? params.listenOnChange : false;
- render(
- ,
- domNode
- );
- },
-});
diff --git a/src/plugins/visualizations/public/index.ts b/src/plugins/visualizations/public/index.ts
index 0bf8aa6e5c418..b4216e1fc16af 100644
--- a/src/plugins/visualizations/public/index.ts
+++ b/src/plugins/visualizations/public/index.ts
@@ -10,7 +10,6 @@ import { PublicContract } from '@kbn/utility-types';
import { PluginInitializerContext } from 'src/core/public';
import { VisualizationsPlugin, VisualizationsSetup, VisualizationsStart } from './plugin';
import { VisualizeEmbeddableFactory, VisualizeEmbeddable } from './embeddable';
-import { ExprVis as ExprVisClass } from './expressions/vis';
export function plugin(initializerContext: PluginInitializerContext) {
return new VisualizationsPlugin(initializerContext);
@@ -20,39 +19,27 @@ export function plugin(initializerContext: PluginInitializerContext) {
export { Vis } from './vis';
export { TypesService } from './vis_types/types_service';
export { VISUALIZE_EMBEDDABLE_TYPE, VIS_EVENT_TO_TRIGGER } from './embeddable';
-export { VisualizationContainer, VisualizationNoResults } from './components';
-export { getSchemas as getVisSchemas } from './legacy/build_pipeline';
+export { VisualizationContainer } from './components';
+export { getVisSchemas } from './vis_schemas';
/** @public types */
export { VisualizationsSetup, VisualizationsStart };
export { VisGroups } from './vis_types';
-export type {
- VisTypeAlias,
- VisType,
- BaseVisTypeOptions,
- ReactVisTypeOptions,
- Schema,
- ISchemas,
-} from './vis_types';
+export type { VisTypeAlias, VisTypeDefinition, Schema, ISchemas } from './vis_types';
export { SerializedVis, SerializedVisData, VisData } from './vis';
export type VisualizeEmbeddableFactoryContract = PublicContract;
export type VisualizeEmbeddableContract = PublicContract;
export { VisualizeInput } from './embeddable';
-export type ExprVis = ExprVisClass;
-export { SchemaConfig, BuildPipelineParams } from './legacy/build_pipeline';
-// @ts-ignore
+export { SchemaConfig } from './vis_schemas';
export { updateOldState } from './legacy/vis_update_state';
export { PersistedState } from './persisted_state';
export {
- VisualizationControllerConstructor,
- VisualizationController,
ISavedVis,
VisSavedObject,
- VisResponseValue,
VisToExpressionAst,
- VisParams,
+ VisToExpressionAstParams,
+ VisEditorOptionsProps,
} from './types';
-export { ExprVisAPIEvents } from './expressions/vis';
export { VisualizationListItem, VisualizationStage } from './vis_types/vis_type_alias_registry';
export { VISUALIZE_ENABLE_LABS_SETTING } from '../common/constants';
-export { SavedVisState } from '../common';
+export { SavedVisState, VisParams } from '../common';
diff --git a/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap b/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap
deleted file mode 100644
index 3d685064111dc..0000000000000
--- a/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap
+++ /dev/null
@@ -1,3 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`visualize loader pipeline helpers: build pipeline buildPipeline calls toExpression on vis_type if it exists 1`] = `"kibana | kibana_context | test"`;
diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.test.ts b/src/plugins/visualizations/public/legacy/build_pipeline.test.ts
deleted file mode 100644
index a3d867380602d..0000000000000
--- a/src/plugins/visualizations/public/legacy/build_pipeline.test.ts
+++ /dev/null
@@ -1,86 +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
- * and the Server Side Public License, v 1; you may not use this file except in
- * compliance with, at your election, the Elastic License or the Server Side
- * Public License, v 1.
- */
-
-import { prepareJson, prepareString, buildPipeline } from './build_pipeline';
-import { Vis } from '..';
-import { dataPluginMock } from '../../../../plugins/data/public/mocks';
-import { parseExpression } from '../../../expressions/common';
-
-describe('visualize loader pipeline helpers: build pipeline', () => {
- describe('prepareJson', () => {
- it('returns a correctly formatted key/value string', () => {
- const expected = `foo='{}' `; // trailing space is expected
- const actual = prepareJson('foo', {});
- expect(actual).toBe(expected);
- });
-
- it('stringifies provided data', () => {
- const expected = `foo='{\"well\":\"hello\",\"there\":{\"friend\":true}}' `;
- const actual = prepareJson('foo', { well: 'hello', there: { friend: true } });
- expect(actual).toBe(expected);
- });
-
- it('escapes single quotes', () => {
- const expected = `foo='{\"well\":\"hello \\'hi\\'\",\"there\":{\"friend\":true}}' `;
- const actual = prepareJson('foo', { well: `hello 'hi'`, there: { friend: true } });
- expect(actual).toBe(expected);
- });
-
- it('returns empty string if data is undefined', () => {
- const actual = prepareJson('foo', undefined);
- expect(actual).toBe('');
- });
- });
-
- describe('prepareString', () => {
- it('returns a correctly formatted key/value string', () => {
- const expected = `foo='bar' `; // trailing space is expected
- const actual = prepareString('foo', 'bar');
- expect(actual).toBe(expected);
- });
-
- it('escapes single quotes', () => {
- const expected = `foo='\\'bar\\'' `;
- const actual = prepareString('foo', `'bar'`);
- expect(actual).toBe(expected);
- });
-
- it('returns empty string if data is undefined', () => {
- const actual = prepareString('foo', undefined);
- expect(actual).toBe('');
- });
- });
-
- describe('buildPipeline', () => {
- const dataStart = dataPluginMock.createStartContract();
-
- it('calls toExpression on vis_type if it exists', async () => {
- const vis = ({
- getState: () => {},
- isHierarchical: () => false,
- data: {
- aggs: {
- getResponseAggs: () => [],
- },
- searchSource: {
- getField: jest.fn(),
- getParent: jest.fn(),
- },
- },
- // @ts-ignore
- type: {
- toExpressionAst: () => parseExpression('test'),
- },
- } as unknown) as Vis;
- const expression = await buildPipeline(vis, {
- timefilter: dataStart.query.timefilter.timefilter,
- });
- expect(expression).toMatchSnapshot();
- });
- });
-});
diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.ts b/src/plugins/visualizations/public/legacy/build_pipeline.ts
deleted file mode 100644
index d337ef7bcf379..0000000000000
--- a/src/plugins/visualizations/public/legacy/build_pipeline.ts
+++ /dev/null
@@ -1,253 +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
- * and the Server Side Public License, v 1; you may not use this file except in
- * compliance with, at your election, the Elastic License or the Server Side
- * Public License, v 1.
- */
-
-import {
- buildExpression,
- formatExpression,
- SerializedFieldFormat,
-} from '../../../../plugins/expressions/public';
-import { IAggConfig, search, TimefilterContract } from '../../../../plugins/data/public';
-import { Vis } from '../types';
-const { isDateHistogramBucketAggConfig } = search.aggs;
-
-interface SchemaConfigParams {
- precision?: number;
- useGeocentroid?: boolean;
-}
-
-export interface SchemaConfig {
- accessor: number;
- label: string;
- format: SerializedFieldFormat;
- params: SchemaConfigParams;
- aggType: string;
-}
-
-export interface Schemas {
- metric: SchemaConfig[];
- bucket?: SchemaConfig[];
- geo_centroid?: any[];
- group?: any[];
- params?: any[];
- radius?: any[];
- segment?: any[];
- split_column?: SchemaConfig[];
- split_row?: SchemaConfig[];
- width?: any[];
- // catch all for schema name
- [key: string]: any[] | undefined;
-}
-export interface BuildPipelineParams {
- timefilter: TimefilterContract;
- timeRange?: any;
- abortSignal?: AbortSignal;
-}
-
-export const getSchemas = (
- vis: Vis,
- { timeRange, timefilter }: BuildPipelineParams
-): Schemas => {
- const createSchemaConfig = (accessor: number, agg: IAggConfig): SchemaConfig => {
- if (isDateHistogramBucketAggConfig(agg)) {
- agg.params.timeRange = timeRange;
- const bounds =
- agg.params.timeRange && agg.fieldIsTimeField()
- ? timefilter.calculateBounds(agg.params.timeRange)
- : undefined;
- agg.buckets.setBounds(bounds);
- agg.buckets.setInterval(agg.params.interval);
- }
-
- const hasSubAgg = [
- 'derivative',
- 'moving_avg',
- 'serial_diff',
- 'cumulative_sum',
- 'sum_bucket',
- 'avg_bucket',
- 'min_bucket',
- 'max_bucket',
- ].includes(agg.type.name);
-
- const formatAgg = hasSubAgg
- ? agg.params.customMetric || agg.aggConfigs.getRequestAggById(agg.params.metricAgg)
- : agg;
-
- const params: SchemaConfigParams = {};
-
- if (agg.type.name === 'geohash_grid') {
- params.precision = agg.params.precision;
- params.useGeocentroid = agg.params.useGeocentroid;
- }
-
- const label = agg.makeLabel && agg.makeLabel();
-
- return {
- accessor,
- format: formatAgg.toSerializedFieldFormat(),
- params,
- label,
- aggType: agg.type.name,
- };
- };
-
- let cnt = 0;
- const schemas: Schemas = {
- metric: [],
- };
-
- if (!vis.data.aggs) {
- return schemas;
- }
-
- const responseAggs = vis.data.aggs.getResponseAggs().filter((agg: IAggConfig) => agg.enabled);
- const isHierarchical = vis.isHierarchical();
- const metrics = responseAggs.filter((agg: IAggConfig) => agg.type.type === 'metrics');
- responseAggs.forEach((agg: IAggConfig) => {
- let skipMetrics = false;
- let schemaName = agg.schema;
- if (!schemaName) {
- if (agg.type.name === 'geo_centroid') {
- schemaName = 'geo_centroid';
- } else {
- cnt++;
- return;
- }
- }
- if (schemaName === 'split') {
- // TODO: We should check if there's a better way then casting to `any` here
- schemaName = `split_${(vis.params as any).row ? 'row' : 'column'}`;
- skipMetrics = responseAggs.length - metrics.length > 1;
- }
- if (!schemas[schemaName]) {
- schemas[schemaName] = [];
- }
- if (!isHierarchical || agg.type.type !== 'metrics') {
- schemas[schemaName]!.push(createSchemaConfig(cnt++, agg));
- }
- if (isHierarchical && (agg.type.type !== 'metrics' || metrics.length === responseAggs.length)) {
- metrics.forEach((metric: any) => {
- const schemaConfig = createSchemaConfig(cnt++, metric);
- if (!skipMetrics) {
- schemas.metric.push(schemaConfig);
- }
- });
- }
- });
- return schemas;
-};
-
-export const prepareJson = (variable: string, data?: object): string => {
- if (data === undefined) {
- return '';
- }
- return `${variable}='${JSON.stringify(data).replace(/\\/g, `\\\\`).replace(/'/g, `\\'`)}' `;
-};
-
-export const escapeString = (data: string): string => {
- return data.replace(/\\/g, `\\\\`).replace(/'/g, `\\'`);
-};
-
-export const prepareString = (variable: string, data?: string): string => {
- if (data === undefined) {
- return '';
- }
- return `${variable}='${escapeString(data)}' `;
-};
-
-export const prepareValue = (variable: string, data: any, raw: boolean = false) => {
- if (data === undefined) {
- return '';
- }
- if (raw) {
- return `${variable}=${data} `;
- }
- switch (typeof data) {
- case 'string':
- return prepareString(variable, data);
- case 'object':
- return prepareJson(variable, data);
- default:
- return `${variable}=${data} `;
- }
-};
-
-export const prepareDimension = (variable: string, data: any) => {
- if (data === undefined) {
- return '';
- }
-
- let expr = `${variable}={visdimension ${data.accessor} `;
- if (data.format) {
- expr += prepareValue('format', data.format.id);
- expr += prepareJson('formatParams', data.format.params);
- }
- expr += '} ';
-
- return expr;
-};
-
-export const buildPipeline = async (vis: Vis, params: BuildPipelineParams) => {
- const { indexPattern, searchSource } = vis.data;
- const query = searchSource!.getField('query');
- const filters = searchSource!.getField('filter');
- const { uiState, title } = vis;
-
- // context
- let pipeline = `kibana | kibana_context `;
- if (query) {
- pipeline += prepareJson('query', query);
- }
- if (filters) {
- pipeline += prepareJson('filters', filters);
- }
- if (vis.data.savedSearchId) {
- pipeline += prepareString('savedSearchId', vis.data.savedSearchId);
- }
- pipeline += '| ';
-
- if (vis.type.toExpressionAst) {
- const visAst = await vis.type.toExpressionAst(vis, params);
- pipeline += formatExpression(visAst);
- } else {
- // request handler
- if (vis.type.requestHandler === 'courier') {
- pipeline += `esaggs
- index={indexPatternLoad ${prepareString('id', indexPattern!.id)}}
- metricsAtAllLevels=${vis.isHierarchical()}
- partialRows=${vis.params.showPartialRows || false} `;
- if (vis.data.aggs) {
- vis.data.aggs.aggs.forEach((agg) => {
- const ast = agg.toExpressionAst();
- if (ast) {
- pipeline += `aggs={${buildExpression(ast).toString()}} `;
- }
- });
- }
- pipeline += `| `;
- } else {
- const schemas = getSchemas(vis, params);
- const visConfig = { ...vis.params };
- visConfig.dimensions = schemas;
- visConfig.title = title;
- pipeline += `visualization type='${vis.type.name}'
- ${prepareJson('visConfig', visConfig)}
- ${prepareJson('uiState', uiState)}
- metricsAtAllLevels=${vis.isHierarchical()}
- partialRows=${vis.params.showPartialRows || false} `;
- if (indexPattern) {
- pipeline += `${prepareString('index', indexPattern.id)} `;
- if (vis.data.aggs) {
- pipeline += `${prepareJson('aggConfigs', vis.data.aggs!.aggs)}`;
- }
- }
- }
- }
-
- return pipeline;
-};
diff --git a/src/plugins/visualizations/public/mocks.ts b/src/plugins/visualizations/public/mocks.ts
index a8bf3b1ff3eb9..4f9fd53125847 100644
--- a/src/plugins/visualizations/public/mocks.ts
+++ b/src/plugins/visualizations/public/mocks.ts
@@ -22,7 +22,6 @@ import { savedObjectsPluginMock } from '../../../plugins/saved_objects/public/mo
const createSetupContract = (): VisualizationsSetup => ({
createBaseVisualization: jest.fn(),
- createReactVisualization: jest.fn(),
registerAlias: jest.fn(),
hideTypes: jest.fn(),
});
diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts
index 8a82b36f37caf..24514dde10cda 100644
--- a/src/plugins/visualizations/public/plugin.ts
+++ b/src/plugins/visualizations/public/plugin.ts
@@ -20,15 +20,12 @@ import { TypesService, TypesSetup, TypesStart } from './vis_types';
import {
setUISettings,
setTypes,
- setI18n,
setApplication,
setCapabilities,
setHttp,
- setIndexPatterns,
setSearch,
setSavedObjects,
setUsageCollector,
- setFilterManager,
setExpressions,
setUiActions,
setSavedVisualizationsLoader,
@@ -47,8 +44,6 @@ import {
} from './embeddable';
import { ExpressionsSetup, ExpressionsStart } from '../../expressions/public';
import { EmbeddableSetup, EmbeddableStart } from '../../embeddable/public';
-import { visualization as visualizationFunction } from './expressions/visualization_function';
-import { visualization as visualizationRenderer } from './expressions/visualization_renderer';
import { range as rangeExpressionFunction } from './expression_functions/range';
import { visDimension as visDimensionExpressionFunction } from './expression_functions/vis_dimension';
import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../plugins/data/public';
@@ -138,8 +133,6 @@ export class VisualizationsPlugin
setUISettings(core.uiSettings);
setUsageCollector(usageCollection);
- expressions.registerFunction(visualizationFunction);
- expressions.registerRenderer(visualizationRenderer);
expressions.registerFunction(rangeExpressionFunction);
expressions.registerFunction(visDimensionExpressionFunction);
const embeddableFactory = new VisualizeEmbeddableFactory({ start });
@@ -155,7 +148,6 @@ export class VisualizationsPlugin
{ data, expressions, uiActions, embeddable, dashboard, savedObjects }: VisualizationsStartDeps
): VisualizationsStart {
const types = this.types.start();
- setI18n(core.i18n);
setTypes(types);
setEmbeddable(embeddable);
setApplication(core.application);
@@ -163,9 +155,7 @@ export class VisualizationsPlugin
setHttp(core.http);
setSavedObjects(core.savedObjects);
setDocLinks(core.docLinks);
- setIndexPatterns(data.indexPatterns);
setSearch(data.search);
- setFilterManager(data.query.filterManager);
setExpressions(expressions);
setUiActions(uiActions);
setTimeFilter(data.query.timefilter.timefilter);
diff --git a/src/plugins/visualizations/public/services.ts b/src/plugins/visualizations/public/services.ts
index dd7d0941e7378..30cc514e48de4 100644
--- a/src/plugins/visualizations/public/services.ts
+++ b/src/plugins/visualizations/public/services.ts
@@ -11,7 +11,6 @@ import {
Capabilities,
ChromeStart,
HttpStart,
- I18nStart,
IUiSettingsClient,
OverlayStart,
SavedObjectsStart,
@@ -19,12 +18,7 @@ import {
} from '../../../core/public';
import { TypesStart } from './vis_types';
import { createGetterSetter } from '../../../plugins/kibana_utils/public';
-import {
- DataPublicPluginStart,
- FilterManager,
- IndexPatternsContract,
- TimefilterContract,
-} from '../../../plugins/data/public';
+import { DataPublicPluginStart, TimefilterContract } from '../../../plugins/data/public';
import { UsageCollectionSetup } from '../../../plugins/usage_collection/public';
import { ExpressionsStart } from '../../../plugins/expressions/public';
import { UiActionsStart } from '../../../plugins/ui_actions/public';
@@ -48,20 +42,10 @@ export const [getSavedObjects, setSavedObjects] = createGetterSetter('Types');
-export const [getI18n, setI18n] = createGetterSetter('I18n');
-
export const [getDocLinks, setDocLinks] = createGetterSetter('DocLinks');
-export const [getFilterManager, setFilterManager] = createGetterSetter(
- 'FilterManager'
-);
-
export const [getTimeFilter, setTimeFilter] = createGetterSetter('TimeFilter');
-export const [getIndexPatterns, setIndexPatterns] = createGetterSetter(
- 'IndexPatterns'
-);
-
export const [getSearch, setSearch] = createGetterSetter('Search');
export const [getUsageCollector, setUsageCollector] = createGetterSetter(
diff --git a/src/plugins/visualizations/public/types.ts b/src/plugins/visualizations/public/types.ts
index dc9ca49840561..3a751c92f3ae7 100644
--- a/src/plugins/visualizations/public/types.ts
+++ b/src/plugins/visualizations/public/types.ts
@@ -7,26 +7,27 @@
*/
import { SavedObject } from '../../../plugins/saved_objects/public';
-import { SearchSourceFields, TimefilterContract } from '../../../plugins/data/public';
+import {
+ AggConfigOptions,
+ IAggConfigs,
+ SearchSourceFields,
+ TimefilterContract,
+} from '../../../plugins/data/public';
import { ExpressionAstExpression } from '../../expressions/public';
import { SerializedVis, Vis } from './vis';
-import { ExprVis } from './expressions/vis';
-import { SavedVisState, VisParams } from '../common/types';
+import { PersistedState } from './persisted_state';
+import { VisParams } from '../common';
export { Vis, SerializedVis, VisParams };
-export interface VisualizationController {
- render(visData: any, visParams: any): Promise;
- destroy(): void;
- isLoaded?(): Promise | void;
+export interface SavedVisState {
+ title: string;
+ type: string;
+ params: VisParams;
+ aggs: AggConfigOptions[];
}
-export type VisualizationControllerConstructor = new (
- el: HTMLElement,
- vis: ExprVis
-) => VisualizationController;
-
export interface ISavedVis {
id?: string;
title: string;
@@ -40,13 +41,6 @@ export interface ISavedVis {
export interface VisSavedObject extends SavedObject, ISavedVis {}
-export interface VisResponseValue {
- visType: string;
- visData: object;
- visConfig: object;
- params?: object;
-}
-
export interface VisToExpressionAstParams {
timefilter: TimefilterContract;
timeRange?: any;
@@ -57,3 +51,15 @@ export type VisToExpressionAst = (
vis: Vis,
params: VisToExpressionAstParams
) => Promise