diff --git a/plugins/scaffolder-backend/src/service/helpers.ts b/plugins/scaffolder-backend/src/service/helpers.ts index 23200d6aac..13197861a8 100644 --- a/plugins/scaffolder-backend/src/service/helpers.ts +++ b/plugins/scaffolder-backend/src/service/helpers.ts @@ -95,11 +95,6 @@ export async function findTemplate(options: { }): Promise { const { entityRef, token, catalogApi } = options; - if (entityRef.namespace.toLocaleLowerCase('en-US') !== DEFAULT_NAMESPACE) { - throw new InputError( - `Invalid namespace, only '${DEFAULT_NAMESPACE}' namespace is supported`, - ); - } if (entityRef.kind.toLocaleLowerCase('en-US') !== 'template') { throw new InputError(`Invalid kind, only 'Template' kind is supported`); } diff --git a/plugins/scaffolder/src/components/Router.tsx b/plugins/scaffolder/src/components/Router.tsx index 6beaa3419a..172a21234a 100644 --- a/plugins/scaffolder/src/components/Router.tsx +++ b/plugins/scaffolder/src/components/Router.tsx @@ -15,7 +15,7 @@ */ import React, { ComponentType } from 'react'; -import { Routes, Route, useOutlet, Navigate } from 'react-router'; +import { Routes, Route, useOutlet, Navigate, useParams } from 'react-router'; import { Entity } from '@backstage/catalog-model'; import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; import { ScaffolderPage } from './ScaffolderPage'; @@ -31,10 +31,11 @@ import { FIELD_EXTENSION_KEY, DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS, } from '../extensions'; -import { useElementFilter } from '@backstage/core-plugin-api'; +import { useElementFilter, useRouteRef } from '@backstage/core-plugin-api'; import { actionsRouteRef, editRouteRef, + legacySelectedTemplateRouteRef, scaffolderListTaskRouteRef, scaffolderTaskRouteRef, selectedTemplateRouteRef, @@ -101,6 +102,12 @@ export const Router = (props: RouterProps) => { ), ]; + const RedirectingComponent = () => { + const { templateName } = useParams(); + const newLink = useRouteRef(selectedTemplateRouteRef); + return ; + }; + return ( { /> } /> + + + { const formData = taskStream.task!.spec.parameters; - const { name } = parseEntityRef( + const { name, namespace } = parseEntityRef( taskStream.task!.spec.templateInfo?.entityRef, ); navigate( - `${templateRoute({ templateName: name })}?${qs.stringify({ + `${templateRoute({ templateName: name, namespace })}?${qs.stringify({ formData: JSON.stringify(formData), })}`, ); diff --git a/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx b/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx index 6f7e375cce..df46a119f2 100644 --- a/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx +++ b/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx @@ -13,7 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model'; +import { + Entity, + parseEntityRef, + RELATION_OWNED_BY, + stringifyEntityRef, +} from '@backstage/catalog-model'; import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; import { ScmIntegrationIcon, @@ -162,7 +167,8 @@ export const TemplateCard = ({ template, deprecated }: TemplateCardProps) => { : 'other'; const theme = backstageTheme.getPageTheme({ themeId }); const classes = useStyles({ backgroundImage: theme.backgroundImage }); - const href = templateRoute({ templateName: templateProps.name }); + const { name, namespace } = parseEntityRef(stringifyEntityRef(template)); + const href = templateRoute({ templateName: name, namespace }); const scmIntegrationsApi = useApi(scmIntegrationsApiRef); const sourceLocation = getEntitySourceLocation(template, scmIntegrationsApi); diff --git a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx index 22f995eae4..f694688801 100644 --- a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx +++ b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx @@ -54,11 +54,16 @@ export const TemplatePage = ({ const secretsContext = useContext(SecretsContext); const errorApi = useApi(errorApiRef); const scaffolderApi = useApi(scaffolderApiRef); - const { templateName } = useParams(); + const { templateName, namespace } = useParams(); + const templateRef = stringifyEntityRef({ + name: templateName, + kind: 'template', + namespace, + }); const navigate = useNavigate(); const scaffolderTaskRoute = useRouteRef(scaffolderTaskRouteRef); const rootRoute = useRouteRef(rootRouteRef); - const { schema, loading, error } = useTemplateParameterSchema(templateName); + const { schema, loading, error } = useTemplateParameterSchema(templateRef); const [formState, setFormState] = useState>(() => { const query = qs.parse(window.location.search, { ignoreQueryPrefix: true, @@ -78,11 +83,7 @@ export const TemplatePage = ({ const handleCreate = async () => { const { taskId } = await scaffolderApi.scaffold({ - templateRef: stringifyEntityRef({ - name: templateName, - kind: 'template', - namespace: 'default', - }), + templateRef, values: formState, secrets: secretsContext?.secrets, }); diff --git a/plugins/scaffolder/src/next/Router/Router.test.tsx b/plugins/scaffolder/src/next/Router/Router.test.tsx index 5094c786e0..1aaca1a94b 100644 --- a/plugins/scaffolder/src/next/Router/Router.test.tsx +++ b/plugins/scaffolder/src/next/Router/Router.test.tsx @@ -47,7 +47,9 @@ describe('Router', () => { describe('/templates/:templateName', () => { it('should render the TemplateWizard page', async () => { - await renderInTestApp(, { routeEntries: ['/templates/foo'] }); + await renderInTestApp(, { + routeEntries: ['/templates/default/foo'], + }); expect(TemplateWizardPage).toHaveBeenCalled(); }); @@ -67,7 +69,7 @@ describe('Router', () => { , - { routeEntries: ['/templates/foo'] }, + { routeEntries: ['/templates/default/foo'] }, ); const mock = TemplateWizardPage as jest.Mock; diff --git a/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/TemplateCard.test.tsx b/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/TemplateCard.test.tsx index 45f4dc6317..44e3a0bbbf 100644 --- a/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/TemplateCard.test.tsx +++ b/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/TemplateCard.test.tsx @@ -233,7 +233,7 @@ describe('TemplateCard', () => { expect(getByRole('button', { name: 'Choose' })).toBeInTheDocument(); expect(getByRole('button', { name: 'Choose' })).toHaveAttribute( 'href', - '/templates/bob', + '/templates/default/bob', ); }); }); diff --git a/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/TemplateCard.tsx b/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/TemplateCard.tsx index 0e67ecd82a..874c20a9ac 100644 --- a/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/TemplateCard.tsx +++ b/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/TemplateCard.tsx @@ -26,7 +26,11 @@ import { } from '@material-ui/core'; import { CardHeader } from './CardHeader'; import { MarkdownContent, UserIcon, Button } from '@backstage/core-components'; -import { RELATION_OWNED_BY } from '@backstage/catalog-model'; +import { + parseEntityRef, + RELATION_OWNED_BY, + stringifyEntityRef, +} from '@backstage/catalog-model'; import { EntityRefLinks, getEntityRelations, @@ -91,7 +95,8 @@ export const TemplateCard = (props: TemplateCardProps) => { const styles = useStyles(); const ownedByRelations = getEntityRelations(template, RELATION_OWNED_BY); const templateRoute = useRouteRef(selectedTemplateRouteRef); - const href = templateRoute({ templateName: template.metadata.name }); + const { name, namespace } = parseEntityRef(stringifyEntityRef(template)); + const href = templateRoute({ templateName: name, namespace: namespace }); return ( diff --git a/plugins/scaffolder/src/routes.ts b/plugins/scaffolder/src/routes.ts index be0d69135a..dbc54bb22c 100644 --- a/plugins/scaffolder/src/routes.ts +++ b/plugins/scaffolder/src/routes.ts @@ -28,10 +28,16 @@ export const rootRouteRef = createRouteRef({ id: 'scaffolder', }); +export const legacySelectedTemplateRouteRef = createSubRouteRef({ + id: 'scaffolder/legacy/selected-template', + parent: rootRouteRef, + path: '/templates/:templateName', +}); + export const selectedTemplateRouteRef = createSubRouteRef({ id: 'scaffolder/selected-template', parent: rootRouteRef, - path: '/templates/:templateName', + path: '/templates/:namespace/:templateName', }); export const scaffolderTaskRouteRef = createSubRouteRef({