@backstage/plugin-scaffolder 0.11.12 → 0.11.16
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 +72 -0
- package/dist/esm/{Router-5919d585.esm.js → Router-a8e778fd.esm.js} +124 -74
- package/dist/esm/Router-a8e778fd.esm.js.map +1 -0
- package/dist/esm/{index-9d7df4e7.esm.js → index-f378943f.esm.js} +228 -78
- package/dist/esm/index-f378943f.esm.js.map +1 -0
- package/dist/index.d.ts +34 -19
- package/dist/index.esm.js +5 -6
- package/dist/index.esm.js.map +1 -1
- package/package.json +19 -17
- package/dist/esm/Router-5919d585.esm.js.map +0 -1
- package/dist/esm/index-9d7df4e7.esm.js.map +0 -1
|
@@ -2,19 +2,19 @@ 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 { useAsync } from 'react-use';
|
|
11
|
-
import { KubernetesValidatorFunctions, RELATION_OWNED_BY, stringifyEntityRef } from '@backstage/catalog-model';
|
|
9
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
10
|
+
import { useAsync, useEffectOnce } from 'react-use';
|
|
11
|
+
import { KubernetesValidatorFunctions, makeValidator, RELATION_OWNED_BY, stringifyEntityRef } from '@backstage/catalog-model';
|
|
12
|
+
import { Autocomplete as Autocomplete$1 } from '@material-ui/lab';
|
|
13
|
+
import { Progress, Select, ItemCardHeader, Button, ContentHeader, WarningPanel, Link as Link$1, Content, ItemCardGrid } from '@backstage/core-components';
|
|
12
14
|
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
15
|
import FormHelperText from '@material-ui/core/FormHelperText';
|
|
17
|
-
import
|
|
16
|
+
import Input from '@material-ui/core/Input';
|
|
17
|
+
import InputLabel from '@material-ui/core/InputLabel';
|
|
18
18
|
import Star from '@material-ui/icons/Star';
|
|
19
19
|
import StarBorder from '@material-ui/icons/StarBorder';
|
|
20
20
|
import WarningIcon from '@material-ui/icons/Warning';
|
|
@@ -23,11 +23,9 @@ import capitalize from 'lodash/capitalize';
|
|
|
23
23
|
import CheckBoxIcon from '@material-ui/icons/CheckBox';
|
|
24
24
|
import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
|
|
25
25
|
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
|
26
|
-
import { Autocomplete as Autocomplete$1 } from '@material-ui/lab';
|
|
27
26
|
|
|
28
27
|
const scaffolderApiRef = createApiRef({
|
|
29
|
-
id: "plugin.scaffolder.service"
|
|
30
|
-
description: "Used to make requests towards the scaffolder backend"
|
|
28
|
+
id: "plugin.scaffolder.service"
|
|
31
29
|
});
|
|
32
30
|
class ScaffolderClient {
|
|
33
31
|
constructor(options) {
|
|
@@ -43,17 +41,17 @@ class ScaffolderClient {
|
|
|
43
41
|
...this.scmIntegrationsApi.bitbucket.list(),
|
|
44
42
|
...this.scmIntegrationsApi.github.list(),
|
|
45
43
|
...this.scmIntegrationsApi.gitlab.list()
|
|
46
|
-
].map((c) => ({type: c.type, title: c.title, host: c.config.host})).filter((c) => options.allowedHosts.includes(c.host));
|
|
44
|
+
].map((c) => ({ type: c.type, title: c.title, host: c.config.host })).filter((c) => options.allowedHosts.includes(c.host));
|
|
47
45
|
}
|
|
48
46
|
async getTemplateParameterSchema(templateName) {
|
|
49
|
-
const {namespace, kind, name} = templateName;
|
|
47
|
+
const { namespace, kind, name } = templateName;
|
|
50
48
|
const token = await this.identityApi.getIdToken();
|
|
51
49
|
const baseUrl = await this.discoveryApi.getBaseUrl("scaffolder");
|
|
52
50
|
const templatePath = [namespace, kind, name].map((s) => encodeURIComponent(s)).join("/");
|
|
53
51
|
const url = `${baseUrl}/v2/templates/${templatePath}/parameter-schema`;
|
|
54
52
|
const response = await fetch(url, {
|
|
55
53
|
headers: {
|
|
56
|
-
...token && {Authorization: `Bearer ${token}`}
|
|
54
|
+
...token && { Authorization: `Bearer ${token}` }
|
|
57
55
|
}
|
|
58
56
|
});
|
|
59
57
|
if (!response.ok) {
|
|
@@ -69,16 +67,16 @@ class ScaffolderClient {
|
|
|
69
67
|
method: "POST",
|
|
70
68
|
headers: {
|
|
71
69
|
"Content-Type": "application/json",
|
|
72
|
-
...token && {Authorization: `Bearer ${token}`}
|
|
70
|
+
...token && { Authorization: `Bearer ${token}` }
|
|
73
71
|
},
|
|
74
|
-
body: JSON.stringify({templateName, values: {...values}})
|
|
72
|
+
body: JSON.stringify({ templateName, values: { ...values } })
|
|
75
73
|
});
|
|
76
74
|
if (response.status !== 201) {
|
|
77
75
|
const status = `${response.status} ${response.statusText}`;
|
|
78
76
|
const body = await response.text();
|
|
79
77
|
throw new Error(`Backend request failed, ${status} ${body.trim()}`);
|
|
80
78
|
}
|
|
81
|
-
const {id} = await response.json();
|
|
79
|
+
const { id } = await response.json();
|
|
82
80
|
return id;
|
|
83
81
|
}
|
|
84
82
|
async getTask(taskId) {
|
|
@@ -86,7 +84,7 @@ class ScaffolderClient {
|
|
|
86
84
|
const baseUrl = await this.discoveryApi.getBaseUrl("scaffolder");
|
|
87
85
|
const url = `${baseUrl}/v2/tasks/${encodeURIComponent(taskId)}`;
|
|
88
86
|
const response = await fetch(url, {
|
|
89
|
-
headers: token ? {Authorization: `Bearer ${token}`} : {}
|
|
87
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
|
90
88
|
});
|
|
91
89
|
if (!response.ok) {
|
|
92
90
|
throw await ResponseError.fromResponse(response);
|
|
@@ -110,7 +108,7 @@ class ScaffolderClient {
|
|
|
110
108
|
}
|
|
111
109
|
this.discoveryApi.getBaseUrl("scaffolder").then((baseUrl) => {
|
|
112
110
|
const url = `${baseUrl}/v2/tasks/${encodeURIComponent(taskId)}/eventstream`;
|
|
113
|
-
const eventSource = new EventSource(url, {withCredentials: true});
|
|
111
|
+
const eventSource = new EventSource(url, { withCredentials: true });
|
|
114
112
|
eventSource.addEventListener("log", (event) => {
|
|
115
113
|
if (event.data) {
|
|
116
114
|
try {
|
|
@@ -147,7 +145,7 @@ class ScaffolderClient {
|
|
|
147
145
|
return new ObservableImpl((subscriber) => {
|
|
148
146
|
this.discoveryApi.getBaseUrl("scaffolder").then(async (baseUrl) => {
|
|
149
147
|
while (!subscriber.closed) {
|
|
150
|
-
const url = `${baseUrl}/v2/tasks/${encodeURIComponent(taskId)}/events?${qs.stringify({after})}`;
|
|
148
|
+
const url = `${baseUrl}/v2/tasks/${encodeURIComponent(taskId)}/events?${qs.stringify({ after })}`;
|
|
151
149
|
const response = await fetch(url);
|
|
152
150
|
if (!response.ok) {
|
|
153
151
|
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
@@ -170,7 +168,7 @@ class ScaffolderClient {
|
|
|
170
168
|
const baseUrl = await this.discoveryApi.getBaseUrl("scaffolder");
|
|
171
169
|
const token = await this.identityApi.getIdToken();
|
|
172
170
|
const response = await fetch(`${baseUrl}/v2/actions`, {
|
|
173
|
-
headers: token ? {Authorization: `Bearer ${token}`} : {}
|
|
171
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
|
174
172
|
});
|
|
175
173
|
if (!response.ok) {
|
|
176
174
|
throw await ResponseError.fromResponse(response);
|
|
@@ -179,9 +177,13 @@ class ScaffolderClient {
|
|
|
179
177
|
}
|
|
180
178
|
}
|
|
181
179
|
|
|
180
|
+
const allowArbitraryValues = (uiSchema) => {
|
|
181
|
+
var _a, _b;
|
|
182
|
+
return (_b = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.allowArbitraryValues) != null ? _b : true;
|
|
183
|
+
};
|
|
182
184
|
const EntityPicker = ({
|
|
183
185
|
onChange,
|
|
184
|
-
schema: {title = "Entity", description = "An entity from the catalog"},
|
|
186
|
+
schema: { title = "Entity", description = "An entity from the catalog" },
|
|
185
187
|
required,
|
|
186
188
|
uiSchema,
|
|
187
189
|
rawErrors,
|
|
@@ -192,23 +194,29 @@ const EntityPicker = ({
|
|
|
192
194
|
const allowedKinds = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.allowedKinds;
|
|
193
195
|
const defaultKind = (_b = uiSchema["ui:options"]) == null ? void 0 : _b.defaultKind;
|
|
194
196
|
const catalogApi = useApi(catalogApiRef);
|
|
195
|
-
const {value: entities, loading} = useAsync(() => catalogApi.getEntities(allowedKinds ? {filter: {kind: allowedKinds}} : void 0));
|
|
196
|
-
const entityRefs = entities == null ? void 0 : entities.items.map((e) => formatEntityRefTitle(e, {defaultKind}));
|
|
197
|
-
const onSelect = (_, value) => {
|
|
197
|
+
const { value: entities, loading } = useAsync(() => catalogApi.getEntities(allowedKinds ? { filter: { kind: allowedKinds } } : void 0));
|
|
198
|
+
const entityRefs = entities == null ? void 0 : entities.items.map((e) => formatEntityRefTitle(e, { defaultKind }));
|
|
199
|
+
const onSelect = useCallback((_, value) => {
|
|
198
200
|
onChange(value || "");
|
|
199
|
-
};
|
|
201
|
+
}, [onChange]);
|
|
202
|
+
useEffect(() => {
|
|
203
|
+
if ((entityRefs == null ? void 0 : entityRefs.length) === 1) {
|
|
204
|
+
onChange(entityRefs[0]);
|
|
205
|
+
}
|
|
206
|
+
}, [entityRefs, onChange]);
|
|
200
207
|
return /* @__PURE__ */ React.createElement(FormControl, {
|
|
201
208
|
margin: "normal",
|
|
202
209
|
required,
|
|
203
210
|
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !formData
|
|
204
211
|
}, /* @__PURE__ */ React.createElement(Autocomplete, {
|
|
212
|
+
disabled: (entityRefs == null ? void 0 : entityRefs.length) === 1,
|
|
205
213
|
id: idSchema == null ? void 0 : idSchema.$id,
|
|
206
214
|
value: formData || "",
|
|
207
215
|
loading,
|
|
208
216
|
onChange: onSelect,
|
|
209
217
|
options: entityRefs || [],
|
|
210
218
|
autoSelect: true,
|
|
211
|
-
freeSolo:
|
|
219
|
+
freeSolo: allowArbitraryValues(uiSchema),
|
|
212
220
|
renderInput: (params) => /* @__PURE__ */ React.createElement(TextField, {
|
|
213
221
|
...params,
|
|
214
222
|
label: title,
|
|
@@ -224,10 +232,10 @@ const EntityPicker = ({
|
|
|
224
232
|
const TextValuePicker = ({
|
|
225
233
|
onChange,
|
|
226
234
|
required,
|
|
227
|
-
schema: {title, description},
|
|
235
|
+
schema: { title, description },
|
|
228
236
|
rawErrors,
|
|
229
237
|
formData,
|
|
230
|
-
uiSchema: {"ui:autofocus": autoFocus},
|
|
238
|
+
uiSchema: { "ui:autofocus": autoFocus },
|
|
231
239
|
idSchema,
|
|
232
240
|
placeholder
|
|
233
241
|
}) => /* @__PURE__ */ React.createElement(TextField, {
|
|
@@ -237,17 +245,17 @@ const TextValuePicker = ({
|
|
|
237
245
|
helperText: description,
|
|
238
246
|
required,
|
|
239
247
|
value: formData != null ? formData : "",
|
|
240
|
-
onChange: ({target: {value}}) => onChange(value),
|
|
248
|
+
onChange: ({ target: { value } }) => onChange(value),
|
|
241
249
|
margin: "normal",
|
|
242
250
|
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !formData,
|
|
243
|
-
inputProps: {autoFocus}
|
|
251
|
+
inputProps: { autoFocus }
|
|
244
252
|
});
|
|
245
253
|
|
|
246
254
|
const EntityNamePicker = ({
|
|
247
|
-
schema: {title = "Name", description = "Unique name of the component"},
|
|
255
|
+
schema: { title = "Name", description = "Unique name of the component" },
|
|
248
256
|
...props
|
|
249
257
|
}) => /* @__PURE__ */ React.createElement(TextValuePicker, {
|
|
250
|
-
schema: {title, description},
|
|
258
|
+
schema: { title, description },
|
|
251
259
|
...props
|
|
252
260
|
});
|
|
253
261
|
|
|
@@ -257,8 +265,70 @@ const entityNamePickerValidation = (value, validation) => {
|
|
|
257
265
|
}
|
|
258
266
|
};
|
|
259
267
|
|
|
268
|
+
const EntityTagsPicker = ({
|
|
269
|
+
formData,
|
|
270
|
+
onChange,
|
|
271
|
+
uiSchema
|
|
272
|
+
}) => {
|
|
273
|
+
var _a;
|
|
274
|
+
const catalogApi = useApi(catalogApiRef);
|
|
275
|
+
const [inputValue, setInputValue] = useState("");
|
|
276
|
+
const [inputError, setInputError] = useState(false);
|
|
277
|
+
const tagValidator = makeValidator().isValidTag;
|
|
278
|
+
const kinds = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.kinds;
|
|
279
|
+
const { loading, value: existingTags } = useAsync(async () => {
|
|
280
|
+
const tagsRequest = { fields: ["metadata.tags"] };
|
|
281
|
+
if (kinds) {
|
|
282
|
+
tagsRequest.filter = { kind: kinds };
|
|
283
|
+
}
|
|
284
|
+
const entities = await catalogApi.getEntities(tagsRequest);
|
|
285
|
+
return [
|
|
286
|
+
...new Set(entities.items.flatMap((e) => {
|
|
287
|
+
var _a2;
|
|
288
|
+
return (_a2 = e.metadata) == null ? void 0 : _a2.tags;
|
|
289
|
+
}).filter(Boolean))
|
|
290
|
+
].sort();
|
|
291
|
+
});
|
|
292
|
+
const setTags = (_, values) => {
|
|
293
|
+
let hasError = false;
|
|
294
|
+
let addDuplicate = false;
|
|
295
|
+
const currentTags = formData || [];
|
|
296
|
+
if ((values == null ? void 0 : values.length) && currentTags.length < values.length) {
|
|
297
|
+
const newTag = values[values.length - 1] = values[values.length - 1].toLocaleLowerCase("en-US").trim();
|
|
298
|
+
hasError = !tagValidator(newTag);
|
|
299
|
+
addDuplicate = currentTags.indexOf(newTag) !== -1;
|
|
300
|
+
}
|
|
301
|
+
setInputError(hasError);
|
|
302
|
+
setInputValue(!hasError ? "" : inputValue);
|
|
303
|
+
if (!hasError && !addDuplicate) {
|
|
304
|
+
onChange(values || []);
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
useEffectOnce(() => onChange(formData || []));
|
|
308
|
+
return /* @__PURE__ */ React.createElement(FormControl$1, {
|
|
309
|
+
margin: "normal"
|
|
310
|
+
}, /* @__PURE__ */ React.createElement(Autocomplete$1, {
|
|
311
|
+
multiple: true,
|
|
312
|
+
freeSolo: true,
|
|
313
|
+
filterSelectedOptions: true,
|
|
314
|
+
onChange: setTags,
|
|
315
|
+
value: formData || [],
|
|
316
|
+
inputValue,
|
|
317
|
+
loading,
|
|
318
|
+
options: existingTags || [],
|
|
319
|
+
ChipProps: { size: "small" },
|
|
320
|
+
renderInput: (params) => /* @__PURE__ */ React.createElement(TextField, {
|
|
321
|
+
...params,
|
|
322
|
+
label: "Tags",
|
|
323
|
+
onChange: (e) => setInputValue(e.target.value),
|
|
324
|
+
error: inputError,
|
|
325
|
+
helperText: "Add any relevant tags, hit 'Enter' to add new tags. Valid format: [a-z0-9+#] separated by [-], at most 63 characters"
|
|
326
|
+
})
|
|
327
|
+
}));
|
|
328
|
+
};
|
|
329
|
+
|
|
260
330
|
const OwnerPicker = ({
|
|
261
|
-
schema: {title = "Owner", description = "The owner of the component"},
|
|
331
|
+
schema: { title = "Owner", description = "The owner of the component" },
|
|
262
332
|
uiSchema,
|
|
263
333
|
...props
|
|
264
334
|
}) => {
|
|
@@ -275,12 +345,12 @@ const OwnerPicker = ({
|
|
|
275
345
|
};
|
|
276
346
|
return /* @__PURE__ */ React.createElement(EntityPicker, {
|
|
277
347
|
...props,
|
|
278
|
-
schema: {title, description},
|
|
348
|
+
schema: { title, description },
|
|
279
349
|
uiSchema: ownerUiSchema
|
|
280
350
|
});
|
|
281
351
|
};
|
|
282
352
|
|
|
283
|
-
function splitFormData(url) {
|
|
353
|
+
function splitFormData(url, allowedOwners) {
|
|
284
354
|
let host = void 0;
|
|
285
355
|
let owner = void 0;
|
|
286
356
|
let repo = void 0;
|
|
@@ -291,7 +361,7 @@ function splitFormData(url) {
|
|
|
291
361
|
if (url) {
|
|
292
362
|
const parsed = new URL(`https://${url}`);
|
|
293
363
|
host = parsed.host;
|
|
294
|
-
owner = parsed.searchParams.get("owner") || void 0;
|
|
364
|
+
owner = parsed.searchParams.get("owner") || (allowedOwners == null ? void 0 : allowedOwners[0]);
|
|
295
365
|
repo = parsed.searchParams.get("repo") || void 0;
|
|
296
366
|
organization = parsed.searchParams.get("organization") || void 0;
|
|
297
367
|
workspace = parsed.searchParams.get("workspace") || void 0;
|
|
@@ -299,7 +369,7 @@ function splitFormData(url) {
|
|
|
299
369
|
}
|
|
300
370
|
} catch {
|
|
301
371
|
}
|
|
302
|
-
return {host, owner, repo, organization, workspace, project};
|
|
372
|
+
return { host, owner, repo, organization, workspace, project };
|
|
303
373
|
}
|
|
304
374
|
function serializeFormData(data) {
|
|
305
375
|
if (!data.host) {
|
|
@@ -329,17 +399,18 @@ const RepoUrlPicker = ({
|
|
|
329
399
|
rawErrors,
|
|
330
400
|
formData
|
|
331
401
|
}) => {
|
|
332
|
-
var _a, _b, _c;
|
|
402
|
+
var _a, _b, _c, _d, _e;
|
|
333
403
|
const scaffolderApi = useApi(scaffolderApiRef);
|
|
334
404
|
const integrationApi = useApi(scmIntegrationsApiRef);
|
|
335
405
|
const allowedHosts = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.allowedHosts;
|
|
336
|
-
const
|
|
337
|
-
|
|
406
|
+
const allowedOwners = (_b = uiSchema["ui:options"]) == null ? void 0 : _b.allowedOwners;
|
|
407
|
+
const { value: integrations, loading } = useAsync(async () => {
|
|
408
|
+
return await scaffolderApi.getIntegrationsList({ allowedHosts });
|
|
338
409
|
});
|
|
339
|
-
const {host, owner, repo, organization, workspace, project} = splitFormData(formData);
|
|
340
|
-
const updateHost = useCallback((
|
|
410
|
+
const { host, owner, repo, organization, workspace, project } = splitFormData(formData, allowedOwners);
|
|
411
|
+
const updateHost = useCallback((value) => {
|
|
341
412
|
onChange(serializeFormData({
|
|
342
|
-
host:
|
|
413
|
+
host: value,
|
|
343
414
|
owner,
|
|
344
415
|
repo,
|
|
345
416
|
organization,
|
|
@@ -347,6 +418,14 @@ const RepoUrlPicker = ({
|
|
|
347
418
|
project
|
|
348
419
|
}));
|
|
349
420
|
}, [onChange, owner, repo, organization, workspace, project]);
|
|
421
|
+
const updateOwnerSelect = useCallback((value) => onChange(serializeFormData({
|
|
422
|
+
host,
|
|
423
|
+
owner: value,
|
|
424
|
+
repo,
|
|
425
|
+
organization,
|
|
426
|
+
workspace,
|
|
427
|
+
project
|
|
428
|
+
})), [onChange, host, repo, organization, workspace, project]);
|
|
350
429
|
const updateOwner = useCallback((evt) => onChange(serializeFormData({
|
|
351
430
|
host,
|
|
352
431
|
owner: evt.target.value,
|
|
@@ -411,21 +490,20 @@ const RepoUrlPicker = ({
|
|
|
411
490
|
if (loading) {
|
|
412
491
|
return /* @__PURE__ */ React.createElement(Progress, null);
|
|
413
492
|
}
|
|
493
|
+
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" }];
|
|
494
|
+
const ownersOptions = allowedOwners ? allowedOwners.map((i) => ({ label: i, value: i })) : [{ label: "Loading...", value: "loading" }];
|
|
414
495
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(FormControl, {
|
|
415
496
|
margin: "normal",
|
|
416
497
|
required: true,
|
|
417
498
|
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !host
|
|
418
|
-
}, /* @__PURE__ */ React.createElement(
|
|
419
|
-
htmlFor: "hostInput"
|
|
420
|
-
}, "Host"), /* @__PURE__ */ React.createElement(Select, {
|
|
499
|
+
}, /* @__PURE__ */ React.createElement(Select, {
|
|
421
500
|
native: true,
|
|
422
|
-
|
|
501
|
+
disabled: hostsOptions.length === 1,
|
|
502
|
+
label: "Host",
|
|
423
503
|
onChange: updateHost,
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
value: i.host
|
|
428
|
-
}, 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, {
|
|
504
|
+
selected: host,
|
|
505
|
+
items: hostsOptions
|
|
506
|
+
}), /* @__PURE__ */ React.createElement(FormHelperText, null, "The host where the repository will be created")), host === "dev.azure.com" && /* @__PURE__ */ React.createElement(FormControl, {
|
|
429
507
|
margin: "normal",
|
|
430
508
|
required: true,
|
|
431
509
|
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !organization
|
|
@@ -435,7 +513,7 @@ const RepoUrlPicker = ({
|
|
|
435
513
|
id: "repoInput",
|
|
436
514
|
onChange: updateOrganization,
|
|
437
515
|
value: organization
|
|
438
|
-
}), /* @__PURE__ */ React.createElement(FormHelperText, null, "The name of the organization")), host && ((
|
|
516
|
+
}), /* @__PURE__ */ React.createElement(FormHelperText, null, "The name of the organization")), host && ((_c = integrationApi.byHost(host)) == null ? void 0 : _c.type) === "bitbucket" && /* @__PURE__ */ React.createElement(React.Fragment, null, host === "bitbucket.org" && /* @__PURE__ */ React.createElement(FormControl, {
|
|
439
517
|
margin: "normal",
|
|
440
518
|
required: true,
|
|
441
519
|
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !workspace
|
|
@@ -455,7 +533,7 @@ const RepoUrlPicker = ({
|
|
|
455
533
|
id: "wokrspaceInput",
|
|
456
534
|
onChange: updateProject,
|
|
457
535
|
value: project
|
|
458
|
-
}), /* @__PURE__ */ React.createElement(FormHelperText, null, "The project where the repository will be created"))), host && ((
|
|
536
|
+
}), /* @__PURE__ */ React.createElement(FormHelperText, null, "The project where the repository will be created"))), host && ((_d = integrationApi.byHost(host)) == null ? void 0 : _d.type) !== "bitbucket" && !allowedOwners && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(FormControl, {
|
|
459
537
|
margin: "normal",
|
|
460
538
|
required: true,
|
|
461
539
|
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !owner
|
|
@@ -465,6 +543,17 @@ const RepoUrlPicker = ({
|
|
|
465
543
|
id: "ownerInput",
|
|
466
544
|
onChange: updateOwner,
|
|
467
545
|
value: owner
|
|
546
|
+
}), /* @__PURE__ */ React.createElement(FormHelperText, null, "The organization, user or project that this repo will belong to"))), host && ((_e = integrationApi.byHost(host)) == null ? void 0 : _e.type) !== "bitbucket" && allowedOwners && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(FormControl, {
|
|
547
|
+
margin: "normal",
|
|
548
|
+
required: true,
|
|
549
|
+
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !owner
|
|
550
|
+
}, /* @__PURE__ */ React.createElement(Select, {
|
|
551
|
+
native: true,
|
|
552
|
+
label: "Owner Available",
|
|
553
|
+
onChange: updateOwnerSelect,
|
|
554
|
+
disabled: ownersOptions.length === 1,
|
|
555
|
+
selected: owner,
|
|
556
|
+
items: ownersOptions
|
|
468
557
|
}), /* @__PURE__ */ React.createElement(FormHelperText, null, "The organization, user or project that this repo will belong to"))), /* @__PURE__ */ React.createElement(FormControl, {
|
|
469
558
|
margin: "normal",
|
|
470
559
|
required: true,
|
|
@@ -481,7 +570,7 @@ const RepoUrlPicker = ({
|
|
|
481
570
|
const repoPickerValidation = (value, validation, context) => {
|
|
482
571
|
var _a;
|
|
483
572
|
try {
|
|
484
|
-
const {host, searchParams} = new URL(`https://${value}`);
|
|
573
|
+
const { host, searchParams } = new URL(`https://${value}`);
|
|
485
574
|
const integrationApi = context.apiHolder.get(scmIntegrationsApiRef);
|
|
486
575
|
if (!host) {
|
|
487
576
|
validation.addError("Incomplete repository location provided, host not provided");
|
|
@@ -507,6 +596,47 @@ const repoPickerValidation = (value, validation, context) => {
|
|
|
507
596
|
}
|
|
508
597
|
};
|
|
509
598
|
|
|
599
|
+
const OwnedEntityPicker = ({
|
|
600
|
+
onChange,
|
|
601
|
+
schema: { title = "Entity", description = "An entity from the catalog" },
|
|
602
|
+
required,
|
|
603
|
+
uiSchema,
|
|
604
|
+
rawErrors,
|
|
605
|
+
formData,
|
|
606
|
+
idSchema
|
|
607
|
+
}) => {
|
|
608
|
+
var _a, _b;
|
|
609
|
+
const allowedKinds = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.allowedKinds;
|
|
610
|
+
const defaultKind = (_b = uiSchema["ui:options"]) == null ? void 0 : _b.defaultKind;
|
|
611
|
+
const { ownedEntities, loading } = useOwnedEntities(allowedKinds);
|
|
612
|
+
const entityRefs = ownedEntities == null ? void 0 : ownedEntities.items.map((e) => formatEntityRefTitle(e, { defaultKind })).filter((n) => n);
|
|
613
|
+
const onSelect = (_, value) => {
|
|
614
|
+
onChange(value || "");
|
|
615
|
+
};
|
|
616
|
+
return /* @__PURE__ */ React.createElement(FormControl, {
|
|
617
|
+
margin: "normal",
|
|
618
|
+
required,
|
|
619
|
+
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !formData
|
|
620
|
+
}, /* @__PURE__ */ React.createElement(Autocomplete, {
|
|
621
|
+
id: idSchema == null ? void 0 : idSchema.$id,
|
|
622
|
+
value: formData || "",
|
|
623
|
+
loading,
|
|
624
|
+
onChange: onSelect,
|
|
625
|
+
options: entityRefs || [],
|
|
626
|
+
autoSelect: true,
|
|
627
|
+
freeSolo: true,
|
|
628
|
+
renderInput: (params) => /* @__PURE__ */ React.createElement(TextField, {
|
|
629
|
+
...params,
|
|
630
|
+
label: title,
|
|
631
|
+
margin: "normal",
|
|
632
|
+
helperText: description,
|
|
633
|
+
variant: "outlined",
|
|
634
|
+
required,
|
|
635
|
+
InputProps: params.InputProps
|
|
636
|
+
})
|
|
637
|
+
}));
|
|
638
|
+
};
|
|
639
|
+
|
|
510
640
|
const FIELD_EXTENSION_WRAPPER_KEY = "scaffolder.extensions.wrapper.v1";
|
|
511
641
|
const FIELD_EXTENSION_KEY = "scaffolder.extensions.field.v1";
|
|
512
642
|
function createScaffolderFieldExtension(options) {
|
|
@@ -539,7 +669,7 @@ const scaffolderPlugin = createPlugin({
|
|
|
539
669
|
identityApi: identityApiRef,
|
|
540
670
|
scmIntegrationsApi: scmIntegrationsApiRef
|
|
541
671
|
},
|
|
542
|
-
factory: ({discoveryApi, identityApi, scmIntegrationsApi}) => new ScaffolderClient({discoveryApi, identityApi, scmIntegrationsApi})
|
|
672
|
+
factory: ({ discoveryApi, identityApi, scmIntegrationsApi }) => new ScaffolderClient({ discoveryApi, identityApi, scmIntegrationsApi })
|
|
543
673
|
})
|
|
544
674
|
],
|
|
545
675
|
routes: {
|
|
@@ -569,9 +699,17 @@ const OwnerPickerFieldExtension = scaffolderPlugin.provide(createScaffolderField
|
|
|
569
699
|
}));
|
|
570
700
|
const ScaffolderPage = scaffolderPlugin.provide(createRoutableExtension({
|
|
571
701
|
name: "ScaffolderPage",
|
|
572
|
-
component: () => import('./Router-
|
|
702
|
+
component: () => import('./Router-a8e778fd.esm.js').then((m) => m.Router),
|
|
573
703
|
mountPoint: rootRouteRef
|
|
574
704
|
}));
|
|
705
|
+
const OwnedEntityPickerFieldExtension = scaffolderPlugin.provide(createScaffolderFieldExtension({
|
|
706
|
+
component: OwnedEntityPicker,
|
|
707
|
+
name: "OwnedEntityPicker"
|
|
708
|
+
}));
|
|
709
|
+
const EntityTagsPickerFieldExtension = scaffolderPlugin.provide(createScaffolderFieldExtension({
|
|
710
|
+
component: EntityTagsPicker,
|
|
711
|
+
name: "EntityTagsPicker"
|
|
712
|
+
}));
|
|
575
713
|
|
|
576
714
|
const YellowStar = withStyles({
|
|
577
715
|
root: {
|
|
@@ -595,7 +733,7 @@ const favouriteTemplateTooltip = (isStarred) => isStarred ? "Remove from favorit
|
|
|
595
733
|
const favouriteTemplateIcon = (isStarred) => isStarred ? /* @__PURE__ */ React.createElement(YellowStar, null) : /* @__PURE__ */ React.createElement(WhiteBorderStar, null);
|
|
596
734
|
const FavouriteTemplate = (props) => {
|
|
597
735
|
const classes = useStyles$1();
|
|
598
|
-
const {toggleStarredEntity, isStarredEntity} = useStarredEntity(props.entity);
|
|
736
|
+
const { toggleStarredEntity, isStarredEntity } = useStarredEntity(props.entity);
|
|
599
737
|
return /* @__PURE__ */ React.createElement(IconButton, {
|
|
600
738
|
color: "inherit",
|
|
601
739
|
className: classes.starButton,
|
|
@@ -611,7 +749,7 @@ const useStyles = makeStyles((theme) => ({
|
|
|
611
749
|
position: "relative"
|
|
612
750
|
},
|
|
613
751
|
title: {
|
|
614
|
-
backgroundImage: ({backgroundImage}) => backgroundImage
|
|
752
|
+
backgroundImage: ({ backgroundImage }) => backgroundImage
|
|
615
753
|
},
|
|
616
754
|
box: {
|
|
617
755
|
overflow: "hidden",
|
|
@@ -659,7 +797,7 @@ const getTemplateCardProps = (template) => {
|
|
|
659
797
|
const DeprecationWarning = () => {
|
|
660
798
|
const styles = useDeprecationStyles();
|
|
661
799
|
const Title = /* @__PURE__ */ React.createElement(Typography, {
|
|
662
|
-
style: {padding: 10, maxWidth: 300}
|
|
800
|
+
style: { padding: 10, maxWidth: 300 }
|
|
663
801
|
}, "This template syntax is deprecated. Click for more info.");
|
|
664
802
|
return /* @__PURE__ */ React.createElement("div", {
|
|
665
803
|
className: styles.deprecationIcon
|
|
@@ -670,15 +808,15 @@ const DeprecationWarning = () => {
|
|
|
670
808
|
className: styles.link
|
|
671
809
|
}, /* @__PURE__ */ React.createElement(WarningIcon, null))));
|
|
672
810
|
};
|
|
673
|
-
const TemplateCard = ({template, deprecated}) => {
|
|
811
|
+
const TemplateCard = ({ template, deprecated }) => {
|
|
674
812
|
var _a;
|
|
675
813
|
const backstageTheme = useTheme();
|
|
676
814
|
const rootLink = useRouteRef(rootRouteRef);
|
|
677
815
|
const templateProps = getTemplateCardProps(template);
|
|
678
816
|
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});
|
|
817
|
+
const themeId = backstageTheme.getPageTheme({ themeId: templateProps.type }) ? templateProps.type : "other";
|
|
818
|
+
const theme = backstageTheme.getPageTheme({ themeId });
|
|
819
|
+
const classes = useStyles({ backgroundImage: theme.backgroundImage });
|
|
682
820
|
const href = generatePath(`${rootLink()}/templates/:templateName`, {
|
|
683
821
|
templateName: templateProps.name
|
|
684
822
|
});
|
|
@@ -691,9 +829,9 @@ const TemplateCard = ({template, deprecated}) => {
|
|
|
691
829
|
}), deprecated && /* @__PURE__ */ React.createElement(DeprecationWarning, null), /* @__PURE__ */ React.createElement(ItemCardHeader, {
|
|
692
830
|
title: templateProps.title,
|
|
693
831
|
subtitle: templateProps.type,
|
|
694
|
-
classes: {root: classes.title}
|
|
832
|
+
classes: { root: classes.title }
|
|
695
833
|
})), /* @__PURE__ */ React.createElement(CardContent, {
|
|
696
|
-
style: {display: "grid"}
|
|
834
|
+
style: { display: "grid" }
|
|
697
835
|
}, /* @__PURE__ */ React.createElement(Box, {
|
|
698
836
|
className: classes.box
|
|
699
837
|
}, /* @__PURE__ */ React.createElement(Typography, {
|
|
@@ -726,19 +864,31 @@ const TemplateCard = ({template, deprecated}) => {
|
|
|
726
864
|
}, "Choose")));
|
|
727
865
|
};
|
|
728
866
|
|
|
729
|
-
const TemplateList = ({
|
|
730
|
-
|
|
867
|
+
const TemplateList = ({
|
|
868
|
+
TemplateCardComponent,
|
|
869
|
+
group
|
|
870
|
+
}) => {
|
|
871
|
+
const { loading, error, entities } = useEntityListProvider();
|
|
731
872
|
const Card = TemplateCardComponent || TemplateCard;
|
|
873
|
+
const maybeFilteredEntities = group ? entities.filter((e) => group.filter(e)) : entities;
|
|
874
|
+
const title = group ? group.titleComponent || /* @__PURE__ */ React.createElement(ContentHeader, {
|
|
875
|
+
title: group.title
|
|
876
|
+
}) : /* @__PURE__ */ React.createElement(ContentHeader, {
|
|
877
|
+
title: "Other Templates"
|
|
878
|
+
});
|
|
879
|
+
if (group && maybeFilteredEntities.length === 0) {
|
|
880
|
+
return null;
|
|
881
|
+
}
|
|
732
882
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, loading && /* @__PURE__ */ React.createElement(Progress, null), error && /* @__PURE__ */ React.createElement(WarningPanel, {
|
|
733
883
|
title: "Oops! Something went wrong loading the templates"
|
|
734
884
|
}, error.message), !error && !loading && !entities.length && /* @__PURE__ */ React.createElement(Typography, {
|
|
735
885
|
variant: "body2"
|
|
736
|
-
}, "No templates found that match your filter. Learn more about", " ", /* @__PURE__ */ React.createElement(Link, {
|
|
737
|
-
|
|
738
|
-
}, "adding templates"), "."), /* @__PURE__ */ React.createElement(ItemCardGrid, null,
|
|
886
|
+
}, "No templates found that match your filter. Learn more about", " ", /* @__PURE__ */ React.createElement(Link$1, {
|
|
887
|
+
to: "https://backstage.io/docs/features/software-templates/adding-templates"
|
|
888
|
+
}, "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, {
|
|
739
889
|
key: stringifyEntityRef(template),
|
|
740
890
|
template
|
|
741
|
-
}))));
|
|
891
|
+
})))));
|
|
742
892
|
};
|
|
743
893
|
|
|
744
894
|
const icon = /* @__PURE__ */ React.createElement(CheckBoxOutlineBlankIcon, {
|
|
@@ -749,7 +899,7 @@ const checkedIcon = /* @__PURE__ */ React.createElement(CheckBoxIcon, {
|
|
|
749
899
|
});
|
|
750
900
|
const TemplateTypePicker = () => {
|
|
751
901
|
const alertApi = useApi(alertApiRef);
|
|
752
|
-
const {error, loading, availableTypes, selectedTypes, setSelectedTypes} = useEntityTypeFilter();
|
|
902
|
+
const { error, loading, availableTypes, selectedTypes, setSelectedTypes } = useEntityTypeFilter();
|
|
753
903
|
if (loading)
|
|
754
904
|
return /* @__PURE__ */ React.createElement(Progress, null);
|
|
755
905
|
if (!availableTypes)
|
|
@@ -772,7 +922,7 @@ const TemplateTypePicker = () => {
|
|
|
772
922
|
options: availableTypes,
|
|
773
923
|
value: selectedTypes,
|
|
774
924
|
onChange: (_, value) => setSelectedTypes(value),
|
|
775
|
-
renderOption: (option, {selected}) => /* @__PURE__ */ React.createElement(FormControlLabel, {
|
|
925
|
+
renderOption: (option, { selected }) => /* @__PURE__ */ React.createElement(FormControlLabel, {
|
|
776
926
|
control: /* @__PURE__ */ React.createElement(Checkbox, {
|
|
777
927
|
icon,
|
|
778
928
|
checkedIcon,
|
|
@@ -791,5 +941,5 @@ const TemplateTypePicker = () => {
|
|
|
791
941
|
}));
|
|
792
942
|
};
|
|
793
943
|
|
|
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,
|
|
795
|
-
//# sourceMappingURL=index-
|
|
944
|
+
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 };
|
|
945
|
+
//# sourceMappingURL=index-f378943f.esm.js.map
|