@backstage/plugin-catalog-react 3.2.4-next.0 → 3.3.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # @backstage/plugin-catalog-react
2
2
 
3
+ ## 3.3.0-next.2
4
+
5
+ ### Minor Changes
6
+
7
+ - 6c74f3f: Added `getCatalogQuery` to `EntityFilter`, which lets filters express their backend constraints as a filter predicate. This supports logical operators such as `$any` and `$not`, as well as matchers like `$hasPrefix`, that cannot be expressed with `getCatalogFilters`. All builtin filters now implement it, and `getCatalogFilters` is deprecated. If a filter implements both, only `getCatalogQuery` is used.
8
+
9
+ The `EntityListProvider` and related components combine the predicates of all active filters into a `query`, and send it to the catalog backend alongside a `filter` built from any remaining legacy filters. When not paginating, entities are now fetched using `streamEntities` instead of `getEntities`, since the latter does not support predicate queries. Tests that mock `getEntities` to provide entities to an `EntityListProvider` need to mock `streamEntities` instead.
10
+
11
+ This requires a catalog backend from Backstage release 1.49 or later, which added support for predicate queries.
12
+
13
+ Since entities are now fetched using `queryEntities` also when not paginating, an `EntityOrderFilter` that orders by a field that some entities lack now excludes those entities from the result, consistent with paginated mode. Previously they were placed last.
14
+
15
+ Some builtin filters became more precise as part of this change:
16
+
17
+ - `EntityTagFilter` with multiple tags now requires all of them on the backend, matching its frontend behavior.
18
+ - `EntityUserFilter.starred` matches the full starred entity refs rather than just their names, and the starred count is computed by the backend.
19
+
20
+ ### Patch Changes
21
+
22
+ - Updated dependencies
23
+ - @backstage/frontend-plugin-api@0.18.2-next.1
24
+ - @backstage/catalog-client@1.16.3-next.1
25
+ - @backstage/core-components@0.18.15-next.3
26
+ - @backstage/ui@0.19.0-next.0
27
+ - @backstage/core-compat-api@0.5.16-next.1
28
+
29
+ ## 3.2.4-next.1
30
+
31
+ ### Patch Changes
32
+
33
+ - c162b94: Fixed the catalog entity list re-writing the URL on every render, which spammed `history.replaceState` and crashed Safari with a `SecurityError`. The URL is now only updated when it actually changes.
34
+ - Updated dependencies
35
+ - @backstage/core-components@0.18.15-next.2
36
+
3
37
  ## 3.2.4-next.0
4
38
 
5
39
  ### Patch Changes
