Merge branch 'master' of https://github.com/backstage/backstage into marley/7678-pull-request-custom-filters-2

This commit is contained in:
Marley Powell
2022-01-11 09:32:52 +00:00
1192 changed files with 23669 additions and 9041 deletions
-7
View File
@@ -1,7 +0,0 @@
---
'@backstage/app-defaults': patch
'@backstage/core-app-api': patch
'@backstage/core-plugin-api': patch
---
Add `FetchApi` and related `fetchApiRef` which implement fetch, with an added Backstage token header when available.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/test-utils': patch
---
Add new `MockConfigApi` as a more discoverable and leaner method for mocking configuration.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/config': patch
---
The `ConfigReader#get` method now always returns a deep clone of the configuration data.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend': patch
---
fix: Register plugin to prioritise Component kind for entityRef
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/integration': patch
---
Adds a new GitHub credentials provider (DefaultGithubCredentialsProvider). It handles multiple app configurations. It looks up the app configuration based on the url.
+1 -1
View File
@@ -2,7 +2,7 @@
"$schema": "https://unpkg.com/@changesets/config@1.3.0/schema.json",
"changelog": "./backstage-changelog.js",
"commit": false,
"linked": [["*"]],
"linked": [],
"access": "public",
"baseBranch": "master",
"updateInternalDependencies": "patch",
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-todo-backend': patch
---
Properly exported all referenced types
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-components': patch
---
Create a short delay when `<SidebarSubmenu/>` is opened
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend': patch
---
Add catalog permission rules.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-catalog-react': patch
---
added useOwnedEntities hook to get the list of entities of the logged-in user
+28
View File
@@ -0,0 +1,28 @@
---
'@backstage/cli': minor
---
ESLint upgraded to version 8 and all it's plugins updated to newest version.
If you use any custom plugins for ESLint please check compatibility.
```diff
- "@typescript-eslint/eslint-plugin": "^v4.33.0",
- "@typescript-eslint/parser": "^v4.28.3",
+ "@typescript-eslint/eslint-plugin": "^5.9.0",
+ "@typescript-eslint/parser": "^5.9.0",
- "eslint": "^7.30.0",
+ "eslint": "^8.6.0",
- "eslint-plugin-import": "^2.20.2",
- "eslint-plugin-jest": "^24.1.0",
- "eslint-plugin-jsx-a11y": "^6.2.1",
+ "eslint-plugin-import": "^2.25.4",
+ "eslint-plugin-jest": "^25.3.4",
+ "eslint-plugin-jsx-a11y": "^6.5.1",
- "eslint-plugin-react": "^7.12.4",
- "eslint-plugin-react-hooks": "^4.0.0",
+ "eslint-plugin-react": "^7.28.0",
+ "eslint-plugin-react-hooks": "^4.3.0",
```
Please consult changelogs from packages if you find any problems.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-tech-insights': patch
---
Added possibility to pass customized title and description for the scorecards instead of using hardcoded ones.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-plugin-api': minor
---
Removed the deprecated `OldIconComponent` type.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-graphql': patch
---
Bump graphql versions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend-module-ldap': patch
---
Make sure to avoid accidental data sharing / mutation of `set` values
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-permission-node': patch
---
Add helpers for creating PermissionRules with inferred types
-25
View File
@@ -1,25 +0,0 @@
---
'@backstage/plugin-catalog': patch
---
Deprecated the `CatalogClientWrapper` class.
The default implementation of `catalogApiRef` that this plugin exposes, is now powered by the new `fetchApiRef`. The default implementation of _that_ API, in turn, has the ability to inject the user's Backstage token in requests in a similar manner to what the deprecated `CatalogClientWrapper` used to do. The latter has therefore been taken out of the default catalog API implementation.
If you use a custom `fetchApiRef` implementation that does NOT issue tokens, or use a custom `catalogApiRef` implementation which does NOT use the default `fetchApiRef`, you can still for some time wrap your catalog API in this class to get back the old behavior:
```ts
// Add this to your packages/app/src/plugins.ts if you want to get back the old
// catalog client behavior:
createApiFactory({
api: catalogApiRef,
deps: { discoveryApi: discoveryApiRef, identityApi: identityApiRef },
factory: ({ discoveryApi, identityApi }) =>
new CatalogClientWrapper({
client: new CatalogClient({ discoveryApi }),
identityApi,
}),
}),
```
But do consider migrating to making use of the `fetchApiRef` as soon as convenient, since the wrapper class will be removed in a future release.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend': patch
---
Allow a custom GithubCredentialsProvider to be passed to the GitHub processors.
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/core-plugin-api': minor
---
Removed previously deprecated exports: `PluginHooks`, `PluginOutput`, and `FeatureFlagOutput`.
The deprecated `register` method of `PluginConfig` has been removed, as well as the deprecated `output` method of `BackstagePlugin`.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-catalog-backend-module-ldap': patch
'@backstage/plugin-catalog-backend-module-msgraph': patch
---
Clean up API report
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/catalog-client': patch
---
Add the ability to supply a custom `fetchApi`. In the default frontend app setup, this will use the `fetchApiRef` implementation.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-home': patch
---
Adds two new home components - CompanyLogo and Toolkit.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Switch Webpack minification to use `esbuild` instead of `terser`.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-scaffolder': patch
---
Added OwnedEntityPicker field which displays Owned Entities in options
+25
View File
@@ -0,0 +1,25 @@
---
'@backstage/backend-common': patch
'@backstage/core-components': patch
'@backstage/create-app': patch
'@backstage/integration': patch
'@backstage/techdocs-common': patch
'@backstage/plugin-apache-airflow': patch
'@backstage/plugin-auth-backend': patch
'@backstage/plugin-azure-devops': patch
'@backstage/plugin-catalog-backend': patch
'@backstage/plugin-catalog-import': patch
'@backstage/plugin-catalog-react': patch
'@backstage/plugin-code-coverage': patch
'@backstage/plugin-code-coverage-backend': patch
'@backstage/plugin-cost-insights': patch
'@backstage/plugin-fossa': patch
'@backstage/plugin-jenkins': patch
'@backstage/plugin-scaffolder': patch
'@backstage/plugin-scaffolder-backend': patch
'@backstage/plugin-search-backend-node': patch
'@backstage/plugin-techdocs': patch
'@backstage/plugin-techdocs-backend': patch
---
Cleaned up API exports
+60
View File
@@ -0,0 +1,60 @@
---
'@backstage/create-app': patch
---
Add permissions to create-app's PluginEnvironment
`CatalogEnvironment` now has a `permissions` field, which means that a permission client must now be provided as part of `PluginEnvironment`. To apply these changes to an existing app, add the following to the `makeCreateEnv` function in `packages/backend/src/index.ts`:
```diff
// packages/backend/src/index.ts
+ import { ServerPermissionClient } from '@backstage/plugin-permission-node';
function makeCreateEnv(config: Config) {
...
+ const permissions = ServerPerimssionClient.fromConfig(config, {
+ discovery,
+ tokenManager,
+ });
root.info(`Created UrlReader ${reader}`);
return (plugin: string): PluginEnvironment => {
...
return {
logger,
cache,
database,
config,
reader,
discovery,
tokenManager,
scheduler,
+ permissions,
};
}
}
```
And add a permissions field to the `PluginEnvironment` type in `packages/backend/src/types.ts`:
```diff
// packages/backend/src/types.ts
+ import { PermissionAuthorizer } from '@backstage/plugin-permission-common';
export type PluginEnvironment = {
...
+ permissions: PermissionAuthorizer;
};
```
[`@backstage/plugin-permission-common`](https://www.npmjs.com/package/@backstage/plugin-permission-common) and [`@backstage/plugin-permission-node`](https://www.npmjs.com/package/@backstage/plugin-permission-node) will need to be installed as dependencies:
```diff
// packages/backend/package.json
+ "@backstage/plugin-permission-common": "...",
+ "@backstage/plugin-permission-node": "...",
```
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-common': patch
---
bump `logform` to use fixed version of `color` dependency
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-kubernetes': patch
'@backstage/plugin-pagerduty': patch
'@backstage/plugin-splunk-on-call': patch
---
Clean up emptystate.svg image, removing wrong white artifact from the background
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-components': patch
---
Updated `ResponseErrorPanel` to not use the deprecated `data` property of `ResponseError`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Update `config/eslint.js` to forbid imports of `@material-ui/icons/` as well.
+12
View File
@@ -0,0 +1,12 @@
---
'@backstage/plugin-api-docs': patch
---
Update @asyncapi/react-component to 1.0.0-next.26, which will fix the
`ResizeObserver loop limit exceeded` errors which we encountered in several E2E
tests.
For more details check the following links:
- https://github.com/backstage/backstage/pull/8088#issuecomment-991183968
- https://github.com/asyncapi/asyncapi-react/issues/498
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/core-app-api': patch
---
Fixed an issue where valid SAML and GitHub sessions would be considered invalid and not be stored.
Deprecated the `SamlSession` and `GithubSession` types.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-auth-backend': patch
---
Removed `@backstage/test-utils` dependency.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend': patch
---
Allow a GitHubCredentialsProvider to be passed to the GitHub scaffolder tasks actions.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend': patch
---
Add apply-conditions endpoint for evaluating conditional permissions in catalog backend.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/cli': patch
'@backstage/backend-common': patch
---
Switched to using `@manypkg/get-packages` to list monorepo packages, which provides better support for different kind of monorepo setups.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-app-api': minor
---
Removed previously deprecated `ApiRegistry` export.
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-auth-backend': patch
---
Switched the secure cookie mode set on the `express-session` to use `'auto'` rather than `true`. This works around an issue where cookies would not be set if TLS termination was handled in a proxy rather than having the backend served directly with HTTPS.
The downside of this change is that secure cookies won't be used unless the backend is directly served with HTTPS. This will be remedied in a future update that allows the backend to configured for trusted proxy mode.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-common': patch
---
The GithubUrlReader is switched to use the DefaultGithubCredentialsProvider
@@ -0,0 +1,5 @@
---
'@backstage/plugin-techdocs': patch
---
Fix an issue where the TechDocs sidebar is hidden when the Backstage sidebar is pinned at smaller screen sizes
-9
View File
@@ -1,9 +0,0 @@
---
'@backstage/core-components': patch
'@backstage/plugin-scaffolder': patch
---
In @backstage/plugin-scaffolder - When user will have one option available in hostUrl or owner - autoselect and select component should be readonly.
in @backstage/core-components - Select component has extended API with few more props: native : boolean, disabled: boolean. native - if set to true - Select component will use native browser select picker (not rendered by Material UI lib ).
disabled - if set to true - action on component will not be possible.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-test-utils': patch
---
Bump `testcontainers` dependency to version `8.1.2`
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-airbrake': minor
---
A plugin for Airbrake (https://airbrake.io/) has been created
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-catalog-backend': minor
---
In order to integrate the permissions system with the refresh endpoint in catalog-backend, a new AuthorizedRefreshService was created as a thin wrapper around the existing refresh service which performs authorization and handles the case when authorization is denied. In order to instantiate AuthorizedRefreshService, a permission client is required, which was added as a new field to `CatalogEnvironment`.
The new `permissions` field in `CatalogEnvironment` should already receive the permission client from the `PluginEnvrionment`, so there should be no changes required to the catalog backend setup. See [the create-app changelog](https://github.com/backstage/backstage/blob/master/packages/create-app/CHANGELOG.md) for more details.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/core-app-api': patch
'@backstage/core-plugin-api': patch
---
Removed direct and transitive MUI dependencies.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-common': patch
---
Clean up API reports
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/errors': minor
---
Removed the deprecated exports `ErrorResponse` and `parseErrorResponse`.
Removed the deprecated `constructor` and the deprecated `data` property of `ResponseError`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-kubernetes-backend': patch
---
Exclude the AWS session token from credential validation, because it's not necessary in this context.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-tech-insights': patch
---
fix React warning because of missing `key` prop
-6
View File
@@ -25,12 +25,6 @@ module.exports = {
{
// eslint-disable-next-line no-restricted-syntax
templateFile: path.resolve(__dirname, './scripts/copyright-header.txt'),
templateVars: {
NAME: 'The Backstage Authors',
},
varRegexps: {
NAME: /(The Backstage Authors)|(Spotify AB)/,
},
onNonMatchingHeader: 'replace',
},
],
+2
View File
@@ -42,3 +42,5 @@
/.changeset/search-* @backstage/techdocs-core
/.changeset/techdocs-* @backstage/techdocs-core
/cypress/src/integration/plugins/techdocs.spec.ts @backstage/techdocs-core
/plugins/apache-airflow @backstage/reviewers @cmpadden
/plugins/newrelic-dashboard @backstage/reviewers @mufaddal7
+2 -2
View File
@@ -5,7 +5,7 @@ updates:
schedule:
interval: daily
time: '04:00'
open-pull-requests-limit: 5
open-pull-requests-limit: 10
labels:
- dependencies
- package-ecosystem: npm
@@ -13,6 +13,6 @@ updates:
schedule:
interval: daily
time: '04:00'
open-pull-requests-limit: 2
open-pull-requests-limit: 5
labels:
- dependencies
+13
View File
@@ -9,3 +9,16 @@ search:
- plugins/search/**/*
- plugins/search-*/**/*
- packages/search-*/**/*
docs-like-code:
- plugins/techdocs/**/*
- plugins/techdocs-*/**/*
- packages/techdocs-*/**/*
documentation:
- docs/**/*
microsite:
- microsite/**/*
auth:
- plugins/auth-backend/**/*
- packages/core-app-api/src/apis/implementations/auth/**/*
- packages/core-app-api/src/lib/Auth*/**/*
- packages/core-plugin-api/src/apis/definitions/auth.ts
+8 -1
View File
@@ -1,5 +1,6 @@
abc
accessors
Airbrake
Anddddd
Apdex
api
@@ -92,10 +93,12 @@ Figma
firehydrant
FireHydrant
Firekube
Firestore
Fiverr
gitbeaker
GitHub
GitLab
Gource
Grafana
graphql
GraphQL
@@ -189,6 +192,7 @@ OAuth
octokit
oidc
Okta
Oldsberg
onboarding
Onboarding
OpenShift
@@ -196,6 +200,8 @@ orgs
pagerduty
pageview
parallelization
Patrik
Peloton
plantuml
Platformize
Podman
@@ -295,6 +301,7 @@ truthy
typeahead
ui
unbreak
Unconference
unmanaged
unregister
unregistration
@@ -306,6 +313,7 @@ utils
validator
validators
varchar
Wayfair
Weaveworks
Webpack
winston
@@ -318,4 +326,3 @@ Zalando
Zhou
zoomable
zsh
Firestore
@@ -4,6 +4,14 @@ on:
paths:
- '.github/workflows/chromatic-storybook-test.yml'
- 'packages/storybook/**'
- 'packages/config/src/**'
- 'packages/theme/src/**'
- 'packages/types/src/**'
- 'packages/errors/src/**'
- 'packages/version-bridge/src/**'
- 'packages/test-utils/src/**'
- 'packages/core-app-api/src/**'
- 'packages/core-plugin-api/src/**'
- 'packages/core-components/src/**'
- '**/*.stories.tsx'
+3
View File
@@ -124,6 +124,9 @@ jobs:
- name: check api reports and generate API reference
run: yarn build:api-reports:only --ci --docs
- name: verify changesets
run: node scripts/verify-changesets.js
- name: verify doc links
run: node scripts/verify-links.js
@@ -0,0 +1,72 @@
name: 'Dependabot changeset maker'
on:
- pull_request_target
jobs:
generate-changeset:
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]' && github.repository == 'backstage/backstage'
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 2
ref: ${{ github.head_ref }}
- name: Configure Git
run: |
git config --global user.email noreply@backstage.io
git config --global user.name 'Github changeset workflow'
- name: Generate changeset
uses: actions/github-script@v5
with:
script: |
const { promises: fs } = require('fs');
// Parses package.json files and returns the package names
async function getPackagesNames(files) {
const names = [];
for (const file of files) {
const data = JSON.parse(await fs.readFile(file, 'utf8'));
names.push(data.name);
}
return names;
}
async function createChangeset(fileName, commitMessage, packages) {
const pkgs = packages.map(pkg => `'${pkg}': patch`).join('\n');
const message = commitMessage.replace(/(b|B)ump ([a-z-]+)/, 'Bump `$2`');
const body = `---\n${pkgs}\n---\n\n${message}`;
await fs.writeFile(fileName, body);
}
const branch = await exec.getExecOutput('git branch --show-current');
if (!branch.stdout.startsWith('dependabot/')) {
console.log('Not a dependabot branch, skipping');
return;
}
const diffOutput = await exec.getExecOutput('git diff --name-only HEAD~1');
const diffFiles = diffOutput.stdout.split('\n');
if (diffFiles.find(f => f.startsWith('.changeset'))) {
console.log('Changeset already exists, skipping');
return;
}
const files = diffFiles
.filter(file => file !== 'package.json') // skip root package.json
.filter(file => file.includes('package.json'));
if (!files.length) {
console.log('No package.json changes, skipping');
return;
}
const packageNames = await getPackagesNames(files);
const { stdout: shortHash } = await exec.getExecOutput('git rev-parse --short HEAD');
const fileName = `.changeset/dependabot-${shortHash.trim()}.md`;
const { stdout: commitMessage } = await exec.getExecOutput('git show --pretty=format:%s -s HEAD');
await createChangeset(fileName, commitMessage, packageNames);
await exec.exec('git', ['add', fileName]);
await exec.exec('git commit -C HEAD --amend --no-edit');
await exec.exec('git push --force');
+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
for your contributions.
days-before-issue-stale: 60
days-before-issue-close: 7
exempt-issue-labels: 'pinned,security,plugin,help wanted,good first issue,rfc'
exempt-issue-labels: 'pinned,security,plugin,help wanted,good first issue,rfc,will-fix'
stale-issue-label: stale
stale-pr-message: >
This PR has been automatically marked as stale because it has not had
@@ -25,6 +25,6 @@ jobs:
If you are the author and the PR has been closed, feel free to re-open the PR and continue the contribution!
days-before-pr-stale: 7
days-before-pr-close: 5
exempt-pr-labels: reviewer-approved,awaiting-review
exempt-pr-labels: reviewer-approved,awaiting-review,will-fix
stale-pr-label: stale
operations-per-run: 100
+3
View File
@@ -136,3 +136,6 @@ site
# e2e tests
cypress/cypress/*
# Possible leftover from build:api-reports
tsconfig.tmp.json
+6
View File
@@ -75,3 +75,9 @@
| [Telstra](https://www.telstra.com.au) | [@kiranpatel11](https://github.com/kiranpatel11), [JasonC](https://github.com/JasonC17) | Primary usage: software catalog and templates<br/> Emerging usage : TechDocs, Explore Ecosystem, TechRadar, etc |
| [Mosaico](https://www.mosaico.com.br/) | [Wédney Yuri](https://github.com/wedneyyuri),[@tino.milton](https://github.com/miltonjacomini) | A centralized service catalog of our documentation for our service engineers. |
| [Mox Bank](https://www.mox.com/) | [Nick Laqua](https://github.com/nick-laqua-dragon), [Gauthier Roebroeck](https://github.com/gauthier-roebroeck-mox) | "Single pane of glass" developer portal for providing a best-in-class developer experience to our product teams and making Mox the best tech environment in Hongkong 🥰🚀 |
| [Keyloop](https://www.keyloop.com/) | [Andre Wanlin](https://github.com/awanlin) | Future-motive Developer Portal to help our teams create technology to make everything about buying and owning a car better. 🚗 |
| [Simply Business](https://sbtech.simplybusiness.co.uk/) | [@addersuk](https://github.com/addersuk), [@LightningStairs](https://github.com/LightningStairs), [@punitcse](https://github.com/punitcse), [@moltenice](https://github.com/moltenice) | Central developer portal to access everything a developer needs such as docs, internal service catalog, and the ability to quickly create a new service from a template. Internally developed Backstage plugins allow us to customise the experience to how we work. |
| [Overwolf](https://www.overwolf.com) | [@tomwolfgang](https://github.com/tomwolfgang) | Dev portal - software catalog, tech-docs, scaffolding |
| [Hotmart](https://www.hotmart.com) | [@fabioviana-hotmart](https://github.com/fabioviana-hotmart) | The main Developers Portal to centralize docs, applications and technical metrics. |
| [EF Education First](https://www.ef.com) | [Daan Boerlage](https://github.com/runebaas), [Rafał Nowosielski](https://github.com/rnowosielski) | Our developer portal - primarily used for cataloging and scaffolding with the ambition to expand with more feature adoptions over time |
| [Power Home Remodeling](https://www.techatpower.com) | [Ben Langfeld](https://github.com/benlangfeld) | Developer portal to our internal services, build on open-source software (including Kubernetes) in our own datacenters. Our Portal allows our team members to navigate inherant complexity and standardise. |
+5
View File
@@ -74,6 +74,11 @@ proxy:
headers:
X-Api-Key: ${NEW_RELIC_REST_API_KEY}
'/newrelic/api':
target: https://api.newrelic.com
headers:
X-Api-Key: ${NEW_RELIC_USER_KEY}
'/pagerduty':
target: https://api.pagerduty.com
headers:
+1 -1
View File
@@ -5,7 +5,7 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
version: 0.1.1
version: 0.1.2
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application.
@@ -40,7 +40,7 @@ spec:
servicePort: 80
{{/* Route the backend inside the same hostname as the frontend when they are the same */}}
{{- if eq $frontendUrl.host $backendUrl.host}}
- path: /api
- path: /api/
backend:
serviceName: {{ include "backend.serviceName" . }}
servicePort: 80
@@ -129,8 +129,12 @@ function msUntilExpiry(token: string): number {
// Calls the specified url regularly using an auth token to set a token cookie
// to authorize regular HTTP requests when loading techdocs
async function setTokenCookie(url: string, getIdToken: () => Promise<string>) {
const token = await getIdToken();
async function setTokenCookie(url: string, identityApi: IdentityApi) {
const { token } = await identityApi.getCredentials();
if (!token) {
return;
}
await fetch(url, {
mode: 'cors',
credentials: 'include',
@@ -138,11 +142,12 @@ async function setTokenCookie(url: string, getIdToken: () => Promise<string>) {
Authorization: `Bearer ${token}`,
},
});
// Call this function again a few minutes before the token expires
const ms = msUntilExpiry(token) - 4 * 60 * 1000;
setTimeout(
() => {
setTokenCookie(url, getIdToken);
setTokenCookie(url, identityApi);
},
ms > 0 ? ms : 10000,
);
@@ -160,16 +165,13 @@ const app = createApp({
providers={['guest', 'custom', ...providers]}
title="Select a sign-in method"
align="center"
onResult={async result => {
// When logged in, set a token cookie
if (typeof result.getIdToken !== 'undefined') {
setTokenCookie(
await discoveryApi.getBaseUrl('cookie'),
result.getIdToken,
);
}
// Forward results
props.onResult(result);
onSignInSuccess={async (identityApi: IdentityApi) => {
setTokenCookie(
await discoveryApi.getBaseUrl('cookie'),
identityApi,
);
props.onSignInSuccess(identityApi);
}}
/>
);
@@ -4,7 +4,7 @@ ExampleFetchComponent.tsx reference
```tsx
import React from 'react';
import { useAsync } from 'react-use';
import useAsync from 'react-use/lib/useAsync';
import Alert from '@material-ui/lab/Alert';
import { githubAuthApiRef, useApi } from '@backstage/core-plugin-api';
import { Table, TableColumn, Progress } from '@backstage/core-components';
@@ -1,5 +1,5 @@
/*
* Copyright 2020 Spotify AB
* Copyright 2020 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.
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2020 Spotify AB
* Copyright 2020 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.
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2020 Spotify AB
* Copyright 2020 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.
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2021 Spotify AB
* 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.
+4 -4
View File
@@ -128,8 +128,8 @@ the core APIs. The core APIs are the ones exported by
[configApiRef](../reference/core-plugin-api.configapiref.md).
The core APIs are loaded for any app created with
[createApp](../reference/core-app-api.createapp.md) from
[@backstage/core-plugin-api](../reference/core-plugin-api.md), which means that
[createApp](../reference/app-defaults.createapp.md) from
[@backstage/core-plugin-api](../reference/app-defaults.md), which means that
there is no step that needs to be taken to include these APIs in an app.
### Plugin APIs
@@ -168,7 +168,7 @@ Lastly, the app itself is the final point where APIs can be added, and what has
the final say in what APIs will be loaded at runtime. The app may override the
factories for any of the core or plugin APIs, with the exception of the config,
app theme, and identity APIs. These are static APIs that are tied into the
[createApp](../reference/core-app-api.createapp.md) implementation, and
[createApp](../reference/app-defaults.createapp.md) implementation, and
therefore not possible to override.
Overriding APIs is useful for apps that want to switch out behavior to tailor it
@@ -313,7 +313,7 @@ The common development environment for plugins is included in
[createDevApp](../reference/dev-utils.createdevapp.md) function creates an
application with implementations for all core APIs already present. Contrary to
the method for wiring up Utility API implementations in an app created with
[createApp](../reference/core-app-api.createapp.md),
[createApp](../reference/app-defaults.createapp.md),
[createDevApp](../reference/dev-utils.createdevapp.md) uses automatic dependency
injection. This is to make it possible to replace any API implementation, and
having that be reflected in dependents of that API.
@@ -1,6 +1,7 @@
---
id: adrs-adr000
title: ADR000: [TITLE]
title: 'ADR000: [TITLE]'
# prettier-ignore
description: Architecture Decision Record (ADR) for [TITLE] [DESCRIPTION]
---
@@ -1,6 +1,7 @@
---
id: adrs-adr001
title: ADR001: Architecture Decision Record (ADR) log
title: 'ADR001: Architecture Decision Record (ADR) log'
# prettier-ignore
description: Architecture Decision Record (ADR) logs as a reference point for the team
---
@@ -1,6 +1,7 @@
---
id: adrs-adr002
title: ADR002: Default Software Catalog File Format
title: 'ADR002: Default Software Catalog File Format'
# prettier-ignore
description: Architecture Decision Record (ADR) log on Default Software Catalog File Format
---
@@ -1,6 +1,7 @@
---
id: adrs-adr003
title: ADR003: Avoid Default Exports and Prefer Named Exports
title: 'ADR003: Avoid Default Exports and Prefer Named Exports'
# prettier-ignore
description: Architecture Decision Record (ADR) log on Avoid Default Exports and Prefer Named Exports
---
@@ -1,6 +1,7 @@
---
id: adrs-adr004
title: ADR004: Module Export Structure
title: 'ADR004: Module Export Structure'
# prettier-ignore
description: Architecture Decision Record (ADR) log on Module Export Structure
---
@@ -1,6 +1,7 @@
---
id: adrs-adr005
title: ADR005: Catalog Core Entities
title: 'ADR005: Catalog Core Entities'
# prettier-ignore
description: Architecture Decision Record (ADR) log on Catalog Core Entities
---
@@ -1,6 +1,7 @@
---
id: adrs-adr006
title: ADR006: Avoid React.FC and React.SFC
title: 'ADR006: Avoid React.FC and React.SFC'
# prettier-ignore
description: Architecture Decision Record (ADR) log on Avoid React.FC and React.SFC
---
@@ -1,6 +1,7 @@
---
id: adrs-adr007
title: ADR007: Use MSW to mock http requests
title: 'ADR007: Use MSW to mock http requests'
# prettier-ignore
description: Architecture Decision Record (ADR) log on Use MSW to mock http requests
---
@@ -1,6 +1,7 @@
---
id: adrs-adr008
title: ADR008: Default Catalog File Name
title: 'ADR008: Default Catalog File Name'
# prettier-ignore
description: Architecture Decision Record (ADR) log on Default Catalog File Name
---
@@ -1,6 +1,7 @@
---
id: adrs-adr009
title: ADR009: Entity References
title: 'ADR009: Entity References'
# prettier-ignore
description: Architecture Decision Record (ADR) log on Entity References
---
@@ -1,6 +1,7 @@
---
id: adrs-adr010
title: ADR010: Use the Luxon Date Library
title: 'ADR010: Use the Luxon Date Library'
# prettier-ignore
description: Architecture Decision Record (ADR) for Luxon Date Library
---
@@ -1,6 +1,7 @@
---
id: adrs-adr011
title: ADR011: Plugin Package Structure
title: 'ADR011: Plugin Package Structure'
# prettier-ignore
description: Architecture Decision Record (ADR) for Plugin Package Structure
---
@@ -34,9 +35,11 @@ We follow this structure for plugin packages (where `x` is the plugin name, for
example `catalog` or `techdocs`):
- `x`: Contains the main frontend code of the plugin.
- `x-module-<name>`: Contains optional modules related to the frontend plugin
package.
- `x-backend`: Contains the main backend code of the plugin.
- `x-backend-module-<name>`: Contains optional modules related to the backend
plugin.
plugin package.
- `x-react`: Contains shared widgets, hooks and similar that both the plugin
itself (`x`) and third-party frontend plugins can depend on.
- `x-node`: Contains utilities for backends that both the plugin backend itself
@@ -1,6 +1,7 @@
---
id: adrs-adr012
title: ADR012: Use Luxon.toLocaleString and date/time presets
title: 'ADR012: Use Luxon.toLocaleString and date/time presets'
# prettier-ignore
description: Architecture Decision Record (ADR) for using Luxon's toLocaleString method and date/time presets for displaying dates and times
---
@@ -0,0 +1,71 @@
---
id: adrs-adr013
title: 'ADR013: Proper use of HTTP fetching libraries'
# prettier-ignore
description: Architecture Decision Record (ADR) for the proper use of fetchApiRef, node-fetch, and cross-fetch for data fetching.
---
## Context
Using multiple HTTP packages for data fetching increases the complexity and the
support burden of keeping said package up to date.
## Decision
Backend (node) packages should use the `node-fetch` package for HTTP data
fetching. Example:
```ts
import fetch from 'node-fetch';
import { ResponseError } from '@backstage/errors';
const response = await fetch('https://example.com/api/v1/users.json');
if (!response.ok) {
throw await ResponseError.fromResponse(response);
}
const users = await response.json();
```
Frontend plugins and packages should prefer to use the
[`fetchApiRef`](https://backstage.io/docs/reference/core-plugin-api.fetchapiref).
It uses `cross-fetch` internally. Example:
```ts
import { useApi } from '@backstage/core-plugin-api';
const { fetch } = useApi(fetchApiRef);
const response = await fetch('https://example.com/api/v1/users.json');
if (!response.ok) {
throw await ResponseError.fromResponse(response);
}
const users = await response.json();
```
Isomorphic packages should have a dependency on the `cross-fetch` package for
mocking and type definitions. Preferably, classes and functions in isomorphic
packages should accept an argument of type `typeof fetch` to let callers supply
their preferred implementation of `fetch`. This lets them adorn the calls with
auth or other information, and track metrics etc, in a cross-platform way.
Example:
```ts
import crossFetch from 'cross-fetch';
export class MyClient {
private readonly fetch: typeof crossFetch;
constructor(options: { fetch?: typeof crossFetch }) {
this.fetch = options.fetch || crossFetch;
}
async users() {
return await this.fetch('https://example.com/api/v1/users.json');
}
}
```
## Consequences
We will gradually transition away from third party packages such as `axios`,
`got` and others. Once we have transitioned to `node-fetch` we will add lint
rules to enforce this decision.
+1
View File
@@ -2,6 +2,7 @@
id: adrs-overview
title: Architecture Decision Records (ADR)
sidebar_label: Overview
# prettier-ignore
description: Overview of Architecture Decision Records (ADR)
---
@@ -0,0 +1,746 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1261px" height="642px" viewBox="-0.5 -0.5 1261 642" content="&lt;mxfile&gt;&lt;diagram id=&quot;M4OCM2KiCGRnt6vHj1W_&quot; name=&quot;Page-1&quot;&gt;7V1dl5s2E/41vnSO+bR9mXV227SbdpNsT5v35j0yyDYNIB8s73rz6ysw2IAEyHxKjnNOT40MXph5ZuYZaUaMtIV3+CUA280nZEN3pE7sw0j7MFJVZWJMyf/CkbfjyEyfHwfWgWPHJ50Hvjo/YHJlPLp3bLjLnIgRcrGzzQ5ayPehhTNjIAjQa/a0FXKzf3UL1pAa+GoBlx7927HxJh5VZ5PzF79CZ72J/7Rpxl8sgfV9HaC9H/+9kaqton/Hrz2Q/FZ8/m4DbPSaGtLuR9oiQAgfP3mHBXRD2SZiO173UPDt6b4D6GOeC9TjBS/A3cPkjqP7wm+JLKKngeH5k5F297pxMPy6BVb47SvRPhnbYM8lRwr5uMMB+g4XyEUBGfGRT067i/8GDDA8FN6ncnp6giqIPIiDN3LKIQuMt+zha0o58dAmpZZkDMRwWJ9+9ywS8iGWCltCBxf+Zh908/7vL9aXp3t19/QvHiuG1rLggOusffLZhSty83ehsByCx/fxMEbh+SvHdZmypQTJEHexbOdZ4RpM6bLEq3Ul37lOSRPaxELjw/jZswJGAd6gNfKB+4hCaUVi/Rdi/Bb7F7DHKCt0eHDwP6nP38KfemfERx8O8S9HB2/JgU8e8J/0Qeqq8PB8WXSUXLdCPk50R7zCJPoXnWW/Dz0WGV66yPr+vHH84/CD457NCgQ4fgz9+G3qqBADO7QPrFh+f32GD+jtsPzw6XGGXha//fi41saJbwXBGuKS82K0hzooRVQAXYCdl6wXbd/6EoAWwOOMhPvzaHtoOSPkWxogTLR0qHVrH7xEj6O0DQH7r8Ni/ON39eO3F/+PPfhswx/fxqopGAZoh1uNgYzMOgKEUgqIa3cfTOxMp2JhJ8GyENiZ8nuTIvBMrwU9THVN1aHQU3bbKeYHtttm5G8LA4fcHAzCUxx/HV+VJ4GeY9sRFHNkcXe86jE6+jA7j5AjNUcbifZtAGcriyLr5BvTmsHl6vRNkvqUBhh+npmjmapO08wZg2V2RjIVRRPIDTT3AlI6AZUzhBi6YCFk1oiCKv0lLLKonXneYMThz+fPC/1/c2f+5Y8naxE8m9PJ58T11/cX7REFXpb5TjUyXoLDRzwAz3HDgWewQR6IR+MbVBSJIMX0JIM5krK7TrGJcAqRyEQmRrFarVSLyShsc2kaZk+MQp8MzSgYs4CSJRY5f6HIyCk04RMLNnq0xmnpwHOgqpQUlBcuhmhwmQoDF5UXLzXoiLR4UQabMS277RTT2Lr7teOPR6oJvJA/uGHojwfJTyXDa3wSlxx05OIJDi3huPHjqSXQa4G2qNPqmRBl2u9USGNf0hVx+Ymm06eiZzAFcah8sbbPODStGYekpLkMuAgWhwrwYgiDl3o0V87lFl64DEdzy267Jm0Z/wyzKQPTF9a0S2f0pSwgpjByl6h98ugsAxA4cEdBYPfqeC6IKonSNjdJKdAiUoIBQ9XHeqwWhGnkKtv0OS3MU7VgpvjK6EqaBiXNkR4VNhIpEZegPsQ2NbaQ5yH/ykyL1mkp5PitJqtnjWEzXc1VMh/A5NUyhjs83mPHpS3opmkOTU+H1jQdQYs0DXbfb0qupWSFVTPbq5ZnlJYXsXsWPQQqEz03H2JyxsCTbbUuzXmVzVgAAxetx5brhE8ukdHYBpzZOstoZupSMy8wmtmlms4bDYPr9Gs0idUW6xm/bRl2c1OvDBRHoUv18laM/JWzvqlXSl6TVIAXqxcGAQpu1ispo1EYPVbsIOwdGxBvaq6hZmNoNbNa6bJqdggDXQcAO3JNNoij5OFDsUpPNzwEiOg1mqN7HxbQTxYogJR+hchQqokNe4qujf5TtjjpbI9iNgEcg+2W/OdIZTTVa/ecRnOCnLT8Va3MQkMF23AF9i6Wi+aIo+U+aWxB7QWluV6bWS9aESUHTwlSkrHM+iRj9bJ6yfPyVlb2JB6jMqssnoqyIK7eANAdANgiHwoApbddtpnEbgO24UciKfftLghn7HG1V882HK1cZ/vr6IL9JnYx2YpA0QKPms9yvJRBpBiu15h353vnNWyvm7q3n6IypbS3T+DSlIJKpjqee+DKtyxghK/Y5waMWJWSpbfdsJYpnO7auzB3UTwoc9226IVPiplLEBl12323sNdpQxY3fsnpjqbil1YWJB6D7qN0UQtRXxsppUrEdd1Mo7OqRjw6yudGPYBquC7G0vuuG+SuNbjVakrakSzP2sQ3tnbBbhd/Pu0bmdjGUCGQUfvbcwik4Haqe3pGyA31IeSaAlX1pDBmIpnpcBtFT0xRVq+3ulKtJUDFNuCUZXJzc6oB9gJcKchkWEooCPWi7nXE2d93ir5y9PeVNmKJm7YWYKdxg9+w+2RdK3YEo4Ol950KKvcHEgCI8snoU0QCR8KXKE+N3MQ1b4VyK106BVtQ8/FshDcwGBex7QACS67q5Torx+XIvICyVQV1lbVNdndBnXM+sRwC8nVx1Sqs6gYDLM7eLwY4t/Uox4CPbLr0SmAE1Gr86QYBrDrZfiFgDlpActm07wC7npfOxEq3ecKkzoL1IKmbUFvfc4NAinys8QYaA+9bnKpTkGH3lWYeRLCELLnvFGGQJQsztOosjDmt1koWxt4Mnm4gvGi14xrzrzbX6Msh3GLmdlrYaJuyMWFDF3ZfhJqrTNnEhw2rVLxX2NBdHxfB5gqzPPFBw+yj7Ao1pQSktLy4OL5nAZJZeM6sSKeXqm2w21QT9AuW03LLzozCYZ0Z+DvbIEIvbWg7MareetrKNV8bvINvRqXzdbrFLu+nbXbTpW9202lOlN+KQ8qNqATS8eA7Nkwrt5az4ctNxXLv2kAvSqUCY78zDC0FRdUYMCoyE4HKzftSWyZc5RwDpz0pUu3rxlR1pcuM+I+FvC3yiTCuzXHy5Rt9wWFwlqTS3jXPkjbQkyrLF8jah98LQONoBV4TZW5pkRQl1erEQr4PLQyWyS9MLhEK83XwrAmMVl4Hz96NkeEEGyGcq/s5jdP4jfOcFOOsxmokDifVGSPjuna/0Vhd/PMkDF2qnRkIvQwlkypbLOdprEpmRVefqmRsWimRKluszWusSlZ7bq+qVClVpueICWmLKFtOueRZcVaDWVHGoYgRnSqi2knDLMBkIZWZv6cR0J0GtUq/qvSrQpqPfdwhDwXbjWPdlMilRNbbzfpVYtn7QW4arNYgKy/qSoPMR5CbqbbYh9gCU+0vKBZUsFK6XDx+vBkiZzwc2hIVXe5Uo1bWWKP9NzeHwthluWe1yZ1W1MoQG6vN7DGvL1AbnULIpLZa2WBza5sNrjaOaVyR1daPkzSFc5ImpSUx30waFjU+AM9xw4FnsEEeiEfjP6MoI0H7Jk4Eorr1KrGiys6JBG7zHKDa2H+94NYY63ASGXhP4dQULpw23u71ZuDVBq5wGzhvY90ABt72kuNVEq98BB+eeE0lMfBGO2BK6xn4Q78hrGdIWKoAEKvYM7PRLuHSYow/+oiLMb3xtr4Du7GrRph6BQgz6YmlBfK2ADtLx3XwG4U/Cabh061/kxboDaUORtuZwtrqpTt+Y3KshCUCfgRL6D6hnRO9OI+YCsIYeQwNHMvd0pqNXzbiHdZEDZt3S7BzrHdnl/J/HDjAXzP2FuEr1+Wz/uLVsmzXp0LT39OKW4AwiB9/HJZytkR7lUram7xYJFPKN28Oi7YnFplGXCAQhth4lx1NVhEAq9qxBctpe2qmMxFR8yM9Cqnt9LY3IbFKgtqR0SiszkI49d0voff7FL1cVrv/Dw==&lt;/diagram&gt;&lt;/mxfile&gt;" style="background-color: rgb(255, 255, 255);">
<defs/>
<g>
<rect x="0" y="0" width="10" height="10" fill="#ffffff" stroke="none" pointer-events="all"/>
<rect x="180" y="500" width="100" height="130" fill="none" stroke="#000000" pointer-events="all"/>
<path d="M 160 245 L 253.88 245" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 258.88 245 L 253.88 246.67 L 253.88 243.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 120 230 Q 120 230 120 136.12" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 120 131.12 L 121.67 136.12 L 118.33 136.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 120 260 Q 120 305 143.88 305" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 148.88 305 L 143.88 306.67 L 143.88 303.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 140 230 Q 140 137.5 243.88 137.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 248.88 137.5 L 243.88 139.17 L 243.88 135.83 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
<rect x="80" y="230" width="80" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 70px; height: 1px; padding-top: 245px; margin-left: 90px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
app
</div>
</div>
</div>
</foreignObject>
<text x="90" y="249" fill="#000000" font-family="Helvetica" font-size="12px">
app
</text>
</switch>
</g>
<path d="M 120 390 Q 120 345 143.88 345" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 148.88 345 L 143.88 346.67 L 143.88 343.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 160 405 L 253.88 405" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 258.88 405 L 253.88 406.67 L 253.88 403.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 140 420 Q 140 450 347.5 450 Q 555 450 555 473.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 555 478.88 L 553.33 473.88 L 556.67 473.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
<rect x="80" y="390" width="80" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 70px; height: 1px; padding-top: 405px; margin-left: 90px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
backend
</div>
</div>
</div>
</foreignObject>
<text x="90" y="409" fill="#000000" font-family="Helvetica" font-size="12px">
backend
</text>
</switch>
</g>
<path d="M 387.5 230 Q 387.5 205 378.75 205 Q 370 205 370 186.12" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 370 181.12 L 371.67 186.12 L 368.33 186.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 430 245 Q 475 245 475 266.25 Q 475 287.5 513.88 287.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 518.88 287.5 L 513.88 289.17 L 513.88 285.83 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 430 237.5 Q 585 237.5 585 233.75 Q 585 230 690 230 Q 795 230 795 243.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 795 248.88 L 793.33 243.88 L 796.67 243.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
<rect x="260" y="230" width="170" height="30" fill="#dae8fc" stroke="#6c8ebf" stroke-width="3" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 160px; height: 1px; padding-top: 245px; margin-left: 270px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; font-style: italic; white-space: normal; word-wrap: normal; ">
plugin-&lt;plugin-id&gt;
</div>
</div>
</div>
</foreignObject>
<text x="270" y="249" fill="#000000" font-family="Helvetica" font-size="12px" font-style="italic">
plugin-&lt;plugin-id&gt;
</text>
</switch>
</g>
<path d="M 387.5 420 Q 387.5 555 493.88 555" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 498.88 555 L 493.88 556.67 L 493.88 553.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 430 412.5 Q 585 412.5 585 416.25 Q 585 420 690 420 Q 795 420 795 406.12" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 795 401.12 L 796.67 406.12 L 793.33 406.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 430 405 Q 475 405 475 383.75 Q 475 362.5 513.88 362.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 518.88 362.5 L 513.88 364.17 L 513.88 360.83 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
<rect x="260" y="390" width="170" height="30" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 160px; height: 1px; padding-top: 405px; margin-left: 270px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; font-style: italic; white-space: normal; word-wrap: normal; ">
plugin-&lt;plugin-id&gt;-backend
</div>
</div>
</div>
</foreignObject>
<text x="270" y="409" fill="#000000" font-family="Helvetica" font-size="12px" font-style="italic">
plugin-&lt;plugin-id&gt;-backend
</text>
</switch>
</g>
<path d="M 500 480 L 500 480 L 720 480 L 720 480" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 500 480 L 500 630 L 720 630 L 720 480" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 500 480 L 720 480" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px">
<text x="609.5" y="497.5">
Backend Libraries
</text>
</g>
<rect x="520" y="510" width="180" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 525px; margin-left: 530px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
@backstage/backend-common
</div>
</div>
</div>
</foreignObject>
<text x="530" y="529" fill="#000000" font-family="Helvetica" font-size="12px">
@backstage/backend-common
</text>
</switch>
</g>
<rect x="520" y="550" width="180" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 565px; margin-left: 530px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
@backstage/backend-test-utils
</div>
</div>
</div>
</foreignObject>
<text x="530" y="569" fill="#000000" font-family="Helvetica" font-size="12px">
@backstage/backend-test-utils
</text>
</switch>
</g>
<rect x="520" y="590" width="180" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 605px; margin-left: 530px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
@backstage/backend-tasks
</div>
</div>
</div>
</foreignObject>
<text x="530" y="609" fill="#000000" font-family="Helvetica" font-size="12px">
@backstage/backend-tasks
</text>
</switch>
</g>
<path d="M 1030 250 L 1030 250 L 1250 250 L 1250 250" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1030 250 L 1030 520 L 1250 520 L 1250 250" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1030 250 L 1250 250" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px">
<text x="1139.5" y="267.5">
Common Libraries
</text>
</g>
<rect x="1050" y="440" width="180" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 455px; margin-left: 1060px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
@backstage/catalog-client
</div>
</div>
</div>
</foreignObject>
<text x="1060" y="459" fill="#000000" font-family="Helvetica" font-size="12px">
@backstage/catalog-client
</text>
</switch>
</g>
<rect x="1050" y="280" width="180" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 295px; margin-left: 1060px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
@backstage/types
</div>
</div>
</div>
</foreignObject>
<text x="1060" y="299" fill="#000000" font-family="Helvetica" font-size="12px">
@backstage/types
</text>
</switch>
</g>
<rect x="1050" y="320" width="180" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 335px; margin-left: 1060px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
@backstage/config
</div>
</div>
</div>
</foreignObject>
<text x="1060" y="339" fill="#000000" font-family="Helvetica" font-size="12px">
@backstage/config
</text>
</switch>
</g>
<rect x="1050" y="360" width="180" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 375px; margin-left: 1060px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
@backstage/errors
</div>
</div>
</div>
</foreignObject>
<text x="1060" y="379" fill="#000000" font-family="Helvetica" font-size="12px">
@backstage/errors
</text>
</switch>
</g>
<rect x="1050" y="400" width="180" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 415px; margin-left: 1060px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
@backstage/catalog-model
</div>
</div>
</div>
</foreignObject>
<text x="1060" y="419" fill="#000000" font-family="Helvetica" font-size="12px">
@backstage/catalog-model
</text>
</switch>
</g>
<rect x="1050" y="480" width="180" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 495px; margin-left: 1060px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
@backstage/integration
</div>
</div>
</div>
</foreignObject>
<text x="1060" y="499" fill="#000000" font-family="Helvetica" font-size="12px">
@backstage/integration
</text>
</switch>
</g>
<path d="M 10 20 L 10 20 L 230 20 L 230 20" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 10 20 L 10 130 L 230 130 L 230 20" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 10 20 L 230 20" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px">
<text x="119.5" y="37.5">
Frontend App Core
</text>
</g>
<rect x="30" y="50" width="180" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 65px; margin-left: 40px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
@backstage/core-app-api
</div>
</div>
</div>
</foreignObject>
<text x="40" y="69" fill="#000000" font-family="Helvetica" font-size="12px">
@backstage/core-app-api
</text>
</switch>
</g>
<rect x="30" y="90" width="180" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 105px; margin-left: 40px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
@backstage/app-defaults
</div>
</div>
</div>
</foreignObject>
<text x="40" y="109" fill="#000000" font-family="Helvetica" font-size="12px">
@backstage/app-defaults
</text>
</switch>
</g>
<path d="M 988 305 Q 1010 305 1010 345 Q 1010 385 1023.88 385" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1028.88 385 L 1023.88 386.67 L 1023.88 383.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 988 305 Q 1010 305 1010 250 Q 1010 195 1023.88 195" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1028.88 195 L 1023.88 196.67 L 1023.88 193.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 990 10 L 985 10 Q 980 10 980 20 L 980 295 Q 980 305 975 305 L 972.5 305 Q 970 305 975 305 L 977.5 305 Q 980 305 980 315 L 980 590 Q 980 600 985 600 L 990 600" fill="none" stroke="#000000" stroke-miterlimit="10" transform="translate(980,0)scale(-1,1)translate(-980,0)" pointer-events="none"/>
<path d="M 430 345 Q 475 345 475 353.75 Q 475 362.5 513.88 362.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 518.88 362.5 L 513.88 364.17 L 513.88 360.83 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 430 352.5 Q 465 352.5 465 435 Q 465 517.5 493.88 517.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 498.88 517.5 L 493.88 519.17 L 493.88 515.83 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="150" y="330" width="280" height="30" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 270px; height: 1px; padding-top: 345px; margin-left: 160px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; font-style: italic; white-space: normal; word-wrap: normal; ">
plugin-&lt;plugin-id&gt;-backend-module-&lt;module-id&gt;
</div>
</div>
</div>
</foreignObject>
<text x="160" y="349" fill="#000000" font-family="Helvetica" font-size="12px" font-style="italic">
plugin-&lt;plugin-id&gt;-backend-module-&lt;module-id&gt;
</text>
</switch>
</g>
<path d="M 430 305 Q 475 305 475 296.25 Q 475 287.5 513.88 287.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 518.88 287.5 L 513.88 289.17 L 513.88 285.83 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 430 297.5 Q 464.08 297.5 464.08 186.12" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 464.08 181.12 L 465.75 186.12 L 462.41 186.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="150" y="290" width="280" height="30" fill="#dae8fc" stroke="#6c8ebf" stroke-width="3" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 270px; height: 1px; padding-top: 305px; margin-left: 160px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; font-style: italic; white-space: normal; word-wrap: normal; ">
plugin-&lt;plugin-id&gt;-module-&lt;module-id&gt;
</div>
</div>
</div>
</foreignObject>
<text x="160" y="309" fill="#000000" font-family="Helvetica" font-size="12px" font-style="italic">
plugin-&lt;plugin-id&gt;-module-&lt;module-id&gt;
</text>
</switch>
</g>
<path d="M 1030 160 L 1030 160 L 1250 160 L 1250 160" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1030 160 L 1030 230 L 1250 230 L 1250 160" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1030 160 L 1250 160" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px">
<text x="1139.5" y="177.5">
Common Tooling
</text>
</g>
<rect x="1050" y="190" width="180" height="30" fill="#e1d5e7" stroke="#9673a6" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 205px; margin-left: 1060px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
@backstage/cli
</div>
</div>
</div>
</foreignObject>
<text x="1060" y="209" fill="#000000" font-family="Helvetica" font-size="12px">
@backstage/cli
</text>
</switch>
</g>
<path d="M 850 400 Q 850 555 726.12 555" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 721.12 555 L 726.12 553.33 L 726.12 556.67 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 850 250 Q 850 95 736.12 95" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 731.12 95 L 736.12 93.33 L 736.12 96.67 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 740 250 L 740 250 L 960 250 L 960 250" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 740 250 L 740 400 L 960 400 L 960 250" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 740 250 L 960 250" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px">
<text x="849.5" y="267.5">
External Plugin Libraries
</text>
</g>
<rect x="750" y="280" width="200" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 190px; height: 1px; padding-top: 295px; margin-left: 760px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
plugin-&lt;other-plugin-id&gt;-react
</div>
</div>
</div>
</foreignObject>
<text x="760" y="299" fill="#000000" font-family="Helvetica" font-size="12px">
plugin-&lt;other-plugin-id&gt;-react
</text>
</switch>
</g>
<rect x="750" y="320" width="200" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 190px; height: 1px; padding-top: 335px; margin-left: 760px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
plugin-&lt;other-plugin-id&gt;-common
</div>
</div>
</div>
</foreignObject>
<text x="760" y="339" fill="#000000" font-family="Helvetica" font-size="12px">
plugin-&lt;other-plugin-id&gt;-common
</text>
</switch>
</g>
<rect x="750" y="360" width="200" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 190px; height: 1px; padding-top: 375px; margin-left: 760px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
plugin-&lt;other-plugin-id&gt;-node
</div>
</div>
</div>
</foreignObject>
<text x="760" y="379" fill="#000000" font-family="Helvetica" font-size="12px">
plugin-&lt;other-plugin-id&gt;-node
</text>
</switch>
</g>
<path d="M 700 325 Q 700 325 733.88 325" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 738.88 325 L 733.88 326.67 L 733.88 323.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 610 400 Q 610 400 610 473.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 610 478.88 L 608.33 473.88 L 611.67 473.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 610 250 Q 610 250 610 186.12" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 610 181.12 L 611.67 186.12 L 608.33 186.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 520 250 L 520 250 L 700 250 L 700 250" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 520 250 L 520 400 L 700 400 L 700 250" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 520 250 L 700 250" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px">
<text x="609.5" y="267.5">
Plugin Libraries
</text>
</g>
<rect x="530" y="280" width="160" height="30" fill="#dae8fc" stroke="#6c8ebf" stroke-width="3" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 295px; margin-left: 540px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; font-style: italic; white-space: normal; word-wrap: normal; ">
plugin-&lt;plugin-id&gt;-react
</div>
</div>
</div>
</foreignObject>
<text x="540" y="299" fill="#000000" font-family="Helvetica" font-size="12px" font-style="italic">
plugin-&lt;plugin-id&gt;-react
</text>
</switch>
</g>
<rect x="530" y="320" width="160" height="30" fill="#d5e8d4" stroke="#82b366" stroke-width="3" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 335px; margin-left: 540px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; font-style: italic; white-space: normal; word-wrap: normal; ">
plugin-&lt;plugin-id&gt;-common
</div>
</div>
</div>
</foreignObject>
<text x="540" y="339" fill="#000000" font-family="Helvetica" font-size="12px" font-style="italic">
plugin-&lt;plugin-id&gt;-common
</text>
</switch>
</g>
<rect x="530" y="360" width="160" height="30" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 375px; margin-left: 540px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; font-style: italic; white-space: normal; word-wrap: normal; ">
plugin-&lt;plugin-id&gt;-node
</div>
</div>
</div>
</foreignObject>
<text x="540" y="379" fill="#000000" font-family="Helvetica" font-size="12px" font-style="italic">
plugin-&lt;plugin-id&gt;-node
</text>
</switch>
</g>
<path d="M 250 10 L 250 10 L 730 10 L 730 10" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
<path d="M 250 10 L 250 180 L 730 180 L 730 10" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
<path d="M 250 10 L 730 10" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
<path d="M 260 20 L 260 20 L 480 20 L 480 20" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 260 20 L 260 170 L 480 170 L 480 20" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 260 20 L 480 20" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px">
<text x="369.5" y="37.5">
Frontend Plugin Core
</text>
</g>
<rect x="280" y="50" width="180" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 65px; margin-left: 290px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
@backstage/core-plugin-api
</div>
</div>
</div>
</foreignObject>
<text x="290" y="69" fill="#000000" font-family="Helvetica" font-size="12px">
@backstage/core-plugin-api
</text>
</switch>
</g>
<rect x="280" y="90" width="180" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 105px; margin-left: 290px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
@backstage/test-utils
</div>
</div>
</div>
</foreignObject>
<text x="290" y="109" fill="#000000" font-family="Helvetica" font-size="12px">
@backstage/test-utils
</text>
</switch>
</g>
<rect x="280" y="130" width="180" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 145px; margin-left: 290px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
@backstage/dev-utils
</div>
</div>
</div>
</foreignObject>
<text x="290" y="149" fill="#000000" font-family="Helvetica" font-size="12px">
@backstage/dev-utils
</text>
</switch>
</g>
<path d="M 500 20 L 500 20 L 720 20 L 720 20" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 500 20 L 500 170 L 720 170 L 720 20" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 500 20 L 720 20" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px">
<text x="609.5" y="37.5">
Frontend Libraries
</text>
</g>
<rect x="520" y="130" width="180" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 145px; margin-left: 530px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
@backstage/integration-react
</div>
</div>
</div>
</foreignObject>
<text x="530" y="149" fill="#000000" font-family="Helvetica" font-size="12px">
@backstage/integration-react
</text>
</switch>
</g>
<rect x="520" y="50" width="180" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 65px; margin-left: 530px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
@backstage/core-components
</div>
</div>
</div>
</foreignObject>
<text x="530" y="69" fill="#000000" font-family="Helvetica" font-size="12px">
@backstage/core-components
</text>
</switch>
</g>
<rect x="520" y="90" width="180" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 105px; margin-left: 530px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
@backstage/theme
</div>
</div>
</div>
</foreignObject>
<text x="530" y="109" fill="#000000" font-family="Helvetica" font-size="12px">
@backstage/theme
</text>
</switch>
</g>
<rect x="10" y="500" width="160" height="130" fill="none" stroke="#000000" pointer-events="none"/>
<rect x="20" y="510" width="20" height="20" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/>
<rect x="20" y="570" width="20" height="20" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/>
<rect x="20" y="540" width="20" height="20" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 110px; height: 1px; padding-top: 520px; margin-left: 50px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
Frontend Package
</div>
</div>
</div>
</foreignObject>
<text x="50" y="524" fill="#000000" font-family="Helvetica" font-size="12px">
Frontend Package
</text>
</switch>
</g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 110px; height: 1px; padding-top: 550px; margin-left: 50px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
Isomorphic Package
</div>
</div>
</div>
</foreignObject>
<text x="50" y="554" fill="#000000" font-family="Helvetica" font-size="12px">
Isomorphic Package
</text>
</switch>
</g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 110px; height: 1px; padding-top: 580px; margin-left: 50px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
Backend Package
</div>
</div>
</div>
</foreignObject>
<text x="50" y="584" fill="#000000" font-family="Helvetica" font-size="12px">
Backend Package
</text>
</switch>
</g>
<rect x="20" y="600" width="20" height="20" fill="#e1d5e7" stroke="#9673a6" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 110px; height: 1px; padding-top: 610px; margin-left: 50px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
CLI Package
</div>
</div>
</div>
</foreignObject>
<text x="50" y="614" fill="#000000" font-family="Helvetica" font-size="12px">
CLI Package
</text>
</switch>
</g>
<rect x="190" y="540" width="20" height="20" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/>
<rect x="190" y="600" width="20" height="20" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/>
<rect x="190" y="570" width="20" height="20" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/>
<rect x="250" y="540" width="20" height="20" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/>
<path d="M 210 550 Q 210 550 243.88 550" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 248.88 550 L 243.88 551.67 L 243.88 548.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="250" y="600" width="20" height="20" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/>
<path d="M 210 610 Q 210 610 243.88 610" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 248.88 610 L 243.88 611.67 L 243.88 608.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="250" y="570" width="20" height="20" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/>
<path d="M 210 550 Q 230 550 230 562.5 Q 230 575 243.88 575" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 248.88 575 L 243.88 576.67 L 243.88 573.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 210 605 Q 230 605 230 595 Q 230 585 243.88 585" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 248.88 585 L 243.88 586.67 L 243.88 583.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 210 580 Q 210 580 243.88 580" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 248.88 580 L 243.88 581.67 L 243.88 578.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 90px; height: 1px; padding-top: 520px; margin-left: 190px;">
<div style="box-sizing: border-box; font-size: 0; text-align: left; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; ">
Compatibility
</div>
</div>
</div>
</foreignObject>
<text x="190" y="524" fill="#000000" font-family="Helvetica" font-size="12px">
Compatibility
</text>
</switch>
</g>
<path d="M 251 589 L 251 570 L 270 589 Z" fill="#e1d5e7" stroke="#9673a6" stroke-miterlimit="10" transform="rotate(-180,260.5,579.5)" pointer-events="none"/>
<rect x="0" y="630" width="10" height="10" fill="#ffffff" stroke="none" pointer-events="none"/>
<rect x="1250" y="630" width="10" height="10" fill="#ffffff" stroke="none" pointer-events="none"/>
<rect x="1250" y="0" width="10" height="10" fill="#ffffff" stroke="none" pointer-events="none"/>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Viewer does not support full SVG 1.1
</text>
</a>
</switch>
</svg>

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

+38
View File
@@ -50,3 +50,41 @@ The Bitbucket provider is a structure with two configuration keys:
To add the provider to the frontend, add the `bitbucketAuthApi` reference and
`SignInPage` component as shown in
[Adding the provider to the sign-in page](../index.md#adding-the-provider-to-the-sign-in-page).
## Using Bitbucket for sign-in
In order to use the Bitbucket provider for sign-in, you must configure it with a
`signIn.resolver`. See the
[Sign-In Resolver documentation](../identity-resolver.md) for more details on
how this is done. Note that for the Bitbucket provider, you'll want to use
`bitbucket` as the provider ID, and `createBitbucketProvider` for the provider
factory.
The `@backstage/plugin-auth-backend` plugin also comes with two built-in
resolves that can be used if desired. The first one is the
`bitbucketUsernameSignInResolver`, which identifies users by matching their
Bitbucket username to `bitbucket.org/username` annotations of `User` entities in
the catalog. Note that you must populate your catalog with matching entities or
users will not be able to sign in.
The second resolver is the `bitbucketUsernameSignInResolver`, which works the
same way, but uses the Bitbucket user ID instead, and matches on the
`bitbucket.org/user-id` annotation.
The following is an example of how to use one of the built-in resolvers:
```ts
import {
createBitbucketProvider,
bitbucketUsernameSignInResolver,
} from '@backstage/plugin-auth-backend';
// ...
providerFactories: {
bitbucket: createBitbucketProvider({
signIn: {
resolver: bitbucketUsernameSignInResolver,
},
}),
},
```
+1 -1
View File
@@ -48,6 +48,6 @@ The Microsoft provider is a structure with three configuration keys:
## Adding the provider to the Backstage frontend
To add the provider to the frontend, add the `microsoftAuthApi` reference and
To add the provider to the frontend, add the `microsoftAuthApiRef` reference and
`SignInPage` component as shown in
[Adding the provider to the sign-in page](../index.md#adding-the-provider-to-the-sign-in-page).
+4 -1
View File
@@ -67,7 +67,7 @@ production build.
## Configuration Files
It is possible to have multiple configuration files (bundled and/or remote),
It is possible to have multiple configuration files (bundled and/or remote\*),
both to support different environments, but also to define configuration that is
local to specific packages. The configuration files to load are selected using a
`--config <local-path|url>` flag, and it is possible to load any number of
@@ -77,6 +77,9 @@ root when running the backend, you would use `--config ../../my-config.yaml`,
and for config file on a config server you would use
`--config https://some.domain.io/app-config.yaml`
**Note**: In case URLs are passed, it is also needed to set the remote option in
the loadBackendConfig call.
If no `config` flags are specified, the default behavior is to load
`app-config.yaml` and, if it exists, `app-config.local.yaml` from the repo root.
In the provided project setup, `app-config.local.yaml` is `.gitignore`'d, making
+59 -2
View File
@@ -39,6 +39,7 @@ kubernetes:
region: 'europe-west1'
skipTLSVerify: true
skipMetricsLookup: true
exposeDashboard: true
```
### `serviceLocatorMethod`
@@ -113,9 +114,13 @@ kubectl -n <NAMESPACE> get secret $(kubectl -n <NAMESPACE> get sa <SERVICE_ACCOU
Specifies the link to the Kubernetes dashboard managing this cluster.
Note that you should specify the app used for the dashboard using the
**dashboardApp property**, in order to properly format links to kubernetes
`dashboardApp` property, in order to properly format links to kubernetes
resources, otherwise it will assume that you're running the standard one.
Note also that this attribute is optional for some kinds of dashboards, such as
GKE, which requires additional parameters specified in the `dashboardParameters`
option.
##### `clusters.\*.dashboardApp` (optional)
Specifies the app that provides the Kubernetes dashboard.
@@ -124,11 +129,14 @@ This will be used for formatting links to kubernetes objects inside the
dashboard.
The supported dashboards are: `standard`, `rancher`, `openshift`, `gke`, `aks`,
`eks` However, not all of them are implemented yet, so please contribute!
`eks`. However, not all of them are implemented yet, so please contribute!
Note that it will default to the regular dashboard provided by the Kubernetes
project (`standard`), that can run in any Kubernetes cluster.
Note that for the `gke` app, you must provide additional information in the
`dashboardParameters` option.
Note that you can add your own formatter by registering it to the
`clusterLinksFormatters` dictionary, in the app project.
@@ -143,6 +151,43 @@ See also
https://github.com/backstage/backstage/tree/master/plugins/kubernetes/src/utils/clusterLinks/formatters
for real examples.
##### `clusters.\*.dashboardParameters` (optional)
Specifies additional information for the selected `dashboardApp` formatter.
Note that, even though `dashboardParameters` is optional, it might be mandatory
for some dashboards, such as GKE.
###### required parameters for GKE
| Name | Description |
| ----------- | ------------------------------------------------------------------------ |
| projectId | the ID of the GCP project containing your Kubernetes clusters |
| region | the region of GCP containing your Kubernetes clusters |
| clusterName | the name of your kubernetes cluster, within your `projectId` GCP project |
Note that the GKE cluster locator can automatically provide the values for the
`dashboardApp` and `dashboardParameters` options if you set the
`exposeDashboard` property to `true`.
Example:
```yaml
kubernetes:
serviceLocatorMethod:
type: 'multiTenant'
clusterLocatorMethods:
- type: 'config'
clusters:
- url: http://127.0.0.1:9999
name: my-cluster
dashboardApp: gke
dashboardParameters:
projectId: my-project
region: us-east1
clusterName: my-cluster
```
##### `clusters.\*.caData` (optional)
PEM-encoded certificate authority certificates.
@@ -184,6 +229,10 @@ For example:
Will configure the Kubernetes plugin to connect to all GKE clusters in the
project `gke-clusters` in the region `europe-west1`.
Note that the GKE cluster locator can automatically provide the values for the
`dashboardApp` and `dashboardParameters` options if you enable the
`exposeDashboard` option.
##### `projectId`
The Google Cloud project to look for Kubernetes clusters in.
@@ -203,6 +252,14 @@ presented by the API server. Defaults to `false`.
This determines whether the Kubernetes client looks up resource metrics
CPU/Memory for pods returned by the API server. Defaults to `false`.
##### `exposeDashboard`
This determines wether the `dashboardApp` and `dashboardParameters` should be
automatically configured in order to expose the GKE dashboard from the
Kubernetes plugin.
Defaults to `false`.
### `customResources` (optional)
Configures which [custom resources][3] to look for when returning an entity's
+34
View File
@@ -97,3 +97,37 @@ of the `SearchType` component.
...
</Paper>
```
## How to limit what can be searched in the Software Catalog
The Software Catalog includes a wealth of information about the components,
systems, groups, users, and other aspects of your software ecosystem. However,
you may not always want _every_ aspect to appear when a user searches the
catalog. Examples include:
- Entities of kind `Location`, which are often not useful to Backstage users.
- Entities of kind `User` or `Group`, if you'd prefer that users and groups be
exposed to search in a different way (or not at all).
It's possible to write your own [Collator](./concepts.md#collators) to control
exactly what's available to search, (or a [Decorator](./concepts.md#decorators)
to filter things out here and there), but the `DefaultCatalogCollator` that's
provided by `@backstage/plugin-catalog-backend` offers some configuration too!
```diff
// packages/backend/src/plugins/search.ts
indexBuilder.addCollator({
defaultRefreshIntervalSeconds: 600,
collator: DefaultCatalogCollator.fromConfig(config, {
discovery,
tokenManager,
+ filter: {
+ kind: ['API', 'Component', 'Domain', 'Group', 'System', 'User'],
+ },
}),
});
```
As shown above, you can add a catalog entity filter to narrow down what catalog
entities are indexed by the search engine.

Some files were not shown because too many files have changed in this diff Show More