@backstage/plugin-catalog-react 3.2.4-next.1 → 3.3.0
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 +60 -0
- package/dist/alpha.d.ts +6 -6
- package/dist/components/EntityAutocompletePicker/EntityAutocompletePicker.esm.js +1 -1
- package/dist/components/EntityAutocompletePicker/EntityAutocompletePicker.esm.js.map +1 -1
- package/dist/components/UserListPicker/useAllEntitiesCount.esm.js +1 -1
- package/dist/components/UserListPicker/useAllEntitiesCount.esm.js.map +1 -1
- package/dist/components/UserListPicker/useOwnedEntitiesCount.esm.js +6 -7
- package/dist/components/UserListPicker/useOwnedEntitiesCount.esm.js.map +1 -1
- package/dist/components/UserListPicker/useStarredEntitiesCount.esm.js +9 -25
- package/dist/components/UserListPicker/useStarredEntitiesCount.esm.js.map +1 -1
- package/dist/filters.esm.js +100 -0
- package/dist/filters.esm.js.map +1 -1
- package/dist/hooks/useEntityListProvider.esm.js +16 -9
- package/dist/hooks/useEntityListProvider.esm.js.map +1 -1
- package/dist/index.d.ts +47 -0
- package/dist/utils/filters.esm.js +37 -19
- package/dist/utils/filters.esm.js.map +1 -1
- package/package.json +22 -22
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,65 @@
|
|
|
1
1
|
# @backstage/plugin-catalog-react
|
|
2
2
|
|
|
3
|
+
## 3.3.0
|
|
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
|
+
- 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.
|
|
23
|
+
- Updated dependencies
|
|
24
|
+
- @backstage/frontend-plugin-api@0.18.2
|
|
25
|
+
- @backstage/ui@0.19.0
|
|
26
|
+
- @backstage/catalog-client@1.16.3
|
|
27
|
+
- @backstage/core-compat-api@0.5.16
|
|
28
|
+
- @backstage/errors@1.3.2
|
|
29
|
+
- @backstage/filter-predicates@0.2.0
|
|
30
|
+
- @backstage/core-components@0.18.15
|
|
31
|
+
- @backstage/catalog-model@1.10.2
|
|
32
|
+
- @backstage/core-plugin-api@1.12.11
|
|
33
|
+
- @backstage/integration-react@1.2.23
|
|
34
|
+
- @backstage/plugin-permission-common@0.9.12
|
|
35
|
+
- @backstage/plugin-permission-react@0.5.6
|
|
36
|
+
|
|
37
|
+
## 3.3.0-next.2
|
|
38
|
+
|
|
39
|
+
### Minor Changes
|
|
40
|
+
|
|
41
|
+
- 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.
|
|
42
|
+
|
|
43
|
+
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.
|
|
44
|
+
|
|
45
|
+
This requires a catalog backend from Backstage release 1.49 or later, which added support for predicate queries.
|
|
46
|
+
|
|
47
|
+
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.
|
|
48
|
+
|
|
49
|
+
Some builtin filters became more precise as part of this change:
|
|
50
|
+
|
|
51
|
+
- `EntityTagFilter` with multiple tags now requires all of them on the backend, matching its frontend behavior.
|
|
52
|
+
- `EntityUserFilter.starred` matches the full starred entity refs rather than just their names, and the starred count is computed by the backend.
|
|
53
|
+
|
|
54
|
+
### Patch Changes
|
|
55
|
+
|
|
56
|
+
- Updated dependencies
|
|
57
|
+
- @backstage/frontend-plugin-api@0.18.2-next.1
|
|
58
|
+
- @backstage/catalog-client@1.16.3-next.1
|
|
59
|
+
- @backstage/core-components@0.18.15-next.3
|
|
60
|
+
- @backstage/ui@0.19.0-next.0
|
|
61
|
+
- @backstage/core-compat-api@0.5.16-next.1
|
|
62
|
+
|
|
3
63
|
## 3.2.4-next.1
|
|
4
64
|
|
|
5
65
|
### Patch Changes
|
package/dist/alpha.d.ts
CHANGED
|
@@ -104,11 +104,11 @@ declare const EntityCardBlueprint: _backstage_frontend_plugin_api.ExtensionBluep
|
|
|
104
104
|
}>;
|
|
105
105
|
inputs: {};
|
|
106
106
|
config: {
|
|
107
|
-
filter: FilterPredicate | undefined;
|
|
107
|
+
filter: string | FilterPredicate | undefined;
|
|
108
108
|
type: "content" | "info" | undefined;
|
|
109
109
|
};
|
|
110
110
|
configInput: {
|
|
111
|
-
filter?: FilterPredicate | undefined;
|
|
111
|
+
filter?: string | FilterPredicate | undefined;
|
|
112
112
|
type?: "content" | "info" | undefined;
|
|
113
113
|
};
|
|
114
114
|
dataRefs: {
|
|
@@ -160,14 +160,14 @@ declare const EntityContentBlueprint: _backstage_frontend_plugin_api.ExtensionBl
|
|
|
160
160
|
config: {
|
|
161
161
|
path: string | undefined;
|
|
162
162
|
title: string | undefined;
|
|
163
|
-
filter: FilterPredicate | undefined;
|
|
163
|
+
filter: string | FilterPredicate | undefined;
|
|
164
164
|
group: string | false | undefined;
|
|
165
165
|
icon: string | undefined;
|
|
166
166
|
};
|
|
167
167
|
configInput: {
|
|
168
168
|
path?: string | undefined;
|
|
169
169
|
title?: string | undefined;
|
|
170
|
-
filter?: FilterPredicate | undefined;
|
|
170
|
+
filter?: string | FilterPredicate | undefined;
|
|
171
171
|
group?: string | false | undefined;
|
|
172
172
|
icon?: string | undefined;
|
|
173
173
|
};
|
|
@@ -202,11 +202,11 @@ declare const EntityContentLayoutBlueprint: _backstage_frontend_plugin_api.Exten
|
|
|
202
202
|
inputs: {};
|
|
203
203
|
config: {
|
|
204
204
|
type: string | undefined;
|
|
205
|
-
filter: FilterPredicate | undefined;
|
|
205
|
+
filter: string | FilterPredicate | undefined;
|
|
206
206
|
};
|
|
207
207
|
configInput: {
|
|
208
208
|
type?: string | undefined;
|
|
209
|
-
filter?: FilterPredicate | undefined;
|
|
209
|
+
filter?: string | FilterPredicate | undefined;
|
|
210
210
|
};
|
|
211
211
|
dataRefs: {
|
|
212
212
|
filterFunction: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<(entity: Entity) => boolean, "catalog.entity-filter-function", {}>;
|
|
@@ -49,7 +49,7 @@ function EntityAutocompletePicker(props) {
|
|
|
49
49
|
const facet = path;
|
|
50
50
|
const { facets } = await catalogApi.getEntityFacets({
|
|
51
51
|
facets: [facet],
|
|
52
|
-
|
|
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
|
|
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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"}
|
|
@@ -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 (
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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 (
|
|
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 {
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
57
|
-
return
|
|
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 {
|
|
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;;"}
|
package/dist/filters.esm.js
CHANGED
|
@@ -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) };
|
package/dist/filters.esm.js.map
CHANGED
|
@@ -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
|
|
105
|
+
const constraints = reduceBackendCatalogFilters(compacted);
|
|
106
106
|
const { orderFields } = reduceCatalogFilters(compacted);
|
|
107
|
-
fetchParams = {
|
|
108
|
-
doFetch = async () => {
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
}
|
|
@@ -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 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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"}
|
|
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
|
|
10
|
-
const
|
|
11
|
-
(
|
|
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 {
|
|
43
|
+
return {
|
|
44
|
+
...constraints,
|
|
45
|
+
fullTextFilter,
|
|
46
|
+
orderFields
|
|
47
|
+
};
|
|
27
48
|
}
|
|
28
49
|
function reduceBackendCatalogFilters(filters) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
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.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "A frontend library that helps other Backstage plugins interact with the catalog",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "web-library",
|
|
@@ -73,20 +73,20 @@
|
|
|
73
73
|
"test": "backstage-cli package test"
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
|
-
"@backstage/catalog-client": "1.16.3
|
|
77
|
-
"@backstage/catalog-model": "1.10.2
|
|
78
|
-
"@backstage/core-compat-api": "0.5.16
|
|
79
|
-
"@backstage/core-components": "0.18.15
|
|
80
|
-
"@backstage/core-plugin-api": "1.12.11
|
|
81
|
-
"@backstage/errors": "1.3.2
|
|
82
|
-
"@backstage/filter-predicates": "0.
|
|
83
|
-
"@backstage/frontend-plugin-api": "0.18.2
|
|
84
|
-
"@backstage/integration-react": "1.2.23
|
|
85
|
-
"@backstage/plugin-permission-common": "0.9.12
|
|
86
|
-
"@backstage/plugin-permission-react": "0.5.6
|
|
87
|
-
"@backstage/types": "1.2.2",
|
|
88
|
-
"@backstage/ui": "0.
|
|
89
|
-
"@backstage/version-bridge": "1.0.12",
|
|
76
|
+
"@backstage/catalog-client": "^1.16.3",
|
|
77
|
+
"@backstage/catalog-model": "^1.10.2",
|
|
78
|
+
"@backstage/core-compat-api": "^0.5.16",
|
|
79
|
+
"@backstage/core-components": "^0.18.15",
|
|
80
|
+
"@backstage/core-plugin-api": "^1.12.11",
|
|
81
|
+
"@backstage/errors": "^1.3.2",
|
|
82
|
+
"@backstage/filter-predicates": "^0.2.0",
|
|
83
|
+
"@backstage/frontend-plugin-api": "^0.18.2",
|
|
84
|
+
"@backstage/integration-react": "^1.2.23",
|
|
85
|
+
"@backstage/plugin-permission-common": "^0.9.12",
|
|
86
|
+
"@backstage/plugin-permission-react": "^0.5.6",
|
|
87
|
+
"@backstage/types": "^1.2.2",
|
|
88
|
+
"@backstage/ui": "^0.19.0",
|
|
89
|
+
"@backstage/version-bridge": "^1.0.12",
|
|
90
90
|
"@material-ui/core": "^4.12.2",
|
|
91
91
|
"@material-ui/icons": "^4.9.1",
|
|
92
92
|
"@material-ui/lab": "4.0.0-alpha.61",
|
|
@@ -104,12 +104,12 @@
|
|
|
104
104
|
"zod": "^4.0.0"
|
|
105
105
|
},
|
|
106
106
|
"devDependencies": {
|
|
107
|
-
"@backstage/cli": "0.36.7
|
|
108
|
-
"@backstage/core-app-api": "1.20.6
|
|
109
|
-
"@backstage/frontend-test-utils": "0.6.5
|
|
110
|
-
"@backstage/plugin-catalog-common": "1.2.1
|
|
111
|
-
"@backstage/plugin-scaffolder-common": "2.3.1
|
|
112
|
-
"@backstage/test-utils": "1.7.23
|
|
107
|
+
"@backstage/cli": "^0.36.7",
|
|
108
|
+
"@backstage/core-app-api": "^1.20.6",
|
|
109
|
+
"@backstage/frontend-test-utils": "^0.6.5",
|
|
110
|
+
"@backstage/plugin-catalog-common": "^1.2.1",
|
|
111
|
+
"@backstage/plugin-scaffolder-common": "^2.3.1",
|
|
112
|
+
"@backstage/test-utils": "^1.7.23",
|
|
113
113
|
"@testing-library/dom": "^10.0.0",
|
|
114
114
|
"@testing-library/jest-dom": "^6.0.0",
|
|
115
115
|
"@testing-library/react": "^16.0.0",
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
"react-test-renderer": "^16.13.1"
|
|
124
124
|
},
|
|
125
125
|
"peerDependencies": {
|
|
126
|
-
"@backstage/frontend-test-utils": "0.6.5
|
|
126
|
+
"@backstage/frontend-test-utils": "^0.6.5",
|
|
127
127
|
"@types/react": "^17.0.0 || ^18.0.0",
|
|
128
128
|
"react": "^17.0.0 || ^18.0.0",
|
|
129
129
|
"react-dom": "^17.0.0 || ^18.0.0",
|