diff --git a/plugins/catalog-react/api-report.md b/plugins/catalog-react/api-report.md index 8c6b3a729f..2d6152fb07 100644 --- a/plugins/catalog-react/api-report.md +++ b/plugins/catalog-react/api-report.md @@ -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; 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; } diff --git a/plugins/catalog-react/src/components/UserListPicker/useAllEntitiesCount.ts b/plugins/catalog-react/src/components/UserListPicker/useAllEntitiesCount.ts index ba971b088d..dae6df55ee 100644 --- a/plugins/catalog-react/src/components/UserListPicker/useAllEntitiesCount.ts +++ b/plugins/catalog-react/src/components/UserListPicker/useAllEntitiesCount.ts @@ -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; } diff --git a/plugins/catalog-react/src/components/UserListPicker/useOwnedEntitiesCount.ts b/plugins/catalog-react/src/components/UserListPicker/useOwnedEntitiesCount.ts index 2f03a0b285..1fc147daf2 100644 --- a/plugins/catalog-react/src/components/UserListPicker/useOwnedEntitiesCount.ts +++ b/plugins/catalog-react/src/components/UserListPicker/useOwnedEntitiesCount.ts @@ -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; + 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; diff --git a/plugins/catalog-react/src/components/UserListPicker/useStarredEntitiesCount.ts b/plugins/catalog-react/src/components/UserListPicker/useStarredEntitiesCount.ts index f7b2b101f3..66c0dd97e5 100644 --- a/plugins/catalog-react/src/components/UserListPicker/useStarredEntitiesCount.ts +++ b/plugins/catalog-react/src/components/UserListPicker/useStarredEntitiesCount.ts @@ -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 diff --git a/plugins/catalog-react/src/filters.ts b/plugins/catalog-react/src/filters.ts index 55983d124b..5f1324f6cf 100644 --- a/plugins/catalog-react/src/filters.ts +++ b/plugins/catalog-react/src/filters.ts @@ -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, ): Array { diff --git a/plugins/catalog-react/src/hooks/useEntityListProvider.test.tsx b/plugins/catalog-react/src/hooks/useEntityListProvider.test.tsx index 4ba5f51503..d5208d4f10 100644 --- a/plugins/catalog-react/src/hooks/useEntityListProvider.test.tsx +++ b/plugins/catalog-react/src/hooks/useEntityListProvider.test.tsx @@ -338,6 +338,7 @@ describe('', () => { orderFields, fullTextFilter: { term: '2', + fields: ['metadata.name', 'metadata.title'], }, }); }); diff --git a/plugins/catalog-react/src/hooks/useEntityListProvider.tsx b/plugins/catalog-react/src/hooks/useEntityListProvider.tsx index 81bf8fb9e3..68b3fff735 100644 --- a/plugins/catalog-react/src/hooks/useEntityListProvider.tsx +++ b/plugins/catalog-react/src/hooks/useEntityListProvider.tsx @@ -233,17 +233,9 @@ export const EntityListProvider = ( 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' }], }); diff --git a/plugins/catalog-react/src/types.ts b/plugins/catalog-react/src/types.ts index 41c7d75f0d..161398609d 100644 --- a/plugins/catalog-react/src/types.ts +++ b/plugins/catalog-react/src/types.ts @@ -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; + /** * 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 diff --git a/plugins/catalog-react/src/utils/filters.ts b/plugins/catalog-react/src/utils/filters.ts index 580d7b0b76..ad597c08e7 100644 --- a/plugins/catalog-react/src/utils/filters.ts +++ b/plugins/catalog-react/src/utils/filters.ts @@ -26,16 +26,29 @@ import { EntityUserFilter, UserListFilter, } from '../filters'; +import { merge } from 'lodash'; -export function reduceCatalogFilters( - filters: EntityFilter[], -): Record { +export interface CatalogFilters { + [x: string]: + | string + | Record + | undefined; + filter: Record; + fullTextFilter?: { + term: string; + }; +} + +export function reduceCatalogFilters(filters: EntityFilter[]): CatalogFilters { return filters.reduce((compoundFilter, filter) => { - return { - ...compoundFilter, - ...(filter.getCatalogFilters ? filter.getCatalogFilters() : {}), - }; - }, {} as Record); + 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); } /**