implements editUrl for template

Signed-off-by: kosukeKK <koskacts@gmail.com>
This commit is contained in:
kosukeKK
2023-12-30 18:05:01 +09:00
parent 80bcb8a84d
commit 3ae875da8d
6 changed files with 143 additions and 4 deletions
@@ -3,6 +3,8 @@ kind: Location
metadata:
name: example-templates
description: A collection of all Backstage example templates
# annotations:
# backstage.io/edit-url: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/all-templates.yaml
spec:
targets:
- ./remote-templates.yaml
@@ -107,6 +107,7 @@ describe('createRouter', () => {
title: 'Create React App Template',
annotations: {
'backstage.io/managed-by-location': 'url:https://dev.azure.com',
'backstage.io/edit-url': 'url:EDIT_URL',
},
},
spec: {
@@ -878,6 +879,7 @@ data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{
expect(response.body).toEqual({
title: 'Create React App Template',
description: 'Create a new CRA website project',
editUrl: 'url:EDIT_URL',
steps: [
{
title: 'Please enter the following information',
@@ -932,6 +934,7 @@ data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{
expect(response.body).toEqual({
title: 'Create React App Template',
description: 'Create a new CRA website project',
editUrl: 'url:EDIT_URL',
steps: [],
});
});
@@ -963,6 +966,7 @@ data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{
expect(response.body).toEqual({
title: 'Create React App Template',
description: 'Create a new CRA website project',
editUrl: 'url:EDIT_URL',
steps: [
{
title: 'Please enter the following information',
@@ -983,6 +987,56 @@ data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{
],
});
});
it('should not return editUrl', async () => {
jest
.spyOn(catalogClient, 'getEntityByRef')
.mockImplementationOnce(async () => {
const template = getMockTemplate();
delete template.metadata.annotations?.['backstage.io/edit-url'];
return template;
});
const response = await request(app)
.get(
'/v2/templates/default/Template/create-react-app-template/parameter-schema',
)
.send();
expect(response.status).toEqual(200);
expect(response.body).toEqual({
title: 'Create React App Template',
description: 'Create a new CRA website project',
steps: [
{
title: 'Please enter the following information',
schema: {
required: ['requiredParameter1'],
type: 'object',
properties: {
requiredParameter1: {
description: 'Required parameter 1',
type: 'string',
},
},
},
},
{
title: 'Please enter the following information',
schema: {
type: 'object',
required: ['requiredParameter2'],
'backstage:permissions': {
tags: ['parameters-tag'],
},
properties: {
requiredParameter2: {
type: 'string',
description: 'Required parameter 2',
},
},
},
},
],
});
});
});
describe('POST /v2/tasks', () => {
@@ -408,6 +408,7 @@ export async function createRouter(
res.json({
title: template.metadata.title ?? template.metadata.name,
...(presentation ? { presentation } : {}),
editUrl: template.metadata.annotations?.['backstage.io/edit-url'],
description: template.metadata.description,
'ui:options': template.metadata['ui:options'],
steps: parameters.map(schema => ({
@@ -20,7 +20,7 @@ import {
renderInTestApp,
TestApiRegistry,
} from '@backstage/test-utils';
import { act, fireEvent } from '@testing-library/react';
import { act, fireEvent, screen } from '@testing-library/react';
import React from 'react';
import { Workflow } from './Workflow';
import { analyticsApiRef } from '@backstage/core-plugin-api';
@@ -75,7 +75,7 @@ describe('<Workflow />', () => {
title: 'React JSON Schema Form Test',
});
const { getByRole, getAllByRole, getByText } = await renderInTestApp(
const { getByRole, getByText } = await renderInTestApp(
<ApiProvider apis={apis}>
<Workflow
title="Different title than template"
@@ -133,7 +133,7 @@ describe('<Workflow />', () => {
).toBeDefined();
await act(async () => {
fireEvent.click(getAllByRole('button')[1] as HTMLButtonElement);
fireEvent.click(getByText('Make') as HTMLButtonElement);
});
expect(onCreate).toHaveBeenCalledWith({
@@ -141,4 +141,72 @@ describe('<Workflow />', () => {
age: '53',
});
});
it('renders "edit template" button', async () => {
const onCreate = jest.fn();
const onError = jest.fn();
const expectedUrl = '/EDIT_URL';
scaffolderApiMock.getTemplateParameterSchema.mockResolvedValue({
editUrl: expectedUrl,
steps: [
{
title: 'Step 1',
schema: {},
},
],
title: 'React JSON Schema Form Test',
});
await renderInTestApp(
<ApiProvider apis={apis}>
<Workflow
title="test"
description="test description"
onCreate={onCreate}
onError={onError}
namespace="default"
templateName="docs-template"
initialState={{
name: 'prefilled-name',
age: '53',
}}
extensions={[]}
/>
</ApiProvider>,
);
const editLink = screen.getByTitle('Edit Template').closest('a');
expect(editLink).toHaveAttribute('href', expectedUrl);
});
it('renders disable "edit template" button', async () => {
const onCreate = jest.fn();
const onError = jest.fn();
scaffolderApiMock.getTemplateParameterSchema.mockResolvedValue({
steps: [
{
title: 'Step 1',
schema: {},
},
],
editUrl: undefined,
title: 'React JSON Schema Form Test',
});
await renderInTestApp(
<ApiProvider apis={apis}>
<Workflow
title="test"
description="test description"
onCreate={onCreate}
onError={onError}
namespace="default"
templateName="docs-template"
initialState={{
name: 'prefilled-name',
age: '53',
}}
extensions={[]}
/>
</ApiProvider>,
);
const editLink = screen.getByTitle('Edit Template');
expect(editLink).toBeVisible();
expect(editLink).toHaveAttribute('href', '/');
});
});
@@ -20,6 +20,7 @@ import {
InfoCard,
MarkdownContent,
Progress,
Link,
} from '@backstage/core-components';
import { stringifyEntityRef } from '@backstage/catalog-model';
import { makeStyles } from '@material-ui/core';
@@ -29,6 +30,8 @@ import { Stepper, type StepperProps } from '../Stepper/Stepper';
import { SecretsContextProvider } from '../../../secrets/SecretsContext';
import { useFilteredSchemaProperties } from '../../hooks/useFilteredSchemaProperties';
import { ReviewStepProps } from '@backstage/plugin-scaffolder-react';
import IconButton from '@material-ui/core/IconButton';
import EditIcon from '@material-ui/icons/Edit';
const useStyles = makeStyles({
markdown: {
@@ -93,13 +96,23 @@ export const Workflow = (workflowProps: WorkflowProps): JSX.Element | null => {
if (error) {
return props.onError(error);
}
return (
<Content>
{loading && <Progress />}
{sortedManifest && (
<InfoCard
title={title ?? sortedManifest.title}
action={
<IconButton
component={Link}
aria-label="Edit"
title="Edit Template"
to={sortedManifest.editUrl ?? '#'}
disabled={!sortedManifest.editUrl}
>
<EditIcon />
</IconButton>
}
subheader={
<MarkdownContent
className={styles.markdown}
+1
View File
@@ -27,6 +27,7 @@ import { TemplatePresentationV1beta3 } from '@backstage/plugin-scaffolder-common
export type TemplateParameterSchema = {
title: string;
description?: string;
editUrl?: string;
presentation?: TemplatePresentationV1beta3;
steps: Array<{
title: string;