@backstage/plugin-catalog-react 0.6.11 → 0.6.13-next.1

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,65 @@
1
1
  # @backstage/plugin-catalog-react
2
2
 
3
+ ## 0.6.13-next.1
4
+
5
+ ### Patch Changes
6
+
7
+ - f7257dff6f: The `<Link />` component now accepts a `noTrack` prop, which prevents the `click` event from being captured by the Analytics API. This can be used if tracking is explicitly not warranted, or in order to use custom link tracking in specific situations.
8
+ - 300f8cdaee: Fix bug: previously the filter would be set to "all" on page load, even if the
9
+ `initiallySelectedFilter` on the `DefaultCatalogPage` was set to something else,
10
+ or a different query parameter was supplied. Now, the prop and query parameters
11
+ control the filter as expected. Additionally, after this change any filters
12
+ which match 0 items will be disabled, and the filter will be reverted to 'all'
13
+ if they're set on page load.
14
+ - 6acc8f7db7: Add caching to the useEntityPermission hook
15
+
16
+ The hook now caches the authorization decision based on the permission + the entity, and returns the cache match value as the default `allowed` value while loading. This helps avoid flicker in UI elements that would be conditionally rendered based on the `allowed` result of this hook.
17
+
18
+ - Updated dependencies
19
+ - @backstage/core-components@0.8.7-next.1
20
+
21
+ ## 0.6.13-next.0
22
+
23
+ ### Patch Changes
24
+
25
+ - Updated dependencies
26
+ - @backstage/core-components@0.8.7-next.0
27
+
28
+ ## 0.6.12
29
+
30
+ ### Patch Changes
31
+
32
+ - 3d87019269: The `entityRouteRef` is now a well-known route that should be imported directly from `@backstage/plugin-catalog-react`. It is guaranteed to be globally unique across duplicate installations of the `@backstage/plugin-catalog-react`, starting at this version.
33
+
34
+ Deprecated `entityRoute` in favor of `entityRouteRef`.
35
+
36
+ Deprecated `rootRoute` and `catalogRouteRef`. If you want to refer to the catalog index page from a public plugin you now need to use an `ExternalRouteRef` instead. For private plugins it is possible to take the shortcut of referring directly to `catalogPlugin.routes.indexPage` instead.
37
+
38
+ - 2916a83b9c: Deprecated `loadIdentityOwnerRefs`, since they can now be retrieved as `ownershipEntityRefs` from `identityApi.getBackstageIdentity()` instead.
39
+ - 51fbedc445: Migrated usage of deprecated `IdentityApi` methods.
40
+ - c54c0d9d10: Add useEntityPermission hook
41
+ - Updated dependencies
42
+ - @backstage/plugin-permission-react@0.3.0
43
+ - @backstage/core-components@0.8.5
44
+ - @backstage/integration@0.7.2
45
+ - @backstage/plugin-permission-common@0.4.0
46
+ - @backstage/core-plugin-api@0.6.0
47
+ - @backstage/catalog-model@0.9.10
48
+ - @backstage/catalog-client@0.5.5
49
+
50
+ ## 0.6.12-next.0
51
+
52
+ ### Patch Changes
53
+
54
+ - 2916a83b9c: Deprecated `loadIdentityOwnerRefs`, since they can now be retrieved as `ownershipEntityRefs` from `identityApi.getBackstageIdentity()` instead.
55
+ - 51fbedc445: Migrated usage of deprecated `IdentityApi` methods.
56
+ - Updated dependencies
57
+ - @backstage/core-components@0.8.5-next.0
58
+ - @backstage/core-plugin-api@0.6.0-next.0
59
+ - @backstage/catalog-model@0.9.10-next.0
60
+ - @backstage/catalog-client@0.5.5-next.0
61
+ - @backstage/integration@0.7.2-next.0
62
+
3
63
  ## 0.6.11
4
64
 
5
65
  ### Patch Changes
package/dist/index.cjs.js CHANGED
@@ -21,7 +21,7 @@ var isEqual = require('lodash/isEqual');
21
21
  var coreComponents = require('@backstage/core-components');
22
22
  var core = require('@material-ui/core');
23
23
  var useObservable = require('react-use/lib/useObservable');
