mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Prepare for cutover to vis data loader (#29873)
This commit is contained in:
parent
6afcc28c3b
commit
22933775ac
14 changed files with 154 additions and 37 deletions
|
@ -75,6 +75,34 @@ describe('build query', function () {
|
|||
expect(result).to.eql(expectedResult);
|
||||
});
|
||||
|
||||
it('should accept queries and filters as either single objects or arrays', function () {
|
||||
const queries = { query: 'extension:jpg', language: 'lucene' };
|
||||
const filters = {
|
||||
match_all: {},
|
||||
meta: { type: 'match_all' },
|
||||
};
|
||||
const config = {
|
||||
allowLeadingWildcards: true,
|
||||
queryStringOptions: {},
|
||||
};
|
||||
|
||||
const expectedResult = {
|
||||
bool: {
|
||||
must: [
|
||||
decorateQuery(luceneStringToDsl('extension:jpg'), config.queryStringOptions),
|
||||
{ match_all: {} },
|
||||
],
|
||||
filter: [],
|
||||
should: [],
|
||||
must_not: [],
|
||||
}
|
||||
};
|
||||
|
||||
const result = buildEsQuery(indexPattern, queries, filters, config);
|
||||
|
||||
expect(result).to.eql(expectedResult);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -56,7 +56,7 @@ describe('build query', function () {
|
|||
const oldQuery = { query: 'is(foo, bar)', language: 'kuery' };
|
||||
|
||||
expect(buildQueryFromKuery).withArgs(indexPattern, [oldQuery], true).to.throwError(
|
||||
/It looks like you're using an outdated Kuery syntax./
|
||||
/OutdatedKuerySyntaxError/
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -24,8 +24,8 @@ import { buildQueryFromLucene } from './from_lucene';
|
|||
|
||||
/**
|
||||
* @param indexPattern
|
||||
* @param queries - an array of query objects. Each query has a language property and a query property.
|
||||
* @param filters - an array of filter objects
|
||||
* @param queries - a query object or array of query objects. Each query has a language property and a query property.
|
||||
* @param filters - a filter object or array of filter objects
|
||||
* @param config - an objects with query:allowLeadingWildcards and query:queryString:options UI
|
||||
* settings in form of { allowLeadingWildcards, queryStringOptions }
|
||||
*/
|
||||
|
@ -33,15 +33,18 @@ export function buildEsQuery(
|
|||
indexPattern,
|
||||
queries = [],
|
||||
filters = [],
|
||||
{
|
||||
allowLeadingWildcards = false,
|
||||
queryStringOptions = {},
|
||||
config = {
|
||||
allowLeadingWildcards: false,
|
||||
queryStringOptions: {},
|
||||
}) {
|
||||
queries = Array.isArray(queries) ? queries : [queries];
|
||||
filters = Array.isArray(filters) ? filters : [filters];
|
||||
|
||||
const validQueries = queries.filter((query) => has(query, 'query'));
|
||||
const queriesByLanguage = groupBy(validQueries, 'language');
|
||||
|
||||
const kueryQuery = buildQueryFromKuery(indexPattern, queriesByLanguage.kuery, allowLeadingWildcards);
|
||||
const luceneQuery = buildQueryFromLucene(queriesByLanguage.lucene, queryStringOptions);
|
||||
const kueryQuery = buildQueryFromKuery(indexPattern, queriesByLanguage.kuery, config.allowLeadingWildcards);
|
||||
const luceneQuery = buildQueryFromLucene(queriesByLanguage.lucene, config.queryStringOptions);
|
||||
const filterQuery = buildQueryFromFilters(filters, indexPattern);
|
||||
|
||||
return {
|
||||
|
|
|
@ -73,6 +73,7 @@ export function runMochaCli() {
|
|||
'src/**/__tests__/**/*.js',
|
||||
'packages/elastic-datemath/test/**/*.js',
|
||||
'packages/kbn-dev-utils/src/**/__tests__/**/*.js',
|
||||
'packages/kbn-es-query/src/**/__tests__/**/*.js',
|
||||
'tasks/**/__tests__/**/*.js',
|
||||
], {
|
||||
cwd: resolve(__dirname, '../../..'),
|
||||
|
|
|
@ -39,11 +39,12 @@ export const tilemap = () => ({
|
|||
},
|
||||
fn(context, args) {
|
||||
const visConfigParams = JSON.parse(args.visConfig);
|
||||
const { geohash, metric, geocentroid } = visConfigParams.dimensions;
|
||||
|
||||
const convertedData = convertToGeoJson(context, {
|
||||
geohash: visConfigParams.geohash,
|
||||
metric: visConfigParams.metric,
|
||||
geocentroid: visConfigParams.geocentroid,
|
||||
geohash,
|
||||
metric,
|
||||
geocentroid,
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
@ -787,7 +787,7 @@ function discoverController(
|
|||
visualizeHandler.render({
|
||||
as: 'visualization',
|
||||
value: {
|
||||
visType: 'histogram',
|
||||
visType: $scope.vis.type.name,
|
||||
visData: resp,
|
||||
visConfig: $scope.vis.params,
|
||||
params: {},
|
||||
|
|
|
@ -26,6 +26,7 @@ import { VisFactoryProvider } from 'ui/vis/vis_factory';
|
|||
import { Schemas } from 'ui/vis/editors/default/schemas';
|
||||
import tableVisTemplate from './table_vis.html';
|
||||
import { VisTypesRegistryProvider } from 'ui/registry/vis_types';
|
||||
import { LegacyResponseHandlerProvider as legacyResponseHandlerProvider } from 'ui/vis/response_handlers/legacy';
|
||||
import { VisFiltersProvider } from 'ui/vis/vis_filters';
|
||||
|
||||
// we need to load the css ourselves
|
||||
|
@ -39,6 +40,8 @@ import { VisFiltersProvider } from 'ui/vis/vis_filters';
|
|||
// register the provider with the visTypes registry
|
||||
VisTypesRegistryProvider.register(TableVisTypeProvider);
|
||||
|
||||
const legacyTableResponseHandler = legacyResponseHandlerProvider().handler;
|
||||
|
||||
// define the TableVisType
|
||||
function TableVisTypeProvider(Private) {
|
||||
const VisFactory = Private(VisFactoryProvider);
|
||||
|
@ -108,6 +111,7 @@ function TableVisTypeProvider(Private) {
|
|||
}
|
||||
])
|
||||
},
|
||||
responseHandler: legacyTableResponseHandler,
|
||||
events: {
|
||||
filterBucket: {
|
||||
defaultAction: visFilters.filter,
|
||||
|
|
|
@ -27,6 +27,7 @@ import { Schemas } from 'ui/vis/editors/default/schemas';
|
|||
import { VisTypesRegistryProvider } from 'ui/registry/vis_types';
|
||||
import { Status } from 'ui/vis/update_status';
|
||||
import { truncatedColorMaps } from 'ui/vislib/components/color/truncated_colormaps';
|
||||
import { convertToGeoJson } from 'ui/vis/map/convert_to_geojson';
|
||||
|
||||
VisTypesRegistryProvider.register(function TileMapVisType(Private, getAppState, courier, config) {
|
||||
|
||||
|
@ -59,6 +60,7 @@ VisTypesRegistryProvider.register(function TileMapVisType(Private, getAppState,
|
|||
requiresUpdateStatus: [Status.AGGS, Status.PARAMS, Status.RESIZE, Status.UI_STATE],
|
||||
requiresPartialRows: true,
|
||||
visualization: CoordinateMapsVisualization,
|
||||
responseHandler: convertToGeoJson,
|
||||
editorConfig: {
|
||||
collections: {
|
||||
colorSchemas: Object.values(truncatedColorMaps).map(value => ({ id: value.id, label: value.label })),
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export type ResponseHandler = <Response, Data>(response: Response) => Data;
|
||||
export type ResponseHandler = <Response, Data>(response: Response, dimensions?: any) => Data;
|
||||
|
||||
export interface ResponseHandlerDescription {
|
||||
name: string;
|
||||
|
|
|
@ -37,7 +37,12 @@ import { visualizationLoader } from './visualization_loader';
|
|||
import { DataAdapter, RequestAdapter } from '../../inspector/adapters';
|
||||
|
||||
import { getTableAggs } from './pipeline_helpers/utilities';
|
||||
import { VisSavedObject, VisualizeLoaderParams, VisualizeUpdateParams } from './types';
|
||||
import {
|
||||
VisResponseData,
|
||||
VisSavedObject,
|
||||
VisualizeLoaderParams,
|
||||
VisualizeUpdateParams,
|
||||
} from './types';
|
||||
|
||||
interface EmbeddedVisualizeHandlerParams extends VisualizeLoaderParams {
|
||||
Private: IPrivate;
|
||||
|
@ -241,22 +246,18 @@ export class EmbeddedVisualizeHandler {
|
|||
|
||||
/**
|
||||
* renders visualization with provided data
|
||||
* @param visData: visualization data
|
||||
* @param data: visualization data
|
||||
*/
|
||||
public render = (pipelineResponse: any = {}) => {
|
||||
public render = (data: VisResponseData | null = null) => {
|
||||
// TODO: we have this weird situation when we need to render first, and then we call fetch and render ....
|
||||
// we need to get rid of that ....
|
||||
|
||||
const renderer = renderFunctionsRegistry.get(get(pipelineResponse, 'as', 'visualization'));
|
||||
const renderer = renderFunctionsRegistry.get(get(data || {}, 'as', 'visualization'));
|
||||
if (!renderer) {
|
||||
return;
|
||||
}
|
||||
renderer
|
||||
.render(
|
||||
this.element,
|
||||
pipelineResponse.value || { visType: this.vis.type.name },
|
||||
this.handlers
|
||||
)
|
||||
.render(this.element, get(data, 'value', { visType: this.vis.type.name }), this.handlers)
|
||||
.then(() => {
|
||||
if (!this.loaded) {
|
||||
this.loaded = true;
|
||||
|
@ -404,7 +405,7 @@ export class EmbeddedVisualizeHandler {
|
|||
this.vis.filters = { timeRange: this.dataLoaderParams.timeRange };
|
||||
|
||||
return this.dataLoader.fetch(this.dataLoaderParams).then(data => {
|
||||
if (data.value) {
|
||||
if (data && data.value) {
|
||||
this.dataSubject.next(data.value);
|
||||
}
|
||||
return data;
|
||||
|
|
|
@ -26,7 +26,7 @@ exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunct
|
|||
|
||||
exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles tagcloud function without buckets 1`] = `"tagcloud visConfig='{\\"metric\\":0}' "`;
|
||||
|
||||
exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles tile_map function 1`] = `"tilemap visConfig='{\\"metric\\":0,\\"geohash\\":1,\\"geocentroid\\":3}' "`;
|
||||
exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles tile_map function 1`] = `"tilemap visConfig='{\\"metric\\":{},\\"dimensions\\":{\\"metric\\":0,\\"geohash\\":1,\\"geocentroid\\":3}}' "`;
|
||||
|
||||
exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles timelion function 1`] = `"timelion_vis expression='foo' interval='bar' "`;
|
||||
|
||||
|
|
|
@ -50,11 +50,16 @@ interface Schemas {
|
|||
}
|
||||
|
||||
type buildVisFunction = (visState: VisState, schemas: Schemas) => string;
|
||||
type buildVisConfigFunction = (visState: Vis, schemas: Schemas) => VisState;
|
||||
|
||||
interface BuildPipelineVisFunction {
|
||||
[key: string]: buildVisFunction;
|
||||
}
|
||||
|
||||
interface BuildVisConfigFunction {
|
||||
[key: string]: buildVisConfigFunction;
|
||||
}
|
||||
|
||||
const vislibCharts: string[] = [
|
||||
'area',
|
||||
'gauge',
|
||||
|
@ -214,6 +219,33 @@ export const buildPipelineVisFunction: BuildPipelineVisFunction = {
|
|||
const visConfig = prepareJson('visConfig', visState.params);
|
||||
return `kibana_markdown ${expression}${visConfig}`;
|
||||
},
|
||||
table: (visState, schemas) => {
|
||||
const visConfig = buildVisConfig.table(visState, schemas);
|
||||
return `kibana_table ${prepareJson('visConfig', visConfig)}`;
|
||||
},
|
||||
metric: (visState, schemas) => {
|
||||
const visConfig = buildVisConfig.metric(visState, schemas);
|
||||
return `kibana_metric ${prepareJson('visConfig', visConfig)}`;
|
||||
},
|
||||
tagcloud: (visState, schemas) => {
|
||||
const visConfig = buildVisConfig.tagcloud(visState, schemas);
|
||||
return `tagcloud ${prepareJson('visConfig', visConfig)}`;
|
||||
},
|
||||
region_map: (visState, schemas) => {
|
||||
const visConfig = buildVisConfig.region_map(visState, schemas);
|
||||
return `regionmap ${prepareJson('visConfig', visConfig)}`;
|
||||
},
|
||||
tile_map: (visState, schemas) => {
|
||||
const visConfig = buildVisConfig.tile_map(visState, schemas);
|
||||
return `tilemap ${prepareJson('visConfig', visConfig)}`;
|
||||
},
|
||||
pie: (visState, schemas) => {
|
||||
const visConfig = buildVisConfig.pie(visState, schemas);
|
||||
return `kibana_pie ${prepareJson('visConfig', visConfig)}`;
|
||||
},
|
||||
};
|
||||
|
||||
const buildVisConfig: BuildVisConfigFunction = {
|
||||
table: (visState, schemas) => {
|
||||
const visConfig = visState.params;
|
||||
visConfig.dimensions = {
|
||||
|
@ -222,7 +254,7 @@ export const buildPipelineVisFunction: BuildPipelineVisFunction = {
|
|||
splitRow: schemas.split_row,
|
||||
splitColumn: schemas.split_column,
|
||||
};
|
||||
return `kibana_table ${prepareJson('visConfig', visConfig)}`;
|
||||
return visConfig;
|
||||
},
|
||||
metric: (visState, schemas) => {
|
||||
const visConfig = visState.params;
|
||||
|
@ -230,7 +262,7 @@ export const buildPipelineVisFunction: BuildPipelineVisFunction = {
|
|||
if (schemas.group) {
|
||||
visConfig.metric.bucket = schemas.group[0];
|
||||
}
|
||||
return `kibana_metric ${prepareJson('visConfig', visConfig)}`;
|
||||
return visConfig;
|
||||
},
|
||||
tagcloud: (visState, schemas) => {
|
||||
const visConfig = visState.params;
|
||||
|
@ -238,7 +270,7 @@ export const buildPipelineVisFunction: BuildPipelineVisFunction = {
|
|||
if (schemas.segment) {
|
||||
visConfig.bucket = schemas.segment[0];
|
||||
}
|
||||
return `tagcloud ${prepareJson('visConfig', visConfig)}`;
|
||||
return visConfig;
|
||||
},
|
||||
region_map: (visState, schemas) => {
|
||||
const visConfig = visState.params;
|
||||
|
@ -246,14 +278,16 @@ export const buildPipelineVisFunction: BuildPipelineVisFunction = {
|
|||
if (schemas.segment) {
|
||||
visConfig.bucket = schemas.segment[0];
|
||||
}
|
||||
return `regionmap ${prepareJson('visConfig', visConfig)}`;
|
||||
return visConfig;
|
||||
},
|
||||
tile_map: (visState, schemas) => {
|
||||
const visConfig = visState.params;
|
||||
visConfig.metric = schemas.metric[0];
|
||||
visConfig.geohash = schemas.segment ? schemas.segment[0] : null;
|
||||
visConfig.geocentroid = schemas.geo_centroid ? schemas.geo_centroid[0] : null;
|
||||
return `tilemap ${prepareJson('visConfig', visConfig)}`;
|
||||
visConfig.dimensions = {
|
||||
metric: schemas.metric[0],
|
||||
geohash: schemas.segment ? schemas.segment[0] : null,
|
||||
geocentroid: schemas.geo_centroid ? schemas.geo_centroid[0] : null,
|
||||
};
|
||||
return visConfig;
|
||||
},
|
||||
pie: (visState, schemas) => {
|
||||
const visConfig = visState.params;
|
||||
|
@ -263,7 +297,7 @@ export const buildPipelineVisFunction: BuildPipelineVisFunction = {
|
|||
splitRow: schemas.split_row,
|
||||
splitColumn: schemas.split_column,
|
||||
};
|
||||
return `kibana_pie ${prepareJson('visConfig', visConfig)}`;
|
||||
return visConfig;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -291,6 +325,19 @@ export const buildVislibDimensions = (vis: any, timeRange?: any) => {
|
|||
return dimensions;
|
||||
};
|
||||
|
||||
// If not using the expression pipeline (i.e. visualize_data_loader), we need a mechanism to
|
||||
// take a Vis object and decorate it with the necessary params (dimensions, bucket, metric, etc)
|
||||
export const decorateVisObject = (vis: Vis, params: { timeRange?: any }) => {
|
||||
const schemas = getSchemas(vis, params.timeRange);
|
||||
let visConfig = vis.params;
|
||||
if (buildVisConfig[vis.type.name]) {
|
||||
visConfig = buildVisConfig[vis.type.name](vis, schemas);
|
||||
vis.params = visConfig;
|
||||
} else if (vislibCharts.includes(vis.type.name)) {
|
||||
visConfig.dimensions = buildVislibDimensions(vis, params.timeRange);
|
||||
}
|
||||
};
|
||||
|
||||
export const buildPipeline = (
|
||||
vis: Vis,
|
||||
params: { searchSource: SearchSource; timeRange?: any }
|
||||
|
|
|
@ -57,6 +57,18 @@ export interface VisSavedObject {
|
|||
destroy: () => void;
|
||||
}
|
||||
|
||||
interface VisResponseValue {
|
||||
visType: string;
|
||||
visData: object;
|
||||
visConfig: object;
|
||||
params?: object;
|
||||
}
|
||||
|
||||
export interface VisResponseData {
|
||||
as: string;
|
||||
value: VisResponseValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* The parameters accepted by the embedVisualize calls.
|
||||
*/
|
||||
|
|
|
@ -31,10 +31,13 @@ import {
|
|||
Vis,
|
||||
} from '../../vis';
|
||||
|
||||
import { VisResponseData } from './types';
|
||||
|
||||
// @ts-ignore No typing present
|
||||
import { isTermSizeZeroError } from '../../elasticsearch_errors';
|
||||
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
import { decorateVisObject } from 'ui/visualize/loader/pipeline_helpers/build_pipeline';
|
||||
|
||||
function getHandler<T extends RequestHandler | ResponseHandler>(
|
||||
from: Array<{ name: string; handler: T }>,
|
||||
|
@ -67,11 +70,14 @@ export class VisualizeDataLoader {
|
|||
this.responseHandler = getHandler(responseHandlers, responseHandler);
|
||||
}
|
||||
|
||||
public async fetch(params: RequestHandlerParams): Promise<any> {
|
||||
public async fetch(params: RequestHandlerParams): Promise<VisResponseData | void> {
|
||||
this.vis.filters = { timeRange: params.timeRange };
|
||||
this.vis.requestError = undefined;
|
||||
this.vis.showRequestError = false;
|
||||
|
||||
// add necessary params to vis object (dimensions, bucket, metric, etc)
|
||||
decorateVisObject(this.vis, { timeRange: params.timeRange });
|
||||
|
||||
try {
|
||||
// searchSource is only there for courier request handler
|
||||
const requestHandlerResponse = await this.requestHandler({
|
||||
|
@ -96,9 +102,20 @@ export class VisualizeDataLoader {
|
|||
this.previousRequestHandlerResponse = requestHandlerResponse;
|
||||
|
||||
if (!canSkipResponseHandler) {
|
||||
this.visData = await Promise.resolve(this.responseHandler(requestHandlerResponse));
|
||||
this.visData = await Promise.resolve(
|
||||
this.responseHandler(requestHandlerResponse, this.vis.params.dimensions)
|
||||
);
|
||||
}
|
||||
return this.visData;
|
||||
|
||||
return {
|
||||
as: 'visualization',
|
||||
value: {
|
||||
visType: this.vis.type.name,
|
||||
visData: this.visData,
|
||||
visConfig: this.vis.params,
|
||||
params: {},
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
params.searchSource.cancelQueued();
|
||||
|
||||
|
@ -110,11 +127,12 @@ export class VisualizeDataLoader {
|
|||
console.error(error);
|
||||
|
||||
if (isTermSizeZeroError(error)) {
|
||||
return toastNotifications.addDanger(
|
||||
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.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
toastNotifications.addDanger({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue