@backstage/plugin-scaffolder 1.25.1 → 1.26.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -5
- package/alpha/package.json +1 -1
- package/dist/alpha/api/FormFieldsApi.esm.js +40 -0
- package/dist/alpha/api/FormFieldsApi.esm.js.map +1 -0
- package/dist/alpha/api/ref.esm.js +8 -0
- package/dist/alpha/api/ref.esm.js.map +1 -0
- package/dist/alpha/api.esm.js +27 -0
- package/dist/alpha/api.esm.js.map +1 -0
- package/dist/{next → alpha/components}/TemplateEditorPage/CustomFieldExplorer.esm.js +1 -1
- package/dist/alpha/components/TemplateEditorPage/CustomFieldExplorer.esm.js.map +1 -0
- package/dist/alpha/components/TemplateEditorPage/CustomFieldPlaygroud.esm.js +167 -0
- package/dist/alpha/components/TemplateEditorPage/CustomFieldPlaygroud.esm.js.map +1 -0
- package/dist/alpha/components/TemplateEditorPage/CustomFieldsPage.esm.js +33 -0
- package/dist/alpha/components/TemplateEditorPage/CustomFieldsPage.esm.js.map +1 -0
- package/dist/{next → alpha/components}/TemplateEditorPage/DirectoryEditorContext.esm.js +4 -7
- package/dist/alpha/components/TemplateEditorPage/DirectoryEditorContext.esm.js.map +1 -0
- package/dist/alpha/components/TemplateEditorPage/DryRunContext.esm.js.map +1 -0
- package/dist/{next → alpha/components}/TemplateEditorPage/DryRunResults/DryRunResults.esm.js +1 -1
- package/dist/alpha/components/TemplateEditorPage/DryRunResults/DryRunResults.esm.js.map +1 -0
- package/dist/{next → alpha/components}/TemplateEditorPage/DryRunResults/DryRunResultsList.esm.js +2 -2
- package/dist/alpha/components/TemplateEditorPage/DryRunResults/DryRunResultsList.esm.js.map +1 -0
- package/dist/alpha/components/TemplateEditorPage/DryRunResults/DryRunResultsSplitView.esm.js.map +1 -0
- package/dist/{next → alpha/components}/TemplateEditorPage/DryRunResults/DryRunResultsView.esm.js +2 -2
- package/dist/alpha/components/TemplateEditorPage/DryRunResults/DryRunResultsView.esm.js.map +1 -0
- package/dist/alpha/components/TemplateEditorPage/DryRunResults/IconLink.esm.js.map +1 -0
- package/dist/alpha/components/TemplateEditorPage/DryRunResults/TaskPageLinks.esm.js.map +1 -0
- package/dist/{next → alpha/components}/TemplateEditorPage/DryRunResults/TaskStatusStepper.esm.js +1 -1
- package/dist/alpha/components/TemplateEditorPage/DryRunResults/TaskStatusStepper.esm.js.map +1 -0
- package/dist/alpha/components/TemplateEditorPage/TemplateEditor.esm.js +94 -0
- package/dist/alpha/components/TemplateEditorPage/TemplateEditor.esm.js.map +1 -0
- package/dist/{next → alpha/components}/TemplateEditorPage/TemplateEditorBrowser.esm.js +36 -38
- package/dist/alpha/components/TemplateEditorPage/TemplateEditorBrowser.esm.js.map +1 -0
- package/dist/{next → alpha/components}/TemplateEditorPage/TemplateEditorForm.esm.js +12 -20
- package/dist/alpha/components/TemplateEditorPage/TemplateEditorForm.esm.js.map +1 -0
- package/dist/alpha/components/TemplateEditorPage/TemplateEditorIntro.esm.js +146 -0
- package/dist/alpha/components/TemplateEditorPage/TemplateEditorIntro.esm.js.map +1 -0
- package/dist/alpha/components/TemplateEditorPage/TemplateEditorPage.esm.js +53 -0
- package/dist/alpha/components/TemplateEditorPage/TemplateEditorPage.esm.js.map +1 -0
- package/dist/{next → alpha/components}/TemplateEditorPage/TemplateEditorTextArea.esm.js +36 -3
- package/dist/alpha/components/TemplateEditorPage/TemplateEditorTextArea.esm.js.map +1 -0
- package/dist/alpha/components/TemplateEditorPage/TemplateEditorToolbar.esm.js +98 -0
- package/dist/alpha/components/TemplateEditorPage/TemplateEditorToolbar.esm.js.map +1 -0
- package/dist/alpha/components/TemplateEditorPage/TemplateFormPage.esm.js +43 -0
- package/dist/alpha/components/TemplateEditorPage/TemplateFormPage.esm.js.map +1 -0
- package/dist/{next → alpha/components}/TemplateEditorPage/TemplateFormPreviewer.esm.js +77 -39
- package/dist/alpha/components/TemplateEditorPage/TemplateFormPreviewer.esm.js.map +1 -0
- package/dist/alpha/components/TemplateEditorPage/TemplatePage.esm.js +52 -0
- package/dist/alpha/components/TemplateEditorPage/TemplatePage.esm.js.map +1 -0
- package/dist/{next → alpha/components}/TemplateListPage/RegisterExistingButton.esm.js +1 -1
- package/dist/alpha/components/TemplateListPage/RegisterExistingButton.esm.js.map +1 -0
- package/dist/{next → alpha/components}/TemplateListPage/TemplateListPage.esm.js +2 -2
- package/dist/alpha/components/TemplateListPage/TemplateListPage.esm.js.map +1 -0
- package/dist/{next → alpha/components}/TemplateWizardPage/TemplateWizardPage.esm.js +2 -2
- package/dist/alpha/components/TemplateWizardPage/TemplateWizardPage.esm.js.map +1 -0
- package/dist/{next → alpha/components}/TemplateWizardPage/TemplateWizardPageContextMenu.esm.js +1 -1
- package/dist/alpha/components/TemplateWizardPage/TemplateWizardPageContextMenu.esm.js.map +1 -0
- package/dist/alpha/extensions.esm.js +30 -0
- package/dist/alpha/extensions.esm.js.map +1 -0
- package/dist/alpha/fields/RepoUrlPicker.esm.js +37 -0
- package/dist/alpha/fields/RepoUrlPicker.esm.js.map +1 -0
- package/dist/alpha/plugin.esm.js +32 -0
- package/dist/alpha/plugin.esm.js.map +1 -0
- package/dist/alpha.d.ts +80 -9
- package/dist/alpha.esm.js +1 -60
- package/dist/alpha.esm.js.map +1 -1
- package/dist/api.esm.js +19 -5
- package/dist/api.esm.js.map +1 -1
- package/dist/components/ActionsPage/ActionsPage.esm.js +30 -5
- package/dist/components/ActionsPage/ActionsPage.esm.js.map +1 -1
- package/dist/components/FileBrowser/FileBrowser.esm.js +4 -3
- package/dist/components/FileBrowser/FileBrowser.esm.js.map +1 -1
- package/dist/components/ListTasksPage/ListTasksPage.esm.js +17 -3
- package/dist/components/ListTasksPage/ListTasksPage.esm.js.map +1 -1
- package/dist/components/ListTasksPage/OwnerListPicker.esm.js +2 -2
- package/dist/components/ListTasksPage/OwnerListPicker.esm.js.map +1 -1
- package/dist/components/ListTasksPage/columns/CreatedAtColumn.esm.js +10 -5
- package/dist/components/ListTasksPage/columns/CreatedAtColumn.esm.js.map +1 -1
- package/dist/components/OngoingTask/ContextMenu.esm.js +15 -2
- package/dist/components/OngoingTask/ContextMenu.esm.js.map +1 -1
- package/dist/components/OngoingTask/OngoingTask.esm.js +24 -1
- package/dist/components/OngoingTask/OngoingTask.esm.js.map +1 -1
- package/dist/components/Router/Router.esm.js +35 -7
- package/dist/components/Router/Router.esm.js.map +1 -1
- package/dist/components/fields/EntityPicker/EntityPicker.esm.js +1 -1
- package/dist/components/fields/EntityPicker/EntityPicker.esm.js.map +1 -1
- package/dist/components/fields/MultiEntityPicker/MultiEntityPicker.esm.js +1 -0
- package/dist/components/fields/MultiEntityPicker/MultiEntityPicker.esm.js.map +1 -1
- package/dist/components/fields/RepoUrlPicker/RepoUrlPicker.esm.js.map +1 -1
- package/dist/components/fields/RepoUrlPicker/schema.esm.js +5 -6
- package/dist/components/fields/RepoUrlPicker/schema.esm.js.map +1 -1
- package/dist/components/fields/utils.esm.js +2 -1
- package/dist/components/fields/utils.esm.js.map +1 -1
- package/dist/index.d.ts +13 -8
- package/dist/lib/filesystem/WebFileSystemAccess.esm.js +34 -1
- package/dist/lib/filesystem/WebFileSystemAccess.esm.js.map +1 -1
- package/dist/lib/filesystem/createExampleTemplate.esm.js +76 -0
- package/dist/lib/filesystem/createExampleTemplate.esm.js.map +1 -0
- package/dist/packages/opaque-internal/src/OpaqueType.esm.js +103 -0
- package/dist/packages/opaque-internal/src/OpaqueType.esm.js.map +1 -0
- package/dist/packages/scaffolder-internal/src/wiring/InternalFormField.esm.js +6 -0
- package/dist/packages/scaffolder-internal/src/wiring/InternalFormField.esm.js.map +1 -0
- package/dist/plugin.esm.js +5 -2
- package/dist/plugin.esm.js.map +1 -1
- package/dist/routes.esm.js +16 -1
- package/dist/routes.esm.js.map +1 -1
- package/dist/translation.esm.js +22 -3
- package/dist/translation.esm.js.map +1 -1
- package/package.json +34 -23
- package/dist/next/TemplateEditorPage/CustomFieldExplorer.esm.js.map +0 -1
- package/dist/next/TemplateEditorPage/DirectoryEditorContext.esm.js.map +0 -1
- package/dist/next/TemplateEditorPage/DryRunContext.esm.js.map +0 -1
- package/dist/next/TemplateEditorPage/DryRunResults/DryRunResults.esm.js.map +0 -1
- package/dist/next/TemplateEditorPage/DryRunResults/DryRunResultsList.esm.js.map +0 -1
- package/dist/next/TemplateEditorPage/DryRunResults/DryRunResultsSplitView.esm.js.map +0 -1
- package/dist/next/TemplateEditorPage/DryRunResults/DryRunResultsView.esm.js.map +0 -1
- package/dist/next/TemplateEditorPage/DryRunResults/IconLink.esm.js.map +0 -1
- package/dist/next/TemplateEditorPage/DryRunResults/TaskPageLinks.esm.js.map +0 -1
- package/dist/next/TemplateEditorPage/DryRunResults/TaskStatusStepper.esm.js.map +0 -1
- package/dist/next/TemplateEditorPage/TemplateEditor.esm.js +0 -56
- package/dist/next/TemplateEditorPage/TemplateEditor.esm.js.map +0 -1
- package/dist/next/TemplateEditorPage/TemplateEditorBrowser.esm.js.map +0 -1
- package/dist/next/TemplateEditorPage/TemplateEditorForm.esm.js.map +0 -1
- package/dist/next/TemplateEditorPage/TemplateEditorIntro.esm.js +0 -91
- package/dist/next/TemplateEditorPage/TemplateEditorIntro.esm.js.map +0 -1
- package/dist/next/TemplateEditorPage/TemplateEditorPage.esm.js +0 -87
- package/dist/next/TemplateEditorPage/TemplateEditorPage.esm.js.map +0 -1
- package/dist/next/TemplateEditorPage/TemplateEditorTextArea.esm.js.map +0 -1
- package/dist/next/TemplateEditorPage/TemplateFormPreviewer.esm.js.map +0 -1
- package/dist/next/TemplateListPage/RegisterExistingButton.esm.js.map +0 -1
- package/dist/next/TemplateListPage/TemplateListPage.esm.js.map +0 -1
- package/dist/next/TemplateWizardPage/TemplateWizardPage.esm.js.map +0 -1
- package/dist/next/TemplateWizardPage/TemplateWizardPageContextMenu.esm.js.map +0 -1
- /package/dist/{next → alpha/components}/TemplateEditorPage/DryRunContext.esm.js +0 -0
- /package/dist/{next → alpha/components}/TemplateEditorPage/DryRunResults/DryRunResultsSplitView.esm.js +0 -0
- /package/dist/{next → alpha/components}/TemplateEditorPage/DryRunResults/IconLink.esm.js +0 -0
- /package/dist/{next → alpha/components}/TemplateEditorPage/DryRunResults/TaskPageLinks.esm.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContextMenu.esm.js","sources":["../../../src/components/OngoingTask/ContextMenu.tsx"],"sourcesContent":["/*\n * Copyright 2023 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 IconButton from '@material-ui/core/IconButton';\nimport ListItemIcon from '@material-ui/core/ListItemIcon';\nimport ListItemText from '@material-ui/core/ListItemText';\nimport MenuItem from '@material-ui/core/MenuItem';\nimport MenuList from '@material-ui/core/MenuList';\nimport Popover from '@material-ui/core/Popover';\nimport { makeStyles, Theme, useTheme } from '@material-ui/core/styles';\nimport { useAsync } from '@react-hookz/web';\nimport Cancel from '@material-ui/icons/Cancel';\nimport Retry from '@material-ui/icons/Repeat';\nimport Toc from '@material-ui/icons/Toc';\nimport ControlPointIcon from '@material-ui/icons/ControlPoint';\nimport MoreVert from '@material-ui/icons/MoreVert';\nimport React, { useState } from 'react';\nimport { useAnalytics, useApi } from '@backstage/core-plugin-api';\nimport { scaffolderApiRef } from '@backstage/plugin-scaffolder-react';\nimport { usePermission } from '@backstage/plugin-permission-react';\nimport {\n taskCancelPermission,\n taskReadPermission,\n taskCreatePermission,\n} from '@backstage/plugin-scaffolder-common/alpha';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { scaffolderTranslationRef } from '../../translation';\n\ntype ContextMenuProps = {\n cancelEnabled?: boolean;\n logsVisible?: boolean;\n buttonBarVisible?: boolean;\n onStartOver?: () => void;\n onToggleLogs?: (state: boolean) => void;\n onToggleButtonBar?: (state: boolean) => void;\n taskId?: string;\n};\n\nconst useStyles = makeStyles<Theme, { fontColor: string }>(() => ({\n button: {\n color: ({ fontColor }) => fontColor,\n },\n}));\n\nexport const ContextMenu = (props: ContextMenuProps) => {\n const {\n cancelEnabled,\n logsVisible,\n buttonBarVisible,\n onStartOver,\n onToggleLogs,\n onToggleButtonBar,\n taskId,\n } = props;\n const { getPageTheme } = useTheme();\n const pageTheme = getPageTheme({ themeId: 'website' });\n const classes = useStyles({ fontColor: pageTheme.fontColor });\n const scaffolderApi = useApi(scaffolderApiRef);\n const analytics = useAnalytics();\n const [anchorEl, setAnchorEl] = useState<HTMLButtonElement>();\n const { t } = useTranslationRef(scaffolderTranslationRef);\n\n const [{ status: cancelStatus }, { execute: cancel }] = useAsync(async () => {\n if (taskId) {\n analytics.captureEvent('cancelled', 'Template has been cancelled');\n await scaffolderApi.cancelTask(taskId);\n }\n });\n\n const { allowed: canCancelTask } = usePermission({\n permission: taskCancelPermission,\n });\n\n const { allowed: canReadTask } = usePermission({\n permission: taskReadPermission,\n });\n\n const { allowed: canCreateTask } = usePermission({\n permission: taskCreatePermission,\n });\n\n // Start Over endpoint requires user to have both read (to grab parameters) and create (to create new task) permissions\n const canStartOver = canReadTask && canCreateTask;\n\n return (\n <>\n <IconButton\n aria-label=\"more\"\n aria-controls=\"long-menu\"\n aria-haspopup=\"true\"\n onClick={(event: React.SyntheticEvent<HTMLButtonElement>) => {\n setAnchorEl(event.currentTarget);\n }}\n data-testid=\"menu-button\"\n className={classes.button}\n >\n <MoreVert />\n </IconButton>\n <Popover\n open={Boolean(anchorEl)}\n onClose={() => setAnchorEl(undefined)}\n anchorEl={anchorEl}\n anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}\n transformOrigin={{ vertical: 'top', horizontal: 'right' }}\n >\n <MenuList>\n <MenuItem onClick={() => onToggleLogs?.(!logsVisible)}>\n <ListItemIcon>\n <Toc fontSize=\"small\" />\n </ListItemIcon>\n <ListItemText\n primary={\n logsVisible\n ? t('ongoingTask.contextMenu.hideLogs')\n : t('ongoingTask.contextMenu.showLogs')\n }\n />\n </MenuItem>\n <MenuItem onClick={() => onToggleButtonBar?.(!buttonBarVisible)}>\n <ListItemIcon>\n <ControlPointIcon fontSize=\"small\" />\n </ListItemIcon>\n <ListItemText\n primary={\n buttonBarVisible\n ? t('ongoingTask.contextMenu.hideButtonBar')\n : t('ongoingTask.contextMenu.showButtonBar')\n }\n />\n </MenuItem>\n <MenuItem\n onClick={onStartOver}\n disabled={cancelEnabled || !canStartOver}\n data-testid=\"start-over-task\"\n >\n <ListItemIcon>\n <Retry fontSize=\"small\" />\n </ListItemIcon>\n <ListItemText primary={t('ongoingTask.contextMenu.startOver')} />\n </MenuItem>\n <MenuItem\n onClick={cancel}\n disabled={\n !cancelEnabled ||\n cancelStatus !== 'not-executed' ||\n !canCancelTask\n }\n data-testid=\"cancel-task\"\n >\n <ListItemIcon>\n <Cancel fontSize=\"small\" />\n </ListItemIcon>\n <ListItemText primary={t('ongoingTask.contextMenu.cancel')} />\n </MenuItem>\n </MenuList>\n </Popover>\n </>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ContextMenu.esm.js","sources":["../../../src/components/OngoingTask/ContextMenu.tsx"],"sourcesContent":["/*\n * Copyright 2023 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 IconButton from '@material-ui/core/IconButton';\nimport ListItemIcon from '@material-ui/core/ListItemIcon';\nimport ListItemText from '@material-ui/core/ListItemText';\nimport MenuItem from '@material-ui/core/MenuItem';\nimport MenuList from '@material-ui/core/MenuList';\nimport Popover from '@material-ui/core/Popover';\nimport { makeStyles, Theme, useTheme } from '@material-ui/core/styles';\nimport { useAsync } from '@react-hookz/web';\nimport Cancel from '@material-ui/icons/Cancel';\nimport Repeat from '@material-ui/icons/Repeat';\nimport Replay from '@material-ui/icons/Replay';\nimport Toc from '@material-ui/icons/Toc';\nimport ControlPointIcon from '@material-ui/icons/ControlPoint';\nimport MoreVert from '@material-ui/icons/MoreVert';\nimport React, { useState } from 'react';\nimport { useAnalytics, useApi } from '@backstage/core-plugin-api';\nimport { scaffolderApiRef } from '@backstage/plugin-scaffolder-react';\nimport { usePermission } from '@backstage/plugin-permission-react';\nimport {\n taskCancelPermission,\n taskReadPermission,\n taskCreatePermission,\n} from '@backstage/plugin-scaffolder-common/alpha';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { scaffolderTranslationRef } from '../../translation';\n\ntype ContextMenuProps = {\n cancelEnabled?: boolean;\n canRetry: boolean;\n isRetryableTask: boolean;\n logsVisible?: boolean;\n buttonBarVisible?: boolean;\n onRetry?: () => void;\n onStartOver?: () => void;\n onToggleLogs?: (state: boolean) => void;\n onToggleButtonBar?: (state: boolean) => void;\n taskId?: string;\n};\n\nconst useStyles = makeStyles<Theme, { fontColor: string }>(() => ({\n button: {\n color: ({ fontColor }) => fontColor,\n },\n}));\n\nexport const ContextMenu = (props: ContextMenuProps) => {\n const {\n cancelEnabled,\n canRetry,\n isRetryableTask,\n logsVisible,\n buttonBarVisible,\n onRetry,\n onStartOver,\n onToggleLogs,\n onToggleButtonBar,\n taskId,\n } = props;\n const { getPageTheme } = useTheme();\n const pageTheme = getPageTheme({ themeId: 'website' });\n const classes = useStyles({ fontColor: pageTheme.fontColor });\n const scaffolderApi = useApi(scaffolderApiRef);\n const analytics = useAnalytics();\n const [anchorEl, setAnchorEl] = useState<HTMLButtonElement>();\n const { t } = useTranslationRef(scaffolderTranslationRef);\n\n const [{ status: cancelStatus }, { execute: cancel }] = useAsync(async () => {\n if (taskId) {\n analytics.captureEvent('cancelled', 'Template has been cancelled');\n await scaffolderApi.cancelTask(taskId);\n }\n });\n\n const { allowed: canCancelTask } = usePermission({\n permission: taskCancelPermission,\n });\n\n const { allowed: canReadTask } = usePermission({\n permission: taskReadPermission,\n });\n\n const { allowed: canCreateTask } = usePermission({\n permission: taskCreatePermission,\n });\n\n // Start Over endpoint requires user to have both read (to grab parameters) and create (to create new task) permissions\n const canStartOver = canReadTask && canCreateTask;\n\n return (\n <>\n <IconButton\n aria-label=\"more\"\n aria-controls=\"long-menu\"\n aria-haspopup=\"true\"\n onClick={(event: React.SyntheticEvent<HTMLButtonElement>) => {\n setAnchorEl(event.currentTarget);\n }}\n data-testid=\"menu-button\"\n className={classes.button}\n >\n <MoreVert />\n </IconButton>\n <Popover\n open={Boolean(anchorEl)}\n onClose={() => setAnchorEl(undefined)}\n anchorEl={anchorEl}\n anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}\n transformOrigin={{ vertical: 'top', horizontal: 'right' }}\n >\n <MenuList>\n <MenuItem onClick={() => onToggleLogs?.(!logsVisible)}>\n <ListItemIcon>\n <Toc fontSize=\"small\" />\n </ListItemIcon>\n <ListItemText\n primary={\n logsVisible\n ? t('ongoingTask.contextMenu.hideLogs')\n : t('ongoingTask.contextMenu.showLogs')\n }\n />\n </MenuItem>\n <MenuItem onClick={() => onToggleButtonBar?.(!buttonBarVisible)}>\n <ListItemIcon>\n <ControlPointIcon fontSize=\"small\" />\n </ListItemIcon>\n <ListItemText\n primary={\n buttonBarVisible\n ? t('ongoingTask.contextMenu.hideButtonBar')\n : t('ongoingTask.contextMenu.showButtonBar')\n }\n />\n </MenuItem>\n <MenuItem\n onClick={onStartOver}\n disabled={cancelEnabled || !canStartOver}\n data-testid=\"start-over-task\"\n >\n <ListItemIcon>\n <Repeat fontSize=\"small\" />\n </ListItemIcon>\n <ListItemText primary={t('ongoingTask.contextMenu.startOver')} />\n </MenuItem>\n {isRetryableTask && (\n <MenuItem\n onClick={onRetry}\n disabled={cancelEnabled || !canRetry}\n data-testid=\"retry-task\"\n >\n <ListItemIcon>\n <Replay fontSize=\"small\" />\n </ListItemIcon>\n <ListItemText primary={t('ongoingTask.contextMenu.retry')} />\n </MenuItem>\n )}\n <MenuItem\n onClick={cancel}\n disabled={\n !cancelEnabled ||\n cancelStatus !== 'not-executed' ||\n !canCancelTask\n }\n data-testid=\"cancel-task\"\n >\n <ListItemIcon>\n <Cancel fontSize=\"small\" />\n </ListItemIcon>\n <ListItemText primary={t('ongoingTask.contextMenu.cancel')} />\n </MenuItem>\n </MenuList>\n </Popover>\n </>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -31,6 +31,9 @@ const useStyles = makeStyles((theme) => ({
|
|
|
31
31
|
cancelButton: {
|
|
32
32
|
marginRight: theme.spacing(1)
|
|
33
33
|
},
|
|
34
|
+
retryButton: {
|
|
35
|
+
marginRight: theme.spacing(1)
|
|
36
|
+
},
|
|
34
37
|
logsVisibilityButton: {
|
|
35
38
|
marginRight: theme.spacing(1)
|
|
36
39
|
}
|
|
@@ -81,6 +84,8 @@ const OngoingTask = (props) => {
|
|
|
81
84
|
}
|
|
82
85
|
return 0;
|
|
83
86
|
}, [steps]);
|
|
87
|
+
const isRetryableTask = taskStream.task?.spec.EXPERIMENTAL_recovery?.EXPERIMENTAL_strategy === "startOver";
|
|
88
|
+
const canRetry = canReadTask && canCreateTask && isRetryableTask;
|
|
84
89
|
const startOver = useCallback(() => {
|
|
85
90
|
const { namespace, name } = taskStream.task?.spec.templateInfo?.entity?.metadata ?? {};
|
|
86
91
|
const formData = taskStream.task?.spec.parameters ?? {};
|
|
@@ -102,6 +107,12 @@ const OngoingTask = (props) => {
|
|
|
102
107
|
taskStream.task?.spec.templateInfo?.entity?.metadata,
|
|
103
108
|
templateRouteRef
|
|
104
109
|
]);
|
|
110
|
+
const [{ status: _ }, { execute: triggerRetry }] = useAsync(async () => {
|
|
111
|
+
if (taskId) {
|
|
112
|
+
analytics.captureEvent("retried", "Template has been retried");
|
|
113
|
+
await scaffolderApi.retry?.(taskId);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
105
116
|
const [{ status: cancelStatus }, { execute: triggerCancel }] = useAsync(
|
|
106
117
|
async () => {
|
|
107
118
|
if (taskId) {
|
|
@@ -124,9 +135,12 @@ const OngoingTask = (props) => {
|
|
|
124
135
|
ContextMenu,
|
|
125
136
|
{
|
|
126
137
|
cancelEnabled,
|
|
138
|
+
canRetry,
|
|
139
|
+
isRetryableTask,
|
|
127
140
|
logsVisible,
|
|
128
141
|
buttonBarVisible,
|
|
129
142
|
onStartOver: startOver,
|
|
143
|
+
onRetry: triggerRetry,
|
|
130
144
|
onToggleLogs: setLogVisibleState,
|
|
131
145
|
onToggleButtonBar: setButtonBarVisibleState,
|
|
132
146
|
taskId
|
|
@@ -151,11 +165,20 @@ const OngoingTask = (props) => {
|
|
|
151
165
|
Button,
|
|
152
166
|
{
|
|
153
167
|
className: classes.cancelButton,
|
|
154
|
-
disabled: !cancelEnabled || cancelStatus !== "not-executed" || !canCancelTask,
|
|
168
|
+
disabled: !cancelEnabled || cancelStatus !== "not-executed" && !isRetryableTask || !canCancelTask,
|
|
155
169
|
onClick: triggerCancel,
|
|
156
170
|
"data-testid": "cancel-button"
|
|
157
171
|
},
|
|
158
172
|
t("ongoingTask.cancelButtonTitle")
|
|
173
|
+
), isRetryableTask && /* @__PURE__ */ React.createElement(
|
|
174
|
+
Button,
|
|
175
|
+
{
|
|
176
|
+
className: classes.retryButton,
|
|
177
|
+
disabled: cancelEnabled || !canRetry,
|
|
178
|
+
onClick: triggerRetry,
|
|
179
|
+
"data-testid": "retry-button"
|
|
180
|
+
},
|
|
181
|
+
t("ongoingTask.retryButtonTitle")
|
|
159
182
|
), /* @__PURE__ */ React.createElement(
|
|
160
183
|
Button,
|
|
161
184
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OngoingTask.esm.js","sources":["../../../src/components/OngoingTask/OngoingTask.tsx"],"sourcesContent":["/*\n * Copyright 2023 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, useMemo, useState } from 'react';\nimport { Content, ErrorPanel, Header, Page } from '@backstage/core-components';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport Box from '@material-ui/core/Box';\nimport Button from '@material-ui/core/Button';\nimport Paper from '@material-ui/core/Paper';\nimport { makeStyles } from '@material-ui/core/styles';\nimport { ResizableBox } from 'react-resizable';\nimport {\n ScaffolderTaskOutput,\n scaffolderApiRef,\n useTaskEventStream,\n} from '@backstage/plugin-scaffolder-react';\nimport { selectedTemplateRouteRef } from '../../routes';\nimport { useAnalytics, useApi, useRouteRef } from '@backstage/core-plugin-api';\nimport qs from 'qs';\nimport { ContextMenu } from './ContextMenu';\nimport {\n DefaultTemplateOutputs,\n TaskLogStream,\n TaskSteps,\n} from '@backstage/plugin-scaffolder-react/alpha';\nimport { useAsync } from '@react-hookz/web';\nimport { usePermission } from '@backstage/plugin-permission-react';\nimport {\n taskCancelPermission,\n taskReadPermission,\n taskCreatePermission,\n} from '@backstage/plugin-scaffolder-common/alpha';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { scaffolderTranslationRef } from '../../translation';\n\nconst useStyles = makeStyles(theme => ({\n contentWrapper: {\n display: 'flex',\n flexDirection: 'column',\n },\n buttonBar: {\n display: 'flex',\n flexDirection: 'row',\n justifyContent: 'right',\n },\n cancelButton: {\n marginRight: theme.spacing(1),\n },\n logsVisibilityButton: {\n marginRight: theme.spacing(1),\n },\n}));\n\n/**\n * @public\n */\nexport const OngoingTask = (props: {\n TemplateOutputsComponent?: React.ComponentType<{\n output?: ScaffolderTaskOutput;\n }>;\n}) => {\n // todo(blam): check that task Id actually exists, and that it's valid. otherwise redirect to something more useful.\n const { taskId } = useParams();\n const templateRouteRef = useRouteRef(selectedTemplateRouteRef);\n const navigate = useNavigate();\n const analytics = useAnalytics();\n const scaffolderApi = useApi(scaffolderApiRef);\n const taskStream = useTaskEventStream(taskId!);\n const classes = useStyles();\n const steps = useMemo(\n () =>\n taskStream.task?.spec.steps.map(step => ({\n ...step,\n ...taskStream?.steps?.[step.id],\n })) ?? [],\n [taskStream],\n );\n const { t } = useTranslationRef(scaffolderTranslationRef);\n\n const [logsVisible, setLogVisibleState] = useState(false);\n const [buttonBarVisible, setButtonBarVisibleState] = useState(true);\n\n // Used dummy string value for `resourceRef` since `allowed` field will always return `false` if `resourceRef` is `undefined`\n const { allowed: canCancelTask } = usePermission({\n permission: taskCancelPermission,\n });\n\n const { allowed: canReadTask } = usePermission({\n permission: taskReadPermission,\n });\n\n const { allowed: canCreateTask } = usePermission({\n permission: taskCreatePermission,\n });\n\n // Start Over endpoint requires user to have both read (to grab parameters) and create (to create new task) permissions\n const canStartOver = canReadTask && canCreateTask;\n\n useEffect(() => {\n if (taskStream.error) {\n setLogVisibleState(true);\n }\n }, [taskStream.error]);\n\n useEffect(() => {\n if (taskStream.completed && !taskStream.error) {\n setButtonBarVisibleState(false);\n }\n }, [taskStream.error, taskStream.completed]);\n\n const activeStep = useMemo(() => {\n for (let i = steps.length - 1; i >= 0; i--) {\n if (steps[i].status !== 'open') {\n return i;\n }\n }\n\n return 0;\n }, [steps]);\n\n const startOver = useCallback(() => {\n const { namespace, name } =\n taskStream.task?.spec.templateInfo?.entity?.metadata ?? {};\n\n const formData = taskStream.task?.spec.parameters ?? {};\n\n if (!namespace || !name) {\n return;\n }\n\n analytics.captureEvent('click', `Task has been started over`);\n\n navigate({\n pathname: templateRouteRef({\n namespace,\n templateName: name,\n }),\n search: `?${qs.stringify({ formData: JSON.stringify(formData) })}`,\n });\n }, [\n analytics,\n navigate,\n taskStream.task?.spec.parameters,\n taskStream.task?.spec.templateInfo?.entity?.metadata,\n templateRouteRef,\n ]);\n\n const [{ status: cancelStatus }, { execute: triggerCancel }] = useAsync(\n async () => {\n if (taskId) {\n analytics.captureEvent('cancelled', 'Template has been cancelled');\n await scaffolderApi.cancelTask(taskId);\n }\n },\n );\n\n const Outputs = props.TemplateOutputsComponent ?? DefaultTemplateOutputs;\n\n const templateName =\n taskStream.task?.spec.templateInfo?.entity?.metadata.name || '';\n\n const cancelEnabled = !(taskStream.cancelled || taskStream.completed);\n\n return (\n <Page themeId=\"website\">\n <Header\n pageTitleOverride={\n templateName\n ? t('ongoingTask.pageTitle.hasTemplateName', { templateName })\n : t('ongoingTask.pageTitle.noTemplateName')\n }\n title={\n <div>\n {t('ongoingTask.title')} <code>{templateName}</code>\n </div>\n }\n subtitle={t('ongoingTask.subtitle', { taskId: taskId as string })}\n >\n <ContextMenu\n cancelEnabled={cancelEnabled}\n logsVisible={logsVisible}\n buttonBarVisible={buttonBarVisible}\n onStartOver={startOver}\n onToggleLogs={setLogVisibleState}\n onToggleButtonBar={setButtonBarVisibleState}\n taskId={taskId}\n />\n </Header>\n <Content className={classes.contentWrapper}>\n {taskStream.error ? (\n <Box paddingBottom={2}>\n <ErrorPanel\n error={taskStream.error}\n titleFormat=\"markdown\"\n title={taskStream.error.message}\n />\n </Box>\n ) : null}\n\n <Box paddingBottom={2}>\n <TaskSteps\n steps={steps}\n activeStep={activeStep}\n isComplete={taskStream.completed}\n isError={Boolean(taskStream.error)}\n />\n </Box>\n\n <Outputs output={taskStream.output} />\n\n {buttonBarVisible ? (\n <Box paddingBottom={2}>\n <Paper>\n <Box padding={2}>\n <div className={classes.buttonBar}>\n <Button\n className={classes.cancelButton}\n disabled={\n !cancelEnabled ||\n cancelStatus !== 'not-executed' ||\n !canCancelTask\n }\n onClick={triggerCancel}\n data-testid=\"cancel-button\"\n >\n {t('ongoingTask.cancelButtonTitle')}\n </Button>\n <Button\n className={classes.logsVisibilityButton}\n color=\"primary\"\n variant=\"outlined\"\n onClick={() => setLogVisibleState(!logsVisible)}\n >\n {logsVisible\n ? t('ongoingTask.hideLogsButtonTitle')\n : t('ongoingTask.showLogsButtonTitle')}\n </Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={cancelEnabled || !canStartOver}\n onClick={startOver}\n data-testid=\"start-over-button\"\n >\n {t('ongoingTask.startOverButtonTitle')}\n </Button>\n </div>\n </Box>\n </Paper>\n </Box>\n ) : null}\n\n {logsVisible ? (\n <ResizableBox height={240} minConstraints={[0, 160]} axis=\"y\">\n <Paper style={{ height: '100%' }}>\n <Box padding={2} height=\"100%\">\n <TaskLogStream logs={taskStream.stepLogs} />\n </Box>\n </Paper>\n </ResizableBox>\n ) : null}\n </Content>\n </Page>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"OngoingTask.esm.js","sources":["../../../src/components/OngoingTask/OngoingTask.tsx"],"sourcesContent":["/*\n * Copyright 2023 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, useMemo, useState } from 'react';\nimport { Content, ErrorPanel, Header, Page } from '@backstage/core-components';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport Box from '@material-ui/core/Box';\nimport Button from '@material-ui/core/Button';\nimport Paper from '@material-ui/core/Paper';\nimport { makeStyles } from '@material-ui/core/styles';\nimport { ResizableBox } from 'react-resizable';\nimport {\n ScaffolderTaskOutput,\n scaffolderApiRef,\n useTaskEventStream,\n} from '@backstage/plugin-scaffolder-react';\nimport { selectedTemplateRouteRef } from '../../routes';\nimport { useAnalytics, useApi, useRouteRef } from '@backstage/core-plugin-api';\nimport qs from 'qs';\nimport { ContextMenu } from './ContextMenu';\nimport {\n DefaultTemplateOutputs,\n TaskLogStream,\n TaskSteps,\n} from '@backstage/plugin-scaffolder-react/alpha';\nimport { useAsync } from '@react-hookz/web';\nimport { usePermission } from '@backstage/plugin-permission-react';\nimport {\n taskCancelPermission,\n taskReadPermission,\n taskCreatePermission,\n} from '@backstage/plugin-scaffolder-common/alpha';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { scaffolderTranslationRef } from '../../translation';\n\nconst useStyles = makeStyles(theme => ({\n contentWrapper: {\n display: 'flex',\n flexDirection: 'column',\n },\n buttonBar: {\n display: 'flex',\n flexDirection: 'row',\n justifyContent: 'right',\n },\n cancelButton: {\n marginRight: theme.spacing(1),\n },\n retryButton: {\n marginRight: theme.spacing(1),\n },\n logsVisibilityButton: {\n marginRight: theme.spacing(1),\n },\n}));\n\n/**\n * @public\n */\nexport const OngoingTask = (props: {\n TemplateOutputsComponent?: React.ComponentType<{\n output?: ScaffolderTaskOutput;\n }>;\n}) => {\n // todo(blam): check that task Id actually exists, and that it's valid. otherwise redirect to something more useful.\n const { taskId } = useParams();\n const templateRouteRef = useRouteRef(selectedTemplateRouteRef);\n const navigate = useNavigate();\n const analytics = useAnalytics();\n const scaffolderApi = useApi(scaffolderApiRef);\n const taskStream = useTaskEventStream(taskId!);\n const classes = useStyles();\n const steps = useMemo(\n () =>\n taskStream.task?.spec.steps.map(step => ({\n ...step,\n ...taskStream?.steps?.[step.id],\n })) ?? [],\n [taskStream],\n );\n const { t } = useTranslationRef(scaffolderTranslationRef);\n\n const [logsVisible, setLogVisibleState] = useState(false);\n const [buttonBarVisible, setButtonBarVisibleState] = useState(true);\n\n // Used dummy string value for `resourceRef` since `allowed` field will always return `false` if `resourceRef` is `undefined`\n const { allowed: canCancelTask } = usePermission({\n permission: taskCancelPermission,\n });\n\n const { allowed: canReadTask } = usePermission({\n permission: taskReadPermission,\n });\n\n const { allowed: canCreateTask } = usePermission({\n permission: taskCreatePermission,\n });\n\n // Start Over endpoint requires user to have both read (to grab parameters) and create (to create new task) permissions\n const canStartOver = canReadTask && canCreateTask;\n\n useEffect(() => {\n if (taskStream.error) {\n setLogVisibleState(true);\n }\n }, [taskStream.error]);\n\n useEffect(() => {\n if (taskStream.completed && !taskStream.error) {\n setButtonBarVisibleState(false);\n }\n }, [taskStream.error, taskStream.completed]);\n\n const activeStep = useMemo(() => {\n for (let i = steps.length - 1; i >= 0; i--) {\n if (steps[i].status !== 'open') {\n return i;\n }\n }\n\n return 0;\n }, [steps]);\n\n const isRetryableTask =\n taskStream.task?.spec.EXPERIMENTAL_recovery?.EXPERIMENTAL_strategy ===\n 'startOver';\n\n const canRetry = canReadTask && canCreateTask && isRetryableTask;\n\n const startOver = useCallback(() => {\n const { namespace, name } =\n taskStream.task?.spec.templateInfo?.entity?.metadata ?? {};\n\n const formData = taskStream.task?.spec.parameters ?? {};\n\n if (!namespace || !name) {\n return;\n }\n\n analytics.captureEvent('click', `Task has been started over`);\n\n navigate({\n pathname: templateRouteRef({\n namespace,\n templateName: name,\n }),\n search: `?${qs.stringify({ formData: JSON.stringify(formData) })}`,\n });\n }, [\n analytics,\n navigate,\n taskStream.task?.spec.parameters,\n taskStream.task?.spec.templateInfo?.entity?.metadata,\n templateRouteRef,\n ]);\n\n const [{ status: _ }, { execute: triggerRetry }] = useAsync(async () => {\n if (taskId) {\n analytics.captureEvent('retried', 'Template has been retried');\n await scaffolderApi.retry?.(taskId);\n }\n });\n\n const [{ status: cancelStatus }, { execute: triggerCancel }] = useAsync(\n async () => {\n if (taskId) {\n analytics.captureEvent('cancelled', 'Template has been cancelled');\n await scaffolderApi.cancelTask(taskId);\n }\n },\n );\n\n const Outputs = props.TemplateOutputsComponent ?? DefaultTemplateOutputs;\n\n const templateName =\n taskStream.task?.spec.templateInfo?.entity?.metadata.name || '';\n\n const cancelEnabled = !(taskStream.cancelled || taskStream.completed);\n\n return (\n <Page themeId=\"website\">\n <Header\n pageTitleOverride={\n templateName\n ? t('ongoingTask.pageTitle.hasTemplateName', { templateName })\n : t('ongoingTask.pageTitle.noTemplateName')\n }\n title={\n <div>\n {t('ongoingTask.title')} <code>{templateName}</code>\n </div>\n }\n subtitle={t('ongoingTask.subtitle', { taskId: taskId as string })}\n >\n <ContextMenu\n cancelEnabled={cancelEnabled}\n canRetry={canRetry}\n isRetryableTask={isRetryableTask}\n logsVisible={logsVisible}\n buttonBarVisible={buttonBarVisible}\n onStartOver={startOver}\n onRetry={triggerRetry}\n onToggleLogs={setLogVisibleState}\n onToggleButtonBar={setButtonBarVisibleState}\n taskId={taskId}\n />\n </Header>\n <Content className={classes.contentWrapper}>\n {taskStream.error ? (\n <Box paddingBottom={2}>\n <ErrorPanel\n error={taskStream.error}\n titleFormat=\"markdown\"\n title={taskStream.error.message}\n />\n </Box>\n ) : null}\n\n <Box paddingBottom={2}>\n <TaskSteps\n steps={steps}\n activeStep={activeStep}\n isComplete={taskStream.completed}\n isError={Boolean(taskStream.error)}\n />\n </Box>\n\n <Outputs output={taskStream.output} />\n\n {buttonBarVisible ? (\n <Box paddingBottom={2}>\n <Paper>\n <Box padding={2}>\n <div className={classes.buttonBar}>\n <Button\n className={classes.cancelButton}\n disabled={\n !cancelEnabled ||\n (cancelStatus !== 'not-executed' && !isRetryableTask) ||\n !canCancelTask\n }\n onClick={triggerCancel}\n data-testid=\"cancel-button\"\n >\n {t('ongoingTask.cancelButtonTitle')}\n </Button>\n {isRetryableTask && (\n <Button\n className={classes.retryButton}\n disabled={cancelEnabled || !canRetry}\n onClick={triggerRetry}\n data-testid=\"retry-button\"\n >\n {t('ongoingTask.retryButtonTitle')}\n </Button>\n )}\n <Button\n className={classes.logsVisibilityButton}\n color=\"primary\"\n variant=\"outlined\"\n onClick={() => setLogVisibleState(!logsVisible)}\n >\n {logsVisible\n ? t('ongoingTask.hideLogsButtonTitle')\n : t('ongoingTask.showLogsButtonTitle')}\n </Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={cancelEnabled || !canStartOver}\n onClick={startOver}\n data-testid=\"start-over-button\"\n >\n {t('ongoingTask.startOverButtonTitle')}\n </Button>\n </div>\n </Box>\n </Paper>\n </Box>\n ) : null}\n\n {logsVisible ? (\n <ResizableBox height={240} minConstraints={[0, 160]} axis=\"y\">\n <Paper style={{ height: '100%' }}>\n <Box padding={2} height=\"100%\">\n <TaskLogStream logs={taskStream.stepLogs} />\n </Box>\n </Paper>\n </ResizableBox>\n ) : null}\n </Content>\n </Page>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AA+CA,MAAM,SAAA,GAAY,WAAW,CAAU,KAAA,MAAA;AAAA,EACrC,cAAgB,EAAA;AAAA,IACd,OAAS,EAAA,MAAA;AAAA,IACT,aAAe,EAAA,QAAA;AAAA,GACjB;AAAA,EACA,SAAW,EAAA;AAAA,IACT,OAAS,EAAA,MAAA;AAAA,IACT,aAAe,EAAA,KAAA;AAAA,IACf,cAAgB,EAAA,OAAA;AAAA,GAClB;AAAA,EACA,YAAc,EAAA;AAAA,IACZ,WAAA,EAAa,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,GAC9B;AAAA,EACA,WAAa,EAAA;AAAA,IACX,WAAA,EAAa,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,GAC9B;AAAA,EACA,oBAAsB,EAAA;AAAA,IACpB,WAAA,EAAa,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,GAC9B;AACF,CAAE,CAAA,CAAA,CAAA;AAKW,MAAA,WAAA,GAAc,CAAC,KAItB,KAAA;AAEJ,EAAM,MAAA,EAAE,MAAO,EAAA,GAAI,SAAU,EAAA,CAAA;AAC7B,EAAM,MAAA,gBAAA,GAAmB,YAAY,wBAAwB,CAAA,CAAA;AAC7D,EAAA,MAAM,WAAW,WAAY,EAAA,CAAA;AAC7B,EAAA,MAAM,YAAY,YAAa,EAAA,CAAA;AAC/B,EAAM,MAAA,aAAA,GAAgB,OAAO,gBAAgB,CAAA,CAAA;AAC7C,EAAM,MAAA,UAAA,GAAa,mBAAmB,MAAO,CAAA,CAAA;AAC7C,EAAA,MAAM,UAAU,SAAU,EAAA,CAAA;AAC1B,EAAA,MAAM,KAAQ,GAAA,OAAA;AAAA,IACZ,MACE,UAAW,CAAA,IAAA,EAAM,IAAK,CAAA,KAAA,CAAM,IAAI,CAAS,IAAA,MAAA;AAAA,MACvC,GAAG,IAAA;AAAA,MACH,GAAG,UAAA,EAAY,KAAQ,GAAA,IAAA,CAAK,EAAE,CAAA;AAAA,KAChC,CAAE,KAAK,EAAC;AAAA,IACV,CAAC,UAAU,CAAA;AAAA,GACb,CAAA;AACA,EAAA,MAAM,EAAE,CAAA,EAAM,GAAA,iBAAA,CAAkB,wBAAwB,CAAA,CAAA;AAExD,EAAA,MAAM,CAAC,WAAA,EAAa,kBAAkB,CAAA,GAAI,SAAS,KAAK,CAAA,CAAA;AACxD,EAAA,MAAM,CAAC,gBAAA,EAAkB,wBAAwB,CAAA,GAAI,SAAS,IAAI,CAAA,CAAA;AAGlE,EAAA,MAAM,EAAE,OAAA,EAAS,aAAc,EAAA,GAAI,aAAc,CAAA;AAAA,IAC/C,UAAY,EAAA,oBAAA;AAAA,GACb,CAAA,CAAA;AAED,EAAA,MAAM,EAAE,OAAA,EAAS,WAAY,EAAA,GAAI,aAAc,CAAA;AAAA,IAC7C,UAAY,EAAA,kBAAA;AAAA,GACb,CAAA,CAAA;AAED,EAAA,MAAM,EAAE,OAAA,EAAS,aAAc,EAAA,GAAI,aAAc,CAAA;AAAA,IAC/C,UAAY,EAAA,oBAAA;AAAA,GACb,CAAA,CAAA;AAGD,EAAA,MAAM,eAAe,WAAe,IAAA,aAAA,CAAA;AAEpC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,WAAW,KAAO,EAAA;AACpB,MAAA,kBAAA,CAAmB,IAAI,CAAA,CAAA;AAAA,KACzB;AAAA,GACC,EAAA,CAAC,UAAW,CAAA,KAAK,CAAC,CAAA,CAAA;AAErB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,UAAW,CAAA,SAAA,IAAa,CAAC,UAAA,CAAW,KAAO,EAAA;AAC7C,MAAA,wBAAA,CAAyB,KAAK,CAAA,CAAA;AAAA,KAChC;AAAA,KACC,CAAC,UAAA,CAAW,KAAO,EAAA,UAAA,CAAW,SAAS,CAAC,CAAA,CAAA;AAE3C,EAAM,MAAA,UAAA,GAAa,QAAQ,MAAM;AAC/B,IAAA,KAAA,IAAS,IAAI,KAAM,CAAA,MAAA,GAAS,CAAG,EAAA,CAAA,IAAK,GAAG,CAAK,EAAA,EAAA;AAC1C,MAAA,IAAI,KAAM,CAAA,CAAC,CAAE,CAAA,MAAA,KAAW,MAAQ,EAAA;AAC9B,QAAO,OAAA,CAAA,CAAA;AAAA,OACT;AAAA,KACF;AAEA,IAAO,OAAA,CAAA,CAAA;AAAA,GACT,EAAG,CAAC,KAAK,CAAC,CAAA,CAAA;AAEV,EAAA,MAAM,eACJ,GAAA,UAAA,CAAW,IAAM,EAAA,IAAA,CAAK,uBAAuB,qBAC7C,KAAA,WAAA,CAAA;AAEF,EAAM,MAAA,QAAA,GAAW,eAAe,aAAiB,IAAA,eAAA,CAAA;AAEjD,EAAM,MAAA,SAAA,GAAY,YAAY,MAAM;AAClC,IAAM,MAAA,EAAE,SAAW,EAAA,IAAA,EACjB,GAAA,UAAA,CAAW,MAAM,IAAK,CAAA,YAAA,EAAc,MAAQ,EAAA,QAAA,IAAY,EAAC,CAAA;AAE3D,IAAA,MAAM,QAAW,GAAA,UAAA,CAAW,IAAM,EAAA,IAAA,CAAK,cAAc,EAAC,CAAA;AAEtD,IAAI,IAAA,CAAC,SAAa,IAAA,CAAC,IAAM,EAAA;AACvB,MAAA,OAAA;AAAA,KACF;AAEA,IAAU,SAAA,CAAA,YAAA,CAAa,SAAS,CAA4B,0BAAA,CAAA,CAAA,CAAA;AAE5D,IAAS,QAAA,CAAA;AAAA,MACP,UAAU,gBAAiB,CAAA;AAAA,QACzB,SAAA;AAAA,QACA,YAAc,EAAA,IAAA;AAAA,OACf,CAAA;AAAA,MACD,MAAA,EAAQ,CAAI,CAAA,EAAA,EAAA,CAAG,SAAU,CAAA,EAAE,QAAU,EAAA,IAAA,CAAK,SAAU,CAAA,QAAQ,CAAE,EAAC,CAAC,CAAA,CAAA;AAAA,KACjE,CAAA,CAAA;AAAA,GACA,EAAA;AAAA,IACD,SAAA;AAAA,IACA,QAAA;AAAA,IACA,UAAA,CAAW,MAAM,IAAK,CAAA,UAAA;AAAA,IACtB,UAAW,CAAA,IAAA,EAAM,IAAK,CAAA,YAAA,EAAc,MAAQ,EAAA,QAAA;AAAA,IAC5C,gBAAA;AAAA,GACD,CAAA,CAAA;AAED,EAAM,MAAA,CAAC,EAAE,MAAA,EAAQ,CAAE,EAAA,EAAG,EAAE,OAAA,EAAS,YAAa,EAAC,CAAI,GAAA,QAAA,CAAS,YAAY;AACtE,IAAA,IAAI,MAAQ,EAAA;AACV,MAAU,SAAA,CAAA,YAAA,CAAa,WAAW,2BAA2B,CAAA,CAAA;AAC7D,MAAM,MAAA,aAAA,CAAc,QAAQ,MAAM,CAAA,CAAA;AAAA,KACpC;AAAA,GACD,CAAA,CAAA;AAED,EAAM,MAAA,CAAC,EAAE,MAAQ,EAAA,YAAA,IAAgB,EAAE,OAAA,EAAS,aAAc,EAAC,CAAI,GAAA,QAAA;AAAA,IAC7D,YAAY;AACV,MAAA,IAAI,MAAQ,EAAA;AACV,QAAU,SAAA,CAAA,YAAA,CAAa,aAAa,6BAA6B,CAAA,CAAA;AACjE,QAAM,MAAA,aAAA,CAAc,WAAW,MAAM,CAAA,CAAA;AAAA,OACvC;AAAA,KACF;AAAA,GACF,CAAA;AAEA,EAAM,MAAA,OAAA,GAAU,MAAM,wBAA4B,IAAA,sBAAA,CAAA;AAElD,EAAA,MAAM,eACJ,UAAW,CAAA,IAAA,EAAM,KAAK,YAAc,EAAA,MAAA,EAAQ,SAAS,IAAQ,IAAA,EAAA,CAAA;AAE/D,EAAA,MAAM,aAAgB,GAAA,EAAE,UAAW,CAAA,SAAA,IAAa,UAAW,CAAA,SAAA,CAAA,CAAA;AAE3D,EACE,uBAAA,KAAA,CAAA,aAAA,CAAC,IAAK,EAAA,EAAA,OAAA,EAAQ,SACZ,EAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,iBAAA,EACE,eACI,CAAE,CAAA,uCAAA,EAAyC,EAAE,YAAa,EAAC,CAC3D,GAAA,CAAA,CAAE,sCAAsC,CAAA;AAAA,MAE9C,KAAA,kBACG,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA,IAAA,EACE,CAAE,CAAA,mBAAmB,GAAE,GAAC,kBAAA,KAAA,CAAA,aAAA,CAAC,MAAM,EAAA,IAAA,EAAA,YAAa,CAC/C,CAAA;AAAA,MAEF,QAAU,EAAA,CAAA,CAAE,sBAAwB,EAAA,EAAE,QAA0B,CAAA;AAAA,KAAA;AAAA,oBAEhE,KAAA,CAAA,aAAA;AAAA,MAAC,WAAA;AAAA,MAAA;AAAA,QACC,aAAA;AAAA,QACA,QAAA;AAAA,QACA,eAAA;AAAA,QACA,WAAA;AAAA,QACA,gBAAA;AAAA,QACA,WAAa,EAAA,SAAA;AAAA,QACb,OAAS,EAAA,YAAA;AAAA,QACT,YAAc,EAAA,kBAAA;AAAA,QACd,iBAAmB,EAAA,wBAAA;AAAA,QACnB,MAAA;AAAA,OAAA;AAAA,KACF;AAAA,GACF,kBACC,KAAA,CAAA,aAAA,CAAA,OAAA,EAAA,EAAQ,SAAW,EAAA,OAAA,CAAQ,cACzB,EAAA,EAAA,UAAA,CAAW,KACV,mBAAA,KAAA,CAAA,aAAA,CAAC,GAAI,EAAA,EAAA,aAAA,EAAe,CAClB,EAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,UAAA;AAAA,IAAA;AAAA,MACC,OAAO,UAAW,CAAA,KAAA;AAAA,MAClB,WAAY,EAAA,UAAA;AAAA,MACZ,KAAA,EAAO,WAAW,KAAM,CAAA,OAAA;AAAA,KAAA;AAAA,GAE5B,CACE,GAAA,IAAA,kBAEH,KAAA,CAAA,aAAA,CAAA,GAAA,EAAA,EAAI,eAAe,CAClB,EAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,KAAA;AAAA,MACA,UAAA;AAAA,MACA,YAAY,UAAW,CAAA,SAAA;AAAA,MACvB,OAAA,EAAS,OAAQ,CAAA,UAAA,CAAW,KAAK,CAAA;AAAA,KAAA;AAAA,GAErC,mBAEC,KAAA,CAAA,aAAA,CAAA,OAAA,EAAA,EAAQ,QAAQ,UAAW,CAAA,MAAA,EAAQ,CAEnC,EAAA,gBAAA,mBACE,KAAA,CAAA,aAAA,CAAA,GAAA,EAAA,EAAI,eAAe,CAClB,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,KACC,EAAA,IAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,GAAI,EAAA,EAAA,OAAA,EAAS,qBACX,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAI,SAAW,EAAA,OAAA,CAAQ,SACtB,EAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,WAAW,OAAQ,CAAA,YAAA;AAAA,MACnB,UACE,CAAC,aAAA,IACA,iBAAiB,cAAkB,IAAA,CAAC,mBACrC,CAAC,aAAA;AAAA,MAEH,OAAS,EAAA,aAAA;AAAA,MACT,aAAY,EAAA,eAAA;AAAA,KAAA;AAAA,IAEX,EAAE,+BAA+B,CAAA;AAAA,KAEnC,eACC,oBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,WAAW,OAAQ,CAAA,WAAA;AAAA,MACnB,QAAA,EAAU,iBAAiB,CAAC,QAAA;AAAA,MAC5B,OAAS,EAAA,YAAA;AAAA,MACT,aAAY,EAAA,cAAA;AAAA,KAAA;AAAA,IAEX,EAAE,8BAA8B,CAAA;AAAA,GAGrC,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,WAAW,OAAQ,CAAA,oBAAA;AAAA,MACnB,KAAM,EAAA,SAAA;AAAA,MACN,OAAQ,EAAA,UAAA;AAAA,MACR,OAAS,EAAA,MAAM,kBAAmB,CAAA,CAAC,WAAW,CAAA;AAAA,KAAA;AAAA,IAE7C,WACG,GAAA,CAAA,CAAE,iCAAiC,CAAA,GACnC,EAAE,iCAAiC,CAAA;AAAA,GAEzC,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,OAAQ,EAAA,WAAA;AAAA,MACR,KAAM,EAAA,SAAA;AAAA,MACN,QAAA,EAAU,iBAAiB,CAAC,YAAA;AAAA,MAC5B,OAAS,EAAA,SAAA;AAAA,MACT,aAAY,EAAA,mBAAA;AAAA,KAAA;AAAA,IAEX,EAAE,kCAAkC,CAAA;AAAA,GAEzC,CACF,CACF,CACF,CACE,GAAA,IAAA,EAEH,8BACE,KAAA,CAAA,aAAA,CAAA,YAAA,EAAA,EAAa,MAAQ,EAAA,GAAA,EAAK,gBAAgB,CAAC,CAAA,EAAG,GAAG,CAAG,EAAA,IAAA,EAAK,uBACvD,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAM,KAAO,EAAA,EAAE,QAAQ,MAAO,EAAA,EAAA,sCAC5B,GAAI,EAAA,EAAA,OAAA,EAAS,GAAG,MAAO,EAAA,MAAA,EAAA,sCACrB,aAAc,EAAA,EAAA,IAAA,EAAM,WAAW,QAAU,EAAA,CAC5C,CACF,CACF,CAAA,GACE,IACN,CACF,CAAA,CAAA;AAEJ;;;;"}
|
|
@@ -2,14 +2,17 @@ import React from 'react';
|
|
|
2
2
|
import { useOutlet, Routes, Route } from 'react-router-dom';
|
|
3
3
|
import { useCustomFieldExtensions, useCustomLayouts, SecretsContextProvider } from '@backstage/plugin-scaffolder-react';
|
|
4
4
|
import { DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS } from '../../extensions/default.esm.js';
|
|
5
|
-
import { selectedTemplateRouteRef, scaffolderTaskRouteRef, editRouteRef, actionsRouteRef, scaffolderListTaskRouteRef } from '../../routes.esm.js';
|
|
5
|
+
import { selectedTemplateRouteRef, scaffolderTaskRouteRef, editRouteRef, customFieldsRouteRef, templateFormRouteRef, actionsRouteRef, scaffolderListTaskRouteRef, editorRouteRef } from '../../routes.esm.js';
|
|
6
6
|
import { ErrorPage } from '@backstage/core-components';
|
|
7
7
|
import { ActionsPage } from '../ActionsPage/ActionsPage.esm.js';
|
|
8
8
|
import { ListTasksPage } from '../ListTasksPage/ListTasksPage.esm.js';
|
|
9
|
-
import { TemplateListPage } from '../../
|
|
10
|
-
import { TemplateWizardPage } from '../../
|
|
9
|
+
import { TemplateListPage } from '../../alpha/components/TemplateListPage/TemplateListPage.esm.js';
|
|
10
|
+
import { TemplateWizardPage } from '../../alpha/components/TemplateWizardPage/TemplateWizardPage.esm.js';
|
|
11
11
|
import { OngoingTask } from '../OngoingTask/OngoingTask.esm.js';
|
|
12
|
-
import {
|
|
12
|
+
import { TemplatePage } from '../../alpha/components/TemplateEditorPage/TemplatePage.esm.js';
|
|
13
|
+
import { TemplateFormPage } from '../../alpha/components/TemplateEditorPage/TemplateFormPage.esm.js';
|
|
14
|
+
import { TemplateEditorPage } from '../../alpha/components/TemplateEditorPage/TemplateEditorPage.esm.js';
|
|
15
|
+
import { CustomFieldsPage } from '../../alpha/components/TemplateEditorPage/CustomFieldsPage.esm.js';
|
|
13
16
|
|
|
14
17
|
const Router = (props) => {
|
|
15
18
|
const {
|
|
@@ -78,12 +81,24 @@ const Router = (props) => {
|
|
|
78
81
|
Route,
|
|
79
82
|
{
|
|
80
83
|
path: editRouteRef.path,
|
|
84
|
+
element: /* @__PURE__ */ React.createElement(SecretsContextProvider, null, /* @__PURE__ */ React.createElement(TemplateEditorPage, null))
|
|
85
|
+
}
|
|
86
|
+
), /* @__PURE__ */ React.createElement(
|
|
87
|
+
Route,
|
|
88
|
+
{
|
|
89
|
+
path: customFieldsRouteRef.path,
|
|
90
|
+
element: /* @__PURE__ */ React.createElement(SecretsContextProvider, null, /* @__PURE__ */ React.createElement(CustomFieldsPage, { fieldExtensions }))
|
|
91
|
+
}
|
|
92
|
+
), /* @__PURE__ */ React.createElement(
|
|
93
|
+
Route,
|
|
94
|
+
{
|
|
95
|
+
path: templateFormRouteRef.path,
|
|
81
96
|
element: /* @__PURE__ */ React.createElement(SecretsContextProvider, null, /* @__PURE__ */ React.createElement(
|
|
82
|
-
|
|
97
|
+
TemplateFormPage,
|
|
83
98
|
{
|
|
84
|
-
customFieldExtensions: fieldExtensions,
|
|
85
99
|
layouts: customLayouts,
|
|
86
|
-
formProps: props.formProps
|
|
100
|
+
formProps: props.formProps,
|
|
101
|
+
fieldExtensions
|
|
87
102
|
}
|
|
88
103
|
))
|
|
89
104
|
}
|
|
@@ -93,6 +108,19 @@ const Router = (props) => {
|
|
|
93
108
|
path: scaffolderListTaskRouteRef.path,
|
|
94
109
|
element: /* @__PURE__ */ React.createElement(ListTasksPage, null)
|
|
95
110
|
}
|
|
111
|
+
), /* @__PURE__ */ React.createElement(
|
|
112
|
+
Route,
|
|
113
|
+
{
|
|
114
|
+
path: editorRouteRef.path,
|
|
115
|
+
element: /* @__PURE__ */ React.createElement(SecretsContextProvider, null, /* @__PURE__ */ React.createElement(
|
|
116
|
+
TemplatePage,
|
|
117
|
+
{
|
|
118
|
+
layouts: customLayouts,
|
|
119
|
+
formProps: props.formProps,
|
|
120
|
+
fieldExtensions
|
|
121
|
+
}
|
|
122
|
+
))
|
|
123
|
+
}
|
|
96
124
|
), /* @__PURE__ */ React.createElement(
|
|
97
125
|
Route,
|
|
98
126
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Router.esm.js","sources":["../../../src/components/Router/Router.tsx"],"sourcesContent":["/*\n * Copyright 2022 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, { PropsWithChildren } from 'react';\nimport { Routes, Route, useOutlet } from 'react-router-dom';\n\nimport {\n FieldExtensionOptions,\n FormProps,\n ReviewStepProps,\n TemplateGroupFilter,\n} from '@backstage/plugin-scaffolder-react';\nimport {\n ScaffolderTaskOutput,\n SecretsContextProvider,\n useCustomFieldExtensions,\n useCustomLayouts,\n} from '@backstage/plugin-scaffolder-react';\n\nimport { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common';\nimport { DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS } from '../../extensions/default';\n\nimport {\n actionsRouteRef,\n editRouteRef,\n scaffolderListTaskRouteRef,\n scaffolderTaskRouteRef,\n selectedTemplateRouteRef,\n} from '../../routes';\nimport { ErrorPage } from '@backstage/core-components';\n\nimport { ActionsPage } from '../../components/ActionsPage';\nimport { ListTasksPage } from '../../components/ListTasksPage';\n\nimport {\n TemplateListPageProps,\n TemplateWizardPageProps,\n} from '@backstage/plugin-scaffolder/alpha';\nimport { TemplateListPage, TemplateWizardPage } from '../../
|
|
1
|
+
{"version":3,"file":"Router.esm.js","sources":["../../../src/components/Router/Router.tsx"],"sourcesContent":["/*\n * Copyright 2022 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, { PropsWithChildren } from 'react';\nimport { Routes, Route, useOutlet } from 'react-router-dom';\n\nimport {\n FieldExtensionOptions,\n FormProps,\n ReviewStepProps,\n TemplateGroupFilter,\n} from '@backstage/plugin-scaffolder-react';\nimport {\n ScaffolderTaskOutput,\n SecretsContextProvider,\n useCustomFieldExtensions,\n useCustomLayouts,\n} from '@backstage/plugin-scaffolder-react';\n\nimport { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common';\nimport { DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS } from '../../extensions/default';\n\nimport {\n actionsRouteRef,\n editorRouteRef,\n customFieldsRouteRef,\n editRouteRef,\n scaffolderListTaskRouteRef,\n scaffolderTaskRouteRef,\n selectedTemplateRouteRef,\n templateFormRouteRef,\n} from '../../routes';\nimport { ErrorPage } from '@backstage/core-components';\n\nimport { ActionsPage } from '../../components/ActionsPage';\nimport { ListTasksPage } from '../../components/ListTasksPage';\n\nimport {\n TemplateListPageProps,\n TemplateWizardPageProps,\n} from '@backstage/plugin-scaffolder/alpha';\nimport { TemplateListPage, TemplateWizardPage } from '../../alpha/components';\nimport { OngoingTask } from '../OngoingTask';\nimport {\n TemplatePage,\n TemplateFormPage,\n TemplateEditorPage,\n CustomFieldsPage,\n} from '../../alpha/components/TemplateEditorPage';\n\n/**\n * The Props for the Scaffolder Router\n *\n * @public\n */\nexport type RouterProps = {\n components?: {\n ReviewStepComponent?: React.ComponentType<ReviewStepProps>;\n TemplateCardComponent?: React.ComponentType<{\n template: TemplateEntityV1beta3;\n }>;\n TaskPageComponent?: React.ComponentType<PropsWithChildren<{}>>;\n EXPERIMENTAL_TemplateOutputsComponent?: React.ComponentType<{\n output?: ScaffolderTaskOutput;\n }>;\n EXPERIMENTAL_TemplateListPageComponent?: React.ComponentType<TemplateListPageProps>;\n EXPERIMENTAL_TemplateWizardPageComponent?: React.ComponentType<TemplateWizardPageProps>;\n };\n groups?: TemplateGroupFilter[];\n templateFilter?: (entity: TemplateEntityV1beta3) => boolean;\n headerOptions?: {\n pageTitleOverride?: string;\n title?: string;\n subtitle?: string;\n };\n defaultPreviewTemplate?: string;\n formProps?: FormProps;\n contextMenu?: {\n /** Whether to show a link to the template editor */\n editor?: boolean;\n /** Whether to show a link to the actions documentation */\n actions?: boolean;\n /** Whether to show a link to the tasks page */\n tasks?: boolean;\n };\n};\n\n/**\n * The Scaffolder Router\n *\n * @public\n */\nexport const Router = (props: PropsWithChildren<RouterProps>) => {\n const {\n components: {\n TemplateCardComponent,\n TaskPageComponent = OngoingTask,\n ReviewStepComponent,\n EXPERIMENTAL_TemplateOutputsComponent: TemplateOutputsComponent,\n EXPERIMENTAL_TemplateListPageComponent:\n TemplateListPageComponent = TemplateListPage,\n EXPERIMENTAL_TemplateWizardPageComponent:\n TemplateWizardPageComponent = TemplateWizardPage,\n } = {},\n } = props;\n const outlet = useOutlet() || props.children;\n const customFieldExtensions =\n useCustomFieldExtensions<FieldExtensionOptions>(outlet);\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 ] as FieldExtensionOptions[];\n\n const customLayouts = useCustomLayouts(outlet);\n\n return (\n <Routes>\n <Route\n path=\"/\"\n element={\n <TemplateListPageComponent\n TemplateCardComponent={TemplateCardComponent}\n contextMenu={props.contextMenu}\n groups={props.groups}\n templateFilter={props.templateFilter}\n headerOptions={props.headerOptions}\n />\n }\n />\n <Route\n path={selectedTemplateRouteRef.path}\n element={\n <SecretsContextProvider>\n <TemplateWizardPageComponent\n headerOptions={props.headerOptions}\n customFieldExtensions={fieldExtensions}\n layouts={customLayouts}\n components={{ ReviewStepComponent }}\n formProps={props.formProps}\n />\n </SecretsContextProvider>\n }\n />\n <Route\n path={scaffolderTaskRouteRef.path}\n element={\n <TaskPageComponent\n TemplateOutputsComponent={TemplateOutputsComponent}\n />\n }\n />\n <Route\n path={editRouteRef.path}\n element={\n <SecretsContextProvider>\n <TemplateEditorPage />\n </SecretsContextProvider>\n }\n />\n <Route\n path={customFieldsRouteRef.path}\n element={\n <SecretsContextProvider>\n <CustomFieldsPage fieldExtensions={fieldExtensions} />\n </SecretsContextProvider>\n }\n />\n <Route\n path={templateFormRouteRef.path}\n element={\n <SecretsContextProvider>\n <TemplateFormPage\n layouts={customLayouts}\n formProps={props.formProps}\n fieldExtensions={fieldExtensions}\n />\n </SecretsContextProvider>\n }\n />\n\n <Route path={actionsRouteRef.path} element={<ActionsPage />} />\n <Route\n path={scaffolderListTaskRouteRef.path}\n element={<ListTasksPage />}\n />\n <Route\n path={editorRouteRef.path}\n element={\n <SecretsContextProvider>\n <TemplatePage\n layouts={customLayouts}\n formProps={props.formProps}\n fieldExtensions={fieldExtensions}\n />\n </SecretsContextProvider>\n }\n />\n <Route\n path=\"*\"\n element={<ErrorPage status=\"404\" statusMessage=\"Page not found\" />}\n />\n </Routes>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -114,7 +114,7 @@ const EntityPicker = (props) => {
|
|
|
114
114
|
options: entities?.catalogEntities || [],
|
|
115
115
|
getOptionLabel: (option) => (
|
|
116
116
|
// option can be a string due to freeSolo.
|
|
117
|
-
typeof option === "string" ? option : entities?.entityRefToPresentation.get(stringifyEntityRef(option))?.
|
|
117
|
+
typeof option === "string" ? option : entities?.entityRefToPresentation.get(stringifyEntityRef(option))?.primaryTitle
|
|
118
118
|
),
|
|
119
119
|
autoSelect: true,
|
|
120
120
|
freeSolo: allowArbitraryValues,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityPicker.esm.js","sources":["../../../../src/components/fields/EntityPicker/EntityPicker.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 {\n type EntityFilterQuery,\n CATALOG_FILTER_EXISTS,\n} from '@backstage/catalog-client';\nimport {\n Entity,\n parseEntityRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport {\n EntityDisplayName,\n EntityRefPresentationSnapshot,\n catalogApiRef,\n entityPresentationApiRef,\n} from '@backstage/plugin-catalog-react';\nimport TextField from '@material-ui/core/TextField';\nimport FormControl from '@material-ui/core/FormControl';\nimport Autocomplete, {\n AutocompleteChangeReason,\n createFilterOptions,\n} from '@material-ui/lab/Autocomplete';\nimport React, { useCallback, useEffect } from 'react';\nimport useAsync from 'react-use/esm/useAsync';\nimport {\n EntityPickerFilterQueryValue,\n EntityPickerProps,\n EntityPickerUiOptions,\n EntityPickerFilterQuery,\n} from './schema';\nimport { VirtualizedListbox } from '../VirtualizedListbox';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { scaffolderTranslationRef } from '../../../translation';\n\nexport { EntityPickerSchema } from './schema';\n\n/**\n * The underlying component that is rendered in the form for the `EntityPicker`\n * field extension.\n *\n * @public\n */\nexport const EntityPicker = (props: EntityPickerProps) => {\n const { t } = useTranslationRef(scaffolderTranslationRef);\n const {\n onChange,\n schema: {\n title = t('fields.entityPicker.title'),\n description = t('fields.entityPicker.description'),\n },\n required,\n uiSchema,\n rawErrors,\n formData,\n idSchema,\n } = props;\n const catalogFilter = buildCatalogFilter(uiSchema);\n const defaultKind = uiSchema['ui:options']?.defaultKind;\n const defaultNamespace =\n uiSchema['ui:options']?.defaultNamespace || undefined;\n\n const catalogApi = useApi(catalogApiRef);\n const entityPresentationApi = useApi(entityPresentationApiRef);\n\n const { value: entities, loading } = useAsync(async () => {\n const fields = [\n 'metadata.name',\n 'metadata.namespace',\n 'metadata.title',\n 'kind',\n ];\n const { items } = await catalogApi.getEntities(\n catalogFilter\n ? { filter: catalogFilter, fields }\n : { filter: undefined, fields },\n );\n\n const entityRefToPresentation = new Map<\n string,\n EntityRefPresentationSnapshot\n >(\n await Promise.all(\n items.map(async item => {\n const presentation = await entityPresentationApi.forEntity(item)\n .promise;\n return [stringifyEntityRef(item), presentation] as [\n string,\n EntityRefPresentationSnapshot,\n ];\n }),\n ),\n );\n\n return { catalogEntities: items, entityRefToPresentation };\n });\n\n const allowArbitraryValues =\n uiSchema['ui:options']?.allowArbitraryValues ?? true;\n\n const getLabel = useCallback(\n (freeSoloValue: string) => {\n try {\n // Will throw if defaultKind or defaultNamespace are not set\n const parsedRef = parseEntityRef(freeSoloValue, {\n defaultKind,\n defaultNamespace,\n });\n\n return stringifyEntityRef(parsedRef);\n } catch (err) {\n return freeSoloValue;\n }\n },\n [defaultKind, defaultNamespace],\n );\n\n const onSelect = useCallback(\n (_: any, ref: string | Entity | null, reason: AutocompleteChangeReason) => {\n // ref can either be a string from free solo entry or\n if (typeof ref !== 'string') {\n // if ref does not exist: pass 'undefined' to trigger validation for required value\n onChange(ref ? stringifyEntityRef(ref as Entity) : undefined);\n } else {\n if (reason === 'blur' || reason === 'create-option') {\n // Add in default namespace, etc.\n let entityRef = ref;\n try {\n // Attempt to parse the entity ref into it's full form.\n entityRef = stringifyEntityRef(\n parseEntityRef(ref as string, {\n defaultKind,\n defaultNamespace,\n }),\n );\n } catch (err) {\n // If the passed in value isn't an entity ref, do nothing.\n }\n // We need to check against formData here as that's the previous value for this field.\n if (formData !== ref || allowArbitraryValues) {\n onChange(entityRef);\n }\n }\n }\n },\n [onChange, formData, defaultKind, defaultNamespace, allowArbitraryValues],\n );\n\n // Since free solo can be enabled, attempt to parse as a full entity ref first, then fall\n // back to the given value.\n const selectedEntity =\n entities?.catalogEntities.find(e => stringifyEntityRef(e) === formData) ??\n (allowArbitraryValues && formData ? getLabel(formData) : '');\n\n useEffect(() => {\n if (entities?.catalogEntities.length === 1 && selectedEntity === '') {\n onChange(stringifyEntityRef(entities.catalogEntities[0]));\n }\n }, [entities, onChange, selectedEntity]);\n\n return (\n <FormControl\n margin=\"normal\"\n required={required}\n error={rawErrors?.length > 0 && !formData}\n >\n <Autocomplete\n disabled={entities?.catalogEntities.length === 1}\n id={idSchema?.$id}\n value={selectedEntity}\n loading={loading}\n onChange={onSelect}\n options={entities?.catalogEntities || []}\n getOptionLabel={option =>\n // option can be a string due to freeSolo.\n typeof option === 'string'\n ? option\n : entities?.entityRefToPresentation.get(stringifyEntityRef(option))\n ?.entityRef!\n }\n autoSelect\n freeSolo={allowArbitraryValues}\n renderInput={params => (\n <TextField\n {...params}\n label={title}\n margin=\"dense\"\n helperText={description}\n FormHelperTextProps={{ margin: 'dense', style: { marginLeft: 0 } }}\n variant=\"outlined\"\n required={required}\n InputProps={params.InputProps}\n />\n )}\n renderOption={option => <EntityDisplayName entityRef={option} />}\n filterOptions={createFilterOptions<Entity>({\n stringify: option =>\n entities?.entityRefToPresentation.get(stringifyEntityRef(option))\n ?.primaryTitle!,\n })}\n ListboxComponent={VirtualizedListbox}\n />\n </FormControl>\n );\n};\n\n/**\n * Converts a especial `{exists: true}` value to the `CATALOG_FILTER_EXISTS` symbol.\n *\n * @param value - The value to convert.\n * @returns The converted value.\n */\nfunction convertOpsValues(\n value: Exclude<EntityPickerFilterQueryValue, Array<any>>,\n): string | symbol {\n if (typeof value === 'object' && value.exists) {\n return CATALOG_FILTER_EXISTS;\n }\n return value?.toString();\n}\n\n/**\n * Converts schema filters to entity filter query, replacing `{exists:true}` values\n * with the constant `CATALOG_FILTER_EXISTS`.\n *\n * @param schemaFilters - An object containing schema filters with keys as filter names\n * and values as filter values.\n * @returns An object with the same keys as the input object, but with `{exists:true}` values\n * transformed to `CATALOG_FILTER_EXISTS` symbol.\n */\nfunction convertSchemaFiltersToQuery(\n schemaFilters: EntityPickerFilterQuery,\n): Exclude<EntityFilterQuery, Array<any>> {\n const query: EntityFilterQuery = {};\n\n for (const [key, value] of Object.entries(schemaFilters)) {\n if (Array.isArray(value)) {\n query[key] = value;\n } else {\n query[key] = convertOpsValues(value);\n }\n }\n\n return query;\n}\n\n/**\n * Builds an `EntityFilterQuery` based on the `uiSchema` passed in.\n * If `catalogFilter` is specified in the `uiSchema`, it is converted to a `EntityFilterQuery`.\n * If `allowedKinds` is specified in the `uiSchema` will support the legacy `allowedKinds` option.\n *\n * @param uiSchema The `uiSchema` of an `EntityPicker` component.\n * @returns An `EntityFilterQuery` based on the `uiSchema`, or `undefined` if `catalogFilter` is not specified in the `uiSchema`.\n */\nfunction buildCatalogFilter(\n uiSchema: EntityPickerProps['uiSchema'],\n): EntityFilterQuery | undefined {\n const allowedKinds = uiSchema['ui:options']?.allowedKinds;\n\n const catalogFilter: EntityPickerUiOptions['catalogFilter'] | undefined =\n uiSchema['ui:options']?.catalogFilter ||\n (allowedKinds && { kind: allowedKinds });\n\n if (!catalogFilter) {\n return undefined;\n }\n\n if (Array.isArray(catalogFilter)) {\n return catalogFilter.map(convertSchemaFiltersToQuery);\n }\n\n return convertSchemaFiltersToQuery(catalogFilter);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EntityPicker.esm.js","sources":["../../../../src/components/fields/EntityPicker/EntityPicker.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 {\n type EntityFilterQuery,\n CATALOG_FILTER_EXISTS,\n} from '@backstage/catalog-client';\nimport {\n Entity,\n parseEntityRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport {\n EntityDisplayName,\n EntityRefPresentationSnapshot,\n catalogApiRef,\n entityPresentationApiRef,\n} from '@backstage/plugin-catalog-react';\nimport TextField from '@material-ui/core/TextField';\nimport FormControl from '@material-ui/core/FormControl';\nimport Autocomplete, {\n AutocompleteChangeReason,\n createFilterOptions,\n} from '@material-ui/lab/Autocomplete';\nimport React, { useCallback, useEffect } from 'react';\nimport useAsync from 'react-use/esm/useAsync';\nimport {\n EntityPickerFilterQueryValue,\n EntityPickerProps,\n EntityPickerUiOptions,\n EntityPickerFilterQuery,\n} from './schema';\nimport { VirtualizedListbox } from '../VirtualizedListbox';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { scaffolderTranslationRef } from '../../../translation';\n\nexport { EntityPickerSchema } from './schema';\n\n/**\n * The underlying component that is rendered in the form for the `EntityPicker`\n * field extension.\n *\n * @public\n */\nexport const EntityPicker = (props: EntityPickerProps) => {\n const { t } = useTranslationRef(scaffolderTranslationRef);\n const {\n onChange,\n schema: {\n title = t('fields.entityPicker.title'),\n description = t('fields.entityPicker.description'),\n },\n required,\n uiSchema,\n rawErrors,\n formData,\n idSchema,\n } = props;\n const catalogFilter = buildCatalogFilter(uiSchema);\n const defaultKind = uiSchema['ui:options']?.defaultKind;\n const defaultNamespace =\n uiSchema['ui:options']?.defaultNamespace || undefined;\n\n const catalogApi = useApi(catalogApiRef);\n const entityPresentationApi = useApi(entityPresentationApiRef);\n\n const { value: entities, loading } = useAsync(async () => {\n const fields = [\n 'metadata.name',\n 'metadata.namespace',\n 'metadata.title',\n 'kind',\n ];\n const { items } = await catalogApi.getEntities(\n catalogFilter\n ? { filter: catalogFilter, fields }\n : { filter: undefined, fields },\n );\n\n const entityRefToPresentation = new Map<\n string,\n EntityRefPresentationSnapshot\n >(\n await Promise.all(\n items.map(async item => {\n const presentation = await entityPresentationApi.forEntity(item)\n .promise;\n return [stringifyEntityRef(item), presentation] as [\n string,\n EntityRefPresentationSnapshot,\n ];\n }),\n ),\n );\n\n return { catalogEntities: items, entityRefToPresentation };\n });\n\n const allowArbitraryValues =\n uiSchema['ui:options']?.allowArbitraryValues ?? true;\n\n const getLabel = useCallback(\n (freeSoloValue: string) => {\n try {\n // Will throw if defaultKind or defaultNamespace are not set\n const parsedRef = parseEntityRef(freeSoloValue, {\n defaultKind,\n defaultNamespace,\n });\n\n return stringifyEntityRef(parsedRef);\n } catch (err) {\n return freeSoloValue;\n }\n },\n [defaultKind, defaultNamespace],\n );\n\n const onSelect = useCallback(\n (_: any, ref: string | Entity | null, reason: AutocompleteChangeReason) => {\n // ref can either be a string from free solo entry or\n if (typeof ref !== 'string') {\n // if ref does not exist: pass 'undefined' to trigger validation for required value\n onChange(ref ? stringifyEntityRef(ref as Entity) : undefined);\n } else {\n if (reason === 'blur' || reason === 'create-option') {\n // Add in default namespace, etc.\n let entityRef = ref;\n try {\n // Attempt to parse the entity ref into it's full form.\n entityRef = stringifyEntityRef(\n parseEntityRef(ref as string, {\n defaultKind,\n defaultNamespace,\n }),\n );\n } catch (err) {\n // If the passed in value isn't an entity ref, do nothing.\n }\n // We need to check against formData here as that's the previous value for this field.\n if (formData !== ref || allowArbitraryValues) {\n onChange(entityRef);\n }\n }\n }\n },\n [onChange, formData, defaultKind, defaultNamespace, allowArbitraryValues],\n );\n\n // Since free solo can be enabled, attempt to parse as a full entity ref first, then fall\n // back to the given value.\n const selectedEntity =\n entities?.catalogEntities.find(e => stringifyEntityRef(e) === formData) ??\n (allowArbitraryValues && formData ? getLabel(formData) : '');\n\n useEffect(() => {\n if (entities?.catalogEntities.length === 1 && selectedEntity === '') {\n onChange(stringifyEntityRef(entities.catalogEntities[0]));\n }\n }, [entities, onChange, selectedEntity]);\n\n return (\n <FormControl\n margin=\"normal\"\n required={required}\n error={rawErrors?.length > 0 && !formData}\n >\n <Autocomplete\n disabled={entities?.catalogEntities.length === 1}\n id={idSchema?.$id}\n value={selectedEntity}\n loading={loading}\n onChange={onSelect}\n options={entities?.catalogEntities || []}\n getOptionLabel={option =>\n // option can be a string due to freeSolo.\n typeof option === 'string'\n ? option\n : entities?.entityRefToPresentation.get(stringifyEntityRef(option))\n ?.primaryTitle!\n }\n autoSelect\n freeSolo={allowArbitraryValues}\n renderInput={params => (\n <TextField\n {...params}\n label={title}\n margin=\"dense\"\n helperText={description}\n FormHelperTextProps={{ margin: 'dense', style: { marginLeft: 0 } }}\n variant=\"outlined\"\n required={required}\n InputProps={params.InputProps}\n />\n )}\n renderOption={option => <EntityDisplayName entityRef={option} />}\n filterOptions={createFilterOptions<Entity>({\n stringify: option =>\n entities?.entityRefToPresentation.get(stringifyEntityRef(option))\n ?.primaryTitle!,\n })}\n ListboxComponent={VirtualizedListbox}\n />\n </FormControl>\n );\n};\n\n/**\n * Converts a especial `{exists: true}` value to the `CATALOG_FILTER_EXISTS` symbol.\n *\n * @param value - The value to convert.\n * @returns The converted value.\n */\nfunction convertOpsValues(\n value: Exclude<EntityPickerFilterQueryValue, Array<any>>,\n): string | symbol {\n if (typeof value === 'object' && value.exists) {\n return CATALOG_FILTER_EXISTS;\n }\n return value?.toString();\n}\n\n/**\n * Converts schema filters to entity filter query, replacing `{exists:true}` values\n * with the constant `CATALOG_FILTER_EXISTS`.\n *\n * @param schemaFilters - An object containing schema filters with keys as filter names\n * and values as filter values.\n * @returns An object with the same keys as the input object, but with `{exists:true}` values\n * transformed to `CATALOG_FILTER_EXISTS` symbol.\n */\nfunction convertSchemaFiltersToQuery(\n schemaFilters: EntityPickerFilterQuery,\n): Exclude<EntityFilterQuery, Array<any>> {\n const query: EntityFilterQuery = {};\n\n for (const [key, value] of Object.entries(schemaFilters)) {\n if (Array.isArray(value)) {\n query[key] = value;\n } else {\n query[key] = convertOpsValues(value);\n }\n }\n\n return query;\n}\n\n/**\n * Builds an `EntityFilterQuery` based on the `uiSchema` passed in.\n * If `catalogFilter` is specified in the `uiSchema`, it is converted to a `EntityFilterQuery`.\n * If `allowedKinds` is specified in the `uiSchema` will support the legacy `allowedKinds` option.\n *\n * @param uiSchema The `uiSchema` of an `EntityPicker` component.\n * @returns An `EntityFilterQuery` based on the `uiSchema`, or `undefined` if `catalogFilter` is not specified in the `uiSchema`.\n */\nfunction buildCatalogFilter(\n uiSchema: EntityPickerProps['uiSchema'],\n): EntityFilterQuery | undefined {\n const allowedKinds = uiSchema['ui:options']?.allowedKinds;\n\n const catalogFilter: EntityPickerUiOptions['catalogFilter'] | undefined =\n uiSchema['ui:options']?.catalogFilter ||\n (allowedKinds && { kind: allowedKinds });\n\n if (!catalogFilter) {\n return undefined;\n }\n\n if (Array.isArray(catalogFilter)) {\n return catalogFilter.map(convertSchemaFiltersToQuery);\n }\n\n return convertSchemaFiltersToQuery(catalogFilter);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultiEntityPicker.esm.js","sources":["../../../../src/components/fields/MultiEntityPicker/MultiEntityPicker.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 {\n type EntityFilterQuery,\n CATALOG_FILTER_EXISTS,\n} from '@backstage/catalog-client';\nimport {\n Entity,\n parseEntityRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport {\n catalogApiRef,\n entityPresentationApiRef,\n EntityDisplayName,\n EntityRefPresentationSnapshot,\n} from '@backstage/plugin-catalog-react';\nimport TextField from '@material-ui/core/TextField';\nimport FormControl from '@material-ui/core/FormControl';\nimport Autocomplete, {\n AutocompleteChangeReason,\n} from '@material-ui/lab/Autocomplete';\nimport React, { useCallback, useEffect, useState } from 'react';\nimport useAsync from 'react-use/esm/useAsync';\nimport { FieldValidation } from '@rjsf/utils';\nimport {\n MultiEntityPickerFilterQueryValue,\n MultiEntityPickerProps,\n MultiEntityPickerUiOptions,\n MultiEntityPickerFilterQuery,\n} from './schema';\nimport { VirtualizedListbox } from '../VirtualizedListbox';\n\nexport { MultiEntityPickerSchema } from './schema';\n\n/**\n * The underlying component that is rendered in the form for the `MultiEntityPicker`\n * field extension.\n */\nexport const MultiEntityPicker = (props: MultiEntityPickerProps) => {\n const {\n onChange,\n schema: { title = 'Entity', description = 'An entity from the catalog' },\n required,\n uiSchema,\n rawErrors,\n formData,\n idSchema,\n } = props;\n const catalogFilter = buildCatalogFilter(uiSchema);\n const defaultKind = uiSchema['ui:options']?.defaultKind;\n const defaultNamespace =\n uiSchema['ui:options']?.defaultNamespace || undefined;\n const [noOfItemsSelected, setNoOfItemsSelected] = useState(0);\n\n const catalogApi = useApi(catalogApiRef);\n const entityPresentationApi = useApi(entityPresentationApiRef);\n const { value: entities, loading } = useAsync(async () => {\n const { items } = await catalogApi.getEntities(\n catalogFilter ? { filter: catalogFilter } : undefined,\n );\n const entityRefToPresentation = new Map<\n string,\n EntityRefPresentationSnapshot\n >(\n await Promise.all(\n items.map(async item => {\n const presentation = await entityPresentationApi.forEntity(item)\n .promise;\n return [stringifyEntityRef(item), presentation] as [\n string,\n EntityRefPresentationSnapshot,\n ];\n }),\n ),\n );\n return { entities: items, entityRefToPresentation };\n });\n const allowArbitraryValues =\n uiSchema['ui:options']?.allowArbitraryValues ?? true;\n\n // if not specified, maxItems defaults to undefined\n const maxItems = props.schema.maxItems;\n\n const onSelect = useCallback(\n (_: any, refs: (string | Entity)[], reason: AutocompleteChangeReason) => {\n const values = refs\n .map(ref => {\n if (typeof ref !== 'string') {\n // if ref does not exist: pass 'undefined' to trigger validation for required value\n return ref ? stringifyEntityRef(ref as Entity) : undefined;\n }\n if (reason === 'blur' || reason === 'create-option') {\n // Add in default namespace, etc.\n let entityRef = ref;\n try {\n // Attempt to parse the entity ref into it's full form.\n entityRef = stringifyEntityRef(\n parseEntityRef(ref as string, {\n defaultKind,\n defaultNamespace,\n }),\n );\n } catch (err) {\n // If the passed in value isn't an entity ref, do nothing.\n }\n\n // We need to check against formData here as that's the previous value for this field.\n if (formData?.includes(ref) || allowArbitraryValues) {\n return entityRef;\n }\n }\n\n return undefined;\n })\n .filter(ref => ref !== undefined) as string[];\n\n setNoOfItemsSelected(values.length);\n onChange(values);\n },\n [onChange, formData, defaultKind, defaultNamespace, allowArbitraryValues],\n );\n\n useEffect(() => {\n if (entities?.entities?.length === 1) {\n onChange([stringifyEntityRef(entities?.entities[0])]);\n }\n }, [entities, onChange]);\n\n return (\n <FormControl\n margin=\"normal\"\n required={required}\n error={rawErrors?.length > 0 && !formData}\n >\n <Autocomplete\n multiple\n filterSelectedOptions\n disabled={entities?.entities?.length === 1}\n id={idSchema?.$id}\n loading={loading}\n onChange={onSelect}\n options={entities?.entities || []}\n renderOption={option => <EntityDisplayName entityRef={option} />}\n getOptionLabel={option =>\n // option can be a string due to freeSolo.\n typeof option === 'string'\n ? option\n : entities?.entityRefToPresentation.get(stringifyEntityRef(option))\n ?.entityRef!\n }\n getOptionDisabled={_options =>\n maxItems ? noOfItemsSelected >= maxItems : false\n }\n autoSelect\n freeSolo={allowArbitraryValues}\n renderInput={params => (\n <TextField\n {...params}\n label={title}\n margin=\"dense\"\n helperText={description}\n FormHelperTextProps={{\n margin: 'dense',\n style: { marginLeft: 0 },\n }}\n variant=\"outlined\"\n required={required}\n InputProps={{\n ...params.InputProps,\n required: formData?.length === 0 && required,\n }}\n />\n )}\n ListboxComponent={VirtualizedListbox}\n />\n </FormControl>\n );\n};\n\nexport const validateMultiEntityPickerValidation = (\n values: string[],\n validation: FieldValidation,\n) => {\n values.forEach(value => {\n try {\n parseEntityRef(value);\n } catch {\n validation.addError(`${value} is not a valid entity ref`);\n }\n });\n};\n\n/**\n * Converts a special `{exists: true}` value to the `CATALOG_FILTER_EXISTS` symbol.\n *\n * @param value - The value to convert.\n * @returns The converted value.\n */\nfunction convertOpsValues(\n value: Exclude<MultiEntityPickerFilterQueryValue, Array<any>>,\n): string | symbol {\n if (typeof value === 'object' && value.exists) {\n return CATALOG_FILTER_EXISTS;\n }\n return value?.toString();\n}\n\n/**\n * Converts schema filters to entity filter query, replacing `{exists:true}` values\n * with the constant `CATALOG_FILTER_EXISTS`.\n *\n * @param schemaFilters - An object containing schema filters with keys as filter names\n * and values as filter values.\n * @returns An object with the same keys as the input object, but with `{exists:true}` values\n * transformed to `CATALOG_FILTER_EXISTS` symbol.\n */\nfunction convertSchemaFiltersToQuery(\n schemaFilters: MultiEntityPickerFilterQuery,\n): Exclude<EntityFilterQuery, Array<any>> {\n const query: EntityFilterQuery = {};\n\n for (const [key, value] of Object.entries(schemaFilters)) {\n if (Array.isArray(value)) {\n query[key] = value;\n } else {\n query[key] = convertOpsValues(value);\n }\n }\n\n return query;\n}\n\n/**\n * Builds an `EntityFilterQuery` based on the `uiSchema` passed in.\n * If `catalogFilter` is specified in the `uiSchema`, it is converted to a `EntityFilterQuery`.\n *\n * @param uiSchema The `uiSchema` of an `EntityPicker` component.\n * @returns An `EntityFilterQuery` based on the `uiSchema`, or `undefined` if `catalogFilter` is not specified in the `uiSchema`.\n */\nfunction buildCatalogFilter(\n uiSchema: MultiEntityPickerProps['uiSchema'],\n): EntityFilterQuery | undefined {\n const catalogFilter: MultiEntityPickerUiOptions['catalogFilter'] | undefined =\n uiSchema['ui:options']?.catalogFilter;\n\n if (!catalogFilter) {\n return undefined;\n }\n\n if (Array.isArray(catalogFilter)) {\n return catalogFilter.map(convertSchemaFiltersToQuery);\n }\n\n return convertSchemaFiltersToQuery(catalogFilter);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MultiEntityPicker.esm.js","sources":["../../../../src/components/fields/MultiEntityPicker/MultiEntityPicker.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 {\n type EntityFilterQuery,\n CATALOG_FILTER_EXISTS,\n} from '@backstage/catalog-client';\nimport {\n Entity,\n parseEntityRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport {\n catalogApiRef,\n entityPresentationApiRef,\n EntityDisplayName,\n EntityRefPresentationSnapshot,\n} from '@backstage/plugin-catalog-react';\nimport TextField from '@material-ui/core/TextField';\nimport FormControl from '@material-ui/core/FormControl';\nimport Autocomplete, {\n AutocompleteChangeReason,\n} from '@material-ui/lab/Autocomplete';\nimport React, { useCallback, useEffect, useState } from 'react';\nimport useAsync from 'react-use/esm/useAsync';\nimport { FieldValidation } from '@rjsf/utils';\nimport {\n MultiEntityPickerFilterQueryValue,\n MultiEntityPickerProps,\n MultiEntityPickerUiOptions,\n MultiEntityPickerFilterQuery,\n} from './schema';\nimport { VirtualizedListbox } from '../VirtualizedListbox';\n\nexport { MultiEntityPickerSchema } from './schema';\n\n/**\n * The underlying component that is rendered in the form for the `MultiEntityPicker`\n * field extension.\n */\nexport const MultiEntityPicker = (props: MultiEntityPickerProps) => {\n const {\n onChange,\n schema: { title = 'Entity', description = 'An entity from the catalog' },\n required,\n uiSchema,\n rawErrors,\n formData,\n idSchema,\n } = props;\n const catalogFilter = buildCatalogFilter(uiSchema);\n const defaultKind = uiSchema['ui:options']?.defaultKind;\n const defaultNamespace =\n uiSchema['ui:options']?.defaultNamespace || undefined;\n const [noOfItemsSelected, setNoOfItemsSelected] = useState(0);\n\n const catalogApi = useApi(catalogApiRef);\n const entityPresentationApi = useApi(entityPresentationApiRef);\n const { value: entities, loading } = useAsync(async () => {\n const { items } = await catalogApi.getEntities(\n catalogFilter ? { filter: catalogFilter } : undefined,\n );\n const entityRefToPresentation = new Map<\n string,\n EntityRefPresentationSnapshot\n >(\n await Promise.all(\n items.map(async item => {\n const presentation = await entityPresentationApi.forEntity(item)\n .promise;\n return [stringifyEntityRef(item), presentation] as [\n string,\n EntityRefPresentationSnapshot,\n ];\n }),\n ),\n );\n return { entities: items, entityRefToPresentation };\n });\n const allowArbitraryValues =\n uiSchema['ui:options']?.allowArbitraryValues ?? true;\n\n // if not specified, maxItems defaults to undefined\n const maxItems = props.schema.maxItems;\n\n const onSelect = useCallback(\n (_: any, refs: (string | Entity)[], reason: AutocompleteChangeReason) => {\n const values = refs\n .map(ref => {\n if (typeof ref !== 'string') {\n // if ref does not exist: pass 'undefined' to trigger validation for required value\n return ref ? stringifyEntityRef(ref as Entity) : undefined;\n }\n if (reason === 'blur' || reason === 'create-option') {\n // Add in default namespace, etc.\n let entityRef = ref;\n try {\n // Attempt to parse the entity ref into it's full form.\n entityRef = stringifyEntityRef(\n parseEntityRef(ref as string, {\n defaultKind,\n defaultNamespace,\n }),\n );\n } catch (err) {\n // If the passed in value isn't an entity ref, do nothing.\n }\n\n // We need to check against formData here as that's the previous value for this field.\n if (formData?.includes(ref) || allowArbitraryValues) {\n return entityRef;\n }\n }\n\n return undefined;\n })\n .filter(ref => ref !== undefined) as string[];\n\n setNoOfItemsSelected(values.length);\n onChange(values);\n },\n [onChange, formData, defaultKind, defaultNamespace, allowArbitraryValues],\n );\n\n useEffect(() => {\n if (entities?.entities?.length === 1) {\n onChange([stringifyEntityRef(entities?.entities[0])]);\n }\n }, [entities, onChange]);\n\n return (\n <FormControl\n margin=\"normal\"\n required={required}\n error={rawErrors?.length > 0 && !formData}\n >\n <Autocomplete\n multiple\n filterSelectedOptions\n disabled={entities?.entities?.length === 1}\n id={idSchema?.$id}\n defaultValue={formData}\n loading={loading}\n onChange={onSelect}\n options={entities?.entities || []}\n renderOption={option => <EntityDisplayName entityRef={option} />}\n getOptionLabel={option =>\n // option can be a string due to freeSolo.\n typeof option === 'string'\n ? option\n : entities?.entityRefToPresentation.get(stringifyEntityRef(option))\n ?.entityRef!\n }\n getOptionDisabled={_options =>\n maxItems ? noOfItemsSelected >= maxItems : false\n }\n autoSelect\n freeSolo={allowArbitraryValues}\n renderInput={params => (\n <TextField\n {...params}\n label={title}\n margin=\"dense\"\n helperText={description}\n FormHelperTextProps={{\n margin: 'dense',\n style: { marginLeft: 0 },\n }}\n variant=\"outlined\"\n required={required}\n InputProps={{\n ...params.InputProps,\n required: formData?.length === 0 && required,\n }}\n />\n )}\n ListboxComponent={VirtualizedListbox}\n />\n </FormControl>\n );\n};\n\nexport const validateMultiEntityPickerValidation = (\n values: string[],\n validation: FieldValidation,\n) => {\n values.forEach(value => {\n try {\n parseEntityRef(value);\n } catch {\n validation.addError(`${value} is not a valid entity ref`);\n }\n });\n};\n\n/**\n * Converts a special `{exists: true}` value to the `CATALOG_FILTER_EXISTS` symbol.\n *\n * @param value - The value to convert.\n * @returns The converted value.\n */\nfunction convertOpsValues(\n value: Exclude<MultiEntityPickerFilterQueryValue, Array<any>>,\n): string | symbol {\n if (typeof value === 'object' && value.exists) {\n return CATALOG_FILTER_EXISTS;\n }\n return value?.toString();\n}\n\n/**\n * Converts schema filters to entity filter query, replacing `{exists:true}` values\n * with the constant `CATALOG_FILTER_EXISTS`.\n *\n * @param schemaFilters - An object containing schema filters with keys as filter names\n * and values as filter values.\n * @returns An object with the same keys as the input object, but with `{exists:true}` values\n * transformed to `CATALOG_FILTER_EXISTS` symbol.\n */\nfunction convertSchemaFiltersToQuery(\n schemaFilters: MultiEntityPickerFilterQuery,\n): Exclude<EntityFilterQuery, Array<any>> {\n const query: EntityFilterQuery = {};\n\n for (const [key, value] of Object.entries(schemaFilters)) {\n if (Array.isArray(value)) {\n query[key] = value;\n } else {\n query[key] = convertOpsValues(value);\n }\n }\n\n return query;\n}\n\n/**\n * Builds an `EntityFilterQuery` based on the `uiSchema` passed in.\n * If `catalogFilter` is specified in the `uiSchema`, it is converted to a `EntityFilterQuery`.\n *\n * @param uiSchema The `uiSchema` of an `EntityPicker` component.\n * @returns An `EntityFilterQuery` based on the `uiSchema`, or `undefined` if `catalogFilter` is not specified in the `uiSchema`.\n */\nfunction buildCatalogFilter(\n uiSchema: MultiEntityPickerProps['uiSchema'],\n): EntityFilterQuery | undefined {\n const catalogFilter: MultiEntityPickerUiOptions['catalogFilter'] | undefined =\n uiSchema['ui:options']?.catalogFilter;\n\n if (!catalogFilter) {\n return undefined;\n }\n\n if (Array.isArray(catalogFilter)) {\n return catalogFilter.map(convertSchemaFiltersToQuery);\n }\n\n return convertSchemaFiltersToQuery(catalogFilter);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RepoUrlPicker.esm.js","sources":["../../../../src/components/fields/RepoUrlPicker/RepoUrlPicker.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 { useApi } from '@backstage/core-plugin-api';\nimport {\n scmIntegrationsApiRef,\n scmAuthApiRef,\n} from '@backstage/integration-react';\nimport React, { useEffect, useState, useMemo, useCallback } from 'react';\nimport { GithubRepoPicker } from './GithubRepoPicker';\nimport { GiteaRepoPicker } from './GiteaRepoPicker';\nimport { GitlabRepoPicker } from './GitlabRepoPicker';\nimport { AzureRepoPicker } from './AzureRepoPicker';\nimport { BitbucketRepoPicker } from './BitbucketRepoPicker';\nimport { GerritRepoPicker } from './GerritRepoPicker';\nimport { RepoUrlPickerHost } from './RepoUrlPickerHost';\nimport { RepoUrlPickerRepoName } from './RepoUrlPickerRepoName';\nimport { parseRepoPickerUrl, serializeRepoPickerUrl } from './utils';\nimport { RepoUrlPickerProps } from './schema';\nimport { RepoUrlPickerState } from './types';\nimport useDebounce from 'react-use/esm/useDebounce';\nimport { useTemplateSecrets } from '@backstage/plugin-scaffolder-react';\nimport Box from '@material-ui/core/Box';\nimport Divider from '@material-ui/core/Divider';\nimport Typography from '@material-ui/core/Typography';\n\nexport { RepoUrlPickerSchema } from './schema';\n\n/**\n * The underlying component that is rendered in the form for the `RepoUrlPicker`\n * field extension.\n *\n * @public\n */\nexport const RepoUrlPicker = (props: RepoUrlPickerProps) => {\n const { uiSchema, onChange, rawErrors, formData, schema } = props;\n const [state, setState] = useState<RepoUrlPickerState>(\n parseRepoPickerUrl(formData),\n );\n const [credentialsHost, setCredentialsHost] = useState<string | undefined>(\n undefined,\n );\n const integrationApi = useApi(scmIntegrationsApiRef);\n const scmAuthApi = useApi(scmAuthApiRef);\n const { secrets, setSecrets } = useTemplateSecrets();\n const allowedHosts = useMemo(\n () => uiSchema?.['ui:options']?.allowedHosts ?? [],\n [uiSchema],\n );\n const allowedOrganizations = useMemo(\n () => uiSchema?.['ui:options']?.allowedOrganizations ?? [],\n [uiSchema],\n );\n const allowedOwners = useMemo(\n () => uiSchema?.['ui:options']?.allowedOwners ?? [],\n [uiSchema],\n );\n const allowedProjects = useMemo(\n () => uiSchema?.['ui:options']?.allowedProjects ?? [],\n [uiSchema],\n );\n const allowedRepos = useMemo(\n () => uiSchema?.['ui:options']?.allowedRepos ?? [],\n [uiSchema],\n );\n\n const { owner, organization, project, repoName } = state;\n\n useEffect(() => {\n onChange(serializeRepoPickerUrl(state));\n }, [state, onChange]);\n\n /* we deal with calling the repo setting here instead of in each components for ease */\n useEffect(() => {\n if (allowedOrganizations.length > 0 && !organization) {\n setState(prevState => ({\n ...prevState,\n organization: allowedOrganizations[0],\n }));\n }\n }, [setState, allowedOrganizations, organization]);\n\n useEffect(() => {\n if (allowedOwners.length > 0 && !owner) {\n setState(prevState => ({\n ...prevState,\n owner: allowedOwners[0],\n }));\n }\n }, [setState, allowedOwners, owner]);\n\n useEffect(() => {\n if (allowedProjects.length > 0 && !project) {\n setState(prevState => ({\n ...prevState,\n project: allowedProjects[0],\n }));\n }\n }, [setState, allowedProjects, project]);\n\n useEffect(() => {\n if (allowedRepos.length > 0 && !repoName) {\n setState(prevState => ({ ...prevState, repoName: allowedRepos[0] }));\n }\n }, [setState, allowedRepos, repoName]);\n\n const updateLocalState = useCallback(\n (newState: RepoUrlPickerState) => {\n setState(prevState => ({ ...prevState, ...newState }));\n },\n [setState],\n );\n\n useDebounce(\n async () => {\n const { requestUserCredentials } = uiSchema?.['ui:options'] ?? {};\n\n if (!requestUserCredentials || !state.host) {\n return;\n }\n\n // don't show login prompt if secret value is already in state for selected host\n if (\n secrets[requestUserCredentials.secretsKey] &&\n credentialsHost === state.host\n ) {\n return;\n }\n\n // user has requested that we use the users credentials\n // so lets grab them using the scmAuthApi and pass through\n // any additional scopes from the ui:options\n const { token } = await scmAuthApi.getCredentials({\n url: `https://${state.host}`,\n additionalScope: {\n repoWrite: true,\n customScopes: requestUserCredentials.additionalScopes,\n },\n });\n\n // set the secret using the key provided in the ui:options for use\n // in the templating the manifest with ${{ secrets[secretsKey] }}\n setSecrets({ [requestUserCredentials.secretsKey]: token });\n setCredentialsHost(state.host);\n },\n 500,\n [state, uiSchema],\n );\n\n const hostType =\n (state.host && integrationApi.byHost(state.host)?.type) ?? null;\n return (\n <>\n {schema.title && (\n <Box my={1}>\n <Typography variant=\"h5\">{schema.title}</Typography>\n <Divider />\n </Box>\n )}\n {schema.description && (\n <Typography variant=\"body1\">{schema.description}</Typography>\n )}\n <RepoUrlPickerHost\n host={state.host}\n hosts={allowedHosts}\n onChange={host => setState(prevState => ({ ...prevState, host }))}\n rawErrors={rawErrors}\n />\n {hostType === 'github' && (\n <GithubRepoPicker\n allowedOwners={allowedOwners}\n onChange={updateLocalState}\n rawErrors={rawErrors}\n state={state}\n />\n )}\n {hostType === 'gitea' && (\n <GiteaRepoPicker\n allowedOwners={allowedOwners}\n allowedRepos={allowedRepos}\n rawErrors={rawErrors}\n state={state}\n onChange={updateLocalState}\n />\n )}\n {hostType === 'gitlab' && (\n <GitlabRepoPicker\n allowedOwners={allowedOwners}\n rawErrors={rawErrors}\n state={state}\n onChange={updateLocalState}\n />\n )}\n {hostType === 'bitbucket' && (\n <BitbucketRepoPicker\n allowedOwners={allowedOwners}\n allowedProjects={allowedProjects}\n rawErrors={rawErrors}\n state={state}\n onChange={updateLocalState}\n accessToken={\n uiSchema?.['ui:options']?.requestUserCredentials?.secretsKey &&\n secrets[uiSchema['ui:options'].requestUserCredentials.secretsKey]\n }\n />\n )}\n {hostType === 'azure' && (\n <AzureRepoPicker\n allowedOrganizations={allowedOrganizations}\n allowedProject={allowedProjects}\n rawErrors={rawErrors}\n state={state}\n onChange={updateLocalState}\n />\n )}\n {hostType === 'gerrit' && (\n <GerritRepoPicker\n rawErrors={rawErrors}\n state={state}\n onChange={updateLocalState}\n />\n )}\n <RepoUrlPickerRepoName\n repoName={state.repoName}\n allowedRepos={allowedRepos}\n onChange={repo =>\n setState(prevState => ({ ...prevState, repoName: repo }))\n }\n rawErrors={rawErrors}\n availableRepos={state.availableRepos}\n />\n </>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RepoUrlPicker.esm.js","sources":["../../../../src/components/fields/RepoUrlPicker/RepoUrlPicker.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 { useApi } from '@backstage/core-plugin-api';\nimport {\n scmIntegrationsApiRef,\n scmAuthApiRef,\n} from '@backstage/integration-react';\nimport React, { useEffect, useState, useMemo, useCallback } from 'react';\nimport { GithubRepoPicker } from './GithubRepoPicker';\nimport { GiteaRepoPicker } from './GiteaRepoPicker';\nimport { GitlabRepoPicker } from './GitlabRepoPicker';\nimport { AzureRepoPicker } from './AzureRepoPicker';\nimport { BitbucketRepoPicker } from './BitbucketRepoPicker';\nimport { GerritRepoPicker } from './GerritRepoPicker';\nimport { RepoUrlPickerHost } from './RepoUrlPickerHost';\nimport { RepoUrlPickerRepoName } from './RepoUrlPickerRepoName';\nimport { parseRepoPickerUrl, serializeRepoPickerUrl } from './utils';\nimport { RepoUrlPickerFieldSchema } from './schema';\nimport { RepoUrlPickerState } from './types';\nimport useDebounce from 'react-use/esm/useDebounce';\nimport { useTemplateSecrets } from '@backstage/plugin-scaffolder-react';\nimport Box from '@material-ui/core/Box';\nimport Divider from '@material-ui/core/Divider';\nimport Typography from '@material-ui/core/Typography';\n\nexport { RepoUrlPickerSchema } from './schema';\n\n/**\n * The underlying component that is rendered in the form for the `RepoUrlPicker`\n * field extension.\n *\n * @public\n */\nexport const RepoUrlPicker = (\n props: typeof RepoUrlPickerFieldSchema.TProps,\n) => {\n const { uiSchema, onChange, rawErrors, formData, schema } = props;\n const [state, setState] = useState<RepoUrlPickerState>(\n parseRepoPickerUrl(formData),\n );\n const [credentialsHost, setCredentialsHost] = useState<string | undefined>(\n undefined,\n );\n const integrationApi = useApi(scmIntegrationsApiRef);\n const scmAuthApi = useApi(scmAuthApiRef);\n const { secrets, setSecrets } = useTemplateSecrets();\n const allowedHosts = useMemo(\n () => uiSchema?.['ui:options']?.allowedHosts ?? [],\n [uiSchema],\n );\n const allowedOrganizations = useMemo(\n () => uiSchema?.['ui:options']?.allowedOrganizations ?? [],\n [uiSchema],\n );\n const allowedOwners = useMemo(\n () => uiSchema?.['ui:options']?.allowedOwners ?? [],\n [uiSchema],\n );\n const allowedProjects = useMemo(\n () => uiSchema?.['ui:options']?.allowedProjects ?? [],\n [uiSchema],\n );\n const allowedRepos = useMemo(\n () => uiSchema?.['ui:options']?.allowedRepos ?? [],\n [uiSchema],\n );\n\n const { owner, organization, project, repoName } = state;\n\n useEffect(() => {\n onChange(serializeRepoPickerUrl(state));\n }, [state, onChange]);\n\n /* we deal with calling the repo setting here instead of in each components for ease */\n useEffect(() => {\n if (allowedOrganizations.length > 0 && !organization) {\n setState(prevState => ({\n ...prevState,\n organization: allowedOrganizations[0],\n }));\n }\n }, [setState, allowedOrganizations, organization]);\n\n useEffect(() => {\n if (allowedOwners.length > 0 && !owner) {\n setState(prevState => ({\n ...prevState,\n owner: allowedOwners[0],\n }));\n }\n }, [setState, allowedOwners, owner]);\n\n useEffect(() => {\n if (allowedProjects.length > 0 && !project) {\n setState(prevState => ({\n ...prevState,\n project: allowedProjects[0],\n }));\n }\n }, [setState, allowedProjects, project]);\n\n useEffect(() => {\n if (allowedRepos.length > 0 && !repoName) {\n setState(prevState => ({ ...prevState, repoName: allowedRepos[0] }));\n }\n }, [setState, allowedRepos, repoName]);\n\n const updateLocalState = useCallback(\n (newState: RepoUrlPickerState) => {\n setState(prevState => ({ ...prevState, ...newState }));\n },\n [setState],\n );\n\n useDebounce(\n async () => {\n const { requestUserCredentials } = uiSchema?.['ui:options'] ?? {};\n\n if (!requestUserCredentials || !state.host) {\n return;\n }\n\n // don't show login prompt if secret value is already in state for selected host\n if (\n secrets[requestUserCredentials.secretsKey] &&\n credentialsHost === state.host\n ) {\n return;\n }\n\n // user has requested that we use the users credentials\n // so lets grab them using the scmAuthApi and pass through\n // any additional scopes from the ui:options\n const { token } = await scmAuthApi.getCredentials({\n url: `https://${state.host}`,\n additionalScope: {\n repoWrite: true,\n customScopes: requestUserCredentials.additionalScopes,\n },\n });\n\n // set the secret using the key provided in the ui:options for use\n // in the templating the manifest with ${{ secrets[secretsKey] }}\n setSecrets({ [requestUserCredentials.secretsKey]: token });\n setCredentialsHost(state.host);\n },\n 500,\n [state, uiSchema],\n );\n\n const hostType =\n (state.host && integrationApi.byHost(state.host)?.type) ?? null;\n return (\n <>\n {schema.title && (\n <Box my={1}>\n <Typography variant=\"h5\">{schema.title}</Typography>\n <Divider />\n </Box>\n )}\n {schema.description && (\n <Typography variant=\"body1\">{schema.description}</Typography>\n )}\n <RepoUrlPickerHost\n host={state.host}\n hosts={allowedHosts}\n onChange={host => setState(prevState => ({ ...prevState, host }))}\n rawErrors={rawErrors}\n />\n {hostType === 'github' && (\n <GithubRepoPicker\n allowedOwners={allowedOwners}\n onChange={updateLocalState}\n rawErrors={rawErrors}\n state={state}\n />\n )}\n {hostType === 'gitea' && (\n <GiteaRepoPicker\n allowedOwners={allowedOwners}\n allowedRepos={allowedRepos}\n rawErrors={rawErrors}\n state={state}\n onChange={updateLocalState}\n />\n )}\n {hostType === 'gitlab' && (\n <GitlabRepoPicker\n allowedOwners={allowedOwners}\n rawErrors={rawErrors}\n state={state}\n onChange={updateLocalState}\n />\n )}\n {hostType === 'bitbucket' && (\n <BitbucketRepoPicker\n allowedOwners={allowedOwners}\n allowedProjects={allowedProjects}\n rawErrors={rawErrors}\n state={state}\n onChange={updateLocalState}\n accessToken={\n uiSchema?.['ui:options']?.requestUserCredentials?.secretsKey &&\n secrets[uiSchema['ui:options'].requestUserCredentials.secretsKey]\n }\n />\n )}\n {hostType === 'azure' && (\n <AzureRepoPicker\n allowedOrganizations={allowedOrganizations}\n allowedProject={allowedProjects}\n rawErrors={rawErrors}\n state={state}\n onChange={updateLocalState}\n />\n )}\n {hostType === 'gerrit' && (\n <GerritRepoPicker\n rawErrors={rawErrors}\n state={state}\n onChange={updateLocalState}\n />\n )}\n <RepoUrlPickerRepoName\n repoName={state.repoName}\n allowedRepos={allowedRepos}\n onChange={repo =>\n setState(prevState => ({ ...prevState, repoName: repo }))\n }\n rawErrors={rawErrors}\n availableRepos={state.availableRepos}\n />\n </>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { makeFieldSchemaFromZod } from '../utils.esm.js';
|
|
1
|
+
import { makeFieldSchema } from '@backstage/plugin-scaffolder-react';
|
|
3
2
|
|
|
4
|
-
const RepoUrlPickerFieldSchema =
|
|
5
|
-
z.string(),
|
|
6
|
-
z.object({
|
|
3
|
+
const RepoUrlPickerFieldSchema = makeFieldSchema({
|
|
4
|
+
output: (z) => z.string(),
|
|
5
|
+
uiOptions: (z) => z.object({
|
|
7
6
|
allowedHosts: z.array(z.string()).optional().describe("List of allowed SCM platform hosts"),
|
|
8
7
|
allowedOrganizations: z.array(z.string()).optional().describe("List of allowed organizations in the given SCM platform"),
|
|
9
8
|
allowedOwners: z.array(z.string()).optional().describe("List of allowed owners in the given SCM platform"),
|
|
@@ -25,7 +24,7 @@ const RepoUrlPickerFieldSchema = makeFieldSchemaFromZod(
|
|
|
25
24
|
"If defined will request user credentials to auth against the given SCM platform"
|
|
26
25
|
)
|
|
27
26
|
})
|
|
28
|
-
);
|
|
27
|
+
});
|
|
29
28
|
const RepoUrlPickerSchema = RepoUrlPickerFieldSchema.schema;
|
|
30
29
|
|
|
31
30
|
export { RepoUrlPickerFieldSchema, RepoUrlPickerSchema };
|