mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -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) {
|
function assertEditor(query: InfluxQuery, textContent: string) {
|
||||||
const onChange = jest.fn();
|
const onChange = jest.fn();
|
||||||
const onRunQuery = jest.fn();
|
const onRunQuery = jest.fn();
|
||||||
const datasource: InfluxDatasource = {} as InfluxDatasource;
|
const datasource: InfluxDatasource = ({
|
||||||
|
metricFindQuery: () => Promise.resolve([]),
|
||||||
|
} as unknown) as InfluxDatasource;
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<Editor query={query} datasource={datasource} onChange={onChange} onRunQuery={onRunQuery} />
|
<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]);
|
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 => {
|
export const Editor = (props: Props): JSX.Element => {
|
||||||
const uniqueId = useUniqueId();
|
const uniqueId = useUniqueId();
|
||||||
const formatAsId = `influxdb-qe-format-as-${uniqueId}`;
|
const formatAsId = `influxdb-qe-format-as-${uniqueId}`;
|
||||||
@ -63,6 +69,12 @@ export const Editor = (props: Props): JSX.Element => {
|
|||||||
const { datasource } = props;
|
const { datasource } = props;
|
||||||
const { measurement, policy } = query;
|
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 selectLists = useMemo(() => {
|
||||||
const dynamicSelectPartOptions = new Map([
|
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
|
// 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
|
// is used in both memoized and un-memoized parts, so we have no choice
|
||||||
const getTagKeys = useMemo(() => {
|
const getTagKeys = useMemo(() => {
|
||||||
return () => getTagKeysForMeasurementAndTags(measurement, policy, query.tags ?? [], datasource);
|
return () =>
|
||||||
}, [measurement, policy, query.tags, datasource]);
|
allTagKeys.then((keys) =>
|
||||||
|
getTagKeysForMeasurementAndTags(measurement, policy, filterTags(query.tags ?? [], keys), datasource)
|
||||||
|
);
|
||||||
|
}, [measurement, policy, query.tags, datasource, allTagKeys]);
|
||||||
|
|
||||||
const groupByList = useMemo(() => {
|
const groupByList = useMemo(() => {
|
||||||
const dynamicGroupByPartOptions = new Map([['tag_0', getTagKeys]]);
|
const dynamicGroupByPartOptions = new Map([['tag_0', getTagKeys]]);
|
||||||
@ -118,7 +133,13 @@ export const Editor = (props: Props): JSX.Element => {
|
|||||||
getPolicyOptions={() => getAllPolicies(datasource)}
|
getPolicyOptions={() => getAllPolicies(datasource)}
|
||||||
getMeasurementOptions={(filter) =>
|
getMeasurementOptions={(filter) =>
|
||||||
withTemplateVariableOptions(
|
withTemplateVariableOptions(
|
||||||
getAllMeasurementsForTags(filter === '' ? undefined : filter, query.tags ?? [], datasource)
|
allTagKeys.then((keys) =>
|
||||||
|
getAllMeasurementsForTags(
|
||||||
|
filter === '' ? undefined : filter,
|
||||||
|
filterTags(query.tags ?? [], keys),
|
||||||
|
datasource
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onChange={handleFromSectionChange}
|
onChange={handleFromSectionChange}
|
||||||
@ -131,7 +152,11 @@ export const Editor = (props: Props): JSX.Element => {
|
|||||||
onChange={handleTagsSectionChange}
|
onChange={handleTagsSectionChange}
|
||||||
getTagKeyOptions={getTagKeys}
|
getTagKeyOptions={getTagKeys}
|
||||||
getTagValueOptions={(key: string) =>
|
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>
|
</SegmentSection>
|
||||||
|
Loading…
Reference in New Issue
Block a user