mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Template Variables: Fix conversion from non standard data to dataFrame (#73486)
This commit is contained in:
parent
61fd34ca2b
commit
dc6675cade
@ -76,6 +76,14 @@ export abstract class CustomVariableSupport<
|
||||
}
|
||||
|
||||
abstract editor: ComponentType<QueryEditorProps<DSType, TQuery, TOptions, VariableQuery>>;
|
||||
|
||||
/**
|
||||
* This can return data in various formats as DataQueryResponse allows multiple types. In general though the
|
||||
* assumption is that there will be a string Field or value in an Array of objects that will be taken as the possible
|
||||
* variable values. You can also use this type directly MetricFindValue or just use text/value/expendable fields/keys
|
||||
* in the response.
|
||||
* @param request
|
||||
*/
|
||||
abstract query(request: DataQueryRequest<VariableQuery>): Observable<DataQueryResponse>;
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ import { KeyedVariableIdentifier } from '../state/types';
|
||||
import { QueryVariableModel, VariableRefresh } from '../types';
|
||||
import { getTemplatedRegex } from '../utils';
|
||||
|
||||
import { toMetricFindValues, updateOptionsState, validateVariableSelection } from './operators';
|
||||
import { toMetricFindValuesOperator, updateOptionsState, validateVariableSelection } from './operators';
|
||||
import { QueryRunners } from './queryRunners';
|
||||
|
||||
interface UpdateOptionsArgs {
|
||||
@ -134,7 +134,7 @@ export class VariableQueryRunner {
|
||||
|
||||
return of(data);
|
||||
}),
|
||||
toMetricFindValues(),
|
||||
toMetricFindValuesOperator(),
|
||||
updateOptionsState({ variable, dispatch, getTemplatedRegexFunc }),
|
||||
validateVariableSelection({ variable, dispatch, searchFilter }),
|
||||
takeUntil(
|
||||
|
@ -80,8 +80,7 @@ describe('operators', () => {
|
||||
],
|
||||
});
|
||||
|
||||
// it.each wouldn't work here as we need the done callback
|
||||
[
|
||||
it.each([
|
||||
{ series: null, expected: [] },
|
||||
{ series: undefined, expected: [] },
|
||||
{ series: [], expected: [] },
|
||||
@ -127,32 +126,28 @@ describe('operators', () => {
|
||||
{ text: 'C', value: 'C', expandable: true },
|
||||
],
|
||||
},
|
||||
].map((scenario) => {
|
||||
it(`when called with series:${JSON.stringify(scenario.series, null, 0)}`, async () => {
|
||||
const { series, expected } = scenario;
|
||||
const panelData = { series } as PanelData;
|
||||
const observable = of(panelData).pipe(toMetricFindValues());
|
||||
|
||||
await expect(observable).toEmitValuesWith((received) => {
|
||||
const value = received[0];
|
||||
expect(value).toEqual(expected);
|
||||
});
|
||||
});
|
||||
{
|
||||
series: [[{ id: 'foo' }, { id: 'bar' }]],
|
||||
expected: [
|
||||
{ text: 'foo', value: 'foo' },
|
||||
{ text: 'bar', value: 'bar' },
|
||||
],
|
||||
},
|
||||
])('%# when called with: %j', ({ series, expected }) => {
|
||||
const panelData = { series } as PanelData;
|
||||
expect(toMetricFindValues(panelData)).toEqual(expected);
|
||||
});
|
||||
|
||||
describe('when called without metric find values and string fields', () => {
|
||||
it('then the observable throws', async () => {
|
||||
it('then the observable throws', () => {
|
||||
const frameWithTimeField = toDataFrame({
|
||||
fields: [{ name: 'time', type: FieldType.time, values: [1, 2, 3] }],
|
||||
});
|
||||
|
||||
const panelData = { series: [frameWithTimeField] } as PanelData;
|
||||
const observable = of(panelData).pipe(toMetricFindValues());
|
||||
|
||||
await expect(observable).toEmitValuesWith((received) => {
|
||||
const value = received[0];
|
||||
expect(value).toEqual(new Error("Couldn't find any field of type string in the results."));
|
||||
});
|
||||
expect(() => toMetricFindValues(panelData)).toThrow(
|
||||
new Error("Couldn't find any field of type string in the results.")
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -18,87 +18,86 @@ import { getTemplatedRegex, toKeyedVariableIdentifier, toVariablePayload } from
|
||||
|
||||
import { updateVariableOptions } from './reducer';
|
||||
|
||||
export function toMetricFindValues(): OperatorFunction<PanelData, MetricFindValue[]> {
|
||||
return (source) =>
|
||||
source.pipe(
|
||||
map((panelData) => {
|
||||
const frames = panelData.series;
|
||||
if (!frames || !frames.length) {
|
||||
return [];
|
||||
}
|
||||
export function toMetricFindValuesOperator(): OperatorFunction<PanelData, MetricFindValue[]> {
|
||||
return (source) => source.pipe(map(toMetricFindValues));
|
||||
}
|
||||
|
||||
if (areMetricFindValues(frames)) {
|
||||
return frames;
|
||||
}
|
||||
export function toMetricFindValues(panelData: PanelData): MetricFindValue[] {
|
||||
const frames = panelData.series;
|
||||
if (!frames || !frames.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const processedDataFrames = getProcessedDataFrames(frames);
|
||||
const metrics: MetricFindValue[] = [];
|
||||
if (areMetricFindValues(frames)) {
|
||||
return frames;
|
||||
}
|
||||
|
||||
let valueIndex = -1;
|
||||
let textIndex = -1;
|
||||
let stringIndex = -1;
|
||||
let expandableIndex = -1;
|
||||
const processedDataFrames = getProcessedDataFrames(frames);
|
||||
const metrics: MetricFindValue[] = [];
|
||||
|
||||
for (const frame of processedDataFrames) {
|
||||
for (let index = 0; index < frame.fields.length; index++) {
|
||||
const field = frame.fields[index];
|
||||
const fieldName = getFieldDisplayName(field, frame, frames).toLowerCase();
|
||||
let valueIndex = -1;
|
||||
let textIndex = -1;
|
||||
let stringIndex = -1;
|
||||
let expandableIndex = -1;
|
||||
|
||||
if (field.type === FieldType.string && stringIndex === -1) {
|
||||
stringIndex = index;
|
||||
}
|
||||
for (const frame of processedDataFrames) {
|
||||
for (let index = 0; index < frame.fields.length; index++) {
|
||||
const field = frame.fields[index];
|
||||
const fieldName = getFieldDisplayName(field, frame, frames).toLowerCase();
|
||||
|
||||
if (fieldName === 'text' && field.type === FieldType.string && textIndex === -1) {
|
||||
textIndex = index;
|
||||
}
|
||||
if (field.type === FieldType.string && stringIndex === -1) {
|
||||
stringIndex = index;
|
||||
}
|
||||
|
||||
if (fieldName === 'value' && field.type === FieldType.string && valueIndex === -1) {
|
||||
valueIndex = index;
|
||||
}
|
||||
if (fieldName === 'text' && field.type === FieldType.string && textIndex === -1) {
|
||||
textIndex = index;
|
||||
}
|
||||
|
||||
if (
|
||||
fieldName === 'expandable' &&
|
||||
(field.type === FieldType.boolean || field.type === FieldType.number) &&
|
||||
expandableIndex === -1
|
||||
) {
|
||||
expandableIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fieldName === 'value' && field.type === FieldType.string && valueIndex === -1) {
|
||||
valueIndex = index;
|
||||
}
|
||||
|
||||
if (stringIndex === -1) {
|
||||
throw new Error("Couldn't find any field of type string in the results.");
|
||||
}
|
||||
if (
|
||||
fieldName === 'expandable' &&
|
||||
(field.type === FieldType.boolean || field.type === FieldType.number) &&
|
||||
expandableIndex === -1
|
||||
) {
|
||||
expandableIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const frame of frames) {
|
||||
for (let index = 0; index < frame.length; index++) {
|
||||
const expandable = expandableIndex !== -1 ? frame.fields[expandableIndex].values[index] : undefined;
|
||||
const string = frame.fields[stringIndex].values[index];
|
||||
const text = textIndex !== -1 ? frame.fields[textIndex].values[index] : null;
|
||||
const value = valueIndex !== -1 ? frame.fields[valueIndex].values[index] : null;
|
||||
if (stringIndex === -1) {
|
||||
throw new Error("Couldn't find any field of type string in the results.");
|
||||
}
|
||||
|
||||
if (valueIndex === -1 && textIndex === -1) {
|
||||
metrics.push({ text: string, value: string, expandable });
|
||||
continue;
|
||||
}
|
||||
for (const frame of processedDataFrames) {
|
||||
for (let index = 0; index < frame.length; index++) {
|
||||
const expandable = expandableIndex !== -1 ? frame.fields[expandableIndex].values[index] : undefined;
|
||||
const string = frame.fields[stringIndex].values[index];
|
||||
const text = textIndex !== -1 ? frame.fields[textIndex].values[index] : null;
|
||||
const value = valueIndex !== -1 ? frame.fields[valueIndex].values[index] : null;
|
||||
|
||||
if (valueIndex === -1 && textIndex !== -1) {
|
||||
metrics.push({ text, value: text, expandable });
|
||||
continue;
|
||||
}
|
||||
if (valueIndex === -1 && textIndex === -1) {
|
||||
metrics.push({ text: string, value: string, expandable });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (valueIndex !== -1 && textIndex === -1) {
|
||||
metrics.push({ text: value, value, expandable });
|
||||
continue;
|
||||
}
|
||||
if (valueIndex === -1 && textIndex !== -1) {
|
||||
metrics.push({ text, value: text, expandable });
|
||||
continue;
|
||||
}
|
||||
|
||||
metrics.push({ text, value, expandable });
|
||||
}
|
||||
}
|
||||
if (valueIndex !== -1 && textIndex === -1) {
|
||||
metrics.push({ text: value, value, expandable });
|
||||
continue;
|
||||
}
|
||||
|
||||
return metrics;
|
||||
})
|
||||
);
|
||||
metrics.push({ text, value, expandable });
|
||||
}
|
||||
}
|
||||
|
||||
return metrics;
|
||||
}
|
||||
|
||||
export function updateOptionsState(args: {
|
||||
|
Loading…
Reference in New Issue
Block a user