Skip to content

Commit

Permalink
Transform: fixes so we match the field based on the proper name. (#24659
Browse files Browse the repository at this point in the history
)

* fixes so we match the transformer based on name properly.

* changed the signature on the FieldMatcher.

* introduced a names option so you can filter in name specificly.

* changed so the matcher UI uses the new options format.

* moved the exported functions together.

* changing editors a bit.

* made the filter by name work with both regex and name filtering.

* fixed failing tests and make sure we always parse regex the same way.

* removed unused code.

* simplified to make the existing field overrides still working.

* fixed issue reported by hugo.

* added tests for the name matcher.

* added tests for filter by name.

* added more tests.
  • Loading branch information
mckn authored and aknuds1 committed Jun 29, 2020
1 parent 5f53d89 commit 4f157ee
Show file tree
Hide file tree
Showing 24 changed files with 804 additions and 183 deletions.
2 changes: 1 addition & 1 deletion packages/grafana-data/src/field/fieldOverrides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export function applyFieldOverrides(options: ApplyFieldOverrideOptions): DataFra

// Find any matching rules and then override
for (const rule of override) {
if (rule.match(field)) {
if (rule.match(field, frame, options.data!)) {
for (const prop of rule.properties) {
// config.scopedVars is set already here
setDynamicConfigValue(config, prop, context);
Expand Down
1 change: 1 addition & 0 deletions packages/grafana-data/src/transformations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export {
TransformerUIProps,
standardTransformersRegistry,
} from './standardTransformersRegistry';
export { RegexpOrNamesMatcherOptions } from './matchers/nameMatcher';
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ describe('Field Type Matcher', () => {
it('finds numbers', () => {
for (const field of simpleSeriesWithTypes.fields) {
const matches = matcher.get(FieldType.number);
expect(matches(field)).toBe(field.type === FieldType.number);
const didMatch = matches(field, simpleSeriesWithTypes, [simpleSeriesWithTypes]);
expect(didMatch).toBe(field.type === FieldType.number);
}
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Field, FieldType } from '../../types/dataFrame';
import { Field, FieldType, DataFrame } from '../../types/dataFrame';
import { FieldMatcherID } from './ids';
import { FieldMatcherInfo } from '../../types/transformations';

Expand All @@ -10,7 +10,7 @@ const fieldTypeMatcher: FieldMatcherInfo<FieldType> = {
defaultOptions: FieldType.number,

get: (type: FieldType) => {
return (field: Field) => {
return (field: Field, frame: DataFrame, allFrames: DataFrame[]) => {
return type === field.type;
};
},
Expand Down
3 changes: 3 additions & 0 deletions packages/grafana-data/src/transformations/matchers/ids.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ export enum FieldMatcherID {
// With arguments
byType = 'byType',
byName = 'byName',
byNames = 'byNames',
byRegexp = 'byRegexp',
byRegexpOrNames = 'byRegexpOrNames',
// byIndex = 'byIndex',
// byLabel = 'byLabel',
}
Expand Down
266 changes: 259 additions & 7 deletions packages/grafana-data/src/transformations/matchers/nameMatcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ import { getFieldMatcher } from '../matchers';
import { FieldMatcherID } from './ids';
import { toDataFrame } from '../../dataframe/processDataFrame';

describe('Field Name Matcher', () => {
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.byName,
id: FieldMatcherID.byRegexp,
options: '/.*/',
};

const matcher = getFieldMatcher(config);

for (const field of seriesWithNames.fields) {
expect(matcher(field)).toBe(true);
expect(matcher(field, seriesWithNames, [seriesWithNames])).toBe(true);
}
});

Expand All @@ -24,14 +24,14 @@ describe('Field Name Matcher', () => {
fields: [{ name: '12' }, { name: '112' }, { name: '13' }],
});
const config = {
id: FieldMatcherID.byName,
id: FieldMatcherID.byRegexp,
options: '/^\\d+$/',
};

const matcher = getFieldMatcher(config);

for (const field of seriesWithNames.fields) {
expect(matcher(field)).toBe(true);
expect(matcher(field, seriesWithNames, [seriesWithNames])).toBe(true);
}
});

Expand All @@ -40,17 +40,269 @@ describe('Field Name Matcher', () => {
fields: [{ name: 'some.instance.path' }, { name: '112' }, { name: '13' }],
});
const config = {
id: FieldMatcherID.byName,
id: FieldMatcherID.byRegexp,
options: '/\\b(?:\\S+?\\.)+\\S+\\b$/',
};

const matcher = getFieldMatcher(config);
let resultCount = 0;
for (const field of seriesWithNames.fields) {
if (matcher(field)) {
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 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: ['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: ['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: [],
};

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: ['some.instance.path', '112', '13'],
};

const matcher = getFieldMatcher(config);

for (const field of seriesWithNames.fields) {
expect(matcher(field, seriesWithNames, [seriesWithNames])).toBe(true);
}
});
});

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);
}
});
});

0 comments on commit 4f157ee

Please sign in to comment.