@backstage/plugin-scaffolder 1.12.0 → 1.13.0-next.1

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.
Files changed (37) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/alpha/package.json +1 -1
  3. package/dist/alpha.d.ts +12 -59
  4. package/dist/alpha.esm.js +27 -33
  5. package/dist/alpha.esm.js.map +1 -1
  6. package/dist/esm/{alpha/ListTasksPage-2e8f4176.esm.js → ListTasksPage-ca799894.esm.js} +3 -2
  7. package/dist/esm/ListTasksPage-ca799894.esm.js.map +1 -0
  8. package/dist/esm/{index/Router-6fd61bff.esm.js → Router-1b71c4f0.esm.js} +39 -28
  9. package/dist/esm/Router-1b71c4f0.esm.js.map +1 -0
  10. package/dist/esm/TaskPage-589238dc.esm.js +1684 -0
  11. package/dist/esm/TaskPage-589238dc.esm.js.map +1 -0
  12. package/dist/esm/TemplateEditorIntro-644bad26.esm.js +1128 -0
  13. package/dist/esm/TemplateEditorIntro-644bad26.esm.js.map +1 -0
  14. package/dist/esm/TemplateFormPreviewer-32c3dcf3.esm.js +630 -0
  15. package/dist/esm/TemplateFormPreviewer-32c3dcf3.esm.js.map +1 -0
  16. package/dist/esm/TemplateTypePicker-4f07b216.esm.js +58 -0
  17. package/dist/esm/TemplateTypePicker-4f07b216.esm.js.map +1 -0
  18. package/dist/esm/{alpha/index-2131f4a0.esm.js → index-bce9c23d.esm.js} +95 -156
  19. package/dist/esm/index-bce9c23d.esm.js.map +1 -0
  20. package/dist/index.d.ts +28 -167
  21. package/dist/index.esm.js +27 -65
  22. package/dist/index.esm.js.map +1 -1
  23. package/dist/types/plugin.d-bc532f9e.d.ts +232 -0
  24. package/package.json +24 -22
  25. package/dist/esm/alpha/ListTasksPage-2e8f4176.esm.js.map +0 -1
  26. package/dist/esm/alpha/Router-2826a2b8.esm.js +0 -1566
  27. package/dist/esm/alpha/Router-2826a2b8.esm.js.map +0 -1
  28. package/dist/esm/alpha/alpha-714dad1b.esm.js +0 -3422
  29. package/dist/esm/alpha/alpha-714dad1b.esm.js.map +0 -1
  30. package/dist/esm/alpha/index-2131f4a0.esm.js.map +0 -1
  31. package/dist/esm/index/ListTasksPage-fa403ee3.esm.js +0 -192
  32. package/dist/esm/index/ListTasksPage-fa403ee3.esm.js.map +0 -1
  33. package/dist/esm/index/Router-6fd61bff.esm.js.map +0 -1
  34. package/dist/esm/index/index-0b6cdf44.esm.js +0 -3475
  35. package/dist/esm/index/index-0b6cdf44.esm.js.map +0 -1
  36. package/dist/esm/index/index-f404fb0b.esm.js +0 -449
  37. package/dist/esm/index/index-f404fb0b.esm.js.map +0 -1
