From 78bb57db06373eeb040de6754ed047fc537c66ba Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 28 Jan 2021 14:40:44 -0500 Subject: [PATCH 01/31] Update to accordion UX --- .../WarningPanel/WarningPanel.test.tsx | 50 +++++++- .../components/WarningPanel/WarningPanel.tsx | 109 +++++++++++++----- 2 files changed, 126 insertions(+), 33 deletions(-) diff --git a/packages/core/src/components/WarningPanel/WarningPanel.test.tsx b/packages/core/src/components/WarningPanel/WarningPanel.test.tsx index 07a25d34c8..fd7a5f4349 100644 --- a/packages/core/src/components/WarningPanel/WarningPanel.test.tsx +++ b/packages/core/src/components/WarningPanel/WarningPanel.test.tsx @@ -15,23 +15,61 @@ */ import React from 'react'; +import { fireEvent } from '@testing-library/react'; import { renderInTestApp } from '@backstage/test-utils'; +import { Typography } from '@material-ui/core'; import { WarningPanel } from './WarningPanel'; -const minProps = { title: 'Mock title', message: 'Some more info' }; +const propsTitle = { title: 'Mock title' }; +const propsTitleMessage = { title: 'Mock title', message: 'Some more info' }; +const propsMessage = { message: 'Some more info' }; describe('', () => { it('renders without exploding', async () => { - const { getByText } = await renderInTestApp(); - expect(getByText('Mock title')).toBeInTheDocument(); + const { getByText } = await renderInTestApp( + , + ); + expect(getByText('Warning: Mock title')).toBeInTheDocument(); }); - it('renders message and children', async () => { + it('renders title', async () => { const { getByText } = await renderInTestApp( - children, + , ); + const expandIcon = await getByText('Warning: Mock title'); + fireEvent.click(expandIcon); + expect(getByText('Warning: Mock title')).toBeInTheDocument(); expect(getByText('Some more info')).toBeInTheDocument(); - expect(getByText('children')).toBeInTheDocument(); + }); + + it('renders title and children', async () => { + const { getByText } = await renderInTestApp( + + Java stacktrace + , + ); + expect(getByText('Java stacktrace')).toBeInTheDocument(); + }); + + it('renders message', async () => { + const { getByText } = await renderInTestApp( + , + ); + expect(getByText('Warning')).toBeInTheDocument(); + expect(getByText('Some more info')).toBeInTheDocument(); + }); + + it('renders title, message, and children', async () => { + const { getByText } = await renderInTestApp( + + Java stacktrace + , + ); + expect(getByText('Warning: Mock title')).toBeInTheDocument(); + expect(getByText('Some more info')).toBeInTheDocument(); + expect(getByText('Java stacktrace')).toBeInTheDocument(); + // expect(getByText(/Some more info/)).toBeTruthy(); + // expect(getByText(/Java stacktrace/)).toBeTruthy(); }); }); diff --git a/packages/core/src/components/WarningPanel/WarningPanel.tsx b/packages/core/src/components/WarningPanel/WarningPanel.tsx index ae4d2bff02..e55a3c35f4 100644 --- a/packages/core/src/components/WarningPanel/WarningPanel.tsx +++ b/packages/core/src/components/WarningPanel/WarningPanel.tsx @@ -15,8 +15,16 @@ */ import { BackstageTheme } from '@backstage/theme'; -import { makeStyles, Typography } from '@material-ui/core'; +import { + Accordion, + AccordionSummary, + AccordionDetails, + Grid, + makeStyles, + Typography, +} from '@material-ui/core'; import ErrorOutline from '@material-ui/icons/ErrorOutline'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import React from 'react'; const useErrorOutlineStyles = makeStyles(theme => ({ @@ -29,57 +37,104 @@ const ErrorOutlineStyled = () => { const classes = useErrorOutlineStyles(); return ; }; +const ExpandMoreIconStyled = () => { + const classes = useErrorOutlineStyles(); + return ; +}; const useStyles = makeStyles(theme => ({ - message: { - display: 'flex', - flexDirection: 'column', - padding: theme.spacing(1.5), + panel: { + // display: 'flex', + // flexDirection: 'column', + // padding: theme.spacing(1.5), backgroundColor: theme.palette.warningBackground, color: theme.palette.warningText, verticalAlign: 'middle', }, - header: { + summary: { display: 'flex', flexDirection: 'row', - marginBottom: theme.spacing(1), }, - headerText: { + summaryText: { color: theme.palette.warningText, + fontWeight: 'bold', }, - messageText: { + message: { + width: '100%', + display: 'block', color: theme.palette.warningText, + backgroundColor: theme.palette.warningBackground, + }, + details: { + width: '100%', + display: 'block', + color: theme.palette.textContrast, + backgroundColor: theme.palette.background.default, + border: `1px solid ${theme.palette.border}`, + padding: theme.spacing(2.0), + fontFamily: 'sans-serif', }, })); -/** - * WarningPanel. Show a user friendly error message to a user similar to ErrorPanel except that the warning panel - * only shows the warning message to the user - */ - type Props = { - message?: React.ReactNode; title?: string; + severity?: 'warning' | 'error' | 'info'; + message?: React.ReactNode; children?: React.ReactNode; }; +const capitalize = s => { + if (typeof s !== 'string') return ''; + return s.charAt(0).toUpperCase() + s.slice(1); +}; + +/** + * WarningPanel. Show a user friendly error message to a user similar to ErrorPanel except that the warning panel + * only shows the warning message to the user. + * + * @param {string} [severity=warning] Ability to change the severity of the alert. Not fully implemented. (error, warning, info) + * @param {string} [title] A title for the warning. If not supplied, "Warning" will be used. + * @param {Object} [message] Optional more detailed user-friendly message elaborating on the cause of the error. + * @param {Object} [children] Objects to provide context, such as a stack trace or detailed error reporting. + * Will be available inside an unfolded accordion. + */ export const WarningPanel = (props: Props) => { const classes = useStyles(props); - const { title, message, children } = props; + const { severity, title, message, children } = props; + + // If no severity or title provided, the heading will read simply "Warning" + const subTitle = + (severity ? capitalize(severity) : 'Warning') + (title ? `: ${title}` : ''); + return ( -
-
+ + } + className={classes.summary} + > - - {title} - -
- {message && ( - - {message} + + {subTitle} + + {(message || children) && ( + + + {message && ( + + + {message} + + + )} + {children && ( + + {children} + + )} + + )} - {children} -
+ ); }; From 75731af05d65152de6686376a42a8120202cf3cb Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 28 Jan 2021 14:41:14 -0500 Subject: [PATCH 02/31] Update WarningPanel examples for accordion --- .../WarningPanel/WarningPanel.stories.tsx | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/packages/core/src/components/WarningPanel/WarningPanel.stories.tsx b/packages/core/src/components/WarningPanel/WarningPanel.stories.tsx index 5098f0aa31..ef99a0fce4 100644 --- a/packages/core/src/components/WarningPanel/WarningPanel.stories.tsx +++ b/packages/core/src/components/WarningPanel/WarningPanel.stories.tsx @@ -16,7 +16,7 @@ import React from 'react'; import { WarningPanel } from './WarningPanel'; -import { Link, Button } from '@material-ui/core'; +import { Button, Link, Typography } from '@material-ui/core'; export default { title: 'Feedback/Warning Panel', @@ -25,11 +25,11 @@ export default { export const Default = () => ( - This example entity is missing something. If this is unexpected, please - make sure you have set up everything correctly by following{' '} + This example entity is missing an annotation. If this is unexpected, + please make sure you have set up everything correctly by following{' '} this guide. } @@ -37,9 +37,36 @@ export const Default = () => ( ); export const Children = () => ( - - + + + Supports custom children - for example these text elements. This can be + used to hide/expose stack traces for warnings, like this example: +
+ SyntaxError: Error transforming + /home/user/github/backstage/packages/core/src/components/WarningPanel/WarningPanel.stories.tsx: + Unexpected token (42:16) at unexpected + (/home/user/github/backstage/node_modules/sucrase/dist/parser/traverser/util.js:83:15) + at tsParseMaybeAssignWithJSX + (/home/user/github/backstage/node_modules/sucrase/dist/parser/plugins/typescript.js:1399:22) + at tsParseMaybeAssign + (/home/user/github/backstage/node_modules/sucrase/dist/parser/plugins/typescript.js:1373:12) + at parseMaybeAssign + (/home/user/github/backstage/node_modules/sucrase/dist/parser/traverser/expression.js:118:43) + at parseExprListItem + (/home/user/github/backstage/node_modules/sucrase/dist/parser/traverser/expression.js:969:5) +
+
); + +export const FullExample = () => ( + + HTTP 500 Bad Gateway response from + https://usefulservice.mycompany.com/api/entity?44433 + +); + +export const TitleOnly = () => ; From d822468671cc08259a6de908193da9b5fe8f1b9a Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 28 Jan 2021 14:42:13 -0500 Subject: [PATCH 03/31] Add changeset --- .changeset/eight-carrots-talk.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/eight-carrots-talk.md diff --git a/.changeset/eight-carrots-talk.md b/.changeset/eight-carrots-talk.md new file mode 100644 index 0000000000..01e38b845d --- /dev/null +++ b/.changeset/eight-carrots-talk.md @@ -0,0 +1,5 @@ +--- +'@backstage/core': patch +--- + +Update `WarningPanel` component to use accordion-style expansion From 919820ea8a0af024459e6b3fd2712ac33c48b6a5 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 28 Jan 2021 14:53:42 -0500 Subject: [PATCH 04/31] Update warning text color for accordion --- packages/theme/src/themes.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/theme/src/themes.ts b/packages/theme/src/themes.ts index 5b233ac303..fd13343ca8 100644 --- a/packages/theme/src/themes.ts +++ b/packages/theme/src/themes.ts @@ -58,7 +58,7 @@ export const lightTheme = createTheme({ infoBackground: '#ebf5ff', errorText: '#CA001B', infoText: '#004e8a', - warningText: '#FEFEFE', + warningText: '#000000', linkHover: '#2196F3', link: '#0A6EBE', gold: yellow.A700, @@ -120,7 +120,7 @@ export const darkTheme = createTheme({ infoBackground: '#ebf5ff', errorText: '#CA001B', infoText: '#004e8a', - warningText: '#FEFEFE', + warningText: '#000000', linkHover: '#2196F3', link: '#0A6EBE', gold: yellow.A700, From c810082ae6e650cbcfeaaf01272a0f4f4f2f31f3 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 28 Jan 2021 14:54:46 -0500 Subject: [PATCH 05/31] Add theme changeset --- .changeset/fair-kids-laugh.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fair-kids-laugh.md diff --git a/.changeset/fair-kids-laugh.md b/.changeset/fair-kids-laugh.md new file mode 100644 index 0000000000..d478508ed9 --- /dev/null +++ b/.changeset/fair-kids-laugh.md @@ -0,0 +1,5 @@ +--- +'@backstage/theme': patch +--- + +Updates warning text color to align to updated `WarningPanel` styling From ff58e9765c214487d3751c1a9138d398d9d6bf1a Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 28 Jan 2021 15:12:03 -0500 Subject: [PATCH 06/31] Fix TypeScript compile error --- packages/core/src/components/WarningPanel/WarningPanel.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/core/src/components/WarningPanel/WarningPanel.tsx b/packages/core/src/components/WarningPanel/WarningPanel.tsx index e55a3c35f4..3da110c411 100644 --- a/packages/core/src/components/WarningPanel/WarningPanel.tsx +++ b/packages/core/src/components/WarningPanel/WarningPanel.tsx @@ -83,8 +83,7 @@ type Props = { children?: React.ReactNode; }; -const capitalize = s => { - if (typeof s !== 'string') return ''; +const capitalize = (s: string) => { return s.charAt(0).toUpperCase() + s.slice(1); }; From c27107c15ed23f242b1b4c4c93e87caaaea645b2 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Fri, 29 Jan 2021 10:16:41 -0500 Subject: [PATCH 07/31] Refactor to use screen obj --- .../WarningPanel/WarningPanel.test.tsx | 40 ++++++++----------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/packages/core/src/components/WarningPanel/WarningPanel.test.tsx b/packages/core/src/components/WarningPanel/WarningPanel.test.tsx index fd7a5f4349..38ba4bff9b 100644 --- a/packages/core/src/components/WarningPanel/WarningPanel.test.tsx +++ b/packages/core/src/components/WarningPanel/WarningPanel.test.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { fireEvent } from '@testing-library/react'; +import { fireEvent, screen } from '@testing-library/react'; import { renderInTestApp } from '@backstage/test-utils'; import { Typography } from '@material-ui/core'; @@ -27,49 +27,41 @@ const propsMessage = { message: 'Some more info' }; describe('', () => { it('renders without exploding', async () => { - const { getByText } = await renderInTestApp( - , - ); - expect(getByText('Warning: Mock title')).toBeInTheDocument(); + await renderInTestApp(); + expect(screen.getByText('Warning: Mock title')).toBeInTheDocument(); }); it('renders title', async () => { - const { getByText } = await renderInTestApp( - , - ); - const expandIcon = await getByText('Warning: Mock title'); + await renderInTestApp(); + const expandIcon = await screen.getByText('Warning: Mock title'); fireEvent.click(expandIcon); - expect(getByText('Warning: Mock title')).toBeInTheDocument(); - expect(getByText('Some more info')).toBeInTheDocument(); + expect(screen.getByText('Warning: Mock title')).toBeInTheDocument(); + expect(screen.getByText('Some more info')).toBeInTheDocument(); }); it('renders title and children', async () => { - const { getByText } = await renderInTestApp( + await renderInTestApp( Java stacktrace , ); - expect(getByText('Java stacktrace')).toBeInTheDocument(); + expect(screen.getByText('Java stacktrace')).toBeInTheDocument(); }); it('renders message', async () => { - const { getByText } = await renderInTestApp( - , - ); - expect(getByText('Warning')).toBeInTheDocument(); - expect(getByText('Some more info')).toBeInTheDocument(); + await renderInTestApp(); + expect(screen.getByText('Warning')).toBeInTheDocument(); + expect(screen.getByText('Some more info')).toBeInTheDocument(); }); it('renders title, message, and children', async () => { - const { getByText } = await renderInTestApp( + await renderInTestApp( Java stacktrace , ); - expect(getByText('Warning: Mock title')).toBeInTheDocument(); - expect(getByText('Some more info')).toBeInTheDocument(); - expect(getByText('Java stacktrace')).toBeInTheDocument(); - // expect(getByText(/Some more info/)).toBeTruthy(); - // expect(getByText(/Java stacktrace/)).toBeTruthy(); + expect(screen.getByText('Warning: Mock title')).toBeInTheDocument(); + expect(screen.getByText('Some more info')).toBeInTheDocument(); + expect(screen.getByText('Java stacktrace')).toBeInTheDocument(); }); }); From 0dbc8aacad13af965b8f745d9577ebb0e06311f2 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Fri, 29 Jan 2021 10:16:55 -0500 Subject: [PATCH 08/31] Remove unused styling --- packages/core/src/components/WarningPanel/WarningPanel.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/core/src/components/WarningPanel/WarningPanel.tsx b/packages/core/src/components/WarningPanel/WarningPanel.tsx index 3da110c411..e82c49c49a 100644 --- a/packages/core/src/components/WarningPanel/WarningPanel.tsx +++ b/packages/core/src/components/WarningPanel/WarningPanel.tsx @@ -44,9 +44,6 @@ const ExpandMoreIconStyled = () => { const useStyles = makeStyles(theme => ({ panel: { - // display: 'flex', - // flexDirection: 'column', - // padding: theme.spacing(1.5), backgroundColor: theme.palette.warningBackground, color: theme.palette.warningText, verticalAlign: 'middle', From bad33a2ec241636c0ae93af268042649749aa5c5 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 20 Jan 2021 00:12:19 +0100 Subject: [PATCH 09/31] gcp-projects: port to new composability API --- plugins/gcp-projects/package.json | 2 +- .../GcpProjectsPage/GcpProjectsPage.tsx | 29 +++++++ .../src/components/GcpProjectsPage/index.ts | 17 ++++ plugins/gcp-projects/src/index.ts | 2 +- plugins/gcp-projects/src/plugin.ts | 27 +++--- plugins/gcp-projects/src/routes.ts | 30 +++++++ yarn.lock | 83 ++----------------- 7 files changed, 97 insertions(+), 93 deletions(-) create mode 100644 plugins/gcp-projects/src/components/GcpProjectsPage/GcpProjectsPage.tsx create mode 100644 plugins/gcp-projects/src/components/GcpProjectsPage/index.ts create mode 100644 plugins/gcp-projects/src/routes.ts diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 085e08e2a3..d48295e204 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -37,7 +37,7 @@ "@material-ui/lab": "4.0.0-alpha.45", "react": "^16.13.1", "react-dom": "^16.13.1", - "react-router-dom": "^5.2.0", + "react-router-dom": "^6.0.0-beta.0", "react-use": "^15.3.3" }, "devDependencies": { diff --git a/plugins/gcp-projects/src/components/GcpProjectsPage/GcpProjectsPage.tsx b/plugins/gcp-projects/src/components/GcpProjectsPage/GcpProjectsPage.tsx new file mode 100644 index 0000000000..158347a9ff --- /dev/null +++ b/plugins/gcp-projects/src/components/GcpProjectsPage/GcpProjectsPage.tsx @@ -0,0 +1,29 @@ +/* + * 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 { Route, Routes } from 'react-router-dom'; +import { NewProjectPage } from '../NewProjectPage'; +import { ProjectDetailsPage } from '../ProjectDetailsPage'; +import { ProjectListPage } from '../ProjectListPage'; + +export const GcpProjectsPage = () => ( + + } /> + } /> + } /> + +); diff --git a/plugins/gcp-projects/src/components/GcpProjectsPage/index.ts b/plugins/gcp-projects/src/components/GcpProjectsPage/index.ts new file mode 100644 index 0000000000..a39db43c16 --- /dev/null +++ b/plugins/gcp-projects/src/components/GcpProjectsPage/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 { GcpProjectsPage } from './GcpProjectsPage'; diff --git a/plugins/gcp-projects/src/index.ts b/plugins/gcp-projects/src/index.ts index d67bc6a864..7c3f752b30 100644 --- a/plugins/gcp-projects/src/index.ts +++ b/plugins/gcp-projects/src/index.ts @@ -14,5 +14,5 @@ * limitations under the License. */ -export { plugin } from './plugin'; +export { plugin, GcpProjectsPage } from './plugin'; export * from './api'; diff --git a/plugins/gcp-projects/src/plugin.ts b/plugins/gcp-projects/src/plugin.ts index 2baf3bce7d..b263365816 100644 --- a/plugins/gcp-projects/src/plugin.ts +++ b/plugins/gcp-projects/src/plugin.ts @@ -17,29 +17,20 @@ import { createApiFactory, createPlugin, - createRouteRef, + createRoutableExtension, googleAuthApiRef, } from '@backstage/core'; import { gcpApiRef, GcpClient } from './api'; import { NewProjectPage } from './components/NewProjectPage'; import { ProjectDetailsPage } from './components/ProjectDetailsPage'; import { ProjectListPage } from './components/ProjectListPage'; - -export const rootRouteRef = createRouteRef({ - path: '/gcp-projects', - title: 'GCP Projects', -}); -export const projectRouteRef = createRouteRef({ - path: '/gcp-projects/project', - title: 'GCP Project Page', -}); -export const newProjectRouteRef = createRouteRef({ - path: '/gcp-projects/new', - title: 'GCP Project Page', -}); +import { rootRouteRef, projectRouteRef, newProjectRouteRef } from './routes'; export const plugin = createPlugin({ id: 'gcp-projects', + routes: { + root: rootRouteRef, + }, apis: [ createApiFactory({ api: gcpApiRef, @@ -55,3 +46,11 @@ export const plugin = createPlugin({ router.addRoute(newProjectRouteRef, NewProjectPage); }, }); + +export const GcpProjectsPage = plugin.provide( + createRoutableExtension({ + component: () => + import('./components/GcpProjectsPage').then(m => m.GcpProjectsPage), + mountPoint: rootRouteRef, + }), +); diff --git a/plugins/gcp-projects/src/routes.ts b/plugins/gcp-projects/src/routes.ts new file mode 100644 index 0000000000..ff9bf9d85c --- /dev/null +++ b/plugins/gcp-projects/src/routes.ts @@ -0,0 +1,30 @@ +/* + * Copyright 2021 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 rootRouteRef = createRouteRef({ + path: '/gcp-projects', + title: 'GCP Projects', +}); +export const projectRouteRef = createRouteRef({ + path: '/gcp-projects/project', + title: 'GCP Project Page', +}); +export const newProjectRouteRef = createRouteRef({ + path: '/gcp-projects/new', + title: 'GCP Project Page', +}); diff --git a/yarn.lock b/yarn.lock index 2ac7dffa6a..96dcb8bd09 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15055,18 +15055,6 @@ highlight.js@^10.4.1, highlight.js@~10.4.0: resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-10.4.1.tgz#d48fbcf4a9971c4361b3f95f302747afe19dbad0" integrity sha512-yR5lWvNz7c85OhVAEAeFhVCc/GV4C30Fjzc/rCP0aCWzc1UUOPUk55dK/qdwTZHBvMZo+eZ2jpk62ndX/xMFlg== -history@^4.9.0: - version "4.10.1" - resolved "https://registry.npmjs.org/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" - integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== - dependencies: - "@babel/runtime" "^7.1.2" - loose-envify "^1.2.0" - resolve-pathname "^3.0.0" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" - value-equal "^1.0.1" - history@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/history/-/history-5.0.0.tgz#0cabbb6c4bbf835addb874f8259f6d25101efd08" @@ -15083,7 +15071,7 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: +hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -16289,11 +16277,6 @@ is-yarn-global@^0.3.0: resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -18081,7 +18064,7 @@ longest-streak@^2.0.0: resolved "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4" integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg== -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.0, loose-envify@^1.3.1, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -18734,14 +18717,6 @@ min-indent@^1.0.0: resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.0.tgz#cfc45c37e9ec0d8f0a0ec3dd4ef7f7c3abe39256" integrity sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY= -mini-create-react-context@^0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.0.tgz#df60501c83151db69e28eac0ef08b4002efab040" - integrity sha512-b0TytUgFSbgFJGzJqXPKCFCBWigAjpjo+Fl7Vf7ZbKRDptszpppKxXH6DRXEABZ/gcEQczeb0iZ7JvL8e8jjCA== - dependencies: - "@babel/runtime" "^7.5.5" - tiny-warning "^1.0.3" - mini-css-extract-plugin@^0.9.0: version "0.9.0" resolved "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz#47f2cf07aa165ab35733b1fc97d4c46c0564339e" @@ -20503,13 +20478,6 @@ path-to-regexp@0.1.7: resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= -path-to-regexp@^1.7.0: - version "1.8.0" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" - integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== - dependencies: - isarray "0.0.1" - path-type@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -21990,7 +21958,7 @@ react-inspector@^5.0.1: is-dom "^1.1.0" prop-types "^15.6.1" -react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: +react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -22107,7 +22075,7 @@ react-resize-detector@^2.3.0: prop-types "^15.6.0" resize-observer-polyfill "^1.5.0" -react-router-dom@6.0.0-beta.0: +react-router-dom@6.0.0-beta.0, react-router-dom@^6.0.0-beta.0: version "6.0.0-beta.0" resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.0.0-beta.0.tgz#9dcc8555365f22f7fbd09f26b6b82543f3eb97d6" integrity sha512-36yNNGMT8RB9FRPL9nKJi6HKDkgOakU+o/2hHpSzR6e37gN70MpOU6QQlmif4oAWWBwjyGc3ZNOMFCsFuHUY5w== @@ -22115,35 +22083,6 @@ react-router-dom@6.0.0-beta.0: prop-types "^15.7.2" react-router "6.0.0-beta.0" -react-router-dom@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662" - integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA== - dependencies: - "@babel/runtime" "^7.1.2" - history "^4.9.0" - loose-envify "^1.3.1" - prop-types "^15.6.2" - react-router "5.2.0" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.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== - dependencies: - "@babel/runtime" "^7.1.2" - history "^4.9.0" - hoist-non-react-statics "^3.1.0" - loose-envify "^1.3.1" - mini-create-react-context "^0.4.0" - path-to-regexp "^1.7.0" - prop-types "^15.6.2" - react-is "^16.6.0" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" - react-router@6.0.0-beta.0, react-router@^6.0.0-beta.0: version "6.0.0-beta.0" resolved "https://registry.npmjs.org/react-router/-/react-router-6.0.0-beta.0.tgz#3e11f39b6ded4412c2fed9e4f989dd4c8156724d" @@ -22994,11 +22933,6 @@ resolve-from@^4.0.0: resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve-pathname@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" - integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== - resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -25077,7 +25011,7 @@ tiny-emitter@^2.0.0: resolved "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== -tiny-invariant@^1.0.2, tiny-invariant@^1.0.6: +tiny-invariant@^1.0.6: version "1.1.0" resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== @@ -25087,7 +25021,7 @@ tiny-merge-patch@^0.1.2: resolved "https://registry.npmjs.org/tiny-merge-patch/-/tiny-merge-patch-0.1.2.tgz#2e8ded19c56ea15dbd3ad4ed5db1c8e5ad544c3c" integrity sha1-Lo3tGcVuoV29OtTtXbHI5a1UTDw= -tiny-warning@^1.0.0, tiny-warning@^1.0.2, tiny-warning@^1.0.3: +tiny-warning@^1.0.2: version "1.0.3" resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== @@ -26057,11 +25991,6 @@ validate.io-number@^1.0.3: resolved "https://registry.npmjs.org/validate.io-number/-/validate.io-number-1.0.3.tgz#f63ffeda248bf28a67a8d48e0e3b461a1665baf8" integrity sha1-9j/+2iSL8opnqNSODjtGGhZluvg= -value-equal@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" - integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== - vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" From fb265bb4fabca8365a6c2dcf52b256130f1a95e3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 19 Jan 2021 23:33:52 +0100 Subject: [PATCH 10/31] api-docs: port top-level component to composability API --- plugins/api-docs/src/index.ts | 2 +- plugins/api-docs/src/plugin.ts | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/plugins/api-docs/src/index.ts b/plugins/api-docs/src/index.ts index f09aeb1038..e85c693864 100644 --- a/plugins/api-docs/src/index.ts +++ b/plugins/api-docs/src/index.ts @@ -15,4 +15,4 @@ */ export * from './components'; -export { plugin } from './plugin'; +export { plugin, ApiExplorerPage } from './plugin'; diff --git a/plugins/api-docs/src/plugin.ts b/plugins/api-docs/src/plugin.ts index 06db03f2e1..8faf5412fc 100644 --- a/plugins/api-docs/src/plugin.ts +++ b/plugins/api-docs/src/plugin.ts @@ -15,14 +15,21 @@ */ import { ApiEntity } from '@backstage/catalog-model'; -import { createApiFactory, createPlugin } from '@backstage/core'; -import { ApiExplorerPage } from './components/ApiExplorerPage/ApiExplorerPage'; +import { + createApiFactory, + createPlugin, + createRoutableExtension, +} from '@backstage/core'; +import { ApiExplorerPage as Page } from './components/ApiExplorerPage/ApiExplorerPage'; import { defaultDefinitionWidgets } from './components/ApiDefinitionCard'; import { rootRoute } from './routes'; import { apiDocsConfigRef } from './config'; export const plugin = createPlugin({ id: 'api-docs', + routes: { + root: rootRoute, + }, apis: [ createApiFactory({ api: apiDocsConfigRef, @@ -38,6 +45,14 @@ export const plugin = createPlugin({ }), ], register({ router }) { - router.addRoute(rootRoute, ApiExplorerPage); + router.addRoute(rootRoute, Page); }, }); + +export const ApiExplorerPage = plugin.provide( + createRoutableExtension({ + component: () => + import('./components/ApiExplorerPage').then(m => m.ApiExplorerPage), + mountPoint: rootRoute, + }), +); From d955622e81b05df47791b145f431014eaa42232e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 20 Jan 2021 00:46:52 +0100 Subject: [PATCH 11/31] api-docs: add basic dev setup with explore page --- plugins/api-docs/dev/example-api.yaml | 124 ++++++++++++++++++++++++++ plugins/api-docs/dev/index.tsx | 20 ++++- 2 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 plugins/api-docs/dev/example-api.yaml diff --git a/plugins/api-docs/dev/example-api.yaml b/plugins/api-docs/dev/example-api.yaml new file mode 100644 index 0000000000..a314b4e255 --- /dev/null +++ b/plugins/api-docs/dev/example-api.yaml @@ -0,0 +1,124 @@ +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + name: petstore + description: The petstore API + tags: + - store + - rest +spec: + type: openapi + lifecycle: experimental + owner: team-c + definition: | + openapi: "3.0.0" + info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT + servers: + - url: http://petstore.swagger.io/v1 + paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/plugins/api-docs/dev/index.tsx b/plugins/api-docs/dev/index.tsx index 812a5585d4..d7bc61073e 100644 --- a/plugins/api-docs/dev/index.tsx +++ b/plugins/api-docs/dev/index.tsx @@ -14,7 +14,23 @@ * limitations under the License. */ +import React from 'react'; import { createDevApp } from '@backstage/dev-utils'; -import { plugin } from '../src/plugin'; +import { ApiExplorerPage, plugin } from '../src/plugin'; +import { catalogApiRef } from '@backstage/plugin-catalog'; +import petstoreApiEntity from './example-api.yaml'; -createDevApp().registerPlugin(plugin).render(); +createDevApp() + .registerApi({ + api: catalogApiRef, + deps: {}, + factory: () => + (({ + async getEntities() { + return { items: [petstoreApiEntity] }; + }, + } as unknown) as typeof catalogApiRef.T), + }) + .registerPlugin(plugin) + .addPage({ element: }) + .render(); From 67484136929cd53ac5038fd8fdec56ec3f669f6b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 1 Feb 2021 18:17:06 +0100 Subject: [PATCH 12/31] api-docs: update cards to grab entity from context + composability exports --- plugins/api-docs/dev/index.tsx | 6 +-- .../ApiDefinitionCard.test.tsx | 11 ++-- .../ApiDefinitionCard/ApiDefinitionCard.tsx | 25 +++++---- .../ApisCards/ConsumedApisCard.test.tsx | 14 +++-- .../components/ApisCards/ConsumedApisCard.tsx | 7 ++- .../ApisCards/ProvidedApisCard.test.tsx | 14 +++-- .../components/ApisCards/ProvidedApisCard.tsx | 7 ++- .../ConsumingComponentsCard.test.tsx | 14 +++-- .../ConsumingComponentsCard.tsx | 10 ++-- .../ProvidingComponentsCard.test.tsx | 14 +++-- .../ProvidingComponentsCard.tsx | 10 ++-- plugins/api-docs/src/index.ts | 6 ++- plugins/api-docs/src/plugin.test.ts | 4 +- plugins/api-docs/src/plugin.ts | 54 ++++++++++++++++++- 14 files changed, 148 insertions(+), 48 deletions(-) diff --git a/plugins/api-docs/dev/index.tsx b/plugins/api-docs/dev/index.tsx index d7bc61073e..4bf0215f32 100644 --- a/plugins/api-docs/dev/index.tsx +++ b/plugins/api-docs/dev/index.tsx @@ -16,8 +16,8 @@ import React from 'react'; import { createDevApp } from '@backstage/dev-utils'; -import { ApiExplorerPage, plugin } from '../src/plugin'; -import { catalogApiRef } from '@backstage/plugin-catalog'; +import { ApiExplorerPage, apiDocsPlugin } from '../src/plugin'; +import { catalogApiRef } from '@backstage/plugin-catalog-react'; import petstoreApiEntity from './example-api.yaml'; createDevApp() @@ -31,6 +31,6 @@ createDevApp() }, } as unknown) as typeof catalogApiRef.T), }) - .registerPlugin(plugin) + .registerPlugin(apiDocsPlugin) .addPage({ element: }) .render(); diff --git a/plugins/api-docs/src/components/ApiDefinitionCard/ApiDefinitionCard.test.tsx b/plugins/api-docs/src/components/ApiDefinitionCard/ApiDefinitionCard.test.tsx index d59699ab9a..0d2648ab5f 100644 --- a/plugins/api-docs/src/components/ApiDefinitionCard/ApiDefinitionCard.test.tsx +++ b/plugins/api-docs/src/components/ApiDefinitionCard/ApiDefinitionCard.test.tsx @@ -16,6 +16,7 @@ import { ApiEntity } from '@backstage/catalog-model'; import { ApiProvider, ApiRegistry } from '@backstage/core'; +import { EntityProvider } from '@backstage/plugin-catalog-react'; import { renderInTestApp } from '@backstage/test-utils'; import { waitFor } from '@testing-library/react'; import React from 'react'; @@ -54,7 +55,7 @@ paths: get: summary: List all artists responses: - "200": + "200": description: Success `; const apiEntity: ApiEntity = { @@ -81,7 +82,9 @@ paths: const { getByText } = await renderInTestApp( - + + + , ); @@ -110,7 +113,9 @@ paths: const { getByText } = await renderInTestApp( - + + + , ); diff --git a/plugins/api-docs/src/components/ApiDefinitionCard/ApiDefinitionCard.tsx b/plugins/api-docs/src/components/ApiDefinitionCard/ApiDefinitionCard.tsx index 19370bfc18..0a4386e275 100644 --- a/plugins/api-docs/src/components/ApiDefinitionCard/ApiDefinitionCard.tsx +++ b/plugins/api-docs/src/components/ApiDefinitionCard/ApiDefinitionCard.tsx @@ -15,6 +15,7 @@ */ import { ApiEntity } from '@backstage/catalog-model'; +import { useEntity } from '@backstage/plugin-catalog-react'; import { CardTab, TabbedCard, useApi } from '@backstage/core'; import { Alert } from '@material-ui/lab'; import React from 'react'; @@ -22,29 +23,31 @@ import { apiDocsConfigRef } from '../../config'; import { PlainApiDefinitionWidget } from '../PlainApiDefinitionWidget'; type Props = { + /** @deprecated The entity is now grabbed from context instead */ apiEntity?: ApiEntity; }; -export const ApiDefinitionCard = ({ apiEntity }: Props) => { +export const ApiDefinitionCard = (_: Props) => { + const entity = useEntity().entity as ApiEntity; const config = useApi(apiDocsConfigRef); const { getApiDefinitionWidget } = config; - if (!apiEntity) { + if (!entity) { return Could not fetch the API; } - const definitionWidget = getApiDefinitionWidget(apiEntity); + const definitionWidget = getApiDefinitionWidget(entity); if (definitionWidget) { return ( - + - {definitionWidget.component(apiEntity.spec.definition)} + {definitionWidget.component(entity.spec.definition)} @@ -53,13 +56,13 @@ export const ApiDefinitionCard = ({ apiEntity }: Props) => { return ( + , ]} diff --git a/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.test.tsx b/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.test.tsx index d406e160fc..7e9973c25f 100644 --- a/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.test.tsx +++ b/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.test.tsx @@ -16,7 +16,11 @@ import { Entity, RELATION_CONSUMES_API } from '@backstage/catalog-model'; import { ApiProvider, ApiRegistry } from '@backstage/core'; -import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react'; +import { + CatalogApi, + catalogApiRef, + EntityProvider, +} from '@backstage/plugin-catalog-react'; import { renderInTestApp } from '@backstage/test-utils'; import { waitFor } from '@testing-library/react'; import React from 'react'; @@ -63,7 +67,9 @@ describe('', () => { const { getByText } = await renderInTestApp( - + + + , ); @@ -112,7 +118,9 @@ describe('', () => { const { getByText } = await renderInTestApp( - + + + , ); diff --git a/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.tsx b/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.tsx index 0bd4919554..8a808a6488 100644 --- a/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.tsx +++ b/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.tsx @@ -19,6 +19,7 @@ import { Entity, RELATION_CONSUMES_API, } from '@backstage/catalog-model'; +import { useEntity } from '@backstage/plugin-catalog-react'; import { EmptyState, InfoCard, Progress } from '@backstage/core'; import React, { PropsWithChildren } from 'react'; import { ApisTable } from './ApisTable'; @@ -37,11 +38,13 @@ const ApisCard = ({ }; type Props = { - entity: Entity; + /** @deprecated The entity is now grabbed from context instead */ + entity?: Entity; variant?: string; }; -export const ConsumedApisCard = ({ entity, variant = 'gridItem' }: Props) => { +export const ConsumedApisCard = ({ variant = 'gridItem' }: Props) => { + const { entity } = useEntity(); const { entities, loading, error } = useRelatedEntities( entity, RELATION_CONSUMES_API, diff --git a/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.test.tsx b/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.test.tsx index 7b6ce6e18a..2fc5763461 100644 --- a/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.test.tsx +++ b/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.test.tsx @@ -16,7 +16,11 @@ import { Entity, RELATION_PROVIDES_API } from '@backstage/catalog-model'; import { ApiProvider, ApiRegistry } from '@backstage/core'; -import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react'; +import { + CatalogApi, + catalogApiRef, + EntityProvider, +} from '@backstage/plugin-catalog-react'; import { renderInTestApp } from '@backstage/test-utils'; import { waitFor } from '@testing-library/react'; import React from 'react'; @@ -63,7 +67,9 @@ describe('', () => { const { getByText } = await renderInTestApp( - + + + , ); @@ -112,7 +118,9 @@ describe('', () => { const { getByText } = await renderInTestApp( - + + + , ); diff --git a/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.tsx b/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.tsx index 618f2dc1f6..ed6e893a03 100644 --- a/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.tsx +++ b/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.tsx @@ -19,6 +19,7 @@ import { Entity, RELATION_PROVIDES_API, } from '@backstage/catalog-model'; +import { useEntity } from '@backstage/plugin-catalog-react'; import { EmptyState, InfoCard, Progress } from '@backstage/core'; import React, { PropsWithChildren } from 'react'; import { ApisTable } from './ApisTable'; @@ -37,11 +38,13 @@ const ApisCard = ({ }; type Props = { - entity: Entity; + /** @deprecated The entity is now grabbed from context instead */ + entity?: Entity; variant?: string; }; -export const ProvidedApisCard = ({ entity, variant = 'gridItem' }: Props) => { +export const ProvidedApisCard = ({ variant = 'gridItem' }: Props) => { + const { entity } = useEntity(); const { entities, loading, error } = useRelatedEntities( entity, RELATION_PROVIDES_API, diff --git a/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.test.tsx b/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.test.tsx index 99a8c0dc28..78080e8318 100644 --- a/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.test.tsx +++ b/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.test.tsx @@ -16,7 +16,11 @@ import { Entity, RELATION_API_CONSUMED_BY } from '@backstage/catalog-model'; import { ApiProvider, ApiRegistry } from '@backstage/core'; -import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react'; +import { + CatalogApi, + catalogApiRef, + EntityProvider, +} from '@backstage/plugin-catalog-react'; import { renderInTestApp } from '@backstage/test-utils'; import { waitFor } from '@testing-library/react'; import React from 'react'; @@ -62,7 +66,9 @@ describe('', () => { const { getByText } = await renderInTestApp( - + + + , ); @@ -111,7 +117,9 @@ describe('', () => { const { getByText } = await renderInTestApp( - + + + , ); diff --git a/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.tsx b/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.tsx index 0431367aa2..56d2fb977d 100644 --- a/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.tsx +++ b/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.tsx @@ -19,6 +19,7 @@ import { Entity, RELATION_API_CONSUMED_BY, } from '@backstage/catalog-model'; +import { useEntity } from '@backstage/plugin-catalog-react'; import { EmptyState, InfoCard, Progress } from '@backstage/core'; import React, { PropsWithChildren } from 'react'; import { MissingConsumesApisEmptyState } from '../EmptyState'; @@ -37,14 +38,13 @@ const ComponentsCard = ({ }; type Props = { - entity: Entity; + /** @deprecated The entity is now grabbed from context instead */ + entity?: Entity; variant?: string; }; -export const ConsumingComponentsCard = ({ - entity, - variant = 'gridItem', -}: Props) => { +export const ConsumingComponentsCard = ({ variant = 'gridItem' }: Props) => { + const { entity } = useEntity(); const { entities, loading, error } = useRelatedEntities( entity, RELATION_API_CONSUMED_BY, diff --git a/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.test.tsx b/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.test.tsx index a3341ad8d0..5edd2efa14 100644 --- a/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.test.tsx +++ b/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.test.tsx @@ -16,7 +16,11 @@ import { Entity, RELATION_API_PROVIDED_BY } from '@backstage/catalog-model'; import { ApiProvider, ApiRegistry } from '@backstage/core'; -import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react'; +import { + CatalogApi, + catalogApiRef, + EntityProvider, +} from '@backstage/plugin-catalog-react'; import { renderInTestApp } from '@backstage/test-utils'; import { waitFor } from '@testing-library/react'; import React from 'react'; @@ -62,7 +66,9 @@ describe('', () => { const { getByText } = await renderInTestApp( - + + + , ); @@ -111,7 +117,9 @@ describe('', () => { const { getByText } = await renderInTestApp( - + + + , ); diff --git a/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.tsx b/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.tsx index 9e405a3af3..959ff3feed 100644 --- a/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.tsx +++ b/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.tsx @@ -19,6 +19,7 @@ import { Entity, RELATION_API_PROVIDED_BY, } from '@backstage/catalog-model'; +import { useEntity } from '@backstage/plugin-catalog-react'; import { EmptyState, InfoCard, Progress } from '@backstage/core'; import React, { PropsWithChildren } from 'react'; import { MissingProvidesApisEmptyState } from '../EmptyState'; @@ -37,14 +38,13 @@ const ComponentsCard = ({ }; type Props = { - entity: Entity; + /** @deprecated The entity is now grabbed from context instead */ + entity?: Entity; variant?: string; }; -export const ProvidingComponentsCard = ({ - entity, - variant = 'gridItem', -}: Props) => { +export const ProvidingComponentsCard = ({ variant = 'gridItem' }: Props) => { + const { entity } = useEntity(); const { entities, loading, error } = useRelatedEntities( entity, RELATION_API_PROVIDED_BY, diff --git a/plugins/api-docs/src/index.ts b/plugins/api-docs/src/index.ts index e85c693864..dd231d6cb4 100644 --- a/plugins/api-docs/src/index.ts +++ b/plugins/api-docs/src/index.ts @@ -15,4 +15,8 @@ */ export * from './components'; -export { plugin, ApiExplorerPage } from './plugin'; +export { + apiDocsPlugin, + apiDocsPlugin as plugin, + ApiExplorerPage, +} from './plugin'; diff --git a/plugins/api-docs/src/plugin.test.ts b/plugins/api-docs/src/plugin.test.ts index 054e804382..eaafd36b3d 100644 --- a/plugins/api-docs/src/plugin.test.ts +++ b/plugins/api-docs/src/plugin.test.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import { plugin } from './plugin'; +import { apiDocsPlugin } from './plugin'; describe('api-docs', () => { it('should export plugin', () => { - expect(plugin).toBeDefined(); + expect(apiDocsPlugin).toBeDefined(); }); }); diff --git a/plugins/api-docs/src/plugin.ts b/plugins/api-docs/src/plugin.ts index 8faf5412fc..429fd60095 100644 --- a/plugins/api-docs/src/plugin.ts +++ b/plugins/api-docs/src/plugin.ts @@ -19,13 +19,14 @@ import { createApiFactory, createPlugin, createRoutableExtension, + createComponentExtension, } from '@backstage/core'; import { ApiExplorerPage as Page } from './components/ApiExplorerPage/ApiExplorerPage'; import { defaultDefinitionWidgets } from './components/ApiDefinitionCard'; import { rootRoute } from './routes'; import { apiDocsConfigRef } from './config'; -export const plugin = createPlugin({ +export const apiDocsPlugin = createPlugin({ id: 'api-docs', routes: { root: rootRoute, @@ -49,10 +50,59 @@ export const plugin = createPlugin({ }, }); -export const ApiExplorerPage = plugin.provide( +export const ApiExplorerPage = apiDocsPlugin.provide( createRoutableExtension({ component: () => import('./components/ApiExplorerPage').then(m => m.ApiExplorerPage), mountPoint: rootRoute, }), ); + +export const EntityApiDefinitionCard = apiDocsPlugin.provide( + createComponentExtension({ + component: { + lazy: () => + import('./components/ApiDefinitionCard').then(m => m.ApiDefinitionCard), + }, + }), +); + +export const EntityConsumedApisCard = apiDocsPlugin.provide( + createComponentExtension({ + component: { + lazy: () => + import('./components/ApisCards').then(m => m.ConsumedApisCard), + }, + }), +); + +export const EntityConsumingComponentsCard = apiDocsPlugin.provide( + createComponentExtension({ + component: { + lazy: () => + import('./components/ComponentsCards').then( + m => m.ConsumingComponentsCard, + ), + }, + }), +); + +export const EntityProvidedApisCard = apiDocsPlugin.provide( + createComponentExtension({ + component: { + lazy: () => + import('./components/ApisCards').then(m => m.ProvidedApisCard), + }, + }), +); + +export const EntityProvidingComponentsCard = apiDocsPlugin.provide( + createComponentExtension({ + component: { + lazy: () => + import('./components/ComponentsCards').then( + m => m.ProvidingComponentsCard, + ), + }, + }), +); From ae7519f8218a4d41d23f1807e09218f7d5ddeb13 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 1 Feb 2021 18:38:38 +0100 Subject: [PATCH 13/31] gcp-projects: update plugin name --- plugins/gcp-projects/src/index.ts | 6 +++++- plugins/gcp-projects/src/plugin.ts | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/gcp-projects/src/index.ts b/plugins/gcp-projects/src/index.ts index 7c3f752b30..af3f0bc4d9 100644 --- a/plugins/gcp-projects/src/index.ts +++ b/plugins/gcp-projects/src/index.ts @@ -14,5 +14,9 @@ * limitations under the License. */ -export { plugin, GcpProjectsPage } from './plugin'; +export { + gcpProjectsPlugin, + gcpProjectsPlugin as plugin, + GcpProjectsPage, +} from './plugin'; export * from './api'; diff --git a/plugins/gcp-projects/src/plugin.ts b/plugins/gcp-projects/src/plugin.ts index b263365816..9126716d55 100644 --- a/plugins/gcp-projects/src/plugin.ts +++ b/plugins/gcp-projects/src/plugin.ts @@ -26,7 +26,7 @@ import { ProjectDetailsPage } from './components/ProjectDetailsPage'; import { ProjectListPage } from './components/ProjectListPage'; import { rootRouteRef, projectRouteRef, newProjectRouteRef } from './routes'; -export const plugin = createPlugin({ +export const gcpProjectsPlugin = createPlugin({ id: 'gcp-projects', routes: { root: rootRouteRef, @@ -47,7 +47,7 @@ export const plugin = createPlugin({ }, }); -export const GcpProjectsPage = plugin.provide( +export const GcpProjectsPage = gcpProjectsPlugin.provide( createRoutableExtension({ component: () => import('./components/GcpProjectsPage').then(m => m.GcpProjectsPage), From c5636e5afb299215fdcaac3a98ece25f3db00944 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 1 Feb 2021 18:45:53 +0100 Subject: [PATCH 14/31] user-settings: port to new composability API --- plugins/user-settings/dev/index.tsx | 14 +++++++++++--- plugins/user-settings/src/index.ts | 7 ++++++- plugins/user-settings/src/plugin.ts | 21 ++++++++++++++++++--- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/plugins/user-settings/dev/index.tsx b/plugins/user-settings/dev/index.tsx index 264d6f801f..3eaed00022 100644 --- a/plugins/user-settings/dev/index.tsx +++ b/plugins/user-settings/dev/index.tsx @@ -13,7 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { createDevApp } from '@backstage/dev-utils'; -import { plugin } from '../src/plugin'; -createDevApp().registerPlugin(plugin).render(); +import React from 'react'; +import { createDevApp } from '@backstage/dev-utils'; +import { userSettingsPlugin, UserSettingsPage } from '../src/plugin'; + +createDevApp() + .registerPlugin(userSettingsPlugin) + .addPage({ + title: 'Settings', + element: , + }) + .render(); diff --git a/plugins/user-settings/src/index.ts b/plugins/user-settings/src/index.ts index 9b0ce4262c..b26c8b4b0b 100644 --- a/plugins/user-settings/src/index.ts +++ b/plugins/user-settings/src/index.ts @@ -13,5 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { plugin } from './plugin'; + +export { + userSettingsPlugin, + userSettingsPlugin as plugin, + UserSettingsPage, +} from './plugin'; export * from './components/'; diff --git a/plugins/user-settings/src/plugin.ts b/plugins/user-settings/src/plugin.ts index 2f896c8197..5353c12f10 100644 --- a/plugins/user-settings/src/plugin.ts +++ b/plugins/user-settings/src/plugin.ts @@ -13,13 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { createPlugin, createRouteRef } from '@backstage/core'; + +import { + createPlugin, + createRoutableExtension, + createRouteRef, +} from '@backstage/core'; export const settingsRouteRef = createRouteRef({ - path: '/settings', title: 'Settings', }); -export const plugin = createPlugin({ +export const userSettingsPlugin = createPlugin({ id: 'user-settings', + routes: { + settingsPage: settingsRouteRef, + }, }); + +export const UserSettingsPage = userSettingsPlugin.provide( + createRoutableExtension({ + component: () => + import('./components/SettingsPage').then(m => m.SettingsPage), + mountPoint: settingsRouteRef, + }), +); From 5efc24a57c2a806183ccc04489c7407606e96c97 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 1 Feb 2021 18:50:17 +0100 Subject: [PATCH 15/31] welcome: port to new composability API --- plugins/welcome/src/index.ts | 2 +- plugins/welcome/src/plugin.test.ts | 4 ++-- plugins/welcome/src/plugin.ts | 20 +++++++++++++++----- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/plugins/welcome/src/index.ts b/plugins/welcome/src/index.ts index 3a0a0fe2d3..cccc6bacf3 100644 --- a/plugins/welcome/src/index.ts +++ b/plugins/welcome/src/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export { plugin } from './plugin'; +export { welcomePlugin, welcomePlugin as plugin, WelcomePage } from './plugin'; diff --git a/plugins/welcome/src/plugin.test.ts b/plugins/welcome/src/plugin.test.ts index d60c73ec68..381ea80f38 100644 --- a/plugins/welcome/src/plugin.test.ts +++ b/plugins/welcome/src/plugin.test.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import { plugin } from './plugin'; +import { welcomePlugin } from './plugin'; describe('welcome', () => { it('should export plugin', () => { - expect(plugin).toBeDefined(); + expect(welcomePlugin).toBeDefined(); }); }); diff --git a/plugins/welcome/src/plugin.ts b/plugins/welcome/src/plugin.ts index 754f708110..f950b93874 100644 --- a/plugins/welcome/src/plugin.ts +++ b/plugins/welcome/src/plugin.ts @@ -14,18 +14,28 @@ * limitations under the License. */ -import { createPlugin, createRouteRef } from '@backstage/core'; -import WelcomePage from './components/WelcomePage'; +import { + createPlugin, + createRoutableExtension, + createRouteRef, +} from '@backstage/core'; +import WelcomePageComponent from './components/WelcomePage'; export const rootRouteRef = createRouteRef({ - path: '/welcome', title: 'Welcome', }); -export const plugin = createPlugin({ +export const welcomePlugin = createPlugin({ id: 'welcome', register({ router, featureFlags }) { - router.addRoute(rootRouteRef, WelcomePage); + router.addRoute(rootRouteRef, WelcomePageComponent); featureFlags.register('enable-welcome-box'); }, }); + +export const WelcomePage = welcomePlugin.provide( + createRoutableExtension({ + component: () => import('./components/WelcomePage').then(m => m.default), + mountPoint: rootRouteRef, + }), +); From 35efaf5c0565a2e4453c4c679931b00e8b3a6126 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 1 Feb 2021 19:02:45 +0100 Subject: [PATCH 16/31] sonarqube: port to new composability API --- plugins/sonarqube/dev/index.tsx | 104 ++++++++---------- plugins/sonarqube/package.json | 1 + .../SonarQubeCard/SonarQubeCard.tsx | 9 +- plugins/sonarqube/src/index.ts | 6 +- plugins/sonarqube/src/plugin.test.ts | 4 +- plugins/sonarqube/src/plugin.ts | 12 +- 6 files changed, 72 insertions(+), 64 deletions(-) diff --git a/plugins/sonarqube/dev/index.tsx b/plugins/sonarqube/dev/index.tsx index 5e8aedd577..b99e552df9 100644 --- a/plugins/sonarqube/dev/index.tsx +++ b/plugins/sonarqube/dev/index.tsx @@ -22,14 +22,60 @@ import { Header, Page, } from '@backstage/core'; -import { createDevApp } from '@backstage/dev-utils'; +import { createDevApp, EntityGridItem } from '@backstage/dev-utils'; import { Grid } from '@material-ui/core'; import React from 'react'; -import { SonarQubeCard } from '../src'; +import { EntitySonarQubeCard, sonarQubePlugin } from '../src'; import { FindingSummary, SonarQubeApi, sonarQubeApiRef } from '../src/api'; import { SONARQUBE_PROJECT_KEY_ANNOTATION } from '../src/components/useProjectKey'; +const entity = (name?: string) => + ({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + annotations: { + [SONARQUBE_PROJECT_KEY_ANNOTATION]: name, + }, + name: name, + }, + } as Entity); + createDevApp() + .registerPlugin(sonarQubePlugin) + .addPage({ + title: 'Cards', + element: ( + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + ), + }) .registerApi({ api: sonarQubeApiRef, deps: {}, @@ -114,58 +160,4 @@ createDevApp() }, } as SonarQubeApi), }) - .registerPlugin( - createPlugin({ - id: 'defectdojo-demo', - register({ router }) { - const entity = (name?: string) => - ({ - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { - annotations: { - [SONARQUBE_PROJECT_KEY_ANNOTATION]: name, - }, - name: name, - }, - } as Entity); - - const ExamplePage = () => ( - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - ); - - router.addRoute( - createRouteRef({ path: '/', title: 'SonarQube' }), - ExamplePage, - ); - }, - }), - ) .render(); diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index b6701f9849..c286dd9dac 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -33,6 +33,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.0", + "@backstage/plugin-catalog-react": "^0.0.1", "@backstage/core": "^0.5.0", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", diff --git a/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx b/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx index c5e295a22b..5577048fa9 100644 --- a/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx +++ b/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx @@ -22,6 +22,7 @@ import { Progress, useApi, } from '@backstage/core'; +import { useEntity } from '@backstage/plugin-catalog-react'; import { Chip, Grid } from '@material-ui/core'; import { makeStyles } from '@material-ui/core/styles'; import BugReport from '@material-ui/icons/BugReport'; @@ -69,10 +70,10 @@ const useStyles = makeStyles(theme => ({ }, })); -interface DuplicationRating { +type DuplicationRating = { greaterThan: number; rating: '1.0' | '2.0' | '3.0' | '4.0' | '5.0'; -} +}; const defaultDuplicationRatings: DuplicationRating[] = [ { greaterThan: 0, rating: '1.0' }, @@ -83,14 +84,14 @@ const defaultDuplicationRatings: DuplicationRating[] = [ ]; export const SonarQubeCard = ({ - entity, variant = 'gridItem', duplicationRatings = defaultDuplicationRatings, }: { - entity: Entity; + entity?: Entity; variant?: string; duplicationRatings?: DuplicationRating[]; }) => { + const { entity } = useEntity(); const sonarQubeApi = useApi(sonarQubeApiRef); const projectTitle = useProjectKey(entity); diff --git a/plugins/sonarqube/src/index.ts b/plugins/sonarqube/src/index.ts index f09aeb1038..8fae151929 100644 --- a/plugins/sonarqube/src/index.ts +++ b/plugins/sonarqube/src/index.ts @@ -15,4 +15,8 @@ */ export * from './components'; -export { plugin } from './plugin'; +export { + sonarQubePlugin, + sonarQubePlugin as plugin, + EntitySonarQubeCard, +} from './plugin'; diff --git a/plugins/sonarqube/src/plugin.test.ts b/plugins/sonarqube/src/plugin.test.ts index 32730b64c3..246f8b297e 100644 --- a/plugins/sonarqube/src/plugin.test.ts +++ b/plugins/sonarqube/src/plugin.test.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import { plugin } from './plugin'; +import { sonarQubePlugin } from './plugin'; describe('sonarqube', () => { it('should export plugin', () => { - expect(plugin).toBeDefined(); + expect(sonarQubePlugin).toBeDefined(); }); }); diff --git a/plugins/sonarqube/src/plugin.ts b/plugins/sonarqube/src/plugin.ts index f8b8cafc5c..3f58b24abf 100644 --- a/plugins/sonarqube/src/plugin.ts +++ b/plugins/sonarqube/src/plugin.ts @@ -17,12 +17,13 @@ import { configApiRef, createApiFactory, + createComponentExtension, createPlugin, discoveryApiRef, } from '@backstage/core'; import { sonarQubeApiRef, SonarQubeClient } from './api'; -export const plugin = createPlugin({ +export const sonarQubePlugin = createPlugin({ id: 'sonarqube', apis: [ createApiFactory({ @@ -36,3 +37,12 @@ export const plugin = createPlugin({ }), ], }); + +export const EntitySonarQubeCard = sonarQubePlugin.provide( + createComponentExtension({ + component: { + lazy: () => + import('./components/SonarQubeCard').then(m => m.SonarQubeCard), + }, + }), +); From 3d76c84dee701a75dc55d9d202ffa1b5cf17f5d4 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 1 Feb 2021 19:04:26 +0100 Subject: [PATCH 17/31] gcp-projects,user-settings: update plugin test imports --- plugins/gcp-projects/dev/index.tsx | 4 ++-- plugins/gcp-projects/src/plugin.test.ts | 4 ++-- plugins/user-settings/src/plugin.test.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/gcp-projects/dev/index.tsx b/plugins/gcp-projects/dev/index.tsx index 812a5585d4..b87d4b7d47 100644 --- a/plugins/gcp-projects/dev/index.tsx +++ b/plugins/gcp-projects/dev/index.tsx @@ -15,6 +15,6 @@ */ import { createDevApp } from '@backstage/dev-utils'; -import { plugin } from '../src/plugin'; +import { gcpProjectsPlugin } from '../src/plugin'; -createDevApp().registerPlugin(plugin).render(); +createDevApp().registerPlugin(gcpProjectsPlugin).render(); diff --git a/plugins/gcp-projects/src/plugin.test.ts b/plugins/gcp-projects/src/plugin.test.ts index 86e909995f..78eddd8603 100644 --- a/plugins/gcp-projects/src/plugin.test.ts +++ b/plugins/gcp-projects/src/plugin.test.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import { plugin } from './plugin'; +import { gcpProjectsPlugin } from './plugin'; describe('gcp-projects', () => { it('should export plugin', () => { - expect(plugin).toBeDefined(); + expect(gcpProjectsPlugin).toBeDefined(); }); }); diff --git a/plugins/user-settings/src/plugin.test.ts b/plugins/user-settings/src/plugin.test.ts index 810a2cda12..479fd0ac61 100644 --- a/plugins/user-settings/src/plugin.test.ts +++ b/plugins/user-settings/src/plugin.test.ts @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { plugin } from './plugin'; +import { userSettingsPlugin } from './plugin'; describe('user-settings', () => { it('should export plugin', () => { - expect(plugin).toBeDefined(); + expect(userSettingsPlugin).toBeDefined(); }); }); From 8dfdec6139e5be4913b9589ae19f026f2e09c15d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 1 Feb 2021 19:09:52 +0100 Subject: [PATCH 18/31] add changesets --- .changeset/silent-readers-worry.md | 5 +++++ .changeset/stale-zebras-warn.md | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .changeset/silent-readers-worry.md create mode 100644 .changeset/stale-zebras-warn.md diff --git a/.changeset/silent-readers-worry.md b/.changeset/silent-readers-worry.md new file mode 100644 index 0000000000..866485ecc0 --- /dev/null +++ b/.changeset/silent-readers-worry.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-sonarqube': patch +--- + +Migrate to new composability API, exporting the plugin as `sonarQubePlugin` and card as `EntitySonarQubeCard`. diff --git a/.changeset/stale-zebras-warn.md b/.changeset/stale-zebras-warn.md new file mode 100644 index 0000000000..33b53d3f75 --- /dev/null +++ b/.changeset/stale-zebras-warn.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-welcome': patch +--- + +Migrated to new composability API, exporting the plugin as `welcomePlugin` and the page as `WelcomePage`. From 00e3c5a574ece24bc0d9a4f7a68d0a636672e0bd Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 1 Feb 2021 19:15:30 +0100 Subject: [PATCH 19/31] api-docs: add missing entity card exports --- plugins/api-docs/src/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/api-docs/src/index.ts b/plugins/api-docs/src/index.ts index dd231d6cb4..e7484f77c3 100644 --- a/plugins/api-docs/src/index.ts +++ b/plugins/api-docs/src/index.ts @@ -19,4 +19,9 @@ export { apiDocsPlugin, apiDocsPlugin as plugin, ApiExplorerPage, + EntityApiDefinitionCard, + EntityConsumedApisCard, + EntityConsumingComponentsCard, + EntityProvidedApisCard, + EntityProvidingComponentsCard, } from './plugin'; From bc5082a0078dc1d9e64e91a1bca96b3349cdbcb1 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 1 Feb 2021 19:15:43 +0100 Subject: [PATCH 20/31] add changesets --- .changeset/fresh-eels-compare.md | 5 +++++ .changeset/plenty-steaks-confess.md | 5 +++++ .changeset/quick-apes-shop.md | 5 +++++ 3 files changed, 15 insertions(+) create mode 100644 .changeset/fresh-eels-compare.md create mode 100644 .changeset/plenty-steaks-confess.md create mode 100644 .changeset/quick-apes-shop.md diff --git a/.changeset/fresh-eels-compare.md b/.changeset/fresh-eels-compare.md new file mode 100644 index 0000000000..5d17556f7f --- /dev/null +++ b/.changeset/fresh-eels-compare.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-user-settings': patch +--- + +Migrate to new composability API, exporting the plugin as `userSettingsPlugin` and the page as `UserSettingsPage`. diff --git a/.changeset/plenty-steaks-confess.md b/.changeset/plenty-steaks-confess.md new file mode 100644 index 0000000000..f9ffe0f539 --- /dev/null +++ b/.changeset/plenty-steaks-confess.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-gcp-projects': patch +--- + +Migrate to new composability API, exporting the plugin as `gcpProjectsPlugin` and page as `GcpProjectsPage`. diff --git a/.changeset/quick-apes-shop.md b/.changeset/quick-apes-shop.md new file mode 100644 index 0000000000..4c87b9253d --- /dev/null +++ b/.changeset/quick-apes-shop.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-api-docs': patch +--- + +Migrate to new composability API, exporting the plugin as `apiDocsPlugin`, index page as `ApiExplorerPage`, and entity page cards as `EntityApiDefinitionCard`, `EntityConsumedApisCard`, `EntityConsumingComponentsCard`, `EntityProvidedApisCard`, and `EntityProvidingComponentsCard`. From f8eefd6c8f33ad1e8452a52168df8abcf52a30dc Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 1 Feb 2021 19:19:57 +0100 Subject: [PATCH 21/31] graphiql: finalize composability port --- plugins/graphiql/dev/index.tsx | 14 ++++++++++++-- plugins/graphiql/src/index.ts | 6 +++++- plugins/graphiql/src/plugin.test.ts | 4 ++-- plugins/graphiql/src/plugin.ts | 4 ++-- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/plugins/graphiql/dev/index.tsx b/plugins/graphiql/dev/index.tsx index b93995a5dc..918695e0e0 100644 --- a/plugins/graphiql/dev/index.tsx +++ b/plugins/graphiql/dev/index.tsx @@ -14,12 +14,18 @@ * limitations under the License. */ +import React from 'react'; import { createDevApp } from '@backstage/dev-utils'; import { githubAuthApiRef, errorApiRef } from '@backstage/core'; -import { plugin, GraphQLEndpoints, graphQlBrowseApiRef } from '../src'; +import { + graphiqlPlugin, + GraphQLEndpoints, + graphQlBrowseApiRef, + GraphiQLPage, +} from '../src'; createDevApp() - .registerPlugin(plugin) + .registerPlugin(graphiqlPlugin) .registerApi({ api: graphQlBrowseApiRef, deps: { @@ -47,4 +53,8 @@ createDevApp() ]); }, }) + .addPage({ + title: 'GraphiQL', + element: , + }) .render(); diff --git a/plugins/graphiql/src/index.ts b/plugins/graphiql/src/index.ts index 4b1da14079..ed64779eba 100644 --- a/plugins/graphiql/src/index.ts +++ b/plugins/graphiql/src/index.ts @@ -14,7 +14,11 @@ * limitations under the License. */ -export { plugin, GraphiQLPage } from './plugin'; +export { + graphiqlPlugin, + graphiqlPlugin as plugin, + GraphiQLPage, +} from './plugin'; export { GraphiQLPage as Router } from './components'; export * from './lib/api'; export * from './route-refs'; diff --git a/plugins/graphiql/src/plugin.test.ts b/plugins/graphiql/src/plugin.test.ts index 0841d3e071..3683c0be0a 100644 --- a/plugins/graphiql/src/plugin.test.ts +++ b/plugins/graphiql/src/plugin.test.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import { plugin } from './plugin'; +import { graphiqlPlugin } from './plugin'; describe('graphiql', () => { it('should export plugin', () => { - expect(plugin).toBeDefined(); + expect(graphiqlPlugin).toBeDefined(); }); }); diff --git a/plugins/graphiql/src/plugin.ts b/plugins/graphiql/src/plugin.ts index 87f750b132..42969999d9 100644 --- a/plugins/graphiql/src/plugin.ts +++ b/plugins/graphiql/src/plugin.ts @@ -22,7 +22,7 @@ import { import { graphQlBrowseApiRef, GraphQLEndpoints } from './lib/api'; import { graphiQLRouteRef } from './route-refs'; -export const plugin = createPlugin({ +export const graphiqlPlugin = createPlugin({ id: 'graphiql', apis: [ // GitLab is used as an example endpoint, but most will want to plug in @@ -40,7 +40,7 @@ export const plugin = createPlugin({ ], }); -export const GraphiQLPage = plugin.provide( +export const GraphiQLPage = graphiqlPlugin.provide( createRoutableExtension({ component: () => import('./components').then(m => m.GraphiQLPage), mountPoint: graphiQLRouteRef, From b98928a38e0ca833594d18e7d8d3d176ed873fa4 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 1 Feb 2021 19:20:41 +0100 Subject: [PATCH 22/31] sonarqube,welcome: update dev setups --- plugins/sonarqube/dev/index.tsx | 8 +------- plugins/welcome/dev/index.tsx | 4 ++-- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/plugins/sonarqube/dev/index.tsx b/plugins/sonarqube/dev/index.tsx index b99e552df9..2dd9236547 100644 --- a/plugins/sonarqube/dev/index.tsx +++ b/plugins/sonarqube/dev/index.tsx @@ -15,13 +15,7 @@ */ import { Entity } from '@backstage/catalog-model'; -import { - Content, - createPlugin, - createRouteRef, - Header, - Page, -} from '@backstage/core'; +import { Content, Header, Page } from '@backstage/core'; import { createDevApp, EntityGridItem } from '@backstage/dev-utils'; import { Grid } from '@material-ui/core'; import React from 'react'; diff --git a/plugins/welcome/dev/index.tsx b/plugins/welcome/dev/index.tsx index 812a5585d4..b237812f97 100644 --- a/plugins/welcome/dev/index.tsx +++ b/plugins/welcome/dev/index.tsx @@ -15,6 +15,6 @@ */ import { createDevApp } from '@backstage/dev-utils'; -import { plugin } from '../src/plugin'; +import { welcomePlugin } from '../src/plugin'; -createDevApp().registerPlugin(plugin).render(); +createDevApp().registerPlugin(welcomePlugin).render(); From 87b189d00fef710d47c1c275447e90b3c740879c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 1 Feb 2021 19:21:33 +0100 Subject: [PATCH 23/31] changesets: add graphiql migration changeset --- .changeset/cyan-lions-float.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/cyan-lions-float.md diff --git a/.changeset/cyan-lions-float.md b/.changeset/cyan-lions-float.md new file mode 100644 index 0000000000..f5fa064b71 --- /dev/null +++ b/.changeset/cyan-lions-float.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-graphiql': patch +--- + +Finalized composability API migration, now exporting the plugin as `graphiqlPlugin`. From 6c07b5c6e5c1008919e0b10897c05eb1ab716def Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 1 Feb 2021 23:46:51 +0100 Subject: [PATCH 24/31] dev-utils: add path option to addPage, switch to render element in route, and skip sidebar item title is not set --- packages/dev-utils/src/devApp/render.tsx | 37 +++++++++++++++--------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/packages/dev-utils/src/devApp/render.tsx b/packages/dev-utils/src/devApp/render.tsx index f1a6d30b4c..5f86442b46 100644 --- a/packages/dev-utils/src/devApp/render.tsx +++ b/packages/dev-utils/src/devApp/render.tsx @@ -34,16 +34,16 @@ import { attachComponentData, } from '@backstage/core'; import SentimentDissatisfiedIcon from '@material-ui/icons/SentimentDissatisfied'; -import { Outlet } from 'react-router'; const GatheringRoute: (props: { path: string; - children: JSX.Element; -}) => JSX.Element = () => ; + element: JSX.Element; +}) => JSX.Element = ({ element }) => element; attachComponentData(GatheringRoute, 'core.gatherMountPoints', true); type RegisterPageOptions = { + path?: string; element: JSX.Element; title?: string; icon?: IconComponent; @@ -93,21 +93,30 @@ class DevAppBuilder { return this; } - addPage({ element, title, icon }: RegisterPageOptions): DevAppBuilder { - const path = `/page-${this.routes.length + 1}`; - this.sidebarItems.push( - , - ); + /** + * Adds a page component along with accompanying sidebar item. + * + * If no path is provided one will be generated. + * If no title is provider no sidebar item will be created. + */ + addPage(opts: RegisterPageOptions): DevAppBuilder { + const path = opts.path ?? `/page-${this.routes.length + 1}`; + if (opts.title) { + this.sidebarItems.push( + , + ); + } this.routes.push( - , + , ); return this; } + /** * Build a DevApp component using the resources registered so far */ From 5c300a25a72e5ca88bd9b6c598f1f18d09e2f195 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 1 Feb 2021 23:54:51 +0100 Subject: [PATCH 25/31] scaffolder: port to new composability API --- plugins/scaffolder/dev/index.tsx | 29 +++++++++++++++++++++++-- plugins/scaffolder/package.json | 1 + plugins/scaffolder/src/index.ts | 7 +++++- plugins/scaffolder/src/plugin.test.ts | 4 ++-- plugins/scaffolder/src/plugin.ts | 31 ++++++++++++++++++++++----- plugins/scaffolder/src/routes.ts | 3 +-- 6 files changed, 63 insertions(+), 12 deletions(-) diff --git a/plugins/scaffolder/dev/index.tsx b/plugins/scaffolder/dev/index.tsx index 812a5585d4..eff16ab0f5 100644 --- a/plugins/scaffolder/dev/index.tsx +++ b/plugins/scaffolder/dev/index.tsx @@ -14,7 +14,32 @@ * limitations under the License. */ +import React from 'react'; import { createDevApp } from '@backstage/dev-utils'; -import { plugin } from '../src/plugin'; +import { discoveryApiRef } from '@backstage/core'; +import { CatalogClient } from '@backstage/catalog-client'; +import { catalogApiRef } from '@backstage/plugin-catalog-react'; +import { TemplateIndexPage, TemplatePage } from '../src/plugin'; +import { ScaffolderApi, scaffolderApiRef } from '../src'; -createDevApp().registerPlugin(plugin).render(); +createDevApp() + .registerApi({ + api: catalogApiRef, + deps: { discoveryApi: discoveryApiRef }, + factory: ({ discoveryApi }) => new CatalogClient({ discoveryApi }), + }) + .registerApi({ + api: scaffolderApiRef, + deps: { discoveryApi: discoveryApiRef }, + factory: ({ discoveryApi }) => new ScaffolderApi({ discoveryApi }), + }) + .addPage({ + path: '/create', + title: 'Create', + element: , + }) + .addPage({ + path: '/create/:templateName', + element: , + }) + .render(); diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index d174991bb7..69021953a2 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -54,6 +54,7 @@ "@backstage/cli": "^0.5.0", "@backstage/dev-utils": "^0.1.8", "@backstage/test-utils": "^0.1.6", + "@backstage/catalog-client": "^0.3.5", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/scaffolder/src/index.ts b/plugins/scaffolder/src/index.ts index f0fdf5b429..5f102e7853 100644 --- a/plugins/scaffolder/src/index.ts +++ b/plugins/scaffolder/src/index.ts @@ -14,6 +14,11 @@ * limitations under the License. */ -export { plugin } from './plugin'; +export { + scaffolderPlugin, + scaffolderPlugin as plugin, + TemplateIndexPage, + TemplatePage, +} from './plugin'; export { ScaffolderApi, scaffolderApiRef } from './api'; export { rootRoute, templateRoute } from './routes'; diff --git a/plugins/scaffolder/src/plugin.test.ts b/plugins/scaffolder/src/plugin.test.ts index 3b4c92168d..03dd9fe465 100644 --- a/plugins/scaffolder/src/plugin.test.ts +++ b/plugins/scaffolder/src/plugin.test.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import { plugin } from './plugin'; +import { scaffolderPlugin } from './plugin'; describe('scaffolder', () => { it('should export plugin', () => { - expect(plugin).toBeDefined(); + expect(scaffolderPlugin).toBeDefined(); }); }); diff --git a/plugins/scaffolder/src/plugin.ts b/plugins/scaffolder/src/plugin.ts index 41f7a03249..ed20705185 100644 --- a/plugins/scaffolder/src/plugin.ts +++ b/plugins/scaffolder/src/plugin.ts @@ -18,13 +18,14 @@ import { createPlugin, createApiFactory, discoveryApiRef, + createRoutableExtension, } from '@backstage/core'; -import { ScaffolderPage } from './components/ScaffolderPage'; -import { TemplatePage } from './components/TemplatePage'; +import { ScaffolderPage as ScaffolderPageComponent } from './components/ScaffolderPage'; +import { TemplatePage as TemplatePageComponent } from './components/TemplatePage'; import { rootRoute, templateRoute } from './routes'; import { scaffolderApiRef, ScaffolderApi } from './api'; -export const plugin = createPlugin({ +export const scaffolderPlugin = createPlugin({ id: 'scaffolder', apis: [ createApiFactory({ @@ -34,7 +35,27 @@ export const plugin = createPlugin({ }), ], register({ router }) { - router.addRoute(rootRoute, ScaffolderPage); - router.addRoute(templateRoute, TemplatePage); + router.addRoute(rootRoute, ScaffolderPageComponent); + router.addRoute(templateRoute, TemplatePageComponent); + }, + routes: { + templateIndex: rootRoute, + template: templateRoute, }, }); + +export const TemplateIndexPage = scaffolderPlugin.provide( + createRoutableExtension({ + component: () => + import('./components/ScaffolderPage').then(m => m.ScaffolderPage), + mountPoint: rootRoute, + }), +); + +export const TemplatePage = scaffolderPlugin.provide( + createRoutableExtension({ + component: () => + import('./components/TemplatePage').then(m => m.TemplatePage), + mountPoint: templateRoute, + }), +); diff --git a/plugins/scaffolder/src/routes.ts b/plugins/scaffolder/src/routes.ts index 28c77f29a6..8efd1d3aaf 100644 --- a/plugins/scaffolder/src/routes.ts +++ b/plugins/scaffolder/src/routes.ts @@ -16,12 +16,11 @@ import { createRouteRef } from '@backstage/core'; export const rootRoute = createRouteRef({ - icon: () => null, path: '/create', title: 'Create new entity', }); + export const templateRoute = createRouteRef({ - icon: () => null, path: '/create/:templateName', title: 'Entity creation', }); From 7201498545e3dfdc41d6426ac4c03227414015d7 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 1 Feb 2021 23:57:36 +0100 Subject: [PATCH 26/31] add changesets --- .changeset/chilled-toys-raise.md | 5 +++++ .changeset/moody-apricots-warn.md | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .changeset/chilled-toys-raise.md create mode 100644 .changeset/moody-apricots-warn.md diff --git a/.changeset/chilled-toys-raise.md b/.changeset/chilled-toys-raise.md new file mode 100644 index 0000000000..282d5492ec --- /dev/null +++ b/.changeset/chilled-toys-raise.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Migrated to new composability API, exporting the plugin as `scaffolderPlugin`. The template list page (`/create`) is exported as the `TemplateIndexPage` extension, and the templating page itself is exported as `TemplatePage`. diff --git a/.changeset/moody-apricots-warn.md b/.changeset/moody-apricots-warn.md new file mode 100644 index 0000000000..c4d9be0ba3 --- /dev/null +++ b/.changeset/moody-apricots-warn.md @@ -0,0 +1,5 @@ +--- +'@backstage/dev-utils': patch +--- + +Added `path` option to `addPage` that can be used to set a specific path for the page rather than a generated one. Also omit sidebar item altogether if `title` option is not set. From 20ca5f179f1431cd12d3ef84a99a4df9d8b096bf Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 2 Feb 2021 11:34:48 +0100 Subject: [PATCH 27/31] Update packages/dev-utils/src/devApp/render.tsx Co-authored-by: Adam Harvey --- packages/dev-utils/src/devApp/render.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dev-utils/src/devApp/render.tsx b/packages/dev-utils/src/devApp/render.tsx index 5f86442b46..b4ec540f9c 100644 --- a/packages/dev-utils/src/devApp/render.tsx +++ b/packages/dev-utils/src/devApp/render.tsx @@ -97,7 +97,7 @@ class DevAppBuilder { * Adds a page component along with accompanying sidebar item. * * If no path is provided one will be generated. - * If no title is provider no sidebar item will be created. + * If no title is provided, no sidebar item will be created. */ addPage(opts: RegisterPageOptions): DevAppBuilder { const path = opts.path ?? `/page-${this.routes.length + 1}`; From 2430ee7c2e632f6bcac7a8387d54d410b1e87583 Mon Sep 17 00:00:00 2001 From: Andrew Thauer <6507159+andrewthauer@users.noreply.github.com> Date: Mon, 1 Feb 2021 22:15:26 -0500 Subject: [PATCH 28/31] feat(backend-common): support custom logger options --- .changeset/fluffy-nails-sort.md | 25 +++++++ .../backend-common/src/logging/formats.ts | 1 + packages/backend-common/src/logging/index.ts | 1 + .../src/logging/rootLogger.test.ts | 68 ++++++++++++++++++- .../backend-common/src/logging/rootLogger.ts | 49 +++++++++---- 5 files changed, 129 insertions(+), 15 deletions(-) create mode 100644 .changeset/fluffy-nails-sort.md diff --git a/.changeset/fluffy-nails-sort.md b/.changeset/fluffy-nails-sort.md new file mode 100644 index 0000000000..545698c47f --- /dev/null +++ b/.changeset/fluffy-nails-sort.md @@ -0,0 +1,25 @@ +--- +'@backstage/backend-common': patch +--- + +Updated the `rootLogger` in `@backstage/backend-common` to support custom logging options. This is useful when you want to make some changes without re-implementing the entire logger and calling `setRootLogger` or `logger.configure`. For example you can add additional `defaultMeta` tags to each log entry. The following changes are included: + +- Added `createRootLogger` which accepts winston `LoggerOptions`. These options allow overriding the default keys. +- Added an additional error format that can include stack traces. This can be enabled by setting a `LOG_STACKTRACE=true` environment variable. Any `Error` objects passed to `logger.error('message', err)` will include the full stack trace in a `stack` log entry key. + +Example Usage: + +```ts +// Create the logger +const logger = createRootLogger({ + defaultMeta: { appName: 'backstage', appEnv: 'prod' }, +}); + +// Add a custom logger transport +logger.add(new MyCustomTransport()); + +const config = await loadBackendConfig({ + argv: process.argv, + logger: getRootLogger(), // already set to new logger instance +}); +``` diff --git a/packages/backend-common/src/logging/formats.ts b/packages/backend-common/src/logging/formats.ts index 4f7949f115..fcd193f521 100644 --- a/packages/backend-common/src/logging/formats.ts +++ b/packages/backend-common/src/logging/formats.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import * as winston from 'winston'; import { TransformableInfo } from 'logform'; diff --git a/packages/backend-common/src/logging/index.ts b/packages/backend-common/src/logging/index.ts index 0ebf0371e9..ef2e96f6c0 100644 --- a/packages/backend-common/src/logging/index.ts +++ b/packages/backend-common/src/logging/index.ts @@ -14,5 +14,6 @@ * limitations under the License. */ +export * from './formats'; export * from './rootLogger'; export * from './voidLogger'; diff --git a/packages/backend-common/src/logging/rootLogger.test.ts b/packages/backend-common/src/logging/rootLogger.test.ts index 07ef857d50..5506d195a9 100644 --- a/packages/backend-common/src/logging/rootLogger.test.ts +++ b/packages/backend-common/src/logging/rootLogger.test.ts @@ -15,7 +15,7 @@ */ import * as winston from 'winston'; -import { getRootLogger, setRootLogger } from './rootLogger'; +import { createRootLogger, getRootLogger, setRootLogger } from './rootLogger'; describe('rootLogger', () => { it('can replace the default logger', () => { @@ -29,4 +29,70 @@ describe('rootLogger', () => { expect.stringContaining('testing'), ); }); + + describe('createRootLoger', () => { + it('creates a new logger', () => { + const oldLogger = getRootLogger(); + const newLogger = createRootLogger(); + + expect(oldLogger).not.toBe(newLogger); + }); + + it('replaces the existing root logger', () => { + const oldLogger = getRootLogger(); + createRootLogger(); + const newLogger = getRootLogger(); + expect(oldLogger).not.toBe(newLogger); + }); + + it('can append additional default metadata', () => { + const format = winston.format.json(); + const logger = createRootLogger({ + format, + defaultMeta: { + appName: 'backstage', + appEnv: 'prod', + containerId: 'abc', + }, + }); + jest.spyOn(format, 'transform'); + + logger.info('testing'); + + expect(format.transform).toHaveBeenCalledWith( + expect.objectContaining({ + message: 'testing', + service: 'backstage', + appName: 'backstage', + appEnv: 'prod', + containerId: 'abc', + }), + {}, + ); + }); + + it('can add override existing transports', () => { + const transport = new winston.transports.Console({ level: 'debug' }); + const logger = createRootLogger({ transports: [transport] }); + expect(logger.transports.length).toBe(1); + expect(logger.transports[0]).toBe(transport); + }); + + it('can append an additional transport', () => { + const logger = createRootLogger(); + const transport = new winston.transports.Console({ level: 'debug' }); + logger.add(transport); + expect(logger.transports.length).toBe(2); + expect(logger.transports[1]).toBe(transport); + expect(logger.transports[1].level).toBe('debug'); + }); + + it('can override default format', () => { + const format = winston.format(() => false)(); + const logger = createRootLogger({ format }); + expect( + logger.format.transform({ message: 'hello', level: 'info' }), + ).toBeFalsy(); + }); + }); }); diff --git a/packages/backend-common/src/logging/rootLogger.ts b/packages/backend-common/src/logging/rootLogger.ts index 306ed23444..a8cf4721d6 100644 --- a/packages/backend-common/src/logging/rootLogger.ts +++ b/packages/backend-common/src/logging/rootLogger.ts @@ -13,23 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import { merge } from 'lodash'; import * as winston from 'winston'; +import { LoggerOptions } from 'winston'; import { coloredFormat } from './formats'; -let rootLogger: winston.Logger = winston.createLogger({ - level: process.env.LOG_LEVEL || 'info', - format: - process.env.NODE_ENV === 'production' - ? winston.format.json() - : coloredFormat, - defaultMeta: { service: 'backstage' }, - transports: [ - new winston.transports.Console({ - silent: - process.env.JEST_WORKER_ID !== undefined && !process.env.LOG_LEVEL, - }), - ], -}); +let rootLogger: winston.Logger; export function getRootLogger(): winston.Logger { return rootLogger; @@ -38,3 +28,34 @@ export function getRootLogger(): winston.Logger { export function setRootLogger(newLogger: winston.Logger) { rootLogger = newLogger; } + +export function createRootLogger( + options: winston.LoggerOptions = {}, + env = process.env, +): winston.Logger { + const logger = winston.createLogger( + merge( + { + level: env.LOG_LEVEL || 'info', + format: winston.format.combine( + env.NODE_ENV === 'production' ? winston.format.json() : coloredFormat, + ), + defaultMeta: { + service: 'backstage', + }, + transports: [ + new winston.transports.Console({ + silent: env.JEST_WORKER_ID !== undefined && !env.LOG_LEVEL, + }), + ], + }, + options, + ), + ); + + setRootLogger(logger); + + return logger; +} + +rootLogger = createRootLogger(); From 161e4f918cf91262591be7ce757429098257c5d9 Mon Sep 17 00:00:00 2001 From: Andrew Thauer <6507159+andrewthauer@users.noreply.github.com> Date: Tue, 2 Feb 2021 06:11:40 -0500 Subject: [PATCH 29/31] update changeset --- .changeset/fluffy-nails-sort.md | 1 - 1 file changed, 1 deletion(-) diff --git a/.changeset/fluffy-nails-sort.md b/.changeset/fluffy-nails-sort.md index 545698c47f..0aa87b532f 100644 --- a/.changeset/fluffy-nails-sort.md +++ b/.changeset/fluffy-nails-sort.md @@ -5,7 +5,6 @@ Updated the `rootLogger` in `@backstage/backend-common` to support custom logging options. This is useful when you want to make some changes without re-implementing the entire logger and calling `setRootLogger` or `logger.configure`. For example you can add additional `defaultMeta` tags to each log entry. The following changes are included: - Added `createRootLogger` which accepts winston `LoggerOptions`. These options allow overriding the default keys. -- Added an additional error format that can include stack traces. This can be enabled by setting a `LOG_STACKTRACE=true` environment variable. Any `Error` objects passed to `logger.error('message', err)` will include the full stack trace in a `stack` log entry key. Example Usage: From b37501a3d1ff20e6f79229b7ccc5a0cd510f9c63 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 2 Feb 2021 15:07:33 +0100 Subject: [PATCH 30/31] catalog: finalize port to new composability API + tweak dev-utils --- .changeset/hot-rules-shout.md | 5 +++ .changeset/selfish-kids-know.md | 5 +++ packages/dev-utils/src/devApp/render.tsx | 9 +++++- plugins/catalog-react/src/routes.ts | 1 + plugins/catalog/dev/index.tsx | 28 +++++++++++++++-- plugins/catalog/src/extensions.tsx | 40 ------------------------ plugins/catalog/src/index.ts | 8 +++-- plugins/catalog/src/plugin.test.ts | 4 +-- plugins/catalog/src/plugin.ts | 21 ++++++++++++- 9 files changed, 73 insertions(+), 48 deletions(-) create mode 100644 .changeset/hot-rules-shout.md create mode 100644 .changeset/selfish-kids-know.md delete mode 100644 plugins/catalog/src/extensions.tsx diff --git a/.changeset/hot-rules-shout.md b/.changeset/hot-rules-shout.md new file mode 100644 index 0000000000..c88f4b01dc --- /dev/null +++ b/.changeset/hot-rules-shout.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Add `children` option to `addPage`, which will be rendered as the children of the `Route`. diff --git a/.changeset/selfish-kids-know.md b/.changeset/selfish-kids-know.md new file mode 100644 index 0000000000..f434559135 --- /dev/null +++ b/.changeset/selfish-kids-know.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Finalize migration to new composability API, with the plugin instance now exported `catalogPlugin`. diff --git a/packages/dev-utils/src/devApp/render.tsx b/packages/dev-utils/src/devApp/render.tsx index b4ec540f9c..e1bdf4d0e9 100644 --- a/packages/dev-utils/src/devApp/render.tsx +++ b/packages/dev-utils/src/devApp/render.tsx @@ -38,6 +38,7 @@ import SentimentDissatisfiedIcon from '@material-ui/icons/SentimentDissatisfied' const GatheringRoute: (props: { path: string; element: JSX.Element; + children?: ReactNode; }) => JSX.Element = ({ element }) => element; attachComponentData(GatheringRoute, 'core.gatherMountPoints', true); @@ -45,6 +46,7 @@ attachComponentData(GatheringRoute, 'core.gatherMountPoints', true); type RegisterPageOptions = { path?: string; element: JSX.Element; + children?: JSX.Element; title?: string; icon?: IconComponent; }; @@ -112,7 +114,12 @@ class DevAppBuilder { ); } this.routes.push( - , + , ); return this; } diff --git a/plugins/catalog-react/src/routes.ts b/plugins/catalog-react/src/routes.ts index 2983b464ac..0fced32f59 100644 --- a/plugins/catalog-react/src/routes.ts +++ b/plugins/catalog-react/src/routes.ts @@ -19,6 +19,7 @@ import { createRouteRef } from '@backstage/core'; const NoIcon = () => null; +// TODO(Rugvip): Move these route refs back to the catalog plugin once we're all ported to using external routes export const rootRoute = createRouteRef({ icon: NoIcon, path: '', diff --git a/plugins/catalog/dev/index.tsx b/plugins/catalog/dev/index.tsx index 812a5585d4..34f23071b0 100644 --- a/plugins/catalog/dev/index.tsx +++ b/plugins/catalog/dev/index.tsx @@ -14,7 +14,31 @@ * limitations under the License. */ +import React from 'react'; import { createDevApp } from '@backstage/dev-utils'; -import { plugin } from '../src/plugin'; +import { + catalogPlugin, + CatalogIndexPage, + CatalogEntityPage, + EntityLayout, +} from '../src'; -createDevApp().registerPlugin(plugin).render(); +createDevApp() + .registerPlugin(catalogPlugin) + .addPage({ + path: '/catalog', + title: 'Catalog', + element: , + }) + .addPage({ + path: '/catalog/:namespace/:kind/:name', + element: , + children: ( + + +

Overview

+
+
+ ), + }) + .render(); diff --git a/plugins/catalog/src/extensions.tsx b/plugins/catalog/src/extensions.tsx deleted file mode 100644 index 1eaa30b769..0000000000 --- a/plugins/catalog/src/extensions.tsx +++ /dev/null @@ -1,40 +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 { createRoutableExtension } from '@backstage/core'; -import { - catalogRouteRef, - entityRouteRef, -} from '@backstage/plugin-catalog-react'; -import { plugin } from './plugin'; - -export const CatalogIndexPage = plugin.provide( - createRoutableExtension({ - component: () => - import('./components/CatalogPage').then(m => m.CatalogPage), - mountPoint: catalogRouteRef, - }), -); - -export const CatalogEntityPage = plugin.provide( - createRoutableExtension({ - component: () => - import('./components/CatalogEntityPage/CatalogEntityPage').then( - m => m.CatalogEntityPage, - ), - mountPoint: entityRouteRef, - }), -); diff --git a/plugins/catalog/src/index.ts b/plugins/catalog/src/index.ts index 80367f80e4..3ef79680c4 100644 --- a/plugins/catalog/src/index.ts +++ b/plugins/catalog/src/index.ts @@ -19,5 +19,9 @@ export { EntityLayout } from './components/EntityLayout'; export { EntityPageLayout } from './components/EntityPageLayout'; export * from './components/EntitySwitch'; export { Router } from './components/Router'; -export * from './extensions'; -export { plugin } from './plugin'; +export { + catalogPlugin, + catalogPlugin as plugin, + CatalogIndexPage, + CatalogEntityPage, +} from './plugin'; diff --git a/plugins/catalog/src/plugin.test.ts b/plugins/catalog/src/plugin.test.ts index 427efc39e1..d2cf0e4bf5 100644 --- a/plugins/catalog/src/plugin.test.ts +++ b/plugins/catalog/src/plugin.test.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import { plugin } from './plugin'; +import { catalogPlugin } from './plugin'; describe('catalog', () => { it('should export plugin', () => { - expect(plugin).toBeDefined(); + expect(catalogPlugin).toBeDefined(); }); }); diff --git a/plugins/catalog/src/plugin.ts b/plugins/catalog/src/plugin.ts index 69a1285ab4..c19925775d 100644 --- a/plugins/catalog/src/plugin.ts +++ b/plugins/catalog/src/plugin.ts @@ -19,6 +19,7 @@ import { createApiFactory, createPlugin, discoveryApiRef, + createRoutableExtension, } from '@backstage/core'; import { catalogApiRef, @@ -26,7 +27,7 @@ import { entityRouteRef, } from '@backstage/plugin-catalog-react'; -export const plugin = createPlugin({ +export const catalogPlugin = createPlugin({ id: 'catalog', apis: [ createApiFactory({ @@ -40,3 +41,21 @@ export const plugin = createPlugin({ catalogEntity: entityRouteRef, }, }); + +export const CatalogIndexPage = catalogPlugin.provide( + createRoutableExtension({ + component: () => + import('./components/CatalogPage').then(m => m.CatalogPage), + mountPoint: catalogRouteRef, + }), +); + +export const CatalogEntityPage = catalogPlugin.provide( + createRoutableExtension({ + component: () => + import('./components/CatalogEntityPage/CatalogEntityPage').then( + m => m.CatalogEntityPage, + ), + mountPoint: entityRouteRef, + }), +); From 2f28f821a3738384e472ca6c146be7d518944a46 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 2 Feb 2021 15:24:34 +0100 Subject: [PATCH 31/31] backend-common: use separate integration test keys in CI --- .github/workflows/ci.yml | 4 ++++ .github/workflows/master-win.yml | 4 ++++ .github/workflows/master.yml | 4 ++++ .../src/reading/integration.test.ts | 15 +++++++++++---- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c35e2a88ff..aa2feebb3e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,6 +15,10 @@ jobs: env: CI: true NODE_OPTIONS: --max-old-space-size=4096 + INTEGRATION_TEST_GITHUB_TOKEN: ${{ secrets.INTEGRATION_TEST_GITHUB_TOKEN }} + INTEGRATION_TEST_GITLAB_TOKEN: ${{ secrets.INTEGRATION_TEST_GITLAB_TOKEN }} + INTEGRATION_TEST_BITBUCKET_TOKEN: ${{ secrets.INTEGRATION_TEST_BITBUCKET_TOKEN }} + INTEGRATION_TEST_AZURE_TOKEN: ${{ secrets.INTEGRATION_TEST_AZURE_TOKEN }} steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/master-win.yml b/.github/workflows/master-win.yml index e6a9bf158b..a9b5bfdf0e 100644 --- a/.github/workflows/master-win.yml +++ b/.github/workflows/master-win.yml @@ -16,6 +16,10 @@ jobs: env: CI: true NODE_OPTIONS: --max-old-space-size=4096 + INTEGRATION_TEST_GITHUB_TOKEN: ${{ secrets.INTEGRATION_TEST_GITHUB_TOKEN }} + INTEGRATION_TEST_GITLAB_TOKEN: ${{ secrets.INTEGRATION_TEST_GITLAB_TOKEN }} + INTEGRATION_TEST_BITBUCKET_TOKEN: ${{ secrets.INTEGRATION_TEST_BITBUCKET_TOKEN }} + INTEGRATION_TEST_AZURE_TOKEN: ${{ secrets.INTEGRATION_TEST_AZURE_TOKEN }} steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 0a615b7c82..425e1172a4 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -19,6 +19,10 @@ jobs: env: CI: true NODE_OPTIONS: --max-old-space-size=4096 + INTEGRATION_TEST_GITHUB_TOKEN: ${{ secrets.INTEGRATION_TEST_GITHUB_TOKEN }} + INTEGRATION_TEST_GITLAB_TOKEN: ${{ secrets.INTEGRATION_TEST_GITLAB_TOKEN }} + INTEGRATION_TEST_BITBUCKET_TOKEN: ${{ secrets.INTEGRATION_TEST_BITBUCKET_TOKEN }} + INTEGRATION_TEST_AZURE_TOKEN: ${{ secrets.INTEGRATION_TEST_AZURE_TOKEN }} steps: - uses: actions/checkout@v2 diff --git a/packages/backend-common/src/reading/integration.test.ts b/packages/backend-common/src/reading/integration.test.ts index da2e916210..2145a4b0fd 100644 --- a/packages/backend-common/src/reading/integration.test.ts +++ b/packages/backend-common/src/reading/integration.test.ts @@ -26,27 +26,34 @@ const reader = UrlReaders.default({ github: [ { host: 'github.com', - token: `${86}af${617}d9c3c8bf958b37a${630691452765}bb0b0a`, + token: + process.env.INTEGRATION_TEST_GITHUB_TOKEN || + `${86}af${617}d9c3c8bf958b37a${630691452765}bb0b0a`, }, ], gitlab: [ { host: 'gitlab.com', - token: 'tveGtSHDBJM9ZRHZNRfm', + token: + process.env.INTEGRATION_TEST_GITLAB_TOKEN || 'tveGtSHDBJM9ZRHZNRfm', }, ], bitbucket: [ { host: 'bitbucket.org', username: 'backstage-verification', - appPassword: 'H79MAAhtbZwCafkVTrrQ', + appPassword: + process.env.INTEGRATION_TEST_BITBUCKET_TOKEN || + 'H79MAAhtbZwCafkVTrrQ', }, ], azure: [ { host: 'dev.azure.com', // lasts until 2022-01-28 - token: `myvyavvfojh6wvw4ose4bfywqttqx${5}z${5}zs${5}bdxauqaek3yinkazq`, + token: + process.env.INTEGRATION_TEST_AZURE_TOKEN || + `myvyavvfojh6wvw4ose4bfywqttqx${5}z${5}zs${5}bdxauqaek3yinkazq`, }, ], },