@backstage/plugin-scaffolder 1.15.1 → 1.16.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.
@@ -1,14 +1,14 @@
1
1
  import { scmIntegrationsApiRef, scmAuthApiRef } from '@backstage/integration-react';
2
- import { scaffolderApiRef, useTemplateSecrets, createScaffolderFieldExtension } from '@backstage/plugin-scaffolder-react';
2
+ import { scaffolderApiRef, useTemplateSecrets, createScaffolderFieldExtension, useTaskEventStream } from '@backstage/plugin-scaffolder-react';
3
3
  import { parseEntityRef, stringifyEntityRef, KubernetesValidatorFunctions, RELATION_OWNED_BY, makeValidator } from '@backstage/catalog-model';
4
4
  import { ResponseError, NotFoundError } from '@backstage/errors';
5
5
  import qs from 'qs';
6
6
  import ObservableImpl from 'zen-observable';
7
7
  import { EventSourcePolyfill } from 'event-source-polyfill';
8
8
  import { CATALOG_FILTER_EXISTS } from '@backstage/catalog-client';
9
- import { useApi, identityApiRef, createExternalRouteRef, createRouteRef, createSubRouteRef, errorApiRef, createPlugin, createApiFactory, discoveryApiRef, fetchApiRef, createRoutableExtension } from '@backstage/core-plugin-api';
10
- import { catalogApiRef, humanizeEntityRef } from '@backstage/plugin-catalog-react';
11
- import { TextField, FormControl as FormControl$1 } from '@material-ui/core';
9
+ import { useApi, identityApiRef, createExternalRouteRef, createRouteRef, createSubRouteRef, errorApiRef, createPlugin, createApiFactory, discoveryApiRef, fetchApiRef, createRoutableExtension, alertApiRef, useRouteRef } from '@backstage/core-plugin-api';
10
+ import { catalogApiRef, humanizeEntityRef, useEntityTypeFilter } from '@backstage/plugin-catalog-react';
11
+ import { TextField, FormControl as FormControl$1, Box, Typography, FormControlLabel, Checkbox, makeStyles, useTheme, IconButton, Popover, MenuList, MenuItem, ListItemIcon, ListItemText, Paper, Button } from '@material-ui/core';
12
12
  import FormControl from '@material-ui/core/FormControl';
13
13
  import Autocomplete from '@material-ui/lab/Autocomplete';
14
14
  import React, { useCallback, useEffect, useState, useMemo } from 'react';
@@ -18,10 +18,22 @@ import zodToJsonSchema from 'zod-to-json-schema';
18
18
  import FormHelperText from '@material-ui/core/FormHelperText';
19
19
  import Input from '@material-ui/core/Input';
20
20
  import InputLabel from '@material-ui/core/InputLabel';
21
- import { Select, Progress } from '@backstage/core-components';
21
+ import { Select, Progress, Page, Header, Content, ErrorPanel } from '@backstage/core-components';
22
22
  import useDebounce from 'react-use/lib/useDebounce';
23
23
  import useEffectOnce from 'react-use/lib/useEffectOnce';
24
24
  import { Autocomplete as Autocomplete$1 } from '@material-ui/lab';
25
+ import capitalize from 'lodash/capitalize';
26
+ import CheckBoxIcon from '@material-ui/icons/CheckBox';
27
+ import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
28
+ import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
29
+ import { useParams, useNavigate } from 'react-router-dom';
30
+ import { useAsync as useAsync$1 } from '@react-hookz/web';
31
+ import Cancel from '@material-ui/icons/Cancel';
32
+ import Retry from '@material-ui/icons/Repeat';
33
+ import Toc from '@material-ui/icons/Toc';
34
+ import ControlPointIcon from '@material-ui/icons/ControlPoint';
35
+ import MoreVert from '@material-ui/icons/MoreVert';
36
+ import { DefaultTemplateOutputs, TaskSteps, TaskLogStream } from '@backstage/plugin-scaffolder-react/alpha';
25
37
 
26
38
  var __defProp = Object.defineProperty;
27
39
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -1450,7 +1462,7 @@ const MyGroupsPickerFieldExtension = scaffolderPlugin.provide(
1450
1462
  const ScaffolderPage = scaffolderPlugin.provide(
1451
1463
  createRoutableExtension({
1452
1464
  name: "ScaffolderPage",
1453
- component: () => import('./Router-f5f81642.esm.js').then((m) => m.Router),
1465
+ component: () => import('./index-52e5acc5.esm.js').then((m) => m.Router),
1454
1466
  mountPoint: rootRouteRef
1455
1467
  })
1456
1468
  );
@@ -1468,13 +1480,282 @@ const EntityTagsPickerFieldExtension = scaffolderPlugin.provide(
1468
1480
  schema: EntityTagsPickerSchema
1469
1481
  })
