chore: starting to wire together a TaskPage for the router

Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
blam
2022-10-05 21:55:11 +02:00
parent 6edbf33499
commit 4497bd4e48
7 changed files with 109 additions and 22 deletions
+11 -2
View File
@@ -28,8 +28,13 @@ import {
import { useElementFilter } from '@backstage/core-plugin-api';
import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common';
import { TemplateGroupFilter } from '../TemplateListPage/TemplateGroups';
import { nextSelectedTemplateRouteRef } from '../../routes';
import {
nextScaffolderTaskRouteRef,
nextSelectedTemplateRouteRef,
} from '../../routes';
import { SecretsContextProvider } from '../../components/secrets/SecretsContext';
import { TaskPage } from '../TaskPage';
import { ErrorPage } from '@backstage/core-components';
/**
* The Props for the Scaffolder Router
@@ -87,7 +92,6 @@ export const Router = (props: PropsWithChildren<NextRouterProps>) => {
/>
}
/>
<Route
path={nextSelectedTemplateRouteRef.path}
element={
@@ -96,6 +100,11 @@ export const Router = (props: PropsWithChildren<NextRouterProps>) => {
</SecretsContextProvider>
}
/>
<Route path={nextScaffolderTaskRouteRef.path} element={<TaskPage />} />
<Route
path="*"
element={<ErrorPage status="404" statusMessage="Page not found" />}
/>
</Routes>
);
};
@@ -0,0 +1,33 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { Page, Header, Content } from '@backstage/core-components';
export const TaskPage = () => {
return (
<Page themeId="website">
<Header
pageTitleOverride="Task ID"
title="View Task"
subtitle="View the status of a task"
/>
<Content>
<p>Hey</p>
</Content>
</Page>
);
};
@@ -0,0 +1,16 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { TaskPage } from './TaskPage';
@@ -30,7 +30,7 @@ describe('Stepper', () => {
};
const { getByText } = await renderInTestApp(
<Stepper manifest={manifest} extensions={[]} />,
<Stepper manifest={manifest} extensions={[]} onComplete={jest.fn()} />,
);
for (const step of manifest.steps) {
@@ -48,7 +48,7 @@ describe('Stepper', () => {
};
const { getByRole } = await renderInTestApp(
<Stepper manifest={manifest} extensions={[]} />,
<Stepper manifest={manifest} extensions={[]} onComplete={jest.fn()} />,
);
expect(getByRole('button', { name: 'Next' })).toBeInTheDocument();
@@ -88,7 +88,7 @@ describe('Stepper', () => {
};
const { getByRole } = await renderInTestApp(
<Stepper manifest={manifest} extensions={[]} />,
<Stepper manifest={manifest} extensions={[]} onComplete={jest.fn()} />,
);
await fireEvent.change(getByRole('textbox', { name: 'name' }), {
@@ -134,6 +134,7 @@ describe('Stepper', () => {
<Stepper
manifest={manifest}
extensions={[{ name: 'Mock', component: MockComponent }]}
onComplete={jest.fn()}
/>,
);
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useApiHolder } from '@backstage/core-plugin-api';
import { JsonObject } from '@backstage/types';
import { useApi, useApiHolder } from '@backstage/core-plugin-api';
import { JsonObject, JsonValue } from '@backstage/types';
import {
Stepper as MuiStepper,
Step as MuiStep,
@@ -31,6 +31,7 @@ import { createAsyncValidators } from './createAsyncValidators';
import { useTemplateSchema } from './useTemplateSchema';
import { ReviewState } from './ReviewState';
import validator from '@rjsf/validator-ajv8';
import { scaffolderApiRef } from '../../../api';
const useStyles = makeStyles(theme => ({
backButton: {
@@ -50,6 +51,7 @@ const useStyles = makeStyles(theme => ({
export interface StepperProps {
manifest: TemplateParameterSchema;
extensions: NextFieldExtensionOptions<any, any>[];
onComplete: (values: Record<string, JsonValue>) => Promise<void>;
}
// TODO(blam): We require here, as the types in this package depend on @rjsf/core explicitly
@@ -109,10 +111,6 @@ export const Stepper = (props: StepperProps) => {
setFormState(current => ({ ...current, ...formData }));
};
const handleCreate = () => {
// TODO(blam): Create the template in a modal with the ability to view the logs etc.
};
return (
<>
<MuiStepper activeStep={activeStep} alternativeLabel variant="elevation">
@@ -161,7 +159,10 @@ export const Stepper = (props: StepperProps) => {
>
Back
</Button>
<Button variant="contained" onClick={handleCreate}>
<Button
variant="contained"
onClick={() => props.onComplete(formState)}
>
Create
</Button>
</div>
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { useEffect } from 'react';
import React, { useContext, useEffect } from 'react';
import {
Page,
Header,
@@ -23,7 +23,7 @@ import {
MarkdownContent,
} from '@backstage/core-components';
import { NextFieldExtensionOptions } from '../../extensions';
import { Navigate } from 'react-router';
import { Navigate, useNavigate } from 'react-router';
import { stringifyEntityRef } from '@backstage/catalog-model';
import {
errorApiRef,
@@ -36,7 +36,13 @@ import useAsync from 'react-use/lib/useAsync';
import { makeStyles } from '@material-ui/core';
import { Stepper } from './Stepper';
import { BackstageTheme } from '@backstage/theme';
import { nextRouteRef, selectedTemplateRouteRef } from '../../routes';
import {
nextRouteRef,
nextScaffolderTaskRouteRef,
selectedTemplateRouteRef,
} from '../../routes';
import { SecretsContext } from '../../components/secrets/SecretsContext';
import { JsonValue } from '@backstage/types';
export interface TemplateWizardPageProps {
customFieldExtensions: NextFieldExtensionOptions<any, any>[];
@@ -67,17 +73,31 @@ const useTemplateParameterSchema = (templateRef: string) => {
export const TemplateWizardPage = (props: TemplateWizardPageProps) => {
const styles = useStyles();
const rootRef = useRouteRef(nextRouteRef);
const nextTemplateRef = useRouteRef(nextScaffolderTaskRouteRef);
const { secrets } = useContext(SecretsContext) ?? {};
const scaffolderApi = useApi(scaffolderApiRef);
const navigate = useNavigate();
const { templateName, namespace } = useRouteRefParams(
selectedTemplateRouteRef,
);
const templateRef = stringifyEntityRef({
kind: 'Template',
namespace,
name: templateName,
});
const errorApi = useApi(errorApiRef);
const { loading, manifest, error } = useTemplateParameterSchema(
stringifyEntityRef({
kind: 'Template',
namespace,
name: templateName,
}),
);
const { loading, manifest, error } = useTemplateParameterSchema(templateRef);
const onComplete = async (values: Record<string, JsonValue>) => {
const { taskId } = await scaffolderApi.scaffold({
templateRef,
values,
secrets,
});
navigate(nextTemplateRef({ taskId }));
};
useEffect(() => {
if (error) {
@@ -113,6 +133,7 @@ export const TemplateWizardPage = (props: TemplateWizardPageProps) => {
<Stepper
manifest={manifest}
extensions={props.customFieldExtensions}
onComplete={onComplete}
/>
</InfoCard>
)}
+6
View File
@@ -65,6 +65,12 @@ export const scaffolderTaskRouteRef = createSubRouteRef({
path: '/tasks/:taskId',
});
export const nextScaffolderTaskRouteRef = createSubRouteRef({
id: 'scaffolder/next/task',
parent: nextRouteRef,
path: '/tasks/:taskId',
});
export const scaffolderListTaskRouteRef = createSubRouteRef({
id: 'scaffolder/list-tasks',
parent: rootRouteRef,