mirror of
https://github.com/grafana/grafana.git
synced 2025-02-14 01:23:32 -06:00
InfluxDB: InfluxQL: query editor: skip fields in metadata queries (#42543)
* influxdb: influxql: query editor: skip fields for metadata * test added * removed forgotten line * updated test name * updated comment * simplified test code
This commit is contained in:
parent
db3cc738d6
commit
db18acff15
@ -0,0 +1,125 @@
|
||||
import React from 'react';
|
||||
import { InfluxQuery } from '../../types';
|
||||
import InfluxDatasource from '../../datasource';
|
||||
import { render, screen, act } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { Editor } from './Editor';
|
||||
import * as mockedMeta from '../../influxQLMetadataQuery';
|
||||
|
||||
jest.mock('../../influxQLMetadataQuery', () => {
|
||||
return {
|
||||
getTagKeysForMeasurementAndTags: jest
|
||||
.fn()
|
||||
// first time we are called when the widget mounts,
|
||||
// we respond by saying `cpu, host` are the real tags
|
||||
.mockReturnValueOnce(Promise.resolve(['cpu', 'host']))
|
||||
// afterwards we will be called once when we click
|
||||
// on a tag-key in the WHERE section.
|
||||
// it does not matter what we return, as long as it is
|
||||
// promise-of-a-list-of-strings
|
||||
.mockReturnValueOnce(Promise.resolve([])),
|
||||
getTagValues: jest
|
||||
.fn()
|
||||
// it does not matter what we return, as long as it is
|
||||
// promise-of-a-list-of-strings
|
||||
.mockReturnValueOnce(Promise.resolve([])),
|
||||
getAllMeasurementsForTags: jest
|
||||
.fn()
|
||||
// it does not matter what we return, as long as it is
|
||||
// promise-of-a-list-of-strings
|
||||
.mockReturnValueOnce(Promise.resolve([])),
|
||||
};
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
(mockedMeta.getTagKeysForMeasurementAndTags as jest.Mock).mockClear();
|
||||
});
|
||||
|
||||
const ONLY_TAGS = [
|
||||
{
|
||||
key: 'cpu',
|
||||
operator: '=',
|
||||
value: 'cpu1',
|
||||
},
|
||||
{
|
||||
condition: 'AND',
|
||||
key: 'host',
|
||||
operator: '=',
|
||||
value: 'host2',
|
||||
},
|
||||
];
|
||||
|
||||
const query: InfluxQuery = {
|
||||
refId: 'A',
|
||||
policy: 'default',
|
||||
tags: [
|
||||
{
|
||||
key: 'cpu',
|
||||
operator: '=',
|
||||
value: 'cpu1',
|
||||
},
|
||||
{
|
||||
condition: 'AND',
|
||||
key: 'host',
|
||||
operator: '=',
|
||||
value: 'host2',
|
||||
},
|
||||
{
|
||||
condition: 'AND',
|
||||
key: 'field1',
|
||||
operator: '=',
|
||||
value: '45',
|
||||
},
|
||||
],
|
||||
select: [
|
||||
[
|
||||
{
|
||||
type: 'field',
|
||||
params: ['usage_idle'],
|
||||
},
|
||||
],
|
||||
],
|
||||
measurement: 'cpudata',
|
||||
};
|
||||
|
||||
describe('InfluxDB InfluxQL Visual Editor field-filtering', () => {
|
||||
it('should not send fields in tag-structures to metadata queries', async () => {
|
||||
const onChange = jest.fn();
|
||||
const onRunQuery = jest.fn();
|
||||
const datasource: InfluxDatasource = ({
|
||||
metricFindQuery: () => Promise.resolve([]),
|
||||
} as unknown) as InfluxDatasource;
|
||||
render(<Editor query={query} datasource={datasource} onChange={onChange} onRunQuery={onRunQuery} />);
|
||||
|
||||
// when the editor-widget mounts, it calls getTagKeysForMeasurementAndTags
|
||||
expect(mockedMeta.getTagKeysForMeasurementAndTags).toHaveBeenCalledTimes(1);
|
||||
|
||||
// we click the WHERE/cpu button
|
||||
await act(async () => {
|
||||
userEvent.click(screen.getByRole('button', { name: 'cpu' }));
|
||||
});
|
||||
|
||||
// and verify getTagKeysForMeasurementAndTags was called again,
|
||||
// and in the tags-param we did not receive the `field1` part.
|
||||
expect(mockedMeta.getTagKeysForMeasurementAndTags).toHaveBeenCalledTimes(2);
|
||||
expect((mockedMeta.getTagKeysForMeasurementAndTags as jest.Mock).mock.calls[1][2]).toStrictEqual(ONLY_TAGS);
|
||||
|
||||
// now we click on the WHERE/host2 button
|
||||
await act(async () => {
|
||||
userEvent.click(screen.getByRole('button', { name: 'host2' }));
|
||||
});
|
||||
|
||||
// verify `getTagValues` was called once, and in the tags-param we did not receive `field1`
|
||||
expect(mockedMeta.getTagValues).toHaveBeenCalledTimes(1);
|
||||
expect((mockedMeta.getTagValues as jest.Mock).mock.calls[0][3]).toStrictEqual(ONLY_TAGS);
|
||||
|
||||
// now we click on the FROM/cpudata button
|
||||
await act(async () => {
|
||||
userEvent.click(screen.getByRole('button', { name: 'cpudata' }));
|
||||
});
|
||||
|
||||
// verify `getTagValues` was called once, and in the tags-param we did not receive `field1`
|
||||
expect(mockedMeta.getAllMeasurementsForTags).toHaveBeenCalledTimes(1);
|
||||
expect((mockedMeta.getAllMeasurementsForTags as jest.Mock).mock.calls[0][1]).toStrictEqual(ONLY_TAGS);
|
||||
});
|
||||
});
|
@ -36,7 +36,9 @@ jest.mock('./Seg', () => {
|
||||
function assertEditor(query: InfluxQuery, textContent: string) {
|
||||
const onChange = jest.fn();
|
||||
const onRunQuery = jest.fn();
|
||||
const datasource: InfluxDatasource = {} as InfluxDatasource;
|
||||
const datasource: InfluxDatasource = ({
|
||||
metricFindQuery: () => Promise.resolve([]),
|
||||
} as unknown) as InfluxDatasource;
|
||||
const { container } = render(
|
||||
<Editor query={query} datasource={datasource} onChange={onChange} onRunQuery={onRunQuery} />
|
||||
);
|
||||
|
@ -53,6 +53,12 @@ function withTemplateVariableOptions(optionsPromise: Promise<string[]>): Promise
|
||||
return optionsPromise.then((options) => [...getTemplateVariableOptions(), ...options]);
|
||||
}
|
||||
|
||||
// it is possible to add fields into the `InfluxQueryTag` structures, and they do work,
|
||||
// but in some cases, when we do metadata queries, we have to remove them from the queries.
|
||||
function filterTags(parts: InfluxQueryTag[], allTagKeys: Set<string>): InfluxQueryTag[] {
|
||||
return parts.filter((t) => allTagKeys.has(t.key));
|
||||
}
|
||||
|
||||
export const Editor = (props: Props): JSX.Element => {
|
||||
const uniqueId = useUniqueId();
|
||||
const formatAsId = `influxdb-qe-format-as-${uniqueId}`;
|
||||
@ -63,6 +69,12 @@ export const Editor = (props: Props): JSX.Element => {
|
||||
const { datasource } = props;
|
||||
const { measurement, policy } = query;
|
||||
|
||||
const allTagKeys = useMemo(() => {
|
||||
return getTagKeysForMeasurementAndTags(measurement, policy, [], datasource).then((tags) => {
|
||||
return new Set(tags);
|
||||
});
|
||||
}, [measurement, policy, datasource]);
|
||||
|
||||
const selectLists = useMemo(() => {
|
||||
const dynamicSelectPartOptions = new Map([
|
||||
[
|
||||
@ -80,8 +92,11 @@ export const Editor = (props: Props): JSX.Element => {
|
||||
// the following function is not complicated enough to memoize, but it's result
|
||||
// is used in both memoized and un-memoized parts, so we have no choice
|
||||
const getTagKeys = useMemo(() => {
|
||||
return () => getTagKeysForMeasurementAndTags(measurement, policy, query.tags ?? [], datasource);
|
||||
}, [measurement, policy, query.tags, datasource]);
|
||||
return () =>
|
||||
allTagKeys.then((keys) =>
|
||||
getTagKeysForMeasurementAndTags(measurement, policy, filterTags(query.tags ?? [], keys), datasource)
|
||||
);
|
||||
}, [measurement, policy, query.tags, datasource, allTagKeys]);
|
||||
|
||||
const groupByList = useMemo(() => {
|
||||
const dynamicGroupByPartOptions = new Map([['tag_0', getTagKeys]]);
|
||||
@ -118,7 +133,13 @@ export const Editor = (props: Props): JSX.Element => {
|
||||
getPolicyOptions={() => getAllPolicies(datasource)}
|
||||
getMeasurementOptions={(filter) =>
|
||||
withTemplateVariableOptions(
|
||||
getAllMeasurementsForTags(filter === '' ? undefined : filter, query.tags ?? [], datasource)
|
||||
allTagKeys.then((keys) =>
|
||||
getAllMeasurementsForTags(
|
||||
filter === '' ? undefined : filter,
|
||||
filterTags(query.tags ?? [], keys),
|
||||
datasource
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
onChange={handleFromSectionChange}
|
||||
@ -131,7 +152,11 @@ export const Editor = (props: Props): JSX.Element => {
|
||||
onChange={handleTagsSectionChange}
|
||||
getTagKeyOptions={getTagKeys}
|
||||
getTagValueOptions={(key: string) =>
|
||||
withTemplateVariableOptions(getTagValues(key, measurement, policy, query.tags ?? [], datasource))
|
||||
withTemplateVariableOptions(
|
||||
allTagKeys.then((keys) =>
|
||||
getTagValues(key, measurement, policy, filterTags(query.tags ?? [], keys), datasource)
|
||||
)
|
||||
)
|
||||
}
|
||||
/>
|
||||
</SegmentSection>
|
||||
|
Loading…
Reference in New Issue
Block a user