@backstage/plugin-scaffolder 0.12.0 → 0.12.2-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 +45 -0
- package/dist/esm/{Router-5ca674eb.esm.js → Router-5df57206.esm.js} +47 -432
- package/dist/esm/Router-5df57206.esm.js.map +1 -0
- package/dist/esm/{index-8b5d21c1.esm.js → index-71578268.esm.js} +492 -30
- package/dist/esm/index-71578268.esm.js.map +1 -0
- package/dist/index.d.ts +43 -8
- package/dist/index.esm.js +16 -1
- package/dist/index.esm.js.map +1 -1
- package/package.json +12 -10
- package/dist/esm/Router-5ca674eb.esm.js.map +0 -1
- package/dist/esm/index-8b5d21c1.esm.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# @backstage/plugin-scaffolder
|
|
2
2
|
|
|
3
|
+
## 0.12.2-next.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 33e139e652: Adds a loading bar to the scaffolder task page if the task is still loading. This can happen if it takes a while for a task worker to pick up a task.
|
|
8
|
+
- 6458be3307: Encode the `formData` in the `queryString` using `JSON.stringify` to keep the types in the decoded value
|
|
9
|
+
- 319f4b79a2: The ScaffolderPage can be passed an optional `TaskPageComponent` with a `loadingText` string. It will replace the Loading text in the scaffolder task page.
|
|
10
|
+
- Updated dependencies
|
|
11
|
+
- @backstage/core-components@0.8.8-next.0
|
|
12
|
+
- @backstage/plugin-catalog-react@0.6.14-next.0
|
|
13
|
+
- @backstage/integration-react@0.1.21-next.0
|
|
14
|
+
|
|
15
|
+
## 0.12.1
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- ba59832aed: Permission the Register Existing Component button
|
|
20
|
+
- cee44ad289: Added the ability to collect users `oauth` token from the `RepoUrlPicker` for use in the template manifest
|
|
21
|
+
- a681cb9c2f: Make linkTarget configurable for MarkdownContent component
|
|
22
|
+
- Updated dependencies
|
|
23
|
+
- @backstage/core-components@0.8.7
|
|
24
|
+
- @backstage/plugin-catalog-react@0.6.13
|
|
25
|
+
- @backstage/plugin-catalog-common@0.1.2
|
|
26
|
+
- @backstage/integration-react@0.1.20
|
|
27
|
+
|
|
28
|
+
## 0.12.1-next.1
|
|
29
|
+
|
|
30
|
+
### Patch Changes
|
|
31
|
+
|
|
32
|
+
- ba59832aed: Permission the Register Existing Component button
|
|
33
|
+
- Updated dependencies
|
|
34
|
+
- @backstage/core-components@0.8.7-next.1
|
|
35
|
+
- @backstage/plugin-catalog-react@0.6.13-next.1
|
|
36
|
+
- @backstage/plugin-catalog-common@0.1.2-next.0
|
|
37
|
+
|
|
38
|
+
## 0.12.1-next.0
|
|
39
|
+
|
|
40
|
+
### Patch Changes
|
|
41
|
+
|
|
42
|
+
- a681cb9c2f: Make linkTarget configurable for MarkdownContent component
|
|
43
|
+
- Updated dependencies
|
|
44
|
+
- @backstage/core-components@0.8.7-next.0
|
|
45
|
+
- @backstage/integration-react@0.1.20-next.0
|
|
46
|
+
- @backstage/plugin-catalog-react@0.6.13-next.0
|
|
47
|
+
|
|
3
48
|
## 0.12.0
|
|
4
49
|
|
|
5
50
|
### Minor Changes
|
|
@@ -1,40 +1,30 @@
|
|
|
1
|
-
import React, { useState,
|
|
2
|
-
import { useNavigate, Navigate, generatePath,
|
|
3
|
-
import { Page, Header, Lifecycle, Content, ContentHeader, CreateButton, SupportButton, MarkdownContent, StructuredMetadataTable, InfoCard,
|
|
4
|
-
import { useRouteRef, useApi, errorApiRef, useApiHolder,
|
|
5
|
-
import { EntityListProvider, EntitySearchBar, EntityKindPicker, UserListPicker, EntityTagPicker
|
|
6
|
-
import { makeStyles, Stepper, Step, StepLabel, Typography, StepContent, Button, Paper, Box, LinearProgress,
|
|
7
|
-
import { E as EntityPicker, a as EntityNamePicker, e as entityNamePickerValidation, b as EntityTagsPicker, R as RepoUrlPicker, r as repoPickerValidation, O as OwnerPicker, c as OwnedEntityPicker, d as registerComponentRouteRef, T as TemplateTypePicker, f as TemplateList, s as scaffolderApiRef, g as rootRouteRef, F as FIELD_EXTENSION_WRAPPER_KEY, h as FIELD_EXTENSION_KEY } from './index-
|
|
1
|
+
import React, { useState, useContext, useCallback } from 'react';
|
|
2
|
+
import { useNavigate, Navigate, generatePath, useOutlet, Routes, Route } from 'react-router';
|
|
3
|
+
import { Page, Header, Lifecycle, Content, ContentHeader, CreateButton, SupportButton, MarkdownContent, StructuredMetadataTable, InfoCard, Progress, ErrorPage } from '@backstage/core-components';
|
|
4
|
+
import { useRouteRef, useApi, errorApiRef, useApiHolder, useElementFilter } from '@backstage/core-plugin-api';
|
|
5
|
+
import { EntityListProvider, EntitySearchBar, EntityKindPicker, UserListPicker, EntityTagPicker } from '@backstage/plugin-catalog-react';
|
|
6
|
+
import { makeStyles, Stepper, Step, StepLabel, Typography, StepContent, Button, Paper, Box, LinearProgress, TableContainer, Table, TableHead, TableRow, TableCell, TableBody } from '@material-ui/core';
|
|
7
|
+
import { E as EntityPicker, a as EntityNamePicker, e as entityNamePickerValidation, b as EntityTagsPicker, R as RepoUrlPicker, r as repoPickerValidation, O as OwnerPicker, c as OwnedEntityPicker, d as registerComponentRouteRef, T as TemplateTypePicker, f as TemplateList, S as SecretsContext, s as scaffolderApiRef, g as rootRouteRef, F as FIELD_EXTENSION_WRAPPER_KEY, h as FIELD_EXTENSION_KEY, i as SecretsContextProvider, j as TaskPage } from './index-71578268.esm.js';
|
|
8
|
+
import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common';
|
|
9
|
+
import { usePermission } from '@backstage/plugin-permission-react';
|
|
8
10
|
import qs from 'qs';
|
|
9
11
|
import { useParams } from 'react-router-dom';
|
|
10
12
|
import useAsync from 'react-use/lib/useAsync';
|
|
11
13
|
import { withTheme } from '@rjsf/core';
|
|
12
14
|
import { Theme } from '@rjsf/material-ui';
|
|
13
|
-
import Grid$1 from '@material-ui/core/Grid';
|
|
14
|
-
import Step$1 from '@material-ui/core/Step';
|
|
15
|
-
import StepLabel$1 from '@material-ui/core/StepLabel';
|
|
16
|
-
import Stepper$1 from '@material-ui/core/Stepper';
|
|
17
|
-
import { makeStyles as makeStyles$1, createStyles } from '@material-ui/core/styles';
|
|
18
|
-
import Typography$1 from '@material-ui/core/Typography';
|
|
19
|
-
import Cancel from '@material-ui/icons/Cancel';
|
|
20
|
-
import Check from '@material-ui/icons/Check';
|
|
21
|
-
import FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord';
|
|
22
15
|
import classNames from 'classnames';
|
|
23
|
-
import { DateTime, Interval } from 'luxon';
|
|
24
|
-
import useInterval from 'react-use/lib/useInterval';
|
|
25
|
-
import { useImmerReducer } from 'use-immer';
|
|
26
|
-
import { parseEntityName } from '@backstage/catalog-model';
|
|
27
|
-
import LanguageIcon from '@material-ui/icons/Language';
|
|
28
16
|
import '@backstage/errors';
|
|
29
17
|
import 'zen-observable';
|
|
30
18
|
import '@material-ui/core/FormControl';
|
|
31
19
|
import '@material-ui/lab/Autocomplete';
|
|
20
|
+
import '@backstage/catalog-model';
|
|
32
21
|
import 'react-use/lib/useEffectOnce';
|
|
33
22
|
import '@material-ui/lab';
|
|
34
23
|
import '@backstage/integration-react';
|
|
35
24
|
import '@material-ui/core/FormHelperText';
|
|
36
25
|
import '@material-ui/core/Input';
|
|
37
26
|
import '@material-ui/core/InputLabel';
|
|
27
|
+
import 'react-use/lib/useDebounce';
|
|
38
28
|
import '@material-ui/icons/Star';
|
|
39
29
|
import '@material-ui/icons/StarBorder';
|
|
40
30
|
import '@material-ui/icons/Warning';
|
|
@@ -42,6 +32,19 @@ import 'lodash/capitalize';
|
|
|
42
32
|
import '@material-ui/icons/CheckBox';
|
|
43
33
|
import '@material-ui/icons/CheckBoxOutlineBlank';
|
|
44
34
|
import '@material-ui/icons/ExpandMore';
|
|
35
|
+
import '@material-ui/core/Grid';
|
|
36
|
+
import '@material-ui/core/Step';
|
|
37
|
+
import '@material-ui/core/StepLabel';
|
|
38
|
+
import '@material-ui/core/Stepper';
|
|
39
|
+
import '@material-ui/core/styles';
|
|
40
|
+
import '@material-ui/core/Typography';
|
|
41
|
+
import '@material-ui/icons/Cancel';
|
|
42
|
+
import '@material-ui/icons/Check';
|
|
43
|
+
import '@material-ui/icons/FiberManualRecord';
|
|
44
|
+
import 'luxon';
|
|
45
|
+
import 'react-use/lib/useInterval';
|
|
46
|
+
import 'use-immer';
|
|
47
|
+
import '@material-ui/icons/Language';
|
|
45
48
|
|
|
46
49
|
const DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS = [
|
|
47
50
|
{
|
|
@@ -72,7 +75,7 @@ const DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS = [
|
|
|
72
75
|
}
|
|
73
76
|
];
|
|
74
77
|
|
|
75
|
-
const useStyles$
|
|
78
|
+
const useStyles$1 = makeStyles((theme) => ({
|
|
76
79
|
contentWrapper: {
|
|
77
80
|
display: "grid",
|
|
78
81
|
gridTemplateAreas: "'filters' 'grid'",
|
|
@@ -84,7 +87,7 @@ const ScaffolderPageContents = ({
|
|
|
84
87
|
TemplateCardComponent,
|
|
85
88
|
groups
|
|
86
89
|
}) => {
|
|
87
|
-
const styles = useStyles$
|
|
90
|
+
const styles = useStyles$1();
|
|
88
91
|
const registerComponentLink = useRouteRef(registerComponentRouteRef);
|
|
89
92
|
const otherTemplatesGroup = {
|
|
90
93
|
title: groups ? "Other Templates" : "Templates",
|
|
@@ -93,6 +96,7 @@ const ScaffolderPageContents = ({
|
|
|
93
96
|
return !filtered.some((result) => result === true);
|
|
94
97
|
}
|
|
95
98
|
};
|
|
99
|
+
const { allowed } = usePermission(catalogEntityCreatePermission);
|
|
96
100
|
return /* @__PURE__ */ React.createElement(Page, {
|
|
97
101
|
themeId: "home"
|
|
98
102
|
}, /* @__PURE__ */ React.createElement(Header, {
|
|
@@ -103,7 +107,7 @@ const ScaffolderPageContents = ({
|
|
|
103
107
|
subtitle: "Create new software components using standard templates"
|
|
104
108
|
}), /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ContentHeader, {
|
|
105
109
|
title: "Available Templates"
|
|
106
|
-
}, /* @__PURE__ */ React.createElement(CreateButton, {
|
|
110
|
+
}, allowed && /* @__PURE__ */ React.createElement(CreateButton, {
|
|
107
111
|
title: "Register Existing Component",
|
|
108
112
|
to: registerComponentLink && registerComponentLink()
|
|
109
113
|
}), /* @__PURE__ */ React.createElement(SupportButton, null, "Create new software components using standard templates. Different templates create different kinds of components (services, websites, documentation, ...).")), /* @__PURE__ */ React.createElement("div", {
|
|
@@ -212,7 +216,8 @@ function transformSchemaToProps(inputSchema) {
|
|
|
212
216
|
}
|
|
213
217
|
|
|
214
218
|
const DescriptionField = ({ description }) => description && /* @__PURE__ */ React.createElement(MarkdownContent, {
|
|
215
|
-
content: description
|
|
219
|
+
content: description,
|
|
220
|
+
linkTarget: "_blank"
|
|
216
221
|
});
|
|
217
222
|
|
|
218
223
|
var fieldOverrides = /*#__PURE__*/Object.freeze({
|
|
@@ -398,6 +403,7 @@ const TemplatePage = ({
|
|
|
398
403
|
customFieldExtensions = []
|
|
399
404
|
}) => {
|
|
400
405
|
const apiHolder = useApiHolder();
|
|
406
|
+
const secretsContext = useContext(SecretsContext);
|
|
401
407
|
const errorApi = useApi(errorApiRef);
|
|
402
408
|
const scaffolderApi = useApi(scaffolderApiRef);
|
|
403
409
|
const { templateName } = useParams();
|
|
@@ -409,13 +415,17 @@ const TemplatePage = ({
|
|
|
409
415
|
const query = qs.parse(window.location.search, {
|
|
410
416
|
ignoreQueryPrefix: true
|
|
411
417
|
});
|
|
412
|
-
|
|
418
|
+
try {
|
|
419
|
+
return JSON.parse(query.formData);
|
|
420
|
+
} catch (e) {
|
|
421
|
+
return (_a = query.formData) != null ? _a : {};
|
|
422
|
+
}
|
|
413
423
|
});
|
|
414
424
|
const handleFormReset = () => setFormState({});
|
|
415
425
|
const handleChange = useCallback((e) => setFormState(e.formData), [setFormState]);
|
|
416
426
|
const handleCreate = async () => {
|
|
417
427
|
var _a;
|
|
418
|
-
const id = await scaffolderApi.scaffold(templateName, formState);
|
|
428
|
+
const id = await scaffolderApi.scaffold(templateName, formState, secretsContext == null ? void 0 : secretsContext.secrets);
|
|
419
429
|
const formParams = qs.stringify({ formData: formState }, { addQueryPrefix: true });
|
|
420
430
|
const newUrl = `${window.location.pathname}${formParams}`;
|
|
421
431
|
(_a = window.history) == null ? void 0 : _a.replaceState(null, document.title, newUrl);
|
|
@@ -464,406 +474,6 @@ const TemplatePage = ({
|
|
|
464
474
|
}))));
|
|
465
475
|
};
|
|
466
476
|
|
|
467
|
-
function reducer(draft, action) {
|
|
468
|
-
var _a, _b, _c;
|
|
469
|
-
switch (action.type) {
|
|
470
|
-
case "INIT": {
|
|
471
|
-
draft.steps = action.data.spec.steps.reduce((current, next) => {
|
|
472
|
-
current[next.id] = { status: "open", id: next.id };
|
|
473
|
-
return current;
|
|
474
|
-
}, {});
|
|
475
|
-
draft.stepLogs = action.data.spec.steps.reduce((current, next) => {
|
|
476
|
-
current[next.id] = [];
|
|
477
|
-
return current;
|
|
478
|
-
}, {});
|
|
479
|
-
draft.loading = false;
|
|
480
|
-
draft.error = void 0;
|
|
481
|
-
draft.completed = false;
|
|
482
|
-
draft.task = action.data;
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
case "LOGS": {
|
|
486
|
-
const entries = action.data;
|
|
487
|
-
for (const entry of entries) {
|
|
488
|
-
const logLine = `${entry.createdAt} ${entry.body.message}`;
|
|
489
|
-
if (!entry.body.stepId || !((_a = draft.steps) == null ? void 0 : _a[entry.body.stepId])) {
|
|
490
|
-
continue;
|
|
491
|
-
}
|
|
492
|
-
const currentStepLog = (_b = draft.stepLogs) == null ? void 0 : _b[entry.body.stepId];
|
|
493
|
-
const currentStep = (_c = draft.steps) == null ? void 0 : _c[entry.body.stepId];
|
|
494
|
-
if (entry.body.status && entry.body.status !== currentStep.status) {
|
|
495
|
-
currentStep.status = entry.body.status;
|
|
496
|
-
if (currentStep.status === "processing") {
|
|
497
|
-
currentStep.startedAt = entry.createdAt;
|
|
498
|
-
}
|
|
499
|
-
if (["cancelled", "failed", "completed"].includes(currentStep.status)) {
|
|
500
|
-
currentStep.endedAt = entry.createdAt;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
currentStepLog == null ? void 0 : currentStepLog.push(logLine);
|
|
504
|
-
}
|
|
505
|
-
return;
|
|
506
|
-
}
|
|
507
|
-
case "COMPLETED": {
|
|
508
|
-
draft.completed = true;
|
|
509
|
-
draft.output = action.data.body.output;
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
case "ERROR": {
|
|
513
|
-
draft.error = action.data;
|
|
514
|
-
draft.loading = false;
|
|
515
|
-
draft.completed = true;
|
|
516
|
-
return;
|
|
517
|
-
}
|
|
518
|
-
default:
|
|
519
|
-
return;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
const useTaskEventStream = (taskId) => {
|
|
523
|
-
const scaffolderApi = useApi(scaffolderApiRef);
|
|
524
|
-
const [state, dispatch] = useImmerReducer(reducer, {
|
|
525
|
-
loading: true,
|
|
526
|
-
completed: false,
|
|
527
|
-
stepLogs: {},
|
|
528
|
-
steps: {}
|
|
529
|
-
});
|
|
530
|
-
useEffect(() => {
|
|
531
|
-
let didCancel = false;
|
|
532
|
-
let subscription;
|
|
533
|
-
let logPusher;
|
|
534
|
-
scaffolderApi.getTask(taskId).then((task) => {
|
|
535
|
-
if (didCancel) {
|
|
536
|
-
return;
|
|
537
|
-
}
|
|
538
|
-
dispatch({ type: "INIT", data: task });
|
|
539
|
-
const observable = scaffolderApi.streamLogs({ taskId });
|
|
540
|
-
const collectedLogEvents = new Array();
|
|
541
|
-
function emitLogs() {
|
|
542
|
-
if (collectedLogEvents.length) {
|
|
543
|
-
const logs = collectedLogEvents.splice(0, collectedLogEvents.length);
|
|
544
|
-
dispatch({ type: "LOGS", data: logs });
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
logPusher = setInterval(emitLogs, 500);
|
|
548
|
-
subscription = observable.subscribe({
|
|
549
|
-
next: (event) => {
|
|
550
|
-
switch (event.type) {
|
|
551
|
-
case "log":
|
|
552
|
-
return collectedLogEvents.push(event);
|
|
553
|
-
case "completion":
|
|
554
|
-
emitLogs();
|
|
555
|
-
dispatch({ type: "COMPLETED", data: event });
|
|
556
|
-
return void 0;
|
|
557
|
-
default:
|
|
558
|
-
throw new Error(`Unhandled event type ${event.type} in observer`);
|
|
559
|
-
}
|
|
560
|
-
},
|
|
561
|
-
error: (error) => {
|
|
562
|
-
emitLogs();
|
|
563
|
-
dispatch({ type: "ERROR", data: error });
|
|
564
|
-
}
|
|
565
|
-
});
|
|
566
|
-
}, (error) => {
|
|
567
|
-
if (!didCancel) {
|
|
568
|
-
dispatch({ type: "ERROR", data: error });
|
|
569
|
-
}
|
|
570
|
-
});
|
|
571
|
-
return () => {
|
|
572
|
-
didCancel = true;
|
|
573
|
-
if (subscription) {
|
|
574
|
-
subscription.unsubscribe();
|
|
575
|
-
}
|
|
576
|
-
if (logPusher) {
|
|
577
|
-
clearInterval(logPusher);
|
|
578
|
-
}
|
|
579
|
-
};
|
|
580
|
-
}, [scaffolderApi, dispatch, taskId]);
|
|
581
|
-
return state;
|
|
582
|
-
};
|
|
583
|
-
|
|
584
|
-
const useStyles$2 = makeStyles({
|
|
585
|
-
svgIcon: {
|
|
586
|
-
display: "inline-block",
|
|
587
|
-
"& svg": {
|
|
588
|
-
display: "inline-block",
|
|
589
|
-
fontSize: "inherit",
|
|
590
|
-
verticalAlign: "baseline"
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
});
|
|
594
|
-
const IconLink = (props) => {
|
|
595
|
-
const { href, text, Icon, ...linkProps } = props;
|
|
596
|
-
const classes = useStyles$2();
|
|
597
|
-
return /* @__PURE__ */ React.createElement(Grid, {
|
|
598
|
-
container: true,
|
|
599
|
-
direction: "row",
|
|
600
|
-
spacing: 1
|
|
601
|
-
}, /* @__PURE__ */ React.createElement(Grid, {
|
|
602
|
-
item: true
|
|
603
|
-
}, /* @__PURE__ */ React.createElement(Typography, {
|
|
604
|
-
component: "div",
|
|
605
|
-
className: classes.svgIcon
|
|
606
|
-
}, Icon ? /* @__PURE__ */ React.createElement(Icon, null) : /* @__PURE__ */ React.createElement(LanguageIcon, null))), /* @__PURE__ */ React.createElement(Grid, {
|
|
607
|
-
item: true
|
|
608
|
-
}, /* @__PURE__ */ React.createElement(Link, {
|
|
609
|
-
to: href,
|
|
610
|
-
...linkProps
|
|
611
|
-
}, text || href)));
|
|
612
|
-
};
|
|
613
|
-
|
|
614
|
-
const TaskPageLinks = ({ output }) => {
|
|
615
|
-
const { entityRef: entityRefOutput, remoteUrl } = output;
|
|
616
|
-
let { links = [] } = output;
|
|
617
|
-
const app = useApp();
|
|
618
|
-
const entityRoute = useRouteRef(entityRouteRef);
|
|
619
|
-
const iconResolver = (key) => {
|
|
620
|
-
var _a;
|
|
621
|
-
return key ? (_a = app.getSystemIcon(key)) != null ? _a : LanguageIcon : LanguageIcon;
|
|
622
|
-
};
|
|
623
|
-
if (remoteUrl) {
|
|
624
|
-
links = [{ url: remoteUrl, title: "Repo" }, ...links];
|
|
625
|
-
}
|
|
626
|
-
if (entityRefOutput) {
|
|
627
|
-
links = [
|
|
628
|
-
{
|
|
629
|
-
entityRef: entityRefOutput,
|
|
630
|
-
title: "Open in catalog",
|
|
631
|
-
icon: "catalog"
|
|
632
|
-
},
|
|
633
|
-
...links
|
|
634
|
-
];
|
|
635
|
-
}
|
|
636
|
-
return /* @__PURE__ */ React.createElement(Box, {
|
|
637
|
-
px: 3,
|
|
638
|
-
pb: 3
|
|
639
|
-
}, links.filter(({ url, entityRef }) => url || entityRef).map(({ url, entityRef, title, icon }) => {
|
|
640
|
-
if (entityRef) {
|
|
641
|
-
const entityName = parseEntityName(entityRef);
|
|
642
|
-
const target = entityRoute(entityName);
|
|
643
|
-
return { title, icon, url: target };
|
|
644
|
-
}
|
|
645
|
-
return { title, icon, url };
|
|
646
|
-
}).map(({ url, title, icon }, i) => /* @__PURE__ */ React.createElement(IconLink, {
|
|
647
|
-
key: `output-link-${i}`,
|
|
648
|
-
href: url,
|
|
649
|
-
text: title != null ? title : url,
|
|
650
|
-
Icon: iconResolver(icon),
|
|
651
|
-
target: "_blank"
|
|
652
|
-
})));
|
|
653
|
-
};
|
|
654
|
-
|
|
655
|
-
const humanizeDuration = require("humanize-duration");
|
|
656
|
-
const useStyles$1 = makeStyles$1((theme) => createStyles({
|
|
657
|
-
root: {
|
|
658
|
-
width: "100%"
|
|
659
|
-
},
|
|
660
|
-
button: {
|
|
661
|
-
marginBottom: theme.spacing(2),
|
|
662
|
-
marginLeft: theme.spacing(2)
|
|
663
|
-
},
|
|
664
|
-
actionsContainer: {
|
|
665
|
-
marginBottom: theme.spacing(2)
|
|
666
|
-
},
|
|
667
|
-
resetContainer: {
|
|
668
|
-
padding: theme.spacing(3)
|
|
669
|
-
},
|
|
670
|
-
labelWrapper: {
|
|
671
|
-
display: "flex",
|
|
672
|
-
flex: 1,
|
|
673
|
-
flexDirection: "row",
|
|
674
|
-
justifyContent: "space-between"
|
|
675
|
-
},
|
|
676
|
-
stepWrapper: {
|
|
677
|
-
width: "100%"
|
|
678
|
-
}
|
|
679
|
-
}));
|
|
680
|
-
const StepTimeTicker = ({ step }) => {
|
|
681
|
-
const [time, setTime] = useState("");
|
|
682
|
-
useInterval(() => {
|
|
683
|
-
if (!step.startedAt) {
|
|
684
|
-
setTime("");
|
|
685
|
-
return;
|
|
686
|
-
}
|
|
687
|
-
const end = step.endedAt ? DateTime.fromISO(step.endedAt) : DateTime.local();
|
|
688
|
-
const startedAt = DateTime.fromISO(step.startedAt);
|
|
689
|
-
const formatted = Interval.fromDateTimes(startedAt, end).toDuration().valueOf();
|
|
690
|
-
setTime(humanizeDuration(formatted, { round: true }));
|
|
691
|
-
}, 1e3);
|
|
692
|
-
return /* @__PURE__ */ React.createElement(Typography$1, {
|
|
693
|
-
variant: "caption"
|
|
694
|
-
}, time);
|
|
695
|
-
};
|
|
696
|
-
const useStepIconStyles = makeStyles$1((theme) => createStyles({
|
|
697
|
-
root: {
|
|
698
|
-
color: theme.palette.text.disabled,
|
|
699
|
-
display: "flex",
|
|
700
|
-
height: 22,
|
|
701
|
-
alignItems: "center"
|
|
702
|
-
},
|
|
703
|
-
completed: {
|
|
704
|
-
color: theme.palette.status.ok
|
|
705
|
-
},
|
|
706
|
-
error: {
|
|
707
|
-
color: theme.palette.status.error
|
|
708
|
-
}
|
|
709
|
-
}));
|
|
710
|
-
function TaskStepIconComponent(props) {
|
|
711
|
-
const classes = useStepIconStyles();
|
|
712
|
-
const { active, completed, error } = props;
|
|
713
|
-
const getMiddle = () => {
|
|
714
|
-
if (active) {
|
|
715
|
-
return /* @__PURE__ */ React.createElement(CircularProgress, {
|
|
716
|
-
size: "24px"
|
|
717
|
-
});
|
|
718
|
-
}
|
|
719
|
-
if (completed) {
|
|
720
|
-
return /* @__PURE__ */ React.createElement(Check, null);
|
|
721
|
-
}
|
|
722
|
-
if (error) {
|
|
723
|
-
return /* @__PURE__ */ React.createElement(Cancel, null);
|
|
724
|
-
}
|
|
725
|
-
return /* @__PURE__ */ React.createElement(FiberManualRecordIcon, null);
|
|
726
|
-
};
|
|
727
|
-
return /* @__PURE__ */ React.createElement("div", {
|
|
728
|
-
className: classNames(classes.root, {
|
|
729
|
-
[classes.completed]: completed,
|
|
730
|
-
[classes.error]: error
|
|
731
|
-
})
|
|
732
|
-
}, getMiddle());
|
|
733
|
-
}
|
|
734
|
-
const TaskStatusStepper = memo(({
|
|
735
|
-
steps,
|
|
736
|
-
currentStepId,
|
|
737
|
-
onUserStepChange
|
|
738
|
-
}) => {
|
|
739
|
-
const classes = useStyles$1();
|
|
740
|
-
return /* @__PURE__ */ React.createElement("div", {
|
|
741
|
-
className: classes.root
|
|
742
|
-
}, /* @__PURE__ */ React.createElement(Stepper$1, {
|
|
743
|
-
activeStep: steps.findIndex((s) => s.id === currentStepId),
|
|
744
|
-
orientation: "vertical",
|
|
745
|
-
nonLinear: true
|
|
746
|
-
}, steps.map((step, index) => {
|
|
747
|
-
const isCompleted = step.status === "completed";
|
|
748
|
-
const isFailed = step.status === "failed";
|
|
749
|
-
const isActive = step.status === "processing";
|
|
750
|
-
const isSkipped = step.status === "skipped";
|
|
751
|
-
return /* @__PURE__ */ React.createElement(Step$1, {
|
|
752
|
-
key: String(index),
|
|
753
|
-
expanded: true
|
|
754
|
-
}, /* @__PURE__ */ React.createElement(StepButton, {
|
|
755
|
-
onClick: () => onUserStepChange(step.id)
|
|
756
|
-
}, /* @__PURE__ */ React.createElement(StepLabel$1, {
|
|
757
|
-
StepIconProps: {
|
|
758
|
-
completed: isCompleted,
|
|
759
|
-
error: isFailed,
|
|
760
|
-
active: isActive
|
|
761
|
-
},
|
|
762
|
-
StepIconComponent: TaskStepIconComponent,
|
|
763
|
-
className: classes.stepWrapper
|
|
764
|
-
}, /* @__PURE__ */ React.createElement("div", {
|
|
765
|
-
className: classes.labelWrapper
|
|
766
|
-
}, /* @__PURE__ */ React.createElement(Typography$1, {
|
|
767
|
-
variant: "subtitle2"
|
|
768
|
-
}, step.name), isSkipped ? /* @__PURE__ */ React.createElement(Typography$1, {
|
|
769
|
-
variant: "caption"
|
|
770
|
-
}, "Skipped") : /* @__PURE__ */ React.createElement(StepTimeTicker, {
|
|
771
|
-
step
|
|
772
|
-
})))));
|
|
773
|
-
})));
|
|
774
|
-
});
|
|
775
|
-
const hasLinks = ({ entityRef, remoteUrl, links = [] }) => !!(entityRef || remoteUrl || links.length > 0);
|
|
776
|
-
const TaskPage = () => {
|
|
777
|
-
const classes = useStyles$1();
|
|
778
|
-
const navigate = useNavigate();
|
|
779
|
-
const rootLink = useRouteRef(rootRouteRef);
|
|
780
|
-
const [userSelectedStepId, setUserSelectedStepId] = useState(void 0);
|
|
781
|
-
const [lastActiveStepId, setLastActiveStepId] = useState(void 0);
|
|
782
|
-
const { taskId } = useParams$1();
|
|
783
|
-
const taskStream = useTaskEventStream(taskId);
|
|
784
|
-
const completed = taskStream.completed;
|
|
785
|
-
const steps = useMemo(() => {
|
|
786
|
-
var _a, _b;
|
|
787
|
-
return (_b = (_a = taskStream.task) == null ? void 0 : _a.spec.steps.map((step) => {
|
|
788
|
-
var _a2;
|
|
789
|
-
return {
|
|
790
|
-
...step,
|
|
791
|
-
...(_a2 = taskStream == null ? void 0 : taskStream.steps) == null ? void 0 : _a2[step.id]
|
|
792
|
-
};
|
|
793
|
-
})) != null ? _b : [];
|
|
794
|
-
}, [taskStream]);
|
|
795
|
-
useEffect(() => {
|
|
796
|
-
var _a;
|
|
797
|
-
const mostRecentFailedOrActiveStep = steps.find((step) => ["failed", "processing"].includes(step.status));
|
|
798
|
-
if (completed && !mostRecentFailedOrActiveStep) {
|
|
799
|
-
setLastActiveStepId((_a = steps[steps.length - 1]) == null ? void 0 : _a.id);
|
|
800
|
-
return;
|
|
801
|
-
}
|
|
802
|
-
setLastActiveStepId(mostRecentFailedOrActiveStep == null ? void 0 : mostRecentFailedOrActiveStep.id);
|
|
803
|
-
}, [steps, completed]);
|
|
804
|
-
const currentStepId = userSelectedStepId != null ? userSelectedStepId : lastActiveStepId;
|
|
805
|
-
const logAsString = useMemo(() => {
|
|
806
|
-
if (!currentStepId) {
|
|
807
|
-
return "Loading...";
|
|
808
|
-
}
|
|
809
|
-
const log = taskStream.stepLogs[currentStepId];
|
|
810
|
-
if (!(log == null ? void 0 : log.length)) {
|
|
811
|
-
return "Waiting for logs...";
|
|
812
|
-
}
|
|
813
|
-
return log.join("\n");
|
|
814
|
-
}, [taskStream.stepLogs, currentStepId]);
|
|
815
|
-
const taskNotFound = taskStream.completed === true && taskStream.loading === false && !taskStream.task;
|
|
816
|
-
const { output } = taskStream;
|
|
817
|
-
const handleStartOver = () => {
|
|
818
|
-
var _a, _b;
|
|
819
|
-
if (!taskStream.task || !((_b = (_a = taskStream.task) == null ? void 0 : _a.spec.metadata) == null ? void 0 : _b.name)) {
|
|
820
|
-
navigate(generatePath(rootLink()));
|
|
821
|
-
}
|
|
822
|
-
const formData = taskStream.task.spec.apiVersion === "backstage.io/v1beta2" ? taskStream.task.spec.values : taskStream.task.spec.parameters;
|
|
823
|
-
navigate(generatePath(`${rootLink()}/templates/:templateName?${qs.stringify({ formData })}`, {
|
|
824
|
-
templateName: taskStream.task.spec.metadata.name
|
|
825
|
-
}));
|
|
826
|
-
};
|
|
827
|
-
return /* @__PURE__ */ React.createElement(Page, {
|
|
828
|
-
themeId: "home"
|
|
829
|
-
}, /* @__PURE__ */ React.createElement(Header, {
|
|
830
|
-
pageTitleOverride: `Task ${taskId}`,
|
|
831
|
-
title: /* @__PURE__ */ React.createElement(React.Fragment, null, "Task Activity ", /* @__PURE__ */ React.createElement(Lifecycle, {
|
|
832
|
-
alpha: true,
|
|
833
|
-
shorthand: true
|
|
834
|
-
})),
|
|
835
|
-
subtitle: `Activity for task: ${taskId}`
|
|
836
|
-
}), /* @__PURE__ */ React.createElement(Content, null, taskNotFound ? /* @__PURE__ */ React.createElement(ErrorPage, {
|
|
837
|
-
status: "404",
|
|
838
|
-
statusMessage: "Task not found",
|
|
839
|
-
additionalInfo: "No task found with this ID"
|
|
840
|
-
}) : /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Grid$1, {
|
|
841
|
-
container: true
|
|
842
|
-
}, /* @__PURE__ */ React.createElement(Grid$1, {
|
|
843
|
-
item: true,
|
|
844
|
-
xs: 3
|
|
845
|
-
}, /* @__PURE__ */ React.createElement(Paper, null, /* @__PURE__ */ React.createElement(TaskStatusStepper, {
|
|
846
|
-
steps,
|
|
847
|
-
currentStepId,
|
|
848
|
-
onUserStepChange: setUserSelectedStepId
|
|
849
|
-
}), output && hasLinks(output) && /* @__PURE__ */ React.createElement(TaskPageLinks, {
|
|
850
|
-
output
|
|
851
|
-
}), /* @__PURE__ */ React.createElement(Button, {
|
|
852
|
-
className: classes.button,
|
|
853
|
-
onClick: handleStartOver,
|
|
854
|
-
disabled: !completed,
|
|
855
|
-
variant: "contained",
|
|
856
|
-
color: "primary"
|
|
857
|
-
}, "Start Over"))), /* @__PURE__ */ React.createElement(Grid$1, {
|
|
858
|
-
item: true,
|
|
859
|
-
xs: 9
|
|
860
|
-
}, /* @__PURE__ */ React.createElement("div", {
|
|
861
|
-
style: { height: "80vh" }
|
|
862
|
-
}, /* @__PURE__ */ React.createElement(LogViewer, {
|
|
863
|
-
text: logAsString
|
|
864
|
-
})))))));
|
|
865
|
-
};
|
|
866
|
-
|
|
867
477
|
const useStyles = makeStyles((theme) => ({
|
|
868
478
|
code: {
|
|
869
479
|
fontFamily: "Menlo, monospace",
|
|
@@ -972,8 +582,13 @@ const ActionsPage = () => {
|
|
|
972
582
|
}), /* @__PURE__ */ React.createElement(Content, null, items));
|
|
973
583
|
};
|
|
974
584
|
|
|
975
|
-
const Router = ({
|
|
585
|
+
const Router = ({
|
|
586
|
+
TemplateCardComponent,
|
|
587
|
+
TaskPageComponent,
|
|
588
|
+
groups
|
|
589
|
+
}) => {
|
|
976
590
|
const outlet = useOutlet();
|
|
591
|
+
const TaskPageElement = TaskPageComponent || TaskPage;
|
|
977
592
|
const customFieldExtensions = useElementFilter(outlet, (elements) => elements.selectByComponentData({
|
|
978
593
|
key: FIELD_EXTENSION_WRAPPER_KEY
|
|
979
594
|
}).findComponentData({
|
|
@@ -991,12 +606,12 @@ const Router = ({ TemplateCardComponent, groups }) => {
|
|
|
991
606
|
})
|
|
992
607
|
}), /* @__PURE__ */ React.createElement(Route, {
|
|
993
608
|
path: "/templates/:templateName",
|
|
994
|
-
element: /* @__PURE__ */ React.createElement(TemplatePage, {
|
|
609
|
+
element: /* @__PURE__ */ React.createElement(SecretsContextProvider, null, /* @__PURE__ */ React.createElement(TemplatePage, {
|
|
995
610
|
customFieldExtensions: fieldExtensions
|
|
996
|
-
})
|
|
611
|
+
}))
|
|
997
612
|
}), /* @__PURE__ */ React.createElement(Route, {
|
|
998
613
|
path: "/tasks/:taskId",
|
|
999
|
-
element: /* @__PURE__ */ React.createElement(
|
|
614
|
+
element: /* @__PURE__ */ React.createElement(TaskPageElement, null)
|
|
1000
615
|
}), /* @__PURE__ */ React.createElement(Route, {
|
|
1001
616
|
path: "/actions",
|
|
1002
617
|
element: /* @__PURE__ */ React.createElement(ActionsPage, null)
|
|
@@ -1004,4 +619,4 @@ const Router = ({ TemplateCardComponent, groups }) => {
|
|
|
1004
619
|
};
|
|
1005
620
|
|
|
1006
621
|
export { Router };
|
|
1007
|
-
//# sourceMappingURL=Router-
|
|
622
|
+
//# sourceMappingURL=Router-5df57206.esm.js.map
|