@backstage/plugin-scaffolder 0.11.14 → 0.11.18
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 +60 -0
- package/dist/esm/{Router-0282e15a.esm.js → Router-552fb2ce.esm.js} +64 -15
- package/dist/esm/Router-552fb2ce.esm.js.map +1 -0
- package/dist/esm/{index-306a8b5a.esm.js → index-768702c3.esm.js} +165 -41
- package/dist/esm/index-768702c3.esm.js.map +1 -0
- package/dist/index.d.ts +23 -18
- package/dist/index.esm.js +6 -6
- package/package.json +17 -16
- package/dist/esm/Router-0282e15a.esm.js.map +0 -1
- package/dist/esm/index-306a8b5a.esm.js.map +0 -1
|
@@ -2,19 +2,20 @@ import { createApiRef, useApi, attachComponentData, createExternalRouteRef, crea
|
|
|
2
2
|
import { ResponseError } from '@backstage/errors';
|
|
3
3
|
import qs from 'qs';
|
|
4
4
|
import ObservableImpl from 'zen-observable';
|
|
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';
|
|
5
|
+
import { catalogApiRef, formatEntityRefTitle, useOwnedEntities, useStarredEntity, getEntityRelations, getEntitySourceLocation, EntityRefLinks, useEntityListProvider, useEntityTypeFilter } from '@backstage/plugin-catalog-react';
|
|
6
|
+
import { TextField, FormControl as FormControl$1, withStyles, makeStyles, IconButton, Tooltip, useTheme, Card, CardMedia, CardContent, Box, Typography, Chip, CardActions, Link, FormControlLabel, Checkbox } from '@material-ui/core';
|
|
7
7
|
import FormControl from '@material-ui/core/FormControl';
|
|
8
8
|
import Autocomplete from '@material-ui/lab/Autocomplete';
|
|
9
|
-
import React, { useCallback, useEffect } from 'react';
|
|
10
|
-
import
|
|
11
|
-
import { KubernetesValidatorFunctions, RELATION_OWNED_BY, stringifyEntityRef } from '@backstage/catalog-model';
|
|
9
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
10
|
+
import useAsync from 'react-use/lib/useAsync';
|
|
11
|
+
import { KubernetesValidatorFunctions, makeValidator, RELATION_OWNED_BY, stringifyEntityRef } from '@backstage/catalog-model';
|
|
12
|
+
import { useAsync as useAsync$1, useEffectOnce } from 'react-use';
|
|
13
|
+
import { Autocomplete as Autocomplete$1 } from '@material-ui/lab';
|
|
14
|
+
import { Progress, Select, ItemCardHeader, Button, ContentHeader, WarningPanel, Link as Link$1, Content, ItemCardGrid } from '@backstage/core-components';
|
|
12
15
|
import { scmIntegrationsApiRef, ScmIntegrationIcon } from '@backstage/integration-react';
|
|
13
|
-
import Select from '@material-ui/core/Select';
|
|
14
|
-
import InputLabel from '@material-ui/core/InputLabel';
|
|
15
|
-
import Input from '@material-ui/core/Input';
|
|
16
16
|
import FormHelperText from '@material-ui/core/FormHelperText';
|
|
17
|
-
import
|
|
17
|
+
import Input from '@material-ui/core/Input';
|
|
18
|
+
import InputLabel from '@material-ui/core/InputLabel';
|
|
18
19
|
import Star from '@material-ui/icons/Star';
|
|
19
20
|
import StarBorder from '@material-ui/icons/StarBorder';
|
|
20
21
|
import WarningIcon from '@material-ui/icons/Warning';
|
|
@@ -23,11 +24,9 @@ import capitalize from 'lodash/capitalize';
|
|
|
23
24
|
import CheckBoxIcon from '@material-ui/icons/CheckBox';
|
|
24
25
|
import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
|
|
25
26
|
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
|
26
|
-
import { Autocomplete as Autocomplete$1 } from '@material-ui/lab';
|
|
27
27
|
|
|
28
28
|
const scaffolderApiRef = createApiRef({
|
|
29
|
-
id: "plugin.scaffolder.service"
|
|
30
|
-
description: "Used to make requests towards the scaffolder backend"
|
|
29
|
+
id: "plugin.scaffolder.service"
|
|
31
30
|
});
|
|
32
31
|
class ScaffolderClient {
|
|
33
32
|
constructor(options) {
|
|
@@ -179,6 +178,10 @@ class ScaffolderClient {
|
|
|
179
178
|
}
|
|
180
179
|
}
|
|
181
180
|
|
|
181
|
+
const allowArbitraryValues = (uiSchema) => {
|
|
182
|
+
var _a, _b;
|
|
183
|
+
return (_b = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.allowArbitraryValues) != null ? _b : true;
|
|
184
|
+
};
|
|
182
185
|
const EntityPicker = ({
|
|
183
186
|
onChange,
|
|
184
187
|
schema: { title = "Entity", description = "An entity from the catalog" },
|
|
@@ -194,21 +197,27 @@ const EntityPicker = ({
|
|
|
194
197
|
const catalogApi = useApi(catalogApiRef);
|
|
195
198
|
const { value: entities, loading } = useAsync(() => catalogApi.getEntities(allowedKinds ? { filter: { kind: allowedKinds } } : void 0));
|
|
196
199
|
const entityRefs = entities == null ? void 0 : entities.items.map((e) => formatEntityRefTitle(e, { defaultKind }));
|
|
197
|
-
const onSelect = (_, value) => {
|
|
200
|
+
const onSelect = useCallback((_, value) => {
|
|
198
201
|
onChange(value || "");
|
|
199
|
-
};
|
|
202
|
+
}, [onChange]);
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
if ((entityRefs == null ? void 0 : entityRefs.length) === 1) {
|
|
205
|
+
onChange(entityRefs[0]);
|
|
206
|
+
}
|
|
207
|
+
}, [entityRefs, onChange]);
|
|
200
208
|
return /* @__PURE__ */ React.createElement(FormControl, {
|
|
201
209
|
margin: "normal",
|
|
202
210
|
required,
|
|
203
211
|
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !formData
|
|
204
212
|
}, /* @__PURE__ */ React.createElement(Autocomplete, {
|
|
213
|
+
disabled: (entityRefs == null ? void 0 : entityRefs.length) === 1,
|
|
205
214
|
id: idSchema == null ? void 0 : idSchema.$id,
|
|
206
215
|
value: formData || "",
|
|
207
216
|
loading,
|
|
208
217
|
onChange: onSelect,
|
|
209
218
|
options: entityRefs || [],
|
|
210
219
|
autoSelect: true,
|
|
211
|
-
freeSolo:
|
|
220
|
+
freeSolo: allowArbitraryValues(uiSchema),
|
|
212
221
|
renderInput: (params) => /* @__PURE__ */ React.createElement(TextField, {
|
|
213
222
|
...params,
|
|
214
223
|
label: title,
|
|
@@ -257,6 +266,68 @@ const entityNamePickerValidation = (value, validation) => {
|
|
|
257
266
|
}
|
|
258
267
|
};
|
|
259
268
|
|
|
269
|
+
const EntityTagsPicker = ({
|
|
270
|
+
formData,
|
|
271
|
+
onChange,
|
|
272
|
+
uiSchema
|
|
273
|
+
}) => {
|
|
274
|
+
var _a;
|
|
275
|
+
const catalogApi = useApi(catalogApiRef);
|
|
276
|
+
const [inputValue, setInputValue] = useState("");
|
|
277
|
+
const [inputError, setInputError] = useState(false);
|
|
278
|
+
const tagValidator = makeValidator().isValidTag;
|
|
279
|
+
const kinds = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.kinds;
|
|
280
|
+
const { loading, value: existingTags } = useAsync$1(async () => {
|
|
281
|
+
const tagsRequest = { fields: ["metadata.tags"] };
|
|
282
|
+
if (kinds) {
|
|
283
|
+
tagsRequest.filter = { kind: kinds };
|
|
284
|
+
}
|
|
285
|
+
const entities = await catalogApi.getEntities(tagsRequest);
|
|
286
|
+
return [
|
|
287
|
+
...new Set(entities.items.flatMap((e) => {
|
|
288
|
+
var _a2;
|
|
289
|
+
return (_a2 = e.metadata) == null ? void 0 : _a2.tags;
|
|
290
|
+
}).filter(Boolean))
|
|
291
|
+
].sort();
|
|
292
|
+
});
|
|
293
|
+
const setTags = (_, values) => {
|
|
294
|
+
let hasError = false;
|
|
295
|
+
let addDuplicate = false;
|
|
296
|
+
const currentTags = formData || [];
|
|
297
|
+
if ((values == null ? void 0 : values.length) && currentTags.length < values.length) {
|
|
298
|
+
const newTag = values[values.length - 1] = values[values.length - 1].toLocaleLowerCase("en-US").trim();
|
|
299
|
+
hasError = !tagValidator(newTag);
|
|
300
|
+
addDuplicate = currentTags.indexOf(newTag) !== -1;
|
|
301
|
+
}
|
|
302
|
+
setInputError(hasError);
|
|
303
|
+
setInputValue(!hasError ? "" : inputValue);
|
|
304
|
+
if (!hasError && !addDuplicate) {
|
|
305
|
+
onChange(values || []);
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
useEffectOnce(() => onChange(formData || []));
|
|
309
|
+
return /* @__PURE__ */ React.createElement(FormControl$1, {
|
|
310
|
+
margin: "normal"
|
|
311
|
+
}, /* @__PURE__ */ React.createElement(Autocomplete$1, {
|
|
312
|
+
multiple: true,
|
|
313
|
+
freeSolo: true,
|
|
314
|
+
filterSelectedOptions: true,
|
|
315
|
+
onChange: setTags,
|
|
316
|
+
value: formData || [],
|
|
317
|
+
inputValue,
|
|
318
|
+
loading,
|
|
319
|
+
options: existingTags || [],
|
|
320
|
+
ChipProps: { size: "small" },
|
|
321
|
+
renderInput: (params) => /* @__PURE__ */ React.createElement(TextField, {
|
|
322
|
+
...params,
|
|
323
|
+
label: "Tags",
|
|
324
|
+
onChange: (e) => setInputValue(e.target.value),
|
|
325
|
+
error: inputError,
|
|
326
|
+
helperText: "Add any relevant tags, hit 'Enter' to add new tags. Valid format: [a-z0-9+#] separated by [-], at most 63 characters"
|
|
327
|
+
})
|
|
328
|
+
}));
|
|
329
|
+
};
|
|
330
|
+
|
|
260
331
|
const OwnerPicker = ({
|
|
261
332
|
schema: { title = "Owner", description = "The owner of the component" },
|
|
262
333
|
uiSchema,
|
|
@@ -338,9 +409,9 @@ const RepoUrlPicker = ({
|
|
|
338
409
|
return await scaffolderApi.getIntegrationsList({ allowedHosts });
|
|
339
410
|
});
|
|
340
411
|
const { host, owner, repo, organization, workspace, project } = splitFormData(formData, allowedOwners);
|
|
341
|
-
const updateHost = useCallback((
|
|
412
|
+
const updateHost = useCallback((value) => {
|
|
342
413
|
onChange(serializeFormData({
|
|
343
|
-
host:
|
|
414
|
+
host: value,
|
|
344
415
|
owner,
|
|
345
416
|
repo,
|
|
346
417
|
organization,
|
|
@@ -348,6 +419,14 @@ const RepoUrlPicker = ({
|
|
|
348
419
|
project
|
|
349
420
|
}));
|
|
350
421
|
}, [onChange, owner, repo, organization, workspace, project]);
|
|
422
|
+
const updateOwnerSelect = useCallback((value) => onChange(serializeFormData({
|
|
423
|
+
host,
|
|
424
|
+
owner: value,
|
|
425
|
+
repo,
|
|
426
|
+
organization,
|
|
427
|
+
workspace,
|
|
428
|
+
project
|
|
429
|
+
})), [onChange, host, repo, organization, workspace, project]);
|
|
351
430
|
const updateOwner = useCallback((evt) => onChange(serializeFormData({
|
|
352
431
|
host,
|
|
353
432
|
owner: evt.target.value,
|
|
@@ -412,21 +491,20 @@ const RepoUrlPicker = ({
|
|
|
412
491
|
if (loading) {
|
|
413
492
|
return /* @__PURE__ */ React.createElement(Progress, null);
|
|
414
493
|
}
|
|
494
|
+
const hostsOptions = integrations ? integrations.filter((i) => allowedHosts == null ? void 0 : allowedHosts.includes(i.host)).map((i) => ({ label: i.title, value: i.host })) : [{ label: "Loading...", value: "loading" }];
|
|
495
|
+
const ownersOptions = allowedOwners ? allowedOwners.map((i) => ({ label: i, value: i })) : [{ label: "Loading...", value: "loading" }];
|
|
415
496
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(FormControl, {
|
|
416
497
|
margin: "normal",
|
|
417
498
|
required: true,
|
|
418
499
|
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !host
|
|
419
|
-
}, /* @__PURE__ */ React.createElement(
|
|
420
|
-
htmlFor: "hostInput"
|
|
421
|
-
}, "Host"), /* @__PURE__ */ React.createElement(Select, {
|
|
500
|
+
}, /* @__PURE__ */ React.createElement(Select, {
|
|
422
501
|
native: true,
|
|
423
|
-
|
|
502
|
+
disabled: hostsOptions.length === 1,
|
|
503
|
+
label: "Host",
|
|
424
504
|
onChange: updateHost,
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
value: i.host
|
|
429
|
-
}, i.title)) : /* @__PURE__ */ React.createElement("p", null, "loading")), /* @__PURE__ */ React.createElement(FormHelperText, null, "The host where the repository will be created")), host === "dev.azure.com" && /* @__PURE__ */ React.createElement(FormControl, {
|
|
505
|
+
selected: host,
|
|
506
|
+
items: hostsOptions
|
|
507
|
+
}), /* @__PURE__ */ React.createElement(FormHelperText, null, "The host where the repository will be created")), host === "dev.azure.com" && /* @__PURE__ */ React.createElement(FormControl, {
|
|
430
508
|
margin: "normal",
|
|
431
509
|
required: true,
|
|
432
510
|
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !organization
|
|
@@ -470,17 +548,14 @@ const RepoUrlPicker = ({
|
|
|
470
548
|
margin: "normal",
|
|
471
549
|
required: true,
|
|
472
550
|
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !owner
|
|
473
|
-
}, /* @__PURE__ */ React.createElement(
|
|
474
|
-
htmlFor: "ownerInput"
|
|
475
|
-
}, "Owner Available"), /* @__PURE__ */ React.createElement(Select, {
|
|
551
|
+
}, /* @__PURE__ */ React.createElement(Select, {
|
|
476
552
|
native: true,
|
|
477
|
-
|
|
478
|
-
onChange:
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
}, i)) : /* @__PURE__ */ React.createElement("p", null, "loading"), ";"), /* @__PURE__ */ React.createElement(FormHelperText, null, "The organization, user or project that this repo will belong to"))), /* @__PURE__ */ React.createElement(FormControl, {
|
|
553
|
+
label: "Owner Available",
|
|
554
|
+
onChange: updateOwnerSelect,
|
|
555
|
+
disabled: ownersOptions.length === 1,
|
|
556
|
+
selected: owner,
|
|
557
|
+
items: ownersOptions
|
|
558
|
+
}), /* @__PURE__ */ React.createElement(FormHelperText, null, "The organization, user or project that this repo will belong to"))), /* @__PURE__ */ React.createElement(FormControl, {
|
|
484
559
|
margin: "normal",
|
|
485
560
|
required: true,
|
|
486
561
|
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !repo
|
|
@@ -522,6 +597,47 @@ const repoPickerValidation = (value, validation, context) => {
|
|
|
522
597
|
}
|
|
523
598
|
};
|
|
524
599
|
|
|
600
|
+
const OwnedEntityPicker = ({
|
|
601
|
+
onChange,
|
|
602
|
+
schema: { title = "Entity", description = "An entity from the catalog" },
|
|
603
|
+
required,
|
|
604
|
+
uiSchema,
|
|
605
|
+
rawErrors,
|
|
606
|
+
formData,
|
|
607
|
+
idSchema
|
|
608
|
+
}) => {
|
|
609
|
+
var _a, _b;
|
|
610
|
+
const allowedKinds = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.allowedKinds;
|
|
611
|
+
const defaultKind = (_b = uiSchema["ui:options"]) == null ? void 0 : _b.defaultKind;
|
|
612
|
+
const { ownedEntities, loading } = useOwnedEntities(allowedKinds);
|
|
613
|
+
const entityRefs = ownedEntities == null ? void 0 : ownedEntities.items.map((e) => formatEntityRefTitle(e, { defaultKind })).filter((n) => n);
|
|
614
|
+
const onSelect = (_, value) => {
|
|
615
|
+
onChange(value || "");
|
|
616
|
+
};
|
|
617
|
+
return /* @__PURE__ */ React.createElement(FormControl, {
|
|
618
|
+
margin: "normal",
|
|
619
|
+
required,
|
|
620
|
+
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !formData
|
|
621
|
+
}, /* @__PURE__ */ React.createElement(Autocomplete, {
|
|
622
|
+
id: idSchema == null ? void 0 : idSchema.$id,
|
|
623
|
+
value: formData || "",
|
|
624
|
+
loading,
|
|
625
|
+
onChange: onSelect,
|
|
626
|
+
options: entityRefs || [],
|
|
627
|
+
autoSelect: true,
|
|
628
|
+
freeSolo: true,
|
|
629
|
+
renderInput: (params) => /* @__PURE__ */ React.createElement(TextField, {
|
|
630
|
+
...params,
|
|
631
|
+
label: title,
|
|
632
|
+
margin: "normal",
|
|
633
|
+
helperText: description,
|
|
634
|
+
variant: "outlined",
|
|
635
|
+
required,
|
|
636
|
+
InputProps: params.InputProps
|
|
637
|
+
})
|
|
638
|
+
}));
|
|
639
|
+
};
|
|
640
|
+
|
|
525
641
|
const FIELD_EXTENSION_WRAPPER_KEY = "scaffolder.extensions.wrapper.v1";
|
|
526
642
|
const FIELD_EXTENSION_KEY = "scaffolder.extensions.field.v1";
|
|
527
643
|
function createScaffolderFieldExtension(options) {
|
|
@@ -584,9 +700,17 @@ const OwnerPickerFieldExtension = scaffolderPlugin.provide(createScaffolderField
|
|
|
584
700
|
}));
|
|
585
701
|
const ScaffolderPage = scaffolderPlugin.provide(createRoutableExtension({
|
|
586
702
|
name: "ScaffolderPage",
|
|
587
|
-
component: () => import('./Router-
|
|
703
|
+
component: () => import('./Router-552fb2ce.esm.js').then((m) => m.Router),
|
|
588
704
|
mountPoint: rootRouteRef
|
|
589
705
|
}));
|
|
706
|
+
const OwnedEntityPickerFieldExtension = scaffolderPlugin.provide(createScaffolderFieldExtension({
|
|
707
|
+
component: OwnedEntityPicker,
|
|
708
|
+
name: "OwnedEntityPicker"
|
|
709
|
+
}));
|
|
710
|
+
const EntityTagsPickerFieldExtension = scaffolderPlugin.provide(createScaffolderFieldExtension({
|
|
711
|
+
component: EntityTagsPicker,
|
|
712
|
+
name: "EntityTagsPicker"
|
|
713
|
+
}));
|
|
590
714
|
|
|
591
715
|
const YellowStar = withStyles({
|
|
592
716
|
root: {
|
|
@@ -760,8 +884,8 @@ const TemplateList = ({
|
|
|
760
884
|
title: "Oops! Something went wrong loading the templates"
|
|
761
885
|
}, error.message), !error && !loading && !entities.length && /* @__PURE__ */ React.createElement(Typography, {
|
|
762
886
|
variant: "body2"
|
|
763
|
-
}, "No templates found that match your filter. Learn more about", " ", /* @__PURE__ */ React.createElement(Link, {
|
|
764
|
-
|
|
887
|
+
}, "No templates found that match your filter. Learn more about", " ", /* @__PURE__ */ React.createElement(Link$1, {
|
|
888
|
+
to: "https://backstage.io/docs/features/software-templates/adding-templates"
|
|
765
889
|
}, "adding templates"), "."), /* @__PURE__ */ React.createElement(Content, null, title, /* @__PURE__ */ React.createElement(ItemCardGrid, null, maybeFilteredEntities && (maybeFilteredEntities == null ? void 0 : maybeFilteredEntities.length) > 0 && maybeFilteredEntities.map((template) => /* @__PURE__ */ React.createElement(Card, {
|
|
766
890
|
key: stringifyEntityRef(template),
|
|
767
891
|
template
|
|
@@ -818,5 +942,5 @@ const TemplateTypePicker = () => {
|
|
|
818
942
|
}));
|
|
819
943
|
};
|
|
820
944
|
|
|
821
|
-
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,
|
|
822
|
-
//# sourceMappingURL=index-
|
|
945
|
+
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, EntityTagsPicker as b, OwnedEntityPicker as c, registerComponentRouteRef as d, entityNamePickerValidation as e, TemplateList as f, rootRouteRef as g, FIELD_EXTENSION_KEY as h, createScaffolderFieldExtension as i, ScaffolderFieldExtensions as j, EntityPickerFieldExtension as k, EntityNamePickerFieldExtension as l, EntityTagsPickerFieldExtension as m, OwnerPickerFieldExtension as n, OwnedEntityPickerFieldExtension as o, RepoUrlPickerFieldExtension as p, ScaffolderPage as q, repoPickerValidation as r, scaffolderApiRef as s, scaffolderPlugin as t, TextValuePicker as u, FavouriteTemplate as v };
|
|
946
|
+
//# sourceMappingURL=index-768702c3.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-768702c3.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/EntityTagsPicker/EntityTagsPicker.tsx","../../src/components/fields/OwnerPicker/OwnerPicker.tsx","../../src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx","../../src/components/fields/RepoUrlPicker/validation.ts","../../src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx","../../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});\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 await 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 await 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, UiSchema } from '@rjsf/core';\nimport React, { useCallback, useEffect } from 'react';\nimport useAsync from 'react-use/lib/useAsync';\n\nexport const allowArbitraryValues = (uiSchema: UiSchema): boolean =>\n (uiSchema['ui:options']?.allowArbitraryValues as boolean) ?? true;\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 = useCallback(\n (_: any, value: string | null) => {\n onChange(value || '');\n },\n [onChange],\n );\n\n useEffect(() => {\n if (entityRefs?.length === 1) {\n onChange(entityRefs[0]);\n }\n }, [entityRefs, onChange]);\n\n return (\n <FormControl\n margin=\"normal\"\n required={required}\n error={rawErrors?.length > 0 && !formData}\n >\n <Autocomplete\n disabled={entityRefs?.length === 1}\n id={idSchema?.$id}\n value={(formData as string) || ''}\n loading={loading}\n onChange={onSelect}\n options={entityRefs || []}\n autoSelect\n freeSolo={allowArbitraryValues(uiSchema)}\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 React, { useState } from 'react';\nimport { useAsync, useEffectOnce } from 'react-use';\nimport { CatalogEntitiesRequest } from '@backstage/catalog-client';\nimport { Entity, makeValidator } from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { catalogApiRef } from '@backstage/plugin-catalog-react';\nimport { FormControl, TextField } from '@material-ui/core';\nimport { Autocomplete } from '@material-ui/lab';\nimport { FieldProps } from '@rjsf/core';\n\n/**\n * EntityTagsPicker\n * @public\n */\nexport const EntityTagsPicker = ({\n formData,\n onChange,\n uiSchema,\n}: FieldProps<string[]>) => {\n const catalogApi = useApi(catalogApiRef);\n const [inputValue, setInputValue] = useState('');\n const [inputError, setInputError] = useState(false);\n const tagValidator = makeValidator().isValidTag;\n const kinds = uiSchema['ui:options']?.kinds as string[];\n\n const { loading, value: existingTags } = useAsync(async () => {\n const tagsRequest: CatalogEntitiesRequest = { fields: ['metadata.tags'] };\n if (kinds) {\n tagsRequest.filter = { kind: kinds };\n }\n\n const entities = await catalogApi.getEntities(tagsRequest);\n\n return [\n ...new Set(\n entities.items\n .flatMap((e: Entity) => e.metadata?.tags)\n .filter(Boolean) as string[],\n ),\n ].sort();\n });\n\n const setTags = (_: React.ChangeEvent<{}>, values: string[] | null) => {\n // Reset error state in case all tags were removed\n let hasError = false;\n let addDuplicate = false;\n const currentTags = formData || [];\n\n // If adding a new tag\n if (values?.length && currentTags.length < values.length) {\n const newTag = (values[values.length - 1] = values[values.length - 1]\n .toLocaleLowerCase('en-US')\n .trim());\n hasError = !tagValidator(newTag);\n addDuplicate = currentTags.indexOf(newTag) !== -1;\n }\n\n setInputError(hasError);\n setInputValue(!hasError ? '' : inputValue);\n if (!hasError && !addDuplicate) {\n onChange(values || []);\n }\n };\n\n // Initialize field to always return an array\n useEffectOnce(() => onChange(formData || []));\n\n return (\n <FormControl margin=\"normal\">\n <Autocomplete\n multiple\n freeSolo\n filterSelectedOptions\n onChange={setTags}\n value={formData || []}\n inputValue={inputValue}\n loading={loading}\n options={existingTags || []}\n ChipProps={{ size: 'small' }}\n renderInput={params => (\n <TextField\n {...params}\n label=\"Tags\"\n onChange={e => setInputValue(e.target.value)}\n error={inputError}\n helperText=\"Add any relevant tags, hit 'Enter' to add new tags. Valid format: [a-z0-9+#] separated by [-], at most 63 characters\"\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 { 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 {\n Progress,\n Select,\n SelectedItems,\n SelectItem,\n} from '@backstage/core-components';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { scmIntegrationsApiRef } from '@backstage/integration-react';\nimport FormControl from '@material-ui/core/FormControl';\nimport FormHelperText from '@material-ui/core/FormHelperText';\nimport Input from '@material-ui/core/Input';\nimport InputLabel from '@material-ui/core/InputLabel';\nimport { FieldProps } from '@rjsf/core';\nimport React, { useCallback, useEffect } from 'react';\nimport useAsync from 'react-use/lib/useAsync';\nimport { scaffolderApiRef } from '../../../api';\n\nfunction splitFormData(url: string | undefined, allowedOwners?: string[]) {\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') || allowedOwners?.[0];\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 const allowedOwners = uiSchema['ui:options']?.allowedOwners 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 } = splitFormData(\n formData,\n allowedOwners,\n );\n const updateHost = useCallback(\n (value: SelectedItems) => {\n onChange(\n serializeFormData({\n host: 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 updateOwnerSelect = useCallback(\n (value: SelectedItems) =>\n onChange(\n serializeFormData({\n host,\n owner: value as string,\n repo,\n organization,\n workspace,\n project,\n }),\n ),\n [onChange, host, 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 const hostsOptions: SelectItem[] = integrations\n ? integrations\n .filter(i => allowedHosts?.includes(i.host))\n .map(i => ({ label: i.title, value: i.host }))\n : [{ label: 'Loading...', value: 'loading' }];\n\n const ownersOptions: SelectItem[] = allowedOwners\n ? allowedOwners.map(i => ({ label: i, value: i }))\n : [{ label: 'Loading...', value: 'loading' }];\n\n return (\n <>\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !host}\n >\n <Select\n native\n disabled={hostsOptions.length === 1}\n label=\"Host\"\n onChange={updateHost}\n selected={host}\n items={hostsOptions}\n />\n\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 &&\n integrationApi.byHost(host)?.type !== 'bitbucket' &&\n !allowedOwners && (\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 except bitbucket where allowed owner is set */}\n {host &&\n integrationApi.byHost(host)?.type !== 'bitbucket' &&\n allowedOwners && (\n <>\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !owner}\n >\n <Select\n native\n label=\"Owner Available\"\n onChange={updateOwnerSelect}\n disabled={ownersOptions.length === 1}\n selected={owner}\n items={ownersOptions}\n />\n\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 */\nimport {\n formatEntityRefTitle,\n useOwnedEntities,\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';\n\nexport const OwnedEntityPicker = ({\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 { ownedEntities, loading } = useOwnedEntities(allowedKinds);\n\n const entityRefs = ownedEntities?.items\n .map(e => formatEntityRefTitle(e, { defaultKind }))\n .filter(n => 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 */\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 id: 'scaffolder',\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';\nimport { OwnedEntityPicker } from './components/fields/OwnedEntityPicker';\nimport { EntityTagsPicker } from './components/fields/EntityTagsPicker';\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\nexport const OwnedEntityPickerFieldExtension = scaffolderPlugin.provide(\n createScaffolderFieldExtension({\n component: OwnedEntityPicker,\n name: 'OwnedEntityPicker',\n }),\n);\n\n/**\n * EntityTagsPickerFieldExtension\n * @public\n */\nexport const EntityTagsPickerFieldExtension = scaffolderPlugin.provide(\n createScaffolderFieldExtension({\n component: EntityTagsPicker,\n name: 'EntityTagsPicker',\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 Entity,\n stringifyEntityRef,\n TemplateEntityV1beta2,\n} from '@backstage/catalog-model';\nimport {\n Content,\n ContentHeader,\n ItemCardGrid,\n Link,\n Progress,\n WarningPanel,\n} from '@backstage/core-components';\nimport { useEntityListProvider } from '@backstage/plugin-catalog-react';\nimport { Typography } from '@material-ui/core';\nimport { TemplateCard } from '../TemplateCard';\n\nexport type TemplateListProps = {\n TemplateCardComponent?:\n | ComponentType<{ template: TemplateEntityV1beta2 }>\n | undefined;\n group?: {\n title?: string;\n titleComponent?: React.ReactNode;\n filter: (entity: Entity) => boolean;\n };\n};\n\nexport const TemplateList = ({\n TemplateCardComponent,\n group,\n}: TemplateListProps) => {\n const { loading, error, entities } = useEntityListProvider();\n const Card = TemplateCardComponent || TemplateCard;\n const maybeFilteredEntities = group\n ? entities.filter(e => group.filter(e))\n : entities;\n const title = group ? (\n group.titleComponent || <ContentHeader title={group.title} />\n ) : (\n <ContentHeader title=\"Other Templates\" />\n );\n\n if (group && maybeFilteredEntities.length === 0) {\n return null;\n }\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 to=\"https://backstage.io/docs/features/software-templates/adding-templates\">\n adding templates\n </Link>\n .\n </Typography>\n )}\n\n <Content>\n {title}\n <ItemCardGrid>\n {maybeFilteredEntities &&\n maybeFilteredEntities?.length > 0 &&\n maybeFilteredEntities.map((template: Entity) => (\n <Card\n key={stringifyEntityRef(template)}\n template={template as TemplateEntityV1beta2}\n />\n ))}\n </ItemCardGrid>\n </Content>\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":["useAsync","FormControl","Autocomplete","useStyles","Link"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,26 +4,17 @@ import { DiscoveryApi, IdentityApi, ApiHolder, Extension } from '@backstage/core
|
|
|
4
4
|
import * as _backstage_catalog_model from '@backstage/catalog-model';
|
|
5
5
|
import { JSONSchema, EntityName, Entity, TemplateEntityV1beta2 } from '@backstage/catalog-model';
|
|
6
6
|
import { ScmIntegrationRegistry } from '@backstage/integration';
|
|
7
|
-
import {
|
|
7
|
+
import { Observable, JsonObject } from '@backstage/types';
|
|
8
|
+
import { TaskSpec } from '@backstage/plugin-scaffolder-common';
|
|
8
9
|
import * as react from 'react';
|
|
9
10
|
import react__default, { ComponentProps, ComponentType } from 'react';
|
|
10
11
|
import { FieldValidation, FieldProps } from '@rjsf/core';
|
|
11
12
|
import { IconButton } from '@material-ui/core';
|
|
12
13
|
|
|
13
14
|
declare type Status = 'open' | 'processing' | 'failed' | 'completed' | 'skipped';
|
|
14
|
-
declare type ScaffolderStep = {
|
|
15
|
-
id: string;
|
|
16
|
-
name: string;
|
|
17
|
-
action: string;
|
|
18
|
-
parameters?: {
|
|
19
|
-
[name: string]: JsonValue;
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
15
|
declare type ScaffolderTask = {
|
|
23
16
|
id: string;
|
|
24
|
-
spec:
|
|
25
|
-
steps: ScaffolderStep[];
|
|
26
|
-
};
|
|
17
|
+
spec: TaskSpec;
|
|
27
18
|
status: 'failed' | 'completed' | 'processing' | 'open' | 'cancelled';
|
|
28
19
|
lastHeartbeatAt: string;
|
|
29
20
|
createdAt: string;
|
|
@@ -62,8 +53,8 @@ interface ScaffolderApi {
|
|
|
62
53
|
* Executes the scaffolding of a component, given a template and its
|
|
63
54
|
* parameter values.
|
|
64
55
|
*
|
|
65
|
-
* @param templateName Name of the Template entity for the scaffolder to use. New project is going to be created out of this template.
|
|
66
|
-
* @param values Parameters for the template, e.g. name, description
|
|
56
|
+
* @param templateName - Name of the Template entity for the scaffolder to use. New project is going to be created out of this template.
|
|
57
|
+
* @param values - Parameters for the template, e.g. name, description
|
|
67
58
|
*/
|
|
68
59
|
scaffold(templateName: string, values: Record<string, any>): Promise<string>;
|
|
69
60
|
getTask(taskId: string): Promise<ScaffolderTask>;
|
|
@@ -103,8 +94,8 @@ declare class ScaffolderClient implements ScaffolderApi {
|
|
|
103
94
|
* Executes the scaffolding of a component, given a template and its
|
|
104
95
|
* parameter values.
|
|
105
96
|
*
|
|
106
|
-
* @param templateName Template name for the scaffolder to use. New project is going to be created out of this template.
|
|
107
|
-
* @param values Parameters for the template, e.g. name, description
|
|
97
|
+
* @param templateName - Template name for the scaffolder to use. New project is going to be created out of this template.
|
|
98
|
+
* @param values - Parameters for the template, e.g. name, description
|
|
108
99
|
*/
|
|
109
100
|
scaffold(templateName: string, values: Record<string, any>): Promise<string>;
|
|
110
101
|
getTask(taskId: string): Promise<any>;
|
|
@@ -151,6 +142,12 @@ declare const ScaffolderPage: ({ TemplateCardComponent, groups }: {
|
|
|
151
142
|
filter: (entity: _backstage_catalog_model.Entity) => boolean;
|
|
152
143
|
}[] | undefined;
|
|
153
144
|
}) => JSX.Element;
|
|
145
|
+
declare const OwnedEntityPickerFieldExtension: () => null;
|
|
146
|
+
/**
|
|
147
|
+
* EntityTagsPickerFieldExtension
|
|
148
|
+
* @public
|
|
149
|
+
*/
|
|
150
|
+
declare const EntityTagsPickerFieldExtension: () => null;
|
|
154
151
|
|
|
155
152
|
declare const EntityNamePicker: ({ schema: { title, description }, ...props }: FieldProps<string>) => JSX.Element;
|
|
156
153
|
|
|
@@ -162,12 +159,20 @@ declare const RepoUrlPicker: ({ onChange, uiSchema, rawErrors, formData, }: Fiel
|
|
|
162
159
|
|
|
163
160
|
declare const TextValuePicker: ({ onChange, required, schema: { title, description }, rawErrors, formData, uiSchema: { "ui:autofocus": autoFocus }, idSchema, placeholder, }: FieldProps<string>) => JSX.Element;
|
|
164
161
|
|
|
162
|
+
declare const OwnedEntityPicker: ({ onChange, schema: { title, description }, required, uiSchema, rawErrors, formData, idSchema, }: FieldProps<string>) => JSX.Element;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* EntityTagsPicker
|
|
166
|
+
* @public
|
|
167
|
+
*/
|
|
168
|
+
declare const EntityTagsPicker: ({ formData, onChange, uiSchema, }: FieldProps<string[]>) => JSX.Element;
|
|
169
|
+
|
|
165
170
|
declare type Props = ComponentProps<typeof IconButton> & {
|
|
166
171
|
entity: Entity;
|
|
167
172
|
};
|
|
168
173
|
/**
|
|
169
174
|
* IconButton for showing if a current entity is starred and adding/removing it from the favourite entities
|
|
170
|
-
* @param props MaterialUI IconButton props extended by required `entity` prop
|
|
175
|
+
* @param props - MaterialUI IconButton props extended by required `entity` prop
|
|
171
176
|
*/
|
|
172
177
|
declare const FavouriteTemplate: (props: Props) => JSX.Element;
|
|
173
178
|
|
|
@@ -185,4 +190,4 @@ declare const TemplateList: ({ TemplateCardComponent, group, }: TemplateListProp
|
|
|
185
190
|
|
|
186
191
|
declare const TemplateTypePicker: () => JSX.Element | null;
|
|
187
192
|
|
|
188
|
-
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 };
|
|
193
|
+
export { CustomFieldValidator, EntityNamePicker, EntityNamePickerFieldExtension, EntityPicker, EntityPickerFieldExtension, EntityTagsPicker, EntityTagsPickerFieldExtension, FavouriteTemplate, FieldExtensionOptions, OwnedEntityPicker, OwnedEntityPickerFieldExtension, 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,4 +1,4 @@
|
|
|
1
|
-
export { a as EntityNamePicker,
|
|
1
|
+
export { a as EntityNamePicker, l as EntityNamePickerFieldExtension, E as EntityPicker, k as EntityPickerFieldExtension, b as EntityTagsPicker, m as EntityTagsPickerFieldExtension, v as FavouriteTemplate, c as OwnedEntityPicker, o as OwnedEntityPickerFieldExtension, O as OwnerPicker, n as OwnerPickerFieldExtension, R as RepoUrlPicker, p as RepoUrlPickerFieldExtension, S as ScaffolderClient, j as ScaffolderFieldExtensions, q as ScaffolderPage, f as TemplateList, T as TemplateTypePicker, u as TextValuePicker, i as createScaffolderFieldExtension, t as plugin, s as scaffolderApiRef, t as scaffolderPlugin } from './esm/index-768702c3.esm.js';
|
|
2
2
|
import '@backstage/catalog-model';
|
|
3
3
|
import '@backstage/integration-react';
|
|
4
4
|
import '@backstage/core-plugin-api';
|
|
@@ -10,12 +10,13 @@ import '@material-ui/core';
|
|
|
10
10
|
import '@material-ui/core/FormControl';
|
|
11
11
|
import '@material-ui/lab/Autocomplete';
|
|
12
12
|
import 'react';
|
|
13
|
+
import 'react-use/lib/useAsync';
|
|
13
14
|
import 'react-use';
|
|
14
|
-
import '@material-ui/
|
|
15
|
-
import '@material-ui/core/InputLabel';
|
|
16
|
-
import '@material-ui/core/Input';
|
|
17
|
-
import '@material-ui/core/FormHelperText';
|
|
15
|
+
import '@material-ui/lab';
|
|
18
16
|
import '@backstage/core-components';
|
|
17
|
+
import '@material-ui/core/FormHelperText';
|
|
18
|
+
import '@material-ui/core/Input';
|
|
19
|
+
import '@material-ui/core/InputLabel';
|
|
19
20
|
import '@material-ui/icons/Star';
|
|
20
21
|
import '@material-ui/icons/StarBorder';
|
|
21
22
|
import '@material-ui/icons/Warning';
|
|
@@ -24,5 +25,4 @@ import 'lodash/capitalize';
|
|
|
24
25
|
import '@material-ui/icons/CheckBox';
|
|
25
26
|
import '@material-ui/icons/CheckBoxOutlineBlank';
|
|
26
27
|
import '@material-ui/icons/ExpandMore';
|
|
27
|
-
import '@material-ui/lab';
|
|
28
28
|
//# sourceMappingURL=index.esm.js.map
|