diff --git a/.changeset/clever-trains-greet.md b/.changeset/clever-trains-greet.md new file mode 100644 index 0000000000..dc75f2f02a --- /dev/null +++ b/.changeset/clever-trains-greet.md @@ -0,0 +1,20 @@ +--- +'@backstage/plugin-tech-insights-backend': minor +--- + +**BREAKING**: The `buildTechInsightsContext` function now takes an additional +field in its options argument: `tokenManager`. This is an instance of +`TokenManager`, which can be found in your backend initialization code's +`env`. + +```diff + const builder = buildTechInsightsContext({ + logger: env.logger, + config: env.config, + database: env.database, + discovery: env.discovery, + scheduler: env.scheduler, ++ tokenManager: env.tokenManager, + factRetrievers: [ /* ... */ ], + }); +``` diff --git a/.changeset/fair-pigs-mate.md b/.changeset/fair-pigs-mate.md new file mode 100644 index 0000000000..ce82e138b8 --- /dev/null +++ b/.changeset/fair-pigs-mate.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-tech-insights-node': minor +--- + +**BREAKING**: The `FactRetrieverContext` type now contains an additional +field: `tokenManager`. diff --git a/.changeset/grumpy-panthers-peel.md b/.changeset/grumpy-panthers-peel.md new file mode 100644 index 0000000000..ecf4397554 --- /dev/null +++ b/.changeset/grumpy-panthers-peel.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-tech-insights-backend-module-jsonfc': patch +--- + +Updated usages of `buildTechInsightsContext` in README. diff --git a/.changeset/metal-hairs-build.md b/.changeset/metal-hairs-build.md new file mode 100644 index 0000000000..9ed8bba01b --- /dev/null +++ b/.changeset/metal-hairs-build.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-rollbar-backend': patch +--- + +Updated README to include clearer installation instructions on how to install and configure. diff --git a/ADOPTERS.md b/ADOPTERS.md index acdfc4bf29..1013b18421 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -118,5 +118,7 @@ _If you're using Backstage in your organization, please try to add your company | [Pachama](https://pachama.com/) | [Aron Gates](https://github.com/agates4) | Internal Developer Portal, a catalog of all microservices, architecture documentation, and templates to generate developer resources. | | [SEEK](https://www.seek.com.au) | [Jahred Hope](https://github.com/jahredhope) | Developer portal for developer tooling and technical documentation. | | [Marks & Spencer](https://www.marksandspencer.com/) | [Kamal Cheriyath](https://github.com/kcheriyath) | Centralised discovery, adoption and devops automation hub for Engineering & Architecture. | -| [McKesson](https://www.mckesson.com/) | [Agnel Antony](https://github.com/aantony2) | Internal Developer Platform for developer gated CI/CD templates, technical documentation, cloud automation service catalog, etc. | -| [leboncoin](https://www.leboncoin.fr/) | [Andy Ladjadj](https://github.com/aladjadj) | Centralize our multiple UI in a single portal. Simplify onbording, new features and harmonize how people search information. Core features (catalog,api,docs,scafolder) are good to start the adoption. status: internal beta. | +| [McKesson](https://www.mckesson.com/) | [Agnel Antony](https://github.com/aantony2) | Internal Developer Platform for developer gated CI/CD templates, technical documentation, cloud automation service catalog, etc. | +| [World Fuel Services](https://www.wfscorp.com/) | [Anirudh Kurapathi](https://github.com/anirudhkurapati), [Alex Kwon](https://github.com/alexkwon), [Lester Hernandez](https://github.com/lhernandez-wfscorp), [Avi Boru](https://github.com/aviboru), [Vardhan Annapureddy](https://github.com/harshaaws) | Internal Developer Portal, service catalog product, API's, Software Templates, tech docs and more. | +| [leboncoin](https://www.leboncoin.fr/) | [Andy Ladjadj](https://github.com/aladjadj) | Centralize our multiple UI in a single portal. Simplify onbording, new features and harmonize how people search information. Core features (catalog,api,docs,scafolder) are good to start the adoption. status: internal beta. | + diff --git a/docs/assets/techdocs/addon-locations.png b/docs/assets/techdocs/addon-locations.png new file mode 100644 index 0000000000..e535fe61aa Binary files /dev/null and b/docs/assets/techdocs/addon-locations.png differ diff --git a/docs/auth/add-auth-provider.md b/docs/auth/add-auth-provider.md index 07d6869fe3..068ba01460 100644 --- a/docs/auth/add-auth-provider.md +++ b/docs/auth/add-auth-provider.md @@ -74,7 +74,7 @@ export class ProviderAAuthProvider implements OAuthProviderHandlers { clientID: options.clientId, clientSecret: options.clientSecret, callbackURL: options.callbackUrl, - passReqToCallback: false as true, + passReqToCallback: false, response_type: 'code', /// ... etc } diff --git a/docs/features/techdocs/README.md b/docs/features/techdocs/README.md index 2ac90f791b..c64d0b785d 100644 --- a/docs/features/techdocs/README.md +++ b/docs/features/techdocs/README.md @@ -76,17 +76,19 @@ TechDocs packages: TechDocs promoted to v1.0! To understand how this change affects the package, please check out our [versioning policy](https://backstage.io/docs/overview/versioning-policy). +**v1.2** 🚧 + +With the Backstage 1.2 release, we plan to introduce the [TechDocs Addon Framework](https://github.com/backstage/backstage/issues/9636) for augmenting the TechDocs experience at read-time. + +In addition to the framework itself, we'll be open sourcing a `` addon, helping you to create a feedback loop that drives up documentation quality and fosters a documentation culture at your organization. + ### **Future work 🔮** Some of the following items are coming soon and some are potential ideas. -- [TechDocs Addon Framework](https://github.com/backstage/backstage/issues/9636) -- Contribute to and deploy from a marketplace of TechDocs Addons -- Addon: Highlight text and raise an Issue to create a feedback loop to drive up documentation quality -- Addon: MDX (allows you to use JSX in your Markdown content) - Better integration with [Scaffolder V2](https://github.com/backstage/backstage/issues/2771) (e.g. easy to choose and plug documentation template with Software Templates) -- Static site generator agnostic +- Static site generator agnostic, including possible support for MDX (allowing you to use JSX in your Markdown content) - Possible to configure several aspects about TechDocs (e.g. URL, homepage, theme) diff --git a/docs/features/techdocs/addons.md b/docs/features/techdocs/addons.md new file mode 100644 index 0000000000..0fba3b92e3 --- /dev/null +++ b/docs/features/techdocs/addons.md @@ -0,0 +1,218 @@ +--- +id: addons +title: TechDocs Addons +description: How to find, use, or create TechDocs Addons. +--- + +> Note: This page contains documentation for features that have not yet been +> released on a [main-line version](https://backstage.io/docs/overview/versioning-policy#main-release-line) +> of Backstage and TechDocs. We plan to make Addons generally available in +> Backstage v1.2. + +## Concepts + +TechDocs is a centralized platform for publishing, viewing, and discovering +technical documentation across an entire organization. It's a solid foundation! +But it doesn't solve higher-order documentation needs on its own: how do you +create and reinforce a culture of documentation? How do you build trust in the +quality of technical documentation? + +TechDocs Addons are a mechanism by which you can customize the TechDocs +experience in order to try and address some of these higher-order needs. + +### Addons + +An Addon is just a react component. Like any react component, it can retrieve +and render data using normal Backstage or native hooks, APIs, and components. +Props can be used to configure its behavior, where appropriate. + +### Locations + +Addons declare a `location` where they will be rendered. Most locations are +representative of physical spaces in the TechDocs UI: + +- `Header`: For Addons which fill up the header from the right, on the same + line as the title. +- `Subheader`: For Addons that sit below the header but above all content. + This is a great location for tooling/configuration of TechDocs display. +- `PrimarySidebar`: Left of the content, above of the navigation. +- `SecondarySidebar`: Right of the content, above the table of contents. +- `Content`: A special location intended for Addons which augment the + statically generated content of the documentation itself. +- `Component`: A [proposed-but-not-yet-implemented](https://github.com/backstage/backstage/issues/11109) + virtual location, aimed at simplifying a common type of Addon. + +TechDocs Addon Location Guide + +### Addon Registry + +The installation and configuration of Addons happens within a Backstage app's +frontend. Addons are imported from plugins and added underneath a registry +component called ``. This registry can be configured for both +the TechDocs Reader page as well as the Entity docs page. + +Addons are rendered in the order in which they are registered. + +## Installing and using Addons + +Addons can be installed and configured in much the same way as extensions for +other Backstage plugins: by adding them underneath an extension registry +component (``) under the route representing the TechDocs Reader +page in your `App.tsx`: + +```tsx +// packages/app/src/App.tsx + +import { TechDocsReaderPage } from '@backstage/plugin-techdocs'; +import { TechDocsAddons } from '@backstage/plugin-techdocs-react/alpha'; +import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib'; + +// ... + +}> + + + {/* Other addons can be added here. */} + +; +``` + +The process for configuring Addons on the documentation tab on the entity page +is very similar; instead of adding the `` registry under a +``, you'd add it as a child of ``: + +```tsx +// packages/app/src/components/catalog/EntityPage.tsx + +import { EntityLayout } from '@backstage/plugin-catalog'; +import { EntityTechdocsContent } from '@backstage/plugin-techdocs'; +import { TechDocsAddons } from '@backstage/plugin-techdocs-react/alpha'; +import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib'; + +// ... + + + + + + {/* Other addons can be added here. */} + + +; +``` + +Note that on the entity page, because the Catalog plugin is responsible for the +page header, TechDocs Addons whose location is `Header` will not be rendered. + +## Available Addons + +Addons can, in principle, be provided by any plugin! To make it easier to +discover available Addons, we've compiled a list of them here: + +| Addon | Package/Plugin | Description | +| ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [``](https://backstage.io/docs/reference/plugin-techdocs-module-addons-contrib.reportissue) | `@backstage/plugin-techdocs-module-addons-contrib` | Allows TechDocs users to select a portion of text on a TechDocs page and open an issue against the repository that contains the documentation, populating the issue description with the selected text according to a configurable template. | + +Got an Addon to contribute? Feel free to add a row above! + +## Creating an Addon + +The simplest Addons are plain old react components that get rendered in +specific locations within a TechDocs site. To package such a react component as +an Addon, follow these steps: + +1. Write the component in your plugin like any other component +2. Create, provide, and export the component from your plugin + +```ts +// plugins/your-plugin/src/plugin.ts + +import { + createTechDocsAddonExtension, + TechDocsAddonLocations, +} from '@backstage/plugin-techdocs-react/alpha'; +import { CatGifComponent, CatGifComponentProps } from './addons'; + +// ... + +// You must "provide" your Addon, just like any extension, via your plugin. +export const CatGif = yourPlugin.provide( + // This function "creates" the Addon given a component and location. If your + // component can be configured via props, pass the prop type here too. + createTechDocsAddonExtension({ + name: 'CatGif', + location: TechDocsAddonLocations.Header, + component: CatGifComponent, + }), +); +``` + +### Addons in the Content location + +Beyond the "render a component in a region" use-case, it's also possible for +Addons to access and manipulate a TechDocs site's DOM; this could be used to, +for example, load and instantiate client-side diagramming libraries, replace +elements with dynamically loaded content, etc. + +This type of Addon is still expressed as a react component, but instead of +returning a react element to be rendered, it updates the DOM via side-effects +(e.g. with `useEffect`). Access to the DOM is made available via utility hooks +provided by the Addon framework. + +```tsx +// plugins/your-plugin/src/addons/MakeAllImagesCatGifs.tsx + +import React, { useEffect } from 'react'; +import { useShadowRootElements } from '@backstage/plugin-techdocs-react/alpha'; + +// This is a normal react component; in order to make it an Addon, you would +// still create and provide it via your plugin as described above. The only +// difference is that you'd set `location` to `TechDocsAddonLocations.Content`. +export const MakeAllImagesCatGifsAddon = () => { + // This hook can be used to get references to specific elements. If you need + // access to the whole shadow DOM, use the the underlying useShadowRoot() + // hook instead. + const images = useShadowRootElements(['img']); + + useEffect(() => { + images.forEach(img => { + if (img.src !== 'https://example.com/cat.gif') { + img.src = 'https://example.com/cat.gif'; + } + }); + }, [images]); + + // Nothing to render directly, so we can just return null. + return null; +}; +``` + +### Testing Addons + +Install `@backstage/plugin-techdocs-addons-test-utils` as a `devDependency` in +your plugin for access to utilities that make testing such Addons easier. + +A test for the above Addon might look something like this: + +```tsx +// plugins/your-plugin/src/addons/MakeAllImagesCatGifs.test.tsx +import { TechDocsAddonTester } from '@backstage/plugin-techdocs-addons-test-utils'; + +// Note: import your actual addon (the one provided by your plugin). +import { MakeAllImagesCatGifs } from '../plugin.ts'; + +describe('MakeAllImagesCatGifs', () => { + it('replaces img srcs with cat gif', async () => { + const { getByTestId } = await TechDocsAddonTester.buildAddonsInTechDocs([ + , + ]) + .withDom() + .renderWithEffects(); + + expect(getByTestId('fixture')).toHaveAttribute( + 'src', + 'https://example.com/cat.gif', + ); + }); +}); +``` diff --git a/docs/features/techdocs/concepts.md b/docs/features/techdocs/concepts.md index e34f58bc70..c96db5e3b7 100644 --- a/docs/features/techdocs/concepts.md +++ b/docs/features/techdocs/concepts.md @@ -96,10 +96,6 @@ Documentation generated by TechDocs is generated as static HTML sites. The TechDocs Reader was therefore created to be able to integrate pre-generated HTML sites with the Backstage UI. -The TechDocs Reader purpose is also to open up the opportunity to integrate -TechDocs widgets for a customized full-featured TechDocs experience. -([coming soon V.3](./README.md#project-roadmap)) - [TechDocs Reader](https://github.com/backstage/backstage/blob/master/plugins/techdocs/src/reader/README.md) ## Transformers @@ -110,3 +106,19 @@ transform the HTML content on pre and post render (e.g. rewrite docs links or modify css). [Transformers API docs](https://github.com/backstage/backstage/blob/master/plugins/techdocs/src/reader/README.md) + +## TechDocs Addons + +Addons (introduced in Backstage v1.2) are client-side, React-based extensions +that can be used to augment the TechDocs experience at read-time. They offer a +mechanism for configuring the TechDocs Reader to better suit your +organization's needs. + +Addons can dynamically load and display information anywhere in the TechDocs +Reader, including within the statically generated content itself. + +Addons should not be confused with `mkdocs` plugins, which may be used to +customize a TechDocs site's content at build-time. While it's possible to take +advantage of some `mkdocs` plugins, not all such plugins play well with +TechDocs (primarily, but not exclusively, for security reasons). Addons offer +an alternative. diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 81ad453429..43e9dd3978 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -109,6 +109,7 @@ "features/techdocs/techdocs-overview", "features/techdocs/getting-started", "features/techdocs/concepts", + "features/techdocs/addons", "features/techdocs/architecture", "features/techdocs/creating-and-publishing", "features/techdocs/configuration", diff --git a/packages/backend/src/plugins/techInsights.ts b/packages/backend/src/plugins/techInsights.ts index 94ce27656a..80ac559859 100644 --- a/packages/backend/src/plugins/techInsights.ts +++ b/packages/backend/src/plugins/techInsights.ts @@ -38,6 +38,7 @@ export default async function createPlugin( database: env.database, scheduler: env.scheduler, discovery: env.discovery, + tokenManager: env.tokenManager, factRetrievers: [ createFactRetrieverRegistration({ cadence: '1 1 1 * *', // Example cron, At 01:01 on day-of-month 1. diff --git a/plugins/auth-backend/src/providers/auth0/provider.ts b/plugins/auth-backend/src/providers/auth0/provider.ts index b4446caa95..3181314540 100644 --- a/plugins/auth-backend/src/providers/auth0/provider.ts +++ b/plugins/auth-backend/src/providers/auth0/provider.ts @@ -71,6 +71,8 @@ export class Auth0AuthProvider implements OAuthHandlers { clientSecret: options.clientSecret, callbackURL: options.callbackUrl, domain: options.domain, + // We need passReqToCallback set to false to get params, but there's + // no matching type signature for that, so instead behold this beauty passReqToCallback: false as true, }, ( diff --git a/plugins/auth-backend/src/providers/bitbucket/provider.ts b/plugins/auth-backend/src/providers/bitbucket/provider.ts index b149aa6d28..905181333a 100644 --- a/plugins/auth-backend/src/providers/bitbucket/provider.ts +++ b/plugins/auth-backend/src/providers/bitbucket/provider.ts @@ -94,9 +94,7 @@ export class BitbucketAuthProvider implements OAuthHandlers { clientID: options.clientId, clientSecret: options.clientSecret, callbackURL: options.callbackUrl, - // We need passReqToCallback set to false to get params, but there's - // no matching type signature for that, so instead behold this beauty - passReqToCallback: false as true, + passReqToCallback: false, }, ( accessToken: any, diff --git a/plugins/auth-backend/src/providers/google/provider.ts b/plugins/auth-backend/src/providers/google/provider.ts index 66749c1177..233e28f750 100644 --- a/plugins/auth-backend/src/providers/google/provider.ts +++ b/plugins/auth-backend/src/providers/google/provider.ts @@ -73,9 +73,7 @@ export class GoogleAuthProvider implements OAuthHandlers { clientID: options.clientId, clientSecret: options.clientSecret, callbackURL: options.callbackUrl, - // We need passReqToCallback set to false to get params, but there's - // no matching type signature for that, so instead behold this beauty - passReqToCallback: false as true, + passReqToCallback: false, }, ( accessToken: any, diff --git a/plugins/auth-backend/src/providers/microsoft/provider.ts b/plugins/auth-backend/src/providers/microsoft/provider.ts index 3c1110ab5e..0a4b67d667 100644 --- a/plugins/auth-backend/src/providers/microsoft/provider.ts +++ b/plugins/auth-backend/src/providers/microsoft/provider.ts @@ -83,7 +83,7 @@ export class MicrosoftAuthProvider implements OAuthHandlers { callbackURL: options.callbackUrl, authorizationURL: options.authorizationUrl, tokenURL: options.tokenUrl, - passReqToCallback: false as true, + passReqToCallback: false, }, ( accessToken: any, diff --git a/plugins/auth-backend/src/providers/oauth2/provider.ts b/plugins/auth-backend/src/providers/oauth2/provider.ts index 8b0eac948a..8e0250cf2f 100644 --- a/plugins/auth-backend/src/providers/oauth2/provider.ts +++ b/plugins/auth-backend/src/providers/oauth2/provider.ts @@ -76,7 +76,7 @@ export class OAuth2AuthProvider implements OAuthHandlers { callbackURL: options.callbackUrl, authorizationURL: options.authorizationUrl, tokenURL: options.tokenUrl, - passReqToCallback: false as true, + passReqToCallback: false, scope: options.scope, customHeaders: options.includeBasicAuth ? { diff --git a/plugins/auth-backend/src/providers/oidc/provider.ts b/plugins/auth-backend/src/providers/oidc/provider.ts index ed6bdf8c34..aba9048cc7 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.ts @@ -146,7 +146,7 @@ export class OidcAuthProvider implements OAuthHandlers { const strategy = new OidcStrategy( { client, - passReqToCallback: false as true, + passReqToCallback: false, }, ( tokenset: TokenSet, diff --git a/plugins/auth-backend/src/providers/okta/provider.ts b/plugins/auth-backend/src/providers/okta/provider.ts index f208564cb6..4e05ff4487 100644 --- a/plugins/auth-backend/src/providers/okta/provider.ts +++ b/plugins/auth-backend/src/providers/okta/provider.ts @@ -94,7 +94,7 @@ export class OktaAuthProvider implements OAuthHandlers { clientSecret: options.clientSecret, callbackURL: options.callbackUrl, audience: options.audience, - passReqToCallback: false as true, + passReqToCallback: false, store: this.store, response_type: 'code', }, diff --git a/plugins/auth-backend/src/providers/onelogin/provider.ts b/plugins/auth-backend/src/providers/onelogin/provider.ts index 06a57e4560..7dcc6455ac 100644 --- a/plugins/auth-backend/src/providers/onelogin/provider.ts +++ b/plugins/auth-backend/src/providers/onelogin/provider.ts @@ -71,7 +71,7 @@ export class OneLoginProvider implements OAuthHandlers { clientID: options.clientId, clientSecret: options.clientSecret, callbackURL: options.callbackUrl, - passReqToCallback: false as true, + passReqToCallback: false, }, ( accessToken: any, diff --git a/plugins/rollbar-backend/README.md b/plugins/rollbar-backend/README.md index 2dcf6495dd..d28ee60181 100644 --- a/plugins/rollbar-backend/README.md +++ b/plugins/rollbar-backend/README.md @@ -4,6 +4,58 @@ Simple plugin that proxies requests to the [Rollbar](https://rollbar.com) API. ## Setup +1. Install the plugin using: + +```bash +# From your Backstage root directory +yarn add --cwd packages/backend @backstage/plugin-rollbar-backend +``` + +2. Create a `rollbar.ts` file inside `packages/backend/src/plugins/`: + +```typescript +import { createRouter } from '@backstage/plugin-rollbar-backend'; +import { Router } from 'express'; +import { PluginEnvironment } from '../types'; + +export default async function createPlugin( + env: PluginEnvironment, +): Promise { + return await createRouter({ + logger: env.logger, + config: env.config, + }); +} +``` + +3. Modify your `packages/backend/src/index.ts` to include: + +```diff + ... + + import { Config } from '@backstage/config'; + import app from './plugins/app'; ++import rollbar from './plugins/rollbar'; + import scaffolder from './plugins/scaffolder'; + + ... + + async function main() { + + ... + + const authEnv = useHotMemoize(module, () => createEnv('auth')); ++ const rollbarEnv = useHotMemoize(module, () => createEnv('rollbar')); + const proxyEnv = useHotMemoize(module, () => createEnv('proxy')); + + ... + + const apiRouter = Router(); + apiRouter.use('/catalog', await catalog(catalogEnv)); ++ apiRouter.use('/rollbar', await rollbar(rollbarEnv)); + apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv)); +``` + The following values are read from the configuration file. ```yaml diff --git a/plugins/tech-insights-backend-module-jsonfc/README.md b/plugins/tech-insights-backend-module-jsonfc/README.md index 70b2ef2c40..f61666d671 100644 --- a/plugins/tech-insights-backend-module-jsonfc/README.md +++ b/plugins/tech-insights-backend-module-jsonfc/README.md @@ -28,6 +28,7 @@ and modify the `techInsights.ts` file to contain a reference to the FactCheckers config: env.config, database: env.database, discovery: env.discovery, + tokenManager: env.tokenManager, factRetrievers: [myFactRetrieverRegistration], + factCheckerFactory: myFactCheckerFactory }); diff --git a/plugins/tech-insights-backend/README.md b/plugins/tech-insights-backend/README.md index ee6ef4c0cc..e88d150393 100644 --- a/plugins/tech-insights-backend/README.md +++ b/plugins/tech-insights-backend/README.md @@ -35,6 +35,7 @@ export default async function createPlugin( database: env.database, discovery: env.discovery, scheduler: env.scheduler, + tokenManager: env.tokenManager, factRetrievers: [], // Fact retrievers registrations you want tech insights to use }); @@ -104,6 +105,7 @@ const builder = buildTechInsightsContext({ config: env.config, database: env.database, discovery: env.discovery, + tokenManager: env.tokenManager, - factRetrievers: [], + factRetrievers: [myFactRetrieverRegistration], }); @@ -119,6 +121,7 @@ const builder = buildTechInsightsContext({ config: env.config, database: env.database, discovery: env.discovery, + tokenManager: env.tokenManager, - factRetrievers: [], + factRetrievers: process.env.MAIN_FACT_RETRIEVER_INSTANCE ? [myFactRetrieverRegistration] : [], }); @@ -158,9 +161,12 @@ const myFactRetriever: FactRetriever = { const catalogClient = new CatalogClient({ discoveryApi: discovery, }); - const entities = await catalogClient.getEntities({ - filter: [{ kind: 'component' }], - }); + const entities = await catalogClient.getEntities( + { + filter: [{ kind: 'component' }], + }, + { token }, + ); /** * snip: Do complex logic to retrieve facts from external system or calculate fact values */ @@ -214,6 +220,7 @@ and modify the `techInsights.ts` file to contain a reference to the FactChecker config: env.config, database: env.database, discovery: env.discovery, + tokenManager: env.tokenManager, factRetrievers: [myFactRetrieverRegistration], + factCheckerFactory: myFactCheckerFactory }); @@ -273,6 +280,7 @@ export default async function createPlugin( config: env.config, database: env.database, discovery: env.discovery, + tokenManager: env.tokenManager, factRetrievers: [ createFactRetrieverRegistration({ cadence: '0 */6 * * *', // Run every 6 hours - https://crontab.guru/#0_*/6_*_*_* diff --git a/plugins/tech-insights-backend/api-report.md b/plugins/tech-insights-backend/api-report.md index 210aa50af9..9fee3be281 100644 --- a/plugins/tech-insights-backend/api-report.md +++ b/plugins/tech-insights-backend/api-report.md @@ -17,6 +17,7 @@ import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { TechInsightCheck } from '@backstage/plugin-tech-insights-node'; import { TechInsightsStore } from '@backstage/plugin-tech-insights-node'; +import { TokenManager } from '@backstage/backend-common'; // @public export const buildTechInsightsContext: < @@ -95,6 +96,8 @@ export interface TechInsightsOptions< logger: Logger; // (undocumented) scheduler: PluginTaskScheduler; + // (undocumented) + tokenManager: TokenManager; } // (No @packageDocumentation comment for this package) diff --git a/plugins/tech-insights-backend/src/service/fact/FactRetrieverEngine.test.ts b/plugins/tech-insights-backend/src/service/fact/FactRetrieverEngine.test.ts index 0617c0aedd..454711171f 100644 --- a/plugins/tech-insights-backend/src/service/fact/FactRetrieverEngine.test.ts +++ b/plugins/tech-insights-backend/src/service/fact/FactRetrieverEngine.test.ts @@ -22,7 +22,11 @@ import { } from '@backstage/plugin-tech-insights-node'; import { FactRetrieverRegistry } from './FactRetrieverRegistry'; import { FactRetrieverEngine } from './FactRetrieverEngine'; -import { DatabaseManager, getVoidLogger } from '@backstage/backend-common'; +import { + DatabaseManager, + getVoidLogger, + ServerTokenManager, +} from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils'; import { TaskScheduler } from '@backstage/backend-tasks'; @@ -129,6 +133,7 @@ describe('FactRetrieverEngine', () => { factRetrieverContext: { logger: getVoidLogger(), config: ConfigReader.fromConfigs([]), + tokenManager: ServerTokenManager.noop(), discovery: { getBaseUrl: (_: string) => Promise.resolve('http://mock.url'), getExternalBaseUrl: (_: string) => Promise.resolve('http://mock.url'), diff --git a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityMetadataFactRetriever.test.ts b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityMetadataFactRetriever.test.ts index 9d5c57637d..31400788b5 100644 --- a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityMetadataFactRetriever.test.ts +++ b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityMetadataFactRetriever.test.ts @@ -18,6 +18,7 @@ import { RELATION_OWNED_BY } from '@backstage/catalog-model'; import { PluginEndpointDiscovery, getVoidLogger, + ServerTokenManager, } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { GetEntitiesResponse } from '@backstage/catalog-client'; @@ -104,6 +105,7 @@ const handlerContext = { discovery, logger: getVoidLogger(), config: ConfigReader.fromConfigs([]), + tokenManager: ServerTokenManager.noop(), }; const entityFactRetriever = entityMetadataFactRetriever; diff --git a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityMetadataFactRetriever.ts b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityMetadataFactRetriever.ts index 26b1e4274b..caa19dbeaf 100644 --- a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityMetadataFactRetriever.ts +++ b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityMetadataFactRetriever.ts @@ -44,11 +44,19 @@ export const entityMetadataFactRetriever: FactRetriever = { description: 'The entity has tags in metadata', }, }, - handler: async ({ discovery, entityFilter }: FactRetrieverContext) => { + handler: async ({ + discovery, + entityFilter, + tokenManager, + }: FactRetrieverContext) => { + const { token } = await tokenManager.getToken(); const catalogClient = new CatalogClient({ discoveryApi: discovery, }); - const entities = await catalogClient.getEntities({ filter: entityFilter }); + const entities = await catalogClient.getEntities( + { filter: entityFilter }, + { token }, + ); return entities.items.map((entity: Entity) => { return { diff --git a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityOwnershipFactRetriever.test.ts b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityOwnershipFactRetriever.test.ts index 555f761ae8..7a77ef91c4 100644 --- a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityOwnershipFactRetriever.test.ts +++ b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityOwnershipFactRetriever.test.ts @@ -19,6 +19,7 @@ import { RELATION_OWNED_BY } from '@backstage/catalog-model'; import { PluginEndpointDiscovery, getVoidLogger, + ServerTokenManager, } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { GetEntitiesResponse } from '@backstage/catalog-client'; @@ -104,6 +105,7 @@ const handlerContext = { discovery, logger: getVoidLogger(), config: ConfigReader.fromConfigs([]), + tokenManager: ServerTokenManager.noop(), }; const entityFactRetriever = entityOwnershipFactRetriever; diff --git a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityOwnershipFactRetriever.ts b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityOwnershipFactRetriever.ts index b9a91e67f7..c1e9b2710e 100644 --- a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityOwnershipFactRetriever.ts +++ b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityOwnershipFactRetriever.ts @@ -42,11 +42,19 @@ export const entityOwnershipFactRetriever: FactRetriever = { description: 'The spec.owner field is set and refers to a group', }, }, - handler: async ({ discovery, entityFilter }: FactRetrieverContext) => { + handler: async ({ + discovery, + entityFilter, + tokenManager, + }: FactRetrieverContext) => { + const { token } = await tokenManager.getToken(); const catalogClient = new CatalogClient({ discoveryApi: discovery, }); - const entities = await catalogClient.getEntities({ filter: entityFilter }); + const entities = await catalogClient.getEntities( + { filter: entityFilter }, + { token }, + ); return entities.items.map((entity: Entity) => { return { diff --git a/plugins/tech-insights-backend/src/service/fact/factRetrievers/techdocsFactRetriever.test.ts b/plugins/tech-insights-backend/src/service/fact/factRetrievers/techdocsFactRetriever.test.ts index be9397942d..0c70f6c9f1 100644 --- a/plugins/tech-insights-backend/src/service/fact/factRetrievers/techdocsFactRetriever.test.ts +++ b/plugins/tech-insights-backend/src/service/fact/factRetrievers/techdocsFactRetriever.test.ts @@ -19,6 +19,7 @@ import { RELATION_OWNED_BY } from '@backstage/catalog-model'; import { PluginEndpointDiscovery, getVoidLogger, + ServerTokenManager, } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { GetEntitiesResponse } from '@backstage/catalog-client'; @@ -104,6 +105,7 @@ const handlerContext = { discovery, logger: getVoidLogger(), config: ConfigReader.fromConfigs([]), + tokenManager: ServerTokenManager.noop(), }; const entityFactRetriever = techdocsFactRetriever; diff --git a/plugins/tech-insights-backend/src/service/fact/factRetrievers/techdocsFactRetriever.ts b/plugins/tech-insights-backend/src/service/fact/factRetrievers/techdocsFactRetriever.ts index 2717f94475..6bc84f4aa9 100644 --- a/plugins/tech-insights-backend/src/service/fact/factRetrievers/techdocsFactRetriever.ts +++ b/plugins/tech-insights-backend/src/service/fact/factRetrievers/techdocsFactRetriever.ts @@ -40,11 +40,19 @@ export const techdocsFactRetriever: FactRetriever = { description: 'The entity has a title in metadata', }, }, - handler: async ({ discovery, entityFilter }: FactRetrieverContext) => { + handler: async ({ + discovery, + entityFilter, + tokenManager, + }: FactRetrieverContext) => { + const { token } = await tokenManager.getToken(); const catalogClient = new CatalogClient({ discoveryApi: discovery, }); - const entities = await catalogClient.getEntities({ filter: entityFilter }); + const entities = await catalogClient.getEntities( + { filter: entityFilter }, + { token }, + ); return entities.items.map((entity: Entity) => { return { diff --git a/plugins/tech-insights-backend/src/service/router.test.ts b/plugins/tech-insights-backend/src/service/router.test.ts index df2d471516..8513973598 100644 --- a/plugins/tech-insights-backend/src/service/router.test.ts +++ b/plugins/tech-insights-backend/src/service/router.test.ts @@ -19,6 +19,7 @@ import { DatabaseManager, getVoidLogger, PluginDatabaseManager, + ServerTokenManager, } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import request from 'supertest'; @@ -74,6 +75,7 @@ describe('Tech Insights router tests', () => { getBaseUrl: (_: string) => Promise.resolve('http://mock.url'), getExternalBaseUrl: (_: string) => Promise.resolve('http://mock.url'), }, + tokenManager: ServerTokenManager.noop(), }); const router = await createRouter({ diff --git a/plugins/tech-insights-backend/src/service/techInsightsContextBuilder.ts b/plugins/tech-insights-backend/src/service/techInsightsContextBuilder.ts index 4f8f2ed928..e22bfbd253 100644 --- a/plugins/tech-insights-backend/src/service/techInsightsContextBuilder.ts +++ b/plugins/tech-insights-backend/src/service/techInsightsContextBuilder.ts @@ -21,6 +21,7 @@ import { Config } from '@backstage/config'; import { PluginDatabaseManager, PluginEndpointDiscovery, + TokenManager, } from '@backstage/backend-common'; import { FactChecker, @@ -63,6 +64,7 @@ export interface TechInsightsOptions< discovery: PluginEndpointDiscovery; database: PluginDatabaseManager; scheduler: PluginTaskScheduler; + tokenManager: TokenManager; } /** @@ -104,6 +106,7 @@ export const buildTechInsightsContext = async < database, logger, scheduler, + tokenManager, } = options; const factRetrieverRegistry = new FactRetrieverRegistry(factRetrievers); @@ -121,6 +124,7 @@ export const buildTechInsightsContext = async < config, discovery, logger, + tokenManager, }, }); diff --git a/plugins/tech-insights-node/api-report.md b/plugins/tech-insights-node/api-report.md index 7d9f2e00b7..bcccec65b5 100644 --- a/plugins/tech-insights-node/api-report.md +++ b/plugins/tech-insights-node/api-report.md @@ -10,6 +10,7 @@ import { Duration } from 'luxon'; import { DurationLike } from 'luxon'; import { Logger } from 'winston'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { TokenManager } from '@backstage/backend-common'; // @public export type CheckValidationResponse = { @@ -58,6 +59,7 @@ export type FactRetrieverContext = { config: Config; discovery: PluginEndpointDiscovery; logger: Logger; + tokenManager: TokenManager; entityFilter?: | Record[] | Record; diff --git a/plugins/tech-insights-node/src/facts.ts b/plugins/tech-insights-node/src/facts.ts index a5df4bae85..ffd3549bd5 100644 --- a/plugins/tech-insights-node/src/facts.ts +++ b/plugins/tech-insights-node/src/facts.ts @@ -15,7 +15,10 @@ */ import { DateTime, Duration, DurationLike } from 'luxon'; import { Config } from '@backstage/config'; -import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { + PluginEndpointDiscovery, + TokenManager, +} from '@backstage/backend-common'; import { Logger } from 'winston'; /** @@ -135,6 +138,7 @@ export type FactRetrieverContext = { config: Config; discovery: PluginEndpointDiscovery; logger: Logger; + tokenManager: TokenManager; entityFilter?: | Record[] | Record; diff --git a/yarn.lock b/yarn.lock index 14680b9118..a8150451ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5041,9 +5041,9 @@ integrity sha512-6k/pU2jNlgYvKOy84vpCAZ8MGVwybvAdjzrh4UicCVOCPxz0LSBws1OE6O5TO4sPHaSw+yLfNzNK8RicGFc1Kw== "@react-hookz/web@^13.0.0": - version "13.2.1" - resolved "https://registry.npmjs.org/@react-hookz/web/-/web-13.2.1.tgz#12b3bc30d2cced8b75690c6c3708f96cfdf58172" - integrity sha512-ckHdvZ5rgblBscvHdPWAASH46FvfOGjnC+TG3ZtzkOEaYxFtXAzxgQyjjPS+veLuVEGLZ/9HdU6A2l24QBp+9w== + version "13.3.0" + resolved "https://registry.npmjs.org/@react-hookz/web/-/web-13.3.0.tgz#257e31049e92a121912fe1e67bdd01dbec5a203b" + integrity sha512-KswgkmqBVVDo6UnBFfssrojmDisogxC4jGZmd976R8YHoS3zdQJxjqICpOBSRohbRyYhYS9Cprw7BuV/CZcMaw== dependencies: "@react-hookz/deep-equal" "^1.0.1" @@ -10337,9 +10337,9 @@ cypress-plugin-snapshots@^1.4.4: unidiff "1.0.2" cypress@^9.5.0: - version "9.5.4" - resolved "https://registry.npmjs.org/cypress/-/cypress-9.5.4.tgz#49d9272f62eba12f2314faf29c2a865610e87550" - integrity sha512-6AyJAD8phe7IMvOL4oBsI9puRNOWxZjl8z1lgixJMcgJ85JJmyKeP6uqNA0dI1z14lmJ7Qklf2MOgP/xdAqJ/Q== + version "9.6.0" + resolved "https://registry.npmjs.org/cypress/-/cypress-9.6.0.tgz#84473b3362255fa8f5e627a596e58575c9e5320f" + integrity sha512-nNwt9eBQmSENamwa8LxvggXksfyzpyYaQ7lNBLgks3XZ6dPE/6BCQFBzeAyAPt/bNXfH3tKPkAyhiAZPYkWoEg== dependencies: "@cypress/request" "^2.88.10" "@cypress/xvfb" "^1.2.4" @@ -14421,9 +14421,9 @@ inquirer@^7.3.3: through "^2.3.6" inquirer@^8.0.0, inquirer@^8.1.1, inquirer@^8.2.0: - version "8.2.2" - resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.2.2.tgz#1310517a87a0814d25336c78a20b44c3d9b7629d" - integrity sha512-pG7I/si6K/0X7p1qU+rfWnpTE1UIkTONN1wxtzh0d+dHXtT/JG6qBgLxoyHVsQa8cFABxAPh0pD6uUUHiAoaow== + version "8.2.3" + resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.2.3.tgz#9c0b8a3fda91fdb00590557ec8530160e908c6e7" + integrity sha512-jmoBlmWUChXgVi1wGDZsD7pWCaibJKmL+8+E2jaiWiRj8OlJLwQdQQS2CIjgvdg8UUHX+oyagQKicVVcqwxi9Q== dependencies: ansi-escapes "^4.2.1" chalk "^4.1.1" @@ -14439,6 +14439,7 @@ inquirer@^8.0.0, inquirer@^8.1.1, inquirer@^8.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" through "^2.3.6" + wrap-ansi "^7.0.0" internal-slot@^1.0.3: version "1.0.3" @@ -21288,9 +21289,9 @@ react-virtualized-auto-sizer@^1.0.6: integrity sha512-7tQ0BmZqfVF6YYEWcIGuoR3OdYe8I/ZFbNclFlGOC3pMqunkYF/oL30NCjSGl9sMEb17AnzixDz98Kqc3N76HQ== react-window@^1.8.6: - version "1.8.6" - resolved "https://registry.npmjs.org/react-window/-/react-window-1.8.6.tgz#d011950ac643a994118632665aad0c6382e2a112" - integrity sha512-8VwEEYyjz6DCnGBsd+MgkD0KJ2/OXFULyDtorIiTz+QzwoP94tBoA7CnbtyXMm+cCeAUER5KJcPtWl9cpKbOBg== + version "1.8.7" + resolved "https://registry.npmjs.org/react-window/-/react-window-1.8.7.tgz#5e9fd0d23f48f432d7022cdb327219353a15f0d4" + integrity sha512-JHEZbPXBpKMmoNO1bNhoXOOLg/ujhL/BU4IqVU9r8eQPcy5KQnGHIHDRkJ0ns9IM5+Aq5LNwt3j8t3tIrePQzA== dependencies: "@babel/runtime" "^7.0.0" memoize-one ">=3.1.1 <6"