@backstage/plugin-catalog-react 0.0.0-nightly-202201921950 → 0.0.0-nightly-20220208022044

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,17 +1,78 @@
1
1
  # @backstage/plugin-catalog-react
2
2
 
3
- ## 0.0.0-nightly-202201921950
3
+ ## 0.0.0-nightly-20220208022044
4
4
 
5
5
  ### Patch Changes
6
6
 
7
+ - 680e7c7452: Updated `useEntityListProvider` and catalog pickers to respond to external changes to query parameters in the URL, such as two sidebar links that apply different catalog filters.
8
+ - 7bb1bde7f6: Minor API cleanups
9
+ - Updated dependencies
10
+ - @backstage/core-components@0.0.0-nightly-20220208022044
11
+
12
+ ## 0.6.13
13
+
14
+ ### Patch Changes
15
+
16
+ - 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.
17
+ - 300f8cdaee: Fix bug: previously the filter would be set to "all" on page load, even if the
18
+ `initiallySelectedFilter` on the `DefaultCatalogPage` was set to something else,
19
+ or a different query parameter was supplied. Now, the prop and query parameters
20
+ control the filter as expected. Additionally, after this change any filters
21
+ which match 0 items will be disabled, and the filter will be reverted to 'all'
22
+ if they're set on page load.
23
+ - 6acc8f7db7: Add caching to the useEntityPermission hook
24
+
25
+ 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.
26
+
27
+ - Updated dependencies
28
+ - @backstage/core-components@0.8.7
29
+
30
+ ## 0.6.13-next.1
31
+
32
+ ### Patch Changes
33
+
34
+ - 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.
35
+ - 300f8cdaee: Fix bug: previously the filter would be set to "all" on page load, even if the
36
+ `initiallySelectedFilter` on the `DefaultCatalogPage` was set to something else,
37
+ or a different query parameter was supplied. Now, the prop and query parameters
38
+ control the filter as expected. Additionally, after this change any filters
39
+ which match 0 items will be disabled, and the filter will be reverted to 'all'
40
+ if they're set on page load.
41
+ - 6acc8f7db7: Add caching to the useEntityPermission hook
42
+
43
+ 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.
44
+
45
+ - Updated dependencies
46
+ - @backstage/core-components@0.8.7-next.1
47
+
48
+ ## 0.6.13-next.0
49
+
50
+ ### Patch Changes
51
+
52
+ - Updated dependencies
53
+ - @backstage/core-components@0.8.7-next.0
54
+
55
+ ## 0.6.12
56
+
57
+ ### Patch Changes
58
+
59
+ - 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.
60
+
61
+ Deprecated `entityRoute` in favor of `entityRouteRef`.
62
+
63
+ 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.
64
+
7
65
  - 2916a83b9c: Deprecated `loadIdentityOwnerRefs`, since they can now be retrieved as `ownershipEntityRefs` from `identityApi.getBackstageIdentity()` instead.
8
66
  - 51fbedc445: Migrated usage of deprecated `IdentityApi` methods.
67
+ - c54c0d9d10: Add useEntityPermission hook
9
68
  - Updated dependencies
10
- - @backstage/core-components@0.0.0-nightly-202201921950
11
- - @backstage/core-plugin-api@0.0.0-nightly-202201921950
12
- - @backstage/catalog-model@0.0.0-nightly-202201921950
13
- - @backstage/catalog-client@0.0.0-nightly-202201921950
14
- - @backstage/integration@0.0.0-nightly-202201921950
69
+ - @backstage/plugin-permission-react@0.3.0
70
+ - @backstage/core-components@0.8.5
71
+ - @backstage/integration@0.7.2
72
+ - @backstage/plugin-permission-common@0.4.0
73
+ - @backstage/core-plugin-api@0.6.0
74
+ - @backstage/catalog-model@0.9.10
75
+ - @backstage/catalog-client@0.5.5
15
76
 
16
77
  ## 0.6.12-next.0
17
78
 
package/dist/index.cjs.js CHANGED
@@ -21,6 +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 pluginPermissionReact = require('@backstage/plugin-permission-react');
24
25
  var CheckBoxIcon = require('@material-ui/icons/CheckBox');
