diff --git a/public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/Editor.tags.test.tsx b/public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/Editor.tags.test.tsx
new file mode 100644
index 00000000000..238fd61102f
--- /dev/null
+++ b/public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/Editor.tags.test.tsx
@@ -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();
+
+ // 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);
+ });
+});
diff --git a/public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/Editor.test.tsx b/public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/Editor.test.tsx
index d88f6e329b3..b1a12faf7c5 100644
--- a/public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/Editor.test.tsx
+++ b/public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/Editor.test.tsx
@@ -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(
);
diff --git a/public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/Editor.tsx b/public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/Editor.tsx
index 7e99de7161d..c32be940678 100644
--- a/public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/Editor.tsx
+++ b/public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/Editor.tsx
@@ -53,6 +53,12 @@ function withTemplateVariableOptions(optionsPromise: Promise): 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): 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)
+ )
+ )
}
/>