{ + const { contentRef } = useContent(); + const FinalTitle = ( - + {pageTitle} ); diff --git a/packages/core-components/src/layout/ItemCard/ItemCard.stories.tsx b/packages/core-components/src/layout/ItemCard/ItemCard.stories.tsx index 1ba3dec3fd..a03ad9be41 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCard.stories.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCard.stories.tsx @@ -22,7 +22,7 @@ import CardMedia from '@material-ui/core/CardMedia'; import Typography from '@material-ui/core/Typography'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; -import { LinkButton } from '../../components'; +import { LinkButton } from '../../components/LinkButton/LinkButton'; import { ItemCardGrid } from './ItemCardGrid'; import { ItemCardHeader } from './ItemCardHeader'; diff --git a/packages/core-components/src/layout/ItemCard/ItemCard.tsx b/packages/core-components/src/layout/ItemCard/ItemCard.tsx index 0b1ee34c0c..2d1123b19c 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCard.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCard.tsx @@ -21,7 +21,7 @@ import CardContent from '@material-ui/core/CardContent'; import CardMedia from '@material-ui/core/CardMedia'; import Chip from '@material-ui/core/Chip'; import React, { ReactNode } from 'react'; -import { LinkButton } from '../../components'; +import { LinkButton } from '../../components/LinkButton/LinkButton'; import { ItemCardHeader } from './ItemCardHeader'; type ItemCardProps = { @@ -37,7 +37,7 @@ type ItemCardProps = { }; /** - * This card type has been deprecated. Instead use plain MUI Card and helpers + * This card type has been deprecated. Instead use plain Material UI Card and helpers * where appropriate. * * @example @@ -57,8 +57,8 @@ type ItemCardProps = { * * ``` * - * @deprecated Use plain MUI `` and composable helpers instead. - * @see https://material-ui.com/components/cards/ + * @deprecated Use plain Material UI `` and composable helpers instead. + * @see https://v4.mui.com/components/cards/ */ export function ItemCard(props: ItemCardProps) { const { description, tags, title, type, subtitle, label, onClick, href } = diff --git a/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx b/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx index a1e6d24aeb..30710bdc26 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx @@ -50,7 +50,7 @@ export type ItemCardGridProps = Partial> & { * select among several options. * * @remarks - * The immediate children are expected to be MUI Card components. + * The immediate children are expected to be Material UI Card components. * * Styles for the grid can be overridden using the `classes` prop, e.g.: * diff --git a/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx b/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx index da3d9b7ebe..1e4e7dc316 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx @@ -65,7 +65,7 @@ export type ItemCardHeaderProps = Partial> & { * are arranged in a grid for users to select among several options. * * @remarks - * This component expects to be placed within a MUI ``. + * This component expects to be placed within a Material UI ``. * * Styles for the header can be overridden using the `classes` prop, e.g.: * diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index 1eb1423c61..01adfd12aa 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -22,7 +22,7 @@ import BottomNavigationAction, { import { makeStyles } from '@material-ui/core/styles'; import React, { useContext } from 'react'; import { useLocation } from 'react-router-dom'; -import { Link } from '../../components'; +import { Link } from '../../components/Link/Link'; import { SidebarConfig, SidebarConfigContext } from './config'; import { MobileSidebarContext } from './MobileSidebar'; import { useSidebarPinState } from './SidebarPinStateContext'; @@ -69,7 +69,7 @@ const useStyles = makeStyles( ); /** - * Returns a MUI `BottomNavigationAction`, which is aware of the current location & the selected item in the `BottomNavigation`, + * Returns a Material UI `BottomNavigationAction`, which is aware of the current location & the selected item in the `BottomNavigation`, * such that it will highlight a `MobileSidebarGroup` either on location change or if the selected item changes. * * @param props `to`: pathname of link; `value`: index of the selected item diff --git a/packages/core-components/src/layout/TabbedCard/TabbedCard.tsx b/packages/core-components/src/layout/TabbedCard/TabbedCard.tsx index 90cc928169..a09ea11b3d 100644 --- a/packages/core-components/src/layout/TabbedCard/TabbedCard.tsx +++ b/packages/core-components/src/layout/TabbedCard/TabbedCard.tsx @@ -95,8 +95,11 @@ export function TabbedCard(props: PropsWithChildren) { }); } else { React.Children.map(children, child => { - if (React.isValidElement(child) && child?.props.value === value) { - selectedTabContent = child?.props?.children; + if ( + React.isValidElement<{ children?: unknown; value?: unknown }>(child) && + child?.props.value === value + ) { + selectedTabContent = child?.props.children; } }); } diff --git a/packages/core-plugin-api/CHANGELOG.md b/packages/core-plugin-api/CHANGELOG.md index 5c3fe8d8be..62b7d0fbdb 100644 --- a/packages/core-plugin-api/CHANGELOG.md +++ b/packages/core-plugin-api/CHANGELOG.md @@ -1,5 +1,34 @@ # @backstage/core-plugin-api +## 1.5.3 + +### Patch Changes + +- 8174cf4c0edf: Fixing MUI / Material UI references +- 13426ebd1235: Change `IconComponent` type to be compatible with Material UI v5 icons. +- Updated dependencies + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/version-bridge@1.0.4 + +## 1.5.3-next.1 + +### Patch Changes + +- 8174cf4c0edf: Fixing MUI / Material UI references +- Updated dependencies + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/version-bridge@1.0.4 + +## 1.5.3-next.0 + +### Patch Changes + +- 13426ebd1235: Change `IconComponent` type to be compatible with Material UI v5 icons. +- Updated dependencies + - @backstage/config@1.0.8 + ## 1.5.2 ### Patch Changes @@ -548,7 +577,7 @@ ### Patch Changes -- 784d8078ab: Removed direct and transitive MUI dependencies. +- 784d8078ab: Removed direct and transitive Material UI dependencies. - Updated dependencies - @backstage/config@0.1.12 diff --git a/packages/core-plugin-api/api-report.md b/packages/core-plugin-api/api-report.md index 227b9b9816..5577d94359 100644 --- a/packages/core-plugin-api/api-report.md +++ b/packages/core-plugin-api/api-report.md @@ -502,9 +502,14 @@ export const googleAuthApiRef: ApiRef< >; // @public -export type IconComponent = ComponentType<{ - fontSize?: 'default' | 'small' | 'large'; -}>; +export type IconComponent = ComponentType< + | { + fontSize?: 'large' | 'small' | 'default'; + } + | { + fontSize?: 'medium' | 'large' | 'small'; + } +>; // @public export type IdentityApi = { diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index 71f3463513..138ad24249 100644 --- a/packages/core-plugin-api/package.json +++ b/packages/core-plugin-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-plugin-api", "description": "Core API used by Backstage plugins", - "version": "1.5.2", + "version": "1.5.3", "publishConfig": { "access": "public" }, diff --git a/packages/core-plugin-api/src/icons/types.ts b/packages/core-plugin-api/src/icons/types.ts index d7dca27bf9..aa264294e3 100644 --- a/packages/core-plugin-api/src/icons/types.ts +++ b/packages/core-plugin-api/src/icons/types.ts @@ -22,8 +22,8 @@ import { ComponentType } from 'react'; * * @remarks * - * The type is based on SvgIcon from MUI, but both do not what the plugin-api - * package to have a dependency on MUI, nor do we want the props to be as broad + * The type is based on SvgIcon from Material UI, but both do not what the plugin-api + * package to have a dependency on Material UI, nor do we want the props to be as broad * as the SvgIconProps interface. * * If you have the need to forward additional props from SvgIconProps, you can @@ -32,6 +32,14 @@ import { ComponentType } from 'react'; * * @public */ -export type IconComponent = ComponentType<{ - fontSize?: 'default' | 'small' | 'large'; -}>; + +export type IconComponent = ComponentType< + /* Material UI v4 */ + | { + fontSize?: 'large' | 'small' | 'default'; + } + /* Material UI v5: https://mui.com/material-ui/migration/v5-component-changes/#icon */ + | { + fontSize?: 'medium' | 'large' | 'small'; + } +>; diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index cf92a30050..9a65a15f98 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,50 @@ # @backstage/create-app +## 0.5.4-next.0 + +### Patch Changes + +- Bumped create-app version. +- Updated dependencies + - @backstage/cli-common@0.1.12 + +## 0.5.3 + +### Patch Changes + +- 76b83a4422bb: Post-create message - added instruction to run `yarn install` when app was created with `--skip-install` +- d72e2eeba6f1: Bumped create-app version. +- e3ce5dd3f89a: add cy.\*\*.should to cypress eslintrc +- f920a61031c5: Enable specifying an external application template when using the `create-app` CLI command. +- 294b1629de28: Display a warning alert if relations are defined, which don't exist in the catalog. +- 4f7292c74dff: Updated included Dockerfile to include `g++`. Also updated the comments to note that some of the dependencies are also needed by isolated-vm +- Updated dependencies + - @backstage/cli-common@0.1.12 + +## 0.5.3-next.2 + +### Patch Changes + +- 4f7292c74dff: Updated included Dockerfile to include `g++`. Also updated the comments to note that some of the dependencies are also needed by isolated-vm +- Updated dependencies + - @backstage/cli-common@0.1.12 + +## 0.5.3-next.1 + +### Patch Changes + +- 76b83a4422bb: Post-create message - added instruction to run `yarn install` when app was created with `--skip-install` +- e3ce5dd3f89a: add cy.\*\*.should to cypress eslintrc +- 294b1629de28: Display a warning alert if relations are defined, which don't exist in the catalog. + +## 0.5.3-next.0 + +### Patch Changes + +- Bumped create-app version. +- Updated dependencies + - @backstage/cli-common@0.1.12 + ## 0.5.2 ### Patch Changes diff --git a/packages/create-app/cli-report.md b/packages/create-app/cli-report.md index 1244432c6a..20aa695993 100644 --- a/packages/create-app/cli-report.md +++ b/packages/create-app/cli-report.md @@ -11,5 +11,6 @@ Options: -V, --version --path [directory] --skip-install + --template-path [directory] -h, --help ``` diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 279913ac1d..fb203edf26 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "A CLI that helps you create your own Backstage app", - "version": "0.5.2", + "version": "0.5.4-next.0", "publishConfig": { "access": "public" }, diff --git a/packages/create-app/src/createApp.test.ts b/packages/create-app/src/createApp.test.ts index f455efa6e6..47c62520e0 100644 --- a/packages/create-app/src/createApp.test.ts +++ b/packages/create-app/src/createApp.test.ts @@ -20,6 +20,8 @@ import path from 'path'; import { Command } from 'commander'; import * as tasks from './lib/tasks'; import createApp from './createApp'; +import { findPaths } from '@backstage/cli-common'; +import { tmpdir } from 'os'; jest.mock('./lib/tasks'); @@ -74,6 +76,18 @@ describe('command entrypoint', () => { expect(createTemporaryAppFolderMock).toHaveBeenCalled(); expect(tryInitGitRepositoryMock).toHaveBeenCalled(); expect(templatingMock).toHaveBeenCalled(); + expect(templatingMock.mock.lastCall?.[0]).toEqual( + findPaths(__dirname).resolveTarget( + 'packages', + 'create-app', + 'src', + 'templates', + 'default-app', + ), + ); + expect(templatingMock.mock.lastCall?.[1]).toEqual( + path.join(tmpdir(), 'MyApp'), + ); expect(moveAppMock).toHaveBeenCalled(); expect(buildAppMock).toHaveBeenCalled(); }); @@ -84,6 +98,48 @@ describe('command entrypoint', () => { expect(checkPathExistsMock).toHaveBeenCalled(); expect(tryInitGitRepositoryMock).toHaveBeenCalled(); expect(templatingMock).toHaveBeenCalled(); + expect(templatingMock.mock.lastCall?.[0]).toEqual( + findPaths(__dirname).resolveTarget( + 'packages', + 'create-app', + 'src', + 'templates', + 'default-app', + ), + ); + expect(templatingMock.mock.lastCall?.[1]).toEqual('myDirectory'); + expect(buildAppMock).toHaveBeenCalled(); + }); + + it('should call expected tasks with relative --template-path option', async () => { + const cmd = { + path: 'myDirectory', + templatePath: 'templateDirectory', + } as unknown as Command; + await createApp(cmd); + expect(checkPathExistsMock).toHaveBeenCalled(); + expect(tryInitGitRepositoryMock).toHaveBeenCalled(); + expect(templatingMock).toHaveBeenCalled(); + expect(templatingMock.mock.lastCall?.[0]).toEqual( + findPaths(__dirname).resolveTarget('templateDirectory'), + ); + expect(templatingMock.mock.lastCall?.[1]).toEqual('myDirectory'); + expect(buildAppMock).toHaveBeenCalled(); + }); + + it('should call expected tasks with absolute --template-path option', async () => { + const cmd = { + path: 'myDirectory', + templatePath: path.resolve('somewhere', 'templateDirectory'), + } as unknown as Command; + await createApp(cmd); + expect(checkPathExistsMock).toHaveBeenCalled(); + expect(tryInitGitRepositoryMock).toHaveBeenCalled(); + expect(templatingMock).toHaveBeenCalled(); + expect(templatingMock.mock.lastCall?.[0]).toEqual( + path.resolve('somewhere', 'templateDirectory'), + ); + expect(templatingMock.mock.lastCall?.[1]).toEqual('myDirectory'); expect(buildAppMock).toHaveBeenCalled(); }); diff --git a/packages/create-app/src/createApp.ts b/packages/create-app/src/createApp.ts index 3e66b9801d..efa299a3c9 100644 --- a/packages/create-app/src/createApp.ts +++ b/packages/create-app/src/createApp.ts @@ -65,7 +65,9 @@ export default async (opts: OptionValues): Promise => { }, ]); - const templateDir = paths.resolveOwn('templates/default-app'); + const templateDir = opts.templatePath + ? paths.resolveTarget(opts.templatePath) + : paths.resolveOwn('templates/default-app'); const tempDir = resolvePath(os.tmpdir(), answers.name); // Use `--path` argument as application directory when specified, otherwise @@ -129,7 +131,18 @@ export default async (opts: OptionValues): Promise => { ); Task.log(); Task.section('All set! Now you might want to'); - Task.log(` Run the app: ${chalk.cyan(`cd ${answers.name} && yarn dev`)}`); + if (!opts.skipInstall) { + Task.log( + ` Install the dependencies: ${chalk.cyan( + `cd ${opts.path ?? answers.name} && yarn install`, + )}`, + ); + } + Task.log( + ` Run the app: ${chalk.cyan( + `cd ${opts.path ?? answers.name} && yarn dev`, + )}`, + ); Task.log( ' Set up the software catalog: https://backstage.io/docs/features/software-catalog/configuration', ); diff --git a/packages/create-app/src/index.ts b/packages/create-app/src/index.ts index fa235e6175..d9b1226848 100644 --- a/packages/create-app/src/index.ts +++ b/packages/create-app/src/index.ts @@ -39,6 +39,10 @@ const main = (argv: string[]) => { '--skip-install', 'Skip the install and builds steps after creating the app', ) + .option( + '--template-path [directory]', + 'Use an external application template instead of the default template', + ) .action(cmd => createApp(cmd)); program.parse(argv); diff --git a/packages/create-app/templates/default-app/packages/app/cypress/.eslintrc.json b/packages/create-app/templates/default-app/packages/app/cypress/.eslintrc.json index a467608916..b903ff250a 100644 --- a/packages/create-app/templates/default-app/packages/app/cypress/.eslintrc.json +++ b/packages/create-app/templates/default-app/packages/app/cypress/.eslintrc.json @@ -5,7 +5,7 @@ "jest/expect-expect": [ "error", { - "assertFunctionNames": ["expect", "cy.contains"] + "assertFunctionNames": ["expect", "cy.contains", "cy.**.should"] } ] } diff --git a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx index 54a05eea03..6722ea2d03 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx @@ -25,6 +25,8 @@ import { isKind, hasCatalogProcessingErrors, isOrphan, + hasRelationWarnings, + EntityRelationWarning, } from '@backstage/plugin-catalog'; import { isGithubActionsAvailable, @@ -101,6 +103,14 @@ const entityWarningContent = ( + + + + + + + + diff --git a/packages/create-app/templates/default-app/packages/backend/Dockerfile b/packages/create-app/templates/default-app/packages/backend/Dockerfile index 51f7124db4..d065e4a248 100644 --- a/packages/create-app/templates/default-app/packages/backend/Dockerfile +++ b/packages/create-app/templates/default-app/packages/backend/Dockerfile @@ -11,13 +11,18 @@ FROM node:16-bullseye-slim +# Install isolate-vm dependencies, these are needed by the @backstage/plugin-scaffolder-backend. +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + apt-get update && \ + apt-get install -y --no-install-recommends python3 g++ build-essential && \ + yarn config set python /usr/bin/python3 + # Install sqlite3 dependencies. You can skip this if you don't use sqlite3 in the image, # in which case you should also move better-sqlite3 to "devDependencies" in package.json. RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ - apt-get update && \ - apt-get install -y --no-install-recommends libsqlite3-dev python3 build-essential && \ - yarn config set python /usr/bin/python3 + apt-get install -y --no-install-recommends libsqlite3-dev # From here on we use the least-privileged `node` user to run the backend. USER node diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 65405b221e..45df686876 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,79 @@ # @backstage/dev-utils +## 1.0.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.9.1-next.0 + - @backstage/core-components@0.13.4-next.0 + - @backstage/app-defaults@1.4.2-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/test-utils@1.4.2-next.0 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/integration-react@1.1.16-next.0 + - @backstage/theme@0.4.1 + +## 1.0.17 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.0 + - @backstage/core-components@0.13.3 + - @backstage/core-app-api@1.9.0 + - @backstage/test-utils@1.4.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/app-defaults@1.4.1 + - @backstage/catalog-model@1.4.1 + - @backstage/integration-react@1.1.15 + +## 1.0.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.0-next.2 + - @backstage/theme@0.4.1-next.1 + - @backstage/test-utils@1.4.1-next.2 + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-components@0.13.3-next.2 + - @backstage/core-app-api@1.8.2-next.1 + - @backstage/app-defaults@1.4.1-next.2 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/integration-react@1.1.15-next.2 + +## 1.0.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.0 + - @backstage/core-components@0.13.3-next.1 + - @backstage/core-plugin-api@1.5.3-next.0 + - @backstage/app-defaults@1.4.1-next.1 + - @backstage/integration-react@1.1.15-next.1 + - @backstage/test-utils@1.4.1-next.1 + - @backstage/plugin-catalog-react@1.7.1-next.1 + - @backstage/core-app-api@1.8.2-next.0 + +## 1.0.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.3-next.0 + - @backstage/app-defaults@1.4.1-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/core-app-api@1.8.1 + - @backstage/core-plugin-api@1.5.2 + - @backstage/integration-react@1.1.15-next.0 + - @backstage/test-utils@1.4.1-next.0 + - @backstage/theme@0.4.0 + - @backstage/plugin-catalog-react@1.7.1-next.0 + ## 1.0.16 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index d2ffdb6091..d7e12209c3 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/dev-utils", "description": "Utilities for developing Backstage plugins.", - "version": "1.0.16", + "version": "1.0.18-next.0", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/e2e-test/CHANGELOG.md b/packages/e2e-test/CHANGELOG.md index 48cc255757..f6f29ff3f2 100644 --- a/packages/e2e-test/CHANGELOG.md +++ b/packages/e2e-test/CHANGELOG.md @@ -1,5 +1,48 @@ # e2e-test +## 0.2.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.4-next.0 + - @backstage/cli-common@0.1.12 + - @backstage/errors@1.2.1 + +## 0.2.5 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.3 + - @backstage/errors@1.2.1 + - @backstage/cli-common@0.1.12 + +## 0.2.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.3-next.2 + - @backstage/cli-common@0.1.12 + - @backstage/errors@1.2.1-next.0 + +## 0.2.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.3-next.1 + +## 0.2.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.3-next.0 + - @backstage/errors@1.2.1-next.0 + - @backstage/cli-common@0.1.12 + ## 0.2.4 ### Patch Changes diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index d98cdd792c..48ef90db6f 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -1,7 +1,7 @@ { "name": "e2e-test", "description": "E2E test for verifying Backstage packages", - "version": "0.2.4", + "version": "0.2.6-next.0", "private": true, "backstage": { "role": "cli" diff --git a/packages/errors/CHANGELOG.md b/packages/errors/CHANGELOG.md index ab1367aaf2..295c7d0e4d 100644 --- a/packages/errors/CHANGELOG.md +++ b/packages/errors/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/errors +## 1.2.1 + +### Patch Changes + +- e205b3e6ede8: Set `this.name` in all error classes that extend `CustomErrorBase` class to their actual name +- Updated dependencies + - @backstage/types@1.1.0 + +## 1.2.1-next.0 + +### Patch Changes + +- e205b3e6ede8: Set `this.name` in all error classes that extend `CustomErrorBase` class to their actual name +- Updated dependencies + - @backstage/types@1.1.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/errors/api-report.md b/packages/errors/api-report.md index 0d405d8ba6..3ece63035b 100644 --- a/packages/errors/api-report.md +++ b/packages/errors/api-report.md @@ -9,10 +9,16 @@ import { JsonObject } from '@backstage/types'; export function assertError(value: unknown): asserts value is ErrorLike; // @public -export class AuthenticationError extends CustomErrorBase {} +export class AuthenticationError extends CustomErrorBase { + // (undocumented) + name: 'AuthenticationError'; +} // @public -export class ConflictError extends CustomErrorBase {} +export class ConflictError extends CustomErrorBase { + // (undocumented) + name: 'ConflictError'; +} // @public export type ConsumedResponse = { @@ -73,22 +79,37 @@ export class ForwardedError extends CustomErrorBase { } // @public -export class InputError extends CustomErrorBase {} +export class InputError extends CustomErrorBase { + // (undocumented) + name: 'InputError'; +} // @public export function isError(value: unknown): value is ErrorLike; // @public -export class NotAllowedError extends CustomErrorBase {} +export class NotAllowedError extends CustomErrorBase { + // (undocumented) + name: 'NotAllowedError'; +} // @public -export class NotFoundError extends CustomErrorBase {} +export class NotFoundError extends CustomErrorBase { + // (undocumented) + name: 'NotFoundError'; +} // @public -export class NotImplementedError extends CustomErrorBase {} +export class NotImplementedError extends CustomErrorBase { + // (undocumented) + name: 'NotImplementedError'; +} // @public -export class NotModifiedError extends CustomErrorBase {} +export class NotModifiedError extends CustomErrorBase { + // (undocumented) + name: 'NotModifiedError'; +} // @public export function parseErrorResponseBody( diff --git a/packages/errors/package.json b/packages/errors/package.json index 9f611eef8a..05d04031ca 100644 --- a/packages/errors/package.json +++ b/packages/errors/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/errors", "description": "Common utilities for error handling within Backstage", - "version": "1.2.0", + "version": "1.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/errors/src/errors/CustomErrorBase.ts b/packages/errors/src/errors/CustomErrorBase.ts index b831d4eea3..ea9c91f173 100644 --- a/packages/errors/src/errors/CustomErrorBase.ts +++ b/packages/errors/src/errors/CustomErrorBase.ts @@ -22,8 +22,11 @@ import { isError } from './assertion'; * * @public * @example - *```ts - * class MyCustomError extends CustomErrorBase {} + * + * ```ts + * class MyCustomError extends CustomErrorBase { + * name = 'MyCustomError' as const; + * } * * const e = new MyCustomError('Some message', cause); * // e.name === 'MyCustomError' @@ -53,7 +56,13 @@ export class CustomErrorBase extends Error { Error.captureStackTrace?.(this, this.constructor); - this.name = this.constructor.name; + if (!this.name || this.name === 'Error') { + const baseName = this.constructor.name; + if (baseName !== 'Error') { + this.name = this.constructor.name; + } + } + this.cause = isError(cause) ? cause : undefined; } } diff --git a/packages/errors/src/errors/common.ts b/packages/errors/src/errors/common.ts index a2ea20e99f..861c3a7c3a 100644 --- a/packages/errors/src/errors/common.ts +++ b/packages/errors/src/errors/common.ts @@ -33,21 +33,27 @@ import { CustomErrorBase } from './CustomErrorBase'; * * @public */ -export class InputError extends CustomErrorBase {} +export class InputError extends CustomErrorBase { + name = 'InputError' as const; +} /** * The request requires authentication, which was not properly supplied. * * @public */ -export class AuthenticationError extends CustomErrorBase {} +export class AuthenticationError extends CustomErrorBase { + name = 'AuthenticationError' as const; +} /** * The authenticated caller is not allowed to perform this request. * * @public */ -export class NotAllowedError extends CustomErrorBase {} +export class NotAllowedError extends CustomErrorBase { + name = 'NotAllowedError' as const; +} /** * The requested resource could not be found. @@ -57,7 +63,9 @@ export class NotAllowedError extends CustomErrorBase {} * * @public */ -export class NotFoundError extends CustomErrorBase {} +export class NotFoundError extends CustomErrorBase { + name = 'NotFoundError' as const; +} /** * The request could not complete due to a conflict in the current state of the @@ -65,21 +73,27 @@ export class NotFoundError extends CustomErrorBase {} * * @public */ -export class ConflictError extends CustomErrorBase {} +export class ConflictError extends CustomErrorBase { + name = 'ConflictError' as const; +} /** * The requested resource has not changed since last request. * * @public */ -export class NotModifiedError extends CustomErrorBase {} +export class NotModifiedError extends CustomErrorBase { + name = 'NotModifiedError' as const; +} /** * The server does not support the functionality required to fulfill the request. * * @public */ -export class NotImplementedError extends CustomErrorBase {} +export class NotImplementedError extends CustomErrorBase { + name = 'NotImplementedError' as const; +} /** * The server is not ready to handle the request. diff --git a/packages/integration-aws-node/CHANGELOG.md b/packages/integration-aws-node/CHANGELOG.md index 461e3c4fe6..f6d802fe1c 100644 --- a/packages/integration-aws-node/CHANGELOG.md +++ b/packages/integration-aws-node/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/integration-aws-node +## 0.1.5 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/config@1.0.8 + +## 0.1.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/config@1.0.8 + ## 0.1.4 ### Patch Changes diff --git a/packages/integration-aws-node/package.json b/packages/integration-aws-node/package.json index ef99b51a57..808c976730 100644 --- a/packages/integration-aws-node/package.json +++ b/packages/integration-aws-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/integration-aws-node", "description": "Helpers for fetching AWS account credentials", - "version": "0.1.4", + "version": "0.1.5", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/integration-react/CHANGELOG.md b/packages/integration-react/CHANGELOG.md index c8ae49d171..4f275a8750 100644 --- a/packages/integration-react/CHANGELOG.md +++ b/packages/integration-react/CHANGELOG.md @@ -1,5 +1,59 @@ # @backstage/integration-react +## 1.1.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1 + - @backstage/theme@0.4.1 + +## 1.1.15 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1 + - @backstage/core-components@0.13.3 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1 + +## 1.1.15-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.1 + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-components@0.13.3-next.2 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1-next.0 + +## 1.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.0 + - @backstage/core-components@0.13.3-next.1 + - @backstage/core-plugin-api@1.5.3-next.0 + - @backstage/config@1.0.8 + +## 1.1.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.3-next.0 + - @backstage/config@1.0.8 + - @backstage/core-plugin-api@1.5.2 + - @backstage/integration@1.5.1-next.0 + - @backstage/theme@0.4.0 + ## 1.1.14 ### Patch Changes diff --git a/packages/integration-react/package.json b/packages/integration-react/package.json index 276a1ea8bd..b39d4fcb0c 100644 --- a/packages/integration-react/package.json +++ b/packages/integration-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/integration-react", "description": "Frontend package for managing integrations towards external systems", - "version": "1.1.14", + "version": "1.1.16-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -13,6 +13,12 @@ "backstage": { "role": "web-library" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "packages/integration-react" + }, "scripts": { "build": "backstage-cli package build", "start": "backstage-cli package start", diff --git a/packages/integration/CHANGELOG.md b/packages/integration/CHANGELOG.md index 851e023b3a..d1f3f46306 100644 --- a/packages/integration/CHANGELOG.md +++ b/packages/integration/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/integration +## 1.5.1 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/config@1.0.8 + +## 1.5.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/config@1.0.8 + ## 1.5.0 ### Minor Changes diff --git a/packages/integration/package.json b/packages/integration/package.json index 8cf8cc42dd..e02e070266 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/integration", "description": "Helpers for managing integrations towards external systems", - "version": "1.5.0", + "version": "1.5.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/repo-tools/CHANGELOG.md b/packages/repo-tools/CHANGELOG.md index 7112b9346a..b8f37f4977 100644 --- a/packages/repo-tools/CHANGELOG.md +++ b/packages/repo-tools/CHANGELOG.md @@ -1,5 +1,42 @@ # @backstage/repo-tools +## 0.3.3-next.0 + +### Patch Changes + +- ebeb77586975: Update `schema openapi generate` command to now create a default router that can be imported and used directly. +- Updated dependencies + - @backstage/cli-common@0.1.12 + - @backstage/cli-node@0.1.2 + - @backstage/errors@1.2.1 + +## 0.3.2 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/cli-node@0.1.2 + - @backstage/cli-common@0.1.12 + +## 0.3.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-node@0.1.2-next.1 + - @backstage/cli-common@0.1.12 + - @backstage/errors@1.2.1-next.0 + +## 0.3.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/cli-common@0.1.12 + - @backstage/cli-node@0.1.2-next.0 + ## 0.3.1 ### Patch Changes diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index 6c6cb574c2..c05cd30c75 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/repo-tools", "description": "CLI for Backstage repo tooling ", - "version": "0.3.1", + "version": "0.3.3-next.0", "publishConfig": { "access": "public" }, diff --git a/packages/repo-tools/src/commands/openapi/generate.ts b/packages/repo-tools/src/commands/openapi/generate.ts index e6c9f79f6e..52b8e5b840 100644 --- a/packages/repo-tools/src/commands/openapi/generate.ts +++ b/packages/repo-tools/src/commands/openapi/generate.ts @@ -53,8 +53,12 @@ async function generate( // ****************************************************************** // * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * // ****************************************************************** - -export default ${JSON.stringify(yaml, null, 2)} as const;`, +import {createValidatedOpenApiRouter} from '@backstage/backend-openapi-utils'; +export const spec = ${JSON.stringify(yaml, null, 2)} as const; +export const createOpenApiRouter = async ( + options?: Parameters['1'], +) => createValidatedOpenApiRouter(spec, options); +`, ); await exec(`yarn backstage-cli package lint --fix ${tsPath}`); diff --git a/packages/repo-tools/src/commands/openapi/verify.ts b/packages/repo-tools/src/commands/openapi/verify.ts index 4c4f310f73..2808e72702 100644 --- a/packages/repo-tools/src/commands/openapi/verify.ts +++ b/packages/repo-tools/src/commands/openapi/verify.ts @@ -41,10 +41,10 @@ async function verify(directoryPath: string) { const schema = await import(resolvePath(join(directoryPath, TS_MODULE))); - if (!schema.default) { - throw new Error(`\`${TS_SCHEMA_PATH}\` needs to have a default export.`); + if (!schema.spec) { + throw new Error(`\`${TS_SCHEMA_PATH}\` needs to have a 'spec' export.`); } - if (!isEqual(schema.default, yaml)) { + if (!isEqual(schema.spec, yaml)) { const path = relativePath(cliPaths.targetRoot, directoryPath); throw new Error( `\`${YAML_SCHEMA_PATH}\` and \`${TS_SCHEMA_PATH}\` do not match. Please run \`yarn backstage-repo-tools schema openapi generate ${path}\` to regenerate \`${TS_SCHEMA_PATH}\`.`, diff --git a/packages/techdocs-cli-embedded-app/CHANGELOG.md b/packages/techdocs-cli-embedded-app/CHANGELOG.md index 950c8711b1..26f2246d13 100644 --- a/packages/techdocs-cli-embedded-app/CHANGELOG.md +++ b/packages/techdocs-cli-embedded-app/CHANGELOG.md @@ -1,5 +1,99 @@ # techdocs-cli-embedded-app +## 0.2.85-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.9.1-next.0 + - @backstage/cli@0.22.10-next.0 + - @backstage/core-components@0.13.4-next.0 + - @backstage/plugin-catalog@1.12.1-next.0 + - @backstage/app-defaults@1.4.2-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/test-utils@1.4.2-next.0 + - @backstage/plugin-techdocs@1.6.6-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/integration-react@1.1.16-next.0 + - @backstage/theme@0.4.1 + - @backstage/plugin-techdocs-react@1.1.9-next.0 + +## 0.2.84 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.22.9 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog@1.12.0 + - @backstage/core-components@0.13.3 + - @backstage/core-app-api@1.9.0 + - @backstage/test-utils@1.4.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/app-defaults@1.4.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/integration-react@1.1.15 + - @backstage/plugin-techdocs@1.6.5 + - @backstage/plugin-techdocs-react@1.1.8 + +## 0.2.84-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.22.9-next.1 + - @backstage/plugin-catalog@1.12.0-next.2 + - @backstage/theme@0.4.1-next.1 + - @backstage/test-utils@1.4.1-next.2 + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-components@0.13.3-next.2 + - @backstage/core-app-api@1.8.2-next.1 + - @backstage/app-defaults@1.4.1-next.2 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/integration-react@1.1.15-next.2 + - @backstage/plugin-techdocs@1.6.5-next.2 + - @backstage/plugin-techdocs-react@1.1.8-next.2 + +## 0.2.84-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.0 + - @backstage/core-components@0.13.3-next.1 + - @backstage/plugin-catalog@1.11.3-next.1 + - @backstage/core-plugin-api@1.5.3-next.0 + - @backstage/app-defaults@1.4.1-next.1 + - @backstage/cli@0.22.9-next.0 + - @backstage/integration-react@1.1.15-next.1 + - @backstage/test-utils@1.4.1-next.1 + - @backstage/plugin-techdocs@1.6.5-next.1 + - @backstage/plugin-techdocs-react@1.1.8-next.1 + - @backstage/core-app-api@1.8.2-next.0 + - @backstage/config@1.0.8 + +## 0.2.84-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.22.9-next.0 + - @backstage/core-components@0.13.3-next.0 + - @backstage/app-defaults@1.4.1-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/core-app-api@1.8.1 + - @backstage/core-plugin-api@1.5.2 + - @backstage/integration-react@1.1.15-next.0 + - @backstage/test-utils@1.4.1-next.0 + - @backstage/theme@0.4.0 + - @backstage/plugin-catalog@1.11.3-next.0 + - @backstage/plugin-techdocs@1.6.5-next.0 + - @backstage/plugin-techdocs-react@1.1.8-next.0 + ## 0.2.83 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/cypress/.eslintrc.json b/packages/techdocs-cli-embedded-app/cypress/.eslintrc.json index a467608916..b903ff250a 100644 --- a/packages/techdocs-cli-embedded-app/cypress/.eslintrc.json +++ b/packages/techdocs-cli-embedded-app/cypress/.eslintrc.json @@ -5,7 +5,7 @@ "jest/expect-expect": [ "error", { - "assertFunctionNames": ["expect", "cy.contains"] + "assertFunctionNames": ["expect", "cy.contains", "cy.**.should"] } ] } diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index 1bf87f15ca..1b6af7f9e9 100644 --- a/packages/techdocs-cli-embedded-app/package.json +++ b/packages/techdocs-cli-embedded-app/package.json @@ -1,11 +1,17 @@ { "name": "techdocs-cli-embedded-app", - "version": "0.2.83", + "version": "0.2.85-next.0", "private": true, "backstage": { "role": "frontend" }, "bundled": true, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "packages/techdocs-cli-embedded-app" + }, "dependencies": { "@backstage/app-defaults": "workspace:^", "@backstage/catalog-model": "workspace:^", diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index da33defaca..daefe75e38 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,38 @@ # @techdocs/cli +## 1.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/plugin-techdocs-node@1.7.4-next.0 + +## 1.4.4 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.1 + - @backstage/catalog-model@1.4.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/plugin-techdocs-node@1.7.3 + +## 1.4.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.1-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/plugin-techdocs-node@1.7.3-next.0 + ## 1.4.3 ### Patch Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index a3fef8febb..d59450992b 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,7 +1,7 @@ { "name": "@techdocs/cli", "description": "Utility CLI for managing TechDocs sites in Backstage.", - "version": "1.4.3", + "version": "1.4.5-next.0", "publishConfig": { "access": "public" }, diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index de86ae97fa..a3fb8ba382 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,10 +1,75 @@ # @backstage/test-utils +## 1.4.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.9.1-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-permission-react@0.4.14 + +## 1.4.1 + +### Patch Changes + +- 8174cf4c0edf: Fixing MUI / Material UI references +- Updated dependencies + - @backstage/theme@0.4.1 + - @backstage/core-app-api@1.9.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-permission-react@0.4.14 + +## 1.4.1-next.2 + +### Patch Changes + +- 8174cf4c0edf: Fixing MUI / Material UI references +- Updated dependencies + - @backstage/theme@0.4.1-next.1 + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-app-api@1.8.2-next.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7-next.0 + - @backstage/plugin-permission-react@0.4.14-next.2 + +## 1.4.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.0 + - @backstage/core-plugin-api@1.5.3-next.0 + - @backstage/core-app-api@1.8.2-next.0 + - @backstage/plugin-permission-react@0.4.14-next.1 + - @backstage/config@1.0.8 + +## 1.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.8 + - @backstage/core-app-api@1.8.1 + - @backstage/core-plugin-api@1.5.2 + - @backstage/theme@0.4.0 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7-next.0 + - @backstage/plugin-permission-react@0.4.14-next.0 + ## 1.4.0 ### Minor Changes -- 1fd38bc4141a: Test App Wrapper is now using `UnifiedThemeProvider` for supporting MUI v5 next to MUI v4 in tests. +- 1fd38bc4141a: Test App Wrapper is now using `UnifiedThemeProvider` for supporting Material UI v5 next to Material UI v4 in tests. ### Patch Changes @@ -46,7 +111,7 @@ ### Minor Changes -- 1fd38bc4141a: Test App Wrapper is now using `UnifiedThemeProvider` for supporting MUI v5 next to MUI v4 in tests. +- 1fd38bc4141a: Test App Wrapper is now using `UnifiedThemeProvider` for supporting Material UI v5 next to Material UI v4 in tests. ### Patch Changes diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 5f57fa0dd2..ef074d13b2 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/test-utils", "description": "Utilities to test Backstage plugins and apps.", - "version": "1.4.0", + "version": "1.4.2-next.0", "publishConfig": { "access": "public" }, diff --git a/packages/test-utils/src/testUtils/mockBreakpoint.ts b/packages/test-utils/src/testUtils/mockBreakpoint.ts index ee759c07a5..78d7c9497f 100644 --- a/packages/test-utils/src/testUtils/mockBreakpoint.ts +++ b/packages/test-utils/src/testUtils/mockBreakpoint.ts @@ -16,13 +16,13 @@ /** * This is a mocking method suggested in the Jest docs, as it is not implemented in JSDOM yet. - * It can be used to mock values for the MUI `useMediaQuery` hook if it is used in a tested component. + * It can be used to mock values for the Material UI `useMediaQuery` hook if it is used in a tested component. * * For issues checkout the documentation: * https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom * - * If there are any updates from MUI React on testing `useMediaQuery` this mock should be replaced - * https://material-ui.com/components/use-media-query/#testing + * If there are any updates from Material UI React on testing `useMediaQuery` this mock should be replaced + * https://mui.com/material-ui/react-use-media-query/#testing * * @public */ diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index ab96891b8f..bd0eda744b 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -1,12 +1,36 @@ # @backstage/theme +## 0.4.1 + +### Patch Changes + +- 4f28914d9f0e: Overwrite `PaletteOptions` & `ThemeOptions` type to allow use of `createTheme` from `@backstage/theme` as well as `@material-ui/core/styles` with the same type. Also replaced the default `CSSBaseline` with v4 instead of v5 for better backwards compatibility for now. +- 41c5aa0ab589: Applying the modified `theme.spacing` method only to overrides instead of replacing it in the whole theme. +- 9395baa82413: You can now customize the typography of your theme by passing in your own custom typography defaults +- 8174cf4c0edf: Fixing MUI / Material UI references +- f0444f094396: Removed the hard coded color and background color in the `MuiChip` overrides so that they work better with custom themes +- 874c3e8bf909: Override the spacing to a v5 compliant method + +## 0.4.1-next.1 + +### Patch Changes + +- 8174cf4c0edf: Fixing MUI / Material UI references + +## 0.4.1-next.0 + +### Patch Changes + +- 4f28914d9f0e: Overwrite `PaletteOptions` & `ThemeOptions` type to allow use of `createTheme` from `@backstage/theme` as well as `@material-ui/core/styles` with the same type. Also replaced the default `CSSBaseline` with v4 instead of v5 for better backwards compatibility for now. +- 874c3e8bf909: Override the spacing to a v5 compliant method + ## 0.4.0 ### Minor Changes -- 1fd38bc4141a: **MUI v5 Support:** Adding platform-wide support for MUI v5 allowing a transition phase for migrating central plugins & components over. We still support v4 instances & plugins by adding a +- 1fd38bc4141a: **Material UI v5 Support:** Adding platform-wide support for Material UI v5 allowing a transition phase for migrating central plugins & components over. We still support v4 instances & plugins by adding a - To allow the future support of plugins & components using MUI v5 you want to upgrade your `AppTheme`'s to using the `UnifiedThemeProvider` + To allow the future support of plugins & components using Material UI v5 you want to upgrade your `AppTheme`'s to using the `UnifiedThemeProvider` ```diff Provider: ({ children }) => ( @@ -31,9 +55,9 @@ ### Minor Changes -- 1fd38bc4141a: **MUI v5 Support:** Adding platform-wide support for MUI v5 allowing a transition phase for migrating central plugins & components over. We still support v4 instances & plugins by adding a +- 1fd38bc4141a: **Material UI v5 Support:** Adding platform-wide support for Material UI v5 allowing a transition phase for migrating central plugins & components over. We still support v4 instances & plugins by adding a - To allow the future support of plugins & components using MUI v5 you want to upgrade your `AppTheme`'s to using the `UnifiedThemeProvider` + To allow the future support of plugins & components using Material UI v5 you want to upgrade your `AppTheme`'s to using the `UnifiedThemeProvider` ```diff Provider: ({ children }) => ( @@ -201,7 +225,7 @@ ### Patch Changes -- 2089de76b: Deprecated `ItemCard`. Added `ItemCardGrid` and `ItemCardHeader` instead, that can be used to compose functionality around regular Material-UI `Card` components instead. +- 2089de76b: Deprecated `ItemCard`. Added `ItemCardGrid` and `ItemCardHeader` instead, that can be used to compose functionality around regular Material UI `Card` components instead. ## 0.2.3 diff --git a/packages/theme/api-report.md b/packages/theme/api-report.md index fe05ca6b4e..d6380ee6e8 100644 --- a/packages/theme/api-report.md +++ b/packages/theme/api-report.md @@ -10,7 +10,8 @@ import type { PaletteOptions } from '@material-ui/core/styles/createPalette'; import { PaletteOptions as PaletteOptions_2 } from '@mui/material/styles'; import { ReactNode } from 'react'; import { Theme } from '@mui/material/styles'; -import { Theme as Theme_2 } from '@material-ui/core'; +import { Theme as Theme_2 } from '@material-ui/core/styles'; +import { Theme as Theme_3 } from '@material-ui/core'; import { ThemeOptions } from '@mui/material/styles'; import { ThemeOptions as ThemeOptions_2 } from '@material-ui/core/styles'; import type { ThemeOptions as ThemeOptions_3 } from '@material-ui/core'; @@ -86,7 +87,7 @@ export type BackstagePaletteOptions = PaletteOptions & BackstagePaletteAdditions; // @public @deprecated -export interface BackstageTheme extends Theme_2 { +export interface BackstageTheme extends Theme_3 { // (undocumented) getPageTheme: (selector: PageThemeSelector) => PageTheme; // (undocumented) @@ -111,6 +112,42 @@ export interface BackstageThemeOptions extends ThemeOptions_3 { palette: BackstagePaletteOptions; } +// @public +export type BackstageTypography = { + htmlFontSize: number; + fontFamily: string; + h1: { + fontSize: number; + fontWeight: number; + marginBottom: number; + }; + h2: { + fontSize: number; + fontWeight: number; + marginBottom: number; + }; + h3: { + fontSize: number; + fontWeight: number; + marginBottom: number; + }; + h4: { + fontSize: number; + fontWeight: number; + marginBottom: number; + }; + h5: { + fontSize: number; + fontWeight: number; + marginBottom: number; + }; + h6: { + fontSize: number; + fontWeight: number; + marginBottom: number; + }; +}; + // @public export interface BaseThemeOptionsInput { // (undocumented) @@ -123,6 +160,8 @@ export interface BaseThemeOptionsInput { pageTheme?: Record; // (undocumented) palette: PaletteOptions; + // (undocumented) + typography?: BackstageTypography; } // @public @@ -133,52 +172,19 @@ export function createBaseThemeOptions( options: BaseThemeOptionsInput, ): { palette: PaletteOptions; - typography: { - htmlFontSize: number; - fontFamily: string; - h1: { - fontSize: number; - fontWeight: number; - marginBottom: number; - }; - h2: { - fontSize: number; - fontWeight: number; - marginBottom: number; - }; - h3: { - fontSize: number; - fontWeight: number; - marginBottom: number; - }; - h4: { - fontWeight: number; - fontSize: number; - marginBottom: number; - }; - h5: { - fontWeight: number; - fontSize: number; - marginBottom: number; - }; - h6: { - fontWeight: number; - fontSize: number; - marginBottom: number; - }; - }; + typography: BackstageTypography; page: PageTheme; getPageTheme: ({ themeId }: PageThemeSelector) => PageTheme; }; // @public @deprecated -export function createTheme(options: SimpleThemeOptions): Theme_2; +export function createTheme(options: SimpleThemeOptions): Theme_3; // @public @deprecated export function createThemeOptions(options: SimpleThemeOptions): ThemeOptions_3; // @public @deprecated -export function createThemeOverrides(theme: Theme_2): Overrides; +export function createThemeOverrides(theme: Theme_3): Overrides; // @public export function createUnifiedTheme(options: UnifiedThemeOptions): UnifiedTheme; @@ -187,7 +193,7 @@ export function createUnifiedTheme(options: UnifiedThemeOptions): UnifiedTheme; export function createUnifiedThemeFromV4(options: ThemeOptions_2): UnifiedTheme; // @public @deprecated -export const darkTheme: Theme_2; +export const darkTheme: Theme_3; // @public export const defaultComponentThemes: ThemeOptions['components']; @@ -202,7 +208,7 @@ export function genPageTheme(props: { }): PageTheme; // @public @deprecated -export const lightTheme: Theme_2; +export const lightTheme: Theme_3; // @public export type PageTheme = { @@ -378,8 +384,15 @@ export type SimpleThemeOptions = { pageTheme?: Record; fontFamily?: string; htmlFontSize?: number; + typography?: BackstageTypography; }; +// @public +export type SupportedThemes = Theme_2 | Theme; + +// @public +export type SupportedVersions = 'v4' | 'v5'; + // @public export const themes: { light: UnifiedTheme; @@ -398,7 +411,7 @@ export function transformV5ComponentThemesToV4( // @public export interface UnifiedTheme { // (undocumented) - getTheme(version: string): unknown | undefined; + getTheme(version: SupportedVersions): SupportedThemes | undefined; } // @public @@ -415,6 +428,8 @@ export interface UnifiedThemeOptions { pageTheme?: Record; // (undocumented) palette: PaletteOptions & PaletteOptions_2; + // (undocumented) + typography?: BackstageTypography; } // @public diff --git a/packages/theme/package.json b/packages/theme/package.json index 0cd2643146..f5d1f090cf 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/theme", "description": "material-ui theme for use with Backstage.", - "version": "0.4.0", + "version": "0.4.1", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/theme/src/base/createBaseThemeOptions.ts b/packages/theme/src/base/createBaseThemeOptions.ts index e54e920deb..bca56114d7 100644 --- a/packages/theme/src/base/createBaseThemeOptions.ts +++ b/packages/theme/src/base/createBaseThemeOptions.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { PageTheme, PageThemeSelector } from './types'; +import { BackstageTypography, PageTheme, PageThemeSelector } from './types'; import { pageTheme as defaultPageThemes } from './pageTheme'; const DEFAULT_HTML_FONT_SIZE = 16; @@ -33,6 +33,7 @@ export interface BaseThemeOptionsInput { pageTheme?: Record; fontFamily?: string; htmlFontSize?: number; + typography?: BackstageTypography; } /** @@ -49,48 +50,51 @@ export function createBaseThemeOptions( fontFamily = DEFAULT_FONT_FAMILY, defaultPageTheme = DEFAULT_PAGE_THEME, pageTheme = defaultPageThemes, + typography, } = options; if (!pageTheme[defaultPageTheme]) { throw new Error(`${defaultPageTheme} is not defined in pageTheme.`); } + const defaultTypography = { + htmlFontSize, + fontFamily, + h1: { + fontSize: 54, + fontWeight: 700, + marginBottom: 10, + }, + h2: { + fontSize: 40, + fontWeight: 700, + marginBottom: 8, + }, + h3: { + fontSize: 32, + fontWeight: 700, + marginBottom: 6, + }, + h4: { + fontWeight: 700, + fontSize: 28, + marginBottom: 6, + }, + h5: { + fontWeight: 700, + fontSize: 24, + marginBottom: 4, + }, + h6: { + fontWeight: 700, + fontSize: 20, + marginBottom: 2, + }, + }; + return { palette, - typography: { - htmlFontSize, - fontFamily, - h1: { - fontSize: 54, - fontWeight: 700, - marginBottom: 10, - }, - h2: { - fontSize: 40, - fontWeight: 700, - marginBottom: 8, - }, - h3: { - fontSize: 32, - fontWeight: 700, - marginBottom: 6, - }, - h4: { - fontWeight: 700, - fontSize: 28, - marginBottom: 6, - }, - h5: { - fontWeight: 700, - fontSize: 24, - marginBottom: 4, - }, - h6: { - fontWeight: 700, - fontSize: 20, - marginBottom: 2, - }, - }, + typography: typography ?? defaultTypography, page: pageTheme[defaultPageTheme], getPageTheme: ({ themeId }: PageThemeSelector) => pageTheme[themeId] ?? pageTheme[defaultPageTheme], diff --git a/packages/theme/src/base/index.ts b/packages/theme/src/base/index.ts index 4bb80aa42b..2da9fb0fac 100644 --- a/packages/theme/src/base/index.ts +++ b/packages/theme/src/base/index.ts @@ -23,4 +23,5 @@ export type { BackstagePaletteAdditions, PageTheme, PageThemeSelector, + BackstageTypography, } from './types'; diff --git a/packages/theme/src/base/types.ts b/packages/theme/src/base/types.ts index 8f71ce4292..3e34a0abb7 100644 --- a/packages/theme/src/base/types.ts +++ b/packages/theme/src/base/types.ts @@ -114,3 +114,43 @@ export type BackstageThemeAdditions = { page: PageTheme; getPageTheme: (selector: PageThemeSelector) => PageTheme; }; + +/** + * Custom Typography + * + * @public + */ +export type BackstageTypography = { + htmlFontSize: number; + fontFamily: string; + h1: { + fontSize: number; + fontWeight: number; + marginBottom: number; + }; + h2: { + fontSize: number; + fontWeight: number; + marginBottom: number; + }; + h3: { + fontSize: number; + fontWeight: number; + marginBottom: number; + }; + h4: { + fontSize: number; + fontWeight: number; + marginBottom: number; + }; + h5: { + fontSize: number; + fontWeight: number; + marginBottom: number; + }; + h6: { + fontSize: number; + fontWeight: number; + marginBottom: number; + }; +}; diff --git a/packages/theme/src/unified/MuiClassNameSetup.ts b/packages/theme/src/unified/MuiClassNameSetup.ts index 42b948af32..5d7e0b26fe 100644 --- a/packages/theme/src/unified/MuiClassNameSetup.ts +++ b/packages/theme/src/unified/MuiClassNameSetup.ts @@ -17,7 +17,7 @@ import { unstable_ClassNameGenerator as ClassNameGenerator } from '@mui/material/className'; /** - * This API is introduced in @mui/material (v5.0.5) as a replacement of deprecated createGenerateClassName & only affects v5 MUI components from `@mui/*` + * This API is introduced in @mui/material (v5.0.5) as a replacement of deprecated createGenerateClassName & only affects v5 Material UI components from `@mui/*` */ ClassNameGenerator.configure(componentName => { return `v5-${componentName}`; diff --git a/packages/theme/src/unified/UnifiedTheme.tsx b/packages/theme/src/unified/UnifiedTheme.tsx index 0c5f80357a..aeae9694ee 100644 --- a/packages/theme/src/unified/UnifiedTheme.tsx +++ b/packages/theme/src/unified/UnifiedTheme.tsx @@ -20,21 +20,22 @@ import { createTheme, } from '@material-ui/core/styles'; import type { PaletteOptions as PaletteOptionsV4 } from '@material-ui/core/styles/createPalette'; -import { PaletteOptions as PaletteOptionsV5 } from '@mui/material/styles'; import { - adaptV4Theme, + DeprecatedThemeOptions, Theme as Mui5Theme, - createTheme as createV5Theme, + PaletteOptions as PaletteOptionsV5, ThemeOptions as ThemeOptionsV5, + adaptV4Theme, + createTheme as createV5Theme, } from '@mui/material/styles'; -import { transformV5ComponentThemesToV4 } from './overrides'; -import { PageTheme } from '../base/types'; -import { defaultComponentThemes } from '../v5'; import { createBaseThemeOptions } from '../base/createBaseThemeOptions'; -import { UnifiedTheme } from './types'; +import { BackstageTypography, PageTheme } from '../base/types'; +import { defaultComponentThemes } from '../v5'; +import { transformV5ComponentThemesToV4 } from './overrides'; +import { SupportedThemes, SupportedVersions, UnifiedTheme } from './types'; export class UnifiedThemeHolder implements UnifiedTheme { - #themes = new Map(); + #themes = new Map(); constructor(v4?: Mui4Theme, v5?: Mui5Theme) { this.#themes = new Map(); @@ -46,7 +47,7 @@ export class UnifiedThemeHolder implements UnifiedTheme { } } - getTheme(version: string): unknown | undefined { + getTheme(version: SupportedVersions): SupportedThemes | undefined { return this.#themes.get(version); } } @@ -63,6 +64,7 @@ export interface UnifiedThemeOptions { fontFamily?: string; htmlFontSize?: number; components?: ThemeOptionsV5['components']; + typography?: BackstageTypography; } /** @@ -75,27 +77,21 @@ export function createUnifiedTheme(options: UnifiedThemeOptions): UnifiedTheme { const components = { ...defaultComponentThemes, ...options.components }; const v5Theme = createV5Theme({ ...themeOptions, components }); - // TODO: Not super relevant in the beginning - /* const mui4Styles = maybeLoadMui4Styles(); - if (!mui4Styles) { - return new UnifiedThemeHolder(undefined, v5Theme); - } */ - const v4Overrides = transformV5ComponentThemesToV4(v5Theme, components); const v4Theme = { ...createTheme(themeOptions), ...v4Overrides }; return new UnifiedThemeHolder(v4Theme, v5Theme); } /** - * Creates a new {@link UnifiedTheme} using MUI v4 theme options. - * Note that this uses `adaptV4Theme` from MUI v5, which is deprecated. + * Creates a new {@link UnifiedTheme} using Material UI v4 theme options. + * Note that this uses `adaptV4Theme` from Material UI v5, which is deprecated. * * @public */ export function createUnifiedThemeFromV4( options: ThemeOptionsV4, ): UnifiedTheme { - const v5Theme = adaptV4Theme(options as any); + const v5Theme = adaptV4Theme(options as DeprecatedThemeOptions); const v4Theme = createTheme(options); return new UnifiedThemeHolder(v4Theme, v5Theme); } diff --git a/packages/theme/src/unified/UnifiedThemeProvider.tsx b/packages/theme/src/unified/UnifiedThemeProvider.tsx index 613f89d5e0..35abbe57d8 100644 --- a/packages/theme/src/unified/UnifiedThemeProvider.tsx +++ b/packages/theme/src/unified/UnifiedThemeProvider.tsx @@ -16,16 +16,18 @@ import React, { ReactNode } from 'react'; import './MuiClassNameSetup'; +import { CssBaseline } from '@material-ui/core'; import { ThemeProvider, StylesProvider, createGenerateClassName, + Theme as Mui4Theme, } from '@material-ui/core/styles'; import { StyledEngineProvider, ThemeProvider as Mui5Provider, + Theme as Mui5Theme, } from '@mui/material/styles'; -import CSSBaseline from '@mui/material/CssBaseline'; import { UnifiedTheme } from './types'; /** @@ -41,14 +43,14 @@ export interface UnifiedThemeProviderProps { // Background at https://mui.com/x/migration/migration-data-grid-v4/#using-mui-core-v4-with-v5 // Rather than disabling globals and custom seed, we instead only set a production prefix that -// won't collide with MUI 5 styles. We've already got a separate class name generator for v5 set +// won't collide with Material UI 5 styles. We've already got a separate class name generator for v5 set // up in MuiClassNameSetup.ts, so only the production JSS needs deduplication. const generateV4ClassName = createGenerateClassName({ productionPrefix: 'jss4-', }); /** - * Provides themes for all MUI versions supported by the provided unified theme. + * Provides themes for all Material UI versions supported by the provided unified theme. * * @public */ @@ -57,12 +59,12 @@ export function UnifiedThemeProvider( ): JSX.Element { const { children, theme, noCssBaseline = false } = props; - const v4Theme = theme.getTheme('v4'); - const v5Theme = theme.getTheme('v5'); + const v4Theme = theme.getTheme('v4') as Mui4Theme; + const v5Theme = theme.getTheme('v5') as Mui5Theme; let cssBaseline: JSX.Element | undefined = undefined; if (!noCssBaseline) { - cssBaseline = ; + cssBaseline = ; } let result = ( diff --git a/packages/theme/src/unified/index.ts b/packages/theme/src/unified/index.ts index f5c805d3ed..747662e195 100644 --- a/packages/theme/src/unified/index.ts +++ b/packages/theme/src/unified/index.ts @@ -20,4 +20,4 @@ export type { UnifiedThemeOptions } from './UnifiedTheme'; export { themes } from './themes'; export { UnifiedThemeProvider } from './UnifiedThemeProvider'; export type { UnifiedThemeProviderProps } from './UnifiedThemeProvider'; -export type { UnifiedTheme } from './types'; +export type { UnifiedTheme, SupportedThemes, SupportedVersions } from './types'; diff --git a/packages/theme/src/unified/overrides.test.ts b/packages/theme/src/unified/overrides.test.ts index e84cedade6..c99c0c10a4 100644 --- a/packages/theme/src/unified/overrides.test.ts +++ b/packages/theme/src/unified/overrides.test.ts @@ -18,12 +18,14 @@ import { Theme } from '@mui/material/styles'; import { transformV5ComponentThemesToV4 } from './overrides'; describe('transformV5ComponentThemesToV4', () => { + // TODO: Create actual v5 Theme to avoid missing properties in test const mockTheme = { palette: { primary: { main: 'red', }, }, + spacing: (n: number) => `${n * 8}px`, } as unknown as Theme; it('transforms empty component themes', () => { expect(transformV5ComponentThemesToV4(mockTheme)).toEqual({ diff --git a/packages/theme/src/unified/overrides.ts b/packages/theme/src/unified/overrides.ts index 7417fccedc..3075b88e70 100644 --- a/packages/theme/src/unified/overrides.ts +++ b/packages/theme/src/unified/overrides.ts @@ -29,6 +29,26 @@ type StaticStyleRules = Record< CSSProperties | Record >; +// Utility function based on v5 `createSpacing`: https://github.com/mui/material-ui/blob/master/packages/mui-system/src/createTheme/createSpacing.ts#L42C3-L59C5 +const __v5Spacing = + (defaultSpacing: number) => + (...argsInput: ReadonlyArray) => { + const args = argsInput.length === 0 ? [1] : argsInput; + const transform = (argument: string | number, themeSpacing: number) => { + if (typeof argument === 'string') { + return argument; + } + return themeSpacing * argument; + }; + + return args + .map(argument => { + const output = transform(argument, defaultSpacing); + return typeof output === 'number' ? `${output}px` : output; + }) + .join(' '); + }; + // Converts callback-based overrides to static styles, e.g. // { root: theme => ({ color: theme.color }) } -> { root: { color: 'red' } } function adaptV5CssBaselineOverride( @@ -57,10 +77,17 @@ function adaptV5Override( return undefined; } if (typeof overrides === 'object') { + const _theme = { ...theme }; + const defaultSpacing = theme.spacing(1); + if (typeof defaultSpacing === 'number') { + // Override potential v4 spacing method: https://mui.com/material-ui/migration/v5-style-changes/#%E2%9C%85-remove-px-suffix + // `adaptV4Theme as reference: https://github.com/mui/material-ui/blob/v5.x/packages/mui-material/src/styles/adaptV4Theme.js#L54C41-L54C41 + _theme.spacing = __v5Spacing(defaultSpacing); + } return Object.fromEntries( Object.entries(overrides).map(([className, style]) => { if (typeof style === 'function') { - return [className, style({ theme })]; + return [className, style({ theme: _theme })]; } return [className, style]; }), @@ -105,7 +132,7 @@ function extractV5StateOverrides( } /** - * Transform MUI v5 component themes into a v4 theme props and overrides. + * Transform Material UI v5 component themes into a v4 theme props and overrides. * * @public */ diff --git a/packages/theme/src/unified/themes.ts b/packages/theme/src/unified/themes.ts index 24e1b1026b..197a5cbbd0 100644 --- a/packages/theme/src/unified/themes.ts +++ b/packages/theme/src/unified/themes.ts @@ -18,7 +18,7 @@ import { palettes } from '../base'; import { createUnifiedTheme } from './UnifiedTheme'; /** - * Built-in Backstage MUI themes. + * Built-in Backstage Material UI themes. * * @public */ diff --git a/packages/theme/src/unified/types.ts b/packages/theme/src/unified/types.ts index f090a99b83..0c548e3d4d 100644 --- a/packages/theme/src/unified/types.ts +++ b/packages/theme/src/unified/types.ts @@ -14,13 +14,32 @@ * limitations under the License. */ +import { Theme as Mui4Theme } from '@material-ui/core/styles'; +import { Theme as Mui5Theme } from '@mui/material/styles'; + /** - * A container of one theme for multiple different MUI versions. + * Supported Material UI Versions + * + * Currently: 'v4' and 'v5'. + * + * @public + */ +export type SupportedVersions = 'v4' | 'v5'; + +/** + * Supported Material UI Theme Types for `SupportedVersions` + * + * @public + */ +export type SupportedThemes = Mui4Theme | Mui5Theme; + +/** + * A container of one theme for multiple different Material UI versions. * * Currently known keys are 'v4' and 'v5'. * * @public */ export interface UnifiedTheme { - getTheme(version: string): unknown | undefined; + getTheme(version: SupportedVersions): SupportedThemes | undefined; } diff --git a/packages/theme/src/v4/baseTheme.ts b/packages/theme/src/v4/baseTheme.ts index 8143183ec0..ef63e2a5fa 100644 --- a/packages/theme/src/v4/baseTheme.ts +++ b/packages/theme/src/v4/baseTheme.ts @@ -29,7 +29,7 @@ import { defaultComponentThemes } from '../v5'; import { transformV5ComponentThemesToV4 } from '../unified/overrides'; /** - * An old helper for creating MUI v4 theme options. + * An old helper for creating Material UI v4 theme options. * * @public * @deprecated Use {@link createBaseThemeOptions} instead. @@ -47,7 +47,7 @@ export function createThemeOptions(options: SimpleThemeOptions): ThemeOptions { } /** - * * An old helper for creating MUI v4 theme overrides. + * * An old helper for creating Material UI v4 theme overrides. * * @public * @deprecated Use {@link defaultComponentThemes} with {@link transformV5ComponentThemesToV4} instead. @@ -61,7 +61,7 @@ export function createThemeOverrides(theme: Theme): Overrides { } /** - * The old method to create a Backstage MUI v4 theme using a palette. + * The old method to create a Backstage Material UI v4 theme using a palette. * The theme is created with the common Backstage options and component styles. * * @public diff --git a/packages/theme/src/v4/themes.ts b/packages/theme/src/v4/themes.ts index 488ee58831..6347606d6c 100644 --- a/packages/theme/src/v4/themes.ts +++ b/packages/theme/src/v4/themes.ts @@ -18,7 +18,7 @@ import { createTheme } from './baseTheme'; import { palettes } from '../base'; /** - * The old MUI v4 Backstage light theme. + * The old Material UI v4 Backstage light theme. * * @public * @deprecated Use {@link themes.light} instead. @@ -28,7 +28,7 @@ export const lightTheme = createTheme({ }); /** - * The old MUI v4 Backstage dark theme. + * The old Material UI v4 Backstage dark theme. * * @public * @deprecated Use {@link themes.dark} instead. diff --git a/packages/theme/src/v4/types.ts b/packages/theme/src/v4/types.ts index d059bc2d71..c5100dab8f 100644 --- a/packages/theme/src/v4/types.ts +++ b/packages/theme/src/v4/types.ts @@ -25,6 +25,7 @@ import type { import { BackstagePaletteAdditions, BackstageThemeAdditions, + BackstageTypography, PageTheme, PageThemeSelector, } from '../base/types'; @@ -33,7 +34,7 @@ import { * The full Backstage palette. * * @public - * @deprecated This type is deprecated, the MUI Palette type is now always extended instead. + * @deprecated This type is deprecated, the Material UI Palette type is now always extended instead. */ export type BackstagePalette = MuiPalette & BackstagePaletteAdditions; @@ -41,7 +42,7 @@ export type BackstagePalette = MuiPalette & BackstagePaletteAdditions; * The full Backstage palette options. * * @public - * @deprecated This type is deprecated, the MUI PaletteOptions type is now always extended instead. + * @deprecated This type is deprecated, the Material UI PaletteOptions type is now always extended instead. */ export type BackstagePaletteOptions = MuiPaletteOptions & BackstagePaletteAdditions; @@ -50,7 +51,7 @@ export type BackstagePaletteOptions = MuiPaletteOptions & * Backstage theme options. * * @public - * @deprecated This type is deprecated, the MUI ThemeOptions type is now always extended instead. + * @deprecated This type is deprecated, the Material UI ThemeOptions type is now always extended instead. * @remarks * * This is essentially a partial theme definition made by the user, that then @@ -68,7 +69,7 @@ export interface BackstageThemeOptions extends MuiThemeOptions { * A Backstage theme. * * @public - * @deprecated This type is deprecated, the MUI Theme type is now always extended instead. + * @deprecated This type is deprecated, the Material UI Theme type is now always extended instead. */ export interface BackstageTheme extends MuiTheme { palette: BackstagePalette; @@ -89,12 +90,17 @@ export type SimpleThemeOptions = { pageTheme?: Record; fontFamily?: string; htmlFontSize?: number; + typography?: BackstageTypography; }; declare module '@material-ui/core/styles/createPalette' { interface Palette extends BackstagePaletteAdditions {} + + interface PaletteOptions extends Partial {} } declare module '@material-ui/core/styles/createTheme' { interface Theme extends BackstageThemeAdditions {} + + interface ThemeOptions extends Partial {} } diff --git a/packages/theme/src/v5/defaultComponentThemes.ts b/packages/theme/src/v5/defaultComponentThemes.ts index 14656b007d..e484300818 100644 --- a/packages/theme/src/v5/defaultComponentThemes.ts +++ b/packages/theme/src/v5/defaultComponentThemes.ts @@ -168,14 +168,9 @@ export const defaultComponentThemes: ThemeOptions['components'] = { MuiChip: { styleOverrides: { root: ({ theme }) => ({ - backgroundColor: '#D9D9D9', // By default there's no margin, but it's usually wanted, so we add some trailing margin marginRight: theme.spacing(1), marginBottom: theme.spacing(1), - color: theme.palette.grey[900], - }), - outlined: ({ theme }) => ({ - color: theme.palette.text.primary, }), label: ({ theme }) => ({ lineHeight: theme.spacing(2.5), @@ -186,15 +181,14 @@ export const defaultComponentThemes: ThemeOptions['components'] = { fontSize: theme.spacing(1.5), }), deleteIcon: ({ theme }) => ({ - color: theme.palette.grey[500], width: theme.spacing(3), height: theme.spacing(3), - margin: `0 ${theme.spacing(0.75)} 0 -${theme.spacing(0.75)}`, + margin: theme.spacing(0, 0.75, 0, -0.75), }), deleteIconSmall: ({ theme }) => ({ width: theme.spacing(2), height: theme.spacing(2), - margin: `0 ${theme.spacing(0.5)} 0 -${theme.spacing(0.5)}`, + margin: theme.spacing(0, 0.5, 0, -0.5), }), }, }, @@ -240,4 +234,9 @@ export const defaultComponentThemes: ThemeOptions['components'] = { }, }, }, + MuiLink: { + defaultProps: { + underline: 'hover', + }, + }, }; diff --git a/packages/theme/src/v5/types.ts b/packages/theme/src/v5/types.ts index ab74129b95..0d6d46cef9 100644 --- a/packages/theme/src/v5/types.ts +++ b/packages/theme/src/v5/types.ts @@ -22,10 +22,14 @@ import { declare module '@mui/material/styles' { interface Palette extends BackstagePaletteAdditions {} + + interface PaletteOptions extends Partial {} } declare module '@mui/material/styles' { interface Theme extends BackstageThemeAdditions {} + + interface ThemeOptions extends Partial {} } declare module '@mui/private-theming/defaultTheme' { diff --git a/plugins/adr-backend/CHANGELOG.md b/plugins/adr-backend/CHANGELOG.md index 318a5ab2f2..c63b8bd687 100644 --- a/plugins/adr-backend/CHANGELOG.md +++ b/plugins/adr-backend/CHANGELOG.md @@ -1,5 +1,66 @@ # @backstage/plugin-adr-backend +## 0.3.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.0 + - @backstage/backend-plugin-api@0.5.5-next.0 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/integration@1.5.1 + - @backstage/plugin-adr-common@0.2.11 + - @backstage/plugin-search-common@1.2.5 + +## 0.3.5 + +### Patch Changes + +- a8805a9a4f25: Added support for the [new backend system](https://backstage.io/docs/backend-system/) +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/backend-common@0.19.1 + - @backstage/plugin-adr-common@0.2.11 + - @backstage/backend-plugin-api@0.5.4 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1 + - @backstage/plugin-search-common@1.2.5 + +## 0.3.5-next.1 + +### Patch Changes + +- a8805a9a4f25: Added support for the [new backend system](https://backstage.io/docs/backend-system/) +- Updated dependencies + - @backstage/plugin-adr-common@0.2.11-next.1 + - @backstage/backend-common@0.19.1-next.0 + - @backstage/backend-plugin-api@0.5.4-next.0 + - @backstage/catalog-client@1.4.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1-next.0 + - @backstage/integration@1.5.1-next.0 + - @backstage/plugin-search-common@1.2.5-next.0 + +## 0.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/backend-common@0.19.1-next.0 + - @backstage/catalog-client@1.4.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1-next.0 + - @backstage/plugin-adr-common@0.2.11-next.0 + - @backstage/plugin-search-common@1.2.5-next.0 + ## 0.3.4 ### Patch Changes diff --git a/plugins/adr-backend/README.md b/plugins/adr-backend/README.md index 4ffa9c63eb..6ce2f13ebf 100644 --- a/plugins/adr-backend/README.md +++ b/plugins/adr-backend/README.md @@ -58,6 +58,24 @@ async function main() { 4. Now run `yarn start-backend` from the repo root +### New Backend System + +The ADR backend plugin has support for the [new backend system](https://backstage.io/docs/backend-system/), here's how you can set that up: + +In your `packages/backend/src/index.ts` make the following changes: + +```diff + ++ import { adrPlugin } from '@backstage/plugin-adr-backend'; + const backend = createBackend(); + ++ backend.add(adrPlugin()); + +// ... other feature additions + + backend.start(); +``` + ## Indexing ADR documents for search Before you are able to start indexing ADR documents to search, you need to go through the [search getting started guide](https://backstage.io/docs/features/search/getting-started). diff --git a/plugins/adr-backend/api-report.md b/plugins/adr-backend/api-report.md index d5645ad3a7..2fd22ef9cd 100644 --- a/plugins/adr-backend/api-report.md +++ b/plugins/adr-backend/api-report.md @@ -7,6 +7,7 @@ import { AdrDocument } from '@backstage/plugin-adr-common'; import { AdrFilePathFilterFn } from '@backstage/plugin-adr-common'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { CacheClient } from '@backstage/backend-common'; import { CatalogApi } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; @@ -43,6 +44,9 @@ export type AdrParserContext = { path: string; }; +// @public +export const adrPlugin: () => BackendFeature; + // @public (undocumented) export type AdrRouterOptions = { reader: UrlReader; diff --git a/plugins/adr-backend/package.json b/plugins/adr-backend/package.json index 4bd7aa3956..e7f390b790 100644 --- a/plugins/adr-backend/package.json +++ b/plugins/adr-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-adr-backend", - "version": "0.3.4", + "version": "0.3.6-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,6 +29,7 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", diff --git a/plugins/adr-backend/src/index.ts b/plugins/adr-backend/src/index.ts index 89c88ab3e7..eeaf6b4cd0 100644 --- a/plugins/adr-backend/src/index.ts +++ b/plugins/adr-backend/src/index.ts @@ -22,3 +22,4 @@ export * from './search'; export * from './service'; +export { adrPlugin } from './plugin'; diff --git a/plugins/adr-backend/src/plugin.ts b/plugins/adr-backend/src/plugin.ts new file mode 100644 index 0000000000..f926ade6be --- /dev/null +++ b/plugins/adr-backend/src/plugin.ts @@ -0,0 +1,49 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { loggerToWinstonLogger } from '@backstage/backend-common'; +import { + coreServices, + createBackendPlugin, +} from '@backstage/backend-plugin-api'; +import { createRouter } from './service/router'; + +/** + * ADR backend plugin + * + * @public + */ +export const adrPlugin = createBackendPlugin({ + pluginId: 'adr', + register(env) { + env.registerInit({ + deps: { + logger: coreServices.logger, + reader: coreServices.urlReader, + cache: coreServices.cache, + httpRouter: coreServices.httpRouter, + }, + async init({ httpRouter, logger, reader, cache }) { + httpRouter.use( + await createRouter({ + logger: loggerToWinstonLogger(logger), + reader, + cacheClient: cache, + }), + ); + }, + }); + }, +}); diff --git a/plugins/adr-common/CHANGELOG.md b/plugins/adr-common/CHANGELOG.md index b33ecdb3bb..9b8d5dbf74 100644 --- a/plugins/adr-common/CHANGELOG.md +++ b/plugins/adr-common/CHANGELOG.md @@ -1,5 +1,34 @@ # @backstage/plugin-adr-common +## 0.2.11 + +### Patch Changes + +- 2b4513abb784: fixed error with date parsing. +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/integration@1.5.1 + - @backstage/plugin-search-common@1.2.5 + +## 0.2.11-next.1 + +### Patch Changes + +- 2b4513abb784: fixed error with date parsing. +- Updated dependencies + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/integration@1.5.1-next.0 + - @backstage/plugin-search-common@1.2.5-next.0 + +## 0.2.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/integration@1.5.1-next.0 + - @backstage/plugin-search-common@1.2.5-next.0 + ## 0.2.10 ### Patch Changes diff --git a/plugins/adr-common/package.json b/plugins/adr-common/package.json index 0764eac854..2def65cf55 100644 --- a/plugins/adr-common/package.json +++ b/plugins/adr-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-adr-common", "description": "Common functionalities for the adr plugin", - "version": "0.2.10", + "version": "0.2.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,7 +32,8 @@ "@backstage/catalog-model": "workspace:^", "@backstage/integration": "workspace:^", "@backstage/plugin-search-common": "workspace:^", - "front-matter": "^4.0.2" + "front-matter": "^4.0.2", + "luxon": "^3.0.0" }, "devDependencies": { "@backstage/cli": "workspace:^" diff --git a/plugins/adr-common/src/index.ts b/plugins/adr-common/src/index.ts index 194f4899d1..97c82b2047 100644 --- a/plugins/adr-common/src/index.ts +++ b/plugins/adr-common/src/index.ts @@ -21,6 +21,7 @@ import { Entity, getEntitySourceLocation } from '@backstage/catalog-model'; import { IndexableDocument } from '@backstage/plugin-search-common'; import { ScmIntegrationRegistry } from '@backstage/integration'; import frontMatter from 'front-matter'; +import { DateTime } from 'luxon'; /** * ADR plugin annotation. @@ -135,10 +136,12 @@ export const parseMadrWithFrontmatter = (content: string): ParsedMadr => { const parsed = frontMatter>(content); const status = parsed.attributes.status; const date = parsed.attributes.date; + const luxdate = DateTime.fromJSDate(new Date(`${date}`)); + const formattedDate = luxdate.toISODate(); return { content: parsed.body, status: status ? String(status) : undefined, - date: date ? String(date) : undefined, + date: date ? String(formattedDate) : undefined, attributes: parsed.attributes, }; }; diff --git a/plugins/adr/CHANGELOG.md b/plugins/adr/CHANGELOG.md index 9b1bad0d3a..fbbf4c13de 100644 --- a/plugins/adr/CHANGELOG.md +++ b/plugins/adr/CHANGELOG.md @@ -1,5 +1,79 @@ # @backstage/plugin-adr +## 0.6.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/plugin-search-react@1.6.4-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/integration-react@1.1.16-next.0 + - @backstage/theme@0.4.1 + - @backstage/plugin-adr-common@0.2.11 + - @backstage/plugin-search-common@1.2.5 + +## 0.6.3 + +### Patch Changes + +- 2b4513abb784: fixed error with date parsing. +- Updated dependencies + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.0 + - @backstage/core-components@0.13.3 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-adr-common@0.2.11 + - @backstage/catalog-model@1.4.1 + - @backstage/integration-react@1.1.15 + - @backstage/plugin-search-common@1.2.5 + - @backstage/plugin-search-react@1.6.3 + +## 0.6.3-next.2 + +### Patch Changes + +- 2b4513abb784: fixed error with date parsing. +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.0-next.2 + - @backstage/theme@0.4.1-next.1 + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-components@0.13.3-next.2 + - @backstage/plugin-adr-common@0.2.11-next.1 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/integration-react@1.1.15-next.2 + - @backstage/plugin-search-common@1.2.5-next.0 + - @backstage/plugin-search-react@1.6.3-next.2 + +## 0.6.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.0 + - @backstage/core-components@0.13.3-next.1 + - @backstage/core-plugin-api@1.5.3-next.0 + - @backstage/integration-react@1.1.15-next.1 + - @backstage/plugin-catalog-react@1.7.1-next.1 + - @backstage/plugin-search-react@1.6.3-next.1 + +## 0.6.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/core-plugin-api@1.5.2 + - @backstage/integration-react@1.1.15-next.0 + - @backstage/theme@0.4.0 + - @backstage/plugin-adr-common@0.2.11-next.0 + - @backstage/plugin-catalog-react@1.7.1-next.0 + - @backstage/plugin-search-common@1.2.5-next.0 + - @backstage/plugin-search-react@1.6.3-next.0 + ## 0.6.2 ### Patch Changes diff --git a/plugins/adr/package.json b/plugins/adr/package.json index 4c4458e64f..b032ccad7b 100644 --- a/plugins/adr/package.json +++ b/plugins/adr/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-adr", - "version": "0.6.2", + "version": "0.6.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -12,6 +12,12 @@ "backstage": { "role": "frontend-plugin" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/adr" + }, "scripts": { "start": "backstage-cli package start", "build": "backstage-cli package build", diff --git a/plugins/adr/src/components/EntityAdrContent/EntityAdrContent.tsx b/plugins/adr/src/components/EntityAdrContent/EntityAdrContent.tsx index da575d884f..32e353eba9 100644 --- a/plugins/adr/src/components/EntityAdrContent/EntityAdrContent.tsx +++ b/plugins/adr/src/components/EntityAdrContent/EntityAdrContent.tsx @@ -68,9 +68,10 @@ const useStyles = makeStyles((theme: Theme) => ({ color: theme.palette.grey[700], marginBottom: theme.spacing(1), }, - adrChip: { - position: 'absolute', - right: 0, + adrBox: { + display: 'flex', + justifyContent: 'space-between', + marginTop: '10px', }, })); @@ -131,7 +132,7 @@ const AdrListContainer = (props: { style: { whiteSpace: 'normal' }, }} secondary={ - + {adr.date} {adr.status && ( )} diff --git a/plugins/airbrake-backend/CHANGELOG.md b/plugins/airbrake-backend/CHANGELOG.md index 4c5ca9f4b4..c0f695f372 100644 --- a/plugins/airbrake-backend/CHANGELOG.md +++ b/plugins/airbrake-backend/CHANGELOG.md @@ -1,5 +1,42 @@ # @backstage/plugin-airbrake-backend +## 0.2.21-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.0 + - @backstage/backend-plugin-api@0.5.5-next.0 + - @backstage/config@1.0.8 + +## 0.2.20 + +### Patch Changes + +- a95bb64e461a: Added support for the [new backend system](https://backstage.io/docs/backend-system/) +- Updated dependencies + - @backstage/backend-common@0.19.1 + - @backstage/backend-plugin-api@0.5.4 + - @backstage/config@1.0.8 + +## 0.2.20-next.1 + +### Patch Changes + +- a95bb64e461a: Added support for the [new backend system](https://backstage.io/docs/backend-system/) +- Updated dependencies + - @backstage/backend-common@0.19.1-next.0 + - @backstage/backend-plugin-api@0.5.4-next.0 + - @backstage/config@1.0.8 + +## 0.2.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.1-next.0 + - @backstage/config@1.0.8 + ## 0.2.19 ### Patch Changes diff --git a/plugins/airbrake-backend/api-report.md b/plugins/airbrake-backend/api-report.md index 3ad74e2506..c410245e31 100644 --- a/plugins/airbrake-backend/api-report.md +++ b/plugins/airbrake-backend/api-report.md @@ -3,6 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import express from 'express'; import { Logger } from 'winston'; @@ -12,6 +13,9 @@ export interface AirbrakeConfig { apiKey: string; } +// @public +export const airbrakePlugin: () => BackendFeature; + // @public export function createRouter(options: RouterOptions): Promise; diff --git a/plugins/airbrake-backend/package.json b/plugins/airbrake-backend/package.json index 3159e7609e..226126531b 100644 --- a/plugins/airbrake-backend/package.json +++ b/plugins/airbrake-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-airbrake-backend", - "version": "0.2.19", + "version": "0.2.21-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -12,6 +12,12 @@ "backstage": { "role": "backend-plugin" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/airbrake-backend" + }, "scripts": { "start": "backstage-cli package start", "build": "backstage-cli package build", @@ -23,6 +29,7 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/config": "workspace:^", "@types/express": "*", "express": "^4.17.1", diff --git a/plugins/airbrake-backend/src/index.ts b/plugins/airbrake-backend/src/index.ts index f4ec6008c2..5905a7e0e5 100644 --- a/plugins/airbrake-backend/src/index.ts +++ b/plugins/airbrake-backend/src/index.ts @@ -22,3 +22,4 @@ export * from './service/router'; export * from './config'; +export { airbrakePlugin } from './plugin'; diff --git a/plugins/airbrake-backend/src/plugin.ts b/plugins/airbrake-backend/src/plugin.ts new file mode 100644 index 0000000000..79850c1272 --- /dev/null +++ b/plugins/airbrake-backend/src/plugin.ts @@ -0,0 +1,49 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { loggerToWinstonLogger } from '@backstage/backend-common'; +import { + coreServices, + createBackendPlugin, +} from '@backstage/backend-plugin-api'; +import { createRouter } from './service/router'; +import { extractAirbrakeConfig } from './config'; + +/** + * The Airbrake Backend plugin. + * + * @public + */ +export const airbrakePlugin = createBackendPlugin({ + pluginId: 'airbrake', + register(env) { + env.registerInit({ + deps: { + config: coreServices.config, + logger: coreServices.logger, + httpRouter: coreServices.httpRouter, + }, + async init({ logger, httpRouter, config }) { + httpRouter.use( + await createRouter({ + airbrakeConfig: extractAirbrakeConfig(config), + logger: loggerToWinstonLogger(logger), + }), + ); + }, + }); + }, +}); diff --git a/plugins/airbrake/CHANGELOG.md b/plugins/airbrake/CHANGELOG.md index 7be3d724cd..e5b52f1932 100644 --- a/plugins/airbrake/CHANGELOG.md +++ b/plugins/airbrake/CHANGELOG.md @@ -1,5 +1,71 @@ # @backstage/plugin-airbrake +## 0.3.21-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/dev-utils@1.0.18-next.0 + - @backstage/test-utils@1.4.2-next.0 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## 0.3.20 + +### Patch Changes + +- 7b6033587650: Added documentation for the [new backend system](https://backstage.io/docs/backend-system/) +- Updated dependencies + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.0 + - @backstage/core-components@0.13.3 + - @backstage/test-utils@1.4.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/dev-utils@1.0.17 + +## 0.3.20-next.2 + +### Patch Changes + +- 7b6033587650: Added documentation for the [new backend system](https://backstage.io/docs/backend-system/) +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.0-next.2 + - @backstage/theme@0.4.1-next.1 + - @backstage/test-utils@1.4.1-next.2 + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-components@0.13.3-next.2 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/dev-utils@1.0.17-next.2 + +## 0.3.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.0 + - @backstage/core-components@0.13.3-next.1 + - @backstage/core-plugin-api@1.5.3-next.0 + - @backstage/dev-utils@1.0.17-next.1 + - @backstage/test-utils@1.4.1-next.1 + - @backstage/plugin-catalog-react@1.7.1-next.1 + +## 0.3.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/core-plugin-api@1.5.2 + - @backstage/dev-utils@1.0.17-next.0 + - @backstage/test-utils@1.4.1-next.0 + - @backstage/theme@0.4.0 + - @backstage/plugin-catalog-react@1.7.1-next.0 + ## 0.3.19 ### Patch Changes diff --git a/plugins/airbrake/README.md b/plugins/airbrake/README.md index f37ab08b75..573910a1cf 100644 --- a/plugins/airbrake/README.md +++ b/plugins/airbrake/README.md @@ -116,6 +116,21 @@ The Airbrake plugin provides connectivity between Backstage and Airbrake (https: airbrake.io/project-id: '123456' ``` +#### New Backend System + +The Airbrake backend plugin has support for the [new backend system](https://backstage.io/docs/backend-system/), here's how you can set that up: + +In your `packages/backend/src/index.ts` make the following changes: + +```diff + import { createBackend } from '@backstage/backend-defaults'; ++ import { airbrakePlugin } from '@backstage/plugin-airbrake-backend'; + const backend = createBackend(); + // ... other feature additions ++ backend.add(airbrakePlugin()); + backend.start(); +``` + ## Local Development Start this plugin in standalone mode by running `yarn start` inside the plugin directory. This method of serving the plugin provides quicker diff --git a/plugins/airbrake/package.json b/plugins/airbrake/package.json index f284d4be8c..8a156c775d 100644 --- a/plugins/airbrake/package.json +++ b/plugins/airbrake/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-airbrake", - "version": "0.3.19", + "version": "0.3.21-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -12,6 +12,12 @@ "backstage": { "role": "frontend-plugin" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/airbrake" + }, "scripts": { "build": "backstage-cli package build", "start": "backstage-cli package start", diff --git a/plugins/allure/CHANGELOG.md b/plugins/allure/CHANGELOG.md index 978c0780e1..caa81f04cf 100644 --- a/plugins/allure/CHANGELOG.md +++ b/plugins/allure/CHANGELOG.md @@ -1,5 +1,59 @@ # @backstage/plugin-allure +## 0.1.37-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## 0.1.36 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.0 + - @backstage/core-components@0.13.3 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + +## 0.1.36-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.0-next.2 + - @backstage/theme@0.4.1-next.1 + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-components@0.13.3-next.2 + - @backstage/catalog-model@1.4.1-next.0 + +## 0.1.36-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.0 + - @backstage/core-components@0.13.3-next.1 + - @backstage/core-plugin-api@1.5.3-next.0 + - @backstage/plugin-catalog-react@1.7.1-next.1 + +## 0.1.36-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/core-plugin-api@1.5.2 + - @backstage/theme@0.4.0 + - @backstage/plugin-catalog-react@1.7.1-next.0 + ## 0.1.35 ### Patch Changes diff --git a/plugins/allure/package.json b/plugins/allure/package.json index 9de1ed9d0f..7a65d9f707 100644 --- a/plugins/allure/package.json +++ b/plugins/allure/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-allure", "description": "A Backstage plugin that integrates with Allure", - "version": "0.1.35", + "version": "0.1.37-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -13,6 +13,12 @@ "backstage": { "role": "frontend-plugin" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/allure" + }, "scripts": { "build": "backstage-cli package build", "start": "backstage-cli package start", diff --git a/plugins/analytics-module-ga/CHANGELOG.md b/plugins/analytics-module-ga/CHANGELOG.md index 2980f4e984..d7c09cddb1 100644 --- a/plugins/analytics-module-ga/CHANGELOG.md +++ b/plugins/analytics-module-ga/CHANGELOG.md @@ -1,5 +1,55 @@ # @backstage/plugin-analytics-module-ga +## 0.1.32-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + +## 0.1.31 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1 + - @backstage/core-components@0.13.3 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + +## 0.1.31-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.1 + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-components@0.13.3-next.2 + - @backstage/config@1.0.8 + +## 0.1.31-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.0 + - @backstage/core-components@0.13.3-next.1 + - @backstage/core-plugin-api@1.5.3-next.0 + - @backstage/config@1.0.8 + +## 0.1.31-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.3-next.0 + - @backstage/config@1.0.8 + - @backstage/core-plugin-api@1.5.2 + - @backstage/theme@0.4.0 + ## 0.1.30 ### Patch Changes diff --git a/plugins/analytics-module-ga/package.json b/plugins/analytics-module-ga/package.json index 1bdd7fee2e..8fb8d42a9e 100644 --- a/plugins/analytics-module-ga/package.json +++ b/plugins/analytics-module-ga/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-analytics-module-ga", - "version": "0.1.30", + "version": "0.1.32-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -12,6 +12,12 @@ "backstage": { "role": "frontend-plugin-module" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/analytics-module-ga" + }, "scripts": { "build": "backstage-cli package build", "start": "backstage-cli package start", diff --git a/plugins/analytics-module-ga4/CHANGELOG.md b/plugins/analytics-module-ga4/CHANGELOG.md index 4b25f29010..9715015579 100644 --- a/plugins/analytics-module-ga4/CHANGELOG.md +++ b/plugins/analytics-module-ga4/CHANGELOG.md @@ -1,5 +1,55 @@ # @backstage/plugin-analytics-module-ga4 +## 0.1.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + +## 0.1.2 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1 + - @backstage/core-components@0.13.3 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + +## 0.1.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.1 + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-components@0.13.3-next.2 + - @backstage/config@1.0.8 + +## 0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.0 + - @backstage/core-components@0.13.3-next.1 + - @backstage/core-plugin-api@1.5.3-next.0 + - @backstage/config@1.0.8 + +## 0.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.3-next.0 + - @backstage/config@1.0.8 + - @backstage/core-plugin-api@1.5.2 + - @backstage/theme@0.4.0 + ## 0.1.1 ### Patch Changes diff --git a/plugins/analytics-module-ga4/package.json b/plugins/analytics-module-ga4/package.json index 4d6b603db0..09e7240142 100644 --- a/plugins/analytics-module-ga4/package.json +++ b/plugins/analytics-module-ga4/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-analytics-module-ga4", - "version": "0.1.1", + "version": "0.1.3-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -12,6 +12,12 @@ "backstage": { "role": "frontend-plugin-module" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/plugins/analytics-module-ga4" + }, "scripts": { "build": "backstage-cli package build", "start": "backstage-cli package start", diff --git a/plugins/analytics-module-newrelic-browser/.eslintrc.js b/plugins/analytics-module-newrelic-browser/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/analytics-module-newrelic-browser/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/analytics-module-newrelic-browser/CHANGELOG.md b/plugins/analytics-module-newrelic-browser/CHANGELOG.md new file mode 100644 index 0000000000..beea288700 --- /dev/null +++ b/plugins/analytics-module-newrelic-browser/CHANGELOG.md @@ -0,0 +1,11 @@ +# @backstage/plugin-analytics-module-newrelic-browser + +## 0.0.1-next.0 + +### Patch Changes + +- ec7357258853: Introduced the New Relic Browser analytics module. Check out the plugins [README.md](https://github.com/backstage/backstage/tree/master/plugins/analytics-module-newrelic-browser) for more details! +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 diff --git a/plugins/analytics-module-newrelic-browser/README.md b/plugins/analytics-module-newrelic-browser/README.md new file mode 100644 index 0000000000..2fe3956d2d --- /dev/null +++ b/plugins/analytics-module-newrelic-browser/README.md @@ -0,0 +1,135 @@ +# Analytics Module: New Relic Browser + +This plugin provides an opinionated implementation of the Backstage Analytics API for New Relic Browser. Once installed and configured, analytics events will be sent to New Relic as your users navigate and use your Backstage instance. + +This plugin contains no other functionality. + +## Installation + +1. Install the plugin package in your Backstage app: + +```sh +# From your Backstage root directory +yarn add --cwd packages/app @backstage/plugin-analytics-module-newrelic-browser +``` + +2. Wire up the API implementation to your App: + +```tsx +// packages/app/src/apis.ts +import { + analyticsApiRef, + configApiRef, + identityApiRef, +} from '@backstage/core-plugin-api'; +import { NewRelicBrowser } from '@backstage/plugin-analytics-module-newrelic-browser'; + +export const apis: AnyApiFactory[] = [ + // Instantiate and register the New Relic Browser API Implementation. + createApiFactory({ + api: analyticsApiRef, + deps: { configApi: configApiRef, identityApi: identityApiRef }, + factory: ({ configApi, identityApi }) => + NewRelicBrowser.fromConfig(configApi, { + identityApi, + }), + }), +]; +``` + +3. Configure the plugin in your `app-config.yaml`: + +The following is the minimum configuration required to start sending analytics +events to New Relic Browser. You find this information when creating a new application +in New Relic Browser using the Copy/Paste method. + +```yaml +# app-config.yaml +app: + analytics: + newRelic: + endpoint: 'bam.nr-data.net', + accountId: '1234567' + applicationId: '987654321' + licenseKey: 'NRJS-12a3456bc78de9123f4' +``` + +> Note: Depending on New Relic's data center you are using you'll want to change the `endpoint` to `bam.eu01.nr-data.net` for the EU data center. Refer to [this document](https://docs.newrelic.com/docs/new-relic-solutions/get-started/networks/#data-ingest) for available endpoints. + +## Configuration + +By default the distributed tracing and cookies features are disabled. You can enable them by adding the following to your `app-config.yaml`: + +```yaml +# app-config.yaml +app: + analytics: + newRelic: + ... + distributedTracing: true + cookiesEnabled: true +``` + +### User IDs + +This plugin supports sending user context to New Relic Browser by providing a User ID. This requires instantiating the `NewRelicBrowser` instance with an `identityApi` instance passed to it, but this is optional. If omitted the plugin will not send user context to New Relic Browser. + +By default the user ID is calculated as a SHA-256 hash of the current user's `userEntityRef` as returned by the `identityApi`. To set a +different value, provide a `userIdTransform` function alongside `identityApi` when you instantiate `NewRelicBrowser`. This function will be passed the `userEntityRef` as an argument and should resolve to the value you wish to set as the user ID. For example: + +```typescript +import { + analyticsApiRef, + configApiRef, + identityApiRef, +} from '@backstage/core-plugin-api'; +import { GoogleAnalytics } from '@backstage/plugin-analytics-module-newrelic-browser'; + +export const apis: AnyApiFactory[] = [ + createApiFactory({ + api: analyticsApiRef, + deps: { configApi: configApiRef, identityApi: identityApiRef }, + factory: ({ configApi, identityApi }) => + NewRelicBrowser.fromConfig(configApi, { + identityApi, + userIdTransform: async (userEntityRef: string): Promise => { + return customHashingFunction(userEntityRef); + }, + }), + }), +]; +``` + +## Development + +If you would like to contribute improvements to this plugin, the easiest way to +make and test changes is to do the following: + +1. Clone the main Backstage monorepo `git clone git@github.com:backstage/backstage.git` +2. Install all dependencies `yarn install` +3. If one does not exist, create an `app-config.local.yaml` file in the root of + the monorepo and add config for this plugin (see below) +4. Enter this plugin's working directory: `cd plugins/analytics-provider-newrelic-browser` +5. Start the plugin in isolation: `yarn start` +6. Navigate to the playground page at `http://localhost:3000/newrelic` +7. Open the web console to see events fire when you navigate or when you + interact with instrumented components. + +Code for the isolated version of the plugin can be found inside the [/dev](./dev) +directory. Changes to the plugin are hot-reloaded. + +#### Recommended Dev Config + +Paste this into your `app-config.local.yaml` while developing this plugin: + +```yaml +app: + analytics: + newRelic: + accountId: '1234567' + applicationId: '987654321' + licenseKey: 'NRJS-12a3456bc78de9123f4' + distributedTracingEnabled: true + cookiesEnabled: true + useEuEndpoint: false +``` diff --git a/plugins/analytics-module-newrelic-browser/api-report.md b/plugins/analytics-module-newrelic-browser/api-report.md new file mode 100644 index 0000000000..7556bae317 --- /dev/null +++ b/plugins/analytics-module-newrelic-browser/api-report.md @@ -0,0 +1,28 @@ +## API Report File for "@backstage/plugin-analytics-module-newrelic-browser" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { AnalyticsApi } from '@backstage/core-plugin-api'; +import { AnalyticsEvent } from '@backstage/core-plugin-api'; +import { Config } from '@backstage/config'; +import { IdentityApi } from '@backstage/core-plugin-api'; + +// @public +export class NewRelicBrowser implements AnalyticsApi { + // (undocumented) + captureEvent(event: AnalyticsEvent): void; + // (undocumented) + static fromConfig( + config: Config, + options: { + identityApi?: IdentityApi; + userIdTransform?: + | 'sha-256' + | ((userEntityRef: string) => Promise); + }, + ): NewRelicBrowser; +} + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/analytics-module-newrelic-browser/config.d.ts b/plugins/analytics-module-newrelic-browser/config.d.ts new file mode 100644 index 0000000000..e653f3af7d --- /dev/null +++ b/plugins/analytics-module-newrelic-browser/config.d.ts @@ -0,0 +1,59 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 interface Config { + app: { + analytics?: { + newRelic: { + /** + * Data ingestion endpoint to use, either bam.eu01.nr-data.net (EU) or bam.nr-data.net (US) + * @visibility frontend + */ + endpoint: 'bam.eu01.nr-data.net' | 'bam.nr-data.net'; + + /** + * New Relic Account ID, e.g. 1234567 + * @visibility frontend + */ + accountId: string; + + /** + * New Relic Application ID, e.g. 987654321 + * @visibility frontend + */ + applicationId: string; + + /** + * New Relic License Key, e.g. NRJS-12a3456bc78de9123f4 + * @visibility frontend + */ + licenseKey: string; + + /** + * Whether to enabled distributed tracing, defaults to false + * @visibility frontend + */ + distributedTracingEnabled: boolean; + + /** + * Whether to enabled tracing of cookies, defaults to false + * @visibility frontend + */ + cookiesEnabled: boolean; + }; + }; + }; +} diff --git a/plugins/analytics-module-newrelic-browser/dev/Playground.tsx b/plugins/analytics-module-newrelic-browser/dev/Playground.tsx new file mode 100644 index 0000000000..cf21aadacc --- /dev/null +++ b/plugins/analytics-module-newrelic-browser/dev/Playground.tsx @@ -0,0 +1,26 @@ +/* + * Copyright 2021 The Backstage Authors + * + * 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 { Link } from '@backstage/core-components'; + +export const Playground = () => { + return ( + <> + Click Here + > + ); +}; diff --git a/plugins/analytics-module-newrelic-browser/dev/index.tsx b/plugins/analytics-module-newrelic-browser/dev/index.tsx new file mode 100644 index 0000000000..15caacfe61 --- /dev/null +++ b/plugins/analytics-module-newrelic-browser/dev/index.tsx @@ -0,0 +1,40 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { createDevApp } from '@backstage/dev-utils'; +import { + analyticsApiRef, + configApiRef, + identityApiRef, +} from '@backstage/core-plugin-api'; +import { Playground } from './Playground'; +import { NewRelicBrowser } from '../src'; + +createDevApp() + .registerApi({ + api: analyticsApiRef, + deps: { configApi: configApiRef, identityApi: identityApiRef }, + factory: ({ configApi, identityApi }) => + NewRelicBrowser.fromConfig(configApi, { + identityApi, + }), + }) + .addPage({ + path: '/newrelic', + title: 'New Relic Playground', + element: , + }) + .render(); diff --git a/plugins/analytics-module-newrelic-browser/package.json b/plugins/analytics-module-newrelic-browser/package.json new file mode 100644 index 0000000000..677d3b8e16 --- /dev/null +++ b/plugins/analytics-module-newrelic-browser/package.json @@ -0,0 +1,50 @@ +{ + "name": "@backstage/plugin-analytics-module-newrelic-browser", + "version": "0.0.1-next.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "frontend-plugin-module" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/config": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@newrelic/browser-agent": "^1.236.0", + "react-use": "^17.2.4" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^12.1.3", + "@testing-library/user-event": "^14.0.0", + "@types/node": "*", + "msw": "^1.0.0" + }, + "files": [ + "dist", + "config.d.ts" + ], + "configSchema": "config.d.ts" +} diff --git a/plugins/analytics-module-newrelic-browser/src/apis/implementations/AnalyticsApi/NewRelicBrowser.ts b/plugins/analytics-module-newrelic-browser/src/apis/implementations/AnalyticsApi/NewRelicBrowser.ts new file mode 100644 index 0000000000..f2f35b5765 --- /dev/null +++ b/plugins/analytics-module-newrelic-browser/src/apis/implementations/AnalyticsApi/NewRelicBrowser.ts @@ -0,0 +1,176 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { Config } from '@backstage/config'; +import { + AnalyticsApi, + IdentityApi, + AnalyticsEvent, +} from '@backstage/core-plugin-api'; +import { BrowserAgent } from '@newrelic/browser-agent/loaders/browser-agent'; +import type { setAPI } from '@newrelic/browser-agent/loaders/api/api'; + +type NewRelicAPI = ReturnType; + +type NewRelicBrowserOptions = { + endpoint: string; + accountId: string; + applicationId: string; + licenseKey: string; + distributedTracingEnabled: boolean; + cookiesEnabled: boolean; +}; + +/** + * New Relic Browser API provider for the Backstage Analytics API. + * @public + */ +export class NewRelicBrowser implements AnalyticsApi { + private readonly agent: NewRelicAPI; + + private constructor( + options: NewRelicBrowserOptions, + identityApi?: IdentityApi, + userIdTransform?: 'sha-256' | ((userEntityRef: string) => Promise), + ) { + // Configure the New Relic Browser agent + const agentOptions = { + init: { + distributed_tracing: { + enabled: options.distributedTracingEnabled, + }, + privacy: { + cookies_enabled: options.cookiesEnabled, + }, + ajax: { + deny_list: [options.endpoint], + }, + }, + info: { + beacon: options.endpoint, + errorBeacon: options.endpoint, + licenseKey: options.licenseKey, + applicationID: options.applicationId, + sa: 1, + }, + loader_config: { + accountID: options.accountId, + trustKey: options.accountId, + agentID: options.applicationId, + licenseKey: options.licenseKey, + applicationID: options.applicationId, + }, + }; + + // Initialize the agent + this.agent = new BrowserAgent(agentOptions) as unknown as NewRelicAPI; + + // Check if identity has been provided + if (identityApi) { + identityApi.getBackstageIdentity().then(identity => { + if (typeof userIdTransform === 'function') { + userIdTransform(identity.userEntityRef).then(userId => { + this.agent.setUserId(userId); + }); + } else { + this.hash(identity.userEntityRef).then(userId => { + this.agent.setUserId(userId); + }); + } + }); + } + } + + static fromConfig( + config: Config, + options: { + identityApi?: IdentityApi; + userIdTransform?: + | 'sha-256' + | ((userEntityRef: string) => Promise); + }, + ) { + const newRelicBrowserConfig = config.getConfig('app.analytics.newRelic'); + const browserOptions: NewRelicBrowserOptions = { + endpoint: newRelicBrowserConfig.getString('endpoint'), + accountId: newRelicBrowserConfig.getString('accountId'), + applicationId: newRelicBrowserConfig.getString('applicationId'), + licenseKey: newRelicBrowserConfig.getString('licenseKey'), + distributedTracingEnabled: + newRelicBrowserConfig.getOptionalBoolean('distributedTracingEnabled') ?? + false, + cookiesEnabled: + newRelicBrowserConfig.getOptionalBoolean('cookiesEnabled') ?? false, + }; + return new NewRelicBrowser( + browserOptions, + options.identityApi, + options.userIdTransform, + ); + } + + captureEvent(event: AnalyticsEvent) { + const { context, action, subject, value, attributes } = event; + if (action === 'navigate' && context.extension === 'App') { + const interaction = this.agent.interaction(); + interaction.setName(subject); + if (value) { + interaction.setAttribute('value', value); + } + Object.keys(context).forEach(key => { + if (context[key]) { + interaction.setAttribute(`context.${key}`, context[key]); + } + }); + if (attributes) { + Object.keys(attributes).forEach(key => { + interaction.setAttribute(`attributes.${key}`, attributes[key]); + }); + } + } else { + const customAttributes: { + [x: string]: string | number | boolean | undefined; + } = {}; + if (value) { + customAttributes.value = value; + } + Object.keys(context).forEach(key => { + if (context[key]) { + customAttributes[`context.${key}`] = context[key]; + } + }); + if (attributes) { + Object.keys(attributes).forEach(key => { + customAttributes[`attributes.${key}`] = attributes[key]; + }); + } + + this.agent.addPageAction(action, customAttributes); + } + } + + /** + * Simple hash function; relies on web cryptography + the sha-256 algorithm. + * @param value value to be hashed + */ + private async hash(value: string): Promise { + const digest = await window.crypto.subtle.digest( + 'sha-256', + new TextEncoder().encode(value), + ); + const hashArray = Array.from(new Uint8Array(digest)); + return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); + } +} diff --git a/plugins/analytics-module-newrelic-browser/src/apis/implementations/AnalyticsApi/index.ts b/plugins/analytics-module-newrelic-browser/src/apis/implementations/AnalyticsApi/index.ts new file mode 100644 index 0000000000..6300381cfd --- /dev/null +++ b/plugins/analytics-module-newrelic-browser/src/apis/implementations/AnalyticsApi/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { NewRelicBrowser } from './NewRelicBrowser'; diff --git a/plugins/analytics-module-newrelic-browser/src/index.ts b/plugins/analytics-module-newrelic-browser/src/index.ts new file mode 100644 index 0000000000..0adf114679 --- /dev/null +++ b/plugins/analytics-module-newrelic-browser/src/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 * from './apis/implementations/AnalyticsApi'; diff --git a/plugins/analytics-module-newrelic-browser/src/setupTests.ts b/plugins/analytics-module-newrelic-browser/src/setupTests.ts new file mode 100644 index 0000000000..865308e634 --- /dev/null +++ b/plugins/analytics-module-newrelic-browser/src/setupTests.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 '@testing-library/jest-dom'; diff --git a/plugins/apache-airflow/CHANGELOG.md b/plugins/apache-airflow/CHANGELOG.md index 3200419518..ef1d34dc23 100644 --- a/plugins/apache-airflow/CHANGELOG.md +++ b/plugins/apache-airflow/CHANGELOG.md @@ -1,5 +1,45 @@ # @backstage/plugin-apache-airflow +## 0.2.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + +## 0.2.13 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.3 + - @backstage/core-plugin-api@1.5.3 + +## 0.2.13-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-components@0.13.3-next.2 + +## 0.2.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.3-next.1 + - @backstage/core-plugin-api@1.5.3-next.0 + +## 0.2.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.3-next.0 + - @backstage/core-plugin-api@1.5.2 + ## 0.2.12 ### Patch Changes diff --git a/plugins/apache-airflow/package.json b/plugins/apache-airflow/package.json index f8f4acdca7..60afaa9a24 100644 --- a/plugins/apache-airflow/package.json +++ b/plugins/apache-airflow/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-apache-airflow", - "version": "0.2.12", + "version": "0.2.14-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -12,6 +12,12 @@ "backstage": { "role": "frontend-plugin" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/apache-airflow" + }, "scripts": { "build": "backstage-cli package build", "start": "backstage-cli package start", diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 25e4b3b314..68923046f5 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,11 +1,70 @@ # @backstage/plugin-api-docs +## 0.9.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/plugin-catalog@1.12.1-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## 0.9.6 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.0 + - @backstage/plugin-catalog@1.12.0 + - @backstage/core-components@0.13.3 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + +## 0.9.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.0-next.2 + - @backstage/plugin-catalog@1.12.0-next.2 + - @backstage/theme@0.4.1-next.1 + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-components@0.13.3-next.2 + - @backstage/catalog-model@1.4.1-next.0 + +## 0.9.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.0 + - @backstage/core-components@0.13.3-next.1 + - @backstage/plugin-catalog@1.11.3-next.1 + - @backstage/core-plugin-api@1.5.3-next.0 + - @backstage/plugin-catalog-react@1.7.1-next.1 + +## 0.9.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/core-plugin-api@1.5.2 + - @backstage/theme@0.4.0 + - @backstage/plugin-catalog@1.11.3-next.0 + - @backstage/plugin-catalog-react@1.7.1-next.0 + ## 0.9.5 ### Patch Changes - 865267a6b934: Updated dependency `@asyncapi/react-component` to `1.0.0-next.48`. -- 83bdc3a555c1: Replaced usages of `theme.palette.theme.hint` with `theme.palette.text.secondary` as it has been removed in MUI v5 +- 83bdc3a555c1: Replaced usages of `theme.palette.theme.hint` with `theme.palette.text.secondary` as it has been removed in Material UI v5 - af748a148d52: Add support for OpenAPI Specification (OAS) v3.1.0 using swagger-ui v5.0.0. - Updated dependencies - @backstage/core-plugin-api@1.5.2 @@ -44,7 +103,7 @@ ### Patch Changes -- 83bdc3a555c1: Replaced usages of `theme.palette.theme.hint` with `theme.palette.text.secondary` as it has been removed in MUI v5 +- 83bdc3a555c1: Replaced usages of `theme.palette.theme.hint` with `theme.palette.text.secondary` as it has been removed in Material UI v5 - Updated dependencies - @backstage/plugin-catalog@1.11.1-next.1 - @backstage/core-components@0.13.2-next.1 diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 73e4cfa9f5..625fbd3138 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-api-docs", "description": "A Backstage plugin that helps represent API entities in the frontend", - "version": "0.9.5", + "version": "0.9.7-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/apollo-explorer/CHANGELOG.md b/plugins/apollo-explorer/CHANGELOG.md index ab1b94c129..0051c8a9e7 100644 --- a/plugins/apollo-explorer/CHANGELOG.md +++ b/plugins/apollo-explorer/CHANGELOG.md @@ -1,5 +1,50 @@ # @backstage/plugin-apollo-explorer +## 0.1.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + +## 0.1.13 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1 + - @backstage/core-components@0.13.3 + - @backstage/core-plugin-api@1.5.3 + +## 0.1.13-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.1 + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-components@0.13.3-next.2 + +## 0.1.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.0 + - @backstage/core-components@0.13.3-next.1 + - @backstage/core-plugin-api@1.5.3-next.0 + +## 0.1.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.3-next.0 + - @backstage/core-plugin-api@1.5.2 + - @backstage/theme@0.4.0 + ## 0.1.12 ### Patch Changes diff --git a/plugins/apollo-explorer/package.json b/plugins/apollo-explorer/package.json index 5f6d38b3b4..b9eba3278d 100644 --- a/plugins/apollo-explorer/package.json +++ b/plugins/apollo-explorer/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-apollo-explorer", - "version": "0.1.12", + "version": "0.1.14-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -12,6 +12,12 @@ "backstage": { "role": "frontend-plugin" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/apollo-explorer" + }, "scripts": { "start": "backstage-cli package start", "build": "backstage-cli package build", diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index f6ff48ff56..7e048ba4d5 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/plugin-app-backend +## 0.3.48-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.4.0-next.0 + - @backstage/backend-common@0.19.2-next.0 + - @backstage/backend-plugin-api@0.5.5-next.0 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + +## 0.3.47 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.1 + - @backstage/backend-plugin-api@0.5.4 + - @backstage/config@1.0.8 + - @backstage/config-loader@1.3.2 + - @backstage/types@1.1.0 + +## 0.3.47-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.1-next.0 + - @backstage/backend-plugin-api@0.5.4-next.0 + - @backstage/config@1.0.8 + - @backstage/config-loader@1.3.2-next.0 + - @backstage/types@1.1.0 + ## 0.3.46 ### Patch Changes diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 46cf54d080..5574c8aba3 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-app-backend", "description": "A Backstage backend plugin that serves the Backstage frontend app", - "version": "0.3.46", + "version": "0.3.48-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index b43f2bb460..60555ef74f 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,53 @@ # @backstage/plugin-auth-backend +## 0.18.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.0 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-auth-node@0.2.17-next.0 + +## 0.18.5 + +### Patch Changes + +- c27ae5004fc2: Support for Token Endpoint Auth Method for OIDC Provider +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/backend-common@0.19.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-auth-node@0.2.16 + +## 0.18.5-next.1 + +### Patch Changes + +- c27ae5004fc2: Support for Token Endpoint Auth Method for OIDC Provider +- Updated dependencies + - @backstage/config@1.0.8 + +## 0.18.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/backend-common@0.19.1-next.0 + - @backstage/catalog-client@1.4.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-auth-node@0.2.16-next.0 + ## 0.18.4 ### Patch Changes diff --git a/plugins/auth-backend/config.d.ts b/plugins/auth-backend/config.d.ts index c6579e08f8..d059f2ec04 100644 --- a/plugins/auth-backend/config.d.ts +++ b/plugins/auth-backend/config.d.ts @@ -152,6 +152,8 @@ export interface Config { clientSecret: string; callbackUrl?: string; metadataUrl: string; + tokenEndpointAuthMethod?: string; + tokenSignedResponseAlg?: string; scope?: string; prompt?: string; }; diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 436e8ad125..4e7f80afaf 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend", "description": "A Backstage backend plugin that handles authentication", - "version": "0.18.4", + "version": "0.18.6-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend/src/providers/oidc/provider.test.ts b/plugins/auth-backend/src/providers/oidc/provider.test.ts index 2285b60ed0..7161ef1d6b 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.test.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.test.ts @@ -53,6 +53,7 @@ const clientMetadata: Options = { clientId: 'testclientid', clientSecret: 'testclientsecret', metadataUrl: 'https://oidc.test/.well-known/openid-configuration', + tokenEndpointAuthMethod: 'none', tokenSignedResponseAlg: 'none', }; diff --git a/plugins/auth-backend/src/providers/oidc/provider.ts b/plugins/auth-backend/src/providers/oidc/provider.ts index ed59611f21..7638027b01 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.ts @@ -17,6 +17,7 @@ import express from 'express'; import { Client, + ClientAuthMethod, Issuer, Strategy as OidcStrategy, TokenSet, @@ -72,6 +73,7 @@ export type Options = OAuthProviderOptions & { metadataUrl: string; scope?: string; prompt?: string; + tokenEndpointAuthMethod?: ClientAuthMethod; tokenSignedResponseAlg?: string; signInResolver?: SignInResolver; authHandler: AuthHandler; @@ -144,6 +146,8 @@ export class OidcAuthProvider implements OAuthHandlers { client_secret: options.clientSecret, redirect_uris: [options.callbackUrl], response_types: ['code'], + token_endpoint_auth_method: + options.tokenEndpointAuthMethod || 'client_secret_basic', id_token_signed_response_alg: options.tokenSignedResponseAlg || 'RS256', scope: options.scope || '', }); @@ -232,6 +236,9 @@ export const oidc = createAuthProviderIntegration({ customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`; const metadataUrl = envConfig.getString('metadataUrl'); + const tokenEndpointAuthMethod = envConfig.getOptionalString( + 'tokenEndpointAuthMethod', + ) as ClientAuthMethod; const tokenSignedResponseAlg = envConfig.getOptionalString( 'tokenSignedResponseAlg', ); @@ -252,6 +259,7 @@ export const oidc = createAuthProviderIntegration({ clientId, clientSecret, callbackUrl, + tokenEndpointAuthMethod, tokenSignedResponseAlg, metadataUrl, scope, diff --git a/plugins/auth-node/CHANGELOG.md b/plugins/auth-node/CHANGELOG.md index 5652585c61..cb802b9757 100644 --- a/plugins/auth-node/CHANGELOG.md +++ b/plugins/auth-node/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-auth-node +## 0.2.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.0 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## 0.2.16 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/backend-common@0.19.1 + - @backstage/config@1.0.8 + +## 0.2.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/backend-common@0.19.1-next.0 + - @backstage/config@1.0.8 + ## 0.2.15 ### Patch Changes diff --git a/plugins/auth-node/package.json b/plugins/auth-node/package.json index e2ff24ed38..c5846d60b4 100644 --- a/plugins/auth-node/package.json +++ b/plugins/auth-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-node", - "version": "0.2.15", + "version": "0.2.17-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -12,6 +12,12 @@ "backstage": { "role": "node-library" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/auth-node" + }, "scripts": { "build": "backstage-cli package build", "lint": "backstage-cli package lint", diff --git a/plugins/azure-devops-backend/CHANGELOG.md b/plugins/azure-devops-backend/CHANGELOG.md index df08b3a53f..8aa39659fa 100644 --- a/plugins/azure-devops-backend/CHANGELOG.md +++ b/plugins/azure-devops-backend/CHANGELOG.md @@ -1,5 +1,43 @@ # @backstage/plugin-azure-devops-backend +## 0.3.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.0 + - @backstage/backend-plugin-api@0.5.5-next.0 + - @backstage/config@1.0.8 + - @backstage/plugin-azure-devops-common@0.3.0 + +## 0.3.26 + +### Patch Changes + +- ae261e79d256: Added alpha support for the [new backend system](https://backstage.io/docs/backend-system/) +- Updated dependencies + - @backstage/backend-common@0.19.1 + - @backstage/backend-plugin-api@0.5.4 + - @backstage/config@1.0.8 + - @backstage/plugin-azure-devops-common@0.3.0 + +## 0.3.26-next.1 + +### Patch Changes + +- ae261e79d256: Added alpha support for the [new backend system](https://backstage.io/docs/backend-system/) +- Updated dependencies + - @backstage/config@1.0.8 + +## 0.3.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.1-next.0 + - @backstage/config@1.0.8 + - @backstage/plugin-azure-devops-common@0.3.0 + ## 0.3.25 ### Patch Changes diff --git a/plugins/azure-devops-backend/README.md b/plugins/azure-devops-backend/README.md index ba2a847c0a..e892b84dd1 100644 --- a/plugins/azure-devops-backend/README.md +++ b/plugins/azure-devops-backend/README.md @@ -70,6 +70,25 @@ Here's how to get the backend up and running: 4. Now run `yarn start-backend` from the repo root 5. Finally open `http://localhost:7007/api/azure-devops/health` in a browser and it should return `{"status":"ok"}` +#### New Backend System + +The Azure DevOps backend plugin has support for the [new backend system](https://backstage.io/docs/backend-system/), here's how you can set that up: + +In your `packages/backend/src/index.ts` make the following changes: + +```diff + import { createBackend } from '@backstage/backend-defaults'; ++ import { azureDevOpsPlugin } from '@backstage/plugin-azure-devops-backend'; + + const backend = createBackend(); + + // ... other feature additions + ++ backend.add(azureDevOpsPlugin()); + + backend.start(); +``` + ## Links - [Frontend part of the plugin](https://github.com/backstage/backstage/tree/master/plugins/azure-devops) diff --git a/plugins/azure-devops-backend/api-report.md b/plugins/azure-devops-backend/api-report.md index 900b3c3d12..a2a43cd6af 100644 --- a/plugins/azure-devops-backend/api-report.md +++ b/plugins/azure-devops-backend/api-report.md @@ -3,6 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; import { Build } from 'azure-devops-node-api/interfaces/BuildInterfaces'; import { BuildDefinitionReference } from 'azure-devops-node-api/interfaces/BuildInterfaces'; import { BuildRun } from '@backstage/plugin-azure-devops-common'; @@ -94,6 +95,9 @@ export class AzureDevOpsApi { }): Promise; } +// @public +export const azureDevOpsPlugin: () => BackendFeature; + // @public (undocumented) export function createRouter(options: RouterOptions): Promise; @@ -108,6 +112,4 @@ export interface RouterOptions { // (undocumented) reader: UrlReader; } - -// (No @packageDocumentation comment for this package) ``` diff --git a/plugins/azure-devops-backend/package.json b/plugins/azure-devops-backend/package.json index bb64ad84db..706274df6d 100644 --- a/plugins/azure-devops-backend/package.json +++ b/plugins/azure-devops-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops-backend", - "version": "0.3.25", + "version": "0.3.27-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -12,6 +12,12 @@ "backstage": { "role": "backend-plugin" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/azure-devops-backend" + }, "scripts": { "start": "backstage-cli package start", "build": "backstage-cli package build", @@ -23,6 +29,7 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/config": "workspace:^", "@backstage/plugin-azure-devops-common": "workspace:^", "@types/express": "^4.17.6", diff --git a/plugins/azure-devops-backend/src/index.ts b/plugins/azure-devops-backend/src/index.ts index 6977d3580f..c0b1276acf 100644 --- a/plugins/azure-devops-backend/src/index.ts +++ b/plugins/azure-devops-backend/src/index.ts @@ -13,5 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/** + * Azure DevOps backend plugin that contains the API for retrieving builds, pull requests, etc. which is used by the Azure DevOps frontend plugin. + * + * @packageDocumentation + */ + export { AzureDevOpsApi } from './api'; export * from './service/router'; +export { azureDevOpsPlugin } from './plugin'; diff --git a/plugins/azure-devops-backend/src/plugin.ts b/plugins/azure-devops-backend/src/plugin.ts new file mode 100644 index 0000000000..808bdfa248 --- /dev/null +++ b/plugins/azure-devops-backend/src/plugin.ts @@ -0,0 +1,50 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { loggerToWinstonLogger } from '@backstage/backend-common'; +import { + coreServices, + createBackendPlugin, +} from '@backstage/backend-plugin-api'; +import { createRouter } from './service/router'; + +/** + * Azure DevOps backend plugin + * + * @public + */ +export const azureDevOpsPlugin = createBackendPlugin({ + pluginId: 'azure-devops', + register(env) { + env.registerInit({ + deps: { + config: coreServices.config, + logger: coreServices.logger, + reader: coreServices.urlReader, + httpRouter: coreServices.httpRouter, + }, + async init({ config, logger, reader, httpRouter }) { + httpRouter.use( + await createRouter({ + config, + logger: loggerToWinstonLogger(logger), + reader, + }), + ); + }, + }); + }, +}); diff --git a/plugins/azure-devops/CHANGELOG.md b/plugins/azure-devops/CHANGELOG.md index 81202cf771..1b405ad870 100644 --- a/plugins/azure-devops/CHANGELOG.md +++ b/plugins/azure-devops/CHANGELOG.md @@ -1,5 +1,67 @@ # @backstage/plugin-azure-devops +## 0.3.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-azure-devops-common@0.3.0 + +## 0.3.2 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1 + - @backstage/errors@1.2.1 + - @backstage/plugin-catalog-react@1.8.0 + - @backstage/core-components@0.13.3 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/plugin-azure-devops-common@0.3.0 + +## 0.3.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.0-next.2 + - @backstage/theme@0.4.1-next.1 + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-components@0.13.3-next.2 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/errors@1.2.1-next.0 + - @backstage/plugin-azure-devops-common@0.3.0 + +## 0.3.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.0 + - @backstage/core-components@0.13.3-next.1 + - @backstage/core-plugin-api@1.5.3-next.0 + - @backstage/plugin-catalog-react@1.7.1-next.1 + +## 0.3.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/core-components@0.13.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/core-plugin-api@1.5.2 + - @backstage/theme@0.4.0 + - @backstage/plugin-azure-devops-common@0.3.0 + - @backstage/plugin-catalog-react@1.7.1-next.0 + ## 0.3.1 ### Patch Changes diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index eaf20e035e..daaedae825 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops", - "version": "0.3.1", + "version": "0.3.3-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/azure-sites-backend/CHANGELOG.md b/plugins/azure-sites-backend/CHANGELOG.md index 5c4733f964..6aa958a860 100644 --- a/plugins/azure-sites-backend/CHANGELOG.md +++ b/plugins/azure-sites-backend/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-azure-sites-backend +## 0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.0 + - @backstage/config@1.0.8 + - @backstage/plugin-azure-sites-common@0.1.0 + +## 0.1.9 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.1 + - @backstage/config@1.0.8 + - @backstage/plugin-azure-sites-common@0.1.0 + +## 0.1.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.1-next.0 + - @backstage/config@1.0.8 + - @backstage/plugin-azure-sites-common@0.1.0 + ## 0.1.8 ### Patch Changes diff --git a/plugins/azure-sites-backend/package.json b/plugins/azure-sites-backend/package.json index 8f0e89da8e..81d6c434a6 100644 --- a/plugins/azure-sites-backend/package.json +++ b/plugins/azure-sites-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-sites-backend", - "version": "0.1.8", + "version": "0.1.10-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/azure-sites/CHANGELOG.md b/plugins/azure-sites/CHANGELOG.md index cf98bbf413..636c69c76a 100644 --- a/plugins/azure-sites/CHANGELOG.md +++ b/plugins/azure-sites/CHANGELOG.md @@ -1,5 +1,63 @@ # @backstage/plugin-azure-sites +## 0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-azure-sites-common@0.1.0 + +## 0.1.9 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.0 + - @backstage/core-components@0.13.3 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/plugin-azure-sites-common@0.1.0 + +## 0.1.9-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.0-next.2 + - @backstage/theme@0.4.1-next.1 + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-components@0.13.3-next.2 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/plugin-azure-sites-common@0.1.0 + +## 0.1.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.0 + - @backstage/core-components@0.13.3-next.1 + - @backstage/core-plugin-api@1.5.3-next.0 + - @backstage/plugin-catalog-react@1.7.1-next.1 + +## 0.1.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/core-plugin-api@1.5.2 + - @backstage/theme@0.4.0 + - @backstage/plugin-azure-sites-common@0.1.0 + - @backstage/plugin-catalog-react@1.7.1-next.0 + ## 0.1.8 ### Patch Changes diff --git a/plugins/azure-sites/package.json b/plugins/azure-sites/package.json index 210bd84e2d..82b1bd1041 100644 --- a/plugins/azure-sites/package.json +++ b/plugins/azure-sites/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-sites", - "version": "0.1.8", + "version": "0.1.10-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/badges-backend/CHANGELOG.md b/plugins/badges-backend/CHANGELOG.md index b9077e8998..03ba2fa42b 100644 --- a/plugins/badges-backend/CHANGELOG.md +++ b/plugins/badges-backend/CHANGELOG.md @@ -1,5 +1,52 @@ # @backstage/plugin-badges-backend +## 0.2.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.0 + - @backstage/backend-plugin-api@0.5.5-next.0 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-auth-node@0.2.17-next.0 + +## 0.2.2 + +### Patch Changes + +- 2bbdcb4154c3: Added support for the [new backend system](https://backstage.io/docs/backend-system/) +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/backend-common@0.19.1 + - @backstage/backend-plugin-api@0.5.4 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-auth-node@0.2.16 + +## 0.2.2-next.1 + +### Patch Changes + +- 2bbdcb4154c3: Added support for the [new backend system](https://backstage.io/docs/backend-system/) +- Updated dependencies + - @backstage/config@1.0.8 + +## 0.2.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/backend-common@0.19.1-next.0 + - @backstage/catalog-client@1.4.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/plugin-auth-node@0.2.16-next.0 + ## 0.2.1 ### Patch Changes diff --git a/plugins/badges-backend/README.md b/plugins/badges-backend/README.md index ddbb30bc4e..22c3311ff1 100644 --- a/plugins/badges-backend/README.md +++ b/plugins/badges-backend/README.md @@ -76,6 +76,24 @@ const createEnv = makeCreateEnv(config); apiRouter.use(notFoundHandler()); ``` +### New Backend System + +The Badges backend plugin has support for the [new backend system](https://backstage.io/docs/backend-system/), here's how you can set that up: + +In your `packages/backend/src/index.ts` make the following changes: + +```diff + import { createBackend } from '@backstage/backend-defaults'; ++ import { badgesPlugin } from '@backstage/plugin-badges-backend'; + const backend = createBackend(); + + // ... other feature additions + ++ backend.add(badgesPlugin()); + + backend.start(); +``` + ## Badge builder Badges are created by classes implementing the `BadgeBuilder` type. The default diff --git a/plugins/badges-backend/api-report.md b/plugins/badges-backend/api-report.md index 09a02ba44d..4594228543 100644 --- a/plugins/badges-backend/api-report.md +++ b/plugins/badges-backend/api-report.md @@ -3,6 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; import { CatalogApi } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; import { Entity } from '@backstage/catalog-model'; @@ -81,6 +82,9 @@ export type BadgeSpec = { markdown: string; }; +// @public +export const badgesPlugin: () => BackendFeature; + // @public export interface BadgesStore { // (undocumented) diff --git a/plugins/badges-backend/package.json b/plugins/badges-backend/package.json index decaeeaf1b..02f20826f2 100644 --- a/plugins/badges-backend/package.json +++ b/plugins/badges-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-badges-backend", "description": "A Backstage backend plugin that generates README badges for your entities", - "version": "0.2.1", + "version": "0.2.3-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,6 +34,7 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", diff --git a/plugins/badges-backend/src/index.ts b/plugins/badges-backend/src/index.ts index 35a061c516..0c417a7ad6 100644 --- a/plugins/badges-backend/src/index.ts +++ b/plugins/badges-backend/src/index.ts @@ -25,3 +25,4 @@ export * from './lib'; export * from './service/router'; export * from './types'; export * from './database/badgesStore'; +export { badgesPlugin } from './plugin'; diff --git a/plugins/badges-backend/src/plugin.ts b/plugins/badges-backend/src/plugin.ts new file mode 100644 index 0000000000..ecc03f0c15 --- /dev/null +++ b/plugins/badges-backend/src/plugin.ts @@ -0,0 +1,61 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { loggerToWinstonLogger } from '@backstage/backend-common'; +import { + coreServices, + createBackendPlugin, +} from '@backstage/backend-plugin-api'; +import { createRouter } from './service/router'; + +/** + * Badges backend plugin + * + * @public + */ +export const badgesPlugin = createBackendPlugin({ + pluginId: 'badges', + register(env) { + env.registerInit({ + deps: { + config: coreServices.config, + logger: coreServices.logger, + discovery: coreServices.discovery, + tokenManager: coreServices.tokenManager, + identity: coreServices.identity, + httpRouter: coreServices.httpRouter, + }, + async init({ + config, + logger, + discovery, + tokenManager, + identity, + httpRouter, + }) { + httpRouter.use( + await createRouter({ + config, + logger: loggerToWinstonLogger(logger), + discovery, + tokenManager, + identity, + }), + ); + }, + }); + }, +}); diff --git a/plugins/badges/CHANGELOG.md b/plugins/badges/CHANGELOG.md index 6cfa746feb..5d7527fba4 100644 --- a/plugins/badges/CHANGELOG.md +++ b/plugins/badges/CHANGELOG.md @@ -1,5 +1,63 @@ # @backstage/plugin-badges +## 0.2.45-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## 0.2.44 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1 + - @backstage/errors@1.2.1 + - @backstage/plugin-catalog-react@1.8.0 + - @backstage/core-components@0.13.3 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + +## 0.2.44-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.0-next.2 + - @backstage/theme@0.4.1-next.1 + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-components@0.13.3-next.2 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/errors@1.2.1-next.0 + +## 0.2.44-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.0 + - @backstage/core-components@0.13.3-next.1 + - @backstage/core-plugin-api@1.5.3-next.0 + - @backstage/plugin-catalog-react@1.7.1-next.1 + +## 0.2.44-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/core-components@0.13.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/core-plugin-api@1.5.2 + - @backstage/theme@0.4.0 + - @backstage/plugin-catalog-react@1.7.1-next.0 + ## 0.2.43 ### Patch Changes diff --git a/plugins/badges/package.json b/plugins/badges/package.json index 95eae4441d..79add2944c 100644 --- a/plugins/badges/package.json +++ b/plugins/badges/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-badges", "description": "A Backstage plugin that generates README badges for your entities", - "version": "0.2.43", + "version": "0.2.45-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/bazaar-backend/CHANGELOG.md b/plugins/bazaar-backend/CHANGELOG.md index 1680567b85..15059fe6df 100644 --- a/plugins/bazaar-backend/CHANGELOG.md +++ b/plugins/bazaar-backend/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/plugin-bazaar-backend +## 0.2.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.0 + - @backstage/backend-plugin-api@0.5.5-next.0 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-auth-node@0.2.17-next.0 + +## 0.2.10 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/backend-common@0.19.1 + - @backstage/backend-plugin-api@0.5.4 + - @backstage/config@1.0.8 + - @backstage/plugin-auth-node@0.2.16 + +## 0.2.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/backend-common@0.19.1-next.0 + - @backstage/backend-plugin-api@0.5.4-next.0 + - @backstage/config@1.0.8 + - @backstage/plugin-auth-node@0.2.16-next.0 + ## 0.2.9 ### Patch Changes diff --git a/plugins/bazaar-backend/package.json b/plugins/bazaar-backend/package.json index 3993e823f3..d41127a9fa 100644 --- a/plugins/bazaar-backend/package.json +++ b/plugins/bazaar-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bazaar-backend", - "version": "0.2.9", + "version": "0.2.11-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -13,6 +13,12 @@ "backstage": { "role": "backend-plugin" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/bazaar-backend" + }, "scripts": { "start": "backstage-cli package start", "build": "backstage-cli package build --experimental-type-build", diff --git a/plugins/bazaar/CHANGELOG.md b/plugins/bazaar/CHANGELOG.md index 79fb2122af..7168eaec27 100644 --- a/plugins/bazaar/CHANGELOG.md +++ b/plugins/bazaar/CHANGELOG.md @@ -1,5 +1,77 @@ # @backstage/plugin-bazaar +## 0.2.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.22.10-next.0 + - @backstage/core-components@0.13.4-next.0 + - @backstage/plugin-catalog@1.12.1-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## 0.2.11 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.22.9 + - @backstage/theme@0.4.1 + - @backstage/errors@1.2.1 + - @backstage/plugin-catalog-react@1.8.0 + - @backstage/plugin-catalog@1.12.0 + - @backstage/core-components@0.13.3 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + +## 0.2.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.22.9-next.1 + - @backstage/plugin-catalog-react@1.8.0-next.2 + - @backstage/plugin-catalog@1.12.0-next.2 + - @backstage/theme@0.4.1-next.1 + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-components@0.13.3-next.2 + - @backstage/catalog-client@1.4.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/errors@1.2.1-next.0 + +## 0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.0 + - @backstage/core-components@0.13.3-next.1 + - @backstage/plugin-catalog@1.11.3-next.1 + - @backstage/core-plugin-api@1.5.3-next.0 + - @backstage/cli@0.22.9-next.0 + - @backstage/plugin-catalog-react@1.7.1-next.1 + +## 0.2.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.22.9-next.0 + - @backstage/errors@1.2.1-next.0 + - @backstage/core-components@0.13.3-next.0 + - @backstage/catalog-client@1.4.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/core-plugin-api@1.5.2 + - @backstage/theme@0.4.0 + - @backstage/plugin-catalog@1.11.3-next.0 + - @backstage/plugin-catalog-react@1.7.1-next.0 + ## 0.2.10 ### Patch Changes diff --git a/plugins/bazaar/package.json b/plugins/bazaar/package.json index 8b764ba05f..3181e99833 100644 --- a/plugins/bazaar/package.json +++ b/plugins/bazaar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bazaar", - "version": "0.2.10", + "version": "0.2.12-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -12,6 +12,12 @@ "backstage": { "role": "frontend-plugin" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/bazaar" + }, "scripts": { "build": "backstage-cli package build", "start": "backstage-cli package start", diff --git a/plugins/bitbucket-cloud-common/CHANGELOG.md b/plugins/bitbucket-cloud-common/CHANGELOG.md index 832f031f2d..d438d843fd 100644 --- a/plugins/bitbucket-cloud-common/CHANGELOG.md +++ b/plugins/bitbucket-cloud-common/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-bitbucket-cloud-common +## 0.2.8 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.5.1 + +## 0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.5.1-next.0 + ## 0.2.7 ### Patch Changes diff --git a/plugins/bitbucket-cloud-common/package.json b/plugins/bitbucket-cloud-common/package.json index 6e37633ea4..38ba256c35 100644 --- a/plugins/bitbucket-cloud-common/package.json +++ b/plugins/bitbucket-cloud-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-bitbucket-cloud-common", "description": "Common functionalities for bitbucket-cloud plugins", - "version": "0.2.7", + "version": "0.2.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -14,6 +14,12 @@ "backstage": { "role": "common-library" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/bitbucket-cloud-common" + }, "scripts": { "build": "backstage-cli package build", "lint": "backstage-cli package lint", diff --git a/plugins/bitrise/CHANGELOG.md b/plugins/bitrise/CHANGELOG.md index ef824231fc..db09305c36 100644 --- a/plugins/bitrise/CHANGELOG.md +++ b/plugins/bitrise/CHANGELOG.md @@ -1,5 +1,59 @@ # @backstage/plugin-bitrise +## 0.1.48-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## 0.1.47 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.0 + - @backstage/core-components@0.13.3 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + +## 0.1.47-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.0-next.2 + - @backstage/theme@0.4.1-next.1 + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-components@0.13.3-next.2 + - @backstage/catalog-model@1.4.1-next.0 + +## 0.1.47-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.0 + - @backstage/core-components@0.13.3-next.1 + - @backstage/core-plugin-api@1.5.3-next.0 + - @backstage/plugin-catalog-react@1.7.1-next.1 + +## 0.1.47-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/core-plugin-api@1.5.2 + - @backstage/theme@0.4.0 + - @backstage/plugin-catalog-react@1.7.1-next.0 + ## 0.1.46 ### Patch Changes diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index 0c68413179..5388bb3542 100644 --- a/plugins/bitrise/package.json +++ b/plugins/bitrise/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-bitrise", "description": "A Backstage plugin that integrates towards Bitrise", - "version": "0.1.46", + "version": "0.1.48-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -13,6 +13,12 @@ "backstage": { "role": "frontend-plugin" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/bitrise" + }, "scripts": { "build": "backstage-cli package build", "start": "backstage-cli package start", diff --git a/plugins/catalog-backend-module-aws/CHANGELOG.md b/plugins/catalog-backend-module-aws/CHANGELOG.md index 3ce3ae827f..560e1cafb7 100644 --- a/plugins/catalog-backend-module-aws/CHANGELOG.md +++ b/plugins/catalog-backend-module-aws/CHANGELOG.md @@ -1,5 +1,59 @@ # @backstage/plugin-catalog-backend-module-aws +## 0.2.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.0 + - @backstage/backend-plugin-api@0.5.5-next.0 + - @backstage/backend-tasks@0.5.5-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/integration@1.5.1 + - @backstage/integration-aws-node@0.1.5 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-catalog-node@1.4.1-next.0 + - @backstage/plugin-kubernetes-common@0.6.5 + +## 0.2.2 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/backend-common@0.19.1 + - @backstage/plugin-catalog-node@1.4.0 + - @backstage/backend-plugin-api@0.5.4 + - @backstage/backend-tasks@0.5.4 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1 + - @backstage/integration-aws-node@0.1.5 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-kubernetes-common@0.6.5 + +## 0.2.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/backend-common@0.19.1-next.0 + - @backstage/plugin-catalog-node@1.4.0-next.0 + - @backstage/backend-plugin-api@0.5.4-next.0 + - @backstage/backend-tasks@0.5.4-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1-next.0 + - @backstage/integration-aws-node@0.1.5-next.0 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15-next.0 + - @backstage/plugin-kubernetes-common@0.6.5-next.0 + ## 0.2.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-aws/config.d.ts b/plugins/catalog-backend-module-aws/config.d.ts index b3610ccfc5..4c9c98512e 100644 --- a/plugins/catalog-backend-module-aws/config.d.ts +++ b/plugins/catalog-backend-module-aws/config.d.ts @@ -54,14 +54,19 @@ export interface Config { * @see https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/setting-region.html */ region?: string; + /** + * (Optional) AWS Account id. + * If not set, main account is used. + * @see https://github.com/backstage/backstage/blob/master/packages/integration-aws-node/README.md + */ + accountId?: string; /** * (Optional) TaskScheduleDefinition for the refresh. */ schedule?: TaskScheduleDefinitionConfig; } - | Record< - string, - { + | { + [name: string]: { /** * (Required) AWS S3 Bucket Name */ @@ -77,12 +82,18 @@ export interface Config { * @see https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/setting-region.html */ region?: string; + /** + * (Optional) AWS Account id. + * If not set, main account is used. + * @see https://github.com/backstage/backstage/blob/master/packages/integration-aws-node/README.md + */ + accountId?: string; /** * (Optional) TaskScheduleDefinition for the refresh. */ schedule?: TaskScheduleDefinitionConfig; - } - >; + }; + }; }; }; } diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index a75036e487..d27587a6b3 100644 --- a/plugins/catalog-backend-module-aws/package.json +++ b/plugins/catalog-backend-module-aws/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-aws", "description": "A Backstage catalog backend module that helps integrate towards AWS", - "version": "0.2.1", + "version": "0.2.3-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.ts b/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.ts index 4a87a42e75..4df92b58c2 100644 --- a/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.ts +++ b/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.ts @@ -146,20 +146,22 @@ export class AwsS3EntityProvider implements EntityProvider { /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */ async connect(connection: EntityProviderConnection): Promise { this.connection = connection; - const credProvider = - await this.awsCredentialsManager.getCredentialProvider(); + const { accountId, region, bucketName } = this.config; + const credProvider = await this.awsCredentialsManager.getCredentialProvider( + accountId ? { accountId } : undefined, + ); this.s3 = new S3({ apiVersion: '2006-03-01', credentialDefaultProvider: () => credProvider.sdkCredentialProvider, endpoint: this.integration.config.endpoint, - region: this.config.region, + region, forcePathStyle: this.integration.config.s3ForcePathStyle, }); // https://github.com/aws/aws-sdk-js-v3/issues/4122#issuecomment-1298968804 const endpoint = await getEndpointFromInstructions( { - Bucket: this.config.bucketName, + Bucket: bucketName, }, ListObjectsV2Command, this.s3.config as unknown as Record, diff --git a/plugins/catalog-backend-module-aws/src/providers/config.ts b/plugins/catalog-backend-module-aws/src/providers/config.ts index 5ef2729bf0..1ab52f9fd4 100644 --- a/plugins/catalog-backend-module-aws/src/providers/config.ts +++ b/plugins/catalog-backend-module-aws/src/providers/config.ts @@ -46,6 +46,7 @@ function readAwsS3Config(id: string, config: Config): AwsS3Config { const bucketName = config.getString('bucketName'); const region = config.getOptionalString('region'); const prefix = config.getOptionalString('prefix'); + const accountId = config.getOptionalString('accountId'); const schedule = config.has('schedule') ? readTaskScheduleDefinitionFromConfig(config.getConfig('schedule')) @@ -57,5 +58,6 @@ function readAwsS3Config(id: string, config: Config): AwsS3Config { region, prefix, schedule, + accountId, }; } diff --git a/plugins/catalog-backend-module-aws/src/providers/types.ts b/plugins/catalog-backend-module-aws/src/providers/types.ts index 204fa154c6..8e12b5f096 100644 --- a/plugins/catalog-backend-module-aws/src/providers/types.ts +++ b/plugins/catalog-backend-module-aws/src/providers/types.ts @@ -22,4 +22,5 @@ export type AwsS3Config = { prefix?: string; region?: string; schedule?: TaskScheduleDefinition; + accountId?: string; }; diff --git a/plugins/catalog-backend-module-azure/CHANGELOG.md b/plugins/catalog-backend-module-azure/CHANGELOG.md index 9e577e7bdc..9d60b4a5f2 100644 --- a/plugins/catalog-backend-module-azure/CHANGELOG.md +++ b/plugins/catalog-backend-module-azure/CHANGELOG.md @@ -1,5 +1,53 @@ # @backstage/plugin-catalog-backend-module-azure +## 0.1.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.0 + - @backstage/backend-plugin-api@0.5.5-next.0 + - @backstage/backend-tasks@0.5.5-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/integration@1.5.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-catalog-node@1.4.1-next.0 + +## 0.1.18 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/backend-common@0.19.1 + - @backstage/plugin-catalog-node@1.4.0 + - @backstage/backend-plugin-api@0.5.4 + - @backstage/backend-tasks@0.5.4 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + +## 0.1.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/backend-common@0.19.1-next.0 + - @backstage/plugin-catalog-node@1.4.0-next.0 + - @backstage/backend-plugin-api@0.5.4-next.0 + - @backstage/backend-tasks@0.5.4-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1-next.0 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15-next.0 + ## 0.1.17 ### Patch Changes diff --git a/plugins/catalog-backend-module-azure/config.d.ts b/plugins/catalog-backend-module-azure/config.d.ts index 940695c8ea..757ed2bfd3 100644 --- a/plugins/catalog-backend-module-azure/config.d.ts +++ b/plugins/catalog-backend-module-azure/config.d.ts @@ -16,35 +16,6 @@ import { TaskScheduleDefinitionConfig } from '@backstage/backend-tasks'; -interface AzureDevOpsConfig { - /** - * (Optional) The DevOps host; leave empty for `dev.azure.com`, otherwise set to your self-hosted instance host. - */ - host: string; - /** - * (Required) Your organization slug. - */ - organization: string; - /** - * (Required) Your project slug. - */ - project: string; - /** - * (Optional) The repository name. Wildcards are supported as show on the examples above. - * If not set, all repositories will be searched. - */ - repository?: string; - /** - * (Optional) Where to find catalog-info.yaml files. Wildcards are supported. - * If not set, defaults to /catalog-info.yaml. - */ - path?: string; - /** - * (Optional) TaskScheduleDefinition for the refresh. - */ - schedule?: TaskScheduleDefinitionConfig; -} - export interface Config { catalog?: { /** @@ -54,7 +25,36 @@ export interface Config { /** * AzureDevopsEntityProvider configuration */ - azureDevOps?: Record; + azureDevOps?: { + [name: string]: { + /** + * (Optional) The DevOps host; leave empty for `dev.azure.com`, otherwise set to your self-hosted instance host. + */ + host: string; + /** + * (Required) Your organization slug. + */ + organization: string; + /** + * (Required) Your project slug. + */ + project: string; + /** + * (Optional) The repository name. Wildcards are supported as show on the examples above. + * If not set, all repositories will be searched. + */ + repository?: string; + /** + * (Optional) Where to find catalog-info.yaml files. Wildcards are supported. + * If not set, defaults to /catalog-info.yaml. + */ + path?: string; + /** + * (Optional) TaskScheduleDefinition for the refresh. + */ + schedule?: TaskScheduleDefinitionConfig; + }; + }; }; }; } diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index 30a207952c..ac3cdd2b05 100644 --- a/plugins/catalog-backend-module-azure/package.json +++ b/plugins/catalog-backend-module-azure/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-azure", "description": "A Backstage catalog backend module that helps integrate towards Azure", - "version": "0.1.17", + "version": "0.1.19-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md index d0bc5d3708..4aeca8609e 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,56 @@ # @backstage/plugin-catalog-backend-module-bitbucket-cloud +## 0.1.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.0 + - @backstage/backend-plugin-api@0.5.5-next.0 + - @backstage/backend-tasks@0.5.5-next.0 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.8 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-catalog-node@1.4.1-next.0 + - @backstage/plugin-events-node@0.2.9-next.0 + +## 0.1.14 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.1 + - @backstage/plugin-catalog-node@1.4.0 + - @backstage/backend-plugin-api@0.5.4 + - @backstage/backend-tasks@0.5.4 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.8 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-events-node@0.2.8 + +## 0.1.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.1-next.0 + - @backstage/plugin-catalog-node@1.4.0-next.0 + - @backstage/backend-plugin-api@0.5.4-next.0 + - @backstage/backend-tasks@0.5.4-next.0 + - @backstage/catalog-client@1.4.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1-next.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.8-next.0 + - @backstage/plugin-catalog-common@1.0.15-next.0 + - @backstage/plugin-events-node@0.2.8-next.0 + ## 0.1.13 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-cloud/config.d.ts b/plugins/catalog-backend-module-bitbucket-cloud/config.d.ts index 8fa58413c0..fa7c070042 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/config.d.ts +++ b/plugins/catalog-backend-module-bitbucket-cloud/config.d.ts @@ -45,24 +45,23 @@ export interface Config { */ filters?: { /** - * (Optional) Filter for the repository slug. + * (Optional) Regular expression filter for the repository slug. * @visibility frontend */ - repoSlug?: RegExp; + repoSlug?: string; /** - * (Optional) Filter for the project key. + * (Optional) Regular expression filter for the project key. * @visibility frontend */ - projectKey?: RegExp; + projectKey?: string; }; /** * (Optional) TaskScheduleDefinition for the discovery. */ schedule?: TaskScheduleDefinitionConfig; } - | Record< - string, - { + | { + [name: string]: { /** * (Optional) Path to the catalog file. Default to "/catalog-info.yaml". * @visibility frontend @@ -79,22 +78,22 @@ export interface Config { */ filters?: { /** - * (Optional) Filter for the repository slug. + * (Optional) Regular expression filter for the repository slug. * @visibility frontend */ - repoSlug?: RegExp; + repoSlug?: string; /** - * (Optional) Filter for the project key. + * (Optional) Regular expression filter for the project key. * @visibility frontend */ - projectKey?: RegExp; + projectKey?: string; }; /** * (Optional) TaskScheduleDefinition for the discovery. */ schedule?: TaskScheduleDefinitionConfig; - } - >; + }; + }; }; }; } diff --git a/plugins/catalog-backend-module-bitbucket-cloud/package.json b/plugins/catalog-backend-module-bitbucket-cloud/package.json index c271230c73..1baa14f00f 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/package.json +++ b/plugins/catalog-backend-module-bitbucket-cloud/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-cloud", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket Cloud", - "version": "0.1.13", + "version": "0.1.15-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md index 8fc806bf27..fa0c443e3a 100644 --- a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,47 @@ # @backstage/plugin-catalog-backend-module-bitbucket-server +## 0.1.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.0 + - @backstage/backend-plugin-api@0.5.5-next.0 + - @backstage/backend-tasks@0.5.5-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/integration@1.5.1 + - @backstage/plugin-catalog-node@1.4.1-next.0 + +## 0.1.12 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/backend-common@0.19.1 + - @backstage/plugin-catalog-node@1.4.0 + - @backstage/backend-plugin-api@0.5.4 + - @backstage/backend-tasks@0.5.4 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1 + +## 0.1.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/backend-common@0.19.1-next.0 + - @backstage/plugin-catalog-node@1.4.0-next.0 + - @backstage/backend-plugin-api@0.5.4-next.0 + - @backstage/backend-tasks@0.5.4-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1-next.0 + ## 0.1.11 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-server/config.d.ts b/plugins/catalog-backend-module-bitbucket-server/config.d.ts index 67f694d419..57a94550e9 100644 --- a/plugins/catalog-backend-module-bitbucket-server/config.d.ts +++ b/plugins/catalog-backend-module-bitbucket-server/config.d.ts @@ -37,24 +37,23 @@ export interface Config { */ filters?: { /** - * (Optional) Filter for the repository slug. + * (Optional) Regular expression filter for the repository slug. * @visibility frontend */ - repoSlug?: RegExp; + repoSlug?: string; /** - * (Optional) Filter for the project key. + * (Optional) Regular expression filter for the project key. * @visibility frontend */ - projectKey?: RegExp; + projectKey?: string; }; /** * (Optional) TaskScheduleDefinition for the refresh. */ schedule?: TaskScheduleDefinitionConfig; } - | Record< - string, - { + | { + [name: string]: { /** * (Optional) Path to the catalog file. Default to "/catalog-info.yaml". * @visibility frontend @@ -66,22 +65,22 @@ export interface Config { */ filters?: { /** - * (Optional) Filter for the repository slug. + * (Optional) Regular expression filter for the repository slug. * @visibility frontend */ - repoSlug?: RegExp; + repoSlug?: string; /** - * (Optional) Filter for the project key. + * (Optional) Regular expression filter for the project key. * @visibility frontend */ - projectKey?: RegExp; + projectKey?: string; }; /** * (Optional) TaskScheduleDefinition for the refresh. */ schedule?: TaskScheduleDefinitionConfig; - } - >; + }; + }; }; }; } diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index 15e9ef0038..4b35c8f7d7 100644 --- a/plugins/catalog-backend-module-bitbucket-server/package.json +++ b/plugins/catalog-backend-module-bitbucket-server/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-server", - "version": "0.1.11", + "version": "0.1.13-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-bitbucket/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket/CHANGELOG.md index 13ab3b8816..9c1da14fba 100644 --- a/plugins/catalog-backend-module-bitbucket/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket/CHANGELOG.md @@ -1,5 +1,47 @@ # @backstage/plugin-catalog-backend-module-bitbucket +## 0.2.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/integration@1.5.1 + - @backstage/types@1.1.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.8 + - @backstage/plugin-catalog-node@1.4.1-next.0 + +## 0.2.14 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/backend-common@0.19.1 + - @backstage/plugin-catalog-node@1.4.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1 + - @backstage/types@1.1.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.8 + +## 0.2.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/backend-common@0.19.1-next.0 + - @backstage/plugin-catalog-node@1.4.0-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1-next.0 + - @backstage/types@1.1.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.8-next.0 + ## 0.2.13 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket/package.json b/plugins/catalog-backend-module-bitbucket/package.json index d021291155..e968aad343 100644 --- a/plugins/catalog-backend-module-bitbucket/package.json +++ b/plugins/catalog-backend-module-bitbucket/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket", - "version": "0.2.13", + "version": "0.2.15-next.0", "deprecated": true, "main": "src/index.ts", "types": "src/index.ts", diff --git a/plugins/catalog-backend-module-gcp/.eslintrc.js b/plugins/catalog-backend-module-gcp/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/catalog-backend-module-gcp/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/catalog-backend-module-gcp/README.md b/plugins/catalog-backend-module-gcp/README.md new file mode 100644 index 0000000000..d1ef926310 --- /dev/null +++ b/plugins/catalog-backend-module-gcp/README.md @@ -0,0 +1,41 @@ +# Catalog Backend Module for GCP + +This is an extension module to the plugin-catalog-backend plugin, containing catalog processors and providers to ingest GCP resources as `Resource` kind entities. + +## installation + +Register the plugin in `catalog.ts`` + +```typescript +import { GkeEntityProvider } from '@backstage/plugin-catalog-backend-module-gcp'; + +... + +builder.addEntityProvider( + GkeEntityProvider.fromConfig({ + logger: env.logger, + scheduler: env.scheduler, + config: env.config + }) +); +``` + +Update `app-config.yaml` as follows: + +```yaml +catalog: + providers: + gcp: + gke: + parents: + # consult https://cloud.google.com/kubernetes-engine/docs/ for valid values + # list all clusters in the project + - 'projects/some-project/locations/-' + # list all clusters in the region, in the project + - 'projects/some-other-project/locations/some-region' + schedule: # optional; same options as in TaskScheduleDefinition + # supports cron, ISO duration, "human duration" as used in code + frequency: { minutes: 30 } + # supports ISO duration, "human duration" as used in code + timeout: { minutes: 3 } +``` diff --git a/plugins/catalog-backend-module-gcp/alpha-api-report.md b/plugins/catalog-backend-module-gcp/alpha-api-report.md new file mode 100644 index 0000000000..6e6f0427e9 --- /dev/null +++ b/plugins/catalog-backend-module-gcp/alpha-api-report.md @@ -0,0 +1,7 @@ +## API Report File for "@backstage/plugin-catalog-backend-module-gcp" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +``` diff --git a/plugins/catalog-backend-module-gcp/api-report.md b/plugins/catalog-backend-module-gcp/api-report.md new file mode 100644 index 0000000000..6f963ed51d --- /dev/null +++ b/plugins/catalog-backend-module-gcp/api-report.md @@ -0,0 +1,48 @@ +## API Report File for "@backstage/plugin-catalog-backend-module-gcp" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; +import { Config } from '@backstage/config'; +import * as container from '@google-cloud/container'; +import { EntityProvider } from '@backstage/plugin-catalog-node'; +import { EntityProviderConnection } from '@backstage/plugin-catalog-node'; +import { Logger } from 'winston'; +import { SchedulerService } from '@backstage/backend-plugin-api'; + +// @public +export const catalogModuleGcpGkeEntityProvider: () => BackendFeature; + +// @public +export class GkeEntityProvider implements EntityProvider { + // (undocumented) + connect(connection: EntityProviderConnection): Promise; + // (undocumented) + static fromConfig({ + logger, + scheduler, + config, + }: { + logger: Logger; + scheduler: SchedulerService; + config: Config; + }): GkeEntityProvider; + // (undocumented) + static fromConfigWithClient({ + logger, + scheduler, + config, + clusterManagerClient, + }: { + logger: Logger; + scheduler: SchedulerService; + config: Config; + clusterManagerClient: container.v1.ClusterManagerClient; + }): GkeEntityProvider; + // (undocumented) + getProviderName(): string; + // (undocumented) + refresh(): Promise; +} +``` diff --git a/plugins/catalog-backend-module-gcp/config.d.ts b/plugins/catalog-backend-module-gcp/config.d.ts new file mode 100644 index 0000000000..d6884d32ce --- /dev/null +++ b/plugins/catalog-backend-module-gcp/config.d.ts @@ -0,0 +1,45 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { TaskScheduleDefinitionConfig } from '@backstage/backend-tasks'; + +export interface Config { + catalog?: { + /** + * List of provider-specific options and attributes + */ + providers?: { + /** + * GCPCatalogModuleConfig configuration + */ + gcp?: { + /** + * Config for GKE clusters + */ + gke?: { + /** + * Locations to list clusters from + */ + parents: string[]; + /** + * (Optional) TaskScheduleDefinition for the refresh. + */ + schedule: TaskScheduleDefinitionConfig; + }; + }; + }; + }; +} diff --git a/plugins/catalog-backend-module-gcp/package.json b/plugins/catalog-backend-module-gcp/package.json new file mode 100644 index 0000000000..12285d26eb --- /dev/null +++ b/plugins/catalog-backend-module-gcp/package.json @@ -0,0 +1,66 @@ +{ + "name": "@backstage/plugin-catalog-backend-module-gcp", + "description": "A Backstage catalog backend module that helps integrate towards GCP", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } + }, + "backstage": { + "role": "backend-plugin-module" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/catalog-backend-module-gcp" + }, + "keywords": [ + "backstage" + ], + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "clean": "backstage-cli package clean" + }, + "dependencies": { + "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/backend-tasks": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", + "@backstage/plugin-kubernetes-common": "workspace:^", + "@google-cloud/container": "^4.15.0", + "winston": "^3.2.1" + }, + "devDependencies": { + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^" + }, + "files": [ + "config.d.ts", + "dist" + ], + "configSchema": "config.d.ts" +} diff --git a/plugins/catalog-backend-module-gcp/src/alpha.ts b/plugins/catalog-backend-module-gcp/src/alpha.ts new file mode 100644 index 0000000000..9eec69e76f --- /dev/null +++ b/plugins/catalog-backend-module-gcp/src/alpha.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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. + */ + +/** + * A Backstage catalog backend module that helps integrate towards GCP + * + * @packageDocumentation + */ + +export {}; diff --git a/plugins/catalog-backend-module-gcp/src/index.ts b/plugins/catalog-backend-module-gcp/src/index.ts new file mode 100644 index 0000000000..fb3984a04d --- /dev/null +++ b/plugins/catalog-backend-module-gcp/src/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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. + */ + +/** + * A Backstage catalog backend module that helps integrate towards GCP + * + * @packageDocumentation + */ + +export * from './providers'; +export * from './module'; diff --git a/plugins/catalog-backend-module-gcp/src/module/catalogModuleGcpGkeEntityProvider.ts b/plugins/catalog-backend-module-gcp/src/module/catalogModuleGcpGkeEntityProvider.ts new file mode 100644 index 0000000000..c13c6cbe0e --- /dev/null +++ b/plugins/catalog-backend-module-gcp/src/module/catalogModuleGcpGkeEntityProvider.ts @@ -0,0 +1,52 @@ +/* + * Copyright 2022 The Backstage Authors + * + * 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 { loggerToWinstonLogger } from '@backstage/backend-common'; +import { + coreServices, + createBackendModule, +} from '@backstage/backend-plugin-api'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; +import { GkeEntityProvider } from '../providers/GkeEntityProvider'; + +/** + * Registers the GcpGkeEntityProvider with the catalog processing extension point. + * + * @public + */ +export const catalogModuleGcpGkeEntityProvider = createBackendModule({ + pluginId: 'catalog', + moduleId: 'gcpGkeEntityProvider', + register(env) { + env.registerInit({ + deps: { + config: coreServices.config, + catalog: catalogProcessingExtensionPoint, + logger: coreServices.logger, + scheduler: coreServices.scheduler, + }, + async init({ config, catalog, logger, scheduler }) { + catalog.addEntityProvider( + GkeEntityProvider.fromConfig({ + logger: loggerToWinstonLogger(logger), + scheduler, + config, + }), + ); + }, + }); + }, +}); diff --git a/plugins/catalog-backend-module-gcp/src/module/index.ts b/plugins/catalog-backend-module-gcp/src/module/index.ts new file mode 100644 index 0000000000..c7cd310c97 --- /dev/null +++ b/plugins/catalog-backend-module-gcp/src/module/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { catalogModuleGcpGkeEntityProvider } from './catalogModuleGcpGkeEntityProvider'; diff --git a/plugins/catalog-backend-module-gcp/src/providers/GkeEntityProvider.test.ts b/plugins/catalog-backend-module-gcp/src/providers/GkeEntityProvider.test.ts new file mode 100644 index 0000000000..20e91a85bf --- /dev/null +++ b/plugins/catalog-backend-module-gcp/src/providers/GkeEntityProvider.test.ts @@ -0,0 +1,251 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { GkeEntityProvider } from './GkeEntityProvider'; +import { TaskRunner } from '@backstage/backend-tasks'; +import { + ANNOTATION_KUBERNETES_API_SERVER, + ANNOTATION_KUBERNETES_API_SERVER_CA, + ANNOTATION_KUBERNETES_AUTH_PROVIDER, +} from '@backstage/plugin-kubernetes-common'; +import * as container from '@google-cloud/container'; +import { ConfigReader } from '@backstage/config'; + +describe('GkeEntityProvider', () => { + const clusterManagerClientMock = { + listClusters: jest.fn(), + }; + const connectionMock = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; + const taskRunner = { + createScheduleFn: jest.fn(), + run: jest.fn(), + } as TaskRunner; + const schedulerMock = { + createScheduledTaskRunner: jest.fn(), + } as any; + const logger = { + info: jest.fn(), + error: jest.fn(), + }; + let gkeEntityProvider: GkeEntityProvider; + + beforeEach(async () => { + jest.resetAllMocks(); + schedulerMock.createScheduledTaskRunner.mockReturnValue(taskRunner); + gkeEntityProvider = GkeEntityProvider.fromConfigWithClient({ + logger: logger as any, + config: new ConfigReader({ + catalog: { + providers: { + gcp: { + gke: { + parents: ['parent1', 'parent2'], + schedule: { + frequency: { + minutes: 3, + }, + timeout: { + minutes: 3, + }, + }, + }, + }, + }, + }, + }), + scheduler: schedulerMock, + clusterManagerClient: clusterManagerClientMock as any, + }); + await gkeEntityProvider.connect(connectionMock); + }); + + it('should return clusters as Resources', async () => { + clusterManagerClientMock.listClusters.mockImplementation(req => { + if (req.parent === 'parent1') { + return [ + { + clusters: [ + { + name: 'some-cluster', + endpoint: 'http://127.0.0.1:1234', + location: 'some-location', + selfLink: 'http://127.0.0.1/some-link', + masterAuth: { + clusterCaCertificate: 'abcdefg', + }, + }, + ], + }, + ]; + } else if (req.parent === 'parent2') { + return [ + { + clusters: [ + { + name: 'some-other-cluster', + endpoint: 'http://127.0.0.1:5678', + location: 'some-other-location', + selfLink: 'http://127.0.0.1/some-other-link', + masterAuth: { + // no CA cert is ok + }, + }, + ], + }, + ]; + } + + throw new Error(`unexpected parent ${req.parent}`); + }); + await gkeEntityProvider.refresh(); + expect(connectionMock.applyMutation).toHaveBeenCalledWith({ + type: 'full', + entities: [ + { + locationKey: 'gcp-gke:some-location', + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Resource', + metadata: { + annotations: { + [ANNOTATION_KUBERNETES_API_SERVER]: 'http://127.0.0.1:1234', + [ANNOTATION_KUBERNETES_API_SERVER_CA]: 'abcdefg', + [ANNOTATION_KUBERNETES_AUTH_PROVIDER]: 'google', + 'backstage.io/managed-by-location': 'gcp-gke:some-location', + 'backstage.io/managed-by-origin-location': + 'gcp-gke:some-location', + }, + name: 'some-cluster', + namespace: 'default', + }, + spec: { + type: 'kubernetes-cluster', + owner: 'unknown', + }, + }, + }, + { + locationKey: 'gcp-gke:some-other-location', + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Resource', + metadata: { + annotations: { + [ANNOTATION_KUBERNETES_API_SERVER]: 'http://127.0.0.1:5678', + [ANNOTATION_KUBERNETES_API_SERVER_CA]: '', + [ANNOTATION_KUBERNETES_AUTH_PROVIDER]: 'google', + 'backstage.io/managed-by-location': + 'gcp-gke:some-other-location', + 'backstage.io/managed-by-origin-location': + 'gcp-gke:some-other-location', + }, + name: 'some-other-cluster', + namespace: 'default', + }, + spec: { + type: 'kubernetes-cluster', + owner: 'unknown', + }, + }, + }, + ], + }); + }); + + const ignoredPartialClustersTests: [ + string, + container.protos.google.container.v1.ICluster, + ][] = [ + [ + 'no-cluster-name', + { + endpoint: 'http://127.0.0.1:1234', + location: 'some-location', + selfLink: 'http://127.0.0.1/some-link', + masterAuth: { + clusterCaCertificate: 'abcdefg', + }, + }, + ], + [ + 'no-self-link', + { + // no selfLink + name: 'some-name', + endpoint: 'http://127.0.0.1:1234', + location: 'some-location', + masterAuth: { + clusterCaCertificate: 'abcdefg', + }, + }, + ], + [ + 'no-endpoint', + { + name: 'some-name', + location: 'some-location', + selfLink: 'http://127.0.0.1/some-link', + masterAuth: { + clusterCaCertificate: 'abcdefg', + }, + }, + ], + [ + 'no-location', + { + name: 'some-name', + endpoint: 'http://127.0.0.1:1234', + selfLink: 'http://127.0.0.1/some-link', + masterAuth: { + clusterCaCertificate: 'abcdefg', + }, + }, + ], + ]; + + it.each(ignoredPartialClustersTests)( + 'ignore cluster - %s', + async (_name, ignoredCluster) => { + clusterManagerClientMock.listClusters.mockImplementation(req => { + if (req.parent === 'parent1') { + return [ignoredCluster]; + } + return [ + { + clusters: [], + }, + ]; + }); + await gkeEntityProvider.refresh(); + expect(connectionMock.applyMutation).toHaveBeenCalledWith({ + type: 'full', + entities: [], + }); + }, + ); + + it('should log GKE API errors', async () => { + clusterManagerClientMock.listClusters.mockRejectedValue( + new Error('some-error'), + ); + await gkeEntityProvider.refresh(); + expect(connectionMock.applyMutation).toHaveBeenCalledTimes(0); + expect(logger.error).toHaveBeenCalledTimes(1); + }); +}); diff --git a/plugins/catalog-backend-module-gcp/src/providers/GkeEntityProvider.ts b/plugins/catalog-backend-module-gcp/src/providers/GkeEntityProvider.ts new file mode 100644 index 0000000000..0130955737 --- /dev/null +++ b/plugins/catalog-backend-module-gcp/src/providers/GkeEntityProvider.ts @@ -0,0 +1,223 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { + TaskRunner, + readTaskScheduleDefinitionFromConfig, +} from '@backstage/backend-tasks'; +import { + DeferredEntity, + EntityProvider, + EntityProviderConnection, +} from '@backstage/plugin-catalog-node'; + +import { Logger } from 'winston'; +import * as container from '@google-cloud/container'; +import { + ANNOTATION_KUBERNETES_API_SERVER, + ANNOTATION_KUBERNETES_API_SERVER_CA, + ANNOTATION_KUBERNETES_AUTH_PROVIDER, +} from '@backstage/plugin-kubernetes-common'; +import { Config } from '@backstage/config'; +import { SchedulerService } from '@backstage/backend-plugin-api'; + +/** + * Catalog provider to ingest GKE clusters + * + * @public + */ +export class GkeEntityProvider implements EntityProvider { + private readonly logger: Logger; + private readonly scheduleFn: () => Promise; + private readonly gkeParents: string[]; + private readonly clusterManagerClient: container.v1.ClusterManagerClient; + private connection?: EntityProviderConnection; + + private constructor( + logger: Logger, + taskRunner: TaskRunner, + gkeParents: string[], + clusterManagerClient: container.v1.ClusterManagerClient, + ) { + this.logger = logger; + this.scheduleFn = this.createScheduleFn(taskRunner); + this.gkeParents = gkeParents; + this.clusterManagerClient = clusterManagerClient; + } + + public static fromConfig({ + logger, + scheduler, + config, + }: { + logger: Logger; + scheduler: SchedulerService; + config: Config; + }) { + return GkeEntityProvider.fromConfigWithClient({ + logger, + scheduler: scheduler, + config, + clusterManagerClient: new container.v1.ClusterManagerClient(), + }); + } + + public static fromConfigWithClient({ + logger, + scheduler, + config, + clusterManagerClient, + }: { + logger: Logger; + scheduler: SchedulerService; + config: Config; + clusterManagerClient: container.v1.ClusterManagerClient; + }) { + const gkeProviderConfig = config.getConfig('catalog.providers.gcp.gke'); + const schedule = readTaskScheduleDefinitionFromConfig( + gkeProviderConfig.getConfig('schedule'), + ); + return new GkeEntityProvider( + logger, + scheduler.createScheduledTaskRunner(schedule), + gkeProviderConfig.getStringArray('parents'), + clusterManagerClient, + ); + } + + getProviderName(): string { + return `gcp-gke`; + } + + async connect(connection: EntityProviderConnection): Promise { + this.connection = connection; + await this.scheduleFn(); + } + + private filterOutUndefinedDeferredEntity( + e: DeferredEntity | undefined, + ): e is DeferredEntity { + return e !== undefined; + } + + private filterOutUndefinedCluster( + c: container.protos.google.container.v1.ICluster | null | undefined, + ): c is container.protos.google.container.v1.ICluster { + return c !== undefined && c !== null; + } + + private clusterToResource( + cluster: container.protos.google.container.v1.ICluster, + ): DeferredEntity | undefined { + const location = `${this.getProviderName()}:${cluster.location}`; + + if (!cluster.name || !cluster.selfLink || !location || !cluster.endpoint) { + this.logger.warn( + `ignoring partial cluster, one of name=${cluster.name}, endpoint=${cluster.endpoint}, selfLink=${cluster.selfLink} or location=${cluster.location} is missing`, + ); + return undefined; + } + + // TODO fix location type + return { + locationKey: location, + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Resource', + metadata: { + annotations: { + [ANNOTATION_KUBERNETES_API_SERVER]: cluster.endpoint, + [ANNOTATION_KUBERNETES_API_SERVER_CA]: + cluster.masterAuth?.clusterCaCertificate || '', + [ANNOTATION_KUBERNETES_AUTH_PROVIDER]: 'google', + 'backstage.io/managed-by-location': location, + 'backstage.io/managed-by-origin-location': location, + }, + name: cluster.name, + namespace: 'default', + }, + spec: { + type: 'kubernetes-cluster', + owner: 'unknown', + }, + }, + }; + } + + private createScheduleFn(taskRunner: TaskRunner): () => Promise { + return async () => { + const taskId = `${this.getProviderName()}:refresh`; + return taskRunner.run({ + id: taskId, + fn: async () => { + try { + await this.refresh(); + } catch (error) { + this.logger.error(error); + } + }, + }); + }; + } + + private async getClusters(): Promise< + container.protos.google.container.v1.ICluster[] + > { + const clusters = await Promise.all( + this.gkeParents.map(async parent => { + const request = { + parent: parent, + }; + const [response] = await this.clusterManagerClient.listClusters( + request, + ); + return response.clusters?.filter(this.filterOutUndefinedCluster) ?? []; + }), + ); + return clusters.flat(); + } + + async refresh() { + if (!this.connection) { + throw new Error('Not initialized'); + } + + this.logger.info('Discovering GKE clusters'); + + let clusters: container.protos.google.container.v1.ICluster[]; + + try { + clusters = await this.getClusters(); + } catch (e) { + this.logger.error('error fetching GKE clusters', e); + return; + } + const resources = + clusters + .map(c => this.clusterToResource(c)) + .filter(this.filterOutUndefinedDeferredEntity) ?? []; + + this.logger.info( + `Ingesting GKE clusters [${resources + .map(r => r.entity.metadata.name) + .join(', ')}]`, + ); + + await this.connection.applyMutation({ + type: 'full', + entities: resources, + }); + } +} diff --git a/plugins/catalog-backend-module-gcp/src/providers/index.ts b/plugins/catalog-backend-module-gcp/src/providers/index.ts new file mode 100644 index 0000000000..7846afc3ed --- /dev/null +++ b/plugins/catalog-backend-module-gcp/src/providers/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 The Backstage Authors + * + * 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 { GkeEntityProvider } from './GkeEntityProvider'; diff --git a/plugins/catalog-backend-module-gcp/src/setupTests.ts b/plugins/catalog-backend-module-gcp/src/setupTests.ts new file mode 100644 index 0000000000..aa70772592 --- /dev/null +++ b/plugins/catalog-backend-module-gcp/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 {}; diff --git a/plugins/catalog-backend-module-gerrit/CHANGELOG.md b/plugins/catalog-backend-module-gerrit/CHANGELOG.md index 4e5f8927d7..09df93f029 100644 --- a/plugins/catalog-backend-module-gerrit/CHANGELOG.md +++ b/plugins/catalog-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,47 @@ # @backstage/plugin-catalog-backend-module-gerrit +## 0.1.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.0 + - @backstage/backend-plugin-api@0.5.5-next.0 + - @backstage/backend-tasks@0.5.5-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/integration@1.5.1 + - @backstage/plugin-catalog-node@1.4.1-next.0 + +## 0.1.15 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/backend-common@0.19.1 + - @backstage/plugin-catalog-node@1.4.0 + - @backstage/backend-plugin-api@0.5.4 + - @backstage/backend-tasks@0.5.4 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1 + +## 0.1.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/backend-common@0.19.1-next.0 + - @backstage/plugin-catalog-node@1.4.0-next.0 + - @backstage/backend-plugin-api@0.5.4-next.0 + - @backstage/backend-tasks@0.5.4-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1-next.0 + ## 0.1.14 ### Patch Changes diff --git a/plugins/catalog-backend-module-gerrit/config.d.ts b/plugins/catalog-backend-module-gerrit/config.d.ts index d8b48d2967..a246662b0a 100644 --- a/plugins/catalog-backend-module-gerrit/config.d.ts +++ b/plugins/catalog-backend-module-gerrit/config.d.ts @@ -25,9 +25,8 @@ export interface Config { * * Maps provider id with configuration. */ - gerrit?: Record< - string, - { + gerrit?: { + [name: string]: { /** * (Required) The host of the Gerrit integration to use. */ @@ -42,8 +41,8 @@ export interface Config { * The branch where the provider will try to find entities. Defaults to "master". */ branch?: string; - } - >; + }; + }; }; }; } diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index 232c02b684..aa0fc95907 100644 --- a/plugins/catalog-backend-module-gerrit/package.json +++ b/plugins/catalog-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gerrit", - "version": "0.1.14", + "version": "0.1.16-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-github/CHANGELOG.md b/plugins/catalog-backend-module-github/CHANGELOG.md index 128e77c8b9..3743f035c4 100644 --- a/plugins/catalog-backend-module-github/CHANGELOG.md +++ b/plugins/catalog-backend-module-github/CHANGELOG.md @@ -1,5 +1,73 @@ # @backstage/plugin-catalog-backend-module-github +## 0.3.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.12.0-next.0 + - @backstage/backend-common@0.19.2-next.0 + - @backstage/backend-plugin-api@0.5.5-next.0 + - @backstage/backend-tasks@0.5.5-next.0 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/integration@1.5.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-catalog-node@1.4.1-next.0 + - @backstage/plugin-events-node@0.2.9-next.0 + +## 0.3.2 + +### Patch Changes + +- b01b869d7158: fixed event handler to respect configured organization +- 68531cc70e1f: Break some internal circular import chains +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/backend-common@0.19.1 + - @backstage/plugin-catalog-backend@1.11.0 + - @backstage/plugin-catalog-node@1.4.0 + - @backstage/backend-plugin-api@0.5.4 + - @backstage/backend-tasks@0.5.4 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-events-node@0.2.8 + +## 0.3.2-next.1 + +### Patch Changes + +- b01b869d7158: fixed event handler to respect configured organization +- Updated dependencies + - @backstage/config@1.0.8 + +## 0.3.2-next.0 + +### Patch Changes + +- 68531cc70e1f: Break some internal circular import chains +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/backend-common@0.19.1-next.0 + - @backstage/plugin-catalog-backend@1.11.0-next.0 + - @backstage/plugin-catalog-node@1.4.0-next.0 + - @backstage/backend-plugin-api@0.5.4-next.0 + - @backstage/backend-tasks@0.5.4-next.0 + - @backstage/catalog-client@1.4.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1-next.0 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15-next.0 + - @backstage/plugin-events-node@0.2.8-next.0 + ## 0.3.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-github/config.d.ts b/plugins/catalog-backend-module-github/config.d.ts index 5a699e745f..9755504ee2 100644 --- a/plugins/catalog-backend-module-github/config.d.ts +++ b/plugins/catalog-backend-module-github/config.d.ts @@ -118,9 +118,8 @@ export interface Config { */ schedule?: TaskScheduleDefinitionConfig; } - | Record< - string, - { + | { + [name: string]: { /** * (Optional) The hostname of your GitHub Enterprise instance. * Default: `github.com`. @@ -182,8 +181,8 @@ export interface Config { * (Optional) TaskScheduleDefinition for the refresh. */ schedule?: TaskScheduleDefinitionConfig; - } - >; + }; + }; }; }; } diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index d3265efbec..2e64d39638 100644 --- a/plugins/catalog-backend-module-github/package.json +++ b/plugins/catalog-backend-module-github/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-github", "description": "A Backstage catalog backend module that helps integrate towards GitHub", - "version": "0.3.1", + "version": "0.3.3-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.test.ts b/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.test.ts index 6540962411..d0fc54245f 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.test.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.test.ts @@ -1212,4 +1212,73 @@ describe('GithubEntityProvider', () => { expect(entityProviderConnection.refresh).toHaveBeenCalledTimes(0); expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(0); }); + + it("should skip process when didn't match org from push event", async () => { + const schedule = new PersistingTaskRunner(); + const config = new ConfigReader({ + catalog: { + providers: { + github: { + organization: 'test-org', + filters: { + branch: 'my-special-branch', + repository: 'test-repo', + }, + }, + }, + }, + }); + + const provider = GithubEntityProvider.fromConfig(config, { + logger, + schedule, + })[0]; + + const entityProviderConnection: EntityProviderConnection = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; + await provider.connect(entityProviderConnection); + + const event: EventParams = { + topic: 'github.push', + metadata: { + 'x-github-event': 'push', + }, + eventPayload: { + ref: 'refs/heads/my-special-branch', + repository: { + name: 'test-repo', + url: 'https://github.com/test-org/test-repo', + default_branch: 'main', + stargazers: 0, + master_branch: 'main', + organization: 'other-org', + topics: [], + }, + created: true, + deleted: false, + forced: false, + commits: [ + { + added: ['new-file.yaml'], + removed: [], + modified: [], + }, + { + added: [], + removed: [], + modified: ['catalog-info.yaml'], + }, + ], + }, + }; + + await provider.onEvent(event); + + await provider.onEvent(event); + + expect(entityProviderConnection.refresh).toHaveBeenCalledTimes(0); + expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(0); + }); }); diff --git a/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.ts b/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.ts index bd26f2906a..e91cc328f7 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.ts @@ -296,6 +296,13 @@ export class GithubEntityProvider implements EntityProvider, EventSubscriber { throw new Error('Not initialized'); } + if (this.config.organization !== event.repository.organization) { + this.logger.debug( + `skipping push event from organization ${event.repository.organization}`, + ); + return; + } + const repoName = event.repository.name; const repoUrl = event.repository.url; this.logger.debug(`handle github:push event for ${repoName} - ${repoUrl}`); diff --git a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts index c181510a67..bf82607c6b 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts @@ -28,48 +28,46 @@ import { ScmIntegrations, SingleInstanceGithubCredentialsProvider, } from '@backstage/integration'; -import { EventParams } from '@backstage/plugin-events-node'; -import { EventSubscriber } from '@backstage/plugin-events-node'; import { EntityProvider, EntityProviderConnection, } from '@backstage/plugin-catalog-node'; +import { EventParams, EventSubscriber } from '@backstage/plugin-events-node'; import { graphql } from '@octokit/graphql'; import { + MembershipEvent, OrganizationEvent, OrganizationMemberAddedEvent, OrganizationMemberRemovedEvent, - TeamEvent, TeamEditedEvent, - MembershipEvent, + TeamEvent, } from '@octokit/webhooks-types'; import { merge } from 'lodash'; import * as uuid from 'uuid'; import { Logger } from 'winston'; -import { - assignGroupsToUsers, - buildOrgHierarchy, - defaultOrganizationTeamTransformer, - defaultUserTransformer, - getOrganizationTeams, - getOrganizationUsers, - GithubTeam, - parseGithubOrgUrl, -} from '../lib'; -import { TeamTransformer, UserTransformer } from '../lib'; -import { - createAddEntitiesOperation, - createRemoveEntitiesOperation, - createReplaceEntitiesOperation, - DeferredEntitiesBuilder, - getOrganizationTeam, - getOrganizationTeamsFromUsers, -} from '../lib/github'; import { ANNOTATION_GITHUB_TEAM_SLUG, ANNOTATION_GITHUB_USER_LOGIN, } from '../lib/annotation'; -import { splitTeamSlug } from '../lib/util'; +import { + TeamTransformer, + UserTransformer, + defaultOrganizationTeamTransformer, + defaultUserTransformer, +} from '../lib/defaultTransformers'; +import { + DeferredEntitiesBuilder, + GithubTeam, + createAddEntitiesOperation, + createRemoveEntitiesOperation, + createReplaceEntitiesOperation, + getOrganizationTeam, + getOrganizationTeams, + getOrganizationTeamsFromUsers, + getOrganizationUsers, +} from '../lib/github'; +import { assignGroupsToUsers, buildOrgHierarchy } from '../lib/org'; +import { parseGithubOrgUrl, splitTeamSlug } from '../lib/util'; /** * Options for {@link GithubOrgEntityProvider}. diff --git a/plugins/catalog-backend-module-gitlab/CHANGELOG.md b/plugins/catalog-backend-module-gitlab/CHANGELOG.md index 5f84063c2a..2fcba33d47 100644 --- a/plugins/catalog-backend-module-gitlab/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,50 @@ # @backstage/plugin-catalog-backend-module-gitlab +## 0.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.0 + - @backstage/backend-plugin-api@0.5.5-next.0 + - @backstage/backend-tasks@0.5.5-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/integration@1.5.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-node@1.4.1-next.0 + +## 0.2.3 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/backend-common@0.19.1 + - @backstage/plugin-catalog-node@1.4.0 + - @backstage/backend-plugin-api@0.5.4 + - @backstage/backend-tasks@0.5.4 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1 + - @backstage/types@1.1.0 + +## 0.2.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/backend-common@0.19.1-next.0 + - @backstage/plugin-catalog-node@1.4.0-next.0 + - @backstage/backend-plugin-api@0.5.4-next.0 + - @backstage/backend-tasks@0.5.4-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1-next.0 + - @backstage/types@1.1.0 + ## 0.2.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab/config.d.ts b/plugins/catalog-backend-module-gitlab/config.d.ts index 00ad0ef032..7d183b6e58 100644 --- a/plugins/catalog-backend-module-gitlab/config.d.ts +++ b/plugins/catalog-backend-module-gitlab/config.d.ts @@ -22,9 +22,8 @@ export interface Config { /** * GitlabDiscoveryEntityProvider configuration */ - gitlab?: Record< - string, - { + gitlab?: { + [name: string]: { /** * (Required) Gitlab's host name. */ @@ -51,17 +50,21 @@ export interface Config { /** * (Optional) RegExp for the Project Name Pattern */ - projectPattern?: RegExp; + projectPattern?: string; /** * (Optional) RegExp for the User Name Pattern */ - userPattern?: RegExp; + userPattern?: string; /** * (Optional) RegExp for the Group Name Pattern */ - groupPattern?: RegExp; - } - >; + groupPattern?: string; + /** + * (Optional) Skip forked repository + */ + skipForkedRepos?: boolean; + }; + }; }; }; } diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index 25df604cd9..1a3f0245eb 100644 --- a/plugins/catalog-backend-module-gitlab/package.json +++ b/plugins/catalog-backend-module-gitlab/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab", "description": "A Backstage catalog backend module that helps integrate towards GitLab", - "version": "0.2.2", + "version": "0.2.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-gitlab/src/lib/types.ts b/plugins/catalog-backend-module-gitlab/src/lib/types.ts index 651fbb00a6..09a9fb92c3 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/types.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/types.ts @@ -142,4 +142,5 @@ export type GitlabProviderConfig = { groupPattern: RegExp; orgEnabled?: boolean; schedule?: TaskScheduleDefinition; + skipForkedRepos?: boolean; }; diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.test.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.test.ts index 75a4c6414c..7508b35b84 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.test.ts @@ -343,6 +343,119 @@ describe('GitlabDiscoveryEntityProvider', () => { }); }); + it('should filter fork projects', async () => { + const config = new ConfigReader({ + integrations: { + gitlab: [ + { + host: 'test-gitlab', + apiBaseUrl: 'https://api.gitlab.example/api/v4', + token: '1234', + }, + ], + }, + catalog: { + providers: { + gitlab: { + 'test-id': { + host: 'test-gitlab', + skipForkedRepos: true, + }, + }, + }, + }, + }); + const schedule = new PersistingTaskRunner(); + const entityProviderConnection: EntityProviderConnection = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; + const provider = GitlabDiscoveryEntityProvider.fromConfig(config, { + logger, + schedule, + })[0]; + + server.use( + rest.get( + `https://api.gitlab.example/api/v4/projects`, + (_req, res, ctx) => { + const response = [ + { + id: 123, + default_branch: 'master', + archived: false, + last_activity_at: new Date().toString(), + web_url: 'https://api.gitlab.example/test-group/test-repo', + path_with_namespace: 'test-group/test-repo', + forked_from_project: { + id: 13083, + }, + }, + { + id: 124, + default_branch: 'master', + archived: false, + last_activity_at: new Date().toString(), + web_url: 'https://api.gitlab.example/john/example', + path_with_namespace: 'john/example', + }, + ]; + return res(ctx.json(response)); + }, + ), + rest.head( + 'https://api.gitlab.example/api/v4/projects/test-group%2Ftest-repo/repository/files/catalog-info.yaml', + (req, res, ctx) => { + if (req.url.searchParams.get('ref') === 'master') { + return res(ctx.status(200)); + } + return res(ctx.status(404, 'Not Found')); + }, + ), + rest.head( + 'https://api.gitlab.example/api/v4/projects/john%2Fexample/repository/files/catalog-info.yaml', + (req, res, ctx) => { + if (req.url.searchParams.get('ref') === 'master') { + return res(ctx.status(200)); + } + return res(ctx.status(404, 'Not Found')); + }, + ), + ); + + await provider.connect(entityProviderConnection); + + await provider.refresh(logger); + + expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({ + type: 'full', + entities: [ + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Location', + metadata: { + annotations: { + 'backstage.io/managed-by-location': + 'url:https://api.gitlab.example/john/example/-/blob/master/catalog-info.yaml', + 'backstage.io/managed-by-origin-location': + 'url:https://api.gitlab.example/john/example/-/blob/master/catalog-info.yaml', + }, + name: 'generated-2045212e5b3e9e6bacf51cec709e362282e3cda9', + }, + spec: { + presence: 'optional', + target: + 'https://api.gitlab.example/john/example/-/blob/master/catalog-info.yaml', + type: 'url', + }, + }, + locationKey: 'GitlabDiscoveryEntityProvider:test-id', + }, + ], + }); + }); + it('fail without schedule and scheduler', () => { const config = new ConfigReader({ integrations: { diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts index 6001a32a4a..08d2fe48a7 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts @@ -177,6 +177,13 @@ export class GitlabDiscoveryEntityProvider implements EntityProvider { continue; } + if ( + this.config.skipForkedRepos && + project.hasOwnProperty('forked_from_project') + ) { + continue; + } + if ( !this.config.branch && this.config.fallbackBranch === '*' && diff --git a/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts b/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts index 1035538939..bac56cc6f3 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts @@ -59,6 +59,7 @@ describe('config', () => { userPattern: /[\s\S]*/, orgEnabled: false, schedule: undefined, + skipForkedRepos: false, }), ); }); @@ -95,6 +96,45 @@ describe('config', () => { userPattern: /[\s\S]*/, orgEnabled: false, schedule: undefined, + skipForkedRepos: false, + }), + ); + }); + + it('valid config with skipForkedRepos', () => { + const config = new ConfigReader({ + catalog: { + providers: { + gitlab: { + test: { + group: 'group', + host: 'host', + branch: 'not-master', + fallbackBranch: 'main', + entityFilename: 'custom-file.yaml', + skipForkedRepos: true, + }, + }, + }, + }, + }); + + const result = readGitlabConfigs(config); + expect(result).toHaveLength(1); + result.forEach(r => + expect(r).toStrictEqual({ + id: 'test', + group: 'group', + branch: 'not-master', + fallbackBranch: 'main', + host: 'host', + catalogFile: 'custom-file.yaml', + projectPattern: /[\s\S]*/, + groupPattern: /[\s\S]*/, + userPattern: /[\s\S]*/, + orgEnabled: false, + schedule: undefined, + skipForkedRepos: true, }), ); }); @@ -133,6 +173,7 @@ describe('config', () => { groupPattern: /[\s\S]*/, userPattern: /[\s\S]*/, orgEnabled: false, + skipForkedRepos: false, schedule: { frequency: Duration.fromISO('PT30M'), timeout: { diff --git a/plugins/catalog-backend-module-gitlab/src/providers/config.ts b/plugins/catalog-backend-module-gitlab/src/providers/config.ts index cd15c4825c..c34cb81808 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/config.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/config.ts @@ -43,6 +43,8 @@ function readGitlabConfig(id: string, config: Config): GitlabProviderConfig { config.getOptionalString('groupPattern') ?? /[\s\S]*/, ); const orgEnabled: boolean = config.getOptionalBoolean('orgEnabled') ?? false; + const skipForkedRepos: boolean = + config.getOptionalBoolean('skipForkedRepos') ?? false; const schedule = config.has('schedule') ? readTaskScheduleDefinitionFromConfig(config.getConfig('schedule')) @@ -60,6 +62,7 @@ function readGitlabConfig(id: string, config: Config): GitlabProviderConfig { groupPattern, schedule, orgEnabled, + skipForkedRepos, }; } diff --git a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md index 63d850d68a..583458667d 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md +++ b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md @@ -1,5 +1,145 @@ # @backstage/plugin-catalog-backend-module-incremental-ingestion +## 0.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.12.0-next.0 + - @backstage/backend-common@0.19.2-next.0 + - @backstage/backend-plugin-api@0.5.5-next.0 + - @backstage/backend-tasks@0.5.5-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-catalog-node@1.4.1-next.0 + - @backstage/plugin-events-node@0.2.9-next.0 + - @backstage/plugin-permission-common@0.7.7 + +## 0.4.0 + +### Minor Changes + +- b1cc10696f2f: **BREAKING** Allow incremental event handlers to be async; Force event handler + to indicate if it made a change. Instead of returning `null` or `undefined` from an event + handler to indicate no-oop, instead return the value { type: "ignored" }. + + **before** + + ```javascript + import { createDelta, shouldIgnore } from "./my-delta-creater"; + + eventHandler: { + onEvent(params) { + if (shouldIgnore(params)) { + return; + } + return createDelta(params); + } + } + ``` + + **after** + + ```javascript + import { createDelta, shouldIgnore } from "./my-delta-creater"; + + eventHandler: { + async onEvent(params) { + if (shouldIgnore(params) { + return { type: "ignored" }; + } + // code to create delta can now be async if needed + return await createDelta(params); + } + } + ``` + +### Patch Changes + +- e1d615757f48: Update readme and instructions +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/backend-common@0.19.1 + - @backstage/plugin-catalog-backend@1.11.0 + - @backstage/plugin-catalog-node@1.4.0 + - @backstage/backend-plugin-api@0.5.4 + - @backstage/backend-tasks@0.5.4 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-events-node@0.2.8 + - @backstage/plugin-permission-common@0.7.7 + +## 0.4.0-next.1 + +### Minor Changes + +- b1cc10696f2f: **BREAKING** Allow incremental event handlers to be async; Force event handler + to indicate if it made a change. Instead of returning `null` or `undefined` from an event + handler to indicate no-oop, instead return the value { type: "ignored" }. + + **before** + + ```javascript + import { createDelta, shouldIgnore } from "./my-delta-creater"; + + eventHandler: { + onEvent(params) { + if (shouldIgnore(params)) { + return; + } + return createDelta(params); + } + } + ``` + + **after** + + ```javascript + import { createDelta, shouldIgnore } from "./my-delta-creater"; + + eventHandler: { + async onEvent(params) { + if (shouldIgnore(params) { + return { type: "ignored" }; + } + // code to create delta can now be async if needed + return await createDelta(params); + } + } + ``` + +### Patch Changes + +- e1d615757f48: Update readme and instructions +- Updated dependencies + - @backstage/backend-common@0.19.1-next.0 + - @backstage/backend-plugin-api@0.5.4-next.0 + - @backstage/backend-tasks@0.5.4-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1-next.0 + - @backstage/plugin-catalog-backend@1.11.0-next.0 + - @backstage/plugin-catalog-node@1.4.0-next.0 + - @backstage/plugin-events-node@0.2.8-next.0 + - @backstage/plugin-permission-common@0.7.7-next.0 + +## 0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/backend-common@0.19.1-next.0 + - @backstage/plugin-catalog-backend@1.11.0-next.0 + - @backstage/plugin-catalog-node@1.4.0-next.0 + - @backstage/backend-plugin-api@0.5.4-next.0 + - @backstage/backend-tasks@0.5.4-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/plugin-events-node@0.2.8-next.0 + - @backstage/plugin-permission-common@0.7.7-next.0 + ## 0.3.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-incremental-ingestion/README.md b/plugins/catalog-backend-module-incremental-ingestion/README.md index 6ba3e7c3ca..3728121540 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/README.md +++ b/plugins/catalog-backend-module-incremental-ingestion/README.md @@ -99,18 +99,18 @@ export default async function createPlugin( If you want to manage your incremental entity providers via REST endpoints, the following endpoints are available: -| Method | Path | Description | -| ------ | ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | -| GET | `/incremental/health` | Checks the health of all incremental providers. Returns array of any unhealthy ones. | -| GET | `/incremental/providers` | Get a list of all known incremental entity providers | -| GET | `/incremental/providers/:provider` | Checks the status of an incremental provider (resting, interstitial, etc). | -| POST | `/incremental/providers/:provider/trigger` | Triggers a provider's next action immediately. E.g., if it's currently interstitial, it will trigger the next burst. | -| POST | `/incremental/providers/:provider/start` | Stop the current ingestion cycle and start a new one immediately. | -| POST | `/incremental/providers/:provider/cancel` | Stop the current ingestion cycle and start a new one in 24 hours. | -| DELETE | `/incremental/providers/:provider` | Completely remove all records for the provider and schedule it to start again in 24 hours. | -| GET | `/incremental/providers/:provider/marks` | Retrieve a list of all ingestion marks for the current ingestion cycle. | -| DELETE | `/incremental/providers/:provider/marks` | Remove all ingestion marks for the current ingestion cycle. | -| POST | `/incremental/cleanup` | Completely remove all records for ALL providers and schedule them to start again in 24 hours. (CAUTION! Can cause orphans!) | +| Method | Path | Description | +| ------ | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | +| GET | `/api/catalog/incremental/health` | Checks the health of all incremental providers. Returns array of any unhealthy ones. | +| GET | `/api/catalog/incremental/providers` | Get a list of all known incremental entity providers | +| GET | `/api/catalog/incremental/providers/:provider` | Checks the status of an incremental provider (resting, interstitial, etc). | +| POST | `/api/catalog/incremental/providers/:provider/trigger` | Triggers a provider's next action immediately. E.g., if it's currently interstitial, it will trigger the next burst. | +| POST | `/api/catalog/incremental/providers/:provider/start` | Stop the current ingestion cycle and start a new one immediately. | +| POST | `/api/catalog/incremental/providers/:provider/cancel` | Stop the current ingestion cycle and start a new one in 24 hours. | +| DELETE | `/api/catalog/incremental/providers/:provider` | Completely remove all records for the provider and schedule it to start again in 24 hours. | +| GET | `/api/catalog/incremental/providers/:provider/marks` | Retrieve a list of all ingestion marks for the current ingestion cycle. | +| DELETE | `/api/catalog/incremental/providers/:provider/marks` | Remove all ingestion marks for the current ingestion cycle. | +| POST | `/api/catalog/incremental/cleanup` | Completely remove all records for ALL providers and schedule them to start again in 24 hours. (CAUTION! Can cause orphans!) | In all cases, `:provider` is the name of the incremental entity provider. @@ -323,22 +323,17 @@ incrementalBuilder.addIncrementalEntityProvider(myEntityProvider, { // How long should it attempt to read pages from the API in a // single burst? Keep this short. The Incremental Entity Provider // will attempt to read as many pages as it can in this time - burstLength: Duration.fromObject({ seconds: 3 }), + burstLength: { seconds: 3 }, // How long should it wait between bursts? - burstInterval: Duration.fromObject({ seconds: 3 }), + burstInterval: { seconds: 3 }, // How long should it rest before re-ingesting again? - restLength: Duration.fromObject({ day: 1 }), + restLength: { day: 1 }, // Optional back-off configuration - how long should it wait to retry // in the event of an error? - backoff: [ - Duration.fromObject({ seconds: 5 }), - Duration.fromObject({ seconds: 30 }), - Duration.fromObject({ minutes: 10 }), - Duration.fromObject({ hours: 3 }), - ], + backoff: [{ seconds: 5 }, { seconds: 30 }, { minutes: 10 }, { hours: 3 }], // Optional. Use this to prevent removal of entities above a given // percentage. This can be helpful if a data source is flaky and diff --git a/plugins/catalog-backend-module-incremental-ingestion/api-report.md b/plugins/catalog-backend-module-incremental-ingestion/api-report.md index c2fad4576c..4088b05bb5 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/api-report.md +++ b/plugins/catalog-backend-module-incremental-ingestion/api-report.md @@ -48,18 +48,24 @@ export class IncrementalCatalogBuilder { ): Promise; } +// @public +export type IncrementalEntityEventResult = + | { + type: 'ignored'; + } + | { + type: 'delta'; + added: DeferredEntity[]; + removed: { + entityRef: string; + }[]; + }; + // @public export interface IncrementalEntityProvider { around(burst: (context: TContext) => Promise): Promise; eventHandler?: { - onEvent: (params: EventParams) => - | undefined - | { - added: DeferredEntity[]; - removed: { - entityRef: string; - }[]; - }; + onEvent: (params: EventParams) => Promise; supportsEventTopics: () => string[]; }; getProviderName(): string; diff --git a/plugins/catalog-backend-module-incremental-ingestion/package.json b/plugins/catalog-backend-module-incremental-ingestion/package.json index 1d14965d4a..a6a947c93c 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/package.json +++ b/plugins/catalog-backend-module-incremental-ingestion/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-incremental-ingestion", "description": "An entity provider for streaming large asset sources into the catalog", - "version": "0.3.3", + "version": "0.4.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/engine/IncrementalIngestionEngine.ts b/plugins/catalog-backend-module-incremental-ingestion/src/engine/IncrementalIngestionEngine.ts index 13d5bfff71..0262dfd418 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/engine/IncrementalIngestionEngine.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/engine/IncrementalIngestionEngine.ts @@ -347,10 +347,10 @@ export class IncrementalIngestionEngine return; } - const delta = provider.eventHandler.onEvent(params); + const result = await provider.eventHandler.onEvent(params); - if (delta) { - if (delta.added.length > 0) { + if (result.type === 'delta') { + if (result.added.length > 0) { const ingestionRecord = await this.manager.getCurrentIngestionRecord( providerName, ); @@ -370,24 +370,21 @@ export class IncrementalIngestionEngine `Cannot apply delta, page records are missing! Please re-run incremental ingestion for ${providerName}.`, ); } - await this.manager.createMarkEntities(mark.id, delta.added); + await this.manager.createMarkEntities(mark.id, result.added); } } - if (delta.removed.length > 0) { - await this.manager.deleteEntityRecordsByRef(delta.removed); + if (result.removed.length > 0) { + await this.manager.deleteEntityRecordsByRef(result.removed); } - await connection.applyMutation({ - type: 'delta', - ...delta, - }); + await connection.applyMutation(result); logger.debug( `incremental-engine: ${providerName} processed delta from '${topic}' event`, ); } else { - logger.warn( - `incremental-engine: Rejected delta from '${topic}' event - empty or invalid`, + logger.debug( + `incremental-engine: ${providerName} ignored event from topic '${topic}'`, ); } } diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/index.ts b/plugins/catalog-backend-module-incremental-ingestion/src/index.ts index ff1ee1faa1..f381572149 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/index.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/index.ts @@ -23,6 +23,7 @@ export * from './service'; export { type EntityIteratorResult, + type IncrementalEntityEventResult, type IncrementalEntityProvider, type IncrementalEntityProviderOptions, type PluginEnvironment, diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/types.ts b/plugins/catalog-backend-module-incremental-ingestion/src/types.ts index 5f9d0489e6..68ad2b8b04 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/types.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/types.ts @@ -91,15 +91,12 @@ export interface IncrementalEntityProvider { * optionally maps the payload to an object containing a delta * mutation. * - * If a valid delta is returned by this method, it will be ingested - * automatically by the provider. + * If a delta result is returned by this method, it will be ingested + * automatically by the provider. Alternatively, if an "ignored" result is + * returned, then it is understood that this event should not cause anything + * to be ingested. */ - onEvent: (params: EventParams) => - | undefined - | { - added: DeferredEntity[]; - removed: { entityRef: string }[]; - }; + onEvent: (params: EventParams) => Promise; /** * This method returns an array of topics for the IncrementalEntityProvider @@ -109,6 +106,22 @@ export interface IncrementalEntityProvider { }; } +/** + * An object returned by event handler to indicate whether to ignore the event + * or to apply a delta in response to the event. + * + * @public + */ +export type IncrementalEntityEventResult = + | { + type: 'ignored'; + } + | { + type: 'delta'; + added: DeferredEntity[]; + removed: { entityRef: string }[]; + }; + /** * Value returned by an {@link IncrementalEntityProvider} to provide a * single page of entities to ingest. diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index 4a8fb212a2..4e9cabb577 100644 --- a/plugins/catalog-backend-module-ldap/CHANGELOG.md +++ b/plugins/catalog-backend-module-ldap/CHANGELOG.md @@ -1,5 +1,44 @@ # @backstage/plugin-catalog-backend-module-ldap +## 0.5.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.5-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-catalog-node@1.4.1-next.0 + +## 0.5.14 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/plugin-catalog-node@1.4.0 + - @backstage/backend-tasks@0.5.4 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + +## 0.5.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/plugin-catalog-node@1.4.0-next.0 + - @backstage/backend-tasks@0.5.4-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15-next.0 + ## 0.5.13 ### Patch Changes diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index 0bf799cb39..742065735d 100644 --- a/plugins/catalog-backend-module-ldap/package.json +++ b/plugins/catalog-backend-module-ldap/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-ldap", "description": "A Backstage catalog backend module that helps integrate towards LDAP", - "version": "0.5.13", + "version": "0.5.15-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-msgraph/CHANGELOG.md b/plugins/catalog-backend-module-msgraph/CHANGELOG.md index 54fba3e580..0edda3b91d 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,44 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.5.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.0 + - @backstage/backend-plugin-api@0.5.5-next.0 + - @backstage/backend-tasks@0.5.5-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-catalog-node@1.4.1-next.0 + +## 0.5.6 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.1 + - @backstage/plugin-catalog-node@1.4.0 + - @backstage/backend-plugin-api@0.5.4 + - @backstage/backend-tasks@0.5.4 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-catalog-common@1.0.15 + +## 0.5.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.1-next.0 + - @backstage/plugin-catalog-node@1.4.0-next.0 + - @backstage/backend-plugin-api@0.5.4-next.0 + - @backstage/backend-tasks@0.5.4-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/plugin-catalog-common@1.0.15-next.0 + ## 0.5.5 ### Patch Changes diff --git a/plugins/catalog-backend-module-msgraph/config.d.ts b/plugins/catalog-backend-module-msgraph/config.d.ts index 904ce1f646..9d341215cd 100644 --- a/plugins/catalog-backend-module-msgraph/config.d.ts +++ b/plugins/catalog-backend-module-msgraph/config.d.ts @@ -209,9 +209,8 @@ export interface Config { */ schedule?: TaskScheduleDefinitionConfig; } - | Record< - string, - { + | { + [name: string]: { /** * The prefix of the target that this matches on, e.g. * "https://graph.microsoft.com/v1.0", with no trailing slash. @@ -296,8 +295,8 @@ export interface Config { * (Optional) TaskScheduleDefinition for the refresh. */ schedule?: TaskScheduleDefinitionConfig; - } - >; + }; + }; }; }; } diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index a480450d22..c802b15d16 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-msgraph", "description": "A Backstage catalog backend module that helps integrate towards Microsoft Graph", - "version": "0.5.5", + "version": "0.5.7-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-openapi/CHANGELOG.md b/plugins/catalog-backend-module-openapi/CHANGELOG.md index 4a5121d4dc..8b6a4c3162 100644 --- a/plugins/catalog-backend-module-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-openapi/CHANGELOG.md @@ -1,5 +1,47 @@ # @backstage/plugin-catalog-backend-module-openapi +## 0.1.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.12.0-next.0 + - @backstage/backend-common@0.19.2-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-catalog-node@1.4.1-next.0 + +## 0.1.13 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.1 + - @backstage/plugin-catalog-backend@1.11.0 + - @backstage/plugin-catalog-node@1.4.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + +## 0.1.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.1-next.0 + - @backstage/plugin-catalog-backend@1.11.0-next.0 + - @backstage/plugin-catalog-node@1.4.0-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1-next.0 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15-next.0 + ## 0.1.12 ### Patch Changes diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index fda0603aa8..cbc42851d9 100644 --- a/plugins/catalog-backend-module-openapi/package.json +++ b/plugins/catalog-backend-module-openapi/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-openapi", "description": "A Backstage catalog backend module that helps with OpenAPI specifications", - "version": "0.1.12", + "version": "0.1.14-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md index 8d5d5d18ce..d7606911e3 100644 --- a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md +++ b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md @@ -1,5 +1,47 @@ # @backstage/plugin-catalog-backend-module-puppetdb +## 0.1.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.0 + - @backstage/backend-plugin-api@0.5.5-next.0 + - @backstage/backend-tasks@0.5.5-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-node@1.4.1-next.0 + +## 0.1.4 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/backend-common@0.19.1 + - @backstage/plugin-catalog-node@1.4.0 + - @backstage/backend-plugin-api@0.5.4 + - @backstage/backend-tasks@0.5.4 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + +## 0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/backend-common@0.19.1-next.0 + - @backstage/plugin-catalog-node@1.4.0-next.0 + - @backstage/backend-plugin-api@0.5.4-next.0 + - @backstage/backend-tasks@0.5.4-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + ## 0.1.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-puppetdb/config.d.ts b/plugins/catalog-backend-module-puppetdb/config.d.ts index edd7ab3889..8aab239047 100644 --- a/plugins/catalog-backend-module-puppetdb/config.d.ts +++ b/plugins/catalog-backend-module-puppetdb/config.d.ts @@ -46,9 +46,8 @@ export interface Config { */ schedule?: TaskScheduleDefinition; } - | Record< - string, - { + | { + [name: string]: { /** * (Required) The base URL of PuppetDB API instance. */ @@ -61,8 +60,8 @@ export interface Config { * (Optional) Task schedule definition for the refresh. */ schedule?: TaskScheduleDefinition; - } - >; + }; + }; }; }; } diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index 6ce5d63b85..8adbd65941 100644 --- a/plugins/catalog-backend-module-puppetdb/package.json +++ b/plugins/catalog-backend-module-puppetdb/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-puppetdb", "description": "A Backstage catalog backend module that helps integrate towards PuppetDB", - "version": "0.1.3", + "version": "0.1.5-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md index 8b0390aff2..999c7cf85f 100644 --- a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md +++ b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/plugin-catalog-backend-module-unprocessed +## 0.2.0-next.0 + +### Minor Changes + +- 5156a94c2e2a: **BREAKING**: Fixing typo in exported module. You will have to rename the import to the correct spelling. `UnprocessedEntites` -> `UnprocessedEntities` + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.5.5-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/plugin-auth-node@0.2.17-next.0 + +## 0.1.1 + +### Patch Changes + +- a8fa79ccc105: Fix and improve documentation for the unprocessed entities modules. +- Updated dependencies + - @backstage/backend-plugin-api@0.5.4 + - @backstage/catalog-model@1.4.1 + - @backstage/plugin-auth-node@0.2.16 + +## 0.1.1-next.0 + +### Patch Changes + +- a8fa79ccc105: Fix and improve documentation for the unprocessed entities modules. +- Updated dependencies + - @backstage/backend-plugin-api@0.5.4-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/plugin-auth-node@0.2.16-next.0 + ## 0.1.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-unprocessed/README.md b/plugins/catalog-backend-module-unprocessed/README.md index dc5e830fa4..f2ea7b7741 100644 --- a/plugins/catalog-backend-module-unprocessed/README.md +++ b/plugins/catalog-backend-module-unprocessed/README.md @@ -1,4 +1,4 @@ -# @backstage/plugin-catalog-backend-module-unprocessed-node +# @backstage/plugin-catalog-backend-module-unprocessed This catalog-backend module adds support for viewing unprocessed entities. An unprocessed entity is one that doesn't show up in the catalog. @@ -10,19 +10,22 @@ A `pending` entity has not been processed yet. ## Installation +```shell +yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-unprocessed +``` + ### backend In `packages/backend/src/plugins/catalog.ts` import the module and initialize it after invoking `CatalogBuilder.build()`: -```ts -import { UnprocessedEntitesModule } from '@backstage/plugin-catalog-backend-module-unprocessed'; +```ts title="packages/backend/src/plugins/catalog.ts" +import { UnprocessedEntitiesModule } from '@backstage/plugin-catalog-backend-module-unprocessed'; //... -const unprocessed = new UnprocessedEntitesModule( +const unprocessed = new UnprocessedEntitiesModule( await env.database.getClient(), router, - env.logger, ); unprocessed.registerRoutes(); ``` @@ -31,7 +34,7 @@ unprocessed.registerRoutes(); In `packages/backend-next/src/index.ts` add the module: -```ts +```ts title="packages/backend-next/src/index.ts" backend.add(catalogModuleUnprocessedEntities()); ``` diff --git a/plugins/catalog-backend-module-unprocessed/api-report.md b/plugins/catalog-backend-module-unprocessed/api-report.md index 3fc81c62cc..7fd7119276 100644 --- a/plugins/catalog-backend-module-unprocessed/api-report.md +++ b/plugins/catalog-backend-module-unprocessed/api-report.md @@ -11,7 +11,7 @@ import { Knex } from 'knex'; export const catalogModuleUnprocessedEntities: () => BackendFeature; // @public -export class UnprocessedEntitesModule { +export class UnprocessedEntitiesModule { constructor(database: Knex, router: HttpRouterService); // (undocumented) registerRoutes(): void; diff --git a/plugins/catalog-backend-module-unprocessed/package.json b/plugins/catalog-backend-module-unprocessed/package.json index 5c2d19f61c..e9ca4662ac 100644 --- a/plugins/catalog-backend-module-unprocessed/package.json +++ b/plugins/catalog-backend-module-unprocessed/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-unprocessed", "description": "Backstage Catalog module to view unprocessed entities", - "version": "0.1.0", + "version": "0.2.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -13,6 +13,12 @@ "backstage": { "role": "backend-plugin-module" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/catalog-backend-module-unprocessed" + }, "scripts": { "build": "backstage-cli package build", "lint": "backstage-cli package lint", diff --git a/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts b/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts index 6ea184ce83..bdd3f64488 100644 --- a/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts +++ b/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts @@ -30,7 +30,7 @@ import { getBearerTokenFromAuthorizationHeader } from '@backstage/plugin-auth-no * * @public */ -export class UnprocessedEntitesModule { +export class UnprocessedEntitiesModule { private readonly moduleRouter; constructor( diff --git a/plugins/catalog-backend-module-unprocessed/src/module.ts b/plugins/catalog-backend-module-unprocessed/src/module.ts index 1c4b6c658f..1db4fd70b0 100644 --- a/plugins/catalog-backend-module-unprocessed/src/module.ts +++ b/plugins/catalog-backend-module-unprocessed/src/module.ts @@ -18,7 +18,7 @@ import { coreServices, createBackendModule, } from '@backstage/backend-plugin-api'; -import { UnprocessedEntitesModule } from './UnprocessedEntitiesModule'; +import { UnprocessedEntitiesModule } from './UnprocessedEntitiesModule'; /** * Catalog Module for Unprocessed Entities @@ -36,7 +36,7 @@ export const catalogModuleUnprocessedEntities = createBackendModule({ logger: coreServices.logger, }, async init({ database, router, logger }) { - const module = new UnprocessedEntitesModule( + const module = new UnprocessedEntitiesModule( await database.getClient(), router, ); diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index de0075c016..0c0f3c7be8 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,90 @@ # @backstage/plugin-catalog-backend +## 1.12.0-next.0 + +### Minor Changes + +- ebeb77586975: Now performs request validation based on OpenAPI schema through `@backstage/backend-openapi-utils`. Error responses for invalid input, like `"a"` instead of a number, may have changed. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-openapi-utils@0.0.3-next.0 + - @backstage/backend-common@0.19.2-next.0 + - @backstage/backend-plugin-api@0.5.5-next.0 + - @backstage/backend-tasks@0.5.5-next.0 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/integration@1.5.1 + - @backstage/types@1.1.0 + - @backstage/plugin-auth-node@0.2.17-next.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-catalog-node@1.4.1-next.0 + - @backstage/plugin-events-node@0.2.9-next.0 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-permission-node@0.7.11-next.0 + - @backstage/plugin-scaffolder-common@1.3.2 + - @backstage/plugin-search-backend-module-catalog@0.1.4-next.0 + - @backstage/plugin-search-common@1.2.5 + +## 1.11.0 + +### Minor Changes + +- f06f0e46ba88: Support placeholder resolvers in the CatalogPlugin, also moves `PlaceholderResolver` and related types from `@backstage/plugin-catalog-backend` to `@backstage/plugin-catalog-node`. + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/backend-common@0.19.1 + - @backstage/plugin-catalog-node@1.4.0 + - @backstage/backend-plugin-api@0.5.4 + - @backstage/backend-tasks@0.5.4 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1 + - @backstage/types@1.1.0 + - @backstage/plugin-auth-node@0.2.16 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-events-node@0.2.8 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-permission-node@0.7.10 + - @backstage/plugin-scaffolder-common@1.3.2 + - @backstage/plugin-search-backend-module-catalog@0.1.3 + - @backstage/plugin-search-common@1.2.5 + +## 1.11.0-next.0 + +### Minor Changes + +- f06f0e46ba88: Support placeholder resolvers in the CatalogPlugin, also moves `PlaceholderResolver` and related types from `@backstage/plugin-catalog-backend` to `@backstage/plugin-catalog-node`. + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/backend-common@0.19.1-next.0 + - @backstage/plugin-catalog-node@1.4.0-next.0 + - @backstage/backend-plugin-api@0.5.4-next.0 + - @backstage/backend-tasks@0.5.4-next.0 + - @backstage/catalog-client@1.4.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1-next.0 + - @backstage/types@1.1.0 + - @backstage/plugin-auth-node@0.2.16-next.0 + - @backstage/plugin-catalog-common@1.0.15-next.0 + - @backstage/plugin-events-node@0.2.8-next.0 + - @backstage/plugin-permission-common@0.7.7-next.0 + - @backstage/plugin-permission-node@0.7.10-next.0 + - @backstage/plugin-scaffolder-common@1.3.2-next.0 + - @backstage/plugin-search-backend-module-catalog@0.1.3-next.0 + - @backstage/plugin-search-common@1.2.5-next.0 + ## 1.10.0 ### Minor Changes diff --git a/plugins/catalog-backend/alpha-api-report.md b/plugins/catalog-backend/alpha-api-report.md index 8fd0eb571f..df3e809ade 100644 --- a/plugins/catalog-backend/alpha-api-report.md +++ b/plugins/catalog-backend/alpha-api-report.md @@ -79,7 +79,9 @@ export const catalogPlugin: () => BackendFeature; // @alpha export const createCatalogConditionalDecision: ( permission: ResourcePermission<'catalog-entity'>, - conditions: PermissionCriteria>, + conditions: PermissionCriteria< + PermissionCondition<'catalog-entity', PermissionRuleParams> + >, ) => ConditionalPolicyDecision; // @alpha diff --git a/plugins/catalog-backend/api-report.md b/plugins/catalog-backend/api-report.md index 3e97442dd4..84d8de4753 100644 --- a/plugins/catalog-backend/api-report.md +++ b/plugins/catalog-backend/api-report.md @@ -410,12 +410,12 @@ export type PlaceholderProcessorOptions = { integrations: ScmIntegrationRegistry; }; -// @public (undocumented) +// @public @deprecated (undocumented) export type PlaceholderResolver = ( params: PlaceholderResolverParams, ) => Promise; -// @public (undocumented) +// @public @deprecated (undocumented) export type PlaceholderResolverParams = { key: string; value: JsonValue; @@ -425,10 +425,10 @@ export type PlaceholderResolverParams = { emit: CatalogProcessorEmit_2; }; -// @public (undocumented) +// @public @deprecated (undocumented) export type PlaceholderResolverRead = (url: string) => Promise; -// @public (undocumented) +// @public @deprecated (undocumented) export type PlaceholderResolverResolveUrl = ( url: string, base: string, diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 5afaa938f3..8b4edcbc20 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend", "description": "The Backstage backend plugin that provides the Backstage catalog", - "version": "1.10.0", + "version": "1.12.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -46,6 +46,7 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-openapi-utils": "workspace:^", "@backstage/backend-plugin-api": "workspace:^", "@backstage/backend-tasks": "workspace:^", "@backstage/catalog-client": "workspace:^", @@ -87,7 +88,6 @@ "zod": "^3.21.4" }, "devDependencies": { - "@backstage/backend-openapi-utils": "workspace:^", "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@backstage/plugin-permission-common": "workspace:^", diff --git a/plugins/catalog-backend/src/database/DefaultProviderDatabase.ts b/plugins/catalog-backend/src/database/DefaultProviderDatabase.ts index a0d376327b..2a97d1f2a9 100644 --- a/plugins/catalog-backend/src/database/DefaultProviderDatabase.ts +++ b/plugins/catalog-backend/src/database/DefaultProviderDatabase.ts @@ -81,7 +81,7 @@ export class DefaultProviderDatabase implements ProviderDatabase { if (toRemove.length) { const removedCount = await deleteWithEagerPruningOfChildren({ - tx, + knex: tx, entityRefs: toRemove, sourceKey: options.sourceKey, }); diff --git a/plugins/catalog-backend/src/database/operations/provider/deleteWithEagerPruningOfChildren.test.ts b/plugins/catalog-backend/src/database/operations/provider/deleteWithEagerPruningOfChildren.test.ts index e3cc813c96..dcd87613de 100644 --- a/plugins/catalog-backend/src/database/operations/provider/deleteWithEagerPruningOfChildren.test.ts +++ b/plugins/catalog-backend/src/database/operations/provider/deleteWithEagerPruningOfChildren.test.ts @@ -34,26 +34,6 @@ describe('deleteWithEagerPruningOfChildren', () => { return knex; } - async function run( - knex: Knex, - options: { entityRefs: string[]; sourceKey: string }, - ): Promise { - let result: number; - await knex.transaction( - async tx => { - // We can't return here, as knex swallows the return type in case the - // transaction is rolled back: - // https://github.com/knex/knex/blob/e37aeaa31c8ef9c1b07d2e4d3ec6607e557d800d/lib/transaction.js#L136 - result = await deleteWithEagerPruningOfChildren({ tx, ...options }); - }, - { - // If we explicitly trigger a rollback, don't fail. - doNotRejectOnRollback: true, - }, - ); - return result!; - } - async function insertReference( knex: Knex, ...refs: DbRefreshStateReferencesRow[] @@ -110,7 +90,11 @@ describe('deleteWithEagerPruningOfChildren', () => { { source_key: 'P1', target_entity_ref: 'E4' }, { source_key: 'P2', target_entity_ref: 'E5' }, ); - await run(knex, { sourceKey: 'P1', entityRefs: ['E1', 'E3'] }); + await deleteWithEagerPruningOfChildren({ + knex, + sourceKey: 'P1', + entityRefs: ['E1', 'E3'], + }); await expect(remainingEntities(knex)).resolves.toEqual(['E4', 'E5']); }, ); @@ -139,7 +123,11 @@ describe('deleteWithEagerPruningOfChildren', () => { { source_key: 'P1', target_entity_ref: 'E1' }, { source_key: 'P1', target_entity_ref: 'E2' }, ); - await run(knex, { sourceKey: 'P1', entityRefs: ['E1'] }); + await deleteWithEagerPruningOfChildren({ + knex, + sourceKey: 'P1', + entityRefs: ['E1'], + }); await expect(remainingEntities(knex)).resolves.toEqual(['E2']); }, ); @@ -167,7 +155,11 @@ describe('deleteWithEagerPruningOfChildren', () => { { source_key: 'P2', target_entity_ref: 'E3' }, { source_entity_ref: 'E3', target_entity_ref: 'E2' }, ); - await run(knex, { sourceKey: 'P1', entityRefs: ['E1'] }); + await deleteWithEagerPruningOfChildren({ + knex, + sourceKey: 'P1', + entityRefs: ['E1'], + }); await expect(remainingEntities(knex)).resolves.toEqual(['E2', 'E3']); }, ); @@ -195,7 +187,11 @@ describe('deleteWithEagerPruningOfChildren', () => { { source_key: 'P1', target_entity_ref: 'E3' }, { source_entity_ref: 'E3', target_entity_ref: 'E2' }, ); - await run(knex, { sourceKey: 'P1', entityRefs: ['E1'] }); + await deleteWithEagerPruningOfChildren({ + knex, + sourceKey: 'P1', + entityRefs: ['E1'], + }); await expect(remainingEntities(knex)).resolves.toEqual(['E2', 'E3']); }, ); @@ -228,14 +224,22 @@ describe('deleteWithEagerPruningOfChildren', () => { { source_entity_ref: 'E3', target_entity_ref: 'E5' }, { source_entity_ref: 'E5', target_entity_ref: 'E6' }, ); - await run(knex, { sourceKey: 'P1', entityRefs: ['E1'] }); + await deleteWithEagerPruningOfChildren({ + knex, + sourceKey: 'P1', + entityRefs: ['E1'], + }); await expect(remainingEntities(knex)).resolves.toEqual([ 'E3', 'E4', 'E5', 'E6', ]); - await run(knex, { sourceKey: 'P1', entityRefs: ['E3'] }); + await deleteWithEagerPruningOfChildren({ + knex, + sourceKey: 'P1', + entityRefs: ['E3'], + }); await expect(remainingEntities(knex)).resolves.toEqual([]); }, ); @@ -263,7 +267,11 @@ describe('deleteWithEagerPruningOfChildren', () => { { source_key: 'P1', target_entity_ref: 'E3' }, { source_key: 'P2', target_entity_ref: 'E4' }, ); - await run(knex, { sourceKey: 'P1', entityRefs: ['E2', 'E3', 'E4'] }); + await deleteWithEagerPruningOfChildren({ + knex, + sourceKey: 'P1', + entityRefs: ['E2', 'E3', 'E4'], + }); await expect(remainingEntities(knex)).resolves.toEqual([ 'E1', 'E2', diff --git a/plugins/catalog-backend/src/database/operations/provider/deleteWithEagerPruningOfChildren.ts b/plugins/catalog-backend/src/database/operations/provider/deleteWithEagerPruningOfChildren.ts index 33da37bc55..027860fcc6 100644 --- a/plugins/catalog-backend/src/database/operations/provider/deleteWithEagerPruningOfChildren.ts +++ b/plugins/catalog-backend/src/database/operations/provider/deleteWithEagerPruningOfChildren.ts @@ -26,17 +26,17 @@ import { DbRefreshStateReferencesRow } from '../../tables'; * the removal of their parents. */ export async function deleteWithEagerPruningOfChildren(options: { - tx: Knex.Transaction; + knex: Knex | Knex.Transaction; entityRefs: string[]; sourceKey: string; }): Promise { - const { tx, entityRefs, sourceKey } = options; + const { knex, entityRefs, sourceKey } = options; // Split up the operation by (large) chunks, so that we do not hit database // limits for the number of permitted bindings on a precompiled statement let removedCount = 0; for (const refs of lodash.chunk(entityRefs, 1000)) { - removedCount += await tx + removedCount += await knex .delete() .from('refresh_state') .whereIn('entity_ref', orphans => @@ -186,7 +186,7 @@ export async function deleteWithEagerPruningOfChildren(options: { // Delete the references that originate only from this entity provider. Note // that there may be more than one entity provider making a "claim" for a // given root entity, if they emit with the same location key. - await tx('refresh_state_references') + await knex('refresh_state_references') .where('source_key', '=', sourceKey) .whereIn('target_entity_ref', refs) .delete(); diff --git a/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.test.ts b/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.test.ts index 352ebf5df9..f5e494f514 100644 --- a/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.test.ts +++ b/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.test.ts @@ -518,7 +518,7 @@ describe('jsonPlaceholderResolver', () => { it('rejects invalid json', async () => { read.mockResolvedValue(Buffer.from('}', 'utf-8')); await expect(jsonPlaceholderResolver(params)).rejects.toThrow( - 'Placeholder $a failed to parse JSON data at ./file.json, SyntaxError: Unexpected token } in JSON at position 0', + 'Placeholder $a failed to parse JSON data at ./file.json', ); }); }); diff --git a/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.ts b/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.ts index 79fcee960c..ee61e931e3 100644 --- a/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.ts +++ b/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.ts @@ -26,16 +26,25 @@ import { processingResult, } from '@backstage/plugin-catalog-node'; -/** @public */ +/** + * @public + * @deprecated Use the exported value from `@backstage/plugin-catalog-node` instead. + */ export type PlaceholderResolverRead = (url: string) => Promise; -/** @public */ +/** + * @public + * @deprecated Use the exported value from `@backstage/plugin-catalog-node` instead. + */ export type PlaceholderResolverResolveUrl = ( url: string, base: string, ) => string; -/** @public */ +/** + * @public + * @deprecated Use the exported value from `@backstage/plugin-catalog-node` instead. + */ export type PlaceholderResolverParams = { key: string; value: JsonValue; @@ -45,7 +54,10 @@ export type PlaceholderResolverParams = { emit: CatalogProcessorEmit; }; -/** @public */ +/** + * @public + * @deprecated Use the exported value from `@backstage/plugin-catalog-node` instead. + */ export type PlaceholderResolver = ( params: PlaceholderResolverParams, ) => Promise; diff --git a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts index 8e2f8b2f6f..bed1fe6f50 100644 --- a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts @@ -23,7 +23,7 @@ import { assertError, serializeError, stringifyError } from '@backstage/errors'; import { Hash } from 'crypto'; import stableStringify from 'fast-json-stable-stringify'; import { Logger } from 'winston'; -import { metrics } from '@opentelemetry/api'; +import { metrics, trace } from '@opentelemetry/api'; import { ProcessingDatabase, RefreshStateItem } from '../database/types'; import { createCounterMetric, createSummaryMetric } from '../util/metrics'; import { @@ -35,9 +35,16 @@ import { Stitcher } from '../stitching/Stitcher'; import { startTaskPipeline } from './TaskPipeline'; import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { Config } from '@backstage/config'; +import { + addEntityAttributes, + TRACER_ID, + withActiveSpan, +} from '../util/opentelemetry'; const CACHE_TTL = 5; +const tracer = trace.getTracer(TRACER_ID); + export type ProgressTracker = ReturnType; export class DefaultCatalogProcessingEngine implements CatalogProcessingEngine { @@ -131,177 +138,181 @@ export class DefaultCatalogProcessingEngine implements CatalogProcessingEngine { } }, processTask: async item => { - const track = this.tracker.processStart(item, this.logger); + await withActiveSpan(tracer, 'ProcessingRun', async span => { + const track = this.tracker.processStart(item, this.logger); + addEntityAttributes(span, item.unprocessedEntity); - try { - const { - id, - state, - unprocessedEntity, - entityRef, - locationKey, - resultHash: previousResultHash, - } = item; - const result = await this.orchestrator.process({ - entity: unprocessedEntity, - state, - }); + try { + const { + id, + state, + unprocessedEntity, + entityRef, + locationKey, + resultHash: previousResultHash, + } = item; + const result = await this.orchestrator.process({ + entity: unprocessedEntity, + state, + }); - track.markProcessorsCompleted(result); + track.markProcessorsCompleted(result); - if (result.ok) { - const { ttl: _, ...stateWithoutTtl } = state ?? {}; - if ( - stableStringify(stateWithoutTtl) !== stableStringify(result.state) - ) { + if (result.ok) { + const { ttl: _, ...stateWithoutTtl } = state ?? {}; + if ( + stableStringify(stateWithoutTtl) !== + stableStringify(result.state) + ) { + await this.processingDatabase.transaction(async tx => { + await this.processingDatabase.updateEntityCache(tx, { + id, + state: { + ttl: CACHE_TTL, + ...result.state, + }, + }); + }); + } + } else { + const maybeTtl = state?.ttl; + const ttl = Number.isInteger(maybeTtl) ? (maybeTtl as number) : 0; await this.processingDatabase.transaction(async tx => { await this.processingDatabase.updateEntityCache(tx, { id, - state: { - ttl: CACHE_TTL, - ...result.state, - }, + state: ttl > 0 ? { ...state, ttl: ttl - 1 } : {}, }); }); } - } else { - const maybeTtl = state?.ttl; - const ttl = Number.isInteger(maybeTtl) ? (maybeTtl as number) : 0; - await this.processingDatabase.transaction(async tx => { - await this.processingDatabase.updateEntityCache(tx, { - id, - state: ttl > 0 ? { ...state, ttl: ttl - 1 } : {}, + + const location = + unprocessedEntity?.metadata?.annotations?.[ANNOTATION_LOCATION]; + for (const error of result.errors) { + this.logger.warn(error.message, { + entity: entityRef, + location, }); - }); - } + } + const errorsString = JSON.stringify( + result.errors.map(e => serializeError(e)), + ); - const location = - unprocessedEntity?.metadata?.annotations?.[ANNOTATION_LOCATION]; - for (const error of result.errors) { - this.logger.warn(error.message, { - entity: entityRef, - location, - }); - } - const errorsString = JSON.stringify( - result.errors.map(e => serializeError(e)), - ); + let hashBuilder = this.createHash().update(errorsString); - let hashBuilder = this.createHash().update(errorsString); - - if (result.ok) { - const { entityRefs: parents } = - await this.processingDatabase.transaction(tx => - this.processingDatabase.listParents(tx, { - entityRef, - }), - ); - - hashBuilder = hashBuilder - .update(stableStringify({ ...result.completedEntity })) - .update(stableStringify([...result.deferredEntities])) - .update(stableStringify([...result.relations])) - .update(stableStringify([...result.refreshKeys])) - .update(stableStringify([...parents])); - } - - const resultHash = hashBuilder.digest('hex'); - if (resultHash === previousResultHash) { - // If nothing changed in our produced outputs, we cannot have any - // significant effect on our surroundings; therefore, we just abort - // without any updates / stitching. - track.markSuccessfulWithNoChanges(); - return; - } - - // If the result was marked as not OK, it signals that some part of the - // processing pipeline threw an exception. This can happen both as part of - // non-catastrophic things such as due to validation errors, as well as if - // something fatal happens inside the processing for other reasons. In any - // case, this means we can't trust that anything in the output is okay. So - // just store the errors and trigger a stich so that they become visible to - // the outside. - if (!result.ok) { - // notify the error listener if the entity can not be processed. - Promise.resolve(undefined) - .then(() => - this.onProcessingError?.({ - unprocessedEntity, - errors: result.errors, - }), - ) - .catch(error => { - this.logger.debug( - `Processing error listener threw an exception, ${stringifyError( - error, - )}`, + if (result.ok) { + const { entityRefs: parents } = + await this.processingDatabase.transaction(tx => + this.processingDatabase.listParents(tx, { + entityRef, + }), ); - }); + hashBuilder = hashBuilder + .update(stableStringify({ ...result.completedEntity })) + .update(stableStringify([...result.deferredEntities])) + .update(stableStringify([...result.relations])) + .update(stableStringify([...result.refreshKeys])) + .update(stableStringify([...parents])); + } + + const resultHash = hashBuilder.digest('hex'); + if (resultHash === previousResultHash) { + // If nothing changed in our produced outputs, we cannot have any + // significant effect on our surroundings; therefore, we just abort + // without any updates / stitching. + track.markSuccessfulWithNoChanges(); + return; + } + + // If the result was marked as not OK, it signals that some part of the + // processing pipeline threw an exception. This can happen both as part of + // non-catastrophic things such as due to validation errors, as well as if + // something fatal happens inside the processing for other reasons. In any + // case, this means we can't trust that anything in the output is okay. So + // just store the errors and trigger a stich so that they become visible to + // the outside. + if (!result.ok) { + // notify the error listener if the entity can not be processed. + Promise.resolve(undefined) + .then(() => + this.onProcessingError?.({ + unprocessedEntity, + errors: result.errors, + }), + ) + .catch(error => { + this.logger.debug( + `Processing error listener threw an exception, ${stringifyError( + error, + )}`, + ); + }); + + await this.processingDatabase.transaction(async tx => { + await this.processingDatabase.updateProcessedEntityErrors(tx, { + id, + errors: errorsString, + resultHash, + }); + }); + await this.stitcher.stitch( + new Set([stringifyEntityRef(unprocessedEntity)]), + ); + track.markSuccessfulWithErrors(); + return; + } + + result.completedEntity.metadata.uid = id; + let oldRelationSources: Map; await this.processingDatabase.transaction(async tx => { - await this.processingDatabase.updateProcessedEntityErrors(tx, { - id, - errors: errorsString, - resultHash, - }); + const { previous } = + await this.processingDatabase.updateProcessedEntity(tx, { + id, + processedEntity: result.completedEntity, + resultHash, + errors: errorsString, + relations: result.relations, + deferredEntities: result.deferredEntities, + locationKey, + refreshKeys: result.refreshKeys, + }); + oldRelationSources = new Map( + previous.relations.map(r => [ + `${r.source_entity_ref}:${r.type}`, + r.source_entity_ref, + ]), + ); }); - await this.stitcher.stitch( - new Set([stringifyEntityRef(unprocessedEntity)]), + + const newRelationSources = new Map( + result.relations.map(relation => { + const sourceEntityRef = stringifyEntityRef(relation.source); + return [`${sourceEntityRef}:${relation.type}`, sourceEntityRef]; + }), ); - track.markSuccessfulWithErrors(); - return; + + const setOfThingsToStitch = new Set([ + stringifyEntityRef(result.completedEntity), + ]); + newRelationSources.forEach((sourceEntityRef, uniqueKey) => { + if (!oldRelationSources.has(uniqueKey)) { + setOfThingsToStitch.add(sourceEntityRef); + } + }); + oldRelationSources!.forEach((sourceEntityRef, uniqueKey) => { + if (!newRelationSources.has(uniqueKey)) { + setOfThingsToStitch.add(sourceEntityRef); + } + }); + + await this.stitcher.stitch(setOfThingsToStitch); + + track.markSuccessfulWithChanges(setOfThingsToStitch.size); + } catch (error) { + assertError(error); + track.markFailed(error); } - - result.completedEntity.metadata.uid = id; - let oldRelationSources: Map; - await this.processingDatabase.transaction(async tx => { - const { previous } = - await this.processingDatabase.updateProcessedEntity(tx, { - id, - processedEntity: result.completedEntity, - resultHash, - errors: errorsString, - relations: result.relations, - deferredEntities: result.deferredEntities, - locationKey, - refreshKeys: result.refreshKeys, - }); - oldRelationSources = new Map( - previous.relations.map(r => [ - `${r.source_entity_ref}:${r.type}`, - r.source_entity_ref, - ]), - ); - }); - - const newRelationSources = new Map( - result.relations.map(relation => { - const sourceEntityRef = stringifyEntityRef(relation.source); - return [`${sourceEntityRef}:${relation.type}`, sourceEntityRef]; - }), - ); - - const setOfThingsToStitch = new Set([ - stringifyEntityRef(result.completedEntity), - ]); - newRelationSources.forEach((sourceEntityRef, uniqueKey) => { - if (!oldRelationSources.has(uniqueKey)) { - setOfThingsToStitch.add(sourceEntityRef); - } - }); - oldRelationSources!.forEach((sourceEntityRef, uniqueKey) => { - if (!newRelationSources.has(uniqueKey)) { - setOfThingsToStitch.add(sourceEntityRef); - } - }); - - await this.stitcher.stitch(setOfThingsToStitch); - - track.markSuccessfulWithChanges(setOfThingsToStitch.size); - } catch (error) { - assertError(error); - track.markFailed(error); - } + }); }, }); } diff --git a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.test.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.test.ts index ceb6992d7a..fc6855f6df 100644 --- a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.test.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.test.ts @@ -194,10 +194,12 @@ describe('DefaultCatalogProcessingOrchestrator', () => { it('runs all processor validations when asked to', async () => { const validate = jest.fn(async () => true); - const processor1: Partial = { + const processor1: CatalogProcessor = { + getProcessorName: () => 'processor1', validateEntityKind: validate, }; - const processor2: Partial = { + const processor2: CatalogProcessor = { + getProcessorName: () => 'processor2', validateEntityKind: validate, }; diff --git a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts index d177a7d4a8..c5eaacbc68 100644 --- a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Span, trace } from '@opentelemetry/api'; import { Entity, EntityPolicy, @@ -55,6 +56,13 @@ import { } from './util'; import { CatalogRulesEnforcer } from '../ingestion/CatalogRules'; import { ProcessorCacheManager } from './ProcessorCacheManager'; +import { + addEntityAttributes, + TRACER_ID, + withActiveSpan, +} from '../util/opentelemetry'; + +const tracer = trace.getTracer(TRACER_ID); type Context = { entityRef: string; @@ -64,6 +72,18 @@ type Context = { cache: ProcessorCacheManager; }; +function addProcessorAttributes( + span: Span, + stage: string, + processor: CatalogProcessor, +) { + span.setAttribute('backstage.catalog.processor.stage', stage); + span.setAttribute( + 'backstage.catalog.processor.name', + processor.getProcessorName(), + ); +} + /** @public */ export class DefaultCatalogProcessingOrchestrator implements CatalogProcessingOrchestrator @@ -179,54 +199,71 @@ export class DefaultCatalogProcessingOrchestrator entity: Entity, context: Context, ): Promise { - let res = entity; + return await withActiveSpan(tracer, 'ProcessingStage', async stageSpan => { + addEntityAttributes(stageSpan, entity); + stageSpan.setAttribute('backstage.catalog.processor.stage', 'preProcess'); + let res = entity; - for (const processor of this.options.processors) { - if (processor.preProcessEntity) { - try { - res = await processor.preProcessEntity( - res, - context.location, - context.collector.forProcessor(processor), - context.originLocation, - context.cache.forProcessor(processor), - ); - } catch (e) { - throw new InputError( - `Processor ${processor.constructor.name} threw an error while preprocessing`, - e, - ); + for (const processor of this.options.processors) { + if (processor.preProcessEntity) { + let innerRes = res; + res = await withActiveSpan(tracer, 'ProcessingStep', async span => { + addEntityAttributes(span, entity); + addProcessorAttributes(span, 'preProcessEntity', processor); + try { + innerRes = await processor.preProcessEntity!( + innerRes, + context.location, + context.collector.forProcessor(processor), + context.originLocation, + context.cache.forProcessor(processor), + ); + } catch (e) { + throw new InputError( + `Processor ${processor.constructor.name} threw an error while preprocessing`, + e, + ); + } + return innerRes; + }); } } - } - return res; + return res; + }); } /** * Enforce entity policies making sure that entities conform to a general schema */ private async runPolicyStep(entity: Entity): Promise { - let policyEnforcedEntity: Entity | undefined; - - try { - policyEnforcedEntity = await this.options.policy.enforce(entity); - } catch (e) { - throw new InputError( - `Policy check failed for ${stringifyEntityRef(entity)}`, - e, + return await withActiveSpan(tracer, 'ProcessingStage', async stageSpan => { + addEntityAttributes(stageSpan, entity); + stageSpan.setAttribute( + 'backstage.catalog.processor.stage', + 'enforcePolicy', ); - } + let policyEnforcedEntity: Entity | undefined; - if (!policyEnforcedEntity) { - throw new Error( - `Policy unexpectedly returned no data for ${stringifyEntityRef( - entity, - )}`, - ); - } + try { + policyEnforcedEntity = await this.options.policy.enforce(entity); + } catch (e) { + throw new InputError( + `Policy check failed for ${stringifyEntityRef(entity)}`, + e, + ); + } - return policyEnforcedEntity; + if (!policyEnforcedEntity) { + throw new Error( + `Policy unexpectedly returned no data for ${stringifyEntityRef( + entity, + )}`, + ); + } + + return policyEnforcedEntity; + }); } /** @@ -236,50 +273,62 @@ export class DefaultCatalogProcessingOrchestrator entity: Entity, context: Context, ): Promise { - // Double check that none of the previous steps tried to change something - // related to the entity ref, which would break downstream - if (stringifyEntityRef(entity) !== context.entityRef) { - throw new ConflictError( - 'Fatal: The entity kind, namespace, or name changed during processing', - ); - } + return await withActiveSpan(tracer, 'ProcessingStage', async stageSpan => { + addEntityAttributes(stageSpan, entity); + stageSpan.setAttribute('backstage.catalog.processor.stage', 'validate'); + // Double check that none of the previous steps tried to change something + // related to the entity ref, which would break downstream + if (stringifyEntityRef(entity) !== context.entityRef) { + throw new ConflictError( + 'Fatal: The entity kind, namespace, or name changed during processing', + ); + } - // Validate that the end result is a valid Entity at all - try { - validateEntity(entity); - } catch (e) { - throw new ConflictError( - `Entity envelope for ${context.entityRef} failed validation after preprocessing`, - e, - ); - } + // Validate that the end result is a valid Entity at all + try { + validateEntity(entity); + } catch (e) { + throw new ConflictError( + `Entity envelope for ${context.entityRef} failed validation after preprocessing`, + e, + ); + } - let valid = false; + let valid = false; - for (const processor of this.options.processors) { - if (processor.validateEntityKind) { - try { - const thisValid = await processor.validateEntityKind(entity); - if (thisValid) { - valid = true; - if (this.options.legacySingleProcessorValidation) { - break; + for (const processor of this.options.processors) { + if (processor.validateEntityKind) { + try { + const thisValid = await withActiveSpan( + tracer, + 'ProcessingStep', + async span => { + addEntityAttributes(span, entity); + addProcessorAttributes(span, 'validateEntityKind', processor); + return await processor.validateEntityKind!(entity); + }, + ); + if (thisValid) { + valid = true; + if (this.options.legacySingleProcessorValidation) { + break; + } } + } catch (e) { + throw new InputError( + `Processor ${processor.constructor.name} threw an error while validating the entity ${context.entityRef}`, + e, + ); } - } catch (e) { - throw new InputError( - `Processor ${processor.constructor.name} threw an error while validating the entity ${context.entityRef}`, - e, - ); } } - } - if (!valid) { - throw new InputError( - `No processor recognized the entity ${context.entityRef} as valid, possibly caused by a foreign kind or apiVersion`, - ); - } + if (!valid) { + throw new InputError( + `No processor recognized the entity ${context.entityRef} as valid, possibly caused by a foreign kind or apiVersion`, + ); + } + }); } /** @@ -289,65 +338,81 @@ export class DefaultCatalogProcessingOrchestrator entity: LocationEntity, context: Context, ): Promise { - const { type = context.location.type, presence = 'required' } = entity.spec; - const targets = new Array(); - if (entity.spec.target) { - targets.push(entity.spec.target); - } - if (entity.spec.targets) { - targets.push(...entity.spec.targets); - } - - for (const maybeRelativeTarget of targets) { - if (type === 'file' && maybeRelativeTarget.endsWith(path.sep)) { - context.collector.generic()( - processingResult.inputError( - context.location, - `LocationEntityProcessor cannot handle ${type} type location with target ${context.location.target} that ends with a path separator`, - ), - ); - continue; - } - const target = toAbsoluteUrl( - this.options.integrations, - context.location, - type, - maybeRelativeTarget, + return await withActiveSpan(tracer, 'ProcessingStage', async stageSpan => { + addEntityAttributes(stageSpan, entity); + stageSpan.setAttribute( + 'backstage.catalog.processor.stage', + 'readLocation', ); + const { type = context.location.type, presence = 'required' } = + entity.spec; + const targets = new Array(); + if (entity.spec.target) { + targets.push(entity.spec.target); + } + if (entity.spec.targets) { + targets.push(...entity.spec.targets); + } - let didRead = false; - for (const processor of this.options.processors) { - if (processor.readLocation) { - try { - const read = await processor.readLocation( - { - type, - target, - presence, - }, - presence === 'optional', - context.collector.forProcessor(processor), - this.options.parser, - context.cache.forProcessor(processor, target), - ); - if (read) { - didRead = true; - break; + for (const maybeRelativeTarget of targets) { + if (type === 'file' && maybeRelativeTarget.endsWith(path.sep)) { + context.collector.generic()( + processingResult.inputError( + context.location, + `LocationEntityProcessor cannot handle ${type} type location with target ${context.location.target} that ends with a path separator`, + ), + ); + continue; + } + const target = toAbsoluteUrl( + this.options.integrations, + context.location, + type, + maybeRelativeTarget, + ); + + let didRead = false; + for (const processor of this.options.processors) { + if (processor.readLocation) { + try { + const read = await withActiveSpan( + tracer, + 'ProcessingStep', + async span => { + addEntityAttributes(span, entity); + addProcessorAttributes(span, 'readLocation', processor); + return await processor.readLocation!( + { + type, + target, + presence, + }, + presence === 'optional', + context.collector.forProcessor(processor), + this.options.parser, + context.cache.forProcessor(processor, target), + ); + }, + ); + if (read) { + didRead = true; + break; + } + } catch (e) { + throw new InputError( + `Processor ${processor.constructor.name} threw an error while reading ${type}:${target}`, + e, + ); } - } catch (e) { - throw new InputError( - `Processor ${processor.constructor.name} threw an error while reading ${type}:${target}`, - e, - ); } } + if (!didRead) { + throw new InputError( + `No processor was able to handle reading of ${type}:${target}`, + ); + } } - if (!didRead) { - throw new InputError( - `No processor was able to handle reading of ${type}:${target}`, - ); - } - } + }); } /** @@ -357,26 +422,39 @@ export class DefaultCatalogProcessingOrchestrator entity: Entity, context: Context, ): Promise { - let res = entity; + return await withActiveSpan(tracer, 'ProcessingStage', async stageSpan => { + addEntityAttributes(stageSpan, entity); + stageSpan.setAttribute( + 'backstage.catalog.processor.stage', + 'postProcessEntity', + ); + let res = entity; - for (const processor of this.options.processors) { - if (processor.postProcessEntity) { - try { - res = await processor.postProcessEntity( - res, - context.location, - context.collector.forProcessor(processor), - context.cache.forProcessor(processor), - ); - } catch (e) { - throw new InputError( - `Processor ${processor.constructor.name} threw an error while postprocessing`, - e, - ); + for (const processor of this.options.processors) { + if (processor.postProcessEntity) { + let innerRes = res; + res = await withActiveSpan(tracer, 'ProcessingStep', async span => { + addEntityAttributes(span, entity); + addProcessorAttributes(span, 'postProcessEntity', processor); + try { + innerRes = await processor.postProcessEntity!( + innerRes, + context.location, + context.collector.forProcessor(processor), + context.cache.forProcessor(processor), + ); + } catch (e) { + throw new InputError( + `Processor ${processor.constructor.name} threw an error while postprocessing`, + e, + ); + } + return innerRes; + }); } } - } - return res; + return res; + }); } } diff --git a/plugins/catalog-backend/src/schema/openapi.generated.ts b/plugins/catalog-backend/src/schema/openapi.generated.ts index 9af42325ea..7428e5ace2 100644 --- a/plugins/catalog-backend/src/schema/openapi.generated.ts +++ b/plugins/catalog-backend/src/schema/openapi.generated.ts @@ -17,9 +17,10 @@ // ****************************************************************** // * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * // ****************************************************************** +import { createValidatedOpenApiRouter } from '@backstage/backend-openapi-utils'; -export default { - openapi: '3.1.0', +export const spec = { + openapi: '3.0.3', info: { title: '@backstage/plugin-catalog-backend', version: '1', @@ -97,8 +98,12 @@ export default { in: 'query', description: 'Restrict to just these fields in the response.', required: false, + allowReserved: true, schema: { - type: 'string', + type: 'array', + items: { + type: 'string', + }, }, }, filter: { @@ -107,7 +112,10 @@ export default { description: 'Filter for just the entities defined by this filter.', required: false, schema: { - type: 'string', + type: 'array', + items: { + type: 'string', + }, }, }, offset: { @@ -130,18 +138,16 @@ export default { minimum: 1, }, }, - sortField: { - name: 'sortField', + orderField: { + name: 'orderField', in: 'query', description: 'The fields to sort returned results by.', required: false, + allowReserved: true, schema: { type: 'array', items: { - type: 'array', - items: { - type: 'string', - }, + type: 'string', description: 'A two-item tuple of [field, order].', }, }, @@ -256,7 +262,7 @@ export default { }, ], description: 'Metadata fields common to all versions/kinds of entity.', - additionalProperties: false, + additionalProperties: true, }, EntityRelation: { type: 'object', @@ -305,7 +311,40 @@ export default { required: ['metadata', 'kind', 'apiVersion'], description: "The parts of the format that's common to all versions/kinds of entity.", - additionalProperties: false, + additionalProperties: true, + }, + NullableEntity: { + type: 'object', + properties: { + relations: { + type: 'array', + items: { + $ref: '#/components/schemas/EntityRelation', + }, + description: + 'The relations that this entity has with other entities.', + }, + spec: { + $ref: '#/components/schemas/JsonObject', + }, + metadata: { + $ref: '#/components/schemas/EntityMeta', + }, + kind: { + type: 'string', + description: 'The high level entity type being described.', + }, + apiVersion: { + type: 'string', + description: + 'The version of specification format for this particular entity that\nthis is written against.', + }, + }, + required: ['metadata', 'kind', 'apiVersion'], + description: + "The parts of the format that's common to all versions/kinds of entity.", + additionalProperties: true, + nullable: true, }, EntityAncestryResponse: { type: 'object', @@ -343,10 +382,7 @@ export default { items: { anyOf: [ { - $ref: '#/components/schemas/Entity', - }, - { - type: 'null', + $ref: '#/components/schemas/NullableEntity', }, ], }, @@ -405,10 +441,6 @@ export default { LocationSpec: { type: 'object', properties: { - presence: { - type: 'string', - enum: ['optional', 'required'], - }, target: { type: 'string', }, @@ -559,7 +591,8 @@ export default { 'A text to show to the user to inform about the choices made. Like, it could say\n"Found a CODEOWNERS file that covers this target, so we suggest leaving this\nfield empty; which would currently make it owned by X" where X is taken from the\ncodeowners file.', }, value: { - type: ['string', 'null'], + type: 'string', + nullable: true, }, state: { type: 'string', @@ -626,12 +659,8 @@ export default { target: { type: 'string', }, - presence: { - type: 'string', - enum: ['optional', 'required'], - }, }, - required: ['type', 'target', 'presence'], + required: ['type', 'target'], additionalProperties: false, }, SerializedError: { @@ -995,11 +1024,14 @@ export default { $ref: '#/components/parameters/limit', }, { - $ref: '#/components/parameters/sortField', + $ref: '#/components/parameters/orderField', }, { $ref: '#/components/parameters/cursor', }, + { + $ref: '#/components/parameters/filter', + }, { name: 'fullTextFilterTerm', in: 'query', @@ -1118,10 +1150,6 @@ export default { schema: { type: 'object', properties: { - presence: { - type: 'string', - enum: ['required', 'optional'], - }, target: { type: 'string', }, @@ -1129,7 +1157,7 @@ export default { type: 'string', }, }, - required: ['presence', 'target', 'type'], + required: ['target', 'type'], }, }, }, @@ -1328,7 +1356,8 @@ export default { type: 'string', }, entity: { - $ref: '#/components/schemas/Entity', + type: 'object', + additionalProperties: true, }, }, required: ['location', 'entity'], @@ -1340,3 +1369,6 @@ export default { }, }, } as const; +export const createOpenApiRouter = async ( + options?: Parameters['1'], +) => createValidatedOpenApiRouter(spec, options); diff --git a/plugins/catalog-backend/src/schema/openapi.yaml b/plugins/catalog-backend/src/schema/openapi.yaml index 701b4517bc..34e21adffd 100644 --- a/plugins/catalog-backend/src/schema/openapi.yaml +++ b/plugins/catalog-backend/src/schema/openapi.yaml @@ -1,4 +1,4 @@ -openapi: 3.1.0 +openapi: 3.0.3 info: title: '@backstage/plugin-catalog-backend' @@ -61,15 +61,20 @@ components: in: query description: Restrict to just these fields in the response. required: false + allowReserved: true schema: - type: string + type: array + items: + type: string filter: name: filter in: query description: Filter for just the entities defined by this filter. required: false schema: - type: string + type: array + items: + type: string offset: name: offset in: query @@ -86,17 +91,16 @@ components: schema: type: integer minimum: 1 - sortField: - name: sortField + orderField: + name: orderField in: query description: The fields to sort returned results by. required: false + allowReserved: true schema: type: array items: - type: array - items: - type: string + type: string description: A two-item tuple of [field, order]. explode: true style: form @@ -208,7 +212,7 @@ components: required: - name description: Metadata fields common to all versions/kinds of entity. - additionalProperties: false + additionalProperties: true EntityRelation: type: object properties: @@ -248,7 +252,34 @@ components: - kind - apiVersion description: The parts of the format that's common to all versions/kinds of entity. - additionalProperties: false + additionalProperties: true + NullableEntity: + type: object + properties: + relations: + type: array + items: + $ref: '#/components/schemas/EntityRelation' + description: The relations that this entity has with other entities. + spec: + $ref: '#/components/schemas/JsonObject' + metadata: + $ref: '#/components/schemas/EntityMeta' + kind: + type: string + description: The high level entity type being described. + apiVersion: + type: string + description: |- + The version of specification format for this particular entity that + this is written against. + required: + - metadata + - kind + - apiVersion + description: The parts of the format that's common to all versions/kinds of entity. + additionalProperties: true + nullable: true EntityAncestryResponse: type: object properties: @@ -279,8 +310,7 @@ components: type: array items: anyOf: - - $ref: '#/components/schemas/Entity' - - type: 'null' + - $ref: '#/components/schemas/NullableEntity' description: |- The list of entities, in the same order as the refs in the request. Entries that are null signify that no entity existed with that ref. @@ -326,11 +356,6 @@ components: LocationSpec: type: object properties: - presence: - type: string - enum: - - optional - - required target: type: string type: @@ -478,9 +503,8 @@ components: field empty; which would currently make it owned by X" where X is taken from the codeowners file. value: - type: - - string - - 'null' + type: string + nullable: true state: type: string enum: @@ -540,15 +564,9 @@ components: type: string target: type: string - presence: - type: string - enum: - - optional - - required required: - type - target - - presence additionalProperties: false SerializedError: allOf: @@ -763,8 +781,9 @@ paths: parameters: - $ref: '#/components/parameters/fields' - $ref: '#/components/parameters/limit' - - $ref: '#/components/parameters/sortField' + - $ref: '#/components/parameters/orderField' - $ref: '#/components/parameters/cursor' + - $ref: '#/components/parameters/filter' - name: fullTextFilterTerm in: query description: Text search term. @@ -841,17 +860,11 @@ paths: schema: type: object properties: - presence: - type: string - enum: - - required - - optional target: type: string type: type: string required: - - presence - target - type get: @@ -978,7 +991,8 @@ paths: location: type: string entity: - $ref: '#/components/schemas/Entity' + type: object + additionalProperties: true required: - location - entity diff --git a/plugins/catalog-backend/src/service/CatalogPlugin.ts b/plugins/catalog-backend/src/service/CatalogPlugin.ts index e0399c1683..d77b034517 100644 --- a/plugins/catalog-backend/src/service/CatalogPlugin.ts +++ b/plugins/catalog-backend/src/service/CatalogPlugin.ts @@ -27,10 +27,12 @@ import { EntityProvider, } from '@backstage/plugin-catalog-node'; import { loggerToWinstonLogger } from '@backstage/backend-common'; +import { PlaceholderResolver } from '../modules'; class CatalogExtensionPointImpl implements CatalogProcessingExtensionPoint { #processors = new Array(); #entityProviders = new Array(); + #placeholderResolvers: Record = {}; addProcessor( ...processors: Array> @@ -44,6 +46,14 @@ class CatalogExtensionPointImpl implements CatalogProcessingExtensionPoint { this.#entityProviders.push(...providers.flat()); } + addPlaceholderResolver(key: string, resolver: PlaceholderResolver) { + if (key in this.#placeholderResolvers) + throw new Error( + `A placeholder resolver for '${key}' has already been set up, please check your config.`, + ); + this.#placeholderResolvers[key] = resolver; + } + get processors() { return this.#processors; } @@ -51,6 +61,10 @@ class CatalogExtensionPointImpl implements CatalogProcessingExtensionPoint { get entityProviders() { return this.#entityProviders; } + + get placeholderResolvers() { + return this.#placeholderResolvers; + } } /** @@ -99,6 +113,10 @@ export const catalogPlugin = createBackendPlugin({ }); builder.addProcessor(...processingExtensions.processors); builder.addEntityProvider(...processingExtensions.entityProviders); + Object.entries(processingExtensions.placeholderResolvers).forEach( + ([key, resolver]) => builder.setPlaceholderResolver(key, resolver), + ); + const { processingEngine, router } = await builder.build(); await processingEngine.start(); diff --git a/plugins/catalog-backend/src/service/createRouter.test.ts b/plugins/catalog-backend/src/service/createRouter.test.ts index df934eae68..c14bc05476 100644 --- a/plugins/catalog-backend/src/service/createRouter.test.ts +++ b/plugins/catalog-backend/src/service/createRouter.test.ts @@ -247,6 +247,24 @@ describe('createRouter readonly disabled', () => { expect(response.status).toEqual(400); expect(response.body.error.message).toMatch(/Malformed cursor/); }); + + it('should throw in case of invalid limit', async () => { + const items: Entity[] = [ + { apiVersion: 'a', kind: 'b', metadata: { name: 'n' } }, + ]; + + entitiesCatalog.queryEntities.mockResolvedValueOnce({ + items, + totalItems: 100, + pageInfo: { nextCursor: mockCursor() }, + }); + + const response = await request(app).get(`/entities/by-query?limit=asdf`); + expect(response.status).toEqual(400); + expect(response.body.error.message).toMatch( + /request\/query\/limit must be integer/, + ); + }); }); describe('GET /entities/by-uid/:uid', () => { diff --git a/plugins/catalog-backend/src/service/createRouter.ts b/plugins/catalog-backend/src/service/createRouter.ts index 997cd481a6..30c1fd8e3f 100644 --- a/plugins/catalog-backend/src/service/createRouter.ts +++ b/plugins/catalog-backend/src/service/createRouter.ts @@ -25,7 +25,6 @@ import { import { Config } from '@backstage/config'; import { NotFoundError, serializeError } from '@backstage/errors'; import express from 'express'; -import Router from 'express-promise-router'; import { Logger } from 'winston'; import yn from 'yn'; import { z } from 'zod'; @@ -37,7 +36,6 @@ import { basicEntityFilter, entitiesBatchRequest, parseEntityFilterParams, - parseEntityPaginationParams, parseEntityTransformParams, parseQueryEntitiesParams, } from './request'; @@ -50,10 +48,10 @@ import { locationInput, validateRequestBody, } from './util'; -import type { ApiRouter } from '@backstage/backend-openapi-utils'; -import spec from '../schema/openapi.generated'; +import { createOpenApiRouter } from '../schema/openapi.generated'; import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { getBearerTokenFromAuthorizationHeader } from '@backstage/plugin-auth-node'; +import { parseEntityPaginationParams } from './request/parseEntityPaginationParams'; /** * Options used by {@link createRouter}. @@ -80,6 +78,13 @@ export interface RouterOptions { export async function createRouter( options: RouterOptions, ): Promise { + const router = await createOpenApiRouter({ + validatorOptions: { + // We want the spec to be up to date with the expected value, but the return type needs + // to be controlled by the router implementation not the request validator. + ignorePaths: /^\/validate-entity\/?$/, + }, + }); const { entitiesCatalog, locationAnalyzer, @@ -90,8 +95,6 @@ export async function createRouter( logger, permissionIntegrationRouter, } = options; - const router = Router() as ApiRouter; - router.use(express.json()); const readonlyEnabled = config.getOptionalBoolean('catalog.readonly') || false; @@ -142,6 +145,7 @@ export async function createRouter( .get('/entities/by-query', async (req, res) => { const { items, pageInfo, totalItems } = await entitiesCatalog.queryEntities({ + limit: req.query.limit, ...parseQueryEntitiesParams(req.query), authorizationToken: getBearerTokenFromAuthorizationHeader( req.header('authorization'), diff --git a/plugins/catalog-backend/src/service/request/index.ts b/plugins/catalog-backend/src/service/request/index.ts index d9297fc122..dbfd34aef1 100644 --- a/plugins/catalog-backend/src/service/request/index.ts +++ b/plugins/catalog-backend/src/service/request/index.ts @@ -17,6 +17,5 @@ export { entitiesBatchRequest } from './entitiesBatchRequest'; export { basicEntityFilter } from './basicEntityFilter'; export { parseEntityFilterParams } from './parseEntityFilterParams'; -export { parseEntityPaginationParams } from './parseEntityPaginationParams'; export { parseEntityTransformParams } from './parseEntityTransformParams'; export { parseQueryEntitiesParams } from './parseQueryEntitiesParams'; diff --git a/plugins/catalog-backend/src/service/request/parseEntityPaginationParams.test.ts b/plugins/catalog-backend/src/service/request/parseEntityPaginationParams.test.ts index 331d453973..5e61f55b12 100644 --- a/plugins/catalog-backend/src/service/request/parseEntityPaginationParams.test.ts +++ b/plugins/catalog-backend/src/service/request/parseEntityPaginationParams.test.ts @@ -19,29 +19,23 @@ import { parseEntityPaginationParams } from './parseEntityPaginationParams'; describe('parseEntityPaginationParams', () => { it('works for the happy path', () => { expect(parseEntityPaginationParams({})).toBeUndefined(); - expect(parseEntityPaginationParams({ limit: '1' })).toEqual({ limit: 1 }); - expect(parseEntityPaginationParams({ offset: '0' })).toEqual({ offset: 0 }); - expect(parseEntityPaginationParams({ offset: '2' })).toEqual({ offset: 2 }); + expect(parseEntityPaginationParams({ limit: 1 })).toEqual({ limit: 1 }); + expect(parseEntityPaginationParams({ offset: 0 })).toEqual({ offset: 0 }); + expect(parseEntityPaginationParams({ offset: 2 })).toEqual({ offset: 2 }); expect(parseEntityPaginationParams({ after: 'x' })).toEqual({ after: 'x' }); expect( - parseEntityPaginationParams({ limit: '1', offset: '2', after: 'x' }), + parseEntityPaginationParams({ limit: 1, offset: 2, after: 'x' }), ).toEqual({ limit: 1, offset: 2, after: 'x' }); }); it('rejects bad values', () => { - expect(() => parseEntityPaginationParams({ limit: '' })).toThrow( - 'Invalid limit, not an integer', - ); - expect(() => parseEntityPaginationParams({ limit: '0' })).toThrow( + expect(() => parseEntityPaginationParams({ limit: 0 })).toThrow( 'Invalid limit, must be greater than zero', ); - expect(() => parseEntityPaginationParams({ limit: '-1' })).toThrow( + expect(() => parseEntityPaginationParams({ limit: -1 })).toThrow( 'Invalid limit, must be greater than zero', ); - expect(() => parseEntityPaginationParams({ offset: '' })).toThrow( - 'Invalid offset, not an integer', - ); - expect(() => parseEntityPaginationParams({ offset: '-1' })).toThrow( + expect(() => parseEntityPaginationParams({ offset: -1 })).toThrow( 'Invalid offset, must be zero or greater', ); expect(() => parseEntityPaginationParams({ after: '' })).toThrow( diff --git a/plugins/catalog-backend/src/service/request/parseEntityPaginationParams.ts b/plugins/catalog-backend/src/service/request/parseEntityPaginationParams.ts index 87b19e13de..61905aaa6a 100644 --- a/plugins/catalog-backend/src/service/request/parseEntityPaginationParams.ts +++ b/plugins/catalog-backend/src/service/request/parseEntityPaginationParams.ts @@ -16,19 +16,20 @@ import { InputError } from '@backstage/errors'; import { EntityPagination } from '../../catalog/types'; -import { parseIntegerParam, parseStringParam } from './common'; /** * Parses the pagination related parameters out of a query, e.g. * /entities?offset=100&limit=10 */ -export function parseEntityPaginationParams( - params: Record, -): EntityPagination | undefined { - const offset = parseIntegerParam(params.offset, 'offset'); - const limit = parseIntegerParam(params.limit, 'limit'); - const after = parseStringParam(params.after, 'after'); - +export function parseEntityPaginationParams({ + limit, + offset, + after, +}: { + offset?: number; + limit?: number; + after?: string; +}): EntityPagination | undefined { if (offset === undefined && limit === undefined && after === undefined) { return undefined; } diff --git a/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.test.ts b/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.test.ts index 43ba218eac..dfcfd58c7e 100644 --- a/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.test.ts +++ b/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.test.ts @@ -37,7 +37,6 @@ describe('parseQueryEntitiesParams', () => { const parsedObj = parseQueryEntitiesParams( validRequest, ) as QueryEntitiesInitialRequest; - expect(parsedObj.limit).toBe(3); expect(parsedObj.fields).toBeDefined(); expect(parsedObj.orderFields).toEqual([ { field: 'metadata.name', order: 'desc' }, @@ -54,7 +53,6 @@ describe('parseQueryEntitiesParams', () => { const parsedObj = parseQueryEntitiesParams( {}, ) as QueryEntitiesInitialRequest; - expect(parsedObj.limit).toBeUndefined(); expect(parsedObj.fields).toBeUndefined(); expect(parsedObj.orderFields).toBeUndefined(); expect(parsedObj.filter).toBeUndefined(); @@ -64,9 +62,6 @@ describe('parseQueryEntitiesParams', () => { }); it.each([ - { - limit: 'asd', - }, { filter: 3 }, { orderField: ['metadata.uid,diagonal'] }, { fields: [4] }, @@ -94,7 +89,6 @@ describe('parseQueryEntitiesParams', () => { const parsedObj = parseQueryEntitiesParams( validRequest, ) as QueryEntitiesCursorRequest; - expect(parsedObj.limit).toBe(3); expect(parsedObj.fields).toBeDefined(); expect(parsedObj.cursor).toEqual(cursor); }); @@ -118,7 +112,6 @@ describe('parseQueryEntitiesParams', () => { const parsedObj = parseQueryEntitiesParams( validRequest, ) as QueryEntitiesCursorRequest; - expect(parsedObj.limit).toBe(3); expect(parsedObj.fields).toBeDefined(); expect(parsedObj.cursor).toEqual(cursor); expect(parsedObj).not.toHaveProperty('filter'); @@ -130,18 +123,14 @@ describe('parseQueryEntitiesParams', () => { const parsedObj = parseQueryEntitiesParams( {}, ) as QueryEntitiesCursorRequest; - expect(parsedObj.limit).toBeUndefined(); expect(parsedObj.fields).toBeUndefined(); }); - it.each([ - { - limit: 'asd', + it.each([{ cursor: [] }, { fields: [4] }])( + 'should throw if some parameter is not valid %p', + params => { + expect(() => parseQueryEntitiesParams(params)).toThrow(); }, - { cursor: [] }, - { fields: [4] }, - ])('should throw if some parameter is not valid %p', params => { - expect(() => parseQueryEntitiesParams(params)).toThrow(); - }); + ); }); }); diff --git a/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.ts b/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.ts index ae8b649d13..9b55f193b9 100644 --- a/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.ts +++ b/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.ts @@ -20,7 +20,7 @@ import { QueryEntitiesRequest, } from '../../catalog/types'; import { decodeCursor } from '../util'; -import { parseIntegerParam, parseStringParam } from './common'; +import { parseStringParam } from './common'; import { parseEntityFilterParams } from './parseEntityFilterParams'; import { parseEntityOrderFieldParams } from './parseEntityOrderFieldParams'; import { parseEntityTransformParams } from './parseEntityTransformParams'; @@ -28,16 +28,14 @@ import { parseFullTextFilterFields } from './parseFullTextFilterFields'; export function parseQueryEntitiesParams( params: Record, -): Omit { +): Omit { const fields = parseEntityTransformParams(params); - const limit = parseIntegerParam(params.limit, 'limit'); const cursor = parseStringParam(params.cursor, 'cursor'); if (cursor) { const decodedCursor = decodeCursor(cursor); const response: Omit = { cursor: decodedCursor, fields, - limit, }; return response; } @@ -54,7 +52,6 @@ export function parseQueryEntitiesParams( const response: Omit = { fields, filter, - limit, orderFields, fullTextFilter: { term: fullTextFilterTerm || '', diff --git a/plugins/catalog-backend/src/util/opentelemetry.ts b/plugins/catalog-backend/src/util/opentelemetry.ts new file mode 100644 index 0000000000..3fb83d7909 --- /dev/null +++ b/plugins/catalog-backend/src/util/opentelemetry.ts @@ -0,0 +1,101 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { Span, SpanOptions, SpanStatusCode, Tracer } from '@opentelemetry/api'; +import { Entity } from '@backstage/catalog-model'; + +export const TRACER_ID = 'backstage-plugin-catalog-backend'; + +function setAttributeIfDefined(span: Span, attribute: string, value?: string) { + if (value !== null && value !== undefined) { + span.setAttribute(attribute, value); + } +} + +export function addEntityAttributes(span: Span, entity: Entity) { + setAttributeIfDefined(span, 'backstage.entity.apiVersion', entity.apiVersion); + setAttributeIfDefined(span, 'backstage.entity.kind', entity.kind); + setAttributeIfDefined( + span, + 'backstage.entity.metadata.namespace', + entity.metadata?.namespace, + ); + setAttributeIfDefined( + span, + 'backstage.entity.metadata.name', + entity.metadata?.name, + ); +} + +// Adapted from https://github.com/open-telemetry/opentelemetry-js/blob/359fbcc40a859057a02b14e84599eac399b8dba7/api/src/trace/SugaredTracer.ts +// While waiting for something like https://github.com/open-telemetry/opentelemetry-js/pull/3317 to land upstream + +const onException = (e: Error, span: Span) => { + span.recordException(e); + span.setStatus({ + code: SpanStatusCode.ERROR, + }); +}; + +function isPromiseLike(obj: PromiseLike | S): obj is PromiseLike { + return ( + !!obj && + (typeof obj === 'object' || typeof obj === 'function') && + 'then' in obj && + typeof obj.then === 'function' + ); +} + +function handleFn ReturnType>( + span: Span, + fn: F, +): ReturnType { + try { + const ret = fn(span); + + // if fn is an async function attach a recordException and spanEnd callback to the promise + if (isPromiseLike(ret)) { + ret.then( + () => { + span.end(); + }, + e => { + onException(e, span); + span.end(); + }, + ); + } else { + span.end(); + } + + return ret; + } catch (e) { + onException(e, span); + span.end(); + throw e; + } +} + +export function withActiveSpan ReturnType>( + tracer: Tracer, + name: string, + fn: F, + spanOptions: SpanOptions = {}, +): ReturnType { + return tracer.startActiveSpan(name, spanOptions, (span: Span) => { + return handleFn(span, fn); + }); +} diff --git a/plugins/catalog-common/CHANGELOG.md b/plugins/catalog-common/CHANGELOG.md index 84da127ae3..8fa7f6e886 100644 --- a/plugins/catalog-common/CHANGELOG.md +++ b/plugins/catalog-common/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-catalog-common +## 1.0.15 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## 1.0.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/plugin-permission-common@0.7.7-next.0 + - @backstage/plugin-search-common@1.2.5-next.0 + ## 1.0.14 ### Patch Changes diff --git a/plugins/catalog-common/package.json b/plugins/catalog-common/package.json index 156ebeb4a5..b4a2b75420 100644 --- a/plugins/catalog-common/package.json +++ b/plugins/catalog-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-common", "description": "Common functionalities for the catalog plugin", - "version": "1.0.14", + "version": "1.0.15", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-customized/CHANGELOG.md b/plugins/catalog-customized/CHANGELOG.md index c3d2bdeacd..6b573ac4d3 100644 --- a/plugins/catalog-customized/CHANGELOG.md +++ b/plugins/catalog-customized/CHANGELOG.md @@ -1,5 +1,45 @@ # @internal/plugin-catalog-customized +## 0.0.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.12.1-next.0 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## 0.0.12 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.0 + - @backstage/plugin-catalog@1.12.0 + +## 0.0.12-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.0-next.2 + - @backstage/plugin-catalog@1.12.0-next.2 + +## 0.0.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.11.3-next.1 + - @backstage/plugin-catalog-react@1.7.1-next.1 + +## 0.0.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.11.3-next.0 + - @backstage/plugin-catalog-react@1.7.1-next.0 + ## 0.0.11 ### Patch Changes diff --git a/plugins/catalog-customized/package.json b/plugins/catalog-customized/package.json index 4d9a218260..b745c43aca 100644 --- a/plugins/catalog-customized/package.json +++ b/plugins/catalog-customized/package.json @@ -1,7 +1,7 @@ { "name": "@internal/plugin-catalog-customized", "description": "The internal Backstage Customizable plugin for browsing the Backstage catalog", - "version": "0.0.11", + "version": "0.0.13-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index d03624f2e1..4e04cac514 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,65 @@ # @backstage/plugin-catalog-graph +## 0.2.33-next.0 + +### Patch Changes + +- 64ee2c0c7ca5: Propagate entity spec to EntityNode so that spec info such as type can be used for graph node customization +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + +## 0.2.32 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.0 + - @backstage/core-components@0.13.3 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + +## 0.2.32-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.0-next.2 + - @backstage/theme@0.4.1-next.1 + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-components@0.13.3-next.2 + - @backstage/catalog-client@1.4.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + +## 0.2.32-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.0 + - @backstage/core-components@0.13.3-next.1 + - @backstage/core-plugin-api@1.5.3-next.0 + - @backstage/plugin-catalog-react@1.7.1-next.1 + +## 0.2.32-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.3-next.0 + - @backstage/catalog-client@1.4.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/core-plugin-api@1.5.2 + - @backstage/theme@0.4.0 + - @backstage/plugin-catalog-react@1.7.1-next.0 + ## 0.2.31 ### Patch Changes diff --git a/plugins/catalog-graph/api-report.md b/plugins/catalog-graph/api-report.md index 645fc1162e..80578db6ed 100644 --- a/plugins/catalog-graph/api-report.md +++ b/plugins/catalog-graph/api-report.md @@ -10,6 +10,7 @@ import { CompoundEntityRef } from '@backstage/catalog-model'; import { DependencyGraphTypes } from '@backstage/core-components'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { InfoCardVariants } from '@backstage/core-components'; +import { JsonObject } from '@backstage/types'; import { MouseEvent as MouseEvent_2 } from 'react'; import { MouseEventHandler } from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; @@ -92,6 +93,7 @@ export type EntityNodeData = { kind?: string; title?: string; namespace: string; + spec?: JsonObject; focused?: boolean; color?: 'primary' | 'secondary' | 'default'; onClick?: MouseEventHandler; diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index ebfe768029..2e2b3c2402 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graph", - "version": "0.2.31", + "version": "0.2.33-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -12,6 +12,12 @@ "backstage": { "role": "frontend-plugin" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/catalog-graph" + }, "scripts": { "build": "backstage-cli package build", "start": "backstage-cli package start", @@ -28,6 +34,7 @@ "@backstage/core-plugin-api": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", "@backstage/theme": "workspace:^", + "@backstage/types": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", @@ -49,7 +56,6 @@ "@backstage/dev-utils": "workspace:^", "@backstage/plugin-catalog": "workspace:^", "@backstage/test-utils": "workspace:^", - "@backstage/types": "workspace:^", "@testing-library/dom": "^8.0.0", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/types.ts b/plugins/catalog-graph/src/components/EntityRelationsGraph/types.ts index caf6c8c501..1ce32fc523 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/types.ts +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/types.ts @@ -15,6 +15,7 @@ */ import { DependencyGraphTypes } from '@backstage/core-components'; +import { JsonObject } from '@backstage/types'; import { MouseEventHandler } from 'react'; /** @@ -63,6 +64,10 @@ export type EntityNodeData = { * Namespace of the entity. */ namespace: string; + /** + * Optional spec of the entity. + */ + spec?: JsonObject; /** * Whether the entity is focused, optional, defaults to false. Focused * entities are highlighted in the graph. diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityRelationNodesAndEdges.ts b/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityRelationNodesAndEdges.ts index a1c3416d4f..2e177e2b75 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityRelationNodesAndEdges.ts +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityRelationNodesAndEdges.ts @@ -75,6 +75,7 @@ export function useEntityRelationNodesAndEdges({ kind: entity.kind, name: entity.metadata.name, namespace: entity.metadata.namespace ?? DEFAULT_NAMESPACE, + spec: entity.spec ?? undefined, focused, color: focused ? 'secondary' : 'primary', }; diff --git a/plugins/catalog-graphql/CHANGELOG.md b/plugins/catalog-graphql/CHANGELOG.md index 0545909026..0ce6148bc2 100644 --- a/plugins/catalog-graphql/CHANGELOG.md +++ b/plugins/catalog-graphql/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-catalog-graphql +## 0.3.22 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + +## 0.3.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + ## 0.3.21 ### Patch Changes diff --git a/plugins/catalog-graphql/package.json b/plugins/catalog-graphql/package.json index 2058b8b409..0b4bebc0f7 100644 --- a/plugins/catalog-graphql/package.json +++ b/plugins/catalog-graphql/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-graphql", "description": "An experimental Backstage catalog GraphQL module", - "version": "0.3.21", + "version": "0.3.22", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index 4f7f3f6063..47b286216e 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,82 @@ # @backstage/plugin-catalog-import +## 0.9.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/integration@1.5.1 + - @backstage/integration-react@1.1.16-next.0 + - @backstage/plugin-catalog-common@1.0.15 + +## 0.9.10 + +### Patch Changes + +- 36b7edf39585: Add a "View Component" button as the primary CTA after registering a new component using a link to catalog-info.yaml +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/plugin-catalog-react@1.8.0 + - @backstage/core-components@0.13.3 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1 + - @backstage/integration-react@1.1.15 + - @backstage/plugin-catalog-common@1.0.15 + +## 0.9.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.0-next.2 + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-components@0.13.3-next.2 + - @backstage/catalog-client@1.4.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1-next.0 + - @backstage/integration@1.5.1-next.0 + - @backstage/integration-react@1.1.15-next.2 + - @backstage/plugin-catalog-common@1.0.15-next.0 + +## 0.9.10-next.1 + +### Patch Changes + +- 36b7edf39585: Add a "View Component" button as the primary CTA after registering a new component using a link to catalog-info.yaml +- Updated dependencies + - @backstage/core-components@0.13.3-next.1 + - @backstage/core-plugin-api@1.5.3-next.0 + - @backstage/integration-react@1.1.15-next.1 + - @backstage/plugin-catalog-react@1.7.1-next.1 + - @backstage/config@1.0.8 + +## 0.9.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/core-components@0.13.3-next.0 + - @backstage/catalog-client@1.4.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/core-plugin-api@1.5.2 + - @backstage/integration@1.5.1-next.0 + - @backstage/integration-react@1.1.15-next.0 + - @backstage/plugin-catalog-common@1.0.15-next.0 + - @backstage/plugin-catalog-react@1.7.1-next.0 + ## 0.9.9 ### Patch Changes diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index a0bff2f9c0..19d475d94e 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-import", "description": "A Backstage plugin the helps you import entities into your catalog", - "version": "0.9.9", + "version": "0.9.11-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-import/src/components/Buttons/index.tsx b/plugins/catalog-import/src/components/Buttons/index.tsx index a478bfd4cc..4b9043cd6d 100644 --- a/plugins/catalog-import/src/components/Buttons/index.tsx +++ b/plugins/catalog-import/src/components/Buttons/index.tsx @@ -14,6 +14,7 @@ * limitations under the License. */ +import { LinkButton } from '@backstage/core-components'; import { Button, CircularProgress } from '@material-ui/core'; import { makeStyles } from '@material-ui/core/styles'; import React, { ComponentProps } from 'react'; @@ -68,3 +69,20 @@ export const BackButton = (props: ComponentProps) => {