24
- var jwtDecoder = require('jwt-decode');
24
+ var pluginPermissionReact = require('@backstage/plugin-permission-react');
25
25
  var CheckBoxIcon = require('@material-ui/icons/CheckBox');
26
26
  var CheckBoxOutlineBlankIcon = require('@material-ui/icons/CheckBoxOutlineBlank');
27
27
  var ExpandMoreIcon = require('@material-ui/icons/ExpandMore');
@@ -46,7 +46,6 @@ var useMountedState__default = /*#__PURE__*/_interopDefaultLegacy(useMountedStat
46
46
  var useAsync__default = /*#__PURE__*/_interopDefaultLegacy(useAsync);
47
47
  var isEqual__default = /*#__PURE__*/_interopDefaultLegacy(isEqual);
48
48
  var useObservable__default = /*#__PURE__*/_interopDefaultLegacy(useObservable);
49
- var jwtDecoder__default = /*#__PURE__*/_interopDefaultLegacy(jwtDecoder);
50
49
  var CheckBoxIcon__default = /*#__PURE__*/_interopDefaultLegacy(CheckBoxIcon);
51
50
  var CheckBoxOutlineBlankIcon__default = /*#__PURE__*/_interopDefaultLegacy(CheckBoxOutlineBlankIcon);
52
51
  var ExpandMoreIcon__default = /*#__PURE__*/_interopDefaultLegacy(ExpandMoreIcon);
@@ -129,11 +128,11 @@ const rootRoute = corePluginApi.createRouteRef({
129
128
  id: "catalog"
130
129
  });
131
130
  const catalogRouteRef = rootRoute;
132
- const entityRoute = corePluginApi.createRouteRef({
131
+ const entityRouteRef = versionBridge.getOrCreateGlobalSingleton("catalog:entity-route-ref", () => corePluginApi.createRouteRef({
133
132
  id: "catalog:entity",
134
133
  params: ["namespace", "kind", "name"]
135
- });
136
- const entityRouteRef = entityRoute;
134
+ }));
135
+ const entityRoute = entityRouteRef;
137
136
  function entityRouteParams(entity) {
138
137
  var _a, _b;
139
138
  return {
@@ -624,11 +623,13 @@ function useEntityKinds() {
624
623
  function useOwnUser() {
625
624
  const catalogApi = corePluginApi.useApi(catalogApiRef);
626
625
  const identityApi = corePluginApi.useApi(corePluginApi.identityApiRef);
627
- return useAsync__default["default"](() => catalogApi.getEntityByName({
628
- kind: "User",
629
- namespace: "default",
630
- name: identityApi.getUserId()
631
- }), [catalogApi, identityApi]);
626
+ return useAsync__default["default"](async () => {
627
+ const identity = await identityApi.getBackstageIdentity();
628
+ return catalogApi.getEntityByName(catalogModel.parseEntityRef(identity.userEntityRef, {
629
+ defaultKind: "User",
630
+ defaultNamespace: catalogModel.ENTITY_DEFAULT_NAMESPACE
631
+ }));
632
+ }, [catalogApi, identityApi]);
632
633
  }
633
634
 
634
635
  const BATCH_SIZE = 20;
@@ -712,34 +713,9 @@ function useStarredEntity(entityOrRef) {
712
713
  };
713
714
  }
714
715
 
715
- function extendUserId(id) {
716
- try {
717
- const ref = catalogModel.parseEntityRef(id, {
718
- defaultKind: "User",
719
- defaultNamespace: "default"
720
- });
721
- return catalogModel.stringifyEntityRef(ref);
722
- } catch {
723
- return id;
724
- }
725
- }
726
716
  async function loadIdentityOwnerRefs(identityApi) {
727
- const id = identityApi.getUserId();
728
- const token = await identityApi.getIdToken();
729
- const result = [];
730
- if (id) {
731
- result.push(extendUserId(id));
732
- }
733
- if (token) {
734
- try {
735
- const decoded = jwtDecoder__default["default"](token);
736
- if (decoded == null ? void 0 : decoded.ent) {
737
- [decoded.ent].flat().filter((x) => typeof x === "string").map((x) => x.toLocaleLowerCase("en-US")).forEach((x) => result.push(x));
738
- }
739
- } catch {
740
- }
741
- }
742
- return result;
717
+ const identity = await identityApi.getBackstageIdentity();
718
+ return identity.ownershipEntityRefs;
743
719
  }
744
720
  async function loadCatalogOwnerRefs(catalogApi, identityOwnerRefs) {
745
721
  const result = new Array();
@@ -761,9 +737,9 @@ function useEntityOwnership() {
761
737
  const identityApi = corePluginApi.useApi(corePluginApi.identityApiRef);
762
738
  const catalogApi = corePluginApi.useApi(catalogApiRef);
763
739
  const { loading, value: refs } = useAsync__default["default"](async () => {
764
- const identityRefs = await loadIdentityOwnerRefs(identityApi);
765
- const catalogRefs = await loadCatalogOwnerRefs(catalogApi, identityRefs);
766
- return /* @__PURE__ */ new Set([...identityRefs, ...catalogRefs]);
740
+ const { ownershipEntityRefs } = await identityApi.getBackstageIdentity();
741
+ const catalogRefs = await loadCatalogOwnerRefs(catalogApi, ownershipEntityRefs);
742
+ return /* @__PURE__ */ new Set([...ownershipEntityRefs, ...catalogRefs]);
767
743
  }, []);
768
744
  const isOwnedEntity = React.useMemo(() => {
769
745
  const myOwnerRefs = new Set(refs != null ? refs : []);
@@ -804,6 +780,22 @@ function useOwnedEntities(allowedKinds) {
804
780
  return React.useMemo(() => ({ loading, ownedEntities }), [loading, ownedEntities]);
805
781
  }
806
782
 
783
+ function useEntityPermission(permission) {
784
+ const { entity, loading: loadingEntity, error: entityError } = useEntity();
785
+ const {
786
+ allowed,
787
+ loading: loadingPermission,
788
+ error: permissionError
789
+ } = pluginPermissionReact.usePermission(permission, entity ? catalogModel.stringifyEntityRef(entity) : void 0);
790
+ if (loadingEntity || loadingPermission) {
791
+ return { loading: true, allowed: false };
792
+ }
793
+ if (entityError) {
794
+ return { loading: false, allowed: false, error: entityError };
795
+ }
796
+ return { loading: false, allowed, error: permissionError };
797
+ }
798
+
807
799
  const EntityKindPicker = ({
808
800
  initialFilter,
809
801
  hidden
@@ -1528,7 +1520,7 @@ const UserListPicker = ({
1528
1520
  const classes = useStyles();
1529
1521
  const configApi = corePluginApi.useApi(corePluginApi.configApiRef);
1530
1522
  const orgName = (_a = configApi.getOptionalString("organization.name")) != null ? _a : "Company";
1531
- const { filters, updateFilters, backendEntities, queryParameters } = useEntityListProvider();
1523
+ const { filters, updateFilters, backendEntities, queryParameters, loading } = useEntityListProvider();
1532
1524
  const userAndGroupFilterIds = ["starred", "all"];
1533
1525
  const filterGroups = getFilterGroups(orgName).map((filterGroup) => ({
1534
1526
  ...filterGroup,
@@ -1538,28 +1530,23 @@ const UserListPicker = ({
1538
1530
  const { isOwnedEntity } = useEntityOwnership();
1539
1531
  const ownedFilter = React.useMemo(() => new UserListFilter("owned", isOwnedEntity, isStarredEntity), [isOwnedEntity, isStarredEntity]);
1540
1532
  const starredFilter = React.useMemo(() => new UserListFilter("starred", isOwnedEntity, isStarredEntity), [isOwnedEntity, isStarredEntity]);
1541
- const [entitiesWithoutUserFilter, setEntitiesWithoutUserFilter] = React.useState(backendEntities);
1542
- const totalOwnedUserEntities = entitiesWithoutUserFilter.filter((entity) => ownedFilter.filterEntity(entity)).length;
1543
- const [selectedUserFilter, setSelectedUserFilter] = React.useState(totalOwnedUserEntities > 0 ? (_b = [queryParameters.user].flat()[0]) != null ? _b : initialFilter : "all");
1533
+ const [selectedUserFilter, setSelectedUserFilter] = React.useState((_b = [queryParameters.user].flat()[0]) != null ? _b : initialFilter);
1534
+ const entitiesWithoutUserFilter = React.useMemo(() => backendEntities.filter(reduceEntityFilters(lodash.compact(Object.values({ ...filters, user: void 0 })))), [filters, backendEntities]);
1535
+ const filterCounts = React.useMemo(() => ({
1536
+ all: entitiesWithoutUserFilter.length,
1537
+ starred: entitiesWithoutUserFilter.filter((entity) => starredFilter.filterEntity(entity)).length,
1538
+ owned: entitiesWithoutUserFilter.filter((entity) => ownedFilter.filterEntity(entity)).length
1539
+ }), [entitiesWithoutUserFilter, starredFilter, ownedFilter]);
1540
+ React.useEffect(() => {
1541
+ if (!loading && !!selectedUserFilter && selectedUserFilter !== "all" && filterCounts[selectedUserFilter] === 0) {
1542
+ setSelectedUserFilter("all");
1543
+ }
1544
+ }, [loading, filterCounts, selectedUserFilter, setSelectedUserFilter]);
1544
1545
  React.useEffect(() => {
1545
1546
  updateFilters({
1546
1547
  user: selectedUserFilter ? new UserListFilter(selectedUserFilter, isOwnedEntity, isStarredEntity) : void 0
1547
1548
  });
1548
1549
  }, [selectedUserFilter, isOwnedEntity, isStarredEntity, updateFilters]);
1549
- React.useEffect(() => {
1550
- const filterFn = reduceEntityFilters(lodash.compact(Object.values({ ...filters, user: void 0 })));
1551
- setEntitiesWithoutUserFilter(backendEntities.filter(filterFn));
1552
- }, [filters, backendEntities]);
1553
- function getFilterCount(id) {
1554
- switch (id) {
1555
- case "owned":
1556
- return totalOwnedUserEntities;
1557
- case "starred":
1558
- return entitiesWithoutUserFilter.filter((entity) => starredFilter.filterEntity(entity)).length;
1559
- default:
1560
- return entitiesWithoutUserFilter.length;
1561
- }
1562
- }
1563
1550
  return /* @__PURE__ */ React__default["default"].createElement(core.Card, {
1564
1551
  className: classes.root
1565
1552
  }, filterGroups.map((group) => /* @__PURE__ */ React__default["default"].createElement(React.Fragment, {
@@ -1580,15 +1567,16 @@ const UserListPicker = ({
1580
1567
  divider: true,
1581
1568
  onClick: () => setSelectedUserFilter(item.id),
1582
1569
  selected: item.id === ((_a2 = filters.user) == null ? void 0 : _a2.value),
1583
- className: classes.menuItem
1570
+ className: classes.menuItem,
1571
+ disabled: filterCounts[item.id] === 0,
1572
+ "data-testid": `user-picker-${item.id}`
1584
1573
  }, item.icon && /* @__PURE__ */ React__default["default"].createElement(core.ListItemIcon, {
1585
1574
  className: classes.listIcon
1586
1575
  }, /* @__PURE__ */ React__default["default"].createElement(item.icon, {
1587
1576
  fontSize: "small"
1588
1577
  })), /* @__PURE__ */ React__default["default"].createElement(core.ListItemText, null, /* @__PURE__ */ React__default["default"].createElement(core.Typography, {
1589
- variant: "body1",
1590
- "data-testid": `user-picker-${item.id}`
1591
- }, item.label)), /* @__PURE__ */ React__default["default"].createElement(core.ListItemSecondaryAction, null, (_b2 = getFilterCount(item.id)) != null ? _b2 : "-"));
1578
+ variant: "body1"
1579
+ }, item.label)), /* @__PURE__ */ React__default["default"].createElement(core.ListItemSecondaryAction, null, (_b2 = filterCounts[item.id]) != null ? _b2 : "-"));
1592
1580
  }))))));
1593
1581
  };
1594
1582
 
@@ -1673,6 +1661,7 @@ exports.useEntityFromUrl = useEntityFromUrl;
1673
1661
  exports.useEntityKinds = useEntityKinds;
1674
1662
  exports.useEntityListProvider = useEntityListProvider;
1675
1663
  exports.useEntityOwnership = useEntityOwnership;
1664
+ exports.useEntityPermission = useEntityPermission;
1676
1665
  exports.useEntityTypeFilter = useEntityTypeFilter;
1677
1666
  exports.useOwnUser = useOwnUser;
1678
1667
  exports.useOwnedEntities = useOwnedEntities;