@backstage/plugin-catalog-react 1.4.1-next.2 → 1.5.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 +78 -0
- package/alpha/package.json +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.esm.js +262 -186
- package/dist/index.esm.js.map +1 -1
- package/package.json +16 -16
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,83 @@
|
|
|
1
1
|
# @backstage/plugin-catalog-react
|
|
2
2
|
|
|
3
|
+
## 1.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- a49fb39df5a: Attempt to load entity owner names in the EntityOwnerPicker through the `by-refs` endpoint. If `spec.profile.displayName` or `metadata.title` are populated, we now attempt to show those before showing the `humanizeEntityRef`'d version.
|
|
8
|
+
|
|
9
|
+
**BREAKING**: `EntityOwnerFilter` now uses the full entity ref instead of the `humanizeEntityRef`. If you rely on `EntityOwnerFilter.values` or the `queryParameters.owners` of `useEntityList`, you will need to adjust your code like the following.
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
const { queryParameters: { owners } } = useEntityList();
|
|
13
|
+
// or
|
|
14
|
+
const { filter: { owners } } = useEntityList();
|
|
15
|
+
|
|
16
|
+
// Instead of,
|
|
17
|
+
if (owners.some(ref => ref === humanizeEntityRef(myEntity))) ...
|
|
18
|
+
|
|
19
|
+
// You'll need to use,
|
|
20
|
+
if (owners.some(ref => ref === stringifyEntityRef(myEntity))) ...
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- 81bee24c5de: Fixed bug in catalog filters where you could not click on the text to select a value.
|
|
26
|
+
- 8e00acb28db: Small tweaks to remove warnings in the console during development (mainly focusing on techdocs)
|
|
27
|
+
- d1f5324dff7: Reverted the check if the selected options list is different than the query parameters list before invoking `setSelectedOptions` method. This was preventing updating list items when a query string was already present in the URL when loading the page.
|
|
28
|
+
- 2898b6c8d52: Minor type tweaks for TypeScript 5.0
|
|
29
|
+
- e0c6e8b9c3c: Update peer dependencies
|
|
30
|
+
- Updated dependencies
|
|
31
|
+
- @backstage/core-components@0.13.0
|
|
32
|
+
- @backstage/catalog-client@1.4.1
|
|
33
|
+
- @backstage/plugin-permission-common@0.7.5
|
|
34
|
+
- @backstage/theme@0.2.19
|
|
35
|
+
- @backstage/core-plugin-api@1.5.1
|
|
36
|
+
- @backstage/catalog-model@1.3.0
|
|
37
|
+
- @backstage/plugin-permission-react@0.4.12
|
|
38
|
+
- @backstage/version-bridge@1.0.4
|
|
39
|
+
- @backstage/integration@1.4.4
|
|
40
|
+
- @backstage/errors@1.1.5
|
|
41
|
+
- @backstage/types@1.0.2
|
|
42
|
+
- @backstage/plugin-catalog-common@1.0.13
|
|
43
|
+
|
|
44
|
+
## 1.5.0-next.3
|
|
45
|
+
|
|
46
|
+
### Minor Changes
|
|
47
|
+
|
|
48
|
+
- a49fb39df5a: Attempt to load entity owner names in the EntityOwnerPicker through the `by-refs` endpoint. If `spec.profile.displayName` or `metadata.title` are populated, we now attempt to show those before showing the `humanizeEntityRef`'d version.
|
|
49
|
+
|
|
50
|
+
**BREAKING**: `EntityOwnerFilter` now uses the full entity ref instead of the `humanizeEntityRef`. If you rely on `EntityOwnerFilter.values` or the `queryParameters.owners` of `useEntityList`, you will need to adjust your code like the following.
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
const { queryParameters: { owners } } = useEntityList();
|
|
54
|
+
// or
|
|
55
|
+
const { filter: { owners } } = useEntityList();
|
|
56
|
+
|
|
57
|
+
// Instead of,
|
|
58
|
+
if (owners.some(ref => ref === humanizeEntityRef(myEntity))) ...
|
|
59
|
+
|
|
60
|
+
// You'll need to use,
|
|
61
|
+
if (owners.some(ref => ref === stringifyEntityRef(myEntity))) ...
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Patch Changes
|
|
65
|
+
|
|
66
|
+
- d1f5324dff7: Reverted the check if the selected options list is different than the query parameters list before invoking `setSelectedOptions` method. This was preventing updating list items when a query string was already present in the URL when loading the page.
|
|
67
|
+
- Updated dependencies
|
|
68
|
+
- @backstage/catalog-model@1.3.0-next.0
|
|
69
|
+
- @backstage/core-components@0.13.0-next.3
|
|
70
|
+
- @backstage/catalog-client@1.4.1-next.1
|
|
71
|
+
- @backstage/core-plugin-api@1.5.1-next.1
|
|
72
|
+
- @backstage/errors@1.1.5
|
|
73
|
+
- @backstage/integration@1.4.4-next.0
|
|
74
|
+
- @backstage/theme@0.2.19-next.0
|
|
75
|
+
- @backstage/types@1.0.2
|
|
76
|
+
- @backstage/version-bridge@1.0.4-next.0
|
|
77
|
+
- @backstage/plugin-catalog-common@1.0.13-next.1
|
|
78
|
+
- @backstage/plugin-permission-common@0.7.5-next.0
|
|
79
|
+
- @backstage/plugin-permission-react@0.4.12-next.1
|
|
80
|
+
|
|
3
81
|
## 1.4.1-next.2
|
|
4
82
|
|
|
5
83
|
### Patch Changes
|
package/alpha/package.json
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -430,11 +430,17 @@ declare class EntityTextFilter implements EntityFilter {
|
|
|
430
430
|
/**
|
|
431
431
|
* Filter matching entities that are owned by group.
|
|
432
432
|
* @public
|
|
433
|
+
*
|
|
434
|
+
* CAUTION: This class may contain both full and partial entity refs.
|
|
433
435
|
*/
|
|
434
436
|
declare class EntityOwnerFilter implements EntityFilter {
|
|
435
437
|
readonly values: string[];
|
|
436
438
|
constructor(values: string[]);
|
|
437
439
|
filterEntity(entity: Entity): boolean;
|
|
440
|
+
/**
|
|
441
|
+
* Get the URL query parameter value. May be a mix of full and humanized entity refs.
|
|
442
|
+
* @returns list of entity refs.
|
|
443
|
+
*/
|
|
438
444
|
toQueryValue(): string[];
|
|
439
445
|
}
|
|
440
446
|
/**
|
package/dist/index.esm.js
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
export { CATALOG_FILTER_EXISTS } from '@backstage/catalog-client';
|
|
2
|
-
import { createApiRef,
|
|
2
|
+
import { createApiRef, useApi, identityApiRef, alertApiRef, errorApiRef, createRouteRef, useRouteRef, useApiHolder, useApp, configApiRef } from '@backstage/core-plugin-api';
|
|
3
3
|
import ObservableImpl from 'zen-observable';
|
|
4
|
-
import React, { useState,
|
|
5
|
-
import { Grid, useMediaQuery, useTheme, Button, Drawer, Box, Typography,
|
|
4
|
+
import React, { useState, createContext, useMemo, useCallback, useContext, useEffect, useRef, forwardRef, memo, useLayoutEffect, Fragment } from 'react';
|
|
5
|
+
import { Grid, useMediaQuery, useTheme, Button, Drawer, Box, Typography, makeStyles, FormControlLabel, Checkbox, TextField, Tooltip, IconButton, Card, CardContent, Chip, CardActions, Toolbar, FormControl, Input, InputAdornment, withStyles, DialogContentText, ListItemText as ListItemText$1, ListSubheader as ListSubheader$1, ListItem, ListItemIcon, List, Dialog, DialogTitle, DialogContent, Tabs, Tab, DialogActions, Divider, MenuItem, ListItemSecondaryAction } from '@material-ui/core';
|
|
6
6
|
import FilterListIcon from '@material-ui/icons/FilterList';
|
|
7
|
-
import { Link, Progress, ErrorPanel,
|
|
8
|
-
import {
|
|
9
|
-
import { getOrCreateGlobalSingleton } from '@backstage/version-bridge';
|
|
10
|
-
import useAsync from 'react-use/lib/useAsync';
|
|
7
|
+
import { Select, Link, Progress, ErrorPanel, ResponseErrorPanel, OverflowTooltip, Table, DependencyGraph, DependencyGraphTypes, CodeSnippet } from '@backstage/core-components';
|
|
8
|
+
import { ANNOTATION_SOURCE_LOCATION, parseLocationRef, stringifyEntityRef, parseEntityRef, RELATION_OWNED_BY, DEFAULT_NAMESPACE, getCompoundEntityRef, isUserEntity, isGroupEntity, RELATION_PART_OF, ANNOTATION_LOCATION, ANNOTATION_ORIGIN_LOCATION } from '@backstage/catalog-model';
|
|
11
9
|
import { g as getEntityRelations } from './esm/useEntity-de64059a.esm.js';
|
|
12
10
|
export { A as AsyncEntityProvider, E as EntityProvider, g as getEntityRelations, a as useAsyncEntity, u as useEntity } from './esm/useEntity-de64059a.esm.js';
|
|
13
|
-
import
|
|
11
|
+
import { compact, isEqual, groupBy, chunk, debounce } from 'lodash';
|
|
14
12
|
import qs from 'qs';
|
|
15
13
|
import { useLocation, useNavigate } from 'react-router-dom';
|
|
16
14
|
import useAsyncFn from 'react-use/lib/useAsyncFn';
|
|
17
15
|
import useDebounce from 'react-use/lib/useDebounce';
|
|
18
16
|
import useMountedState from 'react-use/lib/useMountedState';
|
|
17
|
+
import useAsync from 'react-use/lib/useAsync';
|
|
19
18
|
import isEqual$1 from 'lodash/isEqual';
|
|
20
19
|
import sortBy from 'lodash/sortBy';
|
|
21
20
|
import useObservable from 'react-use/lib/useObservable';
|
|
@@ -23,6 +22,8 @@ import CheckBoxIcon from '@material-ui/icons/CheckBox';
|
|
|
23
22
|
import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
|
|
24
23
|
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
|
25
24
|
import { Autocomplete, Alert } from '@material-ui/lab';
|
|
25
|
+
import get from 'lodash/get';
|
|
26
|
+
import { getOrCreateGlobalSingleton } from '@backstage/version-bridge';
|
|
26
27
|
import HoverPopover from 'material-ui-popup-state/HoverPopover';
|
|
27
28
|
import { usePopupState, bindHover, bindPopover } from 'material-ui-popup-state/hooks';
|
|
28
29
|
import InfoIcon from '@material-ui/icons/Info';
|
|
@@ -118,158 +119,6 @@ const CatalogFilterLayout = (props) => {
|
|
|
118
119
|
CatalogFilterLayout.Filters = Filters;
|
|
119
120
|
CatalogFilterLayout.Content = Content;
|
|
120
121
|
|
|
121
|
-
const entityRouteRef = getOrCreateGlobalSingleton(
|
|
122
|
-
"catalog:entity-route-ref",
|
|
123
|
-
() => createRouteRef({
|
|
124
|
-
id: "catalog:entity",
|
|
125
|
-
params: ["namespace", "kind", "name"]
|
|
126
|
-
})
|
|
127
|
-
);
|
|
128
|
-
function entityRouteParams(entity) {
|
|
129
|
-
var _a, _b;
|
|
130
|
-
return {
|
|
131
|
-
kind: entity.kind.toLocaleLowerCase("en-US"),
|
|
132
|
-
namespace: (_b = (_a = entity.metadata.namespace) == null ? void 0 : _a.toLocaleLowerCase("en-US")) != null ? _b : DEFAULT_NAMESPACE,
|
|
133
|
-
name: entity.metadata.name
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function humanizeEntityRef(entityRef, opts) {
|
|
138
|
-
const defaultKind = opts == null ? void 0 : opts.defaultKind;
|
|
139
|
-
let kind;
|
|
140
|
-
let namespace;
|
|
141
|
-
let name;
|
|
142
|
-
if ("metadata" in entityRef) {
|
|
143
|
-
kind = entityRef.kind;
|
|
144
|
-
namespace = entityRef.metadata.namespace;
|
|
145
|
-
name = entityRef.metadata.name;
|
|
146
|
-
} else {
|
|
147
|
-
kind = entityRef.kind;
|
|
148
|
-
namespace = entityRef.namespace;
|
|
149
|
-
name = entityRef.name;
|
|
150
|
-
}
|
|
151
|
-
if (namespace === void 0 || namespace === "") {
|
|
152
|
-
namespace = DEFAULT_NAMESPACE;
|
|
153
|
-
}
|
|
154
|
-
if ((opts == null ? void 0 : opts.defaultNamespace) !== void 0) {
|
|
155
|
-
if ((opts == null ? void 0 : opts.defaultNamespace) === namespace) {
|
|
156
|
-
namespace = void 0;
|
|
157
|
-
}
|
|
158
|
-
} else if (namespace === DEFAULT_NAMESPACE) {
|
|
159
|
-
namespace = void 0;
|
|
160
|
-
}
|
|
161
|
-
kind = kind.toLocaleLowerCase("en-US");
|
|
162
|
-
kind = defaultKind && defaultKind.toLocaleLowerCase("en-US") === kind ? void 0 : kind;
|
|
163
|
-
return `${kind ? `${kind}:` : ""}${namespace ? `${namespace}/` : ""}${name}`;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const EntityRefLink = forwardRef(
|
|
167
|
-
(props, ref) => {
|
|
168
|
-
var _a;
|
|
169
|
-
const { entityRef, defaultKind, title, children, ...linkProps } = props;
|
|
170
|
-
const entityRoute = useRouteRef(entityRouteRef);
|
|
171
|
-
let kind;
|
|
172
|
-
let namespace;
|
|
173
|
-
let name;
|
|
174
|
-
if (typeof entityRef === "string") {
|
|
175
|
-
const parsed = parseEntityRef(entityRef);
|
|
176
|
-
kind = parsed.kind;
|
|
177
|
-
namespace = parsed.namespace;
|
|
178
|
-
name = parsed.name;
|
|
179
|
-
} else if ("metadata" in entityRef) {
|
|
180
|
-
kind = entityRef.kind;
|
|
181
|
-
namespace = entityRef.metadata.namespace;
|
|
182
|
-
name = entityRef.metadata.name;
|
|
183
|
-
} else {
|
|
184
|
-
kind = entityRef.kind;
|
|
185
|
-
namespace = entityRef.namespace;
|
|
186
|
-
name = entityRef.name;
|
|
187
|
-
}
|
|
188
|
-
kind = kind.toLocaleLowerCase("en-US");
|
|
189
|
-
namespace = (_a = namespace == null ? void 0 : namespace.toLocaleLowerCase("en-US")) != null ? _a : DEFAULT_NAMESPACE;
|
|
190
|
-
const routeParams = { kind, namespace, name };
|
|
191
|
-
const formattedEntityRefTitle = humanizeEntityRef(
|
|
192
|
-
{ kind, namespace, name },
|
|
193
|
-
{ defaultKind }
|
|
194
|
-
);
|
|
195
|
-
const link = /* @__PURE__ */ React.createElement(Link, { ...linkProps, ref, to: entityRoute(routeParams) }, children, !children && (title != null ? title : formattedEntityRefTitle));
|
|
196
|
-
return title ? /* @__PURE__ */ React.createElement(Tooltip, { title: formattedEntityRefTitle }, link) : link;
|
|
197
|
-
}
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
function FetchedEntityRefLinks(props) {
|
|
201
|
-
const { entityRefs, defaultKind, getTitle, ...linkProps } = props;
|
|
202
|
-
const catalogApi = useApi(catalogApiRef);
|
|
203
|
-
const {
|
|
204
|
-
value: entities = new Array(),
|
|
205
|
-
loading,
|
|
206
|
-
error
|
|
207
|
-
} = useAsync(async () => {
|
|
208
|
-
const refs = entityRefs.reduce((acc, current) => {
|
|
209
|
-
if (typeof current === "object" && "metadata" in current) {
|
|
210
|
-
return acc;
|
|
211
|
-
}
|
|
212
|
-
return [...acc, parseEntityRef(current)];
|
|
213
|
-
}, new Array());
|
|
214
|
-
const pureEntities = entityRefs.filter(
|
|
215
|
-
(ref) => typeof ref === "object" && "metadata" in ref
|
|
216
|
-
);
|
|
217
|
-
return refs.length > 0 ? [
|
|
218
|
-
...(await catalogApi.getEntities({
|
|
219
|
-
filter: refs.map((ref) => ({
|
|
220
|
-
kind: ref.kind,
|
|
221
|
-
"metadata.namespace": ref.namespace,
|
|
222
|
-
"metadata.name": ref.name
|
|
223
|
-
}))
|
|
224
|
-
})).items,
|
|
225
|
-
...pureEntities
|
|
226
|
-
] : pureEntities;
|
|
227
|
-
}, [entityRefs]);
|
|
228
|
-
if (loading) {
|
|
229
|
-
return /* @__PURE__ */ React.createElement(Progress, null);
|
|
230
|
-
}
|
|
231
|
-
if (error) {
|
|
232
|
-
return /* @__PURE__ */ React.createElement(ErrorPanel, { error });
|
|
233
|
-
}
|
|
234
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, entities.map((r, i) => {
|
|
235
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, { key: i }, i > 0 && ", ", /* @__PURE__ */ React.createElement(
|
|
236
|
-
EntityRefLink,
|
|
237
|
-
{
|
|
238
|
-
...linkProps,
|
|
239
|
-
defaultKind,
|
|
240
|
-
entityRef: r,
|
|
241
|
-
title: getTitle(r)
|
|
242
|
-
}
|
|
243
|
-
));
|
|
244
|
-
}));
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
function EntityRefLinks(props) {
|
|
248
|
-
const { entityRefs, defaultKind, fetchEntities, getTitle, ...linkProps } = props;
|
|
249
|
-
if (fetchEntities) {
|
|
250
|
-
return /* @__PURE__ */ React.createElement(
|
|
251
|
-
FetchedEntityRefLinks,
|
|
252
|
-
{
|
|
253
|
-
...linkProps,
|
|
254
|
-
defaultKind,
|
|
255
|
-
entityRefs,
|
|
256
|
-
getTitle
|
|
257
|
-
}
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, entityRefs.map((r, i) => {
|
|
261
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, { key: i }, i > 0 && ", ", /* @__PURE__ */ React.createElement(
|
|
262
|
-
EntityRefLink,
|
|
263
|
-
{
|
|
264
|
-
...linkProps,
|
|
265
|
-
defaultKind,
|
|
266
|
-
entityRef: r,
|
|
267
|
-
title: getTitle ? getTitle(r) : void 0
|
|
268
|
-
}
|
|
269
|
-
));
|
|
270
|
-
}));
|
|
271
|
-
}
|
|
272
|
-
|
|
273
122
|
function reduceCatalogFilters(filters) {
|
|
274
123
|
return filters.reduce((compoundFilter, filter) => {
|
|
275
124
|
return {
|
|
@@ -366,15 +215,28 @@ class EntityTextFilter {
|
|
|
366
215
|
}
|
|
367
216
|
class EntityOwnerFilter {
|
|
368
217
|
constructor(values) {
|
|
369
|
-
this.values = values
|
|
218
|
+
this.values = values.reduce((fullRefs, ref) => {
|
|
219
|
+
try {
|
|
220
|
+
fullRefs.push(
|
|
221
|
+
stringifyEntityRef(parseEntityRef(ref, { defaultKind: "Group" }))
|
|
222
|
+
);
|
|
223
|
+
return fullRefs;
|
|
224
|
+
} catch (err) {
|
|
225
|
+
return fullRefs;
|
|
226
|
+
}
|
|
227
|
+
}, []);
|
|
370
228
|
}
|
|
371
229
|
filterEntity(entity) {
|
|
372
230
|
return this.values.some(
|
|
373
231
|
(v) => getEntityRelations(entity, RELATION_OWNED_BY).some(
|
|
374
|
-
(o) =>
|
|
232
|
+
(o) => stringifyEntityRef(o) === v
|
|
375
233
|
)
|
|
376
234
|
);
|
|
377
235
|
}
|
|
236
|
+
/**
|
|
237
|
+
* Get the URL query parameter value. May be a mix of full and humanized entity refs.
|
|
238
|
+
* @returns list of entity refs.
|
|
239
|
+
*/
|
|
378
240
|
toQueryValue() {
|
|
379
241
|
return this.values;
|
|
380
242
|
}
|
|
@@ -937,6 +799,44 @@ const EntityLifecyclePicker = (props) => {
|
|
|
937
799
|
)));
|
|
938
800
|
};
|
|
939
801
|
|
|
802
|
+
function humanizeEntityRef(entityRef, opts) {
|
|
803
|
+
const defaultKind = opts == null ? void 0 : opts.defaultKind;
|
|
804
|
+
let kind;
|
|
805
|
+
let namespace;
|
|
806
|
+
let name;
|
|
807
|
+
if ("metadata" in entityRef) {
|
|
808
|
+
kind = entityRef.kind;
|
|
809
|
+
namespace = entityRef.metadata.namespace;
|
|
810
|
+
name = entityRef.metadata.name;
|
|
811
|
+
} else {
|
|
812
|
+
kind = entityRef.kind;
|
|
813
|
+
namespace = entityRef.namespace;
|
|
814
|
+
name = entityRef.name;
|
|
815
|
+
}
|
|
816
|
+
if (namespace === void 0 || namespace === "") {
|
|
817
|
+
namespace = DEFAULT_NAMESPACE;
|
|
818
|
+
}
|
|
819
|
+
if ((opts == null ? void 0 : opts.defaultNamespace) !== void 0) {
|
|
820
|
+
if ((opts == null ? void 0 : opts.defaultNamespace) === namespace) {
|
|
821
|
+
namespace = void 0;
|
|
822
|
+
}
|
|
823
|
+
} else if (namespace === DEFAULT_NAMESPACE) {
|
|
824
|
+
namespace = void 0;
|
|
825
|
+
}
|
|
826
|
+
kind = kind.toLocaleLowerCase("en-US");
|
|
827
|
+
kind = defaultKind && defaultKind.toLocaleLowerCase("en-US") === kind ? void 0 : kind;
|
|
828
|
+
return `${kind ? `${kind}:` : ""}${namespace ? `${namespace}/` : ""}${name}`;
|
|
829
|
+
}
|
|
830
|
+
function humanizeEntity(entity, opts) {
|
|
831
|
+
for (const path of ["spec.profile.displayName", "metadata.title"]) {
|
|
832
|
+
const value = get(entity, path);
|
|
833
|
+
if (value && typeof value === "string") {
|
|
834
|
+
return value;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
return humanizeEntityRef(entity, opts);
|
|
838
|
+
}
|
|
839
|
+
|
|
940
840
|
const useStyles$d = makeStyles(
|
|
941
841
|
{
|
|
942
842
|
input: {}
|
|
@@ -948,7 +848,7 @@ const useStyles$d = makeStyles(
|
|
|
948
848
|
const icon$2 = /* @__PURE__ */ React.createElement(CheckBoxOutlineBlankIcon, { fontSize: "small" });
|
|
949
849
|
const checkedIcon$2 = /* @__PURE__ */ React.createElement(CheckBoxIcon, { fontSize: "small" });
|
|
950
850
|
const EntityOwnerPicker = () => {
|
|
951
|
-
var _a, _b;
|
|
851
|
+
var _a, _b, _c;
|
|
952
852
|
const classes = useStyles$d();
|
|
953
853
|
const {
|
|
954
854
|
updateFilters,
|
|
@@ -956,45 +856,98 @@ const EntityOwnerPicker = () => {
|
|
|
956
856
|
filters,
|
|
957
857
|
queryParameters: { owners: ownersParameter }
|
|
958
858
|
} = useEntityList();
|
|
859
|
+
const catalogApi = useApi(catalogApiRef);
|
|
860
|
+
const errorApi = useApi(errorApiRef);
|
|
959
861
|
const queryParamOwners = useMemo(
|
|
960
862
|
() => [ownersParameter].flat().filter(Boolean),
|
|
961
863
|
[ownersParameter]
|
|
962
864
|
);
|
|
963
865
|
const [selectedOwners, setSelectedOwners] = useState(
|
|
964
|
-
queryParamOwners.length ? queryParamOwners : (_b = (_a = filters.owners) == null ? void 0 : _a.values) != null ? _b : []
|
|
866
|
+
queryParamOwners.length ? new EntityOwnerFilter(queryParamOwners).values : (_b = (_a = filters.owners) == null ? void 0 : _a.values) != null ? _b : []
|
|
965
867
|
);
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
() => [
|
|
868
|
+
const {
|
|
869
|
+
loading,
|
|
870
|
+
error,
|
|
871
|
+
value: ownerEntities
|
|
872
|
+
} = useAsync(async () => {
|
|
873
|
+
const ownerEntityRefs = [
|
|
973
874
|
...new Set(
|
|
974
875
|
backendEntities.flatMap(
|
|
975
876
|
(e) => getEntityRelations(e, RELATION_OWNED_BY).map(
|
|
976
|
-
(o) =>
|
|
877
|
+
(o) => stringifyEntityRef(o)
|
|
977
878
|
)
|
|
978
879
|
).filter(Boolean)
|
|
979
880
|
)
|
|
980
|
-
]
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
881
|
+
];
|
|
882
|
+
const { items: ownerEntitiesOrNull } = await catalogApi.getEntitiesByRefs({
|
|
883
|
+
entityRefs: ownerEntityRefs,
|
|
884
|
+
fields: [
|
|
885
|
+
"kind",
|
|
886
|
+
"metadata.name",
|
|
887
|
+
"metadata.title",
|
|
888
|
+
"metadata.namespace",
|
|
889
|
+
"spec.profile.displayName"
|
|
890
|
+
]
|
|
891
|
+
});
|
|
892
|
+
const owners = ownerEntitiesOrNull.map((entity, index) => {
|
|
893
|
+
if (entity) {
|
|
894
|
+
return {
|
|
895
|
+
label: humanizeEntity(entity, { defaultKind: "Group" }),
|
|
896
|
+
entityRef: stringifyEntityRef(entity)
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
return {
|
|
900
|
+
label: humanizeEntityRef(parseEntityRef(ownerEntityRefs[index]), {
|
|
901
|
+
defaultKind: "group"
|
|
902
|
+
}),
|
|
903
|
+
entityRef: ownerEntityRefs[index]
|
|
904
|
+
};
|
|
986
905
|
});
|
|
987
|
-
|
|
988
|
-
|
|
906
|
+
return owners.sort(
|
|
907
|
+
(a, b) => a.label.localeCompare(b.label, "en-US", {
|
|
908
|
+
ignorePunctuation: true,
|
|
909
|
+
caseFirst: "upper"
|
|
910
|
+
})
|
|
911
|
+
);
|
|
912
|
+
}, [backendEntities]);
|
|
913
|
+
useEffect(() => {
|
|
914
|
+
if (error) {
|
|
915
|
+
errorApi.post(
|
|
916
|
+
{
|
|
917
|
+
...error,
|
|
918
|
+
message: `EntityOwnerPicker failed to initialize: ${error.message}`
|
|
919
|
+
},
|
|
920
|
+
{}
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
}, [error, errorApi]);
|
|
924
|
+
useEffect(() => {
|
|
925
|
+
if (queryParamOwners.length) {
|
|
926
|
+
const filter = new EntityOwnerFilter(queryParamOwners);
|
|
927
|
+
setSelectedOwners(filter.values);
|
|
928
|
+
}
|
|
929
|
+
}, [queryParamOwners]);
|
|
930
|
+
useEffect(() => {
|
|
931
|
+
if (!loading && ownerEntities) {
|
|
932
|
+
updateFilters({
|
|
933
|
+
owners: selectedOwners.length && ownerEntities.length ? new EntityOwnerFilter(selectedOwners) : void 0
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
}, [selectedOwners, updateFilters, ownerEntities, loading]);
|
|
937
|
+
if (!loading && !(ownerEntities == null ? void 0 : ownerEntities.length))
|
|
989
938
|
return null;
|
|
990
939
|
return /* @__PURE__ */ React.createElement(Box, { pb: 1, pt: 1 }, /* @__PURE__ */ React.createElement(Typography, { variant: "button", component: "label" }, "Owner", /* @__PURE__ */ React.createElement(
|
|
991
940
|
Autocomplete,
|
|
992
941
|
{
|
|
993
942
|
multiple: true,
|
|
994
943
|
disableCloseOnSelect: true,
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
944
|
+
loading,
|
|
945
|
+
options: ownerEntities || [],
|
|
946
|
+
value: (_c = ownerEntities == null ? void 0 : ownerEntities.filter(
|
|
947
|
+
(e) => selectedOwners.some((f) => f === e.entityRef)
|
|
948
|
+
)) != null ? _c : [],
|
|
949
|
+
onChange: (_, value) => setSelectedOwners(value.map((e) => e.entityRef)),
|
|
950
|
+
getOptionLabel: (option) => option.label,
|
|
998
951
|
renderOption: (option, { selected }) => /* @__PURE__ */ React.createElement(
|
|
999
952
|
FormControlLabel,
|
|
1000
953
|
{
|
|
@@ -1007,7 +960,7 @@ const EntityOwnerPicker = () => {
|
|
|
1007
960
|
}
|
|
1008
961
|
),
|
|
1009
962
|
onClick: (event) => event.preventDefault(),
|
|
1010
|
-
label: option
|
|
963
|
+
label: option.label
|
|
1011
964
|
}
|
|
1012
965
|
),
|
|
1013
966
|
size: "small",
|
|
@@ -1024,6 +977,129 @@ const EntityOwnerPicker = () => {
|
|
|
1024
977
|
)));
|
|
1025
978
|
};
|
|
1026
979
|
|
|
980
|
+
const entityRouteRef = getOrCreateGlobalSingleton(
|
|
981
|
+
"catalog:entity-route-ref",
|
|
982
|
+
() => createRouteRef({
|
|
983
|
+
id: "catalog:entity",
|
|
984
|
+
params: ["namespace", "kind", "name"]
|
|
985
|
+
})
|
|
986
|
+
);
|
|
987
|
+
function entityRouteParams(entity) {
|
|
988
|
+
var _a, _b;
|
|
989
|
+
return {
|
|
990
|
+
kind: entity.kind.toLocaleLowerCase("en-US"),
|
|
991
|
+
namespace: (_b = (_a = entity.metadata.namespace) == null ? void 0 : _a.toLocaleLowerCase("en-US")) != null ? _b : DEFAULT_NAMESPACE,
|
|
992
|
+
name: entity.metadata.name
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
const EntityRefLink = forwardRef(
|
|
997
|
+
(props, ref) => {
|
|
998
|
+
var _a;
|
|
999
|
+
const { entityRef, defaultKind, title, children, ...linkProps } = props;
|
|
1000
|
+
const entityRoute = useRouteRef(entityRouteRef);
|
|
1001
|
+
let kind;
|
|
1002
|
+
let namespace;
|
|
1003
|
+
let name;
|
|
1004
|
+
if (typeof entityRef === "string") {
|
|
1005
|
+
const parsed = parseEntityRef(entityRef);
|
|
1006
|
+
kind = parsed.kind;
|
|
1007
|
+
namespace = parsed.namespace;
|
|
1008
|
+
name = parsed.name;
|
|
1009
|
+
} else if ("metadata" in entityRef) {
|
|
1010
|
+
kind = entityRef.kind;
|
|
1011
|
+
namespace = entityRef.metadata.namespace;
|
|
1012
|
+
name = entityRef.metadata.name;
|
|
1013
|
+
} else {
|
|
1014
|
+
kind = entityRef.kind;
|
|
1015
|
+
namespace = entityRef.namespace;
|
|
1016
|
+
name = entityRef.name;
|
|
1017
|
+
}
|
|
1018
|
+
kind = kind.toLocaleLowerCase("en-US");
|
|
1019
|
+
namespace = (_a = namespace == null ? void 0 : namespace.toLocaleLowerCase("en-US")) != null ? _a : DEFAULT_NAMESPACE;
|
|
1020
|
+
const routeParams = { kind, namespace, name };
|
|
1021
|
+
const formattedEntityRefTitle = humanizeEntityRef(
|
|
1022
|
+
{ kind, namespace, name },
|
|
1023
|
+
{ defaultKind }
|
|
1024
|
+
);
|
|
1025
|
+
const link = /* @__PURE__ */ React.createElement(Link, { ...linkProps, ref, to: entityRoute(routeParams) }, children, !children && (title != null ? title : formattedEntityRefTitle));
|
|
1026
|
+
return title ? /* @__PURE__ */ React.createElement(Tooltip, { title: formattedEntityRefTitle }, link) : link;
|
|
1027
|
+
}
|
|
1028
|
+
);
|
|
1029
|
+
|
|
1030
|
+
function FetchedEntityRefLinks(props) {
|
|
1031
|
+
const { entityRefs, defaultKind, getTitle, ...linkProps } = props;
|
|
1032
|
+
const catalogApi = useApi(catalogApiRef);
|
|
1033
|
+
const {
|
|
1034
|
+
value: entities = new Array(),
|
|
1035
|
+
loading,
|
|
1036
|
+
error
|
|
1037
|
+
} = useAsync(async () => {
|
|
1038
|
+
const refs = entityRefs.reduce((acc, current) => {
|
|
1039
|
+
if (typeof current === "object" && "metadata" in current) {
|
|
1040
|
+
return acc;
|
|
1041
|
+
}
|
|
1042
|
+
return [...acc, parseEntityRef(current)];
|
|
1043
|
+
}, new Array());
|
|
1044
|
+
const pureEntities = entityRefs.filter(
|
|
1045
|
+
(ref) => typeof ref === "object" && "metadata" in ref
|
|
1046
|
+
);
|
|
1047
|
+
return refs.length > 0 ? [
|
|
1048
|
+
...(await catalogApi.getEntities({
|
|
1049
|
+
filter: refs.map((ref) => ({
|
|
1050
|
+
kind: ref.kind,
|
|
1051
|
+
"metadata.namespace": ref.namespace,
|
|
1052
|
+
"metadata.name": ref.name
|
|
1053
|
+
}))
|
|
1054
|
+
})).items,
|
|
1055
|
+
...pureEntities
|
|
1056
|
+
] : pureEntities;
|
|
1057
|
+
}, [entityRefs]);
|
|
1058
|
+
if (loading) {
|
|
1059
|
+
return /* @__PURE__ */ React.createElement(Progress, null);
|
|
1060
|
+
}
|
|
1061
|
+
if (error) {
|
|
1062
|
+
return /* @__PURE__ */ React.createElement(ErrorPanel, { error });
|
|
1063
|
+
}
|
|
1064
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, entities.map((r, i) => {
|
|
1065
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, { key: i }, i > 0 && ", ", /* @__PURE__ */ React.createElement(
|
|
1066
|
+
EntityRefLink,
|
|
1067
|
+
{
|
|
1068
|
+
...linkProps,
|
|
1069
|
+
defaultKind,
|
|
1070
|
+
entityRef: r,
|
|
1071
|
+
title: getTitle(r)
|
|
1072
|
+
}
|
|
1073
|
+
));
|
|
1074
|
+
}));
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
function EntityRefLinks(props) {
|
|
1078
|
+
const { entityRefs, defaultKind, fetchEntities, getTitle, ...linkProps } = props;
|
|
1079
|
+
if (fetchEntities) {
|
|
1080
|
+
return /* @__PURE__ */ React.createElement(
|
|
1081
|
+
FetchedEntityRefLinks,
|
|
1082
|
+
{
|
|
1083
|
+
...linkProps,
|
|
1084
|
+
defaultKind,
|
|
1085
|
+
entityRefs,
|
|
1086
|
+
getTitle
|
|
1087
|
+
}
|
|
1088
|
+
);
|
|
1089
|
+
}
|
|
1090
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, entityRefs.map((r, i) => {
|
|
1091
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, { key: i }, i > 0 && ", ", /* @__PURE__ */ React.createElement(
|
|
1092
|
+
EntityRefLink,
|
|
1093
|
+
{
|
|
1094
|
+
...linkProps,
|
|
1095
|
+
defaultKind,
|
|
1096
|
+
entityRef: r,
|
|
1097
|
+
title: getTitle ? getTitle(r) : void 0
|
|
1098
|
+
}
|
|
1099
|
+
));
|
|
1100
|
+
}));
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1027
1103
|
const EntityCardActions = (props) => {
|
|
1028
1104
|
const entityRoute = useRouteRef(entityRouteRef);
|
|
1029
1105
|
return /* @__PURE__ */ React.createElement(
|
|
@@ -1441,10 +1517,10 @@ function EntityAutocompletePicker(props) {
|
|
|
1441
1517
|
queryParameters.length ? queryParameters : (_b = (_a = filters[name]) == null ? void 0 : _a.values) != null ? _b : []
|
|
1442
1518
|
);
|
|
1443
1519
|
useEffect(() => {
|
|
1444
|
-
if (queryParameters.length
|
|
1520
|
+
if (queryParameters.length) {
|
|
1445
1521
|
setSelectedOptions(queryParameters);
|
|
1446
1522
|
}
|
|
1447
|
-
}, [
|
|
1523
|
+
}, [queryParameters]);
|
|
1448
1524
|
const availableOptions = Object.keys(availableValues != null ? availableValues : {});
|
|
1449
1525
|
const shouldAddFilter = selectedOptions.length && availableOptions.length;
|
|
1450
1526
|
useEffect(() => {
|