@backstage/plugin-scaffolder 0.11.6 → 0.11.10
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 +94 -0
- package/dist/esm/{Router-bcc15f41.esm.js → Router-6ab934f3.esm.js} +45 -225
- package/dist/esm/Router-6ab934f3.esm.js.map +1 -0
- package/dist/esm/{index-a8f2c080.esm.js → index-23aee701.esm.js} +247 -19
- package/dist/esm/index-23aee701.esm.js.map +1 -0
- package/dist/index.d.ts +19 -6
- package/dist/index.esm.js +11 -3
- package/dist/index.esm.js.map +1 -1
- package/package.json +20 -17
- package/dist/esm/Router-bcc15f41.esm.js.map +0 -1
- package/dist/esm/index-a8f2c080.esm.js.map +0 -1
|
@@ -1,21 +1,29 @@
|
|
|
1
|
+
import { createApiRef, useApi, attachComponentData, createExternalRouteRef, createRouteRef, createPlugin, createApiFactory, discoveryApiRef, identityApiRef, createRoutableExtension, useRouteRef, alertApiRef } from '@backstage/core-plugin-api';
|
|
1
2
|
import { ResponseError } from '@backstage/errors';
|
|
3
|
+
import qs from 'qs';
|
|
2
4
|
import ObservableImpl from 'zen-observable';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { TextField, withStyles, makeStyles, IconButton, Tooltip } from '@material-ui/core';
|
|
5
|
+
import { catalogApiRef, formatEntityRefTitle, useStarredEntity, getEntityRelations, getEntitySourceLocation, EntityRefLinks, useEntityListProvider, useEntityTypeFilter } from '@backstage/plugin-catalog-react';
|
|
6
|
+
import { TextField, withStyles, makeStyles, IconButton, Tooltip, useTheme, Card, CardMedia, CardContent, Box, Typography, Chip, CardActions, Link, FormControlLabel, Checkbox } from '@material-ui/core';
|
|
6
7
|
import FormControl from '@material-ui/core/FormControl';
|
|
7
8
|
import Autocomplete from '@material-ui/lab/Autocomplete';
|
|
8
|
-
import React, { useCallback, useEffect
|
|
9
|
+
import React, { useCallback, useEffect } from 'react';
|
|
9
10
|
import { useAsync } from 'react-use';
|
|
10
|
-
import { KubernetesValidatorFunctions } from '@backstage/catalog-model';
|
|
11
|
-
import { scmIntegrationsApiRef } from '@backstage/integration-react';
|
|
11
|
+
import { KubernetesValidatorFunctions, RELATION_OWNED_BY, stringifyEntityRef } from '@backstage/catalog-model';
|
|
12
|
+
import { scmIntegrationsApiRef, ScmIntegrationIcon } from '@backstage/integration-react';
|
|
12
13
|
import Select from '@material-ui/core/Select';
|
|
13
14
|
import InputLabel from '@material-ui/core/InputLabel';
|
|
14
15
|
import Input from '@material-ui/core/Input';
|
|
15
16
|
import FormHelperText from '@material-ui/core/FormHelperText';
|
|
16
|
-
import { Progress } from '@backstage/core-components';
|
|
17
|
-
import StarBorder from '@material-ui/icons/StarBorder';
|
|
17
|
+
import { Progress, ItemCardHeader, Button, WarningPanel, ItemCardGrid } from '@backstage/core-components';
|
|
18
18
|
import Star from '@material-ui/icons/Star';
|
|
19
|
+
import StarBorder from '@material-ui/icons/StarBorder';
|
|
20
|
+
import WarningIcon from '@material-ui/icons/Warning';
|
|
21
|
+
import { generatePath } from 'react-router';
|
|
22
|
+
import capitalize from 'lodash/capitalize';
|
|
23
|
+
import CheckBoxIcon from '@material-ui/icons/CheckBox';
|
|
24
|
+
import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
|
|
25
|
+
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
|
26
|
+
import { Autocomplete as Autocomplete$1 } from '@material-ui/lab';
|
|
19
27
|
|
|
20
28
|
const scaffolderApiRef = createApiRef({
|
|
21
29
|
id: "plugin.scaffolder.service",
|
|
@@ -23,9 +31,11 @@ const scaffolderApiRef = createApiRef({
|
|
|
23
31
|
});
|
|
24
32
|
class ScaffolderClient {
|
|
25
33
|
constructor(options) {
|
|
34
|
+
var _a;
|
|
26
35
|
this.discoveryApi = options.discoveryApi;
|
|
27
36
|
this.identityApi = options.identityApi;
|
|
28
37
|
this.scmIntegrationsApi = options.scmIntegrationsApi;
|
|
38
|
+
this.useLongPollingLogs = (_a = options.useLongPollingLogs) != null ? _a : false;
|
|
29
39
|
}
|
|
30
40
|
async getIntegrationsList(options) {
|
|
31
41
|
return [
|
|
@@ -83,7 +93,13 @@ class ScaffolderClient {
|
|
|
83
93
|
}
|
|
84
94
|
return await response.json();
|
|
85
95
|
}
|
|
86
|
-
streamLogs({
|
|
96
|
+
streamLogs(opts) {
|
|
97
|
+
if (this.useLongPollingLogs) {
|
|
98
|
+
return this.streamLogsPolling(opts);
|
|
99
|
+
}
|
|
100
|
+
return this.streamLogsEventStream(opts);
|
|
101
|
+
}
|
|
102
|
+
streamLogsEventStream({
|
|
87
103
|
taskId,
|
|
88
104
|
after
|
|
89
105
|
}) {
|
|
@@ -123,6 +139,33 @@ class ScaffolderClient {
|
|
|
123
139
|
});
|
|
124
140
|
});
|
|
125
141
|
}
|
|
142
|
+
streamLogsPolling({
|
|
143
|
+
taskId,
|
|
144
|
+
after: inputAfter
|
|
145
|
+
}) {
|
|
146
|
+
let after = inputAfter;
|
|
147
|
+
return new ObservableImpl((subscriber) => {
|
|
148
|
+
this.discoveryApi.getBaseUrl("scaffolder").then(async (baseUrl) => {
|
|
149
|
+
while (!subscriber.closed) {
|
|
150
|
+
const url = `${baseUrl}/v2/tasks/${encodeURIComponent(taskId)}/events?${qs.stringify({after})}`;
|
|
151
|
+
const response = await fetch(url);
|
|
152
|
+
if (!response.ok) {
|
|
153
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
const logs = await response.json();
|
|
157
|
+
for (const event of logs) {
|
|
158
|
+
after = Number(event.id);
|
|
159
|
+
subscriber.next(event);
|
|
160
|
+
if (event.type === "completion") {
|
|
161
|
+
subscriber.complete();
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
}
|
|
126
169
|
async listActions() {
|
|
127
170
|
const baseUrl = await this.discoveryApi.getBaseUrl("scaffolder");
|
|
128
171
|
const token = await this.identityApi.getIdToken();
|
|
@@ -525,7 +568,8 @@ const OwnerPickerFieldExtension = scaffolderPlugin.provide(createScaffolderField
|
|
|
525
568
|
name: "OwnerPicker"
|
|
526
569
|
}));
|
|
527
570
|
const ScaffolderPage = scaffolderPlugin.provide(createRoutableExtension({
|
|
528
|
-
|
|
571
|
+
name: "ScaffolderPage",
|
|
572
|
+
component: () => import('./Router-6ab934f3.esm.js').then((m) => m.Router),
|
|
529
573
|
mountPoint: rootRouteRef
|
|
530
574
|
}));
|
|
531
575
|
|
|
@@ -539,7 +583,7 @@ const WhiteBorderStar = withStyles({
|
|
|
539
583
|
color: "#ffffff"
|
|
540
584
|
}
|
|
541
585
|
})(StarBorder);
|
|
542
|
-
const useStyles = makeStyles((theme) => ({
|
|
586
|
+
const useStyles$1 = makeStyles((theme) => ({
|
|
543
587
|
starButton: {
|
|
544
588
|
position: "absolute",
|
|
545
589
|
top: theme.spacing(0.5),
|
|
@@ -550,18 +594,202 @@ const useStyles = makeStyles((theme) => ({
|
|
|
550
594
|
const favouriteTemplateTooltip = (isStarred) => isStarred ? "Remove from favorites" : "Add to favorites";
|
|
551
595
|
const favouriteTemplateIcon = (isStarred) => isStarred ? /* @__PURE__ */ React.createElement(YellowStar, null) : /* @__PURE__ */ React.createElement(WhiteBorderStar, null);
|
|
552
596
|
const FavouriteTemplate = (props) => {
|
|
553
|
-
const classes = useStyles();
|
|
554
|
-
const {toggleStarredEntity, isStarredEntity} =
|
|
555
|
-
const isStarred = useMemo(() => isStarredEntity(props.entity), [isStarredEntity, props.entity]);
|
|
597
|
+
const classes = useStyles$1();
|
|
598
|
+
const {toggleStarredEntity, isStarredEntity} = useStarredEntity(props.entity);
|
|
556
599
|
return /* @__PURE__ */ React.createElement(IconButton, {
|
|
557
600
|
color: "inherit",
|
|
558
601
|
className: classes.starButton,
|
|
559
602
|
...props,
|
|
560
|
-
onClick: () => toggleStarredEntity(
|
|
603
|
+
onClick: () => toggleStarredEntity()
|
|
604
|
+
}, /* @__PURE__ */ React.createElement(Tooltip, {
|
|
605
|
+
title: favouriteTemplateTooltip(isStarredEntity)
|
|
606
|
+
}, favouriteTemplateIcon(isStarredEntity)));
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
const useStyles = makeStyles((theme) => ({
|
|
610
|
+
cardHeader: {
|
|
611
|
+
position: "relative"
|
|
612
|
+
},
|
|
613
|
+
title: {
|
|
614
|
+
backgroundImage: ({backgroundImage}) => backgroundImage
|
|
615
|
+
},
|
|
616
|
+
box: {
|
|
617
|
+
overflow: "hidden",
|
|
618
|
+
textOverflow: "ellipsis",
|
|
619
|
+
display: "-webkit-box",
|
|
620
|
+
"-webkit-line-clamp": 10,
|
|
621
|
+
"-webkit-box-orient": "vertical",
|
|
622
|
+
paddingBottom: "0.8em"
|
|
623
|
+
},
|
|
624
|
+
label: {
|
|
625
|
+
color: theme.palette.text.secondary,
|
|
626
|
+
textTransform: "uppercase",
|
|
627
|
+
fontSize: "0.65rem",
|
|
628
|
+
fontWeight: "bold",
|
|
629
|
+
letterSpacing: 0.5,
|
|
630
|
+
lineHeight: 1,
|
|
631
|
+
paddingBottom: "0.2rem"
|
|
632
|
+
},
|
|
633
|
+
leftButton: {
|
|
634
|
+
marginRight: "auto"
|
|
635
|
+
}
|
|
636
|
+
}));
|
|
637
|
+
const useDeprecationStyles = makeStyles((theme) => ({
|
|
638
|
+
deprecationIcon: {
|
|
639
|
+
position: "absolute",
|
|
640
|
+
top: theme.spacing(0.5),
|
|
641
|
+
right: theme.spacing(3.5),
|
|
642
|
+
padding: "0.25rem"
|
|
643
|
+
},
|
|
644
|
+
link: {
|
|
645
|
+
color: theme.palette.warning.light
|
|
646
|
+
}
|
|
647
|
+
}));
|
|
648
|
+
const getTemplateCardProps = (template) => {
|
|
649
|
+
var _a, _b, _c, _d, _e;
|
|
650
|
+
return {
|
|
651
|
+
key: template.metadata.uid,
|
|
652
|
+
name: template.metadata.name,
|
|
653
|
+
title: `${(_a = template.metadata.title || template.metadata.name) != null ? _a : ""}`,
|
|
654
|
+
type: (_b = template.spec.type) != null ? _b : "",
|
|
655
|
+
description: (_c = template.metadata.description) != null ? _c : "-",
|
|
656
|
+
tags: (_e = (_d = template.metadata) == null ? void 0 : _d.tags) != null ? _e : []
|
|
657
|
+
};
|
|
658
|
+
};
|
|
659
|
+
const DeprecationWarning = () => {
|
|
660
|
+
const styles = useDeprecationStyles();
|
|
661
|
+
const Title = /* @__PURE__ */ React.createElement(Typography, {
|
|
662
|
+
style: {padding: 10, maxWidth: 300}
|
|
663
|
+
}, "This template syntax is deprecated. Click for more info.");
|
|
664
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
665
|
+
className: styles.deprecationIcon
|
|
561
666
|
}, /* @__PURE__ */ React.createElement(Tooltip, {
|
|
562
|
-
title:
|
|
563
|
-
},
|
|
667
|
+
title: Title
|
|
668
|
+
}, /* @__PURE__ */ React.createElement(Link, {
|
|
669
|
+
href: "https://backstage.io/docs/features/software-templates/migrating-from-v1alpha1-to-v1beta2",
|
|
670
|
+
className: styles.link
|
|
671
|
+
}, /* @__PURE__ */ React.createElement(WarningIcon, null))));
|
|
672
|
+
};
|
|
673
|
+
const TemplateCard = ({template, deprecated}) => {
|
|
674
|
+
var _a;
|
|
675
|
+
const backstageTheme = useTheme();
|
|
676
|
+
const rootLink = useRouteRef(rootRouteRef);
|
|
677
|
+
const templateProps = getTemplateCardProps(template);
|
|
678
|
+
const ownedByRelations = getEntityRelations(template, RELATION_OWNED_BY);
|
|
679
|
+
const themeId = backstageTheme.getPageTheme({themeId: templateProps.type}) ? templateProps.type : "other";
|
|
680
|
+
const theme = backstageTheme.getPageTheme({themeId});
|
|
681
|
+
const classes = useStyles({backgroundImage: theme.backgroundImage});
|
|
682
|
+
const href = generatePath(`${rootLink()}/templates/:templateName`, {
|
|
683
|
+
templateName: templateProps.name
|
|
684
|
+
});
|
|
685
|
+
const scmIntegrationsApi = useApi(scmIntegrationsApiRef);
|
|
686
|
+
const sourceLocation = getEntitySourceLocation(template, scmIntegrationsApi);
|
|
687
|
+
return /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(CardMedia, {
|
|
688
|
+
className: classes.cardHeader
|
|
689
|
+
}, /* @__PURE__ */ React.createElement(FavouriteTemplate, {
|
|
690
|
+
entity: template
|
|
691
|
+
}), deprecated && /* @__PURE__ */ React.createElement(DeprecationWarning, null), /* @__PURE__ */ React.createElement(ItemCardHeader, {
|
|
692
|
+
title: templateProps.title,
|
|
693
|
+
subtitle: templateProps.type,
|
|
694
|
+
classes: {root: classes.title}
|
|
695
|
+
})), /* @__PURE__ */ React.createElement(CardContent, {
|
|
696
|
+
style: {display: "grid"}
|
|
697
|
+
}, /* @__PURE__ */ React.createElement(Box, {
|
|
698
|
+
className: classes.box
|
|
699
|
+
}, /* @__PURE__ */ React.createElement(Typography, {
|
|
700
|
+
variant: "body2",
|
|
701
|
+
className: classes.label
|
|
702
|
+
}, "Description"), templateProps.description), /* @__PURE__ */ React.createElement(Box, {
|
|
703
|
+
className: classes.box
|
|
704
|
+
}, /* @__PURE__ */ React.createElement(Typography, {
|
|
705
|
+
variant: "body2",
|
|
706
|
+
className: classes.label
|
|
707
|
+
}, "Owner"), /* @__PURE__ */ React.createElement(EntityRefLinks, {
|
|
708
|
+
entityRefs: ownedByRelations,
|
|
709
|
+
defaultKind: "Group"
|
|
710
|
+
})), /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Typography, {
|
|
711
|
+
variant: "body2",
|
|
712
|
+
className: classes.label
|
|
713
|
+
}, "Tags"), (_a = templateProps.tags) == null ? void 0 : _a.map((tag) => /* @__PURE__ */ React.createElement(Chip, {
|
|
714
|
+
size: "small",
|
|
715
|
+
label: tag,
|
|
716
|
+
key: tag
|
|
717
|
+
})))), /* @__PURE__ */ React.createElement(CardActions, null, sourceLocation && /* @__PURE__ */ React.createElement(IconButton, {
|
|
718
|
+
className: classes.leftButton,
|
|
719
|
+
href: sourceLocation.locationTargetUrl
|
|
720
|
+
}, /* @__PURE__ */ React.createElement(ScmIntegrationIcon, {
|
|
721
|
+
type: sourceLocation.integrationType
|
|
722
|
+
})), /* @__PURE__ */ React.createElement(Button, {
|
|
723
|
+
color: "primary",
|
|
724
|
+
to: href,
|
|
725
|
+
"aria-label": `Choose ${templateProps.title}`
|
|
726
|
+
}, "Choose")));
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
const TemplateList = ({TemplateCardComponent}) => {
|
|
730
|
+
const {loading, error, entities} = useEntityListProvider();
|
|
731
|
+
const Card = TemplateCardComponent || TemplateCard;
|
|
732
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, loading && /* @__PURE__ */ React.createElement(Progress, null), error && /* @__PURE__ */ React.createElement(WarningPanel, {
|
|
733
|
+
title: "Oops! Something went wrong loading the templates"
|
|
734
|
+
}, error.message), !error && !loading && !entities.length && /* @__PURE__ */ React.createElement(Typography, {
|
|
735
|
+
variant: "body2"
|
|
736
|
+
}, "No templates found that match your filter. Learn more about", " ", /* @__PURE__ */ React.createElement(Link, {
|
|
737
|
+
href: "https://backstage.io/docs/features/software-templates/adding-templates"
|
|
738
|
+
}, "adding templates"), "."), /* @__PURE__ */ React.createElement(ItemCardGrid, null, entities && (entities == null ? void 0 : entities.length) > 0 && entities.map((template) => /* @__PURE__ */ React.createElement(Card, {
|
|
739
|
+
key: stringifyEntityRef(template),
|
|
740
|
+
template
|
|
741
|
+
}))));
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
const icon = /* @__PURE__ */ React.createElement(CheckBoxOutlineBlankIcon, {
|
|
745
|
+
fontSize: "small"
|
|
746
|
+
});
|
|
747
|
+
const checkedIcon = /* @__PURE__ */ React.createElement(CheckBoxIcon, {
|
|
748
|
+
fontSize: "small"
|
|
749
|
+
});
|
|
750
|
+
const TemplateTypePicker = () => {
|
|
751
|
+
const alertApi = useApi(alertApiRef);
|
|
752
|
+
const {error, loading, availableTypes, selectedTypes, setSelectedTypes} = useEntityTypeFilter();
|
|
753
|
+
if (loading)
|
|
754
|
+
return /* @__PURE__ */ React.createElement(Progress, null);
|
|
755
|
+
if (!availableTypes)
|
|
756
|
+
return null;
|
|
757
|
+
if (error) {
|
|
758
|
+
alertApi.post({
|
|
759
|
+
message: `Failed to load entity types`,
|
|
760
|
+
severity: "error"
|
|
761
|
+
});
|
|
762
|
+
return null;
|
|
763
|
+
}
|
|
764
|
+
return /* @__PURE__ */ React.createElement(Box, {
|
|
765
|
+
pb: 1,
|
|
766
|
+
pt: 1
|
|
767
|
+
}, /* @__PURE__ */ React.createElement(Typography, {
|
|
768
|
+
variant: "button"
|
|
769
|
+
}, "Categories"), /* @__PURE__ */ React.createElement(Autocomplete$1, {
|
|
770
|
+
multiple: true,
|
|
771
|
+
"aria-label": "Categories",
|
|
772
|
+
options: availableTypes,
|
|
773
|
+
value: selectedTypes,
|
|
774
|
+
onChange: (_, value) => setSelectedTypes(value),
|
|
775
|
+
renderOption: (option, {selected}) => /* @__PURE__ */ React.createElement(FormControlLabel, {
|
|
776
|
+
control: /* @__PURE__ */ React.createElement(Checkbox, {
|
|
777
|
+
icon,
|
|
778
|
+
checkedIcon,
|
|
779
|
+
checked: selected
|
|
780
|
+
}),
|
|
781
|
+
label: capitalize(option)
|
|
782
|
+
}),
|
|
783
|
+
size: "small",
|
|
784
|
+
popupIcon: /* @__PURE__ */ React.createElement(ExpandMoreIcon, {
|
|
785
|
+
"data-testid": "categories-picker-expand"
|
|
786
|
+
}),
|
|
787
|
+
renderInput: (params) => /* @__PURE__ */ React.createElement(TextField, {
|
|
788
|
+
...params,
|
|
789
|
+
variant: "outlined"
|
|
790
|
+
})
|
|
791
|
+
}));
|
|
564
792
|
};
|
|
565
793
|
|
|
566
|
-
export { EntityPicker as E,
|
|
567
|
-
//# sourceMappingURL=index-
|
|
794
|
+
export { EntityPicker as E, FIELD_EXTENSION_WRAPPER_KEY as F, OwnerPicker as O, RepoUrlPicker as R, ScaffolderClient as S, TemplateTypePicker as T, EntityNamePicker as a, registerComponentRouteRef as b, TemplateList as c, rootRouteRef as d, entityNamePickerValidation as e, FIELD_EXTENSION_KEY as f, createScaffolderFieldExtension as g, ScaffolderFieldExtensions as h, EntityPickerFieldExtension as i, EntityNamePickerFieldExtension as j, OwnerPickerFieldExtension as k, RepoUrlPickerFieldExtension as l, ScaffolderPage as m, scaffolderPlugin as n, TextValuePicker as o, FavouriteTemplate as p, repoPickerValidation as r, scaffolderApiRef as s };
|
|
795
|
+
//# sourceMappingURL=index-23aee701.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-23aee701.esm.js","sources":["../../src/api.ts","../../src/components/fields/EntityPicker/EntityPicker.tsx","../../src/components/fields/TextValuePicker/TextValuePicker.tsx","../../src/components/fields/EntityNamePicker/EntityNamePicker.tsx","../../src/components/fields/EntityNamePicker/validation.ts","../../src/components/fields/OwnerPicker/OwnerPicker.tsx","../../src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx","../../src/components/fields/RepoUrlPicker/validation.ts","../../src/extensions/index.tsx","../../src/routes.ts","../../src/plugin.ts","../../src/components/FavouriteTemplate/FavouriteTemplate.tsx","../../src/components/TemplateCard/TemplateCard.tsx","../../src/components/TemplateList/TemplateList.tsx","../../src/components/TemplateTypePicker/TemplateTypePicker.tsx"],"sourcesContent":["/*\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 { EntityName } from '@backstage/catalog-model';\nimport {\n createApiRef,\n DiscoveryApi,\n IdentityApi,\n} from '@backstage/core-plugin-api';\nimport { ResponseError } from '@backstage/errors';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport { JsonObject, JsonValue, Observable } from '@backstage/types';\nimport { Field, FieldValidation } from '@rjsf/core';\nimport qs from 'qs';\nimport ObservableImpl from 'zen-observable';\nimport { ListActionsResponse, ScaffolderTask, Status } from './types';\n\nexport const scaffolderApiRef = createApiRef<ScaffolderApi>({\n id: 'plugin.scaffolder.service',\n description: 'Used to make requests towards the scaffolder backend',\n});\n\ntype TemplateParameterSchema = {\n title: string;\n steps: Array<{\n title: string;\n schema: JsonObject;\n }>;\n};\n\nexport type LogEvent = {\n type: 'log' | 'completion';\n body: {\n message: string;\n stepId?: string;\n status?: Status;\n };\n createdAt: string;\n id: string;\n taskId: string;\n};\n\nexport type CustomField = {\n name: string;\n component: Field;\n validation: (data: JsonValue, field: FieldValidation) => void;\n};\n\nexport interface ScaffolderApi {\n getTemplateParameterSchema(\n templateName: EntityName,\n ): Promise<TemplateParameterSchema>;\n\n /**\n * Executes the scaffolding of a component, given a template and its\n * parameter values.\n *\n * @param templateName Name of the Template entity for the scaffolder to use. New project is going to be created out of this template.\n * @param values Parameters for the template, e.g. name, description\n */\n scaffold(templateName: string, values: Record<string, any>): Promise<string>;\n\n getTask(taskId: string): Promise<ScaffolderTask>;\n\n getIntegrationsList(options: {\n allowedHosts: string[];\n }): Promise<{ type: string; title: string; host: string }[]>;\n\n // Returns a list of all installed actions.\n listActions(): Promise<ListActionsResponse>;\n\n streamLogs({\n taskId,\n after,\n }: {\n taskId: string;\n after?: number;\n }): Observable<LogEvent>;\n}\n\nexport class ScaffolderClient implements ScaffolderApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly identityApi: IdentityApi;\n private readonly scmIntegrationsApi: ScmIntegrationRegistry;\n private readonly useLongPollingLogs: boolean;\n\n constructor(options: {\n discoveryApi: DiscoveryApi;\n identityApi: IdentityApi;\n scmIntegrationsApi: ScmIntegrationRegistry;\n useLongPollingLogs?: boolean;\n }) {\n this.discoveryApi = options.discoveryApi;\n this.identityApi = options.identityApi;\n this.scmIntegrationsApi = options.scmIntegrationsApi;\n this.useLongPollingLogs = options.useLongPollingLogs ?? false;\n }\n\n async getIntegrationsList(options: { allowedHosts: string[] }) {\n return [\n ...this.scmIntegrationsApi.azure.list(),\n ...this.scmIntegrationsApi.bitbucket.list(),\n ...this.scmIntegrationsApi.github.list(),\n ...this.scmIntegrationsApi.gitlab.list(),\n ]\n .map(c => ({ type: c.type, title: c.title, host: c.config.host }))\n .filter(c => options.allowedHosts.includes(c.host));\n }\n\n async getTemplateParameterSchema(\n templateName: EntityName,\n ): Promise<TemplateParameterSchema> {\n const { namespace, kind, name } = templateName;\n\n const token = await this.identityApi.getIdToken();\n const baseUrl = await this.discoveryApi.getBaseUrl('scaffolder');\n const templatePath = [namespace, kind, name]\n .map(s => encodeURIComponent(s))\n .join('/');\n const url = `${baseUrl}/v2/templates/${templatePath}/parameter-schema`;\n\n const response = await fetch(url, {\n headers: {\n ...(token && { Authorization: `Bearer ${token}` }),\n },\n });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n const schema: TemplateParameterSchema = await response.json();\n return schema;\n }\n\n /**\n * Executes the scaffolding of a component, given a template and its\n * parameter values.\n *\n * @param templateName Template name for the scaffolder to use. New project is going to be created out of this template.\n * @param values Parameters for the template, e.g. name, description\n */\n async scaffold(\n templateName: string,\n values: Record<string, any>,\n ): Promise<string> {\n const token = await this.identityApi.getIdToken();\n const url = `${await this.discoveryApi.getBaseUrl('scaffolder')}/v2/tasks`;\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(token && { Authorization: `Bearer ${token}` }),\n },\n body: JSON.stringify({ templateName, values: { ...values } }),\n });\n\n if (response.status !== 201) {\n const status = `${response.status} ${response.statusText}`;\n const body = await response.text();\n throw new Error(`Backend request failed, ${status} ${body.trim()}`);\n }\n\n const { id } = (await response.json()) as { id: string };\n return id;\n }\n\n async getTask(taskId: string) {\n const token = await this.identityApi.getIdToken();\n const baseUrl = await this.discoveryApi.getBaseUrl('scaffolder');\n const url = `${baseUrl}/v2/tasks/${encodeURIComponent(taskId)}`;\n const response = await fetch(url, {\n headers: token ? { Authorization: `Bearer ${token}` } : {},\n });\n\n if (!response.ok) {\n throw ResponseError.fromResponse(response);\n }\n\n return await response.json();\n }\n\n streamLogs(opts: { taskId: string; after?: number }): Observable<LogEvent> {\n if (this.useLongPollingLogs) {\n return this.streamLogsPolling(opts);\n }\n\n return this.streamLogsEventStream(opts);\n }\n\n private streamLogsEventStream({\n taskId,\n after,\n }: {\n taskId: string;\n after?: number;\n }): Observable<LogEvent> {\n return new ObservableImpl(subscriber => {\n const params = new URLSearchParams();\n if (after !== undefined) {\n params.set('after', String(Number(after)));\n }\n\n this.discoveryApi.getBaseUrl('scaffolder').then(\n baseUrl => {\n const url = `${baseUrl}/v2/tasks/${encodeURIComponent(\n taskId,\n )}/eventstream`;\n const eventSource = new EventSource(url, { withCredentials: true });\n eventSource.addEventListener('log', (event: any) => {\n if (event.data) {\n try {\n subscriber.next(JSON.parse(event.data));\n } catch (ex) {\n subscriber.error(ex);\n }\n }\n });\n eventSource.addEventListener('completion', (event: any) => {\n if (event.data) {\n try {\n subscriber.next(JSON.parse(event.data));\n } catch (ex) {\n subscriber.error(ex);\n }\n }\n eventSource.close();\n subscriber.complete();\n });\n eventSource.addEventListener('error', event => {\n subscriber.error(event);\n });\n },\n error => {\n subscriber.error(error);\n },\n );\n });\n }\n\n private streamLogsPolling({\n taskId,\n after: inputAfter,\n }: {\n taskId: string;\n after?: number;\n }): Observable<LogEvent> {\n let after = inputAfter;\n\n return new ObservableImpl(subscriber => {\n this.discoveryApi.getBaseUrl('scaffolder').then(async baseUrl => {\n while (!subscriber.closed) {\n const url = `${baseUrl}/v2/tasks/${encodeURIComponent(\n taskId,\n )}/events?${qs.stringify({ after })}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n // wait for one second to not run into an\n await new Promise(resolve => setTimeout(resolve, 1000));\n continue;\n }\n\n const logs = (await response.json()) as LogEvent[];\n\n for (const event of logs) {\n after = Number(event.id);\n\n subscriber.next(event);\n\n if (event.type === 'completion') {\n subscriber.complete();\n return;\n }\n }\n }\n });\n });\n }\n\n /**\n * @returns ListActionsResponse containing all registered actions.\n */\n async listActions(): Promise<ListActionsResponse> {\n const baseUrl = await this.discoveryApi.getBaseUrl('scaffolder');\n const token = await this.identityApi.getIdToken();\n const response = await fetch(`${baseUrl}/v2/actions`, {\n headers: token ? { Authorization: `Bearer ${token}` } : {},\n });\n\n if (!response.ok) {\n throw ResponseError.fromResponse(response);\n }\n\n return await response.json();\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 { useApi } from '@backstage/core-plugin-api';\nimport {\n catalogApiRef,\n formatEntityRefTitle,\n} from '@backstage/plugin-catalog-react';\nimport { TextField } from '@material-ui/core';\nimport FormControl from '@material-ui/core/FormControl';\nimport Autocomplete from '@material-ui/lab/Autocomplete';\nimport { FieldProps } from '@rjsf/core';\nimport React from 'react';\nimport { useAsync } from 'react-use';\n\nexport const EntityPicker = ({\n onChange,\n schema: { title = 'Entity', description = 'An entity from the catalog' },\n required,\n uiSchema,\n rawErrors,\n formData,\n idSchema,\n}: FieldProps<string>) => {\n const allowedKinds = uiSchema['ui:options']?.allowedKinds as string[];\n const defaultKind = uiSchema['ui:options']?.defaultKind as string | undefined;\n const catalogApi = useApi(catalogApiRef);\n\n const { value: entities, loading } = useAsync(() =>\n catalogApi.getEntities(\n allowedKinds ? { filter: { kind: allowedKinds } } : undefined,\n ),\n );\n\n const entityRefs = entities?.items.map(e =>\n formatEntityRefTitle(e, { defaultKind }),\n );\n\n const onSelect = (_: any, value: string | null) => {\n onChange(value || '');\n };\n\n return (\n <FormControl\n margin=\"normal\"\n required={required}\n error={rawErrors?.length > 0 && !formData}\n >\n <Autocomplete\n id={idSchema?.$id}\n value={(formData as string) || ''}\n loading={loading}\n onChange={onSelect}\n options={entityRefs || []}\n autoSelect\n freeSolo\n renderInput={params => (\n <TextField\n {...params}\n label={title}\n margin=\"normal\"\n helperText={description}\n variant=\"outlined\"\n required={required}\n InputProps={params.InputProps}\n />\n )}\n />\n </FormControl>\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 { TextField } from '@material-ui/core';\nimport { FieldProps } from '@rjsf/core';\nimport React from 'react';\n\nexport const TextValuePicker = ({\n onChange,\n required,\n schema: { title, description },\n rawErrors,\n formData,\n uiSchema: { 'ui:autofocus': autoFocus },\n idSchema,\n placeholder,\n}: FieldProps<string>) => (\n <TextField\n id={idSchema?.$id}\n label={title}\n placeholder={placeholder}\n helperText={description}\n required={required}\n value={formData ?? ''}\n onChange={({ target: { value } }) => onChange(value)}\n margin=\"normal\"\n error={rawErrors?.length > 0 && !formData}\n inputProps={{ autoFocus }}\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 { FieldProps } from '@rjsf/core';\nimport { TextValuePicker } from '../TextValuePicker';\n\nexport const EntityNamePicker = ({\n schema: { title = 'Name', description = 'Unique name of the component' },\n ...props\n}: FieldProps<string>) => (\n <TextValuePicker schema={{ title, description }} {...props} />\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 { FieldValidation } from '@rjsf/core';\nimport { KubernetesValidatorFunctions } from '@backstage/catalog-model';\n\nexport const entityNamePickerValidation = (\n value: string,\n validation: FieldValidation,\n) => {\n if (!KubernetesValidatorFunctions.isValidObjectName(value)) {\n validation.addError(\n 'must start and end with an alphanumeric character, and contain only alphanumeric characters, hyphens, underscores, and periods. Maximum length is 63 characters.',\n );\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 { FieldProps } from '@rjsf/core';\nimport React from 'react';\nimport { EntityPicker } from '../EntityPicker';\n\nexport const OwnerPicker = ({\n schema: { title = 'Owner', description = 'The owner of the component' },\n uiSchema,\n ...props\n}: FieldProps<string>) => {\n const ownerUiSchema = {\n ...uiSchema,\n 'ui:options': {\n allowedKinds: (uiSchema['ui:options']?.allowedKinds || [\n 'Group',\n 'User',\n ]) as string[],\n defaultKind: 'Group',\n },\n };\n\n return (\n <EntityPicker\n {...props}\n schema={{ title, description }}\n uiSchema={ownerUiSchema}\n />\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, { useCallback, useEffect } from 'react';\nimport { FieldProps } from '@rjsf/core';\nimport { scaffolderApiRef } from '../../../api';\nimport { scmIntegrationsApiRef } from '@backstage/integration-react';\nimport { useAsync } from 'react-use';\nimport Select from '@material-ui/core/Select';\nimport InputLabel from '@material-ui/core/InputLabel';\nimport Input from '@material-ui/core/Input';\nimport FormControl from '@material-ui/core/FormControl';\nimport FormHelperText from '@material-ui/core/FormHelperText';\n\nimport { useApi } from '@backstage/core-plugin-api';\nimport { Progress } from '@backstage/core-components';\n\nfunction splitFormData(url: string | undefined) {\n let host = undefined;\n let owner = undefined;\n let repo = undefined;\n let organization = undefined;\n let workspace = undefined;\n let project = undefined;\n\n try {\n if (url) {\n const parsed = new URL(`https://${url}`);\n host = parsed.host;\n owner = parsed.searchParams.get('owner') || undefined;\n repo = parsed.searchParams.get('repo') || undefined;\n // This is azure dev ops specific. not used for any other provider.\n organization = parsed.searchParams.get('organization') || undefined;\n // These are bitbucket specific, not used for any other provider.\n workspace = parsed.searchParams.get('workspace') || undefined;\n project = parsed.searchParams.get('project') || undefined;\n }\n } catch {\n /* ok */\n }\n\n return { host, owner, repo, organization, workspace, project };\n}\n\nfunction serializeFormData(data: {\n host?: string;\n owner?: string;\n repo?: string;\n organization?: string;\n workspace?: string;\n project?: string;\n}) {\n if (!data.host) {\n return undefined;\n }\n\n const params = new URLSearchParams();\n if (data.owner) {\n params.set('owner', data.owner);\n }\n if (data.repo) {\n params.set('repo', data.repo);\n }\n if (data.organization) {\n params.set('organization', data.organization);\n }\n if (data.workspace) {\n params.set('workspace', data.workspace);\n }\n if (data.project) {\n params.set('project', data.project);\n }\n\n return `${data.host}?${params.toString()}`;\n}\n\nexport const RepoUrlPicker = ({\n onChange,\n uiSchema,\n rawErrors,\n formData,\n}: FieldProps<string>) => {\n const scaffolderApi = useApi(scaffolderApiRef);\n const integrationApi = useApi(scmIntegrationsApiRef);\n const allowedHosts = uiSchema['ui:options']?.allowedHosts as string[];\n\n const { value: integrations, loading } = useAsync(async () => {\n return await scaffolderApi.getIntegrationsList({ allowedHosts });\n });\n\n const { host, owner, repo, organization, workspace, project } =\n splitFormData(formData);\n const updateHost = useCallback(\n (evt: React.ChangeEvent<{ name?: string; value: unknown }>) => {\n onChange(\n serializeFormData({\n host: evt.target.value as string,\n owner,\n repo,\n organization,\n workspace,\n project,\n }),\n );\n },\n [onChange, owner, repo, organization, workspace, project],\n );\n\n const updateOwner = useCallback(\n (evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>\n onChange(\n serializeFormData({\n host,\n owner: evt.target.value as string,\n repo,\n organization,\n workspace,\n project,\n }),\n ),\n [onChange, host, repo, organization, workspace, project],\n );\n\n const updateRepo = useCallback(\n (evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>\n onChange(\n serializeFormData({\n host,\n owner,\n repo: evt.target.value as string,\n organization,\n workspace,\n project,\n }),\n ),\n [onChange, host, owner, organization, workspace, project],\n );\n\n const updateOrganization = useCallback(\n (evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>\n onChange(\n serializeFormData({\n host,\n owner,\n repo,\n organization: evt.target.value as string,\n workspace,\n project,\n }),\n ),\n [onChange, host, owner, repo, workspace, project],\n );\n\n const updateWorkspace = useCallback(\n (evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>\n onChange(\n serializeFormData({\n host,\n owner,\n repo,\n organization,\n workspace: evt.target.value as string,\n project,\n }),\n ),\n [onChange, host, owner, repo, organization, project],\n );\n\n const updateProject = useCallback(\n (evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>\n onChange(\n serializeFormData({\n host,\n owner,\n repo,\n organization,\n workspace,\n project: evt.target.value as string,\n }),\n ),\n [onChange, host, owner, repo, organization, workspace],\n );\n\n useEffect(() => {\n if (host === undefined && integrations?.length) {\n onChange(\n serializeFormData({\n host: integrations[0].host,\n owner,\n repo,\n organization,\n workspace,\n project,\n }),\n );\n }\n }, [\n onChange,\n integrations,\n host,\n owner,\n repo,\n organization,\n workspace,\n project,\n ]);\n\n if (loading) {\n return <Progress />;\n }\n\n return (\n <>\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !host}\n >\n <InputLabel htmlFor=\"hostInput\">Host</InputLabel>\n <Select native id=\"hostInput\" onChange={updateHost} value={host}>\n {integrations ? (\n integrations\n .filter(i => allowedHosts?.includes(i.host))\n .map(i => (\n <option key={i.host} value={i.host}>\n {i.title}\n </option>\n ))\n ) : (\n <p>loading</p>\n )}\n </Select>\n <FormHelperText>\n The host where the repository will be created\n </FormHelperText>\n </FormControl>\n {/* Show this for dev.azure.com only */}\n {host === 'dev.azure.com' && (\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !organization}\n >\n <InputLabel htmlFor=\"repoInput\">Organization</InputLabel>\n <Input\n id=\"repoInput\"\n onChange={updateOrganization}\n value={organization}\n />\n <FormHelperText>The name of the organization</FormHelperText>\n </FormControl>\n )}\n {host && integrationApi.byHost(host)?.type === 'bitbucket' && (\n <>\n {/* Show this for bitbucket.org only */}\n {host === 'bitbucket.org' && (\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !workspace}\n >\n <InputLabel htmlFor=\"wokrspaceInput\">Workspace</InputLabel>\n <Input\n id=\"wokrspaceInput\"\n onChange={updateWorkspace}\n value={workspace}\n />\n <FormHelperText>\n The workspace where the repository will be created\n </FormHelperText>\n </FormControl>\n )}\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !project}\n >\n <InputLabel htmlFor=\"wokrspaceInput\">Project</InputLabel>\n <Input\n id=\"wokrspaceInput\"\n onChange={updateProject}\n value={project}\n />\n <FormHelperText>\n The project where the repository will be created\n </FormHelperText>\n </FormControl>\n </>\n )}\n {/* Show this for all hosts except bitbucket */}\n {host && integrationApi.byHost(host)?.type !== 'bitbucket' && (\n <>\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !owner}\n >\n <InputLabel htmlFor=\"ownerInput\">Owner</InputLabel>\n <Input id=\"ownerInput\" onChange={updateOwner} value={owner} />\n <FormHelperText>\n The organization, user or project that this repo will belong to\n </FormHelperText>\n </FormControl>\n </>\n )}\n {/* Show this for all hosts */}\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !repo}\n >\n <InputLabel htmlFor=\"repoInput\">Repository</InputLabel>\n <Input id=\"repoInput\" onChange={updateRepo} value={repo} />\n <FormHelperText>The name of the repository</FormHelperText>\n </FormControl>\n </>\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 { FieldValidation } from '@rjsf/core';\nimport { ApiHolder } from '@backstage/core-plugin-api';\nimport { scmIntegrationsApiRef } from '@backstage/integration-react';\n\nexport const repoPickerValidation = (\n value: string,\n validation: FieldValidation,\n context: { apiHolder: ApiHolder },\n) => {\n try {\n const { host, searchParams } = new URL(`https://${value}`);\n\n const integrationApi = context.apiHolder.get(scmIntegrationsApiRef);\n\n if (!host) {\n validation.addError(\n 'Incomplete repository location provided, host not provided',\n );\n } else {\n if (integrationApi?.byHost(host)?.type === 'bitbucket') {\n // workspace is only applicable for bitbucket cloud\n if (host === 'bitbucket.org' && !searchParams.get('workspace')) {\n validation.addError(\n 'Incomplete repository location provided, workspace not provided',\n );\n }\n\n if (!searchParams.get('project')) {\n validation.addError(\n 'Incomplete repository location provided, project not provided',\n );\n }\n }\n // For anything other than bitbucket\n else {\n if (!searchParams.get('owner')) {\n validation.addError(\n 'Incomplete repository location provided, owner not provided',\n );\n }\n }\n\n // Do this for all hosts\n if (!searchParams.get('repo')) {\n validation.addError(\n 'Incomplete repository location provided, repo not provided',\n );\n }\n }\n } catch {\n validation.addError('Unable to parse the Repository URL');\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 from 'react';\nimport { CustomFieldValidator, FieldExtensionOptions } from './types';\nimport { Extension, attachComponentData } from '@backstage/core-plugin-api';\n\nexport const FIELD_EXTENSION_WRAPPER_KEY = 'scaffolder.extensions.wrapper.v1';\nexport const FIELD_EXTENSION_KEY = 'scaffolder.extensions.field.v1';\n\nexport function createScaffolderFieldExtension<T = any>(\n options: FieldExtensionOptions<T>,\n): Extension<() => null> {\n return {\n expose() {\n const FieldExtensionDataHolder: any = () => null;\n\n attachComponentData(\n FieldExtensionDataHolder,\n FIELD_EXTENSION_KEY,\n options,\n );\n\n return FieldExtensionDataHolder;\n },\n };\n}\n\nexport const ScaffolderFieldExtensions: React.ComponentType = () => null;\nattachComponentData(\n ScaffolderFieldExtensions,\n FIELD_EXTENSION_WRAPPER_KEY,\n true,\n);\n\nexport type { CustomFieldValidator, FieldExtensionOptions };\n\nexport { DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS } from './default';\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 {\n createExternalRouteRef,\n createRouteRef,\n} from '@backstage/core-plugin-api';\n\nexport const registerComponentRouteRef = createExternalRouteRef({\n id: 'register-component',\n optional: true,\n});\n\nexport const rootRouteRef = createRouteRef({\n title: 'Create new entity',\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 { scmIntegrationsApiRef } from '@backstage/integration-react';\nimport { scaffolderApiRef, ScaffolderClient } from './api';\nimport { EntityPicker } from './components/fields/EntityPicker';\nimport {\n entityNamePickerValidation,\n EntityNamePicker,\n} from './components/fields/EntityNamePicker';\nimport { OwnerPicker } from './components/fields/OwnerPicker';\nimport {\n repoPickerValidation,\n RepoUrlPicker,\n} from './components/fields/RepoUrlPicker';\nimport { createScaffolderFieldExtension } from './extensions';\nimport { registerComponentRouteRef, rootRouteRef } from './routes';\nimport {\n createApiFactory,\n createPlugin,\n createRoutableExtension,\n discoveryApiRef,\n identityApiRef,\n} from '@backstage/core-plugin-api';\n\nexport const scaffolderPlugin = createPlugin({\n id: 'scaffolder',\n apis: [\n createApiFactory({\n api: scaffolderApiRef,\n deps: {\n discoveryApi: discoveryApiRef,\n identityApi: identityApiRef,\n scmIntegrationsApi: scmIntegrationsApiRef,\n },\n factory: ({ discoveryApi, identityApi, scmIntegrationsApi }) =>\n new ScaffolderClient({ discoveryApi, identityApi, scmIntegrationsApi }),\n }),\n ],\n routes: {\n root: rootRouteRef,\n },\n externalRoutes: {\n registerComponent: registerComponentRouteRef,\n },\n});\n\nexport const EntityPickerFieldExtension = scaffolderPlugin.provide(\n createScaffolderFieldExtension({\n component: EntityPicker,\n name: 'EntityPicker',\n }),\n);\n\nexport const EntityNamePickerFieldExtension = scaffolderPlugin.provide(\n createScaffolderFieldExtension({\n component: EntityNamePicker,\n name: 'EntityNamePicker',\n validation: entityNamePickerValidation,\n }),\n);\n\nexport const RepoUrlPickerFieldExtension = scaffolderPlugin.provide(\n createScaffolderFieldExtension({\n component: RepoUrlPicker,\n name: 'RepoUrlPicker',\n validation: repoPickerValidation,\n }),\n);\n\nexport const OwnerPickerFieldExtension = scaffolderPlugin.provide(\n createScaffolderFieldExtension({\n component: OwnerPicker,\n name: 'OwnerPicker',\n }),\n);\n\nexport const ScaffolderPage = scaffolderPlugin.provide(\n createRoutableExtension({\n name: 'ScaffolderPage',\n component: () => import('./components/Router').then(m => m.Router),\n mountPoint: rootRouteRef,\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 { Entity } from '@backstage/catalog-model';\nimport { useStarredEntity } from '@backstage/plugin-catalog-react';\nimport { IconButton, makeStyles, Tooltip, withStyles } from '@material-ui/core';\nimport Star from '@material-ui/icons/Star';\nimport StarBorder from '@material-ui/icons/StarBorder';\nimport React, { ComponentProps } from 'react';\n\ntype Props = ComponentProps<typeof IconButton> & { entity: Entity };\n\nconst YellowStar = withStyles({\n root: {\n color: '#f3ba37',\n },\n})(Star);\n\nconst WhiteBorderStar = withStyles({\n root: {\n color: '#ffffff',\n },\n})(StarBorder);\n\nconst useStyles = makeStyles(theme => ({\n starButton: {\n position: 'absolute',\n top: theme.spacing(0.5),\n right: theme.spacing(0.5),\n padding: '0.25rem',\n },\n}));\n\nexport const favouriteTemplateTooltip = (isStarred: boolean) =>\n isStarred ? 'Remove from favorites' : 'Add to favorites';\n\nexport const favouriteTemplateIcon = (isStarred: boolean) =>\n isStarred ? <YellowStar /> : <WhiteBorderStar />;\n\n/**\n * IconButton for showing if a current entity is starred and adding/removing it from the favourite entities\n * @param props MaterialUI IconButton props extended by required `entity` prop\n */\nexport const FavouriteTemplate = (props: Props) => {\n const classes = useStyles();\n const { toggleStarredEntity, isStarredEntity } = useStarredEntity(\n props.entity,\n );\n return (\n <IconButton\n color=\"inherit\"\n className={classes.starButton}\n {...props}\n onClick={() => toggleStarredEntity()}\n >\n <Tooltip title={favouriteTemplateTooltip(isStarredEntity)}>\n {favouriteTemplateIcon(isStarredEntity)}\n </Tooltip>\n </IconButton>\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 {\n Entity,\n RELATION_OWNED_BY,\n TemplateEntityV1beta2,\n} from '@backstage/catalog-model';\nimport {\n ScmIntegrationIcon,\n scmIntegrationsApiRef,\n} from '@backstage/integration-react';\nimport {\n EntityRefLinks,\n getEntityRelations,\n getEntitySourceLocation,\n} from '@backstage/plugin-catalog-react';\nimport { BackstageTheme } from '@backstage/theme';\nimport {\n Box,\n Card,\n CardActions,\n CardContent,\n CardMedia,\n Chip,\n IconButton,\n Link,\n makeStyles,\n Tooltip,\n Typography,\n useTheme,\n} from '@material-ui/core';\nimport WarningIcon from '@material-ui/icons/Warning';\nimport React from 'react';\nimport { generatePath } from 'react-router';\nimport { rootRouteRef } from '../../routes';\nimport { FavouriteTemplate } from '../FavouriteTemplate/FavouriteTemplate';\n\nimport { Button, ItemCardHeader } from '@backstage/core-components';\nimport { useApi, useRouteRef } from '@backstage/core-plugin-api';\n\nconst useStyles = makeStyles(theme => ({\n cardHeader: {\n position: 'relative',\n },\n title: {\n backgroundImage: ({ backgroundImage }: any) => backgroundImage,\n },\n box: {\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n display: '-webkit-box',\n '-webkit-line-clamp': 10,\n '-webkit-box-orient': 'vertical',\n paddingBottom: '0.8em',\n },\n label: {\n color: theme.palette.text.secondary,\n textTransform: 'uppercase',\n fontSize: '0.65rem',\n fontWeight: 'bold',\n letterSpacing: 0.5,\n lineHeight: 1,\n paddingBottom: '0.2rem',\n },\n leftButton: {\n marginRight: 'auto',\n },\n}));\n\nconst useDeprecationStyles = makeStyles(theme => ({\n deprecationIcon: {\n position: 'absolute',\n top: theme.spacing(0.5),\n right: theme.spacing(3.5),\n padding: '0.25rem',\n },\n link: {\n color: theme.palette.warning.light,\n },\n}));\n\nexport type TemplateCardProps = {\n template: TemplateEntityV1beta2;\n deprecated?: boolean;\n};\n\ntype TemplateProps = {\n description: string;\n tags: string[];\n title: string;\n type: string;\n name: string;\n};\n\nconst getTemplateCardProps = (\n template: TemplateEntityV1beta2,\n): TemplateProps & { key: string } => {\n return {\n key: template.metadata.uid!,\n name: template.metadata.name,\n title: `${(template.metadata.title || template.metadata.name) ?? ''}`,\n type: template.spec.type ?? '',\n description: template.metadata.description ?? '-',\n tags: (template.metadata?.tags as string[]) ?? [],\n };\n};\n\nconst DeprecationWarning = () => {\n const styles = useDeprecationStyles();\n\n const Title = (\n <Typography style={{ padding: 10, maxWidth: 300 }}>\n This template syntax is deprecated. Click for more info.\n </Typography>\n );\n\n return (\n <div className={styles.deprecationIcon}>\n <Tooltip title={Title}>\n <Link\n href=\"https://backstage.io/docs/features/software-templates/migrating-from-v1alpha1-to-v1beta2\"\n className={styles.link}\n >\n <WarningIcon />\n </Link>\n </Tooltip>\n </div>\n );\n};\n\nexport const TemplateCard = ({ template, deprecated }: TemplateCardProps) => {\n const backstageTheme = useTheme<BackstageTheme>();\n const rootLink = useRouteRef(rootRouteRef);\n const templateProps = getTemplateCardProps(template);\n const ownedByRelations = getEntityRelations(\n template as Entity,\n RELATION_OWNED_BY,\n );\n const themeId = backstageTheme.getPageTheme({ themeId: templateProps.type })\n ? templateProps.type\n : 'other';\n const theme = backstageTheme.getPageTheme({ themeId });\n const classes = useStyles({ backgroundImage: theme.backgroundImage });\n const href = generatePath(`${rootLink()}/templates/:templateName`, {\n templateName: templateProps.name,\n });\n\n const scmIntegrationsApi = useApi(scmIntegrationsApiRef);\n const sourceLocation = getEntitySourceLocation(template, scmIntegrationsApi);\n\n return (\n <Card>\n <CardMedia className={classes.cardHeader}>\n <FavouriteTemplate entity={template} />\n {deprecated && <DeprecationWarning />}\n <ItemCardHeader\n title={templateProps.title}\n subtitle={templateProps.type}\n classes={{ root: classes.title }}\n />\n </CardMedia>\n <CardContent style={{ display: 'grid' }}>\n <Box className={classes.box}>\n <Typography variant=\"body2\" className={classes.label}>\n Description\n </Typography>\n {templateProps.description}\n </Box>\n <Box className={classes.box}>\n <Typography variant=\"body2\" className={classes.label}>\n Owner\n </Typography>\n <EntityRefLinks entityRefs={ownedByRelations} defaultKind=\"Group\" />\n </Box>\n <Box>\n <Typography variant=\"body2\" className={classes.label}>\n Tags\n </Typography>\n {templateProps.tags?.map(tag => (\n <Chip size=\"small\" label={tag} key={tag} />\n ))}\n </Box>\n </CardContent>\n <CardActions>\n {sourceLocation && (\n <IconButton\n className={classes.leftButton}\n href={sourceLocation.locationTargetUrl}\n >\n <ScmIntegrationIcon type={sourceLocation.integrationType} />\n </IconButton>\n )}\n <Button\n color=\"primary\"\n to={href}\n aria-label={`Choose ${templateProps.title}`}\n >\n Choose\n </Button>\n </CardActions>\n </Card>\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 {\n stringifyEntityRef,\n TemplateEntityV1beta2,\n} from '@backstage/catalog-model';\nimport {\n ItemCardGrid,\n Progress,\n WarningPanel,\n} from '@backstage/core-components';\nimport { useEntityListProvider } from '@backstage/plugin-catalog-react';\nimport { Link, Typography } from '@material-ui/core';\nimport { TemplateCard } from '../TemplateCard';\n\nexport type TemplateListProps = {\n TemplateCardComponent?:\n | ComponentType<{ template: TemplateEntityV1beta2 }>\n | undefined;\n};\n\nexport const TemplateList = ({ TemplateCardComponent }: TemplateListProps) => {\n const { loading, error, entities } = useEntityListProvider();\n const Card = TemplateCardComponent || TemplateCard;\n return (\n <>\n {loading && <Progress />}\n\n {error && (\n <WarningPanel title=\"Oops! Something went wrong loading the templates\">\n {error.message}\n </WarningPanel>\n )}\n\n {!error && !loading && !entities.length && (\n <Typography variant=\"body2\">\n No templates found that match your filter. Learn more about{' '}\n <Link href=\"https://backstage.io/docs/features/software-templates/adding-templates\">\n adding templates\n </Link>\n .\n </Typography>\n )}\n\n <ItemCardGrid>\n {entities &&\n entities?.length > 0 &&\n entities.map(template => (\n <Card\n key={stringifyEntityRef(template)}\n template={template as TemplateEntityV1beta2}\n />\n ))}\n </ItemCardGrid>\n </>\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 from 'react';\nimport capitalize from 'lodash/capitalize';\nimport { Progress } from '@backstage/core-components';\nimport {\n Box,\n Checkbox,\n FormControlLabel,\n TextField,\n Typography,\n} from '@material-ui/core';\nimport CheckBoxIcon from '@material-ui/icons/CheckBox';\nimport CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport { Autocomplete } from '@material-ui/lab';\nimport { useEntityTypeFilter } from '@backstage/plugin-catalog-react';\nimport { alertApiRef, useApi } from '@backstage/core-plugin-api';\n\nconst icon = <CheckBoxOutlineBlankIcon fontSize=\"small\" />;\nconst checkedIcon = <CheckBoxIcon fontSize=\"small\" />;\n\nexport const TemplateTypePicker = () => {\n const alertApi = useApi(alertApiRef);\n const { error, loading, availableTypes, selectedTypes, setSelectedTypes } =\n useEntityTypeFilter();\n\n if (loading) return <Progress />;\n\n if (!availableTypes) return null;\n\n if (error) {\n alertApi.post({\n message: `Failed to load entity types`,\n severity: 'error',\n });\n return null;\n }\n\n return (\n <Box pb={1} pt={1}>\n <Typography variant=\"button\">Categories</Typography>\n <Autocomplete\n multiple\n aria-label=\"Categories\"\n options={availableTypes}\n value={selectedTypes}\n onChange={(_: object, value: string[]) => setSelectedTypes(value)}\n renderOption={(option, { selected }) => (\n <FormControlLabel\n control={\n <Checkbox\n icon={icon}\n checkedIcon={checkedIcon}\n checked={selected}\n />\n }\n label={capitalize(option)}\n />\n )}\n size=\"small\"\n popupIcon={<ExpandMoreIcon data-testid=\"categories-picker-expand\" />}\n renderInput={params => <TextField {...params} variant=\"outlined\" />}\n />\n </Box>\n );\n};\n"],"names":["useStyles","Autocomplete"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
|
|
3
|
-
import {
|
|
3
|
+
import { DiscoveryApi, IdentityApi, ApiHolder, Extension } from '@backstage/core-plugin-api';
|
|
4
4
|
import * as _backstage_catalog_model from '@backstage/catalog-model';
|
|
5
|
-
import { JSONSchema, EntityName, Entity } from '@backstage/catalog-model';
|
|
6
|
-
import { JsonValue, JsonObject } from '@backstage/config';
|
|
5
|
+
import { JSONSchema, EntityName, Entity, TemplateEntityV1beta2 } from '@backstage/catalog-model';
|
|
7
6
|
import { ScmIntegrationRegistry } from '@backstage/integration';
|
|
7
|
+
import { JsonValue, Observable, JsonObject } from '@backstage/types';
|
|
8
8
|
import * as React from 'react';
|
|
9
|
-
import React__default, { ComponentProps } from 'react';
|
|
9
|
+
import React__default, { ComponentProps, ComponentType } from 'react';
|
|
10
10
|
import { FieldValidation, FieldProps } from '@rjsf/core';
|
|
11
11
|
import { IconButton } from '@material-ui/core';
|
|
12
12
|
|
|
@@ -84,10 +84,12 @@ declare class ScaffolderClient implements ScaffolderApi {
|
|
|
84
84
|
private readonly discoveryApi;
|
|
85
85
|
private readonly identityApi;
|
|
86
86
|
private readonly scmIntegrationsApi;
|
|
87
|
+
private readonly useLongPollingLogs;
|
|
87
88
|
constructor(options: {
|
|
88
89
|
discoveryApi: DiscoveryApi;
|
|
89
90
|
identityApi: IdentityApi;
|
|
90
91
|
scmIntegrationsApi: ScmIntegrationRegistry;
|
|
92
|
+
useLongPollingLogs?: boolean;
|
|
91
93
|
});
|
|
92
94
|
getIntegrationsList(options: {
|
|
93
95
|
allowedHosts: string[];
|
|
@@ -106,10 +108,12 @@ declare class ScaffolderClient implements ScaffolderApi {
|
|
|
106
108
|
*/
|
|
107
109
|
scaffold(templateName: string, values: Record<string, any>): Promise<string>;
|
|
108
110
|
getTask(taskId: string): Promise<any>;
|
|
109
|
-
streamLogs(
|
|
111
|
+
streamLogs(opts: {
|
|
110
112
|
taskId: string;
|
|
111
113
|
after?: number;
|
|
112
114
|
}): Observable<LogEvent>;
|
|
115
|
+
private streamLogsEventStream;
|
|
116
|
+
private streamLogsPolling;
|
|
113
117
|
/**
|
|
114
118
|
* @returns ListActionsResponse containing all registered actions.
|
|
115
119
|
*/
|
|
@@ -162,4 +166,13 @@ declare type Props = ComponentProps<typeof IconButton> & {
|
|
|
162
166
|
*/
|
|
163
167
|
declare const FavouriteTemplate: (props: Props) => JSX.Element;
|
|
164
168
|
|
|
165
|
-
|
|
169
|
+
declare type TemplateListProps = {
|
|
170
|
+
TemplateCardComponent?: ComponentType<{
|
|
171
|
+
template: TemplateEntityV1beta2;
|
|
172
|
+
}> | undefined;
|
|
173
|
+
};
|
|
174
|
+
declare const TemplateList: ({ TemplateCardComponent }: TemplateListProps) => JSX.Element;
|
|
175
|
+
|
|
176
|
+
declare const TemplateTypePicker: () => JSX.Element | null;
|
|
177
|
+
|
|
178
|
+
export { CustomFieldValidator, EntityNamePicker, EntityNamePickerFieldExtension, EntityPicker, EntityPickerFieldExtension, FavouriteTemplate, FieldExtensionOptions, OwnerPicker, OwnerPickerFieldExtension, RepoUrlPicker, RepoUrlPickerFieldExtension, ScaffolderApi, ScaffolderClient, ScaffolderFieldExtensions, ScaffolderPage, TemplateList, TemplateListProps, TemplateTypePicker, TextValuePicker, createScaffolderFieldExtension, scaffolderPlugin as plugin, scaffolderApiRef, scaffolderPlugin };
|
package/dist/index.esm.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
export { a as EntityNamePicker, j as EntityNamePickerFieldExtension, E as EntityPicker, i as EntityPickerFieldExtension,
|
|
1
|
+
export { a as EntityNamePicker, j as EntityNamePickerFieldExtension, E as EntityPicker, i as EntityPickerFieldExtension, p as FavouriteTemplate, O as OwnerPicker, k as OwnerPickerFieldExtension, R as RepoUrlPicker, l as RepoUrlPickerFieldExtension, S as ScaffolderClient, h as ScaffolderFieldExtensions, m as ScaffolderPage, c as TemplateList, T as TemplateTypePicker, o as TextValuePicker, g as createScaffolderFieldExtension, n as plugin, s as scaffolderApiRef, n as scaffolderPlugin } from './esm/index-23aee701.esm.js';
|
|
2
2
|
import '@backstage/catalog-model';
|
|
3
3
|
import '@backstage/integration-react';
|
|
4
|
+
import '@backstage/core-plugin-api';
|
|
4
5
|
import '@backstage/errors';
|
|
6
|
+
import 'qs';
|
|
5
7
|
import 'zen-observable';
|
|
6
|
-
import '@backstage/core-plugin-api';
|
|
7
8
|
import '@backstage/plugin-catalog-react';
|
|
8
9
|
import '@material-ui/core';
|
|
9
10
|
import '@material-ui/core/FormControl';
|
|
@@ -15,6 +16,13 @@ import '@material-ui/core/InputLabel';
|
|
|
15
16
|
import '@material-ui/core/Input';
|
|
16
17
|
import '@material-ui/core/FormHelperText';
|
|
17
18
|
import '@backstage/core-components';
|
|
18
|
-
import '@material-ui/icons/StarBorder';
|
|
19
19
|
import '@material-ui/icons/Star';
|
|
20
|
+
import '@material-ui/icons/StarBorder';
|
|
21
|
+
import '@material-ui/icons/Warning';
|
|
22
|
+
import 'react-router';
|
|
23
|
+
import 'lodash/capitalize';
|
|
24
|
+
import '@material-ui/icons/CheckBox';
|
|
25
|
+
import '@material-ui/icons/CheckBoxOutlineBlank';
|
|
26
|
+
import '@material-ui/icons/ExpandMore';
|
|
27
|
+
import '@material-ui/lab';
|
|
20
28
|
//# sourceMappingURL=index.esm.js.map
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;"}
|