Skip to content

Commit

Permalink
[Vega] Filter bar in Vega is not usable with non default index patter…
Browse files Browse the repository at this point in the history
…n. (#84090)

* [Vega] Filtering is not working

Closes: #81738

* fix CI

* some work

* some work

* add tests for extract_index_pattern

* simplify extract_index_pattern

* fix type error

* cleanup

* Update vega_base_view.js

* Update extract_index_pattern.test.ts

* fix PR comments

* fix some comments

* findByTitle -> getByTitle

* remove getByTitle

* fix vega_base_view

* fix jest

* allow to set multiple indexes from top_nav

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
alexwizp and kibanamachine committed Dec 1, 2020
1 parent d32d5bd commit 4713fd0
Show file tree
Hide file tree
Showing 17 changed files with 290 additions and 110 deletions.
34 changes: 13 additions & 21 deletions src/plugins/data/common/index_patterns/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
* under the License.
*/

import { find } from 'lodash';
import { SavedObjectsClientCommon, SavedObject } from '..';
import type { IndexPatternSavedObjectAttrs } from './index_patterns';
import type { SavedObjectsClientCommon } from '../types';

/**
* Returns an object matching a given title
Expand All @@ -27,24 +27,16 @@ import { SavedObjectsClientCommon, SavedObject } from '..';
* @param title {string}
* @returns {Promise<SavedObject|undefined>}
*/
export async function findByTitle(
client: SavedObjectsClientCommon,
title: string
): Promise<SavedObject<any> | void> {
if (!title) {
return Promise.resolve();
}

const savedObjects = await client.find({
type: 'index-pattern',
perPage: 10,
search: `"${title}"`,
searchFields: ['title'],
fields: ['title'],
});
export async function findByTitle(client: SavedObjectsClientCommon, title: string) {
if (title) {
const savedObjects = await client.find<IndexPatternSavedObjectAttrs>({
type: 'index-pattern',
perPage: 10,
search: `"${title}"`,
searchFields: ['title'],
fields: ['title'],
});

return find(
savedObjects,
(obj: SavedObject<any>) => obj.attributes.title.toLowerCase() === title.toLowerCase()
);
return savedObjects.find((obj) => obj.attributes.title.toLowerCase() === title.toLowerCase());
}
}
1 change: 1 addition & 0 deletions src/plugins/data/public/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ const createStartContract = (): Start => {
SearchBar: jest.fn().mockReturnValue(null),
},
indexPatterns: ({
find: jest.fn((search) => [{ id: search, title: search }]),
createField: jest.fn(() => {}),
createFieldList: jest.fn(() => []),
ensureDefaultIndexPattern: jest.fn(),
Expand Down
14 changes: 1 addition & 13 deletions src/plugins/vis_type_timeseries/public/metrics_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import { PANEL_TYPES } from '../common/panel_types';
import { toExpressionAst } from './to_ast';
import { VIS_EVENT_TO_TRIGGER, VisGroups, VisParams } from '../../visualizations/public';
import { getDataStart } from './services';
import { INDEXES_SEPARATOR } from '../common/constants';

export const metricsVisDefinition = {
name: 'metrics',
Expand Down Expand Up @@ -84,18 +83,7 @@ export const metricsVisDefinition = {
inspectorAdapters: {},
getUsedIndexPattern: async (params: VisParams) => {
const { indexPatterns } = getDataStart();
const indexes: string = params.index_pattern;

if (indexes) {
const cachedIndexes = await indexPatterns.getIdsWithTitle();
const ids = indexes
.split(INDEXES_SEPARATOR)
.map((title) => cachedIndexes.find((i) => i.title === title)?.id)
.filter((id) => id);

return Promise.all(ids.map((id) => indexPatterns.get(id!)));
}

return [];
return params.index_pattern ? await indexPatterns.find(params.index_pattern) : [];
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ export class EsQueryParser {
const requestObject = requests.find((item) => getRequestName(item, index) === data.name);

if (requestObject) {
requestObject.dataObject.url = requestObject.url;
requestObject.dataObject.values = data.rawResponse;
}
});
Expand Down
5 changes: 3 additions & 2 deletions src/plugins/vis_type_vega/public/data_model/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,9 @@ interface Projection {
name: string;
}

interface RequestDataObject {
interface RequestDataObject<TUrlData = UrlObject> {
name?: string;
url?: TUrlData;
values: SearchResponse<unknown>;
}

Expand Down Expand Up @@ -186,7 +187,7 @@ export interface CacheBounds {
max: number;
}

interface Requests<TUrlData = UrlObject, TRequestDataObject = RequestDataObject> {
interface Requests<TUrlData = UrlObject, TRequestDataObject = RequestDataObject<TUrlData>> {
url: TUrlData;
name: string;
dataObject: TRequestDataObject;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,21 +185,21 @@ describe('VegaParser._resolveEsQueries', () => {
'es',
check(
{ data: { name: 'requestId', url: { index: 'a' }, x: 1 } },
{ data: { name: 'requestId', values: [42], x: 1 } }
{ data: { name: 'requestId', url: { index: 'a', body: {} }, values: [42], x: 1 } }
)
);
test(
'es 2',
check(
{ data: { name: 'requestId', url: { '%type%': 'elasticsearch', index: 'a' } } },
{ data: { name: 'requestId', values: [42] } }
{ data: { name: 'requestId', url: { index: 'a', body: {} }, values: [42] } }
)
);
test(
'es arr',
check(
{ arr: [{ data: { name: 'requestId', url: { index: 'a' }, x: 1 } }] },
{ arr: [{ data: { name: 'requestId', values: [42], x: 1 } }] }
{ arr: [{ data: { name: 'requestId', url: { index: 'a', body: {} }, values: [42], x: 1 } }] }
)
);
test(
Expand Down
125 changes: 125 additions & 0 deletions src/plugins/vis_type_vega/public/lib/extract_index_pattern.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* 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 { dataPluginMock } from '../../../data/public/mocks';
import { extractIndexPatternsFromSpec } from './extract_index_pattern';
import { setData } from '../services';

import type { VegaSpec } from '../data_model/types';

const getMockedSpec = (mockedObj: any) => (mockedObj as unknown) as VegaSpec;

describe('extractIndexPatternsFromSpec', () => {
const dataStart = dataPluginMock.createStartContract();

beforeAll(() => {
setData(dataStart);
});

test('should not throw errors if no index is specified', async () => {
const spec = getMockedSpec({
data: {},
});

const indexes = await extractIndexPatternsFromSpec(spec);

expect(indexes).toMatchInlineSnapshot(`Array []`);
});

test('should extract single index pattern', async () => {
const spec = getMockedSpec({
data: {
url: {
index: 'test',
},
},
});

const indexes = await extractIndexPatternsFromSpec(spec);

expect(indexes).toMatchInlineSnapshot(`
Array [
Object {
"id": "test",
"title": "test",
},
]
`);
});

test('should extract multiple index patterns', async () => {
const spec = getMockedSpec({
data: [
{
url: {
index: 'test1',
},
},
{
url: {
index: 'test2',
},
},
],
});

const indexes = await extractIndexPatternsFromSpec(spec);

expect(indexes).toMatchInlineSnapshot(`
Array [
Object {
"id": "test1",
"title": "test1",
},
Object {
"id": "test2",
"title": "test2",
},
]
`);
});

test('should filter empty values', async () => {
const spec = getMockedSpec({
data: [
{
url: {
wrong: 'wrong',
},
},
{
url: {
index: 'ok',
},
},
],
});

const indexes = await extractIndexPatternsFromSpec(spec);

expect(indexes).toMatchInlineSnapshot(`
Array [
Object {
"id": "ok",
"title": "ok",
},
]
`);
});
});
47 changes: 47 additions & 0 deletions src/plugins/vis_type_vega/public/lib/extract_index_pattern.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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 { flatten } from 'lodash';
import { getData } from '../services';

import type { Data, VegaSpec } from '../data_model/types';
import type { IndexPattern } from '../../../data/public';

export const extractIndexPatternsFromSpec = async (spec: VegaSpec) => {
const { indexPatterns } = getData();
let data: Data[] = [];

if (Array.isArray(spec.data)) {
data = spec.data;
} else if (spec.data) {
data = [spec.data];
}

return flatten<IndexPattern>(
await Promise.all(
data.reduce<Array<Promise<IndexPattern[]>>>((accumulator, currentValue) => {
if (currentValue.url?.index) {
accumulator.push(indexPatterns.find(currentValue.url.index));
}

return accumulator;
}, [])
)
);
};
2 changes: 0 additions & 2 deletions src/plugins/vis_type_vega/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import { Setup as InspectorSetup } from '../../inspector/public';
import {
setNotifications,
setData,
setSavedObjects,
setInjectedVars,
setUISettings,
setMapsLegacyConfig,
Expand Down Expand Up @@ -100,7 +99,6 @@ export class VegaPlugin implements Plugin<Promise<void>, void> {

public start(core: CoreStart, { data }: VegaPluginStartDependencies) {
setNotifications(core.notifications);
setSavedObjects(core.savedObjects);
setData(data);
setInjectedMetadata(core.injectedMetadata);
}
Expand Down
11 changes: 1 addition & 10 deletions src/plugins/vis_type_vega/public/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,7 @@
* under the License.
*/

import {
CoreStart,
SavedObjectsStart,
NotificationsStart,
IUiSettingsClient,
} from 'src/core/public';
import { CoreStart, NotificationsStart, IUiSettingsClient } from 'src/core/public';

import { DataPublicPluginStart } from '../../data/public';
import { createGetterSetter } from '../../kibana_utils/public';
Expand All @@ -40,10 +35,6 @@ export const [getInjectedMetadata, setInjectedMetadata] = createGetterSetter<
CoreStart['injectedMetadata']
>('InjectedMetadata');

export const [getSavedObjects, setSavedObjects] = createGetterSetter<SavedObjectsStart>(
'SavedObjects'
);

export const [getInjectedVars, setInjectedVars] = createGetterSetter<{
enableExternalUrls: boolean;
emsTileLayerId: unknown;
Expand Down
21 changes: 18 additions & 3 deletions src/plugins/vis_type_vega/public/vega_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,24 @@
*/

import { i18n } from '@kbn/i18n';
import { BaseVisTypeOptions } from 'src/plugins/visualizations/public';
import { parse } from 'hjson';
import type { BaseVisTypeOptions } from 'src/plugins/visualizations/public';

import { DefaultEditorSize } from '../../vis_default_editor/public';
import { VegaVisualizationDependencies } from './plugin';
import type { VegaVisualizationDependencies } from './plugin';

import { createVegaRequestHandler } from './vega_request_handler';
import { getDefaultSpec } from './default_spec';
import { extractIndexPatternsFromSpec } from './lib/extract_index_pattern';
import { createInspectorAdapters } from './vega_inspector';
import { VIS_EVENT_TO_TRIGGER, VisGroups } from '../../visualizations/public';
import { toExpressionAst } from './to_ast';
import { VisParams } from './vega_fn';
import { getInfoMessage } from './components/experimental_map_vis_info';
import { VegaVisEditorComponent } from './components/vega_vis_editor_lazy';

import type { VegaSpec } from './data_model/types';
import type { VisParams } from './vega_fn';

export const createVegaTypeDefinition = (
dependencies: VegaVisualizationDependencies
): BaseVisTypeOptions<VisParams> => {
Expand Down Expand Up @@ -68,6 +73,16 @@ export const createVegaTypeDefinition = (
getSupportedTriggers: () => {
return [VIS_EVENT_TO_TRIGGER.applyFilter];
},
getUsedIndexPattern: async (visParams) => {
try {
const spec = parse(visParams.spec, { legacyRoot: false, keepWsc: true });

return extractIndexPatternsFromSpec(spec as VegaSpec);
} catch (e) {
// spec is invalid
}
return [];
},
inspectorAdapters: createInspectorAdapters,
};
};
Loading

0 comments on commit 4713fd0

Please sign in to comment.