25
26
  var CheckBoxOutlineBlankIcon = require('@material-ui/icons/CheckBoxOutlineBlank');
26
27
  var ExpandMoreIcon = require('@material-ui/icons/ExpandMore');
@@ -127,11 +128,11 @@ const rootRoute = corePluginApi.createRouteRef({
127
128
  id: "catalog"
128
129
  });
129
130
  const catalogRouteRef = rootRoute;
130
- const entityRoute = corePluginApi.createRouteRef({
131
+ const entityRouteRef = versionBridge.getOrCreateGlobalSingleton("catalog:entity-route-ref", () => corePluginApi.createRouteRef({
131
132
  id: "catalog:entity",
132
133
  params: ["namespace", "kind", "name"]
133
- });
134
- const entityRouteRef = entityRoute;
134
+ }));
135
+ const entityRoute = entityRouteRef;
135
136
  function entityRouteParams(entity) {
136
137
  var _a, _b;
137
138
  return {
@@ -293,16 +294,18 @@ const EntityListProvider = ({
293
294
  const isMounted = useMountedState__default["default"]();
294
295
  const catalogApi = corePluginApi.useApi(catalogApiRef);
295
296
  const [requestedFilters, setRequestedFilters] = React.useState({});
296
- const [outputState, setOutputState] = React.useState(() => {
297
+ const location = reactRouter.useLocation();
298
+ const queryParameters = React.useMemo(() => {
297
299
  var _a;
298
- const query = qs__default["default"].parse(window.location.search, {
300
+ return (_a = qs__default["default"].parse(location.search, {
299
301
  ignoreQueryPrefix: true
300
- });
302
+ }).filters) != null ? _a : {};
303
+ }, [location]);
304
+ const [outputState, setOutputState] = React.useState(() => {
301
305
  return {
302
306
  appliedFilters: {},
303
307
  entities: [],
304
- backendEntities: [],
305
- queryParameters: (_a = query.filters) != null ? _a : {}
308
+ backendEntities: []
306
309
  };
307
310
  });
308
311
  const [{ loading, error }, refresh] = useAsyncFn__default["default"](async () => {
@@ -325,26 +328,24 @@ const EntityListProvider = ({
325
328
  setOutputState({
326
329
  appliedFilters: requestedFilters,
327
330
  backendEntities: response.items,
328
- entities: response.items.filter(entityFilter),
329
- queryParameters: queryParams
331
+ entities: response.items.filter(entityFilter)
330
332
  });
331
333
  } else {
332
334
  setOutputState({
333
335
  appliedFilters: requestedFilters,
334
336
  backendEntities: outputState.backendEntities,
335
- entities: outputState.backendEntities.filter(entityFilter),
336
- queryParameters: queryParams
337
+ entities: outputState.backendEntities.filter(entityFilter)
337
338
  });
338
339
  }
339
340
  if (isMounted()) {
340
- const oldParams = qs__default["default"].parse(window.location.search, {
341
+ const oldParams = qs__default["default"].parse(location.search, {
341
342
  ignoreQueryPrefix: true
342
343
  });
343
344
  const newParams = qs__default["default"].stringify({ ...oldParams, filters: queryParams }, { addQueryPrefix: true });
344
345
  const newUrl = `${window.location.pathname}${newParams}`;
345
346
  (_a = window.history) == null ? void 0 : _a.replaceState(null, document.title, newUrl);
346
347
  }
347
- }, [catalogApi, requestedFilters, outputState], { loading: true });
348
+ }, [catalogApi, queryParameters, requestedFilters, outputState], { loading: true });
348
349
  useDebounce__default["default"](refresh, 10, [requestedFilters]);
349
350
  const updateFilters = React.useCallback((update) => {
350
351
  setRequestedFilters((prevFilters) => {
@@ -357,10 +358,10 @@ const EntityListProvider = ({
357
358
  entities: outputState.entities,
358
359
  backendEntities: outputState.backendEntities,
359
360
  updateFilters,
360
- queryParameters: outputState.queryParameters,
361
+ queryParameters,
361
362
  loading,
362
363
  error
363
- }), [outputState, updateFilters, loading, error]);
364
+ }), [outputState, updateFilters, queryParameters, loading, error]);
364
365
  return /* @__PURE__ */ React__default["default"].createElement(EntityListContext.Provider, {
365
366
  value
366
367
  }, children);
@@ -547,8 +548,13 @@ function useEntityTypeFilter() {
547
548
  queryParameters,
548
549
  updateFilters
549
550
  } = useEntityListProvider();
550
- const queryParamTypes = [queryParameters.type].flat().filter(Boolean);
551
+ const queryParamTypes = React.useMemo(() => [queryParameters.type].flat().filter(Boolean), [queryParameters]);
551
552
  const [selectedTypes, setSelectedTypes] = React.useState(queryParamTypes.length ? queryParamTypes : (_a = typeFilter == null ? void 0 : typeFilter.getTypes()) != null ? _a : []);
553
+ React.useEffect(() => {
554
+ if (queryParamTypes.length) {
555
+ setSelectedTypes(queryParamTypes);
556
+ }
557
+ }, [queryParamTypes]);
552
558
  const [availableTypes, setAvailableTypes] = React.useState([]);
553
559
  const kind = React.useMemo(() => kindFilter == null ? void 0 : kindFilter.value, [kindFilter]);
554
560
  const {
@@ -779,11 +785,25 @@ function useOwnedEntities(allowedKinds) {
779
785
  return React.useMemo(() => ({ loading, ownedEntities }), [loading, ownedEntities]);
780
786
  }
781
787
 
782
- const EntityKindPicker = ({
783
- initialFilter,
784
- hidden
785
- }) => {
788
+ function useEntityPermission(permission) {
789
+ const { entity, loading: loadingEntity, error: entityError } = useEntity();
790
+ const {
791
+ allowed,
792
+ loading: loadingPermission,
793
+ error: permissionError
794
+ } = pluginPermissionReact.usePermission(permission, entity ? catalogModel.stringifyEntityRef(entity) : void 0);
795
+ if (loadingEntity || loadingPermission) {
796
+ return { loading: true, allowed: false };
797
+ }
798
+ if (entityError) {
799
+ return { loading: false, allowed: false, error: entityError };
800
+ }
801
+ return { loading: false, allowed, error: permissionError };
802
+ }
803
+
804
+ const EntityKindPicker = (props) => {
786
805
  var _a;
806
+ const { initialFilter, hidden } = props;
787
807
  const { updateFilters, queryParameters } = useEntityListProvider();
788
808
  const [selectedKind] = React.useState((_a = [queryParameters.kind].flat()[0]) != null ? _a : initialFilter);
789
809
  React.useEffect(() => {
@@ -813,8 +833,13 @@ const EntityLifecyclePicker = () => {
813
833
  var _a, _b;
814
834
  const classes = useStyles$6();
815
835
  const { updateFilters, backendEntities, filters, queryParameters } = useEntityListProvider();
816
- const queryParamLifecycles = [queryParameters.lifecycles].flat().filter(Boolean);
836
+ const queryParamLifecycles = React.useMemo(() => [queryParameters.lifecycles].flat().filter(Boolean), [queryParameters]);
817
837
  const [selectedLifecycles, setSelectedLifecycles] = React.useState(queryParamLifecycles.length ? queryParamLifecycles : (_b = (_a = filters.lifecycles) == null ? void 0 : _a.values) != null ? _b : []);
838
+ React.useEffect(() => {
839
+ if (queryParamLifecycles.length) {
840
+ setSelectedLifecycles(queryParamLifecycles);
841
+ }
842
+ }, [queryParamLifecycles]);
818
843
  React.useEffect(() => {
819
844
  updateFilters({
820
845
  lifecycles: selectedLifecycles.length ? new EntityLifecycleFilter(selectedLifecycles) : void 0
@@ -874,8 +899,13 @@ const EntityOwnerPicker = () => {
874
899
  var _a, _b;
875
900
  const classes = useStyles$5();
876
901
  const { updateFilters, backendEntities, filters, queryParameters } = useEntityListProvider();
877
- const queryParamOwners = [queryParameters.owners].flat().filter(Boolean);
902
+ const queryParamOwners = React.useMemo(() => [queryParameters.owners].flat().filter(Boolean), [queryParameters]);
878
903
  const [selectedOwners, setSelectedOwners] = React.useState(queryParamOwners.length ? queryParamOwners : (_b = (_a = filters.owners) == null ? void 0 : _a.values) != null ? _b : []);
904
+ React.useEffect(() => {
905
+ if (queryParamOwners.length) {
906
+ setSelectedOwners(queryParamOwners);
907
+ }
908
+ }, [queryParamOwners]);
879
909
  React.useEffect(() => {
880
910
  updateFilters({
881
911
  owners: selectedOwners.length ? new EntityOwnerFilter(selectedOwners) : void 0
@@ -959,9 +989,8 @@ const EntitySearchBar = () => {
959
989
  })));
960
990
  };
961
991
 
962
- function createEntityRefColumn({
963
- defaultKind
964
- }) {
992
+ function createEntityRefColumn(options) {
993
+ const { defaultKind } = options;
965
994
  function formatContent(entity) {
966
995
  var _a;
967
996
  return ((_a = entity.metadata) == null ? void 0 : _a.title) || formatEntityRefTitle(entity, {
@@ -1101,13 +1130,14 @@ const useStyles$3 = core.makeStyles((theme) => ({
1101
1130
  justifyContent: "center"
1102
1131
  }
1103
1132
  }));
1104
- function EntityTable({
1105
- entities,
1106
- title,
1107
- emptyContent,
1108
- variant = "gridItem",
1109
- columns
1110
- }) {
1133
+ function EntityTable(props) {
1134
+ const {
1135
+ entities,
1136
+ title,
1137
+ emptyContent,
1138
+ variant = "gridItem",
1139
+ columns
1140
+ } = props;
1111
1141
  const classes = useStyles$3();
1112
1142
  const tableStyle = {
1113
1143
  minWidth: "0",
@@ -1151,8 +1181,13 @@ const EntityTagPicker = () => {
1151
1181
  var _a, _b;
1152
1182
  const classes = useStyles$2();
1153
1183
  const { updateFilters, backendEntities, filters, queryParameters } = useEntityListProvider();
1154
- const queryParamTags = [queryParameters.tags].flat().filter(Boolean);
1184
+ const queryParamTags = React.useMemo(() => [queryParameters.tags].flat().filter(Boolean), [queryParameters]);
1155
1185
  const [selectedTags, setSelectedTags] = React.useState(queryParamTags.length ? queryParamTags : (_b = (_a = filters.tags) == null ? void 0 : _a.values) != null ? _b : []);
1186
+ React.useEffect(() => {
1187
+ if (queryParamTags.length) {
1188
+ setSelectedTags(queryParamTags);
1189
+ }
1190
+ }, [queryParamTags]);
1156
1191
  React.useEffect(() => {
1157
1192
  updateFilters({
1158
1193
  tags: selectedTags.length ? new EntityTagFilter(selectedTags) : void 0
@@ -1499,11 +1534,11 @@ const UserListPicker = ({
1499
1534
  initialFilter,
1500
1535
  availableFilters
1501
1536
  }) => {
1502
- var _a, _b;
1537
+ var _a;
1503
1538
  const classes = useStyles();
1504
1539
  const configApi = corePluginApi.useApi(corePluginApi.configApiRef);
1505
1540
  const orgName = (_a = configApi.getOptionalString("organization.name")) != null ? _a : "Company";
1506
- const { filters, updateFilters, backendEntities, queryParameters } = useEntityListProvider();
1541
+ const { filters, updateFilters, backendEntities, queryParameters, loading } = useEntityListProvider();
1507
1542
  const userAndGroupFilterIds = ["starred", "all"];
1508
1543
  const filterGroups = getFilterGroups(orgName).map((filterGroup) => ({
1509
1544
  ...filterGroup,
@@ -1513,28 +1548,29 @@ const UserListPicker = ({
1513
1548
  const { isOwnedEntity } = useEntityOwnership();
1514
1549
  const ownedFilter = React.useMemo(() => new UserListFilter("owned", isOwnedEntity, isStarredEntity), [isOwnedEntity, isStarredEntity]);
1515
1550
  const starredFilter = React.useMemo(() => new UserListFilter("starred", isOwnedEntity, isStarredEntity), [isOwnedEntity, isStarredEntity]);
1516
- const [entitiesWithoutUserFilter, setEntitiesWithoutUserFilter] = React.useState(backendEntities);
1517
- const totalOwnedUserEntities = entitiesWithoutUserFilter.filter((entity) => ownedFilter.filterEntity(entity)).length;
1518
- const [selectedUserFilter, setSelectedUserFilter] = React.useState(totalOwnedUserEntities > 0 ? (_b = [queryParameters.user].flat()[0]) != null ? _b : initialFilter : "all");
1551
+ const queryParamUserFilter = React.useMemo(() => [queryParameters.user].flat()[0], [queryParameters]);
1552
+ const [selectedUserFilter, setSelectedUserFilter] = React.useState(queryParamUserFilter != null ? queryParamUserFilter : initialFilter);
1553
+ const entitiesWithoutUserFilter = React.useMemo(() => backendEntities.filter(reduceEntityFilters(lodash.compact(Object.values({ ...filters, user: void 0 })))), [filters, backendEntities]);
1554
+ const filterCounts = React.useMemo(() => ({
1555
+ all: entitiesWithoutUserFilter.length,
1556
+ starred: entitiesWithoutUserFilter.filter((entity) => starredFilter.filterEntity(entity)).length,
1557
+ owned: entitiesWithoutUserFilter.filter((entity) => ownedFilter.filterEntity(entity)).length
1558
+ }), [entitiesWithoutUserFilter, starredFilter, ownedFilter]);
1559
+ React.useEffect(() => {
1560
+ if (queryParamUserFilter) {
1561
+ setSelectedUserFilter(queryParamUserFilter);
1562
+ }
1563
+ }, [queryParamUserFilter]);
1564
+ React.useEffect(() => {
1565
+ if (!loading && !!selectedUserFilter && selectedUserFilter !== "all" && filterCounts[selectedUserFilter] === 0) {
1566
+ setSelectedUserFilter("all");
1567
+ }
1568
+ }, [loading, filterCounts, selectedUserFilter, setSelectedUserFilter]);
1519
1569
  React.useEffect(() => {
1520
1570
  updateFilters({
1521
1571
  user: selectedUserFilter ? new UserListFilter(selectedUserFilter, isOwnedEntity, isStarredEntity) : void 0
1522
1572
  });
1523
1573
  }, [selectedUserFilter, isOwnedEntity, isStarredEntity, updateFilters]);
1524
- React.useEffect(() => {
1525
- const filterFn = reduceEntityFilters(lodash.compact(Object.values({ ...filters, user: void 0 })));
1526
- setEntitiesWithoutUserFilter(backendEntities.filter(filterFn));
1527
- }, [filters, backendEntities]);
1528
- function getFilterCount(id) {
1529
- switch (id) {
1530
- case "owned":
1531
- return totalOwnedUserEntities;
1532
- case "starred":
1533
- return entitiesWithoutUserFilter.filter((entity) => starredFilter.filterEntity(entity)).length;
1534
- default:
1535
- return entitiesWithoutUserFilter.length;
1536
- }
1537
- }
1538
1574
  return /* @__PURE__ */ React__default["default"].createElement(core.Card, {
1539
1575
  className: classes.root
1540
1576
  }, filterGroups.map((group) => /* @__PURE__ */ React__default["default"].createElement(React.Fragment, {
@@ -1548,22 +1584,23 @@ const UserListPicker = ({
1548
1584
  disablePadding: true,
1549
1585
  dense: true
1550
1586
  }, group.items.map((item) => {
1551
- var _a2, _b2;
1587
+ var _a2, _b;
1552
1588
  return /* @__PURE__ */ React__default["default"].createElement(core.MenuItem, {
1553
1589
  key: item.id,
1554
1590
  button: true,
1555
1591
  divider: true,
1556
1592
  onClick: () => setSelectedUserFilter(item.id),
1557
1593
  selected: item.id === ((_a2 = filters.user) == null ? void 0 : _a2.value),
1558
- className: classes.menuItem
1594
+ className: classes.menuItem,
1595
+ disabled: filterCounts[item.id] === 0,
1596
+ "data-testid": `user-picker-${item.id}`
1559
1597
  }, item.icon && /* @__PURE__ */ React__default["default"].createElement(core.ListItemIcon, {
1560
1598
  className: classes.listIcon
1561
1599
  }, /* @__PURE__ */ React__default["default"].createElement(item.icon, {
1562
1600
  fontSize: "small"
1563
1601
  })), /* @__PURE__ */ React__default["default"].createElement(core.ListItemText, null, /* @__PURE__ */ React__default["default"].createElement(core.Typography, {
1564
- variant: "body1",
1565
- "data-testid": `user-picker-${item.id}`
1566
- }, item.label)), /* @__PURE__ */ React__default["default"].createElement(core.ListItemSecondaryAction, null, (_b2 = getFilterCount(item.id)) != null ? _b2 : "-"));
1602
+ variant: "body1"
1603
+ }, item.label)), /* @__PURE__ */ React__default["default"].createElement(core.ListItemSecondaryAction, null, (_b = filterCounts[item.id]) != null ? _b : "-"));
1567
1604
  }))))));
1568
1605
  };
1569
1606
 
@@ -1579,17 +1616,25 @@ const MockEntityListContextProvider = ({
1579
1616
  return { ...prevFilters, ...newFilters };
1580
1617
  });
1581
1618
  }, []);
1582
- const defaultContext = {
1619
+ const defaultValues = React.useMemo(() => ({
1583
1620
  entities: [],
1584
1621
  backendEntities: [],
1585
- updateFilters,
1586
- filters,
1587
- loading: false,
1588
1622
  queryParameters: {}
1589
- };
1590
- const { filters: _, ...otherContextFields } = value != null ? value : {};
1623
+ }), []);
1624
+ const resolvedValue = React.useMemo(() => {
1625
+ var _a2, _b, _c, _d, _e;
1626
+ return {
1627
+ entities: (_a2 = value == null ? void 0 : value.entities) != null ? _a2 : defaultValues.entities,
1628
+ backendEntities: (_b = value == null ? void 0 : value.backendEntities) != null ? _b : defaultValues.backendEntities,
1629
+ updateFilters: (_c = value == null ? void 0 : value.updateFilters) != null ? _c : updateFilters,
1630
+ filters,
1631
+ loading: (_d = value == null ? void 0 : value.loading) != null ? _d : false,
1632
+ queryParameters: (_e = value == null ? void 0 : value.queryParameters) != null ? _e : defaultValues.queryParameters,
1633
+ error: value == null ? void 0 : value.error
1634
+ };
1635
+ }, [value, defaultValues, filters, updateFilters]);
1591
1636
  return /* @__PURE__ */ React__default["default"].createElement(EntityListContext.Provider, {
1592
- value: { ...defaultContext, ...otherContextFields }
1637
+ value: resolvedValue
1593
1638
  }, children);
1594
1639
  };
1595
1640
 
@@ -1648,6 +1693,7 @@ exports.useEntityFromUrl = useEntityFromUrl;
1648
1693
  exports.useEntityKinds = useEntityKinds;
1649
1694
  exports.useEntityListProvider = useEntityListProvider;
1650
1695
  exports.useEntityOwnership = useEntityOwnership;
1696
+ exports.useEntityPermission = useEntityPermission;
1651
1697
  exports.useEntityTypeFilter = useEntityTypeFilter;
1652
1698
  exports.useOwnUser = useOwnUser;
1653
1699
  exports.useOwnedEntities = useOwnedEntities;