@backstage/plugin-catalog-react 1.17.0-next.2 → 1.18.0-next.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 CHANGED
@@ -1,5 +1,122 @@
1
1
  # @backstage/plugin-catalog-react
2
2
 
3
+ ## 1.18.0-next.0
4
+
5
+ ### Minor Changes
6
+
7
+ - d47aaa3: Added EntityOrderFilter to sort entities by different fields/columns. This new filter allows users to specify the order in which entities are displayed in the catalog.
8
+
9
+ Example usage:
10
+
11
+ ```ts
12
+ import {
13
+ EntityOrderFilter,
14
+ useEntityList,
15
+ } from '@backstage/plugin-catalog-react';
16
+ // ...
17
+ const { updateFilters } = useEntityList();
18
+
19
+ // ...
20
+ updateFilters({
21
+ order: new EntityOrderFilter([
22
+ {
23
+ field: 'metadata.name',
24
+ order: 'desc',
25
+ },
26
+ ]),
27
+ });
28
+ ```
29
+
30
+ - 1a003ff: Add `getLocations` method to `CatalogApi` and `CatalogClient`. This method calls the [`GET /locations`](https://backstage.io/docs/features/software-catalog/software-catalog-api/#get-locations) endpoint from the catalog backend.
31
+
32
+ ### Patch Changes
33
+
34
+ - Updated dependencies
35
+ - @backstage/core-compat-api@0.4.2-next.0
36
+ - @backstage/catalog-client@1.10.0-next.0
37
+ - @backstage/integration-react@1.2.7-next.0
38
+ - @backstage/catalog-model@1.7.3
39
+ - @backstage/core-components@0.17.1
40
+ - @backstage/core-plugin-api@1.10.6
41
+ - @backstage/errors@1.2.7
42
+ - @backstage/frontend-plugin-api@0.10.1
43
+ - @backstage/frontend-test-utils@0.3.2-next.0
44
+ - @backstage/types@1.2.1
45
+ - @backstage/version-bridge@1.0.11
46
+ - @backstage/plugin-catalog-common@1.1.3
47
+ - @backstage/plugin-permission-common@0.8.4
48
+ - @backstage/plugin-permission-react@0.4.33
49
+
50
+ ## 1.17.0
51
+
52
+ ### Minor Changes
53
+
54
+ - 0f37fa8: `entityRouteParams` now also accepts entity refs, and can help with encoding the resulting parameters.
55
+ - 61d350f: **BREAKING ALPHA**: `CatalogFilterBlueprint`, used in the new frontend system, is now exported under plugin-catalog-react instead of plugin-catalog.
56
+
57
+ ```diff
58
+ + import { CatalogFilterBlueprint } from '@backstage/plugin-catalog-react/alpha';
59
+ - import { CatalogFilterBlueprint } from '@backstage/plugin-catalog/alpha';
60
+ ```
61
+
62
+ - 09afd67: Adds `EntityContextMenuItemBlueprint` to enable extending the entity page's context menu with user defined items.
63
+
64
+ For example:
65
+
66
+ ```ts
67
+ import { EntityContextMenuItemBlueprint } from '@backstage/plugin-catalog-react/alpha';
68
+
69
+ const myCustomHref = EntityContextMenuItemBlueprint.make({
70
+ name: 'test-href',
71
+ params: {
72
+ icon: <span>Example Icon</span>,
73
+ useProps: () => ({
74
+ title: 'Example Href',
75
+ href: '/example-path',
76
+ disabled: false,
77
+ component: 'a',
78
+ }),
79
+ },
80
+ });
81
+
82
+ const myCustomOnClick = EntityContextMenuItemBlueprint.make({
83
+ name: 'test-click',
84
+ params: {
85
+ icon: <span>Test Icon</span>,
86
+ useProps: () => ({
87
+ title: 'Example onClick',
88
+ onClick: () => window.alert('Hello world!'),
89
+ disabled: false,
90
+ }),
91
+ },
92
+ });
93
+ ```
94
+
95
+ ### Patch Changes
96
+
97
+ - a47fd39: Removes instances of default React imports, a necessary update for the upcoming React 19 migration.
98
+
99
+ <https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html>
100
+
101
+ - 3f7e4f1: Added a new `overview` entity content group for the new frontend system.
102
+ - 186d016: Add `operation` to alpha `defaultEntityContentGroups`.
103
+ - 221ddba: Fix offset pagination to reset when updating filters in `useEntityList`
104
+ - Updated dependencies
105
+ - @backstage/frontend-plugin-api@0.10.1
106
+ - @backstage/frontend-test-utils@0.3.1
107
+ - @backstage/integration-react@1.2.6
108
+ - @backstage/core-compat-api@0.4.1
109
+ - @backstage/core-components@0.17.1
110
+ - @backstage/core-plugin-api@1.10.6
111
+ - @backstage/plugin-permission-react@0.4.33
112
+ - @backstage/catalog-client@1.9.1
113
+ - @backstage/catalog-model@1.7.3
114
+ - @backstage/errors@1.2.7
115
+ - @backstage/types@1.2.1
116
+ - @backstage/version-bridge@1.0.11
117
+ - @backstage/plugin-catalog-common@1.1.3
118
+ - @backstage/plugin-permission-common@0.8.4
119
+
3
120
  ## 1.17.0-next.2
4
121
 
5
122
  ### Minor Changes
@@ -8,6 +8,7 @@ const entityFilterExpressionDataRef = createExtensionDataRef().with({
8
8
  id: "catalog.entity-filter-expression"
9
9
  });
10
10
  const defaultEntityContentGroups = {
11
+ overview: "Overview",
11
12
  documentation: "Documentation",
12
13
  development: "Development",
13
14
  deployment: "Deployment",
@@ -1 +1 @@
1
- {"version":3,"file":"extensionData.esm.js","sources":["../../../src/alpha/blueprints/extensionData.tsx"],"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 { Entity } from '@backstage/catalog-model';\nimport { createExtensionDataRef } from '@backstage/frontend-plugin-api';\n\n/** @internal */\nexport const entityContentTitleDataRef = createExtensionDataRef<string>().with({\n id: 'catalog.entity-content-title',\n});\n\n/** @internal */\nexport const entityFilterFunctionDataRef = createExtensionDataRef<\n (entity: Entity) => boolean\n>().with({ id: 'catalog.entity-filter-function' });\n\n/** @internal */\nexport const entityFilterExpressionDataRef =\n createExtensionDataRef<string>().with({\n id: 'catalog.entity-filter-expression',\n });\n\n/**\n * @alpha\n * Default entity content groups.\n */\nexport const defaultEntityContentGroups = {\n documentation: 'Documentation',\n development: 'Development',\n deployment: 'Deployment',\n operation: 'Operation',\n observability: 'Observability',\n};\n\n/** @internal */\nexport const entityContentGroupDataRef = createExtensionDataRef<string>().with({\n id: 'catalog.entity-content-group',\n});\n\n/**\n * @internal\n * Available entity card types\n */\nexport const entityCardTypes = [\n 'summary',\n 'info',\n 'content',\n] as const satisfies readonly EntityCardType[];\n\n/** @alpha */\nexport type EntityCardType = 'summary' | 'info' | 'content';\n\n/** @internal */\nexport const entityCardTypeDataRef =\n createExtensionDataRef<EntityCardType>().with({\n id: 'catalog.entity-card-type',\n });\n"],"names":[],"mappings":";;AAoBa,MAAA,yBAAA,GAA4B,sBAA+B,EAAA,CAAE,IAAK,CAAA;AAAA,EAC7E,EAAI,EAAA;AACN,CAAC;AAGM,MAAM,8BAA8B,sBAEzC,EAAA,CAAE,KAAK,EAAE,EAAA,EAAI,kCAAkC;AAGpC,MAAA,6BAAA,GACX,sBAA+B,EAAA,CAAE,IAAK,CAAA;AAAA,EACpC,EAAI,EAAA;AACN,CAAC;AAMI,MAAM,0BAA6B,GAAA;AAAA,EACxC,aAAe,EAAA,eAAA;AAAA,EACf,WAAa,EAAA,aAAA;AAAA,EACb,UAAY,EAAA,YAAA;AAAA,EACZ,SAAW,EAAA,WAAA;AAAA,EACX,aAAe,EAAA;AACjB;AAGa,MAAA,yBAAA,GAA4B,sBAA+B,EAAA,CAAE,IAAK,CAAA;AAAA,EAC7E,EAAI,EAAA;AACN,CAAC;AAMM,MAAM,eAAkB,GAAA;AAAA,EAC7B,SAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF;AAMa,MAAA,qBAAA,GACX,sBAAuC,EAAA,CAAE,IAAK,CAAA;AAAA,EAC5C,EAAI,EAAA;AACN,CAAC;;;;"}
1
+ {"version":3,"file":"extensionData.esm.js","sources":["../../../src/alpha/blueprints/extensionData.tsx"],"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 { Entity } from '@backstage/catalog-model';\nimport { createExtensionDataRef } from '@backstage/frontend-plugin-api';\n\n/** @internal */\nexport const entityContentTitleDataRef = createExtensionDataRef<string>().with({\n id: 'catalog.entity-content-title',\n});\n\n/** @internal */\nexport const entityFilterFunctionDataRef = createExtensionDataRef<\n (entity: Entity) => boolean\n>().with({ id: 'catalog.entity-filter-function' });\n\n/** @internal */\nexport const entityFilterExpressionDataRef =\n createExtensionDataRef<string>().with({\n id: 'catalog.entity-filter-expression',\n });\n\n/**\n * @alpha\n * Default entity content groups.\n */\nexport const defaultEntityContentGroups = {\n overview: 'Overview',\n documentation: 'Documentation',\n development: 'Development',\n deployment: 'Deployment',\n operation: 'Operation',\n observability: 'Observability',\n};\n\n/** @internal */\nexport const entityContentGroupDataRef = createExtensionDataRef<string>().with({\n id: 'catalog.entity-content-group',\n});\n\n/**\n * @internal\n * Available entity card types\n */\nexport const entityCardTypes = [\n 'summary',\n 'info',\n 'content',\n] as const satisfies readonly EntityCardType[];\n\n/** @alpha */\nexport type EntityCardType = 'summary' | 'info' | 'content';\n\n/** @internal */\nexport const entityCardTypeDataRef =\n createExtensionDataRef<EntityCardType>().with({\n id: 'catalog.entity-card-type',\n });\n"],"names":[],"mappings":";;AAoBa,MAAA,yBAAA,GAA4B,sBAA+B,EAAA,CAAE,IAAK,CAAA;AAAA,EAC7E,EAAI,EAAA;AACN,CAAC;AAGM,MAAM,8BAA8B,sBAEzC,EAAA,CAAE,KAAK,EAAE,EAAA,EAAI,kCAAkC;AAGpC,MAAA,6BAAA,GACX,sBAA+B,EAAA,CAAE,IAAK,CAAA;AAAA,EACpC,EAAI,EAAA;AACN,CAAC;AAMI,MAAM,0BAA6B,GAAA;AAAA,EACxC,QAAU,EAAA,UAAA;AAAA,EACV,aAAe,EAAA,eAAA;AAAA,EACf,WAAa,EAAA,aAAA;AAAA,EACb,UAAY,EAAA,YAAA;AAAA,EACZ,SAAW,EAAA,WAAA;AAAA,EACX,aAAe,EAAA;AACjB;AAGa,MAAA,yBAAA,GAA4B,sBAA+B,EAAA,CAAE,IAAK,CAAA;AAAA,EAC7E,EAAI,EAAA;AACN,CAAC;AAMM,MAAM,eAAkB,GAAA;AAAA,EAC7B,SAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF;AAMa,MAAA,qBAAA,GACX,sBAAuC,EAAA,CAAE,IAAK,CAAA;AAAA,EAC5C,EAAI,EAAA;AACN,CAAC;;;;"}
package/dist/alpha.d.ts CHANGED
@@ -40,6 +40,7 @@ declare const CatalogFilterBlueprint: _backstage_frontend_plugin_api.ExtensionBl
40
40
  * Default entity content groups.
41
41
  */
