diff --git a/.changeset/carpal-tunnel-driver.md b/.changeset/carpal-tunnel-driver.md
new file mode 100644
index 0000000000..2e6a814533
--- /dev/null
+++ b/.changeset/carpal-tunnel-driver.md
@@ -0,0 +1,13 @@
+---
+'@backstage/plugin-catalog-backend': minor
+---
+
+- The `CatalogProcessor` API was updated to have `preProcessEntity` and
+ `postProcessEntity` methods, instead of just one `processEntity`. This makes
+ it easier to make processors that have several stages in one, and to make
+ different processors more position independent in the list of processors.
+- The `EntityPolicy` is now given directly to the `LocationReaders`, instead of
+ being enforced inside a policy. We have decided to separate out the act of
+ validating an entity to be outside of the processing flow, to make it
+ possible to apply more liberally and to evolve it as a separate concept.
+- Because of the above, the `EntityPolicyProcessor` has been removed.
diff --git a/.changeset/eighty-lamps-smell.md b/.changeset/eighty-lamps-smell.md
new file mode 100644
index 0000000000..85dfba4f82
--- /dev/null
+++ b/.changeset/eighty-lamps-smell.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-catalog': patch
+---
+
+handle the case where no entities are available to show
diff --git a/.changeset/flat-bugs-do.md b/.changeset/flat-bugs-do.md
new file mode 100644
index 0000000000..672daf657d
--- /dev/null
+++ b/.changeset/flat-bugs-do.md
@@ -0,0 +1,8 @@
+---
+'@backstage/core': patch
+'@backstage/plugin-graphiql': patch
+'@backstage/plugin-tech-radar': patch
+'@backstage/plugin-techdocs': patch
+---
+
+add test case for Progress component
diff --git a/.changeset/lovely-suits-flash.md b/.changeset/lovely-suits-flash.md
new file mode 100644
index 0000000000..f093290c85
--- /dev/null
+++ b/.changeset/lovely-suits-flash.md
@@ -0,0 +1,5 @@
+---
+'@backstage/core': patch
+---
+
+update the test cases of CodeSnippet component
diff --git a/.gitignore b/.gitignore
index e7d294d55a..5c27601791 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,11 @@
-.idea/
+# macOS
.DS_Store
+
+# IntelliJ
+.idea/
+*.iml
+
+# Visual Studio Code
.vscode/
.vsls.json
diff --git a/docs/features/software-catalog/configuration.md b/docs/features/software-catalog/configuration.md
index 46de2d3d67..dd19d4cd51 100644
--- a/docs/features/software-catalog/configuration.md
+++ b/docs/features/software-catalog/configuration.md
@@ -1,5 +1,5 @@
---
-id: software-catalog-configuration
+id: configuration
title: Catalog Configuration
description: Documentation on Software Catalog Configuration
---
diff --git a/docs/getting-started/create-an-app.md b/docs/getting-started/create-an-app.md
index 9be0e24452..670c7fbf98 100644
--- a/docs/getting-started/create-an-app.md
+++ b/docs/getting-started/create-an-app.md
@@ -124,3 +124,21 @@ the root directory:
```bash
yarn workspace backend start
```
+
+### Troubleshooting
+
+#### Cannot find module
+
+You may encounter an error similar to below:
+
+```
+internal/modules/cjs/loader.js:968
+ throw err;
+ ^
+
+Error: Cannot find module '../build/Debug/nodegit.node'
+```
+
+This can occur if an npm dependency is not completely installed. Because some
+dependencies run a post-install script, ensure that both your npm and yarn
+configs have the `ignore-scripts` flag set to `false`.
diff --git a/microsite/data/plugins/aws-lambda.yaml b/microsite/data/plugins/aws-lambda.yaml
new file mode 100644
index 0000000000..7fa276f444
--- /dev/null
+++ b/microsite/data/plugins/aws-lambda.yaml
@@ -0,0 +1,9 @@
+---
+title: AWS Lambda
+author: roadie.io
+authorUrl: https://roadie.io
+category: Monitoring
+description: View AWS Lambda functions for your components in Backstage.
+documentation: https://roadie.io/backstage/plugins/aws-lambda
+iconUrl: https://roadie.io/static/77f62f79e27ae8565496e4df7eef8be5/45f2b/logo.png
+npmPackageName: '@roadiehq/backstage-plugin-aws-lambda'
diff --git a/microsite/package.json b/microsite/package.json
index 335ada7212..ecf7752b05 100644
--- a/microsite/package.json
+++ b/microsite/package.json
@@ -15,7 +15,7 @@
},
"devDependencies": {
"@spotify/prettier-config": "^8.0.0",
- "docusaurus": "^2.0.0-alpha.65",
+ "docusaurus": "^2.0.0-alpha.66",
"js-yaml": "^3.14.0",
"prettier": "^2.0.5"
},
diff --git a/microsite/pages/en/plugins.js b/microsite/pages/en/plugins.js
index a8255bcd0d..66f3a9776d 100644
--- a/microsite/pages/en/plugins.js
+++ b/microsite/pages/en/plugins.js
@@ -44,7 +44,7 @@ const Plugins = () => (
-
+
{pluginMetadata.map(
({
iconUrl,
@@ -57,26 +57,25 @@ const Plugins = () => (
}) => (
),
)}
diff --git a/microsite/static/css/custom.css b/microsite/static/css/custom.css
index 6599aef6a6..22855a2b4d 100644
--- a/microsite/static/css/custom.css
+++ b/microsite/static/css/custom.css
@@ -15,9 +15,6 @@ table tr:nth-child(2n) {
background-color: #2b2b2b;
}
-@media only screen and (min-device-width: 360px) and (max-device-width: 736px) {
-}
-
@media only screen and (min-width: 1024px) and (max-width: 1500px) {
/* Experience */
.content-experience {
@@ -857,7 +854,6 @@ code {
position: relative;
padding-top: 75%;
width: 100%;
- /* border: 1px dotted #f00; */
}
.Block__Graphic {
@@ -1088,6 +1084,13 @@ code {
.nav-footer .copyright {
width: 600px;
color: #fff;
- margin: auto;
+ margin: 0 auto;
font-size: 10pt;
}
+
+@media only screen and (max-width: 485px) {
+ .nav-footer .copyright {
+ width: auto;
+ margin: 0 1.5em;
+ }
+}
diff --git a/microsite/static/css/plugins.css b/microsite/static/css/plugins.css
index 9a8bbafa46..8a0d843dc9 100644
--- a/microsite/static/css/plugins.css
+++ b/microsite/static/css/plugins.css
@@ -6,7 +6,7 @@
flex-direction: column;
}
-.grid {
+.PluginGrid {
display: grid;
grid-gap: 1rem;
grid-template-columns: repeat(4, 1fr);
@@ -15,17 +15,23 @@
}
@media (max-width: 1200px) {
- .grid {
+ .PluginGrid {
grid-template-columns: repeat(3, 1fr);
}
}
@media only screen and (max-width: 815px) {
- .grid {
+ .PluginGrid {
grid-template-columns: repeat(2, 1fr);
}
}
+@media only screen and (max-width: 485px) {
+ .PluginGrid {
+ grid-template-columns: 1fr;
+ }
+}
+
.PluginCard img {
float: left;
margin: 0px 16px 8px 0px;
@@ -34,14 +40,32 @@
}
.PluginCardHeader {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
max-height: fit-content;
min-height: fit-content;
}
+.PluginCardImage {
+ width: 80px;
+ height: 80px;
+ margin-right: 16px;
+}
+
+.PluginCardImage img {
+ width: 100%;
+ max-width: 100%;
+}
+
.PluginCardTitle {
color: white;
vertical-align: top;
- margin: 8px 0px 0px 16px;
+ margin: 8px 0 0;
+}
+
+.PluginCardInfo {
+ flex: 1;
}
.PluginAddNewButton {
@@ -50,11 +74,16 @@
right: 0px;
}
+@media only screen and (max-width: 485px) {
+ .PluginAddNewButton {
+ bottom: -4px;
+ }
+}
+
.ButtonFilled {
padding: 4px 8px;
border-radius: 4px;
color: #69ddc7;
- margin-top: 36px;
}
.ButtonFilled:hover {
@@ -62,7 +91,7 @@
background-color: transparent;
}
-.ChipOutlined {
+.PluginCardChipOutlined {
font-size: small;
border-radius: 16px;
padding: 2px 8px;
@@ -70,11 +99,16 @@
color: #69ddc7;
}
-.PluginCardLink {
+.PluginCardFooter {
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-end;
+ margin-top: auto;
+ min-height: 2em;
+}
+
+.PluginCardFooter a {
padding: 2px 8px;
- position: absolute;
- bottom: 0;
- right: 0;
}
.PluginPageLayout {
@@ -95,18 +129,13 @@
padding-top: 8px;
}
-.PluginCardFooter {
- position: relative;
- min-height: 2em;
-}
-
-.Author,
-.Author a {
+.PluginCardAuthor,
+.PluginCardAuthor a {
margin-bottom: 0.25em;
color: rgba(255, 255, 255, 0.6);
}
-.Author a:hover {
+.PluginCardAuthor a:hover {
color: white;
}
diff --git a/microsite/yarn.lock b/microsite/yarn.lock
index 40e46c3545..6707532f9c 100644
--- a/microsite/yarn.lock
+++ b/microsite/yarn.lock
@@ -2233,10 +2233,10 @@ dir-glob@2.0.0:
arrify "^1.0.1"
path-type "^3.0.0"
-docusaurus@^2.0.0-alpha.65:
- version "2.0.0-alpha.65"
- resolved "https://registry.yarnpkg.com/docusaurus/-/docusaurus-2.0.0-alpha.65.tgz#e2b84985529deb79797aff52aa39ffbe03cd8a20"
- integrity sha512-OEnKbXLPy3EpbQA/Dj0kakB6fg/Qni1LORTIero+5Q5G0QT5OH4FAbBQOy3L1qQH0S5Bv18zESxX9Nvclg20ag==
+docusaurus@^2.0.0-alpha.66:
+ version "2.0.0-alpha.66"
+ resolved "https://registry.yarnpkg.com/docusaurus/-/docusaurus-2.0.0-alpha.66.tgz#4dece48b838f773c4c973d9fe2546dd0bf2637cf"
+ integrity sha512-/HmRt3hEqpvZRdR2JRYKpwNUgalO14exxuwQu2vXW5eYzcKbQ922+3SxG7mF+LE8f7KOmgY+A3eS3SEyq+S3XA==
dependencies:
"@babel/core" "^7.9.0"
"@babel/plugin-proposal-class-properties" "^7.8.3"
diff --git a/packages/app/package.json b/packages/app/package.json
index 084bbf465b..dc452e0803 100644
--- a/packages/app/package.json
+++ b/packages/app/package.json
@@ -34,7 +34,7 @@
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"@octokit/rest": "^18.0.0",
- "@roadiehq/backstage-plugin-github-insights": "^0.2.6",
+ "@roadiehq/backstage-plugin-github-insights": "^0.2.7",
"@roadiehq/backstage-plugin-github-pull-requests": "^0.5.2",
"@roadiehq/backstage-plugin-travis-ci": "^0.2.3",
"dayjs": "^1.9.1",
diff --git a/packages/backend-common/src/database/config.test.ts b/packages/backend-common/src/database/config.test.ts
index c9a4107686..eb26013b4b 100644
--- a/packages/backend-common/src/database/config.test.ts
+++ b/packages/backend-common/src/database/config.test.ts
@@ -17,7 +17,7 @@
import { mergeDatabaseConfig } from './config';
describe('config', () => {
- describe(mergeDatabaseConfig, () => {
+ describe('mergeDatabaseConfig', () => {
it('does not mutate the input object', () => {
const input = {
original: 'key',
diff --git a/packages/backend-common/src/database/connection.test.ts b/packages/backend-common/src/database/connection.test.ts
index 2cc3a54e06..de0b0e40dc 100644
--- a/packages/backend-common/src/database/connection.test.ts
+++ b/packages/backend-common/src/database/connection.test.ts
@@ -26,7 +26,7 @@ describe('database connection', () => {
},
]);
- describe(createDatabaseClient, () => {
+ describe('createDatabaseClient', () => {
it('returns a postgres connection', () => {
expect(
createDatabaseClient(
diff --git a/packages/backend-common/src/database/postgres.test.ts b/packages/backend-common/src/database/postgres.test.ts
index 82161dc53c..8d139e4481 100644
--- a/packages/backend-common/src/database/postgres.test.ts
+++ b/packages/backend-common/src/database/postgres.test.ts
@@ -44,7 +44,7 @@ describe('postgres', () => {
},
]);
- describe(buildPgDatabaseConfig, () => {
+ describe('buildPgDatabaseConfig', () => {
it('builds a postgres config', () => {
const mockConnection = createMockConnection();
@@ -125,7 +125,7 @@ describe('postgres', () => {
});
});
- describe(getPgConnectionConfig, () => {
+ describe('getPgConnectionConfig', () => {
it('returns the connection object back', () => {
const mockConnection = createMockConnection();
const config = createConfig(mockConnection);
@@ -163,7 +163,7 @@ describe('postgres', () => {
});
});
- describe(createPgDatabaseClient, () => {
+ describe('createPgDatabaseClient', () => {
it('creates a postgres knex instance', () => {
expect(
createPgDatabaseClient(
@@ -188,8 +188,8 @@ describe('postgres', () => {
});
});
- describe(parsePgConnectionString, () => {
- it('parses a connection string uri ', () => {
+ describe('parsePgConnectionString', () => {
+ it('parses a connection string uri', () => {
expect(
parsePgConnectionString(
'postgresql://postgres:pass@foobar:5432/dbname?ssl=true',
diff --git a/packages/backend-common/src/database/sqlite3.test.ts b/packages/backend-common/src/database/sqlite3.test.ts
index dbff5ee354..a3ab331c2d 100644
--- a/packages/backend-common/src/database/sqlite3.test.ts
+++ b/packages/backend-common/src/database/sqlite3.test.ts
@@ -32,7 +32,7 @@ describe('sqlite3', () => {
},
]);
- describe(buildSqliteDatabaseConfig, () => {
+ describe('buildSqliteDatabaseConfig', () => {
it('buidls a string connection', () => {
expect(buildSqliteDatabaseConfig(createConfig(':memory:'))).toEqual({
client: 'sqlite3',
@@ -72,7 +72,7 @@ describe('sqlite3', () => {
});
});
- describe(createSqliteDatabaseClient, () => {
+ describe('createSqliteDatabaseClient', () => {
it('creates an in memory knex instance', () => {
expect(
createSqliteDatabaseClient(
diff --git a/packages/backend-common/src/paths.ts b/packages/backend-common/src/paths.ts
index 402c0e2252..262be366f6 100644
--- a/packages/backend-common/src/paths.ts
+++ b/packages/backend-common/src/paths.ts
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-/* eslint-disable @typescript-eslint/camelcase */
-
import { resolve as resolvePath } from 'path';
/**
diff --git a/packages/catalog-model/src/entity/policies/ReservedFieldsEntityPolicy.test.ts b/packages/catalog-model/src/entity/policies/ReservedFieldsEntityPolicy.test.ts
deleted file mode 100644
index 5d4e60a1a3..0000000000
--- a/packages/catalog-model/src/entity/policies/ReservedFieldsEntityPolicy.test.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2020 Spotify AB
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import yaml from 'yaml';
-import { ReservedFieldsEntityPolicy } from './ReservedFieldsEntityPolicy';
-
-describe('ReservedFieldsEntityPolicy', () => {
- let data: any;
- let policy: ReservedFieldsEntityPolicy;
-
- beforeEach(() => {
- data = yaml.parse(`
- apiVersion: backstage.io/v1alpha1
- kind: Component
- metadata:
- uid: e01199ab-08cc-44c2-8e19-5c29ded82521
- etag: lsndfkjsndfkjnsdfkjnsd==
- generation: 13
- name: my-component-yay
- namespace: the-namespace
- labels:
- backstage.io/custom: ValueStuff
- annotations:
- example.com/bindings: are-secret
- tags:
- - java
- - data
- spec:
- custom: stuff
- `);
- policy = new ReservedFieldsEntityPolicy();
- });
-
- it('works for the happy path', async () => {
- await expect(policy.enforce(data)).resolves.toBe(data);
- });
-
- it('rejects reserved keys in the spec root', async () => {
- data.spec.apiVersion = 'a/b';
- await expect(policy.enforce(data)).rejects.toThrow(/spec.*apiVersion/i);
- });
-
- it('rejects reserved keys in labels', async () => {
- data.metadata.labels.apiVersion = 'a';
- await expect(policy.enforce(data)).rejects.toThrow(/label.*apiVersion/i);
- });
-
- it('rejects reserved keys in annotations', async () => {
- data.metadata.annotations.apiVersion = 'a';
- await expect(policy.enforce(data)).rejects.toThrow(
- /annotation.*apiVersion/i,
- );
- });
-
- it('rejects core fields mistakenly placed in metadata', async () => {
- data.metadata.owner = 'emma';
- await expect(policy.enforce(data)).rejects.toThrow(/owner/i);
- });
-});
diff --git a/packages/catalog-model/src/entity/policies/ReservedFieldsEntityPolicy.ts b/packages/catalog-model/src/entity/policies/ReservedFieldsEntityPolicy.ts
deleted file mode 100644
index edb63cf05c..0000000000
--- a/packages/catalog-model/src/entity/policies/ReservedFieldsEntityPolicy.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2020 Spotify AB
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EntityPolicy } from '../../types';
-import { Entity } from '../Entity';
-
-const DEFAULT_RESERVED_ENTITY_FIELDS: string[] = [
- 'apiVersion',
- 'kind',
- 'spec',
- 'metadata.uid',
- 'metadata.etag',
- 'metadata.generation',
- 'metadata.name',
- 'metadata.namespace',
- 'metadata.description',
- 'metadata.labels',
- 'metadata.annotations',
- 'metadata.tags',
- // The below items are known to appear in core kinds, and therefore should
- // not be appearing in metadata (which would indicate that the user made a
- // mistake in where to place them).
- 'spec.lifecycle',
- 'spec.owner',
-];
-
-/**
- * Ensures that fields are not given certain reserved names.
- */
-export class ReservedFieldsEntityPolicy implements EntityPolicy {
- private readonly reservedFields: string[];
-
- constructor(fields?: string[]) {
- this.reservedFields = [
- ...(fields ?? []),
- ...DEFAULT_RESERVED_ENTITY_FIELDS,
- ];
- }
-
- async enforce(entity: Entity): Promise {
- for (const path of this.reservedFields) {
- const [where, name] = path.includes('.')
- ? path.split('.')
- : [undefined, path];
-
- if (where !== 'metadata' && entity.metadata.hasOwnProperty(name)) {
- throw new Error(
- `The metadata may not contain the field ${name}, because it has reserved meaning`,
- );
- }
- if (where !== 'spec' && entity.spec?.hasOwnProperty(name)) {
- throw new Error(
- `The spec may not contain the field ${name}, because it has reserved meaning`,
- );
- }
- if (where !== 'labels' && entity.metadata.labels?.hasOwnProperty(name)) {
- throw new Error(
- `A label may not have the field ${name}, because it has reserved meaning`,
- );
- }
- if (
- where !== 'annotations' &&
- entity.metadata.annotations?.hasOwnProperty(name)
- ) {
- throw new Error(
- `An annotation may not have the field ${name}, because it has reserved meaning`,
- );
- }
- }
- return entity;
- }
-}
diff --git a/packages/catalog-model/src/entity/policies/index.ts b/packages/catalog-model/src/entity/policies/index.ts
index eb7c0e8378..c381d29a23 100644
--- a/packages/catalog-model/src/entity/policies/index.ts
+++ b/packages/catalog-model/src/entity/policies/index.ts
@@ -17,5 +17,4 @@
export { DefaultNamespaceEntityPolicy } from './DefaultNamespaceEntityPolicy';
export { FieldFormatEntityPolicy } from './FieldFormatEntityPolicy';
export { NoForeignRootFieldsEntityPolicy } from './NoForeignRootFieldsEntityPolicy';
-export { ReservedFieldsEntityPolicy } from './ReservedFieldsEntityPolicy';
export { SchemaValidEntityPolicy } from './SchemaValidEntityPolicy';
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 084254d7ff..dd9e82c325 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -38,7 +38,9 @@
"@rollup/plugin-json": "^4.0.2",
"@rollup/plugin-node-resolve": "^8.1.0",
"@rollup/plugin-yaml": "^2.1.1",
- "@spotify/eslint-config": "^7.0.1",
+ "@spotify/eslint-config-base": "^8.0.0",
+ "@spotify/eslint-config-react": "^8.0.0",
+ "@spotify/eslint-config-typescript": "^8.0.0",
"@sucrase/webpack-loader": "^2.0.0",
"@svgr/plugin-jsx": "5.4.x",
"@svgr/plugin-svgo": "5.4.x",
@@ -47,6 +49,8 @@
"@types/start-server-webpack-plugin": "^2.2.0",
"@types/webpack-env": "^1.15.2",
"@types/webpack-node-externals": "^2.5.0",
+ "@typescript-eslint/eslint-plugin": "^v3.10.1",
+ "@typescript-eslint/parser": "^v3.10.1",
"bfj": "^7.0.2",
"chalk": "^4.0.0",
"chokidar": "^3.3.1",
@@ -56,9 +60,14 @@
"diff": "^4.0.2",
"esbuild": "^0.7.7",
"eslint": "^7.1.0",
+ "eslint-config-prettier": "^6.0.0",
"eslint-formatter-friendly": "^7.0.0",
"eslint-plugin-import": "^2.20.2",
+ "eslint-plugin-jest": "^24.1.0",
+ "eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-monorepo": "^0.2.1",
+ "eslint-plugin-react": "^7.12.4",
+ "eslint-plugin-react-hooks": "^4.0.0",
"fork-ts-checker-webpack-plugin": "^4.0.5",
"fs-extra": "^9.0.0",
"handlebars": "^4.7.3",
@@ -76,7 +85,7 @@
"recursive-readdir": "^2.2.2",
"replace-in-file": "^6.0.0",
"rollup": "2.23.x",
- "rollup-plugin-dts": "1.4.11",
+ "rollup-plugin-dts": "1.4.13",
"rollup-plugin-esbuild": "^2.0.0",
"rollup-plugin-peer-deps-external": "^2.2.2",
"rollup-plugin-postcss": "^3.1.1",
@@ -84,12 +93,12 @@
"rollup-pluginutils": "^2.8.2",
"start-server-webpack-plugin": "^2.2.5",
"style-loader": "^1.2.1",
- "sucrase": "^3.14.1",
+ "sucrase": "^3.16.0",
"tar": "^6.0.1",
"terser-webpack-plugin": "^1.4.3",
"ts-jest": "^26.0.0",
"ts-loader": "^7.0.4",
- "typescript": "^3.9.3",
+ "typescript": "^4.0.3",
"url-loader": "^4.1.0",
"webpack": "^4.41.6",
"webpack-dev-server": "^3.11.0",
diff --git a/packages/cli/src/commands/remove-plugin/removePlugin.ts b/packages/cli/src/commands/remove-plugin/removePlugin.ts
index 643bd599d7..81d8322ffa 100644
--- a/packages/cli/src/commands/remove-plugin/removePlugin.ts
+++ b/packages/cli/src/commands/remove-plugin/removePlugin.ts
@@ -62,7 +62,7 @@ export const removePluginDirectory = async (destination: string) => {
export const removeSymLink = async (destination: string) => {
await Task.forItem('removing', 'symbolic link', async () => {
- const symLinkExists = fse.pathExists(destination);
+ const symLinkExists = await fse.pathExists(destination);
if (symLinkExists) {
try {
await fse.remove(destination);
@@ -189,7 +189,7 @@ export default async () => {
return chalk.red('Please enter an ID for the plugin');
} else if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(value)) {
return chalk.red(
- 'Plugin IDs must be kehbab-cased and contain only letters, digits and dashes.',
+ 'Plugin IDs must be kebab-cased and contain only letters, digits and dashes.',
);
}
return true;
diff --git a/packages/cli/src/lib/parallel.test.ts b/packages/cli/src/lib/parallel.test.ts
index c8173603d9..8047774a6e 100644
--- a/packages/cli/src/lib/parallel.test.ts
+++ b/packages/cli/src/lib/parallel.test.ts
@@ -17,7 +17,7 @@
import { isParallelDefault, parseParallel } from './parallel';
describe('parallel', () => {
- describe(parseParallel, () => {
+ describe('parseParallel', () => {
it('coerces "false" string to boolean', () => {
expect(parseParallel('false')).toBeFalsy();
});
@@ -44,7 +44,7 @@ describe('parallel', () => {
});
});
- describe(isParallelDefault, () => {
+ describe('isParallelDefault', () => {
it('returns true if default value', () => {
expect(isParallelDefault(undefined)).toBeTruthy();
expect(isParallelDefault(true)).toBeTruthy();
diff --git a/packages/core-api/src/apis/definitions/AlertApi.ts b/packages/core-api/src/apis/definitions/AlertApi.ts
index 123cd47651..91641ac35b 100644
--- a/packages/core-api/src/apis/definitions/AlertApi.ts
+++ b/packages/core-api/src/apis/definitions/AlertApi.ts
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { createApiRef } from '../ApiRef';
+import { createApiRef, ApiRef } from '../system';
import { Observable } from '../../types';
export type AlertMessage = {
@@ -38,7 +38,7 @@ export type AlertApi = {
alert$(): Observable;
};
-export const alertApiRef = createApiRef({
+export const alertApiRef: ApiRef = createApiRef({
id: 'core.alert',
description: 'Used to report alerts and forward them to the app',
});
diff --git a/packages/core-api/src/apis/definitions/AppThemeApi.ts b/packages/core-api/src/apis/definitions/AppThemeApi.ts
index 3f580450f0..515e8df082 100644
--- a/packages/core-api/src/apis/definitions/AppThemeApi.ts
+++ b/packages/core-api/src/apis/definitions/AppThemeApi.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { createApiRef } from '../ApiRef';
+import { ApiRef, createApiRef } from '../system';
import { BackstageTheme } from '@backstage/theme';
import { Observable } from '../../types';
import { SvgIconProps } from '@material-ui/core';
@@ -77,7 +77,7 @@ export type AppThemeApi = {
setActiveThemeId(themeId?: string): void;
};
-export const appThemeApiRef = createApiRef({
+export const appThemeApiRef: ApiRef = createApiRef({
id: 'core.apptheme',
description: 'API Used to configure the app theme, and enumerate options',
});
diff --git a/packages/core-api/src/apis/definitions/ConfigApi.ts b/packages/core-api/src/apis/definitions/ConfigApi.ts
index 1fa6e70d1f..2ce972af0e 100644
--- a/packages/core-api/src/apis/definitions/ConfigApi.ts
+++ b/packages/core-api/src/apis/definitions/ConfigApi.ts
@@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { createApiRef } from '../ApiRef';
+import { ApiRef, createApiRef } from '../system';
import { Config } from '@backstage/config';
// Using interface to make the ConfigApi name show up in docs
export type ConfigApi = Config;
-export const configApiRef = createApiRef({
+export const configApiRef: ApiRef = createApiRef({
id: 'core.config',
description: 'Used to access runtime configuration',
});
diff --git a/packages/core-api/src/apis/definitions/DiscoveryApi.ts b/packages/core-api/src/apis/definitions/DiscoveryApi.ts
index b0773086c7..9777835ec8 100644
--- a/packages/core-api/src/apis/definitions/DiscoveryApi.ts
+++ b/packages/core-api/src/apis/definitions/DiscoveryApi.ts
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { createApiRef } from '../ApiRef';
+import { ApiRef, createApiRef } from '../system';
/**
* The discovery API is used to provide a mechanism for plugins to
@@ -41,7 +41,7 @@ export type DiscoveryApi = {
getBaseUrl(pluginId: string): Promise;
};
-export const discoveryApiRef = createApiRef({
+export const discoveryApiRef: ApiRef = createApiRef({
id: 'core.discovery',
description: 'Provides service discovery of backend plugins',
});
diff --git a/packages/core-api/src/apis/definitions/ErrorApi.ts b/packages/core-api/src/apis/definitions/ErrorApi.ts
index 7f9676ea5a..edd6966968 100644
--- a/packages/core-api/src/apis/definitions/ErrorApi.ts
+++ b/packages/core-api/src/apis/definitions/ErrorApi.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { createApiRef } from '../ApiRef';
+import { ApiRef, createApiRef } from '../system';
import { Observable } from '../../types';
/**
@@ -62,7 +62,7 @@ export type ErrorApi = {
error$(): Observable<{ error: Error; context?: ErrorContext }>;
};
-export const errorApiRef = createApiRef({
+export const errorApiRef: ApiRef = createApiRef({
id: 'core.error',
description: 'Used to report errors and forward them to the app',
});
diff --git a/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts b/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts
index dd9657d7d8..6e8c8cd8fc 100644
--- a/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts
+++ b/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { createApiRef } from '../ApiRef';
+import { ApiRef, createApiRef } from '../system';
import { UserFlags, FeatureFlagsRegistry } from '../../app/FeatureFlags';
import { FeatureFlagName } from '../../plugin';
@@ -57,7 +57,7 @@ export interface FeatureFlagsRegistryItem {
name: FeatureFlagName;
}
-export const featureFlagsApiRef = createApiRef({
+export const featureFlagsApiRef: ApiRef = createApiRef({
id: 'core.featureflags',
description: 'Used to toggle functionality in features across Backstage',
});
diff --git a/packages/core-api/src/apis/definitions/IdentityApi.ts b/packages/core-api/src/apis/definitions/IdentityApi.ts
index 2684422b1e..5cfc4723e0 100644
--- a/packages/core-api/src/apis/definitions/IdentityApi.ts
+++ b/packages/core-api/src/apis/definitions/IdentityApi.ts
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { createApiRef } from '../ApiRef';
+import { ApiRef, createApiRef } from '../system';
import { ProfileInfo } from './auth';
/**
@@ -51,7 +51,7 @@ export type IdentityApi = {
signOut(): Promise;
};
-export const identityApiRef = createApiRef({
+export const identityApiRef: ApiRef = createApiRef({
id: 'core.identity',
description: 'Provides access to the identity of the signed in user',
});
diff --git a/packages/core-api/src/apis/definitions/OAuthRequestApi.ts b/packages/core-api/src/apis/definitions/OAuthRequestApi.ts
index 6fefbd1b9c..7514a88dc1 100644
--- a/packages/core-api/src/apis/definitions/OAuthRequestApi.ts
+++ b/packages/core-api/src/apis/definitions/OAuthRequestApi.ts
@@ -16,7 +16,7 @@
import { IconComponent } from '../../icons';
import { Observable } from '../../types';
-import { createApiRef } from '../ApiRef';
+import { ApiRef, createApiRef } from '../system';
/**
* Information about the auth provider that we're requesting a login towards.
@@ -127,7 +127,7 @@ export type OAuthRequestApi = {
authRequest$(): Observable;
};
-export const oauthRequestApiRef = createApiRef({
+export const oauthRequestApiRef: ApiRef = createApiRef({
id: 'core.oauthrequest',
description: 'An API for implementing unified OAuth flows in Backstage',
});
diff --git a/packages/core-api/src/apis/definitions/StorageApi.ts b/packages/core-api/src/apis/definitions/StorageApi.ts
index 920ee56811..d0f71ea3a4 100644
--- a/packages/core-api/src/apis/definitions/StorageApi.ts
+++ b/packages/core-api/src/apis/definitions/StorageApi.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { createApiRef } from '../ApiRef';
+import { ApiRef, createApiRef } from '../system';
import { Observable } from '../../types';
import { ErrorApi } from './ErrorApi';
@@ -65,7 +65,7 @@ export interface StorageApi {
observe$(key: string): Observable>;
}
-export const storageApiRef = createApiRef({
+export const storageApiRef: ApiRef = createApiRef({
id: 'core.storage',
description: 'Provides the ability to store data which is unique to the user',
});
diff --git a/packages/core-api/src/apis/definitions/auth.ts b/packages/core-api/src/apis/definitions/auth.ts
index 109f15b26e..946baeabd9 100644
--- a/packages/core-api/src/apis/definitions/auth.ts
+++ b/packages/core-api/src/apis/definitions/auth.ts
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-import { createApiRef } from '../ApiRef';
-import { Observable } from '../..';
+import { ApiRef, createApiRef } from '../system';
+import { Observable } from '../../types';
/**
* This file contains declarations for common interfaces of auth-related APIs.
@@ -212,13 +212,13 @@ export type SessionApi = {
* Note that the ID token payload is only guaranteed to contain the user's numerical Google ID,
* email and expiration information. Do not rely on any other fields, as they might not be present.
*/
-export const googleAuthApiRef = createApiRef<
+export const googleAuthApiRef: ApiRef<
OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionApi
->({
+> = createApiRef({
id: 'core.auth.google',
description: 'Provides authentication towards Google APIs and identities',
});
@@ -229,9 +229,9 @@ export const googleAuthApiRef = createApiRef<
* See https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/
* for a full list of supported scopes.
*/
-export const githubAuthApiRef = createApiRef<
+export const githubAuthApiRef: ApiRef<
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
->({
+> = createApiRef({
id: 'core.auth.github',
description: 'Provides authentication towards GitHub APIs',
});
@@ -242,13 +242,13 @@ export const githubAuthApiRef = createApiRef<
* See https://developer.okta.com/docs/guides/implement-oauth-for-okta/scopes/
* for a full list of supported scopes.
*/
-export const oktaAuthApiRef = createApiRef<
+export const oktaAuthApiRef: ApiRef<
OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionApi
->({
+> = createApiRef({
id: 'core.auth.okta',
description: 'Provides authentication towards Okta APIs',
});
@@ -259,9 +259,9 @@ export const oktaAuthApiRef = createApiRef<
* See https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#limiting-scopes-of-a-personal-access-token
* for a full list of supported scopes.
*/
-export const gitlabAuthApiRef = createApiRef<
+export const gitlabAuthApiRef: ApiRef<
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
->({
+> = createApiRef({
id: 'core.auth.gitlab',
description: 'Provides authentication towards GitLab APIs',
});
@@ -272,9 +272,9 @@ export const gitlabAuthApiRef = createApiRef<
* See https://auth0.com/docs/scopes/current/oidc-scopes
* for a full list of supported scopes.
*/
-export const auth0AuthApiRef = createApiRef<
+export const auth0AuthApiRef: ApiRef<
OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
->({
+> = createApiRef({
id: 'core.auth.auth0',
description: 'Provides authentication towards Auth0 APIs',
});
@@ -286,13 +286,13 @@ export const auth0AuthApiRef = createApiRef<
* - https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent
* - https://docs.microsoft.com/en-us/graph/permissions-reference
*/
-export const microsoftAuthApiRef = createApiRef<
+export const microsoftAuthApiRef: ApiRef<
OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionApi
->({
+> = createApiRef({
id: 'core.auth.microsoft',
description: 'Provides authentication towards Microsoft APIs and identities',
});
@@ -300,13 +300,13 @@ export const microsoftAuthApiRef = createApiRef<
/**
* Provides authentication for custom identity providers.
*/
-export const oauth2ApiRef = createApiRef<
+export const oauth2ApiRef: ApiRef<
OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionApi
->({
+> = createApiRef({
id: 'core.auth.oauth2',
description: 'Example of how to use oauth2 custom provider',
});
@@ -314,9 +314,9 @@ export const oauth2ApiRef = createApiRef<
/**
* Provides authentication for saml based identity providers
*/
-export const samlAuthApiRef = createApiRef<
+export const samlAuthApiRef: ApiRef<
ProfileInfoApi & BackstageIdentityApi & SessionApi
->({
+> = createApiRef({
id: 'core.auth.saml',
description: 'Example of how to use SAML custom provider',
});
diff --git a/packages/core-api/src/apis/index.ts b/packages/core-api/src/apis/index.ts
index c856a13668..03569f9570 100644
--- a/packages/core-api/src/apis/index.ts
+++ b/packages/core-api/src/apis/index.ts
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-export { ApiProvider, useApi, useApiHolder } from './ApiProvider';
-export { ApiRegistry } from './ApiRegistry';
-export * from './ApiRef';
-export * from './types';
-export * from './helpers';
+export * from './system';
export * from './definitions';
export * from './implementations';
diff --git a/packages/core-api/src/apis/ApiAggregator.test.ts b/packages/core-api/src/apis/system/ApiAggregator.test.ts
similarity index 100%
rename from packages/core-api/src/apis/ApiAggregator.test.ts
rename to packages/core-api/src/apis/system/ApiAggregator.test.ts
diff --git a/packages/core-api/src/apis/ApiAggregator.ts b/packages/core-api/src/apis/system/ApiAggregator.ts
similarity index 93%
rename from packages/core-api/src/apis/ApiAggregator.ts
rename to packages/core-api/src/apis/system/ApiAggregator.ts
index da49ee6488..1587a1d10b 100644
--- a/packages/core-api/src/apis/ApiAggregator.ts
+++ b/packages/core-api/src/apis/system/ApiAggregator.ts
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-import { ApiRef } from './ApiRef';
-import { ApiHolder } from './types';
+import { ApiRef, ApiHolder } from './types';
/**
* An ApiHolder that queries multiple other holders from for
diff --git a/packages/core-api/src/apis/ApiFactoryRegistry.test.ts b/packages/core-api/src/apis/system/ApiFactoryRegistry.test.ts
similarity index 100%
rename from packages/core-api/src/apis/ApiFactoryRegistry.test.ts
rename to packages/core-api/src/apis/system/ApiFactoryRegistry.test.ts
diff --git a/packages/core-api/src/apis/ApiFactoryRegistry.ts b/packages/core-api/src/apis/system/ApiFactoryRegistry.ts
similarity index 98%
rename from packages/core-api/src/apis/ApiFactoryRegistry.ts
rename to packages/core-api/src/apis/system/ApiFactoryRegistry.ts
index 0d36e6c550..556f63589c 100644
--- a/packages/core-api/src/apis/ApiFactoryRegistry.ts
+++ b/packages/core-api/src/apis/system/ApiFactoryRegistry.ts
@@ -15,12 +15,12 @@
*/
import {
+ ApiRef,
ApiFactoryHolder,
ApiFactory,
AnyApiRef,
AnyApiFactory,
} from './types';
-import { ApiRef } from './ApiRef';
type ApiFactoryScope =
| 'default' // Default factories registered by core and plugins
diff --git a/packages/core-api/src/apis/ApiProvider.test.tsx b/packages/core-api/src/apis/system/ApiProvider.test.tsx
similarity index 100%
rename from packages/core-api/src/apis/ApiProvider.test.tsx
rename to packages/core-api/src/apis/system/ApiProvider.test.tsx
diff --git a/packages/core-api/src/apis/ApiProvider.tsx b/packages/core-api/src/apis/system/ApiProvider.tsx
similarity index 96%
rename from packages/core-api/src/apis/ApiProvider.tsx
rename to packages/core-api/src/apis/system/ApiProvider.tsx
index 24610a1372..f2aa70244e 100644
--- a/packages/core-api/src/apis/ApiProvider.tsx
+++ b/packages/core-api/src/apis/system/ApiProvider.tsx
@@ -16,8 +16,7 @@
import React, { FC, createContext, useContext, ReactNode } from 'react';
import PropTypes from 'prop-types';
-import { ApiRef } from './ApiRef';
-import { ApiHolder, TypesToApiRefs } from './types';
+import { ApiRef, ApiHolder, TypesToApiRefs } from './types';
import { ApiAggregator } from './ApiAggregator';
type ApiProviderProps = {
diff --git a/packages/core-api/src/apis/ApiRef.test.ts b/packages/core-api/src/apis/system/ApiRef.test.ts
similarity index 100%
rename from packages/core-api/src/apis/ApiRef.test.ts
rename to packages/core-api/src/apis/system/ApiRef.test.ts
diff --git a/packages/core-api/src/apis/ApiRef.ts b/packages/core-api/src/apis/system/ApiRef.ts
similarity index 95%
rename from packages/core-api/src/apis/ApiRef.ts
rename to packages/core-api/src/apis/system/ApiRef.ts
index 793c1e5386..0e1eb177c8 100644
--- a/packages/core-api/src/apis/ApiRef.ts
+++ b/packages/core-api/src/apis/system/ApiRef.ts
@@ -14,17 +14,13 @@
* limitations under the License.
*/
+import type { ApiRef } from './types';
+
export type ApiRefConfig = {
id: string;
description: string;
};
-export type ApiRef = {
- id: string;
- description: string;
- T: T;
-};
-
class ApiRefImpl implements ApiRef {
constructor(private readonly config: ApiRefConfig) {
const valid = config.id
diff --git a/packages/core-api/src/apis/ApiRegistry.test.ts b/packages/core-api/src/apis/system/ApiRegistry.test.ts
similarity index 100%
rename from packages/core-api/src/apis/ApiRegistry.test.ts
rename to packages/core-api/src/apis/system/ApiRegistry.test.ts
diff --git a/packages/core-api/src/apis/ApiRegistry.ts b/packages/core-api/src/apis/system/ApiRegistry.ts
similarity index 96%
rename from packages/core-api/src/apis/ApiRegistry.ts
rename to packages/core-api/src/apis/system/ApiRegistry.ts
index 64d97ba234..14f58268b4 100644
--- a/packages/core-api/src/apis/ApiRegistry.ts
+++ b/packages/core-api/src/apis/system/ApiRegistry.ts
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-import { ApiRef } from './ApiRef';
-import { ApiHolder } from './types';
+import { ApiRef, ApiHolder } from './types';
type ApiImpl = readonly [ApiRef, T];
diff --git a/packages/core-api/src/apis/ApiResolver.test.ts b/packages/core-api/src/apis/system/ApiResolver.test.ts
similarity index 100%
rename from packages/core-api/src/apis/ApiResolver.test.ts
rename to packages/core-api/src/apis/system/ApiResolver.test.ts
diff --git a/packages/core-api/src/apis/ApiResolver.ts b/packages/core-api/src/apis/system/ApiResolver.ts
similarity index 98%
rename from packages/core-api/src/apis/ApiResolver.ts
rename to packages/core-api/src/apis/system/ApiResolver.ts
index cb65186a37..0dfeaeedd0 100644
--- a/packages/core-api/src/apis/ApiResolver.ts
+++ b/packages/core-api/src/apis/system/ApiResolver.ts
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-import { ApiRef } from './ApiRef';
import {
+ ApiRef,
ApiHolder,
ApiFactoryHolder,
AnyApiRef,
diff --git a/packages/core-api/src/apis/helpers.ts b/packages/core-api/src/apis/system/helpers.ts
similarity index 93%
rename from packages/core-api/src/apis/helpers.ts
rename to packages/core-api/src/apis/system/helpers.ts
index 7d616acd01..0ccd0cdb80 100644
--- a/packages/core-api/src/apis/helpers.ts
+++ b/packages/core-api/src/apis/system/helpers.ts
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-import { ApiFactory, TypesToApiRefs } from './types';
-import { ApiRef } from './ApiRef';
+import { ApiRef, ApiFactory, TypesToApiRefs } from './types';
/**
* Used to infer types for a standalone ApiFactory that isn't immediately passed
diff --git a/plugins/catalog-backend/src/ingestion/processors/EntityPolicyProcessor.ts b/packages/core-api/src/apis/system/index.ts
similarity index 53%
rename from plugins/catalog-backend/src/ingestion/processors/EntityPolicyProcessor.ts
rename to packages/core-api/src/apis/system/index.ts
index 87543d91f4..10b2e0f084 100644
--- a/plugins/catalog-backend/src/ingestion/processors/EntityPolicyProcessor.ts
+++ b/packages/core-api/src/apis/system/index.ts
@@ -14,21 +14,10 @@
* limitations under the License.
*/
-import { Entity, EntityPolicy } from '@backstage/catalog-model';
-import { CatalogProcessor } from './types';
-
-export class EntityPolicyProcessor implements CatalogProcessor {
- private readonly policy: EntityPolicy;
-
- constructor(policy: EntityPolicy) {
- this.policy = policy;
- }
-
- async processEntity(entity: Entity): Promise {
- const output = await this.policy.enforce(entity);
- if (!output) {
- throw new Error(`Entity did not match any known schema`);
- }
- return output;
- }
-}
+export { ApiProvider, useApi, useApiHolder } from './ApiProvider';
+export { ApiRegistry } from './ApiRegistry';
+export { ApiResolver } from './ApiResolver';
+export { ApiFactoryRegistry } from './ApiFactoryRegistry';
+export { createApiRef } from './ApiRef';
+export * from './types';
+export * from './helpers';
diff --git a/packages/core-api/src/apis/types.ts b/packages/core-api/src/apis/system/types.ts
similarity index 94%
rename from packages/core-api/src/apis/types.ts
rename to packages/core-api/src/apis/system/types.ts
index 61c229b18e..b0c19e551c 100644
--- a/packages/core-api/src/apis/types.ts
+++ b/packages/core-api/src/apis/system/types.ts
@@ -14,7 +14,11 @@
* limitations under the License.
*/
-import { ApiRef } from './ApiRef';
+export type ApiRef = {
+ id: string;
+ description: string;
+ T: T;
+};
export type AnyApiRef = ApiRef;
diff --git a/packages/core-api/src/app/App.tsx b/packages/core-api/src/app/App.tsx
index c222c3c420..0588cd819c 100644
--- a/packages/core-api/src/app/App.tsx
+++ b/packages/core-api/src/app/App.tsx
@@ -54,8 +54,7 @@ import {
} from '../apis';
import { useAsync } from 'react-use';
import { AppIdentity } from './AppIdentity';
-import { ApiFactoryRegistry } from '../apis/ApiFactoryRegistry';
-import { ApiResolver } from '../apis/ApiResolver';
+import { ApiResolver, ApiFactoryRegistry } from '../apis/system';
type FullAppOptions = {
apis: Iterable;
diff --git a/packages/core-api/src/plugin/types.ts b/packages/core-api/src/plugin/types.ts
index 12992f3620..dacb3668bc 100644
--- a/packages/core-api/src/plugin/types.ts
+++ b/packages/core-api/src/plugin/types.ts
@@ -16,7 +16,7 @@
import { ComponentType } from 'react';
import { RouteRef } from '../routing';
-import { AnyApiFactory } from '../apis';
+import { AnyApiFactory } from '../apis/system';
export type RouteOptions = {
// Whether the route path must match exactly, defaults to true.
diff --git a/packages/core/src/components/Button/Button.test.jsx b/packages/core/src/components/Button/Button.test.tsx
similarity index 94%
rename from packages/core/src/components/Button/Button.test.jsx
rename to packages/core/src/components/Button/Button.test.tsx
index 115835be3f..8bae5f2767 100644
--- a/packages/core/src/components/Button/Button.test.jsx
+++ b/packages/core/src/components/Button/Button.test.tsx
@@ -34,7 +34,9 @@ describe(' ', () => {
);
expect(() => getByText(testString)).toThrow();
- await act(async () => fireEvent.click(getByText(buttonLabel)));
+ await act(async () => {
+ fireEvent.click(getByText(buttonLabel));
+ });
expect(getByText(testString)).toBeInTheDocument();
});
});
diff --git a/packages/core/src/components/CodeSnippet/CodeSnippet.test.tsx b/packages/core/src/components/CodeSnippet/CodeSnippet.test.tsx
index 1a76f84f44..466abe6012 100644
--- a/packages/core/src/components/CodeSnippet/CodeSnippet.test.tsx
+++ b/packages/core/src/components/CodeSnippet/CodeSnippet.test.tsx
@@ -20,10 +20,10 @@ import { wrapInTestApp } from '@backstage/test-utils';
import { CodeSnippet } from './CodeSnippet';
-const JAVASCRIPT = `const greeting = "Hello";
-const world = "World";
-
-const greet = person => gretting + " " + person + "!";
+const JAVASCRIPT = `
+ const greeting = "Hello";
+ const world = "World";
+ const greet = person => gretting + " " + person + "!";
`;
const minProps = {
@@ -48,20 +48,20 @@ describe(' ', () => {
});
it('renders with line numbers', () => {
- const { queryByText } = render(
+ const { getByText } = render(
wrapInTestApp( ),
);
- expect(queryByText(/1/)).toBeInTheDocument();
- expect(queryByText(/2/)).toBeInTheDocument();
- expect(queryByText(/3/)).toBeInTheDocument();
+ expect(getByText('1')).toBeInTheDocument();
+ expect(getByText('2')).toBeInTheDocument();
+ expect(getByText('3')).toBeInTheDocument();
});
it('copy code using button', async () => {
document.execCommand = jest.fn();
- const rendered = render(
+ const { getByTitle } = render(
wrapInTestApp( ),
);
- const button = rendered.getByTitle('Text copied to clipboard');
+ const button = getByTitle('Text copied to clipboard');
fireEvent.click(button);
expect(document.execCommand).toHaveBeenCalled();
});
diff --git a/packages/core/src/components/HorizontalScrollGrid/HorizontalScrollGrid.test.jsx b/packages/core/src/components/HorizontalScrollGrid/HorizontalScrollGrid.test.tsx
similarity index 76%
rename from packages/core/src/components/HorizontalScrollGrid/HorizontalScrollGrid.test.jsx
rename to packages/core/src/components/HorizontalScrollGrid/HorizontalScrollGrid.test.tsx
index 83cec85fc5..1dbdbcc3b9 100644
--- a/packages/core/src/components/HorizontalScrollGrid/HorizontalScrollGrid.test.jsx
+++ b/packages/core/src/components/HorizontalScrollGrid/HorizontalScrollGrid.test.tsx
@@ -23,9 +23,10 @@ import { Grid } from '@material-ui/core';
describe(' ', () => {
beforeEach(() => {
jest.spyOn(window.performance, 'now').mockReturnValue(5);
- jest
- .spyOn(window, 'requestAnimationFrame')
- .mockImplementation(cb => cb(20));
+ jest.spyOn(window, 'requestAnimationFrame').mockImplementation(cb => {
+ cb(20);
+ return 1;
+ });
});
afterEach(() => {
@@ -48,29 +49,25 @@ describe(' ', () => {
});
it('should show scroll buttons', async () => {
- Object.defineProperties(HTMLElement.prototype, {
- scrollLeft: {
- configurable: true,
- value: 5,
- },
- offsetWidth: {
- configurable: true,
- value: 10,
- },
- scrollWidth: {
- configurable: true,
- value: 20,
- },
- });
+ jest
+ .spyOn(HTMLElement.prototype, 'scrollLeft', 'get')
+ .mockImplementation(() => 5);
+ jest
+ .spyOn(HTMLElement.prototype, 'offsetWidth', 'get')
+ .mockImplementation(() => 10);
+ jest
+ .spyOn(HTMLElement.prototype, 'scrollWidth', 'get')
+ .mockImplementation(() => 20);
let lastScroll = 0;
- HTMLElement.prototype.scrollBy = ({ left }) => {
- lastScroll = left;
- };
+ const scrollBy = HTMLElement.prototype.scrollBy;
+ HTMLElement.prototype.scrollBy = (({ left }: ScrollToOptions): void => {
+ lastScroll = left || 0;
+ }) as any;
const rendered = await renderWithEffects(
wrapInTestApp(
-
+
item1
@@ -89,9 +86,6 @@ describe(' ', () => {
fireEvent.click(rendered.getByTitle('Scroll Left'));
expect(lastScroll).toBeLessThan(0);
- delete HTMLElement.prototype.scrollLeft;
- delete HTMLElement.prototype.offsetWidth;
- delete HTMLElement.prototype.scrollWidth;
- delete HTMLElement.prototype.scrollBy;
+ HTMLElement.prototype.scrollBy = scrollBy;
});
});
diff --git a/packages/core/src/components/Lifecycle/Lifecycle.test.jsx b/packages/core/src/components/Lifecycle/Lifecycle.test.tsx
similarity index 100%
rename from packages/core/src/components/Lifecycle/Lifecycle.test.jsx
rename to packages/core/src/components/Lifecycle/Lifecycle.test.tsx
diff --git a/packages/core/src/components/Link/Link.test.jsx b/packages/core/src/components/Link/Link.test.tsx
similarity index 94%
rename from packages/core/src/components/Link/Link.test.jsx
rename to packages/core/src/components/Link/Link.test.tsx
index 4bb78ef6d4..23d2728bf6 100644
--- a/packages/core/src/components/Link/Link.test.jsx
+++ b/packages/core/src/components/Link/Link.test.tsx
@@ -34,7 +34,9 @@ describe(' ', () => {
),
);
expect(() => getByText(testString)).toThrow();
- await act(async () => fireEvent.click(getByText(linkText)));
+ await act(async () => {
+ fireEvent.click(getByText(linkText));
+ });
expect(getByText(testString)).toBeInTheDocument();
});
});
diff --git a/packages/core/src/components/Progress/Progress.test.tsx b/packages/core/src/components/Progress/Progress.test.tsx
new file mode 100644
index 0000000000..46a162e8b3
--- /dev/null
+++ b/packages/core/src/components/Progress/Progress.test.tsx
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+import { renderInTestApp } from '@backstage/test-utils';
+import { act } from 'react-dom/test-utils';
+
+import { Progress } from './Progress';
+
+describe(' ', () => {
+ it('renders without exploding', async () => {
+ jest.useFakeTimers();
+ const { getByTestId, queryByTestId } = await renderInTestApp( );
+ expect(queryByTestId('progress')).not.toBeInTheDocument();
+ act(() => {
+ jest.advanceTimersByTime(250);
+ });
+ expect(getByTestId('progress')).toBeInTheDocument();
+ jest.useRealTimers();
+ });
+});
diff --git a/packages/core/src/components/Progress/Progress.tsx b/packages/core/src/components/Progress/Progress.tsx
index e66220e4e7..80f4f38cc9 100644
--- a/packages/core/src/components/Progress/Progress.tsx
+++ b/packages/core/src/components/Progress/Progress.tsx
@@ -28,6 +28,6 @@ export const Progress: FC = props => {
return isVisible ? (
) : (
-
+
);
};
diff --git a/packages/core/src/components/ProgressBars/Gauge.test.jsx b/packages/core/src/components/ProgressBars/Gauge.test.tsx
similarity index 86%
rename from packages/core/src/components/ProgressBars/Gauge.test.jsx
rename to packages/core/src/components/ProgressBars/Gauge.test.tsx
index 709de7b0c4..3d2ac17506 100644
--- a/packages/core/src/components/ProgressBars/Gauge.test.jsx
+++ b/packages/core/src/components/ProgressBars/Gauge.test.tsx
@@ -18,6 +18,7 @@ import React from 'react';
import { render } from '@testing-library/react';
import { wrapInTestApp } from '@backstage/test-utils';
import { Gauge, getProgressColor } from './Gauge';
+import * as theme from '@backstage/theme';
describe(' ', () => {
it('renders without exploding', () => {
@@ -26,6 +27,7 @@ describe(' ', () => {
);
getByText('10%');
});
+
it('handles fractional prop', () => {
const { getByText } = render(
wrapInTestApp( ),
@@ -50,17 +52,20 @@ describe(' ', () => {
const ok = '#111';
const warning = '#222';
const error = '#333';
- const palette = { status: { ok, warning, error } };
+ const palette = {
+ ...theme.lightTheme.palette,
+ status: { ...theme.lightTheme.palette.status, ok, warning, error },
+ };
it('colors the progress correctly', () => {
- expect(getProgressColor(palette)).toBe('#ddd');
+ expect(getProgressColor(palette, 'Not a Number' as any)).toBe('#ddd');
expect(getProgressColor(palette, 10)).toBe(error);
expect(getProgressColor(palette, 50)).toBe(warning);
expect(getProgressColor(palette, 90)).toBe(ok);
});
it('colors the inverse progress correctly', () => {
- expect(getProgressColor(palette)).toBe('#ddd');
+ expect(getProgressColor(palette, 'Not a Number' as any)).toBe('#ddd');
expect(getProgressColor(palette, 10, true)).toBe(ok);
expect(getProgressColor(palette, 50, true)).toBe(warning);
expect(getProgressColor(palette, 90, true)).toBe(error);
diff --git a/packages/core/src/components/ProgressBars/GaugeCard.test.jsx b/packages/core/src/components/ProgressBars/GaugeCard.test.tsx
similarity index 99%
rename from packages/core/src/components/ProgressBars/GaugeCard.test.jsx
rename to packages/core/src/components/ProgressBars/GaugeCard.test.tsx
index 27ef0bb188..496a620170 100644
--- a/packages/core/src/components/ProgressBars/GaugeCard.test.jsx
+++ b/packages/core/src/components/ProgressBars/GaugeCard.test.tsx
@@ -40,7 +40,7 @@ describe(' ', () => {
});
it('handles invalid numbers', () => {
- const badProps = { title: 'Tingle upgrade', progress: 'hejjo' };
+ const badProps = { title: 'Tingle upgrade', progress: 'hejjo' } as any;
const { getByText } = render(wrapInTestApp( ));
expect(getByText(/N\/A.*/)).toBeInTheDocument();
});
diff --git a/packages/core/src/components/Status/Status.test.jsx b/packages/core/src/components/Status/Status.test.tsx
similarity index 100%
rename from packages/core/src/components/Status/Status.test.jsx
rename to packages/core/src/components/Status/Status.test.tsx
diff --git a/packages/core/src/components/StructuredMetadataTable/StructuredMetadataTable.test.jsx b/packages/core/src/components/StructuredMetadataTable/StructuredMetadataTable.test.tsx
similarity index 69%
rename from packages/core/src/components/StructuredMetadataTable/StructuredMetadataTable.test.jsx
rename to packages/core/src/components/StructuredMetadataTable/StructuredMetadataTable.test.tsx
index 1ae810ca17..68d4262c3e 100644
--- a/packages/core/src/components/StructuredMetadataTable/StructuredMetadataTable.test.jsx
+++ b/packages/core/src/components/StructuredMetadataTable/StructuredMetadataTable.test.tsx
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-import React from 'react';
import { render } from '@testing-library/react';
-
-import { StructuredMetadataTable } from './StructuredMetadataTable';
import { startCase } from 'lodash';
+import React from 'react';
+import { StructuredMetadataTable } from './StructuredMetadataTable';
describe(' ', () => {
it('renders without exploding', () => {
@@ -31,32 +30,34 @@ describe(' ', () => {
describe('Item Mappings', () => {
it('Iterates over and displays every field in the map', () => {
- const metadata = { field1: 'one', field2: 'two', field3: 'three' };
+ const metadata = {
+ field1: 'one',
+ field2: 'two',
+ field3: 'three',
+ } as const;
const { getByText } = render(
,
);
- const keys = Object.keys(metadata);
- keys.forEach(value => {
- expect(getByText(startCase(value))).toBeInTheDocument();
- expect(getByText(metadata[value])).toBeInTheDocument();
- });
+ for (const [key, value] of Object.entries(metadata)) {
+ expect(getByText(startCase(key))).toBeInTheDocument();
+ expect(getByText(value)).toBeInTheDocument();
+ }
});
- it('Supports primative value fields', () => {
- const metadata = { strField: 'my field', intField: 1 };
+ it('Supports primitive value fields', () => {
+ const metadata = { strField: 'my field', intField: 1 } as const;
const { getByText } = render(
,
);
- const keys = Object.keys(metadata);
- keys.forEach(value => {
- expect(getByText(startCase(value))).toBeInTheDocument();
- expect(getByText(metadata[value].toString())).toBeInTheDocument();
- });
+ for (const [key, value] of Object.entries(metadata)) {
+ expect(getByText(startCase(key))).toBeInTheDocument();
+ expect(getByText(value.toString())).toBeInTheDocument();
+ }
});
it('Supports array fields', () => {
- const metadata = { arrayField: ['arrVal1', 'arrVal2'] };
+ const metadata = { arrayField: ['arrVal1', 'arrVal2'] } as const;
const { getByText } = render(
,
);
@@ -79,20 +80,19 @@ describe(' ', () => {
});
it('Supports object elements', () => {
- const metadata = { config: { a: 1, b: 2 } };
+ const metadata = {
+ config: { a: 1, b: 2 },
+ } as const;
const { getByText } = render(
,
);
- const keys = Object.keys(metadata.config);
- keys.forEach(value => {
+ for (const [key, value] of Object.entries(metadata.config)) {
+ expect(getByText(startCase(key), { exact: false })).toBeInTheDocument();
expect(
- getByText(startCase(value), { exact: false }),
+ getByText(value.toString(), { exact: false }),
).toBeInTheDocument();
- expect(
- getByText(metadata.config[value].toString(), { exact: false }),
- ).toBeInTheDocument();
- });
+ }
});
});
});
diff --git a/packages/core/src/layout/ErrorPage/MicDrop.jsx b/packages/core/src/layout/ErrorPage/MicDrop.tsx
similarity index 100%
rename from packages/core/src/layout/ErrorPage/MicDrop.jsx
rename to packages/core/src/layout/ErrorPage/MicDrop.tsx
diff --git a/packages/docgen/package.json b/packages/docgen/package.json
index 61efc0bbd4..7c0bf846bd 100644
--- a/packages/docgen/package.json
+++ b/packages/docgen/package.json
@@ -31,7 +31,7 @@
"fs-extra": "^9.0.0",
"github-slugger": "^1.3.0",
"ts-node": "^8.6.2",
- "typescript": "^3.9.3"
+ "typescript": "^4.0.3"
},
"devDependencies": {
"@types/fs-extra": "^9.0.1",
diff --git a/packages/docgen/src/index.ts b/packages/docgen/src/index.ts
index f41629e7fd..4b69fdbe01 100644
--- a/packages/docgen/src/index.ts
+++ b/packages/docgen/src/index.ts
@@ -49,10 +49,6 @@ const main = (argv: string[]) => {
process.exit(1);
});
- if (!process.argv.slice(2).length) {
- program.outputHelp(chalk.yellow);
- }
-
program.parse(argv);
};
diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json
index adce3dd101..30ca6e4538 100644
--- a/packages/techdocs-cli/package.json
+++ b/packages/techdocs-cli/package.json
@@ -28,11 +28,7 @@
"techdocs-cli": "bin/techdocs-cli"
},
"devDependencies": {
- "@spotify/eslint-config": "^7.0.0",
- "@spotify/prettier-config": "^7.0.0",
- "@types/serve-handler": "^6.1.0",
- "eslint": "^7.1.0",
- "eslint-plugin-import": "^2.22.0"
+ "@types/serve-handler": "^6.1.0"
},
"files": [
"bin",
diff --git a/plugins/api-docs/src/components/useComponentApiEntities.ts b/plugins/api-docs/src/components/useComponentApiEntities.ts
index 11b5de988f..9e5cbd968e 100644
--- a/plugins/api-docs/src/components/useComponentApiEntities.ts
+++ b/plugins/api-docs/src/components/useComponentApiEntities.ts
@@ -16,7 +16,11 @@
import { useAsyncRetry } from 'react-use';
import { errorApiRef, useApi } from '@backstage/core';
-import { ApiEntity, ComponentEntity } from '@backstage/catalog-model';
+import {
+ ApiEntity,
+ ComponentEntity,
+ parseEntityName,
+} from '@backstage/catalog-model';
import { catalogApiRef } from '@backstage/plugin-catalog';
import { useComponentApiNames } from './useComponentApiNames';
@@ -43,10 +47,20 @@ export function useComponentApiEntities({
await Promise.all(
apiNames.map(async name => {
try {
- const api = (await catalogApi.getEntityByName({
- kind: 'API',
- name,
- })) as ApiEntity | undefined;
+ const apiEntityName = parseEntityName(name, {
+ defaultNamespace: entity.metadata.namespace,
+ defaultKind: 'API',
+ });
+
+ if (apiEntityName.kind !== 'API') {
+ throw new Error(
+ `Referenced entity of kind "${apiEntityName.kind}" as an API`,
+ );
+ }
+
+ const api = (await catalogApi.getEntityByName(apiEntityName)) as
+ | ApiEntity
+ | undefined;
if (api) {
resultMap.set(api.metadata.name, api);
diff --git a/plugins/catalog-backend/src/ingestion/LocationReaders.ts b/plugins/catalog-backend/src/ingestion/LocationReaders.ts
index c36b3d2b05..e23a4524b6 100644
--- a/plugins/catalog-backend/src/ingestion/LocationReaders.ts
+++ b/plugins/catalog-backend/src/ingestion/LocationReaders.ts
@@ -17,6 +17,7 @@
import { UrlReader } from '@backstage/backend-common';
import {
Entity,
+ EntityPolicy,
ENTITY_DEFAULT_NAMESPACE,
LocationSpec,
} from '@backstage/catalog-model';
@@ -44,6 +45,7 @@ type Options = {
config: Config;
processors: CatalogProcessor[];
rulesEnforcer: CatalogRulesEnforcer;
+ policy: EntityPolicy;
};
/**
@@ -74,10 +76,12 @@ export class LocationReaders implements LocationReader {
} else if (item.type === 'entity') {
if (rulesEnforcer.isAllowed(item.entity, item.location)) {
const entity = await this.handleEntity(item, emit);
- output.entities.push({
- entity,
- location: item.location,
- });
+ if (entity) {
+ output.entities.push({
+ entity,
+ location: item.location,
+ });
+ }
} else {
output.errors.push({
location: item.location,
@@ -162,25 +166,65 @@ export class LocationReaders implements LocationReader {
private async handleEntity(
item: CatalogProcessorEntityResult,
emit: CatalogProcessorEmit,
- ): Promise {
+ ): Promise {
const { processors, logger } = this.options;
let current = item.entity;
+ // Construct the name carefully, this happens before validation below
+ // so we do not want to crash here due to missing metadata or so
+ const kind = current.kind || '';
+ const namespace = !current.metadata
+ ? ''
+ : current.metadata.namespace ?? ENTITY_DEFAULT_NAMESPACE;
+ const name = !current.metadata ? '' : current.metadata.name;
+
for (const processor of processors) {
- if (processor.processEntity) {
+ if (processor.preProcessEntity) {
try {
- current = await processor.processEntity(current, item.location, emit);
+ current = await processor.preProcessEntity(
+ current,
+ item.location,
+ emit,
+ );
} catch (e) {
- // Construct the name carefully, if we got validation errors we do
- // not want to crash here due to missing metadata or so
- const namespace = !current.metadata
- ? ''
- : current.metadata.namespace ?? ENTITY_DEFAULT_NAMESPACE;
- const name = !current.metadata ? '' : current.metadata.name;
- const message = `Processor ${processor.constructor.name} threw an error while processing entity ${current.kind}:${namespace}/${name} at ${item.location.type} ${item.location.target}, ${e}`;
+ const message = `Processor ${processor.constructor.name} threw an error while preprocessing entity ${kind}:${namespace}/${name} at ${item.location.type} ${item.location.target}, ${e}`;
emit(result.generalError(item.location, message));
logger.warn(message);
+ return undefined;
+ }
+ }
+ }
+
+ try {
+ const next = await this.options.policy.enforce(current);
+ if (!next) {
+ const message = `Policy unexpectedly returned no data while analyzing entity ${kind}:${namespace}/${name} at ${item.location.type} ${item.location.target}`;
+ emit(result.generalError(item.location, message));
+ logger.warn(message);
+ return undefined;
+ }
+ current = next;
+ } catch (e) {
+ const message = `Policy check failed while analyzing entity ${kind}:${namespace}/${name} at ${item.location.type} ${item.location.target}, ${e}`;
+ emit(result.inputError(item.location, message));
+ logger.warn(message);
+ return undefined;
+ }
+
+ for (const processor of processors) {
+ if (processor.postProcessEntity) {
+ try {
+ current = await processor.postProcessEntity(
+ current,
+ item.location,
+ emit,
+ );
+ } catch (e) {
+ const message = `Processor ${processor.constructor.name} threw an error while postprocessing entity ${kind}:${namespace}/${name} at ${item.location.type} ${item.location.target}, ${e}`;
+ emit(result.generalError(item.location, message));
+ logger.warn(message);
+ return undefined;
}
}
}
diff --git a/plugins/catalog-backend/src/ingestion/processors/AnnotateLocationEntityProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/AnnotateLocationEntityProcessor.ts
index fea70b6c5e..ea8afcddc5 100644
--- a/plugins/catalog-backend/src/ingestion/processors/AnnotateLocationEntityProcessor.ts
+++ b/plugins/catalog-backend/src/ingestion/processors/AnnotateLocationEntityProcessor.ts
@@ -19,7 +19,10 @@ import lodash from 'lodash';
import { CatalogProcessor } from './types';
export class AnnotateLocationEntityProcessor implements CatalogProcessor {
- async processEntity(entity: Entity, location: LocationSpec): Promise {
+ async preProcessEntity(
+ entity: Entity,
+ location: LocationSpec,
+ ): Promise {
return lodash.merge(
{
metadata: {
diff --git a/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts
index c1c83b7c6c..622af9ea59 100644
--- a/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts
+++ b/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts
@@ -27,7 +27,7 @@ import {
resolveCodeOwner,
} from './CodeOwnersProcessor';
-describe(CodeOwnersProcessor, () => {
+describe('CodeOwnersProcessor', () => {
const mockUrl = ({ basePath = '' } = {}): string =>
`https://github.com/spotify/backstage/blob/master/${basePath}catalog-info.yaml`;
const mockLocation = ({
@@ -79,7 +79,7 @@ describe(CodeOwnersProcessor, () => {
return data;
};
- describe(buildUrl, () => {
+ describe('buildUrl', () => {
it.each([['azure.com'], ['dev.azure.com']])(
'should throw not implemented error',
source => {
@@ -99,7 +99,7 @@ describe(CodeOwnersProcessor, () => {
});
});
- describe(buildCodeOwnerUrl, () => {
+ describe('buildCodeOwnerUrl', () => {
it('should build a location spec to the codeowners', () => {
expect(buildCodeOwnerUrl(mockUrl(), '/docs/CODEOWNERS')).toEqual(
'https://github.com/spotify/backstage/blob/master/docs/CODEOWNERS',
@@ -116,13 +116,13 @@ describe(CodeOwnersProcessor, () => {
});
});
- describe(parseCodeOwners, () => {
+ describe('parseCodeOwners', () => {
it('should parse the codeowners file', () => {
expect(parseCodeOwners(mockCodeOwnersText())).toEqual(mockCodeOwners());
});
});
- describe(normalizeCodeOwner, () => {
+ describe('normalizeCodeOwner', () => {
it('should remove org from org/team format', () => {
expect(normalizeCodeOwner('@acme/foo')).toBe('foo');
});
@@ -139,13 +139,13 @@ describe(CodeOwnersProcessor, () => {
);
});
- describe(findPrimaryCodeOwner, () => {
+ describe('findPrimaryCodeOwner', () => {
it('should return the primary owner', () => {
expect(findPrimaryCodeOwner(mockCodeOwners())).toBe('backstage-core');
});
});
- describe(findRawCodeOwners, () => {
+ describe('findRawCodeOwners', () => {
it('should return found codeowner', async () => {
const ownersText = mockCodeOwnersText();
const read = jest
@@ -184,7 +184,7 @@ describe(CodeOwnersProcessor, () => {
});
});
- describe(resolveCodeOwner, () => {
+ describe('resolveCodeOwner', () => {
it('should return found codeowner', async () => {
const read = jest
.fn()
@@ -207,7 +207,7 @@ describe(CodeOwnersProcessor, () => {
});
});
- describe(CodeOwnersProcessor, () => {
+ describe('CodeOwnersProcessor', () => {
const setupTest = ({ kind = 'Component', spec = {} } = {}) => {
const entity = { kind, spec };
const read = jest
@@ -224,7 +224,7 @@ describe(CodeOwnersProcessor, () => {
spec: { owner: '@acme/foo-team' },
});
- const result = await processor.processEntity(
+ const result = await processor.preProcessEntity(
entity as any,
mockLocation(),
);
@@ -235,7 +235,7 @@ describe(CodeOwnersProcessor, () => {
it('should ignore url locations', async () => {
const { entity, processor } = setupTest();
- const result = await processor.processEntity(
+ const result = await processor.preProcessEntity(
entity as any,
mockLocation({ type: 'url' }),
);
@@ -246,7 +246,7 @@ describe(CodeOwnersProcessor, () => {
it('should ignore invalid kinds', async () => {
const { entity, processor } = setupTest({ kind: 'Group' });
- const result = await processor.processEntity(
+ const result = await processor.preProcessEntity(
entity as any,
mockLocation(),
);
@@ -257,7 +257,7 @@ describe(CodeOwnersProcessor, () => {
it('should set owner from codeowner', async () => {
const { entity, processor } = setupTest();
- const result = await processor.processEntity(
+ const result = await processor.preProcessEntity(
entity as any,
mockLocation(),
);
diff --git a/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.ts
index 1802e01d99..bc78b6e34a 100644
--- a/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.ts
+++ b/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.ts
@@ -40,7 +40,10 @@ type Options = {
export class CodeOwnersProcessor implements CatalogProcessor {
constructor(private readonly options: Options) {}
- async processEntity(entity: Entity, location: LocationSpec): Promise {
+ async preProcessEntity(
+ entity: Entity,
+ location: LocationSpec,
+ ): Promise {
// Only continue if the owner is not set
if (
!entity ||
diff --git a/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.ts
index 1917f58fbd..86e17f07f2 100644
--- a/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.ts
+++ b/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.ts
@@ -19,7 +19,7 @@ import * as result from './results';
import { CatalogProcessor, CatalogProcessorEmit } from './types';
export class LocationRefProcessor implements CatalogProcessor {
- async processEntity(
+ async postProcessEntity(
entity: Entity,
_location: LocationSpec,
emit: CatalogProcessorEmit,
diff --git a/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts
index 294cb10f29..44f3163a97 100644
--- a/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts
+++ b/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts
@@ -46,7 +46,7 @@ describe('PlaceholderProcessor', () => {
reader,
});
await expect(
- processor.processEntity(input, { type: 't', target: 'l' }),
+ processor.preProcessEntity(input, { type: 't', target: 'l' }),
).resolves.toBe(input);
});
@@ -62,7 +62,7 @@ describe('PlaceholderProcessor', () => {
});
await expect(
- processor.processEntity(
+ processor.preProcessEntity(
{
apiVersion: 'a',
kind: 'k',
@@ -98,7 +98,7 @@ describe('PlaceholderProcessor', () => {
});
await expect(
- processor.processEntity(
+ processor.preProcessEntity(
{
apiVersion: 'a',
kind: 'k',
@@ -122,7 +122,7 @@ describe('PlaceholderProcessor', () => {
});
await expect(
- processor.processEntity(
+ processor.preProcessEntity(
{
apiVersion: 'a',
kind: 'k',
@@ -143,7 +143,7 @@ describe('PlaceholderProcessor', () => {
});
await expect(
- processor.processEntity(
+ processor.preProcessEntity(
{
apiVersion: 'a',
kind: 'k',
@@ -177,7 +177,7 @@ describe('PlaceholderProcessor', () => {
});
await expect(
- processor.processEntity(
+ processor.preProcessEntity(
{
apiVersion: 'a',
kind: 'k',
@@ -209,7 +209,7 @@ describe('PlaceholderProcessor', () => {
});
await expect(
- processor.processEntity(
+ processor.preProcessEntity(
{
apiVersion: 'a',
kind: 'k',
@@ -241,7 +241,7 @@ describe('PlaceholderProcessor', () => {
});
await expect(
- processor.processEntity(
+ processor.preProcessEntity(
{
apiVersion: 'a',
kind: 'k',
@@ -277,7 +277,7 @@ describe('PlaceholderProcessor', () => {
});
await expect(
- processor.processEntity(
+ processor.preProcessEntity(
{
apiVersion: 'a',
kind: 'k',
@@ -316,7 +316,7 @@ describe('PlaceholderProcessor', () => {
});
await expect(
- processor.processEntity(
+ processor.preProcessEntity(
{
apiVersion: 'a',
kind: 'k',
diff --git a/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.ts
index 3941a2bedc..0e23d51f61 100644
--- a/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.ts
+++ b/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.ts
@@ -45,7 +45,10 @@ type Options = {
export class PlaceholderProcessor implements CatalogProcessor {
constructor(private readonly options: Options) {}
- async processEntity(entity: Entity, location: LocationSpec): Promise {
+ async preProcessEntity(
+ entity: Entity,
+ location: LocationSpec,
+ ): Promise {
const process = async (data: any): Promise<[any, boolean]> => {
if (!data || !(data instanceof Object)) {
// Scalars can't have placeholders
diff --git a/plugins/catalog-backend/src/ingestion/processors/index.ts b/plugins/catalog-backend/src/ingestion/processors/index.ts
index f966ef3348..59a87af555 100644
--- a/plugins/catalog-backend/src/ingestion/processors/index.ts
+++ b/plugins/catalog-backend/src/ingestion/processors/index.ts
@@ -23,7 +23,6 @@ export { AnnotateLocationEntityProcessor } from './AnnotateLocationEntityProcess
export { AzureApiReaderProcessor } from './AzureApiReaderProcessor';
export { BitbucketApiReaderProcessor } from './BitbucketApiReaderProcessor';
export { CodeOwnersProcessor } from './CodeOwnersProcessor';
-export { EntityPolicyProcessor } from './EntityPolicyProcessor';
export { FileReaderProcessor } from './FileReaderProcessor';
export { GithubOrgReaderProcessor } from './GithubOrgReaderProcessor';
export { GithubReaderProcessor } from './GithubReaderProcessor';
diff --git a/plugins/catalog-backend/src/ingestion/processors/types.ts b/plugins/catalog-backend/src/ingestion/processors/types.ts
index 0e6b1491a6..c9adf07eff 100644
--- a/plugins/catalog-backend/src/ingestion/processors/types.ts
+++ b/plugins/catalog-backend/src/ingestion/processors/types.ts
@@ -46,15 +46,33 @@ export type CatalogProcessor = {
): Promise;
/**
- * Processes an emitted entity, e.g. by validating or modifying it.
+ * Pre-processes an emitted entity, after it has been emitted but before it
+ * has been validated.
*
- * @param entity The entity to process
+ * This type of processing usually involves enriching the entity with
+ * additional data, and the input entity may actually still be incomplete
+ * when the processor is invoked.
+ *
+ * @param entity The (possibly partial) entity to process
* @param location The location that the entity came from
- * @param read Reads the contents of a location
* @param emit A sink for auxiliary items resulting from the processing
* @returns The same entity or a modified version of it
*/
- processEntity?(
+ preProcessEntity?(
+ entity: Entity,
+ location: LocationSpec,
+ emit: CatalogProcessorEmit,
+ ): Promise;
+
+ /**
+ * Post-processes an emitted entity, after it has been validated.
+ *
+ * @param entity The entity to process
+ * @param location The location that the entity came from
+ * @param emit A sink for auxiliary items resulting from the processing
+ * @returns The same entity or a modified version of it
+ */
+ postProcessEntity?(
entity: Entity,
location: LocationSpec,
emit: CatalogProcessorEmit,
diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.test.ts b/plugins/catalog-backend/src/service/CatalogBuilder.test.ts
index 66e7dba682..3f7c613c00 100644
--- a/plugins/catalog-backend/src/service/CatalogBuilder.test.ts
+++ b/plugins/catalog-backend/src/service/CatalogBuilder.test.ts
@@ -49,49 +49,6 @@ describe('CatalogBuilder', () => {
reader.read.mockResolvedValue(Buffer.from('junk'));
const builder = new CatalogBuilder(env)
- .replaceReaderProcessors([
- {
- async readLocation(
- location: LocationSpec,
- _optional: boolean,
- emit: CatalogProcessorEmit,
- ) {
- expect(location.type).toBe('test');
- emit(result.data(location, await reader.read('ignored')));
- return true;
- },
- },
- ])
- .replaceParserProcessors([
- {
- async parseData(
- data: Buffer,
- location: LocationSpec,
- emit: CatalogProcessorEmit,
- ) {
- expect(data.toString()).toEqual('junk');
- emit(
- result.entity(location, {
- apiVersion: 'av',
- kind: 'Component',
- metadata: { name: 'n' },
- }),
- );
- return true;
- },
- },
- ])
- .replacePreProcessors([
- {
- async processEntity(entity: Entity) {
- expect(entity.apiVersion).toBe('av');
- return {
- ...entity,
- metadata: { ...entity.metadata, namespace: 'ns' },
- };
- },
- },
- ])
.replaceEntityPolicies([
{
async enforce(entity: Entity) {
@@ -108,9 +65,51 @@ describe('CatalogBuilder', () => {
},
},
])
- .replacePostProcessors([
+ .setPlaceholderResolver('t', async ({ value }) => {
+ expect(value).toBe('tt');
+ return 'tt2';
+ })
+ .setFieldFormatValidators({
+ isValidEntityName: n => {
+ expect(n).toBe('n');
+ return true;
+ },
+ })
+ .replaceProcessors([
{
- async processEntity(entity: Entity) {
+ async readLocation(
+ location: LocationSpec,
+ _optional: boolean,
+ emit: CatalogProcessorEmit,
+ ) {
+ expect(location.type).toBe('test');
+ emit(result.data(location, await reader.read('ignored')));
+ return true;
+ },
+ async parseData(
+ data: Buffer,
+ location: LocationSpec,
+ emit: CatalogProcessorEmit,
+ ) {
+ expect(data.toString()).toEqual('junk');
+ emit(
+ result.entity(location, {
+ apiVersion: 'av',
+ kind: 'Component',
+ metadata: { name: 'n', replaced: { $t: 'tt' } },
+ }),
+ );
+ return true;
+ },
+ async preProcessEntity(entity: Entity) {
+ expect(entity.apiVersion).toBe('av');
+ return {
+ ...entity,
+ metadata: { ...entity.metadata, namespace: 'ns' },
+ };
+ },
+ async postProcessEntity(entity: Entity) {
+ expect(entity.metadata.namespace).toBe('ns');
return {
...entity,
metadata: { ...entity.metadata, post: 'p' },
@@ -124,6 +123,7 @@ describe('CatalogBuilder', () => {
type: 'test',
target: '',
});
+ expect.assertions(8);
expect(added.entities).toEqual([
{
apiVersion: 'av',
@@ -132,6 +132,7 @@ describe('CatalogBuilder', () => {
name: 'n',
namespace: 'ns',
post: 'p',
+ replaced: 'tt2',
}),
},
]);
diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.ts b/plugins/catalog-backend/src/service/CatalogBuilder.ts
index aa4f97258d..e6fb540f16 100644
--- a/plugins/catalog-backend/src/service/CatalogBuilder.ts
+++ b/plugins/catalog-backend/src/service/CatalogBuilder.ts
@@ -26,7 +26,6 @@ import {
locationEntityV1alpha1Policy,
makeValidator,
NoForeignRootFieldsEntityPolicy,
- ReservedFieldsEntityPolicy,
SchemaValidEntityPolicy,
templateEntityV1alpha1Policy,
userEntityV1alpha1Policy,
@@ -48,7 +47,6 @@ import {
BitbucketApiReaderProcessor,
CatalogProcessor,
CodeOwnersProcessor,
- EntityPolicyProcessor,
FileReaderProcessor,
GithubOrgReaderProcessor,
GithubReaderProcessor,
@@ -84,36 +82,24 @@ export type CatalogEnvironment = {
*
* The touch points where you can replace or extend behavior are as follows:
*
- * - Reader processors can be added or replaced. These implement the
- * functionality of reading raw data from a location, in the form of an
- * entity definition file.
- * - Parser processors can be added or replaced. These accept the raw data as
- * read by the previous processors and parse it into raw structured data
- * (for example, from a binary buffer containing yaml text, to a JS object
- * structure).
+ * - Entity policies can be added or replaced. These are automatically run
+ * after the processors' pre-processing steps. All policies are given the
+ * chance to inspect the entity, and all of them have to pass in order for
+ * the entity to be considered valid from an overall point of view.
+ * - Entity kinds can be added or replaced. These are the second line of
+ * validation that is applied after the entity policies, which adds
+ * additional kind-specific validation (usually based on a schema). Only one
+ * of the entity kinds has to accept the entity, but if none of them do, the
+ * entity is rejected as a whole.
* - Placeholder resolvers can be replaced or added. These run on the raw
* structured data between the parsing and pre-processing steps, to replace
* dollar-prefixed entries with their actual values (like $file).
- * - Pre-processors can be added or replaced. These take the raw unvalidated
- * data from the parser processors and can enrich or extend it before
- * validation. This is the place where for example codeowners data can be
- * injected into partial entity definitions.
- * - Entity policies can be added or replaced. These are the first line of
- * validation from the output of the pre-processing step. All policies are
- * given the chance to inspect the entity, and all of them have to pass in
- * order for the entity to be considered valid from an overall point of
- * view.
* - Field format validators can be replaced. These check the format of
- * individual core fields such as metadata.name, such that they adhere to
- * certain rules.
- * - Entity kinds can be added or replaced. These are the second line of
- * validation that is applied after the entity policies, which add additional
- * kind-specific validation (usually based on a schema). Only one of the
- * entity kinds has to accept the entity, but if none of them do, the
- * entity is rejected as a whole.
- * - Post-processors can be added or replaced. These take the validated
- * entities out of the validation step and can perform additional actions on
- * them.
+ * individual core fields such as metadata.name, to ensure that they adhere
+ * to certain rules.
+ * - Processors can be added or replaced. These implement the functionality of
+ * reading, parsing and processing the entity data before it is persisted in
+ * the catalog.
*/
export class CatalogBuilder {
private readonly env: CatalogEnvironment;
@@ -121,16 +107,10 @@ export class CatalogBuilder {
private entityPoliciesReplace: boolean;
private entityKinds: EntityPolicy[];
private entityKindsReplace: boolean;
- private readerProcessors: CatalogProcessor[];
- private readerProcessorsReplace: boolean;
- private parserProcessors: CatalogProcessor[];
- private parserProcessorsReplace: boolean;
- private preProcessors: CatalogProcessor[];
- private preProcessorsReplace: boolean;
- private postProcessors: CatalogProcessor[];
- private postProcessorsReplace: boolean;
private placeholderResolvers: Record;
private fieldFormatValidators: Partial;
+ private processors: CatalogProcessor[];
+ private processorsReplace: boolean;
constructor(env: CatalogEnvironment) {
this.env = env;
@@ -138,16 +118,10 @@ export class CatalogBuilder {
this.entityPoliciesReplace = false;
this.entityKinds = [];
this.entityKindsReplace = false;
- this.readerProcessors = [];
- this.readerProcessorsReplace = false;
- this.parserProcessors = [];
- this.parserProcessorsReplace = false;
- this.preProcessors = [];
- this.preProcessorsReplace = false;
- this.postProcessors = [];
- this.postProcessorsReplace = false;
this.placeholderResolvers = {};
this.fieldFormatValidators = {};
+ this.processors = [];
+ this.processorsReplace = false;
}
/**
@@ -212,114 +186,6 @@ export class CatalogBuilder {
return this;
}
- /**
- * Adds processors that support reading of definition files. These are run
- * before the entities are parsed, pre-processed, validated and post-
- * processed.
- *
- * @param processors One or more processors
- */
- addReaderProcessor(...processors: CatalogProcessor[]): CatalogBuilder {
- this.readerProcessors.push(...processors);
- return this;
- }
-
- /**
- * Sets what processors to use for the reading of definition files. These are
- * run before the entities are parsed, pre-processed, validated and post-
- * processed.
- *
- * This function replaces the default set of processors in this stage; use
- * with care.
- *
- * @param processors One or more processors
- */
- replaceReaderProcessors(processors: CatalogProcessor[]): CatalogBuilder {
- this.readerProcessors = [...processors];
- this.readerProcessorsReplace = true;
- return this;
- }
-
- /**
- * Adds processors that run after each definition file has been read, in
- * order to parse the raw data. These are run before the entities are
- * pre-processed, validated and post-processed.
- *
- * @param processors One or more processors
- */
- addParserProcessor(...processors: CatalogProcessor[]): CatalogBuilder {
- this.parserProcessors.push(...processors);
- return this;
- }
-
- /**
- * Sets what processors to run after each definition file has been read, in
- * order to parse the raw data. These are run before the entities are
- * pre-processed, validated and post-processed.
- *
- * This function replaces the default set of processors in this stage; use
- * with care.
- *
- * @param processors One or more processors
- */
- replaceParserProcessors(processors: CatalogProcessor[]): CatalogBuilder {
- this.parserProcessors = [...processors];
- this.parserProcessorsReplace = true;
- return this;
- }
-
- /**
- * Adds processors that run after each entity has been read and parsed,
- * but before being validated and post-processed.
- *
- * @param processors One or more processors
- */
- addPreProcessor(...processors: CatalogProcessor[]): CatalogBuilder {
- this.preProcessors.push(...processors);
- return this;
- }
-
- /**
- * Sets what processors to run after each entity has been read and parsed,
- * but before being validated and post-processed.
- *
- * This function replaces the default set of processors in this stage; use
- * with care.
- *
- * @param processors One or more processors
- */
- replacePreProcessors(processors: CatalogProcessor[]): CatalogBuilder {
- this.preProcessors = [...processors];
- this.preProcessorsReplace = true;
- return this;
- }
-
- /**
- * Adds processors that run after each entity has been read, parsed,
- * run through the pre-processors, and validated.
- *
- * @param processors One or more processors
- */
- addPostProcessor(...processors: CatalogProcessor[]): CatalogBuilder {
- this.postProcessors.push(...processors);
- return this;
- }
-
- /**
- * Sets what processors to run after each entity has been read, parsed,
- * run through the pre-processors, and validated.
- *
- * This function replaces the default set of processors in this stage; use
- * with care.
- *
- * @param processors One or more processors
- */
- replacePostProcessors(processors: CatalogProcessor[]): CatalogBuilder {
- this.postProcessors = [...processors];
- this.postProcessorsReplace = true;
- return this;
- }
-
/**
* Adds, or overwrites, a handler for placeholders (e.g. $file) in entity
* definition files.
@@ -327,8 +193,12 @@ export class CatalogBuilder {
* @param key The key that identifies the placeholder, e.g. "file"
* @param resolver The resolver that gets values for this placeholder
*/
- setPlaceholderResolver(key: string, resolver: PlaceholderResolver) {
+ setPlaceholderResolver(
+ key: string,
+ resolver: PlaceholderResolver,
+ ): CatalogBuilder {
this.placeholderResolvers[key] = resolver;
+ return this;
}
/**
@@ -341,8 +211,34 @@ export class CatalogBuilder {
*
* @param validators The (subset of) validators to set
*/
- setFieldFormatValidators(validators: Partial) {
+ setFieldFormatValidators(validators: Partial): CatalogBuilder {
lodash.merge(this.fieldFormatValidators, validators);
+ return this;
+ }
+
+ /**
+ * Adds entity processors. These are responsible for reading, parsing, and
+ * processing entities before they are persisted in the catalog.
+ *
+ * @param processors One or more processors
+ */
+ addProcessor(...processors: CatalogProcessor[]): CatalogBuilder {
+ this.processors.push(...processors);
+ return this;
+ }
+
+ /**
+ * Sets what entity processors to use. These are responsible for reading,
+ * parsing, and processing entities before they are persisted in the catalog.
+ *
+ * This function replaces the default set of processors; use with care.
+ *
+ * @param processors One or more processors
+ */
+ replaceProcessors(processors: CatalogProcessor[]): CatalogBuilder {
+ this.processors = [...processors];
+ this.processorsReplace = true;
+ return this;
}
/**
@@ -355,14 +251,15 @@ export class CatalogBuilder {
}> {
const { config, database, logger } = this.env;
- const entityPolicy = this.buildEntityPolicy();
- const processors = this.buildProcessors(entityPolicy);
+ const policy = this.buildEntityPolicy();
+ const processors = this.buildProcessors();
const rulesEnforcer = CatalogRulesEnforcer.fromConfig(config);
const locationReader = new LocationReaders({
...this.env,
processors,
rulesEnforcer,
+ policy,
});
const db = await DatabaseManager.createDatabase(
@@ -396,7 +293,6 @@ export class CatalogBuilder {
new FieldFormatEntityPolicy(
makeValidator(this.fieldFormatValidators),
),
- new ReservedFieldsEntityPolicy(),
...this.entityPolicies,
];
@@ -418,106 +314,74 @@ export class CatalogBuilder {
]);
}
- private buildProcessors(entityPolicy: EntityPolicy): CatalogProcessor[] {
- const { config, reader } = this.env;
+ private buildProcessors(): CatalogProcessor[] {
+ const { config, logger, reader } = this.env;
- const placeholderResolvers = lodash.merge(
- {
- json: jsonPlaceholderResolver,
- yaml: yamlPlaceholderResolver,
- text: textPlaceholderResolver,
- },
- this.placeholderResolvers,
- );
+ const placeholderResolvers: Record = {
+ json: jsonPlaceholderResolver,
+ yaml: yamlPlaceholderResolver,
+ text: textPlaceholderResolver,
+ ...this.placeholderResolvers,
+ };
+
+ const processors = this.processorsReplace
+ ? this.processors
+ : [
+ new FileReaderProcessor(),
+ GithubOrgReaderProcessor.fromConfig(config, { logger }),
+ LdapOrgReaderProcessor.fromConfig(config, { logger }),
+ new UrlReaderProcessor({ reader, logger }),
+ new YamlProcessor(),
+ new CodeOwnersProcessor({ reader }),
+ new LocationRefProcessor(),
+ new AnnotateLocationEntityProcessor(),
+ ];
return [
StaticLocationProcessor.fromConfig(config),
- ...this.buildReaderProcessors(),
- ...this.buildParserProcessors(),
new PlaceholderProcessor({ resolvers: placeholderResolvers, reader }),
- ...this.buildPreProcessors(),
- new EntityPolicyProcessor(entityPolicy),
- ...this.buildPostProcessors(),
+ ...this.buildDeprecatedReaderProcessors(),
+ ...processors,
];
}
- private buildReaderProcessors(): CatalogProcessor[] {
- const { config, logger, reader } = this.env;
+ // TODO(Rugvip): These are added for backwards compatibility if config exists
+ // The idea is to have everyone migrate from using the old processors to
+ // the new integration config driven UrlReaders. In an upcoming release we
+ // can then completely remove support for the old processors, but still
+ // keep handling the deprecated location types for a while, but with a
+ // warning.
+ private buildDeprecatedReaderProcessors(): CatalogProcessor[] {
+ const { config, logger } = this.env;
- if (this.readerProcessorsReplace) {
- return this.readerProcessors;
- }
-
- // TODO(Rugvip): These are added for backwards compatibility if config exists
- // The idea is to have everyone migrate from using the old processors to the new
- // integration config driven UrlReaders. In an upcoming release we can then completely
- // remove support for the old processors, but still keep handling the deprecated location
- // types for a while, but with a warning.
- const oldProcessors = [];
+ const result = [];
const pc = config.getOptionalConfig('catalog.processors');
if (pc?.has('github')) {
logger.warn(
`Using deprecated configuration for catalog.processors.github, move to using integrations.github instead`,
);
- oldProcessors.push(GithubReaderProcessor.fromConfig(config, logger));
+ result.push(GithubReaderProcessor.fromConfig(config, logger));
}
if (pc?.has('gitlabApi')) {
logger.warn(
`Using deprecated configuration for catalog.processors.gitlabApi, move to using integrations.gitlab instead`,
);
- oldProcessors.push(new GitlabApiReaderProcessor(config));
- oldProcessors.push(new GitlabReaderProcessor());
+ result.push(new GitlabApiReaderProcessor(config));
+ result.push(new GitlabReaderProcessor());
}
if (pc?.has('bitbucketApi')) {
logger.warn(
`Using deprecated configuration for catalog.processors.bitbucketApi, move to using integrations.bitbucket instead`,
);
- oldProcessors.push(new BitbucketApiReaderProcessor(config));
+ result.push(new BitbucketApiReaderProcessor(config));
}
if (pc?.has('azureApi')) {
logger.warn(
`Using deprecated configuration for catalog.processors.azureApi, move to using integrations.azure instead`,
);
- oldProcessors.push(new AzureApiReaderProcessor(config));
+ result.push(new AzureApiReaderProcessor(config));
}
- return [
- new FileReaderProcessor(),
- ...oldProcessors,
- GithubOrgReaderProcessor.fromConfig(config, { logger }),
- LdapOrgReaderProcessor.fromConfig(config, { logger }),
- new UrlReaderProcessor({ reader, logger }),
- ...this.readerProcessors,
- ];
- }
-
- private buildParserProcessors(): CatalogProcessor[] {
- if (this.parserProcessorsReplace) {
- return this.parserProcessors;
- }
-
- return [new YamlProcessor(), ...this.parserProcessors];
- }
-
- private buildPreProcessors(): CatalogProcessor[] {
- const { reader } = this.env;
-
- if (this.preProcessorsReplace) {
- return this.preProcessors;
- }
-
- return [new CodeOwnersProcessor({ reader }), ...this.preProcessors];
- }
-
- private buildPostProcessors(): CatalogProcessor[] {
- if (this.postProcessorsReplace) {
- return this.postProcessors;
- }
-
- return [
- new LocationRefProcessor(),
- new AnnotateLocationEntityProcessor(),
- ...this.postProcessors,
- ];
+ return result;
}
}
diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.tsx
index 939449f719..d9a8142d66 100644
--- a/plugins/catalog/src/components/AboutCard/AboutCard.tsx
+++ b/plugins/catalog/src/components/AboutCard/AboutCard.tsx
@@ -14,24 +14,23 @@
* limitations under the License.
*/
-import React from 'react';
+import { Entity, ENTITY_DEFAULT_NAMESPACE } from '@backstage/catalog-model';
import {
- Grid,
- Typography,
- makeStyles,
- Chip,
- IconButton,
Card,
CardContent,
CardHeader,
+ Chip,
Divider,
+ Grid,
+ IconButton,
+ makeStyles,
+ Typography,
} from '@material-ui/core';
-import { Entity } from '@backstage/catalog-model';
-
-import GitHubIcon from '@material-ui/icons/GitHub';
-import { IconLinkVertical } from './IconLinkVertical';
-import EditIcon from '@material-ui/icons/Edit';
import DocsIcon from '@material-ui/icons/Description';
+import EditIcon from '@material-ui/icons/Edit';
+import GitHubIcon from '@material-ui/icons/GitHub';
+import React from 'react';
+import { IconLinkVertical } from './IconLinkVertical';
const useStyles = makeStyles(theme => ({
links: {
@@ -107,9 +106,9 @@ export function AboutCard({ entity }: AboutCardProps) {
}
label="View Techdocs"
icon={ }
- href={`/docs/${entity.kind}:${entity.metadata.namespace ?? ''}:${
- entity.metadata.name
- }`}
+ href={`/docs/${
+ entity.metadata.namespace || ENTITY_DEFAULT_NAMESPACE
+ }/${entity.kind}/${entity.metadata.name}`}
/>
}
diff --git a/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx b/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx
index e4eec3c21f..258cc38d15 100644
--- a/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx
+++ b/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx
@@ -85,7 +85,7 @@ export const UnregisterEntityDialog: FC = ({
{error.toString()}
) : null}
- {entities ? (
+ {entities?.length ? (
<>
This action will unregister the following entities:
@@ -107,11 +107,11 @@ export const UnregisterEntityDialog: FC = ({
-
- To undo, just re-register the entity in Backstage.
-
>
) : null}
+
+ To undo, just re-register the entity in Backstage.
+
diff --git a/plugins/cost-insights/src/components/BarChart/BarChart.test.tsx b/plugins/cost-insights/src/components/BarChart/BarChart.test.tsx
index a39c5bafc2..737b86018c 100644
--- a/plugins/cost-insights/src/components/BarChart/BarChart.test.tsx
+++ b/plugins/cost-insights/src/components/BarChart/BarChart.test.tsx
@@ -79,12 +79,14 @@ describe(' ', () => {
it('Should display only 6 resources by default, sorted by cost', async () => {
const rendered = await renderWithProps({} as BarChartProps);
- MockResources.sort(resourceSort).forEach((resource, index) => {
- if (index < 6) {
- expect(rendered.getByText(resource.name!)).toBeInTheDocument();
- } else {
- expect(rendered.queryByText(resource.name!)).not.toBeInTheDocument();
- }
+ const sorted = MockResources.sort(resourceSort);
+
+ expect(sorted.length).toBe(10);
+ sorted.slice(0, 6).forEach(resource => {
+ expect(rendered.getByText(resource.name!)).toBeInTheDocument();
+ });
+ sorted.slice(6).forEach(resource => {
+ expect(rendered.queryByText(resource.name!)).not.toBeInTheDocument();
});
});
diff --git a/plugins/cost-insights/src/components/ProjectSelect/ProjectSelect.test.tsx b/plugins/cost-insights/src/components/ProjectSelect/ProjectSelect.test.tsx
index 92de819fe0..a74c79f3b8 100644
--- a/plugins/cost-insights/src/components/ProjectSelect/ProjectSelect.test.tsx
+++ b/plugins/cost-insights/src/components/ProjectSelect/ProjectSelect.test.tsx
@@ -59,10 +59,9 @@ describe(' ', () => {
const button = getByRole(projectSelectContainer, 'button');
UserEvent.click(button);
await waitFor(() => rendered.getByTestId('option-all'));
- mockProjects.forEach(
- project =>
- project.id &&
- expect(rendered.getByText(project.id)).toBeInTheDocument(),
+
+ mockProjects.forEach(project =>
+ expect(rendered.getByText(project.id)).toBeInTheDocument(),
);
});
});
diff --git a/plugins/github-actions/src/api/types.ts b/plugins/github-actions/src/api/types.ts
index ccc9198e58..00d622cccf 100644
--- a/plugins/github-actions/src/api/types.ts
+++ b/plugins/github-actions/src/api/types.ts
@@ -17,7 +17,7 @@
export type Step = {
name: string;
status: string;
- conclusion: string;
+ conclusion?: string;
number: number; // starts from 1
started_at: string;
completed_at: string;
diff --git a/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx b/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx
index 7714ad6cb6..a2753589eb 100644
--- a/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx
+++ b/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx
@@ -77,8 +77,9 @@ const JobsList = ({ jobs, entity }: { jobs?: Jobs; entity: Entity }) => {
{jobs &&
jobs.total_count > 0 &&
- jobs.jobs.map((job: Job) => (
+ jobs.jobs.map(job => (
{
@@ -142,8 +143,8 @@ const JobListItem = ({
- {job.steps.map((step: Step) => (
-
+ {job.steps.map(step => (
+
))}
diff --git a/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.test.tsx b/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.test.tsx
index fb401494eb..7bf48eb7f6 100644
--- a/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.test.tsx
+++ b/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.test.tsx
@@ -19,6 +19,7 @@ import { GraphiQLPage } from './GraphiQLPage';
import { ThemeProvider } from '@material-ui/core';
import { lightTheme } from '@backstage/theme';
import { ApiProvider, ApiRegistry } from '@backstage/core';
+import { act } from 'react-dom/test-utils';
import { renderWithEffects } from '@backstage/test-utils';
import { GraphQLBrowseApi, graphQlBrowseApiRef } from '../../lib/api';
@@ -28,6 +29,7 @@ jest.mock('../GraphiQLBrowser', () => ({
describe('GraphiQLPage', () => {
it('should show progress', async () => {
+ jest.useFakeTimers();
const loadingApi: GraphQLBrowseApi = {
async getEndpoints() {
await new Promise(() => {});
@@ -43,9 +45,12 @@ describe('GraphiQLPage', () => {
,
,
);
-
+ act(() => {
+ jest.advanceTimersByTime(250);
+ });
rendered.getByText('GraphiQL');
rendered.getByTestId('progress');
+ jest.useRealTimers();
});
it('should show error', async () => {
diff --git a/plugins/lighthouse/src/components/AuditView/index.test.tsx b/plugins/lighthouse/src/components/AuditView/index.test.tsx
index 742be67fed..ca292a133d 100644
--- a/plugins/lighthouse/src/components/AuditView/index.test.tsx
+++ b/plugins/lighthouse/src/components/AuditView/index.test.tsx
@@ -163,7 +163,7 @@ describe('AuditView', () => {
});
describe('when the request for the website by id is pending', () => {
- it('it shows the loading', async () => {
+ it('shows the loading', async () => {
mockFetch.mockImplementationOnce(() => new Promise(() => {}));
const rendered = render(
wrapInTestApp(
@@ -177,7 +177,7 @@ describe('AuditView', () => {
});
describe('when the request for the website by id fails', () => {
- it('it shows an error', async () => {
+ it('shows an error', async () => {
mockFetch.mockRejectOnce(new Error('failed to fetch'));
const rendered = render(
wrapInTestApp(
diff --git a/plugins/lighthouse/src/hooks/useWebsiteForEntity.test.tsx b/plugins/lighthouse/src/hooks/useWebsiteForEntity.test.tsx
index 868e26f04c..580162f182 100644
--- a/plugins/lighthouse/src/hooks/useWebsiteForEntity.test.tsx
+++ b/plugins/lighthouse/src/hooks/useWebsiteForEntity.test.tsx
@@ -75,7 +75,7 @@ describe('useWebsiteForEntity', () => {
(mockLighthouseApi.getWebsiteByUrl as jest.Mock).mockResolvedValue(website);
});
- it('returns the lighthouse information for the website url in annotations ', async () => {
+ it('returns the lighthouse information for the website url in annotations', async () => {
const { result, waitForNextUpdate } = subject();
await waitForNextUpdate();
expect(result.current?.value).toBe(website);
diff --git a/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx b/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx
index 10c3d9fcd1..875b1f8660 100644
--- a/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx
+++ b/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx
@@ -30,7 +30,7 @@ describe('RegisterComponentForm', () => {
expect(screen.getByText('Submit').closest('button')).toBeDisabled();
});
- it('should enable a submit button when the target url is set ', async () => {
+ it('should enable a submit button when the target url is set', async () => {
render( );
await act(async () => {
diff --git a/plugins/tech-radar/src/components/RadarComponent.test.tsx b/plugins/tech-radar/src/components/RadarComponent.test.tsx
index 5591d74b82..59cababbc9 100644
--- a/plugins/tech-radar/src/components/RadarComponent.test.tsx
+++ b/plugins/tech-radar/src/components/RadarComponent.test.tsx
@@ -19,6 +19,7 @@ import { render, waitForElement } from '@testing-library/react';
import { ThemeProvider } from '@material-ui/core';
import { lightTheme } from '@backstage/theme';
import { ApiRegistry, ApiProvider, errorApiRef } from '@backstage/core';
+import { act } from 'react-dom/test-utils';
import { withLogCollector } from '@backstage/test-utils';
import GetBBoxPolyfill from '../utils/polyfills/getBBox';
@@ -34,8 +35,9 @@ describe('RadarComponent', () => {
});
it('should render a progress bar', async () => {
- const errorApi = { post: () => {} };
+ jest.useFakeTimers();
+ const errorApi = { post: () => {} };
const { getByTestId, queryByTestId } = render(
@@ -48,9 +50,13 @@ describe('RadarComponent', () => {
,
);
+ act(() => {
+ jest.advanceTimersByTime(250);
+ });
expect(getByTestId('progress')).toBeInTheDocument();
await waitForElement(() => queryByTestId('tech-radar-svg'));
+ jest.useRealTimers();
});
it('should call the errorApi if load fails', async () => {
diff --git a/plugins/tech-radar/src/components/RadarPage.test.tsx b/plugins/tech-radar/src/components/RadarPage.test.tsx
index 97fae16380..ee5cf8e131 100644
--- a/plugins/tech-radar/src/components/RadarPage.test.tsx
+++ b/plugins/tech-radar/src/components/RadarPage.test.tsx
@@ -22,6 +22,7 @@ import { ApiRegistry, ApiProvider, errorApiRef } from '@backstage/core';
import GetBBoxPolyfill from '../utils/polyfills/getBBox';
import { RadarPage } from './RadarPage';
+import { act } from 'react-dom/test-utils';
import { MockErrorApi, wrapInTestApp } from '@backstage/test-utils';
describe('RadarPage', () => {
@@ -34,6 +35,8 @@ describe('RadarPage', () => {
});
it('should render a progress bar', async () => {
+ jest.useFakeTimers();
+
const techRadarProps = {
width: 1200,
height: 800,
@@ -48,9 +51,13 @@ describe('RadarPage', () => {
),
);
+ act(() => {
+ jest.advanceTimersByTime(250);
+ });
expect(getByTestId('progress')).toBeInTheDocument();
await waitForElement(() => queryByTestId('tech-radar-svg'));
+ jest.useRealTimers();
});
it('should render a header with a svg', async () => {
diff --git a/plugins/techdocs/src/reader/components/TechDocsProgressBar.test.tsx b/plugins/techdocs/src/reader/components/TechDocsProgressBar.test.tsx
index be464bd485..f97fcda3f1 100644
--- a/plugins/techdocs/src/reader/components/TechDocsProgressBar.test.tsx
+++ b/plugins/techdocs/src/reader/components/TechDocsProgressBar.test.tsx
@@ -25,12 +25,14 @@ jest.useFakeTimers();
describe(' ', () => {
it('should render a message if techdocs page takes more time to load', () => {
const rendered = render(wrapInTestApp( ));
-
- expect(rendered.getByTestId('progress')).toBeDefined();
+ act(() => {
+ jest.advanceTimersByTime(250);
+ });
+ expect(rendered.getByTestId('progress')).toBeInTheDocument();
expect(rendered.queryByTestId('delay-reason')).toBeNull();
act(() => {
jest.advanceTimersByTime(5000);
});
- expect(rendered.getByTestId('delay-reason')).toBeDefined();
+ expect(rendered.getByTestId('delay-reason')).toBeInTheDocument();
});
});
diff --git a/plugins/user-settings/src/components/AuthProviders/AuthProviders.tsx b/plugins/user-settings/src/components/AuthProviders/AuthProviders.tsx
index 563bf44150..bc2a68578d 100644
--- a/plugins/user-settings/src/components/AuthProviders/AuthProviders.tsx
+++ b/plugins/user-settings/src/components/AuthProviders/AuthProviders.tsx
@@ -37,7 +37,7 @@ export const AuthProviders = ({ providerSettings }: Props) => {
}
return (
-
+
{providers}
);
diff --git a/plugins/user-settings/src/components/AuthProviders/ProviderSettingsItem.tsx b/plugins/user-settings/src/components/AuthProviders/ProviderSettingsItem.tsx
index 7213ee2b83..4bae3dbfbb 100644
--- a/plugins/user-settings/src/components/AuthProviders/ProviderSettingsItem.tsx
+++ b/plugins/user-settings/src/components/AuthProviders/ProviderSettingsItem.tsx
@@ -22,14 +22,13 @@ import {
SessionState,
} from '@backstage/core';
import {
+ Button,
ListItem,
ListItemIcon,
ListItemSecondaryAction,
ListItemText,
Tooltip,
} from '@material-ui/core';
-import PowerButton from '@material-ui/icons/PowerSettingsNew';
-import { ToggleButton } from '@material-ui/lab';
type Props = {
title: string;
@@ -84,14 +83,13 @@ export const ProviderSettingsItem = ({
arrow
title={signedIn ? `Sign out from ${title}` : `Sign in to ${title}`}
>
- (signedIn ? api.signOut() : api.signIn())}
+ (signedIn ? api.signOut() : api.signIn())}
>
-
-
+ {signedIn ? `Sign out` : `Sign in`}
+
diff --git a/plugins/user-settings/src/components/FeatureFlags/FeatureFlags.tsx b/plugins/user-settings/src/components/FeatureFlags/FeatureFlags.tsx
index 71f59c52b8..e0f4d6f40e 100644
--- a/plugins/user-settings/src/components/FeatureFlags/FeatureFlags.tsx
+++ b/plugins/user-settings/src/components/FeatureFlags/FeatureFlags.tsx
@@ -62,7 +62,7 @@ export const FeatureFlags = () => {
}
return (
-
+
{featureFlags.map(featureFlag => {
const enabled = Boolean(state[featureFlag.name]);
diff --git a/plugins/user-settings/src/components/FeatureFlags/FeatureFlagsItem.tsx b/plugins/user-settings/src/components/FeatureFlags/FeatureFlagsItem.tsx
index 92065f2ed6..460a15f56b 100644
--- a/plugins/user-settings/src/components/FeatureFlags/FeatureFlagsItem.tsx
+++ b/plugins/user-settings/src/components/FeatureFlags/FeatureFlagsItem.tsx
@@ -19,10 +19,9 @@ import {
ListItem,
ListItemSecondaryAction,
ListItemText,
+ Switch,
Tooltip,
} from '@material-ui/core';
-import CheckIcon from '@material-ui/icons/CheckCircle';
-import { ToggleButton } from '@material-ui/lab';
import { FeatureFlagsRegistryItem } from '@backstage/core';
type Props = {
@@ -39,14 +38,12 @@ export const FlagItem = ({ flag, enabled, toggleHandler }: Props) => (
/>
- toggleHandler(flag.name)}
- >
-
-
+ name={flag.name}
+ />
diff --git a/plugins/user-settings/src/components/General/General.tsx b/plugins/user-settings/src/components/General/General.tsx
index 00ac17dfe1..b6d30d0103 100644
--- a/plugins/user-settings/src/components/General/General.tsx
+++ b/plugins/user-settings/src/components/General/General.tsx
@@ -21,12 +21,12 @@ import { Profile } from './Profile';
import { ThemeToggle } from './ThemeToggle';
export const General = () => (
-
-
+
+
-
-
+
+
diff --git a/plugins/user-settings/src/components/General/PinButton.test.tsx b/plugins/user-settings/src/components/General/PinButton.test.tsx
index 76af8ce8d4..939588a9ee 100644
--- a/plugins/user-settings/src/components/General/PinButton.test.tsx
+++ b/plugins/user-settings/src/components/General/PinButton.test.tsx
@@ -32,10 +32,9 @@ describe(' ', () => {
,
),
);
-
expect(rendered.getByText('Pin Sidebar')).toBeInTheDocument();
- const pinButton = rendered.getByTitle('Pin Sidebar');
+ const pinButton = rendered.getByLabelText('Pin Sidebar Switch');
fireEvent.click(pinButton);
expect(mockToggleFn).toHaveBeenCalled();
});
diff --git a/plugins/user-settings/src/components/General/PinButton.tsx b/plugins/user-settings/src/components/General/PinButton.tsx
index e36fc365f8..d44e5c9a3b 100644
--- a/plugins/user-settings/src/components/General/PinButton.tsx
+++ b/plugins/user-settings/src/components/General/PinButton.tsx
@@ -19,18 +19,11 @@ import {
ListItem,
ListItemSecondaryAction,
ListItemText,
+ Switch,
Tooltip,
} from '@material-ui/core';
-import LockIcon from '@material-ui/icons/Lock';
-import LockOpenIcon from '@material-ui/icons/LockOpen';
-import { ToggleButton } from '@material-ui/lab';
import { SidebarPinStateContext } from '@backstage/core';
-type PinIconProps = { isPinned: boolean };
-
-const PinIcon = ({ isPinned }: PinIconProps) =>
- isPinned ? : ;
-
export const PinButton = () => {
const { isPinned, toggleSidebarPinState } = useContext(
SidebarPinStateContext,
@@ -48,16 +41,13 @@ export const PinButton = () => {
arrow
title={`${isPinned ? 'Unpin' : 'Pin'} Sidebar`}
>
- {
- toggleSidebarPinState();
- }}
- >
-
-
+ toggleSidebarPinState()}
+ name="pin"
+ inputProps={{ 'aria-label': 'Pin Sidebar Switch' }}
+ />
diff --git a/plugins/user-settings/src/components/General/ThemeToggle.tsx b/plugins/user-settings/src/components/General/ThemeToggle.tsx
index 111bd838a8..dc24a61f8f 100644
--- a/plugins/user-settings/src/components/General/ThemeToggle.tsx
+++ b/plugins/user-settings/src/components/General/ThemeToggle.tsx
@@ -102,12 +102,20 @@ export const ThemeToggle = () => {
title={`Select ${theme.title}`}
value={theme.variant}
>
-
+ <>
+ {theme.variant}
+
+ >
);
})}
-
+
+ Auto
diff --git a/yarn.lock b/yarn.lock
index 9fad11c188..bd4ec09d50 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3650,10 +3650,10 @@
resolved "https://registry.npmjs.org/@rjsf/material-ui/-/material-ui-2.3.0.tgz#a051eb4db2ad778e39933a31ba806f415dd3ba90"
integrity sha512-v/xZ4Xk18ZgBARcCe99IDqdO97GU0UC784gG8PhGGFDdy5Rbdy7ixTjHkGYttkr43PB7SX6ttohNhkKSW4nATQ==
-"@roadiehq/backstage-plugin-github-insights@^0.2.6":
- version "0.2.6"
- resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-insights/-/backstage-plugin-github-insights-0.2.6.tgz#7c854cc6b107ff30b0f03b1a5e1ca5e04723dd99"
- integrity sha512-d3CmA2B0iOB2jKFK3z4q1ehl9nCfh2yrCb6byOvsIyV2xlm3cnt3Uq5hLlQekTYLWRvBWw2ULQVC8+wk2Uq6gg==
+"@roadiehq/backstage-plugin-github-insights@^0.2.7":
+ version "0.2.7"
+ resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-insights/-/backstage-plugin-github-insights-0.2.7.tgz#b8e86af9215270d8c02fca4f2f32880ff84c0503"
+ integrity sha512-oYqGkAkPa2pvlk+tDgTOkdhTUdLvc71x4FRZI82h1aaPzrz7DODooU644Qsduc5yYiXicbFkvMUk1b3fjL4Dxw==
dependencies:
"@backstage/catalog-model" "^0.1.1-alpha.24"
"@backstage/core" "^0.1.1-alpha.24"
@@ -3802,10 +3802,10 @@
dependencies:
"@sinonjs/commons" "^1.7.0"
-"@spotify/eslint-config-base@^7.0.0":
- version "7.0.0"
- resolved "https://registry.npmjs.org/@spotify/eslint-config-base/-/eslint-config-base-7.0.0.tgz#36804ae09ec938f1aa5f9464ea993f3f151cfaa8"
- integrity sha512-XRTrTRyRRYBxPYINKNHw4B8QWlA3p4I+id/Po2sMdejtXH3LZgDgIjdschUZSdQ6J6dVYDdaVykdizRM9I+G6Q==
+"@spotify/eslint-config-base@^8.0.0":
+ version "8.0.0"
+ resolved "https://registry.npmjs.org/@spotify/eslint-config-base/-/eslint-config-base-8.0.0.tgz#c3d10d8a05ad9129d579952cb80063e4c4fa216e"
+ integrity sha512-07Fo+KoTMl4AaZJPD6deFnYmG634wzgeqfFcWdFlIrOLAasvWSH8Lp6ZE9cJBKuffOAy8kuEE1SwFo34+5X8bQ==
"@spotify/eslint-config-oss@^1.0.1":
version "1.0.2"
@@ -3814,51 +3814,21 @@
dependencies:
eslint-plugin-notice "^0.9.10"
-"@spotify/eslint-config-react@^7.0.1":
- version "7.0.1"
- resolved "https://registry.npmjs.org/@spotify/eslint-config-react/-/eslint-config-react-7.0.1.tgz#2e70de9d7911ea9aeaa55a83a332220a28f6c431"
- integrity sha512-HNDHvm19EaBXiDgsg52mjMgKWZTeA0hO2Q75ACNwb8UtjIKkANqyyuzyDGo8jiGMbWIm6wJjShlRtT95emNlqQ==
+"@spotify/eslint-config-react@^8.0.0":
+ version "8.0.2"
+ resolved "https://registry.npmjs.org/@spotify/eslint-config-react/-/eslint-config-react-8.0.2.tgz#91af3c58b22d49a96795336ed66927e722cda430"
+ integrity sha512-V0gzO/jWmjhODLINAfeunHj+OBdj/cQeVDw+92ZUlkonAg6BZLjX6Vy8jeIUFjfgmVOtI9eKbcApxaurj1jN6g==
-"@spotify/eslint-config-typescript@^7.0.0":
- version "7.0.0"
- resolved "https://registry.npmjs.org/@spotify/eslint-config-typescript/-/eslint-config-typescript-7.0.0.tgz#fc5227e3344f74b41ac3a530df24a95ac13254e4"
- integrity sha512-28I/SAf68NKbWZ5IY0WYMa0D18PxWdC9DP9gRbOTlZufmsS8jEgqf3zBUWmP6XOf1nihpKWcqvbFUG5H7/JYXA==
-
-"@spotify/eslint-config@^7.0.0", "@spotify/eslint-config@^7.0.1":
- version "7.0.1"
- resolved "https://registry.npmjs.org/@spotify/eslint-config/-/eslint-config-7.0.1.tgz#07a21cfd7fce89cfc2c6dd5ea5d747e741201b66"
- integrity sha512-8GI/TZGUhS4pr7oipT2MjrZFRgXcKzk9YImEusUdD2f5vlCniRFIBQNrvTMkyjfdQqvIVqJPLcdVPXeAgprsMw==
- dependencies:
- "@spotify/eslint-config-base" "^7.0.0"
- "@spotify/eslint-config-react" "^7.0.1"
- "@spotify/eslint-config-typescript" "^7.0.0"
- "@spotify/web-scripts-utils" "^7.0.0"
- "@typescript-eslint/eslint-plugin" "^2.14.0"
- "@typescript-eslint/parser" "^2.14.0"
- eslint-config-prettier "^6.0.0"
- eslint-plugin-jest "^23.6.0"
- eslint-plugin-jsx-a11y "^6.2.1"
- eslint-plugin-react "^7.12.4"
- eslint-plugin-react-hooks "^4.0.0"
-
-"@spotify/prettier-config@^7.0.0":
- version "7.0.0"
- resolved "https://registry.npmjs.org/@spotify/prettier-config/-/prettier-config-7.0.0.tgz#47750979d1282197295108b6958360660a955c16"
- integrity sha512-lIMcx/2oDqTtW84iHKkRJe+8U6HK6GPwWH5sJp9UEHcDpdXomOQYvwcGXy2I2zwPQQ14gYYE6nEJuSnnYqsYRw==
+"@spotify/eslint-config-typescript@^8.0.0":
+ version "8.1.1"
+ resolved "https://registry.npmjs.org/@spotify/eslint-config-typescript/-/eslint-config-typescript-8.1.1.tgz#a8c6f74ef9445fe740008ae3a26003a2eef78f93"
+ integrity sha512-0NkE6ZiSRf9IBWwCxzn/l7UdwcL/KZsbzjjOSweafyYueajEn+eIpKjOgDGUL/ebxSrOtjEp+YeFc4lSPkP0qg==
"@spotify/prettier-config@^8.0.0":
version "8.0.0"
resolved "https://registry.npmjs.org/@spotify/prettier-config/-/prettier-config-8.0.0.tgz#8b6c2bd579ddc54887155a0721fe04e96c89f7f2"
integrity sha512-so8w32ZV42CHWxOEXcBtbNO/hLXFrQNXVmhfzhUI6dVB9cq2xjRaiqu8GjFj8LvKbWpPj+S+KwTIS4aDVWqrFQ==
-"@spotify/web-scripts-utils@^7.0.0":
- version "7.0.0"
- resolved "https://registry.npmjs.org/@spotify/web-scripts-utils/-/web-scripts-utils-7.0.0.tgz#8c6b8039fc645a36ac48629eb9ba06600f4d828a"
- integrity sha512-McMy0j60lxOHjgDjegthZqEWN/PabphiM30A/mI/Y7xh9+JFnYWBTSm/wgn6EW2BsPpV631xSYYkk6B1Ph14Fw==
- dependencies:
- glob "^7.1.4"
- read-pkg-up "^7.0.1"
-
"@storybook/addon-actions@^6.0.21":
version "6.0.21"
resolved "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-6.0.21.tgz#0de1d109d4b1eb99f644bbe84e74c25cfd2b1b6b"
@@ -5830,49 +5800,113 @@
resolved "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d"
integrity sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg==
-"@typescript-eslint/eslint-plugin@^2.14.0":
- version "2.24.0"
- resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.24.0.tgz#a86cf618c965a462cddf3601f594544b134d6d68"
- integrity sha512-wJRBeaMeT7RLQ27UQkDFOu25MqFOBus8PtOa9KaT5ZuxC1kAsd7JEHqWt4YXuY9eancX0GK9C68i5OROnlIzBA==
+"@typescript-eslint/eslint-plugin@^v3.10.1":
+ version "3.10.1"
+ resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.10.1.tgz#7e061338a1383f59edc204c605899f93dc2e2c8f"
+ integrity sha512-PQg0emRtzZFWq6PxBcdxRH3QIQiyFO3WCVpRL3fgj5oQS3CDs3AeAKfv4DxNhzn8ITdNJGJ4D3Qw8eAJf3lXeQ==
dependencies:
- "@typescript-eslint/experimental-utils" "2.24.0"
- eslint-utils "^1.4.3"
+ "@typescript-eslint/experimental-utils" "3.10.1"
+ debug "^4.1.1"
functional-red-black-tree "^1.0.1"
regexpp "^3.0.0"
+ semver "^7.3.2"
tsutils "^3.17.1"
-"@typescript-eslint/experimental-utils@2.24.0", "@typescript-eslint/experimental-utils@^2.5.0":
- version "2.24.0"
- resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.24.0.tgz#a5cb2ed89fedf8b59638dc83484eb0c8c35e1143"
- integrity sha512-DXrwuXTdVh3ycNCMYmWhUzn/gfqu9N0VzNnahjiDJvcyhfBy4gb59ncVZVxdp5XzBC77dCncu0daQgOkbvPwBw==
+"@typescript-eslint/experimental-utils@3.10.1":
+ version "3.10.1"
+ resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz#e179ffc81a80ebcae2ea04e0332f8b251345a686"
+ integrity sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==
dependencies:
"@types/json-schema" "^7.0.3"
- "@typescript-eslint/typescript-estree" "2.24.0"
+ "@typescript-eslint/types" "3.10.1"
+ "@typescript-eslint/typescript-estree" "3.10.1"
eslint-scope "^5.0.0"
+ eslint-utils "^2.0.0"
-"@typescript-eslint/parser@^2.14.0":
- version "2.24.0"
- resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.24.0.tgz#2cf0eae6e6dd44d162486ad949c126b887f11eb8"
- integrity sha512-H2Y7uacwSSg8IbVxdYExSI3T7uM1DzmOn2COGtCahCC3g8YtM1xYAPi2MAHyfPs61VKxP/J/UiSctcRgw4G8aw==
+"@typescript-eslint/experimental-utils@^4.0.1":
+ version "4.4.1"
+ resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.4.1.tgz#40613b9757fa0170de3e0043254dbb077cafac0c"
+ integrity sha512-Nt4EVlb1mqExW9cWhpV6pd1a3DkUbX9DeyYsdoeziKOpIJ04S2KMVDO+SEidsXRH/XHDpbzXykKcMTLdTXH6cQ==
+ dependencies:
+ "@types/json-schema" "^7.0.3"
+ "@typescript-eslint/scope-manager" "4.4.1"
+ "@typescript-eslint/types" "4.4.1"
+ "@typescript-eslint/typescript-estree" "4.4.1"
+ eslint-scope "^5.0.0"
+ eslint-utils "^2.0.0"
+
+"@typescript-eslint/parser@^v3.10.1":
+ version "3.10.1"
+ resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.10.1.tgz#1883858e83e8b442627e1ac6f408925211155467"
+ integrity sha512-Ug1RcWcrJP02hmtaXVS3axPPTTPnZjupqhgj+NnZ6BCkwSImWk/283347+x9wN+lqOdK9Eo3vsyiyDHgsmiEJw==
dependencies:
"@types/eslint-visitor-keys" "^1.0.0"
- "@typescript-eslint/experimental-utils" "2.24.0"
- "@typescript-eslint/typescript-estree" "2.24.0"
+ "@typescript-eslint/experimental-utils" "3.10.1"
+ "@typescript-eslint/types" "3.10.1"
+ "@typescript-eslint/typescript-estree" "3.10.1"
eslint-visitor-keys "^1.1.0"
-"@typescript-eslint/typescript-estree@2.24.0":
- version "2.24.0"
- resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.24.0.tgz#38bbc8bb479790d2f324797ffbcdb346d897c62a"
- integrity sha512-RJ0yMe5owMSix55qX7Mi9V6z2FDuuDpN6eR5fzRJrp+8in9UF41IGNQHbg5aMK4/PjVaEQksLvz0IA8n+Mr/FA==
+"@typescript-eslint/scope-manager@4.4.1":
+ version "4.4.1"
+ resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.4.1.tgz#d19447e60db2ce9c425898d62fa03b2cce8ea3f9"
+ integrity sha512-2oD/ZqD4Gj41UdFeWZxegH3cVEEH/Z6Bhr/XvwTtGv66737XkR4C9IqEkebCuqArqBJQSj4AgNHHiN1okzD/wQ==
dependencies:
+ "@typescript-eslint/types" "4.4.1"
+ "@typescript-eslint/visitor-keys" "4.4.1"
+
+"@typescript-eslint/types@3.10.1":
+ version "3.10.1"
+ resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.1.tgz#1d7463fa7c32d8a23ab508a803ca2fe26e758727"
+ integrity sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==
+
+"@typescript-eslint/types@4.4.1":
+ version "4.4.1"
+ resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.4.1.tgz#c507b35cf523bc7ba00aae5f75ee9b810cdabbc1"
+ integrity sha512-KNDfH2bCyax5db+KKIZT4rfA8rEk5N0EJ8P0T5AJjo5xrV26UAzaiqoJCxeaibqc0c/IvZxp7v2g3difn2Pn3w==
+
+"@typescript-eslint/typescript-estree@3.10.1":
+ version "3.10.1"
+ resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz#fd0061cc38add4fad45136d654408569f365b853"
+ integrity sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w==
+ dependencies:
+ "@typescript-eslint/types" "3.10.1"
+ "@typescript-eslint/visitor-keys" "3.10.1"
debug "^4.1.1"
- eslint-visitor-keys "^1.1.0"
glob "^7.1.6"
is-glob "^4.0.1"
lodash "^4.17.15"
- semver "^6.3.0"
+ semver "^7.3.2"
tsutils "^3.17.1"
+"@typescript-eslint/typescript-estree@4.4.1":
+ version "4.4.1"
+ resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.4.1.tgz#598f6de488106c2587d47ca2462c60f6e2797cb8"
+ integrity sha512-wP/V7ScKzgSdtcY1a0pZYBoCxrCstLrgRQ2O9MmCUZDtmgxCO/TCqOTGRVwpP4/2hVfqMz/Vw1ZYrG8cVxvN3g==
+ dependencies:
+ "@typescript-eslint/types" "4.4.1"
+ "@typescript-eslint/visitor-keys" "4.4.1"
+ debug "^4.1.1"
+ globby "^11.0.1"
+ is-glob "^4.0.1"
+ lodash "^4.17.15"
+ semver "^7.3.2"
+ tsutils "^3.17.1"
+
+"@typescript-eslint/visitor-keys@3.10.1":
+ version "3.10.1"
+ resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz#cd4274773e3eb63b2e870ac602274487ecd1e931"
+ integrity sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ==
+ dependencies:
+ eslint-visitor-keys "^1.1.0"
+
+"@typescript-eslint/visitor-keys@4.4.1":
+ version "4.4.1"
+ resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.4.1.tgz#1769dc7a9e2d7d2cfd3318b77ed8249187aed5c3"
+ integrity sha512-H2JMWhLaJNeaylSnMSQFEhT/S/FsJbebQALmoJxMPMxLtlVAMy2uJP/Z543n9IizhjRayLSqoInehCeNW9rWcw==
+ dependencies:
+ "@typescript-eslint/types" "4.4.1"
+ eslint-visitor-keys "^2.0.0"
+
"@webassemblyjs/ast@1.9.0":
version "1.9.0"
resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964"
@@ -10677,7 +10711,7 @@ eslint-plugin-graphql@^4.0.0:
lodash.flatten "^4.4.0"
lodash.without "^4.4.0"
-eslint-plugin-import@^2.20.2, eslint-plugin-import@^2.22.0:
+eslint-plugin-import@^2.20.2:
version "2.22.0"
resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz#92f7736fe1fde3e2de77623c838dd992ff5ffb7e"
integrity sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg==
@@ -10696,12 +10730,12 @@ eslint-plugin-import@^2.20.2, eslint-plugin-import@^2.22.0:
resolve "^1.17.0"
tsconfig-paths "^3.9.0"
-eslint-plugin-jest@^23.6.0:
- version "23.8.2"
- resolved "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-23.8.2.tgz#6f28b41c67ef635f803ebd9e168f6b73858eb8d4"
- integrity sha512-xwbnvOsotSV27MtAe7s8uGWOori0nUsrXh2f1EnpmXua8sDfY6VZhHAhHg2sqK7HBNycRQExF074XSZ7DvfoFg==
+eslint-plugin-jest@^24.1.0:
+ version "24.1.0"
+ resolved "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.1.0.tgz#6708037d7602e5288ce877fd0103f329dc978361"
+ integrity sha512-827YJ+E8B9PvXu/0eiVSNFfxxndbKv+qE/3GSMhdorCaeaOehtqHGX2YDW9B85TEOre9n/zscledkFW/KbnyGg==
dependencies:
- "@typescript-eslint/experimental-utils" "^2.5.0"
+ "@typescript-eslint/experimental-utils" "^4.0.1"
eslint-plugin-jsx-a11y@^6.2.1:
version "6.2.3"
@@ -10779,13 +10813,6 @@ eslint-scope@^5.0.0, eslint-scope@^5.1.0:
esrecurse "^4.1.0"
estraverse "^4.1.1"
-eslint-utils@^1.4.3:
- version "1.4.3"
- resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f"
- integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==
- dependencies:
- eslint-visitor-keys "^1.1.0"
-
eslint-utils@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd"
@@ -10798,6 +10825,11 @@ eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.2.0:
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.2.0.tgz#74415ac884874495f78ec2a97349525344c981fa"
integrity sha512-WFb4ihckKil6hu3Dp798xdzSfddwKKU3+nGniKF6HfeW6OLd2OUDEPP7TcHtB5+QXOKg2s6B2DaMPE1Nn/kxKQ==
+eslint-visitor-keys@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8"
+ integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
+
eslint@^7.1.0:
version "7.4.0"
resolved "https://registry.npmjs.org/eslint/-/eslint-7.4.0.tgz#4e35a2697e6c1972f9d6ef2b690ad319f80f206f"
@@ -12189,7 +12221,7 @@ globalthis@^1.0.0:
dependencies:
define-properties "^1.1.3"
-globby@11.0.1, globby@^11.0.0:
+globby@11.0.1, globby@^11.0.0, globby@^11.0.1:
version "11.0.1"
resolved "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357"
integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==
@@ -16932,9 +16964,9 @@ object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1:
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
object-path@^0.11.4:
- version "0.11.4"
- resolved "https://registry.npmjs.org/object-path/-/object-path-0.11.4.tgz#370ae752fbf37de3ea70a861c23bba8915691949"
- integrity sha1-NwrnUvvzfePqcKhhwju6iRVpGUk=
+ version "0.11.5"
+ resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.5.tgz#d4e3cf19601a5140a55a16ad712019a9c50b577a"
+ integrity sha512-jgSbThcoR/s+XumvGMTMf81QVBmah+/Q7K7YduKeKVWL7N111unR2d6pZZarSk6kY/caeNxUDyxOvMWyzoU2eg==
object-visit@^1.0.0:
version "1.0.1"
@@ -20339,10 +20371,10 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^3.0.0"
inherits "^2.0.1"
-rollup-plugin-dts@1.4.11:
- version "1.4.11"
- resolved "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-1.4.11.tgz#aedf0b7bb91d51e20b755e2c18e840edfc7af7a1"
- integrity sha512-yiScAMKgwH77b44a/IFGgjLsmwSlNfQhEM+eCb2uMrupQMPE1n/12wrnT431+v1u6wYMF1XuHqldh+v/7mTvYA==
+rollup-plugin-dts@1.4.13:
+ version "1.4.13"
+ resolved "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-1.4.13.tgz#4f086e84f4fdcc1f49160799ebc66f6b09db292b"
+ integrity sha512-7mxoQ6PcmCkBE5ZhrjGDL4k42XLy8BkSqpiRi1MipwiGs+7lwi4mQkp2afX+OzzLjJp/TGM8llfe8uayIUhPEw==
optionalDependencies:
"@babel/code-frame" "^7.10.4"
@@ -21732,10 +21764,10 @@ subscriptions-transport-ws@^0.9.11, subscriptions-transport-ws@^0.9.16:
symbol-observable "^1.0.4"
ws "^5.2.0"
-sucrase@^3.14.1:
- version "3.15.0"
- resolved "https://registry.npmjs.org/sucrase/-/sucrase-3.15.0.tgz#78596a78be7264a65b52ed8d873883413ef0220c"
- integrity sha512-05TJOUfMgckH7wKqfk/1p4G6q16nIeW/GHQwD44vkT0mQMqqzgfHCwkX3whNmwyOo7nVF0jDLwVu/qOBTtsscw==
+sucrase@^3.16.0:
+ version "3.16.0"
+ resolved "https://registry.npmjs.org/sucrase/-/sucrase-3.16.0.tgz#19b5b886ccca270dd5ca12ff060eeaf0b599735f"
+ integrity sha512-ovVuswxV5TayCPXfTk8bgBgk6uNRvsinIkEpq0J6zS1xXCx5N/LLGcbsKdRhqn/ToZylMX6+yXaR1LSn1I42Pg==
dependencies:
commander "^4.0.0"
glob "7.1.6"
@@ -22702,10 +22734,10 @@ typedarray@^0.0.6:
resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
-typescript@^3.9.3:
- version "3.9.7"
- resolved "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
- integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
+typescript@^4.0.3:
+ version "4.0.3"
+ resolved "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz#153bbd468ef07725c1df9c77e8b453f8d36abba5"
+ integrity sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==
ua-parser-js@^0.7.18:
version "0.7.21"