@backstage/plugin-scaffolder 0.11.17 → 0.12.1-next.0

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.
@@ -1,4 +1,4 @@
1
- import { createApiRef, useApi, attachComponentData, createExternalRouteRef, createRouteRef, createPlugin, createApiFactory, discoveryApiRef, identityApiRef, createRoutableExtension, useRouteRef, alertApiRef } from '@backstage/core-plugin-api';
1
+ import { createApiRef, useApi, attachComponentData, createExternalRouteRef, createRouteRef, createPlugin, createApiFactory, discoveryApiRef, fetchApiRef, createRoutableExtension, useRouteRef, alertApiRef } from '@backstage/core-plugin-api';
2
2
  import { ResponseError } from '@backstage/errors';
3
3
  import qs from 'qs';
4
4
  import ObservableImpl from 'zen-observable';
@@ -6,16 +6,16 @@ import { catalogApiRef, formatEntityRefTitle, useOwnedEntities, useStarredEntity
6
6
  import { TextField, FormControl as FormControl$1, withStyles, makeStyles, IconButton, Tooltip, useTheme, Card, CardMedia, CardContent, Box, Typography, Chip, CardActions, Link, FormControlLabel, Checkbox } from '@material-ui/core';
7
7
  import FormControl from '@material-ui/core/FormControl';
8
8
  import Autocomplete from '@material-ui/lab/Autocomplete';
9
- import React, { useCallback, useEffect, useState } from 'react';
9
+ import React, { useCallback, useEffect, useState, useMemo } from 'react';
10
10
  import useAsync from 'react-use/lib/useAsync';
11
11
  import { KubernetesValidatorFunctions, makeValidator, RELATION_OWNED_BY, stringifyEntityRef } from '@backstage/catalog-model';
12
- import { useAsync as useAsync$1, useEffectOnce } from 'react-use';
12
+ import useEffectOnce from 'react-use/lib/useEffectOnce';
13
13
  import { Autocomplete as Autocomplete$1 } from '@material-ui/lab';
14
- import { Progress, Select, ItemCardHeader, Button, ContentHeader, WarningPanel, Link as Link$1, Content, ItemCardGrid } from '@backstage/core-components';
15
14
  import { scmIntegrationsApiRef, ScmIntegrationIcon } from '@backstage/integration-react';
16
15
  import FormHelperText from '@material-ui/core/FormHelperText';
17
16
  import Input from '@material-ui/core/Input';
18
17
  import InputLabel from '@material-ui/core/InputLabel';
18
+ import { Select, Progress, ItemCardHeader, Button, ContentHeader, WarningPanel, Link as Link$1, Content, ItemCardGrid } from '@backstage/core-components';
19
19
  import Star from '@material-ui/icons/Star';
20
20
  import StarBorder from '@material-ui/icons/StarBorder';
21
21
  import WarningIcon from '@material-ui/icons/Warning';
@@ -30,11 +30,11 @@ const scaffolderApiRef = createApiRef({
30
30
  });
31
31
  class ScaffolderClient {
32
32
  constructor(options) {
33
- var _a;
33
+ var _a, _b;
34
34
  this.discoveryApi = options.discoveryApi;
35
- this.identityApi = options.identityApi;
35
+ this.fetchApi = (_a = options.fetchApi) != null ? _a : { fetch };
36
36
  this.scmIntegrationsApi = options.scmIntegrationsApi;
37
- this.useLongPollingLogs = (_a = options.useLongPollingLogs) != null ? _a : false;
37
+ this.useLongPollingLogs = (_b = options.useLongPollingLogs) != null ? _b : false;
38
38
  }
39
39
  async getIntegrationsList(options) {
40
40
  return [
@@ -46,31 +46,28 @@ class ScaffolderClient {
46
46
  }
47
47
  async getTemplateParameterSchema(templateName) {
48
48
  const { namespace, kind, name } = templateName;
49
- const token = await this.identityApi.getIdToken();
50
49
  const baseUrl = await this.discoveryApi.getBaseUrl("scaffolder");
51
50
  const templatePath = [namespace, kind, name].map((s) => encodeURIComponent(s)).join("/");
52
51
  const url = `${baseUrl}/v2/templates/${templatePath}/parameter-schema`;
53
- const response = await fetch(url, {
54
- headers: {
55
- ...token && { Authorization: `Bearer ${token}` }
56
- }
57
- });
52
+ const response = await this.fetchApi.fetch(url);
58
53
  if (!response.ok) {
59
54
  throw await ResponseError.fromResponse(response);
60
55
  }
61
56
  const schema = await response.json();
62
57
  return schema;
63
58
  }
64
- async scaffold(templateName, values) {
65
- const token = await this.identityApi.getIdToken();
59
+ async scaffold(templateName, values, secrets = {}) {
66
60
  const url = `${await this.discoveryApi.getBaseUrl("scaffolder")}/v2/tasks`;
67
- const response = await fetch(url, {
61
+ const response = await this.fetchApi.fetch(url, {
68
62
  method: "POST",
69
63
  headers: {
70
- "Content-Type": "application/json",
71
- ...token && { Authorization: `Bearer ${token}` }
64
+ "Content-Type": "application/json"
72
65
  },
73
- body: JSON.stringify({ templateName, values: { ...values } })
66
+ body: JSON.stringify({
67
+ templateName,
68
+ values: { ...values },
69
+ secrets
70
+ })
74
71
  });
75
72
  if (response.status !== 201) {
76
73
  const status = `${response.status} ${response.statusText}`;
@@ -81,22 +78,19 @@ class ScaffolderClient {
81
78
  return id;
82
79
  }
83
80
  async getTask(taskId) {
84
- const token = await this.identityApi.getIdToken();
85
81
  const baseUrl = await this.discoveryApi.getBaseUrl("scaffolder");
86
82
  const url = `${baseUrl}/v2/tasks/${encodeURIComponent(taskId)}`;
87
- const response = await fetch(url, {
88
- headers: token ? { Authorization: `Bearer ${token}` } : {}
89
- });
83
+ const response = await this.fetchApi.fetch(url);
90
84
  if (!response.ok) {
91
85
  throw await ResponseError.fromResponse(response);
92
86
  }
93
87
  return await response.json();
94
88
  }
95
- streamLogs(opts) {
89
+ streamLogs(options) {
96
90
  if (this.useLongPollingLogs) {
97
- return this.streamLogsPolling(opts);
91
+ return this.streamLogsPolling(options);
98
92
  }
99
- return this.streamLogsEventStream(opts);
93
+ return this.streamLogsEventStream(options);
100
94
  }
101
95
  streamLogsEventStream({
102
96
  taskId,
@@ -147,7 +141,7 @@ class ScaffolderClient {
147
141
  this.discoveryApi.getBaseUrl("scaffolder").then(async (baseUrl) => {
148
142
  while (!subscriber.closed) {
149
143
  const url = `${baseUrl}/v2/tasks/${encodeURIComponent(taskId)}/events?${qs.stringify({ after })}`;
150
- const response = await fetch(url);
144
+ const response = await this.fetchApi.fetch(url);
151
145
  if (!response.ok) {
152
146
  await new Promise((resolve) => setTimeout(resolve, 1e3));
153
147
  continue;
@@ -167,10 +161,7 @@ class ScaffolderClient {
167
161
  }
168
162
  async listActions() {
169
163
  const baseUrl = await this.discoveryApi.getBaseUrl("scaffolder");
170
- const token = await this.identityApi.getIdToken();
171
- const response = await fetch(`${baseUrl}/v2/actions`, {
172
- headers: token ? { Authorization: `Bearer ${token}` } : {}
173
- });
164
+ const response = await this.fetchApi.fetch(`${baseUrl}/v2/actions`);
174
165
  if (!response.ok) {
175
166
  throw await ResponseError.fromResponse(response);
176
167
  }
@@ -277,7 +268,7 @@ const EntityTagsPicker = ({
277
268
  const [inputError, setInputError] = useState(false);
278
269
  const tagValidator = makeValidator().isValidTag;
279
270
  const kinds = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.kinds;
280
- const { loading, value: existingTags } = useAsync$1(async () => {
271
+ const { loading, value: existingTags } = useAsync(async () => {
281
272
  const tagsRequest = { fields: ["metadata.tags"] };
282
273
  if (kinds) {
283
274
  tagsRequest.filter = { kind: kinds };
@@ -351,221 +342,273 @@ const OwnerPicker = ({
351
342
  });
352
343
  };
353
344
 
354
- function splitFormData(url, allowedOwners) {
355
- let host = void 0;
356
- let owner = void 0;
357
- let repo = void 0;
358
- let organization = void 0;
359
- let workspace = void 0;
360
- let project = void 0;
361
- try {
362
- if (url) {
363
- const parsed = new URL(`https://${url}`);
364
- host = parsed.host;
365
- owner = parsed.searchParams.get("owner") || (allowedOwners == null ? void 0 : allowedOwners[0]);
366
- repo = parsed.searchParams.get("repo") || void 0;
367
- organization = parsed.searchParams.get("organization") || void 0;
368
- workspace = parsed.searchParams.get("workspace") || void 0;
369
- project = parsed.searchParams.get("project") || void 0;
370
- }
371
- } catch {
372
- }
373
- return { host, owner, repo, organization, workspace, project };
374
- }
375
- function serializeFormData(data) {
376
- if (!data.host) {
377
- return void 0;
378
- }
379
- const params = new URLSearchParams();
380
- if (data.owner) {
381
- params.set("owner", data.owner);
382
- }
383
- if (data.repo) {
384
- params.set("repo", data.repo);
385
- }
386
- if (data.organization) {
387
- params.set("organization", data.organization);
388
- }
389
- if (data.workspace) {
390
- params.set("workspace", data.workspace);
391
- }
392
- if (data.project) {
393
- params.set("project", data.project);
394
- }
395
- return `${data.host}?${params.toString()}`;
396
- }
397
- const RepoUrlPicker = ({
398
- onChange,
399
- uiSchema,
400
- rawErrors,
401
- formData
402
- }) => {
403
- var _a, _b, _c, _d, _e;
404
- const scaffolderApi = useApi(scaffolderApiRef);
405
- const integrationApi = useApi(scmIntegrationsApiRef);
406
- const allowedHosts = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.allowedHosts;
407
- const allowedOwners = (_b = uiSchema["ui:options"]) == null ? void 0 : _b.allowedOwners;
408
- const { value: integrations, loading } = useAsync(async () => {
409
- return await scaffolderApi.getIntegrationsList({ allowedHosts });
410
- });
411
- const { host, owner, repo, organization, workspace, project } = splitFormData(formData, allowedOwners);
412
- const updateHost = useCallback((value) => {
413
- onChange(serializeFormData({
414
- host: value,
415
- owner,
416
- repo,
417
- organization,
418
- workspace,
419
- project
420
- }));
421
- }, [onChange, owner, repo, organization, workspace, project]);
422
- const updateOwnerSelect = useCallback((value) => onChange(serializeFormData({
423
- host,
424
- owner: value,
425
- repo,
426
- organization,
427
- workspace,
428
- project
429
- })), [onChange, host, repo, organization, workspace, project]);
430
- const updateOwner = useCallback((evt) => onChange(serializeFormData({
431
- host,
432
- owner: evt.target.value,
433
- repo,
434
- organization,
435
- workspace,
436
- project
437
- })), [onChange, host, repo, organization, workspace, project]);
438
- const updateRepo = useCallback((evt) => onChange(serializeFormData({
439
- host,
440
- owner,
441
- repo: evt.target.value,
442
- organization,
443
- workspace,
444
- project
445
- })), [onChange, host, owner, organization, workspace, project]);
446
- const updateOrganization = useCallback((evt) => onChange(serializeFormData({
447
- host,
448
- owner,
449
- repo,
450
- organization: evt.target.value,
451
- workspace,
452
- project
453
- })), [onChange, host, owner, repo, workspace, project]);
454
- const updateWorkspace = useCallback((evt) => onChange(serializeFormData({
455
- host,
456
- owner,
457
- repo,
458
- organization,
459
- workspace: evt.target.value,
460
- project
461
- })), [onChange, host, owner, repo, organization, project]);
462
- const updateProject = useCallback((evt) => onChange(serializeFormData({
463
- host,
464
- owner,
465
- repo,
466
- organization,
467
- workspace,
468
- project: evt.target.value
469
- })), [onChange, host, owner, repo, organization, workspace]);
470
- useEffect(() => {
471
- if (host === void 0 && (integrations == null ? void 0 : integrations.length)) {
472
- onChange(serializeFormData({
473
- host: integrations[0].host,
474
- owner,
475
- repo,
476
- organization,
477
- workspace,
478
- project
479
- }));
480
- }
481
- }, [
482
- onChange,
483
- integrations,
484
- host,
485
- owner,
486
- repo,
487
- organization,
488
- workspace,
489
- project
490
- ]);
491
- if (loading) {
492
- return /* @__PURE__ */ React.createElement(Progress, null);
493
- }
494
- const hostsOptions = integrations ? integrations.filter((i) => allowedHosts == null ? void 0 : allowedHosts.includes(i.host)).map((i) => ({ label: i.title, value: i.host })) : [{ label: "Loading...", value: "loading" }];
495
- const ownersOptions = allowedOwners ? allowedOwners.map((i) => ({ label: i, value: i })) : [{ label: "Loading...", value: "loading" }];
345
+ const GithubRepoPicker = (props) => {
346
+ const { allowedOwners = [], rawErrors, state, onChange } = props;
347
+ const ownerItems = allowedOwners ? allowedOwners.map((i) => ({ label: i, value: i })) : [{ label: "Loading...", value: "loading" }];
348
+ const { owner, repoName } = state;
496
349
  return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(FormControl, {
497
350
  margin: "normal",
498
351
  required: true,
499
- error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !host
500
- }, /* @__PURE__ */ React.createElement(Select, {
352
+ error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !owner
353
+ }, (allowedOwners == null ? void 0 : allowedOwners.length) ? /* @__PURE__ */ React.createElement(Select, {
501
354
  native: true,
502
- disabled: hostsOptions.length === 1,
503
- label: "Host",
504
- onChange: updateHost,
505
- selected: host,
506
- items: hostsOptions
507
- }), /* @__PURE__ */ React.createElement(FormHelperText, null, "The host where the repository will be created")), host === "dev.azure.com" && /* @__PURE__ */ React.createElement(FormControl, {
355
+ label: "Owner Available",
356
+ onChange: (s) => onChange({ owner: String(Array.isArray(s) ? s[0] : s) }),
357
+ disabled: allowedOwners.length === 1,
358
+ selected: owner,
359
+ items: ownerItems
360
+ }) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(InputLabel, {
361
+ htmlFor: "ownerInput"
362
+ }, "Owner"), /* @__PURE__ */ React.createElement(Input, {
363
+ id: "ownerInput",
364
+ onChange: (e) => onChange({ owner: e.target.value }),
365
+ value: owner
366
+ })), /* @__PURE__ */ React.createElement(FormHelperText, null, "The organization, user or project that this repo will belong to")), /* @__PURE__ */ React.createElement(FormControl, {
367
+ margin: "normal",
368
+ required: true,
369
+ error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !repoName
370
+ }, /* @__PURE__ */ React.createElement(InputLabel, {
371
+ htmlFor: "repoNameInput"
372
+ }, "Repository"), /* @__PURE__ */ React.createElement(Input, {
373
+ id: "repoNameInput",
374
+ onChange: (e) => onChange({ repoName: e.target.value }),
375
+ value: repoName
376
+ }), /* @__PURE__ */ React.createElement(FormHelperText, null, "The name of the repository")));
377
+ };
378
+
379
+ const GitlabRepoPicker = (props) => {
380
+ const { allowedOwners = [], rawErrors, state, onChange } = props;
381
+ const ownerItems = allowedOwners ? allowedOwners.map((i) => ({ label: i, value: i })) : [{ label: "Loading...", value: "loading" }];
382
+ const { owner, repoName } = state;
383
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(FormControl, {
384
+ margin: "normal",
385
+ required: true,
386
+ error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !owner
387
+ }, (allowedOwners == null ? void 0 : allowedOwners.length) ? /* @__PURE__ */ React.createElement(Select, {
388
+ native: true,
389
+ label: "Owner Available",
390
+ onChange: (selected) => onChange({
391
+ owner: String(Array.isArray(selected) ? selected[0] : selected)
392
+ }),
393
+ disabled: allowedOwners.length === 1,
394
+ selected: owner,
395
+ items: ownerItems
396
+ }) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(InputLabel, {
397
+ htmlFor: "ownerInput"
398
+ }, "Owner"), /* @__PURE__ */ React.createElement(Input, {
399
+ id: "ownerInput",
400
+ onChange: (e) => onChange({ owner: e.target.value }),
401
+ value: owner
402
+ })), /* @__PURE__ */ React.createElement(FormHelperText, null, "The organization, user or project that this repo will belong to")), /* @__PURE__ */ React.createElement(FormControl, {
403
+ margin: "normal",
404
+ required: true,
405
+ error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !repoName
406
+ }, /* @__PURE__ */ React.createElement(InputLabel, {
407
+ htmlFor: "repoNameInput"
408
+ }, "Repository"), /* @__PURE__ */ React.createElement(Input, {
409
+ id: "repoNameInput",
410
+ onChange: (e) => onChange({ repoName: e.target.value }),
411
+ value: repoName
412
+ }), /* @__PURE__ */ React.createElement(FormHelperText, null, "The name of the repository")));
413
+ };
414
+
415
+ const AzureRepoPicker = (props) => {
416
+ const { rawErrors, state, onChange } = props;
417
+ const { organization, repoName, owner } = state;
418
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(FormControl, {
508
419
  margin: "normal",
509
420
  required: true,
510
421
  error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !organization
511
422
  }, /* @__PURE__ */ React.createElement(InputLabel, {
512
- htmlFor: "repoInput"
423
+ htmlFor: "orgInput"
513
424
  }, "Organization"), /* @__PURE__ */ React.createElement(Input, {
514
- id: "repoInput",
515
- onChange: updateOrganization,
425
+ id: "orgInput",
426
+ onChange: (e) => onChange({ organization: e.target.value }),
516
427
  value: organization
517
- }), /* @__PURE__ */ React.createElement(FormHelperText, null, "The name of the organization")), host && ((_c = integrationApi.byHost(host)) == null ? void 0 : _c.type) === "bitbucket" && /* @__PURE__ */ React.createElement(React.Fragment, null, host === "bitbucket.org" && /* @__PURE__ */ React.createElement(FormControl, {
428
+ }), /* @__PURE__ */ React.createElement(FormHelperText, null, "The organization that this repo will belong to")), /* @__PURE__ */ React.createElement(FormControl, {
429
+ margin: "normal",
430
+ required: true,
431
+ error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !owner
432
+ }, /* @__PURE__ */ React.createElement(InputLabel, {
433
+ htmlFor: "ownerInput"
434
+ }, "Owner"), /* @__PURE__ */ React.createElement(Input, {
435
+ id: "ownerInput",
436
+ onChange: (e) => onChange({ owner: e.target.value }),
437
+ value: owner
438
+ }), /* @__PURE__ */ React.createElement(FormHelperText, null, "The Owner that this repo will belong to")), /* @__PURE__ */ React.createElement(FormControl, {
439
+ margin: "normal",
440
+ required: true,
441
+ error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !repoName
442
+ }, /* @__PURE__ */ React.createElement(InputLabel, {
443
+ htmlFor: "repoNameInput"
444
+ }, "Repository"), /* @__PURE__ */ React.createElement(Input, {
445
+ id: "repoNameInput",
446
+ onChange: (e) => onChange({ repoName: e.target.value }),
447
+ value: repoName
448
+ }), /* @__PURE__ */ React.createElement(FormHelperText, null, "The name of the repository")));
449
+ };
450
+
451
+ const BitbucketRepoPicker = (props) => {
452
+ const { onChange, rawErrors, state } = props;
453
+ const { host, workspace, project, repoName } = state;
454
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, host === "bitbucket.org" && /* @__PURE__ */ React.createElement(FormControl, {
518
455
  margin: "normal",
519
456
  required: true,
520
457
  error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !workspace
521
458
  }, /* @__PURE__ */ React.createElement(InputLabel, {
522
- htmlFor: "wokrspaceInput"
459
+ htmlFor: "workspaceInput"
523
460
  }, "Workspace"), /* @__PURE__ */ React.createElement(Input, {
524
- id: "wokrspaceInput",
525
- onChange: updateWorkspace,
461
+ id: "workspaceInput",
462
+ onChange: (e) => onChange({ workspace: e.target.value }),
526
463
  value: workspace
527
- }), /* @__PURE__ */ React.createElement(FormHelperText, null, "The workspace where the repository will be created")), /* @__PURE__ */ React.createElement(FormControl, {
464
+ }), /* @__PURE__ */ React.createElement(FormHelperText, null, "The Organization that this repo will belong to")), /* @__PURE__ */ React.createElement(FormControl, {
528
465
  margin: "normal",
529
466
  required: true,
530
467
  error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !project
531
468
  }, /* @__PURE__ */ React.createElement(InputLabel, {
532
- htmlFor: "wokrspaceInput"
469
+ htmlFor: "projectInput"
533
470
  }, "Project"), /* @__PURE__ */ React.createElement(Input, {
534
- id: "wokrspaceInput",
535
- onChange: updateProject,
471
+ id: "projectInput",
472
+ onChange: (e) => onChange({ project: e.target.value }),
536
473
  value: project
537
- }), /* @__PURE__ */ React.createElement(FormHelperText, null, "The project where the repository will be created"))), host && ((_d = integrationApi.byHost(host)) == null ? void 0 : _d.type) !== "bitbucket" && !allowedOwners && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(FormControl, {
474
+ }), /* @__PURE__ */ React.createElement(FormHelperText, null, "The Project that this repo will belong to")), /* @__PURE__ */ React.createElement(FormControl, {
538
475
  margin: "normal",
539
476
  required: true,
540
- error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !owner
477
+ error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !repoName
541
478
  }, /* @__PURE__ */ React.createElement(InputLabel, {
542
- htmlFor: "ownerInput"
543
- }, "Owner"), /* @__PURE__ */ React.createElement(Input, {
544
- id: "ownerInput",
545
- onChange: updateOwner,
546
- value: owner
547
- }), /* @__PURE__ */ React.createElement(FormHelperText, null, "The organization, user or project that this repo will belong to"))), host && ((_e = integrationApi.byHost(host)) == null ? void 0 : _e.type) !== "bitbucket" && allowedOwners && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(FormControl, {
479
+ htmlFor: "repoNameInput"
480
+ }, "Repository"), /* @__PURE__ */ React.createElement(Input, {
481
+ id: "repoNameInput",
482
+ onChange: (e) => onChange({ repoName: e.target.value }),
483
+ value: repoName
484
+ }), /* @__PURE__ */ React.createElement(FormHelperText, null, "The name of the repository")));
485
+ };
486
+
487
+ const RepoUrlPickerHost = (props) => {
488
+ const { host, hosts, onChange, rawErrors } = props;
489
+ const scaffolderApi = useApi(scaffolderApiRef);
490
+ const { value: integrations, loading } = useAsync(async () => {
491
+ return await scaffolderApi.getIntegrationsList({
492
+ allowedHosts: hosts != null ? hosts : []
493
+ });
494
+ });
495
+ useEffect(() => {
496
+ if (hosts && !host) {
497
+ onChange(hosts[0]);
498
+ }
499
+ }, [hosts, host, onChange]);
500
+ const hostsOptions = integrations ? integrations.filter((i) => hosts == null ? void 0 : hosts.includes(i.host)).map((i) => ({ label: i.title, value: i.host })) : [{ label: "Loading...", value: "loading" }];
501
+ if (loading) {
502
+ return /* @__PURE__ */ React.createElement(Progress, null);
503
+ }
504
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(FormControl, {
548
505
  margin: "normal",
549
506
  required: true,
550
- error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !owner
507
+ error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !host
551
508
  }, /* @__PURE__ */ React.createElement(Select, {
552
509
  native: true,
553
- label: "Owner Available",
554
- onChange: updateOwnerSelect,
555
- disabled: ownersOptions.length === 1,
556
- selected: owner,
557
- items: ownersOptions
558
- }), /* @__PURE__ */ React.createElement(FormHelperText, null, "The organization, user or project that this repo will belong to"))), /* @__PURE__ */ React.createElement(FormControl, {
559
- margin: "normal",
560
- required: true,
561
- error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !repo
562
- }, /* @__PURE__ */ React.createElement(InputLabel, {
563
- htmlFor: "repoInput"
564
- }, "Repository"), /* @__PURE__ */ React.createElement(Input, {
565
- id: "repoInput",
566
- onChange: updateRepo,
567
- value: repo
568
- }), /* @__PURE__ */ React.createElement(FormHelperText, null, "The name of the repository")));
510
+ disabled: (hosts == null ? void 0 : hosts.length) === 1,
511
+ label: "Host",
512
+ onChange: (s) => onChange(String(Array.isArray(s) ? s[0] : s)),
513
+ selected: host,
514
+ items: hostsOptions,
515
+ "data-testid": "host-select"
516
+ }), /* @__PURE__ */ React.createElement(FormHelperText, null, "The host where the repository will be created")));
517
+ };
518
+
519
+ function serializeRepoPickerUrl(data) {
520
+ if (!data.host) {
521
+ return void 0;
522
+ }
523
+ const params = new URLSearchParams();
524
+ if (data.owner) {
525
+ params.set("owner", data.owner);
526
+ }
527
+ if (data.repoName) {
528
+ params.set("repo", data.repoName);
529
+ }
530
+ if (data.organization) {
531
+ params.set("organization", data.organization);
532
+ }
533
+ if (data.workspace) {
534
+ params.set("workspace", data.workspace);
535
+ }
536
+ if (data.project) {
537
+ params.set("project", data.project);
538
+ }
539
+ return `${data.host}?${params.toString()}`;
540
+ }
541
+ function parseRepoPickerUrl(url) {
542
+ let host = void 0;
543
+ let owner = void 0;
544
+ let repoName = void 0;
545
+ let organization = void 0;
546
+ let workspace = void 0;
547
+ let project = void 0;
548
+ try {
549
+ if (url) {
550
+ const parsed = new URL(`https://${url}`);
551
+ host = parsed.host;
552
+ owner = parsed.searchParams.get("owner") || void 0;
553
+ repoName = parsed.searchParams.get("repo") || void 0;
554
+ organization = parsed.searchParams.get("organization") || void 0;
555
+ workspace = parsed.searchParams.get("workspace") || void 0;
556
+ project = parsed.searchParams.get("project") || void 0;
557
+ }
558
+ } catch {
559
+ }
560
+ return { host, owner, repoName, organization, workspace, project };
561
+ }
562
+
563
+ const RepoUrlPicker = (props) => {
564
+ var _a, _b;
565
+ const { uiSchema, onChange, rawErrors, formData } = props;
566
+ const [state, setState] = useState(parseRepoPickerUrl(formData));
567
+ const integrationApi = useApi(scmIntegrationsApiRef);
568
+ const allowedHosts = useMemo(() => {
569
+ var _a2, _b2;
570
+ return (_b2 = (_a2 = uiSchema == null ? void 0 : uiSchema["ui:options"]) == null ? void 0 : _a2.allowedHosts) != null ? _b2 : [];
571
+ }, [uiSchema]);
572
+ const allowedOwners = useMemo(() => {
573
+ var _a2, _b2;
574
+ return (_b2 = (_a2 = uiSchema == null ? void 0 : uiSchema["ui:options"]) == null ? void 0 : _a2.allowedOwners) != null ? _b2 : [];
575
+ }, [uiSchema]);
576
+ useEffect(() => {
577
+ onChange(serializeRepoPickerUrl(state));
578
+ }, [state, onChange]);
579
+ useEffect(() => {
580
+ if (allowedOwners.length === 1) {
581
+ setState((prevState) => ({ ...prevState, owner: allowedOwners[0] }));
582
+ }
583
+ }, [setState, allowedOwners]);
584
+ const updateLocalState = useCallback((newState) => {
585
+ setState((prevState) => ({ ...prevState, ...newState }));
586
+ }, [setState]);
587
+ const hostType = (_b = state.host && ((_a = integrationApi.byHost(state.host)) == null ? void 0 : _a.type)) != null ? _b : null;
588
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(RepoUrlPickerHost, {
589
+ host: state.host,
590
+ hosts: allowedHosts,
591
+ onChange: (host) => setState((prevState) => ({ ...prevState, host })),
592
+ rawErrors
593
+ }), hostType === "github" && /* @__PURE__ */ React.createElement(GithubRepoPicker, {
594
+ allowedOwners,
595
+ rawErrors,
596
+ state,
597
+ onChange: updateLocalState
598
+ }), hostType === "gitlab" && /* @__PURE__ */ React.createElement(GitlabRepoPicker, {
599
+ allowedOwners,
600
+ rawErrors,
601
+ state,
602
+ onChange: updateLocalState
603
+ }), hostType === "bitbucket" && /* @__PURE__ */ React.createElement(BitbucketRepoPicker, {
604
+ rawErrors,
605
+ state,
606
+ onChange: updateLocalState
607
+ }), hostType === "azure" && /* @__PURE__ */ React.createElement(AzureRepoPicker, {
608
+ rawErrors,
609
+ state,
610
+ onChange: updateLocalState
611
+ }));
569
612
  };
570
613
 
571
614
  const repoPickerValidation = (value, validation, context) => {
@@ -667,10 +710,14 @@ const scaffolderPlugin = createPlugin({
667
710
  api: scaffolderApiRef,
668
711
  deps: {
669
712
  discoveryApi: discoveryApiRef,
670
- identityApi: identityApiRef,
671
- scmIntegrationsApi: scmIntegrationsApiRef
713
+ scmIntegrationsApi: scmIntegrationsApiRef,
714
+ fetchApi: fetchApiRef
672
715
  },
673
- factory: ({ discoveryApi, identityApi, scmIntegrationsApi }) => new ScaffolderClient({ discoveryApi, identityApi, scmIntegrationsApi })
716
+ factory: ({ discoveryApi, scmIntegrationsApi, fetchApi }) => new ScaffolderClient({
717
+ discoveryApi,
718
+ scmIntegrationsApi,
719
+ fetchApi
720
+ })
674
721
  })
675
722
  ],
676
723
  routes: {
@@ -700,7 +747,7 @@ const OwnerPickerFieldExtension = scaffolderPlugin.provide(createScaffolderField
700
747
  }));
701
748
  const ScaffolderPage = scaffolderPlugin.provide(createRoutableExtension({
702
749
  name: "ScaffolderPage",
703
- component: () => import('./Router-552fb2ce.esm.js').then((m) => m.Router),
750
+ component: () => import('./Router-9da81048.esm.js').then((m) => m.Router),
704
751
  mountPoint: rootRouteRef
705
752
  }));
706
753
  const OwnedEntityPickerFieldExtension = scaffolderPlugin.provide(createScaffolderFieldExtension({
@@ -943,4 +990,4 @@ const TemplateTypePicker = () => {
943
990
  };
944
991
 
945
992
  export { EntityPicker as E, FIELD_EXTENSION_WRAPPER_KEY as F, OwnerPicker as O, RepoUrlPicker as R, ScaffolderClient as S, TemplateTypePicker as T, EntityNamePicker as a, EntityTagsPicker as b, OwnedEntityPicker as c, registerComponentRouteRef as d, entityNamePickerValidation as e, TemplateList as f, rootRouteRef as g, FIELD_EXTENSION_KEY as h, createScaffolderFieldExtension as i, ScaffolderFieldExtensions as j, EntityPickerFieldExtension as k, EntityNamePickerFieldExtension as l, EntityTagsPickerFieldExtension as m, OwnerPickerFieldExtension as n, OwnedEntityPickerFieldExtension as o, RepoUrlPickerFieldExtension as p, ScaffolderPage as q, repoPickerValidation as r, scaffolderApiRef as s, scaffolderPlugin as t, TextValuePicker as u, FavouriteTemplate as v };
946
- //# sourceMappingURL=index-768702c3.esm.js.map
993
+ //# sourceMappingURL=index-837d4328.esm.js.map