@backstage/plugin-scaffolder 0.11.13 → 0.11.17
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 +58 -0
- package/dist/esm/{Router-8b2aa17e.esm.js → Router-552fb2ce.esm.js} +107 -70
- package/dist/esm/Router-552fb2ce.esm.js.map +1 -0
- package/dist/esm/{index-385b3fc1.esm.js → index-768702c3.esm.js} +214 -75
- package/dist/esm/index-768702c3.esm.js.map +1 -0
- package/dist/index.d.ts +18 -13
- package/dist/index.esm.js +6 -6
- package/package.json +18 -16
- package/dist/esm/Router-8b2aa17e.esm.js.map +0 -1
- package/dist/esm/index-385b3fc1.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) {
|
|
@@ -43,17 +42,17 @@ class ScaffolderClient {
|
|
|
43
42
|
...this.scmIntegrationsApi.bitbucket.list(),
|
|
44
43
|
...this.scmIntegrationsApi.github.list(),
|
|
45
44
|
...this.scmIntegrationsApi.gitlab.list()
|
|
46
|
-
].map((c) => ({type: c.type, title: c.title, host: c.config.host})).filter((c) => options.allowedHosts.includes(c.host));
|
|
45
|
+
].map((c) => ({ type: c.type, title: c.title, host: c.config.host })).filter((c) => options.allowedHosts.includes(c.host));
|
|
47
46
|
}
|
|
48
47
|
async getTemplateParameterSchema(templateName) {
|
|
49
|
-
const {namespace, kind, name} = templateName;
|
|
48
|
+
const { namespace, kind, name } = templateName;
|
|
50
49
|
const token = await this.identityApi.getIdToken();
|
|
51
50
|
const baseUrl = await this.discoveryApi.getBaseUrl("scaffolder");
|
|
52
51
|
const templatePath = [namespace, kind, name].map((s) => encodeURIComponent(s)).join("/");
|
|
53
52
|
const url = `${baseUrl}/v2/templates/${templatePath}/parameter-schema`;
|
|
54
53
|
const response = await fetch(url, {
|
|
55
54
|
headers: {
|
|
56
|
-
...token && {Authorization: `Bearer ${token}`}
|
|
55
|
+
...token && { Authorization: `Bearer ${token}` }
|
|
57
56
|
}
|
|
58
57
|
});
|
|
59
58
|
if (!response.ok) {
|
|
@@ -69,16 +68,16 @@ class ScaffolderClient {
|
|
|
69
68
|
method: "POST",
|
|
70
69
|
headers: {
|
|
71
70
|
"Content-Type": "application/json",
|
|
72
|
-
...token && {Authorization: `Bearer ${token}`}
|
|
71
|
+
...token && { Authorization: `Bearer ${token}` }
|
|
73
72
|
},
|
|
74
|
-
body: JSON.stringify({templateName, values: {...values}})
|
|
73
|
+
body: JSON.stringify({ templateName, values: { ...values } })
|
|
75
74
|
});
|
|
76
75
|
if (response.status !== 201) {
|
|
77
76
|
const status = `${response.status} ${response.statusText}`;
|
|
78
77
|
const body = await response.text();
|
|
79
78
|
throw new Error(`Backend request failed, ${status} ${body.trim()}`);
|
|
80
79
|
}
|
|
81
|
-
const {id} = await response.json();
|
|
80
|
+
const { id } = await response.json();
|
|
82
81
|
return id;
|
|
83
82
|
}
|
|
84
83
|
async getTask(taskId) {
|
|
@@ -86,7 +85,7 @@ class ScaffolderClient {
|
|
|
86
85
|
const baseUrl = await this.discoveryApi.getBaseUrl("scaffolder");
|
|
87
86
|
const url = `${baseUrl}/v2/tasks/${encodeURIComponent(taskId)}`;
|
|
88
87
|
const response = await fetch(url, {
|
|
89
|
-
headers: token ? {Authorization: `Bearer ${token}`} : {}
|
|
88
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
|
90
89
|
});
|
|
91
90
|
if (!response.ok) {
|
|
92
91
|
throw await ResponseError.fromResponse(response);
|
|
@@ -110,7 +109,7 @@ class ScaffolderClient {
|
|
|
110
109
|
}
|
|
111
110
|
this.discoveryApi.getBaseUrl("scaffolder").then((baseUrl) => {
|
|
112
111
|
const url = `${baseUrl}/v2/tasks/${encodeURIComponent(taskId)}/eventstream`;
|
|
113
|
-
const eventSource = new EventSource(url, {withCredentials: true});
|
|
112
|
+
const eventSource = new EventSource(url, { withCredentials: true });
|
|
114
113
|
eventSource.addEventListener("log", (event) => {
|
|
115
114
|
if (event.data) {
|
|
116
115
|
try {
|
|
@@ -147,7 +146,7 @@ class ScaffolderClient {
|
|
|
147
146
|
return new ObservableImpl((subscriber) => {
|
|
148
147
|
this.discoveryApi.getBaseUrl("scaffolder").then(async (baseUrl) => {
|
|
149
148
|
while (!subscriber.closed) {
|
|
150
|
-
const url = `${baseUrl}/v2/tasks/${encodeURIComponent(taskId)}/events?${qs.stringify({after})}`;
|
|
149
|
+
const url = `${baseUrl}/v2/tasks/${encodeURIComponent(taskId)}/events?${qs.stringify({ after })}`;
|
|
151
150
|
const response = await fetch(url);
|
|
152
151
|
if (!response.ok) {
|
|
153
152
|
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
@@ -170,7 +169,7 @@ class ScaffolderClient {
|
|
|
170
169
|
const baseUrl = await this.discoveryApi.getBaseUrl("scaffolder");
|
|
171
170
|
const token = await this.identityApi.getIdToken();
|
|
172
171
|
const response = await fetch(`${baseUrl}/v2/actions`, {
|
|
173
|
-
headers: token ? {Authorization: `Bearer ${token}`} : {}
|
|
172
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
|
174
173
|
});
|
|
175
174
|
if (!response.ok) {
|
|
176
175
|
throw await ResponseError.fromResponse(response);
|
|
@@ -179,9 +178,13 @@ 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
|
-
schema: {title = "Entity", description = "An entity from the catalog"},
|
|
187
|
+
schema: { title = "Entity", description = "An entity from the catalog" },
|
|
185
188
|
required,
|
|
186
189
|
uiSchema,
|
|
187
190
|
rawErrors,
|
|
@@ -192,23 +195,29 @@ const EntityPicker = ({
|
|
|
192
195
|
const allowedKinds = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.allowedKinds;
|
|
193
196
|
const defaultKind = (_b = uiSchema["ui:options"]) == null ? void 0 : _b.defaultKind;
|
|
194
197
|
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) => {
|
|
198
|
+
const { value: entities, loading } = useAsync(() => catalogApi.getEntities(allowedKinds ? { filter: { kind: allowedKinds } } : void 0));
|
|
199
|
+
const entityRefs = entities == null ? void 0 : entities.items.map((e) => formatEntityRefTitle(e, { defaultKind }));
|
|
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,
|
|
@@ -224,10 +233,10 @@ const EntityPicker = ({
|
|
|
224
233
|
const TextValuePicker = ({
|
|
225
234
|
onChange,
|
|
226
235
|
required,
|
|
227
|
-
schema: {title, description},
|
|
236
|
+
schema: { title, description },
|
|
228
237
|
rawErrors,
|
|
229
238
|
formData,
|
|
230
|
-
uiSchema: {"ui:autofocus": autoFocus},
|
|
239
|
+
uiSchema: { "ui:autofocus": autoFocus },
|
|
231
240
|
idSchema,
|
|
232
241
|
placeholder
|
|
233
242
|
}) => /* @__PURE__ */ React.createElement(TextField, {
|
|
@@ -237,17 +246,17 @@ const TextValuePicker = ({
|
|
|
237
246
|
helperText: description,
|
|
238
247
|
required,
|
|
239
248
|
value: formData != null ? formData : "",
|
|
240
|
-
onChange: ({target: {value}}) => onChange(value),
|
|
249
|
+
onChange: ({ target: { value } }) => onChange(value),
|
|
241
250
|
margin: "normal",
|
|
242
251
|
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !formData,
|
|
243
|
-
inputProps: {autoFocus}
|
|
252
|
+
inputProps: { autoFocus }
|
|
244
253
|
});
|
|
245
254
|
|
|
246
255
|
const EntityNamePicker = ({
|
|
247
|
-
schema: {title = "Name", description = "Unique name of the component"},
|
|
256
|
+
schema: { title = "Name", description = "Unique name of the component" },
|
|
248
257
|
...props
|
|
249
258
|
}) => /* @__PURE__ */ React.createElement(TextValuePicker, {
|
|
250
|
-
schema: {title, description},
|
|
259
|
+
schema: { title, description },
|
|
251
260
|
...props
|
|
252
261
|
});
|
|
253
262
|
|
|
@@ -257,8 +266,70 @@ 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
|
-
schema: {title = "Owner", description = "The owner of the component"},
|
|
332
|
+
schema: { title = "Owner", description = "The owner of the component" },
|
|
262
333
|
uiSchema,
|
|
263
334
|
...props
|
|
264
335
|
}) => {
|
|
@@ -275,12 +346,12 @@ const OwnerPicker = ({
|
|
|
275
346
|
};
|
|
276
347
|
return /* @__PURE__ */ React.createElement(EntityPicker, {
|
|
277
348
|
...props,
|
|
278
|
-
schema: {title, description},
|
|
349
|
+
schema: { title, description },
|
|
279
350
|
uiSchema: ownerUiSchema
|
|
280
351
|
});
|
|
281
352
|
};
|
|
282
353
|
|
|
283
|
-
function splitFormData(url) {
|
|
354
|
+
function splitFormData(url, allowedOwners) {
|
|
284
355
|
let host = void 0;
|
|
285
356
|
let owner = void 0;
|
|
286
357
|
let repo = void 0;
|
|
@@ -291,7 +362,7 @@ function splitFormData(url) {
|
|
|
291
362
|
if (url) {
|
|
292
363
|
const parsed = new URL(`https://${url}`);
|
|
293
364
|
host = parsed.host;
|
|
294
|
-
owner = parsed.searchParams.get("owner") || void 0;
|
|
365
|
+
owner = parsed.searchParams.get("owner") || (allowedOwners == null ? void 0 : allowedOwners[0]);
|
|
295
366
|
repo = parsed.searchParams.get("repo") || void 0;
|
|
296
367
|
organization = parsed.searchParams.get("organization") || void 0;
|
|
297
368
|
workspace = parsed.searchParams.get("workspace") || void 0;
|
|
@@ -299,7 +370,7 @@ function splitFormData(url) {
|
|
|
299
370
|
}
|
|
300
371
|
} catch {
|
|
301
372
|
}
|
|
302
|
-
return {host, owner, repo, organization, workspace, project};
|
|
373
|
+
return { host, owner, repo, organization, workspace, project };
|
|
303
374
|
}
|
|
304
375
|
function serializeFormData(data) {
|
|
305
376
|
if (!data.host) {
|
|
@@ -329,17 +400,18 @@ const RepoUrlPicker = ({
|
|
|
329
400
|
rawErrors,
|
|
330
401
|
formData
|
|
331
402
|
}) => {
|
|
332
|
-
var _a, _b, _c;
|
|
403
|
+
var _a, _b, _c, _d, _e;
|
|
333
404
|
const scaffolderApi = useApi(scaffolderApiRef);
|
|
334
405
|
const integrationApi = useApi(scmIntegrationsApiRef);
|
|
335
406
|
const allowedHosts = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.allowedHosts;
|
|
336
|
-
const
|
|
337
|
-
|
|
407
|
+
const allowedOwners = (_b = uiSchema["ui:options"]) == null ? void 0 : _b.allowedOwners;
|
|
408
|
+
const { value: integrations, loading } = useAsync(async () => {
|
|
409
|
+
return await scaffolderApi.getIntegrationsList({ allowedHosts });
|
|
338
410
|
});
|
|
339
|
-
const {host, owner, repo, organization, workspace, project} = splitFormData(formData);
|
|
340
|
-
const updateHost = useCallback((
|
|
411
|
+
const { host, owner, repo, organization, workspace, project } = splitFormData(formData, allowedOwners);
|
|
412
|
+
const updateHost = useCallback((value) => {
|
|
341
413
|
onChange(serializeFormData({
|
|
342
|
-
host:
|
|
414
|
+
host: value,
|
|
343
415
|
owner,
|
|
344
416
|
repo,
|
|
345
417
|
organization,
|
|
@@ -347,6 +419,14 @@ const RepoUrlPicker = ({
|
|
|
347
419
|
project
|
|
348
420
|
}));
|
|
349
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]);
|
|
350
430
|
const updateOwner = useCallback((evt) => onChange(serializeFormData({
|
|
351
431
|
host,
|
|
352
432
|
owner: evt.target.value,
|
|
@@ -411,21 +491,20 @@ const RepoUrlPicker = ({
|
|
|
411
491
|
if (loading) {
|
|
412
492
|
return /* @__PURE__ */ React.createElement(Progress, null);
|
|
413
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" }];
|
|
414
496
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(FormControl, {
|
|
415
497
|
margin: "normal",
|
|
416
498
|
required: true,
|
|
417
499
|
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !host
|
|
418
|
-
}, /* @__PURE__ */ React.createElement(
|
|
419
|
-
htmlFor: "hostInput"
|
|
420
|
-
}, "Host"), /* @__PURE__ */ React.createElement(Select, {
|
|
500
|
+
}, /* @__PURE__ */ React.createElement(Select, {
|
|
421
501
|
native: true,
|
|
422
|
-
|
|
502
|
+
disabled: hostsOptions.length === 1,
|
|
503
|
+
label: "Host",
|
|
423
504
|
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, {
|
|
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, {
|
|
429
508
|
margin: "normal",
|
|
430
509
|
required: true,
|
|
431
510
|
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !organization
|
|
@@ -435,7 +514,7 @@ const RepoUrlPicker = ({
|
|
|
435
514
|
id: "repoInput",
|
|
436
515
|
onChange: updateOrganization,
|
|
437
516
|
value: organization
|
|
438
|
-
}), /* @__PURE__ */ React.createElement(FormHelperText, null, "The name of the organization")), host && ((
|
|
517
|
+
}), /* @__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
518
|
margin: "normal",
|
|
440
519
|
required: true,
|
|
441
520
|
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !workspace
|
|
@@ -455,7 +534,7 @@ const RepoUrlPicker = ({
|
|
|
455
534
|
id: "wokrspaceInput",
|
|
456
535
|
onChange: updateProject,
|
|
457
536
|
value: project
|
|
458
|
-
}), /* @__PURE__ */ React.createElement(FormHelperText, null, "The project where the repository will be created"))), host && ((
|
|
537
|
+
}), /* @__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
538
|
margin: "normal",
|
|
460
539
|
required: true,
|
|
461
540
|
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !owner
|
|
@@ -465,6 +544,17 @@ const RepoUrlPicker = ({
|
|
|
465
544
|
id: "ownerInput",
|
|
466
545
|
onChange: updateOwner,
|
|
467
546
|
value: owner
|
|
547
|
+
}), /* @__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, {
|
|
548
|
+
margin: "normal",
|
|
549
|
+
required: true,
|
|
550
|
+
error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !owner
|
|
551
|
+
}, /* @__PURE__ */ React.createElement(Select, {
|
|
552
|
+
native: true,
|
|
553
|
+
label: "Owner Available",
|
|
554
|
+
onChange: updateOwnerSelect,
|
|
555
|
+
disabled: ownersOptions.length === 1,
|
|
556
|
+
selected: owner,
|
|
557
|
+
items: ownersOptions
|
|
468
558
|
}), /* @__PURE__ */ React.createElement(FormHelperText, null, "The organization, user or project that this repo will belong to"))), /* @__PURE__ */ React.createElement(FormControl, {
|
|
469
559
|
margin: "normal",
|
|
470
560
|
required: true,
|
|
@@ -481,7 +571,7 @@ const RepoUrlPicker = ({
|
|
|
481
571
|
const repoPickerValidation = (value, validation, context) => {
|
|
482
572
|
var _a;
|
|
483
573
|
try {
|
|
484
|
-
const {host, searchParams} = new URL(`https://${value}`);
|
|
574
|
+
const { host, searchParams } = new URL(`https://${value}`);
|
|
485
575
|
const integrationApi = context.apiHolder.get(scmIntegrationsApiRef);
|
|
486
576
|
if (!host) {
|
|
487
577
|
validation.addError("Incomplete repository location provided, host not provided");
|
|
@@ -507,6 +597,47 @@ const repoPickerValidation = (value, validation, context) => {
|
|
|
507
597
|
}
|
|
508
598
|
};
|
|
509
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
|
+
|
|
510
641
|
const FIELD_EXTENSION_WRAPPER_KEY = "scaffolder.extensions.wrapper.v1";
|
|
511
642
|
const FIELD_EXTENSION_KEY = "scaffolder.extensions.field.v1";
|
|
512
643
|
function createScaffolderFieldExtension(options) {
|
|
@@ -539,7 +670,7 @@ const scaffolderPlugin = createPlugin({
|
|
|
539
670
|
identityApi: identityApiRef,
|
|
540
671
|
scmIntegrationsApi: scmIntegrationsApiRef
|
|
541
672
|
},
|
|
542
|
-
factory: ({discoveryApi, identityApi, scmIntegrationsApi}) => new ScaffolderClient({discoveryApi, identityApi, scmIntegrationsApi})
|
|
673
|
+
factory: ({ discoveryApi, identityApi, scmIntegrationsApi }) => new ScaffolderClient({ discoveryApi, identityApi, scmIntegrationsApi })
|
|
543
674
|
})
|
|
544
675
|
],
|
|
545
676
|
routes: {
|
|
@@ -569,9 +700,17 @@ const OwnerPickerFieldExtension = scaffolderPlugin.provide(createScaffolderField
|
|
|
569
700
|
}));
|
|
570
701
|
const ScaffolderPage = scaffolderPlugin.provide(createRoutableExtension({
|
|
571
702
|
name: "ScaffolderPage",
|
|
572
|
-
component: () => import('./Router-
|
|
703
|
+
component: () => import('./Router-552fb2ce.esm.js').then((m) => m.Router),
|
|
573
704
|
mountPoint: rootRouteRef
|
|
574
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
|
+
}));
|
|
575
714
|
|
|
576
715
|
const YellowStar = withStyles({
|
|
577
716
|
root: {
|
|
@@ -595,7 +734,7 @@ const favouriteTemplateTooltip = (isStarred) => isStarred ? "Remove from favorit
|
|
|
595
734
|
const favouriteTemplateIcon = (isStarred) => isStarred ? /* @__PURE__ */ React.createElement(YellowStar, null) : /* @__PURE__ */ React.createElement(WhiteBorderStar, null);
|
|
596
735
|
const FavouriteTemplate = (props) => {
|
|
597
736
|
const classes = useStyles$1();
|
|
598
|
-
const {toggleStarredEntity, isStarredEntity} = useStarredEntity(props.entity);
|
|
737
|
+
const { toggleStarredEntity, isStarredEntity } = useStarredEntity(props.entity);
|
|
599
738
|
return /* @__PURE__ */ React.createElement(IconButton, {
|
|
600
739
|
color: "inherit",
|
|
601
740
|
className: classes.starButton,
|
|
@@ -611,7 +750,7 @@ const useStyles = makeStyles((theme) => ({
|
|
|
611
750
|
position: "relative"
|
|
612
751
|
},
|
|
613
752
|
title: {
|
|
614
|
-
backgroundImage: ({backgroundImage}) => backgroundImage
|
|
753
|
+
backgroundImage: ({ backgroundImage }) => backgroundImage
|
|
615
754
|
},
|
|
616
755
|
box: {
|
|
617
756
|
overflow: "hidden",
|
|
@@ -659,7 +798,7 @@ const getTemplateCardProps = (template) => {
|
|
|
659
798
|
const DeprecationWarning = () => {
|
|
660
799
|
const styles = useDeprecationStyles();
|
|
661
800
|
const Title = /* @__PURE__ */ React.createElement(Typography, {
|
|
662
|
-
style: {padding: 10, maxWidth: 300}
|
|
801
|
+
style: { padding: 10, maxWidth: 300 }
|
|
663
802
|
}, "This template syntax is deprecated. Click for more info.");
|
|
664
803
|
return /* @__PURE__ */ React.createElement("div", {
|
|
665
804
|
className: styles.deprecationIcon
|
|
@@ -670,15 +809,15 @@ const DeprecationWarning = () => {
|
|
|
670
809
|
className: styles.link
|
|
671
810
|
}, /* @__PURE__ */ React.createElement(WarningIcon, null))));
|
|
672
811
|
};
|
|
673
|
-
const TemplateCard = ({template, deprecated}) => {
|
|
812
|
+
const TemplateCard = ({ template, deprecated }) => {
|
|
674
813
|
var _a;
|
|
675
814
|
const backstageTheme = useTheme();
|
|
676
815
|
const rootLink = useRouteRef(rootRouteRef);
|
|
677
816
|
const templateProps = getTemplateCardProps(template);
|
|
678
817
|
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});
|
|
818
|
+
const themeId = backstageTheme.getPageTheme({ themeId: templateProps.type }) ? templateProps.type : "other";
|
|
819
|
+
const theme = backstageTheme.getPageTheme({ themeId });
|
|
820
|
+
const classes = useStyles({ backgroundImage: theme.backgroundImage });
|
|
682
821
|
const href = generatePath(`${rootLink()}/templates/:templateName`, {
|
|
683
822
|
templateName: templateProps.name
|
|
684
823
|
});
|
|
@@ -691,9 +830,9 @@ const TemplateCard = ({template, deprecated}) => {
|
|
|
691
830
|
}), deprecated && /* @__PURE__ */ React.createElement(DeprecationWarning, null), /* @__PURE__ */ React.createElement(ItemCardHeader, {
|
|
692
831
|
title: templateProps.title,
|
|
693
832
|
subtitle: templateProps.type,
|
|
694
|
-
classes: {root: classes.title}
|
|
833
|
+
classes: { root: classes.title }
|
|
695
834
|
})), /* @__PURE__ */ React.createElement(CardContent, {
|
|
696
|
-
style: {display: "grid"}
|
|
835
|
+
style: { display: "grid" }
|
|
697
836
|
}, /* @__PURE__ */ React.createElement(Box, {
|
|
698
837
|
className: classes.box
|
|
699
838
|
}, /* @__PURE__ */ React.createElement(Typography, {
|
|
@@ -730,7 +869,7 @@ const TemplateList = ({
|
|
|
730
869
|
TemplateCardComponent,
|
|
731
870
|
group
|
|
732
871
|
}) => {
|
|
733
|
-
const {loading, error, entities} = useEntityListProvider();
|
|
872
|
+
const { loading, error, entities } = useEntityListProvider();
|
|
734
873
|
const Card = TemplateCardComponent || TemplateCard;
|
|
735
874
|
const maybeFilteredEntities = group ? entities.filter((e) => group.filter(e)) : entities;
|
|
736
875
|
const title = group ? group.titleComponent || /* @__PURE__ */ React.createElement(ContentHeader, {
|
|
@@ -745,8 +884,8 @@ const TemplateList = ({
|
|
|
745
884
|
title: "Oops! Something went wrong loading the templates"
|
|
746
885
|
}, error.message), !error && !loading && !entities.length && /* @__PURE__ */ React.createElement(Typography, {
|
|
747
886
|
variant: "body2"
|
|
748
|
-
}, "No templates found that match your filter. Learn more about", " ", /* @__PURE__ */ React.createElement(Link, {
|
|
749
|
-
|
|
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"
|
|
750
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, {
|
|
751
890
|
key: stringifyEntityRef(template),
|
|
752
891
|
template
|
|
@@ -761,7 +900,7 @@ const checkedIcon = /* @__PURE__ */ React.createElement(CheckBoxIcon, {
|
|
|
761
900
|
});
|
|
762
901
|
const TemplateTypePicker = () => {
|
|
763
902
|
const alertApi = useApi(alertApiRef);
|
|
764
|
-
const {error, loading, availableTypes, selectedTypes, setSelectedTypes} = useEntityTypeFilter();
|
|
903
|
+
const { error, loading, availableTypes, selectedTypes, setSelectedTypes } = useEntityTypeFilter();
|
|
765
904
|
if (loading)
|
|
766
905
|
return /* @__PURE__ */ React.createElement(Progress, null);
|
|
767
906
|
if (!availableTypes)
|
|
@@ -784,7 +923,7 @@ const TemplateTypePicker = () => {
|
|
|
784
923
|
options: availableTypes,
|
|
785
924
|
value: selectedTypes,
|
|
786
925
|
onChange: (_, value) => setSelectedTypes(value),
|
|
787
|
-
renderOption: (option, {selected}) => /* @__PURE__ */ React.createElement(FormControlLabel, {
|
|
926
|
+
renderOption: (option, { selected }) => /* @__PURE__ */ React.createElement(FormControlLabel, {
|
|
788
927
|
control: /* @__PURE__ */ React.createElement(Checkbox, {
|
|
789
928
|
icon,
|
|
790
929
|
checkedIcon,
|
|
@@ -803,5 +942,5 @@ const TemplateTypePicker = () => {
|
|
|
803
942
|
}));
|
|
804
943
|
};
|
|
805
944
|
|
|
806
|
-
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,
|
|
807
|
-
//# 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
|