1470
1482
  );
1471
- const NextScaffolderPage = scaffolderPlugin.provide(
1483
+ const LegacyScaffolderPage = scaffolderPlugin.provide(
1472
1484
  createRoutableExtension({
1473
- name: "NextScaffolderPage",
1474
- component: () => import('./index-e1da2925.esm.js').then((m) => m.Router),
1485
+ name: "LegacyScaffolderPage",
1486
+ component: () => import('./Router-b60d8000.esm.js').then((m) => m.LegacyRouter),
1475
1487
  mountPoint: rootRouteRef
1476
1488
  })
1477
1489
  );
1478
1490
 
1479
- export { entityNamePickerValidation as A, EntityNamePickerSchema as B, EntityTagsPicker as C, EntityTagsPickerSchema as D, EntityPickerFieldExtension as E, RepoUrlPicker as F, RepoUrlPickerSchema as G, OwnerPicker as H, OwnerPickerSchema as I, OwnedEntityPicker as J, OwnedEntityPickerSchema as K, MyGroupsPicker as L, MyGroupsPickerFieldExtension as M, NextScaffolderPage as N, OwnerPickerFieldExtension as O, RepoUrlPickerFieldExtension as R, ScaffolderClient as S, EntityNamePickerFieldExtension as a, EntityTagsPickerFieldExtension as b, OwnedEntityPickerFieldExtension as c, ScaffolderPage as d, EntityPickerFieldSchema as e, OwnerPickerFieldSchema as f, RepoUrlPickerFieldSchema as g, repoPickerValidation as h, OwnedEntityPickerFieldSchema as i, EntityTagsPickerFieldSchema as j, MyGroupsPickerSchema as k, MyGroupsPickerFieldSchema as l, makeFieldSchemaFromZod as m, selectedTemplateRouteRef as n, scaffolderTaskRouteRef as o, editRouteRef as p, actionsRouteRef as q, rootRouteRef as r, scaffolderPlugin as s, scaffolderListTaskRouteRef as t, registerComponentRouteRef as u, viewTechDocRouteRef as v, legacySelectedTemplateRouteRef as w, EntityPicker as x, EntityPickerSchema as y, EntityNamePicker as z };
1480
- //# sourceMappingURL=plugin-76d8b756.esm.js.map
1491
+ const icon = /* @__PURE__ */ React.createElement(CheckBoxOutlineBlankIcon, { fontSize: "small" });
1492
+ const checkedIcon = /* @__PURE__ */ React.createElement(CheckBoxIcon, { fontSize: "small" });
1493
+ const TemplateTypePicker = () => {
1494
+ const alertApi = useApi(alertApiRef);
1495
+ const { error, loading, availableTypes, selectedTypes, setSelectedTypes } = useEntityTypeFilter();
1496
+ if (loading)
1497
+ return /* @__PURE__ */ React.createElement(Progress, null);
1498
+ if (!availableTypes)
1499
+ return null;
1500
+ if (error) {
1501
+ alertApi.post({
1502
+ message: `Failed to load entity types`,
1503
+ severity: "error"
1504
+ });
1505
+ return null;
1506
+ }
1507
+ return /* @__PURE__ */ React.createElement(Box, { pb: 1, pt: 1 }, /* @__PURE__ */ React.createElement(
1508
+ Typography,
1509
+ {
1510
+ variant: "button",
1511
+ component: "label",
1512
+ htmlFor: "categories-picker"
1513
+ },
1514
+ "Categories"
1515
+ ), /* @__PURE__ */ React.createElement(
1516
+ Autocomplete$1,
1517
+ {
1518
+ id: "categories-picker",
1519
+ multiple: true,
1520
+ options: availableTypes,
1521
+ value: selectedTypes,
1522
+ onChange: (_, value) => setSelectedTypes(value),
1523
+ renderOption: (option, { selected }) => /* @__PURE__ */ React.createElement(
1524
+ FormControlLabel,
1525
+ {
1526
+ control: /* @__PURE__ */ React.createElement(
1527
+ Checkbox,
1528
+ {
1529
+ icon,
1530
+ checkedIcon,
1531
+ checked: selected
1532
+ }
1533
+ ),
1534
+ label: capitalize(option)
1535
+ }
1536
+ ),
1537
+ size: "small",
1538
+ popupIcon: /* @__PURE__ */ React.createElement(ExpandMoreIcon, { "data-testid": "categories-picker-expand" }),
1539
+ renderInput: (params) => /* @__PURE__ */ React.createElement(TextField, { ...params, variant: "outlined" })
1540
+ }
1541
+ ));
1542
+ };
1543
+
1544
+ const useStyles$1 = makeStyles(() => ({
1545
+ button: {
1546
+ color: ({ fontColor }) => fontColor
1547
+ }
1548
+ }));
1549
+ const ContextMenu = (props) => {
1550
+ const {
1551
+ cancelEnabled,
1552
+ logsVisible,
1553
+ buttonBarVisible,
1554
+ onStartOver,
1555
+ onToggleLogs,
1556
+ onToggleButtonBar,
1557
+ taskId
1558
+ } = props;
1559
+ const { getPageTheme } = useTheme();
1560
+ const pageTheme = getPageTheme({ themeId: "website" });
1561
+ const classes = useStyles$1({ fontColor: pageTheme.fontColor });
1562
+ const scaffolderApi = useApi(scaffolderApiRef);
1563
+ const [anchorEl, setAnchorEl] = useState();
1564
+ const [{ status: cancelStatus }, { execute: cancel }] = useAsync$1(async () => {
1565
+ if (taskId) {
1566
+ await scaffolderApi.cancelTask(taskId);
1567
+ }
1568
+ });
1569
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
1570
+ IconButton,
1571
+ {
1572
+ "aria-label": "more",
1573
+ "aria-controls": "long-menu",
1574
+ "aria-haspopup": "true",
1575
+ onClick: (event) => {
1576
+ setAnchorEl(event.currentTarget);
1577
+ },
1578
+ "data-testid": "menu-button",
1579
+ className: classes.button
1580
+ },
1581
+ /* @__PURE__ */ React.createElement(MoreVert, null)
1582
+ ), /* @__PURE__ */ React.createElement(
1583
+ Popover,
1584
+ {
1585
+ open: Boolean(anchorEl),
1586
+ onClose: () => setAnchorEl(void 0),
1587
+ anchorEl,
1588
+ anchorOrigin: { vertical: "bottom", horizontal: "right" },
1589
+ transformOrigin: { vertical: "top", horizontal: "right" }
1590
+ },
1591
+ /* @__PURE__ */ React.createElement(MenuList, null, /* @__PURE__ */ React.createElement(MenuItem, { onClick: () => onToggleLogs == null ? void 0 : onToggleLogs(!logsVisible) }, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(Toc, { fontSize: "small" })), /* @__PURE__ */ React.createElement(ListItemText, { primary: logsVisible ? "Hide Logs" : "Show Logs" })), /* @__PURE__ */ React.createElement(MenuItem, { onClick: () => onToggleButtonBar == null ? void 0 : onToggleButtonBar(!buttonBarVisible) }, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(ControlPointIcon, { fontSize: "small" })), /* @__PURE__ */ React.createElement(
1592
+ ListItemText,
1593
+ {
1594
+ primary: buttonBarVisible ? "Hide Button Bar" : "Show Button Bar"
1595
+ }
1596
+ )), /* @__PURE__ */ React.createElement(MenuItem, { onClick: onStartOver }, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(Retry, { fontSize: "small" })), /* @__PURE__ */ React.createElement(ListItemText, { primary: "Start Over" })), /* @__PURE__ */ React.createElement(
1597
+ MenuItem,
1598
+ {
1599
+ onClick: cancel,
1600
+ disabled: !cancelEnabled || cancelStatus !== "not-executed",
1601
+ "data-testid": "cancel-task"
1602
+ },
1603
+ /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(Cancel, { fontSize: "small" })),
1604
+ /* @__PURE__ */ React.createElement(ListItemText, { primary: "Cancel" })
1605
+ ))
1606
+ ));
1607
+ };
1608
+
1609
+ const useStyles = makeStyles((theme) => ({
1610
+ contentWrapper: {
1611
+ display: "flex",
1612
+ flexDirection: "column"
1613
+ },
1614
+ buttonBar: {
1615
+ display: "flex",
1616
+ flexDirection: "row",
1617
+ justifyContent: "right"
1618
+ },
1619
+ cancelButton: {
1620
+ marginRight: theme.spacing(1)
1621
+ },
1622
+ logsVisibilityButton: {
1623
+ marginRight: theme.spacing(1)
1624
+ }
1625
+ }));
1626
+ const OngoingTask = (props) => {
1627
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1628
+ const { taskId } = useParams();
1629
+ const templateRouteRef = useRouteRef(selectedTemplateRouteRef);
1630
+ const navigate = useNavigate();
1631
+ const scaffolderApi = useApi(scaffolderApiRef);
1632
+ const taskStream = useTaskEventStream(taskId);
1633
+ const classes = useStyles();
1634
+ const steps = useMemo(
1635
+ () => {
1636
+ var _a2, _b2;
1637
+ return (_b2 = (_a2 = taskStream.task) == null ? void 0 : _a2.spec.steps.map((step) => {
1638
+ var _a3;
1639
+ return {
1640
+ ...step,
1641
+ ...(_a3 = taskStream == null ? void 0 : taskStream.steps) == null ? void 0 : _a3[step.id]
1642
+ };
1643
+ })) != null ? _b2 : [];
1644
+ },
1645
+ [taskStream]
1646
+ );
1647
+ const [logsVisible, setLogVisibleState] = useState(false);
1648
+ const [buttonBarVisible, setButtonBarVisibleState] = useState(true);
1649
+ useEffect(() => {
1650
+ if (taskStream.error) {
1651
+ setLogVisibleState(true);
1652
+ }
1653
+ }, [taskStream.error]);
1654
+ useEffect(() => {
1655
+ if (taskStream.completed && !taskStream.error) {
1656
+ setButtonBarVisibleState(false);
1657
+ }
1658
+ }, [taskStream.error, taskStream.completed]);
1659
+ const activeStep = useMemo(() => {
1660
+ for (let i = steps.length - 1; i >= 0; i--) {
1661
+ if (steps[i].status !== "open") {
1662
+ return i;
1663
+ }
1664
+ }
1665
+ return 0;
1666
+ }, [steps]);
1667
+ const startOver = useCallback(() => {
1668
+ var _a2, _b2, _c2, _d2, _e2, _f2;
1669
+ const { namespace, name } = (_d2 = (_c2 = (_b2 = (_a2 = taskStream.task) == null ? void 0 : _a2.spec.templateInfo) == null ? void 0 : _b2.entity) == null ? void 0 : _c2.metadata) != null ? _d2 : {};
1670
+ const formData = (_f2 = (_e2 = taskStream.task) == null ? void 0 : _e2.spec.parameters) != null ? _f2 : {};
1671
+ if (!namespace || !name) {
1672
+ return;
1673
+ }
1674
+ navigate({
1675
+ pathname: templateRouteRef({
1676
+ namespace,
1677
+ templateName: name
1678
+ }),
1679
+ search: `?${qs.stringify({ formData: JSON.stringify(formData) })}`
1680
+ });
1681
+ }, [
1682
+ navigate,
1683
+ (_a = taskStream.task) == null ? void 0 : _a.spec.parameters,
1684
+ (_d = (_c = (_b = taskStream.task) == null ? void 0 : _b.spec.templateInfo) == null ? void 0 : _c.entity) == null ? void 0 : _d.metadata,
1685
+ templateRouteRef
1686
+ ]);
1687
+ const [{ status: cancelStatus }, { execute: triggerCancel }] = useAsync$1(
1688
+ async () => {
1689
+ if (taskId) {
1690
+ await scaffolderApi.cancelTask(taskId);
1691
+ }
1692
+ }
1693
+ );
1694
+ const Outputs = (_e = props.TemplateOutputsComponent) != null ? _e : DefaultTemplateOutputs;
1695
+ const templateName = (_h = (_g = (_f = taskStream.task) == null ? void 0 : _f.spec.templateInfo) == null ? void 0 : _g.entity) == null ? void 0 : _h.metadata.name;
1696
+ const cancelEnabled = !(taskStream.cancelled || taskStream.completed);
1697
+ return /* @__PURE__ */ React.createElement(Page, { themeId: "website" }, /* @__PURE__ */ React.createElement(
1698
+ Header,
1699
+ {
1700
+ pageTitleOverride: `Run of ${templateName}`,
1701
+ title: /* @__PURE__ */ React.createElement("div", null, "Run of ", /* @__PURE__ */ React.createElement("code", null, templateName)),
1702
+ subtitle: `Task ${taskId}`
1703
+ },
1704
+ /* @__PURE__ */ React.createElement(
1705
+ ContextMenu,
1706
+ {
1707
+ cancelEnabled,
1708
+ logsVisible,
1709
+ buttonBarVisible,
1710
+ onStartOver: startOver,
1711
+ onToggleLogs: setLogVisibleState,
1712
+ onToggleButtonBar: setButtonBarVisibleState,
1713
+ taskId
1714
+ }
1715
+ )
1716
+ ), /* @__PURE__ */ React.createElement(Content, { className: classes.contentWrapper }, taskStream.error ? /* @__PURE__ */ React.createElement(Box, { paddingBottom: 2 }, /* @__PURE__ */ React.createElement(
1717
+ ErrorPanel,
1718
+ {
1719
+ error: taskStream.error,
1720
+ title: taskStream.error.message
1721
+ }
1722
+ )) : null, /* @__PURE__ */ React.createElement(Box, { paddingBottom: 2 }, /* @__PURE__ */ React.createElement(
1723
+ TaskSteps,
1724
+ {
1725
+ steps,
1726
+ activeStep,
1727
+ isComplete: taskStream.completed,
1728
+ isError: Boolean(taskStream.error)
1729
+ }
1730
+ )), /* @__PURE__ */ React.createElement(Outputs, { output: taskStream.output }), buttonBarVisible ? /* @__PURE__ */ React.createElement(Box, { paddingBottom: 2 }, /* @__PURE__ */ React.createElement(Paper, null, /* @__PURE__ */ React.createElement(Box, { padding: 2 }, /* @__PURE__ */ React.createElement("div", { className: classes.buttonBar }, /* @__PURE__ */ React.createElement(
1731
+ Button,
1732
+ {
1733
+ className: classes.cancelButton,
1734
+ disabled: !cancelEnabled || cancelStatus !== "not-executed",
1735
+ onClick: triggerCancel,
1736
+ "data-testid": "cancel-button"
1737
+ },
1738
+ "Cancel"
1739
+ ), /* @__PURE__ */ React.createElement(
1740
+ Button,
1741
+ {
1742
+ className: classes.logsVisibilityButton,
1743
+ color: "primary",
1744
+ variant: "outlined",
1745
+ onClick: () => setLogVisibleState(!logsVisible)
1746
+ },
1747
+ logsVisible ? "Hide Logs" : "Show Logs"
1748
+ ), /* @__PURE__ */ React.createElement(
1749
+ Button,
1750
+ {
1751
+ variant: "contained",
1752
+ color: "primary",
1753
+ disabled: cancelEnabled,
1754
+ onClick: startOver
1755
+ },
1756
+ "Start Over"
1757
+ ))))) : null, logsVisible ? /* @__PURE__ */ React.createElement(Box, { paddingBottom: 2, height: "100%" }, /* @__PURE__ */ React.createElement(Paper, { style: { height: "100%" } }, /* @__PURE__ */ React.createElement(Box, { padding: 2, height: "100%" }, /* @__PURE__ */ React.createElement(TaskLogStream, { logs: taskStream.stepLogs })))) : null));
1758
+ };
1759
+
1760
+ export { EntityNamePicker as A, entityNamePickerValidation as B, EntityNamePickerSchema as C, EntityTagsPicker as D, EntityPickerFieldExtension as E, EntityTagsPickerSchema as F, RepoUrlPicker as G, RepoUrlPickerSchema as H, OwnerPicker as I, OwnerPickerSchema as J, OwnedEntityPicker as K, LegacyScaffolderPage as L, MyGroupsPickerFieldExtension as M, OwnedEntityPickerSchema as N, OwnerPickerFieldExtension as O, MyGroupsPicker as P, RepoUrlPickerFieldExtension as R, ScaffolderClient as S, TemplateTypePicker as T, EntityNamePickerFieldExtension as a, EntityTagsPickerFieldExtension as b, OwnedEntityPickerFieldExtension as c, ScaffolderPage as d, OngoingTask as e, EntityPickerFieldSchema as f, OwnerPickerFieldSchema as g, RepoUrlPickerFieldSchema as h, repoPickerValidation as i, OwnedEntityPickerFieldSchema as j, EntityTagsPickerFieldSchema as k, MyGroupsPickerSchema as l, makeFieldSchemaFromZod as m, MyGroupsPickerFieldSchema as n, selectedTemplateRouteRef as o, editRouteRef as p, actionsRouteRef as q, rootRouteRef as r, scaffolderPlugin as s, scaffolderListTaskRouteRef as t, registerComponentRouteRef as u, viewTechDocRouteRef as v, scaffolderTaskRouteRef as w, legacySelectedTemplateRouteRef as x, EntityPicker as y, EntityPickerSchema as z };
1761
+ //# sourceMappingURL=OngoingTask-8acb140b.esm.js.map