From b2a992650c416a9a0b406bae1b55981346593ce9 Mon Sep 17 00:00:00 2001 From: Patrik Ivarsson Date: Sun, 28 Jan 2024 16:35:38 +0100 Subject: [PATCH 1/3] Autocomplete picker: add argument for which filters should be applied when fetching/counting available values Signed-off-by: Patrik Ivarsson --- .changeset/lazy-pumas-sniff.md | 5 +++++ plugins/catalog-react/api-report.md | 1 + .../EntityAutocompletePicker.tsx | 15 +++++++++++++-- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 .changeset/lazy-pumas-sniff.md diff --git a/.changeset/lazy-pumas-sniff.md b/.changeset/lazy-pumas-sniff.md new file mode 100644 index 0000000000..997c318b30 --- /dev/null +++ b/.changeset/lazy-pumas-sniff.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +Adds an argument for which filters should be applied when fetching/counting available values diff --git a/plugins/catalog-react/api-report.md b/plugins/catalog-react/api-report.md index 39db26df74..20831da43f 100644 --- a/plugins/catalog-react/api-report.md +++ b/plugins/catalog-react/api-report.md @@ -204,6 +204,7 @@ export type EntityAutocompletePickerProps< }; InputProps?: TextFieldProps; initialSelectedOptions?: string[]; + filtersForAvailableValues?: Array; }; // @public diff --git a/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx b/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx index 1b7539b912..1a9f1f48c7 100644 --- a/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx +++ b/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx @@ -50,6 +50,7 @@ export type EntityAutocompletePickerProps< Filter: { new (values: string[]): NonNullable }; InputProps?: TextFieldProps; initialSelectedOptions?: string[]; + filtersForAvailableValues?: Array; }; /** @public */ @@ -76,6 +77,7 @@ export function EntityAutocompletePicker< Filter, InputProps, initialSelectedOptions = [], + filtersForAvailableValues = ['kind'], } = props; const classes = useStyles(); @@ -87,17 +89,26 @@ export function EntityAutocompletePicker< } = useEntityList(); const catalogApi = useApi(catalogApiRef); + const availableValuesFilters = filtersForAvailableValues.map( + f => filters[f] as EntityFilter | undefined, + ); const { value: availableValues } = useAsync(async () => { const facet = path; const { facets } = await catalogApi.getEntityFacets({ facets: [facet], - filter: filters.kind?.getCatalogFilters(), + filter: availableValuesFilters + .map(f => + f && typeof f.getCatalogFilters === 'function' + ? f.getCatalogFilters() + : {}, + ) + .reduce((a, b) => ({ ...a, ...b }), {}), }); return Object.fromEntries( facets[facet].map(({ value, count }) => [value, count]), ); - }, [filters.kind]); + }, [...availableValuesFilters]); const queryParameters = useMemo( () => [queryParameter].flat().filter(Boolean) as string[], From da9083cfb0fa226c796730c1b5ee41d17778f296 Mon Sep 17 00:00:00 2001 From: Patrik Ivarsson Date: Mon, 29 Jan 2024 09:08:41 +0100 Subject: [PATCH 2/3] use reduceBackendCatalogFilters Signed-off-by: Patrik Ivarsson --- .../EntityAutocompletePicker.tsx | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx b/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx index 1a9f1f48c7..fe3296a8a0 100644 --- a/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx +++ b/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx @@ -28,6 +28,7 @@ import { useEntityList, } from '../../hooks/useEntityListProvider'; import { EntityFilter } from '../../types'; +import { reduceBackendCatalogFilters } from '../../utils'; /** @public */ export type AllowedEntityFilters = { @@ -96,13 +97,9 @@ export function EntityAutocompletePicker< const facet = path; const { facets } = await catalogApi.getEntityFacets({ facets: [facet], - filter: availableValuesFilters - .map(f => - f && typeof f.getCatalogFilters === 'function' - ? f.getCatalogFilters() - : {}, - ) - .reduce((a, b) => ({ ...a, ...b }), {}), + filter: reduceBackendCatalogFilters( + availableValuesFilters.filter(Boolean) as EntityFilter[], + ), }); return Object.fromEntries( From a1711da81b4558e7a94db5495289f1499fa8ed26 Mon Sep 17 00:00:00 2001 From: Patrik Ivarsson Date: Wed, 31 Jan 2024 19:13:35 +0100 Subject: [PATCH 3/3] add tests verifying catalog backend is called with correct filters Signed-off-by: Patrik Ivarsson --- .../EntityAutocompletePicker.test.tsx | 81 ++++++++++++++++++- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.test.tsx b/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.test.tsx index 32e66dc578..94af52f374 100644 --- a/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.test.tsx @@ -24,6 +24,7 @@ import { CatalogApi } from '@backstage/catalog-client'; import { DefaultEntityFilters } from '../../hooks'; import { Entity } from '@backstage/catalog-model'; import { EntityFilter } from '../../types'; +import { EntityKindFilter, EntityTypeFilter } from '../../filters'; interface EntityFilters extends DefaultEntityFilters { options?: EntityOptionFilter; @@ -46,13 +47,17 @@ class EntityOptionFilter implements EntityFilter { } describe('', () => { - const mockCatalogApi = { - getEntityFacets: async () => ({ + const mockCatalogApi: Partial> = { + getEntityFacets: jest.fn().mockResolvedValue({ facets: { 'spec.options': options.map((value, idx) => ({ value, count: idx })), }, }), - } as unknown as CatalogApi; + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); it('renders all options', async () => { render( @@ -71,6 +76,12 @@ describe('', () => { expect(screen.getByText('Options')).toBeInTheDocument(), ); + // should have called catalog backend without any filters applied + expect(mockCatalogApi.getEntityFacets).toHaveBeenCalledWith({ + facets: ['spec.options'], + filter: {}, + }); + fireEvent.click(screen.getByTestId('options-picker-expand')); options.forEach(option => { expect(screen.getByText(option)).toBeInTheDocument(); @@ -267,4 +278,68 @@ describe('', () => { options: new EntityOptionFilter(['option2']), }); }); + + it('filters available values by kind as default', async () => { + render( + + + + label="Options" + path="spec.options" + name="options" + Filter={EntityOptionFilter} + /> + + , + ); + + await waitFor(() => + expect(mockCatalogApi.getEntityFacets).toHaveBeenCalledWith({ + facets: ['spec.options'], + filter: { + kind: 'Component', + }, + }), + ); + }); + + it('can be supplied with filters for available values', async () => { + render( + + + + label="Options" + path="spec.options" + name="options" + Filter={EntityOptionFilter} + filtersForAvailableValues={['kind', 'type']} + /> + + , + ); + + await waitFor(() => + expect(mockCatalogApi.getEntityFacets).toHaveBeenCalledWith({ + facets: ['spec.options'], + filter: { + kind: 'Component', + 'spec.type': ['service'], + }, + }), + ); + }); });