From 6de13cb54e8effd0e544f608a9410a947185f26d Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Wed, 1 Jul 2020 20:36:16 +0200 Subject: [PATCH] fix(scaffolder): big cleanup --- plugins/catalog/src/api/types.ts | 14 +- plugins/scaffolder/package.json | 6 +- .../src/components/CreatePage/index.ts | 1 - .../{JobStatusModal => JobStage}/JobStage.tsx | 79 ++++---- .../src/components/JobStage/index.ts | 16 ++ .../JobStatusModal/JobStatusModal.tsx | 25 ++- .../JobStatusModal/useJobPolling.ts | 3 +- .../MultistepJsonForm/MultistepJsonForm.tsx | 47 ++--- .../{index.tsx => ScaffolderPage.tsx} | 39 ++-- .../src/components/ScaffolderPage/index.ts | 16 ++ .../TemplateCard.tsx | 15 +- .../src/components/TemplateCard/index.ts | 17 ++ .../TemplatePage.tsx} | 170 +++++++++++------- .../src/components/TemplatePage/index.ts | 16 ++ plugins/scaffolder/src/index.ts | 2 +- plugins/scaffolder/src/plugin.ts | 6 +- plugins/scaffolder/src/routes.ts | 17 +- .../{components/JobStatusModal => }/types.ts | 0 yarn.lock | 7 +- 19 files changed, 318 insertions(+), 178 deletions(-) delete mode 100644 plugins/scaffolder/src/components/CreatePage/index.ts rename plugins/scaffolder/src/components/{JobStatusModal => JobStage}/JobStage.tsx (79%) create mode 100644 plugins/scaffolder/src/components/JobStage/index.ts rename plugins/scaffolder/src/components/ScaffolderPage/{index.tsx => ScaffolderPage.tsx} (84%) create mode 100644 plugins/scaffolder/src/components/ScaffolderPage/index.ts rename plugins/scaffolder/src/components/{ScaffolderPage => TemplateCard}/TemplateCard.tsx (87%) create mode 100644 plugins/scaffolder/src/components/TemplateCard/index.ts rename plugins/scaffolder/src/components/{CreatePage/CreatePage.tsx => TemplatePage/TemplatePage.tsx} (55%) create mode 100644 plugins/scaffolder/src/components/TemplatePage/index.ts rename plugins/scaffolder/src/{components/JobStatusModal => }/types.ts (100%) diff --git a/plugins/catalog/src/api/types.ts b/plugins/catalog/src/api/types.ts index 3ac8a5d103..a523d97251 100644 --- a/plugins/catalog/src/api/types.ts +++ b/plugins/catalog/src/api/types.ts @@ -34,10 +34,18 @@ export interface CatalogApi { getEntityByName( compoundName: EntityCompoundName, ): Promise; - getEntities(filter?: Record): Promise; - addLocation(type: string, target: string): Promise; + getEntities( + filter?: Record, + ): Promise; + addLocation( + type: string, + target: string, + ): Promise>; getLocationByEntity(entity: Entity): Promise; removeEntityByUid(uid: string): Promise; } -export type AddLocationResponse = { location: Location; entities: Entity[] }; +export type AddLocationResponse = { + location: Location; + entities: EntityType[]; +}; diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 9dfdde2397..312e90169f 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -25,15 +25,17 @@ "@backstage/core": "^0.1.1-alpha.12", "@backstage/plugin-catalog": "^0.1.1-alpha.12", "@backstage/theme": "^0.1.1-alpha.12", - "@backstage/catalog-model": "^0.1.1-alpha.12", - "@backstage/plugin-catalog": "^0.1.1-alpha.12", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", "@rjsf/core": "^2.1.0", "@rjsf/material-ui": "^2.1.0", + "classnames": "^2.2.6", + "moment": "^2.27.0", "react": "^16.13.1", "react-dom": "^16.13.1", + "react-lazylog": "^4.5.2", + "react-router": "^5.2.0", "react-router-dom": "6.0.0-alpha.5", "react-use": "^14.2.0", "swr": "^0.2.2" diff --git a/plugins/scaffolder/src/components/CreatePage/index.ts b/plugins/scaffolder/src/components/CreatePage/index.ts deleted file mode 100644 index e1be7978a9..0000000000 --- a/plugins/scaffolder/src/components/CreatePage/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { CreatePage } from './CreatePage'; diff --git a/plugins/scaffolder/src/components/JobStatusModal/JobStage.tsx b/plugins/scaffolder/src/components/JobStage/JobStage.tsx similarity index 79% rename from plugins/scaffolder/src/components/JobStatusModal/JobStage.tsx rename to plugins/scaffolder/src/components/JobStage/JobStage.tsx index ef799d13b4..616e350ed4 100644 --- a/plugins/scaffolder/src/components/JobStatusModal/JobStage.tsx +++ b/plugins/scaffolder/src/components/JobStage/JobStage.tsx @@ -13,21 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, { useEffect, useState, Suspense } from 'react'; import { + Box, ExpansionPanel, - ExpansionPanelSummary, - Typography, ExpansionPanelDetails, + ExpansionPanelSummary, LinearProgress, + Typography, } from '@material-ui/core'; -import moment from 'moment'; -import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import { makeStyles } from '@material-ui/core/styles'; -import { Job } from './types'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import cn from 'classnames'; +import moment from 'moment'; +import React, { Suspense, useEffect, useState } from 'react'; +import { Job } from '../../types'; const LazyLog = React.lazy(() => import('react-lazylog/build/LazyLog')); moment.relativeTimeThreshold('ss', 0); + const useStyles = makeStyles(theme => ({ expansionPanelDetails: { padding: 0, @@ -37,37 +40,10 @@ const useStyles = makeStyles(theme => ({ marginRight: 0, marginLeft: '-20px', }, - neutral: {}, - failed: { - position: 'relative', - '&:after': { - pointerEvents: 'none', - content: '""', - position: 'absolute', - top: 0, - right: 0, - left: 0, - bottom: 0, - boxShadow: `inset 4px 0px 0px ${theme.palette.error.main}`, - }, - }, - running: { - position: 'relative', - '&:after': { - pointerEvents: 'none', - content: '""', - position: 'absolute', - top: 0, - right: 0, - left: 0, - bottom: 0, - boxShadow: `inset 4px 0px 0px ${theme.palette.info.main}`, - }, - }, cardContent: { backgroundColor: theme.palette.background.default, }, - success: { + expansionPanel: { position: 'relative', '&:after': { pointerEvents: 'none', @@ -77,6 +53,21 @@ const useStyles = makeStyles(theme => ({ right: 0, left: 0, bottom: 0, + }, + }, + neutral: {}, + failed: { + '&:after': { + boxShadow: `inset 4px 0px 0px ${theme.palette.error.main}`, + }, + }, + started: { + '&:after': { + boxShadow: `inset 4px 0px 0px ${theme.palette.info.main}`, + }, + }, + completed: { + '&:after': { boxShadow: `inset 4px 0px 0px ${theme.palette.success.main}`, }, }, @@ -90,13 +81,14 @@ type Props = { endedAt?: string; status?: Job['status']; }; + export const JobStage = ({ endedAt, startedAt, name, log, status }: Props) => { const classes = useStyles(); const [expanded, setExpanded] = useState(false); useEffect(() => { if (status === 'FAILED') setExpanded(true); - }, [status === 'FAILED', setExpanded]); + }, [status, setExpanded]); const timeElapsed = status !== 'PENDING' @@ -108,15 +100,10 @@ export const JobStage = ({ endedAt, startedAt, name, log, status }: Props) => { return ( setExpanded(newState)} > @@ -134,11 +121,11 @@ export const JobStage = ({ endedAt, startedAt, name, log, status }: Props) => { {log.length === 0 ? ( - 'Nothing here...' + No logs available for this step ) : ( }>
- +
)} diff --git a/plugins/scaffolder/src/components/JobStage/index.ts b/plugins/scaffolder/src/components/JobStage/index.ts new file mode 100644 index 0000000000..d6d3534a88 --- /dev/null +++ b/plugins/scaffolder/src/components/JobStage/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { JobStage } from './JobStage'; diff --git a/plugins/scaffolder/src/components/JobStatusModal/JobStatusModal.tsx b/plugins/scaffolder/src/components/JobStatusModal/JobStatusModal.tsx index f795444493..a129d7c26a 100644 --- a/plugins/scaffolder/src/components/JobStatusModal/JobStatusModal.tsx +++ b/plugins/scaffolder/src/components/JobStatusModal/JobStatusModal.tsx @@ -1,3 +1,18 @@ +/* + * Copyright 2020 Spotify AB + * + * 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, { useEffect } from 'react'; import { Dialog, @@ -6,13 +21,14 @@ import { DialogContent, DialogActions, } from '@material-ui/core'; -import { JobStage } from './JobStage'; +import { JobStage } from '../JobStage/JobStage'; import { useJobPolling } from './useJobPolling'; -import { Job } from './types'; +import { Job } from '../../types'; import { ComponentEntityV1alpha1 } from '@backstage/catalog-model'; import { Button } from '@backstage/core'; import { entityRoute } from '@backstage/plugin-catalog'; import { generatePath } from 'react-router-dom'; + type Props = { onClose: () => void; onComplete: (job: Job) => void; @@ -29,8 +45,9 @@ export const JobStatusModal = ({ const job = useJobPolling(jobId); useEffect(() => { - if (job?.status === 'COMPLETED') onComplete(job as Job); - }, [job]); + if (job?.status === 'COMPLETED') onComplete(job); + }, [job, onComplete]); + return ( diff --git a/plugins/scaffolder/src/components/JobStatusModal/useJobPolling.ts b/plugins/scaffolder/src/components/JobStatusModal/useJobPolling.ts index 4762838a9c..9239864c03 100644 --- a/plugins/scaffolder/src/components/JobStatusModal/useJobPolling.ts +++ b/plugins/scaffolder/src/components/JobStatusModal/useJobPolling.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import { useState, useEffect } from 'react'; -import { Job } from './types'; +import { Job } from '../../types'; import { useApi } from '@backstage/core'; import { scaffolderApiRef } from '../../api'; @@ -42,6 +42,7 @@ export const useJobPolling = ( useEffect(() => { if (!jobId) return () => {}; + const stopPolling = poll(async () => { const nextJobState = await scaffolderApi.getJob(jobId); if ( diff --git a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx index 03c513a437..81c8abc2fb 100644 --- a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx +++ b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx @@ -13,34 +13,39 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, { useState } from 'react'; -import { withTheme, IChangeEvent, FormProps } from '@rjsf/core'; -import { Theme as MuiTheme } from '@rjsf/material-ui'; +import { JSONSchema } from '@backstage/catalog-model'; +import { Content, StructuredMetadataTable } from '@backstage/core'; import { - Stepper, - Step, - StepLabel, - StepContent, + Box, Button, Paper, + Step, + StepContent, + StepLabel, + Stepper, Typography, - Box, } from '@material-ui/core'; -import { Content, StructuredMetadataTable } from '@backstage/core'; -import { JSONSchema } from '@backstage/catalog-model'; +import { FormProps, IChangeEvent, withTheme } from '@rjsf/core'; +import { Theme as MuiTheme } from '@rjsf/material-ui'; +import React, { useState } from 'react'; const Form = withTheme(MuiTheme); type Step = { schema: JSONSchema; label: string; -}; +} & Partial; + type Props = { + /** + * Steps for the form, each contains label and form schema + */ steps: Step[]; formData: Record; onChange: (e: IChangeEvent) => void; onReset: () => void; onFinish: () => void; }; + export const MultistepJsonForm = ({ steps, formData, @@ -57,10 +62,11 @@ export const MultistepJsonForm = ({ const handleNext = () => setActiveStep(Math.min(activeStep + 1, steps.length)); const handleBack = () => setActiveStep(Math.max(activeStep - 1, 0)); + return ( <> - {steps.map(({ label, schema }) => ( + {steps.map(({ label, schema, ...formProps }) => ( {label} @@ -72,17 +78,14 @@ export const MultistepJsonForm = ({ onSubmit={e => { if (e.errors.length === 0) handleNext(); }} + {...formProps} > -
-
- - -
-
+ +
diff --git a/plugins/scaffolder/src/components/ScaffolderPage/index.tsx b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx similarity index 84% rename from plugins/scaffolder/src/components/ScaffolderPage/index.tsx rename to plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx index 1570006b44..ac13634842 100644 --- a/plugins/scaffolder/src/components/ScaffolderPage/index.tsx +++ b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx @@ -14,31 +14,43 @@ * limitations under the License. */ -import React, { useEffect } from 'react'; +import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; import { - Lifecycle, Content, ContentHeader, + errorApiRef, Header, - SupportButton, + Lifecycle, Page, pageTheme, + SupportButton, useApi, - errorApiRef, } from '@backstage/core'; import { catalogApiRef } from '@backstage/plugin-catalog'; import { - Typography, - Link, Button, Grid, LinearProgress, + Link, + Typography, } from '@material-ui/core'; +import React, { useEffect } from 'react'; import { Link as RouterLink } from 'react-router-dom'; -import TemplateCard from './TemplateCard'; import useStaleWhileRevalidate from 'swr'; -import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; +import { TemplateCard, TemplateCardProps } from '../TemplateCard'; +const getTemplateCardProps = ( + template: TemplateEntityV1alpha1, +): TemplateCardProps & { key: string } => { + return { + key: template.metadata.uid!, + name: template.metadata.name, + title: `${(template.metadata.title || template.metadata.name) ?? ''}`, + type: template.spec.type ?? '', + description: template.metadata.description ?? '-', + tags: (template.metadata?.tags as string[]) ?? [], + }; +}; export const ScaffolderPage: React.FC<{}> = () => { const catalogApi = useApi(catalogApiRef); const errorApi = useApi(errorApiRef); @@ -96,16 +108,7 @@ export const ScaffolderPage: React.FC<{}> = () => { templates.map(template => { return ( - + ); })} diff --git a/plugins/scaffolder/src/components/ScaffolderPage/index.ts b/plugins/scaffolder/src/components/ScaffolderPage/index.ts new file mode 100644 index 0000000000..a28f771598 --- /dev/null +++ b/plugins/scaffolder/src/components/ScaffolderPage/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { ScaffolderPage } from './ScaffolderPage'; diff --git a/plugins/scaffolder/src/components/ScaffolderPage/TemplateCard.tsx b/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx similarity index 87% rename from plugins/scaffolder/src/components/ScaffolderPage/TemplateCard.tsx rename to plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx index 263bc3846b..2f36e12511 100644 --- a/plugins/scaffolder/src/components/ScaffolderPage/TemplateCard.tsx +++ b/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Button } from '@backstage/core'; +import { Card, Chip, makeStyles, Typography } from '@material-ui/core'; import React from 'react'; import { generatePath } from 'react-router-dom'; -import { Button } from '@backstage/core'; -import { Card, Chip, Typography, makeStyles } from '@material-ui/core'; -import { createTemplateRoute } from '../../routes'; +import { templateRoute } from '../../routes'; const useStyles = makeStyles(theme => ({ header: { @@ -40,14 +40,15 @@ const useStyles = makeStyles(theme => ({ }, })); -type TemplateCardProps = { +export type TemplateCardProps = { description: string; tags: string[]; title: string; type: string; name: string; }; -const TemplateCard = ({ + +export const TemplateCard = ({ description, tags, title, @@ -55,7 +56,7 @@ const TemplateCard = ({ name, }: TemplateCardProps) => { const classes = useStyles(); - const href = generatePath(createTemplateRoute.path, { templateName: name }); + const href = generatePath(templateRoute.path, { templateName: name }); return ( @@ -79,5 +80,3 @@ const TemplateCard = ({ ); }; - -export default TemplateCard; diff --git a/plugins/scaffolder/src/components/TemplateCard/index.ts b/plugins/scaffolder/src/components/TemplateCard/index.ts new file mode 100644 index 0000000000..2ead7d5b43 --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateCard/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { TemplateCard } from './TemplateCard'; +export type { TemplateCardProps } from './TemplateCard'; diff --git a/plugins/scaffolder/src/components/CreatePage/CreatePage.tsx b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx similarity index 55% rename from plugins/scaffolder/src/components/CreatePage/CreatePage.tsx rename to plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx index a2ee93c6f8..5984d6f1c2 100644 --- a/plugins/scaffolder/src/components/CreatePage/CreatePage.tsx +++ b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx @@ -13,45 +13,76 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, { useState } from 'react'; -import useStaleWhileRevalidate from 'swr'; -import { useParams } from 'react-router-dom'; -import { LinearProgress } from '@material-ui/core'; -import { catalogApiRef } from '@backstage/plugin-catalog'; import { - useApi, - Page, - Content, - Header, - Lifecycle, - InfoCard, - errorApiRef, -} from '@backstage/core'; -import { - TemplateEntityV1alpha1, ComponentEntityV1alpha1, + TemplateEntityV1alpha1, } from '@backstage/catalog-model'; +import { + Content, + errorApiRef, + Header, + InfoCard, + Lifecycle, + Page, + useApi, +} from '@backstage/core'; +import { catalogApiRef } from '@backstage/plugin-catalog'; +import { LinearProgress } from '@material-ui/core'; import { IChangeEvent } from '@rjsf/core'; -import { JobStatusModal } from '../JobStatusModal'; +import React, { useState } from 'react'; +import { useParams } from 'react-router-dom'; +import useStaleWhileRevalidate from 'swr'; import { scaffolderApiRef } from '../../api'; +import { JobStatusModal } from '../JobStatusModal'; +import { Job } from '../../types'; import { MultistepJsonForm } from '../MultistepJsonForm'; -import { Job } from '../JobStatusModal/types'; -export const CreatePage = () => { +import { Navigate } from 'react-router'; +import { rootRoute } from '../../routes'; + +const useTemplate = ( + templateName: string, + catalogApi: typeof catalogApiRef.T, +) => { + const { data, error } = useStaleWhileRevalidate( + `templates/${templateName}`, + async () => + catalogApi.getEntities({ + kind: 'Template', + 'metadata.name': templateName, + }), + ); + return { template: data?.[0], loading: !error && !data, error }; +}; + +const OWNER_REPO_SCHEMA = { + $schema: 'http://json-schema.org/draft-07/schema#' as const, + required: ['storePath', 'owner'], + properties: { + owner: { + type: 'string' as const, + title: 'Owner', + description: 'Who is going to own this component', + }, + storePath: { + format: 'GitHub user or org / Repo name', + type: 'string' as const, + title: 'Store path', + description: 'GitHub store path in org/repo format', + }, + }, +}; + +const REPO_FORMAT = { + 'GitHub user or org / Repo name': /[^\/]*\/[^\/]*/, +}; + +export const TemplatePage = () => { const errorApi = useApi(errorApiRef); const catalogApi = useApi(catalogApiRef); const scaffolderApi = useApi(scaffolderApiRef); const { templateName } = useParams(); - const { - data: [template] = [] as TemplateEntityV1alpha1[], - isValidating, - } = useStaleWhileRevalidate( - `templates/${templateName}`, - async () => - (catalogApi.getEntities({ - kind: 'Template', - 'metadata.name': templateName, - }) as any) as Promise, - ); + const { template, loading } = useTemplate(templateName, catalogApi); + const [formState, setFormState] = useState({}); const handleFormReset = () => setFormState({}); @@ -62,22 +93,24 @@ export const CreatePage = () => { const handleClose = () => setJobId(null); const handleCreate = async () => { - const job = await scaffolderApi.scaffold(template, formState); + const job = await scaffolderApi.scaffold(template!, formState); setJobId(job); }; const [entity, setEntity] = React.useState( null, ); + const handleCreateComplete = async (job: Job) => { const componentYaml = job.metadata.remoteUrl?.replace( /\.git$/, '/blob/master/component-info.yaml', ); + if (!componentYaml) { errorApi.post( new Error( - `Failed to find component-info.yaml file in ${job.metadata.remoteUrl}`, + `Failed to find component-info.yaml file in ${job.metadata.remoteUrl}.`, ), ); return; @@ -85,13 +118,27 @@ export const CreatePage = () => { const { entities: [createdEntity], - } = await catalogApi.addLocation('github', componentYaml); + } = await catalogApi.addLocation( + 'github', + componentYaml, + ); - setEntity(createdEntity as ComponentEntityV1alpha1); + setEntity(createdEntity); }; - if (!template && isValidating) return ; - if (!template || !template?.spec?.schema) return null; + if (!loading && !template) { + errorApi.post(new Error('Template was not found.')); + return ; + } + + if (template && !template?.spec?.schema) { + errorApi.post( + new Error( + 'Template schema is corrupted, please check the template.yaml file.', + ), + ); + return ; + } return ( @@ -105,6 +152,7 @@ export const CreatePage = () => { subtitle="Create new software components using standard templates" /> + {loading && } {jobId && ( { entity={entity} /> )} - - + - + { + label: 'Choose owner and repo', + schema: OWNER_REPO_SCHEMA, + customFormats: REPO_FORMAT, + }, + ]} + /> + + )} ); diff --git a/plugins/scaffolder/src/components/TemplatePage/index.ts b/plugins/scaffolder/src/components/TemplatePage/index.ts new file mode 100644 index 0000000000..2c038897f4 --- /dev/null +++ b/plugins/scaffolder/src/components/TemplatePage/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { TemplatePage } from './TemplatePage'; diff --git a/plugins/scaffolder/src/index.ts b/plugins/scaffolder/src/index.ts index 3fd7d38c57..4a1c6ce6a5 100644 --- a/plugins/scaffolder/src/index.ts +++ b/plugins/scaffolder/src/index.ts @@ -15,4 +15,4 @@ */ export { plugin } from './plugin'; -export { rootRoute, createTemplateRoute } from './routes'; +export { rootRoute, templateRoute } from './routes'; diff --git a/plugins/scaffolder/src/plugin.ts b/plugins/scaffolder/src/plugin.ts index d0d38d5812..8c4ed5badd 100644 --- a/plugins/scaffolder/src/plugin.ts +++ b/plugins/scaffolder/src/plugin.ts @@ -16,13 +16,13 @@ import { createPlugin } from '@backstage/core'; import { ScaffolderPage } from './components/ScaffolderPage'; -import { CreatePage } from './components/CreatePage'; -import { rootRoute, createTemplateRoute } from './routes'; +import { TemplatePage } from './components/TemplatePage'; +import { rootRoute, templateRoute } from './routes'; export const plugin = createPlugin({ id: 'scaffolder', register({ router }) { router.addRoute(rootRoute, ScaffolderPage); - router.addRoute(createTemplateRoute, CreatePage); + router.addRoute(templateRoute, TemplatePage); }, }); diff --git a/plugins/scaffolder/src/routes.ts b/plugins/scaffolder/src/routes.ts index f42f9540f9..28c77f29a6 100644 --- a/plugins/scaffolder/src/routes.ts +++ b/plugins/scaffolder/src/routes.ts @@ -1,3 +1,18 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { createRouteRef } from '@backstage/core'; export const rootRoute = createRouteRef({ @@ -5,7 +20,7 @@ export const rootRoute = createRouteRef({ path: '/create', title: 'Create new entity', }); -export const createTemplateRoute = createRouteRef({ +export const templateRoute = createRouteRef({ icon: () => null, path: '/create/:templateName', title: 'Entity creation', diff --git a/plugins/scaffolder/src/components/JobStatusModal/types.ts b/plugins/scaffolder/src/types.ts similarity index 100% rename from plugins/scaffolder/src/components/JobStatusModal/types.ts rename to plugins/scaffolder/src/types.ts diff --git a/yarn.lock b/yarn.lock index 8b28254a52..2a86168c2c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13030,6 +13030,11 @@ moment@2.26.0, moment@^2.25.3, moment@^2.26.0: resolved "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz#5e1f82c6bafca6e83e808b30c8705eed0dcbd39a" integrity sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw== +moment@^2.27.0: + version "2.27.0" + resolved "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d" + integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ== + monaco-editor@^0.20.0: version "0.20.0" resolved "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.20.0.tgz#5d5009343a550124426cb4d965a4d27a348b4dea" @@ -15697,7 +15702,7 @@ react-router-dom@^5.2.0: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" -react-router@5.2.0: +react-router@5.2.0, react-router@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz#424e75641ca8747fbf76e5ecca69781aa37ea293" integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==