42
42
  declare const defaultEntityContentGroups: {
43
+ overview: string;
43
44
  documentation: string;
44
45
  development: string;
45
46
  deployment: string;
@@ -1,9 +1,8 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { parseEntityRef, DEFAULT_NAMESPACE } from '@backstage/catalog-model';
3
2
  import { Link } from '@backstage/core-components';
4
3
  import { useRouteRef } from '@backstage/core-plugin-api';
5
4
  import { forwardRef } from 'react';
6
- import { entityRouteRef } from '../../routes.esm.js';
5
+ import { entityRouteRef, entityRouteParams } from '../../routes.esm.js';
7
6
  import { EntityDisplayName } from '../EntityDisplayName/EntityDisplayName.esm.js';
8
7
 
9
8
  const EntityRefLink = forwardRef(
@@ -34,30 +33,7 @@ const EntityRefLink = forwardRef(
34
33
  );
35
34
  function useEntityRoute(entityRef) {
36
35
  const entityRoute = useRouteRef(entityRouteRef);
37
- let kind;
38
- let namespace;
39
- let name;
40
- if (typeof entityRef === "string") {
41
- const parsed = parseEntityRef(entityRef);
42
- kind = parsed.kind;
43
- namespace = parsed.namespace;
44
- name = parsed.name;
45
- } else if ("metadata" in entityRef) {
46
- kind = entityRef.kind;
47
- namespace = entityRef.metadata.namespace;
48
- name = entityRef.metadata.name;
49
- } else {
50
- kind = entityRef.kind;
51
- namespace = entityRef.namespace;
52
- name = entityRef.name;
53
- }
54
- kind = kind.toLocaleLowerCase("en-US");
55
- namespace = namespace?.toLocaleLowerCase("en-US") ?? DEFAULT_NAMESPACE;
56
- const routeParams = {
57
- kind: encodeURIComponent(kind),
58
- namespace: encodeURIComponent(namespace),
59
- name: encodeURIComponent(name)
60
- };
36
+ const routeParams = entityRouteParams(entityRef, { encodeParams: true });
61
37
  return entityRoute(routeParams);
62
38
  }
63
39
 
@@ -1 +1 @@
1
- {"version":3,"file":"EntityRefLink.esm.js","sources":["../../../src/components/EntityRefLink/EntityRefLink.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 {\n CompoundEntityRef,\n DEFAULT_NAMESPACE,\n Entity,\n parseEntityRef,\n} from '@backstage/catalog-model';\nimport { Link, LinkProps } from '@backstage/core-components';\nimport { useRouteRef } from '@backstage/core-plugin-api';\nimport { ReactNode, forwardRef } from 'react';\nimport { entityRouteRef } from '../../routes';\nimport { EntityDisplayName } from '../EntityDisplayName';\n\n/**\n * Props for {@link EntityRefLink}.\n *\n * @public\n */\nexport type EntityRefLinkProps = {\n entityRef: Entity | CompoundEntityRef | string;\n defaultKind?: string;\n defaultNamespace?: string;\n /** @deprecated This option should no longer be used; presentation is requested through the {@link entityPresentationApiRef} instead */\n title?: string;\n children?: ReactNode;\n hideIcon?: boolean;\n disableTooltip?: boolean;\n} & Omit<LinkProps, 'to'>;\n\n/**\n * Shows a clickable link to an entity.\n *\n * @public\n */\nexport const EntityRefLink = forwardRef<any, EntityRefLinkProps>(\n (props, ref) => {\n const {\n entityRef,\n defaultKind,\n defaultNamespace,\n title,\n children,\n hideIcon,\n disableTooltip,\n ...linkProps\n } = props;\n const entityRoute = useEntityRoute(props.entityRef);\n\n const content = children ?? title ?? (\n <EntityDisplayName\n entityRef={entityRef}\n defaultKind={defaultKind}\n defaultNamespace={defaultNamespace}\n hideIcon={hideIcon}\n disableTooltip={disableTooltip}\n />\n );\n\n return (\n <Link {...linkProps} ref={ref} to={entityRoute}>\n {content}\n </Link>\n );\n },\n) as (props: EntityRefLinkProps) => JSX.Element;\n\n// Hook that computes the route to a given entity / ref. This is a bit\n// contrived, because it tries to retain the casing of the entity name if\n// present, but not of other parts. This is in an attempt to make slightly more\n// nice-looking URLs.\nfunction useEntityRoute(\n entityRef: Entity | CompoundEntityRef | string,\n): string {\n const entityRoute = useRouteRef(entityRouteRef);\n\n let kind;\n let namespace;\n let name;\n\n if (typeof entityRef === 'string') {\n const parsed = parseEntityRef(entityRef);\n kind = parsed.kind;\n namespace = parsed.namespace;\n name = parsed.name;\n } else if ('metadata' in entityRef) {\n kind = entityRef.kind;\n namespace = entityRef.metadata.namespace;\n name = entityRef.metadata.name;\n } else {\n kind = entityRef.kind;\n namespace = entityRef.namespace;\n name = entityRef.name;\n }\n\n kind = kind.toLocaleLowerCase('en-US');\n namespace = namespace?.toLocaleLowerCase('en-US') ?? DEFAULT_NAMESPACE;\n\n const routeParams = {\n kind: encodeURIComponent(kind),\n namespace: encodeURIComponent(namespace),\n name: encodeURIComponent(name),\n };\n\n return entityRoute(routeParams);\n}\n"],"names":[],"mappings":";;;;;;;;AAiDO,MAAM,aAAgB,GAAA,UAAA;AAAA,EAC3B,CAAC,OAAO,GAAQ,KAAA;AACd,IAAM,MAAA;AAAA,MACJ,SAAA;AAAA,MACA,WAAA;AAAA,MACA,gBAAA;AAAA,MACA,KAAA;AAAA,MACA,QAAA;AAAA,MACA,QAAA;AAAA,MACA,cAAA;AAAA,MACA,GAAG;AAAA,KACD,GAAA,KAAA;AACJ,IAAM,MAAA,WAAA,GAAc,cAAe,CAAA,KAAA,CAAM,SAAS,CAAA;AAElD,IAAM,MAAA,OAAA,GAAU,YAAY,KAC1B,oBAAA,GAAA;AAAA,MAAC,iBAAA;AAAA,MAAA;AAAA,QACC,SAAA;AAAA,QACA,WAAA;AAAA,QACA,gBAAA;AAAA,QACA,QAAA;AAAA,QACA;AAAA;AAAA,KACF;AAGF,IAAA,2BACG,IAAM,EAAA,EAAA,GAAG,WAAW,GAAU,EAAA,EAAA,EAAI,aAChC,QACH,EAAA,OAAA,EAAA,CAAA;AAAA;AAGN;AAMA,SAAS,eACP,SACQ,EAAA;AACR,EAAM,MAAA,WAAA,GAAc,YAAY,cAAc,CAAA;AAE9C,EAAI,IAAA,IAAA;AACJ,EAAI,IAAA,SAAA;AACJ,EAAI,IAAA,IAAA;AAEJ,EAAI,IAAA,OAAO,cAAc,QAAU,EAAA;AACjC,IAAM,MAAA,MAAA,GAAS,eAAe,SAAS,CAAA;AACvC,IAAA,IAAA,GAAO,MAAO,CAAA,IAAA;AACd,IAAA,SAAA,GAAY,MAAO,CAAA,SAAA;AACnB,IAAA,IAAA,GAAO,MAAO,CAAA,IAAA;AAAA,GAChB,MAAA,IAAW,cAAc,SAAW,EAAA;AAClC,IAAA,IAAA,GAAO,SAAU,CAAA,IAAA;AACjB,IAAA,SAAA,GAAY,UAAU,QAAS,CAAA,SAAA;AAC/B,IAAA,IAAA,GAAO,UAAU,QAAS,CAAA,IAAA;AAAA,GACrB,MAAA;AACL,IAAA,IAAA,GAAO,SAAU,CAAA,IAAA;AACjB,IAAA,SAAA,GAAY,SAAU,CAAA,SAAA;AACtB,IAAA,IAAA,GAAO,SAAU,CAAA,IAAA;AAAA;AAGnB,EAAO,IAAA,GAAA,IAAA,CAAK,kBAAkB,OAAO,CAAA;AACrC,EAAY,SAAA,GAAA,SAAA,EAAW,iBAAkB,CAAA,OAAO,CAAK,IAAA,iBAAA;AAErD,EAAA,MAAM,WAAc,GAAA;AAAA,IAClB,IAAA,EAAM,mBAAmB,IAAI,CAAA;AAAA,IAC7B,SAAA,EAAW,mBAAmB,SAAS,CAAA;AAAA,IACvC,IAAA,EAAM,mBAAmB,IAAI;AAAA,GAC/B;AAEA,EAAA,OAAO,YAAY,WAAW,CAAA;AAChC;;;;"}
1
+ {"version":3,"file":"EntityRefLink.esm.js","sources":["../../../src/components/EntityRefLink/EntityRefLink.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 { CompoundEntityRef, Entity } from '@backstage/catalog-model';\nimport { Link, LinkProps } from '@backstage/core-components';\nimport { useRouteRef } from '@backstage/core-plugin-api';\nimport { ReactNode, forwardRef } from 'react';\nimport { entityRouteParams, entityRouteRef } from '../../routes';\nimport { EntityDisplayName } from '../EntityDisplayName';\n\n/**\n * Props for {@link EntityRefLink}.\n *\n * @public\n */\nexport type EntityRefLinkProps = {\n entityRef: Entity | CompoundEntityRef | string;\n defaultKind?: string;\n defaultNamespace?: string;\n /** @deprecated This option should no longer be used; presentation is requested through the {@link entityPresentationApiRef} instead */\n title?: string;\n children?: ReactNode;\n hideIcon?: boolean;\n disableTooltip?: boolean;\n} & Omit<LinkProps, 'to'>;\n\n/**\n * Shows a clickable link to an entity.\n *\n * @public\n */\nexport const EntityRefLink = forwardRef<any, EntityRefLinkProps>(\n (props, ref) => {\n const {\n entityRef,\n defaultKind,\n defaultNamespace,\n title,\n children,\n hideIcon,\n disableTooltip,\n ...linkProps\n } = props;\n const entityRoute = useEntityRoute(props.entityRef);\n\n const content = children ?? title ?? (\n <EntityDisplayName\n entityRef={entityRef}\n defaultKind={defaultKind}\n defaultNamespace={defaultNamespace}\n hideIcon={hideIcon}\n disableTooltip={disableTooltip}\n />\n );\n\n return (\n <Link {...linkProps} ref={ref} to={entityRoute}>\n {content}\n </Link>\n );\n },\n) as (props: EntityRefLinkProps) => JSX.Element;\n\n// Hook that computes the route to a given entity / ref. This is a bit\n// contrived, because it tries to retain the casing of the entity name if\n// present, but not of other parts. This is in an attempt to make slightly more\n// nice-looking URLs.\nfunction useEntityRoute(\n entityRef: Entity | CompoundEntityRef | string,\n): string {\n const entityRoute = useRouteRef(entityRouteRef);\n\n const routeParams = entityRouteParams(entityRef, { encodeParams: true });\n\n return entityRoute(routeParams);\n}\n"],"names":[],"mappings":";;;;;;;AA4CO,MAAM,aAAgB,GAAA,UAAA;AAAA,EAC3B,CAAC,OAAO,GAAQ,KAAA;AACd,IAAM,MAAA;AAAA,MACJ,SAAA;AAAA,MACA,WAAA;AAAA,MACA,gBAAA;AAAA,MACA,KAAA;AAAA,MACA,QAAA;AAAA,MACA,QAAA;AAAA,MACA,cAAA;AAAA,MACA,GAAG;AAAA,KACD,GAAA,KAAA;AACJ,IAAM,MAAA,WAAA,GAAc,cAAe,CAAA,KAAA,CAAM,SAAS,CAAA;AAElD,IAAM,MAAA,OAAA,GAAU,YAAY,KAC1B,oBAAA,GAAA;AAAA,MAAC,iBAAA;AAAA,MAAA;AAAA,QACC,SAAA;AAAA,QACA,WAAA;AAAA,QACA,gBAAA;AAAA,QACA,QAAA;AAAA,QACA;AAAA;AAAA,KACF;AAGF,IAAA,2BACG,IAAM,EAAA,EAAA,GAAG,WAAW,GAAU,EAAA,EAAA,EAAI,aAChC,QACH,EAAA,OAAA,EAAA,CAAA;AAAA;AAGN;AAMA,SAAS,eACP,SACQ,EAAA;AACR,EAAM,MAAA,WAAA,GAAc,YAAY,cAAc,CAAA;AAE9C,EAAA,MAAM,cAAc,iBAAkB,CAAA,SAAA,EAAW,EAAE,YAAA,EAAc,MAAM,CAAA;AAEvE,EAAA,OAAO,YAAY,WAAW,CAAA;AAChC;;;;"}
@@ -22,7 +22,7 @@ function useAllEntitiesCount() {
22
22
  const request = useMemo(() => {
23
23
  const { user, ...allFilters } = filters;
24
24
  const compacted = compact(Object.values(allFilters));
25
- const catalogFilters = reduceCatalogFilters(compacted);
25
+ const { orderFields, ...catalogFilters } = reduceCatalogFilters(compacted);
26
26
  const newRequest = {
27
27
  ...catalogFilters,
28
28
  limit: 0
@@ -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 catalogFilters = reduceCatalogFilters(compacted);\n const newRequest: QueryEntitiesInitialRequest = {\n ...catalogFilters,\n limit: 0,\n };\n\n if (Object.keys(catalogFilters.filter).length === 0) {\n prevRequest.current = undefined;\n return prevRequest.current;\n }\n\n if (isEqual(newRequest, prevRequest.current)) {\n return prevRequest.current;\n }\n prevRequest.current = newRequest;\n return newRequest;\n }, [filters]);\n\n const { value: count, loading } = useAsync(async () => {\n if (request === undefined) {\n return 0;\n }\n const { totalItems } = await catalogApi.queryEntities(request);\n\n return totalItems;\n }, [request]);\n\n return { count, loading };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAwBO,SAAS,mBAAsB,GAAA;AACpC,EAAM,MAAA,UAAA,GAAa,OAAO,aAAa,CAAA;AACvC,EAAM,MAAA,EAAE,OAAQ,EAAA,GAAI,aAAc,EAAA;AAElC,EAAA,MAAM,cAAc,MAAoC,EAAA;AACxD,EAAM,MAAA,OAAA,GAAU,QAAQ,MAAM;AAC5B,IAAA,MAAM,EAAE,IAAA,EAAM,GAAG,UAAA,EAAe,GAAA,OAAA;AAChC,IAAA,MAAM,SAAY,GAAA,OAAA,CAAQ,MAAO,CAAA,MAAA,CAAO,UAAU,CAAC,CAAA;AACnD,IAAM,MAAA,cAAA,GAAiB,qBAAqB,SAAS,CAAA;AACrD,IAAA,MAAM,UAA0C,GAAA;AAAA,MAC9C,GAAG,cAAA;AAAA,MACH,KAAO,EAAA;AAAA,KACT;AAEA,IAAA,IAAI,OAAO,IAAK,CAAA,cAAA,CAAe,MAAM,CAAA,CAAE,WAAW,CAAG,EAAA;AACnD,MAAA,WAAA,CAAY,OAAU,GAAA,KAAA,CAAA;AACtB,MAAA,OAAO,WAAY,CAAA,OAAA;AAAA;AAGrB,IAAA,IAAI,OAAQ,CAAA,UAAA,EAAY,WAAY,CAAA,OAAO,CAAG,EAAA;AAC5C,MAAA,OAAO,WAAY,CAAA,OAAA;AAAA;AAErB,IAAA,WAAA,CAAY,OAAU,GAAA,UAAA;AACtB,IAAO,OAAA,UAAA;AAAA,GACT,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,MAAM,EAAE,KAAO,EAAA,KAAA,EAAO,OAAQ,EAAA,GAAI,SAAS,YAAY;AACrD,IAAA,IAAI,YAAY,KAAW,CAAA,EAAA;AACzB,MAAO,OAAA,CAAA;AAAA;AAET,IAAA,MAAM,EAAE,UAAW,EAAA,GAAI,MAAM,UAAA,CAAW,cAAc,OAAO,CAAA;AAE7D,IAAO,OAAA,UAAA;AAAA,GACT,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAO,OAAA,EAAE,OAAO,OAAQ,EAAA;AAC1B;;;;"}
1
+ {"version":3,"file":"useAllEntitiesCount.esm.js","sources":["../../../src/components/UserListPicker/useAllEntitiesCount.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { QueryEntitiesInitialRequest } from '@backstage/catalog-client';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { compact, isEqual } from 'lodash';\nimport { useMemo, useRef } from 'react';\nimport useAsync from 'react-use/esm/useAsync';\nimport { catalogApiRef } from '../../api';\nimport { useEntityList } from '../../hooks';\nimport { reduceCatalogFilters } from '../../utils/filters';\n\nexport function useAllEntitiesCount() {\n const catalogApi = useApi(catalogApiRef);\n const { filters } = useEntityList();\n\n const prevRequest = useRef<QueryEntitiesInitialRequest>();\n const request = useMemo(() => {\n const { user, ...allFilters } = filters;\n const compacted = compact(Object.values(allFilters));\n const { orderFields, ...catalogFilters } = reduceCatalogFilters(compacted);\n const newRequest: QueryEntitiesInitialRequest = {\n ...catalogFilters,\n limit: 0,\n };\n\n if (Object.keys(catalogFilters.filter).length === 0) {\n prevRequest.current = undefined;\n return prevRequest.current;\n }\n\n if (isEqual(newRequest, prevRequest.current)) {\n return prevRequest.current;\n }\n prevRequest.current = newRequest;\n return newRequest;\n }, [filters]);\n\n const { value: count, loading } = useAsync(async () => {\n if (request === undefined) {\n return 0;\n }\n const { totalItems } = await catalogApi.queryEntities(request);\n\n return totalItems;\n }, [request]);\n\n return { count, loading };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAwBO,SAAS,mBAAsB,GAAA;AACpC,EAAM,MAAA,UAAA,GAAa,OAAO,aAAa,CAAA;AACvC,EAAM,MAAA,EAAE,OAAQ,EAAA,GAAI,aAAc,EAAA;AAElC,EAAA,MAAM,cAAc,MAAoC,EAAA;AACxD,EAAM,MAAA,OAAA,GAAU,QAAQ,MAAM;AAC5B,IAAA,MAAM,EAAE,IAAA,EAAM,GAAG,UAAA,EAAe,GAAA,OAAA;AAChC,IAAA,MAAM,SAAY,GAAA,OAAA,CAAQ,MAAO,CAAA,MAAA,CAAO,UAAU,CAAC,CAAA;AACnD,IAAA,MAAM,EAAE,WAAa,EAAA,GAAG,cAAe,EAAA,GAAI,qBAAqB,SAAS,CAAA;AACzE,IAAA,MAAM,UAA0C,GAAA;AAAA,MAC9C,GAAG,cAAA;AAAA,MACH,KAAO,EAAA;AAAA,KACT;AAEA,IAAA,IAAI,OAAO,IAAK,CAAA,cAAA,CAAe,MAAM,CAAA,CAAE,WAAW,CAAG,EAAA;AACnD,MAAA,WAAA,CAAY,OAAU,GAAA,KAAA,CAAA;AACtB,MAAA,OAAO,WAAY,CAAA,OAAA;AAAA;AAGrB,IAAA,IAAI,OAAQ,CAAA,UAAA,EAAY,WAAY,CAAA,OAAO,CAAG,EAAA;AAC5C,MAAA,OAAO,WAAY,CAAA,OAAA;AAAA;AAErB,IAAA,WAAA,CAAY,OAAU,GAAA,UAAA;AACtB,IAAO,OAAA,UAAA;AAAA,GACT,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,MAAM,EAAE,KAAO,EAAA,KAAA,EAAO,OAAQ,EAAA,GAAI,SAAS,YAAY;AACrD,IAAA,IAAI,YAAY,KAAW,CAAA,EAAA;AACzB,MAAO,OAAA,CAAA;AAAA;AAET,IAAA,MAAM,EAAE,UAAW,EAAA,GAAI,MAAM,UAAA,CAAW,cAAc,OAAO,CAAA;AAE7D,IAAO,OAAA,UAAA;AAAA,GACT,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAO,OAAA,EAAE,OAAO,OAAQ,EAAA;AAC1B;;;;"}
@@ -28,7 +28,7 @@ function useOwnedEntitiesCount() {
28
28
  []
29
29
  );
30
30
  const { user, owners, ...allFilters } = filters;
31
- const catalogFilters = reduceCatalogFilters(
31
+ const { orderFields, ...catalogFilters } = reduceCatalogFilters(
32
32
  compact(Object.values(allFilters))
33
33
  );
34
34
  const [{ value: count, loading: loadingEntityOwnership }, fetchEntities] = useAsyncFn(
@@ -1 +1 @@
1
- {"version":3,"file":"useOwnedEntitiesCount.esm.js","sources":["../../../src/components/UserListPicker/useOwnedEntitiesCount.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { identityApiRef, useApi } from '@backstage/core-plugin-api';\nimport { compact, intersection } from 'lodash';\nimport { useMemo } from 'react';\nimport useAsync from 'react-use/esm/useAsync';\nimport { catalogApiRef } from '../../api';\nimport { EntityOwnerFilter, EntityUserFilter } from '../../filters';\nimport { useEntityList } from '../../hooks';\nimport { CatalogFilters, reduceCatalogFilters } from '../../utils/filters';\nimport useAsyncFn from 'react-use/esm/useAsyncFn';\nimport useDeepCompareEffect from 'react-use/esm/useDeepCompareEffect';\n\nexport function useOwnedEntitiesCount() {\n const identityApi = useApi(identityApiRef);\n const catalogApi = useApi(catalogApiRef);\n\n const { filters } = useEntityList();\n\n const { value: ownershipEntityRefs, loading: loadingEntityRefs } = useAsync(\n async () => (await identityApi.getBackstageIdentity()).ownershipEntityRefs,\n // load only on mount\n [],\n );\n\n const { user, owners, ...allFilters } = filters;\n const catalogFilters = reduceCatalogFilters(\n compact(Object.values(allFilters)),\n );\n\n const [{ value: count, loading: loadingEntityOwnership }, fetchEntities] =\n useAsyncFn(\n async (req: {\n ownershipEntityRefs: string[];\n owners: EntityOwnerFilter | undefined;\n filter: CatalogFilters;\n }) => {\n const ownedClaims = getOwnedCountClaims(\n req.owners,\n req.ownershipEntityRefs,\n );\n if (ownedClaims === undefined) {\n // this implicitly means that there aren't claims in common with\n // the logged in users, so avoid invoking the queryEntities endpoint\n // which will implicitly returns 0\n return 0;\n }\n\n const { ['metadata.name']: metadata, ...filter } = req.filter.filter;\n\n const { totalItems } = await catalogApi.queryEntities({\n ...req.filter,\n filter: {\n ...filter,\n 'relations.ownedBy': ownedClaims,\n },\n limit: 0,\n });\n return totalItems;\n },\n [],\n { loading: true },\n );\n\n useDeepCompareEffect(() => {\n // context contains no filter, wait\n if (Object.keys(catalogFilters.filter).length === 0) {\n return;\n }\n // ownershipEntityRefs is loading, wait\n if (ownershipEntityRefs === undefined) {\n return;\n }\n fetchEntities({\n ownershipEntityRefs,\n owners,\n filter: catalogFilters,\n });\n }, [ownershipEntityRefs, owners, catalogFilters]);\n\n const loading = loadingEntityRefs || loadingEntityOwnership;\n\n return {\n count,\n loading,\n filter: useMemo(\n () => EntityUserFilter.owned(ownershipEntityRefs ?? []),\n [ownershipEntityRefs],\n ),\n ownershipEntityRefs,\n };\n}\n\nfunction getOwnedCountClaims(\n owners: EntityOwnerFilter | undefined,\n ownershipEntityRefs: string[] | undefined,\n) {\n if (ownershipEntityRefs === undefined) {\n return undefined;\n }\n const ownersRefs = owners?.values ?? [];\n if (ownersRefs.length) {\n const commonOwnedBy = intersection(ownersRefs, ownershipEntityRefs);\n if (commonOwnedBy.length === 0) {\n return undefined;\n }\n return commonOwnedBy;\n }\n return ownershipEntityRefs;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useOwnedEntitiesCount.esm.js","sources":["../../../src/components/UserListPicker/useOwnedEntitiesCount.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { identityApiRef, useApi } from '@backstage/core-plugin-api';\nimport { compact, intersection } from 'lodash';\nimport { useMemo } from 'react';\nimport useAsync from 'react-use/esm/useAsync';\nimport { catalogApiRef } from '../../api';\nimport { EntityOwnerFilter, EntityUserFilter } from '../../filters';\nimport { useEntityList } from '../../hooks';\nimport { CatalogFilters, reduceCatalogFilters } from '../../utils/filters';\nimport useAsyncFn from 'react-use/esm/useAsyncFn';\nimport useDeepCompareEffect from 'react-use/esm/useDeepCompareEffect';\n\nexport function useOwnedEntitiesCount() {\n const identityApi = useApi(identityApiRef);\n const catalogApi = useApi(catalogApiRef);\n\n const { filters } = useEntityList();\n\n const { value: ownershipEntityRefs, loading: loadingEntityRefs } = useAsync(\n async () => (await identityApi.getBackstageIdentity()).ownershipEntityRefs,\n // load only on mount\n [],\n );\n\n const { user, owners, ...allFilters } = filters;\n const { orderFields, ...catalogFilters } = reduceCatalogFilters(\n compact(Object.values(allFilters)),\n );\n\n const [{ value: count, loading: loadingEntityOwnership }, fetchEntities] =\n useAsyncFn(\n async (req: {\n ownershipEntityRefs: string[];\n owners: EntityOwnerFilter | undefined;\n filter: CatalogFilters;\n }) => {\n const ownedClaims = getOwnedCountClaims(\n req.owners,\n req.ownershipEntityRefs,\n );\n if (ownedClaims === undefined) {\n // this implicitly means that there aren't claims in common with\n // the logged in users, so avoid invoking the queryEntities endpoint\n // which will implicitly returns 0\n return 0;\n }\n\n const { ['metadata.name']: metadata, ...filter } = req.filter.filter;\n\n const { totalItems } = await catalogApi.queryEntities({\n ...req.filter,\n filter: {\n ...filter,\n 'relations.ownedBy': ownedClaims,\n },\n limit: 0,\n });\n return totalItems;\n },\n [],\n { loading: true },\n );\n\n useDeepCompareEffect(() => {\n // context contains no filter, wait\n if (Object.keys(catalogFilters.filter).length === 0) {\n return;\n }\n // ownershipEntityRefs is loading, wait\n if (ownershipEntityRefs === undefined) {\n return;\n }\n fetchEntities({\n ownershipEntityRefs,\n owners,\n filter: catalogFilters,\n });\n }, [ownershipEntityRefs, owners, catalogFilters]);\n\n const loading = loadingEntityRefs || loadingEntityOwnership;\n\n return {\n count,\n loading,\n filter: useMemo(\n () => EntityUserFilter.owned(ownershipEntityRefs ?? []),\n [ownershipEntityRefs],\n ),\n ownershipEntityRefs,\n };\n}\n\nfunction getOwnedCountClaims(\n owners: EntityOwnerFilter | undefined,\n ownershipEntityRefs: string[] | undefined,\n) {\n if (ownershipEntityRefs === undefined) {\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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"}
@@ -24,7 +24,7 @@ function useStarredEntitiesCount() {
24
24
  const request = useMemo(() => {
25
25
  const { user, ...allFilters } = filters;
26
26
  const compacted = compact(Object.values(allFilters));
27
- const catalogFilters = reduceCatalogFilters(compacted);
27
+ const { orderFields, ...catalogFilters } = reduceCatalogFilters(compacted);
28
28
  const facet = "metadata.name";
29
29
  const newRequest = {
30
30
  ...catalogFilters,
@@ -1 +1 @@
1
- {"version":3,"file":"useStarredEntitiesCount.esm.js","sources":["../../../src/components/UserListPicker/useStarredEntitiesCount.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { QueryEntitiesInitialRequest } from '@backstage/catalog-client';\nimport { parseEntityRef, stringifyEntityRef } from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { compact, isEqual } from 'lodash';\nimport { useMemo, useRef } from 'react';\nimport useAsync from 'react-use/esm/useAsync';\nimport { catalogApiRef } from '../../api';\nimport { EntityUserFilter } from '../../filters';\nimport { useEntityList, useStarredEntities } from '../../hooks';\nimport { reduceCatalogFilters } from '../../utils/filters';\n\nexport function useStarredEntitiesCount() {\n const catalogApi = useApi(catalogApiRef);\n const { filters } = useEntityList();\n const { starredEntities } = useStarredEntities();\n\n const prevRequest = useRef<QueryEntitiesInitialRequest>();\n const request = useMemo(() => {\n const { user, ...allFilters } = filters;\n const compacted = compact(Object.values(allFilters));\n const catalogFilters = reduceCatalogFilters(compacted);\n\n const facet = 'metadata.name';\n\n const newRequest: QueryEntitiesInitialRequest = {\n ...catalogFilters,\n filter: {\n ...catalogFilters.filter,\n /**\n * here we are filtering entities by `name`. Given this filter,\n * the response might contain more entities than expected, in case multiple entities\n * of different kind or namespace share the same name. Those extra entities are filtered out\n * client side by `EntityUserFilter`, so they won't be visible to the user.\n */\n [facet]: Array.from(starredEntities).map(e => parseEntityRef(e).name),\n },\n /**\n * limit is set to a high value as we are not expecting many starred entities\n */\n limit: 1000,\n };\n if (isEqual(newRequest, prevRequest.current)) {\n return prevRequest.current;\n }\n prevRequest.current = newRequest;\n\n return newRequest;\n }, [filters, starredEntities]);\n\n const { value: count, loading } = useAsync(async () => {\n if (!starredEntities.size) {\n return 0;\n }\n\n /**\n * given a list of starred entity refs and some filters coming from CatalogPage,\n * it reduces the list of starred entities, to a list of entities that matches the\n * provided filters. It won't be possible to getEntitiesByRefs\n * as the method doesn't accept any filter.\n */\n const response = await catalogApi.queryEntities(request);\n\n return response.items\n .map(e =>\n stringifyEntityRef({\n kind: e.kind,\n namespace: e.metadata.namespace,\n name: e.metadata.name,\n }),\n )\n .filter(e => starredEntities.has(e)).length;\n }, [request, starredEntities]);\n\n const filter = useMemo(\n () => EntityUserFilter.starred(Array.from(starredEntities)),\n [starredEntities],\n );\n\n return { count, loading, filter };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AA2BO,SAAS,uBAA0B,GAAA;AACxC,EAAM,MAAA,UAAA,GAAa,OAAO,aAAa,CAAA;AACvC,EAAM,MAAA,EAAE,OAAQ,EAAA,GAAI,aAAc,EAAA;AAClC,EAAM,MAAA,EAAE,eAAgB,EAAA,GAAI,kBAAmB,EAAA;AAE/C,EAAA,MAAM,cAAc,MAAoC,EAAA;AACxD,EAAM,MAAA,OAAA,GAAU,QAAQ,MAAM;AAC5B,IAAA,MAAM,EAAE,IAAA,EAAM,GAAG,UAAA,EAAe,GAAA,OAAA;AAChC,IAAA,MAAM,SAAY,GAAA,OAAA,CAAQ,MAAO,CAAA,MAAA,CAAO,UAAU,CAAC,CAAA;AACnD,IAAM,MAAA,cAAA,GAAiB,qBAAqB,SAAS,CAAA;AAErD,IAAA,MAAM,KAAQ,GAAA,eAAA;AAEd,IAAA,MAAM,UAA0C,GAAA;AAAA,MAC9C,GAAG,cAAA;AAAA,MACH,MAAQ,EAAA;AAAA,QACN,GAAG,cAAe,CAAA,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAOlB,CAAC,KAAK,GAAG,KAAA,CAAM,IAAK,CAAA,eAAe,CAAE,CAAA,GAAA,CAAI,CAAK,CAAA,KAAA,cAAA,CAAe,CAAC,CAAA,CAAE,IAAI;AAAA,OACtE;AAAA;AAAA;AAAA;AAAA,MAIA,KAAO,EAAA;AAAA,KACT;AACA,IAAA,IAAI,OAAQ,CAAA,UAAA,EAAY,WAAY,CAAA,OAAO,CAAG,EAAA;AAC5C,MAAA,OAAO,WAAY,CAAA,OAAA;AAAA;AAErB,IAAA,WAAA,CAAY,OAAU,GAAA,UAAA;AAEtB,IAAO,OAAA,UAAA;AAAA,GACN,EAAA,CAAC,OAAS,EAAA,eAAe,CAAC,CAAA;AAE7B,EAAA,MAAM,EAAE,KAAO,EAAA,KAAA,EAAO,OAAQ,EAAA,GAAI,SAAS,YAAY;AACrD,IAAI,IAAA,CAAC,gBAAgB,IAAM,EAAA;AACzB,MAAO,OAAA,CAAA;AAAA;AAST,IAAA,MAAM,QAAW,GAAA,MAAM,UAAW,CAAA,aAAA,CAAc,OAAO,CAAA;AAEvD,IAAA,OAAO,SAAS,KACb,CAAA,GAAA;AAAA,MAAI,OACH,kBAAmB,CAAA;AAAA,QACjB,MAAM,CAAE,CAAA,IAAA;AAAA,QACR,SAAA,EAAW,EAAE,QAAS,CAAA,SAAA;AAAA,QACtB,IAAA,EAAM,EAAE,QAAS,CAAA;AAAA,OAClB;AAAA,MAEF,MAAO,CAAA,CAAA,CAAA,KAAK,gBAAgB,GAAI,CAAA,CAAC,CAAC,CAAE,CAAA,MAAA;AAAA,GACtC,EAAA,CAAC,OAAS,EAAA,eAAe,CAAC,CAAA;AAE7B,EAAA,MAAM,MAAS,GAAA,OAAA;AAAA,IACb,MAAM,gBAAiB,CAAA,OAAA,CAAQ,KAAM,CAAA,IAAA,CAAK,eAAe,CAAC,CAAA;AAAA,IAC1D,CAAC,eAAe;AAAA,GAClB;AAEA,EAAO,OAAA,EAAE,KAAO,EAAA,OAAA,EAAS,MAAO,EAAA;AAClC;;;;"}
1
+ {"version":3,"file":"useStarredEntitiesCount.esm.js","sources":["../../../src/components/UserListPicker/useStarredEntitiesCount.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { QueryEntitiesInitialRequest } from '@backstage/catalog-client';\nimport { parseEntityRef, stringifyEntityRef } from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { compact, isEqual } from 'lodash';\nimport { useMemo, useRef } from 'react';\nimport useAsync from 'react-use/esm/useAsync';\nimport { catalogApiRef } from '../../api';\nimport { EntityUserFilter } from '../../filters';\nimport { useEntityList, useStarredEntities } from '../../hooks';\nimport { reduceCatalogFilters } from '../../utils/filters';\n\nexport function useStarredEntitiesCount() {\n const catalogApi = useApi(catalogApiRef);\n const { filters } = useEntityList();\n const { starredEntities } = useStarredEntities();\n\n const prevRequest = useRef<QueryEntitiesInitialRequest>();\n const request = useMemo(() => {\n const { user, ...allFilters } = filters;\n const compacted = compact(Object.values(allFilters));\n const { orderFields, ...catalogFilters } = reduceCatalogFilters(compacted);\n\n const facet = 'metadata.name';\n\n const newRequest: QueryEntitiesInitialRequest = {\n ...catalogFilters,\n filter: {\n ...catalogFilters.filter,\n /**\n * here we are filtering entities by `name`. Given this filter,\n * the response might contain more entities than expected, in case multiple entities\n * of different kind or namespace share the same name. Those extra entities are filtered out\n * client side by `EntityUserFilter`, so they won't be visible to the user.\n */\n [facet]: Array.from(starredEntities).map(e => parseEntityRef(e).name),\n },\n /**\n * limit is set to a high value as we are not expecting many starred entities\n */\n limit: 1000,\n };\n if (isEqual(newRequest, prevRequest.current)) {\n return prevRequest.current;\n }\n prevRequest.current = newRequest;\n\n return newRequest;\n }, [filters, starredEntities]);\n\n const { value: count, loading } = useAsync(async () => {\n if (!starredEntities.size) {\n return 0;\n }\n\n /**\n * given a list of starred entity refs and some filters coming from CatalogPage,\n * it reduces the list of starred entities, to a list of entities that matches the\n * provided filters. It won't be possible to getEntitiesByRefs\n * as the method doesn't accept any filter.\n */\n const response = await catalogApi.queryEntities(request);\n\n return response.items\n .map(e =>\n stringifyEntityRef({\n kind: e.kind,\n namespace: e.metadata.namespace,\n name: e.metadata.name,\n }),\n )\n .filter(e => starredEntities.has(e)).length;\n }, [request, starredEntities]);\n\n const filter = useMemo(\n () => EntityUserFilter.starred(Array.from(starredEntities)),\n [starredEntities],\n );\n\n return { count, loading, filter };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AA2BO,SAAS,uBAA0B,GAAA;AACxC,EAAM,MAAA,UAAA,GAAa,OAAO,aAAa,CAAA;AACvC,EAAM,MAAA,EAAE,OAAQ,EAAA,GAAI,aAAc,EAAA;AAClC,EAAM,MAAA,EAAE,eAAgB,EAAA,GAAI,kBAAmB,EAAA;AAE/C,EAAA,MAAM,cAAc,MAAoC,EAAA;AACxD,EAAM,MAAA,OAAA,GAAU,QAAQ,MAAM;AAC5B,IAAA,MAAM,EAAE,IAAA,EAAM,GAAG,UAAA,EAAe,GAAA,OAAA;AAChC,IAAA,MAAM,SAAY,GAAA,OAAA,CAAQ,MAAO,CAAA,MAAA,CAAO,UAAU,CAAC,CAAA;AACnD,IAAA,MAAM,EAAE,WAAa,EAAA,GAAG,cAAe,EAAA,GAAI,qBAAqB,SAAS,CAAA;AAEzE,IAAA,MAAM,KAAQ,GAAA,eAAA;AAEd,IAAA,MAAM,UAA0C,GAAA;AAAA,MAC9C,GAAG,cAAA;AAAA,MACH,MAAQ,EAAA;AAAA,QACN,GAAG,cAAe,CAAA,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAOlB,CAAC,KAAK,GAAG,KAAA,CAAM,IAAK,CAAA,eAAe,CAAE,CAAA,GAAA,CAAI,CAAK,CAAA,KAAA,cAAA,CAAe,CAAC,CAAA,CAAE,IAAI;AAAA,OACtE;AAAA;AAAA;AAAA;AAAA,MAIA,KAAO,EAAA;AAAA,KACT;AACA,IAAA,IAAI,OAAQ,CAAA,UAAA,EAAY,WAAY,CAAA,OAAO,CAAG,EAAA;AAC5C,MAAA,OAAO,WAAY,CAAA,OAAA;AAAA;AAErB,IAAA,WAAA,CAAY,OAAU,GAAA,UAAA;AAEtB,IAAO,OAAA,UAAA;AAAA,GACN,EAAA,CAAC,OAAS,EAAA,eAAe,CAAC,CAAA;AAE7B,EAAA,MAAM,EAAE,KAAO,EAAA,KAAA,EAAO,OAAQ,EAAA,GAAI,SAAS,YAAY;AACrD,IAAI,IAAA,CAAC,gBAAgB,IAAM,EAAA;AACzB,MAAO,OAAA,CAAA;AAAA;AAST,IAAA,MAAM,QAAW,GAAA,MAAM,UAAW,CAAA,aAAA,CAAc,OAAO,CAAA;AAEvD,IAAA,OAAO,SAAS,KACb,CAAA,GAAA;AAAA,MAAI,OACH,kBAAmB,CAAA;AAAA,QACjB,MAAM,CAAE,CAAA,IAAA;AAAA,QACR,SAAA,EAAW,EAAE,QAAS,CAAA,SAAA;AAAA,QACtB,IAAA,EAAM,EAAE,QAAS,CAAA;AAAA,OAClB;AAAA,MAEF,MAAO,CAAA,CAAA,CAAA,KAAK,gBAAgB,GAAI,CAAA,CAAC,CAAC,CAAE,CAAA,MAAA;AAAA,GACtC,EAAA,CAAC,OAAS,EAAA,eAAe,CAAC,CAAA;AAE7B,EAAA,MAAM,MAAS,GAAA,OAAA;AAAA,IACb,MAAM,gBAAiB,CAAA,OAAA,CAAQ,KAAM,CAAA,IAAA,CAAK,eAAe,CAAC,CAAA;AAAA,IAC1D,CAAC,eAAe;AAAA,GAClB;AAEA,EAAO,OAAA,EAAE,KAAO,EAAA,OAAA,EAAS,MAAO,EAAA;AAClC;;;;"}
@@ -220,6 +220,17 @@ class EntityErrorFilter {
220
220
  return error !== void 0 && this.value === error;
221
221
  }
222
222
  }
223
+ class EntityOrderFilter {
224
+ constructor(values) {
225
+ this.values = values;
226
+ }
227
+ getOrderFilters() {
228
+ return this.values.map(([field, order]) => ({ field, order }));
229
+ }
230
+ toQueryValue() {
231
+ return this.values.flat();
232
+ }
233
+ }
223
234
 
224
- export { EntityErrorFilter, EntityKindFilter, EntityLifecycleFilter, EntityNamespaceFilter, EntityOrphanFilter, EntityOwnerFilter, EntityTagFilter, EntityTextFilter, EntityTypeFilter, EntityUserFilter, UserListFilter };
235
+ export { EntityErrorFilter, EntityKindFilter, EntityLifecycleFilter, EntityNamespaceFilter, EntityOrderFilter, EntityOrphanFilter, EntityOwnerFilter, EntityTagFilter, EntityTextFilter, EntityTypeFilter, EntityUserFilter, UserListFilter };
225
236
  //# sourceMappingURL=filters.esm.js.map
@@ -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';\n\n/**\n * Filter entities based on Kind.\n * @public\n */\nexport class EntityKindFilter implements EntityFilter {\n constructor(readonly value: string, readonly label: string) {}\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 constructor(readonly value: string | string[]) {}\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 constructor(readonly values: string[]) {}\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 constructor(readonly value: string) {}\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 partialMatch = this.toUpperArray([\n entity.metadata.name,\n entity.metadata.title,\n (entity.spec?.profile as { displayName?: string })?.displayName,\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: ['metadata.name', 'metadata.title', 'spec.profile.displayName'],\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))\n .map(m => m.toLocaleUpperCase('en-US'));\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 constructor(readonly values: string[]) {}\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 constructor(readonly values: string[]) {}\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 private constructor(\n readonly value: UserListFilterKind,\n readonly refs?: string[],\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 constructor(\n readonly value: UserListFilterKind,\n readonly isOwnedEntity: (entity: Entity) => boolean,\n readonly isStarredEntity: (entity: Entity) => boolean,\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 constructor(readonly value: boolean) {}\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 constructor(readonly value: boolean) {}\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"],"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';\n\n/**\n * Filter entities based on Kind.\n * @public\n */\nexport class EntityKindFilter implements EntityFilter {\n constructor(readonly value: string, readonly label: string) {}\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 constructor(readonly value: string | string[]) {}\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 constructor(readonly values: string[]) {}\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 constructor(readonly value: string) {}\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 partialMatch = this.toUpperArray([\n entity.metadata.name,\n entity.metadata.title,\n (entity.spec?.profile as { displayName?: string })?.displayName,\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: ['metadata.name', 'metadata.title', 'spec.profile.displayName'],\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))\n .map(m => m.toLocaleUpperCase('en-US'));\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 constructor(readonly values: string[]) {}\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 constructor(readonly values: string[]) {}\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 private constructor(\n readonly value: UserListFilterKind,\n readonly refs?: string[],\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 constructor(\n readonly value: UserListFilterKind,\n readonly isOwnedEntity: (entity: Entity) => boolean,\n readonly isStarredEntity: (entity: Entity) => boolean,\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 constructor(readonly value: boolean) {}\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 constructor(readonly value: boolean) {}\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 constructor(readonly values: [string, 'asc' | 'desc'][]) {}\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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"}
@@ -105,8 +105,7 @@ const EntityListProvider = (props) => {
105
105
  const response = await catalogApi.queryEntities({
106
106
  ...backendFilter,
107
107
  limit,
108
- offset,
109
- orderFields: [{ field: "metadata.name", order: "asc" }]
108
+ offset
110
109
  });
111
110
  setOutputState({
112
111
  appliedFilters: requestedFilters,
@@ -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 { Entity } from '@backstage/catalog-model';\nimport { compact, isEqual } from 'lodash';\nimport qs from 'qs';\nimport {\n createContext,\n PropsWithChildren,\n useCallback,\n useContext,\n useMemo,\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 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';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { QueryEntitiesResponse } from '@backstage/catalog-client';\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};\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 limit: number;\n offset?: number;\n setLimit: (limit: number) => void;\n setOffset?: (offset: number) => void;\n paginationMode: PaginationMode;\n};\n\n/**\n * Creates new context for entity listing and filtering.\n * @public\n */\nexport const EntityListContext = createContext<\n EntityListContextProps<any> | undefined\n>(undefined);\n\ntype OutputState<EntityFilters extends DefaultEntityFilters> = {\n appliedFilters: EntityFilters;\n appliedCursor?: string;\n entities: Entity[];\n backendEntities: Entity[];\n pageInfo?: QueryEntitiesResponse['pageInfo'];\n totalItems?: number;\n offset?: number;\n limit?: 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 const getPaginationMode = (): PaginationMode => {\n if (props.pagination === true) {\n return 'cursor';\n }\n return typeof props.pagination === 'object'\n ? props.pagination.mode ?? 'cursor'\n : 'none';\n };\n\n const paginationMode = getPaginationMode();\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 });\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 [outputState, setOutputState] = useState<OutputState<EntityFilters>>(\n () => {\n return {\n appliedFilters: {} as EntityFilters,\n entities: [],\n backendEntities: [],\n pageInfo: {},\n offset,\n limit,\n };\n },\n );\n\n // The main async filter worker. Note that while it has a lot of dependencies\n // in terms of its implementation, the triggering only happens (debounced)\n // based on the requested filters changing.\n const [{ loading, error }, refresh] = useAsyncFn(\n async () => {\n const compacted = compact(Object.values(requestedFilters));\n\n const queryParams = Object.keys(requestedFilters).reduce(\n (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 },\n {} as Record<string, string | string[]>,\n );\n\n if (paginationMode !== 'none') {\n if (cursor) {\n if (cursor !== outputState.appliedCursor) {\n const entityFilter = reduceEntityFilters(compacted);\n const response = await catalogApi.queryEntities({\n cursor,\n limit,\n });\n setOutputState({\n appliedFilters: requestedFilters,\n appliedCursor: cursor,\n backendEntities: response.items,\n entities: response.items.filter(entityFilter),\n pageInfo: response.pageInfo,\n totalItems: response.totalItems,\n });\n }\n } else {\n const entityFilter = reduceEntityFilters(compacted);\n const backendFilter = reduceCatalogFilters(compacted);\n const previousBackendFilter = reduceCatalogFilters(\n compact(Object.values(outputState.appliedFilters)),\n );\n\n if (\n (paginationMode === 'offset' &&\n (outputState.limit !== limit || outputState.offset !== offset)) ||\n !isEqual(previousBackendFilter, backendFilter)\n ) {\n const response = await catalogApi.queryEntities({\n ...backendFilter,\n limit,\n offset,\n orderFields: [{ field: 'metadata.name', order: 'asc' }],\n });\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: response.items,\n entities: response.items.filter(entityFilter),\n pageInfo: response.pageInfo,\n totalItems: response.totalItems,\n limit,\n offset,\n });\n }\n }\n } else {\n const entityFilter = reduceEntityFilters(compacted);\n const backendFilter = reduceBackendCatalogFilters(compacted);\n const previousBackendFilter = reduceBackendCatalogFilters(\n compact(Object.values(outputState.appliedFilters)),\n );\n\n // TODO(mtlewis): currently entities will never be requested unless\n // there's at least one filter, we should allow an initial request\n // to happen with no filters.\n if (!isEqual(previousBackendFilter, backendFilter)) {\n // TODO(timbonicus): should limit fields here, but would need filter\n // fields + table columns\n const response = await catalogApi.getEntities({\n filter: backendFilter,\n });\n const entities = response.items.filter(entityFilter);\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: response.items,\n entities,\n totalItems: entities.length,\n });\n } else {\n const entities = outputState.backendEntities.filter(entityFilter);\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: outputState.backendEntities,\n entities,\n totalItems: entities.length,\n });\n }\n }\n\n if (isMounted()) {\n const oldParams = qs.parse(location.search, {\n ignoreQueryPrefix: true,\n });\n const newParams = qs.stringify(\n { ...oldParams, filters: queryParams, cursor, offset, limit },\n { addQueryPrefix: true, arrayFormat: 'repeat' },\n );\n const newUrl = `${window.location.pathname}${newParams}`;\n // We use direct history manipulation since useSearchParams and\n // useNavigate in react-router-dom cause unnecessary extra rerenders.\n // Also make sure to replace the state rather than pushing, since we\n // don't want there to be back/forward slots for every single filter\n // change.\n window.history?.replaceState(null, document.title, newUrl);\n }\n },\n [\n catalogApi,\n queryParameters,\n requestedFilters,\n outputState,\n cursor,\n paginationMode,\n limit,\n offset,\n ],\n { loading: true },\n );\n\n // Slight debounce on the refresh, since (especially on page load) several\n // filters will be calling this in rapid succession.\n useDebounce(refresh, 10, [requestedFilters, cursor, limit, offset]);\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 pageInfo = useMemo(() => {\n if (paginationMode !== 'cursor') {\n return undefined;\n }\n\n const prevCursor = outputState.pageInfo?.prevCursor;\n const nextCursor = outputState.pageInfo?.nextCursor;\n return {\n prev: prevCursor ? () => setCursor(prevCursor) : undefined,\n next: nextCursor ? () => setCursor(nextCursor) : undefined,\n };\n }, [paginationMode, outputState.pageInfo]);\n\n const value = useMemo(\n () => ({\n filters: outputState.appliedFilters,\n entities: outputState.entities,\n backendEntities: outputState.backendEntities,\n updateFilters,\n queryParameters,\n loading,\n error,\n pageInfo,\n totalItems: outputState.totalItems,\n limit,\n offset,\n setLimit,\n setOffset,\n paginationMode,\n }),\n [\n outputState,\n updateFilters,\n queryParameters,\n loading,\n error,\n pageInfo,\n limit,\n offset,\n paginationMode,\n setLimit,\n setOffset,\n ],\n );\n\n return (\n <EntityListContext.Provider value={value}>\n {props.children}\n </EntityListContext.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 context = useContext(EntityListContext);\n if (!context)\n throw new Error('useEntityList must be used within EntityListProvider');\n return context;\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 { Entity } from '@backstage/catalog-model';\nimport { compact, isEqual } from 'lodash';\nimport qs from 'qs';\nimport {\n createContext,\n PropsWithChildren,\n useCallback,\n useContext,\n useMemo,\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';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { QueryEntitiesResponse } from '@backstage/catalog-client';\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 limit: number;\n offset?: number;\n setLimit: (limit: number) => void;\n setOffset?: (offset: number) => void;\n paginationMode: PaginationMode;\n};\n\n/**\n * Creates new context for entity listing and filtering.\n * @public\n */\nexport const EntityListContext = createContext<\n EntityListContextProps<any> | undefined\n>(undefined);\n\ntype OutputState<EntityFilters extends DefaultEntityFilters> = {\n appliedFilters: EntityFilters;\n appliedCursor?: string;\n entities: Entity[];\n backendEntities: Entity[];\n pageInfo?: QueryEntitiesResponse['pageInfo'];\n totalItems?: number;\n offset?: number;\n limit?: 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 const getPaginationMode = (): PaginationMode => {\n if (props.pagination === true) {\n return 'cursor';\n }\n return typeof props.pagination === 'object'\n ? props.pagination.mode ?? 'cursor'\n : 'none';\n };\n\n const paginationMode = getPaginationMode();\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 });\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 [outputState, setOutputState] = useState<OutputState<EntityFilters>>(\n () => {\n return {\n appliedFilters: {} as EntityFilters,\n entities: [],\n backendEntities: [],\n pageInfo: {},\n offset,\n limit,\n };\n },\n );\n\n // The main async filter worker. Note that while it has a lot of dependencies\n // in terms of its implementation, the triggering only happens (debounced)\n // based on the requested filters changing.\n const [{ loading, error }, refresh] = useAsyncFn(\n async () => {\n const compacted = compact(Object.values(requestedFilters));\n\n const queryParams = Object.keys(requestedFilters).reduce(\n (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 },\n {} as Record<string, string | string[]>,\n );\n\n if (paginationMode !== 'none') {\n if (cursor) {\n if (cursor !== outputState.appliedCursor) {\n const entityFilter = reduceEntityFilters(compacted);\n const response = await catalogApi.queryEntities({\n cursor,\n limit,\n });\n setOutputState({\n appliedFilters: requestedFilters,\n appliedCursor: cursor,\n backendEntities: response.items,\n entities: response.items.filter(entityFilter),\n pageInfo: response.pageInfo,\n totalItems: response.totalItems,\n });\n }\n } else {\n const entityFilter = reduceEntityFilters(compacted);\n const backendFilter = reduceCatalogFilters(compacted);\n const previousBackendFilter = reduceCatalogFilters(\n compact(Object.values(outputState.appliedFilters)),\n );\n\n if (\n (paginationMode === 'offset' &&\n (outputState.limit !== limit || outputState.offset !== offset)) ||\n !isEqual(previousBackendFilter, backendFilter)\n ) {\n const response = await catalogApi.queryEntities({\n ...backendFilter,\n limit,\n offset,\n });\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: response.items,\n entities: response.items.filter(entityFilter),\n pageInfo: response.pageInfo,\n totalItems: response.totalItems,\n limit,\n offset,\n });\n }\n }\n } else {\n const entityFilter = reduceEntityFilters(compacted);\n const backendFilter = reduceBackendCatalogFilters(compacted);\n const previousBackendFilter = reduceBackendCatalogFilters(\n compact(Object.values(outputState.appliedFilters)),\n );\n\n // TODO(mtlewis): currently entities will never be requested unless\n // there's at least one filter, we should allow an initial request\n // to happen with no filters.\n if (!isEqual(previousBackendFilter, backendFilter)) {\n // TODO(timbonicus): should limit fields here, but would need filter\n // fields + table columns\n const response = await catalogApi.getEntities({\n filter: backendFilter,\n });\n const entities = response.items.filter(entityFilter);\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: response.items,\n entities,\n totalItems: entities.length,\n });\n } else {\n const entities = outputState.backendEntities.filter(entityFilter);\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: outputState.backendEntities,\n entities,\n totalItems: entities.length,\n });\n }\n }\n\n if (isMounted()) {\n const oldParams = qs.parse(location.search, {\n ignoreQueryPrefix: true,\n });\n const newParams = qs.stringify(\n { ...oldParams, filters: queryParams, cursor, offset, limit },\n { addQueryPrefix: true, arrayFormat: 'repeat' },\n );\n const newUrl = `${window.location.pathname}${newParams}`;\n // We use direct history manipulation since useSearchParams and\n // useNavigate in react-router-dom cause unnecessary extra rerenders.\n // Also make sure to replace the state rather than pushing, since we\n // don't want there to be back/forward slots for every single filter\n // change.\n window.history?.replaceState(null, document.title, newUrl);\n }\n },\n [\n catalogApi,\n queryParameters,\n requestedFilters,\n outputState,\n cursor,\n paginationMode,\n limit,\n offset,\n ],\n { loading: true },\n );\n\n // Slight debounce on the refresh, since (especially on page load) several\n // filters will be calling this in rapid succession.\n useDebounce(refresh, 10, [requestedFilters, cursor, limit, offset]);\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 pageInfo = useMemo(() => {\n if (paginationMode !== 'cursor') {\n return undefined;\n }\n\n const prevCursor = outputState.pageInfo?.prevCursor;\n const nextCursor = outputState.pageInfo?.nextCursor;\n return {\n prev: prevCursor ? () => setCursor(prevCursor) : undefined,\n next: nextCursor ? () => setCursor(nextCursor) : undefined,\n };\n }, [paginationMode, outputState.pageInfo]);\n\n const value = useMemo(\n () => ({\n filters: outputState.appliedFilters,\n entities: outputState.entities,\n backendEntities: outputState.backendEntities,\n updateFilters,\n queryParameters,\n loading,\n error,\n pageInfo,\n totalItems: outputState.totalItems,\n limit,\n offset,\n setLimit,\n setOffset,\n paginationMode,\n }),\n [\n outputState,\n updateFilters,\n queryParameters,\n loading,\n error,\n pageInfo,\n limit,\n offset,\n paginationMode,\n setLimit,\n setOffset,\n ],\n );\n\n return (\n <EntityListContext.Provider value={value}>\n {props.children}\n </EntityListContext.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 context = useContext(EntityListContext);\n if (!context)\n throw new Error('useEntityList must be used within EntityListProvider');\n return context;\n}\n"],"names":["limit","offset"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { CatalogApi } from '@backstage/catalog-client';
1
+ import { CatalogApi, EntityOrderQuery } from '@backstage/catalog-client';
2
2
  export { CATALOG_FILTER_EXISTS, CatalogApi } from '@backstage/catalog-client';
3
3
  import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
4
4
  import { ApiRef, IconComponent } from '@backstage/core-plugin-api';
@@ -707,6 +707,16 @@ declare class EntityErrorFilter implements EntityFilter {
707
707
  constructor(value: boolean);
708
708
  filterEntity(entity: Entity): boolean;
709
709
  }
710
+ /**
711
+ * Sort entities by a given field/column.
712
+ * @public
713
+ */
714
+ declare class EntityOrderFilter implements EntityFilter {
715
+ readonly values: [string, 'asc' | 'desc'][];
716
+ constructor(values: [string, 'asc' | 'desc'][]);
717
+ getOrderFilters(): EntityOrderQuery;
718
+ toQueryValue(): string[];
719
+ }
710
720
 
711
721
  /** @public */
712
722
  type DefaultEntityFilters = {
@@ -720,6 +730,7 @@ type DefaultEntityFilters = {
720
730
  orphan?: EntityOrphanFilter;
721
731
  error?: EntityErrorFilter;
722
732
  namespace?: EntityNamespaceFilter;
733
+ order?: EntityOrderFilter;
723
734
  };
724
735
  /** @public */
725
736
  type PaginationMode = 'cursor' | 'offset' | 'none';
@@ -945,11 +956,18 @@ declare const entityRouteRef: _backstage_core_plugin_api.RouteRef<{
945
956
  kind: string;
946
957
  namespace: string;
947
958
  }>;
959
+ /**
960
+ * Configurable options for `entityRouteParams`
961
+ * @public
962
+ */
963
+ type EntityRouteParamsOptions = {
964
+ encodeParams?: boolean;
965
+ };
948
966
  /**
949
967
  * Utility function to get suitable route params for entityRoute, given an
950
968
  * @public
951
969
  */
952
- declare function entityRouteParams(entity: Entity): {
970
+ declare function entityRouteParams(entityOrRef: Entity | CompoundEntityRef | string, options?: EntityRouteParamsOptions): {
953
971
  readonly kind: string;
954
972
  readonly namespace: string;
955
973
  readonly name: string;
@@ -1002,4 +1020,4 @@ declare function MockEntityListContextProvider<T extends DefaultEntityFilters =
1002
1020
  value?: Partial<EntityListContextProps<T>>;
1003
1021
  }>): react_jsx_runtime.JSX.Element;
1004
1022
 
1005
- export { type AllowedEntityFilters, AsyncEntityProvider, type AsyncEntityProviderProps, type BackstageOverrides, CatalogAutocomplete, type CatalogAutocompleteProps, CatalogFilterLayout, type CatalogReactComponentsNameToClassKey, type CatalogReactEntityAutocompletePickerClassKey, type CatalogReactEntityDisplayNameClassKey, type CatalogReactEntityLifecyclePickerClassKey, type CatalogReactEntityNamespacePickerClassKey, type CatalogReactEntityOwnerPickerClassKey, type CatalogReactEntityProcessingStatusPickerClassKey, type CatalogReactEntitySearchBarClassKey, type CatalogReactEntityTagPickerClassKey, type CatalogReactUserListPickerClassKey, type DefaultEntityFilters, DefaultFilters, type DefaultFiltersProps, EntityAutocompletePicker, type EntityAutocompletePickerProps, EntityDisplayName, type EntityDisplayNameProps, EntityErrorFilter, type EntityFilter, EntityKindFilter, EntityKindPicker, type EntityKindPickerProps, EntityLifecycleFilter, EntityLifecyclePicker, EntityListContext, type EntityListContextProps, type EntityListPagination, EntityListProvider, type EntityListProviderProps, type EntityLoadingStatus, EntityNamespaceFilter, EntityNamespacePicker, type EntityNamespacePickerProps, EntityOrphanFilter, EntityOwnerFilter, EntityOwnerPicker, type EntityOwnerPickerProps, EntityPeekAheadPopover, type EntityPeekAheadPopoverProps, type EntityPresentationApi, EntityProcessingStatusPicker, EntityProvider, type EntityProviderProps, EntityRefLink, type EntityRefLinkProps, EntityRefLinks, type EntityRefLinksProps, type EntityRefPresentation, type EntityRefPresentationSnapshot, EntitySearchBar, type EntitySourceLocation, EntityTable, type EntityTableProps, EntityTagFilter, EntityTagPicker, type EntityTagPickerProps, EntityTextFilter, EntityTypeFilter, EntityTypePicker, type EntityTypePickerProps, EntityUserFilter, FavoriteEntity, type FavoriteEntityProps, type FixedWidthFormControlLabelClassKey, InspectEntityDialog, MissingAnnotationEmptyState, type MissingAnnotationEmptyStateClassKey, MockEntityListContextProvider, MockStarredEntitiesApi, type PaginationMode, type StarredEntitiesApi, UnregisterEntityDialog, type UnregisterEntityDialogProps, UserListFilter, type UserListFilterKind, UserListPicker, type UserListPickerProps, catalogApiRef, columnFactories, defaultEntityPresentation, entityPresentationApiRef, entityRouteParams, entityRouteRef, getEntityRelations, getEntitySourceLocation, humanizeEntityRef, starredEntitiesApiRef, useAsyncEntity, useEntity, useEntityList, useEntityOwnership, useEntityPresentation, useEntityTypeFilter, useRelatedEntities, useStarredEntities, useStarredEntity };
1023
+ export { type AllowedEntityFilters, AsyncEntityProvider, type AsyncEntityProviderProps, type BackstageOverrides, CatalogAutocomplete, type CatalogAutocompleteProps, CatalogFilterLayout, type CatalogReactComponentsNameToClassKey, type CatalogReactEntityAutocompletePickerClassKey, type CatalogReactEntityDisplayNameClassKey, type CatalogReactEntityLifecyclePickerClassKey, type CatalogReactEntityNamespacePickerClassKey, type CatalogReactEntityOwnerPickerClassKey, type CatalogReactEntityProcessingStatusPickerClassKey, type CatalogReactEntitySearchBarClassKey, type CatalogReactEntityTagPickerClassKey, type CatalogReactUserListPickerClassKey, type DefaultEntityFilters, DefaultFilters, type DefaultFiltersProps, EntityAutocompletePicker, type EntityAutocompletePickerProps, EntityDisplayName, type EntityDisplayNameProps, EntityErrorFilter, type EntityFilter, EntityKindFilter, EntityKindPicker, type EntityKindPickerProps, EntityLifecycleFilter, EntityLifecyclePicker, EntityListContext, type EntityListContextProps, type EntityListPagination, EntityListProvider, type EntityListProviderProps, type EntityLoadingStatus, EntityNamespaceFilter, EntityNamespacePicker, type EntityNamespacePickerProps, EntityOrderFilter, EntityOrphanFilter, EntityOwnerFilter, EntityOwnerPicker, type EntityOwnerPickerProps, EntityPeekAheadPopover, type EntityPeekAheadPopoverProps, type EntityPresentationApi, EntityProcessingStatusPicker, EntityProvider, type EntityProviderProps, EntityRefLink, type EntityRefLinkProps, EntityRefLinks, type EntityRefLinksProps, type EntityRefPresentation, type EntityRefPresentationSnapshot, type EntityRouteParamsOptions, EntitySearchBar, type EntitySourceLocation, EntityTable, type EntityTableProps, EntityTagFilter, EntityTagPicker, type EntityTagPickerProps, EntityTextFilter, EntityTypeFilter, EntityTypePicker, type EntityTypePickerProps, EntityUserFilter, FavoriteEntity, type FavoriteEntityProps, type FixedWidthFormControlLabelClassKey, InspectEntityDialog, MissingAnnotationEmptyState, type MissingAnnotationEmptyStateClassKey, MockEntityListContextProvider, MockStarredEntitiesApi, type PaginationMode, type StarredEntitiesApi, UnregisterEntityDialog, type UnregisterEntityDialogProps, UserListFilter, type UserListFilterKind, UserListPicker, type UserListPickerProps, catalogApiRef, columnFactories, defaultEntityPresentation, entityPresentationApiRef, entityRouteParams, entityRouteRef, getEntityRelations, getEntitySourceLocation, humanizeEntityRef, starredEntitiesApiRef, useAsyncEntity, useEntity, useEntityList, useEntityOwnership, useEntityPresentation, useEntityTypeFilter, useRelatedEntities, useStarredEntities, useStarredEntity };
package/dist/index.esm.js CHANGED
@@ -36,7 +36,7 @@ export { useRelatedEntities } from './hooks/useRelatedEntities.esm.js';
36
36
  export { useStarredEntities } from './hooks/useStarredEntities.esm.js';
37
37
  export { useStarredEntity } from './hooks/useStarredEntity.esm.js';
38
38
  export { useEntityOwnership } from './hooks/useEntityOwnership.esm.js';
39
- export { EntityErrorFilter, EntityKindFilter, EntityLifecycleFilter, EntityNamespaceFilter, EntityOrphanFilter, EntityOwnerFilter, EntityTagFilter, EntityTextFilter, EntityTypeFilter, EntityUserFilter, UserListFilter } from './filters.esm.js';
39
+ export { EntityErrorFilter, EntityKindFilter, EntityLifecycleFilter, EntityNamespaceFilter, EntityOrderFilter, EntityOrphanFilter, EntityOwnerFilter, EntityTagFilter, EntityTextFilter, EntityTypeFilter, EntityUserFilter, UserListFilter } from './filters.esm.js';
40
40
  export { entityRouteParams, entityRouteRef } from './routes.esm.js';
41
41
  export { getEntityRelations } from './utils/getEntityRelations.esm.js';
42
42
  export { getEntitySourceLocation } from './utils/getEntitySourceLocation.esm.js';
@@ -1,4 +1,4 @@
1
- import { DEFAULT_NAMESPACE } from '@backstage/catalog-model';
1
+ import { parseEntityRef, DEFAULT_NAMESPACE } from '@backstage/catalog-model';
2
2
  import { createRouteRef } from '@backstage/core-plugin-api';
3
3
  import { getOrCreateGlobalSingleton } from '@backstage/version-bridge';
4
4
 
@@ -9,11 +9,36 @@ const entityRouteRef = getOrCreateGlobalSingleton(
9
9
  params: ["namespace", "kind", "name"]
10
10
  })
11
11
  );
12
- function entityRouteParams(entity) {
12
+ function entityRouteParams(entityOrRef, options) {
13
+ let kind;
14
+ let namespace;
15
+ let name;
16
+ if (typeof entityOrRef === "string") {
17
+ const parsed = parseEntityRef(entityOrRef);
18
+ kind = parsed.kind;
19
+ namespace = parsed.namespace;
20
+ name = parsed.name;
21
+ } else if ("metadata" in entityOrRef) {
22
+ kind = entityOrRef.kind;
23
+ namespace = entityOrRef.metadata.namespace;
24
+ name = entityOrRef.metadata.name;
25
+ } else {
26
+ kind = entityOrRef.kind;
27
+ namespace = entityOrRef.namespace;
28
+ name = entityOrRef.name;
29
+ }
30
+ kind = kind.toLocaleLowerCase("en-US");
31
+ namespace = namespace?.toLocaleLowerCase("en-US") ?? DEFAULT_NAMESPACE;
32
+ const { encodeParams = false } = options || {};
33
+ if (encodeParams) {
34
+ kind = encodeURIComponent(kind);
35
+ namespace = encodeURIComponent(namespace);
36
+ name = encodeURIComponent(name);
37
+ }
13
38
  return {
14
- kind: entity.kind.toLocaleLowerCase("en-US"),
15
- namespace: entity.metadata.namespace?.toLocaleLowerCase("en-US") ?? DEFAULT_NAMESPACE,
16
- name: entity.metadata.name
39
+ kind,
40
+ namespace,
41
+ name
17
42
  };
18
43
  }
19
44
 
@@ -1 +1 @@
1
- {"version":3,"file":"routes.esm.js","sources":["../src/routes.ts"],"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 { Entity, DEFAULT_NAMESPACE } from '@backstage/catalog-model';\nimport { createRouteRef } from '@backstage/core-plugin-api';\nimport { getOrCreateGlobalSingleton } from '@backstage/version-bridge';\n\n/**\n * A stable route ref that points to the catalog page for an individual entity.\n *\n * This `RouteRef` can be imported and used directly, and does not need to be referenced\n * via an `ExternalRouteRef`.\n *\n * If you want to replace the `EntityPage` from `@backstage/catalog-plugin` in your app,\n * you need to use the `entityRouteRef` as the mount point instead of your own.\n * @public\n */\nexport const entityRouteRef = getOrCreateGlobalSingleton(\n 'catalog:entity-route-ref',\n () =>\n createRouteRef({\n id: 'catalog:entity',\n params: ['namespace', 'kind', 'name'],\n }),\n);\n\n/**\n * Utility function to get suitable route params for entityRoute, given an\n * @public\n */\nexport function entityRouteParams(entity: Entity) {\n return {\n kind: entity.kind.toLocaleLowerCase('en-US'),\n namespace:\n entity.metadata.namespace?.toLocaleLowerCase('en-US') ??\n DEFAULT_NAMESPACE,\n name: entity.metadata.name,\n } as const;\n}\n"],"names":[],"mappings":";;;;AA8BO,MAAM,cAAiB,GAAA,0BAAA;AAAA,EAC5B,0BAAA;AAAA,EACA,MACE,cAAe,CAAA;AAAA,IACb,EAAI,EAAA,gBAAA;AAAA,IACJ,MAAQ,EAAA,CAAC,WAAa,EAAA,MAAA,EAAQ,MAAM;AAAA,GACrC;AACL;AAMO,SAAS,kBAAkB,MAAgB,EAAA;AAChD,EAAO,OAAA;AAAA,IACL,IAAM,EAAA,MAAA,CAAO,IAAK,CAAA,iBAAA,CAAkB,OAAO,CAAA;AAAA,IAC3C,WACE,MAAO,CAAA,QAAA,CAAS,SAAW,EAAA,iBAAA,CAAkB,OAAO,CACpD,IAAA,iBAAA;AAAA,IACF,IAAA,EAAM,OAAO,QAAS,CAAA;AAAA,GACxB;AACF;;;;"}
1
+ {"version":3,"file":"routes.esm.js","sources":["../src/routes.ts"],"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 {\n Entity,\n DEFAULT_NAMESPACE,\n CompoundEntityRef,\n parseEntityRef,\n} from '@backstage/catalog-model';\nimport { createRouteRef } from '@backstage/core-plugin-api';\nimport { getOrCreateGlobalSingleton } from '@backstage/version-bridge';\n\n/**\n * A stable route ref that points to the catalog page for an individual entity.\n *\n * This `RouteRef` can be imported and used directly, and does not need to be referenced\n * via an `ExternalRouteRef`.\n *\n * If you want to replace the `EntityPage` from `@backstage/catalog-plugin` in your app,\n * you need to use the `entityRouteRef` as the mount point instead of your own.\n * @public\n */\nexport const entityRouteRef = getOrCreateGlobalSingleton(\n 'catalog:entity-route-ref',\n () =>\n createRouteRef({\n id: 'catalog:entity',\n params: ['namespace', 'kind', 'name'],\n }),\n);\n\n/**\n * Configurable options for `entityRouteParams`\n * @public\n */\nexport type EntityRouteParamsOptions = {\n encodeParams?: boolean;\n};\n\n/**\n * Utility function to get suitable route params for entityRoute, given an\n * @public\n */\nexport function entityRouteParams(\n entityOrRef: Entity | CompoundEntityRef | string,\n options?: EntityRouteParamsOptions,\n) {\n let kind;\n let namespace;\n let name;\n\n if (typeof entityOrRef === 'string') {\n const parsed = parseEntityRef(entityOrRef);\n kind = parsed.kind;\n namespace = parsed.namespace;\n name = parsed.name;\n } else if ('metadata' in entityOrRef) {\n kind = entityOrRef.kind;\n namespace = entityOrRef.metadata.namespace;\n name = entityOrRef.metadata.name;\n } else {\n kind = entityOrRef.kind;\n namespace = entityOrRef.namespace;\n name = entityOrRef.name;\n }\n\n kind = kind.toLocaleLowerCase('en-US');\n namespace = namespace?.toLocaleLowerCase('en-US') ?? DEFAULT_NAMESPACE;\n\n const { encodeParams = false } = options || {};\n if (encodeParams) {\n kind = encodeURIComponent(kind);\n namespace = encodeURIComponent(namespace);\n name = encodeURIComponent(name);\n }\n\n return {\n kind,\n namespace,\n name,\n } as const;\n}\n"],"names":[],"mappings":";;;;AAmCO,MAAM,cAAiB,GAAA,0BAAA;AAAA,EAC5B,0BAAA;AAAA,EACA,MACE,cAAe,CAAA;AAAA,IACb,EAAI,EAAA,gBAAA;AAAA,IACJ,MAAQ,EAAA,CAAC,WAAa,EAAA,MAAA,EAAQ,MAAM;AAAA,GACrC;AACL;AAcgB,SAAA,iBAAA,CACd,aACA,OACA,EAAA;AACA,EAAI,IAAA,IAAA;AACJ,EAAI,IAAA,SAAA;AACJ,EAAI,IAAA,IAAA;AAEJ,EAAI,IAAA,OAAO,gBAAgB,QAAU,EAAA;AACnC,IAAM,MAAA,MAAA,GAAS,eAAe,WAAW,CAAA;AACzC,IAAA,IAAA,GAAO,MAAO,CAAA,IAAA;AACd,IAAA,SAAA,GAAY,MAAO,CAAA,SAAA;AACnB,IAAA,IAAA,GAAO,MAAO,CAAA,IAAA;AAAA,GAChB,MAAA,IAAW,cAAc,WAAa,EAAA;AACpC,IAAA,IAAA,GAAO,WAAY,CAAA,IAAA;AACnB,IAAA,SAAA,GAAY,YAAY,QAAS,CAAA,SAAA;AACjC,IAAA,IAAA,GAAO,YAAY,QAAS,CAAA,IAAA;AAAA,GACvB,MAAA;AACL,IAAA,IAAA,GAAO,WAAY,CAAA,IAAA;AACnB,IAAA,SAAA,GAAY,WAAY,CAAA,SAAA;AACxB,IAAA,IAAA,GAAO,WAAY,CAAA,IAAA;AAAA;AAGrB,EAAO,IAAA,GAAA,IAAA,CAAK,kBAAkB,OAAO,CAAA;AACrC,EAAY,SAAA,GAAA,SAAA,EAAW,iBAAkB,CAAA,OAAO,CAAK,IAAA,iBAAA;AAErD,EAAA,MAAM,EAAE,YAAA,GAAe,KAAM,EAAA,GAAI,WAAW,EAAC;AAC7C,EAAA,IAAI,YAAc,EAAA;AAChB,IAAA,IAAA,GAAO,mBAAmB,IAAI,CAAA;AAC9B,IAAA,SAAA,GAAY,mBAAmB,SAAS,CAAA;AACxC,IAAA,IAAA,GAAO,mBAAmB,IAAI,CAAA;AAAA;AAGhC,EAAO,OAAA;AAAA,IACL,IAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
@@ -41,6 +41,7 @@ function catalogApiMock(options) {
41
41
  removeEntityByUid: jest.fn(),
42
42
  refreshEntity: jest.fn(),
43
43
  getEntityFacets: jest.fn(),
44
+ getLocations: jest.fn(),
44
45
  getLocationById: jest.fn(),
45
46
  getLocationByRef: jest.fn(),
46
47
  addLocation: jest.fn(),
@@ -1 +1 @@
1
- {"version":3,"file":"catalogApiMock.esm.js","sources":["../../src/testUtils/catalogApiMock.ts"],"sourcesContent":["/*\n * Copyright 2024 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 ApiFactory,\n ApiRef,\n createApiFactory,\n} from '@backstage/frontend-plugin-api';\nimport { InMemoryCatalogClient } from '@backstage/catalog-client/testUtils';\nimport { Entity } from '@backstage/catalog-model';\nimport { catalogApiRef } from '@backstage/plugin-catalog-react';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { ApiMock } from '@backstage/frontend-test-utils';\n\n/** @internal */\nfunction simpleMock<TApi>(\n ref: ApiRef<TApi>,\n mockFactory: () => jest.Mocked<TApi>,\n): (partialImpl?: Partial<TApi>) => ApiMock<TApi> {\n return partialImpl => {\n const mock = mockFactory();\n if (partialImpl) {\n for (const [key, impl] of Object.entries(partialImpl)) {\n if (typeof impl === 'function') {\n (mock as any)[key].mockImplementation(impl);\n } else {\n (mock as any)[key] = impl;\n }\n }\n }\n return Object.assign(mock, {\n factory: createApiFactory({\n api: ref,\n deps: {},\n factory: () => mock,\n }),\n }) as ApiMock<TApi>;\n };\n}\n\n/**\n * Creates a fake catalog client that handles entities in memory storage. Note\n * that this client may be severely limited in functionality, and advanced\n * functions may not be available at all.\n *\n * @public\n */\nexport function catalogApiMock(options?: { entities?: Entity[] }): CatalogApi {\n return new InMemoryCatalogClient(options);\n}\n\n/**\n * A collection of mock functionality for the catalog service.\n *\n * @public\n */\nexport namespace catalogApiMock {\n /**\n * Creates a fake catalog client that handles entities in memory storage. Note\n * that this client may be severely limited in functionality, and advanced\n * functions may not be available at all.\n */\n export const factory = (options?: {\n entities?: Entity[];\n }): ApiFactory<CatalogApi, CatalogApi, {}> =>\n createApiFactory({\n api: catalogApiRef,\n deps: {},\n factory: () => new InMemoryCatalogClient(options),\n });\n /**\n * Creates a catalog client whose methods are mock functions, possibly with\n * some of them overloaded by the caller.\n */\n export const mock = simpleMock(catalogApiRef, () => ({\n getEntities: jest.fn(),\n getEntitiesByRefs: jest.fn(),\n queryEntities: jest.fn(),\n getEntityAncestors: jest.fn(),\n getEntityByRef: jest.fn(),\n removeEntityByUid: jest.fn(),\n refreshEntity: jest.fn(),\n getEntityFacets: jest.fn(),\n getLocationById: jest.fn(),\n getLocationByRef: jest.fn(),\n addLocation: jest.fn(),\n removeLocationById: jest.fn(),\n getLocationByEntity: jest.fn(),\n validateEntity: jest.fn(),\n }));\n}\n"],"names":["catalogApiMock"],"mappings":";;;;AA4BA,SAAS,UAAA,CACP,KACA,WACgD,EAAA;AAChD,EAAA,OAAO,CAAe,WAAA,KAAA;AACpB,IAAA,MAAM,OAAO,WAAY,EAAA;AACzB,IAAA,IAAI,WAAa,EAAA;AACf,MAAA,KAAA,MAAW,CAAC,GAAK,EAAA,IAAI,KAAK,MAAO,CAAA,OAAA,CAAQ,WAAW,CAAG,EAAA;AACrD,QAAI,IAAA,OAAO,SAAS,UAAY,EAAA;AAC9B,UAAC,IAAa,CAAA,GAAG,CAAE,CAAA,kBAAA,CAAmB,IAAI,CAAA;AAAA,SACrC,MAAA;AACL,UAAC,IAAA,CAAa,GAAG,CAAI,GAAA,IAAA;AAAA;AACvB;AACF;AAEF,IAAO,OAAA,MAAA,CAAO,OAAO,IAAM,EAAA;AAAA,MACzB,SAAS,gBAAiB,CAAA;AAAA,QACxB,GAAK,EAAA,GAAA;AAAA,QACL,MAAM,EAAC;AAAA,QACP,SAAS,MAAM;AAAA,OAChB;AAAA,KACF,CAAA;AAAA,GACH;AACF;AASO,SAAS,eAAe,OAA+C,EAAA;AAC5E,EAAO,OAAA,IAAI,sBAAsB,OAAO,CAAA;AAC1C;AAAA,CAOO,CAAUA,eAAV,KAAA;AAME,EAAMA,eAAA,CAAA,OAAA,GAAU,CAAC,OAAA,KAGtB,gBAAiB,CAAA;AAAA,IACf,GAAK,EAAA,aAAA;AAAA,IACL,MAAM,EAAC;AAAA,IACP,OAAS,EAAA,MAAM,IAAI,qBAAA,CAAsB,OAAO;AAAA,GACjD,CAAA;AAKI,EAAMA,eAAA,CAAA,IAAA,GAAO,UAAW,CAAA,aAAA,EAAe,OAAO;AAAA,IACnD,WAAA,EAAa,KAAK,EAAG,EAAA;AAAA,IACrB,iBAAA,EAAmB,KAAK,EAAG,EAAA;AAAA,IAC3B,aAAA,EAAe,KAAK,EAAG,EAAA;AAAA,IACvB,kBAAA,EAAoB,KAAK,EAAG,EAAA;AAAA,IAC5B,cAAA,EAAgB,KAAK,EAAG,EAAA;AAAA,IACxB,iBAAA,EAAmB,KAAK,EAAG,EAAA;AAAA,IAC3B,aAAA,EAAe,KAAK,EAAG,EAAA;AAAA,IACvB,eAAA,EAAiB,KAAK,EAAG,EAAA;AAAA,IACzB,eAAA,EAAiB,KAAK,EAAG,EAAA;AAAA,IACzB,gBAAA,EAAkB,KAAK,EAAG,EAAA;AAAA,IAC1B,WAAA,EAAa,KAAK,EAAG,EAAA;AAAA,IACrB,kBAAA,EAAoB,KAAK,EAAG,EAAA;AAAA,IAC5B,mBAAA,EAAqB,KAAK,EAAG,EAAA;AAAA,IAC7B,cAAA,EAAgB,KAAK,EAAG;AAAA,GACxB,CAAA,CAAA;AAAA,CAjCa,EAAA,cAAA,KAAA,cAAA,GAAA,EAAA,CAAA,CAAA;;;;"}
1
+ {"version":3,"file":"catalogApiMock.esm.js","sources":["../../src/testUtils/catalogApiMock.ts"],"sourcesContent":["/*\n * Copyright 2024 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 ApiFactory,\n ApiRef,\n createApiFactory,\n} from '@backstage/frontend-plugin-api';\nimport { InMemoryCatalogClient } from '@backstage/catalog-client/testUtils';\nimport { Entity } from '@backstage/catalog-model';\nimport { catalogApiRef } from '@backstage/plugin-catalog-react';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { ApiMock } from '@backstage/frontend-test-utils';\n\n/** @internal */\nfunction simpleMock<TApi>(\n ref: ApiRef<TApi>,\n mockFactory: () => jest.Mocked<TApi>,\n): (partialImpl?: Partial<TApi>) => ApiMock<TApi> {\n return partialImpl => {\n const mock = mockFactory();\n if (partialImpl) {\n for (const [key, impl] of Object.entries(partialImpl)) {\n if (typeof impl === 'function') {\n (mock as any)[key].mockImplementation(impl);\n } else {\n (mock as any)[key] = impl;\n }\n }\n }\n return Object.assign(mock, {\n factory: createApiFactory({\n api: ref,\n deps: {},\n factory: () => mock,\n }),\n }) as ApiMock<TApi>;\n };\n}\n\n/**\n * Creates a fake catalog client that handles entities in memory storage. Note\n * that this client may be severely limited in functionality, and advanced\n * functions may not be available at all.\n *\n * @public\n */\nexport function catalogApiMock(options?: { entities?: Entity[] }): CatalogApi {\n return new InMemoryCatalogClient(options);\n}\n\n/**\n * A collection of mock functionality for the catalog service.\n *\n * @public\n */\nexport namespace catalogApiMock {\n /**\n * Creates a fake catalog client that handles entities in memory storage. Note\n * that this client may be severely limited in functionality, and advanced\n * functions may not be available at all.\n */\n export const factory = (options?: {\n entities?: Entity[];\n }): ApiFactory<CatalogApi, CatalogApi, {}> =>\n createApiFactory({\n api: catalogApiRef,\n deps: {},\n factory: () => new InMemoryCatalogClient(options),\n });\n /**\n * Creates a catalog client whose methods are mock functions, possibly with\n * some of them overloaded by the caller.\n */\n export const mock = simpleMock(catalogApiRef, () => ({\n getEntities: jest.fn(),\n getEntitiesByRefs: jest.fn(),\n queryEntities: jest.fn(),\n getEntityAncestors: jest.fn(),\n getEntityByRef: jest.fn(),\n removeEntityByUid: jest.fn(),\n refreshEntity: jest.fn(),\n getEntityFacets: jest.fn(),\n getLocations: jest.fn(),\n getLocationById: jest.fn(),\n getLocationByRef: jest.fn(),\n addLocation: jest.fn(),\n removeLocationById: jest.fn(),\n getLocationByEntity: jest.fn(),\n validateEntity: jest.fn(),\n }));\n}\n"],"names":["catalogApiMock"],"mappings":";;;;AA4BA,SAAS,UAAA,CACP,KACA,WACgD,EAAA;AAChD,EAAA,OAAO,CAAe,WAAA,KAAA;AACpB,IAAA,MAAM,OAAO,WAAY,EAAA;AACzB,IAAA,IAAI,WAAa,EAAA;AACf,MAAA,KAAA,MAAW,CAAC,GAAK,EAAA,IAAI,KAAK,MAAO,CAAA,OAAA,CAAQ,WAAW,CAAG,EAAA;AACrD,QAAI,IAAA,OAAO,SAAS,UAAY,EAAA;AAC9B,UAAC,IAAa,CAAA,GAAG,CAAE,CAAA,kBAAA,CAAmB,IAAI,CAAA;AAAA,SACrC,MAAA;AACL,UAAC,IAAA,CAAa,GAAG,CAAI,GAAA,IAAA;AAAA;AACvB;AACF;AAEF,IAAO,OAAA,MAAA,CAAO,OAAO,IAAM,EAAA;AAAA,MACzB,SAAS,gBAAiB,CAAA;AAAA,QACxB,GAAK,EAAA,GAAA;AAAA,QACL,MAAM,EAAC;AAAA,QACP,SAAS,MAAM;AAAA,OAChB;AAAA,KACF,CAAA;AAAA,GACH;AACF;AASO,SAAS,eAAe,OAA+C,EAAA;AAC5E,EAAO,OAAA,IAAI,sBAAsB,OAAO,CAAA;AAC1C;AAAA,CAOO,CAAUA,eAAV,KAAA;AAME,EAAMA,eAAA,CAAA,OAAA,GAAU,CAAC,OAAA,KAGtB,gBAAiB,CAAA;AAAA,IACf,GAAK,EAAA,aAAA;AAAA,IACL,MAAM,EAAC;AAAA,IACP,OAAS,EAAA,MAAM,IAAI,qBAAA,CAAsB,OAAO;AAAA,GACjD,CAAA;AAKI,EAAMA,eAAA,CAAA,IAAA,GAAO,UAAW,CAAA,aAAA,EAAe,OAAO;AAAA,IACnD,WAAA,EAAa,KAAK,EAAG,EAAA;AAAA,IACrB,iBAAA,EAAmB,KAAK,EAAG,EAAA;AAAA,IAC3B,aAAA,EAAe,KAAK,EAAG,EAAA;AAAA,IACvB,kBAAA,EAAoB,KAAK,EAAG,EAAA;AAAA,IAC5B,cAAA,EAAgB,KAAK,EAAG,EAAA;AAAA,IACxB,iBAAA,EAAmB,KAAK,EAAG,EAAA;AAAA,IAC3B,aAAA,EAAe,KAAK,EAAG,EAAA;AAAA,IACvB,eAAA,EAAiB,KAAK,EAAG,EAAA;AAAA,IACzB,YAAA,EAAc,KAAK,EAAG,EAAA;AAAA,IACtB,eAAA,EAAiB,KAAK,EAAG,EAAA;AAAA,IACzB,gBAAA,EAAkB,KAAK,EAAG,EAAA;AAAA,IAC1B,WAAA,EAAa,KAAK,EAAG,EAAA;AAAA,IACrB,kBAAA,EAAoB,KAAK,EAAG,EAAA;AAAA,IAC5B,mBAAA,EAAqB,KAAK,EAAG,EAAA;AAAA,IAC7B,cAAA,EAAgB,KAAK,EAAG;AAAA,GACxB,CAAA,CAAA;AAAA,CAlCa,EAAA,cAAA,KAAA,cAAA,GAAA,EAAA,CAAA,CAAA;;;;"}
@@ -3,6 +3,9 @@ import { EntityTagFilter, EntityOwnerFilter, EntityLifecycleFilter, EntityNamesp
3
3
  function isEntityTextFilter(t) {
4
4
  return !!t.getFullTextFilters;
5
5
  }
6
+ function isEntityOrderFilter(t) {
7
+ return !!t.getOrderFilters;
8
+ }
6
9
  function reduceCatalogFilters(filters) {
7
10
  const condensedFilters = filters.reduce(
8
11
  (compoundFilter, filter) => {
@@ -14,7 +17,13 @@ function reduceCatalogFilters(filters) {
14
17
  {}
15
18
  );
16
19
  const fullTextFilter = filters.find(isEntityTextFilter)?.getFullTextFilters();
17
- return { filter: condensedFilters, fullTextFilter };
20
+ const orderFields = filters.find(isEntityOrderFilter)?.getOrderFilters() || [
21
+ {
22
+ field: "metadata.name",
23
+ order: "asc"
24
+ }
25
+ ];
26
+ return { filter: condensedFilters, fullTextFilter, orderFields };
18
27
  }
19
28
  function reduceBackendCatalogFilters(filters) {
20
29
  const backendCatalogFilters = {};
@@ -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 EntityOrphanFilter,\n EntityOwnerFilter,\n EntityTagFilter,\n EntityTextFilter,\n EntityUserFilter,\n UserListFilter,\n} from '../filters';\n\nexport interface CatalogFilters {\n filter: Record<string, string | symbol | (string | symbol)[]>;\n fullTextFilter?: {\n term: string;\n };\n}\n\nfunction isEntityTextFilter(t: EntityFilter): t is EntityTextFilter {\n return !!(t as EntityTextFilter).getFullTextFilters;\n}\n\nexport function reduceCatalogFilters(filters: EntityFilter[]): CatalogFilters {\n const condensedFilters = filters.reduce<CatalogFilters['filter']>(\n (compoundFilter, filter) => {\n return {\n ...compoundFilter,\n ...(filter.getCatalogFilters ? filter.getCatalogFilters() : {}),\n };\n },\n {},\n );\n\n const fullTextFilter = filters.find(isEntityTextFilter)?.getFullTextFilters();\n return { filter: condensedFilters, fullTextFilter };\n}\n\n/**\n * This function computes and returns an object containing the filters to be sent\n * to the backend. Any filter coming from `EntityKindFilter` and `EntityTypeFilter`, together\n * with custom filter set by the adopters is allowed. This function is used by `EntityListProvider`\n * and it won't be needed anymore in the future once pagination is implemented, as all the filters\n * will be applied backend-side.\n */\nexport function reduceBackendCatalogFilters(filters: EntityFilter[]) {\n const backendCatalogFilters: Record<\n string,\n string | symbol | (string | symbol)[]\n > = {};\n\n filters.forEach(filter => {\n if (\n filter instanceof EntityTagFilter ||\n filter instanceof EntityOwnerFilter ||\n filter instanceof EntityLifecycleFilter ||\n filter instanceof EntityNamespaceFilter ||\n filter instanceof EntityUserFilter ||\n filter instanceof EntityOrphanFilter ||\n filter instanceof EntityTextFilter ||\n filter instanceof UserListFilter\n ) {\n return;\n }\n Object.assign(backendCatalogFilters, filter.getCatalogFilters?.() || {});\n });\n\n return backendCatalogFilters;\n}\n\nexport function reduceEntityFilters(\n filters: EntityFilter[],\n): (entity: Entity) => boolean {\n return (entity: Entity) =>\n filters.every(\n filter => !filter.filterEntity || filter.filterEntity(entity),\n );\n}\n"],"names":[],"mappings":";;AAoCA,SAAS,mBAAmB,CAAwC,EAAA;AAClE,EAAO,OAAA,CAAC,CAAE,CAAuB,CAAA,kBAAA;AACnC;AAEO,SAAS,qBAAqB,OAAyC,EAAA;AAC5E,EAAA,MAAM,mBAAmB,OAAQ,CAAA,MAAA;AAAA,IAC/B,CAAC,gBAAgB,MAAW,KAAA;AAC1B,MAAO,OAAA;AAAA,QACL,GAAG,cAAA;AAAA,QACH,GAAI,MAAO,CAAA,iBAAA,GAAoB,MAAO,CAAA,iBAAA,KAAsB;AAAC,OAC/D;AAAA,KACF;AAAA,IACA;AAAC,GACH;AAEA,EAAA,MAAM,cAAiB,GAAA,OAAA,CAAQ,IAAK,CAAA,kBAAkB,GAAG,kBAAmB,EAAA;AAC5E,EAAO,OAAA,EAAE,MAAQ,EAAA,gBAAA,EAAkB,cAAe,EAAA;AACpD;AASO,SAAS,4BAA4B,OAAyB,EAAA;AACnE,EAAA,MAAM,wBAGF,EAAC;AAEL,EAAA,OAAA,CAAQ,QAAQ,CAAU,MAAA,KAAA;AACxB,IAAA,IACE,MAAkB,YAAA,eAAA,IAClB,MAAkB,YAAA,iBAAA,IAClB,kBAAkB,qBAClB,IAAA,MAAA,YAAkB,qBAClB,IAAA,MAAA,YAAkB,oBAClB,MAAkB,YAAA,kBAAA,IAClB,MAAkB,YAAA,gBAAA,IAClB,kBAAkB,cAClB,EAAA;AACA,MAAA;AAAA;AAEF,IAAA,MAAA,CAAO,OAAO,qBAAuB,EAAA,MAAA,CAAO,iBAAoB,IAAA,IAAK,EAAE,CAAA;AAAA,GACxE,CAAA;AAED,EAAO,OAAA,qBAAA;AACT;AAEO,SAAS,oBACd,OAC6B,EAAA;AAC7B,EAAO,OAAA,CAAC,WACN,OAAQ,CAAA,KAAA;AAAA,IACN,YAAU,CAAC,MAAA,CAAO,YAAgB,IAAA,MAAA,CAAO,aAAa,MAAM;AAAA,GAC9D;AACJ;;;;"}
1
+ {"version":3,"file":"filters.esm.js","sources":["../../src/utils/filters.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { EntityFilter } from '../types';\nimport {\n EntityLifecycleFilter,\n EntityNamespaceFilter,\n EntityOrderFilter,\n EntityOrphanFilter,\n EntityOwnerFilter,\n EntityTagFilter,\n EntityTextFilter,\n EntityUserFilter,\n UserListFilter,\n} from '../filters';\nimport { EntityOrderQuery } from '@backstage/catalog-client';\n\nexport interface CatalogFilters {\n filter: Record<string, string | symbol | (string | symbol)[]>;\n fullTextFilter?: {\n term: string;\n };\n orderFields?: EntityOrderQuery;\n}\n\nfunction isEntityTextFilter(t: EntityFilter): t is EntityTextFilter {\n return !!(t as EntityTextFilter).getFullTextFilters;\n}\n\nfunction isEntityOrderFilter(t: EntityFilter): t is EntityOrderFilter {\n return !!(t as EntityOrderFilter).getOrderFilters;\n}\n\nexport function reduceCatalogFilters(filters: EntityFilter[]): CatalogFilters {\n const condensedFilters = filters.reduce<CatalogFilters['filter']>(\n (compoundFilter, filter) => {\n return {\n ...compoundFilter,\n ...(filter.getCatalogFilters ? filter.getCatalogFilters() : {}),\n };\n },\n {},\n );\n\n const fullTextFilter = filters.find(isEntityTextFilter)?.getFullTextFilters();\n\n const orderFields = filters.find(isEntityOrderFilter)?.getOrderFilters() || [\n {\n field: 'metadata.name',\n order: 'asc',\n },\n ];\n return { filter: condensedFilters, fullTextFilter, orderFields };\n}\n\n/**\n * This function computes and returns an object containing the filters to be sent\n * to the backend. Any filter coming from `EntityKindFilter` and `EntityTypeFilter`, together\n * with custom filter set by the adopters is allowed. This function is used by `EntityListProvider`\n * and it won't be needed anymore in the future once pagination is implemented, as all the filters\n * will be applied backend-side.\n */\nexport function reduceBackendCatalogFilters(filters: EntityFilter[]) {\n const backendCatalogFilters: Record<\n string,\n string | symbol | (string | symbol)[]\n > = {};\n\n filters.forEach(filter => {\n if (\n filter instanceof EntityTagFilter ||\n filter instanceof EntityOwnerFilter ||\n filter instanceof EntityLifecycleFilter ||\n filter instanceof EntityNamespaceFilter ||\n filter instanceof EntityUserFilter ||\n filter instanceof EntityOrphanFilter ||\n filter instanceof EntityTextFilter ||\n filter instanceof UserListFilter\n ) {\n return;\n }\n Object.assign(backendCatalogFilters, filter.getCatalogFilters?.() || {});\n });\n\n return backendCatalogFilters;\n}\n\nexport function reduceEntityFilters(\n filters: EntityFilter[],\n): (entity: Entity) => boolean {\n return (entity: Entity) =>\n filters.every(\n filter => !filter.filterEntity || filter.filterEntity(entity),\n );\n}\n"],"names":[],"mappings":";;AAuCA,SAAS,mBAAmB,CAAwC,EAAA;AAClE,EAAO,OAAA,CAAC,CAAE,CAAuB,CAAA,kBAAA;AACnC;AAEA,SAAS,oBAAoB,CAAyC,EAAA;AACpE,EAAO,OAAA,CAAC,CAAE,CAAwB,CAAA,eAAA;AACpC;AAEO,SAAS,qBAAqB,OAAyC,EAAA;AAC5E,EAAA,MAAM,mBAAmB,OAAQ,CAAA,MAAA;AAAA,IAC/B,CAAC,gBAAgB,MAAW,KAAA;AAC1B,MAAO,OAAA;AAAA,QACL,GAAG,cAAA;AAAA,QACH,GAAI,MAAO,CAAA,iBAAA,GAAoB,MAAO,CAAA,iBAAA,KAAsB;AAAC,OAC/D;AAAA,KACF;AAAA,IACA;AAAC,GACH;AAEA,EAAA,MAAM,cAAiB,GAAA,OAAA,CAAQ,IAAK,CAAA,kBAAkB,GAAG,kBAAmB,EAAA;AAE5E,EAAA,MAAM,cAAc,OAAQ,CAAA,IAAA,CAAK,mBAAmB,CAAA,EAAG,iBAAqB,IAAA;AAAA,IAC1E;AAAA,MACE,KAAO,EAAA,eAAA;AAAA,MACP,KAAO,EAAA;AAAA;AACT,GACF;AACA,EAAA,OAAO,EAAE,MAAA,EAAQ,gBAAkB,EAAA,cAAA,EAAgB,WAAY,EAAA;AACjE;AASO,SAAS,4BAA4B,OAAyB,EAAA;AACnE,EAAA,MAAM,wBAGF,EAAC;AAEL,EAAA,OAAA,CAAQ,QAAQ,CAAU,MAAA,KAAA;AACxB,IAAA,IACE,MAAkB,YAAA,eAAA,IAClB,MAAkB,YAAA,iBAAA,IAClB,kBAAkB,qBAClB,IAAA,MAAA,YAAkB,qBAClB,IAAA,MAAA,YAAkB,oBAClB,MAAkB,YAAA,kBAAA,IAClB,MAAkB,YAAA,gBAAA,IAClB,kBAAkB,cAClB,EAAA;AACA,MAAA;AAAA;AAEF,IAAA,MAAA,CAAO,OAAO,qBAAuB,EAAA,MAAA,CAAO,iBAAoB,IAAA,IAAK,EAAE,CAAA;AAAA,GACxE,CAAA;AAED,EAAO,OAAA,qBAAA;AACT;AAEO,SAAS,oBACd,OAC6B,EAAA;AAC7B,EAAO,OAAA,CAAC,WACN,OAAQ,CAAA,KAAA;AAAA,IACN,YAAU,CAAC,MAAA,CAAO,YAAgB,IAAA,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": "1.17.0-next.2",
3
+ "version": "1.18.0-next.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,18 +73,18 @@
73
73
  "test": "backstage-cli package test"
74
74
  },
75
75
  "dependencies": {
76
- "@backstage/catalog-client": "1.9.1",
76
+ "@backstage/catalog-client": "1.10.0-next.0",
77
77
  "@backstage/catalog-model": "1.7.3",
78
- "@backstage/core-compat-api": "0.4.1-next.2",
79
- "@backstage/core-components": "0.17.1-next.1",
80
- "@backstage/core-plugin-api": "1.10.6-next.0",
78
+ "@backstage/core-compat-api": "0.4.2-next.0",
79
+ "@backstage/core-components": "0.17.1",
80
+ "@backstage/core-plugin-api": "1.10.6",
81
81
  "@backstage/errors": "1.2.7",
82
- "@backstage/frontend-plugin-api": "0.10.1-next.1",
83
- "@backstage/frontend-test-utils": "0.3.1-next.1",
84
- "@backstage/integration-react": "1.2.6-next.1",
82
+ "@backstage/frontend-plugin-api": "0.10.1",
83
+ "@backstage/frontend-test-utils": "0.3.2-next.0",
84
+ "@backstage/integration-react": "1.2.7-next.0",
85
85
  "@backstage/plugin-catalog-common": "1.1.3",
86
86
  "@backstage/plugin-permission-common": "0.8.4",
87
- "@backstage/plugin-permission-react": "0.4.33-next.0",
87
+ "@backstage/plugin-permission-react": "0.4.33",
88
88
  "@backstage/types": "1.2.1",
89
89
  "@backstage/version-bridge": "1.0.11",
90
90
  "@material-ui/core": "^4.12.2",
@@ -100,11 +100,11 @@
100
100
  "zen-observable": "^0.10.0"
101
101
  },
102
102
  "devDependencies": {
103
- "@backstage/cli": "0.32.0-next.2",
104
- "@backstage/core-app-api": "1.16.1-next.0",
103
+ "@backstage/cli": "0.32.1-next.0",
104
+ "@backstage/core-app-api": "1.16.1",
105
105
  "@backstage/plugin-catalog-common": "1.1.3",
106
106
  "@backstage/plugin-scaffolder-common": "1.5.10",
107
- "@backstage/test-utils": "1.7.7-next.0",
107
+ "@backstage/test-utils": "1.7.7",
108
108
  "@testing-library/dom": "^10.0.0",
109
109
  "@testing-library/jest-dom": "^6.0.0",
110
110
  "@testing-library/react": "^16.0.0",