diff --git a/packages/kbn-interpreter/plugin_src/types/index.js b/packages/kbn-interpreter/plugin_src/types/index.js index 1ae5f874835c3..ea3aa7d519891 100644 --- a/packages/kbn-interpreter/plugin_src/types/index.js +++ b/packages/kbn-interpreter/plugin_src/types/index.js @@ -29,6 +29,8 @@ import { render } from './render'; import { shape } from './shape'; import { string } from './string'; import { style } from './style'; +import { kibanaTable } from './kibana_table'; +import { kibanaContext } from './kibana_context'; export const typeSpecs = [ boolean, @@ -43,4 +45,6 @@ export const typeSpecs = [ shape, string, style, + kibanaTable, + kibanaContext, ]; diff --git a/packages/kbn-interpreter/plugin_src/types/kibana_context.js b/packages/kbn-interpreter/plugin_src/types/kibana_context.js new file mode 100644 index 0000000000000..b186ae135788d --- /dev/null +++ b/packages/kbn-interpreter/plugin_src/types/kibana_context.js @@ -0,0 +1,36 @@ +/* + * 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. + */ + +export const kibanaContext = () => ({ + name: 'kibana_context', + from: { + null: () => { + return { + type: 'kibana_context', + }; + }, + }, + to: { + null: () => { + return { + type: 'null', + }; + }, + } +}); diff --git a/packages/kbn-interpreter/plugin_src/types/kibana_table.js b/packages/kbn-interpreter/plugin_src/types/kibana_table.js new file mode 100644 index 0000000000000..bdc8da9fecf41 --- /dev/null +++ b/packages/kbn-interpreter/plugin_src/types/kibana_table.js @@ -0,0 +1,138 @@ +/* + * 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 _ from 'lodash'; + +export const kibanaTable = () => ({ + name: 'kibana_table', + serialize: context => { + context.columns.forEach(column => { + column.aggConfig = column.aggConfig.toJSON(); + }); + return context; + }, + deserialize: () => { + // config.visData.columns.forEach(column => { + // column.aggConfig = new AggConfig(a, column.aggConfig); + // }); + }, + validate: tabify => { + if (!tabify.columns) { + throw new Error('tabify must have a columns array, even if it is empty'); + } + }, + from: { + null: () => { + return { + type: 'kibana_table', + columns: [], + }; + }, + datatable: context => { + const converted = { + columns: context.columns.map(column => { + return { + id: column.name, + title: column.name, + ...column + }; + }), + rows: context.rows.map(row => { + const crow = {}; + context.columns.forEach(column => { + crow[column.name] = (row[column.name]); + }); + return crow; + }) + }; + return { + type: 'kibana_table', + ...converted, + }; + }, + number: context => { + return { + type: 'kibana_table', + columns: [{ id: 'col-0', title: 'Count' }], + rows: [{ 'col-0': context }] + }; + }, + pointseries: context => { + const converted = { + tables: [{ + columns: _.map(context.columns, (column, name) => { + return { + title: column.name || name, + ...column + }; + }), + rows: context.rows.map(row => { + const crow = []; + _.each(context.columns, (column, i) => { + crow.push(row[i]); + }); + return crow; + }) + }] + }; + return { + type: 'kibana_table', + value: converted, + }; + } + }, + to: { + datatable: context => { + const columns = context.columns.map(column => { + return { name: column.title, ...column }; + }); + const rows = context.rows.map(row => { + const converted = {}; + columns.forEach((column) => { + converted[column.name] = row[column.id]; + }); + return converted; + }); + + return { + type: 'datatable', + columns: columns, + rows: rows, + }; + }, + pointseries: context => { + const columns = context.value.tables[0].columns.map(column => { + return { name: column.title, ...column }; + }); + const rows = context.value.tables[0].rows.map(row => { + const converted = {}; + columns.forEach((column, i) => { + converted[column.name] = row[i]; + }); + return converted; + }); + + return { + type: 'pointseries', + columns: columns, + rows: rows, + }; + } + } +}); diff --git a/packages/kbn-interpreter/public/interpreter.js b/packages/kbn-interpreter/public/interpreter.js index 5c1e199bce363..2fd5cdd2f8412 100644 --- a/packages/kbn-interpreter/public/interpreter.js +++ b/packages/kbn-interpreter/public/interpreter.js @@ -48,13 +48,13 @@ export async function initialize() { } // Use the above promise to seed the interpreter with the functions it can defer to -export async function interpretAst(ast, context) { +export async function interpretAst(ast, context, handlers) { // Load plugins before attempting to get functions, otherwise this gets racey return Promise.all([functionList, getBrowserRegistries()]) .then(([serverFunctionList]) => { return socketInterpreterProvider({ types: typesRegistry.toJS(), - handlers: createHandlers(socket), + handlers: { ...handlers, ...createHandlers(socket) }, functions: functionsRegistry.toJS(), referableFunctions: serverFunctionList, socket: socket, diff --git a/src/core_plugins/interpreter/public/functions/esaggs.js b/src/core_plugins/interpreter/public/functions/esaggs.js new file mode 100644 index 0000000000000..945ad83c13af2 --- /dev/null +++ b/src/core_plugins/interpreter/public/functions/esaggs.js @@ -0,0 +1,100 @@ +/* + * 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 _ from 'lodash'; +import { CourierRequestHandlerProvider } from 'ui/vis/request_handlers/courier'; +import { AggConfigs } from 'ui/vis/agg_configs'; + +// need to get rid of angular from these +import { IndexPatternsProvider } from 'ui/index_patterns'; +import { SearchSourceProvider } from 'ui/courier/search_source'; +import { FilterBarQueryFilterProvider } from 'ui/filter_bar/query_filter'; + +import chrome from 'ui/chrome'; + +const courierRequestHandlerProvider = CourierRequestHandlerProvider; +const courierRequestHandler = courierRequestHandlerProvider().handler; + +export default () => ({ + name: 'esaggs', + type: 'kibana_table', + context: { + types: [ + 'kibana_context', + 'null', + ], + }, + help: 'Run AggConfig aggregation.', + args: { + index: { + types: ['string', 'null'], + default: null, + }, + metricsAtAllLevels: { + types: ['boolean'], + default: false, + }, + partialRows: { + types: ['boolean'], + default: false, + }, + aggConfigs: { + types: ['string'], + default: '""', + help: 'AggConfig definition', + multi: false, + }, + }, + fn(context, args, handlers) { + return chrome.dangerouslyGetActiveInjector().then(async $injector => { + const Private = $injector.get('Private'); + const indexPatterns = Private(IndexPatternsProvider); + const SearchSource = Private(SearchSourceProvider); + const queryFilter = Private(FilterBarQueryFilterProvider); + + const aggConfigsState = JSON.parse(args.aggConfigs); + const indexPattern = await indexPatterns.get(args.index); + const aggs = new AggConfigs(indexPattern, aggConfigsState); + + // we should move searchSource creation inside courier request handler + const searchSource = new SearchSource(); + searchSource.setField('index', indexPattern); + + const response = await courierRequestHandler({ + searchSource: searchSource, + aggs: aggs, + timeRange: _.get(context, 'timeRange', null), + query: _.get(context, 'query', null), + filters: _.get(context, 'filters', null), + forceFetch: true, + isHierarchical: args.metricsAtAllLevels, + partialRows: args.partialRows, + inspectorAdapters: handlers.inspectorAdapters, + queryFilter, + }); + + return { + type: 'kibana_table', + index: args.index, + ...response, + }; + + }); + }, +}); diff --git a/src/core_plugins/interpreter/public/functions/index.js b/src/core_plugins/interpreter/public/functions/index.js new file mode 100644 index 0000000000000..1d975b528815d --- /dev/null +++ b/src/core_plugins/interpreter/public/functions/index.js @@ -0,0 +1,32 @@ +/* + * 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 visualization from './visualization'; +import esaggs from './esaggs'; +import kibana from './kibana'; +import kibanaContext from './kibana_context'; +import vega from './vega'; +import timelion from './timelion_vis'; +import tsvb from './tsvb'; +import markdown from './markdown'; +import inputControl from './input_control'; + +export const functions = [ + visualization, esaggs, kibana, kibanaContext, vega, timelion, tsvb, markdown, inputControl +]; diff --git a/src/core_plugins/interpreter/public/functions/input_control.js b/src/core_plugins/interpreter/public/functions/input_control.js new file mode 100644 index 0000000000000..29356fb06340f --- /dev/null +++ b/src/core_plugins/interpreter/public/functions/input_control.js @@ -0,0 +1,48 @@ +/* + * 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. + */ + +export default () => ({ + name: 'input_control_vis', + type: 'render', + context: { + types: [], + }, + help: 'A input control visualization.', + args: { + visConfig: { + types: ['string'], + default: '"{}"', + help: 'markdown configuration object', + multi: false, + } + }, + fn(context, args) { + const params = args.visConfig ? JSON.parse(args.visConfig) : {}; + return { + type: 'render', + as: 'visualization', + value: { + visConfig: { + type: 'input_controls_vis', + params: params + }, + } + }; + } +}); diff --git a/src/core_plugins/interpreter/public/functions/kibana.js b/src/core_plugins/interpreter/public/functions/kibana.js new file mode 100644 index 0000000000000..9b580c54f7e0a --- /dev/null +++ b/src/core_plugins/interpreter/public/functions/kibana.js @@ -0,0 +1,34 @@ +/* + * 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. + */ + +export default () => ({ + name: 'kibana', + type: 'kibana_context', + context: {}, + help: 'Gets kibana global context.', + args: {}, + fn(context) { + return { + type: 'kibana_context', + query: context.query, + filters: context.filters, + timeRange: context.timeRange, + }; + }, +}); diff --git a/src/core_plugins/interpreter/public/functions/kibana_context.js b/src/core_plugins/interpreter/public/functions/kibana_context.js new file mode 100644 index 0000000000000..4b5ea69dcee14 --- /dev/null +++ b/src/core_plugins/interpreter/public/functions/kibana_context.js @@ -0,0 +1,90 @@ +/* + * 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 chrome from 'ui/chrome'; + +export default () => ({ + name: 'kibana_context', + type: 'kibana_context', + context: { + types: [ + 'kibana_context', + 'null', + ], + }, + help: 'Gets kibana global context.', + args: { + q: { + types: ['string', 'null'], + aliases: ['query'], + help: 'A Lucene query string', + default: null, + }, + filters: { + types: ['string', 'null'], + help: 'Filters object', + default: '"[]"', + }, + timeRange: { + types: ['string', 'null'], + help: 'Sets date range to query', + default: null, + }, + savedSearchId: { + types: ['string', 'null'], + help: 'Sets date range to query', + default: null, + } + }, + fn(context, args) { + return chrome.dangerouslyGetActiveInjector().then(async $injector => { + const savedSearches = $injector.get('savedSearches'); + const queryArg = args.q ? JSON.parse(args.q) : []; + let queries = Array.isArray(queryArg) ? queryArg : [queryArg]; + let filters = args.filters ? JSON.parse(args.filters) : []; + + if (args.savedSearchId) { + const savedSearch = await savedSearches.get(args.savedSearchId); + const searchQuery = savedSearch.searchSource.getField('query'); + const searchFilters = savedSearch.searchSource.getField('filter'); + queries = queries.concat(searchQuery); + filters = filters.concat(searchFilters); + } + + if (context.query) { + const contextQueries = Array.isArray(context.query) ? context.query : [context.query]; + queries = queries.concat(contextQueries); + } + + if (context.filters) { + // merge filters + filters = filters.concat(context.filters); + } + + const timeRange = args.timeRange ? JSON.parse(args.timeRange) : context.timeRange; + + return { + type: 'kibana_context', + query: queries, + filters: filters, + timeRange: timeRange, + }; + }); + }, +}); diff --git a/src/core_plugins/interpreter/public/functions/markdown.js b/src/core_plugins/interpreter/public/functions/markdown.js new file mode 100644 index 0000000000000..a553d86891675 --- /dev/null +++ b/src/core_plugins/interpreter/public/functions/markdown.js @@ -0,0 +1,57 @@ +/* + * 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. + */ + +export default () => ({ + name: 'markdown', + type: 'render', + context: { + types: [], + }, + help: 'A markdown visualization.', + args: { + spec: { + types: ['string'], + default: '', + help: 'markdown', + multi: false, + }, + params: { + types: ['string'], + default: '"{}"', + help: 'markdown configuration object', + multi: false, + } + }, + fn(context, args) { + const params = args.params ? JSON.parse(args.params) : {}; + return { + type: 'render', + as: 'visualization', + value: { + visConfig: { + type: 'markdown', + params: { + markdown: args.spec, + ...params, + } + }, + } + }; + } +}); diff --git a/src/core_plugins/interpreter/public/functions/timelion_vis.js b/src/core_plugins/interpreter/public/functions/timelion_vis.js new file mode 100644 index 0000000000000..bd1af09e32769 --- /dev/null +++ b/src/core_plugins/interpreter/public/functions/timelion_vis.js @@ -0,0 +1,73 @@ +/* + * 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 _ from 'lodash'; + +import { TimelionRequestHandlerProvider } from '../../../timelion/public/vis/timelion_request_handler'; + + +import chrome from 'ui/chrome'; + + +export default () => ({ + name: 'timelion_vis', + type: 'render', + context: { + types: [ + 'kibana_context', + 'null', + ], + }, + help: 'Run tsvb request.', + args: { + expression: { + types: ['string'], + default: '".es(*)"', + help: 'timelion expression definition', + multi: false, + }, + interval: { + types: ['string', 'null'], + default: 'auto', + help: 'timelion interval', + multi: false, + } + }, + fn(context, args) { + return chrome.dangerouslyGetActiveInjector().then(async $injector => { + const Private = $injector.get('Private'); + const timelionRequestHandler = Private(TimelionRequestHandlerProvider).handler; + + const response = await timelionRequestHandler({ + timeRange: _.get(context, 'timeRange', null), + query: _.get(context, 'query', null), + filters: _.get(context, 'filters', null), + forceFetch: true, + visParams: { expression: args.expression, interval: args.interval } + }); + + return { + type: 'render', + as: 'visualization', + value: response, + }; + + }); + }, +}); diff --git a/src/core_plugins/interpreter/public/functions/tsvb.js b/src/core_plugins/interpreter/public/functions/tsvb.js new file mode 100644 index 0000000000000..ce2619f890d0c --- /dev/null +++ b/src/core_plugins/interpreter/public/functions/tsvb.js @@ -0,0 +1,81 @@ +/* + * 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 _ from 'lodash'; + +import { MetricsRequestHandlerProvider } from '../../../metrics/public/kbn_vis_types/request_handler'; +import { PersistedState } from 'ui/persisted_state'; + +import chrome from 'ui/chrome'; + + +export default () => ({ + name: 'tsvb', + type: 'render', + context: { + types: [ + 'kibana_context', + 'null', + ], + }, + help: 'Run tsvb request.', + args: { + index: { + types: ['string', 'null'], + default: null, + }, + params: { + types: ['string'], + default: '""', + help: 'tsvb params definition', + multi: false, + }, + uiState: { + types: ['string'], + default: '""', + help: 'tsvb uiState', + multi: false + } + }, + fn(context, args) { + return chrome.dangerouslyGetActiveInjector().then(async $injector => { + const Private = $injector.get('Private'); + const metricsRequestHandler = Private(MetricsRequestHandlerProvider).handler; + + const params = JSON.parse(args.params); + const uiStateParams = args.uiState ? JSON.parse(args.uiState) : {}; + const uiState = new PersistedState(uiStateParams); + + const response = await metricsRequestHandler({ + timeRange: _.get(context, 'timeRange', null), + query: _.get(context, 'query', null), + filters: _.get(context, 'filters', null), + visParams: params, + uiState: uiState, + }); + + return { + type: 'render', + as: 'visualization', + value: response, + }; + + }); + }, +}); diff --git a/src/core_plugins/interpreter/public/functions/vega.js b/src/core_plugins/interpreter/public/functions/vega.js new file mode 100644 index 0000000000000..09733cb26234d --- /dev/null +++ b/src/core_plugins/interpreter/public/functions/vega.js @@ -0,0 +1,68 @@ +/* + * 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 chrome from 'ui/chrome'; + +import { VegaRequestHandlerProvider } from '../../../vega/public/vega_request_handler'; +import _ from 'lodash'; + +export default () => ({ + name: 'vega', + type: 'render', + context: { + types: [], + }, + help: 'A vega visualization.', + args: { + spec: { + types: ['string'], + default: '{}', + help: 'config of the visualization', + multi: false, + }, + }, + fn(context, args) { + return chrome.dangerouslyGetActiveInjector().then(async $injector => { + const Private = $injector.get('Private'); + const vegaRequestHandler = Private(VegaRequestHandlerProvider).handler; + + const response = await vegaRequestHandler({ + timeRange: _.get(context, 'timeRange', null), + query: _.get(context, 'q', null), + filters: _.get(context, 'filters', null), + visParams: { spec: args.spec }, + forceFetch: true + }); + + return { + type: 'render', + as: 'visualization', + value: { + visData: response, + visConfig: { + type: 'vega', + params: { + spec: args.spec + } + }, + } + }; + }); + } +}); diff --git a/src/core_plugins/interpreter/public/functions/visualization.js b/src/core_plugins/interpreter/public/functions/visualization.js new file mode 100644 index 0000000000000..1d83508295e9b --- /dev/null +++ b/src/core_plugins/interpreter/public/functions/visualization.js @@ -0,0 +1,106 @@ +/* + * 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 { VisResponseHandlersRegistryProvider } from 'ui/registry/vis_response_handlers'; +import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; + +import chrome from 'ui/chrome'; + +export default () => ({ + name: 'visualization', + type: 'render', + context: { + types: [ + 'kibana_table', 'null' + ], + }, + help: 'A simple visualization.', + args: { + type: { + types: ['string'], + default: 'metric', + help: 'visualization type', + multi: false, + }, + schemas: { + types: ['string'], + default: '"{}"', + help: 'schemas configuration object', + multi: false, + }, + visConfig: { + types: ['string', 'null'], + default: '"{}"', + help: 'config of the visualization', + multi: false, + }, + }, + fn(context, args) { + return chrome.dangerouslyGetActiveInjector().then(async $injector => { + const Private = $injector.get('Private'); + const responseHandlers = Private(VisResponseHandlersRegistryProvider); + const visTypes = Private(VisTypesRegistryProvider); + const visConfigParams = JSON.parse(args.visConfig || {}); + const schemas = JSON.parse(args.schemas); + const visType = visTypes.byName[args.type || 'table']; + + const getResponseHandler = (responseHandler) => { + if (typeof responseHandler === 'function') { + return responseHandler; + } + const searchResult = responseHandlers.find(handler => handler.name === visType.responseHandler); + if (!searchResult) { + return; + } + return searchResult.handler; + }; + + if (context.columns) { + // assign schemas to aggConfigs + context.columns.forEach(column => { + column.aggConfig.aggConfigs.schemas = visType.schemas.all; + }); + + Object.keys(schemas).forEach(key => { + schemas[key].forEach(i => { + context.columns[i].aggConfig.schema = key; + }); + }); + } + + const responseHandler = getResponseHandler(visType.responseHandler); + const convertedData = responseHandler ? await responseHandler(context) : context; + + return { + type: 'render', + as: 'visualization', + value: { + visData: convertedData, + visConfig: { + type: args.type, + params: visConfigParams, + }, + params: { + listenOnChange: true, + } + }, + }; + }); + }, +}); diff --git a/src/core_plugins/interpreter/public/load_browser_plugins.js b/src/core_plugins/interpreter/public/load_browser_plugins.js index 6322e8e340e45..e1e4723156a32 100644 --- a/src/core_plugins/interpreter/public/load_browser_plugins.js +++ b/src/core_plugins/interpreter/public/load_browser_plugins.js @@ -17,9 +17,15 @@ * under the License. */ +import chrome from 'ui/chrome'; import { populateBrowserRegistries } from '@kbn/interpreter/public/browser_registries'; import { typesRegistry } from '@kbn/interpreter/common/lib/types_registry'; import { functionsRegistry } from '@kbn/interpreter/common/lib/functions_registry'; +import { createSocket } from '@kbn/interpreter/public/socket'; +import { initialize } from '@kbn/interpreter/public/interpreter'; +import { functions } from './functions'; + +const basePath = chrome.getBasePath(); const types = { commonFunctions: functionsRegistry, @@ -27,4 +33,13 @@ const types = { types: typesRegistry }; -populateBrowserRegistries(types); +function addFunction(fnDef) { + functionsRegistry.register(fnDef); +} + +functions.forEach(addFunction); + +createSocket(basePath).then(async () => { + await populateBrowserRegistries(types); + await initialize(); +}); diff --git a/src/core_plugins/kibana/public/visualize/editor/editor.html b/src/core_plugins/kibana/public/visualize/editor/editor.html index c73805e4eb678..06c058d37da5a 100644 --- a/src/core_plugins/kibana/public/visualize/editor/editor.html +++ b/src/core_plugins/kibana/public/visualize/editor/editor.html @@ -73,6 +73,7 @@ saved-obj="savedVis" ui-state="uiState" time-range="timeRange" + filters="globalFilters" class="visEditor__content" /> diff --git a/src/core_plugins/kibana/public/visualize/editor/editor.js b/src/core_plugins/kibana/public/visualize/editor/editor.js index 5e4ec07ff5e83..fa0a679c459d5 100644 --- a/src/core_plugins/kibana/public/visualize/editor/editor.js +++ b/src/core_plugins/kibana/public/visualize/editor/editor.js @@ -128,18 +128,6 @@ function VisEditor( // SearchSource is a promise-based stream of search results that can inherit from other search sources. const { vis, searchSource } = savedVis; - // adds top level search source to the stack to which global filters are applied - const getTopLevelSearchSource = (searchSource) => { - if (searchSource.getParent()) return getTopLevelSearchSource(searchSource.getParent()); - return searchSource; - }; - - const topLevelSearchSource = getTopLevelSearchSource(searchSource); - const globalFiltersSearchSource = searchSource.create(); - globalFiltersSearchSource.setField('index', searchSource.getField('index')); - topLevelSearchSource.setParent(globalFiltersSearchSource); - - $scope.vis = vis; const $appStatus = this.appStatus = { @@ -338,10 +326,9 @@ function VisEditor( // update the searchSource when query updates $scope.fetch = function () { $state.save(); - const globalFilters = queryFilter.getGlobalFilters(); savedVis.searchSource.setField('query', $state.query); savedVis.searchSource.setField('filter', $state.filters); - globalFiltersSearchSource.setField('filter', globalFilters); + $scope.globalFilters = queryFilter.getGlobalFilters(); $scope.vis.forceReload(); }; diff --git a/src/core_plugins/kibana/public/visualize/editor/visualization_editor.js b/src/core_plugins/kibana/public/visualize/editor/visualization_editor.js index 1f8426386613c..544b3cd6621aa 100644 --- a/src/core_plugins/kibana/public/visualize/editor/visualization_editor.js +++ b/src/core_plugins/kibana/public/visualize/editor/visualization_editor.js @@ -31,7 +31,8 @@ uiModules scope: { savedObj: '=', uiState: '=?', - timeRange: '=' + timeRange: '=', + filters: '=', }, link: function ($scope, element) { const editorType = $scope.savedObj.vis.type.editor; @@ -43,6 +44,7 @@ uiModules editor.render({ uiState: $scope.uiState, timeRange: $scope.timeRange, + filters: $scope.filters, appState: getAppState(), }); }; @@ -57,6 +59,7 @@ uiModules }); $scope.$watch('timeRange', $scope.renderFunction); + $scope.$watch('filters', $scope.renderFunction); } }; diff --git a/src/core_plugins/kibana/public/visualize/saved_visualizations/_saved_vis.js b/src/core_plugins/kibana/public/visualize/saved_visualizations/_saved_vis.js index 8909fabb97b33..292d54be9ff66 100644 --- a/src/core_plugins/kibana/public/visualize/saved_visualizations/_saved_vis.js +++ b/src/core_plugins/kibana/public/visualize/saved_visualizations/_saved_vis.js @@ -140,6 +140,7 @@ uiModules self.searchSource.getField('index'), self.visState ); + self.vis.savedSearchId = self.savedSearchId; return self.vis; }; @@ -150,6 +151,7 @@ uiModules self.vis.indexPattern = self.searchSource.getField('index'); self.visState.title = self.title; self.vis.setState(self.visState); + self.vis.savedSearchId = self.savedSearchId; }; return SavedVis; diff --git a/src/core_plugins/metrics/public/kbn_vis_types/request_handler.js b/src/core_plugins/metrics/public/kbn_vis_types/request_handler.js index 51bce4c094fbe..1174335d4d972 100644 --- a/src/core_plugins/metrics/public/kbn_vis_types/request_handler.js +++ b/src/core_plugins/metrics/public/kbn_vis_types/request_handler.js @@ -28,7 +28,7 @@ const MetricsRequestHandlerProvider = function (Private, Notifier, config, $http return { name: 'metrics', - handler: function ({ aggs, uiState, timeRange, filters, query, visParams }) { + handler: function ({ indexPattern, uiState, timeRange, filters, query, visParams }) { const timezone = Private(timezoneProvider)(); return new Promise((resolve) => { const panel = visParams; @@ -39,7 +39,7 @@ const MetricsRequestHandlerProvider = function (Private, Notifier, config, $http if (panel && panel.id) { const params = { timerange: { timezone, ...parsedTimeRange }, - filters: [buildEsQuery(aggs.indexPattern, [query], filters)], + filters: [buildEsQuery(indexPattern, [query], filters)], panels: [panel], state: uiStateObj }; diff --git a/src/core_plugins/timelion/public/vis/timelion_request_handler.js b/src/core_plugins/timelion/public/vis/timelion_request_handler.js index a2aa4b8f5582b..61df971775e5b 100644 --- a/src/core_plugins/timelion/public/vis/timelion_request_handler.js +++ b/src/core_plugins/timelion/public/vis/timelion_request_handler.js @@ -31,7 +31,7 @@ const TimelionRequestHandlerProvider = function (Private, Notifier, $http) { return { name: 'timelion', - handler: function ({ aggs, timeRange, filters, query, visParams }) { + handler: function ({ indexPattern, timeRange, filters, query, visParams }) { return new Promise((resolve, reject) => { const expression = visParams.expression; @@ -41,7 +41,7 @@ const TimelionRequestHandlerProvider = function (Private, Notifier, $http) { sheet: [expression], extended: { es: { - filter: buildEsQuery(aggs.indexPattern, [query], filters) + filter: buildEsQuery(indexPattern, [query], filters) } }, time: _.extend(timeRange, { diff --git a/src/core_plugins/vega/public/vega_request_handler.js b/src/core_plugins/vega/public/vega_request_handler.js index 3fba8216c7236..5429304ef1ae4 100644 --- a/src/core_plugins/vega/public/vega_request_handler.js +++ b/src/core_plugins/vega/public/vega_request_handler.js @@ -33,9 +33,9 @@ export function VegaRequestHandlerProvider(Private, es, serviceSettings) { name: 'vega', - handler({ aggs, timeRange, filters, query, visParams }) { + handler({ indexPattern, timeRange, filters, query, visParams }) { timeCache.setTimeRange(timeRange); - const filtersDsl = buildEsQuery(aggs.indexPattern, [query], filters); + const filtersDsl = buildEsQuery(indexPattern, [query], filters); const vp = new VegaParser(visParams.spec, searchCache, timeCache, filtersDsl, serviceSettings); return vp.parseAsync(); } diff --git a/src/ui/public/agg_types/buckets/geo_hash.js b/src/ui/public/agg_types/buckets/geo_hash.js index 1ade0f904c332..a532b9d6780b5 100644 --- a/src/ui/public/agg_types/buckets/geo_hash.js +++ b/src/ui/public/agg_types/buckets/geo_hash.js @@ -100,6 +100,11 @@ export const geoHashBucketAgg = new BucketAggType({ default: [0, 0], write: _.noop }, + { + name: 'mapBounds', + default: null, + write: _.noop + }, { name: 'precision', editor: precisionTemplate, diff --git a/src/ui/public/vis/agg_configs.js b/src/ui/public/vis/agg_configs.js index 6aebeabd08159..33ba6e163d2ec 100644 --- a/src/ui/public/vis/agg_configs.js +++ b/src/ui/public/vis/agg_configs.js @@ -52,7 +52,7 @@ class AggConfigs extends IndexedArray { super({ index: ['id'], - group: ['schema.group', 'type.name', 'schema.name'], + group: ['schema.group', 'type.name', 'type.type', 'schema.name'], }); this.indexPattern = indexPattern; @@ -152,7 +152,7 @@ class AggConfigs extends IndexedArray { if (hierarchical) { // collect all metrics, and filter out the ones that we won't be copying - nestedMetrics = _(this.bySchemaGroup.metrics) + nestedMetrics = _(this.byTypeType.metrics) .filter(function (agg) { return agg.type.name !== 'count'; }) diff --git a/src/ui/public/vis/editors/default/default.js b/src/ui/public/vis/editors/default/default.js index e1802960eb2a0..7d7cc96640776 100644 --- a/src/ui/public/vis/editors/default/default.js +++ b/src/ui/public/vis/editors/default/default.js @@ -32,8 +32,8 @@ import { DefaultEditorSize } from '../../editor_size'; import { VisEditorTypesRegistryProvider } from '../../../registry/vis_editor_types'; import { getVisualizeLoader } from '../../../visualize/loader/visualize_loader'; +const defaultEditor = function (Private, $rootScope, $compile, i18n) { -const defaultEditor = function ($rootScope, $compile, i18n) { return class DefaultEditor { static key = 'default'; @@ -53,7 +53,7 @@ const defaultEditor = function ($rootScope, $compile, i18n) { } } - render({ uiState, timeRange, appState }) { + render({ uiState, timeRange, appState, filters }) { let $scope; const updateScope = () => { @@ -166,12 +166,14 @@ const defaultEditor = function ($rootScope, $compile, i18n) { uiState: uiState, listenOnChange: false, timeRange: timeRange, + filters: filters, appState: appState, }); }); } else { this._handler.update({ - timeRange: timeRange + timeRange: timeRange, + filters: filters, }); } diff --git a/src/ui/public/vis/request_handlers/request_handlers.d.ts b/src/ui/public/vis/request_handlers/request_handlers.d.ts index 357cced0e1fb5..a6395ac027136 100644 --- a/src/ui/public/vis/request_handlers/request_handlers.d.ts +++ b/src/ui/public/vis/request_handlers/request_handlers.d.ts @@ -38,6 +38,7 @@ export interface RequestHandlerParams { inspectorAdapters?: Adapters; isHierarchical?: boolean; visParams?: any; + indexPattern?: any; } export type RequestHandler = (params: RequestHandlerParams) => T; diff --git a/src/ui/public/vis/vis_types/vislib_vis_type.js b/src/ui/public/vis/vis_types/vislib_vis_type.js index 15a3f69eda87c..21823d49932b7 100644 --- a/src/ui/public/vis/vis_types/vislib_vis_type.js +++ b/src/ui/public/vis/vis_types/vislib_vis_type.js @@ -99,13 +99,14 @@ export function VislibVisTypeProvider(Private, $rootScope, $timeout, $compile) { } destroy() { + $(this.container).find('vislib-legend').remove(); + $(this.vis.vislibVis.el.parentElement).find('vislib-legend').remove(); if (this.vis.vislibVis) { this.vis.vislibVis.off('brush', this.vis.API.events.brush); this.vis.vislibVis.off('click', this.vis.API.events.filter); this.vis.vislibVis.destroy(); delete this.vis.vislibVis; } - $(this.container).find('vislib-legend').remove(); if (this.$scope) { this.$scope.$destroy(); this.$scope = null; diff --git a/src/ui/public/visualize/loader/__tests__/visualize_data_loader.js b/src/ui/public/visualize/loader/__tests__/visualize_data_loader.js index fcd2b4b930618..4c2e31a46d7cb 100644 --- a/src/ui/public/visualize/loader/__tests__/visualize_data_loader.js +++ b/src/ui/public/visualize/loader/__tests__/visualize_data_loader.js @@ -60,14 +60,6 @@ describe('visualize data loader', () => { })); setupAndTeardownInjectorStub(); - it('should have a requestHandler', () => { - expect(visualizeDataLoader.requestHandler).to.be.a('function'); - }); - - it('should have a responseHandler', () => { - expect(visualizeDataLoader.responseHandler).to.be.a('function'); - }); - describe('fetch', () => { it('should be a function', () => { expect(visualizeDataLoader.fetch).to.be.a('function'); diff --git a/src/ui/public/visualize/loader/embedded_visualize_handler.ts b/src/ui/public/visualize/loader/embedded_visualize_handler.ts index 66a630d331392..b11348dc6c87a 100644 --- a/src/ui/public/visualize/loader/embedded_visualize_handler.ts +++ b/src/ui/public/visualize/loader/embedded_visualize_handler.ts @@ -91,7 +91,7 @@ export class EmbeddedVisualizeHandler { ) { const { searchSource, vis } = savedObject; - const { appState, uiState, queryFilter, timeRange, filters, query, Private } = params; + const { appState, uiState, queryFilter, timeRange, filters, query } = params; this.dataLoaderParams = { searchSource, @@ -124,7 +124,7 @@ export class EmbeddedVisualizeHandler { this.uiState.on('change', this.onUiStateChange); timefilter.on('autoRefreshFetch', this.reload); - this.dataLoader = new VisualizeDataLoader(vis, Private); + this.dataLoader = new VisualizeDataLoader(vis); this.renderCompleteHelper = new RenderCompleteHelper(element); this.inspectorAdapters = this.getActiveInspectorAdapters(); this.vis.openInspector = this.openInspector; @@ -346,13 +346,25 @@ export class EmbeddedVisualizeHandler { this.dataLoaderParams.forceFetch = forceFetch; this.dataLoaderParams.inspectorAdapters = this.inspectorAdapters; + this.vis.filters = { timeRange: this.dataLoaderParams.timeRange }; + return this.dataLoader.fetch(this.dataLoaderParams).then(data => { this.dataSubject.next(data); return data; }); }; - private render = (visData: any = null) => { + private render = (pipelineResponse: any = null) => { + let visData; + if (pipelineResponse) { + if (!pipelineResponse.value) { + console.log(pipelineResponse.error); + console.log(pipelineResponse.error.stack); + throw new Error('error executing pipeline'); + return; + } + visData = pipelineResponse.value.visData || pipelineResponse.value; + } return visualizationLoader .render(this.element, this.vis, visData, this.uiState, { listenOnChange: false, diff --git a/src/ui/public/visualize/loader/pipeline_helpers.js b/src/ui/public/visualize/loader/pipeline_helpers.js new file mode 100644 index 0000000000000..ef25c14c68e9a --- /dev/null +++ b/src/ui/public/visualize/loader/pipeline_helpers.js @@ -0,0 +1,84 @@ +/* + * 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 { fromExpression } from '@kbn/interpreter/common/lib/ast'; +import { interpretAst } from '@kbn/interpreter/public/interpreter'; + +export const buildPipeline = (vis, params) => { + const { searchSource } = params; + const { indexPattern } = vis; + const query = searchSource.getField('query'); + const filters = searchSource.getField('filter'); + const visState = vis.getCurrentState(); + + let pipeline = `kibana | kibana_context `; + if (query) { + pipeline += `q='${JSON.stringify(query).replace(/'/g, `\\'`)}' `; + } + if (filters) { + pipeline += `filters='${JSON.stringify(filters).replace(/'/g, `\\'`)}' `; + } + if (vis.savedSearchId) { + pipeline += `savedSearchId='${vis.savedSearchId}' `; + } + pipeline += '| '; + if (vis.type.name === 'vega') { + pipeline += `vega spec='${visState.params.spec.replace(/'/g, `\\'`)}'`; + } else if (vis.type.name === 'input_control_vis') { + pipeline += `input_control_vis visConfig='${JSON.stringify(visState.params)}'`; + } else if (vis.type.name === 'metrics') { + pipeline += `tsvb params='${JSON.stringify(visState.params)}'`; + } else if (vis.type.name === 'timelion') { + pipeline += `timelion_vis expression='${visState.params.expression}' interval='${visState.params.interval}'`; + } else if (vis.type.name === 'markdown') { + pipeline = `markdown spec='${visState.params.markdown.replace(/'/g, `\\'`)}' + params='${JSON.stringify(visState.params).replace(/'/g, `\\'`)}'`; + } else { + const schemas = {}; + let cnt = 0; + vis.aggs.getResponseAggs().forEach((agg) => { + if (!agg.enabled) return; + let schemaName = agg.schema ? agg.schema.name || agg.schema : null; + if (typeof schemaName === 'object') schemaName = null; + if (!schemaName) return; + if (!schemas[schemaName]) schemas[schemaName] = []; + schemas[schemaName].push(cnt++); + }); + pipeline += ` + esaggs index='${indexPattern.id}' metricsAtAllLevels=${vis.isHierarchical()} + partialRows=${vis.params.showPartialRows || vis.type.name === 'tile_map'} + aggConfigs='${JSON.stringify(visState.aggs)}' | + visualization type='${vis.type.name}' visConfig='${JSON.stringify(visState.params)}' schemas='${JSON.stringify(schemas)}' + `; + } + + return pipeline; +}; + +export const runPipeline = async (pipeline, context, handlers) => { + try { + const ast = fromExpression(pipeline); + const pipelineResponse = await interpretAst(ast, context, handlers); + return pipelineResponse; + } catch (e) { + // eslint-disable-next-line no-console + console.log(e, pipeline); + } +}; diff --git a/src/ui/public/visualize/loader/visualize_data_loader.ts b/src/ui/public/visualize/loader/visualize_data_loader.ts index 52f78e83d39d9..b746b9b690f33 100644 --- a/src/ui/public/visualize/loader/visualize_data_loader.ts +++ b/src/ui/public/visualize/loader/visualize_data_loader.ts @@ -16,89 +16,35 @@ * specific language governing permissions and limitations * under the License. */ -import { isEqual } from 'lodash'; - -import { VisRequestHandlersRegistryProvider as RequestHandlersProvider } from '../../registry/vis_request_handlers'; -import { VisResponseHandlersRegistryProvider as ResponseHandlerProvider } from '../../registry/vis_response_handlers'; - -import { IPrivate } from '../../private'; -import { - RequestHandler, - RequestHandlerDescription, - RequestHandlerParams, - ResponseHandler, - ResponseHandlerDescription, - Vis, -} from '../../vis'; - -// @ts-ignore No typing present -import { isTermSizeZeroError } from '../../elasticsearch_errors'; import { toastNotifications } from 'ui/notify'; +import { RequestHandlerParams, Vis } from '../../vis'; -function getHandler( - from: Array<{ name: string; handler: T }>, - type: string | T -): T { - if (typeof type === 'function') { - return type; - } - const handlerDesc = from.find(handler => handler.name === type); - if (!handlerDesc) { - throw new Error(`Could not find handler "${type}".`); - } - return handlerDesc.handler; -} +// @ts-ignore No typing present +import { buildPipeline, runPipeline } from './pipeline_helpers'; export class VisualizeDataLoader { - private requestHandler: RequestHandler; - private responseHandler: ResponseHandler; - - private visData: any; - private previousVisState: any; - private previousRequestHandlerResponse: any; - - constructor(private readonly vis: Vis, Private: IPrivate) { - const { requestHandler, responseHandler } = vis.type; - - const requestHandlers: RequestHandlerDescription[] = Private(RequestHandlersProvider); - const responseHandlers: ResponseHandlerDescription[] = Private(ResponseHandlerProvider); - this.requestHandler = getHandler(requestHandlers, requestHandler); - this.responseHandler = getHandler(responseHandlers, responseHandler); - } + constructor(private readonly vis: Vis) {} public async fetch(params: RequestHandlerParams): Promise { - this.vis.filters = { timeRange: params.timeRange }; this.vis.requestError = undefined; this.vis.showRequestError = false; + this.vis.pipelineExpression = buildPipeline(this.vis, params); try { - // searchSource is only there for courier request handler - const requestHandlerResponse = await this.requestHandler({ - partialRows: this.vis.params.partialRows || this.vis.type.requiresPartialRows, - isHierarchical: this.vis.isHierarchical(), - visParams: this.vis.params, - ...params, - filters: params.filters - ? params.filters.filter(filter => !filter.meta.disabled) - : undefined, - }); - - // No need to call the response handler when there have been no data nor has been there changes - // in the vis-state (response handler does not depend on uiStat - const canSkipResponseHandler = - this.previousRequestHandlerResponse && - this.previousRequestHandlerResponse === requestHandlerResponse && - this.previousVisState && - isEqual(this.previousVisState, this.vis.getState()); - - this.previousVisState = this.vis.getState(); - this.previousRequestHandlerResponse = requestHandlerResponse; - - if (!canSkipResponseHandler) { - this.visData = await Promise.resolve(this.responseHandler(requestHandlerResponse)); - } - return this.visData; + return await runPipeline( + this.vis.pipelineExpression, + { + query: params.query, + timeRange: params.timeRange, + filters: params.filters + ? params.filters.filter(filter => !filter.meta.disabled) + : undefined, + }, + { + inspectorAdapters: params.inspectorAdapters, + } + ); } catch (error) { params.searchSource.cancelQueued(); @@ -109,14 +55,6 @@ export class VisualizeDataLoader { // tslint:disable-next-line console.error(error); - if (isTermSizeZeroError(error)) { - return toastNotifications.addDanger( - `Your visualization ('${this.vis.title}') has an error: it has a term ` + - `aggregation with a size of 0. Please set it to a number greater than 0 to resolve ` + - `the error.` - ); - } - toastNotifications.addDanger({ title: 'Error in visualization', text: error.message, diff --git a/test/functional/apps/visualize/_data_table.js b/test/functional/apps/visualize/_data_table.js index 5cd9b280c5550..0c3d200cf8000 100644 --- a/test/functional/apps/visualize/_data_table.js +++ b/test/functional/apps/visualize/_data_table.js @@ -392,9 +392,9 @@ export default function ({ getService, getPageObjects }) { ]); }); - it('should allow nesting multiple splits', async () => { + it.skip('should allow nesting multiple splits', async () => { // This test can be removed as soon as we remove the nested split table - // feature (https://github.com/elastic/kibana/issues/24560). + // feature (https://github.com/elastic/kibana/issues/24560). (7.0) await PageObjects.visualize.clickData(); await PageObjects.visualize.clickAddBucket(); await PageObjects.visualize.clickBucket('Split Table'); diff --git a/x-pack/plugins/canvas/public/components/app/index.js b/x-pack/plugins/canvas/public/components/app/index.js index b776bf59efc99..369f54df37b71 100644 --- a/x-pack/plugins/canvas/public/components/app/index.js +++ b/x-pack/plugins/canvas/public/components/app/index.js @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createSocket } from '@kbn/interpreter/public/socket'; -import { initialize as initializeInterpreter } from '@kbn/interpreter/public/interpreter'; import { connect } from 'react-redux'; import { compose, withProps } from 'recompose'; import { populateBrowserRegistries } from '@kbn/interpreter/public/browser_registries'; @@ -46,13 +44,11 @@ const types = { const mapDispatchToProps = dispatch => ({ // TODO: the correct socket path should come from upstream, using the constant here is not ideal - setAppReady: basePath => async () => { + setAppReady: () => async () => { try { // initialize the socket and interpreter - await createSocket(basePath); loadPrivateBrowserFunctions(); await populateBrowserRegistries(types); - await initializeInterpreter(); // set app state to ready dispatch(appReady());