@@ -49,7 +49,7 @@ function EntityAutocompletePicker(props) {
49
49
  const facet = path;
50
50
  const { facets } = await catalogApi.getEntityFacets({
51
51
  facets: [facet],
52
- filter: reduceBackendCatalogFilters(
52
+ ...reduceBackendCatalogFilters(
53
53
  availableValuesFilters.filter(Boolean)
54
54
  )
55
55
  });
@@ -1 +1 @@
1
- {"version":3,"file":"EntityAutocompletePicker.esm.js","sources":["../../../src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport Box from '@material-ui/core/Box';\nimport { TextFieldProps } from '@material-ui/core/TextField';\nimport { makeStyles } from '@material-ui/core/styles';\nimport { ReactNode, useEffect, useMemo, useState } from 'react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport useAsync from 'react-use/esm/useAsync';\nimport { catalogApiRef } from '../../api';\nimport { EntityAutocompletePickerOption } from './EntityAutocompletePickerOption';\nimport {\n DefaultEntityFilters,\n useEntityList,\n} from '../../hooks/useEntityListProvider';\nimport { EntityFilter } from '../../types';\nimport { reduceBackendCatalogFilters } from '../../utils/filters';\nimport { CatalogAutocomplete } from '../CatalogAutocomplete';\nimport { AutocompleteRenderOptionState } from '@material-ui/lab/Autocomplete';\nimport { isEqual } from 'lodash';\n\n/** @public */\nexport type AllowedEntityFilters<T extends DefaultEntityFilters> = {\n [K in keyof T]-?: NonNullable<T[K]> extends EntityFilter & {\n values: string[];\n }\n ? K\n : never;\n}[keyof T];\n\n/** @public */\nexport type EntityAutocompletePickerProps<\n T extends DefaultEntityFilters = DefaultEntityFilters,\n Name extends AllowedEntityFilters<T> = AllowedEntityFilters<T>,\n> = {\n label: string;\n name: Name;\n path: string;\n showCounts?: boolean;\n Filter: { new (values: string[]): NonNullable<T[Name]> };\n InputProps?: TextFieldProps;\n initialSelectedOptions?: string[];\n filtersForAvailableValues?: Array<keyof T>;\n hidden?: boolean;\n getOptionLabel?: (option: string) => string;\n renderOption?: (\n option: string,\n state: AutocompleteRenderOptionState,\n ) => ReactNode;\n};\n\n/** @public */\nexport type CatalogReactEntityAutocompletePickerClassKey = 'root' | 'label';\n\nconst useStyles = makeStyles(\n {\n root: {},\n label: {\n textTransform: 'none',\n fontWeight: 'bold',\n },\n },\n { name: 'CatalogReactEntityAutocompletePicker' },\n);\n\n/** @public */\nexport function EntityAutocompletePicker<\n T extends DefaultEntityFilters = DefaultEntityFilters,\n Name extends AllowedEntityFilters<T> = AllowedEntityFilters<T>,\n>(props: EntityAutocompletePickerProps<T, Name>) {\n const {\n label,\n name,\n path,\n showCounts,\n Filter,\n InputProps,\n initialSelectedOptions = [],\n filtersForAvailableValues = ['kind'],\n hidden,\n getOptionLabel,\n renderOption,\n } = props;\n const classes = useStyles();\n\n const {\n updateFilters,\n filters,\n queryParameters: { [name]: queryParameter },\n } = useEntityList<T>();\n\n const catalogApi = useApi(catalogApiRef);\n const availableValuesFilters = filtersForAvailableValues.map(\n f => filters[f] as EntityFilter | undefined,\n );\n const { value: availableValues } = useAsync(async () => {\n const facet = path;\n const { facets } = await catalogApi.getEntityFacets({\n facets: [facet],\n filter: reduceBackendCatalogFilters(\n availableValuesFilters.filter(Boolean) as EntityFilter[],\n ),\n });\n\n return Object.fromEntries(\n facets[facet].map(({ value, count }) => [value, count]),\n );\n }, [...availableValuesFilters]);\n\n const queryParameters = useMemo(\n () => [queryParameter].flat().filter(Boolean) as string[],\n [queryParameter],\n );\n\n const filteredOptions = (filters[name] as unknown as { values: string[] })\n ?.values;\n\n const [selectedOptions, setSelectedOptions] = useState(\n queryParameters.length\n ? queryParameters\n : filteredOptions ?? initialSelectedOptions,\n );\n\n // Set selected options on query parameter updates; this happens at initial page load and from\n // external updates to the page location\n useEffect(() => {\n if (queryParameters.length) {\n setSelectedOptions(queryParameters);\n }\n }, [queryParameters]);\n\n const availableOptions = Object.keys(availableValues ?? {});\n const shouldAddFilter = selectedOptions.length && availableOptions.length;\n\n // Update filter value when selectedOptions change\n useEffect(() => {\n updateFilters({\n [name]: shouldAddFilter ? new Filter(selectedOptions) : undefined,\n } as Partial<T>);\n }, [name, shouldAddFilter, selectedOptions, Filter, updateFilters]);\n\n // Update selected options when filter value changes\n useEffect(() => {\n if (!shouldAddFilter) return;\n\n const newSelectedOptions = filteredOptions ?? [];\n\n // Check value is actually different (not just a different reference) to prevent selectedOptions <> filters loop\n if (!isEqual(newSelectedOptions, selectedOptions)) {\n setSelectedOptions(newSelectedOptions);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- Don't re-set filter value when selectedOptions changes\n }, [filteredOptions]);\n\n const filter = filters[name];\n if (\n (filter && typeof filter === 'object' && !('values' in filter)) ||\n !availableOptions.length\n ) {\n return null;\n }\n\n return hidden ? null : (\n <Box className={classes.root} pb={1} pt={1}>\n <CatalogAutocomplete<string, true>\n multiple\n disableCloseOnSelect\n label={label}\n name={`${String(name)}-picker`}\n options={availableOptions}\n value={selectedOptions}\n TextFieldProps={InputProps}\n onChange={(_event: object, options: string[]) =>\n setSelectedOptions(options)\n }\n {...(getOptionLabel && { getOptionLabel })}\n renderOption={\n renderOption ??\n ((option, { selected }) => (\n <EntityAutocompletePickerOption\n selected={selected}\n value={option}\n availableOptions={availableValues}\n showCounts={!!showCounts}\n />\n ))\n }\n />\n </Box>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"EntityAutocompletePicker.esm.js","sources":["../../../src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport Box from '@material-ui/core/Box';\nimport { TextFieldProps } from '@material-ui/core/TextField';\nimport { makeStyles } from '@material-ui/core/styles';\nimport { ReactNode, useEffect, useMemo, useState } from 'react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport useAsync from 'react-use/esm/useAsync';\nimport { catalogApiRef } from '../../api';\nimport { EntityAutocompletePickerOption } from './EntityAutocompletePickerOption';\nimport {\n DefaultEntityFilters,\n useEntityList,\n} from '../../hooks/useEntityListProvider';\nimport { EntityFilter } from '../../types';\nimport { reduceBackendCatalogFilters } from '../../utils/filters';\nimport { CatalogAutocomplete } from '../CatalogAutocomplete';\nimport { AutocompleteRenderOptionState } from '@material-ui/lab/Autocomplete';\nimport { isEqual } from 'lodash';\n\n/** @public */\nexport type AllowedEntityFilters<T extends DefaultEntityFilters> = {\n [K in keyof T]-?: NonNullable<T[K]> extends EntityFilter & {\n values: string[];\n }\n ? K\n : never;\n}[keyof T];\n\n/** @public */\nexport type EntityAutocompletePickerProps<\n T extends DefaultEntityFilters = DefaultEntityFilters,\n Name extends AllowedEntityFilters<T> = AllowedEntityFilters<T>,\n> = {\n label: string;\n name: Name;\n path: string;\n showCounts?: boolean;\n Filter: { new (values: string[]): NonNullable<T[Name]> };\n InputProps?: TextFieldProps;\n initialSelectedOptions?: string[];\n filtersForAvailableValues?: Array<keyof T>;\n hidden?: boolean;\n getOptionLabel?: (option: string) => string;\n renderOption?: (\n option: string,\n state: AutocompleteRenderOptionState,\n ) => ReactNode;\n};\n\n/** @public */\nexport type CatalogReactEntityAutocompletePickerClassKey = 'root' | 'label';\n\nconst useStyles = makeStyles(\n {\n root: {},\n label: {\n textTransform: 'none',\n fontWeight: 'bold',\n },\n },\n { name: 'CatalogReactEntityAutocompletePicker' },\n);\n\n/** @public */\nexport function EntityAutocompletePicker<\n T extends DefaultEntityFilters = DefaultEntityFilters,\n Name extends AllowedEntityFilters<T> = AllowedEntityFilters<T>,\n>(props: EntityAutocompletePickerProps<T, Name>) {\n const {\n label,\n name,\n path,\n showCounts,\n Filter,\n InputProps,\n initialSelectedOptions = [],\n filtersForAvailableValues = ['kind'],\n hidden,\n getOptionLabel,\n renderOption,\n } = props;\n const classes = useStyles();\n\n const {\n updateFilters,\n filters,\n queryParameters: { [name]: queryParameter },\n } = useEntityList<T>();\n\n const catalogApi = useApi(catalogApiRef);\n const availableValuesFilters = filtersForAvailableValues.map(\n f => filters[f] as EntityFilter | undefined,\n );\n const { value: availableValues } = useAsync(async () => {\n const facet = path;\n const { facets } = await catalogApi.getEntityFacets({\n facets: [facet],\n ...reduceBackendCatalogFilters(\n availableValuesFilters.filter(Boolean) as EntityFilter[],\n ),\n });\n\n return Object.fromEntries(\n facets[facet].map(({ value, count }) => [value, count]),\n );\n }, [...availableValuesFilters]);\n\n const queryParameters = useMemo(\n () => [queryParameter].flat().filter(Boolean) as string[],\n [queryParameter],\n );\n\n const filteredOptions = (filters[name] as unknown as { values: string[] })\n ?.values;\n\n const [selectedOptions, setSelectedOptions] = useState(\n queryParameters.length\n ? queryParameters\n : filteredOptions ?? initialSelectedOptions,\n );\n\n // Set selected options on query parameter updates; this happens at initial page load and from\n // external updates to the page location\n useEffect(() => {\n if (queryParameters.length) {\n setSelectedOptions(queryParameters);\n }\n }, [queryParameters]);\n\n const availableOptions = Object.keys(availableValues ?? {});\n const shouldAddFilter = selectedOptions.length && availableOptions.length;\n\n // Update filter value when selectedOptions change\n useEffect(() => {\n updateFilters({\n [name]: shouldAddFilter ? new Filter(selectedOptions) : undefined,\n } as Partial<T>);\n }, [name, shouldAddFilter, selectedOptions, Filter, updateFilters]);\n\n // Update selected options when filter value changes\n useEffect(() => {\n if (!shouldAddFilter) return;\n\n const newSelectedOptions = filteredOptions ?? [];\n\n // Check value is actually different (not just a different reference) to prevent selectedOptions <> filters loop\n if (!isEqual(newSelectedOptions, selectedOptions)) {\n setSelectedOptions(newSelectedOptions);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- Don't re-set filter value when selectedOptions changes\n }, [filteredOptions]);\n\n const filter = filters[name];\n if (\n (filter && typeof filter === 'object' && !('values' in filter)) ||\n !availableOptions.length\n ) {\n return null;\n }\n\n return hidden ? null : (\n <Box className={classes.root} pb={1} pt={1}>\n <CatalogAutocomplete<string, true>\n multiple\n disableCloseOnSelect\n label={label}\n name={`${String(name)}-picker`}\n options={availableOptions}\n value={selectedOptions}\n TextFieldProps={InputProps}\n onChange={(_event: object, options: string[]) =>\n setSelectedOptions(options)\n }\n {...(getOptionLabel && { getOptionLabel })}\n renderOption={\n renderOption ??\n ((option, { selected }) => (\n <EntityAutocompletePickerOption\n selected={selected}\n value={option}\n availableOptions={availableValues}\n showCounts={!!showCounts}\n />\n ))\n }\n />\n </Box>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -27,7 +27,7 @@ function useAllEntitiesCount() {
27
27
  ...catalogFilters,
28
28
  limit: 0
29
29
  };
30
- if (Object.keys(catalogFilters.filter).length === 0) {
30
+ if (!catalogFilters.filter && !catalogFilters.query) {
31
31
  prevRequest.current = void 0;
32
32
  return prevRequest.current;
33
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useAllEntitiesCount.esm.js","sources":["../../../src/components/UserListPicker/useAllEntitiesCount.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { QueryEntitiesInitialRequest } from '@backstage/catalog-client';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { compact, isEqual } from 'lodash';\nimport { useMemo, useRef } from 'react';\nimport useAsync from 'react-use/esm/useAsync';\nimport { catalogApiRef } from '../../api';\nimport { useEntityList } from '../../hooks';\nimport { reduceCatalogFilters } from '../../utils/filters';\n\nexport function useAllEntitiesCount() {\n const catalogApi = useApi(catalogApiRef);\n const { filters } = useEntityList();\n\n const prevRequest = useRef<QueryEntitiesInitialRequest>();\n const request = useMemo(() => {\n const { user, ...allFilters } = filters;\n const compacted = compact(Object.values(allFilters));\n const { orderFields, ...catalogFilters } = reduceCatalogFilters(compacted);\n const newRequest: QueryEntitiesInitialRequest = {\n ...catalogFilters,\n limit: 0,\n };\n\n if (Object.keys(catalogFilters.filter).length === 0) {\n prevRequest.current = undefined;\n return prevRequest.current;\n }\n\n if (isEqual(newRequest, prevRequest.current)) {\n return prevRequest.current;\n }\n prevRequest.current = newRequest;\n return newRequest;\n }, [filters]);\n\n const { value: count, loading } = useAsync(async () => {\n if (request === undefined) {\n return 0;\n }\n const { totalItems } = await catalogApi.queryEntities(request);\n\n return totalItems;\n }, [request]);\n\n return { count, loading };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAwBO,SAAS,mBAAA,GAAsB;AACpC,EAAA,MAAM,UAAA,GAAa,OAAO,aAAa,CAAA;AACvC,EAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,aAAA,EAAc;AAElC,EAAA,MAAM,cAAc,MAAA,EAAoC;AACxD,EAAA,MAAM,OAAA,GAAU,QAAQ,MAAM;AAC5B,IAAA,MAAM,EAAE,IAAA,EAAM,GAAG,UAAA,EAAW,GAAI,OAAA;AAChC,IAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,MAAA,CAAO,MAAA,CAAO,UAAU,CAAC,CAAA;AACnD,IAAA,MAAM,EAAE,WAAA,EAAa,GAAG,cAAA,EAAe,GAAI,qBAAqB,SAAS,CAAA;AACzE,IAAA,MAAM,UAAA,GAA0C;AAAA,MAC9C,GAAG,cAAA;AAAA,MACH,KAAA,EAAO;AAAA,KACT;AAEA,IAAA,IAAI,OAAO,IAAA,CAAK,cAAA,CAAe,MAAM,CAAA,CAAE,WAAW,CAAA,EAAG;AACnD,MAAA,WAAA,CAAY,OAAA,GAAU,MAAA;AACtB,MAAA,OAAO,WAAA,CAAY,OAAA;AAAA,IACrB;AAEA,IAAA,IAAI,OAAA,CAAQ,UAAA,EAAY,WAAA,CAAY,OAAO,CAAA,EAAG;AAC5C,MAAA,OAAO,WAAA,CAAY,OAAA;AAAA,IACrB;AACA,IAAA,WAAA,CAAY,OAAA,GAAU,UAAA;AACtB,IAAA,OAAO,UAAA;AAAA,EACT,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAO,OAAA,EAAQ,GAAI,SAAS,YAAY;AACrD,IAAA,IAAI,YAAY,MAAA,EAAW;AACzB,MAAA,OAAO,CAAA;AAAA,IACT;AACA,IAAA,MAAM,EAAE,UAAA,EAAW,GAAI,MAAM,UAAA,CAAW,cAAc,OAAO,CAAA;AAE7D,IAAA,OAAO,UAAA;AAAA,EACT,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,OAAO,EAAE,OAAO,OAAA,EAAQ;AAC1B;;"}
1
+ {"version":3,"file":"useAllEntitiesCount.esm.js","sources":["../../../src/components/UserListPicker/useAllEntitiesCount.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { QueryEntitiesInitialRequest } from '@backstage/catalog-client';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { compact, isEqual } from 'lodash';\nimport { useMemo, useRef } from 'react';\nimport useAsync from 'react-use/esm/useAsync';\nimport { catalogApiRef } from '../../api';\nimport { useEntityList } from '../../hooks';\nimport { reduceCatalogFilters } from '../../utils/filters';\n\nexport function useAllEntitiesCount() {\n const catalogApi = useApi(catalogApiRef);\n const { filters } = useEntityList();\n\n const prevRequest = useRef<QueryEntitiesInitialRequest>();\n const request = useMemo(() => {\n const { user, ...allFilters } = filters;\n const compacted = compact(Object.values(allFilters));\n const { orderFields, ...catalogFilters } = reduceCatalogFilters(compacted);\n const newRequest: QueryEntitiesInitialRequest = {\n ...catalogFilters,\n limit: 0,\n };\n\n if (!catalogFilters.filter && !catalogFilters.query) {\n prevRequest.current = undefined;\n return prevRequest.current;\n }\n\n if (isEqual(newRequest, prevRequest.current)) {\n return prevRequest.current;\n }\n prevRequest.current = newRequest;\n return newRequest;\n }, [filters]);\n\n const { value: count, loading } = useAsync(async () => {\n if (request === undefined) {\n return 0;\n }\n const { totalItems } = await catalogApi.queryEntities(request);\n\n return totalItems;\n }, [request]);\n\n return { count, loading };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAwBO,SAAS,mBAAA,GAAsB;AACpC,EAAA,MAAM,UAAA,GAAa,OAAO,aAAa,CAAA;AACvC,EAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,aAAA,EAAc;AAElC,EAAA,MAAM,cAAc,MAAA,EAAoC;AACxD,EAAA,MAAM,OAAA,GAAU,QAAQ,MAAM;AAC5B,IAAA,MAAM,EAAE,IAAA,EAAM,GAAG,UAAA,EAAW,GAAI,OAAA;AAChC,IAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,MAAA,CAAO,MAAA,CAAO,UAAU,CAAC,CAAA;AACnD,IAAA,MAAM,EAAE,WAAA,EAAa,GAAG,cAAA,EAAe,GAAI,qBAAqB,SAAS,CAAA;AACzE,IAAA,MAAM,UAAA,GAA0C;AAAA,MAC9C,GAAG,cAAA;AAAA,MACH,KAAA,EAAO;AAAA,KACT;AAEA,IAAA,IAAI,CAAC,cAAA,CAAe,MAAA,IAAU,CAAC,eAAe,KAAA,EAAO;AACnD,MAAA,WAAA,CAAY,OAAA,GAAU,MAAA;AACtB,MAAA,OAAO,WAAA,CAAY,OAAA;AAAA,IACrB;AAEA,IAAA,IAAI,OAAA,CAAQ,UAAA,EAAY,WAAA,CAAY,OAAO,CAAA,EAAG;AAC5C,MAAA,OAAO,WAAA,CAAY,OAAA;AAAA,IACrB;AACA,IAAA,WAAA,CAAY,OAAA,GAAU,UAAA;AACtB,IAAA,OAAO,UAAA;AAAA,EACT,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAO,OAAA,EAAQ,GAAI,SAAS,YAAY;AACrD,IAAA,IAAI,YAAY,MAAA,EAAW;AACzB,MAAA,OAAO,CAAA;AAAA,IACT;AACA,IAAA,MAAM,EAAE,UAAA,EAAW,GAAI,MAAM,UAAA,CAAW,cAAc,OAAO,CAAA;AAE7D,IAAA,OAAO,UAAA;AAAA,EACT,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,OAAO,EAAE,OAAO,OAAA,EAAQ;AAC1B;;"}
@@ -14,7 +14,7 @@ import '../../apis/EntityPresentationApi/EntityPresentationApi.esm.js';
14
14
  import 'lodash/get';
15
15
  import '../../apis/StarredEntitiesApi/StarredEntitiesApi.esm.js';
16
16
  import 'zen-observable';
17
- import { reduceCatalogFilters } from '../../utils/filters.esm.js';
17
+ import { reduceCatalogFilters, combinePredicates } from '../../utils/filters.esm.js';
18
18
  import useAsyncFn from 'react-use/esm/useAsyncFn';
19
19
  import useDeepCompareEffect from 'react-use/esm/useDeepCompareEffect';
20
20
 
@@ -40,13 +40,12 @@ function useOwnedEntitiesCount() {
40
40
  if (ownedClaims === void 0) {
41
41
  return 0;
42
42
  }
43
- const { ["metadata.name"]: metadata, ...filter } = req.filter.filter;
44
43
  const { totalItems } = await catalogApi.queryEntities({
45
44
  ...req.filter,
46
- filter: {
47
- ...filter,
48
- "relations.ownedBy": ownedClaims
49
- },
45
+ query: combinePredicates([
46
+ req.filter.query,
47
+ EntityUserFilter.owned(ownedClaims).getCatalogQuery()
48
+ ]),
50
49
  limit: 0
51
50
  });
52
51
  return totalItems;
@@ -55,7 +54,7 @@ function useOwnedEntitiesCount() {
55
54
  { loading: true }
56
55
  );
57
56
  useDeepCompareEffect(() => {
58
- if (Object.keys(catalogFilters.filter).length === 0) {
57
+ if (!catalogFilters.filter && !catalogFilters.query) {
59
58
  return;
60
59
  }
61
60
  if (ownershipEntityRefs === void 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"useOwnedEntitiesCount.esm.js","sources":["../../../src/components/UserListPicker/useOwnedEntitiesCount.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { identityApiRef, useApi } from '@backstage/core-plugin-api';\nimport { compact, intersection } from 'lodash';\nimport { useMemo } from 'react';\nimport useAsync from 'react-use/esm/useAsync';\nimport { catalogApiRef } from '../../api';\nimport { EntityOwnerFilter, EntityUserFilter } from '../../filters';\nimport { useEntityList } from '../../hooks';\nimport { CatalogFilters, reduceCatalogFilters } from '../../utils/filters';\nimport useAsyncFn from 'react-use/esm/useAsyncFn';\nimport useDeepCompareEffect from 'react-use/esm/useDeepCompareEffect';\n\nexport function useOwnedEntitiesCount() {\n const identityApi = useApi(identityApiRef);\n const catalogApi = useApi(catalogApiRef);\n\n const { filters } = useEntityList();\n\n const { value: ownershipEntityRefs, loading: loadingEntityRefs } = useAsync(\n async () => (await identityApi.getBackstageIdentity()).ownershipEntityRefs,\n // load only on mount\n [],\n );\n\n const { user, owners, ...allFilters } = filters;\n const { orderFields, ...catalogFilters } = reduceCatalogFilters(\n compact(Object.values(allFilters)),\n );\n\n const [{ value: count, loading: loadingEntityOwnership }, fetchEntities] =\n useAsyncFn(\n async (req: {\n ownershipEntityRefs: string[];\n owners: EntityOwnerFilter | undefined;\n filter: CatalogFilters;\n }) => {\n const ownedClaims = getOwnedCountClaims(\n req.owners,\n req.ownershipEntityRefs,\n );\n if (ownedClaims === undefined) {\n // this implicitly means that there aren't claims in common with\n // the logged in users, so avoid invoking the queryEntities endpoint\n // which will implicitly returns 0\n return 0;\n }\n\n const { ['metadata.name']: metadata, ...filter } = req.filter.filter;\n\n const { totalItems } = await catalogApi.queryEntities({\n ...req.filter,\n filter: {\n ...filter,\n 'relations.ownedBy': ownedClaims,\n },\n limit: 0,\n });\n return totalItems;\n },\n [],\n { loading: true },\n );\n\n useDeepCompareEffect(() => {\n // context contains no filter, wait\n if (Object.keys(catalogFilters.filter).length === 0) {\n return;\n }\n // ownershipEntityRefs is loading, wait\n if (ownershipEntityRefs === undefined) {\n return;\n }\n fetchEntities({\n ownershipEntityRefs,\n owners,\n filter: catalogFilters,\n });\n }, [ownershipEntityRefs, owners, catalogFilters]);\n\n const loading = loadingEntityRefs || loadingEntityOwnership;\n\n return {\n count,\n loading,\n filter: useMemo(\n () => EntityUserFilter.owned(ownershipEntityRefs ?? []),\n [ownershipEntityRefs],\n ),\n ownershipEntityRefs,\n };\n}\n\nfunction getOwnedCountClaims(\n owners: EntityOwnerFilter | undefined,\n ownershipEntityRefs: string[] | undefined,\n) {\n if (!ownershipEntityRefs?.length) {\n return undefined;\n }\n const ownersRefs = owners?.values ?? [];\n if (ownersRefs.length) {\n const commonOwnedBy = intersection(ownersRefs, ownershipEntityRefs);\n if (commonOwnedBy.length === 0) {\n return undefined;\n }\n return commonOwnedBy;\n }\n return ownershipEntityRefs;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useOwnedEntitiesCount.esm.js","sources":["../../../src/components/UserListPicker/useOwnedEntitiesCount.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { identityApiRef, useApi } from '@backstage/core-plugin-api';\nimport { compact, intersection } from 'lodash';\nimport { useMemo } from 'react';\nimport useAsync from 'react-use/esm/useAsync';\nimport { catalogApiRef } from '../../api';\nimport { EntityOwnerFilter, EntityUserFilter } from '../../filters';\nimport { useEntityList } from '../../hooks';\nimport {\n CatalogFilters,\n combinePredicates,\n reduceCatalogFilters,\n} from '../../utils/filters';\nimport useAsyncFn from 'react-use/esm/useAsyncFn';\nimport useDeepCompareEffect from 'react-use/esm/useDeepCompareEffect';\n\nexport function useOwnedEntitiesCount() {\n const identityApi = useApi(identityApiRef);\n const catalogApi = useApi(catalogApiRef);\n\n const { filters } = useEntityList();\n\n const { value: ownershipEntityRefs, loading: loadingEntityRefs } = useAsync(\n async () => (await identityApi.getBackstageIdentity()).ownershipEntityRefs,\n // load only on mount\n [],\n );\n\n const { user, owners, ...allFilters } = filters;\n const { orderFields, ...catalogFilters } = reduceCatalogFilters(\n compact(Object.values(allFilters)),\n );\n\n const [{ value: count, loading: loadingEntityOwnership }, fetchEntities] =\n useAsyncFn(\n async (req: {\n ownershipEntityRefs: string[];\n owners: EntityOwnerFilter | undefined;\n filter: CatalogFilters;\n }) => {\n const ownedClaims = getOwnedCountClaims(\n req.owners,\n req.ownershipEntityRefs,\n );\n if (ownedClaims === undefined) {\n // this implicitly means that there aren't claims in common with\n // the logged in users, so avoid invoking the queryEntities endpoint\n // which will implicitly returns 0\n return 0;\n }\n\n const { totalItems } = await catalogApi.queryEntities({\n ...req.filter,\n query: combinePredicates([\n req.filter.query,\n EntityUserFilter.owned(ownedClaims).getCatalogQuery(),\n ]),\n limit: 0,\n });\n return totalItems;\n },\n [],\n { loading: true },\n );\n\n useDeepCompareEffect(() => {\n // context contains no filter, wait\n if (!catalogFilters.filter && !catalogFilters.query) {\n return;\n }\n // ownershipEntityRefs is loading, wait\n if (ownershipEntityRefs === undefined) {\n return;\n }\n fetchEntities({\n ownershipEntityRefs,\n owners,\n filter: catalogFilters,\n });\n }, [ownershipEntityRefs, owners, catalogFilters]);\n\n const loading = loadingEntityRefs || loadingEntityOwnership;\n\n return {\n count,\n loading,\n filter: useMemo(\n () => EntityUserFilter.owned(ownershipEntityRefs ?? []),\n [ownershipEntityRefs],\n ),\n ownershipEntityRefs,\n };\n}\n\nfunction getOwnedCountClaims(\n owners: EntityOwnerFilter | undefined,\n ownershipEntityRefs: string[] | undefined,\n) {\n if (!ownershipEntityRefs?.length) {\n return undefined;\n }\n const ownersRefs = owners?.values ?? [];\n if (ownersRefs.length) {\n const commonOwnedBy = intersection(ownersRefs, ownershipEntityRefs);\n if (commonOwnedBy.length === 0) {\n return undefined;\n }\n return commonOwnedBy;\n }\n return ownershipEntityRefs;\n}\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,3 @@
1
- import { parseEntityRef, stringifyEntityRef } from '@backstage/catalog-model';
2
1
  import { useApi } from '@backstage/core-plugin-api';
3
2
  import { compact, isEqual } from 'lodash';
4
3
  import { useRef, useMemo } from 'react';
@@ -9,12 +8,13 @@ import '../../hooks/useEntity.esm.js';
9
8
  import { useEntityList } from '../../hooks/useEntityListProvider.esm.js';
10
9
  import 'lodash/isEqual';
11
10
  import 'lodash/sortBy';
11
+ import '@backstage/catalog-model';
12
12
  import { useStarredEntities } from '../../hooks/useStarredEntities.esm.js';
13
13
  import '../../apis/EntityPresentationApi/EntityPresentationApi.esm.js';
14
14
  import 'lodash/get';
15
15
  import '../../apis/StarredEntitiesApi/StarredEntitiesApi.esm.js';
16
16
  import 'zen-observable';
17
- import { reduceCatalogFilters } from '../../utils/filters.esm.js';
17
+ import { reduceCatalogFilters, combinePredicates } from '../../utils/filters.esm.js';
18
18
 
19
19
  function useStarredEntitiesCount() {
20
20
  const catalogApi = useApi(catalogApiRef);
@@ -25,23 +25,13 @@ function useStarredEntitiesCount() {
25
25
  const { user, ...allFilters } = filters;
26
26
  const compacted = compact(Object.values(allFilters));
27
27
  const { orderFields, ...catalogFilters } = reduceCatalogFilters(compacted);
28
- const facet = "metadata.name";
29
28
  const newRequest = {
30
29
  ...catalogFilters,
31
- filter: {
32
- ...catalogFilters.filter,
33
- /**
34
- * here we are filtering entities by `name`. Given this filter,
35
- * the response might contain more entities than expected, in case multiple entities
36
- * of different kind or namespace share the same name. Those extra entities are filtered out
37
- * client side by `EntityUserFilter`, so they won't be visible to the user.
38
- */
39
- [facet]: Array.from(starredEntities).map((e) => parseEntityRef(e).name)
40
- },
41
- /**
42
- * limit is set to a high value as we are not expecting many starred entities
43
- */
44
- limit: 1e3
30
+ query: combinePredicates([
31
+ catalogFilters.query,
32
+ EntityUserFilter.starred(Array.from(starredEntities)).getCatalogQuery()
33
+ ]),
34
+ limit: 0
45
35
  };
46
36
  if (isEqual(newRequest, prevRequest.current)) {
47
37
  return prevRequest.current;
@@ -53,14 +43,8 @@ function useStarredEntitiesCount() {
53
43
  if (!starredEntities.size) {
54
44
  return 0;
55
45
  }
56
- const response = await catalogApi.queryEntities(request);
57
- return response.items.map(
58
- (e) => stringifyEntityRef({
59
- kind: e.kind,
60
- namespace: e.metadata.namespace,
61
- name: e.metadata.name
62
- })
63
- ).filter((e) => starredEntities.has(e)).length;
46
+ const { totalItems } = await catalogApi.queryEntities(request);
47
+ return totalItems;
64
48
  }, [request, starredEntities]);
65
49
  const filter = useMemo(
66
50
  () => EntityUserFilter.starred(Array.from(starredEntities)),
@@ -1 +1 @@
1
- {"version":3,"file":"useStarredEntitiesCount.esm.js","sources":["../../../src/components/UserListPicker/useStarredEntitiesCount.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { QueryEntitiesInitialRequest } from '@backstage/catalog-client';\nimport { parseEntityRef, stringifyEntityRef } from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { compact, isEqual } from 'lodash';\nimport { useMemo, useRef } from 'react';\nimport useAsync from 'react-use/esm/useAsync';\nimport { catalogApiRef } from '../../api';\nimport { EntityUserFilter } from '../../filters';\nimport { useEntityList, useStarredEntities } from '../../hooks';\nimport { reduceCatalogFilters } from '../../utils/filters';\n\nexport function useStarredEntitiesCount() {\n const catalogApi = useApi(catalogApiRef);\n const { filters } = useEntityList();\n const { starredEntities } = useStarredEntities();\n\n const prevRequest = useRef<QueryEntitiesInitialRequest>();\n const request = useMemo(() => {\n const { user, ...allFilters } = filters;\n const compacted = compact(Object.values(allFilters));\n const { orderFields, ...catalogFilters } = reduceCatalogFilters(compacted);\n\n const facet = 'metadata.name';\n\n const newRequest: QueryEntitiesInitialRequest = {\n ...catalogFilters,\n filter: {\n ...catalogFilters.filter,\n /**\n * here we are filtering entities by `name`. Given this filter,\n * the response might contain more entities than expected, in case multiple entities\n * of different kind or namespace share the same name. Those extra entities are filtered out\n * client side by `EntityUserFilter`, so they won't be visible to the user.\n */\n [facet]: Array.from(starredEntities).map(e => parseEntityRef(e).name),\n },\n /**\n * limit is set to a high value as we are not expecting many starred entities\n */\n limit: 1000,\n };\n if (isEqual(newRequest, prevRequest.current)) {\n return prevRequest.current;\n }\n prevRequest.current = newRequest;\n\n return newRequest;\n }, [filters, starredEntities]);\n\n const { value: count, loading } = useAsync(async () => {\n if (!starredEntities.size) {\n return 0;\n }\n\n /**\n * given a list of starred entity refs and some filters coming from CatalogPage,\n * it reduces the list of starred entities, to a list of entities that matches the\n * provided filters. It won't be possible to getEntitiesByRefs\n * as the method doesn't accept any filter.\n */\n const response = await catalogApi.queryEntities(request);\n\n return response.items\n .map(e =>\n stringifyEntityRef({\n kind: e.kind,\n namespace: e.metadata.namespace,\n name: e.metadata.name,\n }),\n )\n .filter(e => starredEntities.has(e)).length;\n }, [request, starredEntities]);\n\n const filter = useMemo(\n () => EntityUserFilter.starred(Array.from(starredEntities)),\n [starredEntities],\n );\n\n return { count, loading, filter };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AA2BO,SAAS,uBAAA,GAA0B;AACxC,EAAA,MAAM,UAAA,GAAa,OAAO,aAAa,CAAA;AACvC,EAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,aAAA,EAAc;AAClC,EAAA,MAAM,EAAE,eAAA,EAAgB,GAAI,kBAAA,EAAmB;AAE/C,EAAA,MAAM,cAAc,MAAA,EAAoC;AACxD,EAAA,MAAM,OAAA,GAAU,QAAQ,MAAM;AAC5B,IAAA,MAAM,EAAE,IAAA,EAAM,GAAG,UAAA,EAAW,GAAI,OAAA;AAChC,IAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,MAAA,CAAO,MAAA,CAAO,UAAU,CAAC,CAAA;AACnD,IAAA,MAAM,EAAE,WAAA,EAAa,GAAG,cAAA,EAAe,GAAI,qBAAqB,SAAS,CAAA;AAEzE,IAAA,MAAM,KAAA,GAAQ,eAAA;AAEd,IAAA,MAAM,UAAA,GAA0C;AAAA,MAC9C,GAAG,cAAA;AAAA,MACH,MAAA,EAAQ;AAAA,QACN,GAAG,cAAA,CAAe,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAOlB,CAAC,KAAK,GAAG,KAAA,CAAM,IAAA,CAAK,eAAe,CAAA,CAAE,GAAA,CAAI,CAAA,CAAA,KAAK,cAAA,CAAe,CAAC,CAAA,CAAE,IAAI;AAAA,OACtE;AAAA;AAAA;AAAA;AAAA,MAIA,KAAA,EAAO;AAAA,KACT;AACA,IAAA,IAAI,OAAA,CAAQ,UAAA,EAAY,WAAA,CAAY,OAAO,CAAA,EAAG;AAC5C,MAAA,OAAO,WAAA,CAAY,OAAA;AAAA,IACrB;AACA,IAAA,WAAA,CAAY,OAAA,GAAU,UAAA;AAEtB,IAAA,OAAO,UAAA;AAAA,EACT,CAAA,EAAG,CAAC,OAAA,EAAS,eAAe,CAAC,CAAA;AAE7B,EAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAO,OAAA,EAAQ,GAAI,SAAS,YAAY;AACrD,IAAA,IAAI,CAAC,gBAAgB,IAAA,EAAM;AACzB,MAAA,OAAO,CAAA;AAAA,IACT;AAQA,IAAA,MAAM,QAAA,GAAW,MAAM,UAAA,CAAW,aAAA,CAAc,OAAO,CAAA;AAEvD,IAAA,OAAO,SAAS,KAAA,CACb,GAAA;AAAA,MAAI,OACH,kBAAA,CAAmB;AAAA,QACjB,MAAM,CAAA,CAAE,IAAA;AAAA,QACR,SAAA,EAAW,EAAE,QAAA,CAAS,SAAA;AAAA,QACtB,IAAA,EAAM,EAAE,QAAA,CAAS;AAAA,OAClB;AAAA,MAEF,MAAA,CAAO,CAAA,CAAA,KAAK,gBAAgB,GAAA,CAAI,CAAC,CAAC,CAAA,CAAE,MAAA;AAAA,EACzC,CAAA,EAAG,CAAC,OAAA,EAAS,eAAe,CAAC,CAAA;AAE7B,EAAA,MAAM,MAAA,GAAS,OAAA;AAAA,IACb,MAAM,gBAAA,CAAiB,OAAA,CAAQ,KAAA,CAAM,IAAA,CAAK,eAAe,CAAC,CAAA;AAAA,IAC1D,CAAC,eAAe;AAAA,GAClB;AAEA,EAAA,OAAO,EAAE,KAAA,EAAO,OAAA,EAAS,MAAA,EAAO;AAClC;;"}
1
+ {"version":3,"file":"useStarredEntitiesCount.esm.js","sources":["../../../src/components/UserListPicker/useStarredEntitiesCount.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { QueryEntitiesInitialRequest } from '@backstage/catalog-client';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { compact, isEqual } from 'lodash';\nimport { useMemo, useRef } from 'react';\nimport useAsync from 'react-use/esm/useAsync';\nimport { catalogApiRef } from '../../api';\nimport { EntityUserFilter } from '../../filters';\nimport { useEntityList, useStarredEntities } from '../../hooks';\nimport { combinePredicates, reduceCatalogFilters } from '../../utils/filters';\n\nexport function useStarredEntitiesCount() {\n const catalogApi = useApi(catalogApiRef);\n const { filters } = useEntityList();\n const { starredEntities } = useStarredEntities();\n\n const prevRequest = useRef<QueryEntitiesInitialRequest>();\n const request = useMemo(() => {\n const { user, ...allFilters } = filters;\n const compacted = compact(Object.values(allFilters));\n const { orderFields, ...catalogFilters } = reduceCatalogFilters(compacted);\n\n const newRequest: QueryEntitiesInitialRequest = {\n ...catalogFilters,\n query: combinePredicates([\n catalogFilters.query,\n EntityUserFilter.starred(Array.from(starredEntities)).getCatalogQuery(),\n ]),\n limit: 0,\n };\n if (isEqual(newRequest, prevRequest.current)) {\n return prevRequest.current;\n }\n prevRequest.current = newRequest;\n\n return newRequest;\n }, [filters, starredEntities]);\n\n const { value: count, loading } = useAsync(async () => {\n if (!starredEntities.size) {\n return 0;\n }\n\n const { totalItems } = await catalogApi.queryEntities(request);\n return totalItems;\n }, [request, starredEntities]);\n\n const filter = useMemo(\n () => EntityUserFilter.starred(Array.from(starredEntities)),\n [starredEntities],\n );\n\n return { count, loading, filter };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AA0BO,SAAS,uBAAA,GAA0B;AACxC,EAAA,MAAM,UAAA,GAAa,OAAO,aAAa,CAAA;AACvC,EAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,aAAA,EAAc;AAClC,EAAA,MAAM,EAAE,eAAA,EAAgB,GAAI,kBAAA,EAAmB;AAE/C,EAAA,MAAM,cAAc,MAAA,EAAoC;AACxD,EAAA,MAAM,OAAA,GAAU,QAAQ,MAAM;AAC5B,IAAA,MAAM,EAAE,IAAA,EAAM,GAAG,UAAA,EAAW,GAAI,OAAA;AAChC,IAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,MAAA,CAAO,MAAA,CAAO,UAAU,CAAC,CAAA;AACnD,IAAA,MAAM,EAAE,WAAA,EAAa,GAAG,cAAA,EAAe,GAAI,qBAAqB,SAAS,CAAA;AAEzE,IAAA,MAAM,UAAA,GAA0C;AAAA,MAC9C,GAAG,cAAA;AAAA,MACH,OAAO,iBAAA,CAAkB;AAAA,QACvB,cAAA,CAAe,KAAA;AAAA,QACf,iBAAiB,OAAA,CAAQ,KAAA,CAAM,KAAK,eAAe,CAAC,EAAE,eAAA;AAAgB,OACvE,CAAA;AAAA,MACD,KAAA,EAAO;AAAA,KACT;AACA,IAAA,IAAI,OAAA,CAAQ,UAAA,EAAY,WAAA,CAAY,OAAO,CAAA,EAAG;AAC5C,MAAA,OAAO,WAAA,CAAY,OAAA;AAAA,IACrB;AACA,IAAA,WAAA,CAAY,OAAA,GAAU,UAAA;AAEtB,IAAA,OAAO,UAAA;AAAA,EACT,CAAA,EAAG,CAAC,OAAA,EAAS,eAAe,CAAC,CAAA;AAE7B,EAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAO,OAAA,EAAQ,GAAI,SAAS,YAAY;AACrD,IAAA,IAAI,CAAC,gBAAgB,IAAA,EAAM;AACzB,MAAA,OAAO,CAAA;AAAA,IACT;AAEA,IAAA,MAAM,EAAE,UAAA,EAAW,GAAI,MAAM,UAAA,CAAW,cAAc,OAAO,CAAA;AAC7D,IAAA,OAAO,UAAA;AAAA,EACT,CAAA,EAAG,CAAC,OAAA,EAAS,eAAe,CAAC,CAAA;AAE7B,EAAA,MAAM,MAAA,GAAS,OAAA;AAAA,IACb,MAAM,gBAAA,CAAiB,OAAA,CAAQ,KAAA,CAAM,IAAA,CAAK,eAAe,CAAC,CAAA;AAAA,IAC1D,CAAC,eAAe;AAAA,GAClB;AAEA,EAAA,OAAO,EAAE,KAAA,EAAO,OAAA,EAAS,MAAA,EAAO;AAClC;;"}
@@ -1,6 +1,28 @@
1
1
  import { stringifyEntityRef, parseEntityRef, RELATION_OWNED_BY } from '@backstage/catalog-model';
2
2
  import { getEntityRelations } from './utils/getEntityRelations.esm.js';
3
3
 
4
+ function ownedByPredicate(refs) {
5
+ if (refs.length === 0) {
6
+ return { $any: [] };
7
+ }
8
+ return {
9
+ relations: {
10
+ $contains: {
11
+ type: RELATION_OWNED_BY,
12
+ targetRef: refs.length === 1 ? refs[0] : { $in: refs }
13
+ }
14
+ }
15
+ };
16
+ }
17
+ function anyOfPredicate(field, values) {
18
+ if (values.length === 0) {
19
+ return { $any: [] };
20
+ }
21
+ if (values.length === 1) {
22
+ return { [field]: values[0] };
23
+ }
24
+ return { [field]: { $in: values } };
25
+ }
4
26
  class EntityKindFilter {
5
27
  value;
6
28
  label;
@@ -8,6 +30,12 @@ class EntityKindFilter {
8
30
  this.value = value;
9
31
  this.label = label;
10
32
  }
33
+ getCatalogQuery() {
34
+ return { kind: this.value };
35
+ }
36
+ /**
37
+ * @deprecated Use `getCatalogQuery` instead.
38
+ */
11
39
  getCatalogFilters() {
12
40
  return { kind: this.value };
13
41
  }
@@ -24,6 +52,16 @@ class EntityTypeFilter {
24
52
  getTypes() {
25
53
  return Array.isArray(this.value) ? this.value : [this.value];
26
54
  }
55
+ getCatalogQuery() {
56
+ const types = this.getTypes();
57
+ if (types.length === 0) {
58
+ return void 0;
59
+ }
60
+ return anyOfPredicate("spec.type", types);
61
+ }
62
+ /**
63
+ * @deprecated Use `getCatalogQuery` instead.
64
+ */
27
65
  getCatalogFilters() {
28
66
  return { "spec.type": this.getTypes() };
29
67
  }
@@ -39,6 +77,20 @@ class EntityTagFilter {
39
77
  filterEntity(entity) {
40
78
  return this.values.every((v) => (entity.metadata.tags ?? []).includes(v));
41
79
  }
80
+ getCatalogQuery() {
81
+ if (this.values.length === 0) {
82
+ return void 0;
83
+ }
84
+ if (this.values.length === 1) {
85
+ return { "metadata.tags": { $contains: this.values[0] } };
86
+ }
87
+ return {
88
+ $all: this.values.map((tag) => ({ "metadata.tags": { $contains: tag } }))
89
+ };
90
+ }
91
+ /**
92
+ * @deprecated Use `getCatalogQuery` instead.
93
+ */
42
94
  getCatalogFilters() {
43
95
  return { "metadata.tags": this.values };
44
96
  }
@@ -103,6 +155,12 @@ class EntityOwnerFilter {
103
155
  }
104
156
  }, []);
105
157
  }
158
+ getCatalogQuery() {
159
+ return ownedByPredicate(this.values);
160
+ }
161
+ /**
162
+ * @deprecated Use `getCatalogQuery` instead.
163
+ */
106
164
  getCatalogFilters() {
107
165
  return { "relations.ownedBy": this.values };
108
166
  }
@@ -126,6 +184,12 @@ class EntityLifecycleFilter {
126
184
  constructor(values) {
127
185
  this.values = values;
128
186
  }
187
+ getCatalogQuery() {
188
+ return anyOfPredicate("spec.lifecycle", this.values);
189
+ }
190
+ /**
191
+ * @deprecated Use `getCatalogQuery` instead.
192
+ */
129
193
  getCatalogFilters() {
130
194
  return { "spec.lifecycle": this.values };
131
195
  }
@@ -141,6 +205,12 @@ class EntityNamespaceFilter {
141
205
  constructor(values) {
142
206
  this.values = values;
143
207
  }
208
+ getCatalogQuery() {
209
+ return anyOfPredicate("metadata.namespace", this.values);
210
+ }
211
+ /**
212
+ * @deprecated Use `getCatalogQuery` instead.
213
+ */
144
214
  getCatalogFilters() {
145
215
  return { "metadata.namespace": this.values };
146
216
  }
@@ -167,6 +237,27 @@ class EntityUserFilter {
167
237
  static starred(starredEntityRefs) {
168
238
  return new EntityUserFilter("starred", starredEntityRefs);
169
239
  }
240
+ getCatalogQuery() {
241
+ if (this.value === "owned") {
242
+ return ownedByPredicate(this.refs ?? []);
243
+ }
244
+ if (this.value === "starred") {
245
+ return {
246
+ $any: (this.refs ?? []).map((ref) => {
247
+ const { kind, namespace, name } = parseEntityRef(ref);
248
+ return {
249
+ kind,
250
+ "metadata.namespace": namespace,
251
+ "metadata.name": name
252
+ };
253
+ })
254
+ };
255
+ }
256
+ return void 0;
257
+ }
258
+ /**
259
+ * @deprecated Use `getCatalogQuery` instead.
260
+ */
170
261
  getCatalogFilters() {
171
262
  if (this.value === "owned") {
172
263
  return { "relations.ownedBy": this.refs ?? [] };
@@ -222,6 +313,15 @@ class EntityOrphanFilter {
222
313
  constructor(value) {
223
314
  this.value = value;
224
315
  }
316
+ getCatalogQuery() {
317
+ if (this.value) {
318
+ return { "metadata.annotations.backstage.io/orphan": "true" };
319
+ }
320
+ return void 0;
321
+ }
322
+ /**
323
+ * @deprecated Use `getCatalogQuery` instead.
324
+ */
225
325
  getCatalogFilters() {
226
326
  if (this.value) {
227
327
  return { "metadata.annotations.backstage.io/orphan": String(this.value) };
@@ -1 +1 @@
1
- {"version":3,"file":"filters.esm.js","sources":["../src/filters.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n parseEntityRef,\n RELATION_OWNED_BY,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { AlphaEntity } from '@backstage/catalog-model/alpha';\nimport { EntityFilter, UserListFilterKind } from './types';\nimport { getEntityRelations } from './utils/getEntityRelations';\nimport { EntityOrderQuery } from '@backstage/catalog-client';\n\n/**\n * Filter entities based on Kind.\n * @public\n */\nexport class EntityKindFilter implements EntityFilter {\n readonly value: string;\n readonly label: string;\n\n constructor(value: string, label: string) {\n this.value = value;\n this.label = label;\n }\n\n getCatalogFilters(): Record<string, string | string[]> {\n return { kind: this.value };\n }\n\n toQueryValue(): string {\n return this.value;\n }\n}\n\n/**\n * Filters entities based on type\n * @public\n */\nexport class EntityTypeFilter implements EntityFilter {\n readonly value: string | string[];\n\n constructor(value: string | string[]) {\n this.value = value;\n }\n\n // Simplify `string | string[]` for consumers, always returns an array\n getTypes(): string[] {\n return Array.isArray(this.value) ? this.value : [this.value];\n }\n\n getCatalogFilters(): Record<string, string | string[]> {\n return { 'spec.type': this.getTypes() };\n }\n\n toQueryValue(): string[] {\n return this.getTypes();\n }\n}\n\n/**\n * Filters entities based on tag.\n * @public\n */\nexport class EntityTagFilter implements EntityFilter {\n readonly values: string[];\n\n constructor(values: string[]) {\n this.values = values;\n }\n\n filterEntity(entity: Entity): boolean {\n return this.values.every(v => (entity.metadata.tags ?? []).includes(v));\n }\n\n getCatalogFilters(): Record<string, string | string[]> {\n return { 'metadata.tags': this.values };\n }\n\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\n/**\n * Filters entities where the text matches spec, title or tags.\n * @public\n */\nexport class EntityTextFilter implements EntityFilter {\n readonly value: string;\n\n constructor(value: string) {\n this.value = value;\n }\n\n filterEntity(entity: Entity): boolean {\n const words = this.toUpperArray(this.value.split(/\\s/));\n const exactMatch = this.toUpperArray([entity.metadata.tags]);\n const targets = (entity.spec as { targets?: unknown })?.targets;\n const partialMatch = this.toUpperArray([\n entity.metadata.name,\n entity.metadata.title,\n (entity.spec?.profile as { displayName?: string })?.displayName,\n (entity.spec as { target?: string })?.target,\n ...(Array.isArray(targets) ? targets : []),\n ]);\n\n for (const word of words) {\n if (\n exactMatch.every(m => m !== word) &&\n partialMatch.every(m => !m.includes(word))\n ) {\n return false;\n }\n }\n\n return true;\n }\n\n getFullTextFilters() {\n return {\n term: this.value,\n // Update this to be more dynamic based on table columns.\n fields: [\n 'metadata.name',\n 'metadata.title',\n 'spec.profile.displayName',\n 'spec.target',\n 'spec.targets',\n ],\n };\n }\n\n toQueryValue() {\n return this.value;\n }\n\n private toUpperArray(\n value: Array<string | string[] | undefined>,\n ): Array<string> {\n return value\n .flat()\n .filter((m): m is string => Boolean(m) && typeof m === 'string')\n .map(m => m.toUpperCase());\n }\n}\n\n/**\n * Filter matching entities that are owned by group.\n * @public\n *\n * CAUTION: This class may contain both full and partial entity refs.\n */\nexport class EntityOwnerFilter implements EntityFilter {\n readonly values: string[];\n constructor(values: string[]) {\n this.values = values.reduce((fullRefs, ref) => {\n // Attempt to remove bad entity references here.\n try {\n fullRefs.push(\n stringifyEntityRef(parseEntityRef(ref, { defaultKind: 'Group' })),\n );\n return fullRefs;\n } catch (err) {\n return fullRefs;\n }\n }, [] as string[]);\n }\n\n getCatalogFilters(): Record<string, string | string[]> {\n return { 'relations.ownedBy': this.values };\n }\n\n filterEntity(entity: Entity): boolean {\n return this.values.some(v =>\n getEntityRelations(entity, RELATION_OWNED_BY).some(\n o => stringifyEntityRef(o) === v,\n ),\n );\n }\n\n /**\n * Get the URL query parameter value. May be a mix of full and humanized entity refs.\n * @returns list of entity refs.\n */\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\n/**\n * Filters entities on lifecycle.\n * @public\n */\nexport class EntityLifecycleFilter implements EntityFilter {\n readonly values: string[];\n\n constructor(values: string[]) {\n this.values = values;\n }\n\n getCatalogFilters(): Record<string, string | string[]> {\n return { 'spec.lifecycle': this.values };\n }\n\n filterEntity(entity: Entity): boolean {\n return this.values.some(v => entity.spec?.lifecycle === v);\n }\n\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\n/**\n * Filters entities to those within the given namespace(s).\n * @public\n */\nexport class EntityNamespaceFilter implements EntityFilter {\n readonly values: string[];\n\n constructor(values: string[]) {\n this.values = values;\n }\n\n getCatalogFilters(): Record<string, string | string[]> {\n return { 'metadata.namespace': this.values };\n }\n filterEntity(entity: Entity): boolean {\n return this.values.some(v => entity.metadata.namespace === v);\n }\n\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\n/**\n * @public\n */\nexport class EntityUserFilter implements EntityFilter {\n readonly value: UserListFilterKind;\n readonly refs?: string[];\n\n private constructor(value: UserListFilterKind, refs?: string[]) {\n this.value = value;\n this.refs = refs;\n }\n\n static owned(ownershipEntityRefs: string[]) {\n return new EntityUserFilter('owned', ownershipEntityRefs);\n }\n\n static all() {\n return new EntityUserFilter('all');\n }\n\n static starred(starredEntityRefs: string[]) {\n return new EntityUserFilter('starred', starredEntityRefs);\n }\n\n getCatalogFilters(): Record<string, string[]> {\n if (this.value === 'owned') {\n return { 'relations.ownedBy': this.refs ?? [] };\n }\n if (this.value === 'starred') {\n return {\n 'metadata.name': this.refs?.map(e => parseEntityRef(e).name) ?? [],\n };\n }\n return {};\n }\n\n filterEntity(entity: Entity) {\n if (this.value === 'starred') {\n return this.refs?.includes(stringifyEntityRef(entity)) ?? true;\n }\n // used only for retro-compatibility with non paginated data.\n // This is supposed to return always true for paginated\n // owned entities, since the filters are applied server side.\n if (this.value === 'owned') {\n const relations = getEntityRelations(entity, RELATION_OWNED_BY);\n\n return (\n this.refs?.some(v =>\n relations.some(o => stringifyEntityRef(o) === v),\n ) ?? false\n );\n }\n return true;\n }\n\n toQueryValue(): string {\n return this.value;\n }\n}\n\n/**\n * Filters entities based on whatever the user has starred or owns them.\n * @deprecated use EntityUserFilter\n * @public\n */\nexport class UserListFilter implements EntityFilter {\n readonly value: UserListFilterKind;\n readonly isOwnedEntity: (entity: Entity) => boolean;\n readonly isStarredEntity: (entity: Entity) => boolean;\n\n constructor(\n value: UserListFilterKind,\n isOwnedEntity: (entity: Entity) => boolean,\n isStarredEntity: (entity: Entity) => boolean,\n ) {\n this.value = value;\n this.isOwnedEntity = isOwnedEntity;\n this.isStarredEntity = isStarredEntity;\n }\n\n filterEntity(entity: Entity): boolean {\n switch (this.value) {\n case 'owned':\n return this.isOwnedEntity(entity);\n case 'starred':\n return this.isStarredEntity(entity);\n default:\n return true;\n }\n }\n\n toQueryValue(): string {\n return this.value;\n }\n}\n\n/**\n * Filters entities based if it is an orphan or not.\n * @public\n */\nexport class EntityOrphanFilter implements EntityFilter {\n readonly value: boolean;\n\n constructor(value: boolean) {\n this.value = value;\n }\n\n getCatalogFilters(): Record<string, string | string[]> {\n if (this.value) {\n return { 'metadata.annotations.backstage.io/orphan': String(this.value) };\n }\n return {};\n }\n\n filterEntity(entity: Entity): boolean {\n const orphan = entity.metadata.annotations?.['backstage.io/orphan'];\n return orphan !== undefined && this.value.toString() === orphan;\n }\n}\n\n/**\n * Filters entities based on if it has errors or not.\n * @public\n */\nexport class EntityErrorFilter implements EntityFilter {\n readonly value: boolean;\n\n constructor(value: boolean) {\n this.value = value;\n }\n\n filterEntity(entity: Entity): boolean {\n const error =\n ((entity as AlphaEntity)?.status?.items?.length as number) > 0;\n return error !== undefined && this.value === error;\n }\n}\n\n/**\n * Sort entities by a given field/column.\n * @public\n */\nexport class EntityOrderFilter implements EntityFilter {\n readonly values: [string, 'asc' | 'desc'][];\n\n constructor(values: [string, 'asc' | 'desc'][]) {\n this.values = values;\n }\n\n getOrderFilters(): EntityOrderQuery {\n return this.values.map(([field, order]) => ({ field, order }));\n }\n\n toQueryValue(): string[] {\n return this.values.flat();\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"filters.esm.js","sources":["../src/filters.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n parseEntityRef,\n RELATION_OWNED_BY,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { AlphaEntity } from '@backstage/catalog-model/alpha';\nimport { EntityFilter, UserListFilterKind } from './types';\nimport { getEntityRelations } from './utils/getEntityRelations';\nimport { EntityOrderQuery } from '@backstage/catalog-client';\nimport { FilterPredicate } from '@backstage/filter-predicates';\n\n/**\n * Matches entities where the given field has any of the given values.\n */\nfunction ownedByPredicate(refs: string[]): FilterPredicate {\n if (refs.length === 0) {\n // Never matches anything\n return { $any: [] };\n }\n return {\n relations: {\n $contains: {\n type: RELATION_OWNED_BY,\n targetRef: refs.length === 1 ? refs[0] : { $in: refs },\n },\n },\n };\n}\n\nfunction anyOfPredicate(field: string, values: string[]): FilterPredicate {\n if (values.length === 0) {\n // Never matches anything\n return { $any: [] };\n }\n if (values.length === 1) {\n return { [field]: values[0] } as FilterPredicate;\n }\n return { [field]: { $in: values } } as FilterPredicate;\n}\n\n/**\n * Filter entities based on Kind.\n * @public\n */\nexport class EntityKindFilter implements EntityFilter {\n readonly value: string;\n readonly label: string;\n\n constructor(value: string, label: string) {\n this.value = value;\n this.label = label;\n }\n\n getCatalogQuery(): FilterPredicate {\n return { kind: this.value };\n }\n\n /**\n * @deprecated Use `getCatalogQuery` instead.\n */\n getCatalogFilters(): Record<string, string | string[]> {\n return { kind: this.value };\n }\n\n toQueryValue(): string {\n return this.value;\n }\n}\n\n/**\n * Filters entities based on type\n * @public\n */\nexport class EntityTypeFilter implements EntityFilter {\n readonly value: string | string[];\n\n constructor(value: string | string[]) {\n this.value = value;\n }\n\n // Simplify `string | string[]` for consumers, always returns an array\n getTypes(): string[] {\n return Array.isArray(this.value) ? this.value : [this.value];\n }\n\n getCatalogQuery(): FilterPredicate | undefined {\n const types = this.getTypes();\n if (types.length === 0) {\n return undefined;\n }\n return anyOfPredicate('spec.type', types);\n }\n\n /**\n * @deprecated Use `getCatalogQuery` instead.\n */\n getCatalogFilters(): Record<string, string | string[]> {\n return { 'spec.type': this.getTypes() };\n }\n\n toQueryValue(): string[] {\n return this.getTypes();\n }\n}\n\n/**\n * Filters entities based on tag.\n * @public\n */\nexport class EntityTagFilter implements EntityFilter {\n readonly values: string[];\n\n constructor(values: string[]) {\n this.values = values;\n }\n\n filterEntity(entity: Entity): boolean {\n return this.values.every(v => (entity.metadata.tags ?? []).includes(v));\n }\n\n getCatalogQuery(): FilterPredicate | undefined {\n if (this.values.length === 0) {\n return undefined;\n }\n if (this.values.length === 1) {\n return { 'metadata.tags': { $contains: this.values[0] } };\n }\n return {\n $all: this.values.map(tag => ({ 'metadata.tags': { $contains: tag } })),\n };\n }\n\n /**\n * @deprecated Use `getCatalogQuery` instead.\n */\n getCatalogFilters(): Record<string, string | string[]> {\n return { 'metadata.tags': this.values };\n }\n\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\n/**\n * Filters entities where the text matches spec, title or tags.\n * @public\n */\nexport class EntityTextFilter implements EntityFilter {\n readonly value: string;\n\n constructor(value: string) {\n this.value = value;\n }\n\n filterEntity(entity: Entity): boolean {\n const words = this.toUpperArray(this.value.split(/\\s/));\n const exactMatch = this.toUpperArray([entity.metadata.tags]);\n const targets = (entity.spec as { targets?: unknown })?.targets;\n const partialMatch = this.toUpperArray([\n entity.metadata.name,\n entity.metadata.title,\n (entity.spec?.profile as { displayName?: string })?.displayName,\n (entity.spec as { target?: string })?.target,\n ...(Array.isArray(targets) ? targets : []),\n ]);\n\n for (const word of words) {\n if (\n exactMatch.every(m => m !== word) &&\n partialMatch.every(m => !m.includes(word))\n ) {\n return false;\n }\n }\n\n return true;\n }\n\n getFullTextFilters() {\n return {\n term: this.value,\n // Update this to be more dynamic based on table columns.\n fields: [\n 'metadata.name',\n 'metadata.title',\n 'spec.profile.displayName',\n 'spec.target',\n 'spec.targets',\n ],\n };\n }\n\n toQueryValue() {\n return this.value;\n }\n\n private toUpperArray(\n value: Array<string | string[] | undefined>,\n ): Array<string> {\n return value\n .flat()\n .filter((m): m is string => Boolean(m) && typeof m === 'string')\n .map(m => m.toUpperCase());\n }\n}\n\n/**\n * Filter matching entities that are owned by group.\n * @public\n *\n * CAUTION: This class may contain both full and partial entity refs.\n */\nexport class EntityOwnerFilter implements EntityFilter {\n readonly values: string[];\n constructor(values: string[]) {\n this.values = values.reduce((fullRefs, ref) => {\n // Attempt to remove bad entity references here.\n try {\n fullRefs.push(\n stringifyEntityRef(parseEntityRef(ref, { defaultKind: 'Group' })),\n );\n return fullRefs;\n } catch (err) {\n return fullRefs;\n }\n }, [] as string[]);\n }\n\n getCatalogQuery(): FilterPredicate {\n return ownedByPredicate(this.values);\n }\n\n /**\n * @deprecated Use `getCatalogQuery` instead.\n */\n getCatalogFilters(): Record<string, string | string[]> {\n return { 'relations.ownedBy': this.values };\n }\n\n filterEntity(entity: Entity): boolean {\n return this.values.some(v =>\n getEntityRelations(entity, RELATION_OWNED_BY).some(\n o => stringifyEntityRef(o) === v,\n ),\n );\n }\n\n /**\n * Get the URL query parameter value. May be a mix of full and humanized entity refs.\n * @returns list of entity refs.\n */\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\n/**\n * Filters entities on lifecycle.\n * @public\n */\nexport class EntityLifecycleFilter implements EntityFilter {\n readonly values: string[];\n\n constructor(values: string[]) {\n this.values = values;\n }\n\n getCatalogQuery(): FilterPredicate {\n return anyOfPredicate('spec.lifecycle', this.values);\n }\n\n /**\n * @deprecated Use `getCatalogQuery` instead.\n */\n getCatalogFilters(): Record<string, string | string[]> {\n return { 'spec.lifecycle': this.values };\n }\n\n filterEntity(entity: Entity): boolean {\n return this.values.some(v => entity.spec?.lifecycle === v);\n }\n\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\n/**\n * Filters entities to those within the given namespace(s).\n * @public\n */\nexport class EntityNamespaceFilter implements EntityFilter {\n readonly values: string[];\n\n constructor(values: string[]) {\n this.values = values;\n }\n\n getCatalogQuery(): FilterPredicate {\n return anyOfPredicate('metadata.namespace', this.values);\n }\n\n /**\n * @deprecated Use `getCatalogQuery` instead.\n */\n getCatalogFilters(): Record<string, string | string[]> {\n return { 'metadata.namespace': this.values };\n }\n filterEntity(entity: Entity): boolean {\n return this.values.some(v => entity.metadata.namespace === v);\n }\n\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\n/**\n * @public\n */\nexport class EntityUserFilter implements EntityFilter {\n readonly value: UserListFilterKind;\n readonly refs?: string[];\n\n private constructor(value: UserListFilterKind, refs?: string[]) {\n this.value = value;\n this.refs = refs;\n }\n\n static owned(ownershipEntityRefs: string[]) {\n return new EntityUserFilter('owned', ownershipEntityRefs);\n }\n\n static all() {\n return new EntityUserFilter('all');\n }\n\n static starred(starredEntityRefs: string[]) {\n return new EntityUserFilter('starred', starredEntityRefs);\n }\n\n getCatalogQuery(): FilterPredicate | undefined {\n if (this.value === 'owned') {\n return ownedByPredicate(this.refs ?? []);\n }\n if (this.value === 'starred') {\n return {\n $any: (this.refs ?? []).map(ref => {\n const { kind, namespace, name } = parseEntityRef(ref);\n return {\n kind,\n 'metadata.namespace': namespace,\n 'metadata.name': name,\n };\n }),\n };\n }\n return undefined;\n }\n\n /**\n * @deprecated Use `getCatalogQuery` instead.\n */\n getCatalogFilters(): Record<string, string[]> {\n if (this.value === 'owned') {\n return { 'relations.ownedBy': this.refs ?? [] };\n }\n if (this.value === 'starred') {\n return {\n 'metadata.name': this.refs?.map(e => parseEntityRef(e).name) ?? [],\n };\n }\n return {};\n }\n\n filterEntity(entity: Entity) {\n if (this.value === 'starred') {\n return this.refs?.includes(stringifyEntityRef(entity)) ?? true;\n }\n // used only for retro-compatibility with non paginated data.\n // This is supposed to return always true for paginated\n // owned entities, since the filters are applied server side.\n if (this.value === 'owned') {\n const relations = getEntityRelations(entity, RELATION_OWNED_BY);\n\n return (\n this.refs?.some(v =>\n relations.some(o => stringifyEntityRef(o) === v),\n ) ?? false\n );\n }\n return true;\n }\n\n toQueryValue(): string {\n return this.value;\n }\n}\n\n/**\n * Filters entities based on whatever the user has starred or owns them.\n * @deprecated use EntityUserFilter\n * @public\n */\nexport class UserListFilter implements EntityFilter {\n readonly value: UserListFilterKind;\n readonly isOwnedEntity: (entity: Entity) => boolean;\n readonly isStarredEntity: (entity: Entity) => boolean;\n\n constructor(\n value: UserListFilterKind,\n isOwnedEntity: (entity: Entity) => boolean,\n isStarredEntity: (entity: Entity) => boolean,\n ) {\n this.value = value;\n this.isOwnedEntity = isOwnedEntity;\n this.isStarredEntity = isStarredEntity;\n }\n\n filterEntity(entity: Entity): boolean {\n switch (this.value) {\n case 'owned':\n return this.isOwnedEntity(entity);\n case 'starred':\n return this.isStarredEntity(entity);\n default:\n return true;\n }\n }\n\n toQueryValue(): string {\n return this.value;\n }\n}\n\n/**\n * Filters entities based if it is an orphan or not.\n * @public\n */\nexport class EntityOrphanFilter implements EntityFilter {\n readonly value: boolean;\n\n constructor(value: boolean) {\n this.value = value;\n }\n\n getCatalogQuery(): FilterPredicate | undefined {\n if (this.value) {\n return { 'metadata.annotations.backstage.io/orphan': 'true' };\n }\n return undefined;\n }\n\n /**\n * @deprecated Use `getCatalogQuery` instead.\n */\n getCatalogFilters(): Record<string, string | string[]> {\n if (this.value) {\n return { 'metadata.annotations.backstage.io/orphan': String(this.value) };\n }\n return {};\n }\n\n filterEntity(entity: Entity): boolean {\n const orphan = entity.metadata.annotations?.['backstage.io/orphan'];\n return orphan !== undefined && this.value.toString() === orphan;\n }\n}\n\n/**\n * Filters entities based on if it has errors or not.\n * @public\n */\nexport class EntityErrorFilter implements EntityFilter {\n readonly value: boolean;\n\n constructor(value: boolean) {\n this.value = value;\n }\n\n filterEntity(entity: Entity): boolean {\n const error =\n ((entity as AlphaEntity)?.status?.items?.length as number) > 0;\n return error !== undefined && this.value === error;\n }\n}\n\n/**\n * Sort entities by a given field/column.\n * @public\n */\nexport class EntityOrderFilter implements EntityFilter {\n readonly values: [string, 'asc' | 'desc'][];\n\n constructor(values: [string, 'asc' | 'desc'][]) {\n this.values = values;\n }\n\n getOrderFilters(): EntityOrderQuery {\n return this.values.map(([field, order]) => ({ field, order }));\n }\n\n toQueryValue(): string[] {\n return this.values.flat();\n }\n}\n"],"names":[],"mappings":"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"}
@@ -102,15 +102,22 @@ const EntityListProvider = (props) => {
102
102
  };
103
103
  }
104
104
  } else {
105
- const backendFilter = reduceBackendCatalogFilters(compacted);
105
+ const constraints = reduceBackendCatalogFilters(compacted);
106
106
  const { orderFields } = reduceCatalogFilters(compacted);
107
- fetchParams = { filter: backendFilter, order: orderFields };
108
- doFetch = async () => {
109
- const response = await catalogApi.getEntities({
110
- filter: backendFilter,
111
- order: orderFields
112
- });
113
- return { backendEntities: response.items };
107
+ fetchParams = { ...constraints, orderFields };
108
+ doFetch = async (isStale) => {
109
+ const backendEntities = [];
110
+ for await (const page of catalogApi.streamEntities({
111
+ ...constraints,
112
+ orderFields,
113
+ totalItems: "exclude"
114
+ })) {
115
+ if (isStale()) {
116
+ break;
117
+ }
118
+ backendEntities.push(...page);
119
+ }
120
+ return { backendEntities };
114
121
  };
115
122
  }
116
123
  if (compacted.length === 0 && lastFetchParamsRef.current === void 0 && !cursor) {
@@ -125,7 +132,7 @@ const EntityListProvider = (props) => {
125
132
  setLoading(true);
126
133
  setError(void 0);
127
134
  try {
128
- const result = await doFetch();
135
+ const result = await doFetch(() => gen !== fetchGenRef.current);
129
136
  if (gen === fetchGenRef.current) {
130
137
  setBackendState(result);
131
138
  }
@@ -196,7 +203,10 @@ const EntityListProvider = (props) => {
196
203
  { addQueryPrefix: true, arrayFormat: "repeat" }
197
204
  );
198
205
  const newUrl = `${window.location.pathname}${newParams}`;
199
- window.history?.replaceState(null, document.title, newUrl);
206
+ const currentUrl = `${window.location.pathname}${window.location.search}`;
207
+ if (newUrl !== currentUrl) {
208
+ window.history?.replaceState(null, document.title, newUrl);
209
+ }
200
210
  }, [
201
211
  cursor,
202
212
  isMounted,
@@ -1 +1 @@
1
- {"version":3,"file":"useEntityListProvider.esm.js","sources":["../../src/hooks/useEntityListProvider.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { QueryEntitiesResponse } from '@backstage/catalog-client';\nimport { Entity } from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport {\n createVersionedContext,\n createVersionedValueMap,\n useVersionedContext,\n} from '@backstage/version-bridge';\nimport { compact, isEqual } from 'lodash';\nimport qs from 'qs';\nimport {\n createContext,\n PropsWithChildren,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useLocation } from 'react-router-dom';\nimport useAsyncFn from 'react-use/esm/useAsyncFn';\nimport useDebounce from 'react-use/esm/useDebounce';\nimport useMountedState from 'react-use/esm/useMountedState';\nimport { catalogApiRef } from '../api';\nimport {\n EntityErrorFilter,\n EntityKindFilter,\n EntityLifecycleFilter,\n EntityNamespaceFilter,\n EntityOrderFilter,\n EntityOrphanFilter,\n EntityOwnerFilter,\n EntityTagFilter,\n EntityTextFilter,\n EntityTypeFilter,\n EntityUserFilter,\n UserListFilter,\n} from '../filters';\nimport { EntityFilter, EntityListPagination } from '../types';\nimport {\n reduceBackendCatalogFilters,\n reduceCatalogFilters,\n reduceEntityFilters,\n} from '../utils/filters';\n\n/** @public */\nexport type DefaultEntityFilters = {\n kind?: EntityKindFilter;\n type?: EntityTypeFilter;\n user?: UserListFilter | EntityUserFilter;\n owners?: EntityOwnerFilter;\n lifecycles?: EntityLifecycleFilter;\n tags?: EntityTagFilter;\n text?: EntityTextFilter;\n orphan?: EntityOrphanFilter;\n error?: EntityErrorFilter;\n namespace?: EntityNamespaceFilter;\n order?: EntityOrderFilter;\n};\n\n/** @public */\nexport type PaginationMode = 'cursor' | 'offset' | 'none';\n\n/** @public */\nexport type EntityListContextProps<\n EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,\n> = {\n /**\n * The currently registered filters, adhering to the shape of DefaultEntityFilters or an extension\n * of that default (to add custom filter types).\n */\n filters: EntityFilters;\n\n /**\n * The resolved list of catalog entities, after all filters are applied.\n */\n entities: Entity[];\n\n /**\n * The resolved list of catalog entities, after _only catalog-backend_ filters are applied.\n */\n backendEntities: Entity[];\n\n /**\n * Update one or more of the registered filters. Optional filters can be set to `undefined` to\n * reset the filter.\n */\n updateFilters: (\n filters:\n | Partial<EntityFilters>\n | ((prevFilters: EntityFilters) => Partial<EntityFilters>),\n ) => void;\n\n /**\n * Filter values from query parameters.\n */\n queryParameters: Partial<Record<keyof EntityFilters, string | string[]>>;\n\n loading: boolean;\n error?: Error;\n\n pageInfo?: {\n next?: () => void;\n prev?: () => void;\n };\n totalItems?: number;\n totalItemsLoading: boolean;\n limit: number;\n offset?: number;\n setLimit: (limit: number) => void;\n setOffset?: (offset: number) => void;\n paginationMode: PaginationMode;\n\n /**\n * Trigger refresh of entities and entity totals.\n */\n refresh?: () => void;\n};\n\n// This context has support for multiple concurrent versions of this package.\n// It is currently used in parallel with the old context in order to provide\n// a smooth transition, but will eventually be the only context we use.\nexport const NewEntityListContext = createVersionedContext<{\n 1: EntityListContextProps<any>;\n}>('entity-list-context');\n\n/**\n * Creates new context for entity listing and filtering.\n */\nexport const OldEntityListContext = createContext<\n EntityListContextProps<any> | undefined\n>(undefined);\n\ntype BackendState = {\n backendEntities: Entity[];\n pageInfo?: QueryEntitiesResponse['pageInfo'];\n totalItems?: number;\n};\n\n/**\n * @public\n */\nexport type EntityListProviderProps = PropsWithChildren<{\n pagination?: EntityListPagination;\n}>;\n\n/**\n * Provides entities and filters for a catalog listing.\n * @public\n */\nexport const EntityListProvider = <EntityFilters extends DefaultEntityFilters>(\n props: EntityListProviderProps,\n) => {\n const isMounted = useMountedState();\n const catalogApi = useApi(catalogApiRef);\n const [requestedFilters, setRequestedFilters] = useState<EntityFilters>(\n {} as EntityFilters,\n );\n\n // We use react-router's useLocation hook so updates from external sources trigger an update to\n // the queryParameters in outputState. Updates from this hook use replaceState below and won't\n // trigger a useLocation change; this would instead come from an external source, such as a manual\n // update of the URL or two catalog sidebar links with different catalog filters.\n const location = useLocation();\n\n let paginationMode: PaginationMode = 'none';\n if (props.pagination === true) {\n paginationMode = 'cursor';\n } else if (typeof props.pagination === 'object') {\n paginationMode = props.pagination.mode ?? 'cursor';\n }\n const paginationLimit =\n typeof props.pagination === 'object' ? props.pagination.limit ?? 20 : 20;\n\n const {\n queryParameters,\n cursor: initialCursor,\n offset: initialOffset,\n limit: initialLimit,\n } = useMemo(() => {\n const parsed = qs.parse(location.search, {\n ignoreQueryPrefix: true,\n arrayLimit: 10000,\n });\n\n let limit = paginationLimit;\n if (typeof parsed.limit === 'string') {\n const queryLimit = Number.parseInt(parsed.limit, 10);\n if (!isNaN(queryLimit)) {\n limit = queryLimit;\n }\n }\n\n const offset =\n typeof parsed.offset === 'string' && paginationMode === 'offset'\n ? Number.parseInt(parsed.offset, 10)\n : undefined;\n\n return {\n queryParameters: (parsed.filters ?? {}) as Record<\n string,\n string | string[]\n >,\n cursor:\n typeof parsed.cursor === 'string' && paginationMode === 'cursor'\n ? parsed.cursor\n : undefined,\n offset:\n paginationMode === 'offset' && offset && !isNaN(offset)\n ? offset\n : undefined,\n limit,\n };\n }, [paginationMode, location.search, paginationLimit]);\n\n const [cursor, setCursor] = useState(initialCursor);\n const [offset, setOffset] = useState<number | undefined>(initialOffset);\n const [limit, setLimit] = useState(initialLimit);\n\n const [backendState, setBackendState] = useState<BackendState>({\n backendEntities: [],\n });\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | undefined>();\n const [refreshToken, setRefreshToken] = useState(0);\n\n // Tracks the params of the last API call so identical requests are\n // skipped even when requestedFilters changes (e.g. a label change\n // or a frontend-only filter addition).\n const lastFetchParamsRef = useRef<unknown>(undefined);\n // Generation counter — only the most recent fetch updates state,\n // so out-of-order responses from overlapping requests are discarded.\n const fetchGenRef = useRef(0);\n\n // Adjusted filters remove the owners filter for user/group kinds,\n // since ownership is not meaningful for those entity types.\n const adjustedFilters = useMemo(() => {\n const kindValue = requestedFilters.kind?.value?.toLowerCase();\n return kindValue === 'user' || kindValue === 'group'\n ? { ...requestedFilters, owners: undefined }\n : requestedFilters;\n }, [requestedFilters]);\n\n const refresh = async () => {\n const compacted = compact(Object.values(adjustedFilters));\n\n let fetchParams: unknown;\n let doFetch: () => Promise<BackendState>;\n\n if (paginationMode !== 'none') {\n if (cursor) {\n fetchParams = { cursor, limit };\n doFetch = async () => {\n const response = await catalogApi.queryEntities({\n cursor,\n limit,\n totalItems: 'exclude',\n });\n return {\n backendEntities: response.items,\n pageInfo: response.pageInfo,\n };\n };\n } else {\n const backendFilter = reduceCatalogFilters(compacted);\n fetchParams = { ...backendFilter, limit, offset };\n doFetch = async () => {\n const response = await catalogApi.queryEntities({\n ...backendFilter,\n limit,\n offset,\n totalItems: 'exclude',\n });\n return {\n backendEntities: response.items,\n pageInfo: response.pageInfo,\n };\n };\n }\n } else {\n const backendFilter = reduceBackendCatalogFilters(compacted);\n const { orderFields } = reduceCatalogFilters(compacted);\n fetchParams = { filter: backendFilter, order: orderFields };\n doFetch = async () => {\n const response = await catalogApi.getEntities({\n filter: backendFilter,\n order: orderFields,\n });\n return { backendEntities: response.items };\n };\n }\n\n // No filters registered yet — wait for filter components to\n // call updateFilters before making the first request.\n // Exception: a cursor in the URL is a self-contained page reference\n // that doesn't need filter state to be valid.\n if (\n compacted.length === 0 &&\n lastFetchParamsRef.current === undefined &&\n !cursor\n ) {\n setLoading(false);\n return;\n }\n\n if (isEqual(fetchParams, lastFetchParamsRef.current)) {\n return;\n }\n lastFetchParamsRef.current = fetchParams;\n\n const gen = ++fetchGenRef.current;\n setLoading(true);\n setError(undefined);\n\n try {\n const result = await doFetch();\n if (gen === fetchGenRef.current) {\n setBackendState(result);\n }\n } catch (e) {\n if (gen === fetchGenRef.current) {\n // Clear the ref so the same params can be retried, and so\n // a response discarded due to a concurrent request (gen mismatch)\n // doesn't permanently block fetching those params again.\n lastFetchParamsRef.current = undefined;\n setError(e as Error);\n }\n } finally {\n if (gen === fetchGenRef.current) {\n setLoading(false);\n }\n }\n };\n\n // Slight debounce on the refresh, since (especially on page load)\n // several filters will be calling updateFilters in rapid succession.\n useDebounce(refresh, 10, [\n adjustedFilters,\n cursor,\n limit,\n offset,\n refreshToken,\n ]);\n\n // Fetch the total count separately, only when filters change. This is\n // decoupled from the main list fetch so that page navigation doesn't\n // re-run the expensive count query, and so that the count can arrive\n // asynchronously without blocking the list response.\n const [{ value: totalItems, loading: totalItemsLoading }, refreshCount] =\n useAsyncFn(async () => {\n if (paginationMode === 'none') {\n return undefined;\n }\n const compacted = compact(Object.values(adjustedFilters));\n if (compacted.length === 0) {\n return undefined;\n }\n const backendFilter = reduceCatalogFilters(compacted);\n try {\n const response = await catalogApi.queryEntities({\n ...backendFilter,\n limit: 0,\n });\n return response.totalItems;\n } catch {\n return undefined;\n }\n }, [catalogApi, paginationMode, adjustedFilters]);\n\n useDebounce(refreshCount, 10, [adjustedFilters, refreshToken]);\n\n // Frontend filtering — synchronous, no debounce needed. Updates\n // instantly when requestedFilters or backendEntities change.\n const entities = useMemo(() => {\n const compacted = compact(Object.values(adjustedFilters));\n const entityFilter = reduceEntityFilters(compacted);\n return backendState.backendEntities.filter(entityFilter);\n }, [adjustedFilters, backendState.backendEntities]);\n\n // Sync filter state to URL query parameters. We use direct history\n // manipulation since useSearchParams and useNavigate in\n // react-router-dom cause unnecessary extra rerenders. Also make sure\n // to replace the state rather than pushing, since we don't want\n // there to be back/forward slots for every single filter change.\n useEffect(() => {\n if (!isMounted() || Object.keys(requestedFilters).length === 0) {\n return;\n }\n const queryParams = Object.keys(requestedFilters).reduce((params, key) => {\n const filter = requestedFilters[key as keyof EntityFilters] as\n | EntityFilter\n | undefined;\n if (filter?.toQueryValue) {\n params[key] = filter.toQueryValue();\n }\n return params;\n }, {} as Record<string, string | string[]>);\n\n const oldParams = qs.parse(location.search, {\n ignoreQueryPrefix: true,\n arrayLimit: 10000,\n });\n const newParams = qs.stringify(\n {\n ...oldParams,\n filters: queryParams,\n ...(paginationMode === 'none' ? {} : { cursor, limit, offset }),\n },\n { addQueryPrefix: true, arrayFormat: 'repeat' },\n );\n const newUrl = `${window.location.pathname}${newParams}`;\n window.history?.replaceState(null, document.title, newUrl);\n }, [\n cursor,\n isMounted,\n limit,\n location.search,\n offset,\n requestedFilters,\n paginationMode,\n ]);\n\n const updateFilters = useCallback(\n (\n update:\n | Partial<EntityFilter>\n | ((prevFilters: EntityFilters) => Partial<EntityFilters>),\n ) => {\n // changing filters will affect pagination, so we need to reset\n // the cursor and start from the first page.\n // TODO(vinzscam): this is currently causing issues at page reload\n // where the state is not kept. Unfortunately we need to rethink\n // the way filters work in order to fix this.\n if (paginationMode === 'cursor') {\n setCursor(undefined);\n } else if (paginationMode === 'offset') {\n // Same thing with offset\n setOffset(0);\n }\n setRequestedFilters(prevFilters => {\n const newFilters =\n typeof update === 'function' ? update(prevFilters) : update;\n return { ...prevFilters, ...newFilters };\n });\n },\n [paginationMode],\n );\n\n const triggerRefresh = useCallback(() => {\n // Clear the ref to allow refetching with the same params.\n lastFetchParamsRef.current = undefined;\n setRefreshToken(t => t + 1);\n }, []);\n\n const pageInfo = useMemo(() => {\n if (paginationMode !== 'cursor') {\n return undefined;\n }\n\n const prevCursor = backendState.pageInfo?.prevCursor;\n const nextCursor = backendState.pageInfo?.nextCursor;\n return {\n prev: prevCursor ? () => setCursor(prevCursor) : undefined,\n next: nextCursor ? () => setCursor(nextCursor) : undefined,\n };\n }, [paginationMode, backendState.pageInfo]);\n\n const value = useMemo(\n () => ({\n filters: requestedFilters,\n entities,\n backendEntities: backendState.backendEntities,\n updateFilters,\n queryParameters,\n loading,\n error,\n pageInfo,\n totalItems:\n paginationMode === 'none'\n ? entities.length\n : totalItems ?? backendState.totalItems,\n totalItemsLoading: paginationMode !== 'none' && totalItemsLoading,\n limit,\n offset,\n setLimit,\n setOffset,\n paginationMode,\n refresh: triggerRefresh,\n }),\n [\n requestedFilters,\n entities,\n backendState,\n totalItems,\n totalItemsLoading,\n updateFilters,\n queryParameters,\n loading,\n error,\n pageInfo,\n paginationMode,\n limit,\n offset,\n setLimit,\n setOffset,\n triggerRefresh,\n ],\n );\n\n return (\n <OldEntityListContext.Provider value={value}>\n <NewEntityListContext.Provider\n value={createVersionedValueMap({ 1: value })}\n >\n {props.children}\n </NewEntityListContext.Provider>\n </OldEntityListContext.Provider>\n );\n};\n\n/**\n * Hook for interacting with the entity list context provided by the {@link EntityListProvider}.\n * @public\n */\nexport function useEntityList<\n EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,\n>(): EntityListContextProps<EntityFilters> {\n const versionedHolder = useVersionedContext<{\n 1: EntityListContextProps<any>;\n }>('entity-list-context');\n const oldContext = useContext(OldEntityListContext);\n\n if (versionedHolder) {\n const value = versionedHolder.atVersion(1);\n if (!value) {\n throw new Error('EntityListContext v1 not available');\n }\n return value;\n }\n\n if (oldContext) {\n return oldContext;\n }\n\n throw new Error('useEntityList must be used within EntityListProvider');\n}\n"],"names":["limit","offset"],"mappings":"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"}
1
+ {"version":3,"file":"useEntityListProvider.esm.js","sources":["../../src/hooks/useEntityListProvider.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { QueryEntitiesResponse } from '@backstage/catalog-client';\nimport { Entity } from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport {\n createVersionedContext,\n createVersionedValueMap,\n useVersionedContext,\n} from '@backstage/version-bridge';\nimport { compact, isEqual } from 'lodash';\nimport qs from 'qs';\nimport {\n createContext,\n PropsWithChildren,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useLocation } from 'react-router-dom';\nimport useAsyncFn from 'react-use/esm/useAsyncFn';\nimport useDebounce from 'react-use/esm/useDebounce';\nimport useMountedState from 'react-use/esm/useMountedState';\nimport { catalogApiRef } from '../api';\nimport {\n EntityErrorFilter,\n EntityKindFilter,\n EntityLifecycleFilter,\n EntityNamespaceFilter,\n EntityOrderFilter,\n EntityOrphanFilter,\n EntityOwnerFilter,\n EntityTagFilter,\n EntityTextFilter,\n EntityTypeFilter,\n EntityUserFilter,\n UserListFilter,\n} from '../filters';\nimport { EntityFilter, EntityListPagination } from '../types';\nimport {\n reduceBackendCatalogFilters,\n reduceCatalogFilters,\n reduceEntityFilters,\n} from '../utils/filters';\n\n/** @public */\nexport type DefaultEntityFilters = {\n kind?: EntityKindFilter;\n type?: EntityTypeFilter;\n user?: UserListFilter | EntityUserFilter;\n owners?: EntityOwnerFilter;\n lifecycles?: EntityLifecycleFilter;\n tags?: EntityTagFilter;\n text?: EntityTextFilter;\n orphan?: EntityOrphanFilter;\n error?: EntityErrorFilter;\n namespace?: EntityNamespaceFilter;\n order?: EntityOrderFilter;\n};\n\n/** @public */\nexport type PaginationMode = 'cursor' | 'offset' | 'none';\n\n/** @public */\nexport type EntityListContextProps<\n EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,\n> = {\n /**\n * The currently registered filters, adhering to the shape of DefaultEntityFilters or an extension\n * of that default (to add custom filter types).\n */\n filters: EntityFilters;\n\n /**\n * The resolved list of catalog entities, after all filters are applied.\n */\n entities: Entity[];\n\n /**\n * The resolved list of catalog entities, after _only catalog-backend_ filters are applied.\n */\n backendEntities: Entity[];\n\n /**\n * Update one or more of the registered filters. Optional filters can be set to `undefined` to\n * reset the filter.\n */\n updateFilters: (\n filters:\n | Partial<EntityFilters>\n | ((prevFilters: EntityFilters) => Partial<EntityFilters>),\n ) => void;\n\n /**\n * Filter values from query parameters.\n */\n queryParameters: Partial<Record<keyof EntityFilters, string | string[]>>;\n\n loading: boolean;\n error?: Error;\n\n pageInfo?: {\n next?: () => void;\n prev?: () => void;\n };\n totalItems?: number;\n totalItemsLoading: boolean;\n limit: number;\n offset?: number;\n setLimit: (limit: number) => void;\n setOffset?: (offset: number) => void;\n paginationMode: PaginationMode;\n\n /**\n * Trigger refresh of entities and entity totals.\n */\n refresh?: () => void;\n};\n\n// This context has support for multiple concurrent versions of this package.\n// It is currently used in parallel with the old context in order to provide\n// a smooth transition, but will eventually be the only context we use.\nexport const NewEntityListContext = createVersionedContext<{\n 1: EntityListContextProps<any>;\n}>('entity-list-context');\n\n/**\n * Creates new context for entity listing and filtering.\n */\nexport const OldEntityListContext = createContext<\n EntityListContextProps<any> | undefined\n>(undefined);\n\ntype BackendState = {\n backendEntities: Entity[];\n pageInfo?: QueryEntitiesResponse['pageInfo'];\n totalItems?: number;\n};\n\n/**\n * @public\n */\nexport type EntityListProviderProps = PropsWithChildren<{\n pagination?: EntityListPagination;\n}>;\n\n/**\n * Provides entities and filters for a catalog listing.\n * @public\n */\nexport const EntityListProvider = <EntityFilters extends DefaultEntityFilters>(\n props: EntityListProviderProps,\n) => {\n const isMounted = useMountedState();\n const catalogApi = useApi(catalogApiRef);\n const [requestedFilters, setRequestedFilters] = useState<EntityFilters>(\n {} as EntityFilters,\n );\n\n // We use react-router's useLocation hook so updates from external sources trigger an update to\n // the queryParameters in outputState. Updates from this hook use replaceState below and won't\n // trigger a useLocation change; this would instead come from an external source, such as a manual\n // update of the URL or two catalog sidebar links with different catalog filters.\n const location = useLocation();\n\n let paginationMode: PaginationMode = 'none';\n if (props.pagination === true) {\n paginationMode = 'cursor';\n } else if (typeof props.pagination === 'object') {\n paginationMode = props.pagination.mode ?? 'cursor';\n }\n const paginationLimit =\n typeof props.pagination === 'object' ? props.pagination.limit ?? 20 : 20;\n\n const {\n queryParameters,\n cursor: initialCursor,\n offset: initialOffset,\n limit: initialLimit,\n } = useMemo(() => {\n const parsed = qs.parse(location.search, {\n ignoreQueryPrefix: true,\n arrayLimit: 10000,\n });\n\n let limit = paginationLimit;\n if (typeof parsed.limit === 'string') {\n const queryLimit = Number.parseInt(parsed.limit, 10);\n if (!isNaN(queryLimit)) {\n limit = queryLimit;\n }\n }\n\n const offset =\n typeof parsed.offset === 'string' && paginationMode === 'offset'\n ? Number.parseInt(parsed.offset, 10)\n : undefined;\n\n return {\n queryParameters: (parsed.filters ?? {}) as Record<\n string,\n string | string[]\n >,\n cursor:\n typeof parsed.cursor === 'string' && paginationMode === 'cursor'\n ? parsed.cursor\n : undefined,\n offset:\n paginationMode === 'offset' && offset && !isNaN(offset)\n ? offset\n : undefined,\n limit,\n };\n }, [paginationMode, location.search, paginationLimit]);\n\n const [cursor, setCursor] = useState(initialCursor);\n const [offset, setOffset] = useState<number | undefined>(initialOffset);\n const [limit, setLimit] = useState(initialLimit);\n\n const [backendState, setBackendState] = useState<BackendState>({\n backendEntities: [],\n });\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | undefined>();\n const [refreshToken, setRefreshToken] = useState(0);\n\n // Tracks the params of the last API call so identical requests are\n // skipped even when requestedFilters changes (e.g. a label change\n // or a frontend-only filter addition).\n const lastFetchParamsRef = useRef<unknown>(undefined);\n // Generation counter — only the most recent fetch updates state,\n // so out-of-order responses from overlapping requests are discarded.\n const fetchGenRef = useRef(0);\n\n // Adjusted filters remove the owners filter for user/group kinds,\n // since ownership is not meaningful for those entity types.\n const adjustedFilters = useMemo(() => {\n const kindValue = requestedFilters.kind?.value?.toLowerCase();\n return kindValue === 'user' || kindValue === 'group'\n ? { ...requestedFilters, owners: undefined }\n : requestedFilters;\n }, [requestedFilters]);\n\n const refresh = async () => {\n const compacted = compact(Object.values(adjustedFilters));\n\n let fetchParams: unknown;\n let doFetch: (isStale: () => boolean) => Promise<BackendState>;\n\n if (paginationMode !== 'none') {\n if (cursor) {\n fetchParams = { cursor, limit };\n doFetch = async () => {\n const response = await catalogApi.queryEntities({\n cursor,\n limit,\n totalItems: 'exclude',\n });\n return {\n backendEntities: response.items,\n pageInfo: response.pageInfo,\n };\n };\n } else {\n const backendFilter = reduceCatalogFilters(compacted);\n fetchParams = { ...backendFilter, limit, offset };\n doFetch = async () => {\n const response = await catalogApi.queryEntities({\n ...backendFilter,\n limit,\n offset,\n totalItems: 'exclude',\n });\n return {\n backendEntities: response.items,\n pageInfo: response.pageInfo,\n };\n };\n }\n } else {\n const constraints = reduceBackendCatalogFilters(compacted);\n const { orderFields } = reduceCatalogFilters(compacted);\n fetchParams = { ...constraints, orderFields };\n doFetch = async isStale => {\n const backendEntities: Entity[] = [];\n for await (const page of catalogApi.streamEntities({\n ...constraints,\n orderFields,\n totalItems: 'exclude',\n })) {\n // Stop traversing the catalog once a newer request supersedes this one\n if (isStale()) {\n break;\n }\n backendEntities.push(...page);\n }\n return { backendEntities };\n };\n }\n\n // No filters registered yet — wait for filter components to\n // call updateFilters before making the first request.\n // Exception: a cursor in the URL is a self-contained page reference\n // that doesn't need filter state to be valid.\n if (\n compacted.length === 0 &&\n lastFetchParamsRef.current === undefined &&\n !cursor\n ) {\n setLoading(false);\n return;\n }\n\n if (isEqual(fetchParams, lastFetchParamsRef.current)) {\n return;\n }\n lastFetchParamsRef.current = fetchParams;\n\n const gen = ++fetchGenRef.current;\n setLoading(true);\n setError(undefined);\n\n try {\n const result = await doFetch(() => gen !== fetchGenRef.current);\n if (gen === fetchGenRef.current) {\n setBackendState(result);\n }\n } catch (e) {\n if (gen === fetchGenRef.current) {\n // Clear the ref so the same params can be retried, and so\n // a response discarded due to a concurrent request (gen mismatch)\n // doesn't permanently block fetching those params again.\n lastFetchParamsRef.current = undefined;\n setError(e as Error);\n }\n } finally {\n if (gen === fetchGenRef.current) {\n setLoading(false);\n }\n }\n };\n\n // Slight debounce on the refresh, since (especially on page load)\n // several filters will be calling updateFilters in rapid succession.\n useDebounce(refresh, 10, [\n adjustedFilters,\n cursor,\n limit,\n offset,\n refreshToken,\n ]);\n\n // Fetch the total count separately, only when filters change. This is\n // decoupled from the main list fetch so that page navigation doesn't\n // re-run the expensive count query, and so that the count can arrive\n // asynchronously without blocking the list response.\n const [{ value: totalItems, loading: totalItemsLoading }, refreshCount] =\n useAsyncFn(async () => {\n if (paginationMode === 'none') {\n return undefined;\n }\n const compacted = compact(Object.values(adjustedFilters));\n if (compacted.length === 0) {\n return undefined;\n }\n const backendFilter = reduceCatalogFilters(compacted);\n try {\n const response = await catalogApi.queryEntities({\n ...backendFilter,\n limit: 0,\n });\n return response.totalItems;\n } catch {\n return undefined;\n }\n }, [catalogApi, paginationMode, adjustedFilters]);\n\n useDebounce(refreshCount, 10, [adjustedFilters, refreshToken]);\n\n // Frontend filtering — synchronous, no debounce needed. Updates\n // instantly when requestedFilters or backendEntities change.\n const entities = useMemo(() => {\n const compacted = compact(Object.values(adjustedFilters));\n const entityFilter = reduceEntityFilters(compacted);\n return backendState.backendEntities.filter(entityFilter);\n }, [adjustedFilters, backendState.backendEntities]);\n\n // Sync filter state to URL query parameters. We use direct history\n // manipulation since useSearchParams and useNavigate in\n // react-router-dom cause unnecessary extra rerenders. Also make sure\n // to replace the state rather than pushing, since we don't want\n // there to be back/forward slots for every single filter change.\n useEffect(() => {\n if (!isMounted() || Object.keys(requestedFilters).length === 0) {\n return;\n }\n const queryParams = Object.keys(requestedFilters).reduce((params, key) => {\n const filter = requestedFilters[key as keyof EntityFilters] as\n | EntityFilter\n | undefined;\n if (filter?.toQueryValue) {\n params[key] = filter.toQueryValue();\n }\n return params;\n }, {} as Record<string, string | string[]>);\n\n const oldParams = qs.parse(location.search, {\n ignoreQueryPrefix: true,\n arrayLimit: 10000,\n });\n const newParams = qs.stringify(\n {\n ...oldParams,\n filters: queryParams,\n ...(paginationMode === 'none' ? {} : { cursor, limit, offset }),\n },\n { addQueryPrefix: true, arrayFormat: 'repeat' },\n );\n const newUrl = `${window.location.pathname}${newParams}`;\n const currentUrl = `${window.location.pathname}${window.location.search}`;\n // Only rewrite the URL when it actually changes. Some filters (e.g. the\n // user filter with `?filters[user]=all`) produce a new requestedFilters\n // object on every render while serializing to the same URL; without this\n // guard replaceState is called repeatedly, which crashes Safari with a\n // SecurityError once it exceeds its replaceState rate limit.\n if (newUrl !== currentUrl) {\n window.history?.replaceState(null, document.title, newUrl);\n }\n }, [\n cursor,\n isMounted,\n limit,\n location.search,\n offset,\n requestedFilters,\n paginationMode,\n ]);\n\n const updateFilters = useCallback(\n (\n update:\n | Partial<EntityFilter>\n | ((prevFilters: EntityFilters) => Partial<EntityFilters>),\n ) => {\n // changing filters will affect pagination, so we need to reset\n // the cursor and start from the first page.\n // TODO(vinzscam): this is currently causing issues at page reload\n // where the state is not kept. Unfortunately we need to rethink\n // the way filters work in order to fix this.\n if (paginationMode === 'cursor') {\n setCursor(undefined);\n } else if (paginationMode === 'offset') {\n // Same thing with offset\n setOffset(0);\n }\n setRequestedFilters(prevFilters => {\n const newFilters =\n typeof update === 'function' ? update(prevFilters) : update;\n return { ...prevFilters, ...newFilters };\n });\n },\n [paginationMode],\n );\n\n const triggerRefresh = useCallback(() => {\n // Clear the ref to allow refetching with the same params.\n lastFetchParamsRef.current = undefined;\n setRefreshToken(t => t + 1);\n }, []);\n\n const pageInfo = useMemo(() => {\n if (paginationMode !== 'cursor') {\n return undefined;\n }\n\n const prevCursor = backendState.pageInfo?.prevCursor;\n const nextCursor = backendState.pageInfo?.nextCursor;\n return {\n prev: prevCursor ? () => setCursor(prevCursor) : undefined,\n next: nextCursor ? () => setCursor(nextCursor) : undefined,\n };\n }, [paginationMode, backendState.pageInfo]);\n\n const value = useMemo(\n () => ({\n filters: requestedFilters,\n entities,\n backendEntities: backendState.backendEntities,\n updateFilters,\n queryParameters,\n loading,\n error,\n pageInfo,\n totalItems:\n paginationMode === 'none'\n ? entities.length\n : totalItems ?? backendState.totalItems,\n totalItemsLoading: paginationMode !== 'none' && totalItemsLoading,\n limit,\n offset,\n setLimit,\n setOffset,\n paginationMode,\n refresh: triggerRefresh,\n }),\n [\n requestedFilters,\n entities,\n backendState,\n totalItems,\n totalItemsLoading,\n updateFilters,\n queryParameters,\n loading,\n error,\n pageInfo,\n paginationMode,\n limit,\n offset,\n setLimit,\n setOffset,\n triggerRefresh,\n ],\n );\n\n return (\n <OldEntityListContext.Provider value={value}>\n <NewEntityListContext.Provider\n value={createVersionedValueMap({ 1: value })}\n >\n {props.children}\n </NewEntityListContext.Provider>\n </OldEntityListContext.Provider>\n );\n};\n\n/**\n * Hook for interacting with the entity list context provided by the {@link EntityListProvider}.\n * @public\n */\nexport function useEntityList<\n EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,\n>(): EntityListContextProps<EntityFilters> {\n const versionedHolder = useVersionedContext<{\n 1: EntityListContextProps<any>;\n }>('entity-list-context');\n const oldContext = useContext(OldEntityListContext);\n\n if (versionedHolder) {\n const value = versionedHolder.atVersion(1);\n if (!value) {\n throw new Error('EntityListContext v1 not available');\n }\n return value;\n }\n\n if (oldContext) {\n return oldContext;\n }\n\n throw new Error('useEntityList must be used within EntityListProvider');\n}\n"],"names":["limit","offset"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -12,6 +12,7 @@ import { OutlinedTextFieldProps, TextFieldProps } from '@material-ui/core/TextFi
12
12
  import { AutocompleteProps, AutocompleteRenderOptionState } from '@material-ui/lab/Autocomplete';
13
13
  import * as react from 'react';
14
14
  import { ReactNode, PropsWithChildren, ComponentProps } from 'react';
15
+ import { FilterPredicate } from '@backstage/filter-predicates';
15
16
  import { LinkProps, InfoCardVariants, TableColumn, TableOptions } from '@backstage/core-components';
16
17
  import { Overrides } from '@material-ui/core/styles/overrides';
17
18
  import { StyleRules } from '@material-ui/core/styles/withStyles';
@@ -329,8 +330,22 @@ type EntityFilter = {
329
330
  * value(s) to accept, extracted on the backend by parseEntityFilterParams. For example:
330
331
  * `{ field: 'kind', values: ['component'] }`
331
332
  * `{ field: 'metadata.name', values: ['component-1', 'component-2'] }`
333
+ *
334
+ * @deprecated Use `getCatalogQuery` instead. If a filter implements both,
335
+ * only `getCatalogQuery` is used.
332
336
  */
333
337
  getCatalogFilters?: () => Record<string, string | symbol | (string | symbol)[]>;
338
+ /**
339
+ * Get a filter predicate to add to the catalog-backend request. Predicates
340
+ * from all active filters are combined using `$all`. For example:
341
+ * `{ kind: 'component' }`
342
+ * `{ 'spec.type': { $in: ['service', 'website'] } }`
343
+ * `{ $not: { 'metadata.annotations.backstage.io/orphan': 'true' } }`
344
+ *
345
+ * Return `undefined` if the filter does not currently constrain the
346
+ * backend request.
347
+ */
348
+ getCatalogQuery?: () => FilterPredicate | undefined;
334
349
  /**
335
350
  * Filter entities on the frontend after a catalog-backend request. This function will be called
336
351
  * with each backend-resolved entity. This is used when frontend information is required for
@@ -697,6 +712,10 @@ declare class EntityKindFilter implements EntityFilter {
697
712
  readonly value: string;
698
713
  readonly label: string;
699
714
  constructor(value: string, label: string);
715
+ getCatalogQuery(): FilterPredicate;
716
+ /**
717
+ * @deprecated Use `getCatalogQuery` instead.
718
+ */
700
719
  getCatalogFilters(): Record<string, string | string[]>;
701
720
  toQueryValue(): string;
702
721
  }
@@ -708,6 +727,10 @@ declare class EntityTypeFilter implements EntityFilter {
708
727
  readonly value: string | string[];
709
728
  constructor(value: string | string[]);
710
729
  getTypes(): string[];
730
+ getCatalogQuery(): FilterPredicate | undefined;
731
+ /**
732
+ * @deprecated Use `getCatalogQuery` instead.
733
+ */
711
734
  getCatalogFilters(): Record<string, string | string[]>;
712
735
  toQueryValue(): string[];
713
736
  }
@@ -719,6 +742,10 @@ declare class EntityTagFilter implements EntityFilter {
719
742
  readonly values: string[];
720
743
  constructor(values: string[]);
721
744
  filterEntity(entity: Entity): boolean;
745
+ getCatalogQuery(): FilterPredicate | undefined;
746
+ /**
747
+ * @deprecated Use `getCatalogQuery` instead.
748
+ */
722
749
  getCatalogFilters(): Record<string, string | string[]>;
723
750
  toQueryValue(): string[];
724
751
  }
@@ -746,6 +773,10 @@ declare class EntityTextFilter implements EntityFilter {
746
773
  declare class EntityOwnerFilter implements EntityFilter {
747
774
  readonly values: string[];
748
775
  constructor(values: string[]);
776
+ getCatalogQuery(): FilterPredicate;
777
+ /**
778
+ * @deprecated Use `getCatalogQuery` instead.
779
+ */
749
780
  getCatalogFilters(): Record<string, string | string[]>;
750
781
  filterEntity(entity: Entity): boolean;
751
782
  /**
@@ -761,6 +792,10 @@ declare class EntityOwnerFilter implements EntityFilter {
761
792
  declare class EntityLifecycleFilter implements EntityFilter {
762
793
  readonly values: string[];
763
794
  constructor(values: string[]);
795
+ getCatalogQuery(): FilterPredicate;
796
+ /**
797
+ * @deprecated Use `getCatalogQuery` instead.
798
+ */
764
799
  getCatalogFilters(): Record<string, string | string[]>;
765
800
  filterEntity(entity: Entity): boolean;
766
801
  toQueryValue(): string[];
@@ -772,6 +807,10 @@ declare class EntityLifecycleFilter implements EntityFilter {
772
807
  declare class EntityNamespaceFilter implements EntityFilter {
773
808
  readonly values: string[];
774
809
  constructor(values: string[]);
810
+ getCatalogQuery(): FilterPredicate;
811
+ /**
812
+ * @deprecated Use `getCatalogQuery` instead.
813
+ */
775
814
  getCatalogFilters(): Record<string, string | string[]>;
776
815
  filterEntity(entity: Entity): boolean;
777
816
  toQueryValue(): string[];
@@ -786,6 +825,10 @@ declare class EntityUserFilter implements EntityFilter {
786
825
  static owned(ownershipEntityRefs: string[]): EntityUserFilter;
787
826
  static all(): EntityUserFilter;
788
827
  static starred(starredEntityRefs: string[]): EntityUserFilter;
828
+ getCatalogQuery(): FilterPredicate | undefined;
829
+ /**
830
+ * @deprecated Use `getCatalogQuery` instead.
831
+ */
789
832
  getCatalogFilters(): Record<string, string[]>;
790
833
  filterEntity(entity: Entity): boolean;
791
834
  toQueryValue(): string;
@@ -810,6 +853,10 @@ declare class UserListFilter implements EntityFilter {
810
853
  declare class EntityOrphanFilter implements EntityFilter {
811
854
  readonly value: boolean;
812
855
  constructor(value: boolean);
856
+ getCatalogQuery(): FilterPredicate | undefined;
857
+ /**
858
+ * @deprecated Use `getCatalogQuery` instead.
859
+ */
813
860
  getCatalogFilters(): Record<string, string | string[]>;
814
861
  filterEntity(entity: Entity): boolean;
815
862
  }
@@ -6,16 +6,33 @@ function isEntityTextFilter(t) {
6
6
  function isEntityOrderFilter(t) {
7
7
  return !!t.getOrderFilters;
8
8
  }
9
- function reduceCatalogFilters(filters) {
10
- const condensedFilters = filters.reduce(
11
- (compoundFilter, filter) => {
12
- return {
13
- ...compoundFilter,
14
- ...filter.getCatalogFilters ? filter.getCatalogFilters() : {}
15
- };
16
- },
17
- {}
9
+ function combinePredicates(predicates) {
10
+ const defined = predicates.filter(
11
+ (p) => p !== void 0
18
12
  );
13
+ if (defined.length === 0) {
14
+ return void 0;
15
+ }
16
+ return defined.length === 1 ? defined[0] : { $all: defined };
17
+ }
18
+ function reduceBackendConstraints(filters) {
19
+ const filter = {};
20
+ const predicates = [];
21
+ for (const f of filters) {
22
+ if (f.getCatalogQuery) {
23
+ predicates.push(f.getCatalogQuery());
24
+ } else if (f.getCatalogFilters) {
25
+ Object.assign(filter, f.getCatalogFilters());
26
+ }
27
+ }
28
+ const query = combinePredicates(predicates);
29
+ return {
30
+ ...Object.keys(filter).length > 0 && { filter },
31
+ ...query !== void 0 && { query }
32
+ };
33
+ }
34
+ function reduceCatalogFilters(filters) {
35
+ const constraints = reduceBackendConstraints(filters);
19
36
  const fullTextFilter = filters.find(isEntityTextFilter)?.getFullTextFilters();
20
37
  const orderFields = filters.find(isEntityOrderFilter)?.getOrderFilters() || [
21
38
  {
@@ -23,17 +40,18 @@ function reduceCatalogFilters(filters) {
23
40
  order: "asc"
24
41
  }
25
42
  ];
26
- return { filter: condensedFilters, fullTextFilter, orderFields };
43
+ return {
44
+ ...constraints,
45
+ fullTextFilter,
46
+ orderFields
47
+ };
27
48
  }
28
49
  function reduceBackendCatalogFilters(filters) {
29
- const backendCatalogFilters = {};
30
- filters.forEach((filter) => {
31
- if (filter instanceof EntityTagFilter || filter instanceof EntityOwnerFilter || filter instanceof EntityLifecycleFilter || filter instanceof EntityNamespaceFilter || filter instanceof EntityUserFilter || filter instanceof EntityOrphanFilter || filter instanceof EntityTextFilter || filter instanceof UserListFilter) {
32
- return;
33
- }
34
- Object.assign(backendCatalogFilters, filter.getCatalogFilters?.() || {});
35
- });
36
- return backendCatalogFilters;
50
+ return reduceBackendConstraints(
51
+ filters.filter(
52
+ (filter) => !(filter instanceof EntityTagFilter || filter instanceof EntityOwnerFilter || filter instanceof EntityLifecycleFilter || filter instanceof EntityNamespaceFilter || filter instanceof EntityUserFilter || filter instanceof EntityOrphanFilter || filter instanceof EntityTextFilter || filter instanceof UserListFilter)
53
+ )
54
+ );
37
55
  }
38
56
  function reduceEntityFilters(filters) {
39
57
  return (entity) => filters.every(
@@ -41,5 +59,5 @@ function reduceEntityFilters(filters) {
41
59
  );
42
60
  }
43
61
 
44
- export { reduceBackendCatalogFilters, reduceCatalogFilters, reduceEntityFilters };
62
+ export { combinePredicates, reduceBackendCatalogFilters, reduceCatalogFilters, reduceEntityFilters };
45
63
  //# sourceMappingURL=filters.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"filters.esm.js","sources":["../../src/utils/filters.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { EntityFilter } from '../types';\nimport {\n EntityLifecycleFilter,\n EntityNamespaceFilter,\n EntityOrderFilter,\n EntityOrphanFilter,\n EntityOwnerFilter,\n EntityTagFilter,\n EntityTextFilter,\n EntityUserFilter,\n UserListFilter,\n} from '../filters';\nimport { EntityOrderQuery } from '@backstage/catalog-client';\n\nexport interface CatalogFilters {\n filter: Record<string, string | symbol | (string | symbol)[]>;\n fullTextFilter?: {\n term: string;\n };\n orderFields?: EntityOrderQuery;\n}\n\nfunction isEntityTextFilter(t: EntityFilter): t is EntityTextFilter {\n return !!(t as EntityTextFilter).getFullTextFilters;\n}\n\nfunction isEntityOrderFilter(t: EntityFilter): t is EntityOrderFilter {\n return !!(t as EntityOrderFilter).getOrderFilters;\n}\n\nexport function reduceCatalogFilters(filters: EntityFilter[]): CatalogFilters {\n const condensedFilters = filters.reduce<CatalogFilters['filter']>(\n (compoundFilter, filter) => {\n return {\n ...compoundFilter,\n ...(filter.getCatalogFilters ? filter.getCatalogFilters() : {}),\n };\n },\n {},\n );\n\n const fullTextFilter = filters.find(isEntityTextFilter)?.getFullTextFilters();\n\n const orderFields = filters.find(isEntityOrderFilter)?.getOrderFilters() || [\n {\n field: 'metadata.name',\n order: 'asc',\n },\n ];\n return { filter: condensedFilters, fullTextFilter, orderFields };\n}\n\n/**\n * This function computes and returns an object containing the filters to be sent\n * to the backend. Any filter coming from `EntityKindFilter` and `EntityTypeFilter`, together\n * with custom filter set by the adopters is allowed. This function is used by `EntityListProvider`\n * and it won't be needed anymore in the future once pagination is implemented, as all the filters\n * will be applied backend-side.\n */\nexport function reduceBackendCatalogFilters(filters: EntityFilter[]) {\n const backendCatalogFilters: Record<\n string,\n string | symbol | (string | symbol)[]\n > = {};\n\n filters.forEach(filter => {\n if (\n filter instanceof EntityTagFilter ||\n filter instanceof EntityOwnerFilter ||\n filter instanceof EntityLifecycleFilter ||\n filter instanceof EntityNamespaceFilter ||\n filter instanceof EntityUserFilter ||\n filter instanceof EntityOrphanFilter ||\n filter instanceof EntityTextFilter ||\n filter instanceof UserListFilter\n ) {\n return;\n }\n Object.assign(backendCatalogFilters, filter.getCatalogFilters?.() || {});\n });\n\n return backendCatalogFilters;\n}\n\nexport function reduceEntityFilters(\n filters: EntityFilter[],\n): (entity: Entity) => boolean {\n return (entity: Entity) =>\n filters.every(\n filter => !filter.filterEntity || filter.filterEntity(entity),\n );\n}\n"],"names":[],"mappings":";;AAuCA,SAAS,mBAAmB,CAAA,EAAwC;AAClE,EAAA,OAAO,CAAC,CAAE,CAAA,CAAuB,kBAAA;AACnC;AAEA,SAAS,oBAAoB,CAAA,EAAyC;AACpE,EAAA,OAAO,CAAC,CAAE,CAAA,CAAwB,eAAA;AACpC;AAEO,SAAS,qBAAqB,OAAA,EAAyC;AAC5E,EAAA,MAAM,mBAAmB,OAAA,CAAQ,MAAA;AAAA,IAC/B,CAAC,gBAAgB,MAAA,KAAW;AAC1B,MAAA,OAAO;AAAA,QACL,GAAG,cAAA;AAAA,QACH,GAAI,MAAA,CAAO,iBAAA,GAAoB,MAAA,CAAO,iBAAA,KAAsB;AAAC,OAC/D;AAAA,IACF,CAAA;AAAA,IACA;AAAC,GACH;AAEA,EAAA,MAAM,cAAA,GAAiB,OAAA,CAAQ,IAAA,CAAK,kBAAkB,GAAG,kBAAA,EAAmB;AAE5E,EAAA,MAAM,cAAc,OAAA,CAAQ,IAAA,CAAK,mBAAmB,CAAA,EAAG,iBAAgB,IAAK;AAAA,IAC1E;AAAA,MACE,KAAA,EAAO,eAAA;AAAA,MACP,KAAA,EAAO;AAAA;AACT,GACF;AACA,EAAA,OAAO,EAAE,MAAA,EAAQ,gBAAA,EAAkB,cAAA,EAAgB,WAAA,EAAY;AACjE;AASO,SAAS,4BAA4B,OAAA,EAAyB;AACnE,EAAA,MAAM,wBAGF,EAAC;AAEL,EAAA,OAAA,CAAQ,QAAQ,CAAA,MAAA,KAAU;AACxB,IAAA,IACE,MAAA,YAAkB,eAAA,IAClB,MAAA,YAAkB,iBAAA,IAClB,kBAAkB,qBAAA,IAClB,MAAA,YAAkB,qBAAA,IAClB,MAAA,YAAkB,oBAClB,MAAA,YAAkB,kBAAA,IAClB,MAAA,YAAkB,gBAAA,IAClB,kBAAkB,cAAA,EAClB;AACA,MAAA;AAAA,IACF;AACA,IAAA,MAAA,CAAO,OAAO,qBAAA,EAAuB,MAAA,CAAO,iBAAA,IAAoB,IAAK,EAAE,CAAA;AAAA,EACzE,CAAC,CAAA;AAED,EAAA,OAAO,qBAAA;AACT;AAEO,SAAS,oBACd,OAAA,EAC6B;AAC7B,EAAA,OAAO,CAAC,WACN,OAAA,CAAQ,KAAA;AAAA,IACN,YAAU,CAAC,MAAA,CAAO,YAAA,IAAgB,MAAA,CAAO,aAAa,MAAM;AAAA,GAC9D;AACJ;;"}
1
+ {"version":3,"file":"filters.esm.js","sources":["../../src/utils/filters.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { EntityFilter } from '../types';\nimport {\n EntityLifecycleFilter,\n EntityNamespaceFilter,\n EntityOrderFilter,\n EntityOrphanFilter,\n EntityOwnerFilter,\n EntityTagFilter,\n EntityTextFilter,\n EntityUserFilter,\n UserListFilter,\n} from '../filters';\nimport { EntityOrderQuery } from '@backstage/catalog-client';\nimport { FilterPredicate } from '@backstage/filter-predicates';\n\nexport interface CatalogFilters {\n filter?: Record<string, string | symbol | (string | symbol)[]>;\n query?: FilterPredicate;\n fullTextFilter?: {\n term: string;\n };\n orderFields?: EntityOrderQuery;\n}\n\nfunction isEntityTextFilter(t: EntityFilter): t is EntityTextFilter {\n return !!(t as EntityTextFilter).getFullTextFilters;\n}\n\nfunction isEntityOrderFilter(t: EntityFilter): t is EntityOrderFilter {\n return !!(t as EntityOrderFilter).getOrderFilters;\n}\n\n/**\n * Combines predicates using `$all`, or returns `undefined` if there are none.\n */\nexport function combinePredicates(\n predicates: Array<FilterPredicate | undefined>,\n): FilterPredicate | undefined {\n const defined = predicates.filter(\n (p): p is FilterPredicate => p !== undefined,\n );\n if (defined.length === 0) {\n return undefined;\n }\n return defined.length === 1 ? defined[0] : { $all: defined };\n}\n\n/**\n * Condenses the backend constraints of a set of filters. Filters that\n * implement `getCatalogQuery` contribute only through that method, and their\n * predicates are combined into a query. Others fall back to the deprecated\n * `getCatalogFilters`, which are merged into a legacy filter. The catalog\n * client applies both when given.\n */\nfunction reduceBackendConstraints(\n filters: EntityFilter[],\n): Pick<CatalogFilters, 'filter' | 'query'> {\n const filter: NonNullable<CatalogFilters['filter']> = {};\n const predicates: Array<FilterPredicate | undefined> = [];\n\n for (const f of filters) {\n if (f.getCatalogQuery) {\n predicates.push(f.getCatalogQuery());\n } else if (f.getCatalogFilters) {\n Object.assign(filter, f.getCatalogFilters());\n }\n }\n\n const query = combinePredicates(predicates);\n return {\n ...(Object.keys(filter).length > 0 && { filter }),\n ...(query !== undefined && { query }),\n };\n}\n\nexport function reduceCatalogFilters(filters: EntityFilter[]): CatalogFilters {\n const constraints = reduceBackendConstraints(filters);\n\n const fullTextFilter = filters.find(isEntityTextFilter)?.getFullTextFilters();\n\n const orderFields = filters.find(isEntityOrderFilter)?.getOrderFilters() || [\n {\n field: 'metadata.name',\n order: 'asc',\n },\n ];\n return {\n ...constraints,\n fullTextFilter,\n orderFields,\n };\n}\n\n/**\n * This function computes and returns the filters to be sent to the backend\n * when not using pagination. Any filter coming from `EntityKindFilter` and\n * `EntityTypeFilter`, together with custom filters set by the adopters, is\n * allowed. The remaining builtin filters are applied frontend-side, so that\n * pickers can compute their available options from the backend response.\n */\nexport function reduceBackendCatalogFilters(\n filters: EntityFilter[],\n): Pick<CatalogFilters, 'filter' | 'query'> {\n return reduceBackendConstraints(\n filters.filter(\n filter =>\n !(\n filter instanceof EntityTagFilter ||\n filter instanceof EntityOwnerFilter ||\n filter instanceof EntityLifecycleFilter ||\n filter instanceof EntityNamespaceFilter ||\n filter instanceof EntityUserFilter ||\n filter instanceof EntityOrphanFilter ||\n filter instanceof EntityTextFilter ||\n filter instanceof UserListFilter\n ),\n ),\n );\n}\n\nexport function reduceEntityFilters(\n filters: EntityFilter[],\n): (entity: Entity) => boolean {\n return (entity: Entity) =>\n filters.every(\n filter => !filter.filterEntity || filter.filterEntity(entity),\n );\n}\n"],"names":[],"mappings":";;AAyCA,SAAS,mBAAmB,CAAA,EAAwC;AAClE,EAAA,OAAO,CAAC,CAAE,CAAA,CAAuB,kBAAA;AACnC;AAEA,SAAS,oBAAoB,CAAA,EAAyC;AACpE,EAAA,OAAO,CAAC,CAAE,CAAA,CAAwB,eAAA;AACpC;AAKO,SAAS,kBACd,UAAA,EAC6B;AAC7B,EAAA,MAAM,UAAU,UAAA,CAAW,MAAA;AAAA,IACzB,CAAC,MAA4B,CAAA,KAAM;AAAA,GACrC;AACA,EAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AACxB,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,OAAO,OAAA,CAAQ,WAAW,CAAA,GAAI,OAAA,CAAQ,CAAC,CAAA,GAAI,EAAE,MAAM,OAAA,EAAQ;AAC7D;AASA,SAAS,yBACP,OAAA,EAC0C;AAC1C,EAAA,MAAM,SAAgD,EAAC;AACvD,EAAA,MAAM,aAAiD,EAAC;AAExD,EAAA,KAAA,MAAW,KAAK,OAAA,EAAS;AACvB,IAAA,IAAI,EAAE,eAAA,EAAiB;AACrB,MAAA,UAAA,CAAW,IAAA,CAAK,CAAA,CAAE,eAAA,EAAiB,CAAA;AAAA,IACrC,CAAA,MAAA,IAAW,EAAE,iBAAA,EAAmB;AAC9B,MAAA,MAAA,CAAO,MAAA,CAAO,MAAA,EAAQ,CAAA,CAAE,iBAAA,EAAmB,CAAA;AAAA,IAC7C;AAAA,EACF;AAEA,EAAA,MAAM,KAAA,GAAQ,kBAAkB,UAAU,CAAA;AAC1C,EAAA,OAAO;AAAA,IACL,GAAI,OAAO,IAAA,CAAK,MAAM,EAAE,MAAA,GAAS,CAAA,IAAK,EAAE,MAAA,EAAO;AAAA,IAC/C,GAAI,KAAA,KAAU,MAAA,IAAa,EAAE,KAAA;AAAM,GACrC;AACF;AAEO,SAAS,qBAAqB,OAAA,EAAyC;AAC5E,EAAA,MAAM,WAAA,GAAc,yBAAyB,OAAO,CAAA;AAEpD,EAAA,MAAM,cAAA,GAAiB,OAAA,CAAQ,IAAA,CAAK,kBAAkB,GAAG,kBAAA,EAAmB;AAE5E,EAAA,MAAM,cAAc,OAAA,CAAQ,IAAA,CAAK,mBAAmB,CAAA,EAAG,iBAAgB,IAAK;AAAA,IAC1E;AAAA,MACE,KAAA,EAAO,eAAA;AAAA,MACP,KAAA,EAAO;AAAA;AACT,GACF;AACA,EAAA,OAAO;AAAA,IACL,GAAG,WAAA;AAAA,IACH,cAAA;AAAA,IACA;AAAA,GACF;AACF;AASO,SAAS,4BACd,OAAA,EAC0C;AAC1C,EAAA,OAAO,wBAAA;AAAA,IACL,OAAA,CAAQ,MAAA;AAAA,MACN,YACE,EACE,MAAA,YAAkB,eAAA,IAClB,MAAA,YAAkB,qBAClB,MAAA,YAAkB,qBAAA,IAClB,MAAA,YAAkB,qBAAA,IAClB,kBAAkB,gBAAA,IAClB,MAAA,YAAkB,kBAAA,IAClB,MAAA,YAAkB,oBAClB,MAAA,YAAkB,cAAA;AAAA;AAExB,GACF;AACF;AAEO,SAAS,oBACd,OAAA,EAC6B;AAC7B,EAAA,OAAO,CAAC,WACN,OAAA,CAAQ,KAAA;AAAA,IACN,YAAU,CAAC,MAAA,CAAO,YAAA,IAAgB,MAAA,CAAO,aAAa,MAAM;AAAA,GAC9D;AACJ;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-catalog-react",
3
- "version": "3.2.4-next.0",
3
+ "version": "3.3.0-next.2",
4
4
  "description": "A frontend library that helps other Backstage plugins interact with the catalog",
5
5
  "backstage": {
6
6
  "role": "web-library",
@@ -73,19 +73,19 @@
73
73
  "test": "backstage-cli package test"
74
74
  },
75
75
  "dependencies": {
76
- "@backstage/catalog-client": "1.16.3-next.0",
76
+ "@backstage/catalog-client": "1.16.3-next.1",
77
77
  "@backstage/catalog-model": "1.10.2-next.0",
78
- "@backstage/core-compat-api": "0.5.16-next.0",
79
- "@backstage/core-components": "0.18.15-next.0",
78
+ "@backstage/core-compat-api": "0.5.16-next.1",
79
+ "@backstage/core-components": "0.18.15-next.3",
80
80
  "@backstage/core-plugin-api": "1.12.11-next.0",
81
81
  "@backstage/errors": "1.3.2-next.0",
82
82
  "@backstage/filter-predicates": "0.1.6-next.0",
83
- "@backstage/frontend-plugin-api": "0.18.2-next.0",
83
+ "@backstage/frontend-plugin-api": "0.18.2-next.1",
84
84
  "@backstage/integration-react": "1.2.23-next.0",
85
85
  "@backstage/plugin-permission-common": "0.9.12-next.0",
86
86
  "@backstage/plugin-permission-react": "0.5.6-next.0",
87
87
  "@backstage/types": "1.2.2",
88
- "@backstage/ui": "0.18.0",
88
+ "@backstage/ui": "0.19.0-next.0",
89
89
  "@backstage/version-bridge": "1.0.12",
90
90
  "@material-ui/core": "^4.12.2",
91
91
  "@material-ui/icons": "^4.9.1",
@@ -105,7 +105,7 @@
105
105
  },
106
106
  "devDependencies": {
107
107
  "@backstage/cli": "0.36.7-next.0",
108
- "@backstage/core-app-api": "1.20.6-next.0",
108
+ "@backstage/core-app-api": "1.20.6-next.1",
109
109
  "@backstage/frontend-test-utils": "0.6.5-next.0",
110
110
  "@backstage/plugin-catalog-common": "1.2.1-next.0",
111
111
  "@backstage/plugin-scaffolder-common": "2.3.1-next.0",