support non 'filter'-keyed filters in paginated backend requests
Signed-off-by: Aramis <sennyeyaramis@gmail.com>
This commit is contained in:
@@ -15,6 +15,8 @@ import { Entity } from '@backstage/catalog-model';
|
||||
import { IconButton } from '@material-ui/core';
|
||||
import { IconComponent } from '@backstage/core-plugin-api';
|
||||
import { InfoCardVariants } from '@backstage/core-components';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import { LinkProps } from '@backstage/core-components';
|
||||
import { Observable } from '@backstage/types';
|
||||
import { Overrides } from '@material-ui/core/styles/overrides';
|
||||
@@ -217,6 +219,7 @@ export type EntityFilter = {
|
||||
string,
|
||||
string | symbol | (string | symbol)[]
|
||||
>;
|
||||
getBackendRequestParameter?: () => Record<string, JsonObject | JsonValue>;
|
||||
filterEntity?: (entity: Entity) => boolean;
|
||||
toQueryValue?: () => string | string[];
|
||||
};
|
||||
@@ -541,6 +544,13 @@ export class EntityTextFilter implements EntityFilter {
|
||||
// (undocumented)
|
||||
filterEntity(entity: Entity): boolean;
|
||||
// (undocumented)
|
||||
getBackendRequestParameter(): {
|
||||
fullTextFilter: {
|
||||
term: string;
|
||||
fields: string[];
|
||||
};
|
||||
};
|
||||
// (undocumented)
|
||||
readonly value: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,13 +30,13 @@ export function useAllEntitiesCount() {
|
||||
const request = useMemo(() => {
|
||||
const { user, ...allFilters } = filters;
|
||||
const compacted = compact(Object.values(allFilters));
|
||||
const filter = reduceCatalogFilters(compacted);
|
||||
const catalogFilters = reduceCatalogFilters(compacted);
|
||||
const newRequest: QueryEntitiesInitialRequest = {
|
||||
filter,
|
||||
...catalogFilters,
|
||||
limit: 0,
|
||||
};
|
||||
|
||||
if (Object.keys(filter).length === 0) {
|
||||
if (Object.keys(catalogFilters).length === 0) {
|
||||
prevRequest.current = undefined;
|
||||
return prevRequest.current;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import useAsync from 'react-use/lib/useAsync';
|
||||
import { catalogApiRef } from '../../api';
|
||||
import { EntityOwnerFilter, EntityUserFilter } from '../../filters';
|
||||
import { useEntityList } from '../../hooks';
|
||||
import { reduceCatalogFilters } from '../../utils';
|
||||
import { CatalogFilters, reduceCatalogFilters } from '../../utils';
|
||||
import useAsyncFn from 'react-use/lib/useAsyncFn';
|
||||
import useDeepCompareEffect from 'react-use/lib/useDeepCompareEffect';
|
||||
|
||||
@@ -38,7 +38,7 @@ export function useOwnedEntitiesCount() {
|
||||
);
|
||||
|
||||
const { user, owners, ...allFilters } = filters;
|
||||
const { ['metadata.name']: metadata, ...filter } = reduceCatalogFilters(
|
||||
const catalogFilters = reduceCatalogFilters(
|
||||
compact(Object.values(allFilters)),
|
||||
);
|
||||
|
||||
@@ -47,7 +47,7 @@ export function useOwnedEntitiesCount() {
|
||||
async (req: {
|
||||
ownershipEntityRefs: string[];
|
||||
owners: EntityOwnerFilter | undefined;
|
||||
filter: Record<string, string | symbol | (string | symbol)[]>;
|
||||
filter: CatalogFilters;
|
||||
}) => {
|
||||
const ownedClaims = getOwnedCountClaims(
|
||||
req.owners,
|
||||
@@ -60,9 +60,12 @@ export function useOwnedEntitiesCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const { ['metadata.name']: metadata, ...filter } = req.filter.filter;
|
||||
|
||||
const { totalItems } = await catalogApi.queryEntities({
|
||||
...req.filter,
|
||||
filter: {
|
||||
...req.filter,
|
||||
...filter,
|
||||
'relations.ownedBy': ownedClaims,
|
||||
},
|
||||
limit: 0,
|
||||
@@ -75,15 +78,19 @@ export function useOwnedEntitiesCount() {
|
||||
|
||||
useDeepCompareEffect(() => {
|
||||
// context contains no filter, wait
|
||||
if (Object.keys(filter).length === 0) {
|
||||
if (Object.keys(catalogFilters).length === 0) {
|
||||
return;
|
||||
}
|
||||
// ownershipEntityRefs is loading, wait
|
||||
if (ownershipEntityRefs === undefined) {
|
||||
return;
|
||||
}
|
||||
fetchEntities({ ownershipEntityRefs, owners, filter });
|
||||
}, [ownershipEntityRefs, owners, filter]);
|
||||
fetchEntities({
|
||||
ownershipEntityRefs,
|
||||
owners,
|
||||
filter: catalogFilters,
|
||||
});
|
||||
}, [ownershipEntityRefs, owners, catalogFilters]);
|
||||
|
||||
const loading = loadingEntityRefs || loadingEntityOwnership;
|
||||
|
||||
|
||||
@@ -34,13 +34,14 @@ export function useStarredEntitiesCount() {
|
||||
const request = useMemo(() => {
|
||||
const { user, ...allFilters } = filters;
|
||||
const compacted = compact(Object.values(allFilters));
|
||||
const filter = reduceCatalogFilters(compacted);
|
||||
const catalogFilters = reduceCatalogFilters(compacted);
|
||||
|
||||
const facet = 'metadata.name';
|
||||
|
||||
const newRequest: QueryEntitiesInitialRequest = {
|
||||
...catalogFilters,
|
||||
filter: {
|
||||
...filter,
|
||||
...catalogFilters.filter,
|
||||
/**
|
||||
* here we are filtering entities by `name`. Given this filter,
|
||||
* the response might contain more entities than expected, in case multiple entities
|
||||
|
||||
@@ -108,6 +108,16 @@ export class EntityTextFilter implements EntityFilter {
|
||||
return true;
|
||||
}
|
||||
|
||||
getBackendRequestParameter() {
|
||||
return {
|
||||
fullTextFilter: {
|
||||
term: this.value,
|
||||
// Update this to be more dynamic based on table columns.
|
||||
fields: ['metadata.name', 'metadata.title'],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private toUpperArray(
|
||||
value: Array<string | string[] | undefined>,
|
||||
): Array<string> {
|
||||
|
||||
@@ -338,6 +338,7 @@ describe('<EntityListProvider pagination />', () => {
|
||||
orderFields,
|
||||
fullTextFilter: {
|
||||
term: '2',
|
||||
fields: ['metadata.name', 'metadata.title'],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -233,17 +233,9 @@ export const EntityListProvider = <EntityFilters extends DefaultEntityFilters>(
|
||||
compact(Object.values(outputState.appliedFilters)),
|
||||
);
|
||||
|
||||
if (
|
||||
!isEqual(previousBackendFilter, backendFilter) ||
|
||||
requestedFilters.text?.value !== outputState.textSearch
|
||||
) {
|
||||
if (!isEqual(previousBackendFilter, backendFilter)) {
|
||||
const response = await catalogApi.queryEntities({
|
||||
filter: backendFilter,
|
||||
fullTextFilter: requestedFilters.text
|
||||
? {
|
||||
term: requestedFilters.text.value,
|
||||
}
|
||||
: undefined,
|
||||
...backendFilter,
|
||||
limit,
|
||||
orderFields: [{ field: 'metadata.name', order: 'asc' }],
|
||||
});
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { JsonObject, JsonValue } from '@backstage/types';
|
||||
|
||||
/** @public */
|
||||
export type EntityFilter = {
|
||||
@@ -29,6 +30,13 @@ export type EntityFilter = {
|
||||
string | symbol | (string | symbol)[]
|
||||
>;
|
||||
|
||||
/**
|
||||
* A superset of `getCatalogFilters` that supports filtering based on parameters outside of the
|
||||
* catalog filter system. Examples include `fullTextFilter` or `orderFields`.
|
||||
* @returns
|
||||
*/
|
||||
getBackendRequestParameter?: () => Record<string, JsonObject | JsonValue>;
|
||||
|
||||
/**
|
||||
* Filter entities on the frontend after a catalog-backend request. This function will be called
|
||||
* with each backend-resolved entity. This is used when frontend information is required for
|
||||
|
||||
@@ -26,16 +26,29 @@ import {
|
||||
EntityUserFilter,
|
||||
UserListFilter,
|
||||
} from '../filters';
|
||||
import { merge } from 'lodash';
|
||||
|
||||
export function reduceCatalogFilters(
|
||||
filters: EntityFilter[],
|
||||
): Record<string, string | symbol | (string | symbol)[]> {
|
||||
export interface CatalogFilters {
|
||||
[x: string]:
|
||||
| string
|
||||
| Record<string, string | symbol | (string | symbol)[]>
|
||||
| undefined;
|
||||
filter: Record<string, string | symbol | (string | symbol)[]>;
|
||||
fullTextFilter?: {
|
||||
term: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function reduceCatalogFilters(filters: EntityFilter[]): CatalogFilters {
|
||||
return filters.reduce((compoundFilter, filter) => {
|
||||
return {
|
||||
...compoundFilter,
|
||||
...(filter.getCatalogFilters ? filter.getCatalogFilters() : {}),
|
||||
};
|
||||
}, {} as Record<string, string | symbol | (string | symbol)[]>);
|
||||
const addedFilters = {};
|
||||
if (filter.getBackendRequestParameter) {
|
||||
Object.assign(addedFilters, filter.getBackendRequestParameter());
|
||||
} else if (filter.getCatalogFilters) {
|
||||
Object.assign(addedFilters, { filter: filter.getCatalogFilters() });
|
||||
}
|
||||
return merge(compoundFilter, addedFilters);
|
||||
}, {} as CatalogFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user