Allow singleton and flexible EntityFilters.
Signed-off-by: Joon Park <joonp@spotify.com>
This commit is contained in:
@@ -2,4 +2,4 @@
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Allow nested EntityFilters
|
||||
Allow singleton and flexibly nested EntityFilters
|
||||
|
||||
@@ -870,11 +870,14 @@ export type EntityAncestryResponse = {
|
||||
// Warning: (ae-missing-release-tag) "EntityFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public
|
||||
export type EntityFilter = {
|
||||
anyOf: {
|
||||
allOf: (EntitiesSearchFilter | EntityFilter)[];
|
||||
}[];
|
||||
};
|
||||
export type EntityFilter =
|
||||
| {
|
||||
allOf: EntityFilter[];
|
||||
}
|
||||
| {
|
||||
anyOf: EntityFilter[];
|
||||
}
|
||||
| EntitiesSearchFilter;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "EntityPagination" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
|
||||
@@ -22,9 +22,10 @@ import { Entity, EntityRelationSpec } from '@backstage/catalog-model';
|
||||
* Any (at least one) of the outer sets must match, within which all of the
|
||||
* individual filters must match.
|
||||
*/
|
||||
export type EntityFilter = {
|
||||
anyOf: { allOf: (EntitiesSearchFilter | EntityFilter)[] }[];
|
||||
};
|
||||
export type EntityFilter =
|
||||
| { allOf: EntityFilter[] }
|
||||
| { anyOf: EntityFilter[] }
|
||||
| EntitiesSearchFilter;
|
||||
|
||||
/**
|
||||
* A pagination rule for entities.
|
||||
|
||||
@@ -46,11 +46,11 @@ import {
|
||||
DbPageInfo,
|
||||
Transaction,
|
||||
} from './types';
|
||||
import {
|
||||
EntityPagination,
|
||||
EntityFilter,
|
||||
EntitiesSearchFilter,
|
||||
} from '../../catalog/types';
|
||||
import { EntityPagination, EntitiesSearchFilter } from '../../catalog/types';
|
||||
|
||||
type LegacyEntityFilter = {
|
||||
anyOf: { allOf: EntitiesSearchFilter[] }[];
|
||||
};
|
||||
|
||||
// The number of items that are sent per batch to the database layer, when
|
||||
// doing .batchInsert calls to knex. This needs to be low enough to not cause
|
||||
@@ -221,10 +221,23 @@ export class CommonDatabase implements Database {
|
||||
|
||||
let entitiesQuery = tx<DbEntitiesRow>('entities');
|
||||
|
||||
for (const singleFilter of request?.filter?.anyOf ?? []) {
|
||||
if (
|
||||
request?.filter &&
|
||||
(request.filter.hasOwnProperty('key') ||
|
||||
request.filter.hasOwnProperty('allOf'))
|
||||
) {
|
||||
throw new Error(
|
||||
'Filters for the legacy CommonDatabase must obey the { anyOf: [{ allOf: [] }] } format.',
|
||||
);
|
||||
}
|
||||
for (const singleFilter of (request?.filter as LegacyEntityFilter)?.anyOf ??
|
||||
[]) {
|
||||
entitiesQuery = entitiesQuery.orWhere(function singleFilterFn() {
|
||||
for (const filter of singleFilter.allOf) {
|
||||
if (isEntityFilter(filter)) {
|
||||
if (
|
||||
filter.hasOwnProperty('anyOf') ||
|
||||
filter.hasOwnProperty('allOf')
|
||||
) {
|
||||
throw new Error(
|
||||
'Nested filters are not supported in the legacy CommonDatabase',
|
||||
);
|
||||
@@ -612,9 +625,3 @@ function deduplicateRelations(
|
||||
r => `${r.source_full_name}:${r.target_full_name}:${r.type}`,
|
||||
);
|
||||
}
|
||||
|
||||
function isEntityFilter(
|
||||
filter: EntitiesSearchFilter | EntityFilter,
|
||||
): filter is EntityFilter {
|
||||
return filter.hasOwnProperty('anyOf');
|
||||
}
|
||||
|
||||
@@ -284,9 +284,7 @@ describe('NextEntitiesCatalog', () => {
|
||||
key: 'spec.test',
|
||||
matchValueExists: true,
|
||||
};
|
||||
const request = {
|
||||
filter: { anyOf: [{ allOf: [testFilter] }] },
|
||||
};
|
||||
const request = { filter: testFilter };
|
||||
const { entities } = await catalog.entities(request);
|
||||
|
||||
expect(entities.length).toBe(1);
|
||||
@@ -344,14 +342,10 @@ describe('NextEntitiesCatalog', () => {
|
||||
};
|
||||
const request = {
|
||||
filter: {
|
||||
anyOf: [
|
||||
allOf: [
|
||||
testFilter1,
|
||||
{
|
||||
allOf: [
|
||||
testFilter1,
|
||||
{
|
||||
anyOf: [{ allOf: [testFilter2] }, { allOf: [testFilter3] }],
|
||||
},
|
||||
],
|
||||
anyOf: [testFilter2, testFilter3],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -107,30 +107,56 @@ function addCondition(
|
||||
);
|
||||
}
|
||||
|
||||
function isEntityFilter(
|
||||
function isEntitiesSearchFilter(
|
||||
filter: EntitiesSearchFilter | EntityFilter,
|
||||
): filter is EntityFilter {
|
||||
): filter is EntitiesSearchFilter {
|
||||
return filter.hasOwnProperty('key');
|
||||
}
|
||||
|
||||
function isAndEntityFilter(
|
||||
filter: { allOf: EntityFilter[] } | EntityFilter,
|
||||
): filter is { allOf: EntityFilter[] } {
|
||||
return filter.hasOwnProperty('allOf');
|
||||
}
|
||||
|
||||
function isOrEntityFilter(
|
||||
filter: { anyOf: EntityFilter[] } | EntityFilter,
|
||||
): filter is { anyOf: EntityFilter[] } {
|
||||
return filter.hasOwnProperty('anyOf');
|
||||
}
|
||||
|
||||
function parseFilter(
|
||||
filters: EntityFilter,
|
||||
filter: EntityFilter,
|
||||
query: Knex.QueryBuilder,
|
||||
db: Knex,
|
||||
): Knex.QueryBuilder {
|
||||
let cumulativeQuery = query;
|
||||
for (const singleFilter of filters?.anyOf ?? []) {
|
||||
cumulativeQuery = cumulativeQuery.orWhere(function singleFilterFn() {
|
||||
for (const filter of singleFilter.allOf) {
|
||||
if (isEntityFilter(filter)) {
|
||||
this.andWhere(subQuery => parseFilter(filter, subQuery, db));
|
||||
} else {
|
||||
addCondition(this, db, filter);
|
||||
}
|
||||
}
|
||||
if (isEntitiesSearchFilter(filter)) {
|
||||
return query.where(function filterFunction() {
|
||||
addCondition(this, db, filter);
|
||||
});
|
||||
}
|
||||
return cumulativeQuery;
|
||||
|
||||
if (isOrEntityFilter(filter)) {
|
||||
let cumulativeQuery = query;
|
||||
for (const subFilter of filter.anyOf ?? []) {
|
||||
cumulativeQuery = cumulativeQuery.orWhere(subQuery =>
|
||||
parseFilter(subFilter, subQuery, db),
|
||||
);
|
||||
}
|
||||
return cumulativeQuery;
|
||||
}
|
||||
|
||||
if (isAndEntityFilter(filter)) {
|
||||
let cumulativeQuery = query;
|
||||
for (const subFilter of filter.allOf ?? []) {
|
||||
cumulativeQuery = cumulativeQuery.andWhere(subQuery =>
|
||||
parseFilter(subFilter, subQuery, db),
|
||||
);
|
||||
}
|
||||
return cumulativeQuery;
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
export class NextEntitiesCatalog implements EntitiesCatalog {
|
||||
|
||||
Reference in New Issue
Block a user