@@ -1,3475 +0,0 @@
1
- import { parseEntityRef, stringifyEntityRef, KubernetesValidatorFunctions, RELATION_OWNED_BY, makeValidator } from '@backstage/catalog-model';
2
- import { ResponseError } from '@backstage/errors';
3
- import qs from 'qs';
4
- import ObservableImpl from 'zen-observable';
5
- import { scmIntegrationsApiRef, scmAuthApiRef } from '@backstage/integration-react';
6
- import { scaffolderApiRef as scaffolderApiRef$1, useTemplateSecrets as useTemplateSecrets$1, useTaskEventStream, createScaffolderFieldExtension as createScaffolderFieldExtension$1, ScaffolderFieldExtensions as ScaffolderFieldExtensions$1, createScaffolderLayout as createScaffolderLayout$1, ScaffolderLayouts as ScaffolderLayouts$1 } from '@backstage/plugin-scaffolder-react';
7
- import { useApi, identityApiRef, createExternalRouteRef, createRouteRef, createSubRouteRef, useRouteRef, useApiHolder, useApp, useRouteRefParams, alertApiRef, createPlugin, createApiFactory, discoveryApiRef, fetchApiRef, createRoutableExtension } from '@backstage/core-plugin-api';
8
- import { catalogApiRef, humanizeEntityRef, entityRouteRef, useEntityTypeFilter } from '@backstage/plugin-catalog-react';
9
- import { TextField, FormControl as FormControl$1, makeStyles as makeStyles$1, IconButton as IconButton$1, Popover as Popover$1, MenuList as MenuList$1, MenuItem as MenuItem$1, ListItemIcon as ListItemIcon$1, ListItemText as ListItemText$1, Box, Paper, Typography, Accordion, AccordionSummary, AccordionDetails, Grid, TableContainer, Table, TableHead, TableRow, TableCell, TableBody, Chip, Card, List as List$1, InputLabel as InputLabel$1, Select as Select$1, CardHeader, CardContent, Button, StepButton, CircularProgress, Tooltip, Divider as Divider$1, LinearProgress, FormControlLabel, Checkbox } from '@material-ui/core';
10
- import FormControl from '@material-ui/core/FormControl';
11
- import Autocomplete from '@material-ui/lab/Autocomplete';
12
- import React, { useCallback, useEffect, useState, useMemo, Fragment, createContext, useRef, useContext, Component, memo, Children } from 'react';
13
- import useAsync from 'react-use/lib/useAsync';
14
- import { z } from 'zod';
15
- import zodToJsonSchema from 'zod-to-json-schema';
16
- import FormHelperText from '@material-ui/core/FormHelperText';
17
- import Input from '@material-ui/core/Input';
18
- import InputLabel from '@material-ui/core/InputLabel';
19
- import { Select, Progress, Page, Header, Content, ErrorPanel, ErrorPage, MarkdownContent, CodeSnippet, DismissableBanner, Link, LogViewer } from '@backstage/core-components';
20
- import useDebounce from 'react-use/lib/useDebounce';
21
- import useEffectOnce from 'react-use/lib/useEffectOnce';
22
- import { Autocomplete as Autocomplete$1 } from '@material-ui/lab';
23
- import { useNavigate, useParams } from 'react-router-dom';
24
- import capitalize from 'lodash/capitalize';
25
- import CheckBoxIcon from '@material-ui/icons/CheckBox';
26
- import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
27
- import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
28
- import '@material-ui/core/Button';
29
- import IconButton from '@material-ui/core/IconButton';
30
- import '@material-ui/core/useMediaQuery';
31
- import '@material-ui/icons/AddCircleOutline';
32
- import '@backstage/plugin-catalog-common/alpha';
33
- import '@backstage/plugin-permission-react';
34
- import { DefaultTemplateOutputs, TaskSteps, TaskLogStream, Stepper, Form } from '@backstage/plugin-scaffolder-react/alpha';
35
- import ListItemIcon from '@material-ui/core/ListItemIcon';
36
- import ListItemText from '@material-ui/core/ListItemText';
37
- import MenuItem from '@material-ui/core/MenuItem';
38
- import MenuList from '@material-ui/core/MenuList';
39
- import Popover from '@material-ui/core/Popover';
40
- import { makeStyles, createStyles } from '@material-ui/core/styles';
41
- import Description from '@material-ui/icons/Description';
42
- import Edit from '@material-ui/icons/Edit';
43
- import List from '@material-ui/icons/List';
44
- import MoreVert from '@material-ui/icons/MoreVert';
45
- import Retry from '@material-ui/icons/Repeat';
46
- import Toc from '@material-ui/icons/Toc';
47
- import classNames from 'classnames';
48
- import SettingsIcon from '@material-ui/icons/Settings';
49
- import AllIcon from '@material-ui/icons/FontDownload';
50
- import { DateTime, Interval } from 'luxon';
51
- import 'humanize-duration';
52
- import Typography$1 from '@material-ui/core/Typography';
53
- import { StreamLanguage } from '@codemirror/language';
54
- import { yaml as yaml$1 } from '@codemirror/legacy-modes/mode/yaml';
55
- import CloseIcon from '@material-ui/icons/Close';
56
- import CodeMirror from '@uiw/react-codemirror';
57
- import yaml from 'yaml';
58
- import { useAsync as useAsync$1, useRerender, usePrevious, useKeyboardEvent } from '@react-hookz/web';
59
- import validator from '@rjsf/validator-ajv8';
60
- import Accordion$1 from '@material-ui/core/Accordion';
61
- import AccordionDetails$1 from '@material-ui/core/AccordionDetails';
62
- import AccordionSummary$1 from '@material-ui/core/AccordionSummary';
63
- import Divider from '@material-ui/core/Divider';
64
- import ExpandMoreIcon$1 from '@material-ui/icons/ExpandLess';
65
- import List$2 from '@material-ui/core/List';
66
- import ListItem from '@material-ui/core/ListItem';
67
- import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
68
- import CancelIcon from '@material-ui/icons/Cancel';
69
- import CheckIcon from '@material-ui/icons/Check';
70
- import DeleteIcon from '@material-ui/icons/Delete';
71
- import Box$1 from '@material-ui/core/Box';
72
- import Tab from '@material-ui/core/Tab';
73
- import Tabs from '@material-ui/core/Tabs';
74
- import Grid$1 from '@material-ui/core/Grid';
75
- import Step from '@material-ui/core/Step';
76
- import StepLabel from '@material-ui/core/StepLabel';
77
- import Stepper$1 from '@material-ui/core/Stepper';
78
- import FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord';
79
- import useInterval from 'react-use/lib/useInterval';
80
- import LanguageIcon from '@material-ui/icons/Language';
81
- import TreeView from '@material-ui/lab/TreeView';
82
- import ChevronRightIcon from '@material-ui/icons/ChevronRight';
83
- import TreeItem from '@material-ui/lab/TreeItem';
84
- import RefreshIcon from '@material-ui/icons/Refresh';
85
- import SaveIcon from '@material-ui/icons/Save';
86
- import { showPanel } from '@codemirror/view';
87
- import Card$1 from '@material-ui/core/Card';
88
- import CardActionArea from '@material-ui/core/CardActionArea';
89
- import CardContent$1 from '@material-ui/core/CardContent';
90
- import Tooltip$1 from '@material-ui/core/Tooltip';
91
- import InfoOutlinedIcon from '@material-ui/icons/InfoOutlined';
92
-
93
- class ScaffolderClient {
94
- constructor(options) {
95
- var _a, _b;
96
- this.discoveryApi = options.discoveryApi;
97
- this.fetchApi = (_a = options.fetchApi) != null ? _a : { fetch };
98
- this.scmIntegrationsApi = options.scmIntegrationsApi;
99
- this.useLongPollingLogs = (_b = options.useLongPollingLogs) != null ? _b : false;
100
- this.identityApi = options.identityApi;
101
- }
102
- async listTasks(options) {
103
- if (!this.identityApi) {
104
- throw new Error(
105
- "IdentityApi is not available in the ScaffolderClient, please pass through the IdentityApi to the ScaffolderClient constructor in order to use the listTasks method"
106
- );
107
- }
108
- const baseUrl = await this.discoveryApi.getBaseUrl("scaffolder");
109
- const { userEntityRef } = await this.identityApi.getBackstageIdentity();
110
- const query = qs.stringify(
111
- options.filterByOwnership === "owned" ? { createdBy: userEntityRef } : {}
112
- );
113
- const response = await this.fetchApi.fetch(`${baseUrl}/v2/tasks?${query}`);
114
- if (!response.ok) {
115
- throw await ResponseError.fromResponse(response);
116
- }
117
- return await response.json();
118
- }
119
- async getIntegrationsList(options) {
120
- const integrations = [
121
- ...this.scmIntegrationsApi.azure.list(),
122
- ...this.scmIntegrationsApi.bitbucket.list().filter(
123
- (item) => !this.scmIntegrationsApi.bitbucketCloud.byHost(item.config.host) && !this.scmIntegrationsApi.bitbucketServer.byHost(item.config.host)
124
- ),
125
- ...this.scmIntegrationsApi.bitbucketCloud.list(),
126
- ...this.scmIntegrationsApi.bitbucketServer.list(),
127
- ...this.scmIntegrationsApi.gerrit.list(),
128
- ...this.scmIntegrationsApi.github.list(),
129
- ...this.scmIntegrationsApi.gitlab.list()
130
- ].map((c) => ({ type: c.type, title: c.title, host: c.config.host })).filter((c) => options.allowedHosts.includes(c.host));
131
- return {
132
- integrations
133
- };
134
- }
135
- async getTemplateParameterSchema(templateRef) {
136
- const { namespace, kind, name } = parseEntityRef(templateRef, {
137
- defaultKind: "template"
138
- });
139
- const baseUrl = await this.discoveryApi.getBaseUrl("scaffolder");
140
- const templatePath = [namespace, kind, name].map((s) => encodeURIComponent(s)).join("/");
141
- const url = `${baseUrl}/v2/templates/${templatePath}/parameter-schema`;
142
- const response = await this.fetchApi.fetch(url);
143
- if (!response.ok) {
144
- throw await ResponseError.fromResponse(response);
145
- }
146
- const schema = await response.json();
147
- return schema;
148
- }
149
- async scaffold(options) {
150
- const { templateRef, values, secrets = {} } = options;
151
- const url = `${await this.discoveryApi.getBaseUrl("scaffolder")}/v2/tasks`;
152
- const response = await this.fetchApi.fetch(url, {
153
- method: "POST",
154
- headers: {
155
- "Content-Type": "application/json"
156
- },
157
- body: JSON.stringify({
158
- templateRef,
159
- values: { ...values },
160
- secrets
161
- })
162
- });
163
- if (response.status !== 201) {
164
- const status = `${response.status} ${response.statusText}`;
165
- const body = await response.text();
166
- throw new Error(`Backend request failed, ${status} ${body.trim()}`);
167
- }
168
- const { id } = await response.json();
169
- return { taskId: id };
170
- }
171
- async getTask(taskId) {
172
- const baseUrl = await this.discoveryApi.getBaseUrl("scaffolder");
173
- const url = `${baseUrl}/v2/tasks/${encodeURIComponent(taskId)}`;
174
- const response = await this.fetchApi.fetch(url);
175
- if (!response.ok) {
176
- throw await ResponseError.fromResponse(response);
177
- }
178
- return await response.json();
179
- }
180
- streamLogs(options) {
181
- if (this.useLongPollingLogs) {
182
- return this.streamLogsPolling(options);
183
- }
184
- return this.streamLogsEventStream(options);
185
- }
186
- async dryRun(options) {
187
- const baseUrl = await this.discoveryApi.getBaseUrl("scaffolder");
188
- const response = await this.fetchApi.fetch(`${baseUrl}/v2/dry-run`, {
189
- method: "POST",
190
- headers: {
191
- "Content-Type": "application/json"
192
- },
193
- body: JSON.stringify({
194
- template: options.template,
195
- values: options.values,
196
- secrets: options.secrets,
197
- directoryContents: options.directoryContents
198
- })
199
- });
200
- if (!response.ok) {
201
- throw await ResponseError.fromResponse(response);
202
- }
203
- return response.json();
204
- }
205
- streamLogsEventStream({
206
- taskId,
207
- after
208
- }) {
209
- return new ObservableImpl((subscriber) => {
210
- const params = new URLSearchParams();
211
- if (after !== void 0) {
212
- params.set("after", String(Number(after)));
213
- }
214
- this.discoveryApi.getBaseUrl("scaffolder").then(
215
- (baseUrl) => {
216
- const url = `${baseUrl}/v2/tasks/${encodeURIComponent(
217
- taskId
218
- )}/eventstream`;
219
- const eventSource = new EventSource(url, { withCredentials: true });
220
- eventSource.addEventListener("log", (event) => {
221
- if (event.data) {
222
- try {
223
- subscriber.next(JSON.parse(event.data));
224
- } catch (ex) {
225
- subscriber.error(ex);
226
- }
227
- }
228
- });
229
- eventSource.addEventListener("completion", (event) => {
230
- if (event.data) {
231
- try {
232
- subscriber.next(JSON.parse(event.data));
233
- } catch (ex) {
234
- subscriber.error(ex);
235
- }
236
- }
237
- eventSource.close();
238
- subscriber.complete();
239
- });
240
- eventSource.addEventListener("error", (event) => {
241
- subscriber.error(event);
242
- });
243
- },
244
- (error) => {
245
- subscriber.error(error);
246
- }
247
- );
248
- });
249
- }
250
- streamLogsPolling({
251
- taskId,
252
- after: inputAfter
253
- }) {
254
- let after = inputAfter;
255
- return new ObservableImpl((subscriber) => {
256
- this.discoveryApi.getBaseUrl("scaffolder").then(async (baseUrl) => {
257
- while (!subscriber.closed) {
258
- const url = `${baseUrl}/v2/tasks/${encodeURIComponent(
259
- taskId
260
- )}/events?${qs.stringify({ after })}`;
261
- const response = await this.fetchApi.fetch(url);
262
- if (!response.ok) {
263
- await new Promise((resolve) => setTimeout(resolve, 1e3));
264
- continue;
265
- }
266
- const logs = await response.json();
267
- for (const event of logs) {
268
- after = Number(event.id);
269
- subscriber.next(event);
270
- if (event.type === "completion") {
271
- subscriber.complete();
272
- return;
273
- }
274
- }
275
- }
276
- });
277
- });
278
- }
279
- async listActions() {
280
- const baseUrl = await this.discoveryApi.getBaseUrl("scaffolder");
281
- const response = await this.fetchApi.fetch(`${baseUrl}/v2/actions`);
282
- if (!response.ok) {
283
- throw await ResponseError.fromResponse(response);
284
- }
285
- return await response.json();
286
- }
287
- }
288
-
289
- function makeFieldSchemaFromZod(returnSchema, uiOptionsSchema) {
290
- return {
291
- schema: {
292
- returnValue: zodToJsonSchema(returnSchema),
293
- uiOptions: uiOptionsSchema ? zodToJsonSchema(uiOptionsSchema) : void 0
294
- },
295
- type: null,
296
- uiOptionsType: null
297
- };
298
- }
299
-
300
- const entityQueryFilterExpressionSchema = z.record(
301
- z.string().or(z.array(z.string()))
302
- );
303
- const EntityPickerFieldSchema = makeFieldSchemaFromZod(
304
- z.string(),
305
- z.object({
306
- /**
307
- * @deprecated Use `catalogFilter` instead.
308
- */
309
- allowedKinds: z.array(z.string()).optional().describe(
310
- "DEPRECATED: Use `catalogFilter` instead. List of kinds of entities to derive options from"
311
- ),
312
- defaultKind: z.string().optional().describe(
313
- "The default entity kind. Options of this kind will not be prefixed."
314
- ),
315
- allowArbitraryValues: z.boolean().optional().describe("Whether to allow arbitrary user input. Defaults to true"),
316
- defaultNamespace: z.union([z.string(), z.literal(false)]).optional().describe(
317
- "The default namespace. Options with this namespace will not be prefixed."
318
- ),
319
- catalogFilter: z.array(entityQueryFilterExpressionSchema).or(entityQueryFilterExpressionSchema).optional().describe("List of key-value filter expression for entities")
320
- })
321
- );
322
- const EntityPickerSchema = EntityPickerFieldSchema.schema;
323
-
324
- const EntityPicker = (props) => {
325
- var _a, _b, _c, _d, _e, _f, _g;
326
- const {
327
- onChange,
328
- schema: { title = "Entity", description = "An entity from the catalog" },
329
- required,
330
- uiSchema,
331
- rawErrors,
332
- formData,
333
- idSchema
334
- } = props;
335
- const allowedKinds = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.allowedKinds;
336
- const catalogFilter = ((_b = uiSchema["ui:options"]) == null ? void 0 : _b.catalogFilter) || allowedKinds && { kind: allowedKinds };
337
- const defaultKind = (_c = uiSchema["ui:options"]) == null ? void 0 : _c.defaultKind;
338
- const defaultNamespace = ((_d = uiSchema["ui:options"]) == null ? void 0 : _d.defaultNamespace) || void 0;
339
- const catalogApi = useApi(catalogApiRef);
340
- const { value: entities, loading } = useAsync(async () => {
341
- const { items } = await catalogApi.getEntities(
342
- catalogFilter ? { filter: catalogFilter } : void 0
343
- );
344
- return items;
345
- });
346
- const allowArbitraryValues = (_f = (_e = uiSchema["ui:options"]) == null ? void 0 : _e.allowArbitraryValues) != null ? _f : true;
347
- const getLabel = useCallback(
348
- (ref) => {
349
- try {
350
- return humanizeEntityRef(
351
- parseEntityRef(ref, { defaultKind, defaultNamespace }),
352
- {
353
- defaultKind,
354
- defaultNamespace
355
- }
356
- );
357
- } catch (err) {
358
- return ref;
359
- }
360
- },
361
- [defaultKind, defaultNamespace]
362
- );
363
- const onSelect = useCallback(
364
- (_, ref, reason) => {
365
- if (typeof ref !== "string") {
366
- onChange(ref ? stringifyEntityRef(ref) : "");
367
- } else {
368
- if (reason === "blur" || reason === "create-option") {
369
- let entityRef = ref;
370
- try {
371
- entityRef = stringifyEntityRef(
372
- parseEntityRef(ref, {
373
- defaultKind,
374
- defaultNamespace
375
- })
376
- );
377
- } catch (err) {
378
- }
379
- if (formData !== ref || allowArbitraryValues) {
380
- onChange(entityRef);
381
- }
382
- }
383
- }
384
- },
385
- [onChange, formData, defaultKind, defaultNamespace, allowArbitraryValues]
386
- );
387
- useEffect(() => {
388
- if ((entities == null ? void 0 : entities.length) === 1) {
389
- onChange(stringifyEntityRef(entities[0]));
390
- }
391
- }, [entities, onChange]);
392
- return /* @__PURE__ */ React.createElement(
393
- FormControl,
394
- {
395
- margin: "normal",
396
- required,
397
- error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !formData
398
- },
399
- /* @__PURE__ */ React.createElement(
400
- Autocomplete,
401
- {
402
- disabled: (entities == null ? void 0 : entities.length) === 1,
403
- id: idSchema == null ? void 0 : idSchema.$id,
404
- value: (
405
- // Since free solo can be enabled, attempt to parse as a full entity ref first, then fall
406
- // back to the given value.
407
- (_g = entities == null ? void 0 : entities.find((e) => stringifyEntityRef(e) === formData)) != null ? _g : allowArbitraryValues && formData ? getLabel(formData) : ""
408
- ),
409
- loading,
410
- onChange: onSelect,
411
- options: entities || [],
412
- getOptionLabel: (option) => (
413
- // option can be a string due to freeSolo.
414
- typeof option === "string" ? option : humanizeEntityRef(option, { defaultKind, defaultNamespace })
415
- ),
416
- autoSelect: true,
417
- freeSolo: allowArbitraryValues,
418
- renderInput: (params) => /* @__PURE__ */ React.createElement(
419
- TextField,
420
- {
421
- ...params,
422
- label: title,
423
- margin: "dense",
424
- helperText: description,
425
- FormHelperTextProps: { margin: "dense", style: { marginLeft: 0 } },
426
- variant: "outlined",
427
- required,
428
- InputProps: params.InputProps
429
- }
430
- )
431
- }
432
- )
433
- );
434
- };
435
-
436
- const entityNamePickerValidation = (value, validation) => {
437
- if (!KubernetesValidatorFunctions.isValidObjectName(value)) {
438
- validation.addError(
439
- "Must start and end with an alphanumeric character, and contain only alphanumeric characters, hyphens, underscores, and periods. Maximum length is 63 characters."
440
- );
441
- }
442
- };
443
-
444
- const EntityNamePickerFieldSchema = makeFieldSchemaFromZod(z.string());
445
- const EntityNamePickerSchema = EntityNamePickerFieldSchema.schema;
446
-
447
- const EntityNamePicker = (props) => {
448
- const {
449
- onChange,
450
- required,
451
- schema: { title = "Name", description = "Unique name of the component" },
452
- rawErrors,
453
- formData,
454
- uiSchema: { "ui:autofocus": autoFocus },
455
- idSchema,
456
- placeholder
457
- } = props;
458
- return /* @__PURE__ */ React.createElement(
459
- TextField,
460
- {
461
- id: idSchema == null ? void 0 : idSchema.$id,
462
- label: title,
463
- placeholder,
464
- helperText: description,
465
- required,
466
- value: formData != null ? formData : "",
467
- onChange: ({ target: { value } }) => onChange(value),
468
- margin: "normal",
469
- error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !formData,
470
- inputProps: { autoFocus }
471
- }
472
- );
473
- };
474
-
475
- const OwnerPickerFieldSchema = makeFieldSchemaFromZod(
476
- z.string(),
477
- z.object({
478
- /**
479
- * @deprecated Use `catalogFilter` instead.
480
- */
481
- allowedKinds: z.array(z.string()).default(["Group", "User"]).optional().describe(
482
- "DEPRECATED: Use `catalogFilter` instead. List of kinds of entities to derive options from. Defaults to Group and User"
483
- ),
484
- allowArbitraryValues: z.boolean().optional().describe("Whether to allow arbitrary user input. Defaults to true"),
485
- defaultNamespace: z.union([z.string(), z.literal(false)]).optional().describe(
486
- "The default namespace. Options with this namespace will not be prefixed."
487
- ),
488
- catalogFilter: z.array(entityQueryFilterExpressionSchema).or(entityQueryFilterExpressionSchema).optional().describe("List of key-value filter expression for entities")
489
- })
490
- );
491
- const OwnerPickerSchema = OwnerPickerFieldSchema.schema;
492
-
493
- const OwnerPicker = (props) => {
494
- var _a, _b, _c, _d, _e;
495
- const {
496
- schema: { title = "Owner", description = "The owner of the component" },
497
- uiSchema,
498
- ...restProps
499
- } = props;
500
- const defaultNamespace = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.defaultNamespace;
501
- const allowedKinds = (_b = uiSchema["ui:options"]) == null ? void 0 : _b.allowedKinds;
502
- const catalogFilter = ((_c = uiSchema["ui:options"]) == null ? void 0 : _c.catalogFilter) || {
503
- kind: allowedKinds || ["Group", "User"]
504
- };
505
- const ownerUiSchema = {
506
- ...uiSchema,
507
- "ui:options": {
508
- catalogFilter,
509
- defaultKind: "Group",
510
- allowArbitraryValues: (_e = (_d = uiSchema["ui:options"]) == null ? void 0 : _d.allowArbitraryValues) != null ? _e : true,
511
- ...defaultNamespace !== void 0 ? { defaultNamespace } : {}
512
- }
513
- };
514
- return /* @__PURE__ */ React.createElement(
515
- EntityPicker,
516
- {
517
- ...restProps,
518
- schema: { title, description },
519
- uiSchema: ownerUiSchema
520
- }
521
- );
522
- };
523
-
524
- const RepoUrlPickerFieldSchema = makeFieldSchemaFromZod(
525
- z.string(),
526
- z.object({
527
- allowedHosts: z.array(z.string()).optional().describe("List of allowed SCM platform hosts"),
528
- allowedOrganizations: z.array(z.string()).optional().describe("List of allowed organizations in the given SCM platform"),
529
- allowedOwners: z.array(z.string()).optional().describe("List of allowed owners in the given SCM platform"),
530
- allowedProjects: z.array(z.string()).optional().describe("List of allowed projects in the given SCM platform"),
531
- allowedRepos: z.array(z.string()).optional().describe("List of allowed repos in the given SCM platform"),
532
- requestUserCredentials: z.object({
533
- secretsKey: z.string().describe(
534
- "Key used within the template secrets context to store the credential"
535
- ),
536
- additionalScopes: z.object({
537
- gerrit: z.array(z.string()).optional().describe("Additional Gerrit scopes to request"),
538
- github: z.array(z.string()).optional().describe("Additional GitHub scopes to request"),
539
- gitlab: z.array(z.string()).optional().describe("Additional GitLab scopes to request"),
540
- bitbucket: z.array(z.string()).optional().describe("Additional BitBucket scopes to request"),
541
- azure: z.array(z.string()).optional().describe("Additional Azure scopes to request")
542
- }).optional().describe("Additional permission scopes to request")
543
- }).optional().describe(
544
- "If defined will request user credentials to auth against the given SCM platform"
545
- )
546
- })
547
- );
548
- const RepoUrlPickerSchema = RepoUrlPickerFieldSchema.schema;
549
-
550
- const repoPickerValidation = (value, validation, context) => {
551
- var _a, _b;
552
- try {
553
- const { host, searchParams } = new URL(`https://${value}`);
554
- const integrationApi = context.apiHolder.get(scmIntegrationsApiRef);
555
- if (!host) {
556
- validation.addError(
557
- "Incomplete repository location provided, host not provided"
558
- );
559
- } else {
560
- if (((_a = integrationApi == null ? void 0 : integrationApi.byHost(host)) == null ? void 0 : _a.type) === "bitbucket") {
561
- if (host === "bitbucket.org" && !searchParams.get("workspace")) {
562
- validation.addError(
563
- "Incomplete repository location provided, workspace not provided"
564
- );
565
- }
566
- if (!searchParams.get("project")) {
567
- validation.addError(
568
- "Incomplete repository location provided, project not provided"
569
- );
570
- }
571
- } else if (((_b = integrationApi == null ? void 0 : integrationApi.byHost(host)) == null ? void 0 : _b.type) !== "gerrit") {
572
- if (!searchParams.get("owner")) {
573
- validation.addError(
574
- "Incomplete repository location provided, owner not provided"
575
- );
576
- }
577
- }
578
- if (!searchParams.get("repo")) {
579
- validation.addError(
580
- "Incomplete repository location provided, repo not provided"
581
- );
582
- }
583
- }
584
- } catch {
585
- validation.addError("Unable to parse the Repository URL");
586
- }
587
- };
588
-
589
- const GithubRepoPicker = (props) => {
590
- const { allowedOwners = [], rawErrors, state, onChange } = props;
591
- const ownerItems = allowedOwners ? allowedOwners.map((i) => ({ label: i, value: i })) : [{ label: "Loading...", value: "loading" }];
592
- const { owner } = state;
593
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
594
- FormControl,
595
- {
596
- margin: "normal",
597
- required: true,
598
- error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !owner
599
- },
600
- (allowedOwners == null ? void 0 : allowedOwners.length) ? /* @__PURE__ */ React.createElement(
601
- Select,
602
- {
603
- native: true,
604
- label: "Owner Available",
605
- onChange: (s) => onChange({ owner: String(Array.isArray(s) ? s[0] : s) }),
606
- disabled: allowedOwners.length === 1,
607
- selected: owner,
608
- items: ownerItems
609
- }
610
- ) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(InputLabel, { htmlFor: "ownerInput" }, "Owner"), /* @__PURE__ */ React.createElement(
611
- Input,
612
- {
613
- id: "ownerInput",
614
- onChange: (e) => onChange({ owner: e.target.value }),
615
- value: owner
616
- }
617
- )),
618
- /* @__PURE__ */ React.createElement(FormHelperText, null, "The organization, user or project that this repo will belong to")
619
- ));
620
- };
621
-
622
- const GitlabRepoPicker = (props) => {
623
- const { allowedOwners = [], state, onChange, rawErrors } = props;
624
- const ownerItems = allowedOwners ? allowedOwners.map((i) => ({ label: i, value: i })) : [{ label: "Loading...", value: "loading" }];
625
- const { owner } = state;
626
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
627
- FormControl,
628
- {
629
- margin: "normal",
630
- required: true,
631
- error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !owner
632
- },
633
- (allowedOwners == null ? void 0 : allowedOwners.length) ? /* @__PURE__ */ React.createElement(
634
- Select,
635
- {
636
- native: true,
637
- label: "Owner Available",
638
- onChange: (selected) => onChange({
639
- owner: String(Array.isArray(selected) ? selected[0] : selected)
640
- }),
641
- disabled: allowedOwners.length === 1,
642
- selected: owner,
643
- items: ownerItems
644
- }
645
- ) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(InputLabel, { htmlFor: "ownerInput" }, "Owner"), /* @__PURE__ */ React.createElement(
646
- Input,
647
- {
648
- id: "ownerInput",
649
- onChange: (e) => onChange({ owner: e.target.value }),
650
- value: owner
651
- }
652
- )),
653
- /* @__PURE__ */ React.createElement(FormHelperText, null, "GitLab namespace where this repository will belong to. It can be the name of organization, group, subgroup, user, or the project.")
654
- ));
655
- };
656
-
657
- const AzureRepoPicker = (props) => {
658
- const {
659
- allowedOrganizations = [],
660
- allowedOwners = [],
661
- rawErrors,
662
- state,
663
- onChange
664
- } = props;
665
- const organizationItems = allowedOrganizations ? allowedOrganizations.map((i) => ({ label: i, value: i })) : [{ label: "Loading...", value: "loading" }];
666
- const ownerItems = allowedOwners ? allowedOwners.map((i) => ({ label: i, value: i })) : [{ label: "Loading...", value: "loading" }];
667
- const { organization, owner } = state;
668
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
669
- FormControl,
670
- {
671
- margin: "normal",
672
- required: true,
673
- error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !organization
674
- },
675
- (allowedOrganizations == null ? void 0 : allowedOrganizations.length) ? /* @__PURE__ */ React.createElement(
676
- Select,
677
- {
678
- native: true,
679
- label: "Organization",
680
- onChange: (s) => onChange({ organization: String(Array.isArray(s) ? s[0] : s) }),
681
- disabled: allowedOrganizations.length === 1,
682
- selected: organization,
683
- items: organizationItems
684
- }
685
- ) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(InputLabel, { htmlFor: "orgInput" }, "Organization"), /* @__PURE__ */ React.createElement(
686
- Input,
687
- {
688
- id: "orgInput",
689
- onChange: (e) => onChange({ organization: e.target.value }),
690
- value: organization
691
- }
692
- )),
693
- /* @__PURE__ */ React.createElement(FormHelperText, null, "The Organization that this repo will belong to")
694
- ), /* @__PURE__ */ React.createElement(
695
- FormControl,
696
- {
697
- margin: "normal",
698
- required: true,
699
- error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !owner
700
- },
701
- (allowedOwners == null ? void 0 : allowedOwners.length) ? /* @__PURE__ */ React.createElement(
702
- Select,
703
- {
704
- native: true,
705
- label: "Owner",
706
- onChange: (s) => onChange({ owner: String(Array.isArray(s) ? s[0] : s) }),
707
- disabled: allowedOwners.length === 1,
708
- selected: owner,
709
- items: ownerItems
710
- }
711
- ) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(InputLabel, { htmlFor: "ownerInput" }, "Project"), /* @__PURE__ */ React.createElement(
712
- Input,
713
- {
714
- id: "ownerInput",
715
- onChange: (e) => onChange({ owner: e.target.value }),
716
- value: owner
717
- }
718
- )),
719
- /* @__PURE__ */ React.createElement(FormHelperText, null, "The Project that this repo will belong to")
720
- ));
721
- };
722
-
723
- const BitbucketRepoPicker = (props) => {
724
- const {
725
- allowedOwners = [],
726
- allowedProjects = [],
727
- onChange,
728
- rawErrors,
729
- state
730
- } = props;
731
- const { host, workspace, project } = state;
732
- const ownerItems = allowedOwners ? allowedOwners == null ? void 0 : allowedOwners.map((i) => ({ label: i, value: i })) : [];
733
- const projectItems = allowedProjects ? allowedProjects == null ? void 0 : allowedProjects.map((i) => ({ label: i, value: i })) : [];
734
- useEffect(() => {
735
- if (host === "bitbucket.org" && allowedOwners.length) {
736
- onChange({ workspace: allowedOwners[0] });
737
- }
738
- }, [allowedOwners, host, onChange]);
739
- return /* @__PURE__ */ React.createElement(React.Fragment, null, host === "bitbucket.org" && /* @__PURE__ */ React.createElement(
740
- FormControl,
741
- {
742
- margin: "normal",
743
- required: true,
744
- error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !workspace
745
- },
746
- (allowedOwners == null ? void 0 : allowedOwners.length) ? /* @__PURE__ */ React.createElement(
747
- Select,
748
- {
749
- native: true,
750
- label: "Allowed Workspaces",
751
- onChange: (s) => onChange({ workspace: String(Array.isArray(s) ? s[0] : s) }),
752
- disabled: allowedOwners.length === 1,
753
- selected: workspace,
754
- items: ownerItems
755
- }
756
- ) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(InputLabel, { htmlFor: "workspaceInput" }, "Workspace"), /* @__PURE__ */ React.createElement(
757
- Input,
758
- {
759
- id: "workspaceInput",
760
- onChange: (e) => onChange({ workspace: e.target.value }),
761
- value: workspace
762
- }
763
- )),
764
- /* @__PURE__ */ React.createElement(FormHelperText, null, "The Workspace that this repo will belong to")
765
- ), /* @__PURE__ */ React.createElement(
766
- FormControl,
767
- {
768
- margin: "normal",
769
- required: true,
770
- error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !project
771
- },
772
- (allowedProjects == null ? void 0 : allowedProjects.length) ? /* @__PURE__ */ React.createElement(
773
- Select,
774
- {
775
- native: true,
776
- label: "Allowed Projects",
777
- onChange: (s) => onChange({ project: String(Array.isArray(s) ? s[0] : s) }),
778
- disabled: allowedProjects.length === 1,
779
- selected: project,
780
- items: projectItems
781
- }
782
- ) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(InputLabel, { htmlFor: "projectInput" }, "Project"), /* @__PURE__ */ React.createElement(
783
- Input,
784
- {
785
- id: "projectInput",
786
- onChange: (e) => onChange({ project: e.target.value }),
787
- value: project
788
- }
789
- )),
790
- /* @__PURE__ */ React.createElement(FormHelperText, null, "The Project that this repo will belong to")
791
- ));
792
- };
793
-
794
- const GerritRepoPicker = (props) => {
795
- const { onChange, rawErrors, state } = props;
796
- const { workspace, owner } = state;
797
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(FormControl, { margin: "normal", error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !workspace }, /* @__PURE__ */ React.createElement(InputLabel, { htmlFor: "ownerInput" }, "Owner"), /* @__PURE__ */ React.createElement(
798
- Input,
799
- {
800
- id: "ownerInput",
801
- onChange: (e) => onChange({ owner: e.target.value }),
802
- value: owner
803
- }
804
- ), /* @__PURE__ */ React.createElement(FormHelperText, null, "The owner of the project (optional)")), /* @__PURE__ */ React.createElement(
805
- FormControl,
806
- {
807
- margin: "normal",
808
- required: true,
809
- error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !workspace
810
- },
811
- /* @__PURE__ */ React.createElement(InputLabel, { htmlFor: "parentInput" }, "Parent"),
812
- /* @__PURE__ */ React.createElement(
813
- Input,
814
- {
815
- id: "parentInput",
816
- onChange: (e) => onChange({ workspace: e.target.value }),
817
- value: workspace
818
- }
819
- ),
820
- /* @__PURE__ */ React.createElement(FormHelperText, null, "The project parent that the repo will belong to")
821
- ));
822
- };
823
-
824
- const RepoUrlPickerHost = (props) => {
825
- const { host, hosts, onChange, rawErrors } = props;
826
- const scaffolderApi = useApi(scaffolderApiRef$1);
827
- const { value: { integrations } = { integrations: [] }, loading } = useAsync(
828
- async () => {
829
- return await scaffolderApi.getIntegrationsList({
830
- allowedHosts: hosts != null ? hosts : []
831
- });
832
- }
833
- );
834
- useEffect(() => {
835
- if (!host) {
836
- if (hosts == null ? void 0 : hosts.length) {
837
- onChange(hosts[0]);
838
- } else if (integrations == null ? void 0 : integrations.length) {
839
- onChange(integrations[0].host);
840
- }
841
- }
842
- }, [hosts, host, onChange, integrations]);
843
- const hostsOptions = integrations ? integrations.filter((i) => (hosts == null ? void 0 : hosts.length) ? hosts == null ? void 0 : hosts.includes(i.host) : true).map((i) => ({ label: i.title, value: i.host })) : [{ label: "Loading...", value: "loading" }];
844
- if (loading) {
845
- return /* @__PURE__ */ React.createElement(Progress, null);
846
- }
847
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
848
- FormControl,
849
- {
850
- margin: "normal",
851
- required: true,
852
- error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !host
853
- },
854
- /* @__PURE__ */ React.createElement(
855
- Select,
856
- {
857
- native: true,
858
- disabled: (hosts == null ? void 0 : hosts.length) === 1,
859
- label: "Host",
860
- onChange: (s) => onChange(String(Array.isArray(s) ? s[0] : s)),
861
- selected: host,
862
- items: hostsOptions,
863
- "data-testid": "host-select"
864
- }
865
- ),
866
- /* @__PURE__ */ React.createElement(FormHelperText, null, "The host where the repository will be created")
867
- ));
868
- };
869
-
870
- const RepoUrlPickerRepoName = (props) => {
871
- const { repoName, allowedRepos, onChange, rawErrors } = props;
872
- useEffect(() => {
873
- if (!repoName) {
874
- if (allowedRepos == null ? void 0 : allowedRepos.length) {
875
- onChange(allowedRepos[0]);
876
- }
877
- }
878
- }, [allowedRepos, repoName, onChange]);
879
- const repoItems = allowedRepos ? allowedRepos.map((i) => ({ label: i, value: i })) : [{ label: "Loading...", value: "loading" }];
880
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
881
- FormControl,
882
- {
883
- margin: "normal",
884
- required: true,
885
- error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !repoName
886
- },
887
- (allowedRepos == null ? void 0 : allowedRepos.length) ? /* @__PURE__ */ React.createElement(
888
- Select,
889
- {
890
- native: true,
891
- label: "Repositories Available",
892
- onChange: (selected) => String(Array.isArray(selected) ? selected[0] : selected),
893
- disabled: allowedRepos.length === 1,
894
- selected: repoName,
895
- items: repoItems
896
- }
897
- ) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(InputLabel, { htmlFor: "repoNameInput" }, "Repository"), /* @__PURE__ */ React.createElement(
898
- Input,
899
- {
900
- id: "repoNameInput",
901
- onChange: (e) => onChange(String(e.target.value)),
902
- value: repoName
903
- }
904
- )),
905
- /* @__PURE__ */ React.createElement(FormHelperText, null, "The name of the repository")
906
- ));
907
- };
908
-
909
- function serializeRepoPickerUrl(data) {
910
- if (!data.host) {
911
- return void 0;
912
- }
913
- const params = new URLSearchParams();
914
- if (data.owner) {
915
- params.set("owner", data.owner);
916
- }
917
- if (data.repoName) {
918
- params.set("repo", data.repoName);
919
- }
920
- if (data.organization) {
921
- params.set("organization", data.organization);
922
- }
923
- if (data.workspace) {
924
- params.set("workspace", data.workspace);
925
- }
926
- if (data.project) {
927
- params.set("project", data.project);
928
- }
929
- return `${data.host}?${params.toString()}`;
930
- }
931
- function parseRepoPickerUrl(url) {
932
- let host = "";
933
- let owner = "";
934
- let repoName = "";
935
- let organization = "";
936
- let workspace = "";
937
- let project = "";
938
- try {
939
- if (url) {
940
- const parsed = new URL(`https://${url}`);
941
- host = parsed.host;
942
- owner = parsed.searchParams.get("owner") || "";
943
- repoName = parsed.searchParams.get("repo") || "";
944
- organization = parsed.searchParams.get("organization") || "";
945
- workspace = parsed.searchParams.get("workspace") || "";
946
- project = parsed.searchParams.get("project") || "";
947
- }
948
- } catch {
949
- }
950
- return { host, owner, repoName, organization, workspace, project };
951
- }
952
-
953
- const RepoUrlPicker = (props) => {
954
- var _a, _b;
955
- const { uiSchema, onChange, rawErrors, formData } = props;
956
- const [state, setState] = useState(
957
- parseRepoPickerUrl(formData)
958
- );
959
- const integrationApi = useApi(scmIntegrationsApiRef);
960
- const scmAuthApi = useApi(scmAuthApiRef);
961
- const { setSecrets } = useTemplateSecrets$1();
962
- const allowedHosts = useMemo(
963
- () => {
964
- var _a2, _b2;
965
- return (_b2 = (_a2 = uiSchema == null ? void 0 : uiSchema["ui:options"]) == null ? void 0 : _a2.allowedHosts) != null ? _b2 : [];
966
- },
967
- [uiSchema]
968
- );
969
- const allowedOrganizations = useMemo(
970
- () => {
971
- var _a2, _b2;
972
- return (_b2 = (_a2 = uiSchema == null ? void 0 : uiSchema["ui:options"]) == null ? void 0 : _a2.allowedOrganizations) != null ? _b2 : [];
973
- },
974
- [uiSchema]
975
- );
976
- const allowedOwners = useMemo(
977
- () => {
978
- var _a2, _b2;
979
- return (_b2 = (_a2 = uiSchema == null ? void 0 : uiSchema["ui:options"]) == null ? void 0 : _a2.allowedOwners) != null ? _b2 : [];
980
- },
981
- [uiSchema]
982
- );
983
- const allowedProjects = useMemo(
984
- () => {
985
- var _a2, _b2;
986
- return (_b2 = (_a2 = uiSchema == null ? void 0 : uiSchema["ui:options"]) == null ? void 0 : _a2.allowedProjects) != null ? _b2 : [];
987
- },
988
- [uiSchema]
989
- );
990
- const allowedRepos = useMemo(
991
- () => {
992
- var _a2, _b2;
993
- return (_b2 = (_a2 = uiSchema == null ? void 0 : uiSchema["ui:options"]) == null ? void 0 : _a2.allowedRepos) != null ? _b2 : [];
994
- },
995
- [uiSchema]
996
- );
997
- const { owner, organization, project, repoName } = state;
998
- useEffect(() => {
999
- onChange(serializeRepoPickerUrl(state));
1000
- }, [state, onChange]);
1001
- useEffect(() => {
1002
- if (allowedOrganizations.length > 0 && !organization) {
1003
- setState((prevState) => ({
1004
- ...prevState,
1005
- organization: allowedOrganizations[0]
1006
- }));
1007
- }
1008
- }, [setState, allowedOrganizations, organization]);
1009
- useEffect(() => {
1010
- if (allowedOwners.length > 0 && !owner) {
1011
- setState((prevState) => ({
1012
- ...prevState,
1013
- owner: allowedOwners[0]
1014
- }));
1015
- }
1016
- }, [setState, allowedOwners, owner]);
1017
- useEffect(() => {
1018
- if (allowedProjects.length > 0 && !project) {
1019
- setState((prevState) => ({
1020
- ...prevState,
1021
- project: allowedProjects[0]
1022
- }));
1023
- }
1024
- }, [setState, allowedProjects, project]);
1025
- useEffect(() => {
1026
- if (allowedRepos.length > 0 && !repoName) {
1027
- setState((prevState) => ({ ...prevState, repoName: allowedRepos[0] }));
1028
- }
1029
- }, [setState, allowedRepos, repoName]);
1030
- const updateLocalState = useCallback(
1031
- (newState) => {
1032
- setState((prevState) => ({ ...prevState, ...newState }));
1033
- },
1034
- [setState]
1035
- );
1036
- useDebounce(
1037
- async () => {
1038
- var _a2;
1039
- const { requestUserCredentials } = (_a2 = uiSchema == null ? void 0 : uiSchema["ui:options"]) != null ? _a2 : {};
1040
- const workspace = state.owner ? state.owner : state.project;
1041
- if (!requestUserCredentials || !(state.host && workspace && state.repoName)) {
1042
- return;
1043
- }
1044
- const [encodedHost, encodedWorkspace, encodedRepoName] = [
1045
- state.host,
1046
- workspace,
1047
- state.repoName
1048
- ].map(encodeURIComponent);
1049
- const { token } = await scmAuthApi.getCredentials({
1050
- url: `https://${encodedHost}/${encodedWorkspace}/${encodedRepoName}`,
1051
- additionalScope: {
1052
- repoWrite: true,
1053
- customScopes: requestUserCredentials.additionalScopes
1054
- }
1055
- });
1056
- setSecrets({ [requestUserCredentials.secretsKey]: token });
1057
- },
1058
- 500,
1059
- [state, uiSchema]
1060
- );
1061
- const hostType = (_b = state.host && ((_a = integrationApi.byHost(state.host)) == null ? void 0 : _a.type)) != null ? _b : null;
1062
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
1063
- RepoUrlPickerHost,
1064
- {
1065
- host: state.host,
1066
- hosts: allowedHosts,
1067
- onChange: (host) => setState((prevState) => ({ ...prevState, host })),
1068
- rawErrors
1069
- }
1070
- ), hostType === "github" && /* @__PURE__ */ React.createElement(
1071
- GithubRepoPicker,
1072
- {
1073
- allowedOwners,
1074
- onChange: updateLocalState,
1075
- rawErrors,
1076
- state
1077
- }
1078
- ), hostType === "gitlab" && /* @__PURE__ */ React.createElement(
1079
- GitlabRepoPicker,
1080
- {
1081
- allowedOwners,
1082
- rawErrors,
1083
- state,
1084
- onChange: updateLocalState
1085
- }
1086
- ), hostType === "bitbucket" && /* @__PURE__ */ React.createElement(
1087
- BitbucketRepoPicker,
1088
- {
1089
- allowedOwners,
1090
- allowedProjects,
1091
- rawErrors,
1092
- state,
1093
- onChange: updateLocalState
1094
- }
1095
- ), hostType === "azure" && /* @__PURE__ */ React.createElement(
1096
- AzureRepoPicker,
1097
- {
1098
- allowedOrganizations,
1099
- allowedOwners,
1100
- rawErrors,
1101
- state,
1102
- onChange: updateLocalState
1103
- }
1104
- ), hostType === "gerrit" && /* @__PURE__ */ React.createElement(
1105
- GerritRepoPicker,
1106
- {
1107
- rawErrors,
1108
- state,
1109
- onChange: updateLocalState
1110
- }
1111
- ), /* @__PURE__ */ React.createElement(
1112
- RepoUrlPickerRepoName,
1113
- {
1114
- repoName: state.repoName,
1115
- allowedRepos,
1116
- onChange: (repo) => setState((prevState) => ({ ...prevState, repoName: repo })),
1117
- rawErrors
1118
- }
1119
- ));
1120
- };
1121
-
1122
- const OwnedEntityPickerFieldSchema = makeFieldSchemaFromZod(
1123
- z.string(),
1124
- z.object({
1125
- allowedKinds: z.array(z.string()).optional().describe("List of kinds of entities to derive options from"),
1126
- defaultKind: z.string().optional().describe(
1127
- "The default entity kind. Options of this kind will not be prefixed."
1128
- ),
1129
- allowArbitraryValues: z.boolean().optional().describe("Whether to allow arbitrary user input. Defaults to true"),
1130
- defaultNamespace: z.union([z.string(), z.literal(false)]).optional().describe(
1131
- "The default namespace. Options with this namespace will not be prefixed."
1132
- )
1133
- })
1134
- );
1135
- const OwnedEntityPickerSchema = OwnedEntityPickerFieldSchema.schema;
1136
-
1137
- const OwnedEntityPicker = (props) => {
1138
- var _a;
1139
- const {
1140
- schema: { title = "Entity", description = "An entity from the catalog" },
1141
- uiSchema,
1142
- required
1143
- } = props;
1144
- const identityApi = useApi(identityApiRef);
1145
- const { loading, value: identityRefs } = useAsync(async () => {
1146
- const identity = await identityApi.getBackstageIdentity();
1147
- return identity.ownershipEntityRefs;
1148
- });
1149
- const allowedKinds = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.allowedKinds;
1150
- if (loading)
1151
- return /* @__PURE__ */ React.createElement(
1152
- Autocomplete,
1153
- {
1154
- loading,
1155
- renderInput: (params) => /* @__PURE__ */ React.createElement(
1156
- TextField,
1157
- {
1158
- ...params,
1159
- label: title,
1160
- margin: "dense",
1161
- helperText: description,
1162
- FormHelperTextProps: { margin: "dense", style: { marginLeft: 0 } },
1163
- variant: "outlined",
1164
- required,
1165
- InputProps: params.InputProps
1166
- }
1167
- ),
1168
- options: []
1169
- }
1170
- );
1171
- return /* @__PURE__ */ React.createElement(
1172
- EntityPicker,
1173
- {
1174
- ...props,
1175
- schema: { title, description },
1176
- allowedKinds,
1177
- catalogFilter: allowedKinds ? {
1178
- filter: {
1179
- kind: allowedKinds,
1180
- [`relations.${RELATION_OWNED_BY}`]: identityRefs || []
1181
- }
1182
- } : {
1183
- filter: {
1184
- [`relations.${RELATION_OWNED_BY}`]: identityRefs || []
1185
- }
1186
- }
1187
- }
1188
- );
1189
- };
1190
-
1191
- const EntityTagsPickerFieldSchema = makeFieldSchemaFromZod(
1192
- z.array(z.string()),
1193
- z.object({
1194
- kinds: z.array(z.string()).optional().describe("List of kinds of entities to derive tags from"),
1195
- showCounts: z.boolean().optional().describe("Whether to show usage counts per tag"),
1196
- helperText: z.string().optional().describe("Helper text to display")
1197
- })
1198
- );
1199
- const EntityTagsPickerSchema = EntityTagsPickerFieldSchema.schema;
1200
-
1201
- const EntityTagsPicker = (props) => {
1202
- var _a, _b, _c;
1203
- const { formData, onChange, uiSchema } = props;
1204
- const catalogApi = useApi(catalogApiRef);
1205
- const [tagOptions, setTagOptions] = useState([]);
1206
- const [inputValue, setInputValue] = useState("");
1207
- const [inputError, setInputError] = useState(false);
1208
- const tagValidator = makeValidator().isValidTag;
1209
- const kinds = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.kinds;
1210
- const showCounts = (_b = uiSchema["ui:options"]) == null ? void 0 : _b.showCounts;
1211
- const helperText = (_c = uiSchema["ui:options"]) == null ? void 0 : _c.helperText;
1212
- const { loading, value: existingTags } = useAsync(async () => {
1213
- const facet = "metadata.tags";
1214
- const tagsRequest = { facets: [facet] };
1215
- if (kinds) {
1216
- tagsRequest.filter = { kind: kinds };
1217
- }
1218
- const { facets } = await catalogApi.getEntityFacets(tagsRequest);
1219
- const tagFacets = Object.fromEntries(
1220
- facets[facet].map(({ value, count }) => [value, count])
1221
- );
1222
- setTagOptions(
1223
- Object.keys(tagFacets).sort(
1224
- (a, b) => showCounts ? tagFacets[b] - tagFacets[a] : a.localeCompare(b)
1225
- )
1226
- );
1227
- return tagFacets;
1228
- });
1229
- const setTags = (_, values) => {
1230
- let hasError = false;
1231
- let addDuplicate = false;
1232
- const currentTags = formData || [];
1233
- if ((values == null ? void 0 : values.length) && currentTags.length < values.length) {
1234
- const newTag = values[values.length - 1] = values[values.length - 1].toLocaleLowerCase("en-US").trim();
1235
- hasError = !tagValidator(newTag);
1236
- addDuplicate = currentTags.indexOf(newTag) !== -1;
1237
- }
1238
- setInputError(hasError);
1239
- setInputValue(!hasError ? "" : inputValue);
1240
- if (!hasError && !addDuplicate) {
1241
- onChange(values || []);
1242
- }
1243
- };
1244
- useEffectOnce(() => onChange(formData || []));
1245
- return /* @__PURE__ */ React.createElement(FormControl$1, { margin: "normal" }, /* @__PURE__ */ React.createElement(
1246
- Autocomplete$1,
1247
- {
1248
- multiple: true,
1249
- freeSolo: true,
1250
- filterSelectedOptions: true,
1251
- onChange: setTags,
1252
- value: formData || [],
1253
- inputValue,
1254
- loading,
1255
- options: tagOptions,
1256
- ChipProps: { size: "small" },
1257
- renderOption: (option) => showCounts ? `${option} (${existingTags == null ? void 0 : existingTags[option]})` : option,
1258
- renderInput: (params) => /* @__PURE__ */ React.createElement(
1259
- TextField,
1260
- {
1261
- ...params,
1262
- label: "Tags",
1263
- onChange: (e) => setInputValue(e.target.value),
1264
- error: inputError,
1265
- helperText: helperText != null ? helperText : "Add any relevant tags, hit 'Enter' to add new tags. Valid format: [a-z0-9+#] separated by [-], at most 63 characters"
1266
- }
1267
- )
1268
- }
1269
- ));
1270
- };
1271
-
1272
- const registerComponentRouteRef = createExternalRouteRef({
1273
- id: "register-component",
1274
- optional: true
1275
- });
1276
- const viewTechDocRouteRef = createExternalRouteRef({
1277
- id: "view-techdoc",
1278
- optional: true,
1279
- params: ["namespace", "kind", "name"]
1280
- });
1281
- const rootRouteRef$1 = createRouteRef({
1282
- id: "scaffolder"
1283
- });
1284
- const legacySelectedTemplateRouteRef = createSubRouteRef({
1285
- id: "scaffolder/legacy/selected-template",
1286
- parent: rootRouteRef$1,
1287
- path: "/templates/:templateName"
1288
- });
1289
- const selectedTemplateRouteRef = createSubRouteRef({
1290
- id: "scaffolder/selected-template",
1291
- parent: rootRouteRef$1,
1292
- path: "/templates/:namespace/:templateName"
1293
- });
1294
- const scaffolderTaskRouteRef = createSubRouteRef({
1295
- id: "scaffolder/task",
1296
- parent: rootRouteRef$1,
1297
- path: "/tasks/:taskId"
1298
- });
1299
- const scaffolderListTaskRouteRef = createSubRouteRef({
1300
- id: "scaffolder/list-tasks",
1301
- parent: rootRouteRef$1,
1302
- path: "/tasks"
1303
- });
1304
- const actionsRouteRef = createSubRouteRef({
1305
- id: "scaffolder/actions",
1306
- parent: rootRouteRef$1,
1307
- path: "/actions"
1308
- });
1309
- const editRouteRef = createSubRouteRef({
1310
- id: "scaffolder/edit",
1311
- parent: rootRouteRef$1,
1312
- path: "/edit"
1313
- });
1314
-
1315
- const nextRouteRef = createRouteRef({
1316
- id: "scaffolder/next"
1317
- });
1318
- const nextSelectedTemplateRouteRef = createSubRouteRef({
1319
- id: "scaffolder/next/selected-template",
1320
- parent: nextRouteRef,
1321
- path: "/templates/:namespace/:templateName"
1322
- });
1323
- const nextScaffolderTaskRouteRef = createSubRouteRef({
1324
- id: "scaffolder/next/task",
1325
- parent: nextRouteRef,
1326
- path: "/tasks/:taskId"
1327
- });
1328
- const nextScaffolderListTaskRouteRef = createSubRouteRef({
1329
- id: "scaffolder/next/list-tasks",
1330
- parent: nextRouteRef,
1331
- path: "/tasks"
1332
- });
1333
- const nextActionsRouteRef = createSubRouteRef({
1334
- id: "scaffolder/next/actions",
1335
- parent: nextRouteRef,
1336
- path: "/actions"
1337
- });
1338
- const nextEditRouteRef = createSubRouteRef({
1339
- id: "scaffolder/next/edit",
1340
- parent: nextRouteRef,
1341
- path: "/edit"
1342
- });
1343
-
1344
- const useStyles$i = makeStyles((theme) => ({
1345
- button: {
1346
- color: theme.page.fontColor
1347
- }
1348
- }));
1349
- function ContextMenu$1(props) {
1350
- const classes = useStyles$i();
1351
- const [anchorEl, setAnchorEl] = useState();
1352
- const editLink = useRouteRef(nextEditRouteRef);
1353
- const actionsLink = useRouteRef(nextActionsRouteRef);
1354
- const tasksLink = useRouteRef(nextScaffolderListTaskRouteRef);
1355
- const navigate = useNavigate();
1356
- const showEditor = props.editor !== false;
1357
- const showActions = props.actions !== false;
1358
- const showTasks = props.tasks !== false;
1359
- if (!showEditor && !showActions) {
1360
- return null;
1361
- }
1362
- const onOpen = (event) => {
1363
- setAnchorEl(event.currentTarget);
1364
- };
1365
- const onClose = () => {
1366
- setAnchorEl(void 0);
1367
- };
1368
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
1369
- IconButton,
1370
- {
1371
- "aria-label": "more",
1372
- "aria-controls": "long-menu",
1373
- "aria-haspopup": "true",
1374
- onClick: onOpen,
1375
- "data-testid": "menu-button",
1376
- color: "inherit",
1377
- className: classes.button
1378
- },
1379
- /* @__PURE__ */ React.createElement(MoreVert, null)
1380
- ), /* @__PURE__ */ React.createElement(
1381
- Popover,
1382
- {
1383
- open: Boolean(anchorEl),
1384
- onClose,
1385
- anchorEl,
1386
- anchorOrigin: { vertical: "bottom", horizontal: "right" },
1387
- transformOrigin: { vertical: "top", horizontal: "right" }
1388
- },
1389
- /* @__PURE__ */ React.createElement(MenuList, null, showEditor && /* @__PURE__ */ React.createElement(MenuItem, { onClick: () => navigate(editLink()) }, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(Edit, { fontSize: "small" })), /* @__PURE__ */ React.createElement(ListItemText, { primary: "Template Editor" })), showActions && /* @__PURE__ */ React.createElement(MenuItem, { onClick: () => navigate(actionsLink()) }, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(Description, { fontSize: "small" })), /* @__PURE__ */ React.createElement(ListItemText, { primary: "Installed Actions" })), showTasks && /* @__PURE__ */ React.createElement(MenuItem, { onClick: () => navigate(tasksLink()) }, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(List, { fontSize: "small" })), /* @__PURE__ */ React.createElement(ListItemText, { primary: "Task List" })))
1390
- ));
1391
- }
1392
-
1393
- const useStyles$h = makeStyles$1((theme) => ({
1394
- button: {
1395
- color: theme.palette.common.white
1396
- }
1397
- }));
1398
- const ContextMenu = (props) => {
1399
- const { logsVisible, onToggleLogs, onStartOver } = props;
1400
- const classes = useStyles$h();
1401
- const [anchorEl, setAnchorEl] = useState();
1402
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
1403
- IconButton$1,
1404
- {
1405
- "aria-label": "more",
1406
- "aria-controls": "long-menu",
1407
- "aria-haspopup": "true",
1408
- onClick: (event) => {
1409
- setAnchorEl(event.currentTarget);
1410
- },
1411
- "data-testid": "menu-button",
1412
- color: "inherit",
1413
- className: classes.button
1414
- },
1415
- /* @__PURE__ */ React.createElement(MoreVert, null)
1416
- ), /* @__PURE__ */ React.createElement(
1417
- Popover$1,
1418
- {
1419
- open: Boolean(anchorEl),
1420
- onClose: () => setAnchorEl(void 0),
1421
- anchorEl,
1422
- anchorOrigin: { vertical: "bottom", horizontal: "right" },
1423
- transformOrigin: { vertical: "top", horizontal: "right" }
1424
- },
1425
- /* @__PURE__ */ React.createElement(MenuList$1, null, /* @__PURE__ */ React.createElement(MenuItem$1, { onClick: () => onToggleLogs == null ? void 0 : onToggleLogs(!logsVisible) }, /* @__PURE__ */ React.createElement(ListItemIcon$1, null, /* @__PURE__ */ React.createElement(Toc, { fontSize: "small" })), /* @__PURE__ */ React.createElement(ListItemText$1, { primary: logsVisible ? "Hide Logs" : "Show Logs" })), /* @__PURE__ */ React.createElement(MenuItem$1, { onClick: onStartOver }, /* @__PURE__ */ React.createElement(ListItemIcon$1, null, /* @__PURE__ */ React.createElement(Retry, { fontSize: "small" })), /* @__PURE__ */ React.createElement(ListItemText$1, { primary: "Start Over" })))
1426
- ));
1427
- };
1428
-
1429
- const useStyles$g = makeStyles$1({
1430
- contentWrapper: {
1431
- display: "flex",
1432
- flexDirection: "column"
1433
- }
1434
- });
1435
- const OngoingTask = (props) => {
1436
- var _a, _b, _c, _d, _e, _f, _g, _h;
1437
- const { taskId } = useParams();
1438
- const templateRouteRef = useRouteRef(nextSelectedTemplateRouteRef);
1439
- const navigate = useNavigate();
1440
- const taskStream = useTaskEventStream(taskId);
1441
- const classes = useStyles$g();
1442
- const steps = useMemo(
1443
- () => {
1444
- var _a2, _b2;
1445
- return (_b2 = (_a2 = taskStream.task) == null ? void 0 : _a2.spec.steps.map((step) => {
1446
- var _a3;
1447
- return {
1448
- ...step,
1449
- ...(_a3 = taskStream == null ? void 0 : taskStream.steps) == null ? void 0 : _a3[step.id]
1450
- };
1451
- })) != null ? _b2 : [];
1452
- },
1453
- [taskStream]
1454
- );
1455
- const [logsVisible, setLogVisibleState] = useState(false);
1456
- useEffect(() => {
1457
- if (taskStream.error) {
1458
- setLogVisibleState(true);
1459
- }
1460
- }, [taskStream.error]);
1461
- const activeStep = useMemo(() => {
1462
- for (let i = steps.length - 1; i >= 0; i--) {
1463
- if (steps[i].status !== "open") {
1464
- return i;
1465
- }
1466
- }
1467
- return 0;
1468
- }, [steps]);
1469
- const startOver = useCallback(() => {
1470
- var _a2, _b2, _c2, _d2, _e2, _f2;
1471
- const { namespace, name } = (_d2 = (_c2 = (_b2 = (_a2 = taskStream.task) == null ? void 0 : _a2.spec.templateInfo) == null ? void 0 : _b2.entity) == null ? void 0 : _c2.metadata) != null ? _d2 : {};
1472
- const formData = (_f2 = (_e2 = taskStream.task) == null ? void 0 : _e2.spec.parameters) != null ? _f2 : {};
1473
- if (!namespace || !name) {
1474
- return;
1475
- }
1476
- navigate({
1477
- pathname: templateRouteRef({
1478
- namespace,
1479
- templateName: name
1480
- }),
1481
- search: `?${qs.stringify({ formData: JSON.stringify(formData) })}`
1482
- });
1483
- }, [
1484
- navigate,
1485
- (_a = taskStream.task) == null ? void 0 : _a.spec.parameters,
1486
- (_d = (_c = (_b = taskStream.task) == null ? void 0 : _b.spec.templateInfo) == null ? void 0 : _c.entity) == null ? void 0 : _d.metadata,
1487
- templateRouteRef
1488
- ]);
1489
- const Outputs = (_e = props.TemplateOutputsComponent) != null ? _e : DefaultTemplateOutputs;
1490
- const templateName = (_h = (_g = (_f = taskStream.task) == null ? void 0 : _f.spec.templateInfo) == null ? void 0 : _g.entity) == null ? void 0 : _h.metadata.name;
1491
- return /* @__PURE__ */ React.createElement(Page, { themeId: "website" }, /* @__PURE__ */ React.createElement(
1492
- Header,
1493
- {
1494
- pageTitleOverride: `Run of ${templateName}`,
1495
- title: /* @__PURE__ */ React.createElement("div", null, "Run of ", /* @__PURE__ */ React.createElement("code", null, templateName)),
1496
- subtitle: `Task ${taskId}`
1497
- },
1498
- /* @__PURE__ */ React.createElement(
1499
- ContextMenu,
1500
- {
1501
- onToggleLogs: setLogVisibleState,
1502
- onStartOver: startOver,
1503
- logsVisible
1504
- }
1505
- )
1506
- ), /* @__PURE__ */ React.createElement(Content, { className: classes.contentWrapper }, taskStream.error ? /* @__PURE__ */ React.createElement(Box, { paddingBottom: 2 }, /* @__PURE__ */ React.createElement(
1507
- ErrorPanel,
1508
- {
1509
- error: taskStream.error,
1510
- title: taskStream.error.message
1511
- }
1512
- )) : null, /* @__PURE__ */ React.createElement(Box, { paddingBottom: 2 }, /* @__PURE__ */ React.createElement(
1513
- TaskSteps,
1514
- {
1515
- steps,
1516
- activeStep,
1517
- isComplete: taskStream.completed,
1518
- isError: Boolean(taskStream.error)
1519
- }
1520
- )), /* @__PURE__ */ React.createElement(Outputs, { output: taskStream.output }), logsVisible ? /* @__PURE__ */ React.createElement(Box, { paddingBottom: 2, height: "100%" }, /* @__PURE__ */ React.createElement(Paper, { style: { height: "100%" } }, /* @__PURE__ */ React.createElement(Box, { padding: 2, height: "100%" }, /* @__PURE__ */ React.createElement(TaskLogStream, { logs: taskStream.stepLogs })))) : null));
1521
- };
1522
-
1523
- const useStyles$f = makeStyles$1((theme) => ({
1524
- code: {
1525
- fontFamily: "Menlo, monospace",
1526
- padding: theme.spacing(1),
1527
- backgroundColor: theme.palette.type === "dark" ? theme.palette.grey[700] : theme.palette.grey[300],
1528
- display: "inline-block",
1529
- borderRadius: 5,
1530
- border: `1px solid ${theme.palette.grey[500]}`,
1531
- position: "relative"
1532
- },
1533
- codeRequired: {
1534
- "&::after": {
1535
- position: "absolute",
1536
- content: '"*"',
1537
- top: 0,
1538
- right: theme.spacing(0.5),
1539
- fontWeight: "bolder",
1540
- color: theme.palette.error.light
1541
- }
1542
- }
1543
- }));
1544
- const ExamplesTable = (props) => {
1545
- return /* @__PURE__ */ React.createElement(Grid, { container: true }, props.examples.map((example, index) => {
1546
- return /* @__PURE__ */ React.createElement(Fragment, { key: `example-${index}` }, /* @__PURE__ */ React.createElement(Grid, { item: true, lg: 3 }, /* @__PURE__ */ React.createElement(Box, { padding: 4 }, /* @__PURE__ */ React.createElement(Typography, null, example.description))), /* @__PURE__ */ React.createElement(Grid, { item: true, lg: 9 }, /* @__PURE__ */ React.createElement(Box, { padding: 1 }, /* @__PURE__ */ React.createElement(
1547
- CodeSnippet,
1548
- {
1549
- text: example.example,
1550
- showLineNumbers: true,
1551
- showCopyCodeButton: true,
1552
- language: "yaml"
1553
- }
1554
- ))));
1555
- }));
1556
- };
1557
- const ActionsPage = () => {
1558
- const api = useApi(scaffolderApiRef$1);
1559
- const classes = useStyles$f();
1560
- const { loading, value, error } = useAsync(async () => {
1561
- return api.listActions();
1562
- });
1563
- if (loading) {
1564
- return /* @__PURE__ */ React.createElement(Progress, null);
1565
- }
1566
- if (error) {
1567
- return /* @__PURE__ */ React.createElement(
1568
- ErrorPage,
1569
- {
1570
- statusMessage: "Failed to load installed actions",
1571
- status: "500"
1572
- }
1573
- );
1574
- }
1575
- const formatRows = (input) => {
1576
- const properties = input.properties;
1577
- if (!properties) {
1578
- return void 0;
1579
- }
1580
- return Object.entries(properties).map((entry) => {
1581
- var _a;
1582
- const [key] = entry;
1583
- const props = entry[1];
1584
- const codeClassname = classNames(classes.code, {
1585
- [classes.codeRequired]: (_a = input.required) == null ? void 0 : _a.includes(key)
1586
- });
1587
- return /* @__PURE__ */ React.createElement(TableRow, { key }, /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement("div", { className: codeClassname }, key)), /* @__PURE__ */ React.createElement(TableCell, null, props.title), /* @__PURE__ */ React.createElement(TableCell, null, props.description), /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(React.Fragment, null, [props.type].flat().map((type) => /* @__PURE__ */ React.createElement(Chip, { label: type, key: type })))));
1588
- });
1589
- };
1590
- const renderTable = (input) => {
1591
- if (!input.properties) {
1592
- return void 0;
1593
- }
1594
- return /* @__PURE__ */ React.createElement(TableContainer, { component: Paper }, /* @__PURE__ */ React.createElement(Table, { size: "small" }, /* @__PURE__ */ React.createElement(TableHead, null, /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, null, "Name"), /* @__PURE__ */ React.createElement(TableCell, null, "Title"), /* @__PURE__ */ React.createElement(TableCell, null, "Description"), /* @__PURE__ */ React.createElement(TableCell, null, "Type"))), /* @__PURE__ */ React.createElement(TableBody, null, formatRows(input))));
1595
- };
1596
- const renderTables = (name, input) => {
1597
- if (!input) {
1598
- return void 0;
1599
- }
1600
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, name), input.map((i, index) => /* @__PURE__ */ React.createElement("div", { key: index }, renderTable(i))));
1601
- };
1602
- const items = value == null ? void 0 : value.map((action) => {
1603
- var _a, _b, _c, _d;
1604
- if (action.id.startsWith("legacy:")) {
1605
- return void 0;
1606
- }
1607
- const oneOf = renderTables("oneOf", (_b = (_a = action.schema) == null ? void 0 : _a.input) == null ? void 0 : _b.oneOf);
1608
- return /* @__PURE__ */ React.createElement(Box, { pb: 4, key: action.id }, /* @__PURE__ */ React.createElement(Typography, { variant: "h4", className: classes.code }, action.id), action.description && /* @__PURE__ */ React.createElement(MarkdownContent, { content: action.description }), ((_c = action.schema) == null ? void 0 : _c.input) && /* @__PURE__ */ React.createElement(Box, { pb: 2 }, /* @__PURE__ */ React.createElement(Typography, { variant: "h5" }, "Input"), renderTable(action.schema.input), oneOf), ((_d = action.schema) == null ? void 0 : _d.output) && /* @__PURE__ */ React.createElement(Box, { pb: 2 }, /* @__PURE__ */ React.createElement(Typography, { variant: "h5" }, "Output"), renderTable(action.schema.output)), action.examples && /* @__PURE__ */ React.createElement(Accordion, null, /* @__PURE__ */ React.createElement(AccordionSummary, { expandIcon: /* @__PURE__ */ React.createElement(ExpandMoreIcon, null) }, /* @__PURE__ */ React.createElement(Typography, { variant: "h5" }, "Examples")), /* @__PURE__ */ React.createElement(AccordionDetails, null, /* @__PURE__ */ React.createElement(Box, { pb: 2 }, /* @__PURE__ */ React.createElement(ExamplesTable, { examples: action.examples })))));
1609
- });
1610
- return /* @__PURE__ */ React.createElement(Page, { themeId: "home" }, /* @__PURE__ */ React.createElement(
1611
- Header,
1612
- {
1613
- pageTitleOverride: "Create a New Component",
1614
- title: "Installed actions",
1615
- subtitle: "This is the collection of all installed actions"
1616
- }
1617
- ), /* @__PURE__ */ React.createElement(Content, null, items));
1618
- };
1619
-
1620
- const useStyles$e = makeStyles$1(
1621
- (theme) => ({
1622
- root: {
1623
- backgroundColor: "rgba(0, 0, 0, .11)",
1624
- boxShadow: "none",
1625
- margin: theme.spacing(1, 0, 1, 0)
1626
- },
1627
- title: {
1628
- margin: theme.spacing(1, 0, 0, 1),
1629
- textTransform: "uppercase",
1630
- fontSize: 12,
1631
- fontWeight: "bold"
1632
- },
1633
- listIcon: {
1634
- minWidth: 30,
1635
- color: theme.palette.text.primary
1636
- },
1637
- menuItem: {
1638
- minHeight: theme.spacing(6)
1639
- },
1640
- groupWrapper: {
1641
- margin: theme.spacing(1, 1, 2, 1)
1642
- }
1643
- }),
1644
- {
1645
- name: "ScaffolderReactOwnerListPicker"
1646
- }
1647
- );
1648
- function getFilterGroups() {
1649
- return [
1650
- {
1651
- name: "Task Owner",
1652
- items: [
1653
- {
1654
- id: "owned",
1655
- label: "Owned",
1656
- icon: SettingsIcon
1657
- },
1658
- {
1659
- id: "all",
1660
- label: "All",
1661
- icon: AllIcon
1662
- }
1663
- ]
1664
- }
1665
- ];
1666
- }
1667
- const OwnerListPicker = (props) => {
1668
- const { filter, onSelectOwner } = props;
1669
- const classes = useStyles$e();
1670
- const filterGroups = getFilterGroups();
1671
- return /* @__PURE__ */ React.createElement(Card, { className: classes.root }, filterGroups.map((group) => /* @__PURE__ */ React.createElement(Fragment, { key: group.name }, /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle2", className: classes.title }, group.name), /* @__PURE__ */ React.createElement(Card, { className: classes.groupWrapper }, /* @__PURE__ */ React.createElement(List$1, { disablePadding: true, dense: true }, group.items.map((item) => /* @__PURE__ */ React.createElement(
1672
- MenuItem$1,
1673
- {
1674
- key: item.id,
1675
- button: true,
1676
- divider: true,
1677
- onClick: () => onSelectOwner(item.id),
1678
- selected: item.id === filter,
1679
- className: classes.menuItem,
1680
- "data-testid": `owner-picker-${item.id}`
1681
- },
1682
- item.icon && /* @__PURE__ */ React.createElement(ListItemIcon$1, { className: classes.listIcon }, /* @__PURE__ */ React.createElement(item.icon, { fontSize: "small" })),
1683
- /* @__PURE__ */ React.createElement(ListItemText$1, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body1" }, item.label))
1684
- )))))));
1685
- };
1686
-
1687
- const showDirectoryPicker = window.showDirectoryPicker;
1688
- class WebFileAccess {
1689
- constructor(path, handle) {
1690
- this.path = path;
1691
- this.handle = handle;
1692
- }
1693
- file() {
1694
- return this.handle.getFile();
1695
- }
1696
- async save(data) {
1697
- const writable = await this.handle.createWritable();
1698
- await writable.write(data);
1699
- await writable.close();
1700
- }
1701
- }
1702
- class WebDirectoryAccess {
1703
- constructor(handle) {
1704
- this.handle = handle;
1705
- }
1706
- async listFiles() {
1707
- const content = [];
1708
- for await (const entry of this.listDirectoryContents(this.handle)) {
1709
- content.push(entry);
1710
- }
1711
- return content;
1712
- }
1713
- async *listDirectoryContents(dirHandle, basePath = []) {
1714
- for await (const handle of dirHandle.values()) {
1715
- if (handle.kind === "file") {
1716
- yield new WebFileAccess([...basePath, handle.name].join("/"), handle);
1717
- } else if (handle.kind === "directory") {
1718
- if (handle.name === ".git") {
1719
- continue;
1720
- }
1721
- yield* this.listDirectoryContents(handle, [...basePath, handle.name]);
1722
- }
1723
- }
1724
- }
1725
- }
1726
- class WebFileSystemAccess {
1727
- static isSupported() {
1728
- return Boolean(showDirectoryPicker);
1729
- }
1730
- static async requestDirectoryAccess() {
1731
- if (!showDirectoryPicker) {
1732
- throw new Error("File system access is not supported");
1733
- }
1734
- const handle = await showDirectoryPicker();
1735
- return new WebDirectoryAccess(handle);
1736
- }
1737
- constructor() {
1738
- }
1739
- }
1740
-
1741
- const MAX_CONTENT_SIZE = 64 * 1024;
1742
- const CHUNK_SIZE = 32 * 1024;
1743
- const DryRunContext = createContext(void 0);
1744
- function base64EncodeContent(content) {
1745
- if (content.length > MAX_CONTENT_SIZE) {
1746
- return window.btoa("<file too large>");
1747
- }
1748
- try {
1749
- return window.btoa(content);
1750
- } catch {
1751
- const decoder = new TextEncoder();
1752
- const buffer = decoder.encode(content);
1753
- const chunks = new Array();
1754
- for (let offset = 0; offset < buffer.length; offset += CHUNK_SIZE) {
1755
- chunks.push(
1756
- String.fromCharCode(...buffer.slice(offset, offset + CHUNK_SIZE))
1757
- );
1758
- }
1759
- return window.btoa(chunks.join(""));
1760
- }
1761
- }
1762
- function DryRunProvider(props) {
1763
- const scaffolderApi = useApi(scaffolderApiRef$1);
1764
- const [state, setState] = useState({
1765
- results: [],
1766
- selectedResult: void 0
1767
- });
1768
- const idRef = useRef(1);
1769
- const selectResult = useCallback((id) => {
1770
- setState((prevState) => {
1771
- const result = prevState.results.find((r) => r.id === id);
1772
- if (result === prevState.selectedResult) {
1773
- return prevState;
1774
- }
1775
- return {
1776
- results: prevState.results,
1777
- selectedResult: result
1778
- };
1779
- });
1780
- }, []);
1781
- const deleteResult = useCallback((id) => {
1782
- setState((prevState) => {
1783
- var _a;
1784
- const index = prevState.results.findIndex((r) => r.id === id);
1785
- if (index === -1) {
1786
- return prevState;
1787
- }
1788
- const newResults = prevState.results.slice();
1789
- const [deleted] = newResults.splice(index, 1);
1790
- return {
1791
- results: newResults,
1792
- selectedResult: ((_a = prevState.selectedResult) == null ? void 0 : _a.id) === deleted.id ? newResults[0] : prevState.selectedResult
1793
- };
1794
- });
1795
- }, []);
1796
- const execute = useCallback(
1797
- async (options) => {
1798
- if (!scaffolderApi.dryRun) {
1799
- throw new Error("Scaffolder API does not support dry-run");
1800
- }
1801
- const parsed = yaml.parse(options.templateContent);
1802
- const response = await scaffolderApi.dryRun({
1803
- template: parsed,
1804
- values: options.values,
1805
- secrets: {},
1806
- directoryContents: options.files.map((file) => ({
1807
- path: file.path,
1808
- base64Content: base64EncodeContent(file.content)
1809
- }))
1810
- });
1811
- const result = {
1812
- ...response,
1813
- id: idRef.current++
1814
- };
1815
- setState((prevState) => {
1816
- var _a;
1817
- return {
1818
- results: [...prevState.results, result],
1819
- selectedResult: (_a = prevState.selectedResult) != null ? _a : result
1820
- };
1821
- });
1822
- },
1823
- [scaffolderApi]
1824
- );
1825
- const dryRun = useMemo(
1826
- () => ({
1827
- ...state,
1828
- selectResult,
1829
- deleteResult,
1830
- execute
1831
- }),
1832
- [state, selectResult, deleteResult, execute]
1833
- );
1834
- return /* @__PURE__ */ React.createElement(DryRunContext.Provider, { value: dryRun }, props.children);
1835
- }
1836
- function useDryRun() {
1837
- const value = useContext(DryRunContext);
1838
- if (!value) {
1839
- throw new Error("must be used within a DryRunProvider");
1840
- }
1841
- return value;
1842
- }
1843
-
1844
- var __accessCheck = (obj, member, msg) => {
1845
- if (!member.has(obj))
1846
- throw TypeError("Cannot " + msg);
1847
- };
1848
- var __privateGet = (obj, member, getter) => {
1849
- __accessCheck(obj, member, "read from private field");
1850
- return getter ? getter.call(obj) : member.get(obj);
1851
- };
1852
- var __privateAdd = (obj, member, value) => {
1853
- if (member.has(obj))
1854
- throw TypeError("Cannot add the same private member more than once");
1855
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1856
- };
1857
- var __privateSet = (obj, member, value, setter) => {
1858
- __accessCheck(obj, member, "write to private field");
1859
- setter ? setter.call(obj, value) : member.set(obj, value);
1860
- return value;
1861
- };
1862
- var _access, _signalUpdate, _content, _savedContent, _access2, _listeners, _files, _selectedFile, _signalUpdate2;
1863
- const MAX_SIZE = 1024 * 1024;
1864
- const MAX_SIZE_MESSAGE = "This file is too large to be displayed";
1865
- class DirectoryEditorFileManager {
1866
- constructor(access, signalUpdate) {
1867
- __privateAdd(this, _access, void 0);
1868
- __privateAdd(this, _signalUpdate, void 0);
1869
- __privateAdd(this, _content, void 0);
1870
- __privateAdd(this, _savedContent, void 0);
1871
- __privateSet(this, _access, access);
1872
- __privateSet(this, _signalUpdate, signalUpdate);
1873
- }
1874
- get path() {
1875
- return __privateGet(this, _access).path;
1876
- }
1877
- get content() {
1878
- var _a;
1879
- return (_a = __privateGet(this, _content)) != null ? _a : MAX_SIZE_MESSAGE;
1880
- }
1881
- updateContent(content) {
1882
- if (__privateGet(this, _content) === void 0) {
1883
- return;
1884
- }
1885
- __privateSet(this, _content, content);
1886
- __privateGet(this, _signalUpdate).call(this);
1887
- }
1888
- get dirty() {
1889
- return __privateGet(this, _content) !== __privateGet(this, _savedContent);
1890
- }
1891
- async save() {
1892
- if (__privateGet(this, _content) !== void 0) {
1893
- await __privateGet(this, _access).save(__privateGet(this, _content));
1894
- __privateSet(this, _savedContent, __privateGet(this, _content));
1895
- __privateGet(this, _signalUpdate).call(this);
1896
- }
1897
- }
1898
- async reload() {
1899
- const file = await __privateGet(this, _access).file();
1900
- if (file.size > MAX_SIZE) {
1901
- if (__privateGet(this, _content) !== void 0) {
1902
- __privateSet(this, _content, void 0);
1903
- __privateSet(this, _savedContent, void 0);
1904
- __privateGet(this, _signalUpdate).call(this);
1905
- }
1906
- return;
1907
- }
1908
- const content = await file.text();
1909
- if (__privateGet(this, _content) !== content) {
1910
- __privateSet(this, _content, content);
1911
- __privateSet(this, _savedContent, content);
1912
- __privateGet(this, _signalUpdate).call(this);
1913
- }
1914
- }
1915
- }
1916
- _access = new WeakMap();
1917
- _signalUpdate = new WeakMap();
1918
- _content = new WeakMap();
1919
- _savedContent = new WeakMap();
1920
- class DirectoryEditorManager {
1921
- constructor(access) {
1922
- __privateAdd(this, _access2, void 0);
1923
- __privateAdd(this, _listeners, /* @__PURE__ */ new Set());
1924
- __privateAdd(this, _files, []);
1925
- __privateAdd(this, _selectedFile, void 0);
1926
- this.setSelectedFile = (path) => {
1927
- const prev = __privateGet(this, _selectedFile);
1928
- const next = __privateGet(this, _files).find((file) => file.path === path);
1929
- if (prev !== next) {
1930
- __privateSet(this, _selectedFile, next);
1931
- __privateGet(this, _signalUpdate2).call(this);
1932
- }
1933
- };
1934
- __privateAdd(this, _signalUpdate2, () => {
1935
- __privateGet(this, _listeners).forEach((listener) => listener());
1936
- });
1937
- __privateSet(this, _access2, access);
1938
- }
1939
- get files() {
1940
- return __privateGet(this, _files);
1941
- }
1942
- get selectedFile() {
1943
- return __privateGet(this, _selectedFile);
1944
- }
1945
- get dirty() {
1946
- return __privateGet(this, _files).some((file) => file.dirty);
1947
- }
1948
- async save() {
1949
- await Promise.all(__privateGet(this, _files).map((file) => file.save()));
1950
- }
1951
- async reload() {
1952
- var _a;
1953
- const selectedPath = (_a = __privateGet(this, _selectedFile)) == null ? void 0 : _a.path;
1954
- const files = await __privateGet(this, _access2).listFiles();
1955
- const fileManagers = await Promise.all(
1956
- files.map(async (file) => {
1957
- const manager = new DirectoryEditorFileManager(
1958
- file,
1959
- __privateGet(this, _signalUpdate2)
1960
- );
1961
- await manager.reload();
1962
- return manager;
1963
- })
1964
- );
1965
- __privateGet(this, _files).length = 0;
1966
- __privateGet(this, _files).push(...fileManagers);
1967
- this.setSelectedFile(selectedPath);
1968
- __privateGet(this, _signalUpdate2).call(this);
1969
- }
1970
- subscribe(listener) {
1971
- __privateGet(this, _listeners).add(listener);
1972
- return () => {
1973
- __privateGet(this, _listeners).delete(listener);
1974
- };
1975
- }
1976
- }
1977
- _access2 = new WeakMap();
1978
- _listeners = new WeakMap();
1979
- _files = new WeakMap();
1980
- _selectedFile = new WeakMap();
1981
- _signalUpdate2 = new WeakMap();
1982
- const DirectoryEditorContext = createContext(
1983
- void 0
1984
- );
1985
- function useDirectoryEditor() {
1986
- const value = useContext(DirectoryEditorContext);
1987
- const rerender = useRerender();
1988
- useEffect(() => value == null ? void 0 : value.subscribe(rerender), [value, rerender]);
1989
- if (!value) {
1990
- throw new Error("must be used within a DirectoryEditorProvider");
1991
- }
1992
- return value;
1993
- }
1994
- function DirectoryEditorProvider(props) {
1995
- const { directory } = props;
1996
- const [{ result, error }, { execute }] = useAsync$1(
1997
- async (dir) => {
1998
- const manager = new DirectoryEditorManager(dir);
1999
- await manager.reload();
2000
- const firstYaml = manager.files.find((file) => file.path.match(/\.ya?ml$/));
2001
- if (firstYaml) {
2002
- manager.setSelectedFile(firstYaml.path);
2003
- }
2004
- return manager;
2005
- }
2006
- );
2007
- useEffect(() => {
2008
- execute(directory);
2009
- }, [execute, directory]);
2010
- if (error) {
2011
- return /* @__PURE__ */ React.createElement(ErrorPanel, { error });
2012
- } else if (!result) {
2013
- return /* @__PURE__ */ React.createElement(Progress, null);
2014
- }
2015
- return /* @__PURE__ */ React.createElement(DirectoryEditorContext.Provider, { value: result }, props.children);
2016
- }
2017
-
2018
- const useStyles$d = makeStyles({
2019
- containerWrapper: {
2020
- position: "relative",
2021
- width: "100%",
2022
- height: "100%"
2023
- },
2024
- container: {
2025
- position: "absolute",
2026
- top: 0,
2027
- bottom: 0,
2028
- left: 0,
2029
- right: 0,
2030
- overflow: "auto"
2031
- }
2032
- });
2033
- class ErrorBoundary extends Component {
2034
- constructor() {
2035
- super(...arguments);
2036
- this.state = {
2037
- shouldRender: true
2038
- };
2039
- }
2040
- componentDidUpdate(prevProps) {
2041
- if (prevProps.invalidator !== this.props.invalidator) {
2042
- this.setState({ shouldRender: true });
2043
- }
2044
- }
2045
- componentDidCatch(error) {
2046
- this.props.setErrorText(error.message);
2047
- this.setState({ shouldRender: false });
2048
- }
2049
- render() {
2050
- return this.state.shouldRender ? this.props.children : null;
2051
- }
2052
- }
2053
- function isJsonObject(value) {
2054
- return typeof value === "object" && value !== null && !Array.isArray(value);
2055
- }
2056
- function TemplateEditorForm(props) {
2057
- const {
2058
- content,
2059
- contentIsSpec,
2060
- onDryRun,
2061
- setErrorText,
2062
- fieldExtensions = [],
2063
- layouts = []
2064
- } = props;
2065
- const classes = useStyles$d();
2066
- const apiHolder = useApiHolder();
2067
- const [steps, setSteps] = useState();
2068
- useDebounce(
2069
- () => {
2070
- try {
2071
- if (!content) {
2072
- setSteps(void 0);
2073
- return;
2074
- }
2075
- const parsed = yaml.parse(content);
2076
- if (!isJsonObject(parsed)) {
2077
- setSteps(void 0);
2078
- return;
2079
- }
2080
- let rootObj = parsed;
2081
- if (!contentIsSpec) {
2082
- const isTemplate = String(parsed.kind).toLocaleLowerCase("en-US") === "template";
2083
- if (!isTemplate) {
2084
- setSteps(void 0);
2085
- return;
2086
- }
2087
- rootObj = isJsonObject(parsed.spec) ? parsed.spec : {};
2088
- }
2089
- const { parameters } = rootObj;
2090
- if (!Array.isArray(parameters)) {
2091
- setErrorText("Template parameters must be an array");
2092
- setSteps(void 0);
2093
- return;
2094
- }
2095
- setErrorText();
2096
- setSteps(
2097
- parameters.flatMap(
2098
- (param) => isJsonObject(param) ? [
2099
- {
2100
- title: String(param.title),
2101
- schema: param
2102
- }
2103
- ] : []
2104
- )
2105
- );
2106
- } catch (e) {
2107
- setErrorText(e.message);
2108
- }
2109
- },
2110
- 250,
2111
- [contentIsSpec, content, apiHolder]
2112
- );
2113
- if (!steps) {
2114
- return null;
2115
- }
2116
- return /* @__PURE__ */ React.createElement("div", { className: classes.containerWrapper }, /* @__PURE__ */ React.createElement("div", { className: classes.container }, /* @__PURE__ */ React.createElement(ErrorBoundary, { invalidator: steps, setErrorText }, /* @__PURE__ */ React.createElement(
2117
- Stepper,
2118
- {
2119
- manifest: { steps, title: "Template Editor" },
2120
- extensions: fieldExtensions,
2121
- onCreate: async (data) => {
2122
- await (onDryRun == null ? void 0 : onDryRun(data));
2123
- },
2124
- layouts,
2125
- components: { createButtonText: onDryRun && "Try It" }
2126
- }
2127
- ))));
2128
- }
2129
- function TemplateEditorFormDirectoryEditorDryRun(props) {
2130
- const { setErrorText, fieldExtensions = [], layouts } = props;
2131
- const dryRun = useDryRun();
2132
- const directoryEditor = useDirectoryEditor();
2133
- const { selectedFile } = directoryEditor;
2134
- const handleDryRun = async (values) => {
2135
- if (!selectedFile) {
2136
- return;
2137
- }
2138
- try {
2139
- await dryRun.execute({
2140
- templateContent: selectedFile.content,
2141
- values,
2142
- files: directoryEditor.files
2143
- });
2144
- setErrorText();
2145
- } catch (e) {
2146
- setErrorText(String(e.cause || e));
2147
- throw e;
2148
- }
2149
- };
2150
- const content = selectedFile && selectedFile.path.match(/\.ya?ml$/) ? selectedFile.content : void 0;
2151
- return /* @__PURE__ */ React.createElement(
2152
- TemplateEditorForm,
2153
- {
2154
- onDryRun: handleDryRun,
2155
- fieldExtensions,
2156
- setErrorText,
2157
- content,
2158
- layouts
2159
- }
2160
- );
2161
- }
2162
- TemplateEditorForm.DirectoryEditorDryRun = TemplateEditorFormDirectoryEditorDryRun;
2163
-
2164
- const useStyles$c = makeStyles$1((theme) => ({
2165
- root: {
2166
- gridArea: "pageContent",
2167
- display: "grid",
2168
- gridTemplateAreas: `
2169
- "controls controls"
2170
- "fieldForm preview"
2171
- `,
2172
- gridTemplateRows: "auto 1fr",
2173
- gridTemplateColumns: "1fr 1fr"
2174
- },
2175
- controls: {
2176
- gridArea: "controls",
2177
- display: "flex",
2178
- flexFlow: "row nowrap",
2179
- alignItems: "center",
2180
- margin: theme.spacing(1)
2181
- },
2182
- fieldForm: {
2183
- gridArea: "fieldForm"
2184
- },
2185
- preview: {
2186
- gridArea: "preview"
2187
- }
2188
- }));
2189
- const CustomFieldExplorer = ({
2190
- customFieldExtensions = [],
2191
- onClose
2192
- }) => {
2193
- var _a, _b;
2194
- const classes = useStyles$c();
2195
- const fieldOptions = customFieldExtensions.filter((field) => !!field.schema);
2196
- const [selectedField, setSelectedField] = useState(fieldOptions[0]);
2197
- const [fieldFormState, setFieldFormState] = useState({});
2198
- const [refreshKey, setRefreshKey] = useState(Date.now());
2199
- const sampleFieldTemplate = useMemo(
2200
- () => {
2201
- var _a2, _b2;
2202
- return yaml.stringify({
2203
- parameters: [
2204
- {
2205
- title: `${selectedField.name} Example`,
2206
- properties: {
2207
- [selectedField.name]: {
2208
- type: (_b2 = (_a2 = selectedField.schema) == null ? void 0 : _a2.returnValue) == null ? void 0 : _b2.type,
2209
- "ui:field": selectedField.name,
2210
- "ui:options": fieldFormState
2211
- }
2212
- }
2213
- }
2214
- ]
2215
- });
2216
- },
2217
- [fieldFormState, selectedField]
2218
- );
2219
- const fieldComponents = useMemo(() => {
2220
- return Object.fromEntries(
2221
- customFieldExtensions.map(({ name, component }) => [name, component])
2222
- );
2223
- }, [customFieldExtensions]);
2224
- const handleSelectionChange = useCallback(
2225
- (selection) => {
2226
- setSelectedField(selection);
2227
- setFieldFormState({});
2228
- },
2229
- [setFieldFormState, setSelectedField]
2230
- );
2231
- const handleFieldConfigChange = useCallback(
2232
- (state) => {
2233
- setFieldFormState(state);
2234
- setRefreshKey(Date.now());
2235
- },
2236
- [setFieldFormState, setRefreshKey]
2237
- );
2238
- return /* @__PURE__ */ React.createElement("main", { className: classes.root }, /* @__PURE__ */ React.createElement("div", { className: classes.controls }, /* @__PURE__ */ React.createElement(FormControl$1, { variant: "outlined", size: "small", fullWidth: true }, /* @__PURE__ */ React.createElement(InputLabel$1, { id: "select-field-label" }, "Choose Custom Field Extension"), /* @__PURE__ */ React.createElement(
2239
- Select$1,
2240
- {
2241
- value: selectedField,
2242
- label: "Choose Custom Field Extension",
2243
- labelId: "select-field-label",
2244
- onChange: (e) => handleSelectionChange(e.target.value)
2245
- },
2246
- fieldOptions.map((option, idx) => /* @__PURE__ */ React.createElement(MenuItem$1, { key: idx, value: option }, option.name))
2247
- )), /* @__PURE__ */ React.createElement(IconButton$1, { size: "medium", onClick: onClose }, /* @__PURE__ */ React.createElement(CloseIcon, null))), /* @__PURE__ */ React.createElement("div", { className: classes.fieldForm }, /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(CardHeader, { title: "Field Options" }), /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(
2248
- Form,
2249
- {
2250
- showErrorList: false,
2251
- fields: { ...fieldComponents },
2252
- noHtml5Validate: true,
2253
- formData: fieldFormState,
2254
- formContext: { fieldFormState },
2255
- onSubmit: (e) => handleFieldConfigChange(e.formData),
2256
- validator,
2257
- schema: ((_a = selectedField.schema) == null ? void 0 : _a.uiOptions) || {}
2258
- },
2259
- /* @__PURE__ */ React.createElement(
2260
- Button,
2261
- {
2262
- variant: "contained",
2263
- color: "primary",
2264
- type: "submit",
2265
- disabled: !((_b = selectedField.schema) == null ? void 0 : _b.uiOptions)
2266
- },
2267
- "Apply"
2268
- )
2269
- )))), /* @__PURE__ */ React.createElement("div", { className: classes.preview }, /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(CardHeader, { title: "Example Template Spec" }), /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(
2270
- CodeMirror,
2271
- {
2272
- readOnly: true,
2273
- theme: "dark",
2274
- height: "100%",
2275
- extensions: [StreamLanguage.define(yaml$1)],
2276
- value: sampleFieldTemplate
2277
- }
2278
- ))), /* @__PURE__ */ React.createElement(
2279
- TemplateEditorForm,
2280
- {
2281
- key: refreshKey,
2282
- content: sampleFieldTemplate,
2283
- contentIsSpec: true,
2284
- fieldExtensions: customFieldExtensions,
2285
- setErrorText: () => null
2286
- }
2287
- )));
2288
- };
2289
-
2290
- const useStyles$b = makeStyles((theme) => ({
2291
- root: {
2292
- overflowY: "auto",
2293
- background: theme.palette.background.default
2294
- },
2295
- iconSuccess: {
2296
- minWidth: 0,
2297
- marginRight: theme.spacing(1),
2298
- color: theme.palette.status.ok
2299
- },
2300
- iconFailure: {
2301
- minWidth: 0,
2302
- marginRight: theme.spacing(1),
2303
- color: theme.palette.status.error
2304
- }
2305
- }));
2306
- function DryRunResultsList() {
2307
- const classes = useStyles$b();
2308
- const dryRun = useDryRun();
2309
- return /* @__PURE__ */ React.createElement(List$2, { className: classes.root, dense: true }, dryRun.results.map((result) => {
2310
- var _a;
2311
- const failed = result.log.some((l) => l.body.status === "failed");
2312
- return /* @__PURE__ */ React.createElement(
2313
- ListItem,
2314
- {
2315
- button: true,
2316
- key: result.id,
2317
- selected: ((_a = dryRun.selectedResult) == null ? void 0 : _a.id) === result.id,
2318
- onClick: () => dryRun.selectResult(result.id)
2319
- },
2320
- /* @__PURE__ */ React.createElement(
2321
- ListItemIcon,
2322
- {
2323
- className: failed ? classes.iconFailure : classes.iconSuccess
2324
- },
2325
- failed ? /* @__PURE__ */ React.createElement(CancelIcon, null) : /* @__PURE__ */ React.createElement(CheckIcon, null)
2326
- ),
2327
- /* @__PURE__ */ React.createElement(ListItemText, { primary: `Result ${result.id}` }),
2328
- /* @__PURE__ */ React.createElement(ListItemSecondaryAction, null, /* @__PURE__ */ React.createElement(
2329
- IconButton,
2330
- {
2331
- edge: "end",
2332
- "aria-label": "delete",
2333
- onClick: () => dryRun.deleteResult(result.id)
2334
- },
2335
- /* @__PURE__ */ React.createElement(DeleteIcon, null)
2336
- ))
2337
- );
2338
- }));
2339
- }
2340
-
2341
- const TaskErrors = ({ error }) => {
2342
- const id = useRef("");
2343
- useEffect(() => {
2344
- id.current = String(Math.random());
2345
- }, [error]);
2346
- return error ? /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(
2347
- DismissableBanner,
2348
- {
2349
- id: id.current,
2350
- variant: "warning",
2351
- message: error.message
2352
- }
2353
- )) : null;
2354
- };
2355
-
2356
- const useStyles$a = makeStyles$1({
2357
- svgIcon: {
2358
- display: "inline-block",
2359
- "& svg": {
2360
- display: "inline-block",
2361
- fontSize: "inherit",
2362
- verticalAlign: "baseline"
2363
- }
2364
- }
2365
- });
2366
- const IconLink = (props) => {
2367
- const { href, text, Icon, ...linkProps } = props;
2368
- const classes = useStyles$a();
2369
- return /* @__PURE__ */ React.createElement(Grid, { container: true, direction: "row", spacing: 1 }, /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { component: "div", className: classes.svgIcon }, Icon ? /* @__PURE__ */ React.createElement(Icon, null) : /* @__PURE__ */ React.createElement(LanguageIcon, null))), /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Link, { to: href, ...linkProps }, text || href)));
2370
- };
2371
-
2372
- const TaskPageLinks = ({ output }) => {
2373
- const { links = [] } = output;
2374
- const app = useApp();
2375
- const entityRoute = useRouteRef(entityRouteRef);
2376
- const iconResolver = (key) => {
2377
- var _a;
2378
- return key ? (_a = app.getSystemIcon(key)) != null ? _a : LanguageIcon : LanguageIcon;
2379
- };
2380
- return /* @__PURE__ */ React.createElement(Box, { px: 3, pb: 3 }, links.filter(({ url, entityRef }) => url || entityRef).map(({ url, entityRef, title, icon }) => {
2381
- if (entityRef) {
2382
- const entityName = parseEntityRef(entityRef, {
2383
- defaultKind: "<unknown>",
2384
- defaultNamespace: "<unknown>"
2385
- });
2386
- const target = entityRoute(entityName);
2387
- return { title, icon, url: target };
2388
- }
2389
- return { title, icon, url };
2390
- }).map(({ url, title, icon }, i) => /* @__PURE__ */ React.createElement(
2391
- IconLink,
2392
- {
2393
- key: `output-link-${i}`,
2394
- href: url,
2395
- text: title != null ? title : url,
2396
- Icon: iconResolver(icon),
2397
- target: "_blank"
2398
- }
2399
- )));
2400
- };
2401
-
2402
- const humanizeDuration = require("humanize-duration");
2403
- const useStyles$9 = makeStyles(
2404
- (theme) => createStyles({
2405
- root: {
2406
- width: "100%"
2407
- },
2408
- button: {
2409
- marginBottom: theme.spacing(2),
2410
- marginLeft: theme.spacing(2)
2411
- },
2412
- actionsContainer: {
2413
- marginBottom: theme.spacing(2)
2414
- },
2415
- resetContainer: {
2416
- padding: theme.spacing(3)
2417
- },
2418
- labelWrapper: {
2419
- display: "flex",
2420
- flex: 1,
2421
- flexDirection: "row",
2422
- justifyContent: "space-between"
2423
- },
2424
- stepWrapper: {
2425
- width: "100%"
2426
- }
2427
- })
2428
- );
2429
- const StepTimeTicker = ({ step }) => {
2430
- const [time, setTime] = useState("");
2431
- useInterval(() => {
2432
- if (!step.startedAt) {
2433
- setTime("");
2434
- return;
2435
- }
2436
- const end = step.endedAt ? DateTime.fromISO(step.endedAt) : DateTime.local();
2437
- const startedAt = DateTime.fromISO(step.startedAt);
2438
- const formatted = Interval.fromDateTimes(startedAt, end).toDuration().valueOf();
2439
- setTime(humanizeDuration(formatted, { round: true }));
2440
- }, 1e3);
2441
- return /* @__PURE__ */ React.createElement(Typography$1, { variant: "caption" }, time);
2442
- };
2443
- const useStepIconStyles = makeStyles(
2444
- (theme) => createStyles({
2445
- root: {
2446
- color: theme.palette.text.disabled,
2447
- display: "flex",
2448
- height: 22,
2449
- alignItems: "center"
2450
- },
2451
- completed: {
2452
- color: theme.palette.status.ok
2453
- },
2454
- error: {
2455
- color: theme.palette.status.error
2456
- }
2457
- })
2458
- );
2459
- function TaskStepIconComponent(props) {
2460
- const classes = useStepIconStyles();
2461
- const { active, completed, error } = props;
2462
- const getMiddle = () => {
2463
- if (active) {
2464
- return /* @__PURE__ */ React.createElement(CircularProgress, { size: "24px" });
2465
- }
2466
- if (completed) {
2467
- return /* @__PURE__ */ React.createElement(CheckIcon, null);
2468
- }
2469
- if (error) {
2470
- return /* @__PURE__ */ React.createElement(CancelIcon, null);
2471
- }
2472
- return /* @__PURE__ */ React.createElement(FiberManualRecordIcon, null);
2473
- };
2474
- return /* @__PURE__ */ React.createElement(
2475
- "div",
2476
- {
2477
- className: classNames(classes.root, {
2478
- [classes.completed]: completed,
2479
- [classes.error]: error
2480
- })
2481
- },
2482
- getMiddle()
2483
- );
2484
- }
2485
- const TaskStatusStepper = memo(
2486
- (props) => {
2487
- const { steps, currentStepId, onUserStepChange } = props;
2488
- const classes = useStyles$9(props);
2489
- return /* @__PURE__ */ React.createElement("div", { className: classes.root }, /* @__PURE__ */ React.createElement(
2490
- Stepper$1,
2491
- {
2492
- activeStep: steps.findIndex((s) => s.id === currentStepId),
2493
- orientation: "vertical",
2494
- nonLinear: true
2495
- },
2496
- steps.map((step, index) => {
2497
- const isCompleted = step.status === "completed";
2498
- const isFailed = step.status === "failed";
2499
- const isActive = step.status === "processing";
2500
- const isSkipped = step.status === "skipped";
2501
- return /* @__PURE__ */ React.createElement(Step, { key: String(index), expanded: true }, /* @__PURE__ */ React.createElement(StepButton, { onClick: () => onUserStepChange(step.id) }, /* @__PURE__ */ React.createElement(
2502
- StepLabel,
2503
- {
2504
- StepIconProps: {
2505
- completed: isCompleted,
2506
- error: isFailed,
2507
- active: isActive
2508
- },
2509
- StepIconComponent: TaskStepIconComponent,
2510
- className: classes.stepWrapper
2511
- },
2512
- /* @__PURE__ */ React.createElement("div", { className: classes.labelWrapper }, /* @__PURE__ */ React.createElement(Typography$1, { variant: "subtitle2" }, step.name), isSkipped ? /* @__PURE__ */ React.createElement(Typography$1, { variant: "caption" }, "Skipped") : /* @__PURE__ */ React.createElement(StepTimeTicker, { step }))
2513
- )));
2514
- })
2515
- ));
2516
- }
2517
- );
2518
- const hasLinks = ({ links = [] }) => links.length > 0;
2519
- const TaskPage = (props) => {
2520
- const { loadingText } = props;
2521
- const classes = useStyles$9();
2522
- const navigate = useNavigate();
2523
- const rootPath = useRouteRef(rootRouteRef$1);
2524
- const templateRoute = useRouteRef(selectedTemplateRouteRef);
2525
- const [userSelectedStepId, setUserSelectedStepId] = useState(void 0);
2526
- const [lastActiveStepId, setLastActiveStepId] = useState(
2527
- void 0
2528
- );
2529
- const { taskId } = useRouteRefParams(scaffolderTaskRouteRef);
2530
- const taskStream = useTaskEventStream(taskId);
2531
- const completed = taskStream.completed;
2532
- const steps = useMemo(
2533
- () => {
2534
- var _a, _b;
2535
- return (_b = (_a = taskStream.task) == null ? void 0 : _a.spec.steps.map((step) => {
2536
- var _a2;
2537
- return {
2538
- ...step,
2539
- ...(_a2 = taskStream == null ? void 0 : taskStream.steps) == null ? void 0 : _a2[step.id]
2540
- };
2541
- })) != null ? _b : [];
2542
- },
2543
- [taskStream]
2544
- );
2545
- useEffect(() => {
2546
- var _a;
2547
- const mostRecentFailedOrActiveStep = steps.find(
2548
- (step) => ["failed", "processing"].includes(step.status)
2549
- );
2550
- if (completed && !mostRecentFailedOrActiveStep) {
2551
- setLastActiveStepId((_a = steps[steps.length - 1]) == null ? void 0 : _a.id);
2552
- return;
2553
- }
2554
- setLastActiveStepId(mostRecentFailedOrActiveStep == null ? void 0 : mostRecentFailedOrActiveStep.id);
2555
- }, [steps, completed]);
2556
- const currentStepId = userSelectedStepId != null ? userSelectedStepId : lastActiveStepId;
2557
- const logAsString = useMemo(() => {
2558
- if (!currentStepId) {
2559
- return loadingText ? loadingText : "Loading...";
2560
- }
2561
- const log = taskStream.stepLogs[currentStepId];
2562
- if (!(log == null ? void 0 : log.length)) {
2563
- return "Waiting for logs...";
2564
- }
2565
- return log.join("\n");
2566
- }, [taskStream.stepLogs, currentStepId, loadingText]);
2567
- const taskNotFound = taskStream.completed === true && taskStream.loading === false && !taskStream.task;
2568
- const { output } = taskStream;
2569
- const handleStartOver = () => {
2570
- var _a, _b, _c;
2571
- if (!taskStream.task || !((_b = (_a = taskStream.task) == null ? void 0 : _a.spec.templateInfo) == null ? void 0 : _b.entityRef)) {
2572
- navigate(rootPath());
2573
- return;
2574
- }
2575
- const formData = taskStream.task.spec.parameters;
2576
- const { name, namespace } = parseEntityRef(
2577
- (_c = taskStream.task.spec.templateInfo) == null ? void 0 : _c.entityRef
2578
- );
2579
- navigate(
2580
- `${templateRoute({ templateName: name, namespace })}?${qs.stringify({
2581
- formData: JSON.stringify(formData)
2582
- })}`
2583
- );
2584
- };
2585
- return /* @__PURE__ */ React.createElement(Page, { themeId: "home" }, /* @__PURE__ */ React.createElement(
2586
- Header,
2587
- {
2588
- pageTitleOverride: `Task ${taskId}`,
2589
- title: "Task Activity",
2590
- subtitle: `Activity for task: ${taskId}`
2591
- }
2592
- ), /* @__PURE__ */ React.createElement(Content, null, taskNotFound ? /* @__PURE__ */ React.createElement(
2593
- ErrorPage,
2594
- {
2595
- status: "404",
2596
- statusMessage: "Task not found",
2597
- additionalInfo: "No task found with this ID"
2598
- }
2599
- ) : /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Grid$1, { container: true }, /* @__PURE__ */ React.createElement(Grid$1, { item: true, xs: 3 }, /* @__PURE__ */ React.createElement(Paper, null, /* @__PURE__ */ React.createElement(
2600
- TaskStatusStepper,
2601
- {
2602
- steps,
2603
- currentStepId,
2604
- onUserStepChange: setUserSelectedStepId
2605
- }
2606
- ), output && hasLinks(output) && /* @__PURE__ */ React.createElement(TaskPageLinks, { output }), /* @__PURE__ */ React.createElement(
2607
- Button,
2608
- {
2609
- className: classes.button,
2610
- onClick: handleStartOver,
2611
- disabled: !completed,
2612
- variant: "contained",
2613
- color: "primary"
2614
- },
2615
- "Start Over"
2616
- ))), /* @__PURE__ */ React.createElement(Grid$1, { item: true, xs: 9 }, !currentStepId && /* @__PURE__ */ React.createElement(Progress, null), /* @__PURE__ */ React.createElement("div", { style: { height: "80vh" } }, /* @__PURE__ */ React.createElement(TaskErrors, { error: taskStream.error }), /* @__PURE__ */ React.createElement(LogViewer, { text: logAsString })))))));
2617
- };
2618
-
2619
- const useStyles$8 = makeStyles({
2620
- root: {
2621
- whiteSpace: "nowrap",
2622
- overflowY: "auto"
2623
- }
2624
- });
2625
- function parseFileEntires(paths) {
2626
- const root = {
2627
- type: "directory",
2628
- name: "",
2629
- path: "",
2630
- children: []
2631
- };
2632
- for (const path of paths.slice().sort()) {
2633
- const parts = path.split("/");
2634
- let current = root;
2635
- for (let i = 0; i < parts.length; i++) {
2636
- const part = parts[i];
2637
- if (part === "") {
2638
- throw new Error(`Invalid path part: ''`);
2639
- }
2640
- const entryPath = parts.slice(0, i + 1).join("/");
2641
- const existing = current.children.find((child) => child.name === part);
2642
- if ((existing == null ? void 0 : existing.type) === "file") {
2643
- throw new Error(`Duplicate filename at '${entryPath}'`);
2644
- } else if (existing) {
2645
- current = existing;
2646
- } else {
2647
- if (i < parts.length - 1) {
2648
- const newEntry = {
2649
- type: "directory",
2650
- name: part,
2651
- path: entryPath,
2652
- children: []
2653
- };
2654
- const firstFileIndex = current.children.findIndex(
2655
- (child) => child.type === "file"
2656
- );
2657
- current.children.splice(firstFileIndex, 0, newEntry);
2658
- current = newEntry;
2659
- } else {
2660
- current.children.push({
2661
- type: "file",
2662
- name: part,
2663
- path: entryPath
2664
- });
2665
- }
2666
- }
2667
- }
2668
- }
2669
- return root.children;
2670
- }
2671
- function FileTreeItem({ entry }) {
2672
- if (entry.type === "file") {
2673
- return /* @__PURE__ */ React.createElement(TreeItem, { nodeId: entry.path, label: entry.name });
2674
- }
2675
- return /* @__PURE__ */ React.createElement(TreeItem, { nodeId: entry.path, label: entry.name }, entry.children.map((child) => /* @__PURE__ */ React.createElement(FileTreeItem, { key: child.path, entry: child })));
2676
- }
2677
- function FileBrowser(props) {
2678
- const classes = useStyles$8();
2679
- const fileTree = useMemo(
2680
- () => parseFileEntires(props.filePaths),
2681
- [props.filePaths]
2682
- );
2683
- return /* @__PURE__ */ React.createElement(
2684
- TreeView,
2685
- {
2686
- selected: props.selected,
2687
- className: classes.root,
2688
- defaultCollapseIcon: /* @__PURE__ */ React.createElement(ExpandMoreIcon, null),
2689
- defaultExpandIcon: /* @__PURE__ */ React.createElement(ChevronRightIcon, null),
2690
- onNodeSelect: (_e, nodeId) => {
2691
- if (props.onSelect && props.filePaths.includes(nodeId)) {
2692
- props.onSelect(nodeId);
2693
- }
2694
- }
2695
- },
2696
- fileTree.map((entry) => /* @__PURE__ */ React.createElement(FileTreeItem, { key: entry.path, entry }))
2697
- );
2698
- }
2699
-
2700
- const useStyles$7 = makeStyles((theme) => ({
2701
- root: {
2702
- display: "grid",
2703
- gridTemplateColumns: "280px auto 3fr",
2704
- gridTemplateRows: "1fr"
2705
- },
2706
- child: {
2707
- overflowY: "auto",
2708
- height: "100%",
2709
- minHeight: 0
2710
- },
2711
- firstChild: {
2712
- background: theme.palette.background.paper
2713
- }
2714
- }));
2715
- function DryRunResultsSplitView(props) {
2716
- const classes = useStyles$7();
2717
- const childArray = Children.toArray(props.children);
2718
- if (childArray.length !== 2) {
2719
- throw new Error("must have exactly 2 children");
2720
- }
2721
- return /* @__PURE__ */ React.createElement("div", { className: classes.root }, /* @__PURE__ */ React.createElement("div", { className: classNames(classes.child, classes.firstChild) }, childArray[0]), /* @__PURE__ */ React.createElement(Divider, { orientation: "horizontal" }), /* @__PURE__ */ React.createElement("div", { className: classes.child }, childArray[1]));
2722
- }
2723
-
2724
- const useStyles$6 = makeStyles({
2725
- root: {
2726
- display: "flex",
2727
- flexFlow: "column nowrap"
2728
- },
2729
- contentWrapper: {
2730
- flex: 1,
2731
- position: "relative"
2732
- },
2733
- content: {
2734
- position: "absolute",
2735
- top: 0,
2736
- left: 0,
2737
- right: 0,
2738
- bottom: 0,
2739
- display: "flex",
2740
- "& > *": {
2741
- flex: 1
2742
- }
2743
- },
2744
- codeMirror: {
2745
- height: "100%",
2746
- overflowY: "auto"
2747
- }
2748
- });
2749
- function FilesContent() {
2750
- const classes = useStyles$6();
2751
- const { selectedResult } = useDryRun();
2752
- const [selectedPath, setSelectedPath] = useState("");
2753
- const selectedFile = selectedResult == null ? void 0 : selectedResult.directoryContents.find(
2754
- (f) => f.path === selectedPath
2755
- );
2756
- useEffect(() => {
2757
- if (selectedResult) {
2758
- const [firstFile] = selectedResult.directoryContents;
2759
- if (firstFile) {
2760
- setSelectedPath(firstFile.path);
2761
- } else {
2762
- setSelectedPath("");
2763
- }
2764
- }
2765
- return void 0;
2766
- }, [selectedResult]);
2767
- if (!selectedResult) {
2768
- return null;
2769
- }
2770
- return /* @__PURE__ */ React.createElement(DryRunResultsSplitView, null, /* @__PURE__ */ React.createElement(
2771
- FileBrowser,
2772
- {
2773
- selected: selectedPath,
2774
- onSelect: setSelectedPath,
2775
- filePaths: selectedResult.directoryContents.map((file) => file.path)
2776
- }
2777
- ), /* @__PURE__ */ React.createElement(
2778
- CodeMirror,
2779
- {
2780
- className: classes.codeMirror,
2781
- theme: "dark",
2782
- height: "100%",
2783
- extensions: [StreamLanguage.define(yaml$1)],
2784
- readOnly: true,
2785
- value: (selectedFile == null ? void 0 : selectedFile.base64Content) ? atob(selectedFile.base64Content) : ""
2786
- }
2787
- ));
2788
- }
2789
- function LogContent() {
2790
- var _a, _b;
2791
- const { selectedResult } = useDryRun();
2792
- const [currentStepId, setUserSelectedStepId] = useState();
2793
- const steps = useMemo(() => {
2794
- var _a2;
2795
- if (!selectedResult) {
2796
- return [];
2797
- }
2798
- return (_a2 = selectedResult.steps.map((step) => {
2799
- var _a3, _b2;
2800
- const stepLog = selectedResult.log.filter(
2801
- (l) => l.body.stepId === step.id
2802
- );
2803
- return {
2804
- id: step.id,
2805
- name: step.name,
2806
- logString: stepLog.map((l) => l.body.message).join("\n"),
2807
- status: (_b2 = (_a3 = stepLog[stepLog.length - 1]) == null ? void 0 : _a3.body.status) != null ? _b2 : "completed"
2808
- };
2809
- })) != null ? _a2 : [];
2810
- }, [selectedResult]);
2811
- if (!selectedResult) {
2812
- return null;
2813
- }
2814
- const selectedStep = (_a = steps.find((s) => s.id === currentStepId)) != null ? _a : steps[0];
2815
- return /* @__PURE__ */ React.createElement(DryRunResultsSplitView, null, /* @__PURE__ */ React.createElement(
2816
- TaskStatusStepper,
2817
- {
2818
- steps,
2819
- currentStepId: selectedStep.id,
2820
- onUserStepChange: setUserSelectedStepId
2821
- }
2822
- ), /* @__PURE__ */ React.createElement(LogViewer, { text: (_b = selectedStep == null ? void 0 : selectedStep.logString) != null ? _b : "" }));
2823
- }
2824
- function OutputContent() {
2825
- var _a, _b;
2826
- const classes = useStyles$6();
2827
- const { selectedResult } = useDryRun();
2828
- if (!selectedResult) {
2829
- return null;
2830
- }
2831
- return /* @__PURE__ */ React.createElement(DryRunResultsSplitView, null, /* @__PURE__ */ React.createElement(Box$1, { pt: 2 }, ((_b = (_a = selectedResult.output) == null ? void 0 : _a.links) == null ? void 0 : _b.length) && /* @__PURE__ */ React.createElement(TaskPageLinks, { output: selectedResult.output })), /* @__PURE__ */ React.createElement(
2832
- CodeMirror,
2833
- {
2834
- className: classes.codeMirror,
2835
- theme: "dark",
2836
- height: "100%",
2837
- extensions: [StreamLanguage.define(yaml$1)],
2838
- readOnly: true,
2839
- value: JSON.stringify(selectedResult.output, null, 2)
2840
- }
2841
- ));
2842
- }
2843
- function DryRunResultsView() {
2844
- const classes = useStyles$6();
2845
- const [selectedTab, setSelectedTab] = useState(
2846
- "files"
2847
- );
2848
- return /* @__PURE__ */ React.createElement("div", { className: classes.root }, /* @__PURE__ */ React.createElement(Tabs, { value: selectedTab, onChange: (_, v) => setSelectedTab(v) }, /* @__PURE__ */ React.createElement(Tab, { value: "files", label: "Files" }), /* @__PURE__ */ React.createElement(Tab, { value: "log", label: "Log" }), /* @__PURE__ */ React.createElement(Tab, { value: "output", label: "Output" })), /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement("div", { className: classes.contentWrapper }, /* @__PURE__ */ React.createElement("div", { className: classes.content }, selectedTab === "files" && /* @__PURE__ */ React.createElement(FilesContent, null), selectedTab === "log" && /* @__PURE__ */ React.createElement(LogContent, null), selectedTab === "output" && /* @__PURE__ */ React.createElement(OutputContent, null))));
2849
- }
2850
-
2851
- const useStyles$5 = makeStyles((theme) => ({
2852
- header: {
2853
- height: 48,
2854
- minHeight: 0,
2855
- "&.Mui-expanded": {
2856
- height: 48,
2857
- minHeight: 0
2858
- }
2859
- },
2860
- content: {
2861
- display: "grid",
2862
- background: theme.palette.background.default,
2863
- gridTemplateColumns: "180px auto 1fr",
2864
- gridTemplateRows: "1fr",
2865
- padding: 0,
2866
- height: 400
2867
- }
2868
- }));
2869
- function DryRunResults() {
2870
- const classes = useStyles$5();
2871
- const dryRun = useDryRun();
2872
- const [expanded, setExpanded] = useState(false);
2873
- const [hidden, setHidden] = useState(true);
2874
- const resultsLength = dryRun.results.length;
2875
- const prevResultsLength = usePrevious(resultsLength);
2876
- useEffect(() => {
2877
- if (prevResultsLength === 0 && resultsLength === 1) {
2878
- setHidden(false);
2879
- setExpanded(true);
2880
- } else if (prevResultsLength === 1 && resultsLength === 0) {
2881
- setExpanded(false);
2882
- }
2883
- }, [prevResultsLength, resultsLength]);
2884
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
2885
- Accordion$1,
2886
- {
2887
- variant: "outlined",
2888
- expanded,
2889
- hidden: resultsLength === 0 && hidden,
2890
- onChange: (_, exp) => setExpanded(exp),
2891
- onTransitionEnd: () => resultsLength === 0 && setHidden(true)
2892
- },
2893
- /* @__PURE__ */ React.createElement(
2894
- AccordionSummary$1,
2895
- {
2896
- className: classes.header,
2897
- expandIcon: /* @__PURE__ */ React.createElement(ExpandMoreIcon$1, null)
2898
- },
2899
- /* @__PURE__ */ React.createElement(Typography$1, null, "Dry-run results")
2900
- ),
2901
- /* @__PURE__ */ React.createElement(Divider, { orientation: "horizontal" }),
2902
- /* @__PURE__ */ React.createElement(AccordionDetails$1, { className: classes.content }, /* @__PURE__ */ React.createElement(DryRunResultsList, null), /* @__PURE__ */ React.createElement(Divider, { orientation: "horizontal" }), /* @__PURE__ */ React.createElement(DryRunResultsView, null))
2903
- ));
2904
- }
2905
-
2906
- const useStyles$4 = makeStyles$1((theme) => ({
2907
- button: {
2908
- padding: theme.spacing(1)
2909
- },
2910
- buttons: {
2911
- display: "flex",
2912
- flexFlow: "row nowrap",
2913
- alignItems: "center",
2914
- justifyContent: "flex-start"
2915
- },
2916
- buttonsGap: {
2917
- flex: "1 1 auto"
2918
- },
2919
- buttonsDivider: {
2920
- marginBottom: theme.spacing(1)
2921
- }
2922
- }));
2923
- function TemplateEditorBrowser(props) {
2924
- var _a, _b;
2925
- const classes = useStyles$4();
2926
- const directoryEditor = useDirectoryEditor();
2927
- const changedFiles = directoryEditor.files.filter((file) => file.dirty);
2928
- const handleClose = () => {
2929
- if (!props.onClose) {
2930
- return;
2931
- }
2932
- if (changedFiles.length > 0) {
2933
- const accepted = window.confirm(
2934
- "Are you sure? Unsaved changes will be lost"
2935
- );
2936
- if (!accepted) {
2937
- return;
2938
- }
2939
- }
2940
- props.onClose();
2941
- };
2942
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: classes.buttons }, /* @__PURE__ */ React.createElement(Tooltip, { title: "Save all files" }, /* @__PURE__ */ React.createElement(
2943
- IconButton$1,
2944
- {
2945
- className: classes.button,
2946
- disabled: directoryEditor.files.every((file) => !file.dirty),
2947
- onClick: () => directoryEditor.save()
2948
- },
2949
- /* @__PURE__ */ React.createElement(SaveIcon, null)
2950
- )), /* @__PURE__ */ React.createElement(Tooltip, { title: "Reload directory" }, /* @__PURE__ */ React.createElement(
2951
- IconButton$1,
2952
- {
2953
- className: classes.button,
2954
- onClick: () => directoryEditor.reload()
2955
- },
2956
- /* @__PURE__ */ React.createElement(RefreshIcon, null)
2957
- )), /* @__PURE__ */ React.createElement("div", { className: classes.buttonsGap }), /* @__PURE__ */ React.createElement(Tooltip, { title: "Close directory" }, /* @__PURE__ */ React.createElement(IconButton$1, { className: classes.button, onClick: handleClose }, /* @__PURE__ */ React.createElement(CloseIcon, null)))), /* @__PURE__ */ React.createElement(Divider$1, { className: classes.buttonsDivider }), /* @__PURE__ */ React.createElement(
2958
- FileBrowser,
2959
- {
2960
- selected: (_b = (_a = directoryEditor.selectedFile) == null ? void 0 : _a.path) != null ? _b : "",
2961
- onSelect: directoryEditor.setSelectedFile,
2962
- filePaths: directoryEditor.files.map((file) => file.path)
2963
- }
2964
- ));
2965
- }
2966
-
2967
- const useStyles$3 = makeStyles$1((theme) => ({
2968
- container: {
2969
- position: "relative",
2970
- width: "100%",
2971
- height: "100%"
2972
- },
2973
- codeMirror: {
2974
- position: "absolute",
2975
- top: 0,
2976
- bottom: 0,
2977
- left: 0,
2978
- right: 0
2979
- },
2980
- errorPanel: {
2981
- color: theme.palette.error.main,
2982
- lineHeight: 2,
2983
- margin: theme.spacing(0, 1)
2984
- },
2985
- floatingButtons: {
2986
- position: "absolute",
2987
- top: theme.spacing(1),
2988
- right: theme.spacing(3)
2989
- },
2990
- floatingButton: {
2991
- padding: theme.spacing(1)
2992
- }
2993
- }));
2994
- function TemplateEditorTextArea(props) {
2995
- const { errorText } = props;
2996
- const classes = useStyles$3();
2997
- const panelExtension = useMemo(() => {
2998
- if (!errorText) {
2999
- return showPanel.of(null);
3000
- }
3001
- const dom = document.createElement("div");
3002
- dom.classList.add(classes.errorPanel);
3003
- dom.textContent = errorText;
3004
- return showPanel.of(() => ({ dom, bottom: true }));
3005
- }, [classes, errorText]);
3006
- useKeyboardEvent(
3007
- (e) => e.key === "s" && (e.ctrlKey || e.metaKey),
3008
- (e) => {
3009
- e.preventDefault();
3010
- if (props.onSave) {
3011
- props.onSave();
3012
- }
3013
- }
3014
- );
3015
- return /* @__PURE__ */ React.createElement("div", { className: classes.container }, /* @__PURE__ */ React.createElement(
3016
- CodeMirror,
3017
- {
3018
- className: classes.codeMirror,
3019
- theme: "dark",
3020
- height: "100%",
3021
- extensions: [StreamLanguage.define(yaml$1), panelExtension],
3022
- value: props.content,
3023
- onChange: props.onUpdate
3024
- }
3025
- ), (props.onSave || props.onReload) && /* @__PURE__ */ React.createElement("div", { className: classes.floatingButtons }, /* @__PURE__ */ React.createElement(Paper, null, props.onSave && /* @__PURE__ */ React.createElement(Tooltip, { title: "Save file" }, /* @__PURE__ */ React.createElement(
3026
- IconButton$1,
3027
- {
3028
- className: classes.floatingButton,
3029
- onClick: () => {
3030
- var _a;
3031
- return (_a = props.onSave) == null ? void 0 : _a.call(props);
3032
- }
3033
- },
3034
- /* @__PURE__ */ React.createElement(SaveIcon, null)
3035
- )), props.onReload && /* @__PURE__ */ React.createElement(Tooltip, { title: "Reload file" }, /* @__PURE__ */ React.createElement(
3036
- IconButton$1,
3037
- {
3038
- className: classes.floatingButton,
3039
- onClick: () => {
3040
- var _a;
3041
- return (_a = props.onReload) == null ? void 0 : _a.call(props);
3042
- }
3043
- },
3044
- /* @__PURE__ */ React.createElement(RefreshIcon, null)
3045
- )))));
3046
- }
3047
- function TemplateEditorDirectoryEditorTextArea(props) {
3048
- var _a, _b;
3049
- const directoryEditor = useDirectoryEditor();
3050
- const actions = ((_a = directoryEditor.selectedFile) == null ? void 0 : _a.dirty) ? {
3051
- onSave: () => directoryEditor.save(),
3052
- onReload: () => directoryEditor.reload()
3053
- } : {
3054
- onReload: () => directoryEditor.reload()
3055
- };
3056
- return /* @__PURE__ */ React.createElement(
3057
- TemplateEditorTextArea,
3058
- {
3059
- errorText: props.errorText,
3060
- content: (_b = directoryEditor.selectedFile) == null ? void 0 : _b.content,
3061
- onUpdate: (content) => {
3062
- var _a2;
3063
- return (_a2 = directoryEditor.selectedFile) == null ? void 0 : _a2.updateContent(content);
3064
- },
3065
- ...actions
3066
- }
3067
- );
3068
- }
3069
- TemplateEditorTextArea.DirectoryEditor = TemplateEditorDirectoryEditorTextArea;
3070
-
3071
- const useStyles$2 = makeStyles$1({
3072
- // Reset and fix sizing to make sure scrolling behaves correctly
3073
- root: {
3074
- gridArea: "pageContent",
3075
- display: "grid",
3076
- gridTemplateAreas: `
3077
- "browser editor preview"
3078
- "results results results"
3079
- `,
3080
- gridTemplateColumns: "1fr 3fr 2fr",
3081
- gridTemplateRows: "1fr auto"
3082
- },
3083
- browser: {
3084
- gridArea: "browser",
3085
- overflow: "auto"
3086
- },
3087
- editor: {
3088
- gridArea: "editor",
3089
- overflow: "auto"
3090
- },
3091
- preview: {
3092
- gridArea: "preview",
3093
- overflow: "auto"
3094
- },
3095
- results: {
3096
- gridArea: "results"
3097
- }
3098
- });
3099
- const TemplateEditor = (props) => {
3100
- const classes = useStyles$2();
3101
- const [errorText, setErrorText] = useState();
3102
- return /* @__PURE__ */ React.createElement(DirectoryEditorProvider, { directory: props.directory }, /* @__PURE__ */ React.createElement(DryRunProvider, null, /* @__PURE__ */ React.createElement("main", { className: classes.root }, /* @__PURE__ */ React.createElement("section", { className: classes.browser }, /* @__PURE__ */ React.createElement(TemplateEditorBrowser, { onClose: props.onClose })), /* @__PURE__ */ React.createElement("section", { className: classes.editor }, /* @__PURE__ */ React.createElement(TemplateEditorTextArea.DirectoryEditor, { errorText })), /* @__PURE__ */ React.createElement("section", { className: classes.preview }, /* @__PURE__ */ React.createElement(
3103
- TemplateEditorForm.DirectoryEditorDryRun,
3104
- {
3105
- setErrorText,
3106
- fieldExtensions: props.fieldExtensions,
3107
- layouts: props.layouts
3108
- }
3109
- )), /* @__PURE__ */ React.createElement("section", { className: classes.results }, /* @__PURE__ */ React.createElement(DryRunResults, null)))));
3110
- };
3111
-
3112
- const EXAMPLE_TEMPLATE_PARAMS_YAML = `# Edit the template parameters below to see how they will render in the scaffolder form UI
3113
- parameters:
3114
- - title: Fill in some steps
3115
- required:
3116
- - name
3117
- properties:
3118
- name:
3119
- title: Name
3120
- type: string
3121
- description: Unique name of the component
3122
- owner:
3123
- title: Owner
3124
- type: string
3125
- description: Owner of the component
3126
- ui:field: OwnerPicker
3127
- ui:options:
3128
- catalogFilter:
3129
- kind: Group
3130
- - title: Choose a location
3131
- required:
3132
- - repoUrl
3133
- properties:
3134
- repoUrl:
3135
- title: Repository Location
3136
- type: string
3137
- ui:field: RepoUrlPicker
3138
- ui:options:
3139
- allowedHosts:
3140
- - github.com
3141
- steps:
3142
- - id: fetch-base
3143
- name: Fetch Base
3144
- action: fetch:template
3145
- input:
3146
- url: ./template
3147
- values:
3148
- name: \${{parameters.name}}
3149
- `;
3150
- const useStyles$1 = makeStyles$1((theme) => ({
3151
- root: {
3152
- gridArea: "pageContent",
3153
- display: "grid",
3154
- gridTemplateAreas: `
3155
- "controls controls"
3156
- "textArea preview"
3157
- `,
3158
- gridTemplateRows: "auto 1fr",
3159
- gridTemplateColumns: "1fr 1fr"
3160
- },
3161
- controls: {
3162
- gridArea: "controls",
3163
- display: "flex",
3164
- flexFlow: "row nowrap",
3165
- alignItems: "center",
3166
- margin: theme.spacing(1)
3167
- },
3168
- textArea: {
3169
- gridArea: "textArea"
3170
- },
3171
- preview: {
3172
- gridArea: "preview"
3173
- }
3174
- }));
3175
- const TemplateFormPreviewer = ({
3176
- defaultPreviewTemplate = EXAMPLE_TEMPLATE_PARAMS_YAML,
3177
- customFieldExtensions = [],
3178
- onClose,
3179
- layouts = []
3180
- }) => {
3181
- const classes = useStyles$1();
3182
- const alertApi = useApi(alertApiRef);
3183
- const catalogApi = useApi(catalogApiRef);
3184
- const [selectedTemplate, setSelectedTemplate] = useState("");
3185
- const [errorText, setErrorText] = useState();
3186
- const [templateOptions, setTemplateOptions] = useState([]);
3187
- const [templateYaml, setTemplateYaml] = useState(defaultPreviewTemplate);
3188
- const { loading } = useAsync(
3189
- () => catalogApi.getEntities({
3190
- filter: { kind: "template" },
3191
- fields: [
3192
- "kind",
3193
- "metadata.namespace",
3194
- "metadata.name",
3195
- "metadata.title",
3196
- "spec.parameters",
3197
- "spec.steps",
3198
- "spec.output"
3199
- ]
3200
- }).then(
3201
- ({ items }) => setTemplateOptions(
3202
- items.map((template) => {
3203
- var _a;
3204
- return {
3205
- label: (_a = template.metadata.title) != null ? _a : humanizeEntityRef(template, { defaultKind: "template" }),
3206
- value: template
3207
- };
3208
- })
3209
- )
3210
- ).catch(
3211
- (e) => alertApi.post({
3212
- message: `Error loading exisiting templates: ${e.message}`,
3213
- severity: "error"
3214
- })
3215
- ),
3216
- [catalogApi]
3217
- );
3218
- const handleSelectChange = useCallback(
3219
- (selected) => {
3220
- setSelectedTemplate(selected);
3221
- setTemplateYaml(yaml.stringify(selected.spec));
3222
- },
3223
- [setTemplateYaml]
3224
- );
3225
- return /* @__PURE__ */ React.createElement(React.Fragment, null, loading && /* @__PURE__ */ React.createElement(LinearProgress, null), /* @__PURE__ */ React.createElement("main", { className: classes.root }, /* @__PURE__ */ React.createElement("div", { className: classes.controls }, /* @__PURE__ */ React.createElement(FormControl$1, { variant: "outlined", size: "small", fullWidth: true }, /* @__PURE__ */ React.createElement(InputLabel$1, { id: "select-template-label" }, "Load Existing Template"), /* @__PURE__ */ React.createElement(
3226
- Select$1,
3227
- {
3228
- value: selectedTemplate,
3229
- label: "Load Existing Template",
3230
- labelId: "select-template-label",
3231
- onChange: (e) => handleSelectChange(e.target.value)
3232
- },
3233
- templateOptions.map((option, idx) => /* @__PURE__ */ React.createElement(MenuItem$1, { key: idx, value: option.value }, option.label))
3234
- )), /* @__PURE__ */ React.createElement(IconButton$1, { size: "medium", onClick: onClose }, /* @__PURE__ */ React.createElement(CloseIcon, null))), /* @__PURE__ */ React.createElement("div", { className: classes.textArea }, /* @__PURE__ */ React.createElement(
3235
- TemplateEditorTextArea,
3236
- {
3237
- content: templateYaml,
3238
- onUpdate: setTemplateYaml,
3239
- errorText
3240
- }
3241
- )), /* @__PURE__ */ React.createElement("div", { className: classes.preview }, /* @__PURE__ */ React.createElement(
3242
- TemplateEditorForm,
3243
- {
3244
- content: templateYaml,
3245
- contentIsSpec: true,
3246
- fieldExtensions: customFieldExtensions,
3247
- setErrorText,
3248
- layouts
3249
- }
3250
- ))));
3251
- };
3252
-
3253
- const useStyles = makeStyles((theme) => ({
3254
- introText: {
3255
- textAlign: "center",
3256
- marginTop: theme.spacing(2)
3257
- },
3258
- card: {
3259
- position: "relative",
3260
- maxWidth: 340,
3261
- marginTop: theme.spacing(4),
3262
- margin: theme.spacing(0, 2)
3263
- },
3264
- infoIcon: {
3265
- position: "absolute",
3266
- top: theme.spacing(1),
3267
- right: theme.spacing(1)
3268
- }
3269
- }));
3270
- function TemplateEditorIntro(props) {
3271
- const classes = useStyles();
3272
- const supportsLoad = WebFileSystemAccess.isSupported();
3273
- const cardLoadLocal = /* @__PURE__ */ React.createElement(Card$1, { className: classes.card, elevation: 4 }, /* @__PURE__ */ React.createElement(
3274
- CardActionArea,
3275
- {
3276
- disabled: !supportsLoad,
3277
- onClick: () => {
3278
- var _a;
3279
- return (_a = props.onSelect) == null ? void 0 : _a.call(props, "local");
3280
- }
3281
- },
3282
- /* @__PURE__ */ React.createElement(CardContent$1, null, /* @__PURE__ */ React.createElement(
3283
- Typography$1,
3284
- {
3285
- variant: "h5",
3286
- gutterBottom: true,
3287
- color: supportsLoad ? void 0 : "textSecondary",
3288
- style: { display: "flex", flexFlow: "row nowrap" }
3289
- },
3290
- "Load Template Directory"
3291
- ), /* @__PURE__ */ React.createElement(
3292
- Typography$1,
3293
- {
3294
- variant: "body1",
3295
- color: supportsLoad ? void 0 : "textSecondary"
3296
- },
3297
- "Load a local template directory, allowing you to both edit and try executing your own template."
3298
- ))
3299
- ), !supportsLoad && /* @__PURE__ */ React.createElement("div", { className: classes.infoIcon }, /* @__PURE__ */ React.createElement(
3300
- Tooltip$1,
3301
- {
3302
- placement: "top",
3303
- title: "Only supported in some Chromium-based browsers"
3304
- },
3305
- /* @__PURE__ */ React.createElement(InfoOutlinedIcon, null)
3306
- )));
3307
- const cardFormEditor = /* @__PURE__ */ React.createElement(Card$1, { className: classes.card, elevation: 4 }, /* @__PURE__ */ React.createElement(CardActionArea, { onClick: () => {
3308
- var _a;
3309
- return (_a = props.onSelect) == null ? void 0 : _a.call(props, "form");
3310
- } }, /* @__PURE__ */ React.createElement(CardContent$1, null, /* @__PURE__ */ React.createElement(Typography$1, { variant: "h5", gutterBottom: true }, "Edit Template Form"), /* @__PURE__ */ React.createElement(Typography$1, { variant: "body1" }, "Preview and edit a template form, either using a sample template or by loading a template from the catalog."))));
3311
- const cardFieldExplorer = /* @__PURE__ */ React.createElement(Card$1, { className: classes.card, elevation: 4 }, /* @__PURE__ */ React.createElement(CardActionArea, { onClick: () => {
3312
- var _a;
3313
- return (_a = props.onSelect) == null ? void 0 : _a.call(props, "field-explorer");
3314
- } }, /* @__PURE__ */ React.createElement(CardContent$1, null, /* @__PURE__ */ React.createElement(Typography$1, { variant: "h5", gutterBottom: true }, "Custom Field Explorer"), /* @__PURE__ */ React.createElement(Typography$1, { variant: "body1" }, "View and play around with available installed custom field extensions."))));
3315
- return /* @__PURE__ */ React.createElement("div", { style: props.style }, /* @__PURE__ */ React.createElement(Typography$1, { variant: "h6", className: classes.introText }, "Get started by choosing one of the options below"), /* @__PURE__ */ React.createElement(
3316
- "div",
3317
- {
3318
- style: {
3319
- display: "flex",
3320
- flexFlow: "row wrap",
3321
- alignItems: "flex-start",
3322
- justifyContent: "center",
3323
- alignContent: "flex-start"
3324
- }
3325
- },
3326
- supportsLoad && cardLoadLocal,
3327
- cardFormEditor,
3328
- !supportsLoad && cardLoadLocal,
3329
- cardFieldExplorer
3330
- ));
3331
- }
3332
-
3333
- const scaffolderPlugin = createPlugin({
3334
- id: "scaffolder",
3335
- apis: [
3336
- createApiFactory({
3337
- api: scaffolderApiRef$1,
3338
- deps: {
3339
- discoveryApi: discoveryApiRef,
3340
- scmIntegrationsApi: scmIntegrationsApiRef,
3341
- fetchApi: fetchApiRef,
3342
- identityApi: identityApiRef
3343
- },
3344
- factory: ({ discoveryApi, scmIntegrationsApi, fetchApi, identityApi }) => new ScaffolderClient({
3345
- discoveryApi,
3346
- scmIntegrationsApi,
3347
- fetchApi,
3348
- identityApi
3349
- })
3350
- })
3351
- ],
3352
- routes: {
3353
- root: rootRouteRef$1,
3354
- selectedTemplate: selectedTemplateRouteRef,
3355
- ongoingTask: scaffolderTaskRouteRef
3356
- },
3357
- externalRoutes: {
3358
- registerComponent: registerComponentRouteRef,
3359
- viewTechDoc: viewTechDocRouteRef
3360
- }
3361
- });
3362
- const EntityPickerFieldExtension = scaffolderPlugin.provide(
3363
- createScaffolderFieldExtension$1({
3364
- component: EntityPicker,
3365
- name: "EntityPicker",
3366
- schema: EntityPickerSchema
3367
- })
3368
- );
3369
- const EntityNamePickerFieldExtension = scaffolderPlugin.provide(
3370
- createScaffolderFieldExtension$1({
3371
- component: EntityNamePicker,
3372
- name: "EntityNamePicker",
3373
- validation: entityNamePickerValidation,
3374
- schema: EntityNamePickerSchema
3375
- })
3376
- );
3377
- const RepoUrlPickerFieldExtension = scaffolderPlugin.provide(
3378
- createScaffolderFieldExtension$1({
3379
- component: RepoUrlPicker,
3380
- name: "RepoUrlPicker",
3381
- validation: repoPickerValidation,
3382
- schema: RepoUrlPickerSchema
3383
- })
3384
- );
3385
- const OwnerPickerFieldExtension = scaffolderPlugin.provide(
3386
- createScaffolderFieldExtension$1({
3387
- component: OwnerPicker,
3388
- name: "OwnerPicker",
3389
- schema: OwnerPickerSchema
3390
- })
3391
- );
3392
- const ScaffolderPage = scaffolderPlugin.provide(
3393
- createRoutableExtension({
3394
- name: "ScaffolderPage",
3395
- component: () => import('./Router-6fd61bff.esm.js').then((m) => m.Router),
3396
- mountPoint: rootRouteRef$1
3397
- })
3398
- );
3399
- const OwnedEntityPickerFieldExtension = scaffolderPlugin.provide(
3400
- createScaffolderFieldExtension$1({
3401
- component: OwnedEntityPicker,
3402
- name: "OwnedEntityPicker",
3403
- schema: OwnedEntityPickerSchema
3404
- })
3405
- );
3406
- const EntityTagsPickerFieldExtension = scaffolderPlugin.provide(
3407
- createScaffolderFieldExtension$1({
3408
- component: EntityTagsPicker,
3409
- name: "EntityTagsPicker",
3410
- schema: EntityTagsPickerSchema
3411
- })
3412
- );
3413
- scaffolderPlugin.provide(
3414
- createRoutableExtension({
3415
- name: "NextScaffolderPage",
3416
- component: () => import('./index-f404fb0b.esm.js').then((m) => m.Router),
3417
- mountPoint: nextRouteRef
3418
- })
3419
- );
3420
-
3421
- const icon = /* @__PURE__ */ React.createElement(CheckBoxOutlineBlankIcon, { fontSize: "small" });
3422
- const checkedIcon = /* @__PURE__ */ React.createElement(CheckBoxIcon, { fontSize: "small" });
3423
- const TemplateTypePicker = () => {
3424
- const alertApi = useApi(alertApiRef);
3425
- const { error, loading, availableTypes, selectedTypes, setSelectedTypes } = useEntityTypeFilter();
3426
- if (loading)
3427
- return /* @__PURE__ */ React.createElement(Progress, null);
3428
- if (!availableTypes)
3429
- return null;
3430
- if (error) {
3431
- alertApi.post({
3432
- message: `Failed to load entity types`,
3433
- severity: "error"
3434
- });
3435
- return null;
3436
- }
3437
- return /* @__PURE__ */ React.createElement(Box, { pb: 1, pt: 1 }, /* @__PURE__ */ React.createElement(Typography, { variant: "button" }, "Categories"), /* @__PURE__ */ React.createElement(
3438
- Autocomplete$1,
3439
- {
3440
- multiple: true,
3441
- "aria-label": "Categories",
3442
- options: availableTypes,
3443
- value: selectedTypes,
3444
- onChange: (_, value) => setSelectedTypes(value),
3445
- renderOption: (option, { selected }) => /* @__PURE__ */ React.createElement(
3446
- FormControlLabel,
3447
- {
3448
- control: /* @__PURE__ */ React.createElement(
3449
- Checkbox,
3450
- {
3451
- icon,
3452
- checkedIcon,
3453
- checked: selected
3454
- }
3455
- ),
3456
- label: capitalize(option)
3457
- }
3458
- ),
3459
- size: "small",
3460
- popupIcon: /* @__PURE__ */ React.createElement(ExpandMoreIcon, { "data-testid": "categories-picker-expand" }),
3461
- renderInput: (params) => /* @__PURE__ */ React.createElement(TextField, { ...params, variant: "outlined" })
3462
- }
3463
- ));
3464
- };
3465
-
3466
- const rootRouteRef = rootRouteRef$1;
3467
- const createScaffolderFieldExtension = createScaffolderFieldExtension$1;
3468
- const ScaffolderFieldExtensions = ScaffolderFieldExtensions$1;
3469
- const useTemplateSecrets = useTemplateSecrets$1;
3470
- const scaffolderApiRef = scaffolderApiRef$1;
3471
- const createScaffolderLayout = createScaffolderLayout$1;
3472
- const ScaffolderLayouts = ScaffolderLayouts$1;
3473
-
3474
- export { RepoUrlPickerFieldExtension as $, ActionsPage as A, OwnedEntityPicker as B, OwnedEntityPickerSchema as C, DirectoryEditorProvider as D, EntityPicker as E, OwnerListPicker as F, nextSelectedTemplateRouteRef as G, ContextMenu$1 as H, nextRouteRef as I, nextScaffolderTaskRouteRef as J, TemplateEditor as K, TemplateFormPreviewer as L, CustomFieldExplorer as M, nextEditRouteRef as N, OwnerPicker as O, nextActionsRouteRef as P, nextScaffolderListTaskRouteRef as Q, RepoUrlPicker as R, OngoingTask as S, TemplateTypePicker as T, ScaffolderClient as U, EntityPickerFieldExtension as V, WebFileSystemAccess as W, EntityNamePickerFieldExtension as X, EntityTagsPickerFieldExtension as Y, OwnerPickerFieldExtension as Z, OwnedEntityPickerFieldExtension as _, actionsRouteRef as a, ScaffolderPage as a0, scaffolderPlugin as a1, makeFieldSchemaFromZod as a2, EntityPickerFieldSchema as a3, OwnerPickerFieldSchema as a4, RepoUrlPickerFieldSchema as a5, OwnedEntityPickerFieldSchema as a6, EntityTagsPickerFieldSchema as a7, rootRouteRef as a8, createScaffolderFieldExtension as a9, ScaffolderFieldExtensions as aa, useTemplateSecrets as ab, scaffolderApiRef as ac, createScaffolderLayout as ad, ScaffolderLayouts as ae, scaffolderListTaskRouteRef as b, scaffolderTaskRouteRef as c, rootRouteRef$1 as d, editRouteRef as e, useDirectoryEditor as f, DryRunProvider as g, TemplateEditorBrowser as h, TemplateEditorTextArea as i, DryRunResults as j, TemplateEditorIntro as k, legacySelectedTemplateRouteRef as l, TaskPage as m, EntityPickerSchema as n, EntityNamePicker as o, entityNamePickerValidation as p, EntityNamePickerSchema as q, registerComponentRouteRef as r, selectedTemplateRouteRef as s, EntityTagsPicker as t, useDryRun as u, viewTechDocRouteRef as v, EntityTagsPickerSchema as w, repoPickerValidation as x, RepoUrlPickerSchema as y, OwnerPickerSchema as z };
3475
- //# sourceMappingURL=index-0b6cdf44.esm.js.map