diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 993fd77978..cb0853a013 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -26,6 +26,11 @@ import * as plugins from './plugins'; import { apis } from './apis'; import { hot } from 'react-hot-loader/root'; import { providers } from './identityProviders'; +import { CatalogPlugin } from '@backstage/plugin-catalog'; +// import { ExplorePlugin } from '@backstage/plugin-explore'; +import { Route, Routes } from 'react-router'; + +import { EntityPage } from './components/catalog'; const app = createApp({ apis, @@ -46,8 +51,16 @@ const app = createApp({ const AppProvider = app.getProvider(); const AppRouter = app.getRouter(); -const AppRoutes = app.getRoutes(); +const AppRoutes = () => ( + + } + /> + {/* } /> */} + +); const App: FC<{}> = () => ( diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 1d32b6e16e..c6ce87444c 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -89,7 +89,7 @@ const Root: FC<{}> = ({ children }) => ( {/* Global nav, not org-specific */} - + diff --git a/packages/app/src/components/catalog/Component.tsx b/packages/app/src/components/catalog/Component.tsx new file mode 100644 index 0000000000..2d2aef9f79 --- /dev/null +++ b/packages/app/src/components/catalog/Component.tsx @@ -0,0 +1,82 @@ +/* + * 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 from 'react'; +import { + GitHubActionsPlugin, + GITHUB_ACTIONS_ANNOTATION, +} from '@backstage/plugin-github-actions'; +import { + useEntity, + EntityPageTabs as Tabs, + EntityMetadataCard, +} from '@backstage/plugin-catalog'; +import { Grid } from '@material-ui/core'; + +const OverviewPage = () => { + const entity = useEntity(); + return ( + + + + ); +}; + +// Just for illustration purposes, gonna live in plugin-circle-ci +const CIRCLE_CI_ANNOTATION = 'circle-ci-slug-dummy'; + +const DefaultEntity = () => { + const entity = useEntity(); + + const isCIAvailable = [ + entity!.metadata!.annotations?.[GITHUB_ACTIONS_ANNOTATION], + entity!.metadata!.annotations?.[CIRCLE_CI_ANNOTATION], + ].some(Boolean); + + return ( + + + + + {isCIAvailable && ( + + {entity!.metadata!.annotations?.[GITHUB_ACTIONS_ANNOTATION] && ( + + )} + + )} + + {/* eslint-disable-next-line no-console */} + {console.log('was here /docs')}Docs tab contents + + + ); +}; + +const Service = () => ; +const Website = () => ; +const UnkownEntity = () => Unknown entity!; + +export const ComponentEntity = () => { + const entity = useEntity(); + switch (entity.spec!.type) { + case 'service': + return ; + case 'website': + return ; + default: + return ; + } +}; diff --git a/plugins/github-actions/src/components/WorkflowRunDetailsPage/index.ts b/packages/app/src/components/catalog/index.tsx similarity index 60% rename from plugins/github-actions/src/components/WorkflowRunDetailsPage/index.ts rename to packages/app/src/components/catalog/index.tsx index 443bcacc88..9aaf1a568b 100644 --- a/plugins/github-actions/src/components/WorkflowRunDetailsPage/index.ts +++ b/packages/app/src/components/catalog/index.tsx @@ -13,5 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import React from 'react'; +import { useEntity } from '@backstage/plugin-catalog'; +import { ComponentEntity } from './Component'; -export { WorkflowRunDetailsPage } from './WorkflowRunDetailsPage'; +const UnkownEntityKind = () => Unknown entity kind!; + +export const EntityPage = () => { + const entity = useEntity(); + switch (entity.kind) { + case 'Component': + return ; + default: + return ; + } +}; diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 1e10e91478..4768fdf985 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -36,6 +36,7 @@ "moment": "^2.26.0", "react": "^16.13.1", "react-dom": "^16.13.1", + "react-helmet": "6.1.0", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-use": "^15.3.3", diff --git a/plugins/catalog/src/Router.tsx b/plugins/catalog/src/Router.tsx new file mode 100644 index 0000000000..701b9d1045 --- /dev/null +++ b/plugins/catalog/src/Router.tsx @@ -0,0 +1,164 @@ +/* + * 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, { FC, useState } from 'react'; +import { CatalogPage } from './components/CatalogPage/CatalogPage'; +// import { EntityPage } from './components/EntityPage/EntityPage'; +import { Route, Routes, useParams, useNavigate } from 'react-router'; +import { entityRoute, rootRoute, entityRouteDefault } from './routes'; +import { useEntityFromUrl, EntityContext, useEntity } from './hooks/useEntity'; +import { + pageTheme, + PageTheme, + Page, + Header, + HeaderLabel, + Content, + Progress, +} from '@backstage/core'; +import { Entity } from '@backstage/catalog-model'; +import { FavouriteEntity } from './components/FavouriteEntity/FavouriteEntity'; +import { Box } from '@material-ui/core'; +import { EntityContextMenu } from './components/EntityContextMenu/EntityContextMenu'; +import { UnregisterEntityDialog } from './components/UnregisterEntityDialog/UnregisterEntityDialog'; +import { Alert } from '@material-ui/lab'; +// const EntityContext = React.createContext(null); +export const getPageTheme = (entity?: Entity): PageTheme => { + const themeKey = entity?.spec?.type?.toString() ?? 'home'; + return pageTheme[themeKey] ?? pageTheme.home; +}; + +const EntityPageTitle = ({ + entity, + title, +}: { + title: string; + entity: Entity | undefined; +}) => ( + + {title} + {entity && } + +); + +function headerProps( + kind: string, + namespace: string | undefined, + name: string, + entity: Entity | undefined, +): { headerTitle: string; headerType: string } { + return { + headerTitle: `${name}${namespace ? ` in ${namespace}` : ''}`, + headerType: (() => { + let t = kind.toLowerCase(); + if (entity && entity.spec && 'type' in entity.spec) { + t += ' — '; + t += (entity.spec as { type: string }).type.toLowerCase(); + } + return t; + })(), + }; +} +const EntityPageLayout = ({ children }: { children: React.ReactNode }) => { + const { entity, loading, error } = useEntityFromUrl(); + const { optionalNamespaceAndName, kind } = useParams() as { + optionalNamespaceAndName: string; + kind: string; + }; + const [name, namespace] = optionalNamespaceAndName.split(':').reverse(); + + const { headerTitle, headerType } = headerProps( + kind, + namespace, + name, + entity!, + ); + + const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false); + const navigate = useNavigate(); + const cleanUpAfterRemoval = async () => { + setConfirmationDialogOpen(false); + navigate('/'); + }; + + const showRemovalDialog = () => setConfirmationDialogOpen(true); + + return ( + + } + pageTitleOverride={headerTitle} + type={headerType} + > + {entity && ( + <> + + + + > + )} + + + {loading && } + + {entity && ( + + {children} + + )} + {error && ( + + {error.toString()} + + )} + setConfirmationDialogOpen(false)} + /> + + ); +}; + +export const CatalogPlugin = ({ EntityPage }) => ( + + } /> + + + + } + /> + + + + } + /> + +); + +export { EntityMetadataCard } from './components/EntityMetadataCard/EntityMetadataCard'; diff --git a/plugins/catalog/src/components/EntityPageTabs/EntityPageTabs.tsx b/plugins/catalog/src/components/EntityPageTabs/EntityPageTabs.tsx new file mode 100644 index 0000000000..f4f9308669 --- /dev/null +++ b/plugins/catalog/src/components/EntityPageTabs/EntityPageTabs.tsx @@ -0,0 +1,94 @@ +/* + * 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 from 'react'; +import { + useParams, + useNavigate, + PartialRouteObject, + matchRoutes, + RouteObject, + useRoutes, + Navigate, + RouteMatch, +} from 'react-router'; +import { Tab, HeaderTabs, Content } from '@backstage/core'; +import { Grid } from '@material-ui/core'; +import { Helmet } from 'react-helmet'; + +const getSelectedIndex = (matchedRoute: RouteMatch, tabs: Tab[]) => { + if (!matchedRoute) return 0; + const tabIndex = tabs.findIndex(t => t.id === matchedRoute.route.path); + return ~tabIndex ? tabIndex : 0; +}; +export const EntityPageTabs = ({ children }: { children: React.ReactNode }) => { + const routes: PartialRouteObject[] = []; + const tabs: Tab[] = []; + const params = useParams(); + const navigate = useNavigate(); + React.Children.forEach(children, child => { + if (!React.isValidElement(child)) { + // Skip conditionals resolved to falses/nulls/undefineds etc + return; + } + const pathAndId = + (child as JSX.Element).props.path + + ((child as JSX.Element).props.exact ? '' : '/*'); + routes.push({ + path: pathAndId, + element: (child as JSX.Element).props.children, + }); + tabs.push({ + id: pathAndId, + label: (child as JSX.Element).props.title, + }); + routes.push({ + path: '/*', + element: , + }); + }); + const [matchedRoute] = + matchRoutes(routes as RouteObject[], `/${params['*']}`) ?? []; + const selectedIndex = getSelectedIndex(matchedRoute, tabs); + const currentTab = tabs[selectedIndex]; + const title = currentTab.label; + + const onTabChange = (index: number) => navigate(tabs[index].id.slice(1, -2)); + + const currentRouteElement = useRoutes(routes); + + return ( + <> + + + + + {currentRouteElement} + + + > + ); +}; +type TabProps = { + children: React.ReactNode; + title: string; + path: string; + exact?: boolean; +}; +EntityPageTabs.Tab = (_props: TabProps) => null; diff --git a/plugins/github-actions/src/components/WorkflowRunsPage/index.ts b/plugins/catalog/src/components/EntityPageTabs/index.ts similarity index 91% rename from plugins/github-actions/src/components/WorkflowRunsPage/index.ts rename to plugins/catalog/src/components/EntityPageTabs/index.ts index ef511763f7..83fc3d1095 100644 --- a/plugins/github-actions/src/components/WorkflowRunsPage/index.ts +++ b/plugins/catalog/src/components/EntityPageTabs/index.ts @@ -13,5 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -export { WorkflowRunsPage } from './WorkflowRunsPage'; +export { EntityPageTabs } from './EntityPageTabs'; diff --git a/plugins/catalog/src/hooks/useEntity.ts b/plugins/catalog/src/hooks/useEntity.ts new file mode 100644 index 0000000000..022be1126f --- /dev/null +++ b/plugins/catalog/src/hooks/useEntity.ts @@ -0,0 +1,55 @@ +/* + * 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 { useEffect, createContext, useContext } from 'react'; +import { useNavigate, useParams } from 'react-router'; +import { useApi, errorApiRef } from '@backstage/core'; +import { catalogApiRef } from '../api/types'; +import { useAsync } from 'react-use'; +import { Entity } from '@backstage/catalog-model'; + +const REDIRECT_DELAY = 2000; + +export const EntityContext = createContext((null as any) as Entity); +export const useEntityFromUrl = () => { + const { optionalNamespaceAndName, kind } = useParams(); + const [name, namespace] = optionalNamespaceAndName.split(':').reverse(); + const navigate = useNavigate(); + const errorApi = useApi(errorApiRef); + const catalogApi = useApi(catalogApiRef); + + const { value: entity, error, loading } = useAsync( + () => catalogApi.getEntityByName({ kind, namespace, name }), + [catalogApi, kind, namespace, name], + ); + + useEffect(() => { + if (!error && !loading && !entity) { + errorApi.post(new Error('Entity not found!')); + setTimeout(() => { + navigate('/'); + }, REDIRECT_DELAY); + } + }, [errorApi, navigate, error, loading, entity]); + + if (!name) { + navigate('/catalog'); + return { entity: null, loading: null, error: new Error('No name in url') }; + } + + return { entity, loading, error }; +}; + +export const useEntity = () => useContext(EntityContext); diff --git a/plugins/catalog/src/index.ts b/plugins/catalog/src/index.ts index 36fd69971d..ba8826aaa9 100644 --- a/plugins/catalog/src/index.ts +++ b/plugins/catalog/src/index.ts @@ -19,3 +19,6 @@ export * from './api/CatalogClient'; export * from './api/types'; export * from './routes'; export { useEntityCompoundName } from './components/useEntityCompoundName'; +export * from './Router'; +export { useEntity } from './hooks/useEntity'; +export { EntityPageTabs } from './components/EntityPageTabs'; diff --git a/plugins/catalog/src/routes.ts b/plugins/catalog/src/routes.ts index a8ff8df685..c3fd97564a 100644 --- a/plugins/catalog/src/routes.ts +++ b/plugins/catalog/src/routes.ts @@ -20,16 +20,16 @@ const NoIcon = () => null; export const rootRoute = createRouteRef({ icon: NoIcon, - path: '/', + path: '', title: 'Catalog', }); export const entityRoute = createRouteRef({ icon: NoIcon, - path: '/catalog/:kind/:optionalNamespaceAndName/:selectedTabId/*', + path: ':kind/:optionalNamespaceAndName/*', title: 'Entity', }); export const entityRouteDefault = createRouteRef({ icon: NoIcon, - path: '/catalog/:kind/:optionalNamespaceAndName', + path: ':kind/:optionalNamespaceAndName', title: 'Entity', }); diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index db35022fae..3386ed0db2 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -35,6 +35,7 @@ "moment": "^2.27.0", "react": "^16.13.1", "react-dom": "^16.13.1", + "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-use": "^15.3.3" }, diff --git a/plugins/github-actions/src/Router.tsx b/plugins/github-actions/src/Router.tsx new file mode 100644 index 0000000000..b843c594b9 --- /dev/null +++ b/plugins/github-actions/src/Router.tsx @@ -0,0 +1,34 @@ +/* + * 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 from 'react'; +import { Entity } from '@backstage/catalog-model'; +import { Routes, Route } from 'react-router'; +import { rootRouteRef, buildRouteRef } from './plugin'; +import { WorkflowRunDetails } from './components/WorkflowRunDetails'; +import { WorkflowRunsTable } from './components/WorkflowRunsTable'; + +export const GitHubActionsPlugin = ({ entity }: { entity: Entity }) => ( + + } + /> + } + /> + +); diff --git a/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx b/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx index 8c445ca9f6..d8e3b02524 100644 --- a/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx +++ b/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx @@ -14,7 +14,6 @@ * limitations under the License. */ import React from 'react'; -import { useEntityCompoundName } from '@backstage/plugin-catalog'; import { useWorkflowRunsDetails } from './useWorkflowRunsDetails'; import { useWorkflowRunJobs } from './useWorkflowRunJobs'; import { useProjectName } from '../useProjectName'; @@ -35,13 +34,16 @@ import { LinearProgress, CircularProgress, Theme, - Link, + Breadcrumbs, + Link as MaterialLink, } from '@material-ui/core'; import { Jobs, Job, Step } from '../../api'; import moment from 'moment'; import { WorkflowRunStatus } from '../WorkflowRunStatus'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import ExternalLinkIcon from '@material-ui/icons/Launch'; +import { Entity } from '@backstage/catalog-model'; +import { Link } from '@backstage/core'; const useStyles = makeStyles(theme => ({ root: { @@ -140,18 +142,8 @@ const JobListItem = ({ job, className }: { job: Job; className: string }) => { ); }; -export const WorkflowRunDetails = () => { - let entityCompoundName = useEntityCompoundName(); - if (!entityCompoundName.name) { - // TODO(shmidt-i): remove when is fully integrated - // into the entity view - entityCompoundName = { - kind: 'Component', - name: 'backstage', - namespace: 'default', - }; - } - const projectName = useProjectName(entityCompoundName); +export const WorkflowRunDetails = ({ entity }: { entity: Entity }) => { + const projectName = useProjectName(entity); const [owner, repo] = projectName.value ? projectName.value.split('/') : []; const details = useWorkflowRunsDetails(repo, owner); @@ -170,6 +162,10 @@ export const WorkflowRunDetails = () => { } return ( + + Workflow runs + Workflow run details + @@ -211,10 +207,10 @@ export const WorkflowRunDetails = () => { {details.value?.html_url && ( - + Workflow runs on GitHub{' '} - + )} diff --git a/plugins/github-actions/src/components/WorkflowRunDetailsPage/WorkflowRunDetailsPage.tsx b/plugins/github-actions/src/components/WorkflowRunDetailsPage/WorkflowRunDetailsPage.tsx deleted file mode 100644 index ec9a3f484d..0000000000 --- a/plugins/github-actions/src/components/WorkflowRunDetailsPage/WorkflowRunDetailsPage.tsx +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 { Typography, Grid, Breadcrumbs } from '@material-ui/core'; - -import React from 'react'; -import { - Link, - Page, - Header, - HeaderLabel, - Content, - ContentHeader, - SupportButton, - pageTheme, -} from '@backstage/core'; - -import { WorkflowRunDetails } from '../WorkflowRunDetails'; - -/** - * A component for Jobs visualization. Jobs are a property of a Workflow Run. - */ -export const WorkflowRunDetailsPage = () => { - return ( - - - - - - - - - This plugin allows you to view and interact with your builds within - the GitHub Actions environment. - - - - Workflow runs - Workflow run details - - - - - - - - - ); -}; diff --git a/plugins/github-actions/src/components/WorkflowRunsPage/WorkflowRunsPage.tsx b/plugins/github-actions/src/components/WorkflowRunsPage/WorkflowRunsPage.tsx deleted file mode 100644 index b75a70f326..0000000000 --- a/plugins/github-actions/src/components/WorkflowRunsPage/WorkflowRunsPage.tsx +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 { - Header, - HeaderLabel, - pageTheme, - Page, - Content, - ContentHeader, - SupportButton, -} from '@backstage/core'; -import { Grid } from '@material-ui/core'; -import React from 'react'; - -import { WorkflowRunsTable } from '../WorkflowRunsTable'; - -export const WorkflowRunsPage = () => { - return ( - - - - - - - - - This plugin allows you to view and interact with your builds within - the GitHub Actions environment. - - - - - - - - - - ); -}; diff --git a/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx b/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx index a2a6ead9b5..7e971118fc 100644 --- a/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx +++ b/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx @@ -25,6 +25,7 @@ import SyncIcon from '@material-ui/icons/Sync'; import { buildRouteRef } from '../../plugin'; import { useEntityCompoundName } from '@backstage/plugin-catalog'; import { useProjectName } from '../useProjectName'; +import { Entity } from '@backstage/catalog-model'; export type WorkflowRun = { id: string; @@ -134,6 +135,7 @@ export const WorkflowRunsTableView: FC = ({ data={runs ?? []} onChangePage={onChangePage} onChangeRowsPerPage={onChangePageSize} + style={{ width: '100%' }} title={ @@ -146,25 +148,14 @@ export const WorkflowRunsTableView: FC = ({ ); }; -export const WorkflowRunsTable = () => { - let entityCompoundName = useEntityCompoundName(); - - if (!entityCompoundName.name) { - // TODO(shmidt-i): remove when is fully integrated - // into the entity view - entityCompoundName = { - kind: 'Component', - name: 'backstage', - namespace: 'default', - }; - } - - const { value: projectName, loading } = useProjectName(entityCompoundName); +export const WorkflowRunsTable = ({ entity }: { entity: Entity }) => { + const { value: projectName, loading } = useProjectName(entity); const [owner, repo] = (projectName ?? '/').split('/'); const [tableProps, { retry, setPage, setPageSize }] = useWorkflowRuns({ owner, repo, }); + return ( { - const catalogApi = useApi(catalogApiRef); +export const GITHUB_ACTIONS_ANNOTATION = 'github.com/project-slug'; +export const useProjectName = (entity: Entity) => { const { value, loading, error } = useAsync(async () => { - const entity = await catalogApi.getEntityByName(name); - return entity?.metadata.annotations?.['github.com/project-slug'] ?? ''; + return entity?.metadata.annotations?.[GITHUB_ACTIONS_ANNOTATION] ?? ''; }); return { value, loading, error }; }; diff --git a/plugins/github-actions/src/index.ts b/plugins/github-actions/src/index.ts index 4a69c363cd..f500685507 100644 --- a/plugins/github-actions/src/index.ts +++ b/plugins/github-actions/src/index.ts @@ -17,3 +17,5 @@ export { plugin } from './plugin'; export * from './api'; export { Widget } from './components/Widget'; +export { GitHubActionsPlugin } from './Router'; +export { GITHUB_ACTIONS_ANNOTATION } from './components/useProjectName'; diff --git a/plugins/github-actions/src/plugin.ts b/plugins/github-actions/src/plugin.ts index dbc366bd71..ead33b7959 100644 --- a/plugins/github-actions/src/plugin.ts +++ b/plugins/github-actions/src/plugin.ts @@ -15,28 +15,19 @@ */ import { createPlugin, createRouteRef } from '@backstage/core'; -import { WorkflowRunDetailsPage } from './components/WorkflowRunDetailsPage'; -import { WorkflowRunsPage } from './components/WorkflowRunsPage'; // TODO(freben): This is just a demo route for now export const rootRouteRef = createRouteRef({ - path: '/github-actions', + path: '', title: 'GitHub Actions', }); -export const projectRouteRef = createRouteRef({ - path: '/github-actions/:kind/:optionalNamespaceAndName/', - title: 'GitHub Actions for project', -}); + export const buildRouteRef = createRouteRef({ - path: '/github-actions/workflow-run/:id', + path: ':id', title: 'GitHub Actions Workflow Run', }); export const plugin = createPlugin({ id: 'github-actions', - register({ router }) { - router.addRoute(rootRouteRef, WorkflowRunsPage); - router.addRoute(projectRouteRef, WorkflowRunsPage); - router.addRoute(buildRouteRef, WorkflowRunDetailsPage); - }, + register() {}, });