Upstream grafana/grafana state of the touched files before 5492c79366
This commit is contained in:
commit
00681578cc
2 files changed
+728
No files matched your search
@@ -0,0 +1,485 @@
|
|||||||
|
import { toDataFrame } from '../../dataframe/processDataFrame';
|
||||||
|
import { type DataFrame, FieldType } from '../../types/dataFrame';
|
||||||
|
import { getFieldMatcher } from '../matchers';
|
||||||
|
|
||||||
|
import { FieldMatcherID } from './ids';
|
||||||
|
import { ByNamesMatcherMode } from './nameMatcher';
|
||||||
|
|
||||||
|
describe('Field Name by Regexp Matcher', () => {
|
||||||
|
it('Match all with wildcard regex', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: 'A hello world' }, { name: 'AAA' }, { name: 'C' }],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
id: FieldMatcherID.byRegexp,
|
||||||
|
options: '/.*/',
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher(config);
|
||||||
|
|
||||||
|
for (const field of seriesWithNames.fields) {
|
||||||
|
expect(matcher(field, seriesWithNames, [seriesWithNames])).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Match all with decimals regex', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: '12' }, { name: '112' }, { name: '13' }],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
id: FieldMatcherID.byRegexp,
|
||||||
|
options: '/^\\d+$/',
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher(config);
|
||||||
|
|
||||||
|
for (const field of seriesWithNames.fields) {
|
||||||
|
expect(matcher(field, seriesWithNames, [seriesWithNames])).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Match complex regex', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: 'some.instance.path' }, { name: '112' }, { name: '13' }],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
id: FieldMatcherID.byRegexp,
|
||||||
|
options: '/\\b(?:\\S+?\\.)+\\S+\\b$/',
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher(config);
|
||||||
|
let resultCount = 0;
|
||||||
|
for (const field of seriesWithNames.fields) {
|
||||||
|
if (matcher(field, seriesWithNames, [seriesWithNames])) {
|
||||||
|
resultCount++;
|
||||||
|
}
|
||||||
|
expect(resultCount).toBe(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Field Name Matcher', () => {
|
||||||
|
it('Match only exact name', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: 'A hello world' }, { name: 'AAA' }, { name: 'C' }],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
id: FieldMatcherID.byName,
|
||||||
|
options: 'C',
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher(config);
|
||||||
|
|
||||||
|
for (const field of seriesWithNames.fields) {
|
||||||
|
const didMatch = matcher(field, seriesWithNames, [seriesWithNames]);
|
||||||
|
expect(didMatch).toBe(field.name === 'C');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Match should respect letter case', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: '12' }, { name: '112' }, { name: '13' }, { name: 'C' }],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
id: FieldMatcherID.byName,
|
||||||
|
options: 'c',
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher(config);
|
||||||
|
|
||||||
|
for (const field of seriesWithNames.fields) {
|
||||||
|
expect(matcher(field, seriesWithNames, [seriesWithNames])).toBe(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Match name or displayName', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: 'a', config: { displayName: 'LongerName' } }],
|
||||||
|
});
|
||||||
|
const field = seriesWithNames.fields[0];
|
||||||
|
expect(
|
||||||
|
getFieldMatcher({
|
||||||
|
id: FieldMatcherID.byName,
|
||||||
|
options: 'c',
|
||||||
|
})(field, seriesWithNames, [seriesWithNames])
|
||||||
|
).toBe(false); // No match
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getFieldMatcher({
|
||||||
|
id: FieldMatcherID.byName,
|
||||||
|
options: 'a',
|
||||||
|
})(field, seriesWithNames, [seriesWithNames])
|
||||||
|
).toBe(true);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getFieldMatcher({
|
||||||
|
id: FieldMatcherID.byName,
|
||||||
|
options: 'LongerName',
|
||||||
|
})(field, seriesWithNames, [seriesWithNames])
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Match none of the field names', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: 'some.instance.path' }, { name: '112' }, { name: '13' }],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
id: FieldMatcherID.byName,
|
||||||
|
options: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher(config);
|
||||||
|
|
||||||
|
for (const field of seriesWithNames.fields) {
|
||||||
|
expect(matcher(field, seriesWithNames, [seriesWithNames])).toBe(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Field Multiple Names Matcher', () => {
|
||||||
|
it('Match only exact name', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: 'A hello world' }, { name: 'AAA' }, { name: 'C' }],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
id: FieldMatcherID.byNames,
|
||||||
|
options: {
|
||||||
|
mode: ByNamesMatcherMode.include,
|
||||||
|
names: ['C'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher(config);
|
||||||
|
|
||||||
|
for (const field of seriesWithNames.fields) {
|
||||||
|
const didMatch = matcher(field, seriesWithNames, [seriesWithNames]);
|
||||||
|
expect(didMatch).toBe(field.name === 'C');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Match should default to include mode', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: 'A hello world' }, { name: 'AAA' }, { name: 'C' }],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
id: FieldMatcherID.byNames,
|
||||||
|
options: {
|
||||||
|
names: ['C'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher(config);
|
||||||
|
|
||||||
|
for (const field of seriesWithNames.fields) {
|
||||||
|
const didMatch = matcher(field, seriesWithNames, [seriesWithNames]);
|
||||||
|
expect(didMatch).toBe(field.name === 'C');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Match should respect letter case', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: '12' }, { name: '112' }, { name: '13' }, { name: 'C' }],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
id: FieldMatcherID.byNames,
|
||||||
|
options: {
|
||||||
|
mode: ByNamesMatcherMode.include,
|
||||||
|
names: ['c'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher(config);
|
||||||
|
|
||||||
|
for (const field of seriesWithNames.fields) {
|
||||||
|
expect(matcher(field, seriesWithNames, [seriesWithNames])).toBe(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Match none of the field names', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: 'some.instance.path' }, { name: '112' }, { name: '13' }],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
id: FieldMatcherID.byNames,
|
||||||
|
options: {
|
||||||
|
mode: ByNamesMatcherMode.include,
|
||||||
|
names: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher(config);
|
||||||
|
|
||||||
|
for (const field of seriesWithNames.fields) {
|
||||||
|
expect(matcher(field, seriesWithNames, [seriesWithNames])).toBe(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Match all of the field names', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: 'some.instance.path' }, { name: '112' }, { name: '13' }],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
id: FieldMatcherID.byNames,
|
||||||
|
options: {
|
||||||
|
mode: ByNamesMatcherMode.include,
|
||||||
|
names: ['some.instance.path', '112', '13'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher(config);
|
||||||
|
|
||||||
|
for (const field of seriesWithNames.fields) {
|
||||||
|
expect(matcher(field, seriesWithNames, [seriesWithNames])).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Match all but supplied names', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: 'A hello world' }, { name: 'AAA' }, { name: 'C' }],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
id: FieldMatcherID.byNames,
|
||||||
|
options: {
|
||||||
|
mode: ByNamesMatcherMode.exclude,
|
||||||
|
names: ['C'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher(config);
|
||||||
|
|
||||||
|
for (const field of seriesWithNames.fields) {
|
||||||
|
const didMatch = matcher(field, seriesWithNames, [seriesWithNames]);
|
||||||
|
expect(didMatch).toBe(field.name !== 'C');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Field Regexp or Names Matcher', () => {
|
||||||
|
it('Match only exact name by name', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: 'A hello world' }, { name: 'AAA' }, { name: 'C' }],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
id: FieldMatcherID.byRegexpOrNames,
|
||||||
|
options: {
|
||||||
|
names: ['C'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher(config);
|
||||||
|
|
||||||
|
for (const field of seriesWithNames.fields) {
|
||||||
|
const didMatch = matcher(field, seriesWithNames, [seriesWithNames]);
|
||||||
|
expect(didMatch).toBe(field.name === 'C');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Match all starting with AA', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: 'A hello world' }, { name: 'AAA' }, { name: 'C' }],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
id: FieldMatcherID.byRegexpOrNames,
|
||||||
|
options: {
|
||||||
|
pattern: '/^AA/',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher(config);
|
||||||
|
|
||||||
|
for (const field of seriesWithNames.fields) {
|
||||||
|
const didMatch = matcher(field, seriesWithNames, [seriesWithNames]);
|
||||||
|
expect(didMatch).toBe(field.name === 'AAA');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Match all starting with AA and C', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: 'A hello world' }, { name: 'AAA' }, { name: 'C' }],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
id: FieldMatcherID.byRegexpOrNames,
|
||||||
|
options: {
|
||||||
|
pattern: '/^AA/',
|
||||||
|
names: ['C'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher(config);
|
||||||
|
|
||||||
|
for (const field of seriesWithNames.fields) {
|
||||||
|
const didMatch = matcher(field, seriesWithNames, [seriesWithNames]);
|
||||||
|
expect(didMatch).toBe(field.name === 'AAA' || field.name === 'C');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Match should respect letter case by name if not igored in pattern', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: '12' }, { name: '112' }, { name: '13' }, { name: 'C' }],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
id: FieldMatcherID.byRegexpOrNames,
|
||||||
|
options: {
|
||||||
|
names: ['c'],
|
||||||
|
pattern: '/c/i',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher(config);
|
||||||
|
|
||||||
|
for (const field of seriesWithNames.fields) {
|
||||||
|
const didMatch = matcher(field, seriesWithNames, [seriesWithNames]);
|
||||||
|
expect(didMatch).toBe(field.name === 'C');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Match none of the field names by name', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: 'some.instance.path' }, { name: '112' }, { name: '13' }],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
id: FieldMatcherID.byRegexpOrNames,
|
||||||
|
options: {
|
||||||
|
names: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher(config);
|
||||||
|
|
||||||
|
for (const field of seriesWithNames.fields) {
|
||||||
|
expect(matcher(field, seriesWithNames, [seriesWithNames])).toBe(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Match all of the field names by name', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: 'some.instance.path' }, { name: '112' }, { name: '13' }],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
id: FieldMatcherID.byRegexpOrNames,
|
||||||
|
options: {
|
||||||
|
names: ['some.instance.path', '112', '13'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher(config);
|
||||||
|
|
||||||
|
for (const field of seriesWithNames.fields) {
|
||||||
|
expect(matcher(field, seriesWithNames, [seriesWithNames])).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Match all of the field names by regexp', () => {
|
||||||
|
const seriesWithNames = toDataFrame({
|
||||||
|
fields: [{ name: 'some.instance.path' }, { name: '112' }, { name: '13' }],
|
||||||
|
});
|
||||||
|
const config = {
|
||||||
|
id: FieldMatcherID.byRegexpOrNames,
|
||||||
|
options: {
|
||||||
|
pattern: '/.*/',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher(config);
|
||||||
|
|
||||||
|
for (const field of seriesWithNames.fields) {
|
||||||
|
expect(matcher(field, seriesWithNames, [seriesWithNames])).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Support fallback name matchers', () => {
|
||||||
|
const frame: DataFrame = {
|
||||||
|
fields: [
|
||||||
|
{ name: 'time', type: FieldType.time, config: {}, values: [1, 2] },
|
||||||
|
{
|
||||||
|
name: 'UP',
|
||||||
|
type: FieldType.number,
|
||||||
|
config: {},
|
||||||
|
values: [1, 2],
|
||||||
|
labels: { __name__: 'UP' },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
name: 'X',
|
||||||
|
length: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
let matcher = getFieldMatcher({
|
||||||
|
id: FieldMatcherID.byName,
|
||||||
|
options: 'Value',
|
||||||
|
});
|
||||||
|
expect(matcher(frame.fields[0], frame, [])).toBeFalsy();
|
||||||
|
expect(matcher(frame.fields[1], frame, [])).toBeTruthy();
|
||||||
|
|
||||||
|
matcher = getFieldMatcher({
|
||||||
|
id: FieldMatcherID.byName,
|
||||||
|
options: 'Time',
|
||||||
|
});
|
||||||
|
expect(matcher(frame.fields[0], frame, [])).toBeTruthy();
|
||||||
|
expect(matcher(frame.fields[1], frame, [])).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Support fallback multiple names matchers', () => {
|
||||||
|
const frame: DataFrame = {
|
||||||
|
fields: [
|
||||||
|
{ name: 'time', type: FieldType.time, config: {}, values: [1, 2] },
|
||||||
|
{
|
||||||
|
name: 'UP',
|
||||||
|
type: FieldType.number,
|
||||||
|
config: {},
|
||||||
|
values: [1, 2],
|
||||||
|
labels: { __name__: 'UP' },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
name: 'X',
|
||||||
|
length: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
let matcher = getFieldMatcher({
|
||||||
|
id: FieldMatcherID.byNames,
|
||||||
|
options: {
|
||||||
|
mode: ByNamesMatcherMode.include,
|
||||||
|
names: ['Value'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(matcher(frame.fields[0], frame, [])).toBeFalsy();
|
||||||
|
expect(matcher(frame.fields[1], frame, [])).toBeTruthy();
|
||||||
|
|
||||||
|
matcher = getFieldMatcher({
|
||||||
|
id: FieldMatcherID.byNames,
|
||||||
|
options: {
|
||||||
|
mode: ByNamesMatcherMode.include,
|
||||||
|
names: ['Time'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(matcher(frame.fields[0], frame, [])).toBeTruthy();
|
||||||
|
expect(matcher(frame.fields[1], frame, [])).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Fields returned by query with refId', () => {
|
||||||
|
it('Match all fields in frame with refId: A', () => {
|
||||||
|
const data = [
|
||||||
|
toDataFrame({
|
||||||
|
refId: 'A',
|
||||||
|
fields: [{ name: 'field_1' }, { name: 'field_2' }],
|
||||||
|
}),
|
||||||
|
toDataFrame({
|
||||||
|
refId: 'B',
|
||||||
|
fields: [{ name: 'field_1' }, { name: 'field_2' }],
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const matcher = getFieldMatcher({
|
||||||
|
id: FieldMatcherID.byFrameRefID,
|
||||||
|
options: 'A',
|
||||||
|
});
|
||||||
|
|
||||||
|
const frameA = data[0];
|
||||||
|
expect(matcher(frameA.fields[0], frameA, data)).toBe(true);
|
||||||
|
expect(matcher(frameA.fields[1], frameA, data)).toBe(true);
|
||||||
|
|
||||||
|
const frameB = data[1];
|
||||||
|
expect(matcher(frameB.fields[0], frameB, data)).toBe(false);
|
||||||
|
expect(matcher(frameB.fields[1], frameB, data)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
import { getFieldDisplayName } from '../../field/fieldState';
|
||||||
|
import { stringToJsRegex } from '../../text/string';
|
||||||
|
import { type DataFrame, type Field, FieldType, TIME_SERIES_VALUE_FIELD_NAME } from '../../types/dataFrame';
|
||||||
|
import { type FieldMatcher, type FieldMatcherInfo, type FrameMatcherInfo } from '../../types/transformations';
|
||||||
|
|
||||||
|
import { FieldMatcherID, FrameMatcherID } from './ids';
|
||||||
|
|
||||||
|
export interface RegexpOrNamesMatcherOptions {
|
||||||
|
pattern?: string;
|
||||||
|
names?: string[];
|
||||||
|
variable?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mode to be able to toggle if the names matcher should match fields in provided
|
||||||
|
* list or all except provided names.
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export enum ByNamesMatcherMode {
|
||||||
|
exclude = 'exclude',
|
||||||
|
include = 'include',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options to instruct the by names matcher to either match all fields in given list
|
||||||
|
* or all except the fields in the list.
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface ByNamesMatcherOptions {
|
||||||
|
mode?: ByNamesMatcherMode;
|
||||||
|
names?: string[];
|
||||||
|
readOnly?: boolean;
|
||||||
|
prefix?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// General Field matcher
|
||||||
|
const fieldNameMatcher: FieldMatcherInfo<string> = {
|
||||||
|
id: FieldMatcherID.byName,
|
||||||
|
name: 'Field Name',
|
||||||
|
description: 'match the field name',
|
||||||
|
defaultOptions: '',
|
||||||
|
|
||||||
|
get: (name: string): FieldMatcher => {
|
||||||
|
const uniqueNames = new Set<string>([name]);
|
||||||
|
|
||||||
|
const fallback = fieldNameFallback(uniqueNames);
|
||||||
|
|
||||||
|
return (field: Field, frame: DataFrame, allFrames: DataFrame[]) => {
|
||||||
|
return (
|
||||||
|
name === field.name ||
|
||||||
|
name === getFieldDisplayName(field, frame, allFrames) ||
|
||||||
|
Boolean(fallback && fallback(field, frame, allFrames))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getOptionsDisplayText: (name: string) => {
|
||||||
|
return `Field name: ${name}`;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const multipleFieldNamesMatcher: FieldMatcherInfo<ByNamesMatcherOptions> = {
|
||||||
|
id: FieldMatcherID.byNames,
|
||||||
|
name: 'Field Names',
|
||||||
|
description: 'match any of the given the field names',
|
||||||
|
defaultOptions: {
|
||||||
|
mode: ByNamesMatcherMode.include,
|
||||||
|
names: [],
|
||||||
|
},
|
||||||
|
|
||||||
|
get: (options: ByNamesMatcherOptions): FieldMatcher => {
|
||||||
|
const { names, mode = ByNamesMatcherMode.include } = options;
|
||||||
|
const uniqueNames = new Set<string>(names ?? []);
|
||||||
|
|
||||||
|
const fallback = fieldNameFallback(uniqueNames);
|
||||||
|
|
||||||
|
const matcher = (field: Field, frame: DataFrame, frames: DataFrame[]) => {
|
||||||
|
return (
|
||||||
|
uniqueNames.has(field.name) ||
|
||||||
|
uniqueNames.has(getFieldDisplayName(field, frame, frames)) ||
|
||||||
|
Boolean(fallback && fallback(field, frame, frames))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (mode === ByNamesMatcherMode.exclude) {
|
||||||
|
return (field: Field, frame: DataFrame, frames: DataFrame[]) => {
|
||||||
|
return !matcher(field, frame, frames);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return matcher;
|
||||||
|
},
|
||||||
|
|
||||||
|
getOptionsDisplayText: (options: ByNamesMatcherOptions): string => {
|
||||||
|
const { names, mode } = options;
|
||||||
|
const displayText = (names ?? []).join(', ');
|
||||||
|
if (mode === ByNamesMatcherMode.exclude) {
|
||||||
|
return `All except: ${displayText}`;
|
||||||
|
}
|
||||||
|
return `All of: ${displayText}`;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// In an effort to support migrating to a consistent data contract, the
|
||||||
|
// naming conventions need to get normalized. However, many existing setups
|
||||||
|
// exist that would no longer match names if that changes. This injects
|
||||||
|
// fallback logic when the data frame has not type version specified
|
||||||
|
function fieldNameFallback(fields: Set<string>) {
|
||||||
|
let fallback: FieldMatcher | undefined = undefined;
|
||||||
|
|
||||||
|
if (fields.has(TIME_SERIES_VALUE_FIELD_NAME)) {
|
||||||
|
fallback = (field: Field, frame: DataFrame) => {
|
||||||
|
return (
|
||||||
|
Boolean(field.labels) && // Value was reasonable when the name was set in labels or on the frame
|
||||||
|
field.labels?.__name__ === field.name
|
||||||
|
);
|
||||||
|
};
|
||||||
|
} else if (fields.has('Time') || fields.has('time')) {
|
||||||
|
fallback = (field: Field, frame: DataFrame) => {
|
||||||
|
return frame.meta?.typeVersion == null && field.type === FieldType.time;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
const regexpFieldNameMatcher: FieldMatcherInfo<string> = {
|
||||||
|
id: FieldMatcherID.byRegexp,
|
||||||
|
name: 'Field Name by Regexp',
|
||||||
|
description: 'match the field name by a given regexp pattern',
|
||||||
|
defaultOptions: '/.*/',
|
||||||
|
|
||||||
|
get: (pattern: string): FieldMatcher => {
|
||||||
|
const regexp = patternToRegex(pattern);
|
||||||
|
|
||||||
|
return (field: Field, frame: DataFrame, allFrames: DataFrame[]) => {
|
||||||
|
const displayName = getFieldDisplayName(field, frame, allFrames);
|
||||||
|
return !!regexp && regexp.test(displayName);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getOptionsDisplayText: (pattern: string): string => {
|
||||||
|
return `Field name by pattern: ${pattern}`;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Field matcher that will match all fields that exists in a
|
||||||
|
* data frame with configured refId.
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
const fieldsInFrameMatcher: FieldMatcherInfo<string> = {
|
||||||
|
id: FieldMatcherID.byFrameRefID,
|
||||||
|
name: 'Fields by frame refId',
|
||||||
|
description: 'match all fields returned in data frame with refId.',
|
||||||
|
defaultOptions: '',
|
||||||
|
|
||||||
|
get: (refId: string): FieldMatcher => {
|
||||||
|
return (field: Field, frame: DataFrame, allFrames: DataFrame[]) => {
|
||||||
|
return frame.refId === refId;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getOptionsDisplayText: (refId: string): string => {
|
||||||
|
return `Math all fields returned by query with reference ID: ${refId}`;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const regexpOrMultipleNamesMatcher: FieldMatcherInfo<RegexpOrNamesMatcherOptions> = {
|
||||||
|
id: FieldMatcherID.byRegexpOrNames,
|
||||||
|
name: 'Field Name by Regexp or Names',
|
||||||
|
description: 'match the field name by a given regexp pattern or given names',
|
||||||
|
defaultOptions: {
|
||||||
|
pattern: '/.*/',
|
||||||
|
names: [],
|
||||||
|
},
|
||||||
|
|
||||||
|
get: (options: RegexpOrNamesMatcherOptions): FieldMatcher => {
|
||||||
|
const regexpMatcher = regexpFieldNameMatcher.get(options?.pattern || '');
|
||||||
|
const namesMatcher = multipleFieldNamesMatcher.get({
|
||||||
|
mode: ByNamesMatcherMode.include,
|
||||||
|
names: options?.names ?? [],
|
||||||
|
});
|
||||||
|
|
||||||
|
return (field: Field, frame: DataFrame, allFrames: DataFrame[]) => {
|
||||||
|
return namesMatcher(field, frame, allFrames) || regexpMatcher(field, frame, allFrames);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getOptionsDisplayText: (options: RegexpOrNamesMatcherOptions): string => {
|
||||||
|
const pattern = options?.pattern ?? '';
|
||||||
|
const names = options?.names?.join(',') ?? '';
|
||||||
|
return `Field name by pattern: ${pattern} or names: ${names}`;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const patternToRegex = (pattern?: string): RegExp | undefined => {
|
||||||
|
if (!pattern) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return stringToJsRegex(pattern);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// General Frame matcher
|
||||||
|
const frameNameMatcher: FrameMatcherInfo<string> = {
|
||||||
|
id: FrameMatcherID.byName,
|
||||||
|
name: 'Frame Name',
|
||||||
|
description: 'match the frame name',
|
||||||
|
defaultOptions: '/.*/',
|
||||||
|
|
||||||
|
get: (pattern: string) => {
|
||||||
|
const regex = stringToJsRegex(pattern);
|
||||||
|
return (frame: DataFrame) => {
|
||||||
|
return regex.test(frame.name || '');
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getOptionsDisplayText: (pattern: string) => {
|
||||||
|
return `Frame name: ${pattern}`;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registry Initialization
|
||||||
|
*/
|
||||||
|
export function getFieldNameMatchers(): FieldMatcherInfo[] {
|
||||||
|
return [
|
||||||
|
fieldNameMatcher,
|
||||||
|
regexpFieldNameMatcher,
|
||||||
|
multipleFieldNamesMatcher,
|
||||||
|
regexpOrMultipleNamesMatcher,
|
||||||
|
fieldsInFrameMatcher,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFrameNameMatchers(): FrameMatcherInfo[] {
|
||||||
|
return [frameNameMatcher];
|
||||||
|
}
|
||||||
Reference in new issue
Block a user