Merge pull request #26366 from SnowBlitzer/jordans/addFilterNegation
Negation key word to entity filter
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': minor
|
||||
---
|
||||
|
||||
Adding negation keyword for entity filtering
|
||||
@@ -93,6 +93,20 @@ describe('parseFilterExpression', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('recognizes negation key', () => {
|
||||
const component = { kind: 'Component' } as unknown as Entity;
|
||||
expect(run('not:kind:user')(component)).toBe(true);
|
||||
});
|
||||
|
||||
it('supports negation and affirmative expressions', () => {
|
||||
const component = {
|
||||
kind: 'Component',
|
||||
spec: { type: 'service' },
|
||||
} as unknown as Entity;
|
||||
expect(run('not:kind:user type:service')(component)).toBe(true);
|
||||
expect(run('type:service not:kind:user')(component)).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects unknown keys', () => {
|
||||
expect(() => run('unknown:foo')).toThrowErrorMatchingInlineSnapshot(
|
||||
`"'unknown' is not a valid filter expression key, expected one of 'kind','type','is','has'"`,
|
||||
@@ -123,17 +137,25 @@ describe('splitFilterExpression', () => {
|
||||
expect(run('')).toEqual([]);
|
||||
expect(run(' ')).toEqual([]);
|
||||
expect(run('kind:component')).toEqual([
|
||||
{ key: 'kind', parameters: ['component'] },
|
||||
{ key: 'kind', parameters: ['component'], negation: false },
|
||||
]);
|
||||
expect(run('kind:component,user')).toEqual([
|
||||
{ key: 'kind', parameters: ['component', 'user'] },
|
||||
{ key: 'kind', parameters: ['component', 'user'], negation: false },
|
||||
]);
|
||||
expect(run('kind:component,user not:type:foo')).toEqual([
|
||||
{ key: 'kind', parameters: ['component', 'user'], negation: false },
|
||||
{ key: 'type', parameters: ['foo'], negation: true },
|
||||
]);
|
||||
expect(run('not:type:foo kind:component,user')).toEqual([
|
||||
{ key: 'type', parameters: ['foo'], negation: true },
|
||||
{ key: 'kind', parameters: ['component', 'user'], negation: false },
|
||||
]);
|
||||
expect(run('kind:component,user type:foo')).toEqual([
|
||||
{ key: 'kind', parameters: ['component', 'user'] },
|
||||
{ key: 'type', parameters: ['foo'] },
|
||||
{ key: 'kind', parameters: ['component', 'user'], negation: false },
|
||||
{ key: 'type', parameters: ['foo'], negation: false },
|
||||
]);
|
||||
expect(run('with:multiple:colons')).toEqual([
|
||||
{ key: 'with', parameters: ['multiple:colons'] },
|
||||
{ key: 'with', parameters: ['multiple:colons'], negation: false },
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ const rootMatcherFactories: Record<
|
||||
(
|
||||
parameters: string[],
|
||||
onParseError: (error: Error) => void,
|
||||
negation?: boolean,
|
||||
) => EntityMatcherFn
|
||||
> = {
|
||||
kind: createKindMatcher,
|
||||
@@ -60,9 +61,9 @@ export function parseFilterExpression(expression: string): {
|
||||
const parts = splitFilterExpression(expression, e =>
|
||||
expressionParseErrors.push(e),
|
||||
);
|
||||
|
||||
const matchers = parts.flatMap(part => {
|
||||
const factory = rootMatcherFactories[part.key];
|
||||
const negation = part.negation;
|
||||
if (!factory) {
|
||||
const known = Object.keys(rootMatcherFactories).map(m => `'${m}'`);
|
||||
expressionParseErrors.push(
|
||||
@@ -76,7 +77,8 @@ export function parseFilterExpression(expression: string): {
|
||||
const matcher = factory(part.parameters, e =>
|
||||
expressionParseErrors.push(e),
|
||||
);
|
||||
return [matcher];
|
||||
|
||||
return [negation ? (entity: Entity) => !matcher(entity) : matcher];
|
||||
});
|
||||
|
||||
const filterFn = (entity: Entity) =>
|
||||
@@ -97,16 +99,20 @@ export function parseFilterExpression(expression: string): {
|
||||
export function splitFilterExpression(
|
||||
expression: string,
|
||||
onParseError: (error: Error) => void,
|
||||
): Array<{ key: string; parameters: string[] }> {
|
||||
): Array<{ key: string; parameters: string[]; negation: boolean }> {
|
||||
const words = expression
|
||||
.split(' ')
|
||||
.map(w => w.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
const result = new Array<{ key: string; parameters: string[] }>();
|
||||
const result = new Array<{
|
||||
key: string;
|
||||
parameters: string[];
|
||||
negation: boolean;
|
||||
}>();
|
||||
|
||||
for (const word of words) {
|
||||
const match = word.match(/^([^:]+):(.+)$/);
|
||||
const match = word.match(/^(not:)?([^:]+):(.+)$/);
|
||||
if (!match) {
|
||||
onParseError(
|
||||
new InputError(
|
||||
@@ -115,11 +121,10 @@ export function splitFilterExpression(
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const key = match[1];
|
||||
const parameters = match[2].split(',').filter(Boolean); // silently ignore double commas
|
||||
|
||||
result.push({ key, parameters });
|
||||
const key = match[2];
|
||||
const parameters = match[3].split(',').filter(Boolean); // silently ignore double commas
|
||||
const negation = Boolean(match[1]);
|
||||
result.push({ key, parameters, negation });
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
Reference in New Issue
Block a user