From be2d7b3376f34c4f1a2ba04d6da246a8f2e5d65d Mon Sep 17 00:00:00 2001 From: Marek Calus Date: Thu, 12 Nov 2020 09:08:49 +0100 Subject: [PATCH] Change ragister-component to catalog-import --- packages/app/src/App.tsx | 5 +- packages/app/src/plugins.ts | 1 + .../src/ingestion/LocationAnalyzer.ts | 4 +- plugins/catalog-import/package.json | 2 +- .../src/components/ComponentConfigDisplay.tsx | 129 ++++++++++++++---- .../src/components/ImportComponentForm.tsx | 25 +++- .../src/components/ImportComponentPage.tsx | 36 +++-- .../src/components/ImportFinished.tsx | 48 +++---- .../catalog-import/src/components/Router.tsx | 7 +- .../catalog-import/src/util/useGithubRepos.ts | 4 +- .../ScaffolderPage/ScaffolderPage.tsx | 9 -- 11 files changed, 185 insertions(+), 85 deletions(-) diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 0454da74e1..c02d83841f 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -68,7 +68,10 @@ const catalogRouteRef = createRouteRef({ const AppRoutes = () => ( - } /> + } + /> } diff --git a/packages/app/src/plugins.ts b/packages/app/src/plugins.ts index 90c0287b08..81d2c2cdec 100644 --- a/packages/app/src/plugins.ts +++ b/packages/app/src/plugins.ts @@ -38,4 +38,5 @@ export { plugin as Cloudbuild } from '@backstage/plugin-cloudbuild'; export { plugin as CostInsights } from '@backstage/plugin-cost-insights'; export { plugin as GitHubInsights } from '@roadiehq/backstage-plugin-github-insights'; export { plugin as CatalogImport } from '@backstage/plugin-catalog-import'; +export { plugin as BulkCatalogImport } from '@roadiehq/backstage-plugin-bulk-catalog-import'; export { plugin as UserSettings } from '@backstage/plugin-user-settings'; diff --git a/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts b/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts index 560a5c0563..611505d707 100644 --- a/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts +++ b/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts @@ -15,7 +15,7 @@ */ import { Logger } from 'winston'; -import GitUriParser from 'git-url-parse'; +import parseGitUri from 'git-url-parse'; import { AnalyzeLocationRequest, AnalyzeLocationResponse, @@ -31,7 +31,7 @@ export class LocationAnalyzerClient implements LocationAnalyzer { async generateConfig( request: AnalyzeLocationRequest, ): Promise { - const { owner, name, source } = GitUriParser(request.location.target); + const { owner, name, source } = parseGitUri(request.location.target); const entity = { apiVersion: 'backstage.io/v1alpha1', kind: 'Component', diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index d7f34584b4..d6a08a35c7 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -34,7 +34,7 @@ "react": "^16.13.1", "react-dom": "^16.13.1", "react-hook-form": "^6.6.0", - "react-router": "^5.2.0", + "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-use": "^15.3.3", "yaml": "^1.10.0" diff --git a/plugins/catalog-import/src/components/ComponentConfigDisplay.tsx b/plugins/catalog-import/src/components/ComponentConfigDisplay.tsx index 0265440fa6..8e47db8e14 100644 --- a/plugins/catalog-import/src/components/ComponentConfigDisplay.tsx +++ b/plugins/catalog-import/src/components/ComponentConfigDisplay.tsx @@ -15,57 +15,140 @@ */ import React, { useCallback, useState } from 'react'; -import { Button, CircularProgress, Grid, Tooltip } from '@material-ui/core'; -import Alert from '@material-ui/lab/Alert'; +import { + Button, + CircularProgress, + Grid, + Link, + List, + ListItem, + Typography, + Divider, +} from '@material-ui/core'; import { useGithubRepos } from '../util/useGithubRepos'; import { ConfigSpec } from './ImportComponentPage'; -import { errorApiRef, useApi } from '@backstage/core'; +import { + errorApiRef, + RouteRef, + StructuredMetadataTable, + useApi, +} from '@backstage/core'; +import parseGitUri from 'git-url-parse'; +import { PartialEntity } from '../util/types'; +import { generatePath, resolvePath } from 'react-router'; +import { entityRoute, entityRouteParams } from '@backstage/plugin-catalog'; +import { Entity } from '@backstage/catalog-model'; +import { Link as RouterLink } from 'react-router-dom'; + +const getEntityCatalogPath = ({ + entity, + catalogRouteRef, +}: { + entity: PartialEntity; + catalogRouteRef: RouteRef; +}) => { + const relativeEntityPathInsideCatalog = generatePath( + entityRoute.path, + entityRouteParams(entity as Entity), + ); + + const resolvedAbsolutePath = resolvePath( + relativeEntityPathInsideCatalog, + catalogRouteRef.path, + )?.pathname; + + return resolvedAbsolutePath; +}; type Props = { nextStep: () => void; configFile: ConfigSpec; savePRLink: (PRLink: string) => void; + catalogRouteRef: RouteRef; }; const ComponentConfigDisplay = ({ nextStep, configFile, savePRLink, + catalogRouteRef, }: Props) => { const [submitting, setSubmitting] = useState(false); const errorApi = useApi(errorApiRef); - const { submitPrToRepo } = useGithubRepos(); + const { submitPrToRepo, addLocation } = useGithubRepos(); const onNext = useCallback(async () => { try { setSubmitting(true); - const result = await submitPrToRepo(configFile); - savePRLink(result.link); - setSubmitting(false); - nextStep(); + if (!parseGitUri(configFile.location).filepathtype) { + const result = await submitPrToRepo(configFile); + savePRLink(result.link); + setSubmitting(false); + nextStep(); + } else { + addLocation(configFile.location); + setSubmitting(false); + nextStep(); + } } catch (e) { setSubmitting(false); errorApi.post(e); } - }, [submitPrToRepo, configFile, nextStep, savePRLink, errorApi]); + }, [submitPrToRepo, configFile, nextStep, savePRLink, errorApi, addLocation]); return ( + {!parseGitUri(configFile.location).filepathtype ? ( + + Following config object will be submitted in a pull request to the + repository{' '} + {configFile.location} and + added as a new location to the backend + + ) : ( + + Following config object will be added as a new location to the backend{' '} + {configFile.location} + + )} + - - config file has been generated. You can optionally edit it before - submitting Pull Request - + {!parseGitUri(configFile.location).filepathtype ? ( +
{JSON.stringify(configFile.config, null, 2)}
+ ) : ( + + {configFile.config.map((entity: any, index: number) => { + const entityPath = getEntityCatalogPath({ + entity, + catalogRouteRef, + }); + return ( + + + + {entityPath} + + ), + }} + /> + + {index < configFile.config.length - 1 && ( + + )} + + ); + })} + + )}
- - - - - - - - + {submitting ? ( diff --git a/plugins/catalog-import/src/components/ImportComponentForm.tsx b/plugins/catalog-import/src/components/ImportComponentForm.tsx index da078e10f0..9a4fd38a06 100644 --- a/plugins/catalog-import/src/components/ImportComponentForm.tsx +++ b/plugins/catalog-import/src/components/ImportComponentForm.tsx @@ -26,9 +26,11 @@ import { makeStyles } from '@material-ui/core/styles'; import React from 'react'; import { useForm } from 'react-hook-form'; import { useMountedState } from 'react-use'; +import parseGitUri from 'git-url-parse'; import { ComponentIdValidators } from '../util/validate'; import { useGithubRepos } from '../util/useGithubRepos'; import { ConfigSpec } from './ImportComponentPage'; +import { catalogApiRef } from '@backstage/plugin-catalog'; const useStyles = makeStyles(theme => ({ form: { @@ -53,6 +55,7 @@ export const RegisterComponentForm = ({ nextStep, saveConfig }: Props) => { const classes = useStyles(); const hasErrors = !!errors.componentLocation; const dirty = formState?.isDirty; + const catalogApi = useApi(catalogApiRef); const isMounted = useMountedState(); const errorApi = useApi(errorApiRef); @@ -62,12 +65,22 @@ export const RegisterComponentForm = ({ nextStep, saveConfig }: Props) => { const { componentLocation: target } = formData; try { if (!isMounted()) return; + const type = !parseGitUri(target).filepathtype ? 'repo' : 'file'; - const config = await generateEntityDefinitions(target); - saveConfig({ - repo: target, - config, - }); + if (type === 'repo') { + saveConfig({ + type, + location: target, + config: await generateEntityDefinitions(target), + }); + } else { + const data = await catalogApi.addLocation({ target }); + saveConfig({ + type, + location: data.location.target, + config: data.entities, + }); + } nextStep(); } catch (e) { errorApi.post(e); @@ -111,7 +124,7 @@ export const RegisterComponentForm = ({ nextStep, saveConfig }: Props) => { disabled={!dirty || hasErrors} className={classes.submit} > - Submit + Next ); diff --git a/plugins/catalog-import/src/components/ImportComponentPage.tsx b/plugins/catalog-import/src/components/ImportComponentPage.tsx index 8371eda927..c2eb935e54 100644 --- a/plugins/catalog-import/src/components/ImportComponentPage.tsx +++ b/plugins/catalog-import/src/components/ImportComponentPage.tsx @@ -23,6 +23,7 @@ import { Header, SupportButton, ContentHeader, + RouteRef, } from '@backstage/core'; import { RegisterComponentForm } from './ImportComponentForm'; import ImportStepper from './ImportStepper'; @@ -31,29 +32,35 @@ import { ImportFinished } from './ImportFinished'; import { PartialEntity } from '../util/types'; export type ConfigSpec = { - repo: string; + type: 'repo' | 'file'; + location: string; config: PartialEntity[]; }; -export const ImportComponentPage = () => { +export const ImportComponentPage = ({ + catalogRouteRef, +}: { + catalogRouteRef: RouteRef; +}) => { const [activeStep, setActiveStep] = useState(0); const [configFile, setConfigFile] = useState({ - repo: '', + type: 'repo', + location: '', config: [], }); - const [PRLink, setPRLink] = useState(''); + const [endLink, setEndLink] = useState(''); const nextStep = (options?: { reset: boolean }) => { setActiveStep(step => (options?.reset ? 0 : step + 1)); }; return ( -
+
- + - Generate a component definition file and automatically submit it as - a pull request. TODO: Add more information about what this is. + Start tracking your component in Backstage. TODO: Add more + information about what this is. @@ -62,7 +69,7 @@ export const ImportComponentPage = () => { { ), }, { - step: 'Review generated component config files', + step: 'Review', content: ( ), }, { step: 'Finish', content: ( - + ), }, ]} diff --git a/plugins/catalog-import/src/components/ImportFinished.tsx b/plugins/catalog-import/src/components/ImportFinished.tsx index e7680dbf90..d4ff66ddab 100644 --- a/plugins/catalog-import/src/components/ImportFinished.tsx +++ b/plugins/catalog-import/src/components/ImportFinished.tsx @@ -13,52 +13,44 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React from 'react'; -import Button from '@material-ui/core/Button'; -import { - Grid, - Link, - Typography, - createStyles, - makeStyles, - Theme, -} from '@material-ui/core'; -import { Alert } from '@material-ui/lab'; -const useStyles = makeStyles((theme: Theme) => - createStyles({ - heading: { - fontSize: theme.typography.pxToRem(15), - fontWeight: theme.typography.fontWeightRegular, - }, - }), -); +import React from 'react'; +import { Alert } from '@material-ui/lab'; +import { Link as RouterLink } from 'react-router-dom'; +import { Button, Grid, Link } from '@material-ui/core'; + type Props = { + type: 'repo' | 'file'; nextStep: (options?: { reset: boolean }) => void; PRLink: string; }; -export const ImportFinished = ({ nextStep, PRLink }: Props) => { - const classes = useStyles(); +export const ImportFinished = ({ nextStep, PRLink, type }: Props) => { return ( - Pull requests have been successfully opened. You can start again to - import more repositories + {type === 'repo' + ? 'Pull requests have been successfully opened. You can start again to import more repositories' + : 'Entity added to catalog successfully'} - Pull request link: - {PRLink} - - + {type === 'repo' ? ( + + 'View pull request on GitHub' + + ) : null} diff --git a/plugins/catalog-import/src/components/Router.tsx b/plugins/catalog-import/src/components/Router.tsx index bc3c776d0b..5175f8f077 100644 --- a/plugins/catalog-import/src/components/Router.tsx +++ b/plugins/catalog-import/src/components/Router.tsx @@ -14,12 +14,15 @@ * limitations under the License. */ +import { RouteRef } from '@backstage/core'; import React from 'react'; import { Route, Routes } from 'react-router-dom'; import { ImportComponentPage } from './ImportComponentPage'; -export const Router = () => ( +export const Router = ({ catalogRouteRef }: { catalogRouteRef: RouteRef }) => ( - } /> + } + /> ); diff --git a/plugins/catalog-import/src/util/useGithubRepos.ts b/plugins/catalog-import/src/util/useGithubRepos.ts index 3ec4f56c68..84c594c913 100644 --- a/plugins/catalog-import/src/util/useGithubRepos.ts +++ b/plugins/catalog-import/src/util/useGithubRepos.ts @@ -26,7 +26,7 @@ export function useGithubRepos() { const submitPrToRepo = async (selectedRepo: ConfigSpec) => { const token = await auth.getAccessToken(['repo']); - const [ownerName, repoName] = selectedRepo.repo.split('/').slice(-2); + const [ownerName, repoName] = selectedRepo.location.split('/').slice(-2); const submitPRResponse = await api .submitPrToRepo({ oAuthToken: token, @@ -55,5 +55,7 @@ export function useGithubRepos() { submitPrToRepo, generateEntityDefinitions: (repo: string) => api.generateEntityDefinitions({ repo }), + addLocation: (location: string) => + api.createRepositoryLocation({ location }), }; } diff --git a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx index cc57db2161..8ab8c10ddb 100644 --- a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx +++ b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx @@ -82,15 +82,6 @@ export const ScaffolderPage = () => { color="primary" component={RouterLink} to="/catalog-import" - style={{ marginRight: '8px' }} - > - Import Repository - -