@backstage/plugin-scaffolder 0.11.11 → 0.11.15
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 +70 -0
- package/dist/esm/{Router-de99a5d2.esm.js → Router-6eee3fa9.esm.js} +85 -68
- package/dist/esm/Router-6eee3fa9.esm.js.map +1 -0
- package/dist/esm/{index-8b883a7e.esm.js → index-210d6da0.esm.js} +159 -75
- package/dist/esm/index-210d6da0.esm.js.map +1 -0
- package/dist/index.d.ts +20 -7
- package/dist/index.esm.js +4 -5
- package/dist/index.esm.js.map +1 -1
- package/package.json +21 -20
- package/dist/esm/Router-de99a5d2.esm.js.map +0 -1
- package/dist/esm/index-8b883a7e.esm.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,75 @@
|
|
|
1
1
|
# @backstage/plugin-scaffolder
|
|
2
2
|
|
|
3
|
+
## 0.11.15
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- c5eb756760: Fix a small browser console warning
|
|
8
|
+
- ff5ff57883: EntityPicker can require an existing entity be selected by disallowing arbitrary values
|
|
9
|
+
- 0f645a7947: Added OwnedEntityPicker field which displays Owned Entities in options
|
|
10
|
+
- b646a73fe0: In @backstage/plugin-scaffolder - When user will have one option available in hostUrl or owner - autoselect and select component should be readonly.
|
|
11
|
+
|
|
12
|
+
in @backstage/core-components - Select component has extended API with few more props: native : boolean, disabled: boolean. native - if set to true - Select component will use native browser select picker (not rendered by Material UI lib ).
|
|
13
|
+
disabled - if set to true - action on component will not be possible.
|
|
14
|
+
|
|
15
|
+
- 7a4bd2ceac: Prefer using `Link` from `@backstage/core-components` rather than material-UI.
|
|
16
|
+
- 4c269c7c23: Add DescriptionField override to support Markdown
|
|
17
|
+
- Updated dependencies
|
|
18
|
+
- @backstage/core-plugin-api@0.4.0
|
|
19
|
+
- @backstage/plugin-catalog-react@0.6.8
|
|
20
|
+
- @backstage/core-components@0.8.2
|
|
21
|
+
- @backstage/catalog-client@0.5.3
|
|
22
|
+
- @backstage/integration-react@0.1.16
|
|
23
|
+
|
|
24
|
+
## 0.11.14
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- 6845cce533: Can specify allowedOwners to the RepoUrlPicker picker in a template definition
|
|
29
|
+
- cd450844f6: Moved React dependencies to `peerDependencies` and allow both React v16 and v17 to be used.
|
|
30
|
+
- 2edcf7738f: Fix bug with setting owner in RepoUrlPicker causing validation failure
|
|
31
|
+
- b291c3176e: Switch to using `LogViewer` component from `@backstage/core-components` to display scaffolder logs.
|
|
32
|
+
- Updated dependencies
|
|
33
|
+
- @backstage/core-components@0.8.0
|
|
34
|
+
- @backstage/core-plugin-api@0.3.0
|
|
35
|
+
- @backstage/integration-react@0.1.15
|
|
36
|
+
- @backstage/plugin-catalog-react@0.6.5
|
|
37
|
+
|
|
38
|
+
## 0.11.13
|
|
39
|
+
|
|
40
|
+
### Patch Changes
|
|
41
|
+
|
|
42
|
+
- ed5bef529e: Add group filtering to the scaffolder page so that individuals can surface specific templates to end users ahead of others, or group templates together. This can be accomplished by passing in a `groups` prop to the `ScaffolderPage`
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
<ScaffolderPage
|
|
46
|
+
groups={[
|
|
47
|
+
{
|
|
48
|
+
title: "Recommended",
|
|
49
|
+
filter: entity =>
|
|
50
|
+
entity?.metadata?.tags?.includes('recommended') ?? false,
|
|
51
|
+
},
|
|
52
|
+
]}
|
|
53
|
+
/>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- Updated dependencies
|
|
57
|
+
- @backstage/integration@0.6.10
|
|
58
|
+
- @backstage/core-components@0.7.6
|
|
59
|
+
- @backstage/theme@0.2.14
|
|
60
|
+
- @backstage/core-plugin-api@0.2.2
|
|
61
|
+
|
|
62
|
+
## 0.11.12
|
|
63
|
+
|
|
64
|
+
### Patch Changes
|
|
65
|
+
|
|
66
|
+
- 2d7d165737: Bump `react-jsonschema-form`
|
|
67
|
+
- 9f21236a29: Fixed a missing `await` when throwing server side errors
|
|
68
|
+
- Updated dependencies
|
|
69
|
+
- @backstage/errors@0.1.5
|
|
70
|
+
- @backstage/core-plugin-api@0.2.1
|
|
71
|
+
- @backstage/core-components@0.7.5
|
|
72
|
+
|
|
3
73
|
## 0.11.11
|
|
4
74
|
|
|
5
75
|
### Patch Changes
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import React, { useState, useCallback, useEffect, memo,
|
|
1
|
+
import React, { useState, useCallback, useEffect, memo, useMemo } from 'react';
|
|
2
2
|
import { useNavigate, Navigate, generatePath, useParams as useParams$1, useOutlet, Routes, Route } from 'react-router';
|
|
3
|
-
import { Page, Header, Lifecycle, Content, ContentHeader, CreateButton, SupportButton, StructuredMetadataTable, InfoCard, Link,
|
|
3
|
+
import { Page, Header, Lifecycle, Content, ContentHeader, CreateButton, SupportButton, MarkdownContent, StructuredMetadataTable, InfoCard, Link, ErrorPage, LogViewer, Progress } from '@backstage/core-components';
|
|
4
4
|
import { useRouteRef, useApi, errorApiRef, useApiHolder, useApp, useElementFilter } from '@backstage/core-plugin-api';
|
|
5
5
|
import { EntityListProvider, EntitySearchBar, EntityKindPicker, UserListPicker, EntityTagPicker, entityRouteRef } from '@backstage/plugin-catalog-react';
|
|
6
6
|
import { makeStyles, Stepper, Step, StepLabel, Typography, StepContent, Button, Paper, Box, LinearProgress, Grid, StepButton, CircularProgress, TableContainer, Table, TableHead, TableRow, TableCell, TableBody } from '@material-ui/core';
|
|
7
|
-
import { E as EntityPicker, a as EntityNamePicker, e as entityNamePickerValidation, R as RepoUrlPicker, r as repoPickerValidation, O as OwnerPicker, b as registerComponentRouteRef, T as TemplateTypePicker,
|
|
7
|
+
import { E as EntityPicker, a as EntityNamePicker, e as entityNamePickerValidation, R as RepoUrlPicker, r as repoPickerValidation, O as OwnerPicker, b as OwnedEntityPicker, c as registerComponentRouteRef, T as TemplateTypePicker, d as TemplateList, s as scaffolderApiRef, f as rootRouteRef, F as FIELD_EXTENSION_WRAPPER_KEY, g as FIELD_EXTENSION_KEY } from './index-210d6da0.esm.js';
|
|
8
8
|
import { useParams } from 'react-router-dom';
|
|
9
9
|
import { useAsync, useInterval } from 'react-use';
|
|
10
10
|
import { withTheme } from '@rjsf/core';
|
|
@@ -29,10 +29,9 @@ import 'zen-observable';
|
|
|
29
29
|
import '@material-ui/core/FormControl';
|
|
30
30
|
import '@material-ui/lab/Autocomplete';
|
|
31
31
|
import '@backstage/integration-react';
|
|
32
|
-
import '@material-ui/core/Select';
|
|
33
|
-
import '@material-ui/core/InputLabel';
|
|
34
|
-
import '@material-ui/core/Input';
|
|
35
32
|
import '@material-ui/core/FormHelperText';
|
|
33
|
+
import '@material-ui/core/Input';
|
|
34
|
+
import '@material-ui/core/InputLabel';
|
|
36
35
|
import '@material-ui/icons/Star';
|
|
37
36
|
import '@material-ui/icons/StarBorder';
|
|
38
37
|
import '@material-ui/icons/Warning';
|
|
@@ -60,6 +59,10 @@ const DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS = [
|
|
|
60
59
|
{
|
|
61
60
|
component: OwnerPicker,
|
|
62
61
|
name: "OwnerPicker"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
component: OwnedEntityPicker,
|
|
65
|
+
name: "OwnedEntityPicker"
|
|
63
66
|
}
|
|
64
67
|
];
|
|
65
68
|
|
|
@@ -72,10 +75,18 @@ const useStyles$3 = makeStyles((theme) => ({
|
|
|
72
75
|
}
|
|
73
76
|
}));
|
|
74
77
|
const ScaffolderPageContents = ({
|
|
75
|
-
TemplateCardComponent
|
|
78
|
+
TemplateCardComponent,
|
|
79
|
+
groups
|
|
76
80
|
}) => {
|
|
77
81
|
const styles = useStyles$3();
|
|
78
82
|
const registerComponentLink = useRouteRef(registerComponentRouteRef);
|
|
83
|
+
const otherTemplatesGroup = {
|
|
84
|
+
title: groups ? "Other Templates" : "Templates",
|
|
85
|
+
filter: (entity) => {
|
|
86
|
+
const filtered = (groups != null ? groups : []).map((group) => group.filter(entity));
|
|
87
|
+
return !filtered.some((result) => result === true);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
79
90
|
return /* @__PURE__ */ React.createElement(Page, {
|
|
80
91
|
themeId: "home"
|
|
81
92
|
}, /* @__PURE__ */ React.createElement(Header, {
|
|
@@ -97,14 +108,22 @@ const ScaffolderPageContents = ({
|
|
|
97
108
|
}), /* @__PURE__ */ React.createElement(UserListPicker, {
|
|
98
109
|
initialFilter: "all",
|
|
99
110
|
availableFilters: ["all", "starred"]
|
|
100
|
-
}), /* @__PURE__ */ React.createElement(TemplateTypePicker, null), /* @__PURE__ */ React.createElement(EntityTagPicker, null)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(TemplateList, {
|
|
101
|
-
|
|
111
|
+
}), /* @__PURE__ */ React.createElement(TemplateTypePicker, null), /* @__PURE__ */ React.createElement(EntityTagPicker, null)), /* @__PURE__ */ React.createElement("div", null, groups && groups.map((group, index) => /* @__PURE__ */ React.createElement(TemplateList, {
|
|
112
|
+
key: index,
|
|
113
|
+
TemplateCardComponent,
|
|
114
|
+
group
|
|
115
|
+
})), /* @__PURE__ */ React.createElement(TemplateList, {
|
|
116
|
+
key: "other",
|
|
117
|
+
TemplateCardComponent,
|
|
118
|
+
group: otherTemplatesGroup
|
|
102
119
|
})))));
|
|
103
120
|
};
|
|
104
121
|
const ScaffolderPage = ({
|
|
105
|
-
TemplateCardComponent
|
|
122
|
+
TemplateCardComponent,
|
|
123
|
+
groups
|
|
106
124
|
}) => /* @__PURE__ */ React.createElement(EntityListProvider, null, /* @__PURE__ */ React.createElement(ScaffolderPageContents, {
|
|
107
|
-
TemplateCardComponent
|
|
125
|
+
TemplateCardComponent,
|
|
126
|
+
groups
|
|
108
127
|
}));
|
|
109
128
|
|
|
110
129
|
function isObject$1(value) {
|
|
@@ -114,7 +133,7 @@ function extractUiSchema(schema, uiSchema) {
|
|
|
114
133
|
if (!isObject$1(schema)) {
|
|
115
134
|
return;
|
|
116
135
|
}
|
|
117
|
-
const {properties, items, anyOf, oneOf, allOf, dependencies} = schema;
|
|
136
|
+
const { properties, items, anyOf, oneOf, allOf, dependencies } = schema;
|
|
118
137
|
for (const propName in schema) {
|
|
119
138
|
if (!schema.hasOwnProperty(propName)) {
|
|
120
139
|
continue;
|
|
@@ -183,9 +202,18 @@ function transformSchemaToProps(inputSchema) {
|
|
|
183
202
|
delete schema.title;
|
|
184
203
|
const uiSchema = {};
|
|
185
204
|
extractUiSchema(schema, uiSchema);
|
|
186
|
-
return {schema, uiSchema};
|
|
205
|
+
return { schema, uiSchema };
|
|
187
206
|
}
|
|
188
207
|
|
|
208
|
+
const DescriptionField = ({ description }) => description && /* @__PURE__ */ React.createElement(MarkdownContent, {
|
|
209
|
+
content: description
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
var fieldOverrides = /*#__PURE__*/Object.freeze({
|
|
213
|
+
__proto__: null,
|
|
214
|
+
DescriptionField: DescriptionField
|
|
215
|
+
});
|
|
216
|
+
|
|
189
217
|
const Form = withTheme(Theme);
|
|
190
218
|
function getUiSchemasFromSteps(steps) {
|
|
191
219
|
const uiSchemas = [];
|
|
@@ -264,7 +292,7 @@ const MultistepJsonForm = ({
|
|
|
264
292
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Stepper, {
|
|
265
293
|
activeStep,
|
|
266
294
|
orientation: "vertical"
|
|
267
|
-
}, steps.map(({title, schema, ...formProps}, index) => {
|
|
295
|
+
}, steps.map(({ title, schema, ...formProps }, index) => {
|
|
268
296
|
return /* @__PURE__ */ React.createElement(Step, {
|
|
269
297
|
key: title
|
|
270
298
|
}, /* @__PURE__ */ React.createElement(StepLabel, {
|
|
@@ -278,7 +306,7 @@ const MultistepJsonForm = ({
|
|
|
278
306
|
key: title
|
|
279
307
|
}, /* @__PURE__ */ React.createElement(Form, {
|
|
280
308
|
showErrorList: false,
|
|
281
|
-
fields,
|
|
309
|
+
fields: { ...fieldOverrides, ...fields },
|
|
282
310
|
widgets,
|
|
283
311
|
noHtml5Validate: true,
|
|
284
312
|
formData,
|
|
@@ -323,12 +351,12 @@ const MultistepJsonForm = ({
|
|
|
323
351
|
|
|
324
352
|
const useTemplateParameterSchema = (templateName) => {
|
|
325
353
|
const scaffolderApi = useApi(scaffolderApiRef);
|
|
326
|
-
const {value, loading, error} = useAsync(() => scaffolderApi.getTemplateParameterSchema({
|
|
354
|
+
const { value, loading, error } = useAsync(() => scaffolderApi.getTemplateParameterSchema({
|
|
327
355
|
name: templateName,
|
|
328
356
|
kind: "template",
|
|
329
357
|
namespace: "default"
|
|
330
358
|
}), [scaffolderApi, templateName]);
|
|
331
|
-
return {schema: value, loading, error};
|
|
359
|
+
return { schema: value, loading, error };
|
|
332
360
|
};
|
|
333
361
|
function isObject(obj) {
|
|
334
362
|
return typeof obj === "object" && obj !== null && !Array.isArray(obj);
|
|
@@ -366,16 +394,16 @@ const TemplatePage = ({
|
|
|
366
394
|
const apiHolder = useApiHolder();
|
|
367
395
|
const errorApi = useApi(errorApiRef);
|
|
368
396
|
const scaffolderApi = useApi(scaffolderApiRef);
|
|
369
|
-
const {templateName} = useParams();
|
|
397
|
+
const { templateName } = useParams();
|
|
370
398
|
const navigate = useNavigate();
|
|
371
399
|
const rootLink = useRouteRef(rootRouteRef);
|
|
372
|
-
const {schema, loading, error} = useTemplateParameterSchema(templateName);
|
|
400
|
+
const { schema, loading, error } = useTemplateParameterSchema(templateName);
|
|
373
401
|
const [formState, setFormState] = useState({});
|
|
374
402
|
const handleFormReset = () => setFormState({});
|
|
375
403
|
const handleChange = useCallback((e) => setFormState(e.formData), [setFormState]);
|
|
376
404
|
const handleCreate = async () => {
|
|
377
405
|
const id = await scaffolderApi.scaffold(templateName, formState);
|
|
378
|
-
navigate(generatePath(`${rootLink()}/tasks/:taskId`, {taskId: id}));
|
|
406
|
+
navigate(generatePath(`${rootLink()}/tasks/:taskId`, { taskId: id }));
|
|
379
407
|
};
|
|
380
408
|
if (error) {
|
|
381
409
|
errorApi.post(new Error(`Failed to load template, ${error}`));
|
|
@@ -389,8 +417,8 @@ const TemplatePage = ({
|
|
|
389
417
|
to: rootLink()
|
|
390
418
|
});
|
|
391
419
|
}
|
|
392
|
-
const customFieldComponents = Object.fromEntries(customFieldExtensions.map(({name, component}) => [name, component]));
|
|
393
|
-
const customFieldValidators = Object.fromEntries(customFieldExtensions.map(({name, validation}) => [name, validation]));
|
|
420
|
+
const customFieldComponents = Object.fromEntries(customFieldExtensions.map(({ name, component }) => [name, component]));
|
|
421
|
+
const customFieldValidators = Object.fromEntries(customFieldExtensions.map(({ name, validation }) => [name, validation]));
|
|
394
422
|
return /* @__PURE__ */ React.createElement(Page, {
|
|
395
423
|
themeId: "home"
|
|
396
424
|
}, /* @__PURE__ */ React.createElement(Header, {
|
|
@@ -404,7 +432,7 @@ const TemplatePage = ({
|
|
|
404
432
|
}), schema && /* @__PURE__ */ React.createElement(InfoCard, {
|
|
405
433
|
title: schema.title,
|
|
406
434
|
noPadding: true,
|
|
407
|
-
titleTypographyProps: {component: "h2"}
|
|
435
|
+
titleTypographyProps: { component: "h2" }
|
|
408
436
|
}, /* @__PURE__ */ React.createElement(MultistepJsonForm, {
|
|
409
437
|
formData: formState,
|
|
410
438
|
fields: customFieldComponents,
|
|
@@ -414,7 +442,7 @@ const TemplatePage = ({
|
|
|
414
442
|
steps: schema.steps.map((step) => {
|
|
415
443
|
return {
|
|
416
444
|
...step,
|
|
417
|
-
validate: createValidator(step.schema, customFieldValidators, {apiHolder})
|
|
445
|
+
validate: createValidator(step.schema, customFieldValidators, { apiHolder })
|
|
418
446
|
};
|
|
419
447
|
})
|
|
420
448
|
}))));
|
|
@@ -425,7 +453,7 @@ function reducer(draft, action) {
|
|
|
425
453
|
switch (action.type) {
|
|
426
454
|
case "INIT": {
|
|
427
455
|
draft.steps = action.data.spec.steps.reduce((current, next) => {
|
|
428
|
-
current[next.id] = {status: "open", id: next.id};
|
|
456
|
+
current[next.id] = { status: "open", id: next.id };
|
|
429
457
|
return current;
|
|
430
458
|
}, {});
|
|
431
459
|
draft.stepLogs = action.data.spec.steps.reduce((current, next) => {
|
|
@@ -491,13 +519,13 @@ const useTaskEventStream = (taskId) => {
|
|
|
491
519
|
if (didCancel) {
|
|
492
520
|
return;
|
|
493
521
|
}
|
|
494
|
-
dispatch({type: "INIT", data: task});
|
|
495
|
-
const observable = scaffolderApi.streamLogs({taskId});
|
|
522
|
+
dispatch({ type: "INIT", data: task });
|
|
523
|
+
const observable = scaffolderApi.streamLogs({ taskId });
|
|
496
524
|
const collectedLogEvents = new Array();
|
|
497
525
|
function emitLogs() {
|
|
498
526
|
if (collectedLogEvents.length) {
|
|
499
527
|
const logs = collectedLogEvents.splice(0, collectedLogEvents.length);
|
|
500
|
-
dispatch({type: "LOGS", data: logs});
|
|
528
|
+
dispatch({ type: "LOGS", data: logs });
|
|
501
529
|
}
|
|
502
530
|
}
|
|
503
531
|
logPusher = setInterval(emitLogs, 500);
|
|
@@ -508,7 +536,7 @@ const useTaskEventStream = (taskId) => {
|
|
|
508
536
|
return collectedLogEvents.push(event);
|
|
509
537
|
case "completion":
|
|
510
538
|
emitLogs();
|
|
511
|
-
dispatch({type: "COMPLETED", data: event});
|
|
539
|
+
dispatch({ type: "COMPLETED", data: event });
|
|
512
540
|
return void 0;
|
|
513
541
|
default:
|
|
514
542
|
throw new Error(`Unhandled event type ${event.type} in observer`);
|
|
@@ -516,12 +544,12 @@ const useTaskEventStream = (taskId) => {
|
|
|
516
544
|
},
|
|
517
545
|
error: (error) => {
|
|
518
546
|
emitLogs();
|
|
519
|
-
dispatch({type: "ERROR", data: error});
|
|
547
|
+
dispatch({ type: "ERROR", data: error });
|
|
520
548
|
}
|
|
521
549
|
});
|
|
522
550
|
}, (error) => {
|
|
523
551
|
if (!didCancel) {
|
|
524
|
-
dispatch({type: "ERROR", data: error});
|
|
552
|
+
dispatch({ type: "ERROR", data: error });
|
|
525
553
|
}
|
|
526
554
|
});
|
|
527
555
|
return () => {
|
|
@@ -548,7 +576,7 @@ const useStyles$2 = makeStyles({
|
|
|
548
576
|
}
|
|
549
577
|
});
|
|
550
578
|
const IconLink = (props) => {
|
|
551
|
-
const {href, text, Icon, ...linkProps} = props;
|
|
579
|
+
const { href, text, Icon, ...linkProps } = props;
|
|
552
580
|
const classes = useStyles$2();
|
|
553
581
|
return /* @__PURE__ */ React.createElement(Grid, {
|
|
554
582
|
container: true,
|
|
@@ -567,9 +595,9 @@ const IconLink = (props) => {
|
|
|
567
595
|
}, text || href)));
|
|
568
596
|
};
|
|
569
597
|
|
|
570
|
-
const TaskPageLinks = ({output}) => {
|
|
571
|
-
const {entityRef: entityRefOutput, remoteUrl} = output;
|
|
572
|
-
let {links = []} = output;
|
|
598
|
+
const TaskPageLinks = ({ output }) => {
|
|
599
|
+
const { entityRef: entityRefOutput, remoteUrl } = output;
|
|
600
|
+
let { links = [] } = output;
|
|
573
601
|
const app = useApp();
|
|
574
602
|
const entityRoute = useRouteRef(entityRouteRef);
|
|
575
603
|
const iconResolver = (key) => {
|
|
@@ -577,7 +605,7 @@ const TaskPageLinks = ({output}) => {
|
|
|
577
605
|
return key ? (_a = app.getSystemIcon(key)) != null ? _a : LanguageIcon : LanguageIcon;
|
|
578
606
|
};
|
|
579
607
|
if (remoteUrl) {
|
|
580
|
-
links = [{url: remoteUrl, title: "Repo"}, ...links];
|
|
608
|
+
links = [{ url: remoteUrl, title: "Repo" }, ...links];
|
|
581
609
|
}
|
|
582
610
|
if (entityRefOutput) {
|
|
583
611
|
links = [
|
|
@@ -592,14 +620,14 @@ const TaskPageLinks = ({output}) => {
|
|
|
592
620
|
return /* @__PURE__ */ React.createElement(Box, {
|
|
593
621
|
px: 3,
|
|
594
622
|
pb: 3
|
|
595
|
-
}, links.filter(({url, entityRef}) => url || entityRef).map(({url, entityRef, title, icon}) => {
|
|
623
|
+
}, links.filter(({ url, entityRef }) => url || entityRef).map(({ url, entityRef, title, icon }) => {
|
|
596
624
|
if (entityRef) {
|
|
597
625
|
const entityName = parseEntityName(entityRef);
|
|
598
626
|
const target = entityRoute(entityName);
|
|
599
|
-
return {title, icon, url: target};
|
|
627
|
+
return { title, icon, url: target };
|
|
600
628
|
}
|
|
601
|
-
return {title, icon, url};
|
|
602
|
-
}).map(({url, title, icon}, i) => /* @__PURE__ */ React.createElement(IconLink, {
|
|
629
|
+
return { title, icon, url };
|
|
630
|
+
}).map(({ url, title, icon }, i) => /* @__PURE__ */ React.createElement(IconLink, {
|
|
603
631
|
key: `output-link-${i}`,
|
|
604
632
|
href: url,
|
|
605
633
|
text: title != null ? title : url,
|
|
@@ -608,7 +636,6 @@ const TaskPageLinks = ({output}) => {
|
|
|
608
636
|
})));
|
|
609
637
|
};
|
|
610
638
|
|
|
611
|
-
const LazyLog = React.lazy(() => import('react-lazylog/build/LazyLog'));
|
|
612
639
|
const humanizeDuration = require("humanize-duration");
|
|
613
640
|
const useStyles$1 = makeStyles$1((theme) => createStyles({
|
|
614
641
|
root: {
|
|
@@ -634,7 +661,7 @@ const useStyles$1 = makeStyles$1((theme) => createStyles({
|
|
|
634
661
|
width: "100%"
|
|
635
662
|
}
|
|
636
663
|
}));
|
|
637
|
-
const StepTimeTicker = ({step}) => {
|
|
664
|
+
const StepTimeTicker = ({ step }) => {
|
|
638
665
|
const [time, setTime] = useState("");
|
|
639
666
|
useInterval(() => {
|
|
640
667
|
if (!step.startedAt) {
|
|
@@ -644,7 +671,7 @@ const StepTimeTicker = ({step}) => {
|
|
|
644
671
|
const end = step.endedAt ? DateTime.fromISO(step.endedAt) : DateTime.local();
|
|
645
672
|
const startedAt = DateTime.fromISO(step.startedAt);
|
|
646
673
|
const formatted = Interval.fromDateTimes(startedAt, end).toDuration().valueOf();
|
|
647
|
-
setTime(humanizeDuration(formatted, {round: true}));
|
|
674
|
+
setTime(humanizeDuration(formatted, { round: true }));
|
|
648
675
|
}, 1e3);
|
|
649
676
|
return /* @__PURE__ */ React.createElement(Typography$1, {
|
|
650
677
|
variant: "caption"
|
|
@@ -666,7 +693,7 @@ const useStepIconStyles = makeStyles$1((theme) => createStyles({
|
|
|
666
693
|
}));
|
|
667
694
|
function TaskStepIconComponent(props) {
|
|
668
695
|
const classes = useStepIconStyles();
|
|
669
|
-
const {active, completed, error} = props;
|
|
696
|
+
const { active, completed, error } = props;
|
|
670
697
|
const getMiddle = () => {
|
|
671
698
|
if (active) {
|
|
672
699
|
return /* @__PURE__ */ React.createElement(CircularProgress, {
|
|
@@ -729,24 +756,11 @@ const TaskStatusStepper = memo(({
|
|
|
729
756
|
})))));
|
|
730
757
|
})));
|
|
731
758
|
});
|
|
732
|
-
const
|
|
733
|
-
return /* @__PURE__ */ React.createElement(Suspense, {
|
|
734
|
-
fallback: /* @__PURE__ */ React.createElement(Progress, null)
|
|
735
|
-
}, /* @__PURE__ */ React.createElement("div", {
|
|
736
|
-
style: {height: "80vh"}
|
|
737
|
-
}, /* @__PURE__ */ React.createElement(LazyLog, {
|
|
738
|
-
text: log,
|
|
739
|
-
extraLines: 1,
|
|
740
|
-
follow: true,
|
|
741
|
-
selectableLines: true,
|
|
742
|
-
enableSearch: true
|
|
743
|
-
})));
|
|
744
|
-
});
|
|
745
|
-
const hasLinks = ({entityRef, remoteUrl, links = []}) => !!(entityRef || remoteUrl || links.length > 0);
|
|
759
|
+
const hasLinks = ({ entityRef, remoteUrl, links = [] }) => !!(entityRef || remoteUrl || links.length > 0);
|
|
746
760
|
const TaskPage = () => {
|
|
747
761
|
const [userSelectedStepId, setUserSelectedStepId] = useState(void 0);
|
|
748
762
|
const [lastActiveStepId, setLastActiveStepId] = useState(void 0);
|
|
749
|
-
const {taskId} = useParams$1();
|
|
763
|
+
const { taskId } = useParams$1();
|
|
750
764
|
const taskStream = useTaskEventStream(taskId);
|
|
751
765
|
const completed = taskStream.completed;
|
|
752
766
|
const steps = useMemo(() => {
|
|
@@ -780,7 +794,7 @@ const TaskPage = () => {
|
|
|
780
794
|
return log.join("\n");
|
|
781
795
|
}, [taskStream.stepLogs, currentStepId]);
|
|
782
796
|
const taskNotFound = taskStream.completed === true && taskStream.loading === false && !taskStream.task;
|
|
783
|
-
const {output} = taskStream;
|
|
797
|
+
const { output } = taskStream;
|
|
784
798
|
return /* @__PURE__ */ React.createElement(Page, {
|
|
785
799
|
themeId: "home"
|
|
786
800
|
}, /* @__PURE__ */ React.createElement(Header, {
|
|
@@ -808,9 +822,11 @@ const TaskPage = () => {
|
|
|
808
822
|
}))), /* @__PURE__ */ React.createElement(Grid$1, {
|
|
809
823
|
item: true,
|
|
810
824
|
xs: 9
|
|
811
|
-
}, /* @__PURE__ */ React.createElement(
|
|
812
|
-
|
|
813
|
-
}
|
|
825
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
826
|
+
style: { height: "80vh" }
|
|
827
|
+
}, /* @__PURE__ */ React.createElement(LogViewer, {
|
|
828
|
+
text: logAsString
|
|
829
|
+
})))))));
|
|
814
830
|
};
|
|
815
831
|
|
|
816
832
|
const useStyles = makeStyles((theme) => ({
|
|
@@ -837,7 +853,7 @@ const useStyles = makeStyles((theme) => ({
|
|
|
837
853
|
const ActionsPage = () => {
|
|
838
854
|
const api = useApi(scaffolderApiRef);
|
|
839
855
|
const classes = useStyles();
|
|
840
|
-
const {loading, value, error} = useAsync(async () => {
|
|
856
|
+
const { loading, value, error } = useAsync(async () => {
|
|
841
857
|
return api.listActions();
|
|
842
858
|
});
|
|
843
859
|
if (loading) {
|
|
@@ -921,7 +937,7 @@ const ActionsPage = () => {
|
|
|
921
937
|
}), /* @__PURE__ */ React.createElement(Content, null, items));
|
|
922
938
|
};
|
|
923
939
|
|
|
924
|
-
const Router = ({TemplateCardComponent}) => {
|
|
940
|
+
const Router = ({ TemplateCardComponent, groups }) => {
|
|
925
941
|
const outlet = useOutlet();
|
|
926
942
|
const customFieldExtensions = useElementFilter(outlet, (elements) => elements.selectByComponentData({
|
|
927
943
|
key: FIELD_EXTENSION_WRAPPER_KEY
|
|
@@ -930,12 +946,13 @@ const Router = ({TemplateCardComponent}) => {
|
|
|
930
946
|
}));
|
|
931
947
|
const fieldExtensions = [
|
|
932
948
|
...customFieldExtensions,
|
|
933
|
-
...DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS.filter(({name}) => !customFieldExtensions.some((customFieldExtension) => customFieldExtension.name === name))
|
|
949
|
+
...DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS.filter(({ name }) => !customFieldExtensions.some((customFieldExtension) => customFieldExtension.name === name))
|
|
934
950
|
];
|
|
935
951
|
return /* @__PURE__ */ React.createElement(Routes, null, /* @__PURE__ */ React.createElement(Route, {
|
|
936
952
|
path: "/",
|
|
937
953
|
element: /* @__PURE__ */ React.createElement(ScaffolderPage, {
|
|
938
|
-
TemplateCardComponent
|
|
954
|
+
TemplateCardComponent,
|
|
955
|
+
groups
|
|
939
956
|
})
|
|
940
957
|
}), /* @__PURE__ */ React.createElement(Route, {
|
|
941
958
|
path: "/templates/:templateName",
|
|
@@ -952,4 +969,4 @@ const Router = ({TemplateCardComponent}) => {
|
|
|
952
969
|
};
|
|
953
970
|
|
|
954
971
|
export { Router };
|
|
955
|
-
//# sourceMappingURL=Router-
|
|
972
|
+
//# sourceMappingURL=Router-6eee3fa9.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Router-6eee3fa9.esm.js","sources":["../../src/extensions/default.ts","../../src/components/ScaffolderPage/ScaffolderPage.tsx","../../src/components/MultistepJsonForm/schema.ts","../../src/components/MultistepJsonForm/FieldOverrides/DescriptionField.tsx","../../src/components/MultistepJsonForm/MultistepJsonForm.tsx","../../src/components/TemplatePage/TemplatePage.tsx","../../src/components/hooks/useEventStream.ts","../../src/components/TaskPage/IconLink.tsx","../../src/components/TaskPage/TaskPageLinks.tsx","../../src/components/TaskPage/TaskPage.tsx","../../src/components/ActionsPage/ActionsPage.tsx","../../src/components/Router.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { EntityPicker } from '../components/fields/EntityPicker';\nimport {\n EntityNamePicker,\n entityNamePickerValidation,\n} from '../components/fields/EntityNamePicker';\nimport { OwnerPicker } from '../components/fields/OwnerPicker';\nimport {\n repoPickerValidation,\n RepoUrlPicker,\n} from '../components/fields/RepoUrlPicker';\nimport { FieldExtensionOptions } from './types';\nimport { OwnedEntityPicker } from '../components/fields/OwnedEntityPicker';\n\nexport const DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS: FieldExtensionOptions[] = [\n {\n component: EntityPicker,\n name: 'EntityPicker',\n },\n {\n component: EntityNamePicker,\n name: 'EntityNamePicker',\n validation: entityNamePickerValidation,\n },\n {\n component: RepoUrlPicker,\n name: 'RepoUrlPicker',\n validation: repoPickerValidation,\n },\n {\n component: OwnerPicker,\n name: 'OwnerPicker',\n },\n {\n component: OwnedEntityPicker,\n name: 'OwnedEntityPicker',\n },\n];\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Content,\n ContentHeader,\n CreateButton,\n Header,\n Lifecycle,\n Page,\n SupportButton,\n} from '@backstage/core-components';\nimport { TemplateEntityV1beta2, Entity } from '@backstage/catalog-model';\nimport { useRouteRef } from '@backstage/core-plugin-api';\nimport {\n EntityKindPicker,\n EntityListProvider,\n EntitySearchBar,\n EntityTagPicker,\n UserListPicker,\n} from '@backstage/plugin-catalog-react';\nimport { makeStyles } from '@material-ui/core';\nimport React, { ComponentType } from 'react';\nimport { registerComponentRouteRef } from '../../routes';\nimport { TemplateList } from '../TemplateList';\nimport { TemplateTypePicker } from '../TemplateTypePicker';\n\nconst useStyles = makeStyles(theme => ({\n contentWrapper: {\n display: 'grid',\n gridTemplateAreas: \"'filters' 'grid'\",\n gridTemplateColumns: '250px 1fr',\n gridColumnGap: theme.spacing(2),\n },\n}));\n\nexport type ScaffolderPageProps = {\n TemplateCardComponent?:\n | ComponentType<{ template: TemplateEntityV1beta2 }>\n | undefined;\n groups?: Array<{\n title?: string;\n titleComponent?: React.ReactNode;\n filter: (entity: Entity) => boolean;\n }>;\n};\n\nexport const ScaffolderPageContents = ({\n TemplateCardComponent,\n groups,\n}: ScaffolderPageProps) => {\n const styles = useStyles();\n const registerComponentLink = useRouteRef(registerComponentRouteRef);\n const otherTemplatesGroup = {\n title: groups ? 'Other Templates' : 'Templates',\n filter: (entity: Entity) => {\n const filtered = (groups ?? []).map(group => group.filter(entity));\n return !filtered.some(result => result === true);\n },\n };\n\n return (\n <Page themeId=\"home\">\n <Header\n pageTitleOverride=\"Create a New Component\"\n title={\n <>\n Create a New Component <Lifecycle shorthand />\n </>\n }\n subtitle=\"Create new software components using standard templates\"\n />\n <Content>\n <ContentHeader title=\"Available Templates\">\n <CreateButton\n title=\"Register Existing Component\"\n to={registerComponentLink && registerComponentLink()}\n />\n <SupportButton>\n Create new software components using standard templates. Different\n templates create different kinds of components (services, websites,\n documentation, ...).\n </SupportButton>\n </ContentHeader>\n\n <div className={styles.contentWrapper}>\n <div>\n <EntitySearchBar />\n <EntityKindPicker initialFilter=\"template\" hidden />\n <UserListPicker\n initialFilter=\"all\"\n availableFilters={['all', 'starred']}\n />\n <TemplateTypePicker />\n <EntityTagPicker />\n </div>\n <div>\n {groups &&\n groups.map((group, index) => (\n <TemplateList\n key={index}\n TemplateCardComponent={TemplateCardComponent}\n group={group}\n />\n ))}\n <TemplateList\n key=\"other\"\n TemplateCardComponent={TemplateCardComponent}\n group={otherTemplatesGroup}\n />\n </div>\n </div>\n </Content>\n </Page>\n );\n};\n\nexport const ScaffolderPage = ({\n TemplateCardComponent,\n groups,\n}: ScaffolderPageProps) => (\n <EntityListProvider>\n <ScaffolderPageContents\n TemplateCardComponent={TemplateCardComponent}\n groups={groups}\n />\n </EntityListProvider>\n);\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { JsonObject } from '@backstage/types';\nimport { FormProps } from '@rjsf/core';\n\nfunction isObject(value: unknown): value is JsonObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction extractUiSchema(schema: JsonObject, uiSchema: JsonObject) {\n if (!isObject(schema)) {\n return;\n }\n\n const { properties, items, anyOf, oneOf, allOf, dependencies } = schema;\n\n for (const propName in schema) {\n if (!schema.hasOwnProperty(propName)) {\n continue;\n }\n\n if (propName.startsWith('ui:')) {\n uiSchema[propName] = schema[propName];\n delete schema[propName];\n }\n }\n\n if (isObject(properties)) {\n for (const propName in properties) {\n if (!properties.hasOwnProperty(propName)) {\n continue;\n }\n\n const schemaNode = properties[propName];\n if (!isObject(schemaNode)) {\n continue;\n }\n const innerUiSchema = {};\n uiSchema[propName] = innerUiSchema;\n extractUiSchema(schemaNode, innerUiSchema);\n }\n }\n\n if (isObject(items)) {\n const innerUiSchema = {};\n uiSchema.items = innerUiSchema;\n extractUiSchema(items, innerUiSchema);\n }\n\n if (Array.isArray(anyOf)) {\n for (const schemaNode of anyOf) {\n if (!isObject(schemaNode)) {\n continue;\n }\n extractUiSchema(schemaNode, uiSchema);\n }\n }\n\n if (Array.isArray(oneOf)) {\n for (const schemaNode of oneOf) {\n if (!isObject(schemaNode)) {\n continue;\n }\n extractUiSchema(schemaNode, uiSchema);\n }\n }\n\n if (Array.isArray(allOf)) {\n for (const schemaNode of allOf) {\n if (!isObject(schemaNode)) {\n continue;\n }\n extractUiSchema(schemaNode, uiSchema);\n }\n }\n\n if (isObject(dependencies)) {\n for (const depName of Object.keys(dependencies)) {\n const schemaNode = dependencies[depName];\n if (!isObject(schemaNode)) {\n continue;\n }\n extractUiSchema(schemaNode, uiSchema);\n }\n }\n}\n\nexport function transformSchemaToProps(inputSchema: JsonObject): {\n schema: FormProps<any>['schema'];\n uiSchema: FormProps<any>['uiSchema'];\n} {\n inputSchema.type = inputSchema.type || 'object';\n const schema = JSON.parse(JSON.stringify(inputSchema));\n delete schema.title; // Rendered separately\n const uiSchema = {};\n extractUiSchema(schema, uiSchema);\n return { schema, uiSchema };\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React from 'react';\nimport { MarkdownContent } from '@backstage/core-components';\nimport { FieldProps } from '@rjsf/core';\n\nexport const DescriptionField = ({ description }: FieldProps) =>\n description && <MarkdownContent content={description} />;\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { JsonObject } from '@backstage/types';\nimport {\n Box,\n Button,\n Paper,\n Step as StepUI,\n StepContent,\n StepLabel,\n Stepper,\n Typography,\n} from '@material-ui/core';\nimport { errorApiRef, useApi } from '@backstage/core-plugin-api';\nimport { FormProps, IChangeEvent, UiSchema, withTheme } from '@rjsf/core';\nimport { Theme as MuiTheme } from '@rjsf/material-ui';\nimport React, { useState } from 'react';\nimport { transformSchemaToProps } from './schema';\nimport { Content, StructuredMetadataTable } from '@backstage/core-components';\nimport * as fieldOverrides from './FieldOverrides';\n\nconst Form = withTheme(MuiTheme);\ntype Step = {\n schema: JsonObject;\n title: string;\n} & Partial<Omit<FormProps<any>, 'schema'>>;\n\ntype Props = {\n /**\n * Steps for the form, each contains title and form schema\n */\n steps: Step[];\n formData: Record<string, any>;\n onChange: (e: IChangeEvent) => void;\n onReset: () => void;\n onFinish: () => Promise<void>;\n widgets?: FormProps<any>['widgets'];\n fields?: FormProps<any>['fields'];\n};\n\nexport function getUiSchemasFromSteps(steps: Step[]): UiSchema[] {\n const uiSchemas: Array<UiSchema> = [];\n steps.forEach(step => {\n const schemaProps = step.schema.properties as JsonObject;\n for (const key in schemaProps) {\n if (schemaProps.hasOwnProperty(key)) {\n const uiSchema = schemaProps[key] as UiSchema;\n uiSchema.name = key;\n uiSchemas.push(uiSchema);\n }\n }\n });\n return uiSchemas;\n}\n\nexport function getReviewData(formData: Record<string, any>, steps: Step[]) {\n const uiSchemas = getUiSchemasFromSteps(steps);\n const reviewData: Record<string, any> = {};\n for (const key in formData) {\n if (formData.hasOwnProperty(key)) {\n const uiSchema = uiSchemas.find(us => us.name === key);\n\n if (!uiSchema) {\n reviewData[key] = formData[key];\n continue;\n }\n\n if (uiSchema['ui:widget'] === 'password') {\n reviewData[key] = '******';\n continue;\n }\n\n if (!uiSchema['ui:backstage'] || !uiSchema['ui:backstage'].review) {\n reviewData[key] = formData[key];\n continue;\n }\n\n const review = uiSchema['ui:backstage'].review as JsonObject;\n if (!review.show) {\n continue;\n }\n\n if (review.mask) {\n reviewData[key] = review.mask;\n continue;\n }\n reviewData[key] = formData[key];\n }\n }\n\n return reviewData;\n}\n\nexport const MultistepJsonForm = ({\n steps,\n formData,\n onChange,\n onReset,\n onFinish,\n fields,\n widgets,\n}: Props) => {\n const [activeStep, setActiveStep] = useState(0);\n const [disableButtons, setDisableButtons] = useState(false);\n const errorApi = useApi(errorApiRef);\n\n const handleReset = () => {\n setActiveStep(0);\n onReset();\n };\n const handleNext = () => {\n setActiveStep(Math.min(activeStep + 1, steps.length));\n };\n const handleBack = () => setActiveStep(Math.max(activeStep - 1, 0));\n const handleCreate = async () => {\n setDisableButtons(true);\n try {\n await onFinish();\n } catch (err) {\n setDisableButtons(false);\n errorApi.post(err);\n }\n };\n\n return (\n <>\n <Stepper activeStep={activeStep} orientation=\"vertical\">\n {steps.map(({ title, schema, ...formProps }, index) => {\n return (\n <StepUI key={title}>\n <StepLabel\n aria-label={`Step ${index + 1} ${title}`}\n aria-disabled=\"false\"\n tabIndex={0}\n >\n <Typography variant=\"h6\" component=\"h3\">\n {title}\n </Typography>\n </StepLabel>\n <StepContent key={title}>\n <Form\n showErrorList={false}\n fields={{ ...fieldOverrides, ...fields }}\n widgets={widgets}\n noHtml5Validate\n formData={formData}\n onChange={onChange}\n onSubmit={e => {\n if (e.errors.length === 0) handleNext();\n }}\n {...formProps}\n {...transformSchemaToProps(schema)}\n >\n <Button disabled={activeStep === 0} onClick={handleBack}>\n Back\n </Button>\n <Button variant=\"contained\" color=\"primary\" type=\"submit\">\n Next step\n </Button>\n </Form>\n </StepContent>\n </StepUI>\n );\n })}\n </Stepper>\n {activeStep === steps.length && (\n <Content>\n <Paper square elevation={0}>\n <Typography variant=\"h6\">Review and create</Typography>\n <StructuredMetadataTable\n dense\n metadata={getReviewData(formData, steps)}\n />\n <Box mb={4} />\n <Button onClick={handleBack} disabled={disableButtons}>\n Back\n </Button>\n <Button onClick={handleReset} disabled={disableButtons}>\n Reset\n </Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleCreate}\n disabled={disableButtons}\n >\n Create\n </Button>\n </Paper>\n </Content>\n )}\n </>\n );\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { JsonObject, JsonValue } from '@backstage/types';\nimport { LinearProgress } from '@material-ui/core';\nimport { FormValidation, IChangeEvent } from '@rjsf/core';\nimport React, { useCallback, useState } from 'react';\nimport { generatePath, Navigate, useNavigate } from 'react-router';\nimport { useParams } from 'react-router-dom';\nimport { useAsync } from 'react-use';\nimport { scaffolderApiRef } from '../../api';\nimport { CustomFieldValidator, FieldExtensionOptions } from '../../extensions';\nimport { rootRouteRef } from '../../routes';\nimport { MultistepJsonForm } from '../MultistepJsonForm';\n\nimport {\n Content,\n Header,\n InfoCard,\n Lifecycle,\n Page,\n} from '@backstage/core-components';\nimport {\n ApiHolder,\n errorApiRef,\n useApi,\n useApiHolder,\n useRouteRef,\n} from '@backstage/core-plugin-api';\n\nconst useTemplateParameterSchema = (templateName: string) => {\n const scaffolderApi = useApi(scaffolderApiRef);\n const { value, loading, error } = useAsync(\n () =>\n scaffolderApi.getTemplateParameterSchema({\n name: templateName,\n kind: 'template',\n namespace: 'default',\n }),\n [scaffolderApi, templateName],\n );\n return { schema: value, loading, error };\n};\n\nfunction isObject(obj: unknown): obj is JsonObject {\n return typeof obj === 'object' && obj !== null && !Array.isArray(obj);\n}\n\nexport const createValidator = (\n rootSchema: JsonObject,\n validators: Record<string, undefined | CustomFieldValidator<unknown>>,\n context: {\n apiHolder: ApiHolder;\n },\n) => {\n function validate(\n schema: JsonObject,\n formData: JsonObject,\n errors: FormValidation,\n ) {\n const schemaProps = schema.properties;\n if (!isObject(schemaProps)) {\n return;\n }\n\n for (const [key, propData] of Object.entries(formData)) {\n const propValidation = errors[key];\n\n if (isObject(propData)) {\n const propSchemaProps = schemaProps[key];\n if (isObject(propSchemaProps)) {\n validate(\n propSchemaProps,\n propData as JsonObject,\n propValidation as FormValidation,\n );\n }\n } else {\n const propSchema = schemaProps[key];\n const fieldName =\n isObject(propSchema) && (propSchema['ui:field'] as string);\n if (fieldName && typeof validators[fieldName] === 'function') {\n validators[fieldName]!(\n propData as JsonValue,\n propValidation,\n context,\n );\n }\n }\n }\n }\n\n return (formData: JsonObject, errors: FormValidation) => {\n validate(rootSchema, formData, errors);\n return errors;\n };\n};\n\nexport const TemplatePage = ({\n customFieldExtensions = [],\n}: {\n customFieldExtensions?: FieldExtensionOptions[];\n}) => {\n const apiHolder = useApiHolder();\n const errorApi = useApi(errorApiRef);\n const scaffolderApi = useApi(scaffolderApiRef);\n const { templateName } = useParams();\n const navigate = useNavigate();\n const rootLink = useRouteRef(rootRouteRef);\n const { schema, loading, error } = useTemplateParameterSchema(templateName);\n const [formState, setFormState] = useState({});\n const handleFormReset = () => setFormState({});\n const handleChange = useCallback(\n (e: IChangeEvent) => setFormState(e.formData),\n [setFormState],\n );\n\n const handleCreate = async () => {\n const id = await scaffolderApi.scaffold(templateName, formState);\n navigate(generatePath(`${rootLink()}/tasks/:taskId`, { taskId: id }));\n };\n\n if (error) {\n errorApi.post(new Error(`Failed to load template, ${error}`));\n return <Navigate to={rootLink()} />;\n }\n if (!loading && !schema) {\n errorApi.post(new Error('Template was not found.'));\n return <Navigate to={rootLink()} />;\n }\n\n const customFieldComponents = Object.fromEntries(\n customFieldExtensions.map(({ name, component }) => [name, component]),\n );\n\n const customFieldValidators = Object.fromEntries(\n customFieldExtensions.map(({ name, validation }) => [name, validation]),\n );\n\n return (\n <Page themeId=\"home\">\n <Header\n pageTitleOverride=\"Create a New Component\"\n title={\n <>\n Create a New Component <Lifecycle shorthand />\n </>\n }\n subtitle=\"Create new software components using standard templates\"\n />\n <Content>\n {loading && <LinearProgress data-testid=\"loading-progress\" />}\n {schema && (\n <InfoCard\n title={schema.title}\n noPadding\n titleTypographyProps={{ component: 'h2' }}\n >\n <MultistepJsonForm\n formData={formState}\n fields={customFieldComponents}\n onChange={handleChange}\n onReset={handleFormReset}\n onFinish={handleCreate}\n steps={schema.steps.map(step => {\n return {\n ...step,\n validate: createValidator(\n step.schema,\n customFieldValidators,\n { apiHolder },\n ),\n };\n })}\n />\n </InfoCard>\n )}\n </Content>\n </Page>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useImmerReducer } from 'use-immer';\nimport { useEffect } from 'react';\nimport { scaffolderApiRef, LogEvent } from '../../api';\nimport { ScaffolderTask, Status, TaskOutput } from '../../types';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { Subscription } from '@backstage/types';\n\ntype Step = {\n id: string;\n status: Status;\n endedAt?: string;\n startedAt?: string;\n};\n\nexport type TaskStream = {\n loading: boolean;\n error?: Error;\n stepLogs: { [stepId in string]: string[] };\n completed: boolean;\n task?: ScaffolderTask;\n steps: { [stepId in string]: Step };\n output?: TaskOutput;\n};\n\ntype ReducerLogEntry = {\n createdAt: string;\n body: {\n stepId?: string;\n status?: Status;\n message: string;\n output?: TaskOutput;\n };\n};\n\ntype ReducerAction =\n | { type: 'INIT'; data: ScaffolderTask }\n | { type: 'LOGS'; data: ReducerLogEntry[] }\n | { type: 'COMPLETED'; data: ReducerLogEntry }\n | { type: 'ERROR'; data: Error };\n\nfunction reducer(draft: TaskStream, action: ReducerAction) {\n switch (action.type) {\n case 'INIT': {\n draft.steps = action.data.spec.steps.reduce((current, next) => {\n current[next.id] = { status: 'open', id: next.id };\n return current;\n }, {} as { [stepId in string]: Step });\n draft.stepLogs = action.data.spec.steps.reduce((current, next) => {\n current[next.id] = [];\n return current;\n }, {} as { [stepId in string]: string[] });\n draft.loading = false;\n draft.error = undefined;\n draft.completed = false;\n draft.task = action.data;\n return;\n }\n\n case 'LOGS': {\n const entries = action.data;\n const logLines = [];\n\n for (const entry of entries) {\n const logLine = `${entry.createdAt} ${entry.body.message}`;\n logLines.push(logLine);\n\n if (!entry.body.stepId || !draft.steps?.[entry.body.stepId]) {\n continue;\n }\n\n const currentStepLog = draft.stepLogs?.[entry.body.stepId];\n const currentStep = draft.steps?.[entry.body.stepId];\n\n if (entry.body.status && entry.body.status !== currentStep.status) {\n currentStep.status = entry.body.status;\n\n if (currentStep.status === 'processing') {\n currentStep.startedAt = entry.createdAt;\n }\n\n if (\n ['cancelled', 'failed', 'completed'].includes(currentStep.status)\n ) {\n currentStep.endedAt = entry.createdAt;\n }\n }\n\n currentStepLog?.push(logLine);\n }\n\n return;\n }\n\n case 'COMPLETED': {\n draft.completed = true;\n draft.output = action.data.body.output;\n return;\n }\n\n case 'ERROR': {\n draft.error = action.data;\n draft.loading = false;\n draft.completed = true;\n return;\n }\n\n default:\n return;\n }\n}\n\nexport const useTaskEventStream = (taskId: string): TaskStream => {\n const scaffolderApi = useApi(scaffolderApiRef);\n const [state, dispatch] = useImmerReducer(reducer, {\n loading: true,\n completed: false,\n stepLogs: {} as { [stepId in string]: string[] },\n steps: {} as { [stepId in string]: Step },\n });\n\n useEffect(() => {\n let didCancel = false;\n let subscription: Subscription | undefined;\n let logPusher: NodeJS.Timeout | undefined;\n\n scaffolderApi.getTask(taskId).then(\n task => {\n if (didCancel) {\n return;\n }\n dispatch({ type: 'INIT', data: task });\n\n // TODO(blam): Use a normal fetch to fetch the current log for the event stream\n // and use that for an INIT_EVENTs dispatch event, and then\n // use the last event ID to subscribe using after option to\n // stream logs. Without this, if you have a lot of logs, it can look like the\n // task is being rebuilt on load as it progresses through the steps at a slower\n // rate whilst it builds the status from the event logs\n const observable = scaffolderApi.streamLogs({ taskId });\n\n const collectedLogEvents = new Array<LogEvent>();\n\n function emitLogs() {\n if (collectedLogEvents.length) {\n const logs = collectedLogEvents.splice(\n 0,\n collectedLogEvents.length,\n );\n dispatch({ type: 'LOGS', data: logs });\n }\n }\n\n logPusher = setInterval(emitLogs, 500);\n\n subscription = observable.subscribe({\n next: event => {\n switch (event.type) {\n case 'log':\n return collectedLogEvents.push(event);\n case 'completion':\n emitLogs();\n dispatch({ type: 'COMPLETED', data: event });\n return undefined;\n default:\n throw new Error(\n `Unhandled event type ${event.type} in observer`,\n );\n }\n },\n error: error => {\n emitLogs();\n dispatch({ type: 'ERROR', data: error });\n },\n });\n },\n error => {\n if (!didCancel) {\n dispatch({ type: 'ERROR', data: error });\n }\n },\n );\n\n return () => {\n didCancel = true;\n if (subscription) {\n subscription.unsubscribe();\n }\n if (logPusher) {\n clearInterval(logPusher);\n }\n };\n }, [scaffolderApi, dispatch, taskId]);\n\n return state;\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React from 'react';\nimport { Grid, LinkProps, makeStyles, Typography } from '@material-ui/core';\nimport LanguageIcon from '@material-ui/icons/Language';\n\nimport { IconComponent } from '@backstage/core-plugin-api';\nimport { Link } from '@backstage/core-components';\n\nconst useStyles = makeStyles({\n svgIcon: {\n display: 'inline-block',\n '& svg': {\n display: 'inline-block',\n fontSize: 'inherit',\n verticalAlign: 'baseline',\n },\n },\n});\n\nexport const IconLink = (\n props: {\n href: string;\n text?: string;\n Icon?: IconComponent;\n } & LinkProps,\n) => {\n const { href, text, Icon, ...linkProps } = props;\n\n const classes = useStyles();\n\n return (\n <Grid container direction=\"row\" spacing={1}>\n <Grid item>\n <Typography component=\"div\" className={classes.svgIcon}>\n {Icon ? <Icon /> : <LanguageIcon />}\n </Typography>\n </Grid>\n <Grid item>\n <Link to={href} {...linkProps}>\n {text || href}\n </Link>\n </Grid>\n </Grid>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { parseEntityName } from '@backstage/catalog-model';\nimport { entityRouteRef } from '@backstage/plugin-catalog-react';\nimport { Box } from '@material-ui/core';\nimport LanguageIcon from '@material-ui/icons/Language';\nimport React from 'react';\nimport { TaskOutput } from '../../types';\nimport { IconLink } from './IconLink';\nimport { IconComponent, useApp, useRouteRef } from '@backstage/core-plugin-api';\n\ntype TaskPageLinksProps = {\n output: TaskOutput;\n};\n\nexport const TaskPageLinks = ({ output }: TaskPageLinksProps) => {\n const { entityRef: entityRefOutput, remoteUrl } = output;\n let { links = [] } = output;\n const app = useApp();\n const entityRoute = useRouteRef(entityRouteRef);\n\n const iconResolver = (key?: string): IconComponent =>\n key ? app.getSystemIcon(key) ?? LanguageIcon : LanguageIcon;\n\n if (remoteUrl) {\n links = [{ url: remoteUrl, title: 'Repo' }, ...links];\n }\n\n if (entityRefOutput) {\n links = [\n {\n entityRef: entityRefOutput,\n title: 'Open in catalog',\n icon: 'catalog',\n },\n ...links,\n ];\n }\n\n return (\n <Box px={3} pb={3}>\n {links\n .filter(({ url, entityRef }) => url || entityRef)\n .map(({ url, entityRef, title, icon }) => {\n if (entityRef) {\n const entityName = parseEntityName(entityRef);\n const target = entityRoute(entityName);\n return { title, icon, url: target };\n }\n return { title, icon, url: url! };\n })\n .map(({ url, title, icon }, i) => (\n <IconLink\n key={`output-link-${i}`}\n href={url}\n text={title ?? url}\n Icon={iconResolver(icon)}\n target=\"_blank\"\n />\n ))}\n </Box>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Content,\n ErrorPage,\n Header,\n Lifecycle,\n Page,\n LogViewer,\n} from '@backstage/core-components';\nimport { BackstageTheme } from '@backstage/theme';\nimport {\n CircularProgress,\n Paper,\n StepButton,\n StepIconProps,\n} from '@material-ui/core';\nimport Grid from '@material-ui/core/Grid';\nimport Step from '@material-ui/core/Step';\nimport StepLabel from '@material-ui/core/StepLabel';\nimport Stepper from '@material-ui/core/Stepper';\nimport { createStyles, makeStyles, Theme } from '@material-ui/core/styles';\nimport Typography from '@material-ui/core/Typography';\nimport Cancel from '@material-ui/icons/Cancel';\nimport Check from '@material-ui/icons/Check';\nimport FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord';\nimport classNames from 'classnames';\nimport { DateTime, Interval } from 'luxon';\nimport React, { memo, useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router';\nimport { useInterval } from 'react-use';\nimport { Status, TaskOutput } from '../../types';\nimport { useTaskEventStream } from '../hooks/useEventStream';\nimport { TaskPageLinks } from './TaskPageLinks';\n\n// typings are wrong for this library, so fallback to not parsing types.\nconst humanizeDuration = require('humanize-duration');\n\nconst useStyles = makeStyles((theme: Theme) =>\n createStyles({\n root: {\n width: '100%',\n },\n button: {\n marginTop: theme.spacing(1),\n marginRight: theme.spacing(1),\n },\n actionsContainer: {\n marginBottom: theme.spacing(2),\n },\n resetContainer: {\n padding: theme.spacing(3),\n },\n labelWrapper: {\n display: 'flex',\n flex: 1,\n flexDirection: 'row',\n justifyContent: 'space-between',\n },\n stepWrapper: {\n width: '100%',\n },\n }),\n);\n\ntype TaskStep = {\n id: string;\n name: string;\n status: Status;\n startedAt?: string;\n endedAt?: string;\n};\n\nconst StepTimeTicker = ({ step }: { step: TaskStep }) => {\n const [time, setTime] = useState('');\n\n useInterval(() => {\n if (!step.startedAt) {\n setTime('');\n return;\n }\n\n const end = step.endedAt\n ? DateTime.fromISO(step.endedAt)\n : DateTime.local();\n\n const startedAt = DateTime.fromISO(step.startedAt);\n const formatted = Interval.fromDateTimes(startedAt, end)\n .toDuration()\n .valueOf();\n\n setTime(humanizeDuration(formatted, { round: true }));\n }, 1000);\n\n return <Typography variant=\"caption\">{time}</Typography>;\n};\n\nconst useStepIconStyles = makeStyles((theme: BackstageTheme) =>\n createStyles({\n root: {\n color: theme.palette.text.disabled,\n display: 'flex',\n height: 22,\n alignItems: 'center',\n },\n completed: {\n color: theme.palette.status.ok,\n },\n error: {\n color: theme.palette.status.error,\n },\n }),\n);\n\nfunction TaskStepIconComponent(props: StepIconProps) {\n const classes = useStepIconStyles();\n const { active, completed, error } = props;\n\n const getMiddle = () => {\n if (active) {\n return <CircularProgress size=\"24px\" />;\n }\n if (completed) {\n return <Check />;\n }\n if (error) {\n return <Cancel />;\n }\n return <FiberManualRecordIcon />;\n };\n\n return (\n <div\n className={classNames(classes.root, {\n [classes.completed]: completed,\n [classes.error]: error,\n })}\n >\n {getMiddle()}\n </div>\n );\n}\n\nexport const TaskStatusStepper = memo(\n ({\n steps,\n currentStepId,\n onUserStepChange,\n }: {\n steps: TaskStep[];\n currentStepId: string | undefined;\n onUserStepChange: (id: string) => void;\n }) => {\n const classes = useStyles();\n\n return (\n <div className={classes.root}>\n <Stepper\n activeStep={steps.findIndex(s => s.id === currentStepId)}\n orientation=\"vertical\"\n nonLinear\n >\n {steps.map((step, index) => {\n const isCompleted = step.status === 'completed';\n const isFailed = step.status === 'failed';\n const isActive = step.status === 'processing';\n const isSkipped = step.status === 'skipped';\n\n return (\n <Step key={String(index)} expanded>\n <StepButton onClick={() => onUserStepChange(step.id)}>\n <StepLabel\n StepIconProps={{\n completed: isCompleted,\n error: isFailed,\n active: isActive,\n }}\n StepIconComponent={TaskStepIconComponent}\n className={classes.stepWrapper}\n >\n <div className={classes.labelWrapper}>\n <Typography variant=\"subtitle2\">{step.name}</Typography>\n {isSkipped ? (\n <Typography variant=\"caption\">Skipped</Typography>\n ) : (\n <StepTimeTicker step={step} />\n )}\n </div>\n </StepLabel>\n </StepButton>\n </Step>\n );\n })}\n </Stepper>\n </div>\n );\n },\n);\n\nconst hasLinks = ({ entityRef, remoteUrl, links = [] }: TaskOutput): boolean =>\n !!(entityRef || remoteUrl || links.length > 0);\n\nexport const TaskPage = () => {\n const [userSelectedStepId, setUserSelectedStepId] = useState<\n string | undefined\n >(undefined);\n const [lastActiveStepId, setLastActiveStepId] = useState<string | undefined>(\n undefined,\n );\n const { taskId } = useParams();\n const taskStream = useTaskEventStream(taskId);\n const completed = taskStream.completed;\n const steps = useMemo(\n () =>\n taskStream.task?.spec.steps.map(step => ({\n ...step,\n ...taskStream?.steps?.[step.id],\n })) ?? [],\n [taskStream],\n );\n\n useEffect(() => {\n const mostRecentFailedOrActiveStep = steps.find(step =>\n ['failed', 'processing'].includes(step.status),\n );\n if (completed && !mostRecentFailedOrActiveStep) {\n setLastActiveStepId(steps[steps.length - 1]?.id);\n return;\n }\n\n setLastActiveStepId(mostRecentFailedOrActiveStep?.id);\n }, [steps, completed]);\n\n const currentStepId = userSelectedStepId ?? lastActiveStepId;\n\n const logAsString = useMemo(() => {\n if (!currentStepId) {\n return 'Loading...';\n }\n const log = taskStream.stepLogs[currentStepId];\n\n if (!log?.length) {\n return 'Waiting for logs...';\n }\n return log.join('\\n');\n }, [taskStream.stepLogs, currentStepId]);\n\n const taskNotFound =\n taskStream.completed === true &&\n taskStream.loading === false &&\n !taskStream.task;\n\n const { output } = taskStream;\n\n return (\n <Page themeId=\"home\">\n <Header\n pageTitleOverride={`Task ${taskId}`}\n title={\n <>\n Task Activity <Lifecycle alpha shorthand />\n </>\n }\n subtitle={`Activity for task: ${taskId}`}\n />\n <Content>\n {taskNotFound ? (\n <ErrorPage\n status=\"404\"\n statusMessage=\"Task not found\"\n additionalInfo=\"No task found with this ID\"\n />\n ) : (\n <div>\n <Grid container>\n <Grid item xs={3}>\n <Paper>\n <TaskStatusStepper\n steps={steps}\n currentStepId={currentStepId}\n onUserStepChange={setUserSelectedStepId}\n />\n {output && hasLinks(output) && (\n <TaskPageLinks output={output} />\n )}\n </Paper>\n </Grid>\n <Grid item xs={9}>\n <div style={{ height: '80vh' }}>\n <LogViewer text={logAsString} />\n </div>\n </Grid>\n </Grid>\n </div>\n )}\n </Content>\n </Page>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport { useAsync } from 'react-use';\nimport { scaffolderApiRef } from '../../api';\nimport {\n Typography,\n Paper,\n Table,\n TableBody,\n Box,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n makeStyles,\n} from '@material-ui/core';\nimport { JSONSchema } from '@backstage/catalog-model';\nimport { JSONSchema7Definition } from 'json-schema';\nimport classNames from 'classnames';\n\nimport { useApi } from '@backstage/core-plugin-api';\nimport {\n Progress,\n Content,\n Header,\n Page,\n ErrorPage,\n} from '@backstage/core-components';\n\nconst useStyles = makeStyles(theme => ({\n code: {\n fontFamily: 'Menlo, monospace',\n padding: theme.spacing(1),\n backgroundColor:\n theme.palette.type === 'dark'\n ? theme.palette.grey[700]\n : theme.palette.grey[300],\n display: 'inline-block',\n borderRadius: 5,\n border: `1px solid ${theme.palette.grey[500]}`,\n position: 'relative',\n },\n\n codeRequired: {\n '&::after': {\n position: 'absolute',\n content: '\"*\"',\n top: 0,\n right: theme.spacing(0.5),\n fontWeight: 'bolder',\n color: theme.palette.error.light,\n },\n },\n}));\n\nexport const ActionsPage = () => {\n const api = useApi(scaffolderApiRef);\n const classes = useStyles();\n const { loading, value, error } = useAsync(async () => {\n return api.listActions();\n });\n\n if (loading) {\n return <Progress />;\n }\n\n if (error) {\n return (\n <ErrorPage\n statusMessage=\"Failed to load installed actions\"\n status=\"500\"\n />\n );\n }\n\n const formatRows = (input: JSONSchema) => {\n const properties = input.properties;\n if (!properties) {\n return undefined;\n }\n\n return Object.entries(properties).map(entry => {\n const [key] = entry;\n const props = entry[1] as unknown as JSONSchema;\n const codeClassname = classNames(classes.code, {\n [classes.codeRequired]: input.required?.includes(key),\n });\n\n return (\n <TableRow key={key}>\n <TableCell>\n <div className={codeClassname}>{key}</div>\n </TableCell>\n <TableCell>{props.title}</TableCell>\n <TableCell>{props.description}</TableCell>\n <TableCell>\n <span className={classes.code}>{props.type}</span>\n </TableCell>\n </TableRow>\n );\n });\n };\n\n const renderTable = (input: JSONSchema) => {\n if (!input.properties) {\n return undefined;\n }\n return (\n <TableContainer component={Paper}>\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Name</TableCell>\n <TableCell>Title</TableCell>\n <TableCell>Description</TableCell>\n <TableCell>Type</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>{formatRows(input)}</TableBody>\n </Table>\n </TableContainer>\n );\n };\n\n const renderTables = (name: string, input?: JSONSchema7Definition[]) => {\n if (!input) {\n return undefined;\n }\n\n return (\n <>\n <Typography variant=\"h6\">{name}</Typography>\n {input.map((i, index) => (\n <div key={index}>{renderTable(i as unknown as JSONSchema)}</div>\n ))}\n </>\n );\n };\n\n const items = value?.map(action => {\n if (action.id.startsWith('legacy:')) {\n return undefined;\n }\n\n const oneOf = renderTables('oneOf', action.schema?.input?.oneOf);\n return (\n <Box pb={4} key={action.id}>\n <Typography variant=\"h4\" className={classes.code}>\n {action.id}\n </Typography>\n <Typography>{action.description}</Typography>\n {action.schema?.input && (\n <Box pb={2}>\n <Typography variant=\"h5\">Input</Typography>\n {renderTable(action.schema.input)}\n {oneOf}\n </Box>\n )}\n {action.schema?.output && (\n <Box pb={2}>\n <Typography variant=\"h5\">Output</Typography>\n {renderTable(action.schema.output)}\n </Box>\n )}\n </Box>\n );\n });\n\n return (\n <Page themeId=\"home\">\n <Header\n pageTitleOverride=\"Create a New Component\"\n title=\"Installed actions\"\n subtitle=\"This is the collection of all installed actions\"\n />\n <Content>{items}</Content>\n </Page>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { ComponentType } from 'react';\nimport { Routes, Route, useOutlet } from 'react-router';\nimport { TemplateEntityV1beta2, Entity } from '@backstage/catalog-model';\nimport { ScaffolderPage } from './ScaffolderPage';\nimport { TemplatePage } from './TemplatePage';\nimport { TaskPage } from './TaskPage';\nimport { ActionsPage } from './ActionsPage';\n\nimport {\n FieldExtensionOptions,\n FIELD_EXTENSION_WRAPPER_KEY,\n FIELD_EXTENSION_KEY,\n DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS,\n} from '../extensions';\nimport { useElementFilter } from '@backstage/core-plugin-api';\n\ntype RouterProps = {\n TemplateCardComponent?:\n | ComponentType<{ template: TemplateEntityV1beta2 }>\n | undefined;\n groups?: Array<{\n title?: string;\n titleComponent?: React.ReactNode;\n filter: (entity: Entity) => boolean;\n }>;\n};\n\nexport const Router = ({ TemplateCardComponent, groups }: RouterProps) => {\n const outlet = useOutlet();\n\n const customFieldExtensions = useElementFilter(outlet, elements =>\n elements\n .selectByComponentData({\n key: FIELD_EXTENSION_WRAPPER_KEY,\n })\n .findComponentData<FieldExtensionOptions>({\n key: FIELD_EXTENSION_KEY,\n }),\n );\n\n const fieldExtensions = [\n ...customFieldExtensions,\n ...DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS.filter(\n ({ name }) =>\n !customFieldExtensions.some(\n customFieldExtension => customFieldExtension.name === name,\n ),\n ),\n ];\n\n return (\n <Routes>\n <Route\n path=\"/\"\n element={\n <ScaffolderPage\n TemplateCardComponent={TemplateCardComponent}\n groups={groups}\n />\n }\n />\n <Route\n path=\"/templates/:templateName\"\n element={<TemplatePage customFieldExtensions={fieldExtensions} />}\n />\n <Route path=\"/tasks/:taskId\" element={<TaskPage />} />\n <Route path=\"/actions\" element={<ActionsPage />} />\n </Routes>\n );\n};\n"],"names":["useStyles","isObject","MuiTheme","StepUI","makeStyles","Typography","Stepper","Step","StepLabel","useParams","Grid"],"mappings":"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"}
|