@backstage/plugin-scaffolder 0.11.9 → 0.11.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,104 @@
1
1
  # @backstage/plugin-scaffolder
2
2
 
3
+ ## 0.11.13
4
+
5
+ ### Patch Changes
6
+
7
+ - ed5bef529e: Add group filtering to the scaffolder page so that individuals can surface specific templates to end users ahead of others, or group templates together. This can be accomplished by passing in a `groups` prop to the `ScaffolderPage`
8
+
9
+ ```
10
+ <ScaffolderPage
11
+ groups={[
12
+ {
13
+ title: "Recommended",
14
+ filter: entity =>
15
+ entity?.metadata?.tags?.includes('recommended') ?? false,
16
+ },
17
+ ]}
18
+ />
19
+ ```
20
+
21
+ - Updated dependencies
22
+ - @backstage/integration@0.6.10
23
+ - @backstage/core-components@0.7.6
24
+ - @backstage/theme@0.2.14
25
+ - @backstage/core-plugin-api@0.2.2
26
+
27
+ ## 0.11.12
28
+
29
+ ### Patch Changes
30
+
31
+ - 2d7d165737: Bump `react-jsonschema-form`
32
+ - 9f21236a29: Fixed a missing `await` when throwing server side errors
33
+ - Updated dependencies
34
+ - @backstage/errors@0.1.5
35
+ - @backstage/core-plugin-api@0.2.1
36
+ - @backstage/core-components@0.7.5
37
+
38
+ ## 0.11.11
39
+
40
+ ### Patch Changes
41
+
42
+ - 8809b6c0dd: Update the json-schema dependency version.
43
+ - a125278b81: Refactor out the deprecated path and icon from RouteRefs
44
+ - Updated dependencies
45
+ - @backstage/catalog-client@0.5.2
46
+ - @backstage/catalog-model@0.9.7
47
+ - @backstage/plugin-catalog-react@0.6.4
48
+ - @backstage/core-components@0.7.4
49
+ - @backstage/core-plugin-api@0.2.0
50
+ - @backstage/integration-react@0.1.14
51
+
52
+ ## 0.11.10
53
+
54
+ ### Patch Changes
55
+
56
+ - fe5738fe1c: Lazy load `LazyLog` as it is rarely used.
57
+ - b45a34fb15: Adds a new endpoint for consuming logs from the Scaffolder that uses long polling instead of Server Sent Events.
58
+
59
+ This is useful if Backstage is accessed from an environment that doesn't support SSE correctly, which happens in combination with certain enterprise HTTP Proxy servers.
60
+
61
+ It is intended to switch the endpoint globally for the whole instance.
62
+ If you want to use it, you can provide a reconfigured API to the `scaffolderApiRef`:
63
+
64
+ ```tsx
65
+ // packages/app/src/apis.ts
66
+
67
+ // ...
68
+ import {
69
+ scaffolderApiRef,
70
+ ScaffolderClient,
71
+ } from '@backstage/plugin-scaffolder';
72
+
73
+ export const apis: AnyApiFactory[] = [
74
+ // ...
75
+
76
+ createApiFactory({
77
+ api: scaffolderApiRef,
78
+ deps: {
79
+ discoveryApi: discoveryApiRef,
80
+ identityApi: identityApiRef,
81
+ scmIntegrationsApi: scmIntegrationsApiRef,
82
+ },
83
+ factory: ({ discoveryApi, identityApi, scmIntegrationsApi }) =>
84
+ new ScaffolderClient({
85
+ discoveryApi,
86
+ identityApi,
87
+ scmIntegrationsApi,
88
+ // use long polling instead of an eventsource
89
+ useLongPollingLogs: true,
90
+ }),
91
+ }),
92
+ ];
93
+ ```
94
+
95
+ - Updated dependencies
96
+ - @backstage/core-components@0.7.3
97
+ - @backstage/theme@0.2.13
98
+ - @backstage/catalog-client@0.5.1
99
+ - @backstage/core-plugin-api@0.1.13
100
+ - @backstage/plugin-catalog-react@0.6.3
101
+
3
102
  ## 0.11.9
4
103
 
5
104
  ### Patch Changes
@@ -1,30 +1,30 @@
1
- import React, { useState, useCallback, useEffect, memo, useMemo } from 'react';
1
+ import React, { useState, useCallback, useEffect, memo, Suspense, useMemo } from 'react';
2
2
  import { useNavigate, Navigate, generatePath, useParams as useParams$1, useOutlet, Routes, Route } from 'react-router';
3
- import { Page, Header, Lifecycle, Content, ContentHeader, CreateButton, SupportButton, StructuredMetadataTable, InfoCard, Link, ErrorPage, Progress } from '@backstage/core-components';
3
+ import { Page, Header, Lifecycle, Content, ContentHeader, CreateButton, SupportButton, StructuredMetadataTable, InfoCard, Link, Progress, ErrorPage } from '@backstage/core-components';
4
4
  import { useRouteRef, useApi, errorApiRef, useApiHolder, useApp, useElementFilter } from '@backstage/core-plugin-api';
5
5
  import { EntityListProvider, EntitySearchBar, EntityKindPicker, UserListPicker, EntityTagPicker, entityRouteRef } from '@backstage/plugin-catalog-react';
6
6
  import { makeStyles, Stepper, Step, StepLabel, Typography, StepContent, Button, Paper, Box, LinearProgress, Grid, StepButton, CircularProgress, TableContainer, Table, TableHead, TableRow, TableCell, TableBody } from '@material-ui/core';
7
- import { E as EntityPicker, a as EntityNamePicker, e as entityNamePickerValidation, R as RepoUrlPicker, r as repoPickerValidation, O as OwnerPicker, b as registerComponentRouteRef, T as TemplateTypePicker, c as TemplateList, s as scaffolderApiRef, d as rootRouteRef, F as FIELD_EXTENSION_WRAPPER_KEY, f as FIELD_EXTENSION_KEY } from './index-12a93d58.esm.js';
7
+ import { E as EntityPicker, a as EntityNamePicker, e as entityNamePickerValidation, R as RepoUrlPicker, r as repoPickerValidation, O as OwnerPicker, b as registerComponentRouteRef, T as TemplateTypePicker, c as TemplateList, s as scaffolderApiRef, d as rootRouteRef, F as FIELD_EXTENSION_WRAPPER_KEY, f as FIELD_EXTENSION_KEY } from './index-385b3fc1.esm.js';
8
8
  import { useParams } from 'react-router-dom';
9
9
  import { useAsync, useInterval } from 'react-use';
10
10
  import { withTheme } from '@rjsf/core';
11
11
  import { Theme } from '@rjsf/material-ui';
12
- import { makeStyles as makeStyles$1, createStyles } from '@material-ui/core/styles';
13
- import Stepper$1 from '@material-ui/core/Stepper';
12
+ import Grid$1 from '@material-ui/core/Grid';
14
13
  import Step$1 from '@material-ui/core/Step';
15
14
  import StepLabel$1 from '@material-ui/core/StepLabel';
16
- import Grid$1 from '@material-ui/core/Grid';
15
+ import Stepper$1 from '@material-ui/core/Stepper';
16
+ import { makeStyles as makeStyles$1, createStyles } from '@material-ui/core/styles';
17
17
  import Typography$1 from '@material-ui/core/Typography';
18
- import { useImmerReducer } from 'use-immer';
19
- import LazyLog from 'react-lazylog/build/LazyLog';
20
- import { DateTime, Interval } from 'luxon';
21
- import Check from '@material-ui/icons/Check';
22
18
  import Cancel from '@material-ui/icons/Cancel';
19
+ import Check from '@material-ui/icons/Check';
23
20
  import FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord';
24
21
  import classNames from 'classnames';
22
+ import { DateTime, Interval } from 'luxon';
23
+ import { useImmerReducer } from 'use-immer';
25
24
  import { parseEntityName } from '@backstage/catalog-model';
26
25
  import LanguageIcon from '@material-ui/icons/Language';
27
26
  import '@backstage/errors';
27
+ import 'qs';
28
28
  import 'zen-observable';
29
29
  import '@material-ui/core/FormControl';
30
30
  import '@material-ui/lab/Autocomplete';
@@ -72,10 +72,18 @@ const useStyles$3 = makeStyles((theme) => ({
72
72
  }
73
73
  }));
74
74
  const ScaffolderPageContents = ({
75
- TemplateCardComponent
75
+ TemplateCardComponent,
76
+ groups
76
77
  }) => {
77
78
  const styles = useStyles$3();
78
79
  const registerComponentLink = useRouteRef(registerComponentRouteRef);
80
+ const otherTemplatesGroup = {
81
+ title: groups ? "Other Templates" : "Templates",
82
+ filter: (entity) => {
83
+ const filtered = (groups != null ? groups : []).map((group) => group.filter(entity));
84
+ return !filtered.some((result) => result === true);
85
+ }
86
+ };
79
87
  return /* @__PURE__ */ React.createElement(Page, {
80
88
  themeId: "home"
81
89
  }, /* @__PURE__ */ React.createElement(Header, {
@@ -97,14 +105,20 @@ const ScaffolderPageContents = ({
97
105
  }), /* @__PURE__ */ React.createElement(UserListPicker, {
98
106
  initialFilter: "all",
99
107
  availableFilters: ["all", "starred"]
100
- }), /* @__PURE__ */ React.createElement(TemplateTypePicker, null), /* @__PURE__ */ React.createElement(EntityTagPicker, null)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(TemplateList, {
101
- TemplateCardComponent
108
+ }), /* @__PURE__ */ React.createElement(TemplateTypePicker, null), /* @__PURE__ */ React.createElement(EntityTagPicker, null)), /* @__PURE__ */ React.createElement("div", null, groups && groups.map((group) => /* @__PURE__ */ React.createElement(TemplateList, {
109
+ TemplateCardComponent,
110
+ group
111
+ })), /* @__PURE__ */ React.createElement(TemplateList, {
112
+ TemplateCardComponent,
113
+ group: otherTemplatesGroup
102
114
  })))));
103
115
  };
104
116
  const ScaffolderPage = ({
105
- TemplateCardComponent
117
+ TemplateCardComponent,
118
+ groups
106
119
  }) => /* @__PURE__ */ React.createElement(EntityListProvider, null, /* @__PURE__ */ React.createElement(ScaffolderPageContents, {
107
- TemplateCardComponent
120
+ TemplateCardComponent,
121
+ groups
108
122
  }));
109
123
 
110
124
  function isObject$1(value) {
@@ -608,6 +622,7 @@ const TaskPageLinks = ({output}) => {
608
622
  })));
609
623
  };
610
624
 
625
+ const LazyLog = React.lazy(() => import('react-lazylog/build/LazyLog'));
611
626
  const humanizeDuration = require("humanize-duration");
612
627
  const useStyles$1 = makeStyles$1((theme) => createStyles({
613
628
  root: {
@@ -729,7 +744,9 @@ const TaskStatusStepper = memo(({
729
744
  })));
730
745
  });
731
746
  const TaskLogger = memo(({log}) => {
732
- return /* @__PURE__ */ React.createElement("div", {
747
+ return /* @__PURE__ */ React.createElement(Suspense, {
748
+ fallback: /* @__PURE__ */ React.createElement(Progress, null)
749
+ }, /* @__PURE__ */ React.createElement("div", {
733
750
  style: {height: "80vh"}
734
751
  }, /* @__PURE__ */ React.createElement(LazyLog, {
735
752
  text: log,
@@ -737,7 +754,7 @@ const TaskLogger = memo(({log}) => {
737
754
  follow: true,
738
755
  selectableLines: true,
739
756
  enableSearch: true
740
- }));
757
+ })));
741
758
  });
742
759
  const hasLinks = ({entityRef, remoteUrl, links = []}) => !!(entityRef || remoteUrl || links.length > 0);
743
760
  const TaskPage = () => {
@@ -918,7 +935,7 @@ const ActionsPage = () => {
918
935
  }), /* @__PURE__ */ React.createElement(Content, null, items));
919
936
  };
920
937
 
921
- const Router = ({TemplateCardComponent}) => {
938
+ const Router = ({TemplateCardComponent, groups}) => {
922
939
  const outlet = useOutlet();
923
940
  const customFieldExtensions = useElementFilter(outlet, (elements) => elements.selectByComponentData({
924
941
  key: FIELD_EXTENSION_WRAPPER_KEY
@@ -932,7 +949,8 @@ const Router = ({TemplateCardComponent}) => {
932
949
  return /* @__PURE__ */ React.createElement(Routes, null, /* @__PURE__ */ React.createElement(Route, {
933
950
  path: "/",
934
951
  element: /* @__PURE__ */ React.createElement(ScaffolderPage, {
935
- TemplateCardComponent
952
+ TemplateCardComponent,
953
+ groups
936
954
  })
937
955
  }), /* @__PURE__ */ React.createElement(Route, {
938
956
  path: "/templates/:templateName",
@@ -949,4 +967,4 @@ const Router = ({TemplateCardComponent}) => {
949
967
  };
950
968
 
951
969
  export { Router };
952
- //# sourceMappingURL=Router-4bca4743.esm.js.map
970
+ //# sourceMappingURL=Router-8b2aa17e.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Router-8b2aa17e.esm.js","sources":["../../src/extensions/default.ts","../../src/components/ScaffolderPage/ScaffolderPage.tsx","../../src/components/MultistepJsonForm/schema.ts","../../src/components/MultistepJsonForm/MultistepJsonForm.tsx","../../src/components/TemplatePage/TemplatePage.tsx","../../src/components/hooks/useEventStream.ts","../../src/components/TaskPage/IconLink.tsx","../../src/components/TaskPage/TaskPageLinks.tsx","../../src/components/TaskPage/TaskPage.tsx","../../src/components/ActionsPage/ActionsPage.tsx","../../src/components/Router.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { EntityPicker } from '../components/fields/EntityPicker';\nimport {\n EntityNamePicker,\n entityNamePickerValidation,\n} from '../components/fields/EntityNamePicker';\nimport { OwnerPicker } from '../components/fields/OwnerPicker';\nimport {\n repoPickerValidation,\n RepoUrlPicker,\n} from '../components/fields/RepoUrlPicker';\nimport { FieldExtensionOptions } from './types';\n\nexport const DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS: FieldExtensionOptions[] = [\n {\n component: EntityPicker,\n name: 'EntityPicker',\n },\n {\n component: EntityNamePicker,\n name: 'EntityNamePicker',\n validation: entityNamePickerValidation,\n },\n {\n component: RepoUrlPicker,\n name: 'RepoUrlPicker',\n validation: repoPickerValidation,\n },\n {\n component: OwnerPicker,\n name: 'OwnerPicker',\n },\n];\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Content,\n ContentHeader,\n CreateButton,\n Header,\n Lifecycle,\n Page,\n SupportButton,\n} from '@backstage/core-components';\nimport { TemplateEntityV1beta2, Entity } from '@backstage/catalog-model';\nimport { useRouteRef } from '@backstage/core-plugin-api';\nimport {\n EntityKindPicker,\n EntityListProvider,\n EntitySearchBar,\n EntityTagPicker,\n UserListPicker,\n} from '@backstage/plugin-catalog-react';\nimport { makeStyles } from '@material-ui/core';\nimport React, { ComponentType } from 'react';\nimport { registerComponentRouteRef } from '../../routes';\nimport { TemplateList } from '../TemplateList';\nimport { TemplateTypePicker } from '../TemplateTypePicker';\n\nconst useStyles = makeStyles(theme => ({\n contentWrapper: {\n display: 'grid',\n gridTemplateAreas: \"'filters' 'grid'\",\n gridTemplateColumns: '250px 1fr',\n gridColumnGap: theme.spacing(2),\n },\n}));\n\nexport type ScaffolderPageProps = {\n TemplateCardComponent?:\n | ComponentType<{ template: TemplateEntityV1beta2 }>\n | undefined;\n groups?: Array<{\n title?: string;\n titleComponent?: React.ReactNode;\n filter: (entity: Entity) => boolean;\n }>;\n};\n\nexport const ScaffolderPageContents = ({\n TemplateCardComponent,\n groups,\n}: ScaffolderPageProps) => {\n const styles = useStyles();\n const registerComponentLink = useRouteRef(registerComponentRouteRef);\n const otherTemplatesGroup = {\n title: groups ? 'Other Templates' : 'Templates',\n filter: (entity: Entity) => {\n const filtered = (groups ?? []).map(group => group.filter(entity));\n return !filtered.some(result => result === true);\n },\n };\n\n return (\n <Page themeId=\"home\">\n <Header\n pageTitleOverride=\"Create a New Component\"\n title={\n <>\n Create a New Component <Lifecycle shorthand />\n </>\n }\n subtitle=\"Create new software components using standard templates\"\n />\n <Content>\n <ContentHeader title=\"Available Templates\">\n <CreateButton\n title=\"Register Existing Component\"\n to={registerComponentLink && registerComponentLink()}\n />\n <SupportButton>\n Create new software components using standard templates. Different\n templates create different kinds of components (services, websites,\n documentation, ...).\n </SupportButton>\n </ContentHeader>\n\n <div className={styles.contentWrapper}>\n <div>\n <EntitySearchBar />\n <EntityKindPicker initialFilter=\"template\" hidden />\n <UserListPicker\n initialFilter=\"all\"\n availableFilters={['all', 'starred']}\n />\n <TemplateTypePicker />\n <EntityTagPicker />\n </div>\n <div>\n {groups &&\n groups.map(group => (\n <TemplateList\n TemplateCardComponent={TemplateCardComponent}\n group={group}\n />\n ))}\n <TemplateList\n TemplateCardComponent={TemplateCardComponent}\n group={otherTemplatesGroup}\n />\n </div>\n </div>\n </Content>\n </Page>\n );\n};\n\nexport const ScaffolderPage = ({\n TemplateCardComponent,\n groups,\n}: ScaffolderPageProps) => (\n <EntityListProvider>\n <ScaffolderPageContents\n TemplateCardComponent={TemplateCardComponent}\n groups={groups}\n />\n </EntityListProvider>\n);\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { JsonObject } from '@backstage/types';\nimport { FormProps } from '@rjsf/core';\n\nfunction isObject(value: unknown): value is JsonObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction extractUiSchema(schema: JsonObject, uiSchema: JsonObject) {\n if (!isObject(schema)) {\n return;\n }\n\n const { properties, items, anyOf, oneOf, allOf, dependencies } = schema;\n\n for (const propName in schema) {\n if (!schema.hasOwnProperty(propName)) {\n continue;\n }\n\n if (propName.startsWith('ui:')) {\n uiSchema[propName] = schema[propName];\n delete schema[propName];\n }\n }\n\n if (isObject(properties)) {\n for (const propName in properties) {\n if (!properties.hasOwnProperty(propName)) {\n continue;\n }\n\n const schemaNode = properties[propName];\n if (!isObject(schemaNode)) {\n continue;\n }\n const innerUiSchema = {};\n uiSchema[propName] = innerUiSchema;\n extractUiSchema(schemaNode, innerUiSchema);\n }\n }\n\n if (isObject(items)) {\n const innerUiSchema = {};\n uiSchema.items = innerUiSchema;\n extractUiSchema(items, innerUiSchema);\n }\n\n if (Array.isArray(anyOf)) {\n for (const schemaNode of anyOf) {\n if (!isObject(schemaNode)) {\n continue;\n }\n extractUiSchema(schemaNode, uiSchema);\n }\n }\n\n if (Array.isArray(oneOf)) {\n for (const schemaNode of oneOf) {\n if (!isObject(schemaNode)) {\n continue;\n }\n extractUiSchema(schemaNode, uiSchema);\n }\n }\n\n if (Array.isArray(allOf)) {\n for (const schemaNode of allOf) {\n if (!isObject(schemaNode)) {\n continue;\n }\n extractUiSchema(schemaNode, uiSchema);\n }\n }\n\n if (isObject(dependencies)) {\n for (const depName of Object.keys(dependencies)) {\n const schemaNode = dependencies[depName];\n if (!isObject(schemaNode)) {\n continue;\n }\n extractUiSchema(schemaNode, uiSchema);\n }\n }\n}\n\nexport function transformSchemaToProps(inputSchema: JsonObject): {\n schema: FormProps<any>['schema'];\n uiSchema: FormProps<any>['uiSchema'];\n} {\n inputSchema.type = inputSchema.type || 'object';\n const schema = JSON.parse(JSON.stringify(inputSchema));\n delete schema.title; // Rendered separately\n const uiSchema = {};\n extractUiSchema(schema, uiSchema);\n return { schema, uiSchema };\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { JsonObject } from '@backstage/types';\nimport {\n Box,\n Button,\n Paper,\n Step as StepUI,\n StepContent,\n StepLabel,\n Stepper,\n Typography,\n} from '@material-ui/core';\nimport { errorApiRef, useApi } from '@backstage/core-plugin-api';\nimport { FormProps, IChangeEvent, UiSchema, withTheme } from '@rjsf/core';\nimport { Theme as MuiTheme } from '@rjsf/material-ui';\nimport React, { useState } from 'react';\nimport { transformSchemaToProps } from './schema';\nimport { Content, StructuredMetadataTable } from '@backstage/core-components';\n\nconst Form = withTheme(MuiTheme);\ntype Step = {\n schema: JsonObject;\n title: string;\n} & Partial<Omit<FormProps<any>, 'schema'>>;\n\ntype Props = {\n /**\n * Steps for the form, each contains title and form schema\n */\n steps: Step[];\n formData: Record<string, any>;\n onChange: (e: IChangeEvent) => void;\n onReset: () => void;\n onFinish: () => Promise<void>;\n widgets?: FormProps<any>['widgets'];\n fields?: FormProps<any>['fields'];\n};\n\nexport function getUiSchemasFromSteps(steps: Step[]): UiSchema[] {\n const uiSchemas: Array<UiSchema> = [];\n steps.forEach(step => {\n const schemaProps = step.schema.properties as JsonObject;\n for (const key in schemaProps) {\n if (schemaProps.hasOwnProperty(key)) {\n const uiSchema = schemaProps[key] as UiSchema;\n uiSchema.name = key;\n uiSchemas.push(uiSchema);\n }\n }\n });\n return uiSchemas;\n}\n\nexport function getReviewData(formData: Record<string, any>, steps: Step[]) {\n const uiSchemas = getUiSchemasFromSteps(steps);\n const reviewData: Record<string, any> = {};\n for (const key in formData) {\n if (formData.hasOwnProperty(key)) {\n const uiSchema = uiSchemas.find(us => us.name === key);\n\n if (!uiSchema) {\n reviewData[key] = formData[key];\n continue;\n }\n\n if (uiSchema['ui:widget'] === 'password') {\n reviewData[key] = '******';\n continue;\n }\n\n if (!uiSchema['ui:backstage'] || !uiSchema['ui:backstage'].review) {\n reviewData[key] = formData[key];\n continue;\n }\n\n const review = uiSchema['ui:backstage'].review as JsonObject;\n if (!review.show) {\n continue;\n }\n\n if (review.mask) {\n reviewData[key] = review.mask;\n continue;\n }\n reviewData[key] = formData[key];\n }\n }\n\n return reviewData;\n}\n\nexport const MultistepJsonForm = ({\n steps,\n formData,\n onChange,\n onReset,\n onFinish,\n fields,\n widgets,\n}: Props) => {\n const [activeStep, setActiveStep] = useState(0);\n const [disableButtons, setDisableButtons] = useState(false);\n const errorApi = useApi(errorApiRef);\n\n const handleReset = () => {\n setActiveStep(0);\n onReset();\n };\n const handleNext = () => {\n setActiveStep(Math.min(activeStep + 1, steps.length));\n };\n const handleBack = () => setActiveStep(Math.max(activeStep - 1, 0));\n const handleCreate = async () => {\n setDisableButtons(true);\n try {\n await onFinish();\n } catch (err) {\n setDisableButtons(false);\n errorApi.post(err);\n }\n };\n\n return (\n <>\n <Stepper activeStep={activeStep} orientation=\"vertical\">\n {steps.map(({ title, schema, ...formProps }, index) => {\n return (\n <StepUI key={title}>\n <StepLabel\n aria-label={`Step ${index + 1} ${title}`}\n aria-disabled=\"false\"\n tabIndex={0}\n >\n <Typography variant=\"h6\" component=\"h3\">\n {title}\n </Typography>\n </StepLabel>\n <StepContent key={title}>\n <Form\n showErrorList={false}\n fields={fields}\n widgets={widgets}\n noHtml5Validate\n formData={formData}\n onChange={onChange}\n onSubmit={e => {\n if (e.errors.length === 0) handleNext();\n }}\n {...formProps}\n {...transformSchemaToProps(schema)}\n >\n <Button disabled={activeStep === 0} onClick={handleBack}>\n Back\n </Button>\n <Button variant=\"contained\" color=\"primary\" type=\"submit\">\n Next step\n </Button>\n </Form>\n </StepContent>\n </StepUI>\n );\n })}\n </Stepper>\n {activeStep === steps.length && (\n <Content>\n <Paper square elevation={0}>\n <Typography variant=\"h6\">Review and create</Typography>\n <StructuredMetadataTable\n dense\n metadata={getReviewData(formData, steps)}\n />\n <Box mb={4} />\n <Button onClick={handleBack} disabled={disableButtons}>\n Back\n </Button>\n <Button onClick={handleReset} disabled={disableButtons}>\n Reset\n </Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleCreate}\n disabled={disableButtons}\n >\n Create\n </Button>\n </Paper>\n </Content>\n )}\n </>\n );\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { JsonObject, JsonValue } from '@backstage/types';\nimport { LinearProgress } from '@material-ui/core';\nimport { FormValidation, IChangeEvent } from '@rjsf/core';\nimport React, { useCallback, useState } from 'react';\nimport { generatePath, Navigate, useNavigate } from 'react-router';\nimport { useParams } from 'react-router-dom';\nimport { useAsync } from 'react-use';\nimport { scaffolderApiRef } from '../../api';\nimport { CustomFieldValidator, FieldExtensionOptions } from '../../extensions';\nimport { rootRouteRef } from '../../routes';\nimport { MultistepJsonForm } from '../MultistepJsonForm';\n\nimport {\n Content,\n Header,\n InfoCard,\n Lifecycle,\n Page,\n} from '@backstage/core-components';\nimport {\n ApiHolder,\n errorApiRef,\n useApi,\n useApiHolder,\n useRouteRef,\n} from '@backstage/core-plugin-api';\n\nconst useTemplateParameterSchema = (templateName: string) => {\n const scaffolderApi = useApi(scaffolderApiRef);\n const { value, loading, error } = useAsync(\n () =>\n scaffolderApi.getTemplateParameterSchema({\n name: templateName,\n kind: 'template',\n namespace: 'default',\n }),\n [scaffolderApi, templateName],\n );\n return { schema: value, loading, error };\n};\n\nfunction isObject(obj: unknown): obj is JsonObject {\n return typeof obj === 'object' && obj !== null && !Array.isArray(obj);\n}\n\nexport const createValidator = (\n rootSchema: JsonObject,\n validators: Record<string, undefined | CustomFieldValidator<unknown>>,\n context: {\n apiHolder: ApiHolder;\n },\n) => {\n function validate(\n schema: JsonObject,\n formData: JsonObject,\n errors: FormValidation,\n ) {\n const schemaProps = schema.properties;\n if (!isObject(schemaProps)) {\n return;\n }\n\n for (const [key, propData] of Object.entries(formData)) {\n const propValidation = errors[key];\n\n if (isObject(propData)) {\n const propSchemaProps = schemaProps[key];\n if (isObject(propSchemaProps)) {\n validate(\n propSchemaProps,\n propData as JsonObject,\n propValidation as FormValidation,\n );\n }\n } else {\n const propSchema = schemaProps[key];\n const fieldName =\n isObject(propSchema) && (propSchema['ui:field'] as string);\n if (fieldName && typeof validators[fieldName] === 'function') {\n validators[fieldName]!(\n propData as JsonValue,\n propValidation,\n context,\n );\n }\n }\n }\n }\n\n return (formData: JsonObject, errors: FormValidation) => {\n validate(rootSchema, formData, errors);\n return errors;\n };\n};\n\nexport const TemplatePage = ({\n customFieldExtensions = [],\n}: {\n customFieldExtensions?: FieldExtensionOptions[];\n}) => {\n const apiHolder = useApiHolder();\n const errorApi = useApi(errorApiRef);\n const scaffolderApi = useApi(scaffolderApiRef);\n const { templateName } = useParams();\n const navigate = useNavigate();\n const rootLink = useRouteRef(rootRouteRef);\n const { schema, loading, error } = useTemplateParameterSchema(templateName);\n const [formState, setFormState] = useState({});\n const handleFormReset = () => setFormState({});\n const handleChange = useCallback(\n (e: IChangeEvent) => setFormState(e.formData),\n [setFormState],\n );\n\n const handleCreate = async () => {\n const id = await scaffolderApi.scaffold(templateName, formState);\n navigate(generatePath(`${rootLink()}/tasks/:taskId`, { taskId: id }));\n };\n\n if (error) {\n errorApi.post(new Error(`Failed to load template, ${error}`));\n return <Navigate to={rootLink()} />;\n }\n if (!loading && !schema) {\n errorApi.post(new Error('Template was not found.'));\n return <Navigate to={rootLink()} />;\n }\n\n const customFieldComponents = Object.fromEntries(\n customFieldExtensions.map(({ name, component }) => [name, component]),\n );\n\n const customFieldValidators = Object.fromEntries(\n customFieldExtensions.map(({ name, validation }) => [name, validation]),\n );\n\n return (\n <Page themeId=\"home\">\n <Header\n pageTitleOverride=\"Create a New Component\"\n title={\n <>\n Create a New Component <Lifecycle shorthand />\n </>\n }\n subtitle=\"Create new software components using standard templates\"\n />\n <Content>\n {loading && <LinearProgress data-testid=\"loading-progress\" />}\n {schema && (\n <InfoCard\n title={schema.title}\n noPadding\n titleTypographyProps={{ component: 'h2' }}\n >\n <MultistepJsonForm\n formData={formState}\n fields={customFieldComponents}\n onChange={handleChange}\n onReset={handleFormReset}\n onFinish={handleCreate}\n steps={schema.steps.map(step => {\n return {\n ...step,\n validate: createValidator(\n step.schema,\n customFieldValidators,\n { apiHolder },\n ),\n };\n })}\n />\n </InfoCard>\n )}\n </Content>\n </Page>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useImmerReducer } from 'use-immer';\nimport { useEffect } from 'react';\nimport { scaffolderApiRef, LogEvent } from '../../api';\nimport { ScaffolderTask, Status, TaskOutput } from '../../types';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { Subscription } from '@backstage/types';\n\ntype Step = {\n id: string;\n status: Status;\n endedAt?: string;\n startedAt?: string;\n};\n\nexport type TaskStream = {\n loading: boolean;\n error?: Error;\n stepLogs: { [stepId in string]: string[] };\n completed: boolean;\n task?: ScaffolderTask;\n steps: { [stepId in string]: Step };\n output?: TaskOutput;\n};\n\ntype ReducerLogEntry = {\n createdAt: string;\n body: {\n stepId?: string;\n status?: Status;\n message: string;\n output?: TaskOutput;\n };\n};\n\ntype ReducerAction =\n | { type: 'INIT'; data: ScaffolderTask }\n | { type: 'LOGS'; data: ReducerLogEntry[] }\n | { type: 'COMPLETED'; data: ReducerLogEntry }\n | { type: 'ERROR'; data: Error };\n\nfunction reducer(draft: TaskStream, action: ReducerAction) {\n switch (action.type) {\n case 'INIT': {\n draft.steps = action.data.spec.steps.reduce((current, next) => {\n current[next.id] = { status: 'open', id: next.id };\n return current;\n }, {} as { [stepId in string]: Step });\n draft.stepLogs = action.data.spec.steps.reduce((current, next) => {\n current[next.id] = [];\n return current;\n }, {} as { [stepId in string]: string[] });\n draft.loading = false;\n draft.error = undefined;\n draft.completed = false;\n draft.task = action.data;\n return;\n }\n\n case 'LOGS': {\n const entries = action.data;\n const logLines = [];\n\n for (const entry of entries) {\n const logLine = `${entry.createdAt} ${entry.body.message}`;\n logLines.push(logLine);\n\n if (!entry.body.stepId || !draft.steps?.[entry.body.stepId]) {\n continue;\n }\n\n const currentStepLog = draft.stepLogs?.[entry.body.stepId];\n const currentStep = draft.steps?.[entry.body.stepId];\n\n if (entry.body.status && entry.body.status !== currentStep.status) {\n currentStep.status = entry.body.status;\n\n if (currentStep.status === 'processing') {\n currentStep.startedAt = entry.createdAt;\n }\n\n if (\n ['cancelled', 'failed', 'completed'].includes(currentStep.status)\n ) {\n currentStep.endedAt = entry.createdAt;\n }\n }\n\n currentStepLog?.push(logLine);\n }\n\n return;\n }\n\n case 'COMPLETED': {\n draft.completed = true;\n draft.output = action.data.body.output;\n return;\n }\n\n case 'ERROR': {\n draft.error = action.data;\n draft.loading = false;\n draft.completed = true;\n return;\n }\n\n default:\n return;\n }\n}\n\nexport const useTaskEventStream = (taskId: string): TaskStream => {\n const scaffolderApi = useApi(scaffolderApiRef);\n const [state, dispatch] = useImmerReducer(reducer, {\n loading: true,\n completed: false,\n stepLogs: {} as { [stepId in string]: string[] },\n steps: {} as { [stepId in string]: Step },\n });\n\n useEffect(() => {\n let didCancel = false;\n let subscription: Subscription | undefined;\n let logPusher: NodeJS.Timeout | undefined;\n\n scaffolderApi.getTask(taskId).then(\n task => {\n if (didCancel) {\n return;\n }\n dispatch({ type: 'INIT', data: task });\n\n // TODO(blam): Use a normal fetch to fetch the current log for the event stream\n // and use that for an INIT_EVENTs dispatch event, and then\n // use the last event ID to subscribe using after option to\n // stream logs. Without this, if you have a lot of logs, it can look like the\n // task is being rebuilt on load as it progresses through the steps at a slower\n // rate whilst it builds the status from the event logs\n const observable = scaffolderApi.streamLogs({ taskId });\n\n const collectedLogEvents = new Array<LogEvent>();\n\n function emitLogs() {\n if (collectedLogEvents.length) {\n const logs = collectedLogEvents.splice(\n 0,\n collectedLogEvents.length,\n );\n dispatch({ type: 'LOGS', data: logs });\n }\n }\n\n logPusher = setInterval(emitLogs, 500);\n\n subscription = observable.subscribe({\n next: event => {\n switch (event.type) {\n case 'log':\n return collectedLogEvents.push(event);\n case 'completion':\n emitLogs();\n dispatch({ type: 'COMPLETED', data: event });\n return undefined;\n default:\n throw new Error(\n `Unhandled event type ${event.type} in observer`,\n );\n }\n },\n error: error => {\n emitLogs();\n dispatch({ type: 'ERROR', data: error });\n },\n });\n },\n error => {\n if (!didCancel) {\n dispatch({ type: 'ERROR', data: error });\n }\n },\n );\n\n return () => {\n didCancel = true;\n if (subscription) {\n subscription.unsubscribe();\n }\n if (logPusher) {\n clearInterval(logPusher);\n }\n };\n }, [scaffolderApi, dispatch, taskId]);\n\n return state;\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React from 'react';\nimport { Grid, LinkProps, makeStyles, Typography } from '@material-ui/core';\nimport LanguageIcon from '@material-ui/icons/Language';\n\nimport { IconComponent } from '@backstage/core-plugin-api';\nimport { Link } from '@backstage/core-components';\n\nconst useStyles = makeStyles({\n svgIcon: {\n display: 'inline-block',\n '& svg': {\n display: 'inline-block',\n fontSize: 'inherit',\n verticalAlign: 'baseline',\n },\n },\n});\n\nexport const IconLink = (\n props: {\n href: string;\n text?: string;\n Icon?: IconComponent;\n } & LinkProps,\n) => {\n const { href, text, Icon, ...linkProps } = props;\n\n const classes = useStyles();\n\n return (\n <Grid container direction=\"row\" spacing={1}>\n <Grid item>\n <Typography component=\"div\" className={classes.svgIcon}>\n {Icon ? <Icon /> : <LanguageIcon />}\n </Typography>\n </Grid>\n <Grid item>\n <Link to={href} {...linkProps}>\n {text || href}\n </Link>\n </Grid>\n </Grid>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { parseEntityName } from '@backstage/catalog-model';\nimport { entityRouteRef } from '@backstage/plugin-catalog-react';\nimport { Box } from '@material-ui/core';\nimport LanguageIcon from '@material-ui/icons/Language';\nimport React from 'react';\nimport { TaskOutput } from '../../types';\nimport { IconLink } from './IconLink';\nimport { IconComponent, useApp, useRouteRef } from '@backstage/core-plugin-api';\n\ntype TaskPageLinksProps = {\n output: TaskOutput;\n};\n\nexport const TaskPageLinks = ({ output }: TaskPageLinksProps) => {\n const { entityRef: entityRefOutput, remoteUrl } = output;\n let { links = [] } = output;\n const app = useApp();\n const entityRoute = useRouteRef(entityRouteRef);\n\n const iconResolver = (key?: string): IconComponent =>\n key ? app.getSystemIcon(key) ?? LanguageIcon : LanguageIcon;\n\n if (remoteUrl) {\n links = [{ url: remoteUrl, title: 'Repo' }, ...links];\n }\n\n if (entityRefOutput) {\n links = [\n {\n entityRef: entityRefOutput,\n title: 'Open in catalog',\n icon: 'catalog',\n },\n ...links,\n ];\n }\n\n return (\n <Box px={3} pb={3}>\n {links\n .filter(({ url, entityRef }) => url || entityRef)\n .map(({ url, entityRef, title, icon }) => {\n if (entityRef) {\n const entityName = parseEntityName(entityRef);\n const target = entityRoute(entityName);\n return { title, icon, url: target };\n }\n return { title, icon, url: url! };\n })\n .map(({ url, title, icon }, i) => (\n <IconLink\n key={`output-link-${i}`}\n href={url}\n text={title ?? url}\n Icon={iconResolver(icon)}\n target=\"_blank\"\n />\n ))}\n </Box>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Content,\n ErrorPage,\n Header,\n Lifecycle,\n Page,\n Progress,\n} from '@backstage/core-components';\nimport { BackstageTheme } from '@backstage/theme';\nimport {\n CircularProgress,\n Paper,\n StepButton,\n StepIconProps,\n} from '@material-ui/core';\nimport Grid from '@material-ui/core/Grid';\nimport Step from '@material-ui/core/Step';\nimport StepLabel from '@material-ui/core/StepLabel';\nimport Stepper from '@material-ui/core/Stepper';\nimport { createStyles, makeStyles, Theme } from '@material-ui/core/styles';\nimport Typography from '@material-ui/core/Typography';\nimport Cancel from '@material-ui/icons/Cancel';\nimport Check from '@material-ui/icons/Check';\nimport FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord';\nimport classNames from 'classnames';\nimport { DateTime, Interval } from 'luxon';\nimport React, { memo, Suspense, useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router';\nimport { useInterval } from 'react-use';\nimport { Status, TaskOutput } from '../../types';\nimport { useTaskEventStream } from '../hooks/useEventStream';\nimport { TaskPageLinks } from './TaskPageLinks';\n\nconst LazyLog = React.lazy(() => import('react-lazylog/build/LazyLog'));\n\n// typings are wrong for this library, so fallback to not parsing types.\nconst humanizeDuration = require('humanize-duration');\n\nconst useStyles = makeStyles((theme: Theme) =>\n createStyles({\n root: {\n width: '100%',\n },\n button: {\n marginTop: theme.spacing(1),\n marginRight: theme.spacing(1),\n },\n actionsContainer: {\n marginBottom: theme.spacing(2),\n },\n resetContainer: {\n padding: theme.spacing(3),\n },\n labelWrapper: {\n display: 'flex',\n flex: 1,\n flexDirection: 'row',\n justifyContent: 'space-between',\n },\n stepWrapper: {\n width: '100%',\n },\n }),\n);\n\ntype TaskStep = {\n id: string;\n name: string;\n status: Status;\n startedAt?: string;\n endedAt?: string;\n};\n\nconst StepTimeTicker = ({ step }: { step: TaskStep }) => {\n const [time, setTime] = useState('');\n\n useInterval(() => {\n if (!step.startedAt) {\n setTime('');\n return;\n }\n\n const end = step.endedAt\n ? DateTime.fromISO(step.endedAt)\n : DateTime.local();\n\n const startedAt = DateTime.fromISO(step.startedAt);\n const formatted = Interval.fromDateTimes(startedAt, end)\n .toDuration()\n .valueOf();\n\n setTime(humanizeDuration(formatted, { round: true }));\n }, 1000);\n\n return <Typography variant=\"caption\">{time}</Typography>;\n};\n\nconst useStepIconStyles = makeStyles((theme: BackstageTheme) =>\n createStyles({\n root: {\n color: theme.palette.text.disabled,\n display: 'flex',\n height: 22,\n alignItems: 'center',\n },\n completed: {\n color: theme.palette.status.ok,\n },\n error: {\n color: theme.palette.status.error,\n },\n }),\n);\n\nfunction TaskStepIconComponent(props: StepIconProps) {\n const classes = useStepIconStyles();\n const { active, completed, error } = props;\n\n const getMiddle = () => {\n if (active) {\n return <CircularProgress size=\"24px\" />;\n }\n if (completed) {\n return <Check />;\n }\n if (error) {\n return <Cancel />;\n }\n return <FiberManualRecordIcon />;\n };\n\n return (\n <div\n className={classNames(classes.root, {\n [classes.completed]: completed,\n [classes.error]: error,\n })}\n >\n {getMiddle()}\n </div>\n );\n}\n\nexport const TaskStatusStepper = memo(\n ({\n steps,\n currentStepId,\n onUserStepChange,\n }: {\n steps: TaskStep[];\n currentStepId: string | undefined;\n onUserStepChange: (id: string) => void;\n }) => {\n const classes = useStyles();\n\n return (\n <div className={classes.root}>\n <Stepper\n activeStep={steps.findIndex(s => s.id === currentStepId)}\n orientation=\"vertical\"\n nonLinear\n >\n {steps.map((step, index) => {\n const isCompleted = step.status === 'completed';\n const isFailed = step.status === 'failed';\n const isActive = step.status === 'processing';\n const isSkipped = step.status === 'skipped';\n\n return (\n <Step key={String(index)} expanded>\n <StepButton onClick={() => onUserStepChange(step.id)}>\n <StepLabel\n StepIconProps={{\n completed: isCompleted,\n error: isFailed,\n active: isActive,\n }}\n StepIconComponent={TaskStepIconComponent}\n className={classes.stepWrapper}\n >\n <div className={classes.labelWrapper}>\n <Typography variant=\"subtitle2\">{step.name}</Typography>\n {isSkipped ? (\n <Typography variant=\"caption\">Skipped</Typography>\n ) : (\n <StepTimeTicker step={step} />\n )}\n </div>\n </StepLabel>\n </StepButton>\n </Step>\n );\n })}\n </Stepper>\n </div>\n );\n },\n);\n\nconst TaskLogger = memo(({ log }: { log: string }) => {\n return (\n <Suspense fallback={<Progress />}>\n <div style={{ height: '80vh' }}>\n <LazyLog\n text={log}\n extraLines={1}\n follow\n selectableLines\n enableSearch\n />\n </div>\n </Suspense>\n );\n});\n\nconst hasLinks = ({ entityRef, remoteUrl, links = [] }: TaskOutput): boolean =>\n !!(entityRef || remoteUrl || links.length > 0);\n\nexport const TaskPage = () => {\n const [userSelectedStepId, setUserSelectedStepId] = useState<\n string | undefined\n >(undefined);\n const [lastActiveStepId, setLastActiveStepId] = useState<string | undefined>(\n undefined,\n );\n const { taskId } = useParams();\n const taskStream = useTaskEventStream(taskId);\n const completed = taskStream.completed;\n const steps = useMemo(\n () =>\n taskStream.task?.spec.steps.map(step => ({\n ...step,\n ...taskStream?.steps?.[step.id],\n })) ?? [],\n [taskStream],\n );\n\n useEffect(() => {\n const mostRecentFailedOrActiveStep = steps.find(step =>\n ['failed', 'processing'].includes(step.status),\n );\n if (completed && !mostRecentFailedOrActiveStep) {\n setLastActiveStepId(steps[steps.length - 1]?.id);\n return;\n }\n\n setLastActiveStepId(mostRecentFailedOrActiveStep?.id);\n }, [steps, completed]);\n\n const currentStepId = userSelectedStepId ?? lastActiveStepId;\n\n const logAsString = useMemo(() => {\n if (!currentStepId) {\n return 'Loading...';\n }\n const log = taskStream.stepLogs[currentStepId];\n\n if (!log?.length) {\n return 'Waiting for logs...';\n }\n return log.join('\\n');\n }, [taskStream.stepLogs, currentStepId]);\n\n const taskNotFound =\n taskStream.completed === true &&\n taskStream.loading === false &&\n !taskStream.task;\n\n const { output } = taskStream;\n\n return (\n <Page themeId=\"home\">\n <Header\n pageTitleOverride={`Task ${taskId}`}\n title={\n <>\n Task Activity <Lifecycle alpha shorthand />\n </>\n }\n subtitle={`Activity for task: ${taskId}`}\n />\n <Content>\n {taskNotFound ? (\n <ErrorPage\n status=\"404\"\n statusMessage=\"Task not found\"\n additionalInfo=\"No task found with this ID\"\n />\n ) : (\n <div>\n <Grid container>\n <Grid item xs={3}>\n <Paper>\n <TaskStatusStepper\n steps={steps}\n currentStepId={currentStepId}\n onUserStepChange={setUserSelectedStepId}\n />\n {output && hasLinks(output) && (\n <TaskPageLinks output={output} />\n )}\n </Paper>\n </Grid>\n <Grid item xs={9}>\n <TaskLogger log={logAsString} />\n </Grid>\n </Grid>\n </div>\n )}\n </Content>\n </Page>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport { useAsync } from 'react-use';\nimport { scaffolderApiRef } from '../../api';\nimport {\n Typography,\n Paper,\n Table,\n TableBody,\n Box,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n makeStyles,\n} from '@material-ui/core';\nimport { JSONSchema } from '@backstage/catalog-model';\nimport { JSONSchema7Definition } from 'json-schema';\nimport classNames from 'classnames';\n\nimport { useApi } from '@backstage/core-plugin-api';\nimport {\n Progress,\n Content,\n Header,\n Page,\n ErrorPage,\n} from '@backstage/core-components';\n\nconst useStyles = makeStyles(theme => ({\n code: {\n fontFamily: 'Menlo, monospace',\n padding: theme.spacing(1),\n backgroundColor:\n theme.palette.type === 'dark'\n ? theme.palette.grey[700]\n : theme.palette.grey[300],\n display: 'inline-block',\n borderRadius: 5,\n border: `1px solid ${theme.palette.grey[500]}`,\n position: 'relative',\n },\n\n codeRequired: {\n '&::after': {\n position: 'absolute',\n content: '\"*\"',\n top: 0,\n right: theme.spacing(0.5),\n fontWeight: 'bolder',\n color: theme.palette.error.light,\n },\n },\n}));\n\nexport const ActionsPage = () => {\n const api = useApi(scaffolderApiRef);\n const classes = useStyles();\n const { loading, value, error } = useAsync(async () => {\n return api.listActions();\n });\n\n if (loading) {\n return <Progress />;\n }\n\n if (error) {\n return (\n <ErrorPage\n statusMessage=\"Failed to load installed actions\"\n status=\"500\"\n />\n );\n }\n\n const formatRows = (input: JSONSchema) => {\n const properties = input.properties;\n if (!properties) {\n return undefined;\n }\n\n return Object.entries(properties).map(entry => {\n const [key] = entry;\n const props = entry[1] as unknown as JSONSchema;\n const codeClassname = classNames(classes.code, {\n [classes.codeRequired]: input.required?.includes(key),\n });\n\n return (\n <TableRow key={key}>\n <TableCell>\n <div className={codeClassname}>{key}</div>\n </TableCell>\n <TableCell>{props.title}</TableCell>\n <TableCell>{props.description}</TableCell>\n <TableCell>\n <span className={classes.code}>{props.type}</span>\n </TableCell>\n </TableRow>\n );\n });\n };\n\n const renderTable = (input: JSONSchema) => {\n if (!input.properties) {\n return undefined;\n }\n return (\n <TableContainer component={Paper}>\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Name</TableCell>\n <TableCell>Title</TableCell>\n <TableCell>Description</TableCell>\n <TableCell>Type</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>{formatRows(input)}</TableBody>\n </Table>\n </TableContainer>\n );\n };\n\n const renderTables = (name: string, input?: JSONSchema7Definition[]) => {\n if (!input) {\n return undefined;\n }\n\n return (\n <>\n <Typography variant=\"h6\">{name}</Typography>\n {input.map((i, index) => (\n <div key={index}>{renderTable(i as unknown as JSONSchema)}</div>\n ))}\n </>\n );\n };\n\n const items = value?.map(action => {\n if (action.id.startsWith('legacy:')) {\n return undefined;\n }\n\n const oneOf = renderTables('oneOf', action.schema?.input?.oneOf);\n return (\n <Box pb={4} key={action.id}>\n <Typography variant=\"h4\" className={classes.code}>\n {action.id}\n </Typography>\n <Typography>{action.description}</Typography>\n {action.schema?.input && (\n <Box pb={2}>\n <Typography variant=\"h5\">Input</Typography>\n {renderTable(action.schema.input)}\n {oneOf}\n </Box>\n )}\n {action.schema?.output && (\n <Box pb={2}>\n <Typography variant=\"h5\">Output</Typography>\n {renderTable(action.schema.output)}\n </Box>\n )}\n </Box>\n );\n });\n\n return (\n <Page themeId=\"home\">\n <Header\n pageTitleOverride=\"Create a New Component\"\n title=\"Installed actions\"\n subtitle=\"This is the collection of all installed actions\"\n />\n <Content>{items}</Content>\n </Page>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { ComponentType } from 'react';\nimport { Routes, Route, useOutlet } from 'react-router';\nimport { TemplateEntityV1beta2, Entity } from '@backstage/catalog-model';\nimport { ScaffolderPage } from './ScaffolderPage';\nimport { TemplatePage } from './TemplatePage';\nimport { TaskPage } from './TaskPage';\nimport { ActionsPage } from './ActionsPage';\n\nimport {\n FieldExtensionOptions,\n FIELD_EXTENSION_WRAPPER_KEY,\n FIELD_EXTENSION_KEY,\n DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS,\n} from '../extensions';\nimport { useElementFilter } from '@backstage/core-plugin-api';\n\ntype RouterProps = {\n TemplateCardComponent?:\n | ComponentType<{ template: TemplateEntityV1beta2 }>\n | undefined;\n groups?: Array<{\n title?: string;\n titleComponent?: React.ReactNode;\n filter: (entity: Entity) => boolean;\n }>;\n};\n\nexport const Router = ({ TemplateCardComponent, groups }: RouterProps) => {\n const outlet = useOutlet();\n\n const customFieldExtensions = useElementFilter(outlet, elements =>\n elements\n .selectByComponentData({\n key: FIELD_EXTENSION_WRAPPER_KEY,\n })\n .findComponentData<FieldExtensionOptions>({\n key: FIELD_EXTENSION_KEY,\n }),\n );\n\n const fieldExtensions = [\n ...customFieldExtensions,\n ...DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS.filter(\n ({ name }) =>\n !customFieldExtensions.some(\n customFieldExtension => customFieldExtension.name === name,\n ),\n ),\n ];\n\n return (\n <Routes>\n <Route\n path=\"/\"\n element={\n <ScaffolderPage\n TemplateCardComponent={TemplateCardComponent}\n groups={groups}\n />\n }\n />\n <Route\n path=\"/templates/:templateName\"\n element={<TemplatePage customFieldExtensions={fieldExtensions} />}\n />\n <Route path=\"/tasks/:taskId\" element={<TaskPage />} />\n <Route path=\"/actions\" element={<ActionsPage />} />\n </Routes>\n );\n};\n"],"names":["useStyles","isObject","MuiTheme","StepUI","makeStyles","Typography","Stepper","Step","StepLabel","useParams","Grid"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
+ import { createApiRef, useApi, attachComponentData, createExternalRouteRef, createRouteRef, createPlugin, createApiFactory, discoveryApiRef, identityApiRef, createRoutableExtension, useRouteRef, alertApiRef } from '@backstage/core-plugin-api';
1
2
  import { ResponseError } from '@backstage/errors';
3
+ import qs from 'qs';
2
4
  import ObservableImpl from 'zen-observable';
3
- import { createApiRef, useApi, attachComponentData, createExternalRouteRef, createRouteRef, createPlugin, createApiFactory, discoveryApiRef, identityApiRef, createRoutableExtension, useRouteRef, alertApiRef } from '@backstage/core-plugin-api';
4
5
  import { catalogApiRef, formatEntityRefTitle, useStarredEntity, getEntityRelations, getEntitySourceLocation, EntityRefLinks, useEntityListProvider, useEntityTypeFilter } from '@backstage/plugin-catalog-react';
5
6
  import { TextField, withStyles, makeStyles, IconButton, Tooltip, useTheme, Card, CardMedia, CardContent, Box, Typography, Chip, CardActions, Link, FormControlLabel, Checkbox } from '@material-ui/core';
6
7
  import FormControl from '@material-ui/core/FormControl';
@@ -13,7 +14,7 @@ import Select from '@material-ui/core/Select';
13
14
  import InputLabel from '@material-ui/core/InputLabel';
14
15
  import Input from '@material-ui/core/Input';
15
16
  import FormHelperText from '@material-ui/core/FormHelperText';
16
- import { Progress, ItemCardHeader, Button, WarningPanel, ItemCardGrid } from '@backstage/core-components';
17
+ import { Progress, ItemCardHeader, Button, ContentHeader, WarningPanel, Content, ItemCardGrid } from '@backstage/core-components';
17
18
  import Star from '@material-ui/icons/Star';
18
19
  import StarBorder from '@material-ui/icons/StarBorder';
19
20
  import WarningIcon from '@material-ui/icons/Warning';
@@ -30,9 +31,11 @@ const scaffolderApiRef = createApiRef({
30
31
  });
31
32
  class ScaffolderClient {
32
33
  constructor(options) {
34
+ var _a;
33
35
  this.discoveryApi = options.discoveryApi;
34
36
  this.identityApi = options.identityApi;
35
37
  this.scmIntegrationsApi = options.scmIntegrationsApi;
38
+ this.useLongPollingLogs = (_a = options.useLongPollingLogs) != null ? _a : false;
36
39
  }
37
40
  async getIntegrationsList(options) {
38
41
  return [
@@ -86,11 +89,17 @@ class ScaffolderClient {
86
89
  headers: token ? {Authorization: `Bearer ${token}`} : {}
87
90
  });
88
91
  if (!response.ok) {
89
- throw ResponseError.fromResponse(response);
92
+ throw await ResponseError.fromResponse(response);
90
93
  }
91
94
  return await response.json();
92
95
  }
93
- streamLogs({
96
+ streamLogs(opts) {
97
+ if (this.useLongPollingLogs) {
98
+ return this.streamLogsPolling(opts);
99
+ }
100
+ return this.streamLogsEventStream(opts);
101
+ }
102
+ streamLogsEventStream({
94
103
  taskId,
95
104
  after
96
105
  }) {
@@ -130,6 +139,33 @@ class ScaffolderClient {
130
139
  });
131
140
  });
132
141
  }
142
+ streamLogsPolling({
143
+ taskId,
144
+ after: inputAfter
145
+ }) {
146
+ let after = inputAfter;
147
+ return new ObservableImpl((subscriber) => {
148
+ this.discoveryApi.getBaseUrl("scaffolder").then(async (baseUrl) => {
149
+ while (!subscriber.closed) {
150
+ const url = `${baseUrl}/v2/tasks/${encodeURIComponent(taskId)}/events?${qs.stringify({after})}`;
151
+ const response = await fetch(url);
152
+ if (!response.ok) {
153
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
154
+ continue;
155
+ }
156
+ const logs = await response.json();
157
+ for (const event of logs) {
158
+ after = Number(event.id);
159
+ subscriber.next(event);
160
+ if (event.type === "completion") {
161
+ subscriber.complete();
162
+ return;
163
+ }
164
+ }
165
+ }
166
+ });
167
+ });
168
+ }
133
169
  async listActions() {
134
170
  const baseUrl = await this.discoveryApi.getBaseUrl("scaffolder");
135
171
  const token = await this.identityApi.getIdToken();
@@ -137,7 +173,7 @@ class ScaffolderClient {
137
173
  headers: token ? {Authorization: `Bearer ${token}`} : {}
138
174
  });
139
175
  if (!response.ok) {
140
- throw ResponseError.fromResponse(response);
176
+ throw await ResponseError.fromResponse(response);
141
177
  }
142
178
  return await response.json();
143
179
  }
@@ -490,7 +526,7 @@ const registerComponentRouteRef = createExternalRouteRef({
490
526
  optional: true
491
527
  });
492
528
  const rootRouteRef = createRouteRef({
493
- title: "Create new entity"
529
+ id: "scaffolder"
494
530
  });
495
531
 
496
532
  const scaffolderPlugin = createPlugin({
@@ -533,7 +569,7 @@ const OwnerPickerFieldExtension = scaffolderPlugin.provide(createScaffolderField
533
569
  }));
534
570
  const ScaffolderPage = scaffolderPlugin.provide(createRoutableExtension({
535
571
  name: "ScaffolderPage",
536
- component: () => import('./Router-4bca4743.esm.js').then((m) => m.Router),
572
+ component: () => import('./Router-8b2aa17e.esm.js').then((m) => m.Router),
537
573
  mountPoint: rootRouteRef
538
574
  }));
539
575
 
@@ -690,19 +726,31 @@ const TemplateCard = ({template, deprecated}) => {
690
726
  }, "Choose")));
691
727
  };
692
728
 
693
- const TemplateList = ({TemplateCardComponent}) => {
729
+ const TemplateList = ({
730
+ TemplateCardComponent,
731
+ group
732
+ }) => {
694
733
  const {loading, error, entities} = useEntityListProvider();
695
734
  const Card = TemplateCardComponent || TemplateCard;
735
+ const maybeFilteredEntities = group ? entities.filter((e) => group.filter(e)) : entities;
736
+ const title = group ? group.titleComponent || /* @__PURE__ */ React.createElement(ContentHeader, {
737
+ title: group.title
738
+ }) : /* @__PURE__ */ React.createElement(ContentHeader, {
739
+ title: "Other Templates"
740
+ });
741
+ if (group && maybeFilteredEntities.length === 0) {
742
+ return null;
743
+ }
696
744
  return /* @__PURE__ */ React.createElement(React.Fragment, null, loading && /* @__PURE__ */ React.createElement(Progress, null), error && /* @__PURE__ */ React.createElement(WarningPanel, {
697
745
  title: "Oops! Something went wrong loading the templates"
698
746
  }, error.message), !error && !loading && !entities.length && /* @__PURE__ */ React.createElement(Typography, {
699
747
  variant: "body2"
700
748
  }, "No templates found that match your filter. Learn more about", " ", /* @__PURE__ */ React.createElement(Link, {
701
749
  href: "https://backstage.io/docs/features/software-templates/adding-templates"
702
- }, "adding templates"), "."), /* @__PURE__ */ React.createElement(ItemCardGrid, null, entities && (entities == null ? void 0 : entities.length) > 0 && entities.map((template) => /* @__PURE__ */ React.createElement(Card, {
750
+ }, "adding templates"), "."), /* @__PURE__ */ React.createElement(Content, null, title, /* @__PURE__ */ React.createElement(ItemCardGrid, null, maybeFilteredEntities && (maybeFilteredEntities == null ? void 0 : maybeFilteredEntities.length) > 0 && maybeFilteredEntities.map((template) => /* @__PURE__ */ React.createElement(Card, {
703
751
  key: stringifyEntityRef(template),
704
752
  template
705
- }))));
753
+ })))));
706
754
  };
707
755
 
708
756
  const icon = /* @__PURE__ */ React.createElement(CheckBoxOutlineBlankIcon, {
@@ -756,4 +804,4 @@ const TemplateTypePicker = () => {
756
804
  };
757
805
 
758
806
  export { EntityPicker as E, FIELD_EXTENSION_WRAPPER_KEY as F, OwnerPicker as O, RepoUrlPicker as R, ScaffolderClient as S, TemplateTypePicker as T, EntityNamePicker as a, registerComponentRouteRef as b, TemplateList as c, rootRouteRef as d, entityNamePickerValidation as e, FIELD_EXTENSION_KEY as f, createScaffolderFieldExtension as g, ScaffolderFieldExtensions as h, EntityPickerFieldExtension as i, EntityNamePickerFieldExtension as j, OwnerPickerFieldExtension as k, RepoUrlPickerFieldExtension as l, ScaffolderPage as m, scaffolderPlugin as n, TextValuePicker as o, FavouriteTemplate as p, repoPickerValidation as r, scaffolderApiRef as s };
759
- //# sourceMappingURL=index-12a93d58.esm.js.map
807
+ //# sourceMappingURL=index-385b3fc1.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-385b3fc1.esm.js","sources":["../../src/api.ts","../../src/components/fields/EntityPicker/EntityPicker.tsx","../../src/components/fields/TextValuePicker/TextValuePicker.tsx","../../src/components/fields/EntityNamePicker/EntityNamePicker.tsx","../../src/components/fields/EntityNamePicker/validation.ts","../../src/components/fields/OwnerPicker/OwnerPicker.tsx","../../src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx","../../src/components/fields/RepoUrlPicker/validation.ts","../../src/extensions/index.tsx","../../src/routes.ts","../../src/plugin.ts","../../src/components/FavouriteTemplate/FavouriteTemplate.tsx","../../src/components/TemplateCard/TemplateCard.tsx","../../src/components/TemplateList/TemplateList.tsx","../../src/components/TemplateTypePicker/TemplateTypePicker.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { EntityName } from '@backstage/catalog-model';\nimport {\n createApiRef,\n DiscoveryApi,\n IdentityApi,\n} from '@backstage/core-plugin-api';\nimport { ResponseError } from '@backstage/errors';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport { JsonObject, JsonValue, Observable } from '@backstage/types';\nimport { Field, FieldValidation } from '@rjsf/core';\nimport qs from 'qs';\nimport ObservableImpl from 'zen-observable';\nimport { ListActionsResponse, ScaffolderTask, Status } from './types';\n\nexport const scaffolderApiRef = createApiRef<ScaffolderApi>({\n id: 'plugin.scaffolder.service',\n description: 'Used to make requests towards the scaffolder backend',\n});\n\ntype TemplateParameterSchema = {\n title: string;\n steps: Array<{\n title: string;\n schema: JsonObject;\n }>;\n};\n\nexport type LogEvent = {\n type: 'log' | 'completion';\n body: {\n message: string;\n stepId?: string;\n status?: Status;\n };\n createdAt: string;\n id: string;\n taskId: string;\n};\n\nexport type CustomField = {\n name: string;\n component: Field;\n validation: (data: JsonValue, field: FieldValidation) => void;\n};\n\nexport interface ScaffolderApi {\n getTemplateParameterSchema(\n templateName: EntityName,\n ): Promise<TemplateParameterSchema>;\n\n /**\n * Executes the scaffolding of a component, given a template and its\n * parameter values.\n *\n * @param templateName Name of the Template entity for the scaffolder to use. New project is going to be created out of this template.\n * @param values Parameters for the template, e.g. name, description\n */\n scaffold(templateName: string, values: Record<string, any>): Promise<string>;\n\n getTask(taskId: string): Promise<ScaffolderTask>;\n\n getIntegrationsList(options: {\n allowedHosts: string[];\n }): Promise<{ type: string; title: string; host: string }[]>;\n\n // Returns a list of all installed actions.\n listActions(): Promise<ListActionsResponse>;\n\n streamLogs({\n taskId,\n after,\n }: {\n taskId: string;\n after?: number;\n }): Observable<LogEvent>;\n}\n\nexport class ScaffolderClient implements ScaffolderApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly identityApi: IdentityApi;\n private readonly scmIntegrationsApi: ScmIntegrationRegistry;\n private readonly useLongPollingLogs: boolean;\n\n constructor(options: {\n discoveryApi: DiscoveryApi;\n identityApi: IdentityApi;\n scmIntegrationsApi: ScmIntegrationRegistry;\n useLongPollingLogs?: boolean;\n }) {\n this.discoveryApi = options.discoveryApi;\n this.identityApi = options.identityApi;\n this.scmIntegrationsApi = options.scmIntegrationsApi;\n this.useLongPollingLogs = options.useLongPollingLogs ?? false;\n }\n\n async getIntegrationsList(options: { allowedHosts: string[] }) {\n return [\n ...this.scmIntegrationsApi.azure.list(),\n ...this.scmIntegrationsApi.bitbucket.list(),\n ...this.scmIntegrationsApi.github.list(),\n ...this.scmIntegrationsApi.gitlab.list(),\n ]\n .map(c => ({ type: c.type, title: c.title, host: c.config.host }))\n .filter(c => options.allowedHosts.includes(c.host));\n }\n\n async getTemplateParameterSchema(\n templateName: EntityName,\n ): Promise<TemplateParameterSchema> {\n const { namespace, kind, name } = templateName;\n\n const token = await this.identityApi.getIdToken();\n const baseUrl = await this.discoveryApi.getBaseUrl('scaffolder');\n const templatePath = [namespace, kind, name]\n .map(s => encodeURIComponent(s))\n .join('/');\n const url = `${baseUrl}/v2/templates/${templatePath}/parameter-schema`;\n\n const response = await fetch(url, {\n headers: {\n ...(token && { Authorization: `Bearer ${token}` }),\n },\n });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n const schema: TemplateParameterSchema = await response.json();\n return schema;\n }\n\n /**\n * Executes the scaffolding of a component, given a template and its\n * parameter values.\n *\n * @param templateName Template name for the scaffolder to use. New project is going to be created out of this template.\n * @param values Parameters for the template, e.g. name, description\n */\n async scaffold(\n templateName: string,\n values: Record<string, any>,\n ): Promise<string> {\n const token = await this.identityApi.getIdToken();\n const url = `${await this.discoveryApi.getBaseUrl('scaffolder')}/v2/tasks`;\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(token && { Authorization: `Bearer ${token}` }),\n },\n body: JSON.stringify({ templateName, values: { ...values } }),\n });\n\n if (response.status !== 201) {\n const status = `${response.status} ${response.statusText}`;\n const body = await response.text();\n throw new Error(`Backend request failed, ${status} ${body.trim()}`);\n }\n\n const { id } = (await response.json()) as { id: string };\n return id;\n }\n\n async getTask(taskId: string) {\n const token = await this.identityApi.getIdToken();\n const baseUrl = await this.discoveryApi.getBaseUrl('scaffolder');\n const url = `${baseUrl}/v2/tasks/${encodeURIComponent(taskId)}`;\n const response = await fetch(url, {\n headers: token ? { Authorization: `Bearer ${token}` } : {},\n });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return await response.json();\n }\n\n streamLogs(opts: { taskId: string; after?: number }): Observable<LogEvent> {\n if (this.useLongPollingLogs) {\n return this.streamLogsPolling(opts);\n }\n\n return this.streamLogsEventStream(opts);\n }\n\n private streamLogsEventStream({\n taskId,\n after,\n }: {\n taskId: string;\n after?: number;\n }): Observable<LogEvent> {\n return new ObservableImpl(subscriber => {\n const params = new URLSearchParams();\n if (after !== undefined) {\n params.set('after', String(Number(after)));\n }\n\n this.discoveryApi.getBaseUrl('scaffolder').then(\n baseUrl => {\n const url = `${baseUrl}/v2/tasks/${encodeURIComponent(\n taskId,\n )}/eventstream`;\n const eventSource = new EventSource(url, { withCredentials: true });\n eventSource.addEventListener('log', (event: any) => {\n if (event.data) {\n try {\n subscriber.next(JSON.parse(event.data));\n } catch (ex) {\n subscriber.error(ex);\n }\n }\n });\n eventSource.addEventListener('completion', (event: any) => {\n if (event.data) {\n try {\n subscriber.next(JSON.parse(event.data));\n } catch (ex) {\n subscriber.error(ex);\n }\n }\n eventSource.close();\n subscriber.complete();\n });\n eventSource.addEventListener('error', event => {\n subscriber.error(event);\n });\n },\n error => {\n subscriber.error(error);\n },\n );\n });\n }\n\n private streamLogsPolling({\n taskId,\n after: inputAfter,\n }: {\n taskId: string;\n after?: number;\n }): Observable<LogEvent> {\n let after = inputAfter;\n\n return new ObservableImpl(subscriber => {\n this.discoveryApi.getBaseUrl('scaffolder').then(async baseUrl => {\n while (!subscriber.closed) {\n const url = `${baseUrl}/v2/tasks/${encodeURIComponent(\n taskId,\n )}/events?${qs.stringify({ after })}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n // wait for one second to not run into an\n await new Promise(resolve => setTimeout(resolve, 1000));\n continue;\n }\n\n const logs = (await response.json()) as LogEvent[];\n\n for (const event of logs) {\n after = Number(event.id);\n\n subscriber.next(event);\n\n if (event.type === 'completion') {\n subscriber.complete();\n return;\n }\n }\n }\n });\n });\n }\n\n /**\n * @returns ListActionsResponse containing all registered actions.\n */\n async listActions(): Promise<ListActionsResponse> {\n const baseUrl = await this.discoveryApi.getBaseUrl('scaffolder');\n const token = await this.identityApi.getIdToken();\n const response = await fetch(`${baseUrl}/v2/actions`, {\n headers: token ? { Authorization: `Bearer ${token}` } : {},\n });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return await response.json();\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useApi } from '@backstage/core-plugin-api';\nimport {\n catalogApiRef,\n formatEntityRefTitle,\n} from '@backstage/plugin-catalog-react';\nimport { TextField } from '@material-ui/core';\nimport FormControl from '@material-ui/core/FormControl';\nimport Autocomplete from '@material-ui/lab/Autocomplete';\nimport { FieldProps } from '@rjsf/core';\nimport React from 'react';\nimport { useAsync } from 'react-use';\n\nexport const EntityPicker = ({\n onChange,\n schema: { title = 'Entity', description = 'An entity from the catalog' },\n required,\n uiSchema,\n rawErrors,\n formData,\n idSchema,\n}: FieldProps<string>) => {\n const allowedKinds = uiSchema['ui:options']?.allowedKinds as string[];\n const defaultKind = uiSchema['ui:options']?.defaultKind as string | undefined;\n const catalogApi = useApi(catalogApiRef);\n\n const { value: entities, loading } = useAsync(() =>\n catalogApi.getEntities(\n allowedKinds ? { filter: { kind: allowedKinds } } : undefined,\n ),\n );\n\n const entityRefs = entities?.items.map(e =>\n formatEntityRefTitle(e, { defaultKind }),\n );\n\n const onSelect = (_: any, value: string | null) => {\n onChange(value || '');\n };\n\n return (\n <FormControl\n margin=\"normal\"\n required={required}\n error={rawErrors?.length > 0 && !formData}\n >\n <Autocomplete\n id={idSchema?.$id}\n value={(formData as string) || ''}\n loading={loading}\n onChange={onSelect}\n options={entityRefs || []}\n autoSelect\n freeSolo\n renderInput={params => (\n <TextField\n {...params}\n label={title}\n margin=\"normal\"\n helperText={description}\n variant=\"outlined\"\n required={required}\n InputProps={params.InputProps}\n />\n )}\n />\n </FormControl>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { TextField } from '@material-ui/core';\nimport { FieldProps } from '@rjsf/core';\nimport React from 'react';\n\nexport const TextValuePicker = ({\n onChange,\n required,\n schema: { title, description },\n rawErrors,\n formData,\n uiSchema: { 'ui:autofocus': autoFocus },\n idSchema,\n placeholder,\n}: FieldProps<string>) => (\n <TextField\n id={idSchema?.$id}\n label={title}\n placeholder={placeholder}\n helperText={description}\n required={required}\n value={formData ?? ''}\n onChange={({ target: { value } }) => onChange(value)}\n margin=\"normal\"\n error={rawErrors?.length > 0 && !formData}\n inputProps={{ autoFocus }}\n />\n);\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport { FieldProps } from '@rjsf/core';\nimport { TextValuePicker } from '../TextValuePicker';\n\nexport const EntityNamePicker = ({\n schema: { title = 'Name', description = 'Unique name of the component' },\n ...props\n}: FieldProps<string>) => (\n <TextValuePicker schema={{ title, description }} {...props} />\n);\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { FieldValidation } from '@rjsf/core';\nimport { KubernetesValidatorFunctions } from '@backstage/catalog-model';\n\nexport const entityNamePickerValidation = (\n value: string,\n validation: FieldValidation,\n) => {\n if (!KubernetesValidatorFunctions.isValidObjectName(value)) {\n validation.addError(\n 'must start and end with an alphanumeric character, and contain only alphanumeric characters, hyphens, underscores, and periods. Maximum length is 63 characters.',\n );\n }\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { FieldProps } from '@rjsf/core';\nimport React from 'react';\nimport { EntityPicker } from '../EntityPicker';\n\nexport const OwnerPicker = ({\n schema: { title = 'Owner', description = 'The owner of the component' },\n uiSchema,\n ...props\n}: FieldProps<string>) => {\n const ownerUiSchema = {\n ...uiSchema,\n 'ui:options': {\n allowedKinds: (uiSchema['ui:options']?.allowedKinds || [\n 'Group',\n 'User',\n ]) as string[],\n defaultKind: 'Group',\n },\n };\n\n return (\n <EntityPicker\n {...props}\n schema={{ title, description }}\n uiSchema={ownerUiSchema}\n />\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React, { useCallback, useEffect } from 'react';\nimport { FieldProps } from '@rjsf/core';\nimport { scaffolderApiRef } from '../../../api';\nimport { scmIntegrationsApiRef } from '@backstage/integration-react';\nimport { useAsync } from 'react-use';\nimport Select from '@material-ui/core/Select';\nimport InputLabel from '@material-ui/core/InputLabel';\nimport Input from '@material-ui/core/Input';\nimport FormControl from '@material-ui/core/FormControl';\nimport FormHelperText from '@material-ui/core/FormHelperText';\n\nimport { useApi } from '@backstage/core-plugin-api';\nimport { Progress } from '@backstage/core-components';\n\nfunction splitFormData(url: string | undefined) {\n let host = undefined;\n let owner = undefined;\n let repo = undefined;\n let organization = undefined;\n let workspace = undefined;\n let project = undefined;\n\n try {\n if (url) {\n const parsed = new URL(`https://${url}`);\n host = parsed.host;\n owner = parsed.searchParams.get('owner') || undefined;\n repo = parsed.searchParams.get('repo') || undefined;\n // This is azure dev ops specific. not used for any other provider.\n organization = parsed.searchParams.get('organization') || undefined;\n // These are bitbucket specific, not used for any other provider.\n workspace = parsed.searchParams.get('workspace') || undefined;\n project = parsed.searchParams.get('project') || undefined;\n }\n } catch {\n /* ok */\n }\n\n return { host, owner, repo, organization, workspace, project };\n}\n\nfunction serializeFormData(data: {\n host?: string;\n owner?: string;\n repo?: string;\n organization?: string;\n workspace?: string;\n project?: string;\n}) {\n if (!data.host) {\n return undefined;\n }\n\n const params = new URLSearchParams();\n if (data.owner) {\n params.set('owner', data.owner);\n }\n if (data.repo) {\n params.set('repo', data.repo);\n }\n if (data.organization) {\n params.set('organization', data.organization);\n }\n if (data.workspace) {\n params.set('workspace', data.workspace);\n }\n if (data.project) {\n params.set('project', data.project);\n }\n\n return `${data.host}?${params.toString()}`;\n}\n\nexport const RepoUrlPicker = ({\n onChange,\n uiSchema,\n rawErrors,\n formData,\n}: FieldProps<string>) => {\n const scaffolderApi = useApi(scaffolderApiRef);\n const integrationApi = useApi(scmIntegrationsApiRef);\n const allowedHosts = uiSchema['ui:options']?.allowedHosts as string[];\n\n const { value: integrations, loading } = useAsync(async () => {\n return await scaffolderApi.getIntegrationsList({ allowedHosts });\n });\n\n const { host, owner, repo, organization, workspace, project } =\n splitFormData(formData);\n const updateHost = useCallback(\n (evt: React.ChangeEvent<{ name?: string; value: unknown }>) => {\n onChange(\n serializeFormData({\n host: evt.target.value as string,\n owner,\n repo,\n organization,\n workspace,\n project,\n }),\n );\n },\n [onChange, owner, repo, organization, workspace, project],\n );\n\n const updateOwner = useCallback(\n (evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>\n onChange(\n serializeFormData({\n host,\n owner: evt.target.value as string,\n repo,\n organization,\n workspace,\n project,\n }),\n ),\n [onChange, host, repo, organization, workspace, project],\n );\n\n const updateRepo = useCallback(\n (evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>\n onChange(\n serializeFormData({\n host,\n owner,\n repo: evt.target.value as string,\n organization,\n workspace,\n project,\n }),\n ),\n [onChange, host, owner, organization, workspace, project],\n );\n\n const updateOrganization = useCallback(\n (evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>\n onChange(\n serializeFormData({\n host,\n owner,\n repo,\n organization: evt.target.value as string,\n workspace,\n project,\n }),\n ),\n [onChange, host, owner, repo, workspace, project],\n );\n\n const updateWorkspace = useCallback(\n (evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>\n onChange(\n serializeFormData({\n host,\n owner,\n repo,\n organization,\n workspace: evt.target.value as string,\n project,\n }),\n ),\n [onChange, host, owner, repo, organization, project],\n );\n\n const updateProject = useCallback(\n (evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>\n onChange(\n serializeFormData({\n host,\n owner,\n repo,\n organization,\n workspace,\n project: evt.target.value as string,\n }),\n ),\n [onChange, host, owner, repo, organization, workspace],\n );\n\n useEffect(() => {\n if (host === undefined && integrations?.length) {\n onChange(\n serializeFormData({\n host: integrations[0].host,\n owner,\n repo,\n organization,\n workspace,\n project,\n }),\n );\n }\n }, [\n onChange,\n integrations,\n host,\n owner,\n repo,\n organization,\n workspace,\n project,\n ]);\n\n if (loading) {\n return <Progress />;\n }\n\n return (\n <>\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !host}\n >\n <InputLabel htmlFor=\"hostInput\">Host</InputLabel>\n <Select native id=\"hostInput\" onChange={updateHost} value={host}>\n {integrations ? (\n integrations\n .filter(i => allowedHosts?.includes(i.host))\n .map(i => (\n <option key={i.host} value={i.host}>\n {i.title}\n </option>\n ))\n ) : (\n <p>loading</p>\n )}\n </Select>\n <FormHelperText>\n The host where the repository will be created\n </FormHelperText>\n </FormControl>\n {/* Show this for dev.azure.com only */}\n {host === 'dev.azure.com' && (\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !organization}\n >\n <InputLabel htmlFor=\"repoInput\">Organization</InputLabel>\n <Input\n id=\"repoInput\"\n onChange={updateOrganization}\n value={organization}\n />\n <FormHelperText>The name of the organization</FormHelperText>\n </FormControl>\n )}\n {host && integrationApi.byHost(host)?.type === 'bitbucket' && (\n <>\n {/* Show this for bitbucket.org only */}\n {host === 'bitbucket.org' && (\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !workspace}\n >\n <InputLabel htmlFor=\"wokrspaceInput\">Workspace</InputLabel>\n <Input\n id=\"wokrspaceInput\"\n onChange={updateWorkspace}\n value={workspace}\n />\n <FormHelperText>\n The workspace where the repository will be created\n </FormHelperText>\n </FormControl>\n )}\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !project}\n >\n <InputLabel htmlFor=\"wokrspaceInput\">Project</InputLabel>\n <Input\n id=\"wokrspaceInput\"\n onChange={updateProject}\n value={project}\n />\n <FormHelperText>\n The project where the repository will be created\n </FormHelperText>\n </FormControl>\n </>\n )}\n {/* Show this for all hosts except bitbucket */}\n {host && integrationApi.byHost(host)?.type !== 'bitbucket' && (\n <>\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !owner}\n >\n <InputLabel htmlFor=\"ownerInput\">Owner</InputLabel>\n <Input id=\"ownerInput\" onChange={updateOwner} value={owner} />\n <FormHelperText>\n The organization, user or project that this repo will belong to\n </FormHelperText>\n </FormControl>\n </>\n )}\n {/* Show this for all hosts */}\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !repo}\n >\n <InputLabel htmlFor=\"repoInput\">Repository</InputLabel>\n <Input id=\"repoInput\" onChange={updateRepo} value={repo} />\n <FormHelperText>The name of the repository</FormHelperText>\n </FormControl>\n </>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { FieldValidation } from '@rjsf/core';\nimport { ApiHolder } from '@backstage/core-plugin-api';\nimport { scmIntegrationsApiRef } from '@backstage/integration-react';\n\nexport const repoPickerValidation = (\n value: string,\n validation: FieldValidation,\n context: { apiHolder: ApiHolder },\n) => {\n try {\n const { host, searchParams } = new URL(`https://${value}`);\n\n const integrationApi = context.apiHolder.get(scmIntegrationsApiRef);\n\n if (!host) {\n validation.addError(\n 'Incomplete repository location provided, host not provided',\n );\n } else {\n if (integrationApi?.byHost(host)?.type === 'bitbucket') {\n // workspace is only applicable for bitbucket cloud\n if (host === 'bitbucket.org' && !searchParams.get('workspace')) {\n validation.addError(\n 'Incomplete repository location provided, workspace not provided',\n );\n }\n\n if (!searchParams.get('project')) {\n validation.addError(\n 'Incomplete repository location provided, project not provided',\n );\n }\n }\n // For anything other than bitbucket\n else {\n if (!searchParams.get('owner')) {\n validation.addError(\n 'Incomplete repository location provided, owner not provided',\n );\n }\n }\n\n // Do this for all hosts\n if (!searchParams.get('repo')) {\n validation.addError(\n 'Incomplete repository location provided, repo not provided',\n );\n }\n }\n } catch {\n validation.addError('Unable to parse the Repository URL');\n }\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React from 'react';\nimport { CustomFieldValidator, FieldExtensionOptions } from './types';\nimport { Extension, attachComponentData } from '@backstage/core-plugin-api';\n\nexport const FIELD_EXTENSION_WRAPPER_KEY = 'scaffolder.extensions.wrapper.v1';\nexport const FIELD_EXTENSION_KEY = 'scaffolder.extensions.field.v1';\n\nexport function createScaffolderFieldExtension<T = any>(\n options: FieldExtensionOptions<T>,\n): Extension<() => null> {\n return {\n expose() {\n const FieldExtensionDataHolder: any = () => null;\n\n attachComponentData(\n FieldExtensionDataHolder,\n FIELD_EXTENSION_KEY,\n options,\n );\n\n return FieldExtensionDataHolder;\n },\n };\n}\n\nexport const ScaffolderFieldExtensions: React.ComponentType = () => null;\nattachComponentData(\n ScaffolderFieldExtensions,\n FIELD_EXTENSION_WRAPPER_KEY,\n true,\n);\n\nexport type { CustomFieldValidator, FieldExtensionOptions };\n\nexport { DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS } from './default';\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n createExternalRouteRef,\n createRouteRef,\n} from '@backstage/core-plugin-api';\n\nexport const registerComponentRouteRef = createExternalRouteRef({\n id: 'register-component',\n optional: true,\n});\n\nexport const rootRouteRef = createRouteRef({\n id: 'scaffolder',\n});\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { scmIntegrationsApiRef } from '@backstage/integration-react';\nimport { scaffolderApiRef, ScaffolderClient } from './api';\nimport { EntityPicker } from './components/fields/EntityPicker';\nimport {\n entityNamePickerValidation,\n EntityNamePicker,\n} from './components/fields/EntityNamePicker';\nimport { OwnerPicker } from './components/fields/OwnerPicker';\nimport {\n repoPickerValidation,\n RepoUrlPicker,\n} from './components/fields/RepoUrlPicker';\nimport { createScaffolderFieldExtension } from './extensions';\nimport { registerComponentRouteRef, rootRouteRef } from './routes';\nimport {\n createApiFactory,\n createPlugin,\n createRoutableExtension,\n discoveryApiRef,\n identityApiRef,\n} from '@backstage/core-plugin-api';\n\nexport const scaffolderPlugin = createPlugin({\n id: 'scaffolder',\n apis: [\n createApiFactory({\n api: scaffolderApiRef,\n deps: {\n discoveryApi: discoveryApiRef,\n identityApi: identityApiRef,\n scmIntegrationsApi: scmIntegrationsApiRef,\n },\n factory: ({ discoveryApi, identityApi, scmIntegrationsApi }) =>\n new ScaffolderClient({ discoveryApi, identityApi, scmIntegrationsApi }),\n }),\n ],\n routes: {\n root: rootRouteRef,\n },\n externalRoutes: {\n registerComponent: registerComponentRouteRef,\n },\n});\n\nexport const EntityPickerFieldExtension = scaffolderPlugin.provide(\n createScaffolderFieldExtension({\n component: EntityPicker,\n name: 'EntityPicker',\n }),\n);\n\nexport const EntityNamePickerFieldExtension = scaffolderPlugin.provide(\n createScaffolderFieldExtension({\n component: EntityNamePicker,\n name: 'EntityNamePicker',\n validation: entityNamePickerValidation,\n }),\n);\n\nexport const RepoUrlPickerFieldExtension = scaffolderPlugin.provide(\n createScaffolderFieldExtension({\n component: RepoUrlPicker,\n name: 'RepoUrlPicker',\n validation: repoPickerValidation,\n }),\n);\n\nexport const OwnerPickerFieldExtension = scaffolderPlugin.provide(\n createScaffolderFieldExtension({\n component: OwnerPicker,\n name: 'OwnerPicker',\n }),\n);\n\nexport const ScaffolderPage = scaffolderPlugin.provide(\n createRoutableExtension({\n name: 'ScaffolderPage',\n component: () => import('./components/Router').then(m => m.Router),\n mountPoint: rootRouteRef,\n }),\n);\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { useStarredEntity } from '@backstage/plugin-catalog-react';\nimport { IconButton, makeStyles, Tooltip, withStyles } from '@material-ui/core';\nimport Star from '@material-ui/icons/Star';\nimport StarBorder from '@material-ui/icons/StarBorder';\nimport React, { ComponentProps } from 'react';\n\ntype Props = ComponentProps<typeof IconButton> & { entity: Entity };\n\nconst YellowStar = withStyles({\n root: {\n color: '#f3ba37',\n },\n})(Star);\n\nconst WhiteBorderStar = withStyles({\n root: {\n color: '#ffffff',\n },\n})(StarBorder);\n\nconst useStyles = makeStyles(theme => ({\n starButton: {\n position: 'absolute',\n top: theme.spacing(0.5),\n right: theme.spacing(0.5),\n padding: '0.25rem',\n },\n}));\n\nexport const favouriteTemplateTooltip = (isStarred: boolean) =>\n isStarred ? 'Remove from favorites' : 'Add to favorites';\n\nexport const favouriteTemplateIcon = (isStarred: boolean) =>\n isStarred ? <YellowStar /> : <WhiteBorderStar />;\n\n/**\n * IconButton for showing if a current entity is starred and adding/removing it from the favourite entities\n * @param props MaterialUI IconButton props extended by required `entity` prop\n */\nexport const FavouriteTemplate = (props: Props) => {\n const classes = useStyles();\n const { toggleStarredEntity, isStarredEntity } = useStarredEntity(\n props.entity,\n );\n return (\n <IconButton\n color=\"inherit\"\n className={classes.starButton}\n {...props}\n onClick={() => toggleStarredEntity()}\n >\n <Tooltip title={favouriteTemplateTooltip(isStarredEntity)}>\n {favouriteTemplateIcon(isStarredEntity)}\n </Tooltip>\n </IconButton>\n );\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n Entity,\n RELATION_OWNED_BY,\n TemplateEntityV1beta2,\n} from '@backstage/catalog-model';\nimport {\n ScmIntegrationIcon,\n scmIntegrationsApiRef,\n} from '@backstage/integration-react';\nimport {\n EntityRefLinks,\n getEntityRelations,\n getEntitySourceLocation,\n} from '@backstage/plugin-catalog-react';\nimport { BackstageTheme } from '@backstage/theme';\nimport {\n Box,\n Card,\n CardActions,\n CardContent,\n CardMedia,\n Chip,\n IconButton,\n Link,\n makeStyles,\n Tooltip,\n Typography,\n useTheme,\n} from '@material-ui/core';\nimport WarningIcon from '@material-ui/icons/Warning';\nimport React from 'react';\nimport { generatePath } from 'react-router';\nimport { rootRouteRef } from '../../routes';\nimport { FavouriteTemplate } from '../FavouriteTemplate/FavouriteTemplate';\n\nimport { Button, ItemCardHeader } from '@backstage/core-components';\nimport { useApi, useRouteRef } from '@backstage/core-plugin-api';\n\nconst useStyles = makeStyles(theme => ({\n cardHeader: {\n position: 'relative',\n },\n title: {\n backgroundImage: ({ backgroundImage }: any) => backgroundImage,\n },\n box: {\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n display: '-webkit-box',\n '-webkit-line-clamp': 10,\n '-webkit-box-orient': 'vertical',\n paddingBottom: '0.8em',\n },\n label: {\n color: theme.palette.text.secondary,\n textTransform: 'uppercase',\n fontSize: '0.65rem',\n fontWeight: 'bold',\n letterSpacing: 0.5,\n lineHeight: 1,\n paddingBottom: '0.2rem',\n },\n leftButton: {\n marginRight: 'auto',\n },\n}));\n\nconst useDeprecationStyles = makeStyles(theme => ({\n deprecationIcon: {\n position: 'absolute',\n top: theme.spacing(0.5),\n right: theme.spacing(3.5),\n padding: '0.25rem',\n },\n link: {\n color: theme.palette.warning.light,\n },\n}));\n\nexport type TemplateCardProps = {\n template: TemplateEntityV1beta2;\n deprecated?: boolean;\n};\n\ntype TemplateProps = {\n description: string;\n tags: string[];\n title: string;\n type: string;\n name: string;\n};\n\nconst getTemplateCardProps = (\n template: TemplateEntityV1beta2,\n): TemplateProps & { key: string } => {\n return {\n key: template.metadata.uid!,\n name: template.metadata.name,\n title: `${(template.metadata.title || template.metadata.name) ?? ''}`,\n type: template.spec.type ?? '',\n description: template.metadata.description ?? '-',\n tags: (template.metadata?.tags as string[]) ?? [],\n };\n};\n\nconst DeprecationWarning = () => {\n const styles = useDeprecationStyles();\n\n const Title = (\n <Typography style={{ padding: 10, maxWidth: 300 }}>\n This template syntax is deprecated. Click for more info.\n </Typography>\n );\n\n return (\n <div className={styles.deprecationIcon}>\n <Tooltip title={Title}>\n <Link\n href=\"https://backstage.io/docs/features/software-templates/migrating-from-v1alpha1-to-v1beta2\"\n className={styles.link}\n >\n <WarningIcon />\n </Link>\n </Tooltip>\n </div>\n );\n};\n\nexport const TemplateCard = ({ template, deprecated }: TemplateCardProps) => {\n const backstageTheme = useTheme<BackstageTheme>();\n const rootLink = useRouteRef(rootRouteRef);\n const templateProps = getTemplateCardProps(template);\n const ownedByRelations = getEntityRelations(\n template as Entity,\n RELATION_OWNED_BY,\n );\n const themeId = backstageTheme.getPageTheme({ themeId: templateProps.type })\n ? templateProps.type\n : 'other';\n const theme = backstageTheme.getPageTheme({ themeId });\n const classes = useStyles({ backgroundImage: theme.backgroundImage });\n const href = generatePath(`${rootLink()}/templates/:templateName`, {\n templateName: templateProps.name,\n });\n\n const scmIntegrationsApi = useApi(scmIntegrationsApiRef);\n const sourceLocation = getEntitySourceLocation(template, scmIntegrationsApi);\n\n return (\n <Card>\n <CardMedia className={classes.cardHeader}>\n <FavouriteTemplate entity={template} />\n {deprecated && <DeprecationWarning />}\n <ItemCardHeader\n title={templateProps.title}\n subtitle={templateProps.type}\n classes={{ root: classes.title }}\n />\n </CardMedia>\n <CardContent style={{ display: 'grid' }}>\n <Box className={classes.box}>\n <Typography variant=\"body2\" className={classes.label}>\n Description\n </Typography>\n {templateProps.description}\n </Box>\n <Box className={classes.box}>\n <Typography variant=\"body2\" className={classes.label}>\n Owner\n </Typography>\n <EntityRefLinks entityRefs={ownedByRelations} defaultKind=\"Group\" />\n </Box>\n <Box>\n <Typography variant=\"body2\" className={classes.label}>\n Tags\n </Typography>\n {templateProps.tags?.map(tag => (\n <Chip size=\"small\" label={tag} key={tag} />\n ))}\n </Box>\n </CardContent>\n <CardActions>\n {sourceLocation && (\n <IconButton\n className={classes.leftButton}\n href={sourceLocation.locationTargetUrl}\n >\n <ScmIntegrationIcon type={sourceLocation.integrationType} />\n </IconButton>\n )}\n <Button\n color=\"primary\"\n to={href}\n aria-label={`Choose ${templateProps.title}`}\n >\n Choose\n </Button>\n </CardActions>\n </Card>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { ComponentType } from 'react';\nimport {\n Entity,\n stringifyEntityRef,\n TemplateEntityV1beta2,\n} from '@backstage/catalog-model';\nimport {\n Content,\n ContentHeader,\n ItemCardGrid,\n Progress,\n WarningPanel,\n} from '@backstage/core-components';\nimport { useEntityListProvider } from '@backstage/plugin-catalog-react';\nimport { Link, Typography } from '@material-ui/core';\nimport { TemplateCard } from '../TemplateCard';\n\nexport type TemplateListProps = {\n TemplateCardComponent?:\n | ComponentType<{ template: TemplateEntityV1beta2 }>\n | undefined;\n group?: {\n title?: string;\n titleComponent?: React.ReactNode;\n filter: (entity: Entity) => boolean;\n };\n};\n\nexport const TemplateList = ({\n TemplateCardComponent,\n group,\n}: TemplateListProps) => {\n const { loading, error, entities } = useEntityListProvider();\n const Card = TemplateCardComponent || TemplateCard;\n const maybeFilteredEntities = group\n ? entities.filter(e => group.filter(e))\n : entities;\n const title = group ? (\n group.titleComponent || <ContentHeader title={group.title} />\n ) : (\n <ContentHeader title=\"Other Templates\" />\n );\n\n if (group && maybeFilteredEntities.length === 0) {\n return null;\n }\n return (\n <>\n {loading && <Progress />}\n\n {error && (\n <WarningPanel title=\"Oops! Something went wrong loading the templates\">\n {error.message}\n </WarningPanel>\n )}\n\n {!error && !loading && !entities.length && (\n <Typography variant=\"body2\">\n No templates found that match your filter. Learn more about{' '}\n <Link href=\"https://backstage.io/docs/features/software-templates/adding-templates\">\n adding templates\n </Link>\n .\n </Typography>\n )}\n\n <Content>\n {title}\n <ItemCardGrid>\n {maybeFilteredEntities &&\n maybeFilteredEntities?.length > 0 &&\n maybeFilteredEntities.map((template: Entity) => (\n <Card\n key={stringifyEntityRef(template)}\n template={template as TemplateEntityV1beta2}\n />\n ))}\n </ItemCardGrid>\n </Content>\n </>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React from 'react';\nimport capitalize from 'lodash/capitalize';\nimport { Progress } from '@backstage/core-components';\nimport {\n Box,\n Checkbox,\n FormControlLabel,\n TextField,\n Typography,\n} from '@material-ui/core';\nimport CheckBoxIcon from '@material-ui/icons/CheckBox';\nimport CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport { Autocomplete } from '@material-ui/lab';\nimport { useEntityTypeFilter } from '@backstage/plugin-catalog-react';\nimport { alertApiRef, useApi } from '@backstage/core-plugin-api';\n\nconst icon = <CheckBoxOutlineBlankIcon fontSize=\"small\" />;\nconst checkedIcon = <CheckBoxIcon fontSize=\"small\" />;\n\nexport const TemplateTypePicker = () => {\n const alertApi = useApi(alertApiRef);\n const { error, loading, availableTypes, selectedTypes, setSelectedTypes } =\n useEntityTypeFilter();\n\n if (loading) return <Progress />;\n\n if (!availableTypes) return null;\n\n if (error) {\n alertApi.post({\n message: `Failed to load entity types`,\n severity: 'error',\n });\n return null;\n }\n\n return (\n <Box pb={1} pt={1}>\n <Typography variant=\"button\">Categories</Typography>\n <Autocomplete\n multiple\n aria-label=\"Categories\"\n options={availableTypes}\n value={selectedTypes}\n onChange={(_: object, value: string[]) => setSelectedTypes(value)}\n renderOption={(option, { selected }) => (\n <FormControlLabel\n control={\n <Checkbox\n icon={icon}\n checkedIcon={checkedIcon}\n checked={selected}\n />\n }\n label={capitalize(option)}\n />\n )}\n size=\"small\"\n popupIcon={<ExpandMoreIcon data-testid=\"categories-picker-expand\" />}\n renderInput={params => <TextField {...params} variant=\"outlined\" />}\n />\n </Box>\n );\n};\n"],"names":["useStyles","Autocomplete"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -3,10 +3,10 @@ import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
3
3
  import { DiscoveryApi, IdentityApi, ApiHolder, Extension } from '@backstage/core-plugin-api';
4
4
  import * as _backstage_catalog_model from '@backstage/catalog-model';
5
5
  import { JSONSchema, EntityName, Entity, TemplateEntityV1beta2 } from '@backstage/catalog-model';
6
- import { JsonValue, Observable, JsonObject } from '@backstage/types';
7
6
  import { ScmIntegrationRegistry } from '@backstage/integration';
8
- import * as React from 'react';
9
- import React__default, { ComponentProps, ComponentType } from 'react';
7
+ import { JsonValue, Observable, JsonObject } from '@backstage/types';
8
+ import * as react from 'react';
9
+ import react__default, { ComponentProps, ComponentType } from 'react';
10
10
  import { FieldValidation, FieldProps } from '@rjsf/core';
11
11
  import { IconButton } from '@material-ui/core';
12
12
 
@@ -84,10 +84,12 @@ declare class ScaffolderClient implements ScaffolderApi {
84
84
  private readonly discoveryApi;
85
85
  private readonly identityApi;
86
86
  private readonly scmIntegrationsApi;
87
+ private readonly useLongPollingLogs;
87
88
  constructor(options: {
88
89
  discoveryApi: DiscoveryApi;
89
90
  identityApi: IdentityApi;
90
91
  scmIntegrationsApi: ScmIntegrationRegistry;
92
+ useLongPollingLogs?: boolean;
91
93
  });
92
94
  getIntegrationsList(options: {
93
95
  allowedHosts: string[];
@@ -106,10 +108,12 @@ declare class ScaffolderClient implements ScaffolderApi {
106
108
  */
107
109
  scaffold(templateName: string, values: Record<string, any>): Promise<string>;
108
110
  getTask(taskId: string): Promise<any>;
109
- streamLogs({ taskId, after, }: {
111
+ streamLogs(opts: {
110
112
  taskId: string;
111
113
  after?: number;
112
114
  }): Observable<LogEvent>;
115
+ private streamLogsEventStream;
116
+ private streamLogsPolling;
113
117
  /**
114
118
  * @returns ListActionsResponse containing all registered actions.
115
119
  */
@@ -126,7 +130,7 @@ declare type FieldExtensionOptions<T = any> = {
126
130
  };
127
131
 
128
132
  declare function createScaffolderFieldExtension<T = any>(options: FieldExtensionOptions<T>): Extension<() => null>;
129
- declare const ScaffolderFieldExtensions: React__default.ComponentType;
133
+ declare const ScaffolderFieldExtensions: react__default.ComponentType;
130
134
 
131
135
  declare const scaffolderPlugin: _backstage_core_plugin_api.BackstagePlugin<{
132
136
  root: _backstage_core_plugin_api.RouteRef<undefined>;
@@ -137,10 +141,15 @@ declare const EntityPickerFieldExtension: () => null;
137
141
  declare const EntityNamePickerFieldExtension: () => null;
138
142
  declare const RepoUrlPickerFieldExtension: () => null;
139
143
  declare const OwnerPickerFieldExtension: () => null;
140
- declare const ScaffolderPage: ({ TemplateCardComponent }: {
141
- TemplateCardComponent?: React.ComponentType<{
144
+ declare const ScaffolderPage: ({ TemplateCardComponent, groups }: {
145
+ TemplateCardComponent?: react.ComponentType<{
142
146
  template: _backstage_catalog_model.TemplateEntityV1beta2;
143
147
  }> | undefined;
148
+ groups?: {
149
+ title?: string | undefined;
150
+ titleComponent?: react.ReactNode;
151
+ filter: (entity: _backstage_catalog_model.Entity) => boolean;
152
+ }[] | undefined;
144
153
  }) => JSX.Element;
145
154
 
146
155
  declare const EntityNamePicker: ({ schema: { title, description }, ...props }: FieldProps<string>) => JSX.Element;
@@ -166,8 +175,13 @@ declare type TemplateListProps = {
166
175
  TemplateCardComponent?: ComponentType<{
167
176
  template: TemplateEntityV1beta2;
168
177
  }> | undefined;
178
+ group?: {
179
+ title?: string;
180
+ titleComponent?: react__default.ReactNode;
181
+ filter: (entity: Entity) => boolean;
182
+ };
169
183
  };
170
- declare const TemplateList: ({ TemplateCardComponent }: TemplateListProps) => JSX.Element;
184
+ declare const TemplateList: ({ TemplateCardComponent, group, }: TemplateListProps) => JSX.Element | null;
171
185
 
172
186
  declare const TemplateTypePicker: () => JSX.Element | null;
173
187
 
package/dist/index.esm.js CHANGED
@@ -1,9 +1,10 @@
1
- export { a as EntityNamePicker, j as EntityNamePickerFieldExtension, E as EntityPicker, i as EntityPickerFieldExtension, p as FavouriteTemplate, O as OwnerPicker, k as OwnerPickerFieldExtension, R as RepoUrlPicker, l as RepoUrlPickerFieldExtension, S as ScaffolderClient, h as ScaffolderFieldExtensions, m as ScaffolderPage, c as TemplateList, T as TemplateTypePicker, o as TextValuePicker, g as createScaffolderFieldExtension, n as plugin, s as scaffolderApiRef, n as scaffolderPlugin } from './esm/index-12a93d58.esm.js';
1
+ export { a as EntityNamePicker, j as EntityNamePickerFieldExtension, E as EntityPicker, i as EntityPickerFieldExtension, p as FavouriteTemplate, O as OwnerPicker, k as OwnerPickerFieldExtension, R as RepoUrlPicker, l as RepoUrlPickerFieldExtension, S as ScaffolderClient, h as ScaffolderFieldExtensions, m as ScaffolderPage, c as TemplateList, T as TemplateTypePicker, o as TextValuePicker, g as createScaffolderFieldExtension, n as plugin, s as scaffolderApiRef, n as scaffolderPlugin } from './esm/index-385b3fc1.esm.js';
2
2
  import '@backstage/catalog-model';
3
3
  import '@backstage/integration-react';
4
+ import '@backstage/core-plugin-api';
4
5
  import '@backstage/errors';
6
+ import 'qs';
5
7
  import 'zen-observable';
6
- import '@backstage/core-plugin-api';
7
8
  import '@backstage/plugin-catalog-react';
8
9
  import '@material-ui/core';
9
10
  import '@material-ui/core/FormControl';
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@backstage/plugin-scaffolder",
3
3
  "description": "The Backstage plugin that helps you create new things",
4
- "version": "0.11.9",
4
+ "version": "0.11.13",
5
5
  "main": "dist/index.esm.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "Apache-2.0",
@@ -31,30 +31,31 @@
31
31
  "clean": "backstage-cli clean"
32
32
  },
33
33
  "dependencies": {
34
- "@backstage/catalog-client": "^0.5.0",
35
- "@backstage/catalog-model": "^0.9.6",
34
+ "@backstage/catalog-client": "^0.5.2",
35
+ "@backstage/catalog-model": "^0.9.7",
36
36
  "@backstage/config": "^0.1.11",
37
- "@backstage/core-components": "^0.7.2",
38
- "@backstage/core-plugin-api": "^0.1.12",
39
- "@backstage/errors": "^0.1.4",
40
- "@backstage/integration": "^0.6.9",
41
- "@backstage/integration-react": "^0.1.13",
42
- "@backstage/plugin-catalog-react": "^0.6.2",
43
- "@backstage/theme": "^0.2.12",
37
+ "@backstage/core-components": "^0.7.6",
38
+ "@backstage/core-plugin-api": "^0.2.2",
39
+ "@backstage/errors": "^0.1.5",
40
+ "@backstage/integration": "^0.6.10",
41
+ "@backstage/integration-react": "^0.1.14",
42
+ "@backstage/plugin-catalog-react": "^0.6.4",
43
+ "@backstage/theme": "^0.2.14",
44
44
  "@backstage/types": "^0.1.1",
45
45
  "@material-ui/core": "^4.12.2",
46
46
  "@material-ui/icons": "^4.9.1",
47
47
  "@material-ui/lab": "4.0.0-alpha.57",
48
- "@rjsf/core": "^3.0.0",
49
- "@rjsf/material-ui": "^3.0.0",
48
+ "@rjsf/core": "^3.2.1",
49
+ "@rjsf/material-ui": "^3.2.1",
50
50
  "@types/react": "*",
51
51
  "classnames": "^2.2.6",
52
52
  "git-url-parse": "^11.6.0",
53
53
  "humanize-duration": "^3.25.1",
54
54
  "immer": "^9.0.1",
55
- "json-schema": "^0.3.0",
55
+ "json-schema": "^0.4.0",
56
56
  "lodash": "^4.17.21",
57
57
  "luxon": "^2.0.2",
58
+ "qs": "^6.9.4",
58
59
  "react": "^16.13.1",
59
60
  "react-dom": "^16.13.1",
60
61
  "react-lazylog": "^4.5.2",
@@ -65,10 +66,10 @@
65
66
  "zen-observable": "^0.8.15"
66
67
  },
67
68
  "devDependencies": {
68
- "@backstage/cli": "^0.8.1",
69
- "@backstage/core-app-api": "^0.1.19",
70
- "@backstage/dev-utils": "^0.2.12",
71
- "@backstage/test-utils": "^0.1.20",
69
+ "@backstage/cli": "^0.10.0",
70
+ "@backstage/core-app-api": "^0.1.24",
71
+ "@backstage/dev-utils": "^0.2.13",
72
+ "@backstage/test-utils": "^0.1.23",
72
73
  "@testing-library/jest-dom": "^5.10.1",
73
74
  "@testing-library/react": "^11.2.5",
74
75
  "@testing-library/react-hooks": "^7.0.2",
@@ -77,10 +78,11 @@
77
78
  "@types/jest": "^26.0.7",
78
79
  "@types/node": "^14.14.32",
79
80
  "cross-fetch": "^3.0.6",
81
+ "event-source-polyfill": "^1.0.25",
80
82
  "msw": "^0.35.0"
81
83
  },
82
84
  "files": [
83
85
  "dist"
84
86
  ],
85
- "gitHead": "3db0cb3683d3000666802af90a465ba4fb0d1e8d"
87
+ "gitHead": "a05e7081b805006e3f0b2960a08a7753357f532f"
86
88
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"Router-4bca4743.esm.js","sources":["../../src/extensions/default.ts","../../src/components/ScaffolderPage/ScaffolderPage.tsx","../../src/components/MultistepJsonForm/schema.ts","../../src/components/MultistepJsonForm/MultistepJsonForm.tsx","../../src/components/TemplatePage/TemplatePage.tsx","../../src/components/hooks/useEventStream.ts","../../src/components/TaskPage/IconLink.tsx","../../src/components/TaskPage/TaskPageLinks.tsx","../../src/components/TaskPage/TaskPage.tsx","../../src/components/ActionsPage/ActionsPage.tsx","../../src/components/Router.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { EntityPicker } from '../components/fields/EntityPicker';\nimport {\n EntityNamePicker,\n entityNamePickerValidation,\n} from '../components/fields/EntityNamePicker';\nimport { OwnerPicker } from '../components/fields/OwnerPicker';\nimport {\n repoPickerValidation,\n RepoUrlPicker,\n} from '../components/fields/RepoUrlPicker';\nimport { FieldExtensionOptions } from './types';\n\nexport const DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS: FieldExtensionOptions[] = [\n {\n component: EntityPicker,\n name: 'EntityPicker',\n },\n {\n component: EntityNamePicker,\n name: 'EntityNamePicker',\n validation: entityNamePickerValidation,\n },\n {\n component: RepoUrlPicker,\n name: 'RepoUrlPicker',\n validation: repoPickerValidation,\n },\n {\n component: OwnerPicker,\n name: 'OwnerPicker',\n },\n];\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Content,\n ContentHeader,\n CreateButton,\n Header,\n Lifecycle,\n Page,\n SupportButton,\n} from '@backstage/core-components';\nimport { TemplateEntityV1beta2 } from '@backstage/catalog-model';\nimport { useRouteRef } from '@backstage/core-plugin-api';\nimport {\n EntityKindPicker,\n EntityListProvider,\n EntitySearchBar,\n EntityTagPicker,\n UserListPicker,\n} from '@backstage/plugin-catalog-react';\nimport { makeStyles } from '@material-ui/core';\nimport React, { ComponentType } from 'react';\nimport { registerComponentRouteRef } from '../../routes';\nimport { TemplateList } from '../TemplateList';\nimport { TemplateTypePicker } from '../TemplateTypePicker';\n\nconst useStyles = makeStyles(theme => ({\n contentWrapper: {\n display: 'grid',\n gridTemplateAreas: \"'filters' 'grid'\",\n gridTemplateColumns: '250px 1fr',\n gridColumnGap: theme.spacing(2),\n },\n}));\n\nexport type ScaffolderPageProps = {\n TemplateCardComponent?:\n | ComponentType<{ template: TemplateEntityV1beta2 }>\n | undefined;\n};\n\nexport const ScaffolderPageContents = ({\n TemplateCardComponent,\n}: ScaffolderPageProps) => {\n const styles = useStyles();\n\n const registerComponentLink = useRouteRef(registerComponentRouteRef);\n\n return (\n <Page themeId=\"home\">\n <Header\n pageTitleOverride=\"Create a New Component\"\n title={\n <>\n Create a New Component <Lifecycle shorthand />\n </>\n }\n subtitle=\"Create new software components using standard templates\"\n />\n <Content>\n <ContentHeader title=\"Available Templates\">\n <CreateButton\n title=\"Register Existing Component\"\n to={registerComponentLink && registerComponentLink()}\n />\n <SupportButton>\n Create new software components using standard templates. Different\n templates create different kinds of components (services, websites,\n documentation, ...).\n </SupportButton>\n </ContentHeader>\n\n <div className={styles.contentWrapper}>\n <div>\n <EntitySearchBar />\n <EntityKindPicker initialFilter=\"template\" hidden />\n <UserListPicker\n initialFilter=\"all\"\n availableFilters={['all', 'starred']}\n />\n <TemplateTypePicker />\n <EntityTagPicker />\n </div>\n <div>\n <TemplateList TemplateCardComponent={TemplateCardComponent} />\n </div>\n </div>\n </Content>\n </Page>\n );\n};\n\nexport const ScaffolderPage = ({\n TemplateCardComponent,\n}: ScaffolderPageProps) => (\n <EntityListProvider>\n <ScaffolderPageContents TemplateCardComponent={TemplateCardComponent} />\n </EntityListProvider>\n);\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { JsonObject } from '@backstage/types';\nimport { FormProps } from '@rjsf/core';\n\nfunction isObject(value: unknown): value is JsonObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction extractUiSchema(schema: JsonObject, uiSchema: JsonObject) {\n if (!isObject(schema)) {\n return;\n }\n\n const { properties, items, anyOf, oneOf, allOf, dependencies } = schema;\n\n for (const propName in schema) {\n if (!schema.hasOwnProperty(propName)) {\n continue;\n }\n\n if (propName.startsWith('ui:')) {\n uiSchema[propName] = schema[propName];\n delete schema[propName];\n }\n }\n\n if (isObject(properties)) {\n for (const propName in properties) {\n if (!properties.hasOwnProperty(propName)) {\n continue;\n }\n\n const schemaNode = properties[propName];\n if (!isObject(schemaNode)) {\n continue;\n }\n const innerUiSchema = {};\n uiSchema[propName] = innerUiSchema;\n extractUiSchema(schemaNode, innerUiSchema);\n }\n }\n\n if (isObject(items)) {\n const innerUiSchema = {};\n uiSchema.items = innerUiSchema;\n extractUiSchema(items, innerUiSchema);\n }\n\n if (Array.isArray(anyOf)) {\n for (const schemaNode of anyOf) {\n if (!isObject(schemaNode)) {\n continue;\n }\n extractUiSchema(schemaNode, uiSchema);\n }\n }\n\n if (Array.isArray(oneOf)) {\n for (const schemaNode of oneOf) {\n if (!isObject(schemaNode)) {\n continue;\n }\n extractUiSchema(schemaNode, uiSchema);\n }\n }\n\n if (Array.isArray(allOf)) {\n for (const schemaNode of allOf) {\n if (!isObject(schemaNode)) {\n continue;\n }\n extractUiSchema(schemaNode, uiSchema);\n }\n }\n\n if (isObject(dependencies)) {\n for (const depName of Object.keys(dependencies)) {\n const schemaNode = dependencies[depName];\n if (!isObject(schemaNode)) {\n continue;\n }\n extractUiSchema(schemaNode, uiSchema);\n }\n }\n}\n\nexport function transformSchemaToProps(inputSchema: JsonObject): {\n schema: FormProps<any>['schema'];\n uiSchema: FormProps<any>['uiSchema'];\n} {\n inputSchema.type = inputSchema.type || 'object';\n const schema = JSON.parse(JSON.stringify(inputSchema));\n delete schema.title; // Rendered separately\n const uiSchema = {};\n extractUiSchema(schema, uiSchema);\n return { schema, uiSchema };\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { JsonObject } from '@backstage/types';\nimport {\n Box,\n Button,\n Paper,\n Step as StepUI,\n StepContent,\n StepLabel,\n Stepper,\n Typography,\n} from '@material-ui/core';\nimport { errorApiRef, useApi } from '@backstage/core-plugin-api';\nimport { FormProps, IChangeEvent, UiSchema, withTheme } from '@rjsf/core';\nimport { Theme as MuiTheme } from '@rjsf/material-ui';\nimport React, { useState } from 'react';\nimport { transformSchemaToProps } from './schema';\nimport { Content, StructuredMetadataTable } from '@backstage/core-components';\n\nconst Form = withTheme(MuiTheme);\ntype Step = {\n schema: JsonObject;\n title: string;\n} & Partial<Omit<FormProps<any>, 'schema'>>;\n\ntype Props = {\n /**\n * Steps for the form, each contains title and form schema\n */\n steps: Step[];\n formData: Record<string, any>;\n onChange: (e: IChangeEvent) => void;\n onReset: () => void;\n onFinish: () => Promise<void>;\n widgets?: FormProps<any>['widgets'];\n fields?: FormProps<any>['fields'];\n};\n\nexport function getUiSchemasFromSteps(steps: Step[]): UiSchema[] {\n const uiSchemas: Array<UiSchema> = [];\n steps.forEach(step => {\n const schemaProps = step.schema.properties as JsonObject;\n for (const key in schemaProps) {\n if (schemaProps.hasOwnProperty(key)) {\n const uiSchema = schemaProps[key] as UiSchema;\n uiSchema.name = key;\n uiSchemas.push(uiSchema);\n }\n }\n });\n return uiSchemas;\n}\n\nexport function getReviewData(formData: Record<string, any>, steps: Step[]) {\n const uiSchemas = getUiSchemasFromSteps(steps);\n const reviewData: Record<string, any> = {};\n for (const key in formData) {\n if (formData.hasOwnProperty(key)) {\n const uiSchema = uiSchemas.find(us => us.name === key);\n\n if (!uiSchema) {\n reviewData[key] = formData[key];\n continue;\n }\n\n if (uiSchema['ui:widget'] === 'password') {\n reviewData[key] = '******';\n continue;\n }\n\n if (!uiSchema['ui:backstage'] || !uiSchema['ui:backstage'].review) {\n reviewData[key] = formData[key];\n continue;\n }\n\n const review = uiSchema['ui:backstage'].review as JsonObject;\n if (!review.show) {\n continue;\n }\n\n if (review.mask) {\n reviewData[key] = review.mask;\n continue;\n }\n reviewData[key] = formData[key];\n }\n }\n\n return reviewData;\n}\n\nexport const MultistepJsonForm = ({\n steps,\n formData,\n onChange,\n onReset,\n onFinish,\n fields,\n widgets,\n}: Props) => {\n const [activeStep, setActiveStep] = useState(0);\n const [disableButtons, setDisableButtons] = useState(false);\n const errorApi = useApi(errorApiRef);\n\n const handleReset = () => {\n setActiveStep(0);\n onReset();\n };\n const handleNext = () => {\n setActiveStep(Math.min(activeStep + 1, steps.length));\n };\n const handleBack = () => setActiveStep(Math.max(activeStep - 1, 0));\n const handleCreate = async () => {\n setDisableButtons(true);\n try {\n await onFinish();\n } catch (err) {\n setDisableButtons(false);\n errorApi.post(err);\n }\n };\n\n return (\n <>\n <Stepper activeStep={activeStep} orientation=\"vertical\">\n {steps.map(({ title, schema, ...formProps }, index) => {\n return (\n <StepUI key={title}>\n <StepLabel\n aria-label={`Step ${index + 1} ${title}`}\n aria-disabled=\"false\"\n tabIndex={0}\n >\n <Typography variant=\"h6\" component=\"h3\">\n {title}\n </Typography>\n </StepLabel>\n <StepContent key={title}>\n <Form\n showErrorList={false}\n fields={fields}\n widgets={widgets}\n noHtml5Validate\n formData={formData}\n onChange={onChange}\n onSubmit={e => {\n if (e.errors.length === 0) handleNext();\n }}\n {...formProps}\n {...transformSchemaToProps(schema)}\n >\n <Button disabled={activeStep === 0} onClick={handleBack}>\n Back\n </Button>\n <Button variant=\"contained\" color=\"primary\" type=\"submit\">\n Next step\n </Button>\n </Form>\n </StepContent>\n </StepUI>\n );\n })}\n </Stepper>\n {activeStep === steps.length && (\n <Content>\n <Paper square elevation={0}>\n <Typography variant=\"h6\">Review and create</Typography>\n <StructuredMetadataTable\n dense\n metadata={getReviewData(formData, steps)}\n />\n <Box mb={4} />\n <Button onClick={handleBack} disabled={disableButtons}>\n Back\n </Button>\n <Button onClick={handleReset} disabled={disableButtons}>\n Reset\n </Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleCreate}\n disabled={disableButtons}\n >\n Create\n </Button>\n </Paper>\n </Content>\n )}\n </>\n );\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { JsonObject, JsonValue } from '@backstage/types';\nimport { LinearProgress } from '@material-ui/core';\nimport { FormValidation, IChangeEvent } from '@rjsf/core';\nimport React, { useCallback, useState } from 'react';\nimport { generatePath, Navigate, useNavigate } from 'react-router';\nimport { useParams } from 'react-router-dom';\nimport { useAsync } from 'react-use';\nimport { scaffolderApiRef } from '../../api';\nimport { CustomFieldValidator, FieldExtensionOptions } from '../../extensions';\nimport { rootRouteRef } from '../../routes';\nimport { MultistepJsonForm } from '../MultistepJsonForm';\n\nimport {\n Content,\n Header,\n InfoCard,\n Lifecycle,\n Page,\n} from '@backstage/core-components';\nimport {\n ApiHolder,\n errorApiRef,\n useApi,\n useApiHolder,\n useRouteRef,\n} from '@backstage/core-plugin-api';\n\nconst useTemplateParameterSchema = (templateName: string) => {\n const scaffolderApi = useApi(scaffolderApiRef);\n const { value, loading, error } = useAsync(\n () =>\n scaffolderApi.getTemplateParameterSchema({\n name: templateName,\n kind: 'template',\n namespace: 'default',\n }),\n [scaffolderApi, templateName],\n );\n return { schema: value, loading, error };\n};\n\nfunction isObject(obj: unknown): obj is JsonObject {\n return typeof obj === 'object' && obj !== null && !Array.isArray(obj);\n}\n\nexport const createValidator = (\n rootSchema: JsonObject,\n validators: Record<string, undefined | CustomFieldValidator<unknown>>,\n context: {\n apiHolder: ApiHolder;\n },\n) => {\n function validate(\n schema: JsonObject,\n formData: JsonObject,\n errors: FormValidation,\n ) {\n const schemaProps = schema.properties;\n if (!isObject(schemaProps)) {\n return;\n }\n\n for (const [key, propData] of Object.entries(formData)) {\n const propValidation = errors[key];\n\n if (isObject(propData)) {\n const propSchemaProps = schemaProps[key];\n if (isObject(propSchemaProps)) {\n validate(\n propSchemaProps,\n propData as JsonObject,\n propValidation as FormValidation,\n );\n }\n } else {\n const propSchema = schemaProps[key];\n const fieldName =\n isObject(propSchema) && (propSchema['ui:field'] as string);\n if (fieldName && typeof validators[fieldName] === 'function') {\n validators[fieldName]!(\n propData as JsonValue,\n propValidation,\n context,\n );\n }\n }\n }\n }\n\n return (formData: JsonObject, errors: FormValidation) => {\n validate(rootSchema, formData, errors);\n return errors;\n };\n};\n\nexport const TemplatePage = ({\n customFieldExtensions = [],\n}: {\n customFieldExtensions?: FieldExtensionOptions[];\n}) => {\n const apiHolder = useApiHolder();\n const errorApi = useApi(errorApiRef);\n const scaffolderApi = useApi(scaffolderApiRef);\n const { templateName } = useParams();\n const navigate = useNavigate();\n const rootLink = useRouteRef(rootRouteRef);\n const { schema, loading, error } = useTemplateParameterSchema(templateName);\n const [formState, setFormState] = useState({});\n const handleFormReset = () => setFormState({});\n const handleChange = useCallback(\n (e: IChangeEvent) => setFormState(e.formData),\n [setFormState],\n );\n\n const handleCreate = async () => {\n const id = await scaffolderApi.scaffold(templateName, formState);\n navigate(generatePath(`${rootLink()}/tasks/:taskId`, { taskId: id }));\n };\n\n if (error) {\n errorApi.post(new Error(`Failed to load template, ${error}`));\n return <Navigate to={rootLink()} />;\n }\n if (!loading && !schema) {\n errorApi.post(new Error('Template was not found.'));\n return <Navigate to={rootLink()} />;\n }\n\n const customFieldComponents = Object.fromEntries(\n customFieldExtensions.map(({ name, component }) => [name, component]),\n );\n\n const customFieldValidators = Object.fromEntries(\n customFieldExtensions.map(({ name, validation }) => [name, validation]),\n );\n\n return (\n <Page themeId=\"home\">\n <Header\n pageTitleOverride=\"Create a New Component\"\n title={\n <>\n Create a New Component <Lifecycle shorthand />\n </>\n }\n subtitle=\"Create new software components using standard templates\"\n />\n <Content>\n {loading && <LinearProgress data-testid=\"loading-progress\" />}\n {schema && (\n <InfoCard\n title={schema.title}\n noPadding\n titleTypographyProps={{ component: 'h2' }}\n >\n <MultistepJsonForm\n formData={formState}\n fields={customFieldComponents}\n onChange={handleChange}\n onReset={handleFormReset}\n onFinish={handleCreate}\n steps={schema.steps.map(step => {\n return {\n ...step,\n validate: createValidator(\n step.schema,\n customFieldValidators,\n { apiHolder },\n ),\n };\n })}\n />\n </InfoCard>\n )}\n </Content>\n </Page>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useImmerReducer } from 'use-immer';\nimport { useEffect } from 'react';\nimport { scaffolderApiRef, LogEvent } from '../../api';\nimport { ScaffolderTask, Status, TaskOutput } from '../../types';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { Subscription } from '@backstage/types';\n\ntype Step = {\n id: string;\n status: Status;\n endedAt?: string;\n startedAt?: string;\n};\n\nexport type TaskStream = {\n loading: boolean;\n error?: Error;\n stepLogs: { [stepId in string]: string[] };\n completed: boolean;\n task?: ScaffolderTask;\n steps: { [stepId in string]: Step };\n output?: TaskOutput;\n};\n\ntype ReducerLogEntry = {\n createdAt: string;\n body: {\n stepId?: string;\n status?: Status;\n message: string;\n output?: TaskOutput;\n };\n};\n\ntype ReducerAction =\n | { type: 'INIT'; data: ScaffolderTask }\n | { type: 'LOGS'; data: ReducerLogEntry[] }\n | { type: 'COMPLETED'; data: ReducerLogEntry }\n | { type: 'ERROR'; data: Error };\n\nfunction reducer(draft: TaskStream, action: ReducerAction) {\n switch (action.type) {\n case 'INIT': {\n draft.steps = action.data.spec.steps.reduce((current, next) => {\n current[next.id] = { status: 'open', id: next.id };\n return current;\n }, {} as { [stepId in string]: Step });\n draft.stepLogs = action.data.spec.steps.reduce((current, next) => {\n current[next.id] = [];\n return current;\n }, {} as { [stepId in string]: string[] });\n draft.loading = false;\n draft.error = undefined;\n draft.completed = false;\n draft.task = action.data;\n return;\n }\n\n case 'LOGS': {\n const entries = action.data;\n const logLines = [];\n\n for (const entry of entries) {\n const logLine = `${entry.createdAt} ${entry.body.message}`;\n logLines.push(logLine);\n\n if (!entry.body.stepId || !draft.steps?.[entry.body.stepId]) {\n continue;\n }\n\n const currentStepLog = draft.stepLogs?.[entry.body.stepId];\n const currentStep = draft.steps?.[entry.body.stepId];\n\n if (entry.body.status && entry.body.status !== currentStep.status) {\n currentStep.status = entry.body.status;\n\n if (currentStep.status === 'processing') {\n currentStep.startedAt = entry.createdAt;\n }\n\n if (\n ['cancelled', 'failed', 'completed'].includes(currentStep.status)\n ) {\n currentStep.endedAt = entry.createdAt;\n }\n }\n\n currentStepLog?.push(logLine);\n }\n\n return;\n }\n\n case 'COMPLETED': {\n draft.completed = true;\n draft.output = action.data.body.output;\n return;\n }\n\n case 'ERROR': {\n draft.error = action.data;\n draft.loading = false;\n draft.completed = true;\n return;\n }\n\n default:\n return;\n }\n}\n\nexport const useTaskEventStream = (taskId: string): TaskStream => {\n const scaffolderApi = useApi(scaffolderApiRef);\n const [state, dispatch] = useImmerReducer(reducer, {\n loading: true,\n completed: false,\n stepLogs: {} as { [stepId in string]: string[] },\n steps: {} as { [stepId in string]: Step },\n });\n\n useEffect(() => {\n let didCancel = false;\n let subscription: Subscription | undefined;\n let logPusher: NodeJS.Timeout | undefined;\n\n scaffolderApi.getTask(taskId).then(\n task => {\n if (didCancel) {\n return;\n }\n dispatch({ type: 'INIT', data: task });\n\n // TODO(blam): Use a normal fetch to fetch the current log for the event stream\n // and use that for an INIT_EVENTs dispatch event, and then\n // use the last event ID to subscribe using after option to\n // stream logs. Without this, if you have a lot of logs, it can look like the\n // task is being rebuilt on load as it progresses through the steps at a slower\n // rate whilst it builds the status from the event logs\n const observable = scaffolderApi.streamLogs({ taskId });\n\n const collectedLogEvents = new Array<LogEvent>();\n\n function emitLogs() {\n if (collectedLogEvents.length) {\n const logs = collectedLogEvents.splice(\n 0,\n collectedLogEvents.length,\n );\n dispatch({ type: 'LOGS', data: logs });\n }\n }\n\n logPusher = setInterval(emitLogs, 500);\n\n subscription = observable.subscribe({\n next: event => {\n switch (event.type) {\n case 'log':\n return collectedLogEvents.push(event);\n case 'completion':\n emitLogs();\n dispatch({ type: 'COMPLETED', data: event });\n return undefined;\n default:\n throw new Error(\n `Unhandled event type ${event.type} in observer`,\n );\n }\n },\n error: error => {\n emitLogs();\n dispatch({ type: 'ERROR', data: error });\n },\n });\n },\n error => {\n if (!didCancel) {\n dispatch({ type: 'ERROR', data: error });\n }\n },\n );\n\n return () => {\n didCancel = true;\n if (subscription) {\n subscription.unsubscribe();\n }\n if (logPusher) {\n clearInterval(logPusher);\n }\n };\n }, [scaffolderApi, dispatch, taskId]);\n\n return state;\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React from 'react';\nimport { Grid, LinkProps, makeStyles, Typography } from '@material-ui/core';\nimport LanguageIcon from '@material-ui/icons/Language';\n\nimport { IconComponent } from '@backstage/core-plugin-api';\nimport { Link } from '@backstage/core-components';\n\nconst useStyles = makeStyles({\n svgIcon: {\n display: 'inline-block',\n '& svg': {\n display: 'inline-block',\n fontSize: 'inherit',\n verticalAlign: 'baseline',\n },\n },\n});\n\nexport const IconLink = (\n props: {\n href: string;\n text?: string;\n Icon?: IconComponent;\n } & LinkProps,\n) => {\n const { href, text, Icon, ...linkProps } = props;\n\n const classes = useStyles();\n\n return (\n <Grid container direction=\"row\" spacing={1}>\n <Grid item>\n <Typography component=\"div\" className={classes.svgIcon}>\n {Icon ? <Icon /> : <LanguageIcon />}\n </Typography>\n </Grid>\n <Grid item>\n <Link to={href} {...linkProps}>\n {text || href}\n </Link>\n </Grid>\n </Grid>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { parseEntityName } from '@backstage/catalog-model';\nimport { entityRouteRef } from '@backstage/plugin-catalog-react';\nimport { Box } from '@material-ui/core';\nimport LanguageIcon from '@material-ui/icons/Language';\nimport React from 'react';\nimport { TaskOutput } from '../../types';\nimport { IconLink } from './IconLink';\nimport { IconComponent, useApp, useRouteRef } from '@backstage/core-plugin-api';\n\ntype TaskPageLinksProps = {\n output: TaskOutput;\n};\n\nexport const TaskPageLinks = ({ output }: TaskPageLinksProps) => {\n const { entityRef: entityRefOutput, remoteUrl } = output;\n let { links = [] } = output;\n const app = useApp();\n const entityRoute = useRouteRef(entityRouteRef);\n\n const iconResolver = (key?: string): IconComponent =>\n key ? app.getSystemIcon(key) ?? LanguageIcon : LanguageIcon;\n\n if (remoteUrl) {\n links = [{ url: remoteUrl, title: 'Repo' }, ...links];\n }\n\n if (entityRefOutput) {\n links = [\n {\n entityRef: entityRefOutput,\n title: 'Open in catalog',\n icon: 'catalog',\n },\n ...links,\n ];\n }\n\n return (\n <Box px={3} pb={3}>\n {links\n .filter(({ url, entityRef }) => url || entityRef)\n .map(({ url, entityRef, title, icon }) => {\n if (entityRef) {\n const entityName = parseEntityName(entityRef);\n const target = entityRoute(entityName);\n return { title, icon, url: target };\n }\n return { title, icon, url: url! };\n })\n .map(({ url, title, icon }, i) => (\n <IconLink\n key={`output-link-${i}`}\n href={url}\n text={title ?? url}\n Icon={iconResolver(icon)}\n target=\"_blank\"\n />\n ))}\n </Box>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useState, useEffect, memo, useMemo } from 'react';\nimport { makeStyles, Theme, createStyles } from '@material-ui/core/styles';\nimport Stepper from '@material-ui/core/Stepper';\nimport Step from '@material-ui/core/Step';\nimport StepLabel from '@material-ui/core/StepLabel';\nimport Grid from '@material-ui/core/Grid';\nimport Typography from '@material-ui/core/Typography';\nimport { useParams } from 'react-router';\nimport { useTaskEventStream } from '../hooks/useEventStream';\nimport LazyLog from 'react-lazylog/build/LazyLog';\nimport {\n CircularProgress,\n Paper,\n StepButton,\n StepIconProps,\n} from '@material-ui/core';\nimport { Status, TaskOutput } from '../../types';\nimport { DateTime, Interval } from 'luxon';\nimport { useInterval } from 'react-use';\nimport Check from '@material-ui/icons/Check';\nimport Cancel from '@material-ui/icons/Cancel';\nimport FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord';\nimport classNames from 'classnames';\nimport { BackstageTheme } from '@backstage/theme';\nimport { TaskPageLinks } from './TaskPageLinks';\nimport {\n Page,\n Header,\n Lifecycle,\n Content,\n ErrorPage,\n} from '@backstage/core-components';\n\n// typings are wrong for this library, so fallback to not parsing types.\nconst humanizeDuration = require('humanize-duration');\n\nconst useStyles = makeStyles((theme: Theme) =>\n createStyles({\n root: {\n width: '100%',\n },\n button: {\n marginTop: theme.spacing(1),\n marginRight: theme.spacing(1),\n },\n actionsContainer: {\n marginBottom: theme.spacing(2),\n },\n resetContainer: {\n padding: theme.spacing(3),\n },\n labelWrapper: {\n display: 'flex',\n flex: 1,\n flexDirection: 'row',\n justifyContent: 'space-between',\n },\n stepWrapper: {\n width: '100%',\n },\n }),\n);\n\ntype TaskStep = {\n id: string;\n name: string;\n status: Status;\n startedAt?: string;\n endedAt?: string;\n};\n\nconst StepTimeTicker = ({ step }: { step: TaskStep }) => {\n const [time, setTime] = useState('');\n\n useInterval(() => {\n if (!step.startedAt) {\n setTime('');\n return;\n }\n\n const end = step.endedAt\n ? DateTime.fromISO(step.endedAt)\n : DateTime.local();\n\n const startedAt = DateTime.fromISO(step.startedAt);\n const formatted = Interval.fromDateTimes(startedAt, end)\n .toDuration()\n .valueOf();\n\n setTime(humanizeDuration(formatted, { round: true }));\n }, 1000);\n\n return <Typography variant=\"caption\">{time}</Typography>;\n};\n\nconst useStepIconStyles = makeStyles((theme: BackstageTheme) =>\n createStyles({\n root: {\n color: theme.palette.text.disabled,\n display: 'flex',\n height: 22,\n alignItems: 'center',\n },\n completed: {\n color: theme.palette.status.ok,\n },\n error: {\n color: theme.palette.status.error,\n },\n }),\n);\n\nfunction TaskStepIconComponent(props: StepIconProps) {\n const classes = useStepIconStyles();\n const { active, completed, error } = props;\n\n const getMiddle = () => {\n if (active) {\n return <CircularProgress size=\"24px\" />;\n }\n if (completed) {\n return <Check />;\n }\n if (error) {\n return <Cancel />;\n }\n return <FiberManualRecordIcon />;\n };\n\n return (\n <div\n className={classNames(classes.root, {\n [classes.completed]: completed,\n [classes.error]: error,\n })}\n >\n {getMiddle()}\n </div>\n );\n}\n\nexport const TaskStatusStepper = memo(\n ({\n steps,\n currentStepId,\n onUserStepChange,\n }: {\n steps: TaskStep[];\n currentStepId: string | undefined;\n onUserStepChange: (id: string) => void;\n }) => {\n const classes = useStyles();\n\n return (\n <div className={classes.root}>\n <Stepper\n activeStep={steps.findIndex(s => s.id === currentStepId)}\n orientation=\"vertical\"\n nonLinear\n >\n {steps.map((step, index) => {\n const isCompleted = step.status === 'completed';\n const isFailed = step.status === 'failed';\n const isActive = step.status === 'processing';\n const isSkipped = step.status === 'skipped';\n\n return (\n <Step key={String(index)} expanded>\n <StepButton onClick={() => onUserStepChange(step.id)}>\n <StepLabel\n StepIconProps={{\n completed: isCompleted,\n error: isFailed,\n active: isActive,\n }}\n StepIconComponent={TaskStepIconComponent}\n className={classes.stepWrapper}\n >\n <div className={classes.labelWrapper}>\n <Typography variant=\"subtitle2\">{step.name}</Typography>\n {isSkipped ? (\n <Typography variant=\"caption\">Skipped</Typography>\n ) : (\n <StepTimeTicker step={step} />\n )}\n </div>\n </StepLabel>\n </StepButton>\n </Step>\n );\n })}\n </Stepper>\n </div>\n );\n },\n);\n\nconst TaskLogger = memo(({ log }: { log: string }) => {\n return (\n <div style={{ height: '80vh' }}>\n <LazyLog text={log} extraLines={1} follow selectableLines enableSearch />\n </div>\n );\n});\n\nconst hasLinks = ({ entityRef, remoteUrl, links = [] }: TaskOutput): boolean =>\n !!(entityRef || remoteUrl || links.length > 0);\n\nexport const TaskPage = () => {\n const [userSelectedStepId, setUserSelectedStepId] = useState<\n string | undefined\n >(undefined);\n const [lastActiveStepId, setLastActiveStepId] = useState<string | undefined>(\n undefined,\n );\n const { taskId } = useParams();\n const taskStream = useTaskEventStream(taskId);\n const completed = taskStream.completed;\n const steps = useMemo(\n () =>\n taskStream.task?.spec.steps.map(step => ({\n ...step,\n ...taskStream?.steps?.[step.id],\n })) ?? [],\n [taskStream],\n );\n\n useEffect(() => {\n const mostRecentFailedOrActiveStep = steps.find(step =>\n ['failed', 'processing'].includes(step.status),\n );\n if (completed && !mostRecentFailedOrActiveStep) {\n setLastActiveStepId(steps[steps.length - 1]?.id);\n return;\n }\n\n setLastActiveStepId(mostRecentFailedOrActiveStep?.id);\n }, [steps, completed]);\n\n const currentStepId = userSelectedStepId ?? lastActiveStepId;\n\n const logAsString = useMemo(() => {\n if (!currentStepId) {\n return 'Loading...';\n }\n const log = taskStream.stepLogs[currentStepId];\n\n if (!log?.length) {\n return 'Waiting for logs...';\n }\n return log.join('\\n');\n }, [taskStream.stepLogs, currentStepId]);\n\n const taskNotFound =\n taskStream.completed === true &&\n taskStream.loading === false &&\n !taskStream.task;\n\n const { output } = taskStream;\n\n return (\n <Page themeId=\"home\">\n <Header\n pageTitleOverride={`Task ${taskId}`}\n title={\n <>\n Task Activity <Lifecycle alpha shorthand />\n </>\n }\n subtitle={`Activity for task: ${taskId}`}\n />\n <Content>\n {taskNotFound ? (\n <ErrorPage\n status=\"404\"\n statusMessage=\"Task not found\"\n additionalInfo=\"No task found with this ID\"\n />\n ) : (\n <div>\n <Grid container>\n <Grid item xs={3}>\n <Paper>\n <TaskStatusStepper\n steps={steps}\n currentStepId={currentStepId}\n onUserStepChange={setUserSelectedStepId}\n />\n {output && hasLinks(output) && (\n <TaskPageLinks output={output} />\n )}\n </Paper>\n </Grid>\n <Grid item xs={9}>\n <TaskLogger log={logAsString} />\n </Grid>\n </Grid>\n </div>\n )}\n </Content>\n </Page>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport { useAsync } from 'react-use';\nimport { scaffolderApiRef } from '../../api';\nimport {\n Typography,\n Paper,\n Table,\n TableBody,\n Box,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n makeStyles,\n} from '@material-ui/core';\nimport { JSONSchema } from '@backstage/catalog-model';\nimport { JSONSchema7Definition } from 'json-schema';\nimport classNames from 'classnames';\n\nimport { useApi } from '@backstage/core-plugin-api';\nimport {\n Progress,\n Content,\n Header,\n Page,\n ErrorPage,\n} from '@backstage/core-components';\n\nconst useStyles = makeStyles(theme => ({\n code: {\n fontFamily: 'Menlo, monospace',\n padding: theme.spacing(1),\n backgroundColor:\n theme.palette.type === 'dark'\n ? theme.palette.grey[700]\n : theme.palette.grey[300],\n display: 'inline-block',\n borderRadius: 5,\n border: `1px solid ${theme.palette.grey[500]}`,\n position: 'relative',\n },\n\n codeRequired: {\n '&::after': {\n position: 'absolute',\n content: '\"*\"',\n top: 0,\n right: theme.spacing(0.5),\n fontWeight: 'bolder',\n color: theme.palette.error.light,\n },\n },\n}));\n\nexport const ActionsPage = () => {\n const api = useApi(scaffolderApiRef);\n const classes = useStyles();\n const { loading, value, error } = useAsync(async () => {\n return api.listActions();\n });\n\n if (loading) {\n return <Progress />;\n }\n\n if (error) {\n return (\n <ErrorPage\n statusMessage=\"Failed to load installed actions\"\n status=\"500\"\n />\n );\n }\n\n const formatRows = (input: JSONSchema) => {\n const properties = input.properties;\n if (!properties) {\n return undefined;\n }\n\n return Object.entries(properties).map(entry => {\n const [key] = entry;\n const props = entry[1] as unknown as JSONSchema;\n const codeClassname = classNames(classes.code, {\n [classes.codeRequired]: input.required?.includes(key),\n });\n\n return (\n <TableRow key={key}>\n <TableCell>\n <div className={codeClassname}>{key}</div>\n </TableCell>\n <TableCell>{props.title}</TableCell>\n <TableCell>{props.description}</TableCell>\n <TableCell>\n <span className={classes.code}>{props.type}</span>\n </TableCell>\n </TableRow>\n );\n });\n };\n\n const renderTable = (input: JSONSchema) => {\n if (!input.properties) {\n return undefined;\n }\n return (\n <TableContainer component={Paper}>\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Name</TableCell>\n <TableCell>Title</TableCell>\n <TableCell>Description</TableCell>\n <TableCell>Type</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>{formatRows(input)}</TableBody>\n </Table>\n </TableContainer>\n );\n };\n\n const renderTables = (name: string, input?: JSONSchema7Definition[]) => {\n if (!input) {\n return undefined;\n }\n\n return (\n <>\n <Typography variant=\"h6\">{name}</Typography>\n {input.map((i, index) => (\n <div key={index}>{renderTable(i as unknown as JSONSchema)}</div>\n ))}\n </>\n );\n };\n\n const items = value?.map(action => {\n if (action.id.startsWith('legacy:')) {\n return undefined;\n }\n\n const oneOf = renderTables('oneOf', action.schema?.input?.oneOf);\n return (\n <Box pb={4} key={action.id}>\n <Typography variant=\"h4\" className={classes.code}>\n {action.id}\n </Typography>\n <Typography>{action.description}</Typography>\n {action.schema?.input && (\n <Box pb={2}>\n <Typography variant=\"h5\">Input</Typography>\n {renderTable(action.schema.input)}\n {oneOf}\n </Box>\n )}\n {action.schema?.output && (\n <Box pb={2}>\n <Typography variant=\"h5\">Output</Typography>\n {renderTable(action.schema.output)}\n </Box>\n )}\n </Box>\n );\n });\n\n return (\n <Page themeId=\"home\">\n <Header\n pageTitleOverride=\"Create a New Component\"\n title=\"Installed actions\"\n subtitle=\"This is the collection of all installed actions\"\n />\n <Content>{items}</Content>\n </Page>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { ComponentType } from 'react';\nimport { Routes, Route, useOutlet } from 'react-router';\nimport { TemplateEntityV1beta2 } from '@backstage/catalog-model';\nimport { ScaffolderPage } from './ScaffolderPage';\nimport { TemplatePage } from './TemplatePage';\nimport { TaskPage } from './TaskPage';\nimport { ActionsPage } from './ActionsPage';\n\nimport {\n FieldExtensionOptions,\n FIELD_EXTENSION_WRAPPER_KEY,\n FIELD_EXTENSION_KEY,\n DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS,\n} from '../extensions';\nimport { useElementFilter } from '@backstage/core-plugin-api';\n\ntype RouterProps = {\n TemplateCardComponent?:\n | ComponentType<{ template: TemplateEntityV1beta2 }>\n | undefined;\n};\n\nexport const Router = ({ TemplateCardComponent }: RouterProps) => {\n const outlet = useOutlet();\n\n const customFieldExtensions = useElementFilter(outlet, elements =>\n elements\n .selectByComponentData({\n key: FIELD_EXTENSION_WRAPPER_KEY,\n })\n .findComponentData<FieldExtensionOptions>({\n key: FIELD_EXTENSION_KEY,\n }),\n );\n\n const fieldExtensions = [\n ...customFieldExtensions,\n ...DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS.filter(\n ({ name }) =>\n !customFieldExtensions.some(\n customFieldExtension => customFieldExtension.name === name,\n ),\n ),\n ];\n\n return (\n <Routes>\n <Route\n path=\"/\"\n element={\n <ScaffolderPage TemplateCardComponent={TemplateCardComponent} />\n }\n />\n <Route\n path=\"/templates/:templateName\"\n element={<TemplatePage customFieldExtensions={fieldExtensions} />}\n />\n <Route path=\"/tasks/:taskId\" element={<TaskPage />} />\n <Route path=\"/actions\" element={<ActionsPage />} />\n </Routes>\n );\n};\n"],"names":["useStyles","isObject","MuiTheme","StepUI","makeStyles","Typography","Stepper","Step","StepLabel","useParams","Grid"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-12a93d58.esm.js","sources":["../../src/api.ts","../../src/components/fields/EntityPicker/EntityPicker.tsx","../../src/components/fields/TextValuePicker/TextValuePicker.tsx","../../src/components/fields/EntityNamePicker/EntityNamePicker.tsx","../../src/components/fields/EntityNamePicker/validation.ts","../../src/components/fields/OwnerPicker/OwnerPicker.tsx","../../src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx","../../src/components/fields/RepoUrlPicker/validation.ts","../../src/extensions/index.tsx","../../src/routes.ts","../../src/plugin.ts","../../src/components/FavouriteTemplate/FavouriteTemplate.tsx","../../src/components/TemplateCard/TemplateCard.tsx","../../src/components/TemplateList/TemplateList.tsx","../../src/components/TemplateTypePicker/TemplateTypePicker.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { EntityName } from '@backstage/catalog-model';\nimport { JsonObject, JsonValue, Observable } from '@backstage/types';\nimport { ResponseError } from '@backstage/errors';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport { Field, FieldValidation } from '@rjsf/core';\nimport ObservableImpl from 'zen-observable';\nimport { ListActionsResponse, ScaffolderTask, Status } from './types';\nimport {\n createApiRef,\n DiscoveryApi,\n IdentityApi,\n} from '@backstage/core-plugin-api';\n\nexport const scaffolderApiRef = createApiRef<ScaffolderApi>({\n id: 'plugin.scaffolder.service',\n description: 'Used to make requests towards the scaffolder backend',\n});\n\ntype TemplateParameterSchema = {\n title: string;\n steps: Array<{\n title: string;\n schema: JsonObject;\n }>;\n};\n\nexport type LogEvent = {\n type: 'log' | 'completion';\n body: {\n message: string;\n stepId?: string;\n status?: Status;\n };\n createdAt: string;\n id: string;\n taskId: string;\n};\n\nexport type CustomField = {\n name: string;\n component: Field;\n validation: (data: JsonValue, field: FieldValidation) => void;\n};\n\nexport interface ScaffolderApi {\n getTemplateParameterSchema(\n templateName: EntityName,\n ): Promise<TemplateParameterSchema>;\n\n /**\n * Executes the scaffolding of a component, given a template and its\n * parameter values.\n *\n * @param templateName Name of the Template entity for the scaffolder to use. New project is going to be created out of this template.\n * @param values Parameters for the template, e.g. name, description\n */\n scaffold(templateName: string, values: Record<string, any>): Promise<string>;\n\n getTask(taskId: string): Promise<ScaffolderTask>;\n\n getIntegrationsList(options: {\n allowedHosts: string[];\n }): Promise<{ type: string; title: string; host: string }[]>;\n\n // Returns a list of all installed actions.\n listActions(): Promise<ListActionsResponse>;\n\n streamLogs({\n taskId,\n after,\n }: {\n taskId: string;\n after?: number;\n }): Observable<LogEvent>;\n}\n\nexport class ScaffolderClient implements ScaffolderApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly identityApi: IdentityApi;\n private readonly scmIntegrationsApi: ScmIntegrationRegistry;\n\n constructor(options: {\n discoveryApi: DiscoveryApi;\n identityApi: IdentityApi;\n scmIntegrationsApi: ScmIntegrationRegistry;\n }) {\n this.discoveryApi = options.discoveryApi;\n this.identityApi = options.identityApi;\n this.scmIntegrationsApi = options.scmIntegrationsApi;\n }\n\n async getIntegrationsList(options: { allowedHosts: string[] }) {\n return [\n ...this.scmIntegrationsApi.azure.list(),\n ...this.scmIntegrationsApi.bitbucket.list(),\n ...this.scmIntegrationsApi.github.list(),\n ...this.scmIntegrationsApi.gitlab.list(),\n ]\n .map(c => ({ type: c.type, title: c.title, host: c.config.host }))\n .filter(c => options.allowedHosts.includes(c.host));\n }\n\n async getTemplateParameterSchema(\n templateName: EntityName,\n ): Promise<TemplateParameterSchema> {\n const { namespace, kind, name } = templateName;\n\n const token = await this.identityApi.getIdToken();\n const baseUrl = await this.discoveryApi.getBaseUrl('scaffolder');\n const templatePath = [namespace, kind, name]\n .map(s => encodeURIComponent(s))\n .join('/');\n const url = `${baseUrl}/v2/templates/${templatePath}/parameter-schema`;\n\n const response = await fetch(url, {\n headers: {\n ...(token && { Authorization: `Bearer ${token}` }),\n },\n });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n const schema: TemplateParameterSchema = await response.json();\n return schema;\n }\n\n /**\n * Executes the scaffolding of a component, given a template and its\n * parameter values.\n *\n * @param templateName Template name for the scaffolder to use. New project is going to be created out of this template.\n * @param values Parameters for the template, e.g. name, description\n */\n async scaffold(\n templateName: string,\n values: Record<string, any>,\n ): Promise<string> {\n const token = await this.identityApi.getIdToken();\n const url = `${await this.discoveryApi.getBaseUrl('scaffolder')}/v2/tasks`;\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(token && { Authorization: `Bearer ${token}` }),\n },\n body: JSON.stringify({ templateName, values: { ...values } }),\n });\n\n if (response.status !== 201) {\n const status = `${response.status} ${response.statusText}`;\n const body = await response.text();\n throw new Error(`Backend request failed, ${status} ${body.trim()}`);\n }\n\n const { id } = (await response.json()) as { id: string };\n return id;\n }\n\n async getTask(taskId: string) {\n const token = await this.identityApi.getIdToken();\n const baseUrl = await this.discoveryApi.getBaseUrl('scaffolder');\n const url = `${baseUrl}/v2/tasks/${encodeURIComponent(taskId)}`;\n const response = await fetch(url, {\n headers: token ? { Authorization: `Bearer ${token}` } : {},\n });\n\n if (!response.ok) {\n throw ResponseError.fromResponse(response);\n }\n\n return await response.json();\n }\n\n streamLogs({\n taskId,\n after,\n }: {\n taskId: string;\n after?: number;\n }): Observable<LogEvent> {\n return new ObservableImpl(subscriber => {\n const params = new URLSearchParams();\n if (after !== undefined) {\n params.set('after', String(Number(after)));\n }\n\n this.discoveryApi.getBaseUrl('scaffolder').then(\n baseUrl => {\n const url = `${baseUrl}/v2/tasks/${encodeURIComponent(\n taskId,\n )}/eventstream`;\n const eventSource = new EventSource(url, { withCredentials: true });\n eventSource.addEventListener('log', (event: any) => {\n if (event.data) {\n try {\n subscriber.next(JSON.parse(event.data));\n } catch (ex) {\n subscriber.error(ex);\n }\n }\n });\n eventSource.addEventListener('completion', (event: any) => {\n if (event.data) {\n try {\n subscriber.next(JSON.parse(event.data));\n } catch (ex) {\n subscriber.error(ex);\n }\n }\n eventSource.close();\n subscriber.complete();\n });\n eventSource.addEventListener('error', event => {\n subscriber.error(event);\n });\n },\n error => {\n subscriber.error(error);\n },\n );\n });\n }\n\n /**\n * @returns ListActionsResponse containing all registered actions.\n */\n async listActions(): Promise<ListActionsResponse> {\n const baseUrl = await this.discoveryApi.getBaseUrl('scaffolder');\n const token = await this.identityApi.getIdToken();\n const response = await fetch(`${baseUrl}/v2/actions`, {\n headers: token ? { Authorization: `Bearer ${token}` } : {},\n });\n\n if (!response.ok) {\n throw ResponseError.fromResponse(response);\n }\n\n return await response.json();\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useApi } from '@backstage/core-plugin-api';\nimport {\n catalogApiRef,\n formatEntityRefTitle,\n} from '@backstage/plugin-catalog-react';\nimport { TextField } from '@material-ui/core';\nimport FormControl from '@material-ui/core/FormControl';\nimport Autocomplete from '@material-ui/lab/Autocomplete';\nimport { FieldProps } from '@rjsf/core';\nimport React from 'react';\nimport { useAsync } from 'react-use';\n\nexport const EntityPicker = ({\n onChange,\n schema: { title = 'Entity', description = 'An entity from the catalog' },\n required,\n uiSchema,\n rawErrors,\n formData,\n idSchema,\n}: FieldProps<string>) => {\n const allowedKinds = uiSchema['ui:options']?.allowedKinds as string[];\n const defaultKind = uiSchema['ui:options']?.defaultKind as string | undefined;\n const catalogApi = useApi(catalogApiRef);\n\n const { value: entities, loading } = useAsync(() =>\n catalogApi.getEntities(\n allowedKinds ? { filter: { kind: allowedKinds } } : undefined,\n ),\n );\n\n const entityRefs = entities?.items.map(e =>\n formatEntityRefTitle(e, { defaultKind }),\n );\n\n const onSelect = (_: any, value: string | null) => {\n onChange(value || '');\n };\n\n return (\n <FormControl\n margin=\"normal\"\n required={required}\n error={rawErrors?.length > 0 && !formData}\n >\n <Autocomplete\n id={idSchema?.$id}\n value={(formData as string) || ''}\n loading={loading}\n onChange={onSelect}\n options={entityRefs || []}\n autoSelect\n freeSolo\n renderInput={params => (\n <TextField\n {...params}\n label={title}\n margin=\"normal\"\n helperText={description}\n variant=\"outlined\"\n required={required}\n InputProps={params.InputProps}\n />\n )}\n />\n </FormControl>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { TextField } from '@material-ui/core';\nimport { FieldProps } from '@rjsf/core';\nimport React from 'react';\n\nexport const TextValuePicker = ({\n onChange,\n required,\n schema: { title, description },\n rawErrors,\n formData,\n uiSchema: { 'ui:autofocus': autoFocus },\n idSchema,\n placeholder,\n}: FieldProps<string>) => (\n <TextField\n id={idSchema?.$id}\n label={title}\n placeholder={placeholder}\n helperText={description}\n required={required}\n value={formData ?? ''}\n onChange={({ target: { value } }) => onChange(value)}\n margin=\"normal\"\n error={rawErrors?.length > 0 && !formData}\n inputProps={{ autoFocus }}\n />\n);\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport { FieldProps } from '@rjsf/core';\nimport { TextValuePicker } from '../TextValuePicker';\n\nexport const EntityNamePicker = ({\n schema: { title = 'Name', description = 'Unique name of the component' },\n ...props\n}: FieldProps<string>) => (\n <TextValuePicker schema={{ title, description }} {...props} />\n);\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { FieldValidation } from '@rjsf/core';\nimport { KubernetesValidatorFunctions } from '@backstage/catalog-model';\n\nexport const entityNamePickerValidation = (\n value: string,\n validation: FieldValidation,\n) => {\n if (!KubernetesValidatorFunctions.isValidObjectName(value)) {\n validation.addError(\n 'must start and end with an alphanumeric character, and contain only alphanumeric characters, hyphens, underscores, and periods. Maximum length is 63 characters.',\n );\n }\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { FieldProps } from '@rjsf/core';\nimport React from 'react';\nimport { EntityPicker } from '../EntityPicker';\n\nexport const OwnerPicker = ({\n schema: { title = 'Owner', description = 'The owner of the component' },\n uiSchema,\n ...props\n}: FieldProps<string>) => {\n const ownerUiSchema = {\n ...uiSchema,\n 'ui:options': {\n allowedKinds: (uiSchema['ui:options']?.allowedKinds || [\n 'Group',\n 'User',\n ]) as string[],\n defaultKind: 'Group',\n },\n };\n\n return (\n <EntityPicker\n {...props}\n schema={{ title, description }}\n uiSchema={ownerUiSchema}\n />\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React, { useCallback, useEffect } from 'react';\nimport { FieldProps } from '@rjsf/core';\nimport { scaffolderApiRef } from '../../../api';\nimport { scmIntegrationsApiRef } from '@backstage/integration-react';\nimport { useAsync } from 'react-use';\nimport Select from '@material-ui/core/Select';\nimport InputLabel from '@material-ui/core/InputLabel';\nimport Input from '@material-ui/core/Input';\nimport FormControl from '@material-ui/core/FormControl';\nimport FormHelperText from '@material-ui/core/FormHelperText';\n\nimport { useApi } from '@backstage/core-plugin-api';\nimport { Progress } from '@backstage/core-components';\n\nfunction splitFormData(url: string | undefined) {\n let host = undefined;\n let owner = undefined;\n let repo = undefined;\n let organization = undefined;\n let workspace = undefined;\n let project = undefined;\n\n try {\n if (url) {\n const parsed = new URL(`https://${url}`);\n host = parsed.host;\n owner = parsed.searchParams.get('owner') || undefined;\n repo = parsed.searchParams.get('repo') || undefined;\n // This is azure dev ops specific. not used for any other provider.\n organization = parsed.searchParams.get('organization') || undefined;\n // These are bitbucket specific, not used for any other provider.\n workspace = parsed.searchParams.get('workspace') || undefined;\n project = parsed.searchParams.get('project') || undefined;\n }\n } catch {\n /* ok */\n }\n\n return { host, owner, repo, organization, workspace, project };\n}\n\nfunction serializeFormData(data: {\n host?: string;\n owner?: string;\n repo?: string;\n organization?: string;\n workspace?: string;\n project?: string;\n}) {\n if (!data.host) {\n return undefined;\n }\n\n const params = new URLSearchParams();\n if (data.owner) {\n params.set('owner', data.owner);\n }\n if (data.repo) {\n params.set('repo', data.repo);\n }\n if (data.organization) {\n params.set('organization', data.organization);\n }\n if (data.workspace) {\n params.set('workspace', data.workspace);\n }\n if (data.project) {\n params.set('project', data.project);\n }\n\n return `${data.host}?${params.toString()}`;\n}\n\nexport const RepoUrlPicker = ({\n onChange,\n uiSchema,\n rawErrors,\n formData,\n}: FieldProps<string>) => {\n const scaffolderApi = useApi(scaffolderApiRef);\n const integrationApi = useApi(scmIntegrationsApiRef);\n const allowedHosts = uiSchema['ui:options']?.allowedHosts as string[];\n\n const { value: integrations, loading } = useAsync(async () => {\n return await scaffolderApi.getIntegrationsList({ allowedHosts });\n });\n\n const { host, owner, repo, organization, workspace, project } =\n splitFormData(formData);\n const updateHost = useCallback(\n (evt: React.ChangeEvent<{ name?: string; value: unknown }>) => {\n onChange(\n serializeFormData({\n host: evt.target.value as string,\n owner,\n repo,\n organization,\n workspace,\n project,\n }),\n );\n },\n [onChange, owner, repo, organization, workspace, project],\n );\n\n const updateOwner = useCallback(\n (evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>\n onChange(\n serializeFormData({\n host,\n owner: evt.target.value as string,\n repo,\n organization,\n workspace,\n project,\n }),\n ),\n [onChange, host, repo, organization, workspace, project],\n );\n\n const updateRepo = useCallback(\n (evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>\n onChange(\n serializeFormData({\n host,\n owner,\n repo: evt.target.value as string,\n organization,\n workspace,\n project,\n }),\n ),\n [onChange, host, owner, organization, workspace, project],\n );\n\n const updateOrganization = useCallback(\n (evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>\n onChange(\n serializeFormData({\n host,\n owner,\n repo,\n organization: evt.target.value as string,\n workspace,\n project,\n }),\n ),\n [onChange, host, owner, repo, workspace, project],\n );\n\n const updateWorkspace = useCallback(\n (evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>\n onChange(\n serializeFormData({\n host,\n owner,\n repo,\n organization,\n workspace: evt.target.value as string,\n project,\n }),\n ),\n [onChange, host, owner, repo, organization, project],\n );\n\n const updateProject = useCallback(\n (evt: React.ChangeEvent<{ name?: string; value: unknown }>) =>\n onChange(\n serializeFormData({\n host,\n owner,\n repo,\n organization,\n workspace,\n project: evt.target.value as string,\n }),\n ),\n [onChange, host, owner, repo, organization, workspace],\n );\n\n useEffect(() => {\n if (host === undefined && integrations?.length) {\n onChange(\n serializeFormData({\n host: integrations[0].host,\n owner,\n repo,\n organization,\n workspace,\n project,\n }),\n );\n }\n }, [\n onChange,\n integrations,\n host,\n owner,\n repo,\n organization,\n workspace,\n project,\n ]);\n\n if (loading) {\n return <Progress />;\n }\n\n return (\n <>\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !host}\n >\n <InputLabel htmlFor=\"hostInput\">Host</InputLabel>\n <Select native id=\"hostInput\" onChange={updateHost} value={host}>\n {integrations ? (\n integrations\n .filter(i => allowedHosts?.includes(i.host))\n .map(i => (\n <option key={i.host} value={i.host}>\n {i.title}\n </option>\n ))\n ) : (\n <p>loading</p>\n )}\n </Select>\n <FormHelperText>\n The host where the repository will be created\n </FormHelperText>\n </FormControl>\n {/* Show this for dev.azure.com only */}\n {host === 'dev.azure.com' && (\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !organization}\n >\n <InputLabel htmlFor=\"repoInput\">Organization</InputLabel>\n <Input\n id=\"repoInput\"\n onChange={updateOrganization}\n value={organization}\n />\n <FormHelperText>The name of the organization</FormHelperText>\n </FormControl>\n )}\n {host && integrationApi.byHost(host)?.type === 'bitbucket' && (\n <>\n {/* Show this for bitbucket.org only */}\n {host === 'bitbucket.org' && (\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !workspace}\n >\n <InputLabel htmlFor=\"wokrspaceInput\">Workspace</InputLabel>\n <Input\n id=\"wokrspaceInput\"\n onChange={updateWorkspace}\n value={workspace}\n />\n <FormHelperText>\n The workspace where the repository will be created\n </FormHelperText>\n </FormControl>\n )}\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !project}\n >\n <InputLabel htmlFor=\"wokrspaceInput\">Project</InputLabel>\n <Input\n id=\"wokrspaceInput\"\n onChange={updateProject}\n value={project}\n />\n <FormHelperText>\n The project where the repository will be created\n </FormHelperText>\n </FormControl>\n </>\n )}\n {/* Show this for all hosts except bitbucket */}\n {host && integrationApi.byHost(host)?.type !== 'bitbucket' && (\n <>\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !owner}\n >\n <InputLabel htmlFor=\"ownerInput\">Owner</InputLabel>\n <Input id=\"ownerInput\" onChange={updateOwner} value={owner} />\n <FormHelperText>\n The organization, user or project that this repo will belong to\n </FormHelperText>\n </FormControl>\n </>\n )}\n {/* Show this for all hosts */}\n <FormControl\n margin=\"normal\"\n required\n error={rawErrors?.length > 0 && !repo}\n >\n <InputLabel htmlFor=\"repoInput\">Repository</InputLabel>\n <Input id=\"repoInput\" onChange={updateRepo} value={repo} />\n <FormHelperText>The name of the repository</FormHelperText>\n </FormControl>\n </>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { FieldValidation } from '@rjsf/core';\nimport { ApiHolder } from '@backstage/core-plugin-api';\nimport { scmIntegrationsApiRef } from '@backstage/integration-react';\n\nexport const repoPickerValidation = (\n value: string,\n validation: FieldValidation,\n context: { apiHolder: ApiHolder },\n) => {\n try {\n const { host, searchParams } = new URL(`https://${value}`);\n\n const integrationApi = context.apiHolder.get(scmIntegrationsApiRef);\n\n if (!host) {\n validation.addError(\n 'Incomplete repository location provided, host not provided',\n );\n } else {\n if (integrationApi?.byHost(host)?.type === 'bitbucket') {\n // workspace is only applicable for bitbucket cloud\n if (host === 'bitbucket.org' && !searchParams.get('workspace')) {\n validation.addError(\n 'Incomplete repository location provided, workspace not provided',\n );\n }\n\n if (!searchParams.get('project')) {\n validation.addError(\n 'Incomplete repository location provided, project not provided',\n );\n }\n }\n // For anything other than bitbucket\n else {\n if (!searchParams.get('owner')) {\n validation.addError(\n 'Incomplete repository location provided, owner not provided',\n );\n }\n }\n\n // Do this for all hosts\n if (!searchParams.get('repo')) {\n validation.addError(\n 'Incomplete repository location provided, repo not provided',\n );\n }\n }\n } catch {\n validation.addError('Unable to parse the Repository URL');\n }\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React from 'react';\nimport { CustomFieldValidator, FieldExtensionOptions } from './types';\nimport { Extension, attachComponentData } from '@backstage/core-plugin-api';\n\nexport const FIELD_EXTENSION_WRAPPER_KEY = 'scaffolder.extensions.wrapper.v1';\nexport const FIELD_EXTENSION_KEY = 'scaffolder.extensions.field.v1';\n\nexport function createScaffolderFieldExtension<T = any>(\n options: FieldExtensionOptions<T>,\n): Extension<() => null> {\n return {\n expose() {\n const FieldExtensionDataHolder: any = () => null;\n\n attachComponentData(\n FieldExtensionDataHolder,\n FIELD_EXTENSION_KEY,\n options,\n );\n\n return FieldExtensionDataHolder;\n },\n };\n}\n\nexport const ScaffolderFieldExtensions: React.ComponentType = () => null;\nattachComponentData(\n ScaffolderFieldExtensions,\n FIELD_EXTENSION_WRAPPER_KEY,\n true,\n);\n\nexport type { CustomFieldValidator, FieldExtensionOptions };\n\nexport { DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS } from './default';\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n createExternalRouteRef,\n createRouteRef,\n} from '@backstage/core-plugin-api';\n\nexport const registerComponentRouteRef = createExternalRouteRef({\n id: 'register-component',\n optional: true,\n});\n\nexport const rootRouteRef = createRouteRef({\n title: 'Create new entity',\n});\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { scmIntegrationsApiRef } from '@backstage/integration-react';\nimport { scaffolderApiRef, ScaffolderClient } from './api';\nimport { EntityPicker } from './components/fields/EntityPicker';\nimport {\n entityNamePickerValidation,\n EntityNamePicker,\n} from './components/fields/EntityNamePicker';\nimport { OwnerPicker } from './components/fields/OwnerPicker';\nimport {\n repoPickerValidation,\n RepoUrlPicker,\n} from './components/fields/RepoUrlPicker';\nimport { createScaffolderFieldExtension } from './extensions';\nimport { registerComponentRouteRef, rootRouteRef } from './routes';\nimport {\n createApiFactory,\n createPlugin,\n createRoutableExtension,\n discoveryApiRef,\n identityApiRef,\n} from '@backstage/core-plugin-api';\n\nexport const scaffolderPlugin = createPlugin({\n id: 'scaffolder',\n apis: [\n createApiFactory({\n api: scaffolderApiRef,\n deps: {\n discoveryApi: discoveryApiRef,\n identityApi: identityApiRef,\n scmIntegrationsApi: scmIntegrationsApiRef,\n },\n factory: ({ discoveryApi, identityApi, scmIntegrationsApi }) =>\n new ScaffolderClient({ discoveryApi, identityApi, scmIntegrationsApi }),\n }),\n ],\n routes: {\n root: rootRouteRef,\n },\n externalRoutes: {\n registerComponent: registerComponentRouteRef,\n },\n});\n\nexport const EntityPickerFieldExtension = scaffolderPlugin.provide(\n createScaffolderFieldExtension({\n component: EntityPicker,\n name: 'EntityPicker',\n }),\n);\n\nexport const EntityNamePickerFieldExtension = scaffolderPlugin.provide(\n createScaffolderFieldExtension({\n component: EntityNamePicker,\n name: 'EntityNamePicker',\n validation: entityNamePickerValidation,\n }),\n);\n\nexport const RepoUrlPickerFieldExtension = scaffolderPlugin.provide(\n createScaffolderFieldExtension({\n component: RepoUrlPicker,\n name: 'RepoUrlPicker',\n validation: repoPickerValidation,\n }),\n);\n\nexport const OwnerPickerFieldExtension = scaffolderPlugin.provide(\n createScaffolderFieldExtension({\n component: OwnerPicker,\n name: 'OwnerPicker',\n }),\n);\n\nexport const ScaffolderPage = scaffolderPlugin.provide(\n createRoutableExtension({\n name: 'ScaffolderPage',\n component: () => import('./components/Router').then(m => m.Router),\n mountPoint: rootRouteRef,\n }),\n);\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { useStarredEntity } from '@backstage/plugin-catalog-react';\nimport { IconButton, makeStyles, Tooltip, withStyles } from '@material-ui/core';\nimport Star from '@material-ui/icons/Star';\nimport StarBorder from '@material-ui/icons/StarBorder';\nimport React, { ComponentProps } from 'react';\n\ntype Props = ComponentProps<typeof IconButton> & { entity: Entity };\n\nconst YellowStar = withStyles({\n root: {\n color: '#f3ba37',\n },\n})(Star);\n\nconst WhiteBorderStar = withStyles({\n root: {\n color: '#ffffff',\n },\n})(StarBorder);\n\nconst useStyles = makeStyles(theme => ({\n starButton: {\n position: 'absolute',\n top: theme.spacing(0.5),\n right: theme.spacing(0.5),\n padding: '0.25rem',\n },\n}));\n\nexport const favouriteTemplateTooltip = (isStarred: boolean) =>\n isStarred ? 'Remove from favorites' : 'Add to favorites';\n\nexport const favouriteTemplateIcon = (isStarred: boolean) =>\n isStarred ? <YellowStar /> : <WhiteBorderStar />;\n\n/**\n * IconButton for showing if a current entity is starred and adding/removing it from the favourite entities\n * @param props MaterialUI IconButton props extended by required `entity` prop\n */\nexport const FavouriteTemplate = (props: Props) => {\n const classes = useStyles();\n const { toggleStarredEntity, isStarredEntity } = useStarredEntity(\n props.entity,\n );\n return (\n <IconButton\n color=\"inherit\"\n className={classes.starButton}\n {...props}\n onClick={() => toggleStarredEntity()}\n >\n <Tooltip title={favouriteTemplateTooltip(isStarredEntity)}>\n {favouriteTemplateIcon(isStarredEntity)}\n </Tooltip>\n </IconButton>\n );\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n Entity,\n RELATION_OWNED_BY,\n TemplateEntityV1beta2,\n} from '@backstage/catalog-model';\nimport {\n ScmIntegrationIcon,\n scmIntegrationsApiRef,\n} from '@backstage/integration-react';\nimport {\n EntityRefLinks,\n getEntityRelations,\n getEntitySourceLocation,\n} from '@backstage/plugin-catalog-react';\nimport { BackstageTheme } from '@backstage/theme';\nimport {\n Box,\n Card,\n CardActions,\n CardContent,\n CardMedia,\n Chip,\n IconButton,\n Link,\n makeStyles,\n Tooltip,\n Typography,\n useTheme,\n} from '@material-ui/core';\nimport WarningIcon from '@material-ui/icons/Warning';\nimport React from 'react';\nimport { generatePath } from 'react-router';\nimport { rootRouteRef } from '../../routes';\nimport { FavouriteTemplate } from '../FavouriteTemplate/FavouriteTemplate';\n\nimport { Button, ItemCardHeader } from '@backstage/core-components';\nimport { useApi, useRouteRef } from '@backstage/core-plugin-api';\n\nconst useStyles = makeStyles(theme => ({\n cardHeader: {\n position: 'relative',\n },\n title: {\n backgroundImage: ({ backgroundImage }: any) => backgroundImage,\n },\n box: {\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n display: '-webkit-box',\n '-webkit-line-clamp': 10,\n '-webkit-box-orient': 'vertical',\n paddingBottom: '0.8em',\n },\n label: {\n color: theme.palette.text.secondary,\n textTransform: 'uppercase',\n fontSize: '0.65rem',\n fontWeight: 'bold',\n letterSpacing: 0.5,\n lineHeight: 1,\n paddingBottom: '0.2rem',\n },\n leftButton: {\n marginRight: 'auto',\n },\n}));\n\nconst useDeprecationStyles = makeStyles(theme => ({\n deprecationIcon: {\n position: 'absolute',\n top: theme.spacing(0.5),\n right: theme.spacing(3.5),\n padding: '0.25rem',\n },\n link: {\n color: theme.palette.warning.light,\n },\n}));\n\nexport type TemplateCardProps = {\n template: TemplateEntityV1beta2;\n deprecated?: boolean;\n};\n\ntype TemplateProps = {\n description: string;\n tags: string[];\n title: string;\n type: string;\n name: string;\n};\n\nconst getTemplateCardProps = (\n template: TemplateEntityV1beta2,\n): TemplateProps & { key: string } => {\n return {\n key: template.metadata.uid!,\n name: template.metadata.name,\n title: `${(template.metadata.title || template.metadata.name) ?? ''}`,\n type: template.spec.type ?? '',\n description: template.metadata.description ?? '-',\n tags: (template.metadata?.tags as string[]) ?? [],\n };\n};\n\nconst DeprecationWarning = () => {\n const styles = useDeprecationStyles();\n\n const Title = (\n <Typography style={{ padding: 10, maxWidth: 300 }}>\n This template syntax is deprecated. Click for more info.\n </Typography>\n );\n\n return (\n <div className={styles.deprecationIcon}>\n <Tooltip title={Title}>\n <Link\n href=\"https://backstage.io/docs/features/software-templates/migrating-from-v1alpha1-to-v1beta2\"\n className={styles.link}\n >\n <WarningIcon />\n </Link>\n </Tooltip>\n </div>\n );\n};\n\nexport const TemplateCard = ({ template, deprecated }: TemplateCardProps) => {\n const backstageTheme = useTheme<BackstageTheme>();\n const rootLink = useRouteRef(rootRouteRef);\n const templateProps = getTemplateCardProps(template);\n const ownedByRelations = getEntityRelations(\n template as Entity,\n RELATION_OWNED_BY,\n );\n const themeId = backstageTheme.getPageTheme({ themeId: templateProps.type })\n ? templateProps.type\n : 'other';\n const theme = backstageTheme.getPageTheme({ themeId });\n const classes = useStyles({ backgroundImage: theme.backgroundImage });\n const href = generatePath(`${rootLink()}/templates/:templateName`, {\n templateName: templateProps.name,\n });\n\n const scmIntegrationsApi = useApi(scmIntegrationsApiRef);\n const sourceLocation = getEntitySourceLocation(template, scmIntegrationsApi);\n\n return (\n <Card>\n <CardMedia className={classes.cardHeader}>\n <FavouriteTemplate entity={template} />\n {deprecated && <DeprecationWarning />}\n <ItemCardHeader\n title={templateProps.title}\n subtitle={templateProps.type}\n classes={{ root: classes.title }}\n />\n </CardMedia>\n <CardContent style={{ display: 'grid' }}>\n <Box className={classes.box}>\n <Typography variant=\"body2\" className={classes.label}>\n Description\n </Typography>\n {templateProps.description}\n </Box>\n <Box className={classes.box}>\n <Typography variant=\"body2\" className={classes.label}>\n Owner\n </Typography>\n <EntityRefLinks entityRefs={ownedByRelations} defaultKind=\"Group\" />\n </Box>\n <Box>\n <Typography variant=\"body2\" className={classes.label}>\n Tags\n </Typography>\n {templateProps.tags?.map(tag => (\n <Chip size=\"small\" label={tag} key={tag} />\n ))}\n </Box>\n </CardContent>\n <CardActions>\n {sourceLocation && (\n <IconButton\n className={classes.leftButton}\n href={sourceLocation.locationTargetUrl}\n >\n <ScmIntegrationIcon type={sourceLocation.integrationType} />\n </IconButton>\n )}\n <Button\n color=\"primary\"\n to={href}\n aria-label={`Choose ${templateProps.title}`}\n >\n Choose\n </Button>\n </CardActions>\n </Card>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { ComponentType } from 'react';\nimport {\n stringifyEntityRef,\n TemplateEntityV1beta2,\n} from '@backstage/catalog-model';\nimport {\n ItemCardGrid,\n Progress,\n WarningPanel,\n} from '@backstage/core-components';\nimport { useEntityListProvider } from '@backstage/plugin-catalog-react';\nimport { Link, Typography } from '@material-ui/core';\nimport { TemplateCard } from '../TemplateCard';\n\nexport type TemplateListProps = {\n TemplateCardComponent?:\n | ComponentType<{ template: TemplateEntityV1beta2 }>\n | undefined;\n};\n\nexport const TemplateList = ({ TemplateCardComponent }: TemplateListProps) => {\n const { loading, error, entities } = useEntityListProvider();\n const Card = TemplateCardComponent || TemplateCard;\n return (\n <>\n {loading && <Progress />}\n\n {error && (\n <WarningPanel title=\"Oops! Something went wrong loading the templates\">\n {error.message}\n </WarningPanel>\n )}\n\n {!error && !loading && !entities.length && (\n <Typography variant=\"body2\">\n No templates found that match your filter. Learn more about{' '}\n <Link href=\"https://backstage.io/docs/features/software-templates/adding-templates\">\n adding templates\n </Link>\n .\n </Typography>\n )}\n\n <ItemCardGrid>\n {entities &&\n entities?.length > 0 &&\n entities.map(template => (\n <Card\n key={stringifyEntityRef(template)}\n template={template as TemplateEntityV1beta2}\n />\n ))}\n </ItemCardGrid>\n </>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React from 'react';\nimport capitalize from 'lodash/capitalize';\nimport { Progress } from '@backstage/core-components';\nimport {\n Box,\n Checkbox,\n FormControlLabel,\n TextField,\n Typography,\n} from '@material-ui/core';\nimport CheckBoxIcon from '@material-ui/icons/CheckBox';\nimport CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport { Autocomplete } from '@material-ui/lab';\nimport { useEntityTypeFilter } from '@backstage/plugin-catalog-react';\nimport { alertApiRef, useApi } from '@backstage/core-plugin-api';\n\nconst icon = <CheckBoxOutlineBlankIcon fontSize=\"small\" />;\nconst checkedIcon = <CheckBoxIcon fontSize=\"small\" />;\n\nexport const TemplateTypePicker = () => {\n const alertApi = useApi(alertApiRef);\n const { error, loading, availableTypes, selectedTypes, setSelectedTypes } =\n useEntityTypeFilter();\n\n if (loading) return <Progress />;\n\n if (!availableTypes) return null;\n\n if (error) {\n alertApi.post({\n message: `Failed to load entity types`,\n severity: 'error',\n });\n return null;\n }\n\n return (\n <Box pb={1} pt={1}>\n <Typography variant=\"button\">Categories</Typography>\n <Autocomplete\n multiple\n aria-label=\"Categories\"\n options={availableTypes}\n value={selectedTypes}\n onChange={(_: object, value: string[]) => setSelectedTypes(value)}\n renderOption={(option, { selected }) => (\n <FormControlLabel\n control={\n <Checkbox\n icon={icon}\n checkedIcon={checkedIcon}\n checked={selected}\n />\n }\n label={capitalize(option)}\n />\n )}\n size=\"small\"\n popupIcon={<ExpandMoreIcon data-testid=\"categories-picker-expand\" />}\n renderInput={params => <TextField {...params} variant=\"outlined\" />}\n />\n </Box>\n );\n};\n"],"names":["useStyles","Autocomplete"],"mappings":"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"}