From 64b6492f9e049b3202ccda7afc139ca90ed3d86c Mon Sep 17 00:00:00 2001 From: Jordan Snow Date: Wed, 2 Oct 2024 17:00:29 -0400 Subject: [PATCH] Changing how negation key word is processed Signed-off-by: Jordan Snow --- .../alpha/filter/matchers/createHasMatcher.ts | 5 +- .../alpha/filter/matchers/createIsMatcher.ts | 4 +- .../filter/matchers/createKindMatcher.ts | 5 +- .../filter/matchers/createTypeMatcher.ts | 7 ++- .../filter/parseFilterExpression.test.ts | 14 ++++-- .../src/alpha/filter/parseFilterExpression.ts | 49 +++++++------------ 6 files changed, 34 insertions(+), 50 deletions(-) diff --git a/plugins/catalog/src/alpha/filter/matchers/createHasMatcher.ts b/plugins/catalog/src/alpha/filter/matchers/createHasMatcher.ts index a34834e40d..9fd393a561 100644 --- a/plugins/catalog/src/alpha/filter/matchers/createHasMatcher.ts +++ b/plugins/catalog/src/alpha/filter/matchers/createHasMatcher.ts @@ -16,7 +16,6 @@ import { InputError } from '@backstage/errors'; import { EntityMatcherFn } from './types'; -import { Entity } from '@backstage/catalog-model'; const allowedMatchers: Record = { labels: entity => { @@ -33,7 +32,6 @@ const allowedMatchers: Record = { export function createHasMatcher( parameters: string[], onParseError: (error: Error) => void, - negation?: boolean, ): EntityMatcherFn { const matchers = parameters.flatMap(parameter => { const matcher = allowedMatchers[parameter.toLocaleLowerCase('en-US')]; @@ -49,7 +47,6 @@ export function createHasMatcher( return [matcher]; }); - const isMatch = (entity: Entity) => + return entity => matchers.length ? matchers.some(matcher => matcher(entity)) : true; - return negation ? entity => !isMatch(entity) : isMatch; } diff --git a/plugins/catalog/src/alpha/filter/matchers/createIsMatcher.ts b/plugins/catalog/src/alpha/filter/matchers/createIsMatcher.ts index 5df4f2ffe8..49a54349d1 100644 --- a/plugins/catalog/src/alpha/filter/matchers/createIsMatcher.ts +++ b/plugins/catalog/src/alpha/filter/matchers/createIsMatcher.ts @@ -28,7 +28,6 @@ const allowedMatchers: Record = { export function createIsMatcher( parameters: string[], onParseError: (error: Error) => void, - negation?: boolean, ): EntityMatcherFn { const matchers = parameters.flatMap(parameter => { const matcher = allowedMatchers[parameter.toLocaleLowerCase('en-US')]; @@ -44,7 +43,6 @@ export function createIsMatcher( return [matcher]; }); - const isMatch = (entity: any) => + return entity => matchers.length ? matchers.some(matcher => matcher(entity)) : true; - return negation ? entity => !isMatch(entity) : isMatch; } diff --git a/plugins/catalog/src/alpha/filter/matchers/createKindMatcher.ts b/plugins/catalog/src/alpha/filter/matchers/createKindMatcher.ts index 6d7a3ff162..4f7c475ebe 100644 --- a/plugins/catalog/src/alpha/filter/matchers/createKindMatcher.ts +++ b/plugins/catalog/src/alpha/filter/matchers/createKindMatcher.ts @@ -22,10 +22,7 @@ import { EntityMatcherFn } from './types'; export function createKindMatcher( parameters: string[], _onParseError: (error: Error) => void, - negation?: boolean, ): EntityMatcherFn { const items = parameters.map(p => p.toLocaleLowerCase('en-US')); - const isMatch = (entity: any) => - items.includes(entity.kind.toLocaleLowerCase('en-US')); - return negation ? entity => !isMatch(entity) : isMatch; + return entity => items.includes(entity.kind.toLocaleLowerCase('en-US')); } diff --git a/plugins/catalog/src/alpha/filter/matchers/createTypeMatcher.ts b/plugins/catalog/src/alpha/filter/matchers/createTypeMatcher.ts index 7bd7e56dfe..31f7a5a461 100644 --- a/plugins/catalog/src/alpha/filter/matchers/createTypeMatcher.ts +++ b/plugins/catalog/src/alpha/filter/matchers/createTypeMatcher.ts @@ -22,14 +22,13 @@ import { EntityMatcherFn } from './types'; export function createTypeMatcher( parameters: string[], _onParseError: (error: Error) => void, - negation?: boolean, ): EntityMatcherFn { const items = parameters.map(p => p.toLocaleLowerCase('en-US')); return entity => { const value = entity.spec?.type; - const isMatch = + return ( typeof value === 'string' && - items.includes(value.toLocaleLowerCase('en-US')); - return negation ? !isMatch : isMatch; + items.includes(value.toLocaleLowerCase('en-US')) + ); }; } diff --git a/plugins/catalog/src/alpha/filter/parseFilterExpression.test.ts b/plugins/catalog/src/alpha/filter/parseFilterExpression.test.ts index 7ebadde13a..647a4ef824 100644 --- a/plugins/catalog/src/alpha/filter/parseFilterExpression.test.ts +++ b/plugins/catalog/src/alpha/filter/parseFilterExpression.test.ts @@ -137,17 +137,21 @@ 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('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 }, ]); }); diff --git a/plugins/catalog/src/alpha/filter/parseFilterExpression.ts b/plugins/catalog/src/alpha/filter/parseFilterExpression.ts index 906fa45367..71729c6a24 100644 --- a/plugins/catalog/src/alpha/filter/parseFilterExpression.ts +++ b/plugins/catalog/src/alpha/filter/parseFilterExpression.ts @@ -61,14 +61,10 @@ export function parseFilterExpression(expression: string): { const parts = splitFilterExpression(expression, e => expressionParseErrors.push(e), ); - let negation = false; const matchers = parts.flatMap(part => { const factory = rootMatcherFactories[part.key]; + const negation = part.negation; if (!factory) { - if (isNegation(part.key)) { - negation = true; - return []; - } const known = Object.keys(rootMatcherFactories).map(m => `'${m}'`); expressionParseErrors.push( new InputError( @@ -78,13 +74,11 @@ export function parseFilterExpression(expression: string): { return []; } - const matcher = factory( - part.parameters, - e => expressionParseErrors.push(e), - negation, + const matcher = factory(part.parameters, e => + expressionParseErrors.push(e), ); - negation = false; - return [matcher]; + + return [negation ? (entity: Entity) => !matcher(entity) : matcher]; }); const filterFn = (entity: Entity) => @@ -105,20 +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) { - let match = word.match(/^([^:]+):(.+)$/); - if (match && isNegation(match[1])) { - result.push({ key: 'not', parameters: [] }); - match = match[2].match(/^([^:]+):(.+)$/) || null; - } + const match = word.match(/^(not:)?([^:]+):(.+)$/); if (!match) { onParseError( new InputError( @@ -127,19 +121,14 @@ 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 + if (match[1]) { + result.push({ key, parameters, negation: true }); + } else { + result.push({ key, parameters, negation: false }); + } } return result; } - -function isNegation(parameter: string): boolean { - if (parameter.toLocaleLowerCase('en-US') === 'not') { - return true; - } - return false; -}