From 85dad73e9de458ed8610ce7acdb326ff72808970 Mon Sep 17 00:00:00 2001 From: Andrej Ocenas Date: Tue, 28 Jan 2020 18:12:15 +0100 Subject: [PATCH] Influxdb: Fix cascader when doing log query in explore (#21787) * Fix cascader options and add tests * Add comment * Fix typo --- .../src/components/Cascader/Cascader.tsx | 2 + .../components/InfluxLogsQueryField.test.tsx | 48 ++++++++++++++++- .../components/InfluxLogsQueryField.tsx | 2 +- .../datasource/influxdb/datasource.mock.ts | 54 +++++++++++++++++++ 4 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 public/app/plugins/datasource/influxdb/datasource.mock.ts diff --git a/packages/grafana-ui/src/components/Cascader/Cascader.tsx b/packages/grafana-ui/src/components/Cascader/Cascader.tsx index 978d48e71c7..5e5ab5ee94e 100644 --- a/packages/grafana-ui/src/components/Cascader/Cascader.tsx +++ b/packages/grafana-ui/src/components/Cascader/Cascader.tsx @@ -29,9 +29,11 @@ interface CascaderState { export interface CascaderOption { value: any; label: string; + // Items will be just flattened into the main list of items recursively. items?: CascaderOption[]; disabled?: boolean; title?: string; + // Children will be shown in a submenu. children?: CascaderOption[]; } diff --git a/public/app/plugins/datasource/influxdb/components/InfluxLogsQueryField.test.tsx b/public/app/plugins/datasource/influxdb/components/InfluxLogsQueryField.test.tsx index 9f31f5b1286..10987bb52ea 100644 --- a/public/app/plugins/datasource/influxdb/components/InfluxLogsQueryField.test.tsx +++ b/public/app/plugins/datasource/influxdb/components/InfluxLogsQueryField.test.tsx @@ -1,4 +1,10 @@ -import { pairsAreValid } from './InfluxLogsQueryField'; +import React from 'react'; +import { mount } from 'enzyme'; +import { InfluxLogsQueryField, pairsAreValid } from './InfluxLogsQueryField'; +import { InfluxDatasourceMock } from '../datasource.mock'; +import InfluxDatasource from '../datasource'; +import { InfluxQuery } from '../types'; +import { ButtonCascader } from '@grafana/ui'; describe('pairsAreValid()', () => { describe('when all pairs are fully defined', () => { @@ -51,3 +57,43 @@ describe('pairsAreValid()', () => { }); }); }); + +describe('InfluxLogsQueryField', () => { + it('should load and show correct measurements and fields in cascader', async () => { + const wrapper = getInfluxLogsQueryField(); + // Looks strange but we do async stuff in didMount and this will push the stack at the end of eval loop, effectively + // waiting for the didMount to finish. + await Promise.resolve(); + wrapper.update(); + const cascader = wrapper.find(ButtonCascader); + expect(cascader.prop('options')).toEqual([ + { label: 'logs', value: 'logs', children: [{ label: 'description', value: 'description', children: [] }] }, + ]); + }); +}); + +function getInfluxLogsQueryField(props?: any) { + const datasource: InfluxDatasource = new InfluxDatasourceMock( + props?.measurements || { + logs: [{ name: 'description', type: 'string' }], + } + ) as any; + + const defaultProps = { + datasource, + history: [] as any[], + onRunQuery: () => {}, + onChange: (query: InfluxQuery) => {}, + query: { + refId: '', + } as InfluxQuery, + }; + return mount( + + ); +} diff --git a/public/app/plugins/datasource/influxdb/components/InfluxLogsQueryField.tsx b/public/app/plugins/datasource/influxdb/components/InfluxLogsQueryField.tsx index c3e4b579492..8dc886b31f7 100644 --- a/public/app/plugins/datasource/influxdb/components/InfluxLogsQueryField.tsx +++ b/public/app/plugins/datasource/influxdb/components/InfluxLogsQueryField.tsx @@ -75,7 +75,7 @@ export class InfluxLogsQueryField extends React.PureComponent { measurements.push({ label: measurementObj.text, value: measurementObj.text, - items: fields, + children: fields, }); } this.setState({ measurements }); diff --git a/public/app/plugins/datasource/influxdb/datasource.mock.ts b/public/app/plugins/datasource/influxdb/datasource.mock.ts new file mode 100644 index 00000000000..fb9c9e1ecf4 --- /dev/null +++ b/public/app/plugins/datasource/influxdb/datasource.mock.ts @@ -0,0 +1,54 @@ +type FieldsDefinition = { + name: string; + // String type, usually something like 'string' or 'float'. + type: string; +}; +type Measurements = { [measurement: string]: FieldsDefinition[] }; +type FieldReturnValue = { text: string }; + +/** + * Datasource mock for influx. At the moment this only works for queries that should return measurements or their + * fields and no other functionality is implemented. + */ +export class InfluxDatasourceMock { + constructor(private measurements: Measurements) {} + metricFindQuery(query: string) { + if (isMeasurementsQuery(query)) { + return this.getMeasurements(); + } else { + return this.getMeasurementFields(query); + } + } + + private getMeasurements(): FieldReturnValue[] { + return Object.keys(this.measurements).map(key => ({ text: key })); + } + + private getMeasurementFields(query: string): FieldReturnValue[] { + const match = query.match(/SHOW FIELD KEYS FROM \"(.+)\"/); + if (!match) { + throw new Error(`Failed to match query="${query}"`); + } + const measurementName = match[1]; + if (!measurementName) { + throw new Error(`Failed to match measurement name from query="${query}"`); + } + + const fields = this.measurements[measurementName]; + if (!fields) { + throw new Error( + `Failed to find measurement with name="${measurementName}" in measurements="[${Object.keys( + this.measurements + ).join(', ')}]"` + ); + } + + return fields.map(field => ({ + text: field.name, + })); + } +} + +function isMeasurementsQuery(query: string) { + return /SHOW MEASUREMENTS/.test(query); +}