From e8a1c1afe21b78a8b299e1135af99cc766248479 Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Wed, 13 Oct 2021 16:24:07 +0200 Subject: [PATCH 001/196] Don't require a validation pattern for the jenkins base URL Signed-off-by: Dominik Henneke --- .changeset/flat-dodos-bake.md | 5 +++++ plugins/jenkins-backend/config.d.ts | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 .changeset/flat-dodos-bake.md diff --git a/.changeset/flat-dodos-bake.md b/.changeset/flat-dodos-bake.md new file mode 100644 index 0000000000..f6b52aac1a --- /dev/null +++ b/.changeset/flat-dodos-bake.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-jenkins-backend': patch +--- + +Don't require a validation pattern for the Jenkins base URL. diff --git a/plugins/jenkins-backend/config.d.ts b/plugins/jenkins-backend/config.d.ts index bfb519bb58..1fc41c1592 100644 --- a/plugins/jenkins-backend/config.d.ts +++ b/plugins/jenkins-backend/config.d.ts @@ -17,7 +17,6 @@ export interface Config { jenkins?: { /** * Default instance baseUrl, can be specified on a named instance called "default" - * @pattern "^https?://" */ baseUrl?: string; /** @@ -39,9 +38,6 @@ export interface Config { */ name: string; - /** - * @pattern "^https?://" - */ baseUrl: string; username: string; /** @visibility secret */ From 9c0d5a607d59d71dfe0bba313d7be5de83c6d125 Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Tue, 19 Oct 2021 10:19:48 +0100 Subject: [PATCH 002/196] refactor(`@backstage/plugin-azure-devops`): Consume re-exported types from `@backstage/plugin-azure-devops-backend`. Signed-off-by: Marley Powell --- plugins/azure-devops/package.json | 2 +- plugins/azure-devops/src/api/types.ts | 2 +- .../src/components/BuildTable/BuildTable.tsx | 33 ++++++++++--------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index c4aea032d2..f9dff7229c 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -31,12 +31,12 @@ "@backstage/core-components": "^0.7.0", "@backstage/core-plugin-api": "^0.1.10", "@backstage/errors": "^0.1.2", + "@backstage/plugin-azure-devops-backend": "^0.1.2", "@backstage/plugin-catalog-react": "^0.6.0", "@backstage/theme": "^0.2.11", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", - "azure-devops-node-api": "^11.0.1", "luxon": "^2.0.2", "react": "^16.13.1", "react-dom": "^16.13.1", diff --git a/plugins/azure-devops/src/api/types.ts b/plugins/azure-devops/src/api/types.ts index 1fbc34231e..46a9212aca 100644 --- a/plugins/azure-devops/src/api/types.ts +++ b/plugins/azure-devops/src/api/types.ts @@ -17,7 +17,7 @@ import { BuildResult, BuildStatus, -} from 'azure-devops-node-api/interfaces/BuildInterfaces'; +} from '@backstage/plugin-azure-devops-backend'; export type RepoBuild = { id?: number; diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx index bead424ffb..72aa2acda7 100644 --- a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx +++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx @@ -14,26 +14,27 @@ * limitations under the License. */ -import React from 'react'; -import { DateTime } from 'luxon'; -import { - Link, - Table, - TableColumn, - StatusError, - StatusOK, - StatusWarning, - StatusAborted, - StatusRunning, - StatusPending, - ResponseErrorPanel, -} from '@backstage/core-components'; import { Box, Typography } from '@material-ui/core'; -import { RepoBuild } from '../../api/types'; import { BuildResult, BuildStatus, -} from 'azure-devops-node-api/interfaces/BuildInterfaces'; +} from '@backstage/plugin-azure-devops-backend'; +import { + Link, + ResponseErrorPanel, + StatusAborted, + StatusError, + StatusOK, + StatusPending, + StatusRunning, + StatusWarning, + Table, + TableColumn, +} from '@backstage/core-components'; + +import { DateTime } from 'luxon'; +import React from 'react'; +import { RepoBuild } from '../../api/types'; const getBuildResultComponent = (result: number | undefined) => { switch (result) { From b85acc8c3539777d73501ec922e65bbf8dbadfe3 Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Tue, 19 Oct 2021 10:21:21 +0100 Subject: [PATCH 003/196] chore: Created changeset. Signed-off-by: Marley Powell --- .changeset/many-mayflies-notice.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/many-mayflies-notice.md diff --git a/.changeset/many-mayflies-notice.md b/.changeset/many-mayflies-notice.md new file mode 100644 index 0000000000..db058b909a --- /dev/null +++ b/.changeset/many-mayflies-notice.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-azure-devops': minor +--- + +refactor(`@backstage/plugin-azure-devops`): Consume re-exported types from `@backstage/plugin-azure-devops-backend`. From 0611f3b3e2d5e4e742abbf17d6e17c1bd0c95541 Mon Sep 17 00:00:00 2001 From: Matto Date: Mon, 27 Sep 2021 23:41:24 +1000 Subject: [PATCH 004/196] Read config from remote config server Signed-off-by: Matto --- .changeset/giant-years-help.md | 8 + packages/backend-common/src/config.ts | 10 +- packages/cli/package.json | 3 +- packages/cli/src/lib/config.ts | 16 +- packages/config-loader/api-report.md | 34 +++- packages/config-loader/package.json | 10 +- packages/config-loader/src/index.ts | 2 +- packages/config-loader/src/loader.test.ts | 125 +++++++++++- packages/config-loader/src/loader.ts | 232 +++++++++++++++++++--- packages/integration/api-report.md | 5 + packages/integration/src/index.ts | 1 + yarn.lock | 69 ++++++- 12 files changed, 461 insertions(+), 54 deletions(-) create mode 100644 .changeset/giant-years-help.md diff --git a/.changeset/giant-years-help.md b/.changeset/giant-years-help.md new file mode 100644 index 0000000000..f0ca12ef19 --- /dev/null +++ b/.changeset/giant-years-help.md @@ -0,0 +1,8 @@ +--- +'@backstage/backend-common': patch +'@backstage/cli': patch +'@backstage/config-loader': patch +'@backstage/integration': patch +--- + +Reading app config from a remote server diff --git a/packages/backend-common/src/config.ts b/packages/backend-common/src/config.ts index 96907870cb..afdb4fd0f7 100644 --- a/packages/backend-common/src/config.ts +++ b/packages/backend-common/src/config.ts @@ -19,7 +19,8 @@ import parseArgs from 'minimist'; import { Logger } from 'winston'; import { findPaths } from '@backstage/cli-common'; import { Config, ConfigReader, JsonValue } from '@backstage/config'; -import { loadConfig } from '@backstage/config-loader'; +import { ConfigTarget, loadConfig } from '@backstage/config-loader'; +import { isValidUrl } from '@backstage/integration'; class ObservableConfigProxy implements Config { private config: Config = new ConfigReader({}); @@ -117,7 +118,10 @@ export async function loadBackendConfig(options: { argv: string[]; }): Promise { const args = parseArgs(options.argv); - const configPaths: string[] = [args.config ?? []].flat(); + + const configTargets: ConfigTarget[] = [args.config ?? []] + .flat() + .map(arg => (isValidUrl(arg) ? { url: arg } : { path: resolvePath(arg) })); const config = new ObservableConfigProxy(options.logger); @@ -126,7 +130,7 @@ export async function loadBackendConfig(options: { const configs = await loadConfig({ configRoot: paths.targetRoot, - configPaths: configPaths.map(opt => resolvePath(opt)), + configTargets: configTargets, watch: { onChange(newConfigs) { options.logger.info( diff --git a/packages/cli/package.json b/packages/cli/package.json index 0dc4dc3cf7..47475780a6 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -31,8 +31,10 @@ "@babel/core": "^7.4.4", "@babel/plugin-transform-modules-commonjs": "^7.4.4", "@backstage/cli-common": "^0.1.3", + "@backstage/integration": "^0.6.5", "@backstage/config": "^0.1.10", "@backstage/config-loader": "^0.6.8", + "@backstage/backend-common": "^0.9.4", "@hot-loader/react-dom": "^16.13.0", "@lerna/package-graph": "^4.0.0", "@lerna/project": "^4.0.0", @@ -117,7 +119,6 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-common": "^0.9.4", "@backstage/config": "^0.1.10", "@backstage/core-components": "^0.5.0", "@backstage/core-plugin-api": "^0.1.8", diff --git a/packages/cli/src/lib/config.ts b/packages/cli/src/lib/config.ts index a93f040122..218771beb6 100644 --- a/packages/cli/src/lib/config.ts +++ b/packages/cli/src/lib/config.ts @@ -14,9 +14,14 @@ * limitations under the License. */ -import { loadConfig, loadConfigSchema } from '@backstage/config-loader'; +import { + ConfigTarget, + loadConfig, + loadConfigSchema, +} from '@backstage/config-loader'; import { ConfigReader } from '@backstage/config'; import { paths } from './paths'; +import { isValidUrl } from '@backstage/integration'; type Options = { args: string[]; @@ -26,7 +31,12 @@ type Options = { }; export async function loadCliConfig(options: Options) { - const configPaths = options.args.map(arg => paths.resolveTarget(arg)); + const configTargets: ConfigTarget[] = []; + options.args.forEach(arg => { + if (!isValidUrl(arg)) { + configTargets.push({ path: paths.resolveTarget(arg) }); + } + }); // Consider all packages in the monorepo when loading in config const { Project } = require('@lerna/project'); @@ -46,7 +56,7 @@ export async function loadCliConfig(options: Options) { ? async name => process.env[name] || 'x' : undefined, configRoot: paths.targetRoot, - configPaths, + configTargets: configTargets, }); // printing to stderr to not clobber stdout in case the cli command diff --git a/packages/config-loader/api-report.md b/packages/config-loader/api-report.md index bb3ae50d23..bb36bbe750 100644 --- a/packages/config-loader/api-report.md +++ b/packages/config-loader/api-report.md @@ -23,6 +23,17 @@ export type ConfigSchemaProcessingOptions = { withFilteredKeys?: boolean; }; +// Warning: (ae-missing-release-tag) "ConfigTarget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ConfigTarget = + | { + path: string; + } + | { + url: string; + }; + // @public export type ConfigVisibility = 'frontend' | 'backend' | 'secret'; @@ -35,13 +46,11 @@ export function loadConfig(options: LoadConfigOptions): Promise; // @public (undocumented) export type LoadConfigOptions = { configRoot: string; - configPaths: string[]; + configTargets: ConfigTarget[]; env?: string; experimentalEnvFunc?: EnvFunc; - watch?: { - onChange: (configs: AppConfig[]) => void; - stopSignal?: Promise; - }; + remote?: Remote; + watch?: Watch; }; // @public @@ -66,6 +75,13 @@ export function readEnvConfig(env: { [name: string]: string | undefined; }): AppConfig[]; +// Warning: (ae-missing-release-tag) "Remote" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type Remote = { + reloadIntervalSeconds: number; +}; + // @public export type TransformFunc = ( value: T, @@ -73,4 +89,12 @@ export type TransformFunc = ( visibility: ConfigVisibility; }, ) => T | undefined; + +// Warning: (ae-missing-release-tag) "Watch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type Watch = { + onChange: (configs: AppConfig[]) => void; + stopSignal?: Promise; +}; ``` diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 2f2878799e..d20c77bedc 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -30,8 +30,10 @@ "clean": "backstage-cli clean" }, "dependencies": { + "@backstage/integration": "^0.6.5", "@backstage/cli-common": "^0.1.3", "@backstage/config": "^0.1.9", + "@backstage/backend-common": "^0.9.4", "@types/json-schema": "^7.0.6", "ajv": "^7.0.3", "chokidar": "^3.5.2", @@ -39,8 +41,10 @@ "json-schema": "^0.3.0", "json-schema-merge-allof": "^0.8.1", "typescript-json-schema": "^0.50.1", + "uuid": "^8.3.2", "yaml": "^1.9.2", - "yup": "^0.29.3" + "yup": "^0.29.3", + "node-fetch": "2.6.5" }, "devDependencies": { "@types/jest": "^26.0.7", @@ -48,7 +52,9 @@ "@types/mock-fs": "^4.10.0", "@types/node": "^14.14.32", "@types/yup": "^0.29.8", - "mock-fs": "^5.1.0" + "mock-fs": "^5.1.0", + "fetch-mock-jest": "1.5.1", + "fetch-mock": "^9.11.0" }, "files": [ "dist" diff --git a/packages/config-loader/src/index.ts b/packages/config-loader/src/index.ts index 0e66c59ec4..e0eafc00bc 100644 --- a/packages/config-loader/src/index.ts +++ b/packages/config-loader/src/index.ts @@ -30,4 +30,4 @@ export type { TransformFunc, } from './lib'; export { loadConfig } from './loader'; -export type { LoadConfigOptions } from './loader'; +export type { ConfigTarget, LoadConfigOptions, Watch, Remote } from './loader'; diff --git a/packages/config-loader/src/loader.test.ts b/packages/config-loader/src/loader.test.ts index 3f51e76936..190904a870 100644 --- a/packages/config-loader/src/loader.test.ts +++ b/packages/config-loader/src/loader.test.ts @@ -18,12 +18,33 @@ import { AppConfig } from '@backstage/config'; import { loadConfig } from './loader'; import mockFs from 'mock-fs'; import fs from 'fs-extra'; +import { v4 as uuidv4 } from 'uuid'; + +const fetchMock = require('fetch-mock').sandbox(); +const nodeFetch = require('node-fetch'); + +nodeFetch.default = fetchMock; describe('loadConfig', () => { beforeEach(() => { process.env.MY_SECRET = 'is-secret'; process.env.SUBSTITUTE_ME = 'substituted'; + fetchMock.mock( + { + url: 'https://some.domain.io/app-config.yaml', + method: 'GET', + }, + { + body: `app: + title: Remote Example App + sessionKey: 'abc123' + escaped: \$\${Escaped} + `, + headers: { ETag: uuidv4().toString() }, + }, + ); + mockFs({ '/root/app-config.yaml': ` app: @@ -66,6 +87,7 @@ describe('loadConfig', () => { }); afterEach(() => { + fetchMock.restore(); mockFs.restore(); }); @@ -73,7 +95,7 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', - configPaths: [], + configTargets: [], env: 'production', }), ).resolves.toEqual([ @@ -90,11 +112,37 @@ describe('loadConfig', () => { ]); }); + it('load config from remote path', async () => { + const configUrl = 'https://some.domain.io/app-config.yaml'; + + await expect( + loadConfig({ + configRoot: '/root', + configTargets: [{ url: configUrl }], + env: 'production', + remote: { + reloadIntervalSeconds: 30, + }, + }), + ).resolves.toEqual([ + { + context: configUrl, + data: { + app: { + title: 'Remote Example App', + sessionKey: 'abc123', + escaped: '${Escaped}', + }, + }, + }, + ]); + }); + it('loads config with secrets', async () => { await expect( loadConfig({ configRoot: '/root', - configPaths: ['/root/app-config.yaml'], + configTargets: [{ path: '/root/app-config.yaml' }], env: 'production', }), ).resolves.toEqual([ @@ -115,9 +163,9 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', - configPaths: [ - '/root/app-config.yaml', - '/root/app-config.development.yaml', + configTargets: [ + { path: '/root/app-config.yaml' }, + { path: '/root/app-config.development.yaml' }, ], env: 'development', }), @@ -155,7 +203,7 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', - configPaths: ['/root/app-config.substitute.yaml'], + configTargets: [{ path: '/root/app-config.substitute.yaml' }], env: 'development', }), ).resolves.toEqual([ @@ -180,7 +228,7 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', - configPaths: [], + configTargets: [], watch: { onChange: onChange.resolve, stopSignal: stopSignal.promise, @@ -218,12 +266,73 @@ describe('loadConfig', () => { stopSignal.resolve(); }); + it('watches remote config urls', async () => { + const onChange = defer(); + const stopSignal = defer(); + + const configUrl = 'https://some.domain.io/app-config.yaml'; + await expect( + loadConfig({ + configRoot: '/root', + configTargets: [{ url: configUrl }], + watch: { + onChange: onChange.resolve, + stopSignal: stopSignal.promise, + }, + remote: { + reloadIntervalSeconds: 1, + }, + }), + ).resolves.toEqual([ + { + context: configUrl, + data: { + app: { + title: 'Remote Example App', + sessionKey: 'abc123', + escaped: '${Escaped}', + }, + }, + }, + ]); + + fetchMock.mock( + { + url: 'https://some.domain.io/app-config.yaml', + }, + { + body: `app: + title: NEW ReMOTe ExaMPLe App + sessionKey: 'abc123' + escaped: \$\${Escaped} + `, + headers: { ETag: uuidv4().toString() }, + }, + { overwriteRoutes: true }, + ); + + await expect(onChange.promise).resolves.toEqual([ + { + context: configUrl, + data: { + app: { + title: 'NEW ReMOTe ExaMPLe App', + sessionKey: 'abc123', + escaped: '${Escaped}', + }, + }, + }, + ]); + + stopSignal.resolve(); + }); + it('stops watching config files', async () => { const stopSignal = defer(); await loadConfig({ configRoot: '/root', - configPaths: [], + configTargets: [], watch: { onChange: () => { expect('not').toBe('called'); diff --git a/packages/config-loader/src/loader.ts b/packages/config-loader/src/loader.ts index 315b44c4ee..fb8d0aab33 100644 --- a/packages/config-loader/src/loader.ts +++ b/packages/config-loader/src/loader.ts @@ -17,23 +17,67 @@ import fs from 'fs-extra'; import yaml from 'yaml'; import chokidar from 'chokidar'; -import { resolve as resolvePath, dirname, isAbsolute, basename } from 'path'; +import { basename, dirname, isAbsolute, resolve as resolvePath } from 'path'; import { AppConfig } from '@backstage/config'; import { applyConfigTransforms, - readEnvConfig, createIncludeTransform, createSubstitutionTransform, + EnvFunc, + readEnvConfig, } from './lib'; -import { EnvFunc } from './lib/transform/types'; +import fetch from 'node-fetch'; +import { isValidUrl } from '@backstage/integration'; +export type ConfigTarget = { path: string } | { url: string }; + +export type Watch = { + /** + * A listener that is called when a config file is changed. + */ + onChange: (configs: AppConfig[]) => void; + + /** + * An optional signal that stops the watcher once the promise resolves. + */ + stopSignal?: Promise; +}; + +export type Remote = { + /** + * An optional remote config reloading period, in seconds + */ + reloadIntervalSeconds: number; +}; + +export type RemoteConfigProp = { + /** + * URL of the remote config + */ + url: string; + + /** + * Contents of the remote config + */ + content: string | null; + + /** + * An optional new ETag header value. Used when checking for updated config. + */ + newETag?: string; + + /** + * An optional old ETag header value. Used when checking for updated config + */ + oldETag?: string; +}; /** @public */ export type LoadConfigOptions = { // The root directory of the config loading context. Used to find default configs. configRoot: string; - // Absolute paths to load config files from. Configs from earlier paths have lower priority. - configPaths: string[]; + // Paths to load config files from. Configs from earlier paths have lower priority. + configTargets: ConfigTarget[]; /** @deprecated This option has been removed */ env?: string; @@ -45,22 +89,19 @@ export type LoadConfigOptions = { */ experimentalEnvFunc?: EnvFunc; + /** + * An optional remote config + */ + remote?: Remote; + /** * An optional configuration that enables watching of config files. */ - watch?: { - /** - * A listener that is called when a config file is changed. - */ - onChange: (configs: AppConfig[]) => void; - - /** - * An optional signal that stops the watcher once the promise resolves. - */ - stopSignal?: Promise; - }; + watch?: Watch; }; +const HTTP_RESPONSE_HEADER_ETAG = 'ETag'; + /** * Load configuration data. * @@ -69,12 +110,30 @@ export type LoadConfigOptions = { export async function loadConfig( options: LoadConfigOptions, ): Promise { - const { configRoot, experimentalEnvFunc: envFunc, watch } = options; - const configPaths = options.configPaths.slice(); + const { configRoot, experimentalEnvFunc: envFunc, watch, remote } = options; + + const configPaths: string[] = options.configTargets + .slice() + .filter((e): e is { path: string } => e.hasOwnProperty('path')) + .map(configTarget => configTarget.path); + + let configUrls: string[] = options.configTargets + .slice() + .filter((e): e is { url: string } => e.hasOwnProperty('url')) + .map(configTarget => configTarget.url); + + const remoteConfigProps: RemoteConfigProp[] = []; + + if (remote === undefined && configUrls.length > 0) { + console.warn( + `Remote config detected, however, this feature is turned off. Remote config will be ignored.`, + ); + configUrls = []; + } // If no paths are provided, we default to reading // `app-config.yaml` and, if it exists, `app-config.local.yaml` - if (configPaths.length === 0) { + if (configPaths.length === 0 && configUrls.length === 0) { configPaths.push(resolvePath(configRoot, 'app-config.yaml')); const localConfig = resolvePath(configRoot, 'app-config.local.yaml'); @@ -99,6 +158,7 @@ export async function loadConfig( const input = yaml.parse(await readFile(configPath)); const substitutionTransform = createSubstitutionTransform(env); + const data = await applyConfigTransforms(dir, input, [ createIncludeTransform(env, readFile, substitutionTransform), substitutionTransform, @@ -110,7 +170,56 @@ export async function loadConfig( return configs; }; - let fileConfigs; + const loadRemoteConfigFiles = async () => { + const configs: AppConfig[] = []; + + const readConfigFromUrl = async (remoteConfigProp: RemoteConfigProp) => { + const response = await fetch(remoteConfigProp.url); + if (!response.ok) { + throw new Error( + `Could not read config file at ${remoteConfigProp.url}`, + ); + } + + remoteConfigProp.oldETag = remoteConfigProp.newETag ?? undefined; + remoteConfigProp.newETag = + response.headers.get(HTTP_RESPONSE_HEADER_ETAG) ?? undefined; + remoteConfigProp.content = await response.text(); + + return remoteConfigProp; + }; + + for (let i = 0; i < configUrls.length; i++) { + const remoteConfigProp = await readConfigFromUrl({ + url: configUrls[i], + content: null, + }); + + if (!isValidUrl(remoteConfigProp.url)) { + throw new Error( + `Config load path is not valid: '${remoteConfigProp.url}'`, + ); + } + + const dir = configRoot; + if (!remoteConfigProp.content) { + throw new Error(`Config is not valid`); + } + const input = yaml.parse(remoteConfigProp.content); + const substitutionTransform = createSubstitutionTransform(env); + const data = await applyConfigTransforms(dir, input, [ + substitutionTransform, + ]); + + configs.push({ data, context: remoteConfigProp.url }); + + remoteConfigProps.push(remoteConfigProp); + } + + return configs; + }; + + let fileConfigs: AppConfig[]; try { fileConfigs = await loadConfigFiles(); } catch (error) { @@ -119,15 +228,23 @@ export async function loadConfig( ); } + let remoteConfigs: AppConfig[] = []; + if (remote) { + try { + remoteConfigs = await loadRemoteConfigFiles(); + } catch (error) { + throw new Error(`Failed to read remote configuration file, ${error}`); + } + } + const envConfigs = await readEnvConfig(process.env); - // Set up config file watching if requested by the caller - if (watch) { - let currentSerializedConfig = JSON.stringify(fileConfigs); - + const watchConfigFile = (watchProp: Watch) => { const watcher = chokidar.watch(configPaths, { usePolling: process.env.NODE_ENV === 'test', }); + + let currentSerializedConfig = JSON.stringify(fileConfigs); watcher.on('change', async () => { try { const newConfigs = await loadConfigFiles(); @@ -138,18 +255,77 @@ export async function loadConfig( } currentSerializedConfig = newSerializedConfig; - watch.onChange([...newConfigs, ...envConfigs]); + watchProp.onChange([...remoteConfigs, ...newConfigs, ...envConfigs]); } catch (error) { console.error(`Failed to reload configuration files, ${error}`); } }); - if (watch.stopSignal) { - watch.stopSignal.then(() => { + if (watchProp.stopSignal) { + watchProp.stopSignal.then(() => { watcher.close(); }); } + }; + + const watchRemoteConfig = (watchProp: Watch, remoteProp: Remote) => { + const hasConfigChanged = async (remoteConfigProp: RemoteConfigProp) => { + const requestProps = { method: 'HEAD' }; + const { headers } = await fetch(remoteConfigProp.url, requestProps); + remoteConfigProp.oldETag = remoteConfigProp.newETag ?? undefined; + remoteConfigProp.newETag = + headers.get(HTTP_RESPONSE_HEADER_ETAG) ?? undefined; + + return ( + remoteConfigProp.oldETag !== undefined && + remoteConfigProp.newETag !== undefined && + remoteConfigProp.oldETag !== remoteConfigProp.newETag + ); + }; + + let handle: NodeJS.Timeout | undefined; + try { + handle = setInterval(async () => { + console.info(`Checking for config update`); + for (const remoteConfigProp of remoteConfigProps) { + if (await hasConfigChanged(remoteConfigProp)) { + console.info(`Remote config change, reloading config ...`); + const newRemoteConfigs = await loadRemoteConfigFiles(); + watchProp.onChange([ + ...newRemoteConfigs, + ...fileConfigs, + ...envConfigs, + ]); + console.info(`Remote config reloaded`); + break; + } + } + }, remoteProp.reloadIntervalSeconds * 1000); + } catch (error) { + console.error(`Failed to reload configuration files, ${error}`); + } + + if (watchProp.stopSignal) { + watchProp.stopSignal.then(() => { + if (handle !== undefined) { + console.info(`Stopping remote config watch`); + clearInterval(handle); + handle = undefined; + } + }); + } + }; + + // Set up config file watching if requested by the caller + if (watch) { + watchConfigFile(watch); } - return [...fileConfigs, ...envConfigs]; + if (watch && remote) { + watchRemoteConfig(watch, remote); + } + + return remote + ? [...remoteConfigs, ...fileConfigs, ...envConfigs] + : [...fileConfigs, ...envConfigs]; } diff --git a/packages/integration/api-report.md b/packages/integration/api-report.md index 01f84383fe..e07f61eea6 100644 --- a/packages/integration/api-report.md +++ b/packages/integration/api-report.md @@ -347,6 +347,11 @@ export type GoogleGcsIntegrationConfig = { privateKey?: string; }; +// Warning: (ae-missing-release-tag) "isValidUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export function isValidUrl(url: string): boolean; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // Warning: (ae-missing-release-tag) "readAwsS3IntegrationConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/packages/integration/src/index.ts b/packages/integration/src/index.ts index 3d5f336e8d..b8992f52d2 100644 --- a/packages/integration/src/index.ts +++ b/packages/integration/src/index.ts @@ -27,6 +27,7 @@ export * from './gitlab'; export * from './googleGcs'; export * from './awsS3'; export { defaultScmResolveUrl } from './helpers'; +export { isValidUrl } from './helpers'; export { ScmIntegrations } from './ScmIntegrations'; export type { ScmIntegration, ScmIntegrationsGroup } from './types'; export type { ScmIntegrationRegistry } from './registry'; diff --git a/yarn.lock b/yarn.lock index 7657498ff7..87991e1bb5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11336,6 +11336,11 @@ core-js@^2.5.0: resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== +core-js@^3.0.0: + version "3.18.3" + resolved "https://registry.npmjs.org/core-js/-/core-js-3.18.3.tgz#86a0bba2d8ec3df860fefcc07a8d119779f01509" + integrity sha512-tReEhtMReZaPFVw7dajMx0vlsz3oOb8ajgPoHVYGxr8ErnZ6PcYEvvmjGmXlfpnxpkYSdOQttjB+MvVbCGfvLw== + core-js@^3.0.4, core-js@^3.6.5, core-js@^3.8.2: version "3.15.0" resolved "https://registry.npmjs.org/core-js/-/core-js-3.15.0.tgz#db9554ebce0b6fd90dc9b1f2465c841d2d055044" @@ -14037,6 +14042,29 @@ fetch-blob@2.1.2: resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-2.1.2.tgz#a7805db1361bd44c1ef62bb57fb5fe8ea173ef3c" integrity sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow== +fetch-mock-jest@1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/fetch-mock-jest/-/fetch-mock-jest-1.5.1.tgz#0e13df990d286d9239e284f12b279ed509bf53cd" + integrity sha512-+utwzP8C+Pax1GSka3nFXILWMY3Er2L+s090FOgqVNrNCPp0fDqgXnAHAJf12PLHi0z4PhcTaZNTz8e7K3fjqQ== + dependencies: + fetch-mock "^9.11.0" + +fetch-mock@^9.11.0: + version "9.11.0" + resolved "https://registry.npmjs.org/fetch-mock/-/fetch-mock-9.11.0.tgz#371c6fb7d45584d2ae4a18ee6824e7ad4b637a3f" + integrity sha512-PG1XUv+x7iag5p/iNHD4/jdpxL9FtVSqRMUQhPab4hVDt80T1MH5ehzVrL2IdXO9Q2iBggArFvPqjUbHFuI58Q== + dependencies: + "@babel/core" "^7.0.0" + "@babel/runtime" "^7.0.0" + core-js "^3.0.0" + debug "^4.1.1" + glob-to-regexp "^0.4.0" + is-subset "^0.1.1" + lodash.isequal "^4.5.0" + path-to-regexp "^2.2.1" + querystring "^0.2.0" + whatwg-url "^6.5.0" + fetch-readablestream@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/fetch-readablestream/-/fetch-readablestream-0.2.0.tgz#eaa6d1a76b12de2d4731a343393c6ccdcfe2c795" @@ -14867,7 +14895,7 @@ glob-to-regexp@^0.3.0: resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -glob-to-regexp@^0.4.1: +glob-to-regexp@^0.4.0, glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== @@ -16851,6 +16879,11 @@ is-subdir@^1.1.1: dependencies: better-path-resolve "1.0.0" +is-subset@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" + integrity sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY= + is-svg@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" @@ -18719,7 +18752,7 @@ lodash.isempty@^4.4.0: resolved "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" integrity sha1-b4bL7di+TsmHvpqvM8loTbGzHn4= -lodash.isequal@^4.0.0: +lodash.isequal@^4.0.0, lodash.isequal@^4.5.0: version "4.5.0" resolved "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= @@ -20192,6 +20225,13 @@ node-fetch@2.6.1, node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1: resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== +node-fetch@2.6.5: + version "2.6.5" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz#42735537d7f080a7e5f78b6c549b7146be1742fd" + integrity sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ== + dependencies: + whatwg-url "^5.0.0" + node-forge@^0.10.0: version "0.10.0" resolved "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" @@ -21526,6 +21566,11 @@ path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" +path-to-regexp@^2.2.1: + version "2.4.0" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz#35ce7f333d5616f1c1e1bfe266c3aba2e5b2e704" + integrity sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w== + path-type@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -26576,6 +26621,11 @@ tr46@^2.0.2: dependencies: punycode "^2.1.1" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + "traverse@>=0.3.0 <0.4": version "0.3.9" resolved "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" @@ -27702,6 +27752,11 @@ web-namespaces@^1.0.0: resolved "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" @@ -27954,7 +28009,15 @@ whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-url@^6.4.1: +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +whatwg-url@^6.4.1, whatwg-url@^6.5.0: version "6.5.0" resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== From befe7d122f5095122740d0878058ad684219c182 Mon Sep 17 00:00:00 2001 From: Matto Date: Tue, 19 Oct 2021 23:18:19 +1100 Subject: [PATCH 005/196] Added documentation Signed-off-by: Matto --- docs/conf/writing.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/docs/conf/writing.md b/docs/conf/writing.md index 4288d23b17..86b844484b 100644 --- a/docs/conf/writing.md +++ b/docs/conf/writing.md @@ -1,7 +1,6 @@ --- -id: writing -title: Writing Backstage Configuration Files -description: Documentation on Writing Backstage Configuration Files +id: writing title: Writing Backstage Configuration Files description: Documentation on Writing +Backstage Configuration Files --- ## File Format @@ -67,13 +66,15 @@ production build. ## Configuration Files -It is possible to have multiple configuration files, both to support different -environments, but also to define configuration that is local to specific -packages. The configuration files to load are selected using a `--config ` -flag, and it is possible to load any number of files. Paths are relative to the -working directory of the executed process, for example `package/backend`. This -means that to select a config file in the repo root when running the backend, -you would use `--config ../../my-config.yaml`. +It is possible to have multiple configuration files (bundled and/or remote), +both to support different environments, but also to define configuration that is +local to specific packages. The configuration files to load are selected using a +`--config ` flag, and it is possible to load any number of +files. Paths are relative to the working directory of the executed process, for +example `package/backend`. This means that to select a config file in the repo +root when running the backend, you would use `--config ../../my-config.yaml`, +and for config file on a config server you would use +`--config https://some.domain.io/app-config.yaml` If no `config` flags are specified, the default behavior is to load `app-config.yaml` and, if it exists, `app-config.local.yaml` from the repo root. @@ -85,7 +86,7 @@ are NOT loaded. To include them you need to explicitly include them with a flag, for example: ```shell -yarn start --config ../../app-config.yaml --config ../../app-config.staging.yaml +yarn start --config ../../app-config.yaml --config ../../app-config.staging.yaml --config https://some.domain.io/app-config.yaml ``` All loaded configuration files are merged together using the following rules: From 74ceaa64175d5eecd33577a735ce447081b7f0bc Mon Sep 17 00:00:00 2001 From: Matto Date: Thu, 21 Oct 2021 18:20:36 +1100 Subject: [PATCH 006/196] Remove the dependency on Etag headers for reloading config Signed-off-by: Matto --- docs/conf/writing.md | 5 +- packages/cli/package.json | 2 +- packages/config-loader/package.json | 4 +- packages/config-loader/src/loader.test.ts | 79 +++++++++-------- packages/config-loader/src/loader.ts | 102 ++++++---------------- yarn.lock | 7 -- 6 files changed, 73 insertions(+), 126 deletions(-) diff --git a/docs/conf/writing.md b/docs/conf/writing.md index 86b844484b..a0e29820ed 100644 --- a/docs/conf/writing.md +++ b/docs/conf/writing.md @@ -1,6 +1,7 @@ --- -id: writing title: Writing Backstage Configuration Files description: Documentation on Writing -Backstage Configuration Files +id: writing +title: Writing Backstage Configuration Files +description: Documentation on Writing Backstage Configuration Files --- ## File Format diff --git a/packages/cli/package.json b/packages/cli/package.json index 47475780a6..519b45a5ad 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -34,7 +34,6 @@ "@backstage/integration": "^0.6.5", "@backstage/config": "^0.1.10", "@backstage/config-loader": "^0.6.8", - "@backstage/backend-common": "^0.9.4", "@hot-loader/react-dom": "^16.13.0", "@lerna/package-graph": "^4.0.0", "@lerna/project": "^4.0.0", @@ -119,6 +118,7 @@ "yn": "^4.0.0" }, "devDependencies": { + "@backstage/backend-common": "^0.9.4", "@backstage/config": "^0.1.10", "@backstage/core-components": "^0.5.0", "@backstage/core-plugin-api": "^0.1.8", diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index d20c77bedc..80e4121971 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -41,7 +41,6 @@ "json-schema": "^0.3.0", "json-schema-merge-allof": "^0.8.1", "typescript-json-schema": "^0.50.1", - "uuid": "^8.3.2", "yaml": "^1.9.2", "yup": "^0.29.3", "node-fetch": "2.6.5" @@ -53,8 +52,7 @@ "@types/node": "^14.14.32", "@types/yup": "^0.29.8", "mock-fs": "^5.1.0", - "fetch-mock-jest": "1.5.1", - "fetch-mock": "^9.11.0" + "msw": "^0.29.0" }, "files": [ "dist" diff --git a/packages/config-loader/src/loader.test.ts b/packages/config-loader/src/loader.test.ts index 190904a870..e8b5a8828b 100644 --- a/packages/config-loader/src/loader.test.ts +++ b/packages/config-loader/src/loader.test.ts @@ -18,33 +18,47 @@ import { AppConfig } from '@backstage/config'; import { loadConfig } from './loader'; import mockFs from 'mock-fs'; import fs from 'fs-extra'; -import { v4 as uuidv4 } from 'uuid'; - -const fetchMock = require('fetch-mock').sandbox(); -const nodeFetch = require('node-fetch'); - -nodeFetch.default = fetchMock; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; describe('loadConfig', () => { + const server = setupServer(); + const initialLoaderHandler = rest.get( + `https://some.domain.io/app-config.yaml`, + (_req, res, ctx) => { + return res( + ctx.body( + `app: + title: Remote Example App + sessionKey: 'abc123' + escaped: \$\${Escaped} + `, + ), + ); + }, + ); + + const reloadHandler = rest.get( + `https://some.domain.io/app-config.yaml`, + (_req, res, ctx) => { + return res( + ctx.body( + `app: + title: NEW ReMOTe ExaMPLe App + sessionKey: 'abc123' + escaped: \$\${Escaped} + `, + ), + ); + }, + ); + + beforeAll(() => server.listen()); + beforeEach(() => { process.env.MY_SECRET = 'is-secret'; process.env.SUBSTITUTE_ME = 'substituted'; - fetchMock.mock( - { - url: 'https://some.domain.io/app-config.yaml', - method: 'GET', - }, - { - body: `app: - title: Remote Example App - sessionKey: 'abc123' - escaped: \$\${Escaped} - `, - headers: { ETag: uuidv4().toString() }, - }, - ); - mockFs({ '/root/app-config.yaml': ` app: @@ -87,10 +101,12 @@ describe('loadConfig', () => { }); afterEach(() => { - fetchMock.restore(); mockFs.restore(); + server.resetHandlers(); }); + afterAll(() => server.close()); + it('load config from default path', async () => { await expect( loadConfig({ @@ -113,6 +129,8 @@ describe('loadConfig', () => { }); it('load config from remote path', async () => { + server.use(initialLoaderHandler); + const configUrl = 'https://some.domain.io/app-config.yaml'; await expect( @@ -267,6 +285,8 @@ describe('loadConfig', () => { }); it('watches remote config urls', async () => { + server.use(initialLoaderHandler); + const onChange = defer(); const stopSignal = defer(); @@ -296,20 +316,7 @@ describe('loadConfig', () => { }, ]); - fetchMock.mock( - { - url: 'https://some.domain.io/app-config.yaml', - }, - { - body: `app: - title: NEW ReMOTe ExaMPLe App - sessionKey: 'abc123' - escaped: \$\${Escaped} - `, - headers: { ETag: uuidv4().toString() }, - }, - { overwriteRoutes: true }, - ); + server.use(reloadHandler); await expect(onChange.promise).resolves.toEqual([ { diff --git a/packages/config-loader/src/loader.ts b/packages/config-loader/src/loader.ts index fb8d0aab33..b853d30d8a 100644 --- a/packages/config-loader/src/loader.ts +++ b/packages/config-loader/src/loader.ts @@ -50,27 +50,6 @@ export type Remote = { reloadIntervalSeconds: number; }; -export type RemoteConfigProp = { - /** - * URL of the remote config - */ - url: string; - - /** - * Contents of the remote config - */ - content: string | null; - - /** - * An optional new ETag header value. Used when checking for updated config. - */ - newETag?: string; - - /** - * An optional old ETag header value. Used when checking for updated config - */ - oldETag?: string; -}; /** @public */ export type LoadConfigOptions = { // The root directory of the config loading context. Used to find default configs. @@ -100,8 +79,6 @@ export type LoadConfigOptions = { watch?: Watch; }; -const HTTP_RESPONSE_HEADER_ETAG = 'ETag'; - /** * Load configuration data. * @@ -117,18 +94,15 @@ export async function loadConfig( .filter((e): e is { path: string } => e.hasOwnProperty('path')) .map(configTarget => configTarget.path); - let configUrls: string[] = options.configTargets + const configUrls: string[] = options.configTargets .slice() .filter((e): e is { url: string } => e.hasOwnProperty('url')) .map(configTarget => configTarget.url); - const remoteConfigProps: RemoteConfigProp[] = []; - if (remote === undefined && configUrls.length > 0) { - console.warn( + throw new Error( `Remote config detected, however, this feature is turned off. Remote config will be ignored.`, ); - configUrls = []; } // If no paths are provided, we default to reading @@ -173,47 +147,32 @@ export async function loadConfig( const loadRemoteConfigFiles = async () => { const configs: AppConfig[] = []; - const readConfigFromUrl = async (remoteConfigProp: RemoteConfigProp) => { - const response = await fetch(remoteConfigProp.url); + const readConfigFromUrl = async (url: string) => { + const response = await fetch(url); if (!response.ok) { - throw new Error( - `Could not read config file at ${remoteConfigProp.url}`, - ); + throw new Error(`Could not read config file at ${url}`); } - remoteConfigProp.oldETag = remoteConfigProp.newETag ?? undefined; - remoteConfigProp.newETag = - response.headers.get(HTTP_RESPONSE_HEADER_ETAG) ?? undefined; - remoteConfigProp.content = await response.text(); - - return remoteConfigProp; + return await response.text(); }; for (let i = 0; i < configUrls.length; i++) { - const remoteConfigProp = await readConfigFromUrl({ - url: configUrls[i], - content: null, - }); - - if (!isValidUrl(remoteConfigProp.url)) { - throw new Error( - `Config load path is not valid: '${remoteConfigProp.url}'`, - ); + const configUrl = configUrls[i]; + if (!isValidUrl(configUrl)) { + throw new Error(`Config load path is not valid: '${configUrl}'`); } - const dir = configRoot; - if (!remoteConfigProp.content) { + const remoteConfigContent = await readConfigFromUrl(configUrl); + if (!remoteConfigContent) { throw new Error(`Config is not valid`); } - const input = yaml.parse(remoteConfigProp.content); + const configYaml = yaml.parse(remoteConfigContent); const substitutionTransform = createSubstitutionTransform(env); - const data = await applyConfigTransforms(dir, input, [ + const data = await applyConfigTransforms(configRoot, configYaml, [ substitutionTransform, ]); - configs.push({ data, context: remoteConfigProp.url }); - - remoteConfigProps.push(remoteConfigProp); + configs.push({ data, context: configUrl }); } return configs; @@ -269,17 +228,12 @@ export async function loadConfig( }; const watchRemoteConfig = (watchProp: Watch, remoteProp: Remote) => { - const hasConfigChanged = async (remoteConfigProp: RemoteConfigProp) => { - const requestProps = { method: 'HEAD' }; - const { headers } = await fetch(remoteConfigProp.url, requestProps); - remoteConfigProp.oldETag = remoteConfigProp.newETag ?? undefined; - remoteConfigProp.newETag = - headers.get(HTTP_RESPONSE_HEADER_ETAG) ?? undefined; - + const hasConfigChanged = async ( + oldRemoteConfigs: AppConfig[], + newRemoteConfigs: AppConfig[], + ) => { return ( - remoteConfigProp.oldETag !== undefined && - remoteConfigProp.newETag !== undefined && - remoteConfigProp.oldETag !== remoteConfigProp.newETag + JSON.stringify(oldRemoteConfigs) !== JSON.stringify(newRemoteConfigs) ); }; @@ -287,18 +241,12 @@ export async function loadConfig( try { handle = setInterval(async () => { console.info(`Checking for config update`); - for (const remoteConfigProp of remoteConfigProps) { - if (await hasConfigChanged(remoteConfigProp)) { - console.info(`Remote config change, reloading config ...`); - const newRemoteConfigs = await loadRemoteConfigFiles(); - watchProp.onChange([ - ...newRemoteConfigs, - ...fileConfigs, - ...envConfigs, - ]); - console.info(`Remote config reloaded`); - break; - } + const newRemoteConfigs = await loadRemoteConfigFiles(); + if (await hasConfigChanged(remoteConfigs, newRemoteConfigs)) { + remoteConfigs = newRemoteConfigs; + console.info(`Remote config change, reloading config ...`); + watchProp.onChange([...remoteConfigs, ...fileConfigs, ...envConfigs]); + console.info(`Remote config reloaded`); } }, remoteProp.reloadIntervalSeconds * 1000); } catch (error) { diff --git a/yarn.lock b/yarn.lock index 87991e1bb5..00c91f7783 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14042,13 +14042,6 @@ fetch-blob@2.1.2: resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-2.1.2.tgz#a7805db1361bd44c1ef62bb57fb5fe8ea173ef3c" integrity sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow== -fetch-mock-jest@1.5.1: - version "1.5.1" - resolved "https://registry.npmjs.org/fetch-mock-jest/-/fetch-mock-jest-1.5.1.tgz#0e13df990d286d9239e284f12b279ed509bf53cd" - integrity sha512-+utwzP8C+Pax1GSka3nFXILWMY3Er2L+s090FOgqVNrNCPp0fDqgXnAHAJf12PLHi0z4PhcTaZNTz8e7K3fjqQ== - dependencies: - fetch-mock "^9.11.0" - fetch-mock@^9.11.0: version "9.11.0" resolved "https://registry.npmjs.org/fetch-mock/-/fetch-mock-9.11.0.tgz#371c6fb7d45584d2ae4a18ee6824e7ad4b637a3f" From 988c5b8421a82a733cebafc7afcaf01683149a5c Mon Sep 17 00:00:00 2001 From: Matto Date: Thu, 21 Oct 2021 18:26:08 +1100 Subject: [PATCH 007/196] Removed unused dependency Signed-off-by: Matto --- packages/config-loader/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 80e4121971..4b5afb3d29 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -33,7 +33,6 @@ "@backstage/integration": "^0.6.5", "@backstage/cli-common": "^0.1.3", "@backstage/config": "^0.1.9", - "@backstage/backend-common": "^0.9.4", "@types/json-schema": "^7.0.6", "ajv": "^7.0.3", "chokidar": "^3.5.2", From b23f048545e442ef8ddcf7bda230cca7048f3ff0 Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Thu, 21 Oct 2021 13:09:57 +0100 Subject: [PATCH 008/196] chore: Updated changeset Signed-off-by: Marley Powell --- .changeset/many-mayflies-notice.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/many-mayflies-notice.md b/.changeset/many-mayflies-notice.md index db058b909a..b8d551f4fb 100644 --- a/.changeset/many-mayflies-notice.md +++ b/.changeset/many-mayflies-notice.md @@ -1,5 +1,5 @@ --- -'@backstage/plugin-azure-devops': minor +'@backstage/plugin-azure-devops': patch --- refactor(`@backstage/plugin-azure-devops`): Consume re-exported types from `@backstage/plugin-azure-devops-backend`. From 855d460611216a54d045df5b269c65f8641e94f9 Mon Sep 17 00:00:00 2001 From: Matto Date: Tue, 26 Oct 2021 21:50:49 +1100 Subject: [PATCH 009/196] Change variable name Signed-off-by: Matto --- packages/config-loader/api-report.md | 12 ++++++------ packages/config-loader/src/index.ts | 7 ++++++- packages/config-loader/src/loader.ts | 15 +++++++++------ 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/packages/config-loader/api-report.md b/packages/config-loader/api-report.md index bb36bbe750..1dad108c38 100644 --- a/packages/config-loader/api-report.md +++ b/packages/config-loader/api-report.md @@ -49,8 +49,8 @@ export type LoadConfigOptions = { configTargets: ConfigTarget[]; env?: string; experimentalEnvFunc?: EnvFunc; - remote?: Remote; - watch?: Watch; + remote?: LoadConfigOptionsRemote; + watch?: LoadConfigOptionsWatch; }; // @public @@ -75,10 +75,10 @@ export function readEnvConfig(env: { [name: string]: string | undefined; }): AppConfig[]; -// Warning: (ae-missing-release-tag) "Remote" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "LoadConfigOptionsRemote" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export type Remote = { +export type LoadConfigOptionsRemote = { reloadIntervalSeconds: number; }; @@ -90,10 +90,10 @@ export type TransformFunc = ( }, ) => T | undefined; -// Warning: (ae-missing-release-tag) "Watch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "LoadConfigOptionsWatch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export type Watch = { +export type LoadConfigOptionsWatch = { onChange: (configs: AppConfig[]) => void; stopSignal?: Promise; }; diff --git a/packages/config-loader/src/index.ts b/packages/config-loader/src/index.ts index e0eafc00bc..c6b8ef8cef 100644 --- a/packages/config-loader/src/index.ts +++ b/packages/config-loader/src/index.ts @@ -30,4 +30,9 @@ export type { TransformFunc, } from './lib'; export { loadConfig } from './loader'; -export type { ConfigTarget, LoadConfigOptions, Watch, Remote } from './loader'; +export type { + ConfigTarget, + LoadConfigOptions, + LoadConfigOptionsWatch, + LoadConfigOptionsRemote, +} from './loader'; diff --git a/packages/config-loader/src/loader.ts b/packages/config-loader/src/loader.ts index b853d30d8a..2e7a0df801 100644 --- a/packages/config-loader/src/loader.ts +++ b/packages/config-loader/src/loader.ts @@ -31,7 +31,7 @@ import { isValidUrl } from '@backstage/integration'; export type ConfigTarget = { path: string } | { url: string }; -export type Watch = { +export type LoadConfigOptionsWatch = { /** * A listener that is called when a config file is changed. */ @@ -43,7 +43,7 @@ export type Watch = { stopSignal?: Promise; }; -export type Remote = { +export type LoadConfigOptionsRemote = { /** * An optional remote config reloading period, in seconds */ @@ -71,12 +71,12 @@ export type LoadConfigOptions = { /** * An optional remote config */ - remote?: Remote; + remote?: LoadConfigOptionsRemote; /** * An optional configuration that enables watching of config files. */ - watch?: Watch; + watch?: LoadConfigOptionsWatch; }; /** @@ -198,7 +198,7 @@ export async function loadConfig( const envConfigs = await readEnvConfig(process.env); - const watchConfigFile = (watchProp: Watch) => { + const watchConfigFile = (watchProp: LoadConfigOptionsWatch) => { const watcher = chokidar.watch(configPaths, { usePolling: process.env.NODE_ENV === 'test', }); @@ -227,7 +227,10 @@ export async function loadConfig( } }; - const watchRemoteConfig = (watchProp: Watch, remoteProp: Remote) => { + const watchRemoteConfig = ( + watchProp: LoadConfigOptionsWatch, + remoteProp: LoadConfigOptionsRemote, + ) => { const hasConfigChanged = async ( oldRemoteConfigs: AppConfig[], newRemoteConfigs: AppConfig[], From 03b47a476dadb895b0b9d36628ac7ec1759f86d1 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 26 Oct 2021 14:01:32 +0200 Subject: [PATCH 010/196] catalog-react: export loadIdentityOwnerRefs and loadCatalogOwnerRefs all the way Signed-off-by: Himanshu Mishra --- .changeset/smart-penguins-compete.md | 5 +++++ plugins/catalog-react/api-report.md | 16 ++++++++++++++++ plugins/catalog-react/src/hooks/index.ts | 2 +- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 .changeset/smart-penguins-compete.md diff --git a/.changeset/smart-penguins-compete.md b/.changeset/smart-penguins-compete.md new file mode 100644 index 0000000000..d3518600a6 --- /dev/null +++ b/.changeset/smart-penguins-compete.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +export `loadIdentityOwnerRefs` and `loadCatalogOwnerRefs` all the way diff --git a/plugins/catalog-react/api-report.md b/plugins/catalog-react/api-report.md index 06dccf4b7f..07a924bfa3 100644 --- a/plugins/catalog-react/api-report.md +++ b/plugins/catalog-react/api-report.md @@ -15,6 +15,7 @@ import { Context } from 'react'; import { Entity } from '@backstage/catalog-model'; import { EntityName } from '@backstage/catalog-model'; import { IconButton } from '@material-ui/core'; +import { IdentityApi } from '@backstage/core-plugin-api'; import { LinkProps } from '@backstage/core-components'; import { Observable } from '@backstage/types'; import { PropsWithChildren } from 'react'; @@ -726,6 +727,21 @@ export function getEntitySourceLocation( // @public export function isOwnerOf(owner: Entity, owned: Entity): boolean; +// Warning: (ae-missing-release-tag) "loadCatalogOwnerRefs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function loadCatalogOwnerRefs( + catalogApi: CatalogApi, + identityOwnerRefs: string[], +): Promise; + +// Warning: (ae-missing-release-tag) "loadIdentityOwnerRefs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function loadIdentityOwnerRefs( + identityApi: IdentityApi, +): Promise; + // Warning: (ae-missing-release-tag) "MockEntityListContextProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/plugins/catalog-react/src/hooks/index.ts b/plugins/catalog-react/src/hooks/index.ts index 38fc58222d..17c15e4ebb 100644 --- a/plugins/catalog-react/src/hooks/index.ts +++ b/plugins/catalog-react/src/hooks/index.ts @@ -37,4 +37,4 @@ export { useOwnUser } from './useOwnUser'; export { useRelatedEntities } from './useRelatedEntities'; export { useStarredEntities } from './useStarredEntities'; export { useStarredEntity } from './useStarredEntity'; -export { useEntityOwnership } from './useEntityOwnership'; +export * from './useEntityOwnership'; From 48bd2e2be206e76db903f7ded79dbcfca31d80b5 Mon Sep 17 00:00:00 2001 From: Harry Hogg Date: Fri, 22 Oct 2021 14:57:02 +0100 Subject: [PATCH 011/196] chore(Snyk): Added workflow for outputting the Snyk report as JSON Signed-off-by: Harry Hogg Co-Authored-By: Himanshu Mishra Date: Mon, 25 Oct 2021 13:04:56 +0100 Subject: [PATCH 012/196] chore(Snyk): Added scripts for syncing Snyk vulnerabilities to Github issues. Signed-off-by: Harry Hogg --- package.json | 3 +- scripts/snyk-github-issue-sync.ts | 114 ++++++++++++++++++++++++++++++ yarn.lock | 78 ++++++++++++++++++-- 3 files changed, 189 insertions(+), 6 deletions(-) create mode 100644 scripts/snyk-github-issue-sync.ts diff --git a/package.json b/package.json index 4aa6905085..4f25429569 100644 --- a/package.json +++ b/package.json @@ -53,15 +53,16 @@ }, "version": "1.0.0", "dependencies": { + "@octokit/rest": "^18.12.0", "@microsoft/api-documenter": "^7.13.47", "@microsoft/api-extractor": "^7.18.7", "@microsoft/api-extractor-model": "^7.13.5", "@microsoft/tsdoc": "^0.13.2" }, "devDependencies": { - "@types/webpack": "^5.28.0", "@changesets/cli": "^2.14.0", "@spotify/prettier-config": "^11.0.0", + "@types/webpack": "^5.28.0", "command-exists": "^1.2.9", "concurrently": "^6.0.0", "eslint-plugin-notice": "^0.9.10", diff --git a/scripts/snyk-github-issue-sync.ts b/scripts/snyk-github-issue-sync.ts new file mode 100644 index 0000000000..e20d7d4164 --- /dev/null +++ b/scripts/snyk-github-issue-sync.ts @@ -0,0 +1,114 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Octokit } from '@octokit/rest'; +import syncJsonOutput from '../snyk.json'; + +type Vulnerability = { + description: string; + id: string; + packages: Set; +}; + +const octokit = new Octokit({ + auth: process.env.GITHUB_TOKEN, +}); + +const fetchSnykGithubIssueMap = async (): Promise> => { + const snykGithubIssueMap: Record = {}; + + const iterator = octokit.paginate.iterator(octokit.rest.issues.listForRepo, { + owner: 'backstage', + repo: 'backstage', + per_page: 100, + labels: 'snyk', + }); + + for await (const { data: issues } of iterator) { + for (const issue of issues) { + const match = /\([([A-Z0-9-]+)\])/.exec(issue.title); + + if (match && match[1]) { + snykGithubIssueMap[match[1]] = issue.id; + } + } + } + + return snykGithubIssueMap; +}; + +const createGithubIssue = (vulnerability: Vulnerability) => { + console.log( + `Create issue for vulnerability ${ + vulnerability.id + } affecting packages ${Array.from(vulnerability.packages)}`, + ); + // TODO(hhogg): Create github issue with the contents from a Snyk issue. +}; + +const updateGithubIssue = ( + githubIssueId: number, + vulnerability: Vulnerability, +) => { + console.log( + `Update issue ${githubIssueId} for vulnerability ${vulnerability.id}`, + ); + // TODO(hhogg): Update github issue with the contents from a Snyk issue. +}; + +const closeGithubIssue = (githubIssueId: number) => { + console.log(`Delete issue ${githubIssueId}`); + // TODO(hhogg): Delete a github issue +}; + +(async () => { + const snykGithubIssueMap = await fetchSnykGithubIssueMap(); + const vulnerabilityStore: Record = {}; + + // Group the Snyk vulnerabilities, and aggregate the affecting packages. + syncJsonOutput.forEach(({ projectName, vulnerabilities }) => { + vulnerabilities.forEach( + ({ id, description }: { id: string; description: string }) => { + if (id !== undefined && description !== undefined) { + if (vulnerabilityStore[id]) { + vulnerabilityStore[id].packages.add(projectName); + } else { + vulnerabilityStore[id] = { + description, + id, + packages: new Set([projectName]), + }; + } + } + }, + ); + }); + + // Loop over the grouped vulnerabilities and create/update accordingly + Object.entries(vulnerabilityStore).forEach(([id, vulnerability]) => { + if (snykGithubIssueMap[id]) { + updateGithubIssue(snykGithubIssueMap[id], vulnerability); + } else { + createGithubIssue(vulnerability); + } + }); + + // Loop over the Github issues and delete accordingly. + Object.entries(snykGithubIssueMap).forEach(([snykId, githubIssueId]) => { + if (!snykGithubIssueMap[snykId]) { + closeGithubIssue(githubIssueId); + } + }); +})(); diff --git a/yarn.lock b/yarn.lock index c905fbea6f..92e2d56f9d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4961,6 +4961,19 @@ before-after-hook "^2.1.0" universal-user-agent "^6.0.0" +"@octokit/core@^3.5.1": + version "3.5.1" + resolved "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz#8601ceeb1ec0e1b1b8217b960a413ed8e947809b" + integrity sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw== + dependencies: + "@octokit/auth-token" "^2.4.4" + "@octokit/graphql" "^4.5.8" + "@octokit/request" "^5.6.0" + "@octokit/request-error" "^2.0.5" + "@octokit/types" "^6.0.3" + before-after-hook "^2.2.0" + universal-user-agent "^6.0.0" + "@octokit/endpoint@^6.0.1": version "6.0.3" resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.3.tgz#dd09b599662d7e1b66374a177ab620d8cdf73487" @@ -4995,6 +5008,11 @@ "@octokit/types" "^6.12.2" btoa-lite "^1.0.0" +"@octokit/openapi-types@^11.2.0": + version "11.2.0" + resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz#b38d7fc3736d52a1e96b230c1ccd4a58a2f400a6" + integrity sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA== + "@octokit/openapi-types@^7.3.2": version "7.3.2" resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-7.3.2.tgz#065ce49b338043ec7f741316ce06afd4d459d944" @@ -5005,6 +5023,13 @@ resolved "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== +"@octokit/plugin-paginate-rest@^2.16.8": + version "2.17.0" + resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz#32e9c7cab2a374421d3d0de239102287d791bce7" + integrity sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw== + dependencies: + "@octokit/types" "^6.34.0" + "@octokit/plugin-paginate-rest@^2.6.2": version "2.7.0" resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.7.0.tgz#6bb7b043c246e0654119a6ec4e72a172c9e2c7f3" @@ -5017,6 +5042,11 @@ resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.2.tgz#394d59ec734cd2f122431fbaf05099861ece3c44" integrity sha512-oTJSNAmBqyDR41uSMunLQKMX0jmEXbwD1fpz8FG27lScV3RhtGfBa1/BBLym+PxcC16IBlF7KH9vP1BUYxA+Eg== +"@octokit/plugin-request-log@^1.0.4": + version "1.0.4" + resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" + integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== + "@octokit/plugin-rest-endpoint-methods@5.3.1": version "5.3.1" resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.3.1.tgz#deddce769b4ec3179170709ab42e4e9e6195aaa9" @@ -5025,6 +5055,14 @@ "@octokit/types" "^6.16.2" deprecation "^2.3.1" +"@octokit/plugin-rest-endpoint-methods@^5.12.0": + version "5.13.0" + resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz#8c46109021a3412233f6f50d28786f8e552427ba" + integrity sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA== + dependencies: + "@octokit/types" "^6.34.0" + deprecation "^2.3.1" + "@octokit/request-error@^2.0.0", "@octokit/request-error@^2.0.2", "@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": version "2.1.0" resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" @@ -5056,6 +5094,16 @@ "@octokit/plugin-request-log" "^1.0.2" "@octokit/plugin-rest-endpoint-methods" "5.3.1" +"@octokit/rest@^18.12.0": + version "18.12.0" + resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" + integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== + dependencies: + "@octokit/core" "^3.5.1" + "@octokit/plugin-paginate-rest" "^2.16.8" + "@octokit/plugin-request-log" "^1.0.4" + "@octokit/plugin-rest-endpoint-methods" "^5.12.0" + "@octokit/types@^5.0.0", "@octokit/types@^5.0.1": version "5.5.0" resolved "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz#e5f06e8db21246ca102aa28444cdb13ae17a139b" @@ -5070,6 +5118,13 @@ dependencies: "@octokit/openapi-types" "^7.3.2" +"@octokit/types@^6.34.0": + version "6.34.0" + resolved "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz#c6021333334d1ecfb5d370a8798162ddf1ae8218" + integrity sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw== + dependencies: + "@octokit/openapi-types" "^11.2.0" + "@octokit/webhooks-methods@^2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-2.0.0.tgz#1108b9ea661ca6c81e4a8bfa63a09eb27d5bc2db" @@ -7604,10 +7659,19 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@>=16.9.0", "@types/react@^16.9": - version "16.14.18" - resolved "https://registry.npmjs.org/@types/react/-/react-16.14.18.tgz#b2bcea05ee244fde92d409f91bd888ca8e54b20f" - integrity sha512-eeyqd1mqoG43mI0TvNKy9QNf1Tjz3DEOsRP3rlPo35OeMIt05I+v9RR8ZvL2GuYZeF2WAcLXJZMzu6zdz3VbtQ== +"@types/react@*", "@types/react@>=16.9.0": + version "17.0.33" + resolved "https://registry.npmjs.org/@types/react/-/react-17.0.33.tgz#e01ae3de7613dac1094569880bb3792732203ad5" + integrity sha512-pLWntxXpDPaU+RTAuSGWGSEL2FRTNyRQOjSWDke/rxRg14ncsZvx8AKWMWZqvc1UOaJIAoObdZhAWvRaHFi5rw== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/react@^16.9": + version "16.14.20" + resolved "https://registry.npmjs.org/@types/react/-/react-16.14.20.tgz#ff6e932ad71d92c27590e4a8667c7a53a7d0baad" + integrity sha512-SV7TaVc8e9E/5Xuv6TIyJ5VhQpZoVFJqX6IZgj5HZoFCtIDCArE3qXkcHlc6O/Ud4UwcMoX+tlvDA95YrKdLgA== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -9778,6 +9842,11 @@ before-after-hook@^2.1.0: resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635" integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A== +before-after-hook@^2.2.0: + version "2.2.2" + resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" + integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== + better-opn@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/better-opn/-/better-opn-2.1.1.tgz#94a55b4695dc79288f31d7d0e5f658320759f7c6" @@ -20351,7 +20420,6 @@ minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: resolved "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.3.3.tgz#34c7cea038c817a8658461bf35174551dce17a0a" integrity sha512-akCrLDWfbdAWkMLBxJEeWTdNsjML+dt5YgOI4gJ53vuO0vrmYQkUPxa6j6V65s9CcePIr2SSWqjT2EcrNseryQ== dependencies: - encoding "^0.1.12" minipass "^3.1.0" minipass-sized "^1.0.3" minizlib "^2.0.0" From 3cb1e8e6ee5f7561905c639b98427fc9bd109e71 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 26 Oct 2021 14:16:29 +0200 Subject: [PATCH 013/196] fix types/react version Co-authored-by: Harry Hogg Signed-off-by: Himanshu Mishra --- package.json | 2 +- scripts/snyk-github-issue-sync.ts | 2 ++ yarn.lock | 17 ++++------------- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 4f25429569..77ec43ee24 100644 --- a/package.json +++ b/package.json @@ -53,13 +53,13 @@ }, "version": "1.0.0", "dependencies": { - "@octokit/rest": "^18.12.0", "@microsoft/api-documenter": "^7.13.47", "@microsoft/api-extractor": "^7.18.7", "@microsoft/api-extractor-model": "^7.13.5", "@microsoft/tsdoc": "^0.13.2" }, "devDependencies": { + "@octokit/rest": "^18.12.0", "@changesets/cli": "^2.14.0", "@spotify/prettier-config": "^11.0.0", "@types/webpack": "^5.28.0", diff --git a/scripts/snyk-github-issue-sync.ts b/scripts/snyk-github-issue-sync.ts index e20d7d4164..52e39da0cd 100644 --- a/scripts/snyk-github-issue-sync.ts +++ b/scripts/snyk-github-issue-sync.ts @@ -13,7 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +// eslint-disable-next-line import/no-extraneous-dependencies import { Octokit } from '@octokit/rest'; +// The GitHub workflow .github/workflows/ import syncJsonOutput from '../snyk.json'; type Vulnerability = { diff --git a/yarn.lock b/yarn.lock index 92e2d56f9d..71b2fb89d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7659,19 +7659,10 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@>=16.9.0": - version "17.0.33" - resolved "https://registry.npmjs.org/@types/react/-/react-17.0.33.tgz#e01ae3de7613dac1094569880bb3792732203ad5" - integrity sha512-pLWntxXpDPaU+RTAuSGWGSEL2FRTNyRQOjSWDke/rxRg14ncsZvx8AKWMWZqvc1UOaJIAoObdZhAWvRaHFi5rw== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - -"@types/react@^16.9": - version "16.14.20" - resolved "https://registry.npmjs.org/@types/react/-/react-16.14.20.tgz#ff6e932ad71d92c27590e4a8667c7a53a7d0baad" - integrity sha512-SV7TaVc8e9E/5Xuv6TIyJ5VhQpZoVFJqX6IZgj5HZoFCtIDCArE3qXkcHlc6O/Ud4UwcMoX+tlvDA95YrKdLgA== +"@types/react@*", "@types/react@>=16.9.0", "@types/react@^16.9": + version "16.14.18" + resolved "https://registry.npmjs.org/@types/react/-/react-16.14.18.tgz#b2bcea05ee244fde92d409f91bd888ca8e54b20f" + integrity sha512-eeyqd1mqoG43mI0TvNKy9QNf1Tjz3DEOsRP3rlPo35OeMIt05I+v9RR8ZvL2GuYZeF2WAcLXJZMzu6zdz3VbtQ== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" From bf76bb7a1d88762307353dc623de86ab84eb1a34 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 26 Oct 2021 14:43:30 +0200 Subject: [PATCH 014/196] create github issue with a formatted body Co-authored-by: Harry Hogg Signed-off-by: Himanshu Mishra --- scripts/snyk-github-issue-sync.ts | 58 ++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/scripts/snyk-github-issue-sync.ts b/scripts/snyk-github-issue-sync.ts index 52e39da0cd..ed5082717d 100644 --- a/scripts/snyk-github-issue-sync.ts +++ b/scripts/snyk-github-issue-sync.ts @@ -15,15 +15,22 @@ */ // eslint-disable-next-line import/no-extraneous-dependencies import { Octokit } from '@octokit/rest'; -// The GitHub workflow .github/workflows/ -import syncJsonOutput from '../snyk.json'; +// Generated by GitHub workflow .github/workflows/snyk-github-issue-creator +import synkJsonOutput from '../snyk.json'; + +// Pattern for a GitHub Issue title +// Snyk vulnerability [Vulnerability ID] type Vulnerability = { description: string; - id: string; + snykId: string; packages: Set; }; +// Remember to fix me! +const GH_OWNER = 'orkohunter'; +const GH_REPO = 'backstage'; + const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN, }); @@ -32,14 +39,16 @@ const fetchSnykGithubIssueMap = async (): Promise> => { const snykGithubIssueMap: Record = {}; const iterator = octokit.paginate.iterator(octokit.rest.issues.listForRepo, { - owner: 'backstage', - repo: 'backstage', + // TODO(Harry/Himanshu): Use a CLI flag for these values. + owner: GH_OWNER, + repo: GH_REPO, per_page: 100, - labels: 'snyk', + labels: 'snyk-vulnerability', }); for await (const { data: issues } of iterator) { for (const issue of issues) { + // Gets the Vulnerability ID from square braces const match = /\([([A-Z0-9-]+)\])/.exec(issue.title); if (match && match[1]) { @@ -51,13 +60,31 @@ const fetchSnykGithubIssueMap = async (): Promise> => { return snykGithubIssueMap; }; +const generateIssueBody = (vulnerability: Vulnerability) => { + let issueBody = ''; + issueBody += '## Affecting Packages/Plugins\n'; + vulnerability.packages.forEach(pkgName => { + issueBody += `* ${pkgName}\n`; + }); + // TODO: Use displayTargetFile in snyk.json to create hyperlinks + issueBody += '\n'; + issueBody += vulnerability.description; + return issueBody; +}; + const createGithubIssue = (vulnerability: Vulnerability) => { console.log( `Create issue for vulnerability ${ - vulnerability.id + vulnerability.snykId } affecting packages ${Array.from(vulnerability.packages)}`, ); - // TODO(hhogg): Create github issue with the contents from a Snyk issue. + octokit.issues.create({ + owner: GH_OWNER, + repo: GH_REPO, + title: `Snyk vulnerability [${vulnerability.snykId}]`, + labels: ['snyk-vulnerability', 'help wanted'], + body: generateIssueBody(vulnerability), + }); }; const updateGithubIssue = ( @@ -65,7 +92,7 @@ const updateGithubIssue = ( vulnerability: Vulnerability, ) => { console.log( - `Update issue ${githubIssueId} for vulnerability ${vulnerability.id}`, + `Update issue ${githubIssueId} for vulnerability ${vulnerability.snykId}`, ); // TODO(hhogg): Update github issue with the contents from a Snyk issue. }; @@ -75,12 +102,12 @@ const closeGithubIssue = (githubIssueId: number) => { // TODO(hhogg): Delete a github issue }; -(async () => { +async function main() { const snykGithubIssueMap = await fetchSnykGithubIssueMap(); const vulnerabilityStore: Record = {}; // Group the Snyk vulnerabilities, and aggregate the affecting packages. - syncJsonOutput.forEach(({ projectName, vulnerabilities }) => { + synkJsonOutput.forEach(({ projectName, vulnerabilities }) => { vulnerabilities.forEach( ({ id, description }: { id: string; description: string }) => { if (id !== undefined && description !== undefined) { @@ -89,7 +116,7 @@ const closeGithubIssue = (githubIssueId: number) => { } else { vulnerabilityStore[id] = { description, - id, + snykId: id, packages: new Set([projectName]), }; } @@ -113,4 +140,9 @@ const closeGithubIssue = (githubIssueId: number) => { closeGithubIssue(githubIssueId); } }); -})(); +} + +main().catch(error => { + console.error(error.stack); + process.exit(1); +}); From 33d7bd0baaac3fa2d3b0b34d27b270f109953578 Mon Sep 17 00:00:00 2001 From: rodion Date: Tue, 26 Oct 2021 21:57:04 +0300 Subject: [PATCH 015/196] fix: sentry plugin can pass id token Signed-off-by: rodion --- .changeset/nasty-actors-push.md | 5 +++++ plugins/sentry/api-report.md | 18 +++++++++++++++++- plugins/sentry/src/api/production-api.ts | 21 ++++++++++++++++++++- plugins/sentry/src/plugin.ts | 10 ++++++++-- 4 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 .changeset/nasty-actors-push.md diff --git a/.changeset/nasty-actors-push.md b/.changeset/nasty-actors-push.md new file mode 100644 index 0000000000..8f4d0d92a8 --- /dev/null +++ b/.changeset/nasty-actors-push.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-sentry': patch +--- + +fix: sentry-plugin can forward identity token to backend (for case when it requires authorization) diff --git a/plugins/sentry/api-report.md b/plugins/sentry/api-report.md index a23fa112c1..1b7a248e48 100644 --- a/plugins/sentry/api-report.md +++ b/plugins/sentry/api-report.md @@ -9,6 +9,7 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; import { DiscoveryApi } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; +import { IdentityApi } from '@backstage/core-plugin-api'; import { InfoCardVariants } from '@backstage/core-components'; import { RouteRef } from '@backstage/core-plugin-api'; @@ -34,7 +35,22 @@ export class MockSentryApi implements SentryApi { // // @public (undocumented) export class ProductionSentryApi implements SentryApi { - constructor(discoveryApi: DiscoveryApi, organization: string); + constructor( + discoveryApi: DiscoveryApi, + organization: string, + identityApi?: IdentityApi | undefined, + ); + // (undocumented) + authOptions(): Promise< + | { + headers?: undefined; + } + | { + headers: { + authorization: string; + }; + } + >; // (undocumented) fetchIssues( project: string, diff --git a/plugins/sentry/src/api/production-api.ts b/plugins/sentry/src/api/production-api.ts index 5968d20011..0632da711e 100644 --- a/plugins/sentry/src/api/production-api.ts +++ b/plugins/sentry/src/api/production-api.ts @@ -16,12 +16,13 @@ import { SentryIssue } from './sentry-issue'; import { SentryApi } from './sentry-api'; -import { DiscoveryApi } from '@backstage/core-plugin-api'; +import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api'; export class ProductionSentryApi implements SentryApi { constructor( private readonly discoveryApi: DiscoveryApi, private readonly organization: string, + private readonly identityApi?: IdentityApi, ) {} async fetchIssues( @@ -34,11 +35,13 @@ export class ProductionSentryApi implements SentryApi { } const apiUrl = `${await this.discoveryApi.getBaseUrl('proxy')}/sentry/api`; + const options = await this.authOptions(); const queryPart = query ? `&query=${query}` : ''; const response = await fetch( `${apiUrl}/0/projects/${this.organization}/${project}/issues/?statsPeriod=${statsFor}${queryPart}`, + options, ); if (response.status >= 400 && response.status < 600) { @@ -47,4 +50,20 @@ export class ProductionSentryApi implements SentryApi { return (await response.json()) as SentryIssue[]; } + + async authOptions() { + if (!this.identityApi) { + return {}; + } + try { + const token = await this.identityApi.getIdToken(); + return { + headers: { + authorization: `Bearer ${token}`, + }, + }; + } catch (e) { + return {}; + } + } } diff --git a/plugins/sentry/src/plugin.ts b/plugins/sentry/src/plugin.ts index 75de6ba6dc..454c1f02a6 100644 --- a/plugins/sentry/src/plugin.ts +++ b/plugins/sentry/src/plugin.ts @@ -21,6 +21,7 @@ import { createPlugin, createRouteRef, discoveryApiRef, + identityApiRef, } from '@backstage/core-plugin-api'; export const rootRouteRef = createRouteRef({ @@ -33,11 +34,16 @@ export const sentryPlugin = createPlugin({ apis: [ createApiFactory({ api: sentryApiRef, - deps: { configApi: configApiRef, discoveryApi: discoveryApiRef }, - factory: ({ configApi, discoveryApi }) => + deps: { + configApi: configApiRef, + discoveryApi: discoveryApiRef, + identityApi: identityApiRef, + }, + factory: ({ configApi, discoveryApi, identityApi }) => new ProductionSentryApi( discoveryApi, configApi.getString('sentry.organization'), + identityApi, ), }), ], From 197601f21c2f307132f4f0038821e50301ce10de Mon Sep 17 00:00:00 2001 From: rodion Date: Wed, 27 Oct 2021 11:28:58 +0300 Subject: [PATCH 016/196] fix: sentry-plugin passing token - no need for catch Signed-off-by: rodion --- plugins/sentry/src/api/production-api.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/plugins/sentry/src/api/production-api.ts b/plugins/sentry/src/api/production-api.ts index 0632da711e..37a735df14 100644 --- a/plugins/sentry/src/api/production-api.ts +++ b/plugins/sentry/src/api/production-api.ts @@ -55,15 +55,11 @@ export class ProductionSentryApi implements SentryApi { if (!this.identityApi) { return {}; } - try { - const token = await this.identityApi.getIdToken(); - return { - headers: { - authorization: `Bearer ${token}`, - }, - }; - } catch (e) { - return {}; - } + const token = await this.identityApi.getIdToken(); + return { + headers: { + authorization: `Bearer ${token}`, + }, + }; } } From a57927f5d5a41b70b2d90f4f889c65a6f22003a1 Mon Sep 17 00:00:00 2001 From: Matto Date: Wed, 27 Oct 2021 20:33:33 +1100 Subject: [PATCH 017/196] Removed dependency, introduced isValidUrl, and reinstated `configPaths` Signed-off-by: Matto --- packages/backend-common/package.json | 1 - packages/backend-common/src/config.ts | 3 +- packages/backend-common/src/urls.test.ts | 34 ++++++++++++++ packages/backend-common/src/urls.ts | 25 +++++++++++ packages/cli/package.json | 1 - packages/cli/src/lib/config.ts | 3 +- packages/cli/src/lib/urls.test.ts | 34 ++++++++++++++ packages/cli/src/lib/urls.ts | 25 +++++++++++ packages/config-loader/api-report.md | 1 + packages/config-loader/package.json | 1 - packages/config-loader/src/lib/index.ts | 1 + packages/config-loader/src/lib/urls.test.ts | 34 ++++++++++++++ packages/config-loader/src/lib/urls.ts | 25 +++++++++++ packages/config-loader/src/loader.test.ts | 49 ++++++++++++++++++++- packages/config-loader/src/loader.ts | 14 +++++- yarn.lock | 37 ++-------------- 16 files changed, 247 insertions(+), 41 deletions(-) create mode 100644 packages/backend-common/src/urls.test.ts create mode 100644 packages/backend-common/src/urls.ts create mode 100644 packages/cli/src/lib/urls.test.ts create mode 100644 packages/cli/src/lib/urls.ts create mode 100644 packages/config-loader/src/lib/urls.test.ts create mode 100644 packages/config-loader/src/lib/urls.ts diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 67a2c1a853..0c368d7805 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -33,7 +33,6 @@ "@backstage/config": "^0.1.10", "@backstage/config-loader": "^0.6.8", "@backstage/errors": "^0.1.2", - "@backstage/integration": "^0.6.5", "@google-cloud/storage": "^5.8.0", "@octokit/rest": "^18.5.3", "@types/cors": "^2.8.6", diff --git a/packages/backend-common/src/config.ts b/packages/backend-common/src/config.ts index afdb4fd0f7..c93f2cedf9 100644 --- a/packages/backend-common/src/config.ts +++ b/packages/backend-common/src/config.ts @@ -20,7 +20,7 @@ import { Logger } from 'winston'; import { findPaths } from '@backstage/cli-common'; import { Config, ConfigReader, JsonValue } from '@backstage/config'; import { ConfigTarget, loadConfig } from '@backstage/config-loader'; -import { isValidUrl } from '@backstage/integration'; +import { isValidUrl } from './urls'; class ObservableConfigProxy implements Config { private config: Config = new ConfigReader({}); @@ -130,6 +130,7 @@ export async function loadBackendConfig(options: { const configs = await loadConfig({ configRoot: paths.targetRoot, + configPaths: [], configTargets: configTargets, watch: { onChange(newConfigs) { diff --git a/packages/backend-common/src/urls.test.ts b/packages/backend-common/src/urls.test.ts new file mode 100644 index 0000000000..c2a67fb849 --- /dev/null +++ b/packages/backend-common/src/urls.test.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { isValidUrl } from './urls'; + +describe('isValidUrl', () => { + it('should return true for url', () => { + const validUrl = isValidUrl('http://some.valid.url'); + expect(validUrl).toBe(true); + }); + + it('should return false for absolute path', () => { + const validUrl = isValidUrl('/some/absolute/path'); + expect(validUrl).toBe(false); + }); + + it('should return false for relative path', () => { + const validUrl = isValidUrl('../some/relative/path'); + expect(validUrl).toBe(false); + }); +}); diff --git a/packages/backend-common/src/urls.ts b/packages/backend-common/src/urls.ts new file mode 100644 index 0000000000..848cea25d9 --- /dev/null +++ b/packages/backend-common/src/urls.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export function isValidUrl(url: string): boolean { + try { + // eslint-disable-next-line no-new + new URL(url); + return true; + } catch { + return false; + } +} diff --git a/packages/cli/package.json b/packages/cli/package.json index 519b45a5ad..0dc4dc3cf7 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -31,7 +31,6 @@ "@babel/core": "^7.4.4", "@babel/plugin-transform-modules-commonjs": "^7.4.4", "@backstage/cli-common": "^0.1.3", - "@backstage/integration": "^0.6.5", "@backstage/config": "^0.1.10", "@backstage/config-loader": "^0.6.8", "@hot-loader/react-dom": "^16.13.0", diff --git a/packages/cli/src/lib/config.ts b/packages/cli/src/lib/config.ts index 218771beb6..013328d9f3 100644 --- a/packages/cli/src/lib/config.ts +++ b/packages/cli/src/lib/config.ts @@ -21,7 +21,7 @@ import { } from '@backstage/config-loader'; import { ConfigReader } from '@backstage/config'; import { paths } from './paths'; -import { isValidUrl } from '@backstage/integration'; +import { isValidUrl } from './urls'; type Options = { args: string[]; @@ -56,6 +56,7 @@ export async function loadCliConfig(options: Options) { ? async name => process.env[name] || 'x' : undefined, configRoot: paths.targetRoot, + configPaths: [], configTargets: configTargets, }); diff --git a/packages/cli/src/lib/urls.test.ts b/packages/cli/src/lib/urls.test.ts new file mode 100644 index 0000000000..c2a67fb849 --- /dev/null +++ b/packages/cli/src/lib/urls.test.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { isValidUrl } from './urls'; + +describe('isValidUrl', () => { + it('should return true for url', () => { + const validUrl = isValidUrl('http://some.valid.url'); + expect(validUrl).toBe(true); + }); + + it('should return false for absolute path', () => { + const validUrl = isValidUrl('/some/absolute/path'); + expect(validUrl).toBe(false); + }); + + it('should return false for relative path', () => { + const validUrl = isValidUrl('../some/relative/path'); + expect(validUrl).toBe(false); + }); +}); diff --git a/packages/cli/src/lib/urls.ts b/packages/cli/src/lib/urls.ts new file mode 100644 index 0000000000..848cea25d9 --- /dev/null +++ b/packages/cli/src/lib/urls.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export function isValidUrl(url: string): boolean { + try { + // eslint-disable-next-line no-new + new URL(url); + return true; + } catch { + return false; + } +} diff --git a/packages/config-loader/api-report.md b/packages/config-loader/api-report.md index 1dad108c38..c8e01f27b0 100644 --- a/packages/config-loader/api-report.md +++ b/packages/config-loader/api-report.md @@ -46,6 +46,7 @@ export function loadConfig(options: LoadConfigOptions): Promise; // @public (undocumented) export type LoadConfigOptions = { configRoot: string; + configPaths: string[]; configTargets: ConfigTarget[]; env?: string; experimentalEnvFunc?: EnvFunc; diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 4b5afb3d29..9475058b24 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -30,7 +30,6 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/integration": "^0.6.5", "@backstage/cli-common": "^0.1.3", "@backstage/config": "^0.1.9", "@types/json-schema": "^7.0.6", diff --git a/packages/config-loader/src/lib/index.ts b/packages/config-loader/src/lib/index.ts index 32a0191cae..ca88b771ba 100644 --- a/packages/config-loader/src/lib/index.ts +++ b/packages/config-loader/src/lib/index.ts @@ -17,3 +17,4 @@ export { readEnvConfig } from './env'; export * from './transform'; export * from './schema'; +export { isValidUrl } from './urls'; diff --git a/packages/config-loader/src/lib/urls.test.ts b/packages/config-loader/src/lib/urls.test.ts new file mode 100644 index 0000000000..c2a67fb849 --- /dev/null +++ b/packages/config-loader/src/lib/urls.test.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { isValidUrl } from './urls'; + +describe('isValidUrl', () => { + it('should return true for url', () => { + const validUrl = isValidUrl('http://some.valid.url'); + expect(validUrl).toBe(true); + }); + + it('should return false for absolute path', () => { + const validUrl = isValidUrl('/some/absolute/path'); + expect(validUrl).toBe(false); + }); + + it('should return false for relative path', () => { + const validUrl = isValidUrl('../some/relative/path'); + expect(validUrl).toBe(false); + }); +}); diff --git a/packages/config-loader/src/lib/urls.ts b/packages/config-loader/src/lib/urls.ts new file mode 100644 index 0000000000..848cea25d9 --- /dev/null +++ b/packages/config-loader/src/lib/urls.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export function isValidUrl(url: string): boolean { + try { + // eslint-disable-next-line no-new + new URL(url); + return true; + } catch { + return false; + } +} diff --git a/packages/config-loader/src/loader.test.ts b/packages/config-loader/src/loader.test.ts index e8b5a8828b..804254afa0 100644 --- a/packages/config-loader/src/loader.test.ts +++ b/packages/config-loader/src/loader.test.ts @@ -67,6 +67,13 @@ describe('loadConfig', () => { $file: secrets/session-key.txt escaped: \$\${Escaped} `, + '/root/app-config2.yaml': ` + app: + title: Example App 2 + sessionKey: + $file: secrets/session-key.txt + escaped: \$\${Escaped} + `, '/root/app-config.development.yaml': ` app: sessionKey: development-key @@ -111,6 +118,7 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', + configPaths: [], configTargets: [], env: 'production', }), @@ -136,6 +144,7 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', + configPaths: [], configTargets: [{ url: configUrl }], env: 'production', remote: { @@ -156,10 +165,43 @@ describe('loadConfig', () => { ]); }); - it('loads config with secrets', async () => { + it('loads config with secrets from two different files', async () => { await expect( loadConfig({ configRoot: '/root', + configPaths: ['/root/app-config2.yaml'], + configTargets: [{ path: '/root/app-config.yaml' }], + env: 'production', + }), + ).resolves.toEqual([ + { + context: 'app-config.yaml', + data: { + app: { + title: 'Example App', + sessionKey: 'abc123', + escaped: '${Escaped}', + }, + }, + }, + { + context: 'app-config2.yaml', + data: { + app: { + title: 'Example App 2', + sessionKey: 'abc123', + escaped: '${Escaped}', + }, + }, + }, + ]); + }); + + it('loads config with secrets from single file', async () => { + await expect( + loadConfig({ + configRoot: '/root', + configPaths: ['/root/app-config.yaml'], configTargets: [{ path: '/root/app-config.yaml' }], env: 'production', }), @@ -181,6 +223,7 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', + configPaths: [], configTargets: [ { path: '/root/app-config.yaml' }, { path: '/root/app-config.development.yaml' }, @@ -221,6 +264,7 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', + configPaths: [], configTargets: [{ path: '/root/app-config.substitute.yaml' }], env: 'development', }), @@ -246,6 +290,7 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', + configPaths: [], configTargets: [], watch: { onChange: onChange.resolve, @@ -294,6 +339,7 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', + configPaths: [], configTargets: [{ url: configUrl }], watch: { onChange: onChange.resolve, @@ -339,6 +385,7 @@ describe('loadConfig', () => { await loadConfig({ configRoot: '/root', + configPaths: [], configTargets: [], watch: { onChange: () => { diff --git a/packages/config-loader/src/loader.ts b/packages/config-loader/src/loader.ts index 2e7a0df801..1f20c4e437 100644 --- a/packages/config-loader/src/loader.ts +++ b/packages/config-loader/src/loader.ts @@ -24,10 +24,10 @@ import { createIncludeTransform, createSubstitutionTransform, EnvFunc, + isValidUrl, readEnvConfig, } from './lib'; import fetch from 'node-fetch'; -import { isValidUrl } from '@backstage/integration'; export type ConfigTarget = { path: string } | { url: string }; @@ -55,6 +55,11 @@ export type LoadConfigOptions = { // The root directory of the config loading context. Used to find default configs. configRoot: string; + /** Absolute paths to load config files from. Configs from earlier paths have lower priority. + * @deprecated Use {@link configTargets} instead. + */ + configPaths: string[]; + // Paths to load config files from. Configs from earlier paths have lower priority. configTargets: ConfigTarget[]; @@ -94,6 +99,13 @@ export async function loadConfig( .filter((e): e is { path: string } => e.hasOwnProperty('path')) .map(configTarget => configTarget.path); + // Append deprecated configPaths to the absolute config paths received via configTargets. + options.configPaths.forEach(cp => { + if (!configPaths.includes(cp)) { + configPaths.push(cp); + } + }); + const configUrls: string[] = options.configTargets .slice() .filter((e): e is { url: string } => e.hasOwnProperty('url')) diff --git a/yarn.lock b/yarn.lock index 00c91f7783..10c65d5df7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11336,11 +11336,6 @@ core-js@^2.5.0: resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== -core-js@^3.0.0: - version "3.18.3" - resolved "https://registry.npmjs.org/core-js/-/core-js-3.18.3.tgz#86a0bba2d8ec3df860fefcc07a8d119779f01509" - integrity sha512-tReEhtMReZaPFVw7dajMx0vlsz3oOb8ajgPoHVYGxr8ErnZ6PcYEvvmjGmXlfpnxpkYSdOQttjB+MvVbCGfvLw== - core-js@^3.0.4, core-js@^3.6.5, core-js@^3.8.2: version "3.15.0" resolved "https://registry.npmjs.org/core-js/-/core-js-3.15.0.tgz#db9554ebce0b6fd90dc9b1f2465c841d2d055044" @@ -14042,22 +14037,6 @@ fetch-blob@2.1.2: resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-2.1.2.tgz#a7805db1361bd44c1ef62bb57fb5fe8ea173ef3c" integrity sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow== -fetch-mock@^9.11.0: - version "9.11.0" - resolved "https://registry.npmjs.org/fetch-mock/-/fetch-mock-9.11.0.tgz#371c6fb7d45584d2ae4a18ee6824e7ad4b637a3f" - integrity sha512-PG1XUv+x7iag5p/iNHD4/jdpxL9FtVSqRMUQhPab4hVDt80T1MH5ehzVrL2IdXO9Q2iBggArFvPqjUbHFuI58Q== - dependencies: - "@babel/core" "^7.0.0" - "@babel/runtime" "^7.0.0" - core-js "^3.0.0" - debug "^4.1.1" - glob-to-regexp "^0.4.0" - is-subset "^0.1.1" - lodash.isequal "^4.5.0" - path-to-regexp "^2.2.1" - querystring "^0.2.0" - whatwg-url "^6.5.0" - fetch-readablestream@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/fetch-readablestream/-/fetch-readablestream-0.2.0.tgz#eaa6d1a76b12de2d4731a343393c6ccdcfe2c795" @@ -14888,7 +14867,7 @@ glob-to-regexp@^0.3.0: resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -glob-to-regexp@^0.4.0, glob-to-regexp@^0.4.1: +glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== @@ -16872,11 +16851,6 @@ is-subdir@^1.1.1: dependencies: better-path-resolve "1.0.0" -is-subset@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" - integrity sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY= - is-svg@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" @@ -18745,7 +18719,7 @@ lodash.isempty@^4.4.0: resolved "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" integrity sha1-b4bL7di+TsmHvpqvM8loTbGzHn4= -lodash.isequal@^4.0.0, lodash.isequal@^4.5.0: +lodash.isequal@^4.0.0: version "4.5.0" resolved "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= @@ -21559,11 +21533,6 @@ path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" -path-to-regexp@^2.2.1: - version "2.4.0" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz#35ce7f333d5616f1c1e1bfe266c3aba2e5b2e704" - integrity sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w== - path-type@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -28010,7 +27979,7 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -whatwg-url@^6.4.1, whatwg-url@^6.5.0: +whatwg-url@^6.4.1: version "6.5.0" resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== From 021986e8a3f164fece31eaa38b7f2cc9d312252a Mon Sep 17 00:00:00 2001 From: Alex Rybchenko Date: Wed, 27 Oct 2021 13:08:57 +0200 Subject: [PATCH 018/196] fixed route resolving Signed-off-by: Alex Rybchenko --- .changeset/real-mails-add.md | 6 ++++++ .../src/components/TabbedLayout/RoutedTabs.tsx | 9 +++++++-- .../catalog/src/components/EntityLayout/EntityLayout.tsx | 4 +++- 3 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 .changeset/real-mails-add.md diff --git a/.changeset/real-mails-add.md b/.changeset/real-mails-add.md new file mode 100644 index 0000000000..15cdc2997f --- /dev/null +++ b/.changeset/real-mails-add.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-components': patch +'@backstage/plugin-catalog': patch +--- + +fixed route resolving diff --git a/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx b/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx index 2a65c8ffc8..d0c36e286c 100644 --- a/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx +++ b/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx @@ -33,9 +33,14 @@ export function useSelectedSubRoute(subRoutes: SubRoute[]): { element: children, })); - const element = useRoutes(routes) ?? subRoutes[0].children; + // TODO: remove once react-router updated + const sortedRoutes = routes.sort((a, b) => + b.path.slice(0, -2).localeCompare(a.path.slice(0, -2)), + ); - const [matchedRoute] = matchRoutes(routes, `/${params['*']}`) ?? []; + const element = useRoutes(sortedRoutes) ?? subRoutes[0].children; + + const [matchedRoute] = matchRoutes(sortedRoutes, `/${params['*']}`) ?? []; const foundIndex = matchedRoute ? subRoutes.findIndex(t => `${t.path}/*` === matchedRoute.route.path) : 0; diff --git a/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx b/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx index c5679355e5..76619b652b 100644 --- a/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx +++ b/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx @@ -200,7 +200,9 @@ export const EntityLayout = ({ tabProps: props.tabProps, }, ]; - }), + }) + // TODO: remove once react-router updated + .sort((a, b) => b.path.localeCompare(a.path)), [entity], ); From 965117dab87e2314765450b113296f8fe5429f7f Mon Sep 17 00:00:00 2001 From: Alex Rybchenko Date: Wed, 27 Oct 2021 14:48:24 +0200 Subject: [PATCH 019/196] remove wrong sorting Signed-off-by: Alex Rybchenko --- plugins/catalog/src/components/EntityLayout/EntityLayout.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx b/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx index 76619b652b..c5679355e5 100644 --- a/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx +++ b/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx @@ -200,9 +200,7 @@ export const EntityLayout = ({ tabProps: props.tabProps, }, ]; - }) - // TODO: remove once react-router updated - .sort((a, b) => b.path.localeCompare(a.path)), + }), [entity], ); From ab451ef2274fc5ea56124d4f08870d0f3990a832 Mon Sep 17 00:00:00 2001 From: Alex Rybchenko Date: Wed, 27 Oct 2021 15:20:48 +0200 Subject: [PATCH 020/196] updated RoutedTabs.test Signed-off-by: Alex Rybchenko --- .../components/TabbedLayout/RoutedTabs.test.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/core-components/src/components/TabbedLayout/RoutedTabs.test.tsx b/packages/core-components/src/components/TabbedLayout/RoutedTabs.test.tsx index 9b33304a95..bbd48c127c 100644 --- a/packages/core-components/src/components/TabbedLayout/RoutedTabs.test.tsx +++ b/packages/core-components/src/components/TabbedLayout/RoutedTabs.test.tsx @@ -31,6 +31,12 @@ const testRoute2 = { children:
tabbed-test-content-2
, }; +const testRoute3 = { + title: 'tabbed-test-title-3', + path: '/some-other-path-similar', + children:
tabbed-test-content-3
, +}; + describe('RoutedTabs', () => { it('renders simplest case', async () => { const rendered = await renderInTestApp( @@ -46,7 +52,7 @@ describe('RoutedTabs', () => { } + element={} /> , ); @@ -61,6 +67,13 @@ describe('RoutedTabs', () => { expect(rendered.getByText('tabbed-test-title-2')).toBeInTheDocument(); expect(rendered.queryByText('tabbed-test-content-2')).toBeInTheDocument(); + + const thirdTab = rendered.queryAllByRole('tab')[2]; + act(() => { + fireEvent.click(thirdTab); + }); + expect(rendered.getByText('tabbed-test-title-3')).toBeInTheDocument(); + expect(rendered.queryByText('tabbed-test-content-3')).toBeInTheDocument(); }); describe('correctly delegates nested links', () => { From 7205d37a142e5814518feee3cfb7046342e194f3 Mon Sep 17 00:00:00 2001 From: Harry Hogg Date: Thu, 28 Oct 2021 13:04:14 +0100 Subject: [PATCH 021/196] Updated script to create, update and close github issues Signed-off-by: Harry Hogg --- .../workflows/snyk-github-issue-creator.yml | 27 --- .github/workflows/snyk-monitor.yml | 5 + package.json | 3 +- scripts/snyk-github-issue-sync.ts | 182 +++++++++++------- 4 files changed, 124 insertions(+), 93 deletions(-) delete mode 100644 .github/workflows/snyk-github-issue-creator.yml diff --git a/.github/workflows/snyk-github-issue-creator.yml b/.github/workflows/snyk-github-issue-creator.yml deleted file mode 100644 index ad50eef0cc..0000000000 --- a/.github/workflows/snyk-github-issue-creator.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Create and Update Github Issues from Snyk report - -on: - [push, pull_request] - # workflow_dispatch: - # pull_request: - # schedule: - # - cron: '0 */4 * * *' # every 4 hours - -jobs: - sync: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Run Snyk to check for vulnerabilities - uses: snyk/actions/node@master - continue-on-error: - env: - SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - with: - args: --yarn-workspaces --strict-out-of-sync=false - json: true - - - name: Run the Snyk Github Issue command - run: yarn ts-node scripts/snyk-github-issue-sync.ts diff --git a/.github/workflows/snyk-monitor.yml b/.github/workflows/snyk-monitor.yml index 0adb6c5bcb..32df37f35b 100644 --- a/.github/workflows/snyk-monitor.yml +++ b/.github/workflows/snyk-monitor.yml @@ -43,9 +43,14 @@ jobs: --org=backstage-dgh --strict-out-of-sync=false --sarif-file-output=snyk.sarif + --json-file-output=snyk.json + json: true env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - name: Upload Snyk report uses: github/codeql-action/upload-sarif@v1 with: sarif_file: snyk.sarif + + - name: Update Github issues + run: yarn ts-node scripts/snyk-github-issue-sync.ts diff --git a/package.json b/package.json index 77ec43ee24..94de50e792 100644 --- a/package.json +++ b/package.json @@ -59,8 +59,8 @@ "@microsoft/tsdoc": "^0.13.2" }, "devDependencies": { - "@octokit/rest": "^18.12.0", "@changesets/cli": "^2.14.0", + "@octokit/rest": "^18.12.0", "@spotify/prettier-config": "^11.0.0", "@types/webpack": "^5.28.0", "command-exists": "^1.2.9", @@ -70,6 +70,7 @@ "husky": "^6.0.0", "lerna": "^4.0.0", "lint-staged": "^11.1.2", + "minimist": "^1.2.5", "prettier": "^2.2.1", "shx": "^0.3.2", "yarn-lock-check": "^1.0.5" diff --git a/scripts/snyk-github-issue-sync.ts b/scripts/snyk-github-issue-sync.ts index ed5082717d..d7f64cf1ff 100644 --- a/scripts/snyk-github-issue-sync.ts +++ b/scripts/snyk-github-issue-sync.ts @@ -13,46 +13,65 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// eslint-disable-next-line import/no-extraneous-dependencies +/* eslint-disable import/no-extraneous-dependencies */ import { Octokit } from '@octokit/rest'; +import minimist from 'minimist'; // Generated by GitHub workflow .github/workflows/snyk-github-issue-creator import synkJsonOutput from '../snyk.json'; -// Pattern for a GitHub Issue title -// Snyk vulnerability [Vulnerability ID] - type Vulnerability = { description: string; + packages: { + name: string; + target: string; + }[]; snykId: string; - packages: Set; }; -// Remember to fix me! -const GH_OWNER = 'orkohunter'; +const argv = minimist(process.argv.slice(2)); + +const GH_OWNER = 'backstage'; const GH_REPO = 'backstage'; +const SNYK_GH_LABEL = 'snyk-vulnerability'; +const SNYK_ID_REGEX = /\[([A-Z0-9-:]+)]/i; + +const isDryRun = 'dryrun' in argv; + +if (!process.env.GITHUB_TOKEN) { + console.error('GITHUB_TOKEN is not set. Please provide a Github token'); + process.exit(1); +} const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN, }); +if (isDryRun) { + console.log( + '⚠️ Running in dryrun mode, no issues will be updated on Github ⚠️', + ); +} + const fetchSnykGithubIssueMap = async (): Promise> => { const snykGithubIssueMap: Record = {}; const iterator = octokit.paginate.iterator(octokit.rest.issues.listForRepo, { - // TODO(Harry/Himanshu): Use a CLI flag for these values. owner: GH_OWNER, repo: GH_REPO, per_page: 100, - labels: 'snyk-vulnerability', + state: 'open', + labels: SNYK_GH_LABEL, }); for await (const { data: issues } of iterator) { for (const issue of issues) { // Gets the Vulnerability ID from square braces - const match = /\([([A-Z0-9-]+)\])/.exec(issue.title); + const match = SNYK_ID_REGEX.exec(issue.title); if (match && match[1]) { - snykGithubIssueMap[match[1]] = issue.id; + snykGithubIssueMap[match[1]] = issue.number; + } else { + console.log(`Unmatched Snyk ID for ${issue.title}`); } } } @@ -60,86 +79,119 @@ const fetchSnykGithubIssueMap = async (): Promise> => { return snykGithubIssueMap; }; -const generateIssueBody = (vulnerability: Vulnerability) => { - let issueBody = ''; - issueBody += '## Affecting Packages/Plugins\n'; - vulnerability.packages.forEach(pkgName => { - issueBody += `* ${pkgName}\n`; - }); - // TODO: Use displayTargetFile in snyk.json to create hyperlinks - issueBody += '\n'; - issueBody += vulnerability.description; - return issueBody; -}; +const generateIssueBody = (vulnerability: Vulnerability) => ` +## Affecting Packages/Plugins -const createGithubIssue = (vulnerability: Vulnerability) => { +${Array.from(vulnerability.packages).map( + ({ name, target }) => `* [${name}](${target})\n`, +)} + +${vulnerability.description} +`; + +const createGithubIssue = async (vulnerability: Vulnerability) => { console.log( - `Create issue for vulnerability ${ - vulnerability.snykId - } affecting packages ${Array.from(vulnerability.packages)}`, + `Create Github Issue for Snyk Vulnerability ${vulnerability.snykId}`, ); - octokit.issues.create({ - owner: GH_OWNER, - repo: GH_REPO, - title: `Snyk vulnerability [${vulnerability.snykId}]`, - labels: ['snyk-vulnerability', 'help wanted'], - body: generateIssueBody(vulnerability), + + vulnerability.packages.forEach(({ name, target }) => { + console.log(`- ${name} [${target}]`); }); + + if (!isDryRun) { + await octokit.issues.create({ + owner: GH_OWNER, + repo: GH_REPO, + title: `Snyk vulnerability [${vulnerability.snykId}]`, + labels: [SNYK_GH_LABEL, 'help wanted'], + body: generateIssueBody(vulnerability), + }); + } }; -const updateGithubIssue = ( +const updateGithubIssue = async ( githubIssueId: number, vulnerability: Vulnerability, ) => { console.log( - `Update issue ${githubIssueId} for vulnerability ${vulnerability.snykId}`, + `Update Github Issue #${githubIssueId} for Snky Vulnerability ${vulnerability.snykId}`, ); - // TODO(hhogg): Update github issue with the contents from a Snyk issue. + + if (!isDryRun) { + await octokit.issues.update({ + owner: GH_OWNER, + repo: GH_REPO, + issue_number: githubIssueId, + body: generateIssueBody(vulnerability), + }); + } }; -const closeGithubIssue = (githubIssueId: number) => { - console.log(`Delete issue ${githubIssueId}`); - // TODO(hhogg): Delete a github issue +const closeGithubIssue = async (githubIssueId: number) => { + console.log(`Closing Github Issue #${githubIssueId}`); + + if (!isDryRun) { + await octokit.issues.update({ + owner: GH_OWNER, + repo: GH_REPO, + issue_number: githubIssueId, + state: 'closed', + }); + } }; async function main() { const snykGithubIssueMap = await fetchSnykGithubIssueMap(); const vulnerabilityStore: Record = {}; - // Group the Snyk vulnerabilities, and aggregate the affecting packages. - synkJsonOutput.forEach(({ projectName, vulnerabilities }) => { - vulnerabilities.forEach( - ({ id, description }: { id: string; description: string }) => { - if (id !== undefined && description !== undefined) { - if (vulnerabilityStore[id]) { - vulnerabilityStore[id].packages.add(projectName); - } else { - vulnerabilityStore[id] = { - description, - snykId: id, - packages: new Set([projectName]), - }; + // Group the Snyk vulnerabilities, and link back to the affecting packages. + synkJsonOutput.forEach( + ({ projectName, displayTargetFile, vulnerabilities }) => { + vulnerabilities.forEach( + ({ id, description }: { id: string; description: string }) => { + if (id !== undefined && description !== undefined) { + if (vulnerabilityStore[id]) { + if ( + !vulnerabilityStore[id].packages.some( + ({ name }) => name === projectName, + ) + ) { + vulnerabilityStore[id].packages.push({ + name: projectName, + target: displayTargetFile, + }); + } + } else { + vulnerabilityStore[id] = { + description, + snykId: id, + packages: [ + { + name: projectName, + target: displayTargetFile, + }, + ], + }; + } } - } - }, - ); - }); + }, + ); + }, + ); - // Loop over the grouped vulnerabilities and create/update accordingly - Object.entries(vulnerabilityStore).forEach(([id, vulnerability]) => { + for (const [id, vulnerability] of Object.entries(vulnerabilityStore)) { if (snykGithubIssueMap[id]) { - updateGithubIssue(snykGithubIssueMap[id], vulnerability); + await updateGithubIssue(snykGithubIssueMap[id], vulnerability); } else { - createGithubIssue(vulnerability); + await createGithubIssue(vulnerability); } - }); + } - // Loop over the Github issues and delete accordingly. - Object.entries(snykGithubIssueMap).forEach(([snykId, githubIssueId]) => { - if (!snykGithubIssueMap[snykId]) { - closeGithubIssue(githubIssueId); + for (const [snykId, githubIssueId] of Object.entries(snykGithubIssueMap)) { + if (!vulnerabilityStore[snykId]) { + await closeGithubIssue(githubIssueId); } - }); + } } main().catch(error => { From 39e92897e4de9ff3c89276ebeda138241f8f0bae Mon Sep 17 00:00:00 2001 From: alexcurtin Date: Thu, 28 Oct 2021 12:14:45 -0700 Subject: [PATCH 022/196] Adding API documentation for catalog-client Signed-off-by: alexcurtin --- .changeset/mean-cars-wash.md | 5 + packages/catalog-client/api-report.md | 40 ++---- packages/catalog-client/src/CatalogClient.ts | 100 +++++++++++++- packages/catalog-client/src/types/api.ts | 133 +++++++++++++++++-- 4 files changed, 237 insertions(+), 41 deletions(-) create mode 100644 .changeset/mean-cars-wash.md diff --git a/.changeset/mean-cars-wash.md b/.changeset/mean-cars-wash.md new file mode 100644 index 0000000000..187f5d7e21 --- /dev/null +++ b/.changeset/mean-cars-wash.md @@ -0,0 +1,5 @@ +--- +'@backstage/catalog-client': patch +--- + +Improved API documentation for catalog-client. diff --git a/packages/catalog-client/api-report.md b/packages/catalog-client/api-report.md index b7bb713957..e16ca740f6 100644 --- a/packages/catalog-client/api-report.md +++ b/packages/catalog-client/api-report.md @@ -7,7 +7,7 @@ import { Entity } from '@backstage/catalog-model'; import { EntityName } from '@backstage/catalog-model'; import { Location as Location_2 } from '@backstage/catalog-model'; -// @public (undocumented) +// @public export type AddLocationRequest = { type?: string; target: string; @@ -15,126 +15,106 @@ export type AddLocationRequest = { presence?: 'optional' | 'required'; }; -// @public (undocumented) +// @public export type AddLocationResponse = { location: Location_2; entities: Entity[]; exists?: boolean; }; -// @public (undocumented) +// @public export const CATALOG_FILTER_EXISTS: unique symbol; -// @public (undocumented) +// @public export interface CatalogApi { - // (undocumented) addLocation( location: AddLocationRequest, options?: CatalogRequestOptions, ): Promise; - // (undocumented) getEntities( request?: CatalogEntitiesRequest, options?: CatalogRequestOptions, ): Promise>; - // (undocumented) getEntityAncestors( request: CatalogEntityAncestorsRequest, options?: CatalogRequestOptions, ): Promise; - // (undocumented) getEntityByName( name: EntityName, options?: CatalogRequestOptions, ): Promise; - // (undocumented) getLocationByEntity( entity: Entity, options?: CatalogRequestOptions, ): Promise; - // (undocumented) getLocationById( id: string, options?: CatalogRequestOptions, ): Promise; - // (undocumented) getOriginLocationByEntity( entity: Entity, options?: CatalogRequestOptions, ): Promise; - // (undocumented) refreshEntity( entityRef: string, options?: CatalogRequestOptions, ): Promise; - // (undocumented) removeEntityByUid( uid: string, options?: CatalogRequestOptions, ): Promise; - // (undocumented) removeLocationById( id: string, options?: CatalogRequestOptions, ): Promise; } -// @public (undocumented) +// @public export class CatalogClient implements CatalogApi { constructor(options: { discoveryApi: DiscoveryApi }); - // (undocumented) addLocation( { type, target, dryRun, presence }: AddLocationRequest, options?: CatalogRequestOptions, ): Promise; - // (undocumented) getEntities( request?: CatalogEntitiesRequest, options?: CatalogRequestOptions, ): Promise>; - // (undocumented) getEntityAncestors( request: CatalogEntityAncestorsRequest, options?: CatalogRequestOptions, ): Promise; - // (undocumented) getEntityByName( compoundName: EntityName, options?: CatalogRequestOptions, ): Promise; - // (undocumented) getLocationByEntity( entity: Entity, options?: CatalogRequestOptions, ): Promise; - // (undocumented) getLocationById( id: string, options?: CatalogRequestOptions, ): Promise; - // (undocumented) getOriginLocationByEntity( entity: Entity, options?: CatalogRequestOptions, ): Promise; - // (undocumented) refreshEntity( entityRef: string, options?: CatalogRequestOptions, ): Promise; - // (undocumented) removeEntityByUid( uid: string, options?: CatalogRequestOptions, ): Promise; - // (undocumented) removeLocationById( id: string, options?: CatalogRequestOptions, ): Promise; } -// @public (undocumented) +// @public export type CatalogEntitiesRequest = { filter?: | Record[] @@ -143,12 +123,12 @@ export type CatalogEntitiesRequest = { fields?: string[] | undefined; }; -// @public (undocumented) +// @public export type CatalogEntityAncestorsRequest = { entityRef: string; }; -// @public (undocumented) +// @public export type CatalogEntityAncestorsResponse = { root: EntityName; items: { @@ -157,12 +137,12 @@ export type CatalogEntityAncestorsResponse = { }[]; }; -// @public (undocumented) +// @public export type CatalogListResponse = { items: T[]; }; -// @public (undocumented) +// @public export type CatalogRequestOptions = { token?: string; }; diff --git a/packages/catalog-client/src/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts index 75944467d8..29d01fbe5c 100644 --- a/packages/catalog-client/src/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -39,7 +39,11 @@ import { } from './types/api'; import { DiscoveryApi } from './types/discovery'; -/** @public */ +/** + * A frontend and backend compatible client for communicating with the Backstage Catalog. + * + * @public + * */ export class CatalogClient implements CatalogApi { private readonly discoveryApi: DiscoveryApi; @@ -47,6 +51,16 @@ export class CatalogClient implements CatalogApi { this.discoveryApi = options.discoveryApi; } + /** + * Gets the Ancestors of an Entity. + * + * @param request - A request type for retrieving Entity ancestors. + * @param options - An object with your preferred options. + * + * @returns A CatalogEntityAncestorsResponse. + * + * @public + */ async getEntityAncestors( request: CatalogEntityAncestorsRequest, options?: CatalogRequestOptions, @@ -61,6 +75,16 @@ export class CatalogClient implements CatalogApi { ); } + /** + * Gets a Location by Id. + * + * @param id - A string containing the Id. + * @param options - An object with your preferred options. + * + * @returns A {@link catalog-model#Location_2}. + * + * @public + */ async getLocationById( id: string, options?: CatalogRequestOptions, @@ -72,6 +96,16 @@ export class CatalogClient implements CatalogApi { ); } + /** + * Gets a set of Entities. + * + * @param request - A request type for retrieving an Entity. + * @param options - An object with your preferred options. + * + * @returns A CatalogListResponse. + * + * @public + */ async getEntities( request?: CatalogEntitiesRequest, options?: CatalogRequestOptions, @@ -139,6 +173,16 @@ export class CatalogClient implements CatalogApi { return { items: entities.sort(refCompare) }; } + /** + * Gets a given Entity based on a provided name. + * + * @param compoundName - A string containing the name. + * @param options - An object with your preferred options. + * + * @returns An {@link catalog-model#Entity}. + * + * @public + */ async getEntityByName( compoundName: EntityName, options?: CatalogRequestOptions, @@ -153,6 +197,14 @@ export class CatalogClient implements CatalogApi { ); } + /** + * Refreshes an Entity. + * + * @param entityRef - A string containing the entityREf + * @param options - An object with your preferred options. + * + * @public + */ async refreshEntity(entityRef: string, options?: CatalogRequestOptions) { const response = await fetch( `${await this.discoveryApi.getBaseUrl('catalog')}/refresh`, @@ -171,6 +223,16 @@ export class CatalogClient implements CatalogApi { } } + /** + * Adds a location. + * + * @param options - An object with your preferred options. + * @param AddLocationRequest - A request object for adding locations. + * + * @returns An AddLocationResponse + * + * @public + */ async addLocation( { type = 'url', target, dryRun, presence }: AddLocationRequest, options?: CatalogRequestOptions, @@ -206,6 +268,16 @@ export class CatalogClient implements CatalogApi { }; } + /** + * Gets an origin Location By Entity. + * + * @param entity - An Entity + * @param options - An object with your preferred options. + * + * @returns A {@link catalog-model#Location_2}. + * + * @public + */ async getOriginLocationByEntity( entity: Entity, options?: CatalogRequestOptions, @@ -225,6 +297,16 @@ export class CatalogClient implements CatalogApi { .find(l => locationCompound === stringifyLocationReference(l)); } + /** + * Gets a Location by Entity. + * + * @param entity - An Entity + * @param options - An object with your preferred options. + * + * @returns A {@link catalog-model#Location_2}. + * + * @public + */ async getLocationByEntity( entity: Entity, options?: CatalogRequestOptions, @@ -243,6 +325,14 @@ export class CatalogClient implements CatalogApi { .find(l => locationCompound === stringifyLocationReference(l)); } + /** + * Removes a location as identified by Id. + * + * @param id - A string containing the Id + * @param options - An object with your preferred options. + * + * @public + */ async removeLocationById( id: string, options?: CatalogRequestOptions, @@ -254,6 +344,14 @@ export class CatalogClient implements CatalogApi { ); } + /** + * Removes an Entity as identified by Uid. + * + * @param uid - A string containing the Uid + * @param options - An object with your preferred options. + * + * @public + */ async removeEntityByUid( uid: string, options?: CatalogRequestOptions, diff --git a/packages/catalog-client/src/types/api.ts b/packages/catalog-client/src/types/api.ts index c7025e9452..6344caff7c 100644 --- a/packages/catalog-client/src/types/api.ts +++ b/packages/catalog-client/src/types/api.ts @@ -16,10 +16,18 @@ import { Entity, EntityName, Location } from '@backstage/catalog-model'; -/** @public */ +/** + * A Symbol to define if a catalog filter exists or not. + * + * @public + */ export const CATALOG_FILTER_EXISTS = Symbol('CATALOG_FILTER_EXISTS'); -/** @public */ +/** + * A request type for retrieving catalog Entities. + * + * @public + */ export type CatalogEntitiesRequest = { filter?: | Record[] @@ -28,75 +36,176 @@ export type CatalogEntitiesRequest = { fields?: string[] | undefined; }; -/** @public */ +/** + * A request type for Catalog Entity Ancestor information. + * + * @public + */ export type CatalogEntityAncestorsRequest = { entityRef: string; }; -/** @public */ +/** + * A response type for Catalog Entity Ancestor information. + * + * @public + */ export type CatalogEntityAncestorsResponse = { root: EntityName; items: { entity: Entity; parents: EntityName[] }[]; }; -/** @public */ +/** + * A response type for the result of a catalog operation in list form. + * + * @public + */ export type CatalogListResponse = { items: T[]; }; -/** @public */ +/** + * Options you can pass into a catalog request for additional information. + * + * @public + */ export type CatalogRequestOptions = { token?: string; }; -/** @public */ +/** + * Public functions for interacting with the Catalog API. + * + * @public + */ export interface CatalogApi { - // Entities + /** + * Gets the Entities from the catalog based on your request and options. + * + * @param request - An object with your filters and fields. + * @param options - An object with your preferred options. + * + * @returns A CatalogListResponse with items typed Catalog Model Entity. + * + */ getEntities( request?: CatalogEntitiesRequest, options?: CatalogRequestOptions, ): Promise>; + /** + * Gets the Entity ancestor information from the catalog based on your request and options. + * + * @param request - An object with your filters and fields. + * @param options - An object with your preferred options. + * + * @returns A CatalogEntityAncestorsResponse. + */ getEntityAncestors( request: CatalogEntityAncestorsRequest, options?: CatalogRequestOptions, ): Promise; + /** + * Gets a single Entity from the catalog by Entity name. + * + * @param name - A complete Entity name, with the full kind-namespace-name triplet. + * @param options - An object with your preferred options. + * + * @returns A {@link catalog-model#Entity}. + */ getEntityByName( name: EntityName, options?: CatalogRequestOptions, ): Promise; + /** + * Removes a single Entity from the catalog by Entity UID. + * + * @param uid - A string of the Entity UID. + * @param options - An object with your preferred options. + * + */ removeEntityByUid( uid: string, options?: CatalogRequestOptions, ): Promise; + /** + * Refreshes an Entity in the catalog. + * + * @param entityRef - A string in the form of 'Kind/default:foo'. + * @param options - An object with your preferred options. + * + */ refreshEntity( entityRef: string, options?: CatalogRequestOptions, ): Promise; // Locations + /** + * Gets a Location object by ID from the catalog. + * + * @param id - A string in of the Location Id. + * @param options - An object with your preferred options. + * + * @returns A {@link catalog-model#Location_2}. + */ getLocationById( id: string, options?: CatalogRequestOptions, ): Promise; + /** + * Gets origin location by Entity. + * + * @param entity - An {@link catalog-model#Entity}. + * @param options - An object with your preferred options. + * + * @returns A {@link catalog-model#Location_2}. + */ getOriginLocationByEntity( entity: Entity, options?: CatalogRequestOptions, ): Promise; + /** + * Gets Location by Entity. + * + * @param entity - An {@link catalog-model#Entity}. + * @param options - An object with your preferred options. + * + * @returns A {@link catalog-model#Location_2}. + */ getLocationByEntity( entity: Entity, options?: CatalogRequestOptions, ): Promise; + /** + * Adds a Location. + * + * @param location - A request type for adding a Location to the catalog. + * @param options - An object with your preferred options. + * + * @returns A AddLocationResponse. + */ addLocation( location: AddLocationRequest, options?: CatalogRequestOptions, ): Promise; + /** + * Removes a Location by Id. + * + * @param id - A string in of the Location Id. + * @param options - An object with your preferred options. + * + */ removeLocationById( id: string, options?: CatalogRequestOptions, ): Promise; } -/** @public */ +/** + * A request type for adding a Location to the catalog. + * + * @public + */ export type AddLocationRequest = { type?: string; target: string; @@ -104,7 +213,11 @@ export type AddLocationRequest = { presence?: 'optional' | 'required'; }; -/** @public */ +/** + * A response type for adding a Location to the catalog. + * + * @public + */ export type AddLocationResponse = { location: Location; entities: Entity[]; From 5d4053c827ffc307bbeb3dc2ded8da137aee3b58 Mon Sep 17 00:00:00 2001 From: Matto Date: Mon, 27 Sep 2021 23:41:24 +1000 Subject: [PATCH 023/196] Read config from remote config server Signed-off-by: Matto --- .changeset/giant-years-help.md | 8 + packages/backend-common/src/config.ts | 10 +- packages/cli/package.json | 6 + packages/cli/src/lib/config.ts | 16 +- packages/config-loader/api-report.md | 34 +++- packages/config-loader/package.json | 10 +- packages/config-loader/src/index.ts | 2 +- packages/config-loader/src/loader.test.ts | 125 +++++++++++- packages/config-loader/src/loader.ts | 236 +++++++++++++++++++--- packages/integration/api-report.md | 5 + packages/integration/src/index.ts | 1 + yarn.lock | 74 ++++++- 12 files changed, 475 insertions(+), 52 deletions(-) create mode 100644 .changeset/giant-years-help.md diff --git a/.changeset/giant-years-help.md b/.changeset/giant-years-help.md new file mode 100644 index 0000000000..f0ca12ef19 --- /dev/null +++ b/.changeset/giant-years-help.md @@ -0,0 +1,8 @@ +--- +'@backstage/backend-common': patch +'@backstage/cli': patch +'@backstage/config-loader': patch +'@backstage/integration': patch +--- + +Reading app config from a remote server diff --git a/packages/backend-common/src/config.ts b/packages/backend-common/src/config.ts index fa49d5f015..90131ebbe7 100644 --- a/packages/backend-common/src/config.ts +++ b/packages/backend-common/src/config.ts @@ -20,7 +20,8 @@ import { Logger } from 'winston'; import { findPaths } from '@backstage/cli-common'; import { Config, ConfigReader } from '@backstage/config'; import { JsonValue } from '@backstage/types'; -import { loadConfig } from '@backstage/config-loader'; +import { ConfigTarget, loadConfig } from '@backstage/config-loader'; +import { isValidUrl } from '@backstage/integration'; export class ObservableConfigProxy implements Config { private config: Config = new ConfigReader({}); @@ -149,7 +150,10 @@ export async function loadBackendConfig(options: { argv: string[]; }): Promise { const args = parseArgs(options.argv); - const configPaths: string[] = [args.config ?? []].flat(); + + const configTargets: ConfigTarget[] = [args.config ?? []] + .flat() + .map(arg => (isValidUrl(arg) ? { url: arg } : { path: resolvePath(arg) })); const config = new ObservableConfigProxy(options.logger); @@ -158,7 +162,7 @@ export async function loadBackendConfig(options: { const configs = await loadConfig({ configRoot: paths.targetRoot, - configPaths: configPaths.map(opt => resolvePath(opt)), + configTargets: configTargets, watch: { onChange(newConfigs) { options.logger.info( diff --git a/packages/cli/package.json b/packages/cli/package.json index 62a008087e..d85adff199 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -29,10 +29,16 @@ }, "dependencies": { "@backstage/cli-common": "^0.1.4", + "@babel/core": "^7.4.4", + "@babel/plugin-transform-modules-commonjs": "^7.4.4", + "@backstage/cli-common": "^0.1.3", + "@backstage/integration": "^0.6.5", "@backstage/config": "^0.1.10", "@backstage/config-loader": "^0.7.0", "@backstage/errors": "^0.1.3", "@backstage/types": "^0.1.1", + "@backstage/config-loader": "^0.6.8", + "@backstage/backend-common": "^0.9.4", "@hot-loader/react-dom": "^16.13.0", "@lerna/package-graph": "^4.0.0", "@lerna/project": "^4.0.0", diff --git a/packages/cli/src/lib/config.ts b/packages/cli/src/lib/config.ts index 3688d60089..79d30ee220 100644 --- a/packages/cli/src/lib/config.ts +++ b/packages/cli/src/lib/config.ts @@ -14,9 +14,14 @@ * limitations under the License. */ -import { loadConfig, loadConfigSchema } from '@backstage/config-loader'; +import { + ConfigTarget, + loadConfig, + loadConfigSchema, +} from '@backstage/config-loader'; import { ConfigReader } from '@backstage/config'; import { paths } from './paths'; +import { isValidUrl } from '@backstage/integration'; type Options = { args: string[]; @@ -26,7 +31,12 @@ type Options = { }; export async function loadCliConfig(options: Options) { - const configPaths = options.args.map(arg => paths.resolveTarget(arg)); + const configTargets: ConfigTarget[] = []; + options.args.forEach(arg => { + if (!isValidUrl(arg)) { + configTargets.push({ path: paths.resolveTarget(arg) }); + } + }); // Consider all packages in the monorepo when loading in config const { Project } = require('@lerna/project'); @@ -48,7 +58,7 @@ export async function loadCliConfig(options: Options) { ? async name => process.env[name] || 'x' : undefined, configRoot: paths.targetRoot, - configPaths, + configTargets: configTargets, }); // printing to stderr to not clobber stdout in case the cli command diff --git a/packages/config-loader/api-report.md b/packages/config-loader/api-report.md index 19b5ee9e27..4ed409e3ce 100644 --- a/packages/config-loader/api-report.md +++ b/packages/config-loader/api-report.md @@ -23,6 +23,17 @@ export type ConfigSchemaProcessingOptions = { withFilteredKeys?: boolean; }; +// Warning: (ae-missing-release-tag) "ConfigTarget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ConfigTarget = + | { + path: string; + } + | { + url: string; + }; + // @public export type ConfigVisibility = 'frontend' | 'backend' | 'secret'; @@ -32,13 +43,11 @@ export function loadConfig(options: LoadConfigOptions): Promise; // @public export type LoadConfigOptions = { configRoot: string; - configPaths: string[]; + configTargets: ConfigTarget[]; env?: string; experimentalEnvFunc?: (name: string) => Promise; - watch?: { - onChange: (configs: AppConfig[]) => void; - stopSignal?: Promise; - }; + remote?: Remote; + watch?: Watch; }; // @public @@ -64,6 +73,13 @@ export function readEnvConfig(env: { [name: string]: string | undefined; }): AppConfig[]; +// Warning: (ae-missing-release-tag) "Remote" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type Remote = { + reloadIntervalSeconds: number; +}; + // @public export type TransformFunc = ( value: T, @@ -71,4 +87,12 @@ export type TransformFunc = ( visibility: ConfigVisibility; }, ) => T | undefined; + +// Warning: (ae-missing-release-tag) "Watch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type Watch = { + onChange: (configs: AppConfig[]) => void; + stopSignal?: Promise; +}; ``` diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index a731225e2b..cab3a43fc5 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -30,10 +30,12 @@ "clean": "backstage-cli clean" }, "dependencies": { + "@backstage/integration": "^0.6.5", "@backstage/cli-common": "^0.1.4", "@backstage/config": "^0.1.9", "@backstage/errors": "^0.1.3", "@backstage/types": "^0.1.1", + "@backstage/backend-common": "^0.9.4", "@types/json-schema": "^7.0.6", "ajv": "^7.0.3", "chokidar": "^3.5.2", @@ -42,8 +44,10 @@ "json-schema-merge-allof": "^0.8.1", "json-schema-traverse": "^1.0.0", "typescript-json-schema": "^0.50.1", + "uuid": "^8.3.2", "yaml": "^1.9.2", - "yup": "^0.32.9" + "yup": "^0.32.9", + "node-fetch": "2.6.5" }, "devDependencies": { "@types/jest": "^26.0.7", @@ -51,7 +55,9 @@ "@types/mock-fs": "^4.10.0", "@types/node": "^14.14.32", "@types/yup": "^0.29.13", - "mock-fs": "^5.1.0" + "mock-fs": "^5.1.0", + "fetch-mock-jest": "1.5.1", + "fetch-mock": "^9.11.0" }, "files": [ "dist" diff --git a/packages/config-loader/src/index.ts b/packages/config-loader/src/index.ts index 888c68ef70..ee3e6413ad 100644 --- a/packages/config-loader/src/index.ts +++ b/packages/config-loader/src/index.ts @@ -29,4 +29,4 @@ export type { TransformFunc, } from './lib'; export { loadConfig } from './loader'; -export type { LoadConfigOptions } from './loader'; +export type { ConfigTarget, LoadConfigOptions, Watch, Remote } from './loader'; diff --git a/packages/config-loader/src/loader.test.ts b/packages/config-loader/src/loader.test.ts index 3f51e76936..190904a870 100644 --- a/packages/config-loader/src/loader.test.ts +++ b/packages/config-loader/src/loader.test.ts @@ -18,12 +18,33 @@ import { AppConfig } from '@backstage/config'; import { loadConfig } from './loader'; import mockFs from 'mock-fs'; import fs from 'fs-extra'; +import { v4 as uuidv4 } from 'uuid'; + +const fetchMock = require('fetch-mock').sandbox(); +const nodeFetch = require('node-fetch'); + +nodeFetch.default = fetchMock; describe('loadConfig', () => { beforeEach(() => { process.env.MY_SECRET = 'is-secret'; process.env.SUBSTITUTE_ME = 'substituted'; + fetchMock.mock( + { + url: 'https://some.domain.io/app-config.yaml', + method: 'GET', + }, + { + body: `app: + title: Remote Example App + sessionKey: 'abc123' + escaped: \$\${Escaped} + `, + headers: { ETag: uuidv4().toString() }, + }, + ); + mockFs({ '/root/app-config.yaml': ` app: @@ -66,6 +87,7 @@ describe('loadConfig', () => { }); afterEach(() => { + fetchMock.restore(); mockFs.restore(); }); @@ -73,7 +95,7 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', - configPaths: [], + configTargets: [], env: 'production', }), ).resolves.toEqual([ @@ -90,11 +112,37 @@ describe('loadConfig', () => { ]); }); + it('load config from remote path', async () => { + const configUrl = 'https://some.domain.io/app-config.yaml'; + + await expect( + loadConfig({ + configRoot: '/root', + configTargets: [{ url: configUrl }], + env: 'production', + remote: { + reloadIntervalSeconds: 30, + }, + }), + ).resolves.toEqual([ + { + context: configUrl, + data: { + app: { + title: 'Remote Example App', + sessionKey: 'abc123', + escaped: '${Escaped}', + }, + }, + }, + ]); + }); + it('loads config with secrets', async () => { await expect( loadConfig({ configRoot: '/root', - configPaths: ['/root/app-config.yaml'], + configTargets: [{ path: '/root/app-config.yaml' }], env: 'production', }), ).resolves.toEqual([ @@ -115,9 +163,9 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', - configPaths: [ - '/root/app-config.yaml', - '/root/app-config.development.yaml', + configTargets: [ + { path: '/root/app-config.yaml' }, + { path: '/root/app-config.development.yaml' }, ], env: 'development', }), @@ -155,7 +203,7 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', - configPaths: ['/root/app-config.substitute.yaml'], + configTargets: [{ path: '/root/app-config.substitute.yaml' }], env: 'development', }), ).resolves.toEqual([ @@ -180,7 +228,7 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', - configPaths: [], + configTargets: [], watch: { onChange: onChange.resolve, stopSignal: stopSignal.promise, @@ -218,12 +266,73 @@ describe('loadConfig', () => { stopSignal.resolve(); }); + it('watches remote config urls', async () => { + const onChange = defer(); + const stopSignal = defer(); + + const configUrl = 'https://some.domain.io/app-config.yaml'; + await expect( + loadConfig({ + configRoot: '/root', + configTargets: [{ url: configUrl }], + watch: { + onChange: onChange.resolve, + stopSignal: stopSignal.promise, + }, + remote: { + reloadIntervalSeconds: 1, + }, + }), + ).resolves.toEqual([ + { + context: configUrl, + data: { + app: { + title: 'Remote Example App', + sessionKey: 'abc123', + escaped: '${Escaped}', + }, + }, + }, + ]); + + fetchMock.mock( + { + url: 'https://some.domain.io/app-config.yaml', + }, + { + body: `app: + title: NEW ReMOTe ExaMPLe App + sessionKey: 'abc123' + escaped: \$\${Escaped} + `, + headers: { ETag: uuidv4().toString() }, + }, + { overwriteRoutes: true }, + ); + + await expect(onChange.promise).resolves.toEqual([ + { + context: configUrl, + data: { + app: { + title: 'NEW ReMOTe ExaMPLe App', + sessionKey: 'abc123', + escaped: '${Escaped}', + }, + }, + }, + ]); + + stopSignal.resolve(); + }); + it('stops watching config files', async () => { const stopSignal = defer(); await loadConfig({ configRoot: '/root', - configPaths: [], + configTargets: [], watch: { onChange: () => { expect('not').toBe('called'); diff --git a/packages/config-loader/src/loader.ts b/packages/config-loader/src/loader.ts index 6803d61786..2c61b4d1ce 100644 --- a/packages/config-loader/src/loader.ts +++ b/packages/config-loader/src/loader.ts @@ -17,15 +17,66 @@ import fs from 'fs-extra'; import yaml from 'yaml'; import chokidar from 'chokidar'; -import { resolve as resolvePath, dirname, isAbsolute, basename } from 'path'; +import { basename, dirname, isAbsolute, resolve as resolvePath } from 'path'; import { AppConfig } from '@backstage/config'; import { ForwardedError } from '@backstage/errors'; import { applyConfigTransforms, - readEnvConfig, createIncludeTransform, createSubstitutionTransform, + EnvFunc, + readEnvConfig, } from './lib'; +import fetch from 'node-fetch'; +import { isValidUrl } from '@backstage/integration'; + +export type ConfigTarget = { path: string } | { url: string }; + +export type Watch = { + /** + * A listener that is called when a config file is changed. + */ + onChange: (configs: AppConfig[]) => void; + + /** + * An optional signal that stops the watcher once the promise resolves. + */ + stopSignal?: Promise; +}; + +/** + * Options that control the loading of configuration files in the backend. + * + * @public + */ +export type Remote = { + /** + * An optional remote config reloading period, in seconds + */ + reloadIntervalSeconds: number; +}; + +export type RemoteConfigProp = { + /** + * URL of the remote config + */ + url: string; + + /** + * Contents of the remote config + */ + content: string | null; + + /** + * An optional new ETag header value. Used when checking for updated config. + */ + newETag?: string; + + /** + * An optional old ETag header value. Used when checking for updated config + */ + oldETag?: string; +}; /** * Options that control the loading of configuration files in the backend. @@ -36,8 +87,8 @@ export type LoadConfigOptions = { // The root directory of the config loading context. Used to find default configs. configRoot: string; - // Absolute paths to load config files from. Configs from earlier paths have lower priority. - configPaths: string[]; + // Paths to load config files from. Configs from earlier paths have lower priority. + configTargets: ConfigTarget[]; /** @deprecated This option has been removed */ env?: string; @@ -49,22 +100,19 @@ export type LoadConfigOptions = { */ experimentalEnvFunc?: (name: string) => Promise; + /** + * An optional remote config + */ + remote?: Remote; + /** * An optional configuration that enables watching of config files. */ - watch?: { - /** - * A listener that is called when a config file is changed. - */ - onChange: (configs: AppConfig[]) => void; - - /** - * An optional signal that stops the watcher once the promise resolves. - */ - stopSignal?: Promise; - }; + watch?: Watch; }; +const HTTP_RESPONSE_HEADER_ETAG = 'ETag'; + /** * Load configuration data. * @@ -73,12 +121,30 @@ export type LoadConfigOptions = { export async function loadConfig( options: LoadConfigOptions, ): Promise { - const { configRoot, experimentalEnvFunc: envFunc, watch } = options; - const configPaths = options.configPaths.slice(); + const { configRoot, experimentalEnvFunc: envFunc, watch, remote } = options; + + const configPaths: string[] = options.configTargets + .slice() + .filter((e): e is { path: string } => e.hasOwnProperty('path')) + .map(configTarget => configTarget.path); + + let configUrls: string[] = options.configTargets + .slice() + .filter((e): e is { url: string } => e.hasOwnProperty('url')) + .map(configTarget => configTarget.url); + + const remoteConfigProps: RemoteConfigProp[] = []; + + if (remote === undefined && configUrls.length > 0) { + console.warn( + `Remote config detected, however, this feature is turned off. Remote config will be ignored.`, + ); + configUrls = []; + } // If no paths are provided, we default to reading // `app-config.yaml` and, if it exists, `app-config.local.yaml` - if (configPaths.length === 0) { + if (configPaths.length === 0 && configUrls.length === 0) { configPaths.push(resolvePath(configRoot, 'app-config.yaml')); const localConfig = resolvePath(configRoot, 'app-config.local.yaml'); @@ -114,22 +180,79 @@ export async function loadConfig( return configs; }; - let fileConfigs; + const loadRemoteConfigFiles = async () => { + const configs: AppConfig[] = []; + + const readConfigFromUrl = async (remoteConfigProp: RemoteConfigProp) => { + const response = await fetch(remoteConfigProp.url); + if (!response.ok) { + throw new Error( + `Could not read config file at ${remoteConfigProp.url}`, + ); + } + + remoteConfigProp.oldETag = remoteConfigProp.newETag ?? undefined; + remoteConfigProp.newETag = + response.headers.get(HTTP_RESPONSE_HEADER_ETAG) ?? undefined; + remoteConfigProp.content = await response.text(); + + return remoteConfigProp; + }; + + for (let i = 0; i < configUrls.length; i++) { + const remoteConfigProp = await readConfigFromUrl({ + url: configUrls[i], + content: null, + }); + + if (!isValidUrl(remoteConfigProp.url)) { + throw new Error( + `Config load path is not valid: '${remoteConfigProp.url}'`, + ); + } + + const dir = configRoot; + if (!remoteConfigProp.content) { + throw new Error(`Config is not valid`); + } + const input = yaml.parse(remoteConfigProp.content); + const substitutionTransform = createSubstitutionTransform(env); + const data = await applyConfigTransforms(dir, input, [ + substitutionTransform, + ]); + + configs.push({ data, context: remoteConfigProp.url }); + + remoteConfigProps.push(remoteConfigProp); + } + + return configs; + }; + + let fileConfigs: AppConfig[]; try { fileConfigs = await loadConfigFiles(); } catch (error) { throw new ForwardedError('Failed to read static configuration file', error); } + let remoteConfigs: AppConfig[] = []; + if (remote) { + try { + remoteConfigs = await loadRemoteConfigFiles(); + } catch (error) { + throw new Error(`Failed to read remote configuration file, ${error}`); + } + } + const envConfigs = await readEnvConfig(process.env); - // Set up config file watching if requested by the caller - if (watch) { - let currentSerializedConfig = JSON.stringify(fileConfigs); - + const watchConfigFile = (watchProp: Watch) => { const watcher = chokidar.watch(configPaths, { usePolling: process.env.NODE_ENV === 'test', }); + + let currentSerializedConfig = JSON.stringify(fileConfigs); watcher.on('change', async () => { try { const newConfigs = await loadConfigFiles(); @@ -140,18 +263,77 @@ export async function loadConfig( } currentSerializedConfig = newSerializedConfig; - watch.onChange([...newConfigs, ...envConfigs]); + watchProp.onChange([...remoteConfigs, ...newConfigs, ...envConfigs]); } catch (error) { console.error(`Failed to reload configuration files, ${error}`); } }); - if (watch.stopSignal) { - watch.stopSignal.then(() => { + if (watchProp.stopSignal) { + watchProp.stopSignal.then(() => { watcher.close(); }); } + }; + + const watchRemoteConfig = (watchProp: Watch, remoteProp: Remote) => { + const hasConfigChanged = async (remoteConfigProp: RemoteConfigProp) => { + const requestProps = { method: 'HEAD' }; + const { headers } = await fetch(remoteConfigProp.url, requestProps); + remoteConfigProp.oldETag = remoteConfigProp.newETag ?? undefined; + remoteConfigProp.newETag = + headers.get(HTTP_RESPONSE_HEADER_ETAG) ?? undefined; + + return ( + remoteConfigProp.oldETag !== undefined && + remoteConfigProp.newETag !== undefined && + remoteConfigProp.oldETag !== remoteConfigProp.newETag + ); + }; + + let handle: NodeJS.Timeout | undefined; + try { + handle = setInterval(async () => { + console.info(`Checking for config update`); + for (const remoteConfigProp of remoteConfigProps) { + if (await hasConfigChanged(remoteConfigProp)) { + console.info(`Remote config change, reloading config ...`); + const newRemoteConfigs = await loadRemoteConfigFiles(); + watchProp.onChange([ + ...newRemoteConfigs, + ...fileConfigs, + ...envConfigs, + ]); + console.info(`Remote config reloaded`); + break; + } + } + }, remoteProp.reloadIntervalSeconds * 1000); + } catch (error) { + console.error(`Failed to reload configuration files, ${error}`); + } + + if (watchProp.stopSignal) { + watchProp.stopSignal.then(() => { + if (handle !== undefined) { + console.info(`Stopping remote config watch`); + clearInterval(handle); + handle = undefined; + } + }); + } + }; + + // Set up config file watching if requested by the caller + if (watch) { + watchConfigFile(watch); } - return [...fileConfigs, ...envConfigs]; + if (watch && remote) { + watchRemoteConfig(watch, remote); + } + + return remote + ? [...remoteConfigs, ...fileConfigs, ...envConfigs] + : [...fileConfigs, ...envConfigs]; } diff --git a/packages/integration/api-report.md b/packages/integration/api-report.md index 01f84383fe..e07f61eea6 100644 --- a/packages/integration/api-report.md +++ b/packages/integration/api-report.md @@ -347,6 +347,11 @@ export type GoogleGcsIntegrationConfig = { privateKey?: string; }; +// Warning: (ae-missing-release-tag) "isValidUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export function isValidUrl(url: string): boolean; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // Warning: (ae-missing-release-tag) "readAwsS3IntegrationConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/packages/integration/src/index.ts b/packages/integration/src/index.ts index 3d5f336e8d..b8992f52d2 100644 --- a/packages/integration/src/index.ts +++ b/packages/integration/src/index.ts @@ -27,6 +27,7 @@ export * from './gitlab'; export * from './googleGcs'; export * from './awsS3'; export { defaultScmResolveUrl } from './helpers'; +export { isValidUrl } from './helpers'; export { ScmIntegrations } from './ScmIntegrations'; export type { ScmIntegration, ScmIntegrationsGroup } from './types'; export type { ScmIntegrationRegistry } from './registry'; diff --git a/yarn.lock b/yarn.lock index c905fbea6f..e45d685bf8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11555,6 +11555,11 @@ core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.10: resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== +core-js@^3.0.0: + version "3.18.3" + resolved "https://registry.npmjs.org/core-js/-/core-js-3.18.3.tgz#86a0bba2d8ec3df860fefcc07a8d119779f01509" + integrity sha512-tReEhtMReZaPFVw7dajMx0vlsz3oOb8ajgPoHVYGxr8ErnZ6PcYEvvmjGmXlfpnxpkYSdOQttjB+MvVbCGfvLw== + core-js@^3.0.4, core-js@^3.6.5, core-js@^3.8.2: version "3.15.0" resolved "https://registry.npmjs.org/core-js/-/core-js-3.15.0.tgz#db9554ebce0b6fd90dc9b1f2465c841d2d055044" @@ -14238,6 +14243,34 @@ fecha@^4.2.0: resolved "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz#3ffb6395453e3f3efff850404f0a59b6747f5f41" integrity sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg== +fetch-blob@2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-2.1.2.tgz#a7805db1361bd44c1ef62bb57fb5fe8ea173ef3c" + integrity sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow== + +fetch-mock-jest@1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/fetch-mock-jest/-/fetch-mock-jest-1.5.1.tgz#0e13df990d286d9239e284f12b279ed509bf53cd" + integrity sha512-+utwzP8C+Pax1GSka3nFXILWMY3Er2L+s090FOgqVNrNCPp0fDqgXnAHAJf12PLHi0z4PhcTaZNTz8e7K3fjqQ== + dependencies: + fetch-mock "^9.11.0" + +fetch-mock@^9.11.0: + version "9.11.0" + resolved "https://registry.npmjs.org/fetch-mock/-/fetch-mock-9.11.0.tgz#371c6fb7d45584d2ae4a18ee6824e7ad4b637a3f" + integrity sha512-PG1XUv+x7iag5p/iNHD4/jdpxL9FtVSqRMUQhPab4hVDt80T1MH5ehzVrL2IdXO9Q2iBggArFvPqjUbHFuI58Q== + dependencies: + "@babel/core" "^7.0.0" + "@babel/runtime" "^7.0.0" + core-js "^3.0.0" + debug "^4.1.1" + glob-to-regexp "^0.4.0" + is-subset "^0.1.1" + lodash.isequal "^4.5.0" + path-to-regexp "^2.2.1" + querystring "^0.2.0" + whatwg-url "^6.5.0" + fetch-readablestream@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/fetch-readablestream/-/fetch-readablestream-0.2.0.tgz#eaa6d1a76b12de2d4731a343393c6ccdcfe2c795" @@ -15062,7 +15095,7 @@ glob-to-regexp@^0.3.0: resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -glob-to-regexp@^0.4.1: +glob-to-regexp@^0.4.0, glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== @@ -17103,6 +17136,11 @@ is-subdir@^1.1.1: dependencies: better-path-resolve "1.0.0" +is-subset@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" + integrity sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY= + is-svg@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" @@ -18971,7 +19009,7 @@ lodash.isempty@^4.4.0: resolved "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" integrity sha1-b4bL7di+TsmHvpqvM8loTbGzHn4= -lodash.isequal@^4.0.0: +lodash.isequal@^4.0.0, lodash.isequal@^4.5.0: version "4.5.0" resolved "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= @@ -20815,6 +20853,13 @@ node-fetch@2.6.1, node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1: resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== +node-fetch@2.6.5: + version "2.6.5" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz#42735537d7f080a7e5f78b6c549b7146be1742fd" + integrity sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ== + dependencies: + whatwg-url "^5.0.0" + node-forge@^0.10.0: version "0.10.0" resolved "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" @@ -22153,6 +22198,11 @@ path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" +path-to-regexp@^2.2.1: + version "2.4.0" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz#35ce7f333d5616f1c1e1bfe266c3aba2e5b2e704" + integrity sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w== + path-type@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -27219,6 +27269,11 @@ tr46@^2.0.2: dependencies: punycode "^2.1.1" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + "traverse@>=0.3.0 <0.4": version "0.3.9" resolved "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" @@ -28449,6 +28504,11 @@ web-streams-polyfill@4.0.0-beta.1: resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.1.tgz#3b19b9817374b7cee06d374ba7eeb3aeb80e8c95" integrity sha512-3ux37gEX670UUphBF9AMCq8XM6iQ8Ac6A+DSRRjDoRBm1ufCkaCDdNVbaqq60PsEkdNlLKrGtv/YBP4EJXqNtQ== +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" @@ -28671,7 +28731,15 @@ whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-url@^6.4.1: +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +whatwg-url@^6.4.1, whatwg-url@^6.5.0: version "6.5.0" resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== From e51740f9c96cb51c3e742df3289b56ce92264925 Mon Sep 17 00:00:00 2001 From: Matto Date: Tue, 19 Oct 2021 23:18:19 +1100 Subject: [PATCH 024/196] Added documentation Signed-off-by: Matto --- docs/conf/writing.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/docs/conf/writing.md b/docs/conf/writing.md index 4288d23b17..86b844484b 100644 --- a/docs/conf/writing.md +++ b/docs/conf/writing.md @@ -1,7 +1,6 @@ --- -id: writing -title: Writing Backstage Configuration Files -description: Documentation on Writing Backstage Configuration Files +id: writing title: Writing Backstage Configuration Files description: Documentation on Writing +Backstage Configuration Files --- ## File Format @@ -67,13 +66,15 @@ production build. ## Configuration Files -It is possible to have multiple configuration files, both to support different -environments, but also to define configuration that is local to specific -packages. The configuration files to load are selected using a `--config ` -flag, and it is possible to load any number of files. Paths are relative to the -working directory of the executed process, for example `package/backend`. This -means that to select a config file in the repo root when running the backend, -you would use `--config ../../my-config.yaml`. +It is possible to have multiple configuration files (bundled and/or remote), +both to support different environments, but also to define configuration that is +local to specific packages. The configuration files to load are selected using a +`--config ` flag, and it is possible to load any number of +files. Paths are relative to the working directory of the executed process, for +example `package/backend`. This means that to select a config file in the repo +root when running the backend, you would use `--config ../../my-config.yaml`, +and for config file on a config server you would use +`--config https://some.domain.io/app-config.yaml` If no `config` flags are specified, the default behavior is to load `app-config.yaml` and, if it exists, `app-config.local.yaml` from the repo root. @@ -85,7 +86,7 @@ are NOT loaded. To include them you need to explicitly include them with a flag, for example: ```shell -yarn start --config ../../app-config.yaml --config ../../app-config.staging.yaml +yarn start --config ../../app-config.yaml --config ../../app-config.staging.yaml --config https://some.domain.io/app-config.yaml ``` All loaded configuration files are merged together using the following rules: From ce843364f09c59a32f3d4ba8c178d33e73c51d9b Mon Sep 17 00:00:00 2001 From: Matto Date: Thu, 21 Oct 2021 18:20:36 +1100 Subject: [PATCH 025/196] Remove the dependency on Etag headers for reloading config Signed-off-by: Matto --- docs/conf/writing.md | 5 +- packages/cli/package.json | 2 - packages/config-loader/package.json | 4 +- packages/config-loader/src/loader.test.ts | 79 ++++++++++++---------- packages/config-loader/src/loader.ts | 82 +++++++---------------- yarn.lock | 7 -- 6 files changed, 73 insertions(+), 106 deletions(-) diff --git a/docs/conf/writing.md b/docs/conf/writing.md index 86b844484b..a0e29820ed 100644 --- a/docs/conf/writing.md +++ b/docs/conf/writing.md @@ -1,6 +1,7 @@ --- -id: writing title: Writing Backstage Configuration Files description: Documentation on Writing -Backstage Configuration Files +id: writing +title: Writing Backstage Configuration Files +description: Documentation on Writing Backstage Configuration Files --- ## File Format diff --git a/packages/cli/package.json b/packages/cli/package.json index d85adff199..b9ba9fe8a3 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -31,14 +31,12 @@ "@backstage/cli-common": "^0.1.4", "@babel/core": "^7.4.4", "@babel/plugin-transform-modules-commonjs": "^7.4.4", - "@backstage/cli-common": "^0.1.3", "@backstage/integration": "^0.6.5", "@backstage/config": "^0.1.10", "@backstage/config-loader": "^0.7.0", "@backstage/errors": "^0.1.3", "@backstage/types": "^0.1.1", "@backstage/config-loader": "^0.6.8", - "@backstage/backend-common": "^0.9.4", "@hot-loader/react-dom": "^16.13.0", "@lerna/package-graph": "^4.0.0", "@lerna/project": "^4.0.0", diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index cab3a43fc5..456bca90ad 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -44,7 +44,6 @@ "json-schema-merge-allof": "^0.8.1", "json-schema-traverse": "^1.0.0", "typescript-json-schema": "^0.50.1", - "uuid": "^8.3.2", "yaml": "^1.9.2", "yup": "^0.32.9", "node-fetch": "2.6.5" @@ -56,8 +55,7 @@ "@types/node": "^14.14.32", "@types/yup": "^0.29.13", "mock-fs": "^5.1.0", - "fetch-mock-jest": "1.5.1", - "fetch-mock": "^9.11.0" + "msw": "^0.29.0" }, "files": [ "dist" diff --git a/packages/config-loader/src/loader.test.ts b/packages/config-loader/src/loader.test.ts index 190904a870..e8b5a8828b 100644 --- a/packages/config-loader/src/loader.test.ts +++ b/packages/config-loader/src/loader.test.ts @@ -18,33 +18,47 @@ import { AppConfig } from '@backstage/config'; import { loadConfig } from './loader'; import mockFs from 'mock-fs'; import fs from 'fs-extra'; -import { v4 as uuidv4 } from 'uuid'; - -const fetchMock = require('fetch-mock').sandbox(); -const nodeFetch = require('node-fetch'); - -nodeFetch.default = fetchMock; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; describe('loadConfig', () => { + const server = setupServer(); + const initialLoaderHandler = rest.get( + `https://some.domain.io/app-config.yaml`, + (_req, res, ctx) => { + return res( + ctx.body( + `app: + title: Remote Example App + sessionKey: 'abc123' + escaped: \$\${Escaped} + `, + ), + ); + }, + ); + + const reloadHandler = rest.get( + `https://some.domain.io/app-config.yaml`, + (_req, res, ctx) => { + return res( + ctx.body( + `app: + title: NEW ReMOTe ExaMPLe App + sessionKey: 'abc123' + escaped: \$\${Escaped} + `, + ), + ); + }, + ); + + beforeAll(() => server.listen()); + beforeEach(() => { process.env.MY_SECRET = 'is-secret'; process.env.SUBSTITUTE_ME = 'substituted'; - fetchMock.mock( - { - url: 'https://some.domain.io/app-config.yaml', - method: 'GET', - }, - { - body: `app: - title: Remote Example App - sessionKey: 'abc123' - escaped: \$\${Escaped} - `, - headers: { ETag: uuidv4().toString() }, - }, - ); - mockFs({ '/root/app-config.yaml': ` app: @@ -87,10 +101,12 @@ describe('loadConfig', () => { }); afterEach(() => { - fetchMock.restore(); mockFs.restore(); + server.resetHandlers(); }); + afterAll(() => server.close()); + it('load config from default path', async () => { await expect( loadConfig({ @@ -113,6 +129,8 @@ describe('loadConfig', () => { }); it('load config from remote path', async () => { + server.use(initialLoaderHandler); + const configUrl = 'https://some.domain.io/app-config.yaml'; await expect( @@ -267,6 +285,8 @@ describe('loadConfig', () => { }); it('watches remote config urls', async () => { + server.use(initialLoaderHandler); + const onChange = defer(); const stopSignal = defer(); @@ -296,20 +316,7 @@ describe('loadConfig', () => { }, ]); - fetchMock.mock( - { - url: 'https://some.domain.io/app-config.yaml', - }, - { - body: `app: - title: NEW ReMOTe ExaMPLe App - sessionKey: 'abc123' - escaped: \$\${Escaped} - `, - headers: { ETag: uuidv4().toString() }, - }, - { overwriteRoutes: true }, - ); + server.use(reloadHandler); await expect(onChange.promise).resolves.toEqual([ { diff --git a/packages/config-loader/src/loader.ts b/packages/config-loader/src/loader.ts index 2c61b4d1ce..c35b742f92 100644 --- a/packages/config-loader/src/loader.ts +++ b/packages/config-loader/src/loader.ts @@ -56,6 +56,7 @@ export type Remote = { reloadIntervalSeconds: number; }; +/** @public */ export type RemoteConfigProp = { /** * URL of the remote config @@ -111,8 +112,6 @@ export type LoadConfigOptions = { watch?: Watch; }; -const HTTP_RESPONSE_HEADER_ETAG = 'ETag'; - /** * Load configuration data. * @@ -128,18 +127,15 @@ export async function loadConfig( .filter((e): e is { path: string } => e.hasOwnProperty('path')) .map(configTarget => configTarget.path); - let configUrls: string[] = options.configTargets + const configUrls: string[] = options.configTargets .slice() .filter((e): e is { url: string } => e.hasOwnProperty('url')) .map(configTarget => configTarget.url); - const remoteConfigProps: RemoteConfigProp[] = []; - if (remote === undefined && configUrls.length > 0) { - console.warn( + throw new Error( `Remote config detected, however, this feature is turned off. Remote config will be ignored.`, ); - configUrls = []; } // If no paths are provided, we default to reading @@ -183,47 +179,32 @@ export async function loadConfig( const loadRemoteConfigFiles = async () => { const configs: AppConfig[] = []; - const readConfigFromUrl = async (remoteConfigProp: RemoteConfigProp) => { - const response = await fetch(remoteConfigProp.url); + const readConfigFromUrl = async (url: string) => { + const response = await fetch(url); if (!response.ok) { - throw new Error( - `Could not read config file at ${remoteConfigProp.url}`, - ); + throw new Error(`Could not read config file at ${url}`); } - remoteConfigProp.oldETag = remoteConfigProp.newETag ?? undefined; - remoteConfigProp.newETag = - response.headers.get(HTTP_RESPONSE_HEADER_ETAG) ?? undefined; - remoteConfigProp.content = await response.text(); - - return remoteConfigProp; + return await response.text(); }; for (let i = 0; i < configUrls.length; i++) { - const remoteConfigProp = await readConfigFromUrl({ - url: configUrls[i], - content: null, - }); - - if (!isValidUrl(remoteConfigProp.url)) { - throw new Error( - `Config load path is not valid: '${remoteConfigProp.url}'`, - ); + const configUrl = configUrls[i]; + if (!isValidUrl(configUrl)) { + throw new Error(`Config load path is not valid: '${configUrl}'`); } - const dir = configRoot; - if (!remoteConfigProp.content) { + const remoteConfigContent = await readConfigFromUrl(configUrl); + if (!remoteConfigContent) { throw new Error(`Config is not valid`); } - const input = yaml.parse(remoteConfigProp.content); + const configYaml = yaml.parse(remoteConfigContent); const substitutionTransform = createSubstitutionTransform(env); - const data = await applyConfigTransforms(dir, input, [ + const data = await applyConfigTransforms(configRoot, configYaml, [ substitutionTransform, ]); - configs.push({ data, context: remoteConfigProp.url }); - - remoteConfigProps.push(remoteConfigProp); + configs.push({ data, context: configUrl }); } return configs; @@ -277,17 +258,12 @@ export async function loadConfig( }; const watchRemoteConfig = (watchProp: Watch, remoteProp: Remote) => { - const hasConfigChanged = async (remoteConfigProp: RemoteConfigProp) => { - const requestProps = { method: 'HEAD' }; - const { headers } = await fetch(remoteConfigProp.url, requestProps); - remoteConfigProp.oldETag = remoteConfigProp.newETag ?? undefined; - remoteConfigProp.newETag = - headers.get(HTTP_RESPONSE_HEADER_ETAG) ?? undefined; - + const hasConfigChanged = async ( + oldRemoteConfigs: AppConfig[], + newRemoteConfigs: AppConfig[], + ) => { return ( - remoteConfigProp.oldETag !== undefined && - remoteConfigProp.newETag !== undefined && - remoteConfigProp.oldETag !== remoteConfigProp.newETag + JSON.stringify(oldRemoteConfigs) !== JSON.stringify(newRemoteConfigs) ); }; @@ -295,18 +271,12 @@ export async function loadConfig( try { handle = setInterval(async () => { console.info(`Checking for config update`); - for (const remoteConfigProp of remoteConfigProps) { - if (await hasConfigChanged(remoteConfigProp)) { - console.info(`Remote config change, reloading config ...`); - const newRemoteConfigs = await loadRemoteConfigFiles(); - watchProp.onChange([ - ...newRemoteConfigs, - ...fileConfigs, - ...envConfigs, - ]); - console.info(`Remote config reloaded`); - break; - } + const newRemoteConfigs = await loadRemoteConfigFiles(); + if (await hasConfigChanged(remoteConfigs, newRemoteConfigs)) { + remoteConfigs = newRemoteConfigs; + console.info(`Remote config change, reloading config ...`); + watchProp.onChange([...remoteConfigs, ...fileConfigs, ...envConfigs]); + console.info(`Remote config reloaded`); } }, remoteProp.reloadIntervalSeconds * 1000); } catch (error) { diff --git a/yarn.lock b/yarn.lock index e45d685bf8..113b0d48c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14248,13 +14248,6 @@ fetch-blob@2.1.2: resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-2.1.2.tgz#a7805db1361bd44c1ef62bb57fb5fe8ea173ef3c" integrity sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow== -fetch-mock-jest@1.5.1: - version "1.5.1" - resolved "https://registry.npmjs.org/fetch-mock-jest/-/fetch-mock-jest-1.5.1.tgz#0e13df990d286d9239e284f12b279ed509bf53cd" - integrity sha512-+utwzP8C+Pax1GSka3nFXILWMY3Er2L+s090FOgqVNrNCPp0fDqgXnAHAJf12PLHi0z4PhcTaZNTz8e7K3fjqQ== - dependencies: - fetch-mock "^9.11.0" - fetch-mock@^9.11.0: version "9.11.0" resolved "https://registry.npmjs.org/fetch-mock/-/fetch-mock-9.11.0.tgz#371c6fb7d45584d2ae4a18ee6824e7ad4b637a3f" From 8e85a0bca056f8ea245405a7555911e2a001ca66 Mon Sep 17 00:00:00 2001 From: Matto Date: Tue, 26 Oct 2021 21:50:49 +1100 Subject: [PATCH 026/196] Change variable name Signed-off-by: Matto --- packages/config-loader/api-report.md | 12 ++++++------ packages/config-loader/src/index.ts | 7 ++++++- packages/config-loader/src/loader.ts | 22 ++++++++++------------ 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/packages/config-loader/api-report.md b/packages/config-loader/api-report.md index 4ed409e3ce..3b61b67d7b 100644 --- a/packages/config-loader/api-report.md +++ b/packages/config-loader/api-report.md @@ -46,8 +46,8 @@ export type LoadConfigOptions = { configTargets: ConfigTarget[]; env?: string; experimentalEnvFunc?: (name: string) => Promise; - remote?: Remote; - watch?: Watch; + remote?: LoadConfigOptionsRemote; + watch?: LoadConfigOptionsWatch; }; // @public @@ -73,10 +73,10 @@ export function readEnvConfig(env: { [name: string]: string | undefined; }): AppConfig[]; -// Warning: (ae-missing-release-tag) "Remote" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "LoadConfigOptionsRemote" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export type Remote = { +export type LoadConfigOptionsRemote = { reloadIntervalSeconds: number; }; @@ -88,10 +88,10 @@ export type TransformFunc = ( }, ) => T | undefined; -// Warning: (ae-missing-release-tag) "Watch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "LoadConfigOptionsWatch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export type Watch = { +export type LoadConfigOptionsWatch = { onChange: (configs: AppConfig[]) => void; stopSignal?: Promise; }; diff --git a/packages/config-loader/src/index.ts b/packages/config-loader/src/index.ts index ee3e6413ad..97f0d301a3 100644 --- a/packages/config-loader/src/index.ts +++ b/packages/config-loader/src/index.ts @@ -29,4 +29,9 @@ export type { TransformFunc, } from './lib'; export { loadConfig } from './loader'; -export type { ConfigTarget, LoadConfigOptions, Watch, Remote } from './loader'; +export type { + ConfigTarget, + LoadConfigOptions, + LoadConfigOptionsWatch, + LoadConfigOptionsRemote, +} from './loader'; diff --git a/packages/config-loader/src/loader.ts b/packages/config-loader/src/loader.ts index c35b742f92..8c4bbae9d6 100644 --- a/packages/config-loader/src/loader.ts +++ b/packages/config-loader/src/loader.ts @@ -32,7 +32,7 @@ import { isValidUrl } from '@backstage/integration'; export type ConfigTarget = { path: string } | { url: string }; -export type Watch = { +export type LoadConfigOptionsWatch = { /** * A listener that is called when a config file is changed. */ @@ -44,12 +44,7 @@ export type Watch = { stopSignal?: Promise; }; -/** - * Options that control the loading of configuration files in the backend. - * - * @public - */ -export type Remote = { +export type LoadConfigOptionsRemote = { /** * An optional remote config reloading period, in seconds */ @@ -104,12 +99,12 @@ export type LoadConfigOptions = { /** * An optional remote config */ - remote?: Remote; + remote?: LoadConfigOptionsRemote; /** * An optional configuration that enables watching of config files. */ - watch?: Watch; + watch?: LoadConfigOptionsWatch; }; /** @@ -222,13 +217,13 @@ export async function loadConfig( try { remoteConfigs = await loadRemoteConfigFiles(); } catch (error) { - throw new Error(`Failed to read remote configuration file, ${error}`); + throw new ForwardedError(`Failed to read remote configuration file, ${error}`); } } const envConfigs = await readEnvConfig(process.env); - const watchConfigFile = (watchProp: Watch) => { + const watchConfigFile = (watchProp: LoadConfigOptionsWatch) => { const watcher = chokidar.watch(configPaths, { usePolling: process.env.NODE_ENV === 'test', }); @@ -257,7 +252,10 @@ export async function loadConfig( } }; - const watchRemoteConfig = (watchProp: Watch, remoteProp: Remote) => { + const watchRemoteConfig = ( + watchProp: LoadConfigOptionsWatch, + remoteProp: LoadConfigOptionsRemote, + ) => { const hasConfigChanged = async ( oldRemoteConfigs: AppConfig[], newRemoteConfigs: AppConfig[], From 1cecd737f98cc0fca824073af9e0edd9cd75410d Mon Sep 17 00:00:00 2001 From: Matto Date: Wed, 27 Oct 2021 20:33:33 +1100 Subject: [PATCH 027/196] Removed dependency, introduced isValidUrl, and reinstated `configPaths` Signed-off-by: Matto --- packages/backend-common/src/config.ts | 3 +- packages/backend-common/src/urls.test.ts | 34 ++++++++++++++ packages/backend-common/src/urls.ts | 25 +++++++++++ packages/cli/src/lib/config.ts | 3 +- packages/cli/src/lib/urls.test.ts | 34 ++++++++++++++ packages/cli/src/lib/urls.ts | 25 +++++++++++ packages/config-loader/api-report.md | 1 + packages/config-loader/package.json | 1 - packages/config-loader/src/lib/index.ts | 1 + packages/config-loader/src/lib/urls.test.ts | 34 ++++++++++++++ packages/config-loader/src/lib/urls.ts | 25 +++++++++++ packages/config-loader/src/loader.test.ts | 49 ++++++++++++++++++++- packages/config-loader/src/loader.ts | 14 +++++- yarn.lock | 37 ++-------------- 14 files changed, 247 insertions(+), 39 deletions(-) create mode 100644 packages/backend-common/src/urls.test.ts create mode 100644 packages/backend-common/src/urls.ts create mode 100644 packages/cli/src/lib/urls.test.ts create mode 100644 packages/cli/src/lib/urls.ts create mode 100644 packages/config-loader/src/lib/urls.test.ts create mode 100644 packages/config-loader/src/lib/urls.ts diff --git a/packages/backend-common/src/config.ts b/packages/backend-common/src/config.ts index 90131ebbe7..9b0eb8dcd0 100644 --- a/packages/backend-common/src/config.ts +++ b/packages/backend-common/src/config.ts @@ -21,7 +21,7 @@ import { findPaths } from '@backstage/cli-common'; import { Config, ConfigReader } from '@backstage/config'; import { JsonValue } from '@backstage/types'; import { ConfigTarget, loadConfig } from '@backstage/config-loader'; -import { isValidUrl } from '@backstage/integration'; +import { isValidUrl } from './urls'; export class ObservableConfigProxy implements Config { private config: Config = new ConfigReader({}); @@ -162,6 +162,7 @@ export async function loadBackendConfig(options: { const configs = await loadConfig({ configRoot: paths.targetRoot, + configPaths: [], configTargets: configTargets, watch: { onChange(newConfigs) { diff --git a/packages/backend-common/src/urls.test.ts b/packages/backend-common/src/urls.test.ts new file mode 100644 index 0000000000..c2a67fb849 --- /dev/null +++ b/packages/backend-common/src/urls.test.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { isValidUrl } from './urls'; + +describe('isValidUrl', () => { + it('should return true for url', () => { + const validUrl = isValidUrl('http://some.valid.url'); + expect(validUrl).toBe(true); + }); + + it('should return false for absolute path', () => { + const validUrl = isValidUrl('/some/absolute/path'); + expect(validUrl).toBe(false); + }); + + it('should return false for relative path', () => { + const validUrl = isValidUrl('../some/relative/path'); + expect(validUrl).toBe(false); + }); +}); diff --git a/packages/backend-common/src/urls.ts b/packages/backend-common/src/urls.ts new file mode 100644 index 0000000000..848cea25d9 --- /dev/null +++ b/packages/backend-common/src/urls.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export function isValidUrl(url: string): boolean { + try { + // eslint-disable-next-line no-new + new URL(url); + return true; + } catch { + return false; + } +} diff --git a/packages/cli/src/lib/config.ts b/packages/cli/src/lib/config.ts index 79d30ee220..80b374039f 100644 --- a/packages/cli/src/lib/config.ts +++ b/packages/cli/src/lib/config.ts @@ -21,7 +21,7 @@ import { } from '@backstage/config-loader'; import { ConfigReader } from '@backstage/config'; import { paths } from './paths'; -import { isValidUrl } from '@backstage/integration'; +import { isValidUrl } from './urls'; type Options = { args: string[]; @@ -58,6 +58,7 @@ export async function loadCliConfig(options: Options) { ? async name => process.env[name] || 'x' : undefined, configRoot: paths.targetRoot, + configPaths: [], configTargets: configTargets, }); diff --git a/packages/cli/src/lib/urls.test.ts b/packages/cli/src/lib/urls.test.ts new file mode 100644 index 0000000000..c2a67fb849 --- /dev/null +++ b/packages/cli/src/lib/urls.test.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { isValidUrl } from './urls'; + +describe('isValidUrl', () => { + it('should return true for url', () => { + const validUrl = isValidUrl('http://some.valid.url'); + expect(validUrl).toBe(true); + }); + + it('should return false for absolute path', () => { + const validUrl = isValidUrl('/some/absolute/path'); + expect(validUrl).toBe(false); + }); + + it('should return false for relative path', () => { + const validUrl = isValidUrl('../some/relative/path'); + expect(validUrl).toBe(false); + }); +}); diff --git a/packages/cli/src/lib/urls.ts b/packages/cli/src/lib/urls.ts new file mode 100644 index 0000000000..848cea25d9 --- /dev/null +++ b/packages/cli/src/lib/urls.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export function isValidUrl(url: string): boolean { + try { + // eslint-disable-next-line no-new + new URL(url); + return true; + } catch { + return false; + } +} diff --git a/packages/config-loader/api-report.md b/packages/config-loader/api-report.md index 3b61b67d7b..be4678aa0f 100644 --- a/packages/config-loader/api-report.md +++ b/packages/config-loader/api-report.md @@ -43,6 +43,7 @@ export function loadConfig(options: LoadConfigOptions): Promise; // @public export type LoadConfigOptions = { configRoot: string; + configPaths: string[]; configTargets: ConfigTarget[]; env?: string; experimentalEnvFunc?: (name: string) => Promise; diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 456bca90ad..58b0061996 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -30,7 +30,6 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/integration": "^0.6.5", "@backstage/cli-common": "^0.1.4", "@backstage/config": "^0.1.9", "@backstage/errors": "^0.1.3", diff --git a/packages/config-loader/src/lib/index.ts b/packages/config-loader/src/lib/index.ts index 32a0191cae..ca88b771ba 100644 --- a/packages/config-loader/src/lib/index.ts +++ b/packages/config-loader/src/lib/index.ts @@ -17,3 +17,4 @@ export { readEnvConfig } from './env'; export * from './transform'; export * from './schema'; +export { isValidUrl } from './urls'; diff --git a/packages/config-loader/src/lib/urls.test.ts b/packages/config-loader/src/lib/urls.test.ts new file mode 100644 index 0000000000..c2a67fb849 --- /dev/null +++ b/packages/config-loader/src/lib/urls.test.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { isValidUrl } from './urls'; + +describe('isValidUrl', () => { + it('should return true for url', () => { + const validUrl = isValidUrl('http://some.valid.url'); + expect(validUrl).toBe(true); + }); + + it('should return false for absolute path', () => { + const validUrl = isValidUrl('/some/absolute/path'); + expect(validUrl).toBe(false); + }); + + it('should return false for relative path', () => { + const validUrl = isValidUrl('../some/relative/path'); + expect(validUrl).toBe(false); + }); +}); diff --git a/packages/config-loader/src/lib/urls.ts b/packages/config-loader/src/lib/urls.ts new file mode 100644 index 0000000000..848cea25d9 --- /dev/null +++ b/packages/config-loader/src/lib/urls.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export function isValidUrl(url: string): boolean { + try { + // eslint-disable-next-line no-new + new URL(url); + return true; + } catch { + return false; + } +} diff --git a/packages/config-loader/src/loader.test.ts b/packages/config-loader/src/loader.test.ts index e8b5a8828b..804254afa0 100644 --- a/packages/config-loader/src/loader.test.ts +++ b/packages/config-loader/src/loader.test.ts @@ -67,6 +67,13 @@ describe('loadConfig', () => { $file: secrets/session-key.txt escaped: \$\${Escaped} `, + '/root/app-config2.yaml': ` + app: + title: Example App 2 + sessionKey: + $file: secrets/session-key.txt + escaped: \$\${Escaped} + `, '/root/app-config.development.yaml': ` app: sessionKey: development-key @@ -111,6 +118,7 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', + configPaths: [], configTargets: [], env: 'production', }), @@ -136,6 +144,7 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', + configPaths: [], configTargets: [{ url: configUrl }], env: 'production', remote: { @@ -156,10 +165,43 @@ describe('loadConfig', () => { ]); }); - it('loads config with secrets', async () => { + it('loads config with secrets from two different files', async () => { await expect( loadConfig({ configRoot: '/root', + configPaths: ['/root/app-config2.yaml'], + configTargets: [{ path: '/root/app-config.yaml' }], + env: 'production', + }), + ).resolves.toEqual([ + { + context: 'app-config.yaml', + data: { + app: { + title: 'Example App', + sessionKey: 'abc123', + escaped: '${Escaped}', + }, + }, + }, + { + context: 'app-config2.yaml', + data: { + app: { + title: 'Example App 2', + sessionKey: 'abc123', + escaped: '${Escaped}', + }, + }, + }, + ]); + }); + + it('loads config with secrets from single file', async () => { + await expect( + loadConfig({ + configRoot: '/root', + configPaths: ['/root/app-config.yaml'], configTargets: [{ path: '/root/app-config.yaml' }], env: 'production', }), @@ -181,6 +223,7 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', + configPaths: [], configTargets: [ { path: '/root/app-config.yaml' }, { path: '/root/app-config.development.yaml' }, @@ -221,6 +264,7 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', + configPaths: [], configTargets: [{ path: '/root/app-config.substitute.yaml' }], env: 'development', }), @@ -246,6 +290,7 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', + configPaths: [], configTargets: [], watch: { onChange: onChange.resolve, @@ -294,6 +339,7 @@ describe('loadConfig', () => { await expect( loadConfig({ configRoot: '/root', + configPaths: [], configTargets: [{ url: configUrl }], watch: { onChange: onChange.resolve, @@ -339,6 +385,7 @@ describe('loadConfig', () => { await loadConfig({ configRoot: '/root', + configPaths: [], configTargets: [], watch: { onChange: () => { diff --git a/packages/config-loader/src/loader.ts b/packages/config-loader/src/loader.ts index 8c4bbae9d6..338f199132 100644 --- a/packages/config-loader/src/loader.ts +++ b/packages/config-loader/src/loader.ts @@ -25,10 +25,10 @@ import { createIncludeTransform, createSubstitutionTransform, EnvFunc, + isValidUrl, readEnvConfig, } from './lib'; import fetch from 'node-fetch'; -import { isValidUrl } from '@backstage/integration'; export type ConfigTarget = { path: string } | { url: string }; @@ -83,6 +83,11 @@ export type LoadConfigOptions = { // The root directory of the config loading context. Used to find default configs. configRoot: string; + /** Absolute paths to load config files from. Configs from earlier paths have lower priority. + * @deprecated Use {@link configTargets} instead. + */ + configPaths: string[]; + // Paths to load config files from. Configs from earlier paths have lower priority. configTargets: ConfigTarget[]; @@ -122,6 +127,13 @@ export async function loadConfig( .filter((e): e is { path: string } => e.hasOwnProperty('path')) .map(configTarget => configTarget.path); + // Append deprecated configPaths to the absolute config paths received via configTargets. + options.configPaths.forEach(cp => { + if (!configPaths.includes(cp)) { + configPaths.push(cp); + } + }); + const configUrls: string[] = options.configTargets .slice() .filter((e): e is { url: string } => e.hasOwnProperty('url')) diff --git a/yarn.lock b/yarn.lock index 113b0d48c2..079cd96a5c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11555,11 +11555,6 @@ core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.10: resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== -core-js@^3.0.0: - version "3.18.3" - resolved "https://registry.npmjs.org/core-js/-/core-js-3.18.3.tgz#86a0bba2d8ec3df860fefcc07a8d119779f01509" - integrity sha512-tReEhtMReZaPFVw7dajMx0vlsz3oOb8ajgPoHVYGxr8ErnZ6PcYEvvmjGmXlfpnxpkYSdOQttjB+MvVbCGfvLw== - core-js@^3.0.4, core-js@^3.6.5, core-js@^3.8.2: version "3.15.0" resolved "https://registry.npmjs.org/core-js/-/core-js-3.15.0.tgz#db9554ebce0b6fd90dc9b1f2465c841d2d055044" @@ -14248,22 +14243,6 @@ fetch-blob@2.1.2: resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-2.1.2.tgz#a7805db1361bd44c1ef62bb57fb5fe8ea173ef3c" integrity sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow== -fetch-mock@^9.11.0: - version "9.11.0" - resolved "https://registry.npmjs.org/fetch-mock/-/fetch-mock-9.11.0.tgz#371c6fb7d45584d2ae4a18ee6824e7ad4b637a3f" - integrity sha512-PG1XUv+x7iag5p/iNHD4/jdpxL9FtVSqRMUQhPab4hVDt80T1MH5ehzVrL2IdXO9Q2iBggArFvPqjUbHFuI58Q== - dependencies: - "@babel/core" "^7.0.0" - "@babel/runtime" "^7.0.0" - core-js "^3.0.0" - debug "^4.1.1" - glob-to-regexp "^0.4.0" - is-subset "^0.1.1" - lodash.isequal "^4.5.0" - path-to-regexp "^2.2.1" - querystring "^0.2.0" - whatwg-url "^6.5.0" - fetch-readablestream@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/fetch-readablestream/-/fetch-readablestream-0.2.0.tgz#eaa6d1a76b12de2d4731a343393c6ccdcfe2c795" @@ -15088,7 +15067,7 @@ glob-to-regexp@^0.3.0: resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -glob-to-regexp@^0.4.0, glob-to-regexp@^0.4.1: +glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== @@ -17129,11 +17108,6 @@ is-subdir@^1.1.1: dependencies: better-path-resolve "1.0.0" -is-subset@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" - integrity sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY= - is-svg@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" @@ -19002,7 +18976,7 @@ lodash.isempty@^4.4.0: resolved "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" integrity sha1-b4bL7di+TsmHvpqvM8loTbGzHn4= -lodash.isequal@^4.0.0, lodash.isequal@^4.5.0: +lodash.isequal@^4.0.0: version "4.5.0" resolved "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= @@ -22191,11 +22165,6 @@ path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" -path-to-regexp@^2.2.1: - version "2.4.0" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz#35ce7f333d5616f1c1e1bfe266c3aba2e5b2e704" - integrity sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w== - path-type@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -28732,7 +28701,7 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -whatwg-url@^6.4.1, whatwg-url@^6.5.0: +whatwg-url@^6.4.1: version "6.5.0" resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== From 83e462c0be0c43179cfb17e07b8361b5cbce5909 Mon Sep 17 00:00:00 2001 From: Kenneth Feng Date: Thu, 28 Oct 2021 21:39:43 -0400 Subject: [PATCH 028/196] plugins/scaffolder-backend: Fix a bug where only file mode 775 is considered executable. Due to the default umask 002 on Ubuntu, the executable files checked out by Git have file mode 100775 (33277), which by mistake, was assumed to be the only possible file mode produced by Git. This bug was introduced in https://github.com/backstage/backstage/pull/7738. There are other possible file modes, such as 100700, 100755, which should be considered executable. This behavior is consistent with how Git behaves. Git only tracks whether a file is executable. This pull request considers a file to be executable as long as there is an execute bit in owner, group, or everyone. Signed-off-by: Kenneth Feng --- .../builtin/publish/githubPullRequest.test.ts | 80 +++++++++++++++++-- .../builtin/publish/githubPullRequest.ts | 7 +- .../actions/builtin/publish/util.test.ts | 32 +++++++- .../actions/builtin/publish/util.ts | 5 ++ 4 files changed, 114 insertions(+), 10 deletions(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.test.ts index 27c6f1e59d..0d4deb87e0 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.test.ts @@ -254,7 +254,7 @@ describe('createPublishGithubPullRequestAction', () => { }); }); - describe('with executable file', () => { + describe('with executable file mode 755', () => { let input: GithubPullRequestActionInput; let ctx: ActionContext; @@ -268,9 +268,9 @@ describe('createPublishGithubPullRequestAction', () => { mockFs({ [workspacePath]: { - 'file.txt': mockFs.file({ - content: 'Hello there!', - mode: 33277, // File mode: 100755 + 'hello.sh': mockFs.file({ + content: 'echo Hello there!', + mode: 0o100755, }), }, }); @@ -297,8 +297,76 @@ describe('createPublishGithubPullRequestAction', () => { { commit: 'Create my new app', files: { - 'file.txt': { - content: Buffer.from('Hello there!').toString('base64'), + 'hello.sh': { + content: Buffer.from('echo Hello there!').toString('base64'), + encoding: 'base64', + mode: '100755', + }, + }, + }, + ], + }); + }); + + it('creates outputs for the url', async () => { + await instance.handler(ctx); + + expect(ctx.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://github.com/myorg/myrepo/pull/123', + ); + }); + afterEach(() => { + mockFs.restore(); + jest.resetAllMocks(); + }); + }); + + describe('with executable file mode 775', () => { + let input: GithubPullRequestActionInput; + let ctx: ActionContext; + + beforeEach(() => { + input = { + repoUrl: 'github.com?owner=myorg&repo=myrepo', + title: 'Create my new app', + branchName: 'new-app', + description: 'This PR is really good', + }; + + mockFs({ + [workspacePath]: { + 'hello.sh': mockFs.file({ + content: 'echo Hello there!', + mode: 0o100775, + }), + }, + }); + + ctx = { + createTemporaryDirectory: jest.fn(), + output: jest.fn(), + logger: getRootLogger(), + logStream: new Writable(), + input, + workspacePath, + }; + }); + it('creates a pull request', async () => { + await instance.handler(ctx); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'myorg', + repo: 'myrepo', + title: 'Create my new app', + head: 'new-app', + body: 'This PR is really good', + changes: [ + { + commit: 'Create my new app', + files: { + 'hello.sh': { + content: Buffer.from('echo Hello there!').toString('base64'), encoding: 'base64', mode: '100755', }, diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts index e147f49b23..0cceb47d71 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts @@ -16,7 +16,7 @@ import fs from 'fs-extra'; import path from 'path'; -import { parseRepoUrl } from './util'; +import { parseRepoUrl, isExecutable } from './util'; import { GithubCredentialsProvider, @@ -202,10 +202,11 @@ export const createPublishGithubPullRequestAction = ({ .readFileSync(absPath) .toString('base64'); const fileStat = fs.statSync(absPath); - const isExecutable = fileStat.mode === 33277; // aka. 100755 // See the properties of tree items // in https://docs.github.com/en/rest/reference/git#trees - const githubTreeItemMode = isExecutable ? '100755' : '100644'; + const githubTreeItemMode = isExecutable(fileStat.mode) + ? '100755' + : '100644'; // Always use base64 encoding to avoid doubling a binary file in size // due to interpreting a binary file as utf-8 and sending github // the utf-8 encoded content. diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.test.ts index f179289624..cc879d4f55 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.test.ts @@ -15,7 +15,7 @@ */ import path from 'path'; -import { getRepoSourceDirectory } from './util'; +import { getRepoSourceDirectory, isExecutable } from './util'; describe('getRepoSourceDirectory', () => { it('should return workspace root if no sub folder is given', () => { @@ -55,3 +55,33 @@ describe('getRepoSourceDirectory', () => { ).toEqual(path.join('/', 'var', 'workspace', 'absolute', 'secret')); }); }); + +describe('isExecutable', () => { + it('should return true for file mode 777', () => { + expect(isExecutable(0o100777)).toBe(true); + }); + it('should return true for file mode 775', () => { + expect(isExecutable(0o100775)).toBe(true); + }); + it('should return true for file mode 755', () => { + expect(isExecutable(0o100755)).toBe(true); + }); + it('should return true for file mode 700', () => { + expect(isExecutable(0o100700)).toBe(true); + }); + it('should return true for file mode 770', () => { + expect(isExecutable(0o100770)).toBe(true); + }); + it('should return true for file mode 670', () => { + expect(isExecutable(0o100670)).toBe(true); + }); + it('should return false for file mode 644', () => { + expect(isExecutable(0o100644)).toBe(false); + }); + it('should return false for file mode 600', () => { + expect(isExecutable(0o100600)).toBe(false); + }); + it('should return false for file mode 640', () => { + expect(isExecutable(0o100640)).toBe(false); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.ts index 58ba5bfdd1..49156e7d97 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.ts @@ -101,3 +101,8 @@ export const parseRepoUrl = ( return { host, owner, repo, organization, workspace, project }; }; +export const isExecutable = (fileMode: number) => { + const executeBitMask = 0o000111; + const res = fileMode & executeBitMask; + return res > 0; +}; From a794c341ca7f9207affccd06d75f7d530fa27c30 Mon Sep 17 00:00:00 2001 From: Kenneth Feng Date: Thu, 28 Oct 2021 22:20:34 -0400 Subject: [PATCH 029/196] add changeset Signed-off-by: Kenneth Feng --- .changeset/wet-pears-thank.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/wet-pears-thank.md diff --git a/.changeset/wet-pears-thank.md b/.changeset/wet-pears-thank.md new file mode 100644 index 0000000000..f9b5576fb3 --- /dev/null +++ b/.changeset/wet-pears-thank.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Fix a bug where only file mode 775 is considered an executable From 5b51907cbf2da15f03a9e9fccd4ec381b80fd9f9 Mon Sep 17 00:00:00 2001 From: Matto Date: Fri, 29 Oct 2021 14:56:31 +1100 Subject: [PATCH 030/196] resolved conflicts Signed-off-by: Matto --- packages/backend-common/src/config.ts | 16 +++++++++++++++ packages/config-loader/src/loader.ts | 29 ++++----------------------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/packages/backend-common/src/config.ts b/packages/backend-common/src/config.ts index 9b0eb8dcd0..5b09d66608 100644 --- a/packages/backend-common/src/config.ts +++ b/packages/backend-common/src/config.ts @@ -84,51 +84,66 @@ export class ObservableConfigProxy implements Config { has(key: string): boolean { return this.select(false)?.has(key) ?? false; } + keys(): string[] { return this.select(false)?.keys() ?? []; } + get(key?: string): T { return this.select(true).get(key); } + getOptional(key?: string): T | undefined { return this.select(false)?.getOptional(key); } + getConfig(key: string): Config { return new ObservableConfigProxy(this.logger, this, key); } + getOptionalConfig(key: string): Config | undefined { if (this.select(false)?.has(key)) { return new ObservableConfigProxy(this.logger, this, key); } return undefined; } + getConfigArray(key: string): Config[] { return this.select(true).getConfigArray(key); } + getOptionalConfigArray(key: string): Config[] | undefined { return this.select(false)?.getOptionalConfigArray(key); } + getNumber(key: string): number { return this.select(true).getNumber(key); } + getOptionalNumber(key: string): number | undefined { return this.select(false)?.getOptionalNumber(key); } + getBoolean(key: string): boolean { return this.select(true).getBoolean(key); } + getOptionalBoolean(key: string): boolean | undefined { return this.select(false)?.getOptionalBoolean(key); } + getString(key: string): string { return this.select(true).getString(key); } + getOptionalString(key: string): string | undefined { return this.select(false)?.getOptionalString(key); } + getStringArray(key: string): string[] { return this.select(true).getStringArray(key); } + getOptionalStringArray(key: string): string[] | undefined { return this.select(false)?.getOptionalStringArray(key); } @@ -184,6 +199,7 @@ export async function loadBackendConfig(options: { } }), }, + remote: { reloadIntervalSeconds: 10 }, }); options.logger.info( diff --git a/packages/config-loader/src/loader.ts b/packages/config-loader/src/loader.ts index 338f199132..b8fab12917 100644 --- a/packages/config-loader/src/loader.ts +++ b/packages/config-loader/src/loader.ts @@ -24,7 +24,6 @@ import { applyConfigTransforms, createIncludeTransform, createSubstitutionTransform, - EnvFunc, isValidUrl, readEnvConfig, } from './lib'; @@ -51,29 +50,6 @@ export type LoadConfigOptionsRemote = { reloadIntervalSeconds: number; }; -/** @public */ -export type RemoteConfigProp = { - /** - * URL of the remote config - */ - url: string; - - /** - * Contents of the remote config - */ - content: string | null; - - /** - * An optional new ETag header value. Used when checking for updated config. - */ - newETag?: string; - - /** - * An optional old ETag header value. Used when checking for updated config - */ - oldETag?: string; -}; - /** * Options that control the loading of configuration files in the backend. * @@ -229,7 +205,10 @@ export async function loadConfig( try { remoteConfigs = await loadRemoteConfigFiles(); } catch (error) { - throw new ForwardedError(`Failed to read remote configuration file, ${error}`); + throw new ForwardedError( + `Failed to read remote configuration file`, + error, + ); } } From 5cce78f968099c3701303fbb035918c8914e50c0 Mon Sep 17 00:00:00 2001 From: Matto Date: Fri, 29 Oct 2021 15:11:54 +1100 Subject: [PATCH 031/196] Resolved conflicts Signed-off-by: Matto --- .changeset/giant-years-help.md | 1 - ADOPTERS.md | 122 +++++++++++++------------- packages/backend-common/src/config.ts | 15 ---- packages/cli/package.json | 4 - packages/config-loader/package.json | 1 - packages/integration/api-report.md | 5 -- packages/integration/src/index.ts | 1 - 7 files changed, 61 insertions(+), 88 deletions(-) diff --git a/.changeset/giant-years-help.md b/.changeset/giant-years-help.md index f0ca12ef19..c6ca0def30 100644 --- a/.changeset/giant-years-help.md +++ b/.changeset/giant-years-help.md @@ -2,7 +2,6 @@ '@backstage/backend-common': patch '@backstage/cli': patch '@backstage/config-loader': patch -'@backstage/integration': patch --- Reading app config from a remote server diff --git a/ADOPTERS.md b/ADOPTERS.md index 2d2583be95..db751bfb9b 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -1,61 +1,61 @@ -| Organization | Contact | Description of Use | -| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [Spotify](https://www.spotify.com) | [@leemills83](https://github.com/leemills83) | Main interface towards all of Spotify's infrastructure and technical documentation. | -| [bol.com](https://www.bol.com) | [@sagacity](https://github.com/sagacity) | Initial work being done to unify platform tooling. | -| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. | -| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up | -| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. | -| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. | -| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. | -| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications | -| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. | -| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | -| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D | -| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling | -| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling | -| [Telenor Sweden](https://www.telenor.se) | [@O5ten](https://github.com/O5ten) | Building a developer portal for scaffolding projects towards our unified build environment and microservice stacks | -| [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. | -| [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. | -| [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. | -| [Expedia Group](https://www.expediagroup.com) | [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo), [Erik Lindgren](https://github.com/lindgren) | EG Common Developer Toolkit | -| [Paddle.com](https://paddle.com) | [Ioannis Georgoulas](https://github.com/geototti21) | Developer portal (Tech Docs, Service Catalog, Internal Tooling), we use vanilla Backstage FE and custom BE implementation in Go | -| [Acast.com](https://acast.com) | [Olle Lundberg](https://github.com/lndbrg) | Developer portal with tech docs, service catalog and a bunch of other internal tooling | -| [Lunar](https://lunar.app) | [Jacob Valdemar](https://github.com/JacobValdemar) | Internal developer portal for service overview and insights, API documentation, technical guides, onboarding guides and RFC's. | -| [Trendyol](https://trendyol.com) | [Gamze Senturk](https://github.com/gmzsenturk), [Mert Can Bilgic](https://github.com/mertcb) | The Developer Portal has been called `Pandora`. Provides an overview of Trendyol tech ecosystem. TechDocs, Catalog, Custom Plugins and Theme. | -| [Peloton](https://www.onepeloton.com/) | [Jim Haughwout](https://github.com/JimHaughwout) | Creating our first developer portal and tech-docs. Exploring Service Catalog, Tech Insights and Cost Insights as well. | -| [TELUS](https://telus.com) | [Seb Barre](https://github.com/sbarre) | The Go-to place to find answers about development and delivery at TELUS. | -| [Brex](https://www.brex.com/) | [Vamsi Chitters](https://github.com/vamsikc) | A centralized UI to understand how a service fits in the whole Brex architecture and manage a team’s engineering dependencies. | -| [Oriflame](https://www.oriflame.com/) | [Oriflame](https://github.com/oriflame) | Internal developer portal for services, single page apps and packages overview, API documentation, technical guides, tech-radar and more. | -| [Booz Allen Hamilton](https://www.boozallen.com/) | [Jason Miller](https://github.com/JasonMiller-BAH) | Developer portal for a full-stack software development ecosystem that accelerates consistent and repeatable Modern Software Development practices for internal innovation and investments. | -| [Netflix](https://www.netflix.com/) | [bleathem](https://github.com/bleathem) | Our Backstage implementation will be the front door to a unified experience connecting our internal platform products across important workflows with integrated knowledge and support. | -| [b.well](https://www.icanbwell.com/) | [Jacob Rosales](https://github.com/jrosales) | Foundation for our engineering portal and cloud insights. | -| [PagerDuty](https://www.pagerduty.com/) | [Mark Shaw](https://github.com/markshawtoronto) | Developer portal, initially focused on software templates and tech-docs. | -| [MoonShiner](https://moonshiner.at) | [Fabian Hippmann](https://github.com/FabianHippmann) | Developer portal - helps us keep track of our customer projects, onboard new developers & improve our development process 🌕🚀🧑‍🚀 | -| [FundApps](https://www.fundapps.co/) | [Elliot Greenwood](https://github.com/egnwd) | Developer Portal - A place for us to keep track of our projects and documentation for all services and processes | -| [DAZN](https://dazn.com/) | [Lou Bichard](https://twitter.com/loujaybee), [Marco Crivellaro](https://github.com/crivetechie), [Alex Hollerith](mailto:alex.hollerith@dazn.com) | Ingesting all of DAZN's repos for the catalog, migrating our internal platform apps (pull request boards, release information, inner source marketplace etc) to Backstage plugins (where applicable). | -| [HelloFresh](https://www.hellofresh.de/) | [@iammuho](https://github.com/iammuho), [@ElenaForester](https://github.com/ElenaForester), [@diegomarangoni](https://github.com/diegomarangoni) | Our developer portal at HelloFresh - Spread across an organisation of 500+ engineers globally. | -| [FactSet](https://www.factset.com/) | [@kuangp](https://github.com/kuangp) | Developer portal to provide discoverability to all internal components, APIs, documentation, and scaffold templates with integrations to our internal infrastructure tools. | -| [Workrise](https://www.workrise.com/) | [Michael Rode](https://github.com/michaelrode) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | -| [RedVentures](https://www.redventures.com/) | [Chris Diaz](https://github.com/codingdiaz) | Developer portal that brings everything an engineer needs to provide value into a single pane of glass. | -| [MavTek](https://www.mavtek.com/) | [@fgascon](https://github.com/fgascon) | Developer portal focused on standardizing practices, centralizing documentation and streamlining developer practices. | -| [QuintoAndar](https://www.quintoandar.com.br/) | [@quintoandar](https://github.com/quintoandar) | Developer portal, services catalog and centralization of service metrics. | -| [empathy.co](https://empathy.co/) | [@guillermotti](https://github.com/guillermotti) | Developer portal for tech docs, service catalog, plugin discovery and much more. | -| [creditas.com](https://creditas.com/) | [@aureliosaraiva](https://github.com/aureliosaraiva) [@Creditas](https://github.com/creditas) | Centralization of all services, standards, documentation, etc. We started the deployment process. | -| [Prisjakt](https://www.prisjakt.nu) / [PriceSpy](https://pricespy.co.uk) | [@kennylindahl](https://github.com/kennylindahl) | Internal developer portal - Documentation, scaffolding, software catalog, TechRadar, Gitlab org data integration | -| [Powerspike](https://powerspike.tv/) | [@trelore](https://github.com/trelore) | Developer portal for documentation of core libraries and repositories. | -| [2U](https://2u.com) | [Andrew Thal](https://github.com/athal7) | Development team home-base, promoting service discoverability, resource dependencies, and tech radar | -| [Taxfix](https://taxfix.de/) | [Sami Ur Rehman](https://github.com/samiurrehman92) | Developer's portal with software catalog at it's core. Hosts API Specs, Tech Docs, Tech Radar and some custom plugins. | -| [Busuu](https://busuu.com/) | [Adam Tester](https://github.com/adamtester) | Developer portal with service catalog, API docs, Event docs, service templating, and cost insights. | -| [Loadsmart](https://loadsmart.com/) | [Loadsmart](https://github.com/loadsmart) | Improve services visibility and operations for service owners and developers. | -| [Monzo](https://monzo.com/) | [@WillSewell](https://github.com/WillSewell), [@joechrisellis](https://github.com/joechrisellis) | Developer portal showing metadata and docs for over 2000 microservices. We have built a number of plugins such as a UI for our system to measure [software excellence](https://monzo.com/blog/2021/09/15/how-we-measure-software-excellence), and a UI to show deployment and config change events. | -| [Vaimo](https://www.vaimo.com) | [@vaimo-magnus](https://github.com/vaimo-magnus) | Developer Portal for our developers at Vaimo, currently docs and self-service towards our internal PaaS based on k8s. Plans to extend the catalog into Projects, Environments etc | -| [Wayfair](https://www.wayfair.com) | [@fransan](https://github.com/fransan), [@errskipower](https://github.com/errskipower), [@hrrs](https://github.com/hrrs) | Developer portal for service catalog, technical documentation, and APIs. | -| [CircleHD](https://www.circlehd.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | -| [CastDesk](https://castdesk.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | -| [Santagostino](https://santagostino.it) | [@santagostino](https://github.com/santagostino) | Developer portal, gateway to our infrastructure, documentation, service catalog and internal tooling. | -| [Peak](https://peak.ai) | [Luke Beamish](https://github.com/lukebeamish-peak) | Developer portal for all internal engineers to access documentation and tooling. | -| [Gelato](https://gelato.com/) | [Dmitry Makarenko](https://github.com/dmitry-makarenko-gelato) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal and third-party systems🚀. | -| [GoCardless](https://gocardless.com/) | [James Turley](https://github.com/tragiclifestories) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal systems. | -| [Box](https://www.box.com) | [@kielosz](https://github.com/kielosz), [@jluk-box](https://github.com/jluk-box), [@ptychu](https://github.com/ptychu), [@alexrybch](https://github.com/alexrybch), [@szubster](https://github.com/szubster) | Developer portal for service catalog, integration with internal systems, new service onboarding. | -| [Bazaarvoice](https://www.bazaarvoice.com) | [@niallmccullagh](https://github.com/niallmccullagh) | Developer portal for service catalog and scaffolds, publishing Github docs and API documentation, visualising our internal tech radar and our product engineering org structure. | -| [Krateo PlatformOps](https://www.krateo.io) | [@projectkerberus](https://github.com/projectkerberus) | A multi-cloud control plane to create, manage and deploy any kind of resource easily and centrally via a Developer Portal that centralizes via a self-service catalog the templating and ownership of services, the available documentation, the overview of the components that compose an entire domain and all the data of the service lifecycle. | +| Organization | Contact | Description of Use | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [Spotify](https://www.spotify.com) | [@leemills83](https://github.com/leemills83) | Main interface towards all of Spotify's infrastructure and technical documentation. | +| [bol.com](https://www.bol.com) | [@sagacity](https://github.com/sagacity) | Initial work being done to unify platform tooling. | +| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. | +| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up | +| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. | +| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. | +| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. | +| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications | +| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. | +| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | +| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D | +| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling | +| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling | +| [Telenor Sweden](https://www.telenor.se) | [@O5ten](https://github.com/O5ten) | Building a developer portal for scaffolding projects towards our unified build environment and microservice stacks | +| [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. | +| [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. | +| [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. | +| [Expedia Group](https://www.expediagroup.com) | [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo), [Erik Lindgren](https://github.com/lindgren) | EG Common Developer Toolkit | +| [Paddle.com](https://paddle.com) | [Ioannis Georgoulas](https://github.com/geototti21) | Developer portal (Tech Docs, Service Catalog, Internal Tooling), we use vanilla Backstage FE and custom BE implementation in Go | +| [Acast.com](https://acast.com) | [Olle Lundberg](https://github.com/lndbrg) | Developer portal with tech docs, service catalog and a bunch of other internal tooling | +| [Lunar](https://lunar.app) | [Jacob Valdemar](https://github.com/JacobValdemar) | Internal developer portal for service overview and insights, API documentation, technical guides, onboarding guides and RFC's. | +| [Trendyol](https://trendyol.com) | [Gamze Senturk](https://github.com/gmzsenturk), [Mert Can Bilgic](https://github.com/mertcb) | The Developer Portal has been called `Pandora`. Provides an overview of Trendyol tech ecosystem. TechDocs, Catalog, Custom Plugins and Theme. | +| [Peloton](https://www.onepeloton.com/) | [Jim Haughwout](https://github.com/JimHaughwout) | Creating our first developer portal and tech-docs. Exploring Service Catalog, Tech Insights and Cost Insights as well. | +| [TELUS](https://telus.com) | [Seb Barre](https://github.com/sbarre) | The Go-to place to find answers about development and delivery at TELUS. | +| [Brex](https://www.brex.com/) | [Vamsi Chitters](https://github.com/vamsikc) | A centralized UI to understand how a service fits in the whole Brex architecture and manage a team’s engineering dependencies. | +| [Oriflame](https://www.oriflame.com/) | [Oriflame](https://github.com/oriflame) | Internal developer portal for services, single page apps and packages overview, API documentation, technical guides, tech-radar and more. | +| [Booz Allen Hamilton](https://www.boozallen.com/) | [Jason Miller](https://github.com/JasonMiller-BAH) | Developer portal for a full-stack software development ecosystem that accelerates consistent and repeatable Modern Software Development practices for internal innovation and investments. | +| [Netflix](https://www.netflix.com/) | [bleathem](https://github.com/bleathem) | Our Backstage implementation will be the front door to a unified experience connecting our internal platform products across important workflows with integrated knowledge and support. | +| [b.well](https://www.icanbwell.com/) | [Jacob Rosales](https://github.com/jrosales) | Foundation for our engineering portal and cloud insights. | +| [PagerDuty](https://www.pagerduty.com/) | [Mark Shaw](https://github.com/markshawtoronto) | Developer portal, initially focused on software templates and tech-docs. | +| [MoonShiner](https://moonshiner.at) | [Fabian Hippmann](https://github.com/FabianHippmann) | Developer portal - helps us keep track of our customer projects, onboard new developers & improve our development process 🌕🚀🧑‍🚀 | +| [FundApps](https://www.fundapps.co/) | [Elliot Greenwood](https://github.com/egnwd) | Developer Portal - A place for us to keep track of our projects and documentation for all services and processes | +| [DAZN](https://dazn.com/) | [Lou Bichard](https://twitter.com/loujaybee), [Marco Crivellaro](https://github.com/crivetechie), [Alex Hollerith](mailto:alex.hollerith@dazn.com) | Ingesting all of DAZN's repos for the catalog, migrating our internal platform apps (pull request boards, release information, inner source marketplace etc) to Backstage plugins (where applicable). | +| [HelloFresh](https://www.hellofresh.de/) | [@iammuho](https://github.com/iammuho), [@ElenaForester](https://github.com/ElenaForester), [@diegomarangoni](https://github.com/diegomarangoni) | Our developer portal at HelloFresh - Spread across an organisation of 500+ engineers globally. | +| [FactSet](https://www.factset.com/) | [@kuangp](https://github.com/kuangp) | Developer portal to provide discoverability to all internal components, APIs, documentation, and scaffold templates with integrations to our internal infrastructure tools. | +| [Workrise](https://www.workrise.com/) | [Michael Rode](https://github.com/michaelrode) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | +| [RedVentures](https://www.redventures.com/) | [Chris Diaz](https://github.com/codingdiaz) | Developer portal that brings everything an engineer needs to provide value into a single pane of glass. | +| [MavTek](https://www.mavtek.com/) | [@fgascon](https://github.com/fgascon) | Developer portal focused on standardizing practices, centralizing documentation and streamlining developer practices. | +| [QuintoAndar](https://www.quintoandar.com.br/) | [@quintoandar](https://github.com/quintoandar) | Developer portal, services catalog and centralization of service metrics. | +| [empathy.co](https://empathy.co/) | [@guillermotti](https://github.com/guillermotti) | Developer portal for tech docs, service catalog, plugin discovery and much more. | +| [creditas.com](https://creditas.com/) | [@aureliosaraiva](https://github.com/aureliosaraiva) [@Creditas](https://github.com/creditas) | Centralization of all services, standards, documentation, etc. We started the deployment process. | +| [Prisjakt](https://www.prisjakt.nu) / [PriceSpy](https://pricespy.co.uk) | [@kennylindahl](https://github.com/kennylindahl) | Internal developer portal - Documentation, scaffolding, software catalog, TechRadar, Gitlab org data integration | +| [Powerspike](https://powerspike.tv/) | [@trelore](https://github.com/trelore) | Developer portal for documentation of core libraries and repositories. | +| [2U](https://2u.com) | [Andrew Thal](https://github.com/athal7) | Development team home-base, promoting service discoverability, resource dependencies, and tech radar | +| [Taxfix](https://taxfix.de/) | [Sami Ur Rehman](https://github.com/samiurrehman92) | Developer's portal with software catalog at it's core. Hosts API Specs, Tech Docs, Tech Radar and some custom plugins. | +| [Busuu](https://busuu.com/) | [Adam Tester](https://github.com/adamtester) | Developer portal with service catalog, API docs, Event docs, service templating, and cost insights. | +| [Loadsmart](https://loadsmart.com/) | [Loadsmart](https://github.com/loadsmart) | Improve services visibility and operations for service owners and developers. | +| [Monzo](https://monzo.com/) | [@WillSewell](https://github.com/WillSewell), [@joechrisellis](https://github.com/joechrisellis) | Developer portal showing metadata and docs for over 2000 microservices. We have built a number of plugins such as a UI for our system to measure [software excellence](https://monzo.com/blog/2021/09/15/how-we-measure-software-excellence), and a UI to show deployment and config change events. | +| [Vaimo](https://www.vaimo.com) | [@vaimo-magnus](https://github.com/vaimo-magnus) | Developer Portal for our developers at Vaimo, currently docs and self-service towards our internal PaaS based on k8s. Plans to extend the catalog into Projects, Environments etc | +| [Wayfair](https://www.wayfair.com) | [@fransan](https://github.com/fransan), [@errskipower](https://github.com/errskipower), [@hrrs](https://github.com/hrrs) | Developer portal for service catalog, technical documentation, and APIs. | +| [CircleHD](https://www.circlehd.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | +| [CastDesk](https://castdesk.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | +| [Santagostino](https://santagostino.it) | [@santagostino](https://github.com/santagostino) | Developer portal, gateway to our infrastructure, documentation, service catalog and internal tooling. | +| [Peak](https://peak.ai) | [Luke Beamish](https://github.com/lukebeamish-peak) | Developer portal for all internal engineers to access documentation and tooling. | +| [Gelato](https://gelato.com/) | [Dmitry Makarenko](https://github.com/dmitry-makarenko-gelato) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal and third-party systems🚀. | +| [GoCardless](https://gocardless.com/) | [James Turley](https://github.com/tragiclifestories) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal systems. | +| [Box](https://www.box.com) | [@kielosz](https://github.com/kielosz), [@jluk-box](https://github.com/jluk-box), [@ptychu](https://github.com/ptychu), [@alexrybch](https://github.com/alexrybch), [@szubster](https://github.com/szubster) | Developer portal for service catalog, integration with internal systems, new service onboarding. | +| [Bazaarvoice](https://www.bazaarvoice.com) | [@niallmccullagh](https://github.com/niallmccullagh) | Developer portal for service catalog and scaffolds, publishing Github docs and API documentation, visualising our internal tech radar and our product engineering org structure. | +| [Krateo PlatformOps](https://www.krateo.io) | [@projectkerberus](https://github.com/projectkerberus) | A multi-cloud control plane to create, manage and deploy any kind of resource easily and centrally via a Developer Portal that centralizes via a self-service catalog the templating and ownership of services, the available documentation, the overview of the components that compose an entire domain and all the data of the service lifecycle. | diff --git a/packages/backend-common/src/config.ts b/packages/backend-common/src/config.ts index 31869c685a..9b0eb8dcd0 100644 --- a/packages/backend-common/src/config.ts +++ b/packages/backend-common/src/config.ts @@ -84,66 +84,51 @@ export class ObservableConfigProxy implements Config { has(key: string): boolean { return this.select(false)?.has(key) ?? false; } - keys(): string[] { return this.select(false)?.keys() ?? []; } - get(key?: string): T { return this.select(true).get(key); } - getOptional(key?: string): T | undefined { return this.select(false)?.getOptional(key); } - getConfig(key: string): Config { return new ObservableConfigProxy(this.logger, this, key); } - getOptionalConfig(key: string): Config | undefined { if (this.select(false)?.has(key)) { return new ObservableConfigProxy(this.logger, this, key); } return undefined; } - getConfigArray(key: string): Config[] { return this.select(true).getConfigArray(key); } - getOptionalConfigArray(key: string): Config[] | undefined { return this.select(false)?.getOptionalConfigArray(key); } - getNumber(key: string): number { return this.select(true).getNumber(key); } - getOptionalNumber(key: string): number | undefined { return this.select(false)?.getOptionalNumber(key); } - getBoolean(key: string): boolean { return this.select(true).getBoolean(key); } - getOptionalBoolean(key: string): boolean | undefined { return this.select(false)?.getOptionalBoolean(key); } - getString(key: string): string { return this.select(true).getString(key); } - getOptionalString(key: string): string | undefined { return this.select(false)?.getOptionalString(key); } - getStringArray(key: string): string[] { return this.select(true).getStringArray(key); } - getOptionalStringArray(key: string): string[] | undefined { return this.select(false)?.getOptionalStringArray(key); } diff --git a/packages/cli/package.json b/packages/cli/package.json index b9ba9fe8a3..62a008087e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -29,14 +29,10 @@ }, "dependencies": { "@backstage/cli-common": "^0.1.4", - "@babel/core": "^7.4.4", - "@babel/plugin-transform-modules-commonjs": "^7.4.4", - "@backstage/integration": "^0.6.5", "@backstage/config": "^0.1.10", "@backstage/config-loader": "^0.7.0", "@backstage/errors": "^0.1.3", "@backstage/types": "^0.1.1", - "@backstage/config-loader": "^0.6.8", "@hot-loader/react-dom": "^16.13.0", "@lerna/package-graph": "^4.0.0", "@lerna/project": "^4.0.0", diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 58b0061996..efcf753295 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -34,7 +34,6 @@ "@backstage/config": "^0.1.9", "@backstage/errors": "^0.1.3", "@backstage/types": "^0.1.1", - "@backstage/backend-common": "^0.9.4", "@types/json-schema": "^7.0.6", "ajv": "^7.0.3", "chokidar": "^3.5.2", diff --git a/packages/integration/api-report.md b/packages/integration/api-report.md index e07f61eea6..01f84383fe 100644 --- a/packages/integration/api-report.md +++ b/packages/integration/api-report.md @@ -347,11 +347,6 @@ export type GoogleGcsIntegrationConfig = { privateKey?: string; }; -// Warning: (ae-missing-release-tag) "isValidUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public -export function isValidUrl(url: string): boolean; - // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // Warning: (ae-missing-release-tag) "readAwsS3IntegrationConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/packages/integration/src/index.ts b/packages/integration/src/index.ts index b8992f52d2..3d5f336e8d 100644 --- a/packages/integration/src/index.ts +++ b/packages/integration/src/index.ts @@ -27,7 +27,6 @@ export * from './gitlab'; export * from './googleGcs'; export * from './awsS3'; export { defaultScmResolveUrl } from './helpers'; -export { isValidUrl } from './helpers'; export { ScmIntegrations } from './ScmIntegrations'; export type { ScmIntegration, ScmIntegrationsGroup } from './types'; export type { ScmIntegrationRegistry } from './registry'; From f7cd672c7f655427162a7ca0260e3438742537d8 Mon Sep 17 00:00:00 2001 From: Matto Date: Fri, 29 Oct 2021 16:39:13 +1100 Subject: [PATCH 032/196] Updated error message Signed-off-by: Matto --- packages/config-loader/src/loader.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/config-loader/src/loader.ts b/packages/config-loader/src/loader.ts index b8fab12917..4a5660f46c 100644 --- a/packages/config-loader/src/loader.ts +++ b/packages/config-loader/src/loader.ts @@ -116,9 +116,7 @@ export async function loadConfig( .map(configTarget => configTarget.url); if (remote === undefined && configUrls.length > 0) { - throw new Error( - `Remote config detected, however, this feature is turned off. Remote config will be ignored.`, - ); + throw new Error(`Remote config detected but this feature is turned off`); } // If no paths are provided, we default to reading From 7e6186cf6b522c0130d683a8cd6dcaa5cde2f018 Mon Sep 17 00:00:00 2001 From: Elliot Greenwood Date: Wed, 27 Oct 2021 13:35:00 +0100 Subject: [PATCH 033/196] Add NotModifiedError for the AWSS3UrlReader Signed-off-by: Elliot Greenwood --- .../src/reading/AwsS3UrlReader.test.ts | 209 ++++++++++++------ .../src/reading/AwsS3UrlReader.ts | 6 +- 2 files changed, 142 insertions(+), 73 deletions(-) diff --git a/packages/backend-common/src/reading/AwsS3UrlReader.test.ts b/packages/backend-common/src/reading/AwsS3UrlReader.test.ts index 301543e8c9..a3ae8b647f 100644 --- a/packages/backend-common/src/reading/AwsS3UrlReader.test.ts +++ b/packages/backend-common/src/reading/AwsS3UrlReader.test.ts @@ -27,6 +27,7 @@ import { UrlReaderPredicateTuple } from './types'; import AWSMock from 'aws-sdk-mock'; import aws from 'aws-sdk'; import path from 'path'; +import { NotModifiedError } from '@backstage/errors'; const treeResponseFactory = DefaultReadTreeResponseFactory.create({ config: new ConfigReader({}), @@ -131,29 +132,37 @@ describe('AwsS3UrlReader', () => { }); describe('read', () => { - AWSMock.setSDKInstance(aws); - AWSMock.mock( - 'S3', - 'getObject', - Buffer.from( - require('fs').readFileSync( - path.resolve(__dirname, '__fixtures__/awsS3/awsS3-mock-object.yaml'), + let awsS3UrlReader: AwsS3UrlReader; + + beforeAll(() => { + AWSMock.setSDKInstance(aws); + AWSMock.mock( + 'S3', + 'getObject', + Buffer.from( + require('fs').readFileSync( + path.resolve( + __dirname, + '__fixtures__/awsS3/awsS3-mock-object.yaml', + ), + ), ), - ), - ); - const s3 = new aws.S3(); - const awsS3UrlReader = new AwsS3UrlReader( - new AwsS3Integration( - readAwsS3IntegrationConfig( - new ConfigReader({ - host: 'amazonaws.com', - accessKeyId: 'fake-access-key', - secretAccessKey: 'fake-secret-key', - }), + ); + + const s3 = new aws.S3(); + awsS3UrlReader = new AwsS3UrlReader( + new AwsS3Integration( + readAwsS3IntegrationConfig( + new ConfigReader({ + host: 'amazonaws.com', + accessKeyId: 'fake-access-key', + secretAccessKey: 'fake-secret-key', + }), + ), ), - ), - { s3, treeResponseFactory }, - ); + { s3, treeResponseFactory }, + ); + }); it('returns contents of an object in a bucket', async () => { const response = await awsS3UrlReader.read( @@ -176,32 +185,39 @@ describe('AwsS3UrlReader', () => { }); describe('readUrl', () => { - AWSMock.setSDKInstance(aws); + let awsS3UrlReader: AwsS3UrlReader; - AWSMock.mock( - 'S3', - 'getObject', - Buffer.from( - require('fs').readFileSync( - path.resolve(__dirname, '__fixtures__/awsS3/awsS3-mock-object.yaml'), + beforeAll(() => { + AWSMock.setSDKInstance(aws); + + AWSMock.mock( + 'S3', + 'getObject', + Buffer.from( + require('fs').readFileSync( + path.resolve( + __dirname, + '__fixtures__/awsS3/awsS3-mock-object.yaml', + ), + ), ), - ), - ); + ); - const s3 = new aws.S3(); + const s3 = new aws.S3(); - const awsS3UrlReader = new AwsS3UrlReader( - new AwsS3Integration( - readAwsS3IntegrationConfig( - new ConfigReader({ - host: 'amazonaws.com', - accessKeyId: 'fake-access-key', - secretAccessKey: 'fake-secret-key', - }), + awsS3UrlReader = new AwsS3UrlReader( + new AwsS3Integration( + readAwsS3IntegrationConfig( + new ConfigReader({ + host: 'amazonaws.com', + accessKeyId: 'fake-access-key', + secretAccessKey: 'fake-secret-key', + }), + ), ), - ), - { s3, treeResponseFactory }, - ); + { s3, treeResponseFactory }, + ); + }); it('returns contents of an object in a bucket', async () => { const response = await awsS3UrlReader.readUrl( @@ -223,40 +239,89 @@ describe('AwsS3UrlReader', () => { ); }); }); + + describe('readUrl with etag', () => { + let awsS3UrlReader: AwsS3UrlReader; + + beforeAll(() => { + AWSMock.setSDKInstance(aws); + + AWSMock.mock('S3', 'getObject', (_, callback) => { + callback({ statusCode: 304 }, null); + }); + + const s3 = new aws.S3(); + + awsS3UrlReader = new AwsS3UrlReader( + new AwsS3Integration( + readAwsS3IntegrationConfig( + new ConfigReader({ + host: 'amazonaws.com', + accessKeyId: 'fake-access-key', + secretAccessKey: 'fake-secret-key', + }), + ), + ), + { s3, treeResponseFactory }, + ); + }); + + it('returns contents of an object in a bucket', async () => { + await expect( + awsS3UrlReader.readUrl( + 'https://test-bucket.s3.us-east-2.amazonaws.com/awsS3-mock-object.yaml', + { + etag: 'abc123', + }, + ), + ).rejects.toThrow(NotModifiedError); + }); + }); + describe('readTree', () => { - const object: aws.S3.Types.Object = { - Key: 'awsS3-mock-object.yaml', - }; - const objectList: aws.S3.ObjectList = [object]; - const output: aws.S3.Types.ListObjectsV2Output = { - Contents: objectList, - }; - AWSMock.setSDKInstance(aws); - AWSMock.mock('S3', 'listObjectsV2', output); + let awsS3UrlReader: AwsS3UrlReader; - AWSMock.mock( - 'S3', - 'getObject', - Buffer.from( - require('fs').readFileSync( - path.resolve(__dirname, '__fixtures__/awsS3/awsS3-mock-object.yaml'), - ), - ), - ); + beforeAll(() => { + const object: aws.S3.Types.Object = { + Key: 'awsS3-mock-object.yaml', + }; - const s3 = new aws.S3(); - const awsS3UrlReader = new AwsS3UrlReader( - new AwsS3Integration( - readAwsS3IntegrationConfig( - new ConfigReader({ - host: '.amazonaws.com', - accessKeyId: 'fake-access-key', - secretAccessKey: 'fake-secret-key', - }), + const objectList: aws.S3.ObjectList = [object]; + const output: aws.S3.Types.ListObjectsV2Output = { + Contents: objectList, + }; + + AWSMock.setSDKInstance(aws); + AWSMock.mock('S3', 'listObjectsV2', output); + + AWSMock.mock( + 'S3', + 'getObject', + Buffer.from( + require('fs').readFileSync( + path.resolve( + __dirname, + '__fixtures__/awsS3/awsS3-mock-object.yaml', + ), + ), ), - ), - { s3, treeResponseFactory }, - ); + ); + + const s3 = new aws.S3(); + awsS3UrlReader = new AwsS3UrlReader( + new AwsS3Integration( + readAwsS3IntegrationConfig( + new ConfigReader({ + host: '.amazonaws.com', + accessKeyId: 'fake-access-key', + secretAccessKey: 'fake-secret-key', + }), + ), + ), + { s3, treeResponseFactory }, + ); + }); + it('returns contents of an object in a bucket', async () => { const response = await awsS3UrlReader.readTree( 'https://test.s3.us-east-2.amazonaws.com', diff --git a/packages/backend-common/src/reading/AwsS3UrlReader.ts b/packages/backend-common/src/reading/AwsS3UrlReader.ts index 7fd0f6ba19..05a14e26ec 100644 --- a/packages/backend-common/src/reading/AwsS3UrlReader.ts +++ b/packages/backend-common/src/reading/AwsS3UrlReader.ts @@ -27,7 +27,7 @@ import { } from './types'; import getRawBody from 'raw-body'; import { AwsS3Integration, ScmIntegrations } from '@backstage/integration'; -import { ForwardedError } from '@backstage/errors'; +import { ForwardedError, NotModifiedError } from '@backstage/errors'; import { ListObjectsV2Output, ObjectList } from 'aws-sdk/clients/s3'; const parseURL = ( @@ -163,6 +163,10 @@ export class AwsS3UrlReader implements UrlReader { etag: etag, }; } catch (e) { + if (e.statusCode === 304) { + throw new NotModifiedError(); + } + throw new ForwardedError('Could not retrieve file from S3', e); } } From 8c4cad0bf2e51533109fe6f5147fde33cac16618 Mon Sep 17 00:00:00 2001 From: Elliot Greenwood Date: Wed, 27 Oct 2021 14:33:08 +0100 Subject: [PATCH 034/196] Add changeset for backstage common Signed-off-by: Elliot Greenwood --- .changeset/few-waves-dream.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/few-waves-dream.md diff --git a/.changeset/few-waves-dream.md b/.changeset/few-waves-dream.md new file mode 100644 index 0000000000..72123171b8 --- /dev/null +++ b/.changeset/few-waves-dream.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +AWSS3UrlReader now throws a `NotModifiedError` (exported from @backstage/backend-common) when s3 returns a 304 response. From 7c5f6a0400c069f44aa8d2d3827dcbf6a8d74932 Mon Sep 17 00:00:00 2001 From: Alex Rybchenko Date: Fri, 29 Oct 2021 16:15:07 +0200 Subject: [PATCH 035/196] added comments Signed-off-by: Alex Rybchenko --- .changeset/real-mails-add.md | 2 +- .../core-components/src/components/TabbedLayout/RoutedTabs.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.changeset/real-mails-add.md b/.changeset/real-mails-add.md index 15cdc2997f..2769fef898 100644 --- a/.changeset/real-mails-add.md +++ b/.changeset/real-mails-add.md @@ -3,4 +3,4 @@ '@backstage/plugin-catalog': patch --- -fixed route resolving +fixed route resolving (ssue #7741) when user cannot select a tab in any of the tabbed pages (like the Catalog page) if it shares the same initial letters as a preceding tab. (i.e. where tab with a path of /ci is followed by a path of /cid, user cannot select /cid as /ci will always be selected first). diff --git a/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx b/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx index d0c36e286c..c5c771b739 100644 --- a/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx +++ b/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx @@ -35,6 +35,7 @@ export function useSelectedSubRoute(subRoutes: SubRoute[]): { // TODO: remove once react-router updated const sortedRoutes = routes.sort((a, b) => + // remove added "/*" symbols from path before comparing b.path.slice(0, -2).localeCompare(a.path.slice(0, -2)), ); From a0a66000607f14dfd4571b86ea80115e47b9e766 Mon Sep 17 00:00:00 2001 From: Alex Rybchenko Date: Fri, 29 Oct 2021 16:17:47 +0200 Subject: [PATCH 036/196] fix typo Signed-off-by: Alex Rybchenko --- .changeset/real-mails-add.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/real-mails-add.md b/.changeset/real-mails-add.md index 2769fef898..9eff9cbe62 100644 --- a/.changeset/real-mails-add.md +++ b/.changeset/real-mails-add.md @@ -3,4 +3,4 @@ '@backstage/plugin-catalog': patch --- -fixed route resolving (ssue #7741) when user cannot select a tab in any of the tabbed pages (like the Catalog page) if it shares the same initial letters as a preceding tab. (i.e. where tab with a path of /ci is followed by a path of /cid, user cannot select /cid as /ci will always be selected first). +fixed route resolving (issue #7741) when user cannot select a tab in any of the tabbed pages (like the Catalog page) if it shares the same initial letters as a preceding tab. (i.e. where tab with a path of /ci is followed by a path of /cid, user cannot select /cid as /ci will always be selected first). From 0287b186135431c67ddb01834b12e65bd599aaf3 Mon Sep 17 00:00:00 2001 From: Jeremy Guarini Date: Fri, 29 Oct 2021 10:20:14 -0700 Subject: [PATCH 037/196] UI updates to GCP-projects plugin Adds the following to the project list page: * pagination * filtering * sorting * rows per page * show/hide columns Makes breadcrumb a link back to project list for the project details and new project views. In project list page, updates New project button to use RouterLink instead of href to avoid login prompt. In project details view, links to project details and logs now work, clicking on these will open the project or logs in GCP in new tab. Signed-off-by: Jeremy Guarini --- plugins/gcp-projects/package.json | 1 + .../NewProjectPage/NewProjectPage.tsx | 43 +++-- .../ProjectDetailsPage/ProjectDetailsPage.tsx | 51 ++++-- .../ProjectListPage/ProjectListPage.tsx | 161 +++++++++++------- yarn.lock | 30 +++- 5 files changed, 186 insertions(+), 100 deletions(-) diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 65458ea88b..71b212a698 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -35,6 +35,7 @@ "@backstage/core-plugin-api": "^0.1.12", "@backstage/theme": "^0.2.12", "@material-ui/core": "^4.12.2", + "@material-ui/data-grid": "^4.0.0-alpha.37", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", "react": "^16.13.1", diff --git a/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx b/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx index 584cffc50c..5ecc7f15d4 100644 --- a/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx +++ b/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx @@ -28,6 +28,10 @@ import { StructuredMetadataTable, SupportButton, } from '@backstage/core-components'; +import { Link as RouterLink } from 'react-router-dom'; + +import { useRouteRef } from '@backstage/core-plugin-api'; +import { rootRouteRef } from '../../routes'; export const Project = () => { const [projectName, setProjectName] = useState(''); @@ -79,18 +83,20 @@ export const Project = () => { All your software catalog entities diff --git a/yarn.lock b/yarn.lock index 517d66ba0c..fc66a02031 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4416,6 +4416,16 @@ react-is "^16.8.0 || ^17.0.0" react-transition-group "^4.4.0" +"@material-ui/data-grid@^4.0.0-alpha.37": + version "4.0.0-alpha.37" + resolved "https://registry.npmjs.org/@material-ui/data-grid/-/data-grid-4.0.0-alpha.37.tgz#89d907c4e94e6a0db4e89e4f59160f7811546ca2" + integrity sha512-3T2AG31aad/lWLMLwn1XUP4mUf3H9YZES17dGuYByzkRLCXbBZHBTPEnCctWukajzwm+v0KGg3QpwitGoiDAjA== + dependencies: + "@material-ui/utils" "^5.0.0-alpha.14" + clsx "^1.0.4" + prop-types "^15.7.2" + reselect "^4.0.0" + "@material-ui/icons@^4.11.2", "@material-ui/icons@^4.9.1": version "4.11.2" resolved "https://registry.npmjs.org/@material-ui/icons/-/icons-4.11.2.tgz#b3a7353266519cd743b6461ae9fdfcb1b25eb4c5" @@ -4503,6 +4513,17 @@ prop-types "^15.7.2" react-is "^16.8.0 || ^17.0.0" +"@material-ui/utils@^5.0.0-alpha.14": + version "5.0.0-beta.5" + resolved "https://registry.npmjs.org/@material-ui/utils/-/utils-5.0.0-beta.5.tgz#de492037e1f1f0910fda32e6f11b66dfcde2a1c2" + integrity sha512-wtJ3ovXWZdTAz5eLBqvMpYH/IBJb3qMQbGCyL1i00+sf7AUlAuv4QLx+QtX/siA6L7IpxUQVfqpoCpQH1eYRpQ== + dependencies: + "@babel/runtime" "^7.14.8" + "@types/prop-types" "^15.7.4" + "@types/react-is" "^16.7.1 || ^17.0.0" + prop-types "^15.7.2" + react-is "^17.0.2" + "@mattiasbuelens/web-streams-polyfill@^0.2.0": version "0.2.1" resolved "https://registry.npmjs.org/@mattiasbuelens/web-streams-polyfill/-/web-streams-polyfill-0.2.1.tgz#d7c4aa94f98084ec0787be084d47167d62ea5f67" @@ -7434,7 +7455,7 @@ resolved "https://registry.npmjs.org/@types/pretty-hrtime/-/pretty-hrtime-1.0.1.tgz#72a26101dc567b0d68fd956cf42314556e42d601" integrity sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ== -"@types/prop-types@*", "@types/prop-types@^15.7.3": +"@types/prop-types@*", "@types/prop-types@^15.7.3", "@types/prop-types@^15.7.4": version "15.7.4" resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== @@ -7475,6 +7496,13 @@ dependencies: "@types/react" "*" +"@types/react-is@^16.7.1 || ^17.0.0": + version "17.0.3" + resolved "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz#2d855ba575f2fc8d17ef9861f084acc4b90a137a" + integrity sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw== + dependencies: + "@types/react" "*" + "@types/react-lazylog@^4.5.0": version "4.5.1" resolved "https://registry.npmjs.org/@types/react-lazylog/-/react-lazylog-4.5.1.tgz#babb5d814f7035b5434518769975e12f299356a8" From e9206f437c8b4262d8c959546947fac046ca52ec Mon Sep 17 00:00:00 2001 From: Tim Hansen Date: Fri, 29 Oct 2021 13:39:35 -0600 Subject: [PATCH 038/196] Make theme banner warning color optional Signed-off-by: Tim Hansen --- .../src/components/DismissableBanner/DismissableBanner.tsx | 3 ++- packages/theme/src/types.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx b/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx index 40082925c6..92b449f705 100644 --- a/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx +++ b/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx @@ -80,7 +80,8 @@ const useStyles = makeStyles( backgroundColor: theme.palette.banner.error, }, warning: { - backgroundColor: theme.palette.banner.warning, + backgroundColor: + theme.palette.banner.warning ?? theme.palette.banner.error, }, }), { name: 'BackstageDismissableBanner' }, diff --git a/packages/theme/src/types.ts b/packages/theme/src/types.ts index 40f45ee739..e1acba3a01 100644 --- a/packages/theme/src/types.ts +++ b/packages/theme/src/types.ts @@ -76,7 +76,7 @@ export type BackstagePaletteAdditions = { error: string; text: string; link: string; - warning: string; + warning?: string; }; }; From c01e26327efc80555add4bc87171308006116d1a Mon Sep 17 00:00:00 2001 From: Tim Hansen Date: Fri, 29 Oct 2021 13:43:28 -0600 Subject: [PATCH 039/196] update changeset Signed-off-by: Tim Hansen --- .changeset/dry-spies-cover.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.changeset/dry-spies-cover.md b/.changeset/dry-spies-cover.md index 7006dee5c4..aae683faaf 100644 --- a/.changeset/dry-spies-cover.md +++ b/.changeset/dry-spies-cover.md @@ -3,4 +3,6 @@ '@backstage/theme': patch --- -Will Add warning variant to `DismissableBanner` component. +Added a warning variant to `DismissableBanner` component. If you are using a +custom theme, you will need to add the optional `palette.banner.warning` color, +otherwise this variant will fall back to the `palette.banner.error` color. From 741bcb168e875e537d7851f32195d9d30102d806 Mon Sep 17 00:00:00 2001 From: Jeremy Guarini Date: Fri, 29 Oct 2021 16:55:03 -0700 Subject: [PATCH 040/196] add changeset Signed-off-by: Jeremy Guarini --- .changeset/eight-months-agree.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .changeset/eight-months-agree.md diff --git a/.changeset/eight-months-agree.md b/.changeset/eight-months-agree.md new file mode 100644 index 0000000000..372c0b624c --- /dev/null +++ b/.changeset/eight-months-agree.md @@ -0,0 +1,19 @@ +--- +'@backstage/plugin-gcp-projects': patch +--- + +UI updates to GCP-projects plugin + +Adds the following to the project list page: + +- pagination +- filtering +- sorting +- rows per page +- show/hide columns + +Makes breadcrumb a link back to project list for the project details and new project views. + +In project list page, updates New project button to use RouterLink instead of href to avoid login prompt. + +In project details view, links to project details and logs now work, clicking on these will open the project or logs in GCP in new tab. From a2d4389587b5bf0893b820cb18d3f89434993349 Mon Sep 17 00:00:00 2001 From: Colton Padden Date: Sun, 31 Oct 2021 11:15:47 -0400 Subject: [PATCH 041/196] Use Parameter Objects for Publisher Constructors Refactored Techdocs publishers to use a single parameter object as the constructor argument to ease extendability as more options are introduced. Updated local publisher to use `.fromConfig` for instantiation so that it follows the same design pattern as the other publishers. Signed-off-by: Colton Padden --- .changeset/metal-impalas-allow.md | 12 ++++++ .../src/stages/publish/awsS3.ts | 40 ++++++++++-------- .../src/stages/publish/azureBlobStorage.ts | 41 +++++++++++-------- .../src/stages/publish/googleStorage.ts | 38 +++++++++-------- .../src/stages/publish/local.test.ts | 20 +++++++-- .../src/stages/publish/local.ts | 41 ++++++++++++------- .../src/stages/publish/openStackSwift.ts | 26 +++++++----- .../src/stages/publish/publish.ts | 4 +- 8 files changed, 140 insertions(+), 82 deletions(-) create mode 100644 .changeset/metal-impalas-allow.md diff --git a/.changeset/metal-impalas-allow.md b/.changeset/metal-impalas-allow.md new file mode 100644 index 0000000000..6f18969f3d --- /dev/null +++ b/.changeset/metal-impalas-allow.md @@ -0,0 +1,12 @@ +--- +'@backstage/techdocs-common': patch +--- + +1. Techdocs publishers constructors now use parameter objects when being instantiated + +2. The `LocalPublish` publisher can now be created using `fromConfig`: + +``` +--- const publisher = new LocalPublish(config, logger, discovery); ++++ const publisher = LocalPublish.fromConfig(config, logger, discovery); +``` diff --git a/packages/techdocs-common/src/stages/publish/awsS3.ts b/packages/techdocs-common/src/stages/publish/awsS3.ts index 29eecb8d03..cd6914460f 100644 --- a/packages/techdocs-common/src/stages/publish/awsS3.ts +++ b/packages/techdocs-common/src/stages/publish/awsS3.ts @@ -57,6 +57,26 @@ const streamToBuffer = (stream: Readable): Promise => { }; export class AwsS3Publish implements PublisherBase { + private readonly storageClient: aws.S3; + private readonly bucketName: string; + private readonly legacyPathCasing: boolean; + private readonly logger: Logger; + private readonly bucketRootPath: string; + + constructor(options: { + storageClient: aws.S3; + bucketName: string; + legacyPathCasing: boolean; + logger: Logger; + bucketRootPath: string; + }) { + this.storageClient = options.storageClient; + this.bucketName = options.bucketName; + this.legacyPathCasing = options.legacyPathCasing; + this.logger = options.logger; + this.bucketRootPath = options.bucketRootPath; + } + static fromConfig(config: Config, logger: Logger): PublisherBase { let bucketName = ''; try { @@ -112,13 +132,13 @@ export class AwsS3Publish implements PublisherBase { 'techdocs.legacyUseCaseSensitiveTripletPaths', ) || false; - return new AwsS3Publish( + return new AwsS3Publish({ storageClient, bucketName, + bucketRootPath, legacyPathCasing, logger, - bucketRootPath, - ); + }); } private static buildCredentials( @@ -152,20 +172,6 @@ export class AwsS3Publish implements PublisherBase { return explicitCredentials; } - constructor( - private readonly storageClient: aws.S3, - private readonly bucketName: string, - private readonly legacyPathCasing: boolean, - private readonly logger: Logger, - private readonly bucketRootPath: string, - ) { - this.storageClient = storageClient; - this.bucketName = bucketName; - this.legacyPathCasing = legacyPathCasing; - this.logger = logger; - this.bucketRootPath = bucketRootPath; - } - /** * Check if the defined bucket exists. Being able to connect means the configuration is good * and the storage client will work. diff --git a/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts b/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts index f1d60f709a..7a58d92095 100644 --- a/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts +++ b/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts @@ -47,6 +47,23 @@ import { const BATCH_CONCURRENCY = 3; export class AzureBlobStoragePublish implements PublisherBase { + private readonly storageClient: BlobServiceClient; + private readonly containerName: string; + private readonly legacyPathCasing: boolean; + private readonly logger: Logger; + + constructor(options: { + storageClient: BlobServiceClient; + containerName: string; + legacyPathCasing: boolean; + logger: Logger; + }) { + this.storageClient = options.storageClient; + this.containerName = options.containerName; + this.legacyPathCasing = options.legacyPathCasing; + this.logger = options.logger; + } + static fromConfig(config: Config, logger: Logger): PublisherBase { let containerName = ''; try { @@ -95,24 +112,12 @@ export class AzureBlobStoragePublish implements PublisherBase { 'techdocs.legacyUseCaseSensitiveTripletPaths', ) || false; - return new AzureBlobStoragePublish( - storageClient, - containerName, - legacyPathCasing, - logger, - ); - } - - constructor( - private readonly storageClient: BlobServiceClient, - private readonly containerName: string, - private readonly legacyPathCasing: boolean, - private readonly logger: Logger, - ) { - this.storageClient = storageClient; - this.containerName = containerName; - this.legacyPathCasing = legacyPathCasing; - this.logger = logger; + return new AzureBlobStoragePublish({ + storageClient: storageClient, + containerName: containerName, + legacyPathCasing: legacyPathCasing, + logger: logger, + }); } async getReadiness(): Promise { diff --git a/packages/techdocs-common/src/stages/publish/googleStorage.ts b/packages/techdocs-common/src/stages/publish/googleStorage.ts index 5a142e800a..8d91b31dbb 100644 --- a/packages/techdocs-common/src/stages/publish/googleStorage.ts +++ b/packages/techdocs-common/src/stages/publish/googleStorage.ts @@ -41,6 +41,26 @@ import { } from './types'; export class GoogleGCSPublish implements PublisherBase { + private readonly storageClient: Storage; + private readonly bucketName: string; + private readonly legacyPathCasing: boolean; + private readonly logger: Logger; + private readonly bucketRootPath: string; + + constructor(options: { + storageClient: Storage; + bucketName: string; + legacyPathCasing: boolean; + logger: Logger; + bucketRootPath: string; + }) { + this.storageClient = options.storageClient; + this.bucketName = options.bucketName; + this.legacyPathCasing = options.legacyPathCasing; + this.logger = options.logger; + this.bucketRootPath = options.bucketRootPath; + } + static fromConfig(config: Config, logger: Logger): PublisherBase { let bucketName = ''; try { @@ -84,27 +104,13 @@ export class GoogleGCSPublish implements PublisherBase { 'techdocs.legacyUseCaseSensitiveTripletPaths', ) || false; - return new GoogleGCSPublish( + return new GoogleGCSPublish({ storageClient, bucketName, legacyPathCasing, logger, bucketRootPath, - ); - } - - constructor( - private readonly storageClient: Storage, - private readonly bucketName: string, - private readonly legacyPathCasing: boolean, - private readonly logger: Logger, - private readonly bucketRootPath: string, - ) { - this.storageClient = storageClient; - this.bucketName = bucketName; - this.legacyPathCasing = legacyPathCasing; - this.logger = logger; - this.bucketRootPath = bucketRootPath; + }); } /** diff --git a/packages/techdocs-common/src/stages/publish/local.test.ts b/packages/techdocs-common/src/stages/publish/local.test.ts index 38ead236bd..de56580e02 100644 --- a/packages/techdocs-common/src/stages/publish/local.test.ts +++ b/packages/techdocs-common/src/stages/publish/local.test.ts @@ -63,7 +63,11 @@ describe('local publisher', () => { const mockConfig = new ConfigReader({}); - const publisher = new LocalPublish(mockConfig, logger, testDiscovery); + const publisher = LocalPublish.fromConfig( + mockConfig, + logger, + testDiscovery, + ); const mockEntity = createMockEntity(); const lowerMockEntity = createMockEntity(undefined, true); @@ -90,7 +94,11 @@ describe('local publisher', () => { }, }); - const publisher = new LocalPublish(mockConfig, logger, testDiscovery); + const publisher = LocalPublish.fromConfig( + mockConfig, + logger, + testDiscovery, + ); const mockEntity = createMockEntity(); const lowerMockEntity = createMockEntity(undefined, true); @@ -106,7 +114,11 @@ describe('local publisher', () => { describe('docsRouter', () => { const mockConfig = new ConfigReader({}); - const publisher = new LocalPublish(mockConfig, logger, testDiscovery); + const publisher = LocalPublish.fromConfig( + mockConfig, + logger, + testDiscovery, + ); let app: express.Express; beforeEach(() => { @@ -166,7 +178,7 @@ describe('local publisher', () => { legacyUseCaseSensitiveTripletPaths: true, }, }); - const legacyPublisher = new LocalPublish( + const legacyPublisher = LocalPublish.fromConfig( legacyConfig, logger, testDiscovery, diff --git a/packages/techdocs-common/src/stages/publish/local.ts b/packages/techdocs-common/src/stages/publish/local.ts index de0072de7a..70b4eb3ff2 100644 --- a/packages/techdocs-common/src/stages/publish/local.ts +++ b/packages/techdocs-common/src/stages/publish/local.ts @@ -59,24 +59,37 @@ try { * called "static" at the root of techdocs-backend plugin. */ export class LocalPublish implements PublisherBase { - private legacyPathCasing: boolean; + private readonly legacyPathCasing: boolean; + private readonly logger: Logger; + private readonly discovery: PluginEndpointDiscovery; - // TODO: Use a static fromConfig method to create a LocalPublish instance, similar to aws/gcs publishers. - // Move the logic of setting staticDocsDir based on config over to fromConfig, - // and set the value as a class parameter. - constructor( - // @ts-ignore - private readonly config: Config, - private readonly logger: Logger, - private readonly discovery: PluginEndpointDiscovery, - ) { - this.config = config; - this.logger = logger; - this.discovery = discovery; - this.legacyPathCasing = + // TODO: Move the logic of setting staticDocsDir based on config over to + // fromConfig, and set the value as a class parameter. + constructor(options: { + logger: Logger; + discovery: PluginEndpointDiscovery; + legacyPathCasing: boolean; + }) { + this.logger = options.logger; + this.discovery = options.discovery; + this.legacyPathCasing = options.legacyPathCasing; + } + + static fromConfig( + config: Config, + logger: Logger, + discovery: PluginEndpointDiscovery, + ): PublisherBase { + const legacyPathCasing = config.getOptionalBoolean( 'techdocs.legacyUseCaseSensitiveTripletPaths', ) || false; + + return new LocalPublish({ + logger, + discovery, + legacyPathCasing, + }); } async getReadiness(): Promise { diff --git a/packages/techdocs-common/src/stages/publish/openStackSwift.ts b/packages/techdocs-common/src/stages/publish/openStackSwift.ts index 51e7b76406..a463157552 100644 --- a/packages/techdocs-common/src/stages/publish/openStackSwift.ts +++ b/packages/techdocs-common/src/stages/publish/openStackSwift.ts @@ -54,6 +54,20 @@ const bufferToStream = (buffer: Buffer): Readable => { }; export class OpenStackSwiftPublish implements PublisherBase { + private readonly storageClient: SwiftClient; + private readonly containerName: string; + private readonly logger: Logger; + + constructor(options: { + storageClient: SwiftClient; + containerName: string; + logger: Logger; + }) { + this.storageClient = options.storageClient; + this.containerName = options.containerName; + this.logger = options.logger; + } + static fromConfig(config: Config, logger: Logger): PublisherBase { let containerName = ''; try { @@ -78,17 +92,7 @@ export class OpenStackSwiftPublish implements PublisherBase { secret: openStackSwiftConfig.getString('credentials.secret'), }); - return new OpenStackSwiftPublish(storageClient, containerName, logger); - } - - constructor( - private readonly storageClient: SwiftClient, - private readonly containerName: string, - private readonly logger: Logger, - ) { - this.storageClient = storageClient; - this.containerName = containerName; - this.logger = logger; + return new OpenStackSwiftPublish({ storageClient, containerName, logger }); } /* diff --git a/packages/techdocs-common/src/stages/publish/publish.ts b/packages/techdocs-common/src/stages/publish/publish.ts index c7c6154114..eb9e3104bf 100644 --- a/packages/techdocs-common/src/stages/publish/publish.ts +++ b/packages/techdocs-common/src/stages/publish/publish.ts @@ -61,10 +61,10 @@ export class Publisher { return OpenStackSwiftPublish.fromConfig(config, logger); case 'local': logger.info('Creating Local publisher for TechDocs'); - return new LocalPublish(config, logger, discovery); + return LocalPublish.fromConfig(config, logger, discovery); default: logger.info('Creating Local publisher for TechDocs'); - return new LocalPublish(config, logger, discovery); + return LocalPublish.fromConfig(config, logger, discovery); } } } From 311d604bea6d5a7885942ca1adfbec15f7a4cf72 Mon Sep 17 00:00:00 2001 From: rodion Date: Sun, 31 Oct 2021 22:43:24 +0300 Subject: [PATCH 042/196] fix: plugin sentry id token / private method Signed-off-by: rodion --- plugins/sentry/api-report.md | 11 ----------- plugins/sentry/src/api/production-api.ts | 2 +- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/plugins/sentry/api-report.md b/plugins/sentry/api-report.md index 1b7a248e48..6d83ddc5e6 100644 --- a/plugins/sentry/api-report.md +++ b/plugins/sentry/api-report.md @@ -41,17 +41,6 @@ export class ProductionSentryApi implements SentryApi { identityApi?: IdentityApi | undefined, ); // (undocumented) - authOptions(): Promise< - | { - headers?: undefined; - } - | { - headers: { - authorization: string; - }; - } - >; - // (undocumented) fetchIssues( project: string, statsFor: string, diff --git a/plugins/sentry/src/api/production-api.ts b/plugins/sentry/src/api/production-api.ts index 37a735df14..1fc6ece37f 100644 --- a/plugins/sentry/src/api/production-api.ts +++ b/plugins/sentry/src/api/production-api.ts @@ -51,7 +51,7 @@ export class ProductionSentryApi implements SentryApi { return (await response.json()) as SentryIssue[]; } - async authOptions() { + private async authOptions() { if (!this.identityApi) { return {}; } From 777daa10ad6896b5cfe161721120d4d7e00c3d32 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Sun, 31 Oct 2021 19:56:06 -0500 Subject: [PATCH 043/196] Fixed s3 path join to work under any os Signed-off-by: Andre Wanlin --- .changeset/tender-bugs-sort.md | 5 +++++ packages/techdocs-common/src/stages/publish/awsS3.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/tender-bugs-sort.md diff --git a/.changeset/tender-bugs-sort.md b/.changeset/tender-bugs-sort.md new file mode 100644 index 0000000000..b6ad29ba66 --- /dev/null +++ b/.changeset/tender-bugs-sort.md @@ -0,0 +1,5 @@ +--- +'@backstage/techdocs-common': patch +--- + +Updated to properly join URL segments under any OS diff --git a/packages/techdocs-common/src/stages/publish/awsS3.ts b/packages/techdocs-common/src/stages/publish/awsS3.ts index 29eecb8d03..5606ff1fd4 100644 --- a/packages/techdocs-common/src/stages/publish/awsS3.ts +++ b/packages/techdocs-common/src/stages/publish/awsS3.ts @@ -357,7 +357,7 @@ export class AwsS3Publish implements PublisherBase { : lowerCaseEntityTripletInStoragePath(decodedUriNoRoot); // Re-prepend the root path to the relative file path - const filePath = path.join(this.bucketRootPath, filePathNoRoot); + const filePath = path.posix.join(this.bucketRootPath, filePathNoRoot); // Files with different extensions (CSS, HTML) need to be served with different headers const fileExtension = path.extname(filePath); From 4848c9cb2094e9691340c9e68ed65e4688b47753 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Sun, 31 Oct 2021 19:58:51 -0500 Subject: [PATCH 044/196] Fixed other instances of same issue Signed-off-by: Andre Wanlin --- packages/techdocs-common/src/stages/publish/awsS3.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/techdocs-common/src/stages/publish/awsS3.ts b/packages/techdocs-common/src/stages/publish/awsS3.ts index 5606ff1fd4..f0616e4cd3 100644 --- a/packages/techdocs-common/src/stages/publish/awsS3.ts +++ b/packages/techdocs-common/src/stages/publish/awsS3.ts @@ -306,7 +306,7 @@ export class AwsS3Publish implements PublisherBase { ? entityTriplet : lowerCaseEntityTriplet(entityTriplet); - const entityRootDir = path.join(this.bucketRootPath, entityDir); + const entityRootDir = path.posix.join(this.bucketRootPath, entityDir); const stream = this.storageClient .getObject({ @@ -396,7 +396,7 @@ export class AwsS3Publish implements PublisherBase { ? entityTriplet : lowerCaseEntityTriplet(entityTriplet); - const entityRootDir = path.join(this.bucketRootPath, entityDir); + const entityRootDir = path.posix.join(this.bucketRootPath, entityDir); await this.storageClient .headObject({ From ef01b7c7a7a22623c3fd5e7f85d274842b1cb8db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 04:15:34 +0000 Subject: [PATCH 045/196] build(deps-dev): bump @storybook/addons from 6.3.11 to 6.3.12 Bumps [@storybook/addons](https://github.com/storybookjs/storybook/tree/HEAD/lib/addons) from 6.3.11 to 6.3.12. - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v6.3.12/lib/addons) --- updated-dependencies: - dependency-name: "@storybook/addons" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- yarn.lock | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 3fec636e32..e879cff365 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5531,7 +5531,7 @@ react-syntax-highlighter "^13.5.3" regenerator-runtime "^0.13.7" -"@storybook/addons@6.3.11", "@storybook/addons@^6.1.11": +"@storybook/addons@6.3.11": version "6.3.11" resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.3.11.tgz#4b5e88793bcce7ef823340e9010a96e35e3284cc" integrity sha512-2Y03lOwzWDRB/glISa/4luBMM5uyYhkIBixbZF9miIb2SCWRlNmom5NCnKsR18Wu6g7zI7os3aAMfKr24aSofQ== @@ -5561,6 +5561,21 @@ global "^4.4.0" regenerator-runtime "^0.13.7" +"@storybook/addons@^6.1.11": + version "6.3.12" + resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.3.12.tgz#8773dcc113c5086dfff722388b7b65580e43b65b" + integrity sha512-UgoMyr7Qr0FS3ezt8u6hMEcHgyynQS9ucr5mAwZky3wpXRPFyUTmMto9r4BBUdqyUvTUj/LRKIcmLBfj+/l0Fg== + dependencies: + "@storybook/api" "6.3.12" + "@storybook/channels" "6.3.12" + "@storybook/client-logger" "6.3.12" + "@storybook/core-events" "6.3.12" + "@storybook/router" "6.3.12" + "@storybook/theming" "6.3.12" + core-js "^3.8.2" + global "^4.4.0" + regenerator-runtime "^0.13.7" + "@storybook/api@6.3.11": version "6.3.11" resolved "https://registry.npmjs.org/@storybook/api/-/api-6.3.11.tgz#ea3806a0570da65bfb5b39e4edb90289b5ba701e" @@ -5587,6 +5602,32 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" +"@storybook/api@6.3.12": + version "6.3.12" + resolved "https://registry.npmjs.org/@storybook/api/-/api-6.3.12.tgz#2845c20464d5348d676d09665e8ab527825ed7b5" + integrity sha512-LScRXUeCWEW/OP+jiooNMQICVdusv7azTmULxtm72fhkXFRiQs2CdRNTiqNg46JLLC9z95f1W+pGK66X6HiiQA== + dependencies: + "@reach/router" "^1.3.4" + "@storybook/channels" "6.3.12" + "@storybook/client-logger" "6.3.12" + "@storybook/core-events" "6.3.12" + "@storybook/csf" "0.0.1" + "@storybook/router" "6.3.12" + "@storybook/semver" "^7.3.2" + "@storybook/theming" "6.3.12" + "@types/reach__router" "^1.3.7" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + lodash "^4.17.20" + memoizerific "^1.11.3" + qs "^6.10.0" + regenerator-runtime "^0.13.7" + store2 "^2.12.0" + telejson "^5.3.2" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + "@storybook/api@6.3.7": version "6.3.7" resolved "https://registry.npmjs.org/@storybook/api/-/api-6.3.7.tgz#88b8a51422cd0739c91bde0b1d65fb6d8a8485d0" @@ -5724,6 +5765,15 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" +"@storybook/channels@6.3.12": + version "6.3.12" + resolved "https://registry.npmjs.org/@storybook/channels/-/channels-6.3.12.tgz#aa0d793895a8b211f0ad3459c61c1bcafd0093c7" + integrity sha512-l4sA+g1PdUV8YCbgs47fIKREdEQAKNdQIZw0b7BfTvY9t0x5yfBywgQhYON/lIeiNGz2OlIuD+VUtqYfCtNSyw== + dependencies: + core-js "^3.8.2" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + "@storybook/channels@6.3.7": version "6.3.7" resolved "https://registry.npmjs.org/@storybook/channels/-/channels-6.3.7.tgz#85ed5925522b802d959810f78d37aacde7fea66e" @@ -5789,6 +5839,14 @@ core-js "^3.8.2" global "^4.4.0" +"@storybook/client-logger@6.3.12": + version "6.3.12" + resolved "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.3.12.tgz#6585c98923b49fcb25dbceeeb96ef2a83e28e0f4" + integrity sha512-zNDsamZvHnuqLznDdP9dUeGgQ9TyFh4ray3t1VGO7ZqWVZ2xtVCCXjDvMnOXI2ifMpX5UsrOvshIPeE9fMBmiQ== + dependencies: + core-js "^3.8.2" + global "^4.4.0" + "@storybook/client-logger@6.3.7": version "6.3.7" resolved "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.3.7.tgz#ff17b7494e7e9e23089b0d5c5364c371c726bdd1" @@ -5941,6 +5999,13 @@ dependencies: core-js "^3.8.2" +"@storybook/core-events@6.3.12": + version "6.3.12" + resolved "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.3.12.tgz#73f6271d485ef2576234e578bb07705b92805290" + integrity sha512-SXfD7xUUMazaeFkB92qOTUV8Y/RghE4SkEYe5slAdjeocSaH7Nz2WV0rqNEgChg0AQc+JUI66no8L9g0+lw4Gw== + dependencies: + core-js "^3.8.2" + "@storybook/core-events@6.3.7": version "6.3.7" resolved "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.3.7.tgz#c5bc7cae7dc295de73b6b9f671ecbe582582e9bd" @@ -6135,6 +6200,22 @@ qs "^6.10.0" ts-dedent "^2.0.0" +"@storybook/router@6.3.12": + version "6.3.12" + resolved "https://registry.npmjs.org/@storybook/router/-/router-6.3.12.tgz#0d572ec795f588ca886f39cb9b27b94ff3683f84" + integrity sha512-G/pNGCnrJRetCwyEZulHPT+YOcqEj/vkPVDTUfii2qgqukup6K0cjwgd7IukAURnAnnzTi1gmgFuEKUi8GE/KA== + dependencies: + "@reach/router" "^1.3.4" + "@storybook/client-logger" "6.3.12" + "@types/reach__router" "^1.3.7" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + lodash "^4.17.20" + memoizerific "^1.11.3" + qs "^6.10.0" + ts-dedent "^2.0.0" + "@storybook/router@6.3.7": version "6.3.7" resolved "https://registry.npmjs.org/@storybook/router/-/router-6.3.7.tgz#1714a99a58a7b9f08b6fcfe2b678dad6ca896736" @@ -6193,6 +6274,24 @@ resolve-from "^5.0.0" ts-dedent "^2.0.0" +"@storybook/theming@6.3.12": + version "6.3.12" + resolved "https://registry.npmjs.org/@storybook/theming/-/theming-6.3.12.tgz#5bddf9bd90a60709b5ab238ecdb7d9055dd7862e" + integrity sha512-wOJdTEa/VFyFB2UyoqyYGaZdym6EN7RALuQOAMT6zHA282FBmKw8nL5DETHEbctpnHdcrMC/391teK4nNSrdOA== + dependencies: + "@emotion/core" "^10.1.1" + "@emotion/is-prop-valid" "^0.8.6" + "@emotion/styled" "^10.0.27" + "@storybook/client-logger" "6.3.12" + core-js "^3.8.2" + deep-object-diff "^1.1.0" + emotion-theming "^10.0.27" + global "^4.4.0" + memoizerific "^1.11.3" + polished "^4.0.5" + resolve-from "^5.0.0" + ts-dedent "^2.0.0" + "@storybook/theming@6.3.7": version "6.3.7" resolved "https://registry.npmjs.org/@storybook/theming/-/theming-6.3.7.tgz#6daf9a21b26ed607f3c28a82acd90c0248e76d8b" From 8b4284cd5cb1bf071a07d3247e529e12787fbde7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jarek=20=C5=81ukow?= Date: Mon, 1 Nov 2021 10:16:54 +0100 Subject: [PATCH 046/196] Improve API documentation for @backstage/core-plugin-api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jarek Łukow --- .changeset/tender-chefs-approve.md | 6 + packages/core-app-api/api-report.md | 7 +- packages/core-plugin-api/api-report.md | 476 ++++++------------ .../src/analytics/AnalyticsContext.tsx | 8 +- .../core-plugin-api/src/analytics/types.ts | 8 +- .../src/analytics/useAnalytics.tsx | 4 +- .../src/apis/definitions/AlertApi.ts | 13 +- .../src/apis/definitions/AnalyticsApi.ts | 15 + .../src/apis/definitions/AppThemeApi.ts | 9 + .../src/apis/definitions/ConfigApi.ts | 7 + .../src/apis/definitions/DiscoveryApi.ts | 9 + .../src/apis/definitions/ErrorApi.ts | 15 +- .../src/apis/definitions/FeatureFlagsApi.ts | 49 +- .../src/apis/definitions/IdentityApi.ts | 7 + .../src/apis/definitions/OAuthRequestApi.ts | 23 +- .../src/apis/definitions/StorageApi.ts | 30 +- .../src/apis/definitions/auth.ts | 111 +++- .../core-plugin-api/src/apis/system/ApiRef.ts | 12 + .../src/apis/system/helpers.ts | 27 +- .../core-plugin-api/src/apis/system/index.ts | 1 + .../core-plugin-api/src/apis/system/types.ts | 42 ++ .../src/apis/system/useApi.tsx | 17 + packages/core-plugin-api/src/app/types.ts | 30 ++ packages/core-plugin-api/src/app/useApp.tsx | 5 + .../core-plugin-api/src/deprecatedTypes.ts | 3 +- .../src/extensions/componentData.tsx | 24 + .../src/extensions/extensions.tsx | 55 +- .../core-plugin-api/src/extensions/index.ts | 1 + .../src/extensions/useElementFilter.tsx | 18 +- packages/core-plugin-api/src/icons/index.ts | 2 +- packages/core-plugin-api/src/icons/types.ts | 6 + .../core-plugin-api/src/plugin/Plugin.tsx | 9 + packages/core-plugin-api/src/plugin/index.ts | 2 + packages/core-plugin-api/src/plugin/types.ts | 60 ++- .../src/routing/ExternalRouteRef.ts | 13 + .../core-plugin-api/src/routing/RouteRef.ts | 13 + .../src/routing/SubRouteRef.ts | 47 +- packages/core-plugin-api/src/routing/index.ts | 17 +- packages/core-plugin-api/src/routing/types.ts | 99 +++- .../src/routing/useRouteRef.tsx | 38 ++ .../src/routing/useRouteRefParams.ts | 5 + 41 files changed, 939 insertions(+), 404 deletions(-) create mode 100644 .changeset/tender-chefs-approve.md diff --git a/.changeset/tender-chefs-approve.md b/.changeset/tender-chefs-approve.md new file mode 100644 index 0000000000..1cc618507c --- /dev/null +++ b/.changeset/tender-chefs-approve.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-app-api': patch +'@backstage/core-plugin-api': patch +--- + +Improve API documentation for @backstage/core-plugin-api diff --git a/packages/core-app-api/api-report.md b/packages/core-app-api/api-report.md index 09d3313c84..8fb3d5c788 100644 --- a/packages/core-app-api/api-report.md +++ b/packages/core-app-api/api-report.md @@ -28,6 +28,7 @@ import { bitbucketAuthApiRef } from '@backstage/core-plugin-api'; import { ComponentType } from 'react'; import { ConfigReader } from '@backstage/config'; import { DiscoveryApi } from '@backstage/core-plugin-api'; +import { Error as Error_2 } from '@backstage/core-plugin-api'; import { ErrorApi } from '@backstage/core-plugin-api'; import { ErrorContext } from '@backstage/core-plugin-api'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; @@ -298,11 +299,7 @@ export class ErrorAlerter implements ErrorApi { constructor(alertApi: AlertApi, errorApi: ErrorApi); // (undocumented) error$(): Observable<{ - error: { - name: string; - message: string; - stack?: string | undefined; - }; + error: Error_2; context?: ErrorContext | undefined; }>; // (undocumented) diff --git a/packages/core-plugin-api/api-report.md b/packages/core-plugin-api/api-report.md index 88afc0e8e6..3225c1a28f 100644 --- a/packages/core-plugin-api/api-report.md +++ b/packages/core-plugin-api/api-report.md @@ -16,41 +16,29 @@ import { ReactNode } from 'react'; import { Subscription as Subscription_2 } from '@backstage/types'; import { SvgIconProps } from '@material-ui/core'; -// Warning: (ae-missing-release-tag) "AlertApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type AlertApi = { post(alert: AlertMessage): void; alert$(): Observable_2; }; -// Warning: (ae-missing-release-tag) "alertApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export const alertApiRef: ApiRef; -// Warning: (ae-missing-release-tag) "AlertMessage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type AlertMessage = { message: string; severity?: 'success' | 'info' | 'warning' | 'error'; }; -// Warning: (ae-missing-release-tag) "AnalyticsApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type AnalyticsApi = { captureEvent(event: AnalyticsEvent): void; }; -// Warning: (ae-missing-release-tag) "analyticsApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export const analyticsApiRef: ApiRef; -// Warning: (ae-missing-release-tag) "AnalyticsContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export const AnalyticsContext: ({ attributes, @@ -60,14 +48,10 @@ export const AnalyticsContext: ({ children: ReactNode; }) => JSX.Element; -// Warning: (ae-missing-release-tag) "AnalyticsContextValue" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type AnalyticsContextValue = CommonAnalyticsContext & AnyAnalyticsContext; -// Warning: (ae-missing-release-tag) "AnalyticsEvent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type AnalyticsEvent = { action: string; @@ -77,15 +61,11 @@ export type AnalyticsEvent = { context: AnalyticsContextValue; }; -// Warning: (ae-missing-release-tag) "AnalyticsEventAttributes" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type AnalyticsEventAttributes = { [attribute in string]: string | boolean | number; }; -// Warning: (ae-missing-release-tag) "AnalyticsTracker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type AnalyticsTracker = { captureEvent: ( @@ -98,16 +78,12 @@ export type AnalyticsTracker = { ) => void; }; -// Warning: (ae-missing-release-tag) "AnyAnalyticsContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type AnyAnalyticsContext = { [param in string]: string | boolean | number | undefined; }; -// Warning: (ae-missing-release-tag) "AnyApiFactory" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type AnyApiFactory = ApiFactory< unknown, unknown, @@ -116,14 +92,27 @@ export type AnyApiFactory = ApiFactory< } >; -// Warning: (ae-missing-release-tag) "AnyApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type AnyApiRef = ApiRef; -// Warning: (ae-missing-release-tag) "ApiFactory" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public +export type AnyExternalRoutes = { + [name: string]: ExternalRouteRef; +}; + +// @public +export type AnyParams = + | { + [param in string]: string; + } + | undefined; + +// @public +export type AnyRoutes = { + [name: string]: RouteRef | SubRouteRef; +}; + +// @public export type ApiFactory< Api, Impl extends Api, @@ -136,24 +125,24 @@ export type ApiFactory< factory(deps: Deps): Impl; }; -// Warning: (ae-missing-release-tag) "ApiHolder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type ApiHolder = { get(api: ApiRef): T | undefined; }; -// Warning: (ae-missing-release-tag) "ApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type ApiRef = { id: string; T: T; }; -// Warning: (ae-missing-release-tag) "ApiRefsToTypes" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public +export type ApiRefConfig = { + id: string; + description?: string; +}; + +// @public export type ApiRefsToTypes< T extends { [key in string]: ApiRef; @@ -162,14 +151,10 @@ export type ApiRefsToTypes< [key in keyof T]: ApiRefType; }; -// Warning: (ae-missing-release-tag) "ApiRefType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type ApiRefType = T extends ApiRef ? U : never; -// Warning: (ae-missing-release-tag) "AppComponents" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type AppComponents = { NotFoundErrorPage: ComponentType<{}>; BootErrorPage: ComponentType; @@ -179,17 +164,13 @@ export type AppComponents = { SignInPage?: ComponentType; }; -// Warning: (ae-missing-release-tag) "AppContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type AppContext = { getPlugins(): BackstagePlugin[]; getSystemIcon(key: string): IconComponent | undefined; getComponents(): AppComponents; }; -// Warning: (ae-missing-release-tag) "AppTheme" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type AppTheme = { id: string; @@ -199,8 +180,6 @@ export type AppTheme = { icon?: React.ReactElement; }; -// Warning: (ae-missing-release-tag) "AppThemeApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type AppThemeApi = { getInstalledThemes(): AppTheme[]; @@ -209,77 +188,56 @@ export type AppThemeApi = { setActiveThemeId(themeId?: string): void; }; -// Warning: (ae-missing-release-tag) "appThemeApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export const appThemeApiRef: ApiRef; -// Warning: (ae-missing-release-tag) "atlassianAuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export const atlassianAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi >; -// Warning: (ae-missing-release-tag) "attachComponentData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function attachComponentData

( component: ComponentType

, type: string, data: unknown, ): void; -// Warning: (ae-missing-release-tag) "auth0AuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export const auth0AuthApiRef: ApiRef< OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi >; -// Warning: (ae-missing-release-tag) "AuthProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type AuthProvider = { title: string; icon: IconComponent; }; -// Warning: (ae-missing-release-tag) "AuthRequester" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type AuthRequester = ( scopes: Set, ) => Promise; -// Warning: (ae-missing-release-tag) "AuthRequesterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type AuthRequesterOptions = { provider: AuthProvider; onAuthRequest(scopes: Set): Promise; }; -// Warning: (ae-missing-release-tag) "AuthRequestOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type AuthRequestOptions = { optional?: boolean; instantPopup?: boolean; }; -// Warning: (ae-missing-release-tag) "BackstageIdentity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type BackstageIdentity = { id: string; idToken: string; token: string; }; -// Warning: (tsdoc-undefined-tag) The TSDoc tag "@IdentityApi" is not defined in this configuration -// Warning: (ae-missing-release-tag) "BackstageIdentityApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type BackstageIdentityApi = { getBackstageIdentity( @@ -287,11 +245,7 @@ export type BackstageIdentityApi = { ): Promise; }; -// Warning: (ae-forgotten-export) The symbol "AnyRoutes" needs to be exported by the entry point index.d.ts -// Warning: (ae-forgotten-export) The symbol "AnyExternalRoutes" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "BackstagePlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type BackstagePlugin< Routes extends AnyRoutes = {}, ExternalRoutes extends AnyExternalRoutes = {}, @@ -304,23 +258,17 @@ export type BackstagePlugin< externalRoutes: ExternalRoutes; }; -// Warning: (ae-missing-release-tag) "bitbucketAuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export const bitbucketAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi >; -// Warning: (ae-missing-release-tag) "BootErrorPageProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type BootErrorPageProps = { step: 'load-config' | 'load-chunk'; error: Error; }; -// Warning: (ae-missing-release-tag) "CommonAnalyticsContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type CommonAnalyticsContext = { pluginId: string; @@ -328,19 +276,21 @@ export type CommonAnalyticsContext = { extension: string; }; -// Warning: (ae-missing-release-tag) "ConfigApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// +// @public +export type ComponentLoader = + | { + lazy: () => Promise; + } + | { + sync: T; + }; + // @public export type ConfigApi = Config; -// Warning: (ae-missing-release-tag) "configApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export const configApiRef: ApiRef; -// Warning: (ae-missing-release-tag) "createApiFactory" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// Warning: (ae-missing-release-tag) "createApiFactory" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export function createApiFactory< Api, @@ -350,29 +300,21 @@ export function createApiFactory< }, >(factory: ApiFactory): ApiFactory; -// @public (undocumented) +// @public export function createApiFactory( api: ApiRef, instance: Impl, ): ApiFactory; -// Warning: (ae-forgotten-export) The symbol "ApiRefConfig" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "createApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function createApiRef(config: ApiRefConfig): ApiRef; -// Warning: (ae-missing-release-tag) "createComponentExtension" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function createComponentExtension< T extends (props: any) => JSX.Element | null, >(options: { component: ComponentLoader; name?: string }): Extension; -// Warning: (ae-forgotten-export) The symbol "OptionalParams" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "createExternalRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function createExternalRouteRef< Params extends { [param in ParamKey]: string; @@ -385,9 +327,7 @@ export function createExternalRouteRef< optional?: Optional; }): ExternalRouteRef, Optional>; -// Warning: (ae-missing-release-tag) "createPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function createPlugin< Routes extends AnyRoutes = {}, ExternalRoutes extends AnyExternalRoutes = {}, @@ -395,9 +335,7 @@ export function createPlugin< config: PluginConfig, ): BackstagePlugin; -// Warning: (ae-missing-release-tag) "createReactExtension" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function createReactExtension< T extends (props: any) => JSX.Element | null, >(options: { @@ -406,9 +344,7 @@ export function createReactExtension< name?: string; }): Extension; -// Warning: (ae-missing-release-tag) "createRoutableExtension" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function createRoutableExtension< T extends (props: any) => JSX.Element | null, >(options: { @@ -417,9 +353,7 @@ export function createRoutableExtension< name?: string; }): Extension; -// Warning: (ae-missing-release-tag) "createRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function createRouteRef< Params extends { [param in ParamKey]: string; @@ -433,12 +367,7 @@ export function createRouteRef< title?: string; }): RouteRef>; -// Warning: (ae-forgotten-export) The symbol "AnyParams" needs to be exported by the entry point index.d.ts -// Warning: (ae-forgotten-export) The symbol "MakeSubRouteRef" needs to be exported by the entry point index.d.ts -// Warning: (ae-forgotten-export) The symbol "PathParams" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "createSubRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function createSubRouteRef< Path extends string, ParentParams extends AnyParams = never, @@ -448,20 +377,14 @@ export function createSubRouteRef< parent: RouteRef; }): MakeSubRouteRef, ParentParams>; -// Warning: (ae-missing-release-tag) "DiscoveryApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type DiscoveryApi = { getBaseUrl(pluginId: string): Promise; }; -// Warning: (ae-missing-release-tag) "discoveryApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export const discoveryApiRef: ApiRef; -// Warning: (ae-missing-release-tag) "ElementCollection" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export interface ElementCollection { findComponentData(query: { key: string }): T[]; @@ -476,8 +399,14 @@ export interface ElementCollection { }): ElementCollection; } -// Warning: (ae-missing-release-tag) "ErrorApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// +// @public +type Error_2 = { + name: string; + message: string; + stack?: string; +}; +export { Error_2 as Error }; + // @public export type ErrorApi = { post(error: Error_2, context?: ErrorContext): void; @@ -487,37 +416,27 @@ export type ErrorApi = { }>; }; -// Warning: (ae-missing-release-tag) "errorApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export const errorApiRef: ApiRef; -// Warning: (ae-missing-release-tag) "ErrorBoundaryFallbackProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type ErrorBoundaryFallbackProps = { plugin?: BackstagePlugin; error: Error; resetError: () => void; }; -// Warning: (ae-missing-release-tag) "ErrorContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type ErrorContext = { hidden?: boolean; }; -// Warning: (ae-missing-release-tag) "Extension" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type Extension = { expose(plugin: BackstagePlugin): T; }; -// Warning: (ae-missing-release-tag) "ExternalRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type ExternalRouteRef< Params extends AnyParams = any, Optional extends boolean = any, @@ -527,25 +446,19 @@ export type ExternalRouteRef< optional?: Optional; }; -// Warning: (ae-missing-release-tag) "FeatureFlag" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type FeatureFlag = { name: string; pluginId: string; }; -// Warning: (ae-missing-release-tag) "FeatureFlagOutput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type FeatureFlagOutput = { type: 'feature-flag'; name: string; }; -// Warning: (ae-missing-release-tag) "FeatureFlagsApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export interface FeatureFlagsApi { getRegisteredFlags(): FeatureFlag[]; isActive(name: string): boolean; @@ -553,60 +466,42 @@ export interface FeatureFlagsApi { save(options: FeatureFlagsSaveOptions): void; } -// Warning: (ae-missing-release-tag) "featureFlagsApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export const featureFlagsApiRef: ApiRef; -// Warning: (ae-missing-release-tag) "FeatureFlagsHooks" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type FeatureFlagsHooks = { register(name: string): void; }; -// Warning: (ae-missing-release-tag) "FeatureFlagsSaveOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type FeatureFlagsSaveOptions = { states: Record; merge?: boolean; }; -// Warning: (ae-missing-release-tag) "FeatureFlagState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export enum FeatureFlagState { - // (undocumented) Active = 1, - // (undocumented) None = 0, } -// Warning: (ae-missing-release-tag) "getComponentData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function getComponentData( node: ReactNode, type: string, ): T | undefined; -// Warning: (ae-missing-release-tag) "githubAuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export const githubAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi >; -// Warning: (ae-missing-release-tag) "gitlabAuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export const gitlabAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi >; -// Warning: (ae-missing-release-tag) "googleAuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export const googleAuthApiRef: ApiRef< OAuthApi & @@ -616,15 +511,11 @@ export const googleAuthApiRef: ApiRef< SessionApi >; -// Warning: (ae-missing-release-tag) "IconComponent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type IconComponent = ComponentType<{ fontSize?: 'default' | 'small' | 'large'; }>; -// Warning: (ae-missing-release-tag) "IdentityApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type IdentityApi = { getUserId(): string; @@ -633,13 +524,27 @@ export type IdentityApi = { signOut(): Promise; }; -// Warning: (ae-missing-release-tag) "identityApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export const identityApiRef: ApiRef; -// Warning: (ae-missing-release-tag) "microsoftAuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// +// @public +export type MakeSubRouteRef< + Params extends { + [param in string]: string; + }, + ParentParams extends AnyParams, +> = keyof Params & keyof ParentParams extends never + ? SubRouteRef>> + : never; + +// @public +export type MergeParams< + P1 extends { + [param in string]: string; + }, + P2 extends AnyParams, +> = (P1[keyof P1] extends never ? {} : P1) & (P2 extends undefined ? {} : P2); + // @public export const microsoftAuthApiRef: ApiRef< OAuthApi & @@ -649,8 +554,6 @@ export const microsoftAuthApiRef: ApiRef< SessionApi >; -// Warning: (ae-missing-release-tag) "oauth2ApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export const oauth2ApiRef: ApiRef< OAuthApi & @@ -660,8 +563,6 @@ export const oauth2ApiRef: ApiRef< SessionApi >; -// Warning: (ae-missing-release-tag) "OAuthApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type OAuthApi = { getAccessToken( @@ -670,8 +571,6 @@ export type OAuthApi = { ): Promise; }; -// Warning: (ae-missing-release-tag) "OAuthRequestApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type OAuthRequestApi = { createAuthRequester( @@ -680,13 +579,9 @@ export type OAuthRequestApi = { authRequest$(): Observable_2; }; -// Warning: (ae-missing-release-tag) "oauthRequestApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export const oauthRequestApiRef: ApiRef; -// Warning: (ae-missing-release-tag) "OAuthScope" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type OAuthScope = string | string[]; @@ -696,8 +591,6 @@ export type Observable = Observable_2; // @public @deprecated export type Observer = Observer_2; -// Warning: (ae-missing-release-tag) "oidcAuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export const oidcAuthApiRef: ApiRef< OAuthApi & @@ -707,8 +600,6 @@ export const oidcAuthApiRef: ApiRef< SessionApi >; -// Warning: (ae-missing-release-tag) "oktaAuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export const oktaAuthApiRef: ApiRef< OAuthApi & @@ -718,9 +609,10 @@ export const oktaAuthApiRef: ApiRef< SessionApi >; -// Warning: (ae-missing-release-tag) "oneloginAuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public +export type OldIconComponent = ComponentType; + +// @public export const oneloginAuthApiRef: ApiRef< OAuthApi & OpenIdConnectApi & @@ -729,15 +621,39 @@ export const oneloginAuthApiRef: ApiRef< SessionApi >; -// Warning: (ae-missing-release-tag) "OpenIdConnectApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type OpenIdConnectApi = { getIdToken(options?: AuthRequestOptions): Promise; }; -// Warning: (ae-missing-release-tag) "PendingAuthRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// +// @public +export type OptionalParams< + Params extends { + [param in string]: string; + }, +> = Params[keyof Params] extends never ? undefined : Params; + +// @public +export type ParamKeys = keyof Params extends never + ? [] + : (keyof Params)[]; + +// @public +export type ParamNames = + S extends `${infer Part}/${infer Rest}` + ? ParamPart | ParamNames + : ParamPart; + +// @public +export type ParamPart = S extends `:${infer Param}` + ? Param + : never; + +// @public +export type PathParams = { + [name in ParamNames]: string; +}; + // @public export type PendingAuthRequest = { provider: AuthProvider; @@ -745,9 +661,7 @@ export type PendingAuthRequest = { trigger(): Promise; }; -// Warning: (ae-missing-release-tag) "PluginConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type PluginConfig< Routes extends AnyRoutes, ExternalRoutes extends AnyExternalRoutes, @@ -759,20 +673,14 @@ export type PluginConfig< externalRoutes?: ExternalRoutes; }; -// Warning: (ae-missing-release-tag) "PluginHooks" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type PluginHooks = { featureFlags: FeatureFlagsHooks; }; -// Warning: (ae-missing-release-tag) "PluginOutput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type PluginOutput = FeatureFlagOutput; -// Warning: (ae-missing-release-tag) "ProfileInfo" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type ProfileInfo = { email?: string; @@ -780,28 +688,25 @@ export type ProfileInfo = { picture?: string; }; -// Warning: (ae-missing-release-tag) "ProfileInfoApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type ProfileInfoApi = { getProfile(options?: AuthRequestOptions): Promise; }; -// Warning: (ae-missing-release-tag) "RouteOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public +export type RouteFunc = ( + ...[params]: Params extends undefined ? readonly [] : readonly [Params] +) => string; + +// @public export type RouteOptions = { exact?: boolean; }; -// Warning: (ae-missing-release-tag) "RoutePath" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type RoutePath = string; -// Warning: (ae-missing-release-tag) "RouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type RouteRef = { $$routeRefType: 'absolute'; params: ParamKeys; @@ -810,15 +715,11 @@ export type RouteRef = { title?: string; }; -// Warning: (ae-missing-release-tag) "samlAuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export const samlAuthApiRef: ApiRef< ProfileInfoApi & BackstageIdentityApi & SessionApi >; -// Warning: (ae-missing-release-tag) "SessionApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export type SessionApi = { signIn(): Promise; @@ -826,26 +727,18 @@ export type SessionApi = { sessionState$(): Observable_2; }; -// Warning: (ae-missing-release-tag) "SessionState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export enum SessionState { - // (undocumented) SignedIn = 'SignedIn', - // (undocumented) SignedOut = 'SignedOut', } -// Warning: (ae-missing-release-tag) "SignInPageProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type SignInPageProps = { onResult(result: SignInResult): void; }; -// Warning: (ae-missing-release-tag) "SignInResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type SignInResult = { userId: string; profile: ProfileInfo; @@ -853,46 +746,25 @@ export type SignInResult = { signOut?: () => Promise; }; -// Warning: (ae-missing-release-tag) "StorageApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export interface StorageApi { - // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen - // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' forBucket(name: string): StorageApi; - // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen - // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' - // Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag - // Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" - // Warning: (tsdoc-undefined-tag) The TSDoc tag "@return" is not defined in this configuration get(key: string): T | undefined; - // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen - // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' observe$(key: string): Observable_2>; - // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen - // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' remove(key: string): Promise; - // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen - // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' set(key: string, data: any): Promise; } -// Warning: (ae-missing-release-tag) "storageApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export const storageApiRef: ApiRef; -// Warning: (ae-missing-release-tag) "StorageValueChange" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type StorageValueChange = { key: string; newValue?: T; }; -// Warning: (ae-missing-release-tag) "SubRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type SubRouteRef = { $$routeRefType: 'sub'; parent: RouteRef; @@ -903,35 +775,23 @@ export type SubRouteRef = { // @public @deprecated export type Subscription = Subscription_2; -// Warning: (ae-missing-release-tag) "TypesToApiRefs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type TypesToApiRefs = { [key in keyof T]: ApiRef; }; -// Warning: (ae-missing-release-tag) "useAnalytics" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export function useAnalytics(): AnalyticsTracker; -// Warning: (ae-missing-release-tag) "useApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function useApi(apiRef: ApiRef): T; -// Warning: (ae-missing-release-tag) "useApiHolder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function useApiHolder(): ApiHolder; -// Warning: (ae-missing-release-tag) "useApp" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export const useApp: () => AppContext; -// Warning: (ae-missing-release-tag) "useElementFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export function useElementFilter( node: ReactNode, @@ -939,51 +799,29 @@ export function useElementFilter( dependencies?: any[], ): T; -// Warning: (ae-missing-release-tag) "UserFlags" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type UserFlags = {}; -// Warning: (ae-forgotten-export) The symbol "RouteFunc" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "useRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// Warning: (ae-missing-release-tag) "useRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function useRouteRef( routeRef: ExternalRouteRef, ): Optional extends true ? RouteFunc | undefined : RouteFunc; -// @public (undocumented) +// @public export function useRouteRef( routeRef: RouteRef | SubRouteRef, ): RouteFunc; -// Warning: (ae-missing-release-tag) "useRouteRefParams" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function useRouteRefParams( _routeRef: RouteRef | SubRouteRef, ): Params; -// Warning: (ae-missing-release-tag) "withApis" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function withApis(apis: TypesToApiRefs):

( WrappedComponent: React_2.ComponentType

, ) => { (props: React_2.PropsWithChildren>): JSX.Element; displayName: string; }; - -// Warnings were encountered during analysis: -// -// src/apis/definitions/ErrorApi.d.ts:37:5 - (ae-forgotten-export) The symbol "Error" needs to be exported by the entry point index.d.ts -// src/apis/definitions/auth.d.ts:29:8 - (tsdoc-undefined-tag) The TSDoc tag "@default" is not defined in this configuration -// src/apis/definitions/auth.d.ts:38:8 - (tsdoc-undefined-tag) The TSDoc tag "@default" is not defined in this configuration -// src/apis/definitions/auth.d.ts:96:68 - (tsdoc-undefined-tag) The TSDoc tag "@AuthRequestOptions" is not defined in this configuration -// src/apis/definitions/auth.d.ts:110:16 - (tsdoc-undefined-tag) The TSDoc tag "@IdentityApi" is not defined in this configuration -// src/apis/definitions/auth.d.ts:113:68 - (tsdoc-undefined-tag) The TSDoc tag "@AuthRequestOptions" is not defined in this configuration -// src/extensions/extensions.d.ts:15:5 - (ae-forgotten-export) The symbol "ComponentLoader" needs to be exported by the entry point index.d.ts -// src/routing/RouteRef.d.ts:35:5 - (ae-forgotten-export) The symbol "OldIconComponent" needs to be exported by the entry point index.d.ts -// src/routing/types.d.ts:30:5 - (ae-forgotten-export) The symbol "ParamKeys" needs to be exported by the entry point index.d.ts ``` diff --git a/packages/core-plugin-api/src/analytics/AnalyticsContext.tsx b/packages/core-plugin-api/src/analytics/AnalyticsContext.tsx index adbd9be6f3..bb2141d47f 100644 --- a/packages/core-plugin-api/src/analytics/AnalyticsContext.tsx +++ b/packages/core-plugin-api/src/analytics/AnalyticsContext.tsx @@ -27,7 +27,8 @@ const AnalyticsReactContext = /** * A "private" (to this package) hook that enables context inheritance and a * way to read Analytics Context values at event capture-time. - * @private + * + * @internal */ export const useAnalyticsContext = (): AnalyticsContextValue => { const theContext = useContext(AnalyticsReactContext); @@ -54,8 +55,12 @@ export const useAnalyticsContext = (): AnalyticsContextValue => { * Provides components in the child react tree an Analytics Context, ensuring * all analytics events captured within the context have relevant attributes. * + * @remarks + * * Analytics contexts are additive, meaning the context ultimately emitted with * an event is the combination of all contexts in the parent tree. + * + * @public */ export const AnalyticsContext = ({ attributes, @@ -84,6 +89,7 @@ export const AnalyticsContext = ({ * * @param Component - Component to be wrapped with analytics context attributes * @param values - Analytics context key/value pairs. + * @internal */ export function withAnalyticsContext

( Component: React.ComponentType

, diff --git a/packages/core-plugin-api/src/analytics/types.ts b/packages/core-plugin-api/src/analytics/types.ts index ea6c3b030b..d001c38d3e 100644 --- a/packages/core-plugin-api/src/analytics/types.ts +++ b/packages/core-plugin-api/src/analytics/types.ts @@ -16,6 +16,8 @@ /** * Common analytics context attributes. + * + * @public */ export type CommonAnalyticsContext = { /** @@ -35,7 +37,9 @@ export type CommonAnalyticsContext = { }; /** - * Allow arbitrary scalar values as context attributes too. + * Allows arbitrary scalar values as context attributes too. + * + * @public */ export type AnyAnalyticsContext = { [param in string]: string | boolean | number | undefined; @@ -43,6 +47,8 @@ export type AnyAnalyticsContext = { /** * Analytics context envelope. + * + * @public */ export type AnalyticsContextValue = CommonAnalyticsContext & AnyAnalyticsContext; diff --git a/packages/core-plugin-api/src/analytics/useAnalytics.tsx b/packages/core-plugin-api/src/analytics/useAnalytics.tsx index 02d1ecc3c3..5a047e5a63 100644 --- a/packages/core-plugin-api/src/analytics/useAnalytics.tsx +++ b/packages/core-plugin-api/src/analytics/useAnalytics.tsx @@ -33,7 +33,9 @@ function useAnalyticsApi(): AnalyticsApi { } /** - * Get a pre-configured analytics tracker. + * Gets a pre-configured analytics tracker. + * + * @public */ export function useAnalytics(): AnalyticsTracker { const trackerRef = useRef(null); diff --git a/packages/core-plugin-api/src/apis/definitions/AlertApi.ts b/packages/core-plugin-api/src/apis/definitions/AlertApi.ts index 7110c1d9f8..0c84f7c975 100644 --- a/packages/core-plugin-api/src/apis/definitions/AlertApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/AlertApi.ts @@ -17,6 +17,11 @@ import { createApiRef, ApiRef } from '../system'; import { Observable } from '@backstage/types'; +/** + * Message handled by the {@link AlertApi}. + * + * @public + */ export type AlertMessage = { message: string; // Severity will default to success since that is what material ui defaults the value to. @@ -25,8 +30,9 @@ export type AlertMessage = { /** * The alert API is used to report alerts to the app, and display them to the user. + * + * @public */ - export type AlertApi = { /** * Post an alert for handling by the application. @@ -39,6 +45,11 @@ export type AlertApi = { alert$(): Observable; }; +/** + * The {@link ApiRef} of {@link AlertApi}. + * + * @public + */ export const alertApiRef: ApiRef = createApiRef({ id: 'core.alert', }); diff --git a/packages/core-plugin-api/src/apis/definitions/AnalyticsApi.ts b/packages/core-plugin-api/src/apis/definitions/AnalyticsApi.ts index 43e35607a3..ef190d6c21 100644 --- a/packages/core-plugin-api/src/apis/definitions/AnalyticsApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/AnalyticsApi.ts @@ -20,6 +20,8 @@ import { AnalyticsContextValue } from '../../analytics/types'; /** * Represents an event worth tracking in an analytics system that could inform * how users of a Backstage instance are using its features. + * + * @public */ export type AnalyticsEvent = { /** @@ -76,6 +78,8 @@ export type AnalyticsEvent = { /** * A structure allowing other arbitrary metadata to be provided by analytics * event emitters. + * + * @public */ export type AnalyticsEventAttributes = { [attribute in string]: string | boolean | number; @@ -84,6 +88,8 @@ export type AnalyticsEventAttributes = { /** * Represents a tracker with methods that can be called to track events in a * configured analytics service. + * + * @public */ export type AnalyticsTracker = { captureEvent: ( @@ -99,9 +105,13 @@ export type AnalyticsTracker = { /** * The Analytics API is used to track user behavior in a Backstage instance. * + * @remarks + * * To instrument your App or Plugin, retrieve an analytics tracker using the * useAnalytics() hook. This will return a pre-configured AnalyticsTracker * with relevant methods for instrumentation. + * + * @public */ export type AnalyticsApi = { /** @@ -111,6 +121,11 @@ export type AnalyticsApi = { captureEvent(event: AnalyticsEvent): void; }; +/** + * The {@link ApiRef} of {@link AnalyticsApi}. + * + * @public + */ export const analyticsApiRef: ApiRef = createApiRef({ id: 'core.analytics', }); diff --git a/packages/core-plugin-api/src/apis/definitions/AppThemeApi.ts b/packages/core-plugin-api/src/apis/definitions/AppThemeApi.ts index 5ac16f83fd..de523663f2 100644 --- a/packages/core-plugin-api/src/apis/definitions/AppThemeApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/AppThemeApi.ts @@ -20,6 +20,8 @@ import { Observable } from '@backstage/types'; /** * Describes a theme provided by the app. + * + * @public */ export type AppTheme = { /** @@ -51,6 +53,8 @@ export type AppTheme = { /** * The AppThemeApi gives access to the current app theme, and allows switching * to other options that have been registered as a part of the App. + * + * @public */ export type AppThemeApi = { /** @@ -76,6 +80,11 @@ export type AppThemeApi = { setActiveThemeId(themeId?: string): void; }; +/** + * The {@link ApiRef} of {@link AppThemeApi}. + * + * @public + */ export const appThemeApiRef: ApiRef = createApiRef({ id: 'core.apptheme', }); diff --git a/packages/core-plugin-api/src/apis/definitions/ConfigApi.ts b/packages/core-plugin-api/src/apis/definitions/ConfigApi.ts index 08b9f91f57..d3bada9e46 100644 --- a/packages/core-plugin-api/src/apis/definitions/ConfigApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/ConfigApi.ts @@ -19,9 +19,16 @@ import { Config } from '@backstage/config'; /** * The Config API is used to provide a mechanism to access the * runtime configuration of the system. + * + * @public */ export type ConfigApi = Config; +/** + * The {@link ApiRef} of {@link ConfigApi}. + * + * @public + */ export const configApiRef: ApiRef = createApiRef({ id: 'core.config', }); diff --git a/packages/core-plugin-api/src/apis/definitions/DiscoveryApi.ts b/packages/core-plugin-api/src/apis/definitions/DiscoveryApi.ts index 7082cda9dc..d23fe3db6c 100644 --- a/packages/core-plugin-api/src/apis/definitions/DiscoveryApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/DiscoveryApi.ts @@ -19,6 +19,8 @@ import { ApiRef, createApiRef } from '../system'; * The discovery API is used to provide a mechanism for plugins to * discover the endpoint to use to talk to their backend counterpart. * + * @remarks + * * The purpose of the discovery API is to allow for many different deployment * setups and routing methods through a central configuration, instead * of letting each individual plugin manage that configuration. @@ -26,6 +28,8 @@ import { ApiRef, createApiRef } from '../system'; * Implementations of the discovery API can be a simple as a URL pattern * using the pluginId, but could also have overrides for individual plugins, * or query a separate discovery service. + * + * @public */ export type DiscoveryApi = { /** @@ -41,6 +45,11 @@ export type DiscoveryApi = { getBaseUrl(pluginId: string): Promise; }; +/** + * The {@link ApiRef} of {@link DiscoveryApi}. + * + * @public + */ export const discoveryApiRef: ApiRef = createApiRef({ id: 'core.discovery', }); diff --git a/packages/core-plugin-api/src/apis/definitions/ErrorApi.ts b/packages/core-plugin-api/src/apis/definitions/ErrorApi.ts index 69c331bc06..820660ac52 100644 --- a/packages/core-plugin-api/src/apis/definitions/ErrorApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/ErrorApi.ts @@ -20,8 +20,10 @@ import { Observable } from '@backstage/types'; /** * Mirrors the JavaScript Error class, for the purpose of * providing documentation and optional fields. + * + * @public */ -type Error = { +export type Error = { name: string; message: string; stack?: string; @@ -29,6 +31,8 @@ type Error = { /** * Provides additional information about an error that was posted to the application. + * + * @public */ export type ErrorContext = { // If set to true, this error should not be displayed to the user. Defaults to false. @@ -38,6 +42,8 @@ export type ErrorContext = { /** * The error API is used to report errors to the app, and display them to the user. * + * @remarks + * * Plugins can use this API as a method of displaying errors to the user, but also * to report errors for collection by error reporting services. * @@ -49,6 +55,8 @@ export type ErrorContext = { * if it would be useful to collect or log it for debugging purposes, but with * the hidden flag set. For example, an error arising from form field validation * should probably not be reported, while a failed REST call would be useful to report. + * + * @public */ export type ErrorApi = { /** @@ -62,6 +70,11 @@ export type ErrorApi = { error$(): Observable<{ error: Error; context?: ErrorContext }>; }; +/** + * The {@link ApiRef} of {@link ErrorApi}. + * + * @public + */ export const errorApiRef: ApiRef = createApiRef({ id: 'core.error', }); diff --git a/packages/core-plugin-api/src/apis/definitions/FeatureFlagsApi.ts b/packages/core-plugin-api/src/apis/definitions/FeatureFlagsApi.ts index 07797bf3f1..c669710895 100644 --- a/packages/core-plugin-api/src/apis/definitions/FeatureFlagsApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/FeatureFlagsApi.ts @@ -17,29 +17,35 @@ import { ApiRef, createApiRef } from '../system'; /** - * The feature flags API is used to toggle functionality to users across plugins and Backstage. + * Fetaure flag descriptor. * - * Plugins can use this API to register feature flags that they have available - * for users to enable/disable, and this API will centralize the current user's - * state of which feature flags they would like to enable. - * - * This is ideal for Backstage plugins, as well as your own App, to trial incomplete - * or unstable upcoming features. Although there will be a common interface for users - * to enable and disable feature flags, this API acts as another way to enable/disable. + * @public */ - export type FeatureFlag = { name: string; pluginId: string; }; +/** + * Enum representing the state of a feature flag (inactive/active). + * + * @public + */ export enum FeatureFlagState { + /** + * Feature flag inactive (disabled). + */ None = 0, + /** + * Feature flag active (enabled). + */ Active = 1, } /** * Options to use when saving feature flags. + * + * @public */ export type FeatureFlagsSaveOptions = { /** @@ -55,8 +61,28 @@ export type FeatureFlagsSaveOptions = { merge?: boolean; }; +/** + * User flags alias. + * + * @public + */ export type UserFlags = {}; +/** + * The feature flags API is used to toggle functionality to users across plugins and Backstage. + * + * @remarks + * + * Plugins can use this API to register feature flags that they have available + * for users to enable/disable, and this API will centralize the current user's + * state of which feature flags they would like to enable. + * + * This is ideal for Backstage plugins, as well as your own App, to trial incomplete + * or unstable upcoming features. Although there will be a common interface for users + * to enable and disable feature flags, this API acts as another way to enable/disable. + * + * @public + */ export interface FeatureFlagsApi { /** * Registers a new feature flag. Once a feature flag has been registered it @@ -80,6 +106,11 @@ export interface FeatureFlagsApi { save(options: FeatureFlagsSaveOptions): void; } +/** + * The {@link ApiRef} of {@link FeatureFlagsApi}. + * + * @public + */ export const featureFlagsApiRef: ApiRef = createApiRef({ id: 'core.featureflags', }); diff --git a/packages/core-plugin-api/src/apis/definitions/IdentityApi.ts b/packages/core-plugin-api/src/apis/definitions/IdentityApi.ts index 9d7131fed6..9f68a8b2cc 100644 --- a/packages/core-plugin-api/src/apis/definitions/IdentityApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/IdentityApi.ts @@ -18,6 +18,8 @@ import { ProfileInfo } from './auth'; /** * The Identity API used to identify and get information about the signed in user. + * + * @public */ export type IdentityApi = { /** @@ -49,6 +51,11 @@ export type IdentityApi = { signOut(): Promise; }; +/** + * The {@link ApiRef} of {@link IdentityApi}. + * + * @public + */ export const identityApiRef: ApiRef = createApiRef({ id: 'core.identity', }); diff --git a/packages/core-plugin-api/src/apis/definitions/OAuthRequestApi.ts b/packages/core-plugin-api/src/apis/definitions/OAuthRequestApi.ts index 14609f5b3a..03e129ef6a 100644 --- a/packages/core-plugin-api/src/apis/definitions/OAuthRequestApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/OAuthRequestApi.ts @@ -21,8 +21,12 @@ import { ApiRef, createApiRef } from '../system'; /** * Information about the auth provider that we're requesting a login towards. * + * @remarks + * * This should be shown to the user so that they can be informed about what login is being requested * before a popup is shown. + * + * @public */ export type AuthProvider = { /** @@ -39,6 +43,8 @@ export type AuthProvider = { /** * Describes how to handle auth requests. Both how to show them to the user, and what to do when * the user accesses the auth request. + * + * @public */ export type AuthRequesterOptions = { /** @@ -56,12 +62,16 @@ export type AuthRequesterOptions = { /** * Function used to trigger new auth requests for a set of scopes. * + * @remarks + * * The returned promise will resolve to the same value returned by the onAuthRequest in the - * AuthRequesterOptions. Or rejected, if the request is rejected. + * {@link AuthRequesterOptions}. Or rejected, if the request is rejected. * * This function can be called multiple times before the promise resolves. All calls * will be merged into one request, and the scopes forwarded to the onAuthRequest will be the * union of all requested scopes. + * + * @public */ export type AuthRequester = ( scopes: Set, @@ -71,8 +81,12 @@ export type AuthRequester = ( * An pending auth request for a single auth provider. The request will remain in this pending * state until either reject() or trigger() is called. * + * @remarks + * * Any new requests for the same provider are merged into the existing pending request, meaning * there will only ever be a single pending request for a given provider. + * + * @public */ export type PendingAuthRequest = { /** @@ -95,6 +109,8 @@ export type PendingAuthRequest = { /** * Provides helpers for implemented OAuth login flows within Backstage. + * + * @public */ export type OAuthRequestApi = { /** @@ -127,6 +143,11 @@ export type OAuthRequestApi = { authRequest$(): Observable; }; +/** + * The {@link ApiRef} of {@link OAuthRequestApi}. + * + * @public + */ export const oauthRequestApiRef: ApiRef = createApiRef({ id: 'core.oauthrequest', }); diff --git a/packages/core-plugin-api/src/apis/definitions/StorageApi.ts b/packages/core-plugin-api/src/apis/definitions/StorageApi.ts index f9a52a0bf4..73a19a1ceb 100644 --- a/packages/core-plugin-api/src/apis/definitions/StorageApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/StorageApi.ts @@ -17,48 +17,60 @@ import { ApiRef, createApiRef } from '../system'; import { Observable } from '@backstage/types'; +/** + * Describes a value change event. + * + * @public + */ export type StorageValueChange = { key: string; newValue?: T; }; +/** + * Provides key-value persistence API. + * + * @public + */ export interface StorageApi { /** * Create a bucket to store data in. - * @param {String} name Namespace for the storage to be stored under, + * @param name - Namespace for the storage to be stored under, * will inherit previous namespaces too */ forBucket(name: string): StorageApi; /** * Get the current value for persistent data, use observe$ to be notified of updates. - * - * @param {String} key Unique key associated with the data. - * @return {Object} data The data that should is stored. + * @param key - Unique key associated with the data. */ get(key: string): T | undefined; /** * Remove persistent data. - * - * @param {String} key Unique key associated with the data. + * @param key - Unique key associated with the data. */ remove(key: string): Promise; /** * Save persistent data, and emit messages to anyone that is using observe$ for this key - * - * @param {String} key Unique key associated with the data. + * @param key - Unique key associated with the data. + * @param data - The data to be stored under the key. */ set(key: string, data: any): Promise; /** * Observe changes on a particular key in the bucket - * @param {String} key Unique key associated with the data + * @param key - Unique key associated with the data */ observe$(key: string): Observable>; } +/** + * The {@link ApiRef} of {@link StorageApi}. + * + * @public + */ export const storageApiRef: ApiRef = createApiRef({ id: 'core.storage', }); diff --git a/packages/core-plugin-api/src/apis/definitions/auth.ts b/packages/core-plugin-api/src/apis/definitions/auth.ts index 41c918fa6c..8fe532f3a6 100644 --- a/packages/core-plugin-api/src/apis/definitions/auth.ts +++ b/packages/core-plugin-api/src/apis/definitions/auth.ts @@ -32,11 +32,20 @@ import { Observable } from '@backstage/types'; * An array of scopes, or a scope string formatted according to the * auth provider, which is typically a space separated list. * + * @remarks + * * See the documentation for each auth provider for the list of scopes * supported by each provider. + * + * @public */ export type OAuthScope = string | string[]; +/** + * Configuration of an authentication request. + * + * @public + */ export type AuthRequestOptions = { /** * If this is set to true, the user will not be prompted to log in, @@ -45,7 +54,7 @@ export type AuthRequestOptions = { * This can be used to perform a check whether the user is logged in, or if you don't * want to force a user to be logged in, but provide functionality if they already are. * - * @default false + * @defaultValue false */ optional?: boolean; @@ -55,7 +64,7 @@ export type AuthRequestOptions = { * * The method must be called synchronously from a user action for this to work in all browsers. * - * @default false + * @defaultValue false */ instantPopup?: boolean; }; @@ -63,6 +72,8 @@ export type AuthRequestOptions = { /** * This API provides access to OAuth 2 credentials. It lets you request access tokens, * which can be used to act on behalf of the user when talking to APIs. + * + * @public */ export type OAuthApi = { /** @@ -95,6 +106,8 @@ export type OAuthApi = { /** * This API provides access to OpenID Connect credentials. It lets you request ID tokens, * which can be passed to backend services to prove the user's identity. + * + * @public */ export type OpenIdConnectApi = { /** @@ -113,13 +126,15 @@ export type OpenIdConnectApi = { /** * This API provides access to profile information of the user from an auth provider. + * + * @public */ export type ProfileInfoApi = { /** * Get profile information for the user as supplied by this auth provider. * * If the optional flag is not set, a session is guaranteed to be returned, while if - * the optional flag is set, the session may be undefined. See @AuthRequestOptions for more details. + * the optional flag is set, the session may be undefined. See {@link AuthRequestOptions} for more details. */ getProfile(options?: AuthRequestOptions): Promise; }; @@ -127,23 +142,32 @@ export type ProfileInfoApi = { /** * This API provides access to the user's identity within Backstage. * + * @remarks + * * An auth provider that implements this interface can be used to sign-in to backstage. It is * not intended to be used directly from a plugin, but instead serves as a connection between - * this authentication method and the app's @IdentityApi + * this authentication method and the app's {@link IdentityApi} + * + * @public */ export type BackstageIdentityApi = { /** * Get the user's identity within Backstage. This should normally not be called directly, - * use the @IdentityApi instead. + * use the {@link IdentityApi} instead. * * If the optional flag is not set, a session is guaranteed to be returned, while if - * the optional flag is set, the session may be undefined. See @AuthRequestOptions for more details. + * the optional flag is set, the session may be undefined. See {@link AuthRequestOptions} for more details. */ getBackstageIdentity( options?: AuthRequestOptions, ): Promise; }; +/** + * A (user id, token) pair. + * + * @public + */ export type BackstageIdentity = { /** * The backstage user ID. @@ -163,6 +187,8 @@ export type BackstageIdentity = { /** * Profile information of the user. + * + * @public */ export type ProfileInfo = { /** @@ -183,14 +209,24 @@ export type ProfileInfo = { /** * Session state values passed to subscribers of the SessionApi. + * + * @public */ export enum SessionState { + /** + * User signed in. + */ SignedIn = 'SignedIn', + /** + * User not signed in. + */ SignedOut = 'SignedOut', } /** * The SessionApi provides basic controls for any auth provider that is tied to a persistent session. + * + * @public */ export type SessionApi = { /** @@ -212,10 +248,14 @@ export type SessionApi = { /** * Provides authentication towards Google APIs and identities. * - * See https://developers.google.com/identity/protocols/googlescopes for a full list of supported scopes. + * @remarks + * + * See {@link https://developers.google.com/identity/protocols/googlescopes} for a full list of supported scopes. * * 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. + * + * @public */ export const googleAuthApiRef: ApiRef< OAuthApi & @@ -230,8 +270,12 @@ export const googleAuthApiRef: ApiRef< /** * Provides authentication towards GitHub APIs. * - * See https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/ + * @remarks + * + * See {@link https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/} * for a full list of supported scopes. + * + * @public */ export const githubAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi @@ -242,8 +286,12 @@ export const githubAuthApiRef: ApiRef< /** * Provides authentication towards Okta APIs. * - * See https://developer.okta.com/docs/guides/implement-oauth-for-okta/scopes/ + * @remarks + * + * See {@link https://developer.okta.com/docs/guides/implement-oauth-for-okta/scopes/} * for a full list of supported scopes. + * + * @public */ export const oktaAuthApiRef: ApiRef< OAuthApi & @@ -258,8 +306,12 @@ export const oktaAuthApiRef: ApiRef< /** * Provides authentication towards GitLab APIs. * - * See https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#limiting-scopes-of-a-personal-access-token + * @remarks + * + * See {@link 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. + * + * @public */ export const gitlabAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi @@ -270,8 +322,12 @@ export const gitlabAuthApiRef: ApiRef< /** * Provides authentication towards Auth0 APIs. * - * See https://auth0.com/docs/scopes/current/oidc-scopes + * @remarks + * + * See {@link https://auth0.com/docs/scopes/current/oidc-scopes} * for a full list of supported scopes. + * + * @public */ export const auth0AuthApiRef: ApiRef< OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi @@ -282,9 +338,13 @@ export const auth0AuthApiRef: ApiRef< /** * Provides authentication towards Microsoft APIs and identities. * + * @remarks + * * For more info and a full list of supported scopes, see: - * - https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent - * - https://docs.microsoft.com/en-us/graph/permissions-reference + * - {@link https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent} + * - {@link https://docs.microsoft.com/en-us/graph/permissions-reference} + * + * @public */ export const microsoftAuthApiRef: ApiRef< OAuthApi & @@ -298,6 +358,8 @@ export const microsoftAuthApiRef: ApiRef< /** * Provides authentication for custom identity providers. + * + * @public */ export const oauth2ApiRef: ApiRef< OAuthApi & @@ -311,6 +373,8 @@ export const oauth2ApiRef: ApiRef< /** * Provides authentication for custom OpenID Connect identity providers. + * + * @public */ export const oidcAuthApiRef: ApiRef< OAuthApi & @@ -323,7 +387,9 @@ export const oidcAuthApiRef: ApiRef< }); /** - * Provides authentication for saml based identity providers + * Provides authentication for SAML-based identity providers. + * + * @public */ export const samlAuthApiRef: ApiRef< ProfileInfoApi & BackstageIdentityApi & SessionApi @@ -331,6 +397,11 @@ export const samlAuthApiRef: ApiRef< id: 'core.auth.saml', }); +/** + * Provides authentication towards OneLogin APIs. + * + * @public + */ export const oneloginAuthApiRef: ApiRef< OAuthApi & OpenIdConnectApi & @@ -344,8 +415,12 @@ export const oneloginAuthApiRef: ApiRef< /** * Provides authentication towards Bitbucket APIs. * - * See https://support.atlassian.com/bitbucket-cloud/docs/use-oauth-on-bitbucket-cloud/ + * @remarks + * + * See {@link https://support.atlassian.com/bitbucket-cloud/docs/use-oauth-on-bitbucket-cloud/} * for a full list of supported scopes. + * + * @public */ export const bitbucketAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi @@ -356,8 +431,12 @@ export const bitbucketAuthApiRef: ApiRef< /** * Provides authentication towards Atlassian APIs. * - * See https://developer.atlassian.com/cloud/jira/platform/scopes-for-connect-and-oauth-2-3LO-apps/ + * @remarks + * + * See {@link https://developer.atlassian.com/cloud/jira/platform/scopes-for-connect-and-oauth-2-3LO-apps/} * for a full list of supported scopes. + * + * @public */ export const atlassianAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi diff --git a/packages/core-plugin-api/src/apis/system/ApiRef.ts b/packages/core-plugin-api/src/apis/system/ApiRef.ts index 37678ed079..7e13a1963f 100644 --- a/packages/core-plugin-api/src/apis/system/ApiRef.ts +++ b/packages/core-plugin-api/src/apis/system/ApiRef.ts @@ -16,6 +16,11 @@ import type { ApiRef } from './types'; +/** + * API reference configuration - holds an ID of the referenced API. + * + * @public + */ export type ApiRefConfig = { id: string; /** @@ -57,6 +62,13 @@ class ApiRefImpl implements ApiRef { } } +/** + * Creates a reference to an API. + * + * @param config - The descriptor of the API to reference. + * @returns An API reference. + * @public + */ export function createApiRef(config: ApiRefConfig): ApiRef { return new ApiRefImpl(config); } diff --git a/packages/core-plugin-api/src/apis/system/helpers.ts b/packages/core-plugin-api/src/apis/system/helpers.ts index 547d583482..eaffed3003 100644 --- a/packages/core-plugin-api/src/apis/system/helpers.ts +++ b/packages/core-plugin-api/src/apis/system/helpers.ts @@ -17,19 +17,44 @@ import { ApiRef, ApiFactory, TypesToApiRefs } from './types'; /** - * Used to infer types for a standalone ApiFactory that isn't immediately passed + * Used to infer types for a standalone {@link ApiFactory} that isn't immediately passed * to another function. + * + * @remarks + * * This function doesn't actually do anything, it's only used to infer types. + * + * @public */ export function createApiFactory< Api, Impl extends Api, Deps extends { [name in string]: unknown }, >(factory: ApiFactory): ApiFactory; +/** + * Used to infer types for a standalone {@link ApiFactory} that isn't immediately passed + * to another function. + * + * @param api - Ref of the API that will be produced by the factory. + * @param instance - Implementation of the API to use. + * @public + */ export function createApiFactory( api: ApiRef, instance: Impl, ): ApiFactory; +/** + * Used to infer types for a standalone {@link ApiFactory} that isn't immediately passed + * to another function. + * + * @remarks + * + * Creates factory from {@link ApiRef} or returns the factory itself if provided. + * + * @param factory - Existing factory or {@link ApiRef}. + * @param instance - The instance to be returned by the factory. + * @public + */ export function createApiFactory< Api, Impl extends Api, diff --git a/packages/core-plugin-api/src/apis/system/index.ts b/packages/core-plugin-api/src/apis/system/index.ts index ceada1ebd7..964750474e 100644 --- a/packages/core-plugin-api/src/apis/system/index.ts +++ b/packages/core-plugin-api/src/apis/system/index.ts @@ -16,5 +16,6 @@ export { useApi, useApiHolder, withApis } from './useApi'; export { createApiRef } from './ApiRef'; +export type { ApiRefConfig } from './ApiRef'; export * from './types'; export * from './helpers'; diff --git a/packages/core-plugin-api/src/apis/system/types.ts b/packages/core-plugin-api/src/apis/system/types.ts index a97d29fdaa..d7801ea5c1 100644 --- a/packages/core-plugin-api/src/apis/system/types.ts +++ b/packages/core-plugin-api/src/apis/system/types.ts @@ -14,25 +14,62 @@ * limitations under the License. */ +/** + * API reference. + * + * @public + */ export type ApiRef = { id: string; T: T; }; +/** + * Catch-all {@link ApiRef} type. + * + * @public + */ export type AnyApiRef = ApiRef; +/** + * Transforms ApiRef type into its inner API type. + * + * @public + */ export type ApiRefType = T extends ApiRef ? U : never; +/** + * Wraps a type with API properties into a type holding their respective {@link ApiRef}s. + * Reverse type transform of {@link ApiRefsToTypes}. + * + * @public + */ export type TypesToApiRefs = { [key in keyof T]: ApiRef }; +/** + * Unwraps type with {@link ApiRef} properties into a type holding their respective API types. + * Reverse type transform of {@link TypesToApiRefs}. + * + * @public + */ export type ApiRefsToTypes }> = { [key in keyof T]: ApiRefType; }; +/** + * Provides lookup of APIs through their {@link ApiRef}s. + * + * @public + */ export type ApiHolder = { get(api: ApiRef): T | undefined; }; +/** + * Describes type returning API implementations. + * + * @public + */ export type ApiFactory< Api, Impl extends Api, @@ -43,6 +80,11 @@ export type ApiFactory< factory(deps: Deps): Impl; }; +/** + * Catch-all {@link ApiFactory} type. + * + * @public + */ export type AnyApiFactory = ApiFactory< unknown, unknown, diff --git a/packages/core-plugin-api/src/apis/system/useApi.tsx b/packages/core-plugin-api/src/apis/system/useApi.tsx index ef598a4c0e..465a39cff4 100644 --- a/packages/core-plugin-api/src/apis/system/useApi.tsx +++ b/packages/core-plugin-api/src/apis/system/useApi.tsx @@ -18,6 +18,11 @@ import React, { PropsWithChildren } from 'react'; import { ApiRef, ApiHolder, TypesToApiRefs } from './types'; import { useVersionedContext } from '@backstage/version-bridge'; +/** + * React hook for retrieving {@link ApiHolder}, an API catalog. + * + * @public + */ export function useApiHolder(): ApiHolder { const versionedHolder = useVersionedContext<{ 1: ApiHolder }>('api-context'); if (!versionedHolder) { @@ -31,6 +36,12 @@ export function useApiHolder(): ApiHolder { return apiHolder; } +/** + * React hook for retrieving APIs. + * + * @param apiRef - Reference of the API to use. + * @public + */ export function useApi(apiRef: ApiRef): T { const apiHolder = useApiHolder(); @@ -41,6 +52,12 @@ export function useApi(apiRef: ApiRef): T { return api; } +/** + * Wrapper for giving component an API context. + * + * @param apis - APIs for the context. + * @public + */ export function withApis(apis: TypesToApiRefs) { return function withApisWrapper

( WrappedComponent: React.ComponentType

, diff --git a/packages/core-plugin-api/src/app/types.ts b/packages/core-plugin-api/src/app/types.ts index 8b07d699ee..d072f0a2e6 100644 --- a/packages/core-plugin-api/src/app/types.ts +++ b/packages/core-plugin-api/src/app/types.ts @@ -19,11 +19,21 @@ import { ProfileInfo } from '../apis/definitions'; import { IconComponent } from '../icons'; import { BackstagePlugin } from '../plugin/types'; +/** + * Props for the BootErrorPage. + * + * @public + */ export type BootErrorPageProps = { step: 'load-config' | 'load-chunk'; error: Error; }; +/** + * Data and handlers associated with the user sign in event. + * + * @public + */ export type SignInResult = { /** * User ID that will be returned by the IdentityApi @@ -43,6 +53,11 @@ export type SignInResult = { signOut?: () => Promise; }; +/** + * Props for the SignInPage. + * + * @public + */ export type SignInPageProps = { /** * Set the sign-in result for the app. This should only be called once. @@ -50,12 +65,22 @@ export type SignInPageProps = { onResult(result: SignInResult): void; }; +/** + * Props for the ErrorBoundaryFallback. + * + * @public + */ export type ErrorBoundaryFallbackProps = { plugin?: BackstagePlugin; error: Error; resetError: () => void; }; +/** + * Basic app components. + * + * @public + */ export type AppComponents = { NotFoundErrorPage: ComponentType<{}>; BootErrorPage: ComponentType; @@ -75,6 +100,11 @@ export type AppComponents = { SignInPage?: ComponentType; }; +/** + * Provides plugins and components registered in the app. + * + * @public + */ export type AppContext = { /** * Get a list of all plugins that are installed in the app. diff --git a/packages/core-plugin-api/src/app/useApp.tsx b/packages/core-plugin-api/src/app/useApp.tsx index 05d2e1c5c0..f2175a0969 100644 --- a/packages/core-plugin-api/src/app/useApp.tsx +++ b/packages/core-plugin-api/src/app/useApp.tsx @@ -17,6 +17,11 @@ import { useVersionedContext } from '@backstage/version-bridge'; import { AppContext as AppContextV1 } from './types'; +/** + * React hook providing {@link AppContext}. + * + * @public + */ export const useApp = (): AppContextV1 => { const versionedContext = useVersionedContext<{ 1: AppContextV1 }>('app-context'); diff --git a/packages/core-plugin-api/src/deprecatedTypes.ts b/packages/core-plugin-api/src/deprecatedTypes.ts index 4b24614e17..5596ff9f46 100644 --- a/packages/core-plugin-api/src/deprecatedTypes.ts +++ b/packages/core-plugin-api/src/deprecatedTypes.ts @@ -39,8 +39,9 @@ export type Subscription = CoreSubscription; /** * Observable sequence of values and errors, see TC39. * - * https://github.com/tc39/proposal-observable + * @remarks * + * {@link https://github.com/tc39/proposal-observable} * This is used as a common return type for observable values and can be created * using many different observable implementations, such as zen-observable or RxJS 5. * diff --git a/packages/core-plugin-api/src/extensions/componentData.tsx b/packages/core-plugin-api/src/extensions/componentData.tsx index 97a0b74e5b..1f7ef7f06a 100644 --- a/packages/core-plugin-api/src/extensions/componentData.tsx +++ b/packages/core-plugin-api/src/extensions/componentData.tsx @@ -41,6 +41,18 @@ type MaybeComponentNode = ReactNode & { type?: ComponentWithData; }; +/** + * Stores data related to a component in a global store. + * + * @remarks + * + * See {@link https://backstage.io/docs/plugins/composability#component-data}. + * + * @param component - The component to attach the data to. + * @param type - The key under which the data will be stored. + * @param data - Arbitrary value. + * @public + */ export function attachComponentData

( component: ComponentType

, type: string, @@ -70,6 +82,18 @@ export function attachComponentData

( container.map.set(type, data); } +/** + * Retrieves data attached to a component. + * + * @remarks + * + * See {@link https://backstage.io/docs/plugins/composability#component-data}. + * + * @param node - React component to look up. + * @param type - Key of the data to retrieve. + * @returns Data stored using {@link attachComponentData}. + * @public + */ export function getComponentData( node: ReactNode, type: string, diff --git a/packages/core-plugin-api/src/extensions/extensions.tsx b/packages/core-plugin-api/src/extensions/extensions.tsx index 2eb5275c66..f85570a35b 100644 --- a/packages/core-plugin-api/src/extensions/extensions.tsx +++ b/packages/core-plugin-api/src/extensions/extensions.tsx @@ -22,7 +22,12 @@ import { attachComponentData } from './componentData'; import { Extension, BackstagePlugin } from '../plugin/types'; import { PluginErrorBoundary } from './PluginErrorBoundary'; -type ComponentLoader = +/** + * Lazy or synchronous retrieving of extension components. + * + * @public + */ +export type ComponentLoader = | { lazy: () => Promise; } @@ -30,9 +35,19 @@ type ComponentLoader = sync: T; }; -// We do not use ComponentType as the return type, since it doesn't let us convey the children prop. -// ComponentType inserts children as an optional prop whether the inner component accepts it or not, -// making it impossible to make the usage of children type safe. +/** + * Extension for components that can have its own URL route (top-level pages, tabs etc.). + * + * @remarks + * + * We do not use ComponentType as the return type, since it doesn't let us convey the children prop. + * ComponentType inserts children as an optional prop whether the inner component accepts it or not, + * making it impossible to make the usage of children type safe. + * + * See {@link https://backstage.io/docs/plugins/composability#extensions}. + * + * @public + */ export function createRoutableExtension< T extends (props: any) => JSX.Element | null, >(options: { @@ -97,9 +112,19 @@ export function createRoutableExtension< }); } -// We do not use ComponentType as the return type, since it doesn't let us convey the children prop. -// ComponentType inserts children as an optional prop whether the inner component accepts it or not, -// making it impossible to make the usage of children type safe. +/** + * Plain React component extension. + * + * @remarks + * + * We do not use ComponentType as the return type, since it doesn't let us convey the children prop. + * ComponentType inserts children as an optional prop whether the inner component accepts it or not, + * making it impossible to make the usage of children type safe. + * + * See {@link https://backstage.io/docs/plugins/composability#extensions}. + * + * @public + */ export function createComponentExtension< T extends (props: any) => JSX.Element | null, >(options: { component: ComponentLoader; name?: string }): Extension { @@ -107,9 +132,19 @@ export function createComponentExtension< return createReactExtension({ component, name }); } -// We do not use ComponentType as the return type, since it doesn't let us convey the children prop. -// ComponentType inserts children as an optional prop whether the inner component accepts it or not, -// making it impossible to make the usage of children type safe. +/** + * Used by {@link createComponentExtension} and {@link createRoutableExtension}. + * + * @remarks + * + * We do not use ComponentType as the return type, since it doesn't let us convey the children prop. + * ComponentType inserts children as an optional prop whether the inner component accepts it or not, + * making it impossible to make the usage of children type safe. + * + * See {@link https://backstage.io/docs/plugins/composability#extensions}. + * + * @public + */ export function createReactExtension< T extends (props: any) => JSX.Element | null, >(options: { diff --git a/packages/core-plugin-api/src/extensions/index.ts b/packages/core-plugin-api/src/extensions/index.ts index 39db17311a..35803bdeee 100644 --- a/packages/core-plugin-api/src/extensions/index.ts +++ b/packages/core-plugin-api/src/extensions/index.ts @@ -20,5 +20,6 @@ export { createRoutableExtension, createComponentExtension, } from './extensions'; +export type { ComponentLoader } from './extensions'; export { useElementFilter } from './useElementFilter'; export type { ElementCollection } from './useElementFilter'; diff --git a/packages/core-plugin-api/src/extensions/useElementFilter.tsx b/packages/core-plugin-api/src/extensions/useElementFilter.tsx index 7efdd949b2..fa04e7c2c6 100644 --- a/packages/core-plugin-api/src/extensions/useElementFilter.tsx +++ b/packages/core-plugin-api/src/extensions/useElementFilter.tsx @@ -82,17 +82,23 @@ function selectChildren( * A querying interface tailored to traversing a set of selected React elements * and extracting data. * + * @remarks + * * Methods prefixed with `selectBy` are used to narrow the set of selected elements. * * Methods prefixed with `find` return concrete data using a deep traversal of the set. * * Methods prefixed with `get` return concrete data using a shallow traversal of the set. + * + * @public */ export interface ElementCollection { /** * Narrows the set of selected components by doing a deep traversal and * only including those that have defined component data for the given `key`. * + * @remarks + * * Whether an element in the tree has component data set for the given key * is determined by whether `getComponentData` returns undefined. * @@ -104,6 +110,8 @@ export interface ElementCollection { * If `withStrictError` is set, the resulting selection must be a full match, meaning * there may be no elements that were excluded in the selection. If the selection * is not a clean match, an error will be throw with `withStrictError` as the message. + * + * @param query - Filtering query. */ selectByComponentData(query: { key: string; @@ -113,6 +121,8 @@ export interface ElementCollection { /** * Finds all elements using the same criteria as `selectByComponentData`, but * returns the actual component data of each of those elements instead. + * + * @param query - Lookup query. */ findComponentData(query: { key: string }): T[]; @@ -163,7 +173,11 @@ class Collection implements ElementCollection { /** * useElementFilter is a utility that helps you narrow down and retrieve data * from a React element tree, typically operating on the `children` property - * passed in to a component. A common use-case is to construct declarative APIs + * passed in to a component. + * + * @remarks + * + * A common use-case is to construct declarative APIs * where a React component defines its behavior based on its children, such as * the relationship between `Routes` and `Route` in `react-router`. * @@ -175,6 +189,8 @@ class Collection implements ElementCollection { * with added memoization based on the input `node`. If further memoization * dependencies are used in the filter function, they should be added to the * third `dependencies` argument, just like `useMemo`, `useEffect`, etc. + * + * @public */ export function useElementFilter( node: ReactNode, diff --git a/packages/core-plugin-api/src/icons/index.ts b/packages/core-plugin-api/src/icons/index.ts index 9c9e45e54c..de79dd08d3 100644 --- a/packages/core-plugin-api/src/icons/index.ts +++ b/packages/core-plugin-api/src/icons/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export type { IconComponent } from './types'; +export type { IconComponent, OldIconComponent } from './types'; diff --git a/packages/core-plugin-api/src/icons/types.ts b/packages/core-plugin-api/src/icons/types.ts index ccc1c337e7..5648261f3c 100644 --- a/packages/core-plugin-api/src/icons/types.ts +++ b/packages/core-plugin-api/src/icons/types.ts @@ -21,6 +21,8 @@ import { SvgIconProps } from '@material-ui/core'; * IconComponent is the common icon type used throughout Backstage when * working with and rendering generic icons, including the app system icons. * + * @remarks + * * The type is based on SvgIcon from MUI, but both do not what the plugin-api * package to have a dependency on MUI, nor do we want the props to be as broad * as the SvgIconProps interface. @@ -28,6 +30,8 @@ import { SvgIconProps } from '@material-ui/core'; * If you have the need to forward additional props from SvgIconProps, you can * open an issue or submit a PR to the main Backstage repo. When doing so please * also describe your use-case and reasoning of the addition. + * + * @public */ export type IconComponent = ComponentType<{ fontSize?: 'default' | 'small' | 'large'; @@ -37,5 +41,7 @@ export type IconComponent = ComponentType<{ * This exists for backwards compatibility with the old core package. * It's used in some parts of this package in order to smooth out the * migration, but it is not exported. + * + * @public */ export type OldIconComponent = ComponentType; diff --git a/packages/core-plugin-api/src/plugin/Plugin.tsx b/packages/core-plugin-api/src/plugin/Plugin.tsx index 53a53b76d0..52ca0e76e3 100644 --- a/packages/core-plugin-api/src/plugin/Plugin.tsx +++ b/packages/core-plugin-api/src/plugin/Plugin.tsx @@ -24,6 +24,9 @@ import { } from './types'; import { AnyApiFactory } from '../apis'; +/** + * @internal + */ export class PluginImpl< Routes extends AnyRoutes, ExternalRoutes extends AnyExternalRoutes, @@ -80,6 +83,12 @@ export class PluginImpl< } } +/** + * Creates Backstage Plugin from config. + * + * @param config - Plugin configuration. + * @public + */ export function createPlugin< Routes extends AnyRoutes = {}, ExternalRoutes extends AnyExternalRoutes = {}, diff --git a/packages/core-plugin-api/src/plugin/index.ts b/packages/core-plugin-api/src/plugin/index.ts index 321ae6998a..0cb9a2aede 100644 --- a/packages/core-plugin-api/src/plugin/index.ts +++ b/packages/core-plugin-api/src/plugin/index.ts @@ -16,6 +16,8 @@ export { createPlugin } from './Plugin'; export type { + AnyExternalRoutes, + AnyRoutes, BackstagePlugin, Extension, FeatureFlagOutput, diff --git a/packages/core-plugin-api/src/plugin/types.ts b/packages/core-plugin-api/src/plugin/types.ts index b3e1043efd..192e771092 100644 --- a/packages/core-plugin-api/src/plugin/types.ts +++ b/packages/core-plugin-api/src/plugin/types.ts @@ -17,29 +17,72 @@ import { RouteRef, SubRouteRef, ExternalRouteRef } from '../routing'; import { AnyApiFactory } from '../apis/system'; +/** + * Route configuration. + * + * @public + */ export type RouteOptions = { // Whether the route path must match exactly, defaults to true. exact?: boolean; }; +/** + * Type alias for paths. + * + * @public + */ export type RoutePath = string; -// Replace with using RouteRefs +/** + * Replace with using {@link RouteRef}s. + * + * @public + */ export type FeatureFlagOutput = { type: 'feature-flag'; name: string; }; +/** + * {@link FeatureFlagOutput} type. + * + * @public + */ export type PluginOutput = FeatureFlagOutput; +/** + * Plugin extension type. + * + * @remarks + * + * See {@link https://backstage.io/docs/plugins/composability#extensions}. + * + * @public + */ export type Extension = { expose(plugin: BackstagePlugin): T; }; +/** + * Catch-all route type. + * + * @public + */ export type AnyRoutes = { [name: string]: RouteRef | SubRouteRef }; +/** + * Catch-all type for {@link ExternalRouteRef}s. + * + * @public + */ export type AnyExternalRoutes = { [name: string]: ExternalRouteRef }; +/** + * Plugin type. + * + * @public + */ export type BackstagePlugin< Routes extends AnyRoutes = {}, ExternalRoutes extends AnyExternalRoutes = {}, @@ -52,6 +95,11 @@ export type BackstagePlugin< externalRoutes: ExternalRoutes; }; +/** + * Plugin descriptor type. + * + * @public + */ export type PluginConfig< Routes extends AnyRoutes, ExternalRoutes extends AnyExternalRoutes, @@ -63,10 +111,20 @@ export type PluginConfig< externalRoutes?: ExternalRoutes; }; +/** + * Holds hooks registered by the plugin. + * + * @public + */ export type PluginHooks = { featureFlags: FeatureFlagsHooks; }; +/** + * Interface for registering feature flags hooks. + * + * @public + */ export type FeatureFlagsHooks = { register(name: string): void; }; diff --git a/packages/core-plugin-api/src/routing/ExternalRouteRef.ts b/packages/core-plugin-api/src/routing/ExternalRouteRef.ts index c1c17c86df..2ebb81dbfb 100644 --- a/packages/core-plugin-api/src/routing/ExternalRouteRef.ts +++ b/packages/core-plugin-api/src/routing/ExternalRouteRef.ts @@ -22,6 +22,9 @@ import { OptionalParams, } from './types'; +/** + * @internal + */ export class ExternalRouteRefImpl< Params extends AnyParams, Optional extends boolean, @@ -42,6 +45,16 @@ export class ExternalRouteRefImpl< } } +/** + * Creates a route descriptor, to be later bound to a concrete route by the app. Used to implement cross-plugin route references. + * + * @remarks + * + * See {@link https://backstage.io/docs/plugins/composability#routing-system}. + * + * @param options - Description of the route reference to be created. + * @public + */ export function createExternalRouteRef< Params extends { [param in ParamKey]: string }, Optional extends boolean = false, diff --git a/packages/core-plugin-api/src/routing/RouteRef.ts b/packages/core-plugin-api/src/routing/RouteRef.ts index 78211e29c6..a926b775e6 100644 --- a/packages/core-plugin-api/src/routing/RouteRef.ts +++ b/packages/core-plugin-api/src/routing/RouteRef.ts @@ -24,6 +24,10 @@ import { import { OldIconComponent } from '../icons/types'; // TODO(Rugvip): Remove this in the next breaking release, it's exported but unused +/** + * @deprecated + * @internal + */ export type RouteRefConfig = { params?: ParamKeys; path?: string; @@ -31,6 +35,9 @@ export type RouteRefConfig = { title: string; }; +/** + * @internal + */ export class RouteRefImpl implements RouteRef { @@ -66,6 +73,12 @@ export class RouteRefImpl } } +/** + * Create a {@link RouteRef} from a route descriptor. + * + * @param config - Description of the route reference to be created. + * @public + */ export function createRouteRef< // Params is the type that we care about and the one to be embedded in the route ref. // For example, given the params ['name', 'kind'], Params will be {name: string, kind: string} diff --git a/packages/core-plugin-api/src/routing/SubRouteRef.ts b/packages/core-plugin-api/src/routing/SubRouteRef.ts index be30d56096..61d89fcbdb 100644 --- a/packages/core-plugin-api/src/routing/SubRouteRef.ts +++ b/packages/core-plugin-api/src/routing/SubRouteRef.ts @@ -26,6 +26,9 @@ import { // Should match the pattern in react-router const PARAM_PATTERN = /^\w+$/; +/** + * @internal + */ export class SubRouteRefImpl implements SubRouteRef { @@ -45,18 +48,34 @@ export class SubRouteRefImpl } } -// These utility types help us infer a Param object type from a string path -// For example, `/foo/:bar/:baz` inferred to `{ bar: string, baz: string }` -type ParamPart = S extends `:${infer Param}` ? Param : never; -type ParamNames = S extends `${infer Part}/${infer Rest}` - ? ParamPart | ParamNames - : ParamPart; -type PathParams = { [name in ParamNames]: string }; +/** + * Used in {@link PathParams} type declaration. + * @public + */ +export type ParamPart = S extends `:${infer Param}` + ? Param + : never; /** - * Merges a param object type with with an optional params type into a params object + * Used in {@link PathParams} type declaration. + * @public */ -type MergeParams< +export type ParamNames = + S extends `${infer Part}/${infer Rest}` + ? ParamPart | ParamNames + : ParamPart; +/** + * This utility type helps us infer a Param object type from a string path + * For example, `/foo/:bar/:baz` inferred to `{ bar: string, baz: string }` + * @public + */ +export type PathParams = { [name in ParamNames]: string }; + +/** + * Merges a param object type with with an optional params type into a params object. + * @public + */ +export type MergeParams< P1 extends { [param in string]: string }, P2 extends AnyParams, > = (P1[keyof P1] extends never ? {} : P1) & (P2 extends undefined ? {} : P2); @@ -64,14 +83,22 @@ type MergeParams< /** * Creates a SubRouteRef type given the desired parameters and parent route parameters. * The parameters types are merged together while ensuring that there is no overlap between the two. + * + * @public */ -type MakeSubRouteRef< +export type MakeSubRouteRef< Params extends { [param in string]: string }, ParentParams extends AnyParams, > = keyof Params & keyof ParentParams extends never ? SubRouteRef>> : never; +/** + * Create a {@link SubRouteRef} from a route descriptor. + * + * @param config - Description of the route reference to be created. + * @public + */ export function createSubRouteRef< Path extends string, ParentParams extends AnyParams = never, diff --git a/packages/core-plugin-api/src/routing/index.ts b/packages/core-plugin-api/src/routing/index.ts index 79158d2cdc..01d69cd4b0 100644 --- a/packages/core-plugin-api/src/routing/index.ts +++ b/packages/core-plugin-api/src/routing/index.ts @@ -14,9 +14,24 @@ * limitations under the License. */ -export type { RouteRef, SubRouteRef, ExternalRouteRef } from './types'; +export type { + AnyParams, + RouteRef, + SubRouteRef, + ExternalRouteRef, + OptionalParams, + ParamKeys, + RouteFunc, +} from './types'; export { createRouteRef } from './RouteRef'; export { createSubRouteRef } from './SubRouteRef'; +export type { + MakeSubRouteRef, + MergeParams, + ParamNames, + ParamPart, + PathParams, +} from './SubRouteRef'; export { createExternalRouteRef } from './ExternalRouteRef'; export { useRouteRef } from './useRouteRef'; export { useRouteRefParams } from './useRouteRefParams'; diff --git a/packages/core-plugin-api/src/routing/types.ts b/packages/core-plugin-api/src/routing/types.ts index 7e20d1ff67..bd419abb76 100644 --- a/packages/core-plugin-api/src/routing/types.ts +++ b/packages/core-plugin-api/src/routing/types.ts @@ -17,32 +17,70 @@ import { OldIconComponent } from '../icons/types'; import { getOrCreateGlobalSingleton } from '@backstage/version-bridge'; +/** + * Catch-all type for route params. + * + * @public + */ export type AnyParams = { [param in string]: string } | undefined; + +/** + * Type describing the key type of a route parameter mapping. + * + * @public + */ export type ParamKeys = keyof Params extends never ? [] : (keyof Params)[]; + +/** + * Optional route params. + * + * @public + */ export type OptionalParams = Params[keyof Params] extends never ? undefined : Params; -// The extra TS magic here is to require a single params argument if the RouteRef -// had at least one param defined, but require 0 arguments if there are no params defined. -// Without this we'd have to pass in empty object to all parameter-less RouteRefs -// just to make TypeScript happy, or we would have to make the argument optional in -// which case you might forget to pass it in when it is actually required. +/** + * TS magic for handling route parameters. + * + * @remarks + * + * The extra TS magic here is to require a single params argument if the RouteRef + * had at least one param defined, but require 0 arguments if there are no params defined. + * Without this we'd have to pass in empty object to all parameter-less RouteRefs + * just to make TypeScript happy, or we would have to make the argument optional in + * which case you might forget to pass it in when it is actually required. + * + * @public + */ export type RouteFunc = ( ...[params]: Params extends undefined ? readonly [] : readonly [Params] ) => string; -// This symbol is what we use at runtime to determine whether a given object -// is a type of RouteRef or not. It doesn't work well in TypeScript though since -// the `unique symbol` will refer to different values between package versions. -// For that reason we use the marker $$routeRefType to represent the symbol at -// compile-time instead of using the symbol directly. +/** + * This symbol is what we use at runtime to determine whether a given object + * is a type of RouteRef or not. It doesn't work well in TypeScript though since + * the `unique symbol` will refer to different values between package versions. + * For that reason we use the marker $$routeRefType to represent the symbol at + * compile-time instead of using the symbol directly. + * + * @internal + */ export const routeRefType: unique symbol = getOrCreateGlobalSingleton( 'route-ref-type', () => Symbol('route-ref-type'), ); +/** + * Absolute route reference. + * + * @remarks + * + * See {@link https://backstage.io/docs/plugins/composability#routing-system}. + * + * @public + */ export type RouteRef = { $$routeRefType: 'absolute'; // See routeRefType above @@ -57,6 +95,15 @@ export type RouteRef = { title?: string; }; +/** + * Descriptor of a route relative to an absolute {@link RouteRef}. + * + * @remarks + * + * See {@link https://backstage.io/docs/plugins/composability#routing-system}. + * + * @public + */ export type SubRouteRef = { $$routeRefType: 'sub'; // See routeRefType above @@ -67,6 +114,15 @@ export type SubRouteRef = { params: ParamKeys; }; +/** + * Route descriptor, to be later bound to a concrete route by the app. Used to implement cross-plugin route references. + * + * @remarks + * + * See {@link https://backstage.io/docs/plugins/composability#routing-system}. + * + * @public + */ export type ExternalRouteRef< Params extends AnyParams = any, Optional extends boolean = any, @@ -78,20 +134,35 @@ export type ExternalRouteRef< optional?: Optional; }; +/** + * @internal + */ export type AnyRouteRef = | RouteRef | SubRouteRef | ExternalRouteRef; // TODO(Rugvip): None of these should be found in the wild anymore, remove in next minor release -/** @deprecated */ +/** + * @deprecated + * @internal + */ export type ConcreteRoute = {}; -/** @deprecated */ +/** + * @deprecated + * @internal + */ export type AbsoluteRouteRef = RouteRef<{}>; -/** @deprecated */ +/** + * @deprecated + * @internal + */ export type MutableRouteRef = RouteRef<{}>; -// A duplicate of the react-router RouteObject, but with routeRef added +/** + * A duplicate of the react-router RouteObject, but with routeRef added + * @internal + */ export interface BackstageRouteObject { caseSensitive: boolean; children?: BackstageRouteObject[]; diff --git a/packages/core-plugin-api/src/routing/useRouteRef.tsx b/packages/core-plugin-api/src/routing/useRouteRef.tsx index ea05cc14fd..50cfb3bdfb 100644 --- a/packages/core-plugin-api/src/routing/useRouteRef.tsx +++ b/packages/core-plugin-api/src/routing/useRouteRef.tsx @@ -25,6 +25,9 @@ import { SubRouteRef, } from './types'; +/** + * @internal + */ export interface RouteResolver { resolve( anyRouteRef: @@ -35,12 +38,47 @@ export interface RouteResolver { ): RouteFunc | undefined; } +/** + * React hook for constructing URLs to routes. + * + * @remarks + * + * See {@link https://backstage.io/docs/plugins/composability#routing-system} + * + * @param routeRef - The ref to route that should be converted to URL. + * @returns A function that will in turn return the concrete URL of the `routeRef`. + * @public + */ export function useRouteRef( routeRef: ExternalRouteRef, ): Optional extends true ? RouteFunc | undefined : RouteFunc; + +/** + * React hook for constructing URLs to routes. + * + * @remarks + * + * See {@link https://backstage.io/docs/plugins/composability#routing-system} + * + * @param routeRef - The ref to route that should be converted to URL. + * @returns A function that will in turn return the concrete URL of the `routeRef`. + * @public + */ export function useRouteRef( routeRef: RouteRef | SubRouteRef, ): RouteFunc; + +/** + * React hook for constructing URLs to routes. + * + * @remarks + * + * See {@link https://backstage.io/docs/plugins/composability#routing-system} + * + * @param routeRef - The ref to route that should be converted to URL. + * @returns A function that will in turn return the concrete URL of the `routeRef`. + * @public + */ export function useRouteRef( routeRef: | RouteRef diff --git a/packages/core-plugin-api/src/routing/useRouteRefParams.ts b/packages/core-plugin-api/src/routing/useRouteRefParams.ts index a61df97cf5..e24576372b 100644 --- a/packages/core-plugin-api/src/routing/useRouteRefParams.ts +++ b/packages/core-plugin-api/src/routing/useRouteRefParams.ts @@ -17,6 +17,11 @@ import { useParams } from 'react-router-dom'; import { RouteRef, AnyParams, SubRouteRef } from './types'; +/** + * React hook for retrieving dynamic params from the current URL. + * @param _routeRef - Ref of the current route. + * @public + */ export function useRouteRefParams( _routeRef: RouteRef | SubRouteRef, ): Params { From 290fbb3ec2c6fbd4a1425e6eb906a35e4d879999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jarek=20=C5=81ukow?= Date: Mon, 1 Nov 2021 11:17:00 +0100 Subject: [PATCH 047/196] Improve API docs in Scaffolder action plugins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jarek Łukow --- .changeset/wicked-boats-lie.md | 7 +++++++ .../api-report.md | 4 +--- .../src/actions/fetch/cookiecutter.ts | 9 +++++++++ .../src/index.ts | 2 +- plugins/scaffolder-backend-module-rails/api-report.md | 4 +--- .../src/actions/fetch/rails/index.ts | 10 ++++++++++ plugins/scaffolder-backend-module-rails/src/index.ts | 2 +- plugins/scaffolder-backend-module-yeoman/api-report.md | 6 +----- .../src/actions/run/yeoman.ts | 9 +++++++++ plugins/scaffolder-backend-module-yeoman/src/index.ts | 7 +++++++ 10 files changed, 47 insertions(+), 13 deletions(-) create mode 100644 .changeset/wicked-boats-lie.md diff --git a/.changeset/wicked-boats-lie.md b/.changeset/wicked-boats-lie.md new file mode 100644 index 0000000000..8c5da92d9c --- /dev/null +++ b/.changeset/wicked-boats-lie.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-scaffolder-backend-module-cookiecutter': patch +'@backstage/plugin-scaffolder-backend-module-rails': patch +'@backstage/plugin-scaffolder-backend-module-yeoman': patch +--- + +Add missing API docs to scaffolder action plugins diff --git a/plugins/scaffolder-backend-module-cookiecutter/api-report.md b/plugins/scaffolder-backend-module-cookiecutter/api-report.md index 6fa57fd0f3..132d3d4225 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/api-report.md +++ b/plugins/scaffolder-backend-module-cookiecutter/api-report.md @@ -10,9 +10,7 @@ import { ScmIntegrations } from '@backstage/integration'; import { TemplateAction } from '@backstage/plugin-scaffolder-backend'; import { UrlReader } from '@backstage/backend-common'; -// Warning: (ae-missing-release-tag) "createFetchCookiecutterAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function createFetchCookiecutterAction(options: { reader: UrlReader; integrations: ScmIntegrations; diff --git a/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts b/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts index 5120cd3b27..c4d7f1e3e8 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts +++ b/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts @@ -121,6 +121,15 @@ export class CookiecutterRunner { } } +/** + * Creates a `fetch:cookiecutter` Scaffolder action. + * + * @remarks + * + * See {@link https://cookiecutter.readthedocs.io/} and {@link https://backstage.io/docs/features/software-templates/writing-custom-actions}. + * @param options - Templating configuration. + * @public + */ export function createFetchCookiecutterAction(options: { reader: UrlReader; integrations: ScmIntegrations; diff --git a/plugins/scaffolder-backend-module-cookiecutter/src/index.ts b/plugins/scaffolder-backend-module-cookiecutter/src/index.ts index cce8011059..4f0ba9a407 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/src/index.ts +++ b/plugins/scaffolder-backend-module-cookiecutter/src/index.ts @@ -15,7 +15,7 @@ */ /** - * A module for the scaffolder backend that lets you template projects using cookiecutter + * A module for the scaffolder backend that lets you template projects using {@link https://cookiecutter.readthedocs.io/ | cookiecutter}. * * @packageDocumentation */ diff --git a/plugins/scaffolder-backend-module-rails/api-report.md b/plugins/scaffolder-backend-module-rails/api-report.md index 477483f70e..9c0610d4f2 100644 --- a/plugins/scaffolder-backend-module-rails/api-report.md +++ b/plugins/scaffolder-backend-module-rails/api-report.md @@ -8,9 +8,7 @@ import { ScmIntegrations } from '@backstage/integration'; import { TemplateAction } from '@backstage/plugin-scaffolder-backend'; import { UrlReader } from '@backstage/backend-common'; -// Warning: (ae-missing-release-tag) "createFetchRailsAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function createFetchRailsAction(options: { reader: UrlReader; integrations: ScmIntegrations; diff --git a/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/index.ts b/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/index.ts index 5ce7d46689..00a5690c0a 100644 --- a/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/index.ts +++ b/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/index.ts @@ -27,6 +27,16 @@ import { import { resolve as resolvePath } from 'path'; import { RailsNewRunner } from './railsNewRunner'; +/** + * Creates the `fetch:rails` Scaffolder action. + * + * @remarks + * + * See {@link https://guides.rubyonrails.org/rails_application_templates.html} and {@link https://backstage.io/docs/features/software-templates/writing-custom-actions}. + * + * @param options - Configuration of the templater. + * @public + */ export function createFetchRailsAction(options: { reader: UrlReader; integrations: ScmIntegrations; diff --git a/plugins/scaffolder-backend-module-rails/src/index.ts b/plugins/scaffolder-backend-module-rails/src/index.ts index eb51c06c49..773e1feabd 100644 --- a/plugins/scaffolder-backend-module-rails/src/index.ts +++ b/plugins/scaffolder-backend-module-rails/src/index.ts @@ -15,7 +15,7 @@ */ /** - * A module for the scaffolder backend that lets you template projects using Rails + * A module for the scaffolder backend that lets you template projects using {@link https://guides.rubyonrails.org/rails_application_templates.html | Rails}. * * @packageDocumentation */ diff --git a/plugins/scaffolder-backend-module-yeoman/api-report.md b/plugins/scaffolder-backend-module-yeoman/api-report.md index 1f38d3945c..fc18814faa 100644 --- a/plugins/scaffolder-backend-module-yeoman/api-report.md +++ b/plugins/scaffolder-backend-module-yeoman/api-report.md @@ -5,10 +5,6 @@ ```ts import { TemplateAction } from '@backstage/plugin-scaffolder-backend'; -// Warning: (ae-missing-release-tag) "createRunYeomanAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function createRunYeomanAction(): TemplateAction; - -// (No @packageDocumentation comment for this package) ``` diff --git a/plugins/scaffolder-backend-module-yeoman/src/actions/run/yeoman.ts b/plugins/scaffolder-backend-module-yeoman/src/actions/run/yeoman.ts index f6a29037a3..f7b0c54b41 100644 --- a/plugins/scaffolder-backend-module-yeoman/src/actions/run/yeoman.ts +++ b/plugins/scaffolder-backend-module-yeoman/src/actions/run/yeoman.ts @@ -18,6 +18,15 @@ import { JsonObject } from '@backstage/types'; import { createTemplateAction } from '@backstage/plugin-scaffolder-backend'; import { yeomanRun } from './yeomanRun'; +/** + * Creates a `run:yeoman` Scaffolder action. + * + * @remarks + * + * See {@link https://yeoman.io/} and {@link https://backstage.io/docs/features/software-templates/writing-custom-actions}. + * + * @public + */ export function createRunYeomanAction() { return createTemplateAction<{ namespace: string; diff --git a/plugins/scaffolder-backend-module-yeoman/src/index.ts b/plugins/scaffolder-backend-module-yeoman/src/index.ts index 4f06b14a86..1aac8e12f9 100644 --- a/plugins/scaffolder-backend-module-yeoman/src/index.ts +++ b/plugins/scaffolder-backend-module-yeoman/src/index.ts @@ -13,4 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/** + * A module for the scaffolder backend that lets you template projects using + * {@link https://yeoman.io/ | Yeoman}. + * + * @packageDocumentation + */ export * from './actions'; From 044c38e73982d0793686b203d9dd356397c9a544 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Mon, 1 Nov 2021 13:10:54 +0100 Subject: [PATCH 048/196] Lazy load all API definition widgets Signed-off-by: Oliver Sand --- .changeset/swift-turtles-provide.md | 7 + plugins/api-docs/api-report.md | 18 ++- ...t.test.tsx => AsyncApiDefinition.test.tsx} | 8 +- .../AsyncApiDefinition.tsx | 145 ++++++++++++++++++ .../AsyncApiDefinitionWidget.tsx | 137 ++--------------- ...et.test.tsx => GraphQlDefinition.test.tsx} | 6 +- .../GraphQlDefinition.tsx | 63 ++++++++ .../GraphQlDefinitionWidget.tsx | 55 ++----- ...et.test.tsx => OpenApiDefinition.test.tsx} | 8 +- .../OpenApiDefinition.tsx | 92 +++++++++++ .../OpenApiDefinitionWidget.tsx | 86 ++--------- 11 files changed, 375 insertions(+), 250 deletions(-) create mode 100644 .changeset/swift-turtles-provide.md rename plugins/api-docs/src/components/AsyncApiDefinitionWidget/{AsyncApiDefinitionWidget.test.tsx => AsyncApiDefinition.test.tsx} (87%) create mode 100644 plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.tsx rename plugins/api-docs/src/components/GraphQlDefinitionWidget/{GraphQlDefinitionWidget.test.tsx => GraphQlDefinition.test.tsx} (89%) create mode 100644 plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.tsx rename plugins/api-docs/src/components/OpenApiDefinitionWidget/{OpenApiDefinitionWidget.test.tsx => OpenApiDefinition.test.tsx} (87%) create mode 100644 plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.tsx diff --git a/.changeset/swift-turtles-provide.md b/.changeset/swift-turtles-provide.md new file mode 100644 index 0000000000..52cf9a7c8f --- /dev/null +++ b/.changeset/swift-turtles-provide.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-api-docs': patch +--- + +Lazy load all API definition widgets. The widgets use libraries like +`swagger-ui`, `graphiql`, and `@asyncapi/react-component` which are quite heavy +weight. To improve initial load times, the widgets are only loaded once used. diff --git a/plugins/api-docs/api-report.md b/plugins/api-docs/api-report.md index de36857945..8ec6175a6d 100644 --- a/plugins/api-docs/api-report.md +++ b/plugins/api-docs/api-report.md @@ -84,11 +84,13 @@ export const ApiTypeTitle: ({ apiEntity: ApiEntity; }) => JSX.Element; -// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "AsyncApiDefinitionWidgetProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "AsyncApiDefinitionWidget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const AsyncApiDefinitionWidget: ({ definition }: Props_5) => JSX.Element; +export const AsyncApiDefinitionWidget: ({ + definition, +}: AsyncApiDefinitionWidgetProps) => JSX.Element; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ConsumedApisCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -100,7 +102,7 @@ export const ConsumedApisCard: ({ variant }: Props_2) => JSX.Element; // Warning: (ae-missing-release-tag) "ConsumingComponentsCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const ConsumingComponentsCard: ({ variant }: Props_6) => JSX.Element; +export const ConsumingComponentsCard: ({ variant }: Props_5) => JSX.Element; // Warning: (ae-missing-release-tag) "defaultDefinitionWidgets" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -169,11 +171,13 @@ export const EntityProvidingComponentsCard: ({ // @public (undocumented) export const HasApisCard: ({ variant }: Props_3) => JSX.Element; -// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "OpenApiDefinitionWidgetProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "OpenApiDefinitionWidget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const OpenApiDefinitionWidget: ({ definition }: Props_8) => JSX.Element; +export const OpenApiDefinitionWidget: ({ + definition, +}: OpenApiDefinitionWidgetProps) => JSX.Element; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "PlainApiDefinitionWidget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -182,7 +186,7 @@ export const OpenApiDefinitionWidget: ({ definition }: Props_8) => JSX.Element; export const PlainApiDefinitionWidget: ({ definition, language, -}: Props_9) => JSX.Element; +}: Props_7) => JSX.Element; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ProvidedApisCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -194,5 +198,5 @@ export const ProvidedApisCard: ({ variant }: Props_4) => JSX.Element; // Warning: (ae-missing-release-tag) "ProvidingComponentsCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const ProvidingComponentsCard: ({ variant }: Props_7) => JSX.Element; +export const ProvidingComponentsCard: ({ variant }: Props_6) => JSX.Element; ``` diff --git a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.test.tsx b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.test.tsx similarity index 87% rename from plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.test.tsx rename to plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.test.tsx index 0170ada180..cb89476c03 100644 --- a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.test.tsx +++ b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.test.tsx @@ -16,9 +16,9 @@ import { renderInTestApp } from '@backstage/test-utils'; import React from 'react'; -import { AsyncApiDefinitionWidget } from './AsyncApiDefinitionWidget'; +import { AsyncApiDefinition } from './AsyncApiDefinition'; -describe('', () => { +describe('', () => { it('renders asyncapi spec', async () => { const definition = ` asyncapi: 2.0.0 @@ -40,7 +40,7 @@ components: type: string `; const { getByText, getAllByText } = await renderInTestApp( - , + , ); expect(getByText(/Account Service/i)).toBeInTheDocument(); @@ -51,7 +51,7 @@ components: it('renders error if definition is missing', async () => { const { getByText } = await renderInTestApp( - , + , ); expect(getByText(/Error/i)).toBeInTheDocument(); expect(getByText(/Document can't be null or falsey/i)).toBeInTheDocument(); diff --git a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.tsx b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.tsx new file mode 100644 index 0000000000..1c993f67d7 --- /dev/null +++ b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.tsx @@ -0,0 +1,145 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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 AsyncApi from '@asyncapi/react-component'; +import '@asyncapi/react-component/lib/styles/fiori.css'; +import { alpha, makeStyles } from '@material-ui/core/styles'; +import React from 'react'; + +const useStyles = makeStyles(theme => ({ + root: { + '& .asyncapi': { + 'font-family': 'inherit', + background: 'none', + }, + '& h2': { + ...theme.typography.h6, + }, + '& .text-teal': { + color: theme.palette.primary.main, + }, + '& button': { + ...theme.typography.button, + background: 'none', + boxSizing: 'border-box', + minWidth: 64, + borderRadius: theme.shape.borderRadius, + transition: theme.transitions.create( + ['background-color', 'box-shadow', 'border'], + { + duration: theme.transitions.duration.short, + }, + ), + padding: '5px 15px', + color: theme.palette.primary.main, + border: `1px solid ${alpha(theme.palette.primary.main, 0.5)}`, + '&:hover': { + textDecoration: 'none', + '&.Mui-disabled': { + backgroundColor: 'transparent', + }, + border: `1px solid ${theme.palette.primary.main}`, + backgroundColor: alpha( + theme.palette.primary.main, + theme.palette.action.hoverOpacity, + ), + // Reset on touch devices, it doesn't add specificity + '@media (hover: none)': { + backgroundColor: 'transparent', + }, + }, + '&.Mui-disabled': { + color: theme.palette.action.disabled, + }, + }, + '& .asyncapi__collapse-button:hover': { + color: theme.palette.primary.main, + }, + '& button.asyncapi__toggle-button': { + 'min-width': 'inherit', + }, + '& .asyncapi__info-list li': { + 'border-color': theme.palette.primary.main, + '&:hover': { + color: theme.palette.text.primary, + 'border-color': theme.palette.primary.main, + 'background-color': theme.palette.primary.main, + }, + }, + '& .asyncapi__info-list li a': { + color: theme.palette.primary.main, + '&:hover': { + color: theme.palette.getContrastText(theme.palette.primary.main), + }, + }, + '& .asyncapi__enum': { + color: theme.palette.secondary.main, + }, + '& .asyncapi__info, .asyncapi__channel, .asyncapi__channels > div, .asyncapi__schema, .asyncapi__channel-operations-list .asyncapi__messages-list-item .asyncapi__message, .asyncapi__message, .asyncapi__server, .asyncapi__servers > div, .asyncapi__messages > div, .asyncapi__schemas > div': + { + 'background-color': 'inherit', + }, + '& .asyncapi__channel-parameters-header, .asyncapi__channel-operations-header, .asyncapi__channel-operation-oneOf-subscribe-header, .asyncapi__channel-operation-oneOf-publish-header, .asyncapi__channel-operation-message-header, .asyncapi__message-header, .asyncapi__message-header-title, .asyncapi__message-header-title > h3, .asyncapi__bindings, .asyncapi__bindings-header, .asyncapi__bindings-header > h4': + { + 'background-color': 'inherit', + color: theme.palette.text.primary, + }, + '& .asyncapi__additional-properties-notice': { + color: theme.palette.text.hint, + }, + '& .asyncapi__code, .asyncapi__code-pre': { + background: theme.palette.background.default, + }, + '& .asyncapi__schema-example-header-title': { + color: theme.palette.text.secondary, + }, + '& .asyncapi__message-headers-header, .asyncapi__message-payload-header, .asyncapi__server-variables-header, .asyncapi__server-security-header': + { + 'background-color': 'inherit', + color: theme.palette.text.secondary, + }, + '& .asyncapi__table-header': { + background: theme.palette.background.default, + }, + '& .asyncapi__table-body': { + color: theme.palette.text.primary, + }, + '& .asyncapi__server-security-flow': { + background: theme.palette.background.default, + border: 'none', + }, + '& .asyncapi__server-security-flows-list a': { + color: theme.palette.primary.main, + }, + '& .asyncapi__table-row--nested': { + color: theme.palette.text.secondary, + }, + }, +})); + +type Props = { + definition: string; +}; + +export const AsyncApiDefinition = ({ definition }: Props) => { + const classes = useStyles(); + + return ( +

+ +
+ ); +}; diff --git a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx index f32b5d01b5..a3d9bc1f64 100644 --- a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx +++ b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx @@ -14,132 +14,27 @@ * limitations under the License. */ -import AsyncApi from '@asyncapi/react-component'; -import '@asyncapi/react-component/lib/styles/fiori.css'; -import { fade, makeStyles } from '@material-ui/core/styles'; -import React from 'react'; +import { Progress } from '@backstage/core-components'; +import React, { Suspense } from 'react'; -const useStyles = makeStyles(theme => ({ - root: { - '& .asyncapi': { - 'font-family': 'inherit', - background: 'none', - }, - '& h2': { - ...theme.typography.h6, - }, - '& .text-teal': { - color: theme.palette.primary.main, - }, - '& button': { - ...theme.typography.button, - background: 'none', - boxSizing: 'border-box', - minWidth: 64, - borderRadius: theme.shape.borderRadius, - transition: theme.transitions.create( - ['background-color', 'box-shadow', 'border'], - { - duration: theme.transitions.duration.short, - }, - ), - padding: '5px 15px', - color: theme.palette.primary.main, - border: `1px solid ${fade(theme.palette.primary.main, 0.5)}`, - '&:hover': { - textDecoration: 'none', - '&.Mui-disabled': { - backgroundColor: 'transparent', - }, - border: `1px solid ${theme.palette.primary.main}`, - backgroundColor: fade( - theme.palette.primary.main, - theme.palette.action.hoverOpacity, - ), - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: 'transparent', - }, - }, - '&.Mui-disabled': { - color: theme.palette.action.disabled, - }, - }, - '& .asyncapi__collapse-button:hover': { - color: theme.palette.primary.main, - }, - '& button.asyncapi__toggle-button': { - 'min-width': 'inherit', - }, - '& .asyncapi__info-list li': { - 'border-color': theme.palette.primary.main, - '&:hover': { - color: theme.palette.text.primary, - 'border-color': theme.palette.primary.main, - 'background-color': theme.palette.primary.main, - }, - }, - '& .asyncapi__info-list li a': { - color: theme.palette.primary.main, - '&:hover': { - color: theme.palette.getContrastText(theme.palette.primary.main), - }, - }, - '& .asyncapi__enum': { - color: theme.palette.secondary.main, - }, - '& .asyncapi__info, .asyncapi__channel, .asyncapi__channels > div, .asyncapi__schema, .asyncapi__channel-operations-list .asyncapi__messages-list-item .asyncapi__message, .asyncapi__message, .asyncapi__server, .asyncapi__servers > div, .asyncapi__messages > div, .asyncapi__schemas > div': - { - 'background-color': 'inherit', - }, - '& .asyncapi__channel-parameters-header, .asyncapi__channel-operations-header, .asyncapi__channel-operation-oneOf-subscribe-header, .asyncapi__channel-operation-oneOf-publish-header, .asyncapi__channel-operation-message-header, .asyncapi__message-header, .asyncapi__message-header-title, .asyncapi__message-header-title > h3, .asyncapi__bindings, .asyncapi__bindings-header, .asyncapi__bindings-header > h4': - { - 'background-color': 'inherit', - color: theme.palette.text.primary, - }, - '& .asyncapi__additional-properties-notice': { - color: theme.palette.text.hint, - }, - '& .asyncapi__code, .asyncapi__code-pre': { - background: theme.palette.background.default, - }, - '& .asyncapi__schema-example-header-title': { - color: theme.palette.text.secondary, - }, - '& .asyncapi__message-headers-header, .asyncapi__message-payload-header, .asyncapi__server-variables-header, .asyncapi__server-security-header': - { - 'background-color': 'inherit', - color: theme.palette.text.secondary, - }, - '& .asyncapi__table-header': { - background: theme.palette.background.default, - }, - '& .asyncapi__table-body': { - color: theme.palette.text.primary, - }, - '& .asyncapi__server-security-flow': { - background: theme.palette.background.default, - border: 'none', - }, - '& .asyncapi__server-security-flows-list a': { - color: theme.palette.primary.main, - }, - '& .asyncapi__table-row--nested': { - color: theme.palette.text.secondary, - }, - }, -})); +// The asyncapi component and related CSS has a significant size, only load it +// if the element is actually used. +const LazyAsyncApiDefinition = React.lazy(() => + import('./AsyncApiDefinition').then(m => ({ + default: m.AsyncApiDefinition, + })), +); -type Props = { +export type AsyncApiDefinitionWidgetProps = { definition: string; }; -export const AsyncApiDefinitionWidget = ({ definition }: Props) => { - const classes = useStyles(); - +export const AsyncApiDefinitionWidget = ({ + definition, +}: AsyncApiDefinitionWidgetProps) => { return ( -
- -
+ }> + + ); }; diff --git a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.test.tsx b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.test.tsx similarity index 89% rename from plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.test.tsx rename to plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.test.tsx index 63e5a73692..fd78fa9204 100644 --- a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.test.tsx +++ b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.test.tsx @@ -16,9 +16,9 @@ import { renderInTestApp } from '@backstage/test-utils'; import React from 'react'; -import { GraphQlDefinitionWidget } from './GraphQlDefinitionWidget'; +import { GraphQlDefinition } from './GraphQlDefinition'; -describe('', () => { +describe('', () => { it('renders graphql schema', async () => { const definition = ` """Hello World!""" @@ -53,7 +53,7 @@ type Film { }; const { getByText } = await renderInTestApp( - , + , ); expect(getByText(/Film/i)).toBeInTheDocument(); diff --git a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.tsx b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.tsx new file mode 100644 index 0000000000..d3fddf70cf --- /dev/null +++ b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.tsx @@ -0,0 +1,63 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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 { BackstageTheme } from '@backstage/theme'; +import { makeStyles } from '@material-ui/core/styles'; +import GraphiQL from 'graphiql'; +import 'graphiql/graphiql.css'; +import { buildSchema } from 'graphql'; +import React from 'react'; + +const useStyles = makeStyles(() => ({ + root: { + height: '100%', + display: 'flex', + flexFlow: 'column nowrap', + }, + graphiQlWrapper: { + flex: 1, + '@global': { + '.graphiql-container': { + boxSizing: 'initial', + height: '100%', + minHeight: '600px', + flex: '1 1 auto', + }, + }, + }, +})); + +type Props = { + definition: string; +}; + +export const GraphQlDefinition = ({ definition }: Props) => { + const classes = useStyles(); + const schema = buildSchema(definition); + + return ( +
+
+ Promise.resolve(null) as any} + schema={schema} + docExplorerOpen + defaultSecondaryEditorOpen={false} + /> +
+
+ ); +}; diff --git a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.tsx b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.tsx index 8d15ba08a2..e91387bffa 100644 --- a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.tsx +++ b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.tsx @@ -14,54 +14,27 @@ * limitations under the License. */ -import { BackstageTheme } from '@backstage/theme'; -import { makeStyles } from '@material-ui/core/styles'; -import 'graphiql/graphiql.css'; -import { buildSchema } from 'graphql'; -import React, { Suspense } from 'react'; import { Progress } from '@backstage/core-components'; +import React, { Suspense } from 'react'; -const GraphiQL = React.lazy(() => import('graphiql')); +// The graphql component, graphql and related CSS has a significant size, only +// load it if the element is actually used. +const LazyGraphQlDefinition = React.lazy(() => + import('./GraphQlDefinition').then(m => ({ + default: m.GraphQlDefinition, + })), +); -const useStyles = makeStyles(() => ({ - root: { - height: '100%', - display: 'flex', - flexFlow: 'column nowrap', - }, - graphiQlWrapper: { - flex: 1, - '@global': { - '.graphiql-container': { - boxSizing: 'initial', - height: '100%', - minHeight: '600px', - flex: '1 1 auto', - }, - }, - }, -})); - -type Props = { - definition: any; +export type GraphQlDefinitionWidgetProps = { + definition: string; }; -export const GraphQlDefinitionWidget = ({ definition }: Props) => { - const classes = useStyles(); - const schema = buildSchema(definition); - +export const GraphQlDefinitionWidget = ({ + definition, +}: GraphQlDefinitionWidgetProps) => { return ( }> -
-
- Promise.resolve(null) as any} - schema={schema} - docExplorerOpen - defaultSecondaryEditorOpen={false} - /> -
-
+
); }; diff --git a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.test.tsx b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.test.tsx similarity index 87% rename from plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.test.tsx rename to plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.test.tsx index 080aa11a3a..735fba86f7 100644 --- a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.test.tsx +++ b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.test.tsx @@ -17,9 +17,9 @@ import { renderInTestApp } from '@backstage/test-utils'; import { waitFor } from '@testing-library/react'; import React from 'react'; -import { OpenApiDefinitionWidget } from './OpenApiDefinitionWidget'; +import { OpenApiDefinition } from './OpenApiDefinition'; -describe('', () => { +describe('', () => { it('renders openapi spec', async () => { const definition = ` openapi: "3.0.0" @@ -39,7 +39,7 @@ paths: description: Success `; const { getByText } = await renderInTestApp( - , + , ); // swagger-ui loads the documentation asynchronously @@ -51,7 +51,7 @@ paths: it('renders error if definition is missing', async () => { const { getByText } = await renderInTestApp( - , + , ); expect(getByText(/No API definition provided/i)).toBeInTheDocument(); }); diff --git a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.tsx b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.tsx new file mode 100644 index 0000000000..cc3df40ae5 --- /dev/null +++ b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.tsx @@ -0,0 +1,92 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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 { makeStyles } from '@material-ui/core/styles'; +import React, { useEffect, useState } from 'react'; +import SwaggerUI from 'swagger-ui-react'; +import 'swagger-ui-react/swagger-ui.css'; + +const useStyles = makeStyles(theme => ({ + root: { + '& .swagger-ui, .info h1, .info h2, .info h3, .info h4, .info h': { + 'font-family': 'inherit', + color: theme.palette.text.primary, + }, + '& .scheme-container': { + 'background-color': theme.palette.background.default, + }, + '& .opblock-tag, .opblock-tag small, table thead tr td, table thead tr th': + { + color: theme.palette.text.primary, + 'border-color': theme.palette.divider, + }, + '& section.models, section.models.is-open h4': { + 'border-color': theme.palette.divider, + }, + '& .opblock .opblock-summary-description, .parameter__type, table.headers td, .model-title, .model .property.primitive, section h3': + { + color: theme.palette.text.secondary, + }, + '& .opblock .opblock-summary-operation-id, .opblock .opblock-summary-path, .opblock .opblock-summary-path__deprecated, .opblock .opblock-section-header h4, .parameter__name, .response-col_status, .response-col_links, .responses-inner h4, .swagger-ui .responses-inner h5, .opblock-section-header .btn, .tab li, .info li, .info p, .info table, section.models h4, .info .title, table.model tr.description, .property-row': + { + color: theme.palette.text.primary, + }, + '& .opblock .opblock-section-header, .model-box, section.models .model-container': + { + background: theme.palette.background.default, + }, + '& .prop-format, .parameter__in': { + color: theme.palette.text.disabled, + }, + '& ': { + color: theme.palette.text.primary, + 'border-color': theme.palette.divider, + }, + '& .opblock-description-wrapper p, .opblock-external-docs-wrapper p, .opblock-title_normal p, .response-control-media-type__accept-message, .opblock .opblock-section-header>label, .scheme-container .schemes>label, .info .base-url, .model': + { + color: theme.palette.text.hint, + }, + '& .parameter__name.required:after': { + color: theme.palette.warning.dark, + }, + '& .prop-type': { + color: theme.palette.primary.main, + }, + }, +})); + +type Props = { + definition: string; +}; + +export const OpenApiDefinition = ({ definition }: Props) => { + const classes = useStyles(); + + // Due to a bug in the swagger-ui-react component, the component needs + // to be created without content first. + const [def, setDef] = useState(''); + + useEffect(() => { + const timer = setTimeout(() => setDef(definition), 0); + return () => clearTimeout(timer); + }, [definition, setDef]); + + return ( +
+ +
+ ); +}; diff --git a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.tsx b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.tsx index 4c22d29bc2..2c223243d1 100644 --- a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.tsx +++ b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.tsx @@ -14,81 +14,27 @@ * limitations under the License. */ -import { makeStyles } from '@material-ui/core/styles'; -import React, { useEffect, useState } from 'react'; -import SwaggerUI from 'swagger-ui-react'; -import 'swagger-ui-react/swagger-ui.css'; +import { Progress } from '@backstage/core-components'; +import React, { Suspense } from 'react'; -// TODO: Schemas +// The swagger-ui component and related CSS has a significant size, only load it +// if the element is actually used. +const LazyOpenApiDefinition = React.lazy(() => + import('./OpenApiDefinition').then(m => ({ + default: m.OpenApiDefinition, + })), +); -const useStyles = makeStyles(theme => ({ - root: { - '& .swagger-ui, .info h1, .info h2, .info h3, .info h4, .info h': { - 'font-family': 'inherit', - color: theme.palette.text.primary, - }, - '& .scheme-container': { - 'background-color': theme.palette.background.default, - }, - '& .opblock-tag, .opblock-tag small, table thead tr td, table thead tr th': - { - color: theme.palette.text.primary, - 'border-color': theme.palette.divider, - }, - '& section.models, section.models.is-open h4': { - 'border-color': theme.palette.divider, - }, - '& .opblock .opblock-summary-description, .parameter__type, table.headers td, .model-title, .model .property.primitive, section h3': - { - color: theme.palette.text.secondary, - }, - '& .opblock .opblock-summary-operation-id, .opblock .opblock-summary-path, .opblock .opblock-summary-path__deprecated, .opblock .opblock-section-header h4, .parameter__name, .response-col_status, .response-col_links, .responses-inner h4, .swagger-ui .responses-inner h5, .opblock-section-header .btn, .tab li, .info li, .info p, .info table, section.models h4, .info .title, table.model tr.description, .property-row': - { - color: theme.palette.text.primary, - }, - '& .opblock .opblock-section-header, .model-box, section.models .model-container': - { - background: theme.palette.background.default, - }, - '& .prop-format, .parameter__in': { - color: theme.palette.text.disabled, - }, - '& ': { - color: theme.palette.text.primary, - 'border-color': theme.palette.divider, - }, - '& .opblock-description-wrapper p, .opblock-external-docs-wrapper p, .opblock-title_normal p, .response-control-media-type__accept-message, .opblock .opblock-section-header>label, .scheme-container .schemes>label, .info .base-url, .model': - { - color: theme.palette.text.hint, - }, - '& .parameter__name.required:after': { - color: theme.palette.warning.dark, - }, - '& .prop-type': { - color: theme.palette.primary.main, - }, - }, -})); - -type Props = { +export type OpenApiDefinitionWidgetProps = { definition: string; }; -export const OpenApiDefinitionWidget = ({ definition }: Props) => { - const classes = useStyles(); - - // Due to a bug in the swagger-ui-react component, the component needs - // to be created without content first. - const [def, setDef] = useState(''); - - useEffect(() => { - const timer = setTimeout(() => setDef(definition), 0); - return () => clearTimeout(timer); - }, [definition, setDef]); - +export const OpenApiDefinitionWidget = ({ + definition, +}: OpenApiDefinitionWidgetProps) => { return ( -
- -
+ }> + + ); }; From 8666b7bc903b55810632f2239caadb929616d660 Mon Sep 17 00:00:00 2001 From: Alex Rybchenko Date: Tue, 2 Nov 2021 09:26:45 +0100 Subject: [PATCH 049/196] updated changeset Signed-off-by: Alex Rybchenko --- .changeset/real-mails-add.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.changeset/real-mails-add.md b/.changeset/real-mails-add.md index 9eff9cbe62..30a9100699 100644 --- a/.changeset/real-mails-add.md +++ b/.changeset/real-mails-add.md @@ -1,6 +1,5 @@ --- '@backstage/core-components': patch -'@backstage/plugin-catalog': patch --- -fixed route resolving (issue #7741) when user cannot select a tab in any of the tabbed pages (like the Catalog page) if it shares the same initial letters as a preceding tab. (i.e. where tab with a path of /ci is followed by a path of /cid, user cannot select /cid as /ci will always be selected first). +fixed route resolving (issue #7741) when user cannot select a tab in any of the tabbed pages (like the Catalog page) if it shares the same initial letters as a preceding tab. (i.e. where tab with a path of /ci is followed by a path of /ci-2, user cannot select /ci-2 as /ci will always be selected first). From 9587f6f7d26ad9cff89ed0f2fb9f310d86b8cf36 Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Mon, 25 Oct 2021 11:26:13 +0200 Subject: [PATCH 050/196] Add tests for the scaffolder eventstream Signed-off-by: Dominik Henneke --- .../src/service/router.test.ts | 179 ++++++++++++++++-- .../scaffolder-backend/src/service/router.ts | 43 +++-- 2 files changed, 183 insertions(+), 39 deletions(-) diff --git a/plugins/scaffolder-backend/src/service/router.test.ts b/plugins/scaffolder-backend/src/service/router.test.ts index 1c5343d458..5157af4eaf 100644 --- a/plugins/scaffolder-backend/src/service/router.test.ts +++ b/plugins/scaffolder-backend/src/service/router.test.ts @@ -29,17 +29,17 @@ jest.doMock('fs-extra', () => ({ })); import { + DatabaseManager, + DockerContainerRunner, getVoidLogger, PluginDatabaseManager, - DatabaseManager, UrlReaders, - DockerContainerRunner, } from '@backstage/backend-common'; import { CatalogApi } from '@backstage/catalog-client'; +import { TemplateEntityV1beta2 } from '@backstage/catalog-model'; import { ConfigReader } from '@backstage/config'; import express from 'express'; import request from 'supertest'; -import { TemplateEntityV1beta2 } from '@backstage/catalog-model'; /** * TODO: The following should import directly from the router file. * Due to a circular dependency between this plugin and the @@ -47,6 +47,12 @@ import { TemplateEntityV1beta2 } from '@backstage/catalog-model'; * TypeError: _pluginscaffolderbackend.createTemplateAction is not a function */ import { createRouter } from '../index'; +import { StorageTaskBroker } from '../scaffolder/tasks/StorageTaskBroker'; + +jest.mock('../scaffolder/tasks'); + +const MockStorageTaskBroker: jest.MockedClass = + StorageTaskBroker as any; const createCatalogClient = (templates: any[] = []) => ({ @@ -102,7 +108,7 @@ describe('createRouter', () => { }, }; - beforeAll(async () => { + beforeEach(async () => { const router = await createRouter({ logger: getVoidLogger(), config: new ConfigReader({}), @@ -114,7 +120,7 @@ describe('createRouter', () => { app = express().use(router); }); - beforeEach(() => { + afterEach(() => { jest.resetAllMocks(); }); @@ -142,6 +148,10 @@ describe('createRouter', () => { }); it('return the template id', async () => { + MockStorageTaskBroker.prototype.dispatch.mockResolvedValue({ + taskId: 'a-random-id', + }); + const response = await request(app) .post('/v2/tasks') .send({ @@ -151,28 +161,161 @@ describe('createRouter', () => { }, }); - expect(response.body.id).toBeDefined(); + expect(response.body.id).toBe('a-random-id'); expect(response.status).toEqual(201); }); }); describe('GET /v2/tasks/:taskId', () => { it('does not divulge secrets', async () => { - const postResponse = await request(app) - .post('/v2/tasks') - .set('Authorization', 'Bearer secret') - .send({ - templateName: 'create-react-app-template', - values: { - required: 'required-value', - }, - }); + MockStorageTaskBroker.prototype.get.mockResolvedValue({ + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + secrets: { token: 'secret' }, + }); - const response = await request(app) - .get(`/v2/tasks/${postResponse.body.id}`) - .send(); + const response = await request(app).get(`/v2/tasks/a-random-id`); expect(response.status).toEqual(200); + expect(response.body.status).toBe('completed'); expect(response.body.secrets).toBeUndefined(); }); }); + + describe('GET /v2/tasks/:taskId/eventstream', () => { + it('should return log messages', async () => { + const unsubscribe = jest.fn(); + MockStorageTaskBroker.prototype.observe.mockImplementation( + ({ taskId }, callback) => { + // emit after this function returned + setImmediate(() => { + callback(undefined, { + events: [ + { + id: 0, + taskId, + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }, + ], + }); + callback(undefined, { + events: [ + { + id: 1, + taskId, + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ], + }); + }); + + return unsubscribe; + }, + ); + + let statusCode: any = undefined; + let headers: any = {}; + const responseDataFn = jest.fn(); + + const req = request(app) + .get('/v2/tasks/a-random-id/eventstream') + .set('accept', 'text/event-stream') + .parse((res, _) => { + ({ statusCode, headers } = res as any); + + res.on('data', chunk => { + responseDataFn(chunk.toString()); + + // the server expects the client to abort the request + if (chunk.includes('completion')) { + req.abort(); + } + }); + }); + + // wait for the request to finish + await req.catch(() => { + // ignore 'aborted' error + }); + + expect(statusCode).toBe(200); + expect(headers['content-type']).toBe('text/event-stream'); + expect(responseDataFn).toBeCalledTimes(2); + expect(responseDataFn).toBeCalledWith(`event: log +data: {"id":0,"taskId":"a-random-id","type":"log","createdAt":"","body":{"message":"My log message"}} + +`); + expect(responseDataFn).toBeCalledWith(`event: completion +data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{"message":"Finished!"}} + +`); + + expect(MockStorageTaskBroker.prototype.observe).toBeCalledTimes(1); + expect(MockStorageTaskBroker.prototype.observe).toBeCalledWith( + { taskId: 'a-random-id' }, + expect.any(Function), + ); + + expect(unsubscribe).toBeCalledTimes(1); + }); + + it('should return log messages with after query', async () => { + const unsubscribe = jest.fn(); + MockStorageTaskBroker.prototype.observe.mockImplementation( + ({ taskId }, callback) => { + setImmediate(() => { + callback(undefined, { + events: [ + { + id: 1, + taskId, + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ], + }); + }); + return unsubscribe; + }, + ); + + let statusCode: any = undefined; + let headers: any = {}; + + const req = request(app) + .get('/v2/tasks/a-random-id/eventstream') + .query({ after: 10 }) + .set('accept', 'text/event-stream') + .parse((res, _) => { + ({ statusCode, headers } = res as any); + + res.on('data', () => { + // close immediately + req.abort(); + }); + }); + + // wait for the request to finish + await req.catch(() => { + // ignore 'aborted' error + }); + + expect(statusCode).toBe(200); + expect(headers['content-type']).toBe('text/event-stream'); + + expect(MockStorageTaskBroker.prototype.observe).toBeCalledTimes(1); + expect(MockStorageTaskBroker.prototype.observe).toBeCalledWith( + { taskId: 'a-random-id', after: 10 }, + expect.any(Function), + ); + + expect(unsubscribe).toBeCalledTimes(1); + }); + }); }); diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index a78b04da78..fce3b885c6 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -14,33 +14,33 @@ * limitations under the License. */ -import { Config } from '@backstage/config'; -import express from 'express'; -import Router from 'express-promise-router'; -import { Logger } from 'winston'; -import { CatalogEntityClient } from '../lib/catalog'; -import { validate } from 'jsonschema'; -import { - DatabaseTaskStore, - TemplateActionRegistry, - TaskWorker, - TemplateAction, - createBuiltinActions, -} from '../scaffolder'; -import { StorageTaskBroker } from '../scaffolder/tasks/StorageTaskBroker'; -import { getEntityBaseUrl, getWorkingDirectory } from './helpers'; import { ContainerRunner, PluginDatabaseManager, UrlReader, } from '@backstage/backend-common'; -import { InputError, NotFoundError } from '@backstage/errors'; import { CatalogApi } from '@backstage/catalog-client'; -import { TemplateEntityV1beta2, Entity } from '@backstage/catalog-model'; -import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; - +import { Entity, TemplateEntityV1beta2 } from '@backstage/catalog-model'; +import { Config } from '@backstage/config'; +import { InputError, NotFoundError } from '@backstage/errors'; import { ScmIntegrations } from '@backstage/integration'; -import { TaskBroker, TaskSpec } from '../scaffolder/tasks/types'; +import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; +import express from 'express'; +import Router from 'express-promise-router'; +import { validate } from 'jsonschema'; +import { Logger } from 'winston'; +import { CatalogEntityClient } from '../lib/catalog'; +import { + createBuiltinActions, + DatabaseTaskStore, + TaskBroker, + TaskSpec, + TaskWorker, + TemplateAction, + TemplateActionRegistry, +} from '../scaffolder'; +import { StorageTaskBroker } from '../scaffolder/tasks/StorageTaskBroker'; +import { getEntityBaseUrl, getWorkingDirectory } from './helpers'; /** * RouterOptions @@ -278,7 +278,8 @@ export async function createRouter( // to automatically reconnect because it lost connection. } } - res.flush(); + // res.flush() is only available with the compression middleware + res.flush?.(); if (shouldUnsubscribe) unsubscribe(); }, ); From 95a48698bc5d64195076790d00baaebf07ee01e6 Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Mon, 25 Oct 2021 14:17:37 +0200 Subject: [PATCH 051/196] Add tests for the scaffolder eventstream frontend client Signed-off-by: Dominik Henneke --- plugins/scaffolder/package.json | 1 + plugins/scaffolder/src/api.test.ts | 76 +++++++++++++++++++++++++--- plugins/scaffolder/src/setupTests.ts | 3 ++ 3 files changed, 74 insertions(+), 6 deletions(-) diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 0b517671ba..20b8e934f7 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -77,6 +77,7 @@ "@types/jest": "^26.0.7", "@types/node": "^14.14.32", "cross-fetch": "^3.0.6", + "event-source-polyfill": "^1.0.25", "msw": "^0.35.0" }, "files": [ diff --git a/plugins/scaffolder/src/api.test.ts b/plugins/scaffolder/src/api.test.ts index cc4b70283c..f11b2cf06c 100644 --- a/plugins/scaffolder/src/api.test.ts +++ b/plugins/scaffolder/src/api.test.ts @@ -14,12 +14,18 @@ * limitations under the License. */ +import { ConfigReader } from '@backstage/core-app-api'; import { ScmIntegrations } from '@backstage/integration'; import { ScaffolderClient } from './api'; -import { ConfigReader } from '@backstage/core-app-api'; + +const MockedEventSource = global.EventSource as jest.MockedClass< + typeof EventSource +>; describe('api', () => { - const discoveryApi = {} as any; + const mockBaseUrl = 'http://backstage/api'; + + const discoveryApi = { getBaseUrl: async () => mockBaseUrl }; const identityApi = {} as any; const scmIntegrationsApi = ScmIntegrations.fromConfig( new ConfigReader({ @@ -32,10 +38,14 @@ describe('api', () => { }, }), ); - const apiClient = new ScaffolderClient({ - scmIntegrationsApi, - discoveryApi, - identityApi, + + let apiClient: ScaffolderClient; + beforeEach(() => { + apiClient = new ScaffolderClient({ + scmIntegrationsApi, + discoveryApi, + identityApi, + }); }); it('should return default and custom integrations', async () => { @@ -51,4 +61,58 @@ describe('api', () => { expect(allowedHosts).toContain(integration.host), ); }); + + describe('streamEvents', () => { + describe('eventsource', () => { + it('should work', async () => { + MockedEventSource.prototype.addEventListener.mockImplementation( + (type, fn) => { + if (typeof fn !== 'function') { + return; + } + + if (type === 'log') { + fn({ + data: '{"id":1,"taskId":"a-random-id","type":"log","createdAt":"","body":{"message":"My log message"}}', + } as any); + } else if (type === 'completion') { + fn({ + data: '{"id":2,"taskId":"a-random-id","type":"completion","createdAt":"","body":{"message":"Finished!"}}', + } as any); + } + }, + ); + + const next = jest.fn(); + + await new Promise(complete => { + apiClient + .streamLogs({ taskId: 'a-random-task-id' }) + .subscribe({ next, complete }); + }); + + expect(MockedEventSource).toBeCalledWith( + 'http://backstage/api/v2/tasks/a-random-task-id/eventstream', + { withCredentials: true }, + ); + expect(MockedEventSource.prototype.close).toBeCalled(); + + expect(next).toBeCalledTimes(2); + expect(next).toBeCalledWith({ + id: 1, + taskId: 'a-random-id', + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }); + expect(next).toBeCalledWith({ + id: 2, + taskId: 'a-random-id', + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }); + }); + }); + }); }); diff --git a/plugins/scaffolder/src/setupTests.ts b/plugins/scaffolder/src/setupTests.ts index 963c0f188b..84589060ec 100644 --- a/plugins/scaffolder/src/setupTests.ts +++ b/plugins/scaffolder/src/setupTests.ts @@ -15,3 +15,6 @@ */ import '@testing-library/jest-dom'; + +const { EventSourcePolyfill } = jest.requireMock('event-source-polyfill'); +global.EventSource = EventSourcePolyfill; From b45a34fb15e50593470afc73c14647d4d78a9ed2 Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Mon, 25 Oct 2021 11:27:28 +0200 Subject: [PATCH 052/196] Adds a new endpoint for consuming logs from the Scaffolder that uses long polling instead of Server Sent Events Signed-off-by: Dominik Henneke --- .changeset/selfish-countries-prove.md | 42 ++++ .../src/service/router.test.ts | 80 ++++++++ .../scaffolder-backend/src/service/router.ts | 41 +++- plugins/scaffolder/api-report.md | 9 +- plugins/scaffolder/package.json | 1 + plugins/scaffolder/src/api.test.ts | 179 ++++++++++++++++++ plugins/scaffolder/src/api.ts | 66 ++++++- plugins/scaffolder/src/setupTests.ts | 1 + 8 files changed, 404 insertions(+), 15 deletions(-) create mode 100644 .changeset/selfish-countries-prove.md diff --git a/.changeset/selfish-countries-prove.md b/.changeset/selfish-countries-prove.md new file mode 100644 index 0000000000..03e36f6d80 --- /dev/null +++ b/.changeset/selfish-countries-prove.md @@ -0,0 +1,42 @@ +--- +'@backstage/plugin-scaffolder': patch +'@backstage/plugin-scaffolder-backend': patch +--- + +Adds a new endpoint for consuming logs from the Scaffolder that uses long polling instead of Server Sent Events. + +This is useful if Backstage is accessed from an environment that doesn't support SSE correctly, which happens in combination with certain enterprise HTTP Proxy servers. + +It is intended to switch the endpoint globally for the whole instance. +If you want to use it, you can provide a reconfigured API to the `scaffolderApiRef`: + +```tsx +// packages/app/src/apis.ts + +// ... +import { + scaffolderApiRef, + ScaffolderClient, +} from '@backstage/plugin-scaffolder'; + +export const apis: AnyApiFactory[] = [ + // ... + + createApiFactory({ + api: scaffolderApiRef, + deps: { + discoveryApi: discoveryApiRef, + identityApi: identityApiRef, + scmIntegrationsApi: scmIntegrationsApiRef, + }, + factory: ({ discoveryApi, identityApi, scmIntegrationsApi }) => + new ScaffolderClient({ + discoveryApi, + identityApi, + scmIntegrationsApi, + // use long polling instead of an eventsource + useLongPollingLogs: true, + }), + }), +]; +``` diff --git a/plugins/scaffolder-backend/src/service/router.test.ts b/plugins/scaffolder-backend/src/service/router.test.ts index 5157af4eaf..d6136fe872 100644 --- a/plugins/scaffolder-backend/src/service/router.test.ts +++ b/plugins/scaffolder-backend/src/service/router.test.ts @@ -318,4 +318,84 @@ data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{ expect(unsubscribe).toBeCalledTimes(1); }); }); + + describe('GET /v2/tasks/:taskId/logs', () => { + it('should return log messages', async () => { + const unsubscribe = jest.fn(); + MockStorageTaskBroker.prototype.observe.mockImplementation( + ({ taskId }, callback) => { + callback(undefined, { + events: [ + { + id: 0, + taskId, + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }, + { + id: 1, + taskId, + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ], + }); + return unsubscribe; + }, + ); + + const response = await request(app).get('/v2/tasks/a-random-id/logs'); + + expect(response.status).toEqual(200); + expect(response.body).toEqual([ + { + id: 0, + taskId: 'a-random-id', + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }, + { + id: 1, + taskId: 'a-random-id', + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ]); + + expect(MockStorageTaskBroker.prototype.observe).toBeCalledTimes(1); + expect(MockStorageTaskBroker.prototype.observe).toBeCalledWith( + { taskId: 'a-random-id' }, + expect.any(Function), + ); + expect(unsubscribe).toBeCalledTimes(1); + }); + + it('should return log messages with after query', async () => { + const unsubscribe = jest.fn(); + MockStorageTaskBroker.prototype.observe.mockImplementation( + (_, callback) => { + callback(undefined, { events: [] }); + return unsubscribe; + }, + ); + + const response = await request(app) + .get('/v2/tasks/a-random-id/logs') + .query({ after: 10 }); + + expect(response.status).toEqual(200); + expect(response.body).toEqual([]); + + expect(MockStorageTaskBroker.prototype.observe).toBeCalledTimes(1); + expect(MockStorageTaskBroker.prototype.observe).toBeCalledWith( + { taskId: 'a-random-id', after: 10 }, + expect.any(Function), + ); + expect(unsubscribe).toBeCalledTimes(1); + }); + }); }); diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index fce3b885c6..076a7b0036 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -247,7 +247,9 @@ export async function createRouter( }) .get('/v2/tasks/:taskId/eventstream', async (req, res) => { const { taskId } = req.params; - const after = Number(req.query.after) || undefined; + const after = + req.query.after !== undefined ? Number(req.query.after) : undefined; + logger.debug(`Event stream observing taskId '${taskId}' opened`); // Mandatory headers and http status to keep connection open @@ -289,6 +291,43 @@ export async function createRouter( unsubscribe(); logger.debug(`Event stream observing taskId '${taskId}' closed`); }); + }) + .get('/v2/tasks/:taskId/logs', async (req, res) => { + const { taskId } = req.params; + const after = Number(req.query.after) || undefined; + + let unsubscribe = () => {}; + + // cancel the request after 30 seconds. this aligns with the recommendations of RFC 6202. + const timeout = setTimeout(() => { + unsubscribe(); + res.json([]); + }, 30_000); + + // Get all known events after an id (always includes the completion event) and return the first callback + unsubscribe = taskBroker.observe( + { taskId, after }, + (error, { events }) => { + // stop the timeout + clearTimeout(timeout); + unsubscribe(); + + if (error) { + logger.error( + `Received error from log when observing taskId '${taskId}', ${error}`, + ); + } + + res.json(events); + }, + ); + + // When client closes connection we update the clients list + // avoiding the disconnected one + req.on('close', () => { + unsubscribe(); + clearTimeout(timeout); + }); }); const app = express(); diff --git a/plugins/scaffolder/api-report.md b/plugins/scaffolder/api-report.md index 554fccabb7..0ac6910264 100644 --- a/plugins/scaffolder/api-report.md +++ b/plugins/scaffolder/api-report.md @@ -178,6 +178,7 @@ export class ScaffolderClient implements ScaffolderApi { discoveryApi: DiscoveryApi; identityApi: IdentityApi; scmIntegrationsApi: ScmIntegrationRegistry; + useLongPollingLogs?: boolean; }); // (undocumented) getIntegrationsList(options: { allowedHosts: string[] }): Promise< @@ -199,13 +200,7 @@ export class ScaffolderClient implements ScaffolderApi { // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen scaffold(templateName: string, values: Record): Promise; // (undocumented) - streamLogs({ - taskId, - after, - }: { - taskId: string; - after?: number; - }): Observable; + streamLogs(opts: { taskId: string; after?: number }): Observable; } // Warning: (ae-missing-release-tag) "ScaffolderFieldExtensions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 20b8e934f7..58bee32201 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -55,6 +55,7 @@ "json-schema": "^0.3.0", "lodash": "^4.17.21", "luxon": "^2.0.2", + "qs": "^6.9.4", "react": "^16.13.1", "react-dom": "^16.13.1", "react-lazylog": "^4.5.2", diff --git a/plugins/scaffolder/src/api.test.ts b/plugins/scaffolder/src/api.test.ts index f11b2cf06c..8b53029cd7 100644 --- a/plugins/scaffolder/src/api.test.ts +++ b/plugins/scaffolder/src/api.test.ts @@ -16,13 +16,19 @@ import { ConfigReader } from '@backstage/core-app-api'; import { ScmIntegrations } from '@backstage/integration'; +import { setupRequestMockHandlers } from '@backstage/test-utils'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; import { ScaffolderClient } from './api'; const MockedEventSource = global.EventSource as jest.MockedClass< typeof EventSource >; +const server = setupServer(); + describe('api', () => { + setupRequestMockHandlers(server); const mockBaseUrl = 'http://backstage/api'; const discoveryApi = { getBaseUrl: async () => mockBaseUrl }; @@ -114,5 +120,178 @@ describe('api', () => { }); }); }); + + describe('longPolling', () => { + beforeEach(() => { + apiClient = new ScaffolderClient({ + scmIntegrationsApi, + discoveryApi, + identityApi, + useLongPollingLogs: true, + }); + }); + + it('should work', async () => { + server.use( + rest.get(`${mockBaseUrl}/v2/tasks/:taskId/logs`, (req, res, ctx) => { + const { taskId } = req.params; + const after = req.url.searchParams.get('after'); + + if (taskId === 'a-random-task-id') { + if (!after) { + return res( + ctx.json([ + { + id: 1, + taskId: 'a-random-id', + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }, + ]), + ); + } else if (after === '1') { + return res( + ctx.json([ + { + id: 2, + taskId: 'a-random-id', + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ]), + ); + } + } + + return res(ctx.status(500)); + }), + ); + + const next = jest.fn(); + + await new Promise(complete => + apiClient + .streamLogs({ taskId: 'a-random-task-id' }) + .subscribe({ next, complete }), + ); + + expect(next).toBeCalledTimes(2); + expect(next).toBeCalledWith({ + id: 1, + taskId: 'a-random-id', + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }); + expect(next).toBeCalledWith({ + id: 2, + taskId: 'a-random-id', + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }); + }); + + it('should unsubscribe', async () => { + expect.assertions(3); + + server.use( + rest.get(`${mockBaseUrl}/v2/tasks/:taskId/logs`, (req, res, ctx) => { + const { taskId } = req.params; + + const after = req.url.searchParams.get('after'); + + // use assertion to make sure it is not called after unsubscribing + expect(after).toBe(null); + + if (taskId === 'a-random-task-id') { + return res( + ctx.json([ + { + id: 1, + taskId: 'a-random-id', + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }, + ]), + ); + } + + return res(ctx.status(500)); + }), + ); + + const next = jest.fn(); + + await new Promise(complete => { + const subscription = apiClient + .streamLogs({ taskId: 'a-random-task-id' }) + .subscribe({ + next: (...args) => { + next(...args); + subscription.unsubscribe(); + complete(); + }, + }); + }); + + expect(next).toBeCalledTimes(1); + expect(next).toBeCalledWith({ + id: 1, + taskId: 'a-random-id', + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }); + }); + + it('should continue after error', async () => { + const called = jest.fn(); + + server.use( + rest.get(`${mockBaseUrl}/v2/tasks/:taskId/logs`, (_req, res, ctx) => { + called(); + + if (called.mock.calls.length > 1) { + return res( + ctx.json([ + { + id: 2, + taskId: 'a-random-id', + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ]), + ); + } + + return res(ctx.status(500)); + }), + ); + + const next = jest.fn(); + + await new Promise(complete => + apiClient + .streamLogs({ taskId: 'a-random-task-id' }) + .subscribe({ next, complete }), + ); + + expect(called).toBeCalledTimes(2); + + expect(next).toBeCalledTimes(1); + expect(next).toBeCalledWith({ + id: 2, + taskId: 'a-random-id', + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }); + }); + }); }); }); diff --git a/plugins/scaffolder/src/api.ts b/plugins/scaffolder/src/api.ts index 1b531b652d..5f2874d653 100644 --- a/plugins/scaffolder/src/api.ts +++ b/plugins/scaffolder/src/api.ts @@ -15,17 +15,18 @@ */ import { EntityName } from '@backstage/catalog-model'; -import { JsonObject, JsonValue, Observable } from '@backstage/types'; -import { ResponseError } from '@backstage/errors'; -import { ScmIntegrationRegistry } from '@backstage/integration'; -import { Field, FieldValidation } from '@rjsf/core'; -import ObservableImpl from 'zen-observable'; -import { ListActionsResponse, ScaffolderTask, Status } from './types'; import { createApiRef, DiscoveryApi, IdentityApi, } from '@backstage/core-plugin-api'; +import { ResponseError } from '@backstage/errors'; +import { ScmIntegrationRegistry } from '@backstage/integration'; +import { JsonObject, JsonValue, Observable } from '@backstage/types'; +import { Field, FieldValidation } from '@rjsf/core'; +import qs from 'qs'; +import ObservableImpl from 'zen-observable'; +import { ListActionsResponse, ScaffolderTask, Status } from './types'; export const scaffolderApiRef = createApiRef({ id: 'plugin.scaffolder.service', @@ -94,15 +95,18 @@ export class ScaffolderClient implements ScaffolderApi { private readonly discoveryApi: DiscoveryApi; private readonly identityApi: IdentityApi; private readonly scmIntegrationsApi: ScmIntegrationRegistry; + private readonly useLongPollingLogs: boolean; constructor(options: { discoveryApi: DiscoveryApi; identityApi: IdentityApi; scmIntegrationsApi: ScmIntegrationRegistry; + useLongPollingLogs?: boolean; }) { this.discoveryApi = options.discoveryApi; this.identityApi = options.identityApi; this.scmIntegrationsApi = options.scmIntegrationsApi; + this.useLongPollingLogs = options.useLongPollingLogs ?? false; } async getIntegrationsList(options: { allowedHosts: string[] }) { @@ -189,7 +193,15 @@ export class ScaffolderClient implements ScaffolderApi { return await response.json(); } - streamLogs({ + streamLogs(opts: { taskId: string; after?: number }): Observable { + if (this.useLongPollingLogs) { + return this.streamLogsPolling(opts); + } + + return this.streamLogsEventStream(opts); + } + + private streamLogsEventStream({ taskId, after, }: { @@ -239,6 +251,46 @@ export class ScaffolderClient implements ScaffolderApi { }); } + private streamLogsPolling({ + taskId, + after: inputAfter, + }: { + taskId: string; + after?: number; + }): Observable { + let after = inputAfter; + + return new ObservableImpl(subscriber => { + this.discoveryApi.getBaseUrl('scaffolder').then(async baseUrl => { + while (!subscriber.closed) { + const url = `${baseUrl}/v2/tasks/${encodeURIComponent( + taskId, + )}/logs?${qs.stringify({ after })}`; + const response = await fetch(url); + + if (!response.ok) { + // wait for one second to not run into an + await new Promise(resolve => setTimeout(resolve, 1000)); + continue; + } + + const logs = (await response.json()) as LogEvent[]; + + for (const event of logs) { + after = Number(event.id); + + subscriber.next(event); + + if (event.type === 'completion') { + subscriber.complete(); + return; + } + } + } + }); + }); + } + /** * @returns ListActionsResponse containing all registered actions. */ diff --git a/plugins/scaffolder/src/setupTests.ts b/plugins/scaffolder/src/setupTests.ts index 84589060ec..10252413a7 100644 --- a/plugins/scaffolder/src/setupTests.ts +++ b/plugins/scaffolder/src/setupTests.ts @@ -15,6 +15,7 @@ */ import '@testing-library/jest-dom'; +import 'cross-fetch/polyfill'; const { EventSourcePolyfill } = jest.requireMock('event-source-polyfill'); global.EventSource = EventSourcePolyfill; From 471b9243dead0d1fa0fe027137307096f30d2e91 Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Thu, 28 Oct 2021 15:46:16 +0200 Subject: [PATCH 053/196] Rename endpoint to /events Signed-off-by: Dominik Henneke --- .../src/service/router.test.ts | 6 +- .../scaffolder-backend/src/service/router.ts | 2 +- plugins/scaffolder/src/api.test.ts | 145 ++++++++++-------- plugins/scaffolder/src/api.ts | 2 +- 4 files changed, 82 insertions(+), 73 deletions(-) diff --git a/plugins/scaffolder-backend/src/service/router.test.ts b/plugins/scaffolder-backend/src/service/router.test.ts index d6136fe872..5b9b248097 100644 --- a/plugins/scaffolder-backend/src/service/router.test.ts +++ b/plugins/scaffolder-backend/src/service/router.test.ts @@ -319,7 +319,7 @@ data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{ }); }); - describe('GET /v2/tasks/:taskId/logs', () => { + describe('GET /v2/tasks/:taskId/events', () => { it('should return log messages', async () => { const unsubscribe = jest.fn(); MockStorageTaskBroker.prototype.observe.mockImplementation( @@ -346,7 +346,7 @@ data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{ }, ); - const response = await request(app).get('/v2/tasks/a-random-id/logs'); + const response = await request(app).get('/v2/tasks/a-random-id/events'); expect(response.status).toEqual(200); expect(response.body).toEqual([ @@ -384,7 +384,7 @@ data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{ ); const response = await request(app) - .get('/v2/tasks/a-random-id/logs') + .get('/v2/tasks/a-random-id/events') .query({ after: 10 }); expect(response.status).toEqual(200); diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 076a7b0036..35bedb3651 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -292,7 +292,7 @@ export async function createRouter( logger.debug(`Event stream observing taskId '${taskId}' closed`); }); }) - .get('/v2/tasks/:taskId/logs', async (req, res) => { + .get('/v2/tasks/:taskId/events', async (req, res) => { const { taskId } = req.params; const after = Number(req.query.after) || undefined; diff --git a/plugins/scaffolder/src/api.test.ts b/plugins/scaffolder/src/api.test.ts index 8b53029cd7..01463a6e1c 100644 --- a/plugins/scaffolder/src/api.test.ts +++ b/plugins/scaffolder/src/api.test.ts @@ -133,40 +133,43 @@ describe('api', () => { it('should work', async () => { server.use( - rest.get(`${mockBaseUrl}/v2/tasks/:taskId/logs`, (req, res, ctx) => { - const { taskId } = req.params; - const after = req.url.searchParams.get('after'); + rest.get( + `${mockBaseUrl}/v2/tasks/:taskId/events`, + (req, res, ctx) => { + const { taskId } = req.params; + const after = req.url.searchParams.get('after'); - if (taskId === 'a-random-task-id') { - if (!after) { - return res( - ctx.json([ - { - id: 1, - taskId: 'a-random-id', - type: 'log', - createdAt: '', - body: { message: 'My log message' }, - }, - ]), - ); - } else if (after === '1') { - return res( - ctx.json([ - { - id: 2, - taskId: 'a-random-id', - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ]), - ); + if (taskId === 'a-random-task-id') { + if (!after) { + return res( + ctx.json([ + { + id: 1, + taskId: 'a-random-id', + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }, + ]), + ); + } else if (after === '1') { + return res( + ctx.json([ + { + id: 2, + taskId: 'a-random-id', + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ]), + ); + } } - } - return res(ctx.status(500)); - }), + return res(ctx.status(500)); + }, + ), ); const next = jest.fn(); @@ -198,30 +201,33 @@ describe('api', () => { expect.assertions(3); server.use( - rest.get(`${mockBaseUrl}/v2/tasks/:taskId/logs`, (req, res, ctx) => { - const { taskId } = req.params; + rest.get( + `${mockBaseUrl}/v2/tasks/:taskId/events`, + (req, res, ctx) => { + const { taskId } = req.params; - const after = req.url.searchParams.get('after'); + const after = req.url.searchParams.get('after'); - // use assertion to make sure it is not called after unsubscribing - expect(after).toBe(null); + // use assertion to make sure it is not called after unsubscribing + expect(after).toBe(null); - if (taskId === 'a-random-task-id') { - return res( - ctx.json([ - { - id: 1, - taskId: 'a-random-id', - type: 'log', - createdAt: '', - body: { message: 'My log message' }, - }, - ]), - ); - } + if (taskId === 'a-random-task-id') { + return res( + ctx.json([ + { + id: 1, + taskId: 'a-random-id', + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }, + ]), + ); + } - return res(ctx.status(500)); - }), + return res(ctx.status(500)); + }, + ), ); const next = jest.fn(); @@ -252,25 +258,28 @@ describe('api', () => { const called = jest.fn(); server.use( - rest.get(`${mockBaseUrl}/v2/tasks/:taskId/logs`, (_req, res, ctx) => { - called(); + rest.get( + `${mockBaseUrl}/v2/tasks/:taskId/events`, + (_req, res, ctx) => { + called(); - if (called.mock.calls.length > 1) { - return res( - ctx.json([ - { - id: 2, - taskId: 'a-random-id', - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ]), - ); - } + if (called.mock.calls.length > 1) { + return res( + ctx.json([ + { + id: 2, + taskId: 'a-random-id', + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ]), + ); + } - return res(ctx.status(500)); - }), + return res(ctx.status(500)); + }, + ), ); const next = jest.fn(); diff --git a/plugins/scaffolder/src/api.ts b/plugins/scaffolder/src/api.ts index 5f2874d653..a16ab59053 100644 --- a/plugins/scaffolder/src/api.ts +++ b/plugins/scaffolder/src/api.ts @@ -265,7 +265,7 @@ export class ScaffolderClient implements ScaffolderApi { while (!subscriber.closed) { const url = `${baseUrl}/v2/tasks/${encodeURIComponent( taskId, - )}/logs?${qs.stringify({ after })}`; + )}/events?${qs.stringify({ after })}`; const response = await fetch(url); if (!response.ok) { From 7c7b0b27cb3fcc3a8a7bfefaee9fb51d9d03babc Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Tue, 2 Nov 2021 13:51:17 +0100 Subject: [PATCH 054/196] Adapt code to the refactored classes and types Signed-off-by: Dominik Henneke --- .../src/service/router.test.ts | 191 +++++++++--------- .../scaffolder-backend/src/service/router.ts | 4 +- 2 files changed, 102 insertions(+), 93 deletions(-) diff --git a/plugins/scaffolder-backend/src/service/router.test.ts b/plugins/scaffolder-backend/src/service/router.test.ts index 5b9b248097..d8ba15279f 100644 --- a/plugins/scaffolder-backend/src/service/router.test.ts +++ b/plugins/scaffolder-backend/src/service/router.test.ts @@ -46,14 +46,9 @@ import request from 'supertest'; * plugin-scaffolder-backend-module-cookiecutter plugin, it results in an error: * TypeError: _pluginscaffolderbackend.createTemplateAction is not a function */ -import { createRouter } from '../index'; +import { createRouter, DatabaseTaskStore, TaskBroker } from '../index'; import { StorageTaskBroker } from '../scaffolder/tasks/StorageTaskBroker'; -jest.mock('../scaffolder/tasks'); - -const MockStorageTaskBroker: jest.MockedClass = - StorageTaskBroker as any; - const createCatalogClient = (templates: any[] = []) => ({ getEntities: async () => ({ items: templates }), @@ -79,6 +74,7 @@ const mockUrlReader = UrlReaders.default({ describe('createRouter', () => { let app: express.Express; + let taskBroker: TaskBroker; const template: TemplateEntityV1beta2 = { apiVersion: 'backstage.io/v1beta2', kind: 'Template', @@ -109,6 +105,16 @@ describe('createRouter', () => { }; beforeEach(async () => { + const logger = getVoidLogger(); + const databaseTaskStore = await DatabaseTaskStore.create({ + database: await createDatabase().getClient(), + }); + taskBroker = new StorageTaskBroker(databaseTaskStore, logger); + + jest.spyOn(taskBroker, 'dispatch'); + jest.spyOn(taskBroker, 'get'); + jest.spyOn(taskBroker, 'observe'); + const router = await createRouter({ logger: getVoidLogger(), config: new ConfigReader({}), @@ -116,6 +122,7 @@ describe('createRouter', () => { catalogClient: createCatalogClient([template]), containerRunner: new DockerContainerRunner({} as any), reader: mockUrlReader, + taskBroker, }); app = express().use(router); }); @@ -148,7 +155,9 @@ describe('createRouter', () => { }); it('return the template id', async () => { - MockStorageTaskBroker.prototype.dispatch.mockResolvedValue({ + ( + taskBroker.dispatch as jest.Mocked['dispatch'] + ).mockResolvedValue({ taskId: 'a-random-id', }); @@ -168,7 +177,7 @@ describe('createRouter', () => { describe('GET /v2/tasks/:taskId', () => { it('does not divulge secrets', async () => { - MockStorageTaskBroker.prototype.get.mockResolvedValue({ + (taskBroker.get as jest.Mocked['get']).mockResolvedValue({ id: 'a-random-id', spec: {} as any, status: 'completed', @@ -186,37 +195,37 @@ describe('createRouter', () => { describe('GET /v2/tasks/:taskId/eventstream', () => { it('should return log messages', async () => { const unsubscribe = jest.fn(); - MockStorageTaskBroker.prototype.observe.mockImplementation( - ({ taskId }, callback) => { - // emit after this function returned - setImmediate(() => { - callback(undefined, { - events: [ - { - id: 0, - taskId, - type: 'log', - createdAt: '', - body: { message: 'My log message' }, - }, - ], - }); - callback(undefined, { - events: [ - { - id: 1, - taskId, - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ], - }); + ( + taskBroker.observe as jest.Mocked['observe'] + ).mockImplementation(({ taskId }, callback) => { + // emit after this function returned + setImmediate(() => { + callback(undefined, { + events: [ + { + id: 0, + taskId, + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }, + ], }); + callback(undefined, { + events: [ + { + id: 1, + taskId, + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ], + }); + }); - return unsubscribe; - }, - ); + return { unsubscribe }; + }); let statusCode: any = undefined; let headers: any = {}; @@ -255,8 +264,8 @@ data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{ `); - expect(MockStorageTaskBroker.prototype.observe).toBeCalledTimes(1); - expect(MockStorageTaskBroker.prototype.observe).toBeCalledWith( + expect(taskBroker.observe).toBeCalledTimes(1); + expect(taskBroker.observe).toBeCalledWith( { taskId: 'a-random-id' }, expect.any(Function), ); @@ -266,24 +275,24 @@ data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{ it('should return log messages with after query', async () => { const unsubscribe = jest.fn(); - MockStorageTaskBroker.prototype.observe.mockImplementation( - ({ taskId }, callback) => { - setImmediate(() => { - callback(undefined, { - events: [ - { - id: 1, - taskId, - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ], - }); + ( + taskBroker.observe as jest.Mocked['observe'] + ).mockImplementation(({ taskId }, callback) => { + setImmediate(() => { + callback(undefined, { + events: [ + { + id: 1, + taskId, + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ], }); - return unsubscribe; - }, - ); + }); + return { unsubscribe }; + }); let statusCode: any = undefined; let headers: any = {}; @@ -309,8 +318,8 @@ data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{ expect(statusCode).toBe(200); expect(headers['content-type']).toBe('text/event-stream'); - expect(MockStorageTaskBroker.prototype.observe).toBeCalledTimes(1); - expect(MockStorageTaskBroker.prototype.observe).toBeCalledWith( + expect(taskBroker.observe).toBeCalledTimes(1); + expect(taskBroker.observe).toBeCalledWith( { taskId: 'a-random-id', after: 10 }, expect.any(Function), ); @@ -322,29 +331,29 @@ data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{ describe('GET /v2/tasks/:taskId/events', () => { it('should return log messages', async () => { const unsubscribe = jest.fn(); - MockStorageTaskBroker.prototype.observe.mockImplementation( - ({ taskId }, callback) => { - callback(undefined, { - events: [ - { - id: 0, - taskId, - type: 'log', - createdAt: '', - body: { message: 'My log message' }, - }, - { - id: 1, - taskId, - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ], - }); - return unsubscribe; - }, - ); + ( + taskBroker.observe as jest.Mocked['observe'] + ).mockImplementation(({ taskId }, callback) => { + callback(undefined, { + events: [ + { + id: 0, + taskId, + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }, + { + id: 1, + taskId, + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ], + }); + return { unsubscribe }; + }); const response = await request(app).get('/v2/tasks/a-random-id/events'); @@ -366,8 +375,8 @@ data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{ }, ]); - expect(MockStorageTaskBroker.prototype.observe).toBeCalledTimes(1); - expect(MockStorageTaskBroker.prototype.observe).toBeCalledWith( + expect(taskBroker.observe).toBeCalledTimes(1); + expect(taskBroker.observe).toBeCalledWith( { taskId: 'a-random-id' }, expect.any(Function), ); @@ -376,12 +385,12 @@ data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{ it('should return log messages with after query', async () => { const unsubscribe = jest.fn(); - MockStorageTaskBroker.prototype.observe.mockImplementation( - (_, callback) => { - callback(undefined, { events: [] }); - return unsubscribe; - }, - ); + ( + taskBroker.observe as jest.Mocked['observe'] + ).mockImplementation((_, callback) => { + callback(undefined, { events: [] }); + return { unsubscribe }; + }); const response = await request(app) .get('/v2/tasks/a-random-id/events') @@ -390,8 +399,8 @@ data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{ expect(response.status).toEqual(200); expect(response.body).toEqual([]); - expect(MockStorageTaskBroker.prototype.observe).toBeCalledTimes(1); - expect(MockStorageTaskBroker.prototype.observe).toBeCalledWith( + expect(taskBroker.observe).toBeCalledTimes(1); + expect(taskBroker.observe).toBeCalledWith( { taskId: 'a-random-id', after: 10 }, expect.any(Function), ); diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 35bedb3651..2ae30593f2 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -305,7 +305,7 @@ export async function createRouter( }, 30_000); // Get all known events after an id (always includes the completion event) and return the first callback - unsubscribe = taskBroker.observe( + ({ unsubscribe } = taskBroker.observe( { taskId, after }, (error, { events }) => { // stop the timeout @@ -320,7 +320,7 @@ export async function createRouter( res.json(events); }, - ); + )); // When client closes connection we update the clients list // avoiding the disconnected one From e3f665804974a122bed8eb19393a0dad145f9d37 Mon Sep 17 00:00:00 2001 From: Dede Hamzah Date: Wed, 3 Nov 2021 14:38:52 +0700 Subject: [PATCH 055/196] Add overrides ui name for sidebar Signed-off-by: Dede Hamzah --- .../src/layout/Sidebar/Items.tsx | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 883e0395cd..ed089ad6a1 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -350,21 +350,30 @@ export function SidebarSearchField(props: SidebarSearchFieldProps) { ); } -export const SidebarSpace = styled('div')({ - flex: 1, -}); +export const SidebarSpace = styled('div')( + { + flex: 1, + }, + { name: 'BackstageSidebarSpace' }, +); -export const SidebarSpacer = styled('div')({ - height: 8, -}); +export const SidebarSpacer = styled('div')( + { + height: 8, + }, + { name: 'BackstageSidebarSpacer' }, +); -export const SidebarDivider = styled('hr')({ - height: 1, - width: '100%', - background: '#383838', - border: 'none', - margin: '12px 0px', -}); +export const SidebarDivider = styled('hr')( + { + height: 1, + width: '100%', + background: '#383838', + border: 'none', + margin: '12px 0px', + }, + { name: 'BackstageSidebarDivider' }, +); const styledScrollbar = (theme: Theme): CreateCSSProperties => ({ overflowY: 'auto', From a39a2105efc929d7184e6ccfc55d0fa92bd94d46 Mon Sep 17 00:00:00 2001 From: Dede Hamzah Date: Wed, 3 Nov 2021 15:40:25 +0700 Subject: [PATCH 056/196] add changeset Signed-off-by: Dede Hamzah --- .changeset/tall-boxes-sit.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tall-boxes-sit.md diff --git a/.changeset/tall-boxes-sit.md b/.changeset/tall-boxes-sit.md new file mode 100644 index 0000000000..a74e0400bd --- /dev/null +++ b/.changeset/tall-boxes-sit.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Add Theme Overrides for Sidebar From 84ace9a29c03e556c4f0d2be7dad7d6e5b3e22fd Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Wed, 3 Nov 2021 09:41:20 +0000 Subject: [PATCH 057/196] docs: Created stories for BuildTable component to document different component states. Signed-off-by: Marley Powell --- .changeset/tough-buckets-explain.md | 5 + plugins/azure-devops/package.json | 1 + .../BuildTable/BuildTable.stories.tsx | 94 +++++++++++++++++++ .../src/components/BuildTable/BuildTable.tsx | 4 +- 4 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 .changeset/tough-buckets-explain.md create mode 100644 plugins/azure-devops/src/components/BuildTable/BuildTable.stories.tsx diff --git a/.changeset/tough-buckets-explain.md b/.changeset/tough-buckets-explain.md new file mode 100644 index 0000000000..a2ca37d516 --- /dev/null +++ b/.changeset/tough-buckets-explain.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-azure-devops': patch +--- + +Simplified queue time calculation in `BuildTable`. diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index a071205711..1c5e0bba84 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -31,6 +31,7 @@ "@backstage/core-components": "^0.7.2", "@backstage/core-plugin-api": "^0.1.12", "@backstage/errors": "^0.1.4", + "@backstage/plugin-azure-devops-backend": "^0.1.4", "@backstage/plugin-catalog-react": "^0.6.2", "@backstage/theme": "^0.2.12", "@material-ui/core": "^4.12.2", diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.stories.tsx b/plugins/azure-devops/src/components/BuildTable/BuildTable.stories.tsx new file mode 100644 index 0000000000..609466769b --- /dev/null +++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.stories.tsx @@ -0,0 +1,94 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + BuildResult, + BuildStatus, + RepoBuild, +} from '@backstage/plugin-azure-devops-backend'; + +import { BuildTable } from './BuildTable'; +import { MemoryRouter } from 'react-router'; +import React from 'react'; + +export default { + title: 'Plugins/Azure Devops/Build Table', + component: BuildTable, +}; + +const buildStatuses: Array<[BuildStatus, BuildResult]> = [ + [BuildStatus.InProgress, BuildResult.None], // In Progress + [BuildStatus.Completed, BuildResult.Succeeded], // Succeeded + [BuildStatus.Completed, BuildResult.Failed], // Failed + [BuildStatus.Completed, BuildResult.PartiallySucceeded], // Partially Succeeded + [BuildStatus.Completed, BuildResult.Canceled], // Cancelled + [BuildStatus.Completed, BuildResult.None], // Unknown + [BuildStatus.Cancelling, BuildResult.None], // Cancelling + [BuildStatus.Postponed, BuildResult.None], // Postponed + [BuildStatus.NotStarted, BuildResult.None], // Not Started + [BuildStatus.None, BuildResult.None], // Unknown +]; + +const generateTestData = (rows = 10): RepoBuild[] => { + const repoBuilds: RepoBuild[] = []; + + for (let i = 0; i < rows; i++) { + const [status, result] = buildStatuses[i] ?? [ + BuildStatus.Completed, + BuildResult.Succeeded, + ]; + + repoBuilds.push({ + id: rows - i + 12534, + title: `backstage ci - 1.0.0-preview-${rows - i}`, + status, + result, + queueTime: new Date(Date.now() - i * 60000), + source: 'refs/heads/main', + link: '', + }); + } + + return repoBuilds; +}; + +export const Default = () => ( + + + +); + +export const Empty = () => ( + + + +); + +export const Loading = () => ( + + + +); + +export const ErrorMessage = () => ( + + + +); diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx index a308afb81b..f8bb031f0a 100644 --- a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx +++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx @@ -149,9 +149,7 @@ const columns: TableColumn[] = [ field: 'queueTime', width: 'auto', render: (row: Partial) => - DateTime.fromISO( - row.queueTime ? row.queueTime.toString() : new Date().toString(), - ).toRelative(), + DateTime.fromJSDate(row.queueTime ?? new Date()).toRelative(), }, ]; From 58468331751e52832dff5da225b40b5791588172 Mon Sep 17 00:00:00 2001 From: Alex Rybchenko Date: Tue, 2 Nov 2021 09:28:15 +0100 Subject: [PATCH 058/196] used regex instead of slice Signed-off-by: Alex Rybchenko --- .../src/components/TabbedLayout/RoutedTabs.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx b/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx index c5c771b739..5debafd3f8 100644 --- a/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx +++ b/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx @@ -35,8 +35,8 @@ export function useSelectedSubRoute(subRoutes: SubRoute[]): { // TODO: remove once react-router updated const sortedRoutes = routes.sort((a, b) => - // remove added "/*" symbols from path before comparing - b.path.slice(0, -2).localeCompare(a.path.slice(0, -2)), + // remove "/*" symbols from path end before comparing + b.path.replace(/\/\*$/, '').localeCompare(a.path.replace(/\/\*$/, '')), ); const element = useRoutes(sortedRoutes) ?? subRoutes[0].children; From 7240d33df360b4f4ac67f74e803eec71da8561c6 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Wed, 3 Nov 2021 07:30:56 -0500 Subject: [PATCH 059/196] Also fixing for Google Storage Signed-off-by: Andre Wanlin --- .../techdocs-common/src/stages/publish/googleStorage.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/techdocs-common/src/stages/publish/googleStorage.ts b/packages/techdocs-common/src/stages/publish/googleStorage.ts index 5a142e800a..77c366315c 100644 --- a/packages/techdocs-common/src/stages/publish/googleStorage.ts +++ b/packages/techdocs-common/src/stages/publish/googleStorage.ts @@ -231,7 +231,7 @@ export class GoogleGCSPublish implements PublisherBase { ? entityTriplet : lowerCaseEntityTriplet(entityTriplet); - const entityRootDir = path.join(this.bucketRootPath, entityDir); + const entityRootDir = path.posix.join(this.bucketRootPath, entityDir); const fileStreamChunks: Array = []; this.storageClient @@ -270,7 +270,7 @@ export class GoogleGCSPublish implements PublisherBase { : lowerCaseEntityTripletInStoragePath(decodedUriNoRoot); // Re-prepend the root path to the relative file path - const filePath = path.join(this.bucketRootPath, filePathNoRoot); + const filePath = path.posix.join(this.bucketRootPath, filePathNoRoot); // Files with different extensions (CSS, HTML) need to be served with different headers const fileExtension = path.extname(filePath); @@ -310,7 +310,7 @@ export class GoogleGCSPublish implements PublisherBase { ? entityTriplet : lowerCaseEntityTriplet(entityTriplet); - const entityRootDir = path.join(this.bucketRootPath, entityDir); + const entityRootDir = path.posix.join(this.bucketRootPath, entityDir); this.storageClient .bucket(this.bucketName) From 7d95e98682ded152061973a1a336048f4ce97977 Mon Sep 17 00:00:00 2001 From: Tim Jacomb <21194782+timja@users.noreply.github.com> Date: Wed, 3 Nov 2021 14:33:43 +0000 Subject: [PATCH 060/196] Add yarn command for installing jenkins-backend plugin Signed-off-by: Tim Jacomb --- plugins/jenkins-backend/README.md | 6 ++++++ plugins/jenkins/README.md | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/jenkins-backend/README.md b/plugins/jenkins-backend/README.md index bac56b6eaf..56a2a7faab 100644 --- a/plugins/jenkins-backend/README.md +++ b/plugins/jenkins-backend/README.md @@ -12,6 +12,12 @@ This is the backend half of the 2 Jenkins plugins and is responsible for: This plugin needs to be added to an existing backstage instance. +```bash +# From your Backstage root directory +cd packages/backend +yarn add @backstage/plugin-jenkins-backend +``` + Typically, this means creating a `src/plugins/jenkins.ts` file and adding a reference to it to `src/index.ts` ### jenkins.ts diff --git a/plugins/jenkins/README.md b/plugins/jenkins/README.md index 1af511b16f..2012555d93 100644 --- a/plugins/jenkins/README.md +++ b/plugins/jenkins/README.md @@ -16,7 +16,7 @@ cd packages/app yarn add @backstage/plugin-jenkins ``` -2. Add and configure the backend plugin according to it's instructions +2. Add and configure the [jenkins-backend](../jenkins-backend) plugin according to it's instructions 3. Add the `EntityJenkinsContent` extension to the `CI/CD` page and `EntityLatestJenkinsRunCard` to the `overview` page in the app (or wherever you'd prefer): From bc4cc0d2bf73e9cf869d00604a13f18c754a6e34 Mon Sep 17 00:00:00 2001 From: Tim Jacomb Date: Wed, 3 Nov 2021 14:56:18 +0000 Subject: [PATCH 061/196] Add index.ts changes Signed-off-by: Tim Jacomb --- plugins/jenkins-backend/README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/plugins/jenkins-backend/README.md b/plugins/jenkins-backend/README.md index 56a2a7faab..1413a45136 100644 --- a/plugins/jenkins-backend/README.md +++ b/plugins/jenkins-backend/README.md @@ -48,6 +48,29 @@ export default async function createPlugin({ } ``` +### src/index.ts + +```diff +diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts +index f2b14b2..2c64f47 100644 +--- a/packages/backend/src/index.ts ++++ b/packages/backend/src/index.ts +@@ -22,6 +22,7 @@ import { Config } from '@backstage/config'; + import app from './plugins/app'; ++import jenkins from './plugins/jenkins'; + import scaffolder from './plugins/scaffolder'; +@@ -56,6 +57,7 @@ async function main() { + const authEnv = useHotMemoize(module, () => createEnv('auth')); ++ const jenkinsEnv = useHotMemoize(module, () => createEnv('jenkins')); + const proxyEnv = useHotMemoize(module, () => createEnv('proxy')); +@@ -63,6 +65,7 @@ async function main() { + + const apiRouter = Router(); + apiRouter.use('/catalog', await catalog(catalogEnv)); ++ apiRouter.use('/jenkins', await jenkins(jenkinsEnv)); + apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv)); +``` + This plugin must be provided with a JenkinsInfoProvider, this is a strategy object for finding the Jenkins instance and job for an entity. There is a standard one provided, but the Integrator is free to build their own. From 36350bf8b37f5d2cd8457423cdf1429d7b9015cb Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Wed, 3 Nov 2021 15:45:34 +0100 Subject: [PATCH 062/196] Pin version of ElasticSearch client to 7.13.0 Signed-off-by: Jussi Hallila --- .changeset/late-walls-cry.md | 5 +++++ plugins/search-backend-module-elasticsearch/package.json | 2 +- yarn.lock | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/late-walls-cry.md diff --git a/.changeset/late-walls-cry.md b/.changeset/late-walls-cry.md new file mode 100644 index 0000000000..c9fcb3112a --- /dev/null +++ b/.changeset/late-walls-cry.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend-module-elasticsearch': patch +--- + +Pinning version of elastic search client to 7.13.0 to prevent breaking change towards third party ElasticSearch clusters on 7.14.0. diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index 87688e455f..2d5cabf725 100644 --- a/plugins/search-backend-module-elasticsearch/package.json +++ b/plugins/search-backend-module-elasticsearch/package.json @@ -22,7 +22,7 @@ "dependencies": { "@backstage/config": "^0.1.8", "@backstage/search-common": "^0.2.0", - "@elastic/elasticsearch": "^7.13.0", + "@elastic/elasticsearch": "7.13.0", "@acuris/aws-es-connection": "^2.2.0", "aws-sdk": "^2.948.0", "elastic-builder": "^2.16.0", diff --git a/yarn.lock b/yarn.lock index 3fec636e32..4faccd29a0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2717,7 +2717,7 @@ find-my-way "^2.2.2" into-stream "^5.1.1" -"@elastic/elasticsearch@^7.13.0": +"@elastic/elasticsearch@7.13.0": version "7.13.0" resolved "https://registry.npmjs.org/@elastic/elasticsearch/-/elasticsearch-7.13.0.tgz#6dcf511dfa91187e22c81e54f41f4bd0fd96b4d6" integrity sha512-WgwLWo2p9P2tdqzBGX9fHeG8p5IOTXprXNTECQG2mJ7z9n93N5AFBJpEw4d35tWWeCWi9jI13A2wzQZH7XZ/xw== From ade04904d5f66278e85095692b32baf8b03b4a38 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Thu, 4 Nov 2021 13:37:34 +0530 Subject: [PATCH 063/196] fix api reports Signed-off-by: Himanshu Mishra --- packages/theme/api-report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/theme/api-report.md b/packages/theme/api-report.md index 30dcb7b040..2dc6ab7b55 100644 --- a/packages/theme/api-report.md +++ b/packages/theme/api-report.md @@ -64,7 +64,7 @@ export type BackstagePaletteAdditions = { error: string; text: string; link: string; - warning: string; + warning?: string; }; }; From bd93a7811b8b5511488f50a0d782005366f055b1 Mon Sep 17 00:00:00 2001 From: Lykke Axlin Date: Thu, 4 Nov 2021 16:48:30 +0100 Subject: [PATCH 064/196] changed @date-io/luxon from 2.x to 1.0 to be compatible with material-ui-pickers Signed-off-by: Lykke Axlin --- plugins/ilert/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ilert/package.json b/plugins/ilert/package.json index f33e34beed..f93a5b4870 100644 --- a/plugins/ilert/package.json +++ b/plugins/ilert/package.json @@ -27,7 +27,7 @@ "@backstage/errors": "^0.1.3", "@backstage/plugin-catalog-react": "^0.6.1", "@backstage/theme": "^0.2.12", - "@date-io/luxon": "2.x", + "@date-io/luxon": "1.x", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", From fa325fd89dcbc40f610a70e946107835df073b15 Mon Sep 17 00:00:00 2001 From: Lykke Axlin Date: Thu, 4 Nov 2021 17:06:08 +0100 Subject: [PATCH 065/196] added changeset Signed-off-by: Lykke Axlin --- .changeset/light-knives-camp.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/light-knives-camp.md diff --git a/.changeset/light-knives-camp.md b/.changeset/light-knives-camp.md new file mode 100644 index 0000000000..a68f3ebb9a --- /dev/null +++ b/.changeset/light-knives-camp.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-ilert': patch +--- + +Change the version of @date-io/luxon from 2.x to 1.x to make it compatible with material-ui-pickers From ab27aa313c98987d0d66dd63e0984f1ffebcb149 Mon Sep 17 00:00:00 2001 From: Jeremy Guarini Date: Thu, 4 Nov 2021 09:52:59 -0700 Subject: [PATCH 066/196] Minor UI updates to make numbers and dates a bit more human friendly Signed-off-by: Jeremy Guarini --- .../CoverageHistoryChart/CoverageHistoryChart.tsx | 9 +++++++-- .../src/components/FileExplorer/FileExplorer.tsx | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/plugins/code-coverage/src/components/CoverageHistoryChart/CoverageHistoryChart.tsx b/plugins/code-coverage/src/components/CoverageHistoryChart/CoverageHistoryChart.tsx index 61dc7d7158..a251bb1a68 100644 --- a/plugins/code-coverage/src/components/CoverageHistoryChart/CoverageHistoryChart.tsx +++ b/plugins/code-coverage/src/components/CoverageHistoryChart/CoverageHistoryChart.tsx @@ -69,6 +69,11 @@ const getTrendIcon = (trend: number, classes: ClassNameMap) => { } }; +// convert timestamp to human friendly form +function formatDateToHuman(timeStamp: string | number) { + return new Date(timeStamp).toUTCString(); +} + export const CoverageHistoryChart = () => { const { entity } = useEntity(); const codeCoverageApi = useApi(codeCoverageApiRef); @@ -149,10 +154,10 @@ export const CoverageHistoryChart = () => { margin={{ right: 48, top: 32 }} > - + - + { title: 'Coverage', type: 'numeric', field: 'coverage', - render: (row: CoverageTableRow) => `${row.coverage}%`, + render: (row: CoverageTableRow) => `${row.coverage.toFixed(2)}%`, }, { title: 'Missing lines', From a5512851a0991ec27f3eea530bc223a99013f1ed Mon Sep 17 00:00:00 2001 From: Jeremy Guarini Date: Thu, 4 Nov 2021 10:02:11 -0700 Subject: [PATCH 067/196] add changeset Signed-off-by: Jeremy Guarini --- .changeset/neat-pugs-wait.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/neat-pugs-wait.md diff --git a/.changeset/neat-pugs-wait.md b/.changeset/neat-pugs-wait.md new file mode 100644 index 0000000000..8902f44c9d --- /dev/null +++ b/.changeset/neat-pugs-wait.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-code-coverage': patch +--- + +Change represented test date from epoch to something more human friendly. Round test coverage to 2 decimal places. From 779d7a230442fe1abc70f2359c02f3d1902767ee Mon Sep 17 00:00:00 2001 From: Tim Jacomb Date: Thu, 4 Nov 2021 17:07:18 +0000 Subject: [PATCH 068/196] Tweak logic for msgraph catalog ingesting for display names with security groups Signed-off-by: Tim Jacomb --- .changeset/early-trains-hammer.md | 7 ++ .../src/microsoftGraph/read.test.ts | 86 +++++++++++++++++++ .../src/microsoftGraph/read.ts | 9 +- 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 .changeset/early-trains-hammer.md diff --git a/.changeset/early-trains-hammer.md b/.changeset/early-trains-hammer.md new file mode 100644 index 0000000000..853788e015 --- /dev/null +++ b/.changeset/early-trains-hammer.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-catalog-backend-module-msgraph': patch +--- + +Tweak logic for msgraph catalog ingesting for display names with security groups + +Previously security groups that weren't mail enabled were imported with UUIDs, now they use the display name. diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts index fa86d77b27..b0273e1f58 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts @@ -445,6 +445,92 @@ describe('read microsoft graph', () => { // expect(client.getGroupPhotoWithSizeLimit).toBeCalledTimes(1); // expect(client.getGroupPhotoWithSizeLimit).toBeCalledWith('groupid', 120); }); + + it('should read security groups', async () => { + async function* getExampleGroups() { + yield { + id: 'groupid', + displayName: 'Group Name', + description: 'Group Description', + mail: 'group@example.com', + mailNickname: 'df546d53-4f5f-4462-b371-d4a855787047', + mailEnabled: false, + securityEnabled: true, + }; + } + + async function* getExampleGroupMembers(): AsyncIterable { + yield { + '@odata.type': '#microsoft.graph.group', + id: 'childgroupid', + }; + yield { + '@odata.type': '#microsoft.graph.user', + id: 'userid', + }; + } + + client.getGroups.mockImplementation(getExampleGroups); + client.getGroupMembers.mockImplementation(getExampleGroupMembers); + client.getOrganization.mockResolvedValue({ + id: 'tenantid', + displayName: 'Organization Name', + }); + client.getGroupPhotoWithSizeLimit.mockResolvedValue( + 'data:image/jpeg;base64,...', + ); + + const { groups, rootGroup } = await readMicrosoftGraphGroups( + client, + 'tenantid', + { + groupFilter: 'securityEnabled eq true', + }, + ); + + const expectedRootGroup = group({ + metadata: { + annotations: { + 'graph.microsoft.com/tenant-id': 'tenantid', + }, + name: 'organization_name', + description: 'Organization Name', + }, + spec: { + type: 'root', + profile: { + displayName: 'Organization Name', + }, + children: [], + }, + }); + expect(groups).toEqual([ + expectedRootGroup, + group({ + metadata: { + annotations: { + 'graph.microsoft.com/group-id': 'groupid', + }, + name: 'group_name', + description: 'Group Description', + }, + spec: { + type: 'team', + profile: { + displayName: 'Group Name', + email: 'group@example.com', + }, + children: [], + }, + }), + ]); + expect(rootGroup).toEqual(expectedRootGroup); + expect(client.getGroups).toBeCalledWith({ + filter: 'securityEnabled eq true', + }); + expect(client.getGroupMembers).toBeCalledTimes(1); + expect(client.getGroupMembers).toBeCalledWith('groupid'); + }); }); describe('resolveRelations', () => { diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts index fa229ca956..77bea268ee 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts @@ -251,6 +251,13 @@ export async function readMicrosoftGraphOrganization( return { rootGroup }; } +function extractGroupName(group: MicrosoftGraph.Group): string { + if (group.securityEnabled && !group.mailEnabled) { + return group.displayName as string; + } + return (group.mailNickname || group.displayName) as string; +} + export async function defaultGroupTransformer( group: MicrosoftGraph.Group, groupPhoto?: string, @@ -259,7 +266,7 @@ export async function defaultGroupTransformer( return undefined; } - const name = normalizeEntityName(group.mailNickname || group.displayName); + const name = normalizeEntityName(extractGroupName(group)); const entity: GroupEntity = { apiVersion: 'backstage.io/v1alpha1', kind: 'Group', From 98b8ef555fbeb8d41141360307b21ef7546d6092 Mon Sep 17 00:00:00 2001 From: Matto Date: Fri, 5 Nov 2021 11:03:35 +1100 Subject: [PATCH 069/196] Re-ran prettier on all files Signed-off-by: Matto --- ADOPTERS.md | 128 ++++++++++++++++++++++++++-------------------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/ADOPTERS.md b/ADOPTERS.md index a2015086c3..797d178270 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -1,64 +1,64 @@ -| Organization | Contact | Description of Use | -| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [Spotify](https://www.spotify.com) | [@leemills83](https://github.com/leemills83) | Main interface towards all of Spotify's infrastructure and technical documentation. | -| [bol.com](https://www.bol.com) | [@sagacity](https://github.com/sagacity) | Initial work being done to unify platform tooling. | -| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. | -| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up | -| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. | -| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. | -| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. | -| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications | -| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. | -| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | -| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D | -| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling | -| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling | -| [Telenor Sweden](https://www.telenor.se) | [@O5ten](https://github.com/O5ten) | Building a developer portal for scaffolding projects towards our unified build environment and microservice stacks | -| [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. | -| [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. | -| [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. | -| [Expedia Group](https://www.expediagroup.com) | [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo), [Erik Lindgren](https://github.com/lindgren) | EG Common Developer Toolkit | -| [Paddle.com](https://paddle.com) | [Ioannis Georgoulas](https://github.com/geototti21) | Developer portal (Tech Docs, Service Catalog, Internal Tooling), we use vanilla Backstage FE and custom BE implementation in Go | -| [Acast.com](https://acast.com) | [Olle Lundberg](https://github.com/lndbrg) | Developer portal with tech docs, service catalog and a bunch of other internal tooling | -| [Lunar](https://lunar.app) | [Jacob Valdemar](https://github.com/JacobValdemar) | Internal developer portal for service overview and insights, API documentation, technical guides, onboarding guides and RFC's. | -| [Trendyol](https://trendyol.com) | [Gamze Senturk](https://github.com/gmzsenturk), [Mert Can Bilgic](https://github.com/mertcb) | The Developer Portal has been called `Pandora`. Provides an overview of Trendyol tech ecosystem. TechDocs, Catalog, Custom Plugins and Theme. | -| [Peloton](https://www.onepeloton.com/) | [Jim Haughwout](https://github.com/JimHaughwout) | Creating our first developer portal and tech-docs. Exploring Service Catalog, Tech Insights and Cost Insights as well. | -| [TELUS](https://telus.com) | [Seb Barre](https://github.com/sbarre) | The Go-to place to find answers about development and delivery at TELUS. | -| [Brex](https://www.brex.com/) | [Vamsi Chitters](https://github.com/vamsikc) | A centralized UI to understand how a service fits in the whole Brex architecture and manage a team’s engineering dependencies. | -| [Oriflame](https://www.oriflame.com/) | [Oriflame](https://github.com/oriflame) | Internal developer portal for services, single page apps and packages overview, API documentation, technical guides, tech-radar and more. | -| [Booz Allen Hamilton](https://www.boozallen.com/) | [Jason Miller](https://github.com/JasonMiller-BAH) | Developer portal for a full-stack software development ecosystem that accelerates consistent and repeatable Modern Software Development practices for internal innovation and investments. | -| [Netflix](https://www.netflix.com/) | [bleathem](https://github.com/bleathem) | Our Backstage implementation will be the front door to a unified experience connecting our internal platform products across important workflows with integrated knowledge and support. | -| [b.well](https://www.icanbwell.com/) | [Jacob Rosales](https://github.com/jrosales) | Foundation for our engineering portal and cloud insights. | -| [PagerDuty](https://www.pagerduty.com/) | [Mark Shaw](https://github.com/markshawtoronto) | Developer portal, initially focused on software templates and tech-docs. | -| [MoonShiner](https://moonshiner.at) | [Fabian Hippmann](https://github.com/FabianHippmann) | Developer portal - helps us keep track of our customer projects, onboard new developers & improve our development process 🌕🚀🧑‍🚀 | -| [FundApps](https://www.fundapps.co/) | [Elliot Greenwood](https://github.com/egnwd) | Developer Portal - A place for us to keep track of our projects and documentation for all services and processes | -| [DAZN](https://dazn.com/) | [Lou Bichard](https://twitter.com/loujaybee), [Marco Crivellaro](https://github.com/crivetechie), [Alex Hollerith](mailto:alex.hollerith@dazn.com) | Ingesting all of DAZN's repos for the catalog, migrating our internal platform apps (pull request boards, release information, inner source marketplace etc) to Backstage plugins (where applicable). | -| [HelloFresh](https://www.hellofresh.de/) | [@iammuho](https://github.com/iammuho), [@ElenaForester](https://github.com/ElenaForester), [@diegomarangoni](https://github.com/diegomarangoni) | Our developer portal at HelloFresh - Spread across an organisation of 500+ engineers globally. | -| [FactSet](https://www.factset.com/) | [@kuangp](https://github.com/kuangp) | Developer portal to provide discoverability to all internal components, APIs, documentation, and scaffold templates with integrations to our internal infrastructure tools. | -| [Workrise](https://www.workrise.com/) | [Michael Rode](https://github.com/michaelrode) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | -| [RedVentures](https://www.redventures.com/) | [Chris Diaz](https://github.com/codingdiaz) | Developer portal that brings everything an engineer needs to provide value into a single pane of glass. | -| [MavTek](https://www.mavtek.com/) | [@fgascon](https://github.com/fgascon) | Developer portal focused on standardizing practices, centralizing documentation and streamlining developer practices. | -| [QuintoAndar](https://www.quintoandar.com.br/) | [@quintoandar](https://github.com/quintoandar) | Developer portal, services catalog and centralization of service metrics. | -| [empathy.co](https://empathy.co/) | [@guillermotti](https://github.com/guillermotti) | Developer portal for tech docs, service catalog, plugin discovery and much more. | -| [creditas.com](https://creditas.com/) | [@aureliosaraiva](https://github.com/aureliosaraiva) [@Creditas](https://github.com/creditas) | Centralization of all services, standards, documentation, etc. We started the deployment process. | -| [Prisjakt](https://www.prisjakt.nu) / [PriceSpy](https://pricespy.co.uk) | [@kennylindahl](https://github.com/kennylindahl) | Internal developer portal - Documentation, scaffolding, software catalog, TechRadar, Gitlab org data integration | -| [Powerspike](https://powerspike.tv/) | [@trelore](https://github.com/trelore) | Developer portal for documentation of core libraries and repositories. | -| [2U](https://2u.com) | [Andrew Thal](https://github.com/athal7) | Development team home-base, promoting service discoverability, resource dependencies, and tech radar | -| [Taxfix](https://taxfix.de/) | [Sami Ur Rehman](https://github.com/samiurrehman92) | Developer's portal with software catalog at it's core. Hosts API Specs, Tech Docs, Tech Radar and some custom plugins. | -| [Busuu](https://busuu.com/) | [Adam Tester](https://github.com/adamtester) | Developer portal with service catalog, API docs, Event docs, service templating, and cost insights. | -| [Loadsmart](https://loadsmart.com/) | [Loadsmart](https://github.com/loadsmart) | Improve services visibility and operations for service owners and developers. | -| [Monzo](https://monzo.com/) | [@WillSewell](https://github.com/WillSewell), [@joechrisellis](https://github.com/joechrisellis) | Developer portal showing metadata and docs for over 2000 microservices. We have built a number of plugins such as a UI for our system to measure [software excellence](https://monzo.com/blog/2021/09/15/how-we-measure-software-excellence), and a UI to show deployment and config change events. | -| [Vaimo](https://www.vaimo.com) | [@vaimo-magnus](https://github.com/vaimo-magnus) | Developer Portal for our developers at Vaimo, currently docs and self-service towards our internal PaaS based on k8s. Plans to extend the catalog into Projects, Environments etc | -| [Wayfair](https://www.wayfair.com) | [@fransan](https://github.com/fransan), [@errskipower](https://github.com/errskipower), [@hrrs](https://github.com/hrrs) | Developer portal for service catalog, technical documentation, and APIs. | -| [CircleHD](https://www.circlehd.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | -| [CastDesk](https://castdesk.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | -| [Santagostino](https://santagostino.it) | [@santagostino](https://github.com/santagostino) | Developer portal, gateway to our infrastructure, documentation, service catalog and internal tooling. | -| [Peak](https://peak.ai) | [Luke Beamish](https://github.com/lukebeamish-peak) | Developer portal for all internal engineers to access documentation and tooling. | -| [Gelato](https://gelato.com/) | [Dmitry Makarenko](https://github.com/dmitry-makarenko-gelato) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal and third-party systems🚀. | -| [GoCardless](https://gocardless.com/) | [James Turley](https://github.com/tragiclifestories) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal systems. | -| [Box](https://www.box.com) | [@kielosz](https://github.com/kielosz), [@jluk-box](https://github.com/jluk-box), [@ptychu](https://github.com/ptychu), [@alexrybch](https://github.com/alexrybch), [@szubster](https://github.com/szubster) | Developer portal for service catalog, integration with internal systems, new service onboarding. | -| [Bazaarvoice](https://www.bazaarvoice.com) | [@niallmccullagh](https://github.com/niallmccullagh) | Developer portal for service catalog and scaffolds, publishing Github docs and API documentation, visualising our internal tech radar and our product engineering org structure. | -| [Krateo PlatformOps](https://www.krateo.io) | [@projectkerberus](https://github.com/projectkerberus) | A multi-cloud control plane to create, manage and deploy any kind of resource easily and centrally via a Developer Portal that centralizes via a self-service catalog the templating and ownership of services, the available documentation, the overview of the components that compose an entire domain and all the data of the service lifecycle. | -| [Adevinta](https://www.adevinta.com) | [Ray Sinnema](https://github.com/RemonSinnema) | Showcase shared services to our internal customers. | -| [Splunk](https://www.splunk.com) | [@tonytamsf](https://github.com/tonytamsf) | Developer portal as a centralized place to find people, services, documentation, escalation policies and give bravos. This portal is also being used as a centralized search engine for engineering specific documentation.| -| [SoundCloud](https://www.soundcloud.com) | [Julio Zynger](https://github.com/julioz) | Developer portal as a [humane registry](https://martinfowler.com/bliki/HumaneRegistry.html) for the organization: catalog of people, services, documentation, feature toggles, escalation policies, etc.| +| Organization | Contact | Description of Use | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [Spotify](https://www.spotify.com) | [@leemills83](https://github.com/leemills83) | Main interface towards all of Spotify's infrastructure and technical documentation. | +| [bol.com](https://www.bol.com) | [@sagacity](https://github.com/sagacity) | Initial work being done to unify platform tooling. | +| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. | +| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up | +| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. | +| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. | +| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. | +| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications | +| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. | +| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | +| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D | +| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling | +| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling | +| [Telenor Sweden](https://www.telenor.se) | [@O5ten](https://github.com/O5ten) | Building a developer portal for scaffolding projects towards our unified build environment and microservice stacks | +| [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. | +| [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. | +| [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. | +| [Expedia Group](https://www.expediagroup.com) | [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo), [Erik Lindgren](https://github.com/lindgren) | EG Common Developer Toolkit | +| [Paddle.com](https://paddle.com) | [Ioannis Georgoulas](https://github.com/geototti21) | Developer portal (Tech Docs, Service Catalog, Internal Tooling), we use vanilla Backstage FE and custom BE implementation in Go | +| [Acast.com](https://acast.com) | [Olle Lundberg](https://github.com/lndbrg) | Developer portal with tech docs, service catalog and a bunch of other internal tooling | +| [Lunar](https://lunar.app) | [Jacob Valdemar](https://github.com/JacobValdemar) | Internal developer portal for service overview and insights, API documentation, technical guides, onboarding guides and RFC's. | +| [Trendyol](https://trendyol.com) | [Gamze Senturk](https://github.com/gmzsenturk), [Mert Can Bilgic](https://github.com/mertcb) | The Developer Portal has been called `Pandora`. Provides an overview of Trendyol tech ecosystem. TechDocs, Catalog, Custom Plugins and Theme. | +| [Peloton](https://www.onepeloton.com/) | [Jim Haughwout](https://github.com/JimHaughwout) | Creating our first developer portal and tech-docs. Exploring Service Catalog, Tech Insights and Cost Insights as well. | +| [TELUS](https://telus.com) | [Seb Barre](https://github.com/sbarre) | The Go-to place to find answers about development and delivery at TELUS. | +| [Brex](https://www.brex.com/) | [Vamsi Chitters](https://github.com/vamsikc) | A centralized UI to understand how a service fits in the whole Brex architecture and manage a team’s engineering dependencies. | +| [Oriflame](https://www.oriflame.com/) | [Oriflame](https://github.com/oriflame) | Internal developer portal for services, single page apps and packages overview, API documentation, technical guides, tech-radar and more. | +| [Booz Allen Hamilton](https://www.boozallen.com/) | [Jason Miller](https://github.com/JasonMiller-BAH) | Developer portal for a full-stack software development ecosystem that accelerates consistent and repeatable Modern Software Development practices for internal innovation and investments. | +| [Netflix](https://www.netflix.com/) | [bleathem](https://github.com/bleathem) | Our Backstage implementation will be the front door to a unified experience connecting our internal platform products across important workflows with integrated knowledge and support. | +| [b.well](https://www.icanbwell.com/) | [Jacob Rosales](https://github.com/jrosales) | Foundation for our engineering portal and cloud insights. | +| [PagerDuty](https://www.pagerduty.com/) | [Mark Shaw](https://github.com/markshawtoronto) | Developer portal, initially focused on software templates and tech-docs. | +| [MoonShiner](https://moonshiner.at) | [Fabian Hippmann](https://github.com/FabianHippmann) | Developer portal - helps us keep track of our customer projects, onboard new developers & improve our development process 🌕🚀🧑‍🚀 | +| [FundApps](https://www.fundapps.co/) | [Elliot Greenwood](https://github.com/egnwd) | Developer Portal - A place for us to keep track of our projects and documentation for all services and processes | +| [DAZN](https://dazn.com/) | [Lou Bichard](https://twitter.com/loujaybee), [Marco Crivellaro](https://github.com/crivetechie), [Alex Hollerith](mailto:alex.hollerith@dazn.com) | Ingesting all of DAZN's repos for the catalog, migrating our internal platform apps (pull request boards, release information, inner source marketplace etc) to Backstage plugins (where applicable). | +| [HelloFresh](https://www.hellofresh.de/) | [@iammuho](https://github.com/iammuho), [@ElenaForester](https://github.com/ElenaForester), [@diegomarangoni](https://github.com/diegomarangoni) | Our developer portal at HelloFresh - Spread across an organisation of 500+ engineers globally. | +| [FactSet](https://www.factset.com/) | [@kuangp](https://github.com/kuangp) | Developer portal to provide discoverability to all internal components, APIs, documentation, and scaffold templates with integrations to our internal infrastructure tools. | +| [Workrise](https://www.workrise.com/) | [Michael Rode](https://github.com/michaelrode) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | +| [RedVentures](https://www.redventures.com/) | [Chris Diaz](https://github.com/codingdiaz) | Developer portal that brings everything an engineer needs to provide value into a single pane of glass. | +| [MavTek](https://www.mavtek.com/) | [@fgascon](https://github.com/fgascon) | Developer portal focused on standardizing practices, centralizing documentation and streamlining developer practices. | +| [QuintoAndar](https://www.quintoandar.com.br/) | [@quintoandar](https://github.com/quintoandar) | Developer portal, services catalog and centralization of service metrics. | +| [empathy.co](https://empathy.co/) | [@guillermotti](https://github.com/guillermotti) | Developer portal for tech docs, service catalog, plugin discovery and much more. | +| [creditas.com](https://creditas.com/) | [@aureliosaraiva](https://github.com/aureliosaraiva) [@Creditas](https://github.com/creditas) | Centralization of all services, standards, documentation, etc. We started the deployment process. | +| [Prisjakt](https://www.prisjakt.nu) / [PriceSpy](https://pricespy.co.uk) | [@kennylindahl](https://github.com/kennylindahl) | Internal developer portal - Documentation, scaffolding, software catalog, TechRadar, Gitlab org data integration | +| [Powerspike](https://powerspike.tv/) | [@trelore](https://github.com/trelore) | Developer portal for documentation of core libraries and repositories. | +| [2U](https://2u.com) | [Andrew Thal](https://github.com/athal7) | Development team home-base, promoting service discoverability, resource dependencies, and tech radar | +| [Taxfix](https://taxfix.de/) | [Sami Ur Rehman](https://github.com/samiurrehman92) | Developer's portal with software catalog at it's core. Hosts API Specs, Tech Docs, Tech Radar and some custom plugins. | +| [Busuu](https://busuu.com/) | [Adam Tester](https://github.com/adamtester) | Developer portal with service catalog, API docs, Event docs, service templating, and cost insights. | +| [Loadsmart](https://loadsmart.com/) | [Loadsmart](https://github.com/loadsmart) | Improve services visibility and operations for service owners and developers. | +| [Monzo](https://monzo.com/) | [@WillSewell](https://github.com/WillSewell), [@joechrisellis](https://github.com/joechrisellis) | Developer portal showing metadata and docs for over 2000 microservices. We have built a number of plugins such as a UI for our system to measure [software excellence](https://monzo.com/blog/2021/09/15/how-we-measure-software-excellence), and a UI to show deployment and config change events. | +| [Vaimo](https://www.vaimo.com) | [@vaimo-magnus](https://github.com/vaimo-magnus) | Developer Portal for our developers at Vaimo, currently docs and self-service towards our internal PaaS based on k8s. Plans to extend the catalog into Projects, Environments etc | +| [Wayfair](https://www.wayfair.com) | [@fransan](https://github.com/fransan), [@errskipower](https://github.com/errskipower), [@hrrs](https://github.com/hrrs) | Developer portal for service catalog, technical documentation, and APIs. | +| [CircleHD](https://www.circlehd.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | +| [CastDesk](https://castdesk.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | +| [Santagostino](https://santagostino.it) | [@santagostino](https://github.com/santagostino) | Developer portal, gateway to our infrastructure, documentation, service catalog and internal tooling. | +| [Peak](https://peak.ai) | [Luke Beamish](https://github.com/lukebeamish-peak) | Developer portal for all internal engineers to access documentation and tooling. | +| [Gelato](https://gelato.com/) | [Dmitry Makarenko](https://github.com/dmitry-makarenko-gelato) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal and third-party systems🚀. | +| [GoCardless](https://gocardless.com/) | [James Turley](https://github.com/tragiclifestories) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal systems. | +| [Box](https://www.box.com) | [@kielosz](https://github.com/kielosz), [@jluk-box](https://github.com/jluk-box), [@ptychu](https://github.com/ptychu), [@alexrybch](https://github.com/alexrybch), [@szubster](https://github.com/szubster) | Developer portal for service catalog, integration with internal systems, new service onboarding. | +| [Bazaarvoice](https://www.bazaarvoice.com) | [@niallmccullagh](https://github.com/niallmccullagh) | Developer portal for service catalog and scaffolds, publishing Github docs and API documentation, visualising our internal tech radar and our product engineering org structure. | +| [Krateo PlatformOps](https://www.krateo.io) | [@projectkerberus](https://github.com/projectkerberus) | A multi-cloud control plane to create, manage and deploy any kind of resource easily and centrally via a Developer Portal that centralizes via a self-service catalog the templating and ownership of services, the available documentation, the overview of the components that compose an entire domain and all the data of the service lifecycle. | +| [Adevinta](https://www.adevinta.com) | [Ray Sinnema](https://github.com/RemonSinnema) | Showcase shared services to our internal customers. | +| [Splunk](https://www.splunk.com) | [@tonytamsf](https://github.com/tonytamsf) | Developer portal as a centralized place to find people, services, documentation, escalation policies and give bravos. This portal is also being used as a centralized search engine for engineering specific documentation. | +| [SoundCloud](https://www.soundcloud.com) | [Julio Zynger](https://github.com/julioz) | Developer portal as a [humane registry](https://martinfowler.com/bliki/HumaneRegistry.html) for the organization: catalog of people, services, documentation, feature toggles, escalation policies, etc. | From e37a84085624703018b48746fad5337883317a97 Mon Sep 17 00:00:00 2001 From: Matto Date: Fri, 5 Nov 2021 11:17:01 +1100 Subject: [PATCH 070/196] Re-ran prettier on all files Signed-off-by: Matto --- ADOPTERS.md | 128 +++++++++++++------------- packages/backend-common/src/config.ts | 2 +- 2 files changed, 65 insertions(+), 65 deletions(-) diff --git a/ADOPTERS.md b/ADOPTERS.md index a2015086c3..797d178270 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -1,64 +1,64 @@ -| Organization | Contact | Description of Use | -| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [Spotify](https://www.spotify.com) | [@leemills83](https://github.com/leemills83) | Main interface towards all of Spotify's infrastructure and technical documentation. | -| [bol.com](https://www.bol.com) | [@sagacity](https://github.com/sagacity) | Initial work being done to unify platform tooling. | -| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. | -| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up | -| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. | -| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. | -| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. | -| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications | -| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. | -| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | -| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D | -| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling | -| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling | -| [Telenor Sweden](https://www.telenor.se) | [@O5ten](https://github.com/O5ten) | Building a developer portal for scaffolding projects towards our unified build environment and microservice stacks | -| [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. | -| [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. | -| [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. | -| [Expedia Group](https://www.expediagroup.com) | [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo), [Erik Lindgren](https://github.com/lindgren) | EG Common Developer Toolkit | -| [Paddle.com](https://paddle.com) | [Ioannis Georgoulas](https://github.com/geototti21) | Developer portal (Tech Docs, Service Catalog, Internal Tooling), we use vanilla Backstage FE and custom BE implementation in Go | -| [Acast.com](https://acast.com) | [Olle Lundberg](https://github.com/lndbrg) | Developer portal with tech docs, service catalog and a bunch of other internal tooling | -| [Lunar](https://lunar.app) | [Jacob Valdemar](https://github.com/JacobValdemar) | Internal developer portal for service overview and insights, API documentation, technical guides, onboarding guides and RFC's. | -| [Trendyol](https://trendyol.com) | [Gamze Senturk](https://github.com/gmzsenturk), [Mert Can Bilgic](https://github.com/mertcb) | The Developer Portal has been called `Pandora`. Provides an overview of Trendyol tech ecosystem. TechDocs, Catalog, Custom Plugins and Theme. | -| [Peloton](https://www.onepeloton.com/) | [Jim Haughwout](https://github.com/JimHaughwout) | Creating our first developer portal and tech-docs. Exploring Service Catalog, Tech Insights and Cost Insights as well. | -| [TELUS](https://telus.com) | [Seb Barre](https://github.com/sbarre) | The Go-to place to find answers about development and delivery at TELUS. | -| [Brex](https://www.brex.com/) | [Vamsi Chitters](https://github.com/vamsikc) | A centralized UI to understand how a service fits in the whole Brex architecture and manage a team’s engineering dependencies. | -| [Oriflame](https://www.oriflame.com/) | [Oriflame](https://github.com/oriflame) | Internal developer portal for services, single page apps and packages overview, API documentation, technical guides, tech-radar and more. | -| [Booz Allen Hamilton](https://www.boozallen.com/) | [Jason Miller](https://github.com/JasonMiller-BAH) | Developer portal for a full-stack software development ecosystem that accelerates consistent and repeatable Modern Software Development practices for internal innovation and investments. | -| [Netflix](https://www.netflix.com/) | [bleathem](https://github.com/bleathem) | Our Backstage implementation will be the front door to a unified experience connecting our internal platform products across important workflows with integrated knowledge and support. | -| [b.well](https://www.icanbwell.com/) | [Jacob Rosales](https://github.com/jrosales) | Foundation for our engineering portal and cloud insights. | -| [PagerDuty](https://www.pagerduty.com/) | [Mark Shaw](https://github.com/markshawtoronto) | Developer portal, initially focused on software templates and tech-docs. | -| [MoonShiner](https://moonshiner.at) | [Fabian Hippmann](https://github.com/FabianHippmann) | Developer portal - helps us keep track of our customer projects, onboard new developers & improve our development process 🌕🚀🧑‍🚀 | -| [FundApps](https://www.fundapps.co/) | [Elliot Greenwood](https://github.com/egnwd) | Developer Portal - A place for us to keep track of our projects and documentation for all services and processes | -| [DAZN](https://dazn.com/) | [Lou Bichard](https://twitter.com/loujaybee), [Marco Crivellaro](https://github.com/crivetechie), [Alex Hollerith](mailto:alex.hollerith@dazn.com) | Ingesting all of DAZN's repos for the catalog, migrating our internal platform apps (pull request boards, release information, inner source marketplace etc) to Backstage plugins (where applicable). | -| [HelloFresh](https://www.hellofresh.de/) | [@iammuho](https://github.com/iammuho), [@ElenaForester](https://github.com/ElenaForester), [@diegomarangoni](https://github.com/diegomarangoni) | Our developer portal at HelloFresh - Spread across an organisation of 500+ engineers globally. | -| [FactSet](https://www.factset.com/) | [@kuangp](https://github.com/kuangp) | Developer portal to provide discoverability to all internal components, APIs, documentation, and scaffold templates with integrations to our internal infrastructure tools. | -| [Workrise](https://www.workrise.com/) | [Michael Rode](https://github.com/michaelrode) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | -| [RedVentures](https://www.redventures.com/) | [Chris Diaz](https://github.com/codingdiaz) | Developer portal that brings everything an engineer needs to provide value into a single pane of glass. | -| [MavTek](https://www.mavtek.com/) | [@fgascon](https://github.com/fgascon) | Developer portal focused on standardizing practices, centralizing documentation and streamlining developer practices. | -| [QuintoAndar](https://www.quintoandar.com.br/) | [@quintoandar](https://github.com/quintoandar) | Developer portal, services catalog and centralization of service metrics. | -| [empathy.co](https://empathy.co/) | [@guillermotti](https://github.com/guillermotti) | Developer portal for tech docs, service catalog, plugin discovery and much more. | -| [creditas.com](https://creditas.com/) | [@aureliosaraiva](https://github.com/aureliosaraiva) [@Creditas](https://github.com/creditas) | Centralization of all services, standards, documentation, etc. We started the deployment process. | -| [Prisjakt](https://www.prisjakt.nu) / [PriceSpy](https://pricespy.co.uk) | [@kennylindahl](https://github.com/kennylindahl) | Internal developer portal - Documentation, scaffolding, software catalog, TechRadar, Gitlab org data integration | -| [Powerspike](https://powerspike.tv/) | [@trelore](https://github.com/trelore) | Developer portal for documentation of core libraries and repositories. | -| [2U](https://2u.com) | [Andrew Thal](https://github.com/athal7) | Development team home-base, promoting service discoverability, resource dependencies, and tech radar | -| [Taxfix](https://taxfix.de/) | [Sami Ur Rehman](https://github.com/samiurrehman92) | Developer's portal with software catalog at it's core. Hosts API Specs, Tech Docs, Tech Radar and some custom plugins. | -| [Busuu](https://busuu.com/) | [Adam Tester](https://github.com/adamtester) | Developer portal with service catalog, API docs, Event docs, service templating, and cost insights. | -| [Loadsmart](https://loadsmart.com/) | [Loadsmart](https://github.com/loadsmart) | Improve services visibility and operations for service owners and developers. | -| [Monzo](https://monzo.com/) | [@WillSewell](https://github.com/WillSewell), [@joechrisellis](https://github.com/joechrisellis) | Developer portal showing metadata and docs for over 2000 microservices. We have built a number of plugins such as a UI for our system to measure [software excellence](https://monzo.com/blog/2021/09/15/how-we-measure-software-excellence), and a UI to show deployment and config change events. | -| [Vaimo](https://www.vaimo.com) | [@vaimo-magnus](https://github.com/vaimo-magnus) | Developer Portal for our developers at Vaimo, currently docs and self-service towards our internal PaaS based on k8s. Plans to extend the catalog into Projects, Environments etc | -| [Wayfair](https://www.wayfair.com) | [@fransan](https://github.com/fransan), [@errskipower](https://github.com/errskipower), [@hrrs](https://github.com/hrrs) | Developer portal for service catalog, technical documentation, and APIs. | -| [CircleHD](https://www.circlehd.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | -| [CastDesk](https://castdesk.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | -| [Santagostino](https://santagostino.it) | [@santagostino](https://github.com/santagostino) | Developer portal, gateway to our infrastructure, documentation, service catalog and internal tooling. | -| [Peak](https://peak.ai) | [Luke Beamish](https://github.com/lukebeamish-peak) | Developer portal for all internal engineers to access documentation and tooling. | -| [Gelato](https://gelato.com/) | [Dmitry Makarenko](https://github.com/dmitry-makarenko-gelato) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal and third-party systems🚀. | -| [GoCardless](https://gocardless.com/) | [James Turley](https://github.com/tragiclifestories) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal systems. | -| [Box](https://www.box.com) | [@kielosz](https://github.com/kielosz), [@jluk-box](https://github.com/jluk-box), [@ptychu](https://github.com/ptychu), [@alexrybch](https://github.com/alexrybch), [@szubster](https://github.com/szubster) | Developer portal for service catalog, integration with internal systems, new service onboarding. | -| [Bazaarvoice](https://www.bazaarvoice.com) | [@niallmccullagh](https://github.com/niallmccullagh) | Developer portal for service catalog and scaffolds, publishing Github docs and API documentation, visualising our internal tech radar and our product engineering org structure. | -| [Krateo PlatformOps](https://www.krateo.io) | [@projectkerberus](https://github.com/projectkerberus) | A multi-cloud control plane to create, manage and deploy any kind of resource easily and centrally via a Developer Portal that centralizes via a self-service catalog the templating and ownership of services, the available documentation, the overview of the components that compose an entire domain and all the data of the service lifecycle. | -| [Adevinta](https://www.adevinta.com) | [Ray Sinnema](https://github.com/RemonSinnema) | Showcase shared services to our internal customers. | -| [Splunk](https://www.splunk.com) | [@tonytamsf](https://github.com/tonytamsf) | Developer portal as a centralized place to find people, services, documentation, escalation policies and give bravos. This portal is also being used as a centralized search engine for engineering specific documentation.| -| [SoundCloud](https://www.soundcloud.com) | [Julio Zynger](https://github.com/julioz) | Developer portal as a [humane registry](https://martinfowler.com/bliki/HumaneRegistry.html) for the organization: catalog of people, services, documentation, feature toggles, escalation policies, etc.| +| Organization | Contact | Description of Use | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [Spotify](https://www.spotify.com) | [@leemills83](https://github.com/leemills83) | Main interface towards all of Spotify's infrastructure and technical documentation. | +| [bol.com](https://www.bol.com) | [@sagacity](https://github.com/sagacity) | Initial work being done to unify platform tooling. | +| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. | +| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up | +| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. | +| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. | +| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. | +| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications | +| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. | +| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | +| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D | +| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling | +| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling | +| [Telenor Sweden](https://www.telenor.se) | [@O5ten](https://github.com/O5ten) | Building a developer portal for scaffolding projects towards our unified build environment and microservice stacks | +| [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. | +| [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. | +| [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. | +| [Expedia Group](https://www.expediagroup.com) | [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo), [Erik Lindgren](https://github.com/lindgren) | EG Common Developer Toolkit | +| [Paddle.com](https://paddle.com) | [Ioannis Georgoulas](https://github.com/geototti21) | Developer portal (Tech Docs, Service Catalog, Internal Tooling), we use vanilla Backstage FE and custom BE implementation in Go | +| [Acast.com](https://acast.com) | [Olle Lundberg](https://github.com/lndbrg) | Developer portal with tech docs, service catalog and a bunch of other internal tooling | +| [Lunar](https://lunar.app) | [Jacob Valdemar](https://github.com/JacobValdemar) | Internal developer portal for service overview and insights, API documentation, technical guides, onboarding guides and RFC's. | +| [Trendyol](https://trendyol.com) | [Gamze Senturk](https://github.com/gmzsenturk), [Mert Can Bilgic](https://github.com/mertcb) | The Developer Portal has been called `Pandora`. Provides an overview of Trendyol tech ecosystem. TechDocs, Catalog, Custom Plugins and Theme. | +| [Peloton](https://www.onepeloton.com/) | [Jim Haughwout](https://github.com/JimHaughwout) | Creating our first developer portal and tech-docs. Exploring Service Catalog, Tech Insights and Cost Insights as well. | +| [TELUS](https://telus.com) | [Seb Barre](https://github.com/sbarre) | The Go-to place to find answers about development and delivery at TELUS. | +| [Brex](https://www.brex.com/) | [Vamsi Chitters](https://github.com/vamsikc) | A centralized UI to understand how a service fits in the whole Brex architecture and manage a team’s engineering dependencies. | +| [Oriflame](https://www.oriflame.com/) | [Oriflame](https://github.com/oriflame) | Internal developer portal for services, single page apps and packages overview, API documentation, technical guides, tech-radar and more. | +| [Booz Allen Hamilton](https://www.boozallen.com/) | [Jason Miller](https://github.com/JasonMiller-BAH) | Developer portal for a full-stack software development ecosystem that accelerates consistent and repeatable Modern Software Development practices for internal innovation and investments. | +| [Netflix](https://www.netflix.com/) | [bleathem](https://github.com/bleathem) | Our Backstage implementation will be the front door to a unified experience connecting our internal platform products across important workflows with integrated knowledge and support. | +| [b.well](https://www.icanbwell.com/) | [Jacob Rosales](https://github.com/jrosales) | Foundation for our engineering portal and cloud insights. | +| [PagerDuty](https://www.pagerduty.com/) | [Mark Shaw](https://github.com/markshawtoronto) | Developer portal, initially focused on software templates and tech-docs. | +| [MoonShiner](https://moonshiner.at) | [Fabian Hippmann](https://github.com/FabianHippmann) | Developer portal - helps us keep track of our customer projects, onboard new developers & improve our development process 🌕🚀🧑‍🚀 | +| [FundApps](https://www.fundapps.co/) | [Elliot Greenwood](https://github.com/egnwd) | Developer Portal - A place for us to keep track of our projects and documentation for all services and processes | +| [DAZN](https://dazn.com/) | [Lou Bichard](https://twitter.com/loujaybee), [Marco Crivellaro](https://github.com/crivetechie), [Alex Hollerith](mailto:alex.hollerith@dazn.com) | Ingesting all of DAZN's repos for the catalog, migrating our internal platform apps (pull request boards, release information, inner source marketplace etc) to Backstage plugins (where applicable). | +| [HelloFresh](https://www.hellofresh.de/) | [@iammuho](https://github.com/iammuho), [@ElenaForester](https://github.com/ElenaForester), [@diegomarangoni](https://github.com/diegomarangoni) | Our developer portal at HelloFresh - Spread across an organisation of 500+ engineers globally. | +| [FactSet](https://www.factset.com/) | [@kuangp](https://github.com/kuangp) | Developer portal to provide discoverability to all internal components, APIs, documentation, and scaffold templates with integrations to our internal infrastructure tools. | +| [Workrise](https://www.workrise.com/) | [Michael Rode](https://github.com/michaelrode) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | +| [RedVentures](https://www.redventures.com/) | [Chris Diaz](https://github.com/codingdiaz) | Developer portal that brings everything an engineer needs to provide value into a single pane of glass. | +| [MavTek](https://www.mavtek.com/) | [@fgascon](https://github.com/fgascon) | Developer portal focused on standardizing practices, centralizing documentation and streamlining developer practices. | +| [QuintoAndar](https://www.quintoandar.com.br/) | [@quintoandar](https://github.com/quintoandar) | Developer portal, services catalog and centralization of service metrics. | +| [empathy.co](https://empathy.co/) | [@guillermotti](https://github.com/guillermotti) | Developer portal for tech docs, service catalog, plugin discovery and much more. | +| [creditas.com](https://creditas.com/) | [@aureliosaraiva](https://github.com/aureliosaraiva) [@Creditas](https://github.com/creditas) | Centralization of all services, standards, documentation, etc. We started the deployment process. | +| [Prisjakt](https://www.prisjakt.nu) / [PriceSpy](https://pricespy.co.uk) | [@kennylindahl](https://github.com/kennylindahl) | Internal developer portal - Documentation, scaffolding, software catalog, TechRadar, Gitlab org data integration | +| [Powerspike](https://powerspike.tv/) | [@trelore](https://github.com/trelore) | Developer portal for documentation of core libraries and repositories. | +| [2U](https://2u.com) | [Andrew Thal](https://github.com/athal7) | Development team home-base, promoting service discoverability, resource dependencies, and tech radar | +| [Taxfix](https://taxfix.de/) | [Sami Ur Rehman](https://github.com/samiurrehman92) | Developer's portal with software catalog at it's core. Hosts API Specs, Tech Docs, Tech Radar and some custom plugins. | +| [Busuu](https://busuu.com/) | [Adam Tester](https://github.com/adamtester) | Developer portal with service catalog, API docs, Event docs, service templating, and cost insights. | +| [Loadsmart](https://loadsmart.com/) | [Loadsmart](https://github.com/loadsmart) | Improve services visibility and operations for service owners and developers. | +| [Monzo](https://monzo.com/) | [@WillSewell](https://github.com/WillSewell), [@joechrisellis](https://github.com/joechrisellis) | Developer portal showing metadata and docs for over 2000 microservices. We have built a number of plugins such as a UI for our system to measure [software excellence](https://monzo.com/blog/2021/09/15/how-we-measure-software-excellence), and a UI to show deployment and config change events. | +| [Vaimo](https://www.vaimo.com) | [@vaimo-magnus](https://github.com/vaimo-magnus) | Developer Portal for our developers at Vaimo, currently docs and self-service towards our internal PaaS based on k8s. Plans to extend the catalog into Projects, Environments etc | +| [Wayfair](https://www.wayfair.com) | [@fransan](https://github.com/fransan), [@errskipower](https://github.com/errskipower), [@hrrs](https://github.com/hrrs) | Developer portal for service catalog, technical documentation, and APIs. | +| [CircleHD](https://www.circlehd.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | +| [CastDesk](https://castdesk.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | +| [Santagostino](https://santagostino.it) | [@santagostino](https://github.com/santagostino) | Developer portal, gateway to our infrastructure, documentation, service catalog and internal tooling. | +| [Peak](https://peak.ai) | [Luke Beamish](https://github.com/lukebeamish-peak) | Developer portal for all internal engineers to access documentation and tooling. | +| [Gelato](https://gelato.com/) | [Dmitry Makarenko](https://github.com/dmitry-makarenko-gelato) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal and third-party systems🚀. | +| [GoCardless](https://gocardless.com/) | [James Turley](https://github.com/tragiclifestories) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal systems. | +| [Box](https://www.box.com) | [@kielosz](https://github.com/kielosz), [@jluk-box](https://github.com/jluk-box), [@ptychu](https://github.com/ptychu), [@alexrybch](https://github.com/alexrybch), [@szubster](https://github.com/szubster) | Developer portal for service catalog, integration with internal systems, new service onboarding. | +| [Bazaarvoice](https://www.bazaarvoice.com) | [@niallmccullagh](https://github.com/niallmccullagh) | Developer portal for service catalog and scaffolds, publishing Github docs and API documentation, visualising our internal tech radar and our product engineering org structure. | +| [Krateo PlatformOps](https://www.krateo.io) | [@projectkerberus](https://github.com/projectkerberus) | A multi-cloud control plane to create, manage and deploy any kind of resource easily and centrally via a Developer Portal that centralizes via a self-service catalog the templating and ownership of services, the available documentation, the overview of the components that compose an entire domain and all the data of the service lifecycle. | +| [Adevinta](https://www.adevinta.com) | [Ray Sinnema](https://github.com/RemonSinnema) | Showcase shared services to our internal customers. | +| [Splunk](https://www.splunk.com) | [@tonytamsf](https://github.com/tonytamsf) | Developer portal as a centralized place to find people, services, documentation, escalation policies and give bravos. This portal is also being used as a centralized search engine for engineering specific documentation. | +| [SoundCloud](https://www.soundcloud.com) | [Julio Zynger](https://github.com/julioz) | Developer portal as a [humane registry](https://martinfowler.com/bliki/HumaneRegistry.html) for the organization: catalog of people, services, documentation, feature toggles, escalation policies, etc. | diff --git a/packages/backend-common/src/config.ts b/packages/backend-common/src/config.ts index 3c5a2d7b26..3fd5e1b44e 100644 --- a/packages/backend-common/src/config.ts +++ b/packages/backend-common/src/config.ts @@ -22,7 +22,7 @@ import { loadConfigSchema, loadConfig, ConfigSchema, - ConfigTarget + ConfigTarget, } from '@backstage/config-loader'; import { AppConfig, Config, ConfigReader } from '@backstage/config'; import { JsonValue } from '@backstage/types'; From 89e9f486f1f925684741d0f81391f73c5c89977e Mon Sep 17 00:00:00 2001 From: Matto Date: Fri, 5 Nov 2021 12:45:14 +1100 Subject: [PATCH 071/196] api-report fix Signed-off-by: Matto --- packages/config-loader/api-report.md | 30 ++++++++++++++++------------ 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/config-loader/api-report.md b/packages/config-loader/api-report.md index be4678aa0f..6f568df942 100644 --- a/packages/config-loader/api-report.md +++ b/packages/config-loader/api-report.md @@ -51,6 +51,21 @@ export type LoadConfigOptions = { watch?: LoadConfigOptionsWatch; }; +// Warning: (ae-missing-release-tag) "LoadConfigOptionsRemote" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type LoadConfigOptionsRemote = { + reloadIntervalSeconds: number; +}; + +// Warning: (ae-missing-release-tag) "LoadConfigOptionsWatch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type LoadConfigOptionsWatch = { + onChange: (configs: AppConfig[]) => void; + stopSignal?: Promise; +}; + // @public export function loadConfigSchema( options: LoadConfigSchemaOptions, @@ -74,13 +89,6 @@ export function readEnvConfig(env: { [name: string]: string | undefined; }): AppConfig[]; -// Warning: (ae-missing-release-tag) "LoadConfigOptionsRemote" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type LoadConfigOptionsRemote = { - reloadIntervalSeconds: number; -}; - // @public export type TransformFunc = ( value: T, @@ -89,11 +97,7 @@ export type TransformFunc = ( }, ) => T | undefined; -// Warning: (ae-missing-release-tag) "LoadConfigOptionsWatch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warnings were encountered during analysis: // -// @public (undocumented) -export type LoadConfigOptionsWatch = { - onChange: (configs: AppConfig[]) => void; - stopSignal?: Promise; -}; +// src/loader.d.ts:33:5 - (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/config-loader" does not have an export "configTargets" ``` From 42e97f470890daa4cefda0fc239a9c3084d0f1b9 Mon Sep 17 00:00:00 2001 From: Lykke Axlin Date: Fri, 5 Nov 2021 08:09:36 +0100 Subject: [PATCH 072/196] update yarn.lock and fixed typo Signed-off-by: Lykke Axlin --- .changeset/light-knives-camp.md | 2 +- yarn.lock | 15 +++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/.changeset/light-knives-camp.md b/.changeset/light-knives-camp.md index a68f3ebb9a..1e7fa37082 100644 --- a/.changeset/light-knives-camp.md +++ b/.changeset/light-knives-camp.md @@ -2,4 +2,4 @@ '@backstage/plugin-ilert': patch --- -Change the version of @date-io/luxon from 2.x to 1.x to make it compatible with material-ui-pickers +Change the version of `@date-io/luxon` from 2.x to 1.x to make it compatible with material-ui-pickers diff --git a/yarn.lock b/yarn.lock index 3fec636e32..67e23eb89d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2684,11 +2684,6 @@ resolved "https://registry.npmjs.org/@date-io/core/-/core-2.10.7.tgz#0fe1fa0ef02c827919e23c2802a4b25589ac522d" integrity sha512-EG/1qDiQvd12RoNJ6H+sZcHVswC/3uMx/ySvfaJ24vB30rLjkgHggEXbgMbfgki7wMuiQ/zXI8QlmF1k3kWRGQ== -"@date-io/core@^2.10.11": - version "2.10.11" - resolved "https://registry.npmjs.org/@date-io/core/-/core-2.10.11.tgz#b1a3d57730f3eaaab54d5658be4a71727297e357" - integrity sha512-keXQnwH0LM8wyvu+j5Z2KGK56D+eItjy7DnwuWl/oV+DM2UEYl0z5WhdPMpfswSyt/kjuPOzcVF/7u/skMLaoA== - "@date-io/date-fns@^1.3.13": version "1.3.13" resolved "https://registry.npmjs.org/@date-io/date-fns/-/date-fns-1.3.13.tgz#7798844041640ab393f7e21a7769a65d672f4735" @@ -2696,12 +2691,12 @@ dependencies: "@date-io/core" "^1.3.13" -"@date-io/luxon@2.x": - version "2.10.11" - resolved "https://registry.npmjs.org/@date-io/luxon/-/luxon-2.10.11.tgz#d0981b9fdf5e5f17f8ce59265a3ac6c335565fac" - integrity sha512-SS6SIkp0Y9GFwpQycCTUAyW3OZTW05CWI1DJu10hUzcg8SmjJfhjs7hQY3TOeW+JT6VtXGTVGwbWPUBJsNkhZg== +"@date-io/luxon@1.x": + version "1.3.13" + resolved "https://registry.npmjs.org/@date-io/luxon/-/luxon-1.3.13.tgz#68f0134bb38ef486b2ed6df01981f814c633e28a" + integrity sha512-9wUrJCNSMZJeYAiH+dbb45oGpnHeFP7TOH/Lt26If47gjFCkjvyINzWx+K5AGsnlP0Qosxc7hkF1yLi6ecutxw== dependencies: - "@date-io/core" "^2.10.11" + "@date-io/core" "^1.3.13" "@discoveryjs/json-ext@^0.5.3": version "0.5.5" From f3c7eec64be17a50c38d7f13270773e14fd50386 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Fri, 5 Nov 2021 14:48:44 -0500 Subject: [PATCH 073/196] Updated changeset to reflect changes made Signed-off-by: Andre Wanlin --- .changeset/techdocs-tender-bugs-sort.md | 5 +++++ .changeset/tender-bugs-sort.md | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 .changeset/techdocs-tender-bugs-sort.md delete mode 100644 .changeset/tender-bugs-sort.md diff --git a/.changeset/techdocs-tender-bugs-sort.md b/.changeset/techdocs-tender-bugs-sort.md new file mode 100644 index 0000000000..be670b06ff --- /dev/null +++ b/.changeset/techdocs-tender-bugs-sort.md @@ -0,0 +1,5 @@ +--- +'@backstage/techdocs-common': patch +--- + +Updated to properly join URL segments under any OS for both AWS S3 and GCP diff --git a/.changeset/tender-bugs-sort.md b/.changeset/tender-bugs-sort.md deleted file mode 100644 index b6ad29ba66..0000000000 --- a/.changeset/tender-bugs-sort.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/techdocs-common': patch ---- - -Updated to properly join URL segments under any OS From ec64d9590cbb4dad0f8832da7f41e7a02e8115bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Sat, 6 Nov 2021 10:25:17 +0100 Subject: [PATCH 074/196] Call the super constructor early MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/nice-tools-cheat.md | 5 +++++ packages/cli/src/lib/errors.ts | 10 +++++----- 2 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 .changeset/nice-tools-cheat.md diff --git a/.changeset/nice-tools-cheat.md b/.changeset/nice-tools-cheat.md new file mode 100644 index 0000000000..c4f0e162bf --- /dev/null +++ b/.changeset/nice-tools-cheat.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Make `ExitCodeError` call `super` early to avoid compiler warnings diff --git a/packages/cli/src/lib/errors.ts b/packages/cli/src/lib/errors.ts index c0b4c45b6c..dd3955a10a 100644 --- a/packages/cli/src/lib/errors.ts +++ b/packages/cli/src/lib/errors.ts @@ -26,11 +26,11 @@ export class ExitCodeError extends CustomError { readonly code: number; constructor(code: number, command?: string) { - if (command) { - super(`Command '${command}' exited with code ${code}`); - } else { - super(`Child exited with code ${code}`); - } + super( + command + ? `Command '${command}' exited with code ${code}` + : `Child exited with code ${code}`, + ); this.code = code; } } From e7e4626fc9308f63375cd8b07b9196fd57d14079 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 8 Nov 2021 10:38:42 +0100 Subject: [PATCH 075/196] chore: remove warning Signed-off-by: blam --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 13b7c945cf..1078631926 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ # [Backstage](https://backstage.io) -> 🏖 All of the maintainers will be taking a wellness break Nov. 1–5. The repo and Discord may be quieter than usual, but not to worry. We’ll have coverage plans in place and be back in full force, rested and restored, on Nov. 8. 🏖 - [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![CNCF Status](https://img.shields.io/badge/cncf%20status-sandbox-blue.svg)](https://www.cncf.io/projects) [![Main CI Build](https://github.com/backstage/backstage/workflows/Main%20Master%20Build/badge.svg)](https://github.com/backstage/backstage/actions?query=workflow%3A%22Main+Master+Build%22) From 064297ea19eeecb12e8a54ac6d8d503bcd85379a Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 8 Nov 2021 10:57:58 +0100 Subject: [PATCH 076/196] chore: adding some documentation for exported things Signed-off-by: blam --- .../src/hooks/useEntityOwnership.ts | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/plugins/catalog-react/src/hooks/useEntityOwnership.ts b/plugins/catalog-react/src/hooks/useEntityOwnership.ts index aee903603a..54e9e0ff99 100644 --- a/plugins/catalog-react/src/hooks/useEntityOwnership.ts +++ b/plugins/catalog-react/src/hooks/useEntityOwnership.ts @@ -49,9 +49,16 @@ function extendUserId(id: string): string { } } -// Takes the relevant parts of the Backstage identity, and translates them into -// a list of entity refs on string form that represent the user's ownership -// connections. +/** + * Takes the relevant parts of the Backstage identity, and translates them into + * a list of entity refs on string form that represent the user's ownership + * connections. + * + * @public + * + * @param identityApi - The IdentityApi implementation + * @returns IdentityOwner refs as a string array + */ export async function loadIdentityOwnerRefs( identityApi: IdentityApi, ): Promise { @@ -81,9 +88,17 @@ export async function loadIdentityOwnerRefs( return result; } -// Takes the relevant parts of the User entity corresponding to the Backstage -// identity, and translates them into a list of entity refs on string form that -// represent the user's ownership connections. +/** + * Takes the relevant parts of the User entity corresponding to the Backstage + * identity, and translates them into a list of entity refs on string form that + * represent the user's ownership connections. + * + * @public + * + * @param catalogApi - The Catalog API implementation + * @param identityOwnerRefs - List of identity owner refs as strings + * @returns OwnerRefs as a string array + */ export async function loadCatalogOwnerRefs( catalogApi: CatalogApi, identityOwnerRefs: string[], @@ -113,6 +128,10 @@ export async function loadCatalogOwnerRefs( * owner of a given entity. When the hook is initially mounted, the loading * flag will be true and the results returned from the function will always be * false. + * + * @public + * + * @returns a function that checks if the signed in user owns an entity */ export function useEntityOwnership(): { loading: boolean; From 0aec087ee6422bc896b9c33c4b6a9cff6b26e9e9 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 8 Nov 2021 11:00:30 +0100 Subject: [PATCH 077/196] chore: updating API report Signed-off-by: blam --- plugins/catalog-react/api-report.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/plugins/catalog-react/api-report.md b/plugins/catalog-react/api-report.md index 07a924bfa3..19a4dfd930 100644 --- a/plugins/catalog-react/api-report.md +++ b/plugins/catalog-react/api-report.md @@ -727,17 +727,13 @@ export function getEntitySourceLocation( // @public export function isOwnerOf(owner: Entity, owned: Entity): boolean; -// Warning: (ae-missing-release-tag) "loadCatalogOwnerRefs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function loadCatalogOwnerRefs( catalogApi: CatalogApi, identityOwnerRefs: string[], ): Promise; -// Warning: (ae-missing-release-tag) "loadIdentityOwnerRefs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export function loadIdentityOwnerRefs( identityApi: IdentityApi, ): Promise; @@ -829,8 +825,6 @@ export function useEntityListProvider< EntityFilters extends DefaultEntityFilters = DefaultEntityFilters, >(): EntityListContextProps; -// Warning: (ae-missing-release-tag) "useEntityOwnership" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export function useEntityOwnership(): { loading: boolean; From 44308cc80daeafd9ae5debc148d724f1e06cbeae Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 8 Nov 2021 11:40:31 +0100 Subject: [PATCH 078/196] chore: remove some duplicate dependencies Signed-off-by: blam --- yarn.lock | 119 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 93 insertions(+), 26 deletions(-) diff --git a/yarn.lock b/yarn.lock index e879cff365..905d2e7bef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5449,18 +5449,18 @@ integrity sha512-dOI13j1uHMZkRxhZuge/ugOE7Aqcg7Nxki932lDZuXyY4G8CGxkc/66PeQ8pR4PCzThHORXo7Ptvau6bh101lQ== "@storybook/addon-a11y@^6.3.4": - version "6.3.7" - resolved "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-6.3.7.tgz#a802455f2d932eda07314e3d44a96c94bbd22b3d" - integrity sha512-Z5Lhxm8r5CkPW9FYf6zmAk9c7IhUeUQZxKZeEWGZdOvcjQ32rtg4IYvO2SHgWNrEKBdxxFm3pMiyK3wylQLfsQ== + version "6.3.12" + resolved "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-6.3.12.tgz#2f930fc84fc275a4ed43a716fc09cc12caf4e110" + integrity sha512-q1NdRHFJV6sLEEJw0hatCc5ZIthELqM/AWdrEWDyhcJNyiq7Tq4nKqQBMTQSYwHiUAmxVgw7i4oa1vM2M51/3g== dependencies: - "@storybook/addons" "6.3.7" - "@storybook/api" "6.3.7" - "@storybook/channels" "6.3.7" - "@storybook/client-api" "6.3.7" - "@storybook/client-logger" "6.3.7" - "@storybook/components" "6.3.7" - "@storybook/core-events" "6.3.7" - "@storybook/theming" "6.3.7" + "@storybook/addons" "6.3.12" + "@storybook/api" "6.3.12" + "@storybook/channels" "6.3.12" + "@storybook/client-api" "6.3.12" + "@storybook/client-logger" "6.3.12" + "@storybook/components" "6.3.12" + "@storybook/core-events" "6.3.12" + "@storybook/theming" "6.3.12" axe-core "^4.2.0" core-js "^3.8.2" global "^4.4.0" @@ -5546,6 +5546,21 @@ global "^4.4.0" regenerator-runtime "^0.13.7" +"@storybook/addons@6.3.12", "@storybook/addons@^6.1.11": + version "6.3.12" + resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.3.12.tgz#8773dcc113c5086dfff722388b7b65580e43b65b" + integrity sha512-UgoMyr7Qr0FS3ezt8u6hMEcHgyynQS9ucr5mAwZky3wpXRPFyUTmMto9r4BBUdqyUvTUj/LRKIcmLBfj+/l0Fg== + dependencies: + "@storybook/api" "6.3.12" + "@storybook/channels" "6.3.12" + "@storybook/client-logger" "6.3.12" + "@storybook/core-events" "6.3.12" + "@storybook/router" "6.3.12" + "@storybook/theming" "6.3.12" + core-js "^3.8.2" + global "^4.4.0" + regenerator-runtime "^0.13.7" + "@storybook/addons@6.3.7": version "6.3.7" resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.3.7.tgz#7c6b8d11b65f67b1884f6140437fe996dc39537a" @@ -5561,21 +5576,6 @@ global "^4.4.0" regenerator-runtime "^0.13.7" -"@storybook/addons@^6.1.11": - version "6.3.12" - resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.3.12.tgz#8773dcc113c5086dfff722388b7b65580e43b65b" - integrity sha512-UgoMyr7Qr0FS3ezt8u6hMEcHgyynQS9ucr5mAwZky3wpXRPFyUTmMto9r4BBUdqyUvTUj/LRKIcmLBfj+/l0Fg== - dependencies: - "@storybook/api" "6.3.12" - "@storybook/channels" "6.3.12" - "@storybook/client-logger" "6.3.12" - "@storybook/core-events" "6.3.12" - "@storybook/router" "6.3.12" - "@storybook/theming" "6.3.12" - core-js "^3.8.2" - global "^4.4.0" - regenerator-runtime "^0.13.7" - "@storybook/api@6.3.11": version "6.3.11" resolved "https://registry.npmjs.org/@storybook/api/-/api-6.3.11.tgz#ea3806a0570da65bfb5b39e4edb90289b5ba701e" @@ -5743,6 +5743,19 @@ qs "^6.10.0" telejson "^5.3.2" +"@storybook/channel-postmessage@6.3.12": + version "6.3.12" + resolved "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-6.3.12.tgz#3ff9412ac0f445e3b8b44dd414e783a5a47ff7c1" + integrity sha512-Ou/2Ga3JRTZ/4sSv7ikMgUgLTeZMsXXWLXuscz4oaYhmOqAU9CrJw0G1NitwBgK/+qC83lEFSLujHkWcoQDOKg== + dependencies: + "@storybook/channels" "6.3.12" + "@storybook/client-logger" "6.3.12" + "@storybook/core-events" "6.3.12" + core-js "^3.8.2" + global "^4.4.0" + qs "^6.10.0" + telejson "^5.3.2" + "@storybook/channel-postmessage@6.3.7": version "6.3.7" resolved "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-6.3.7.tgz#bd4edf84a29aa2cd4a22d26115c60194d289a840" @@ -5807,6 +5820,30 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" +"@storybook/client-api@6.3.12": + version "6.3.12" + resolved "https://registry.npmjs.org/@storybook/client-api/-/client-api-6.3.12.tgz#a0c6d72a871d1cb02b4b98675472839061e39b5b" + integrity sha512-xnW+lKKK2T774z+rOr9Wopt1aYTStfb86PSs9p3Fpnc2Btcftln+C3NtiHZl8Ccqft8Mz/chLGgewRui6tNI8g== + dependencies: + "@storybook/addons" "6.3.12" + "@storybook/channel-postmessage" "6.3.12" + "@storybook/channels" "6.3.12" + "@storybook/client-logger" "6.3.12" + "@storybook/core-events" "6.3.12" + "@storybook/csf" "0.0.1" + "@types/qs" "^6.9.5" + "@types/webpack-env" "^1.16.0" + core-js "^3.8.2" + global "^4.4.0" + lodash "^4.17.20" + memoizerific "^1.11.3" + qs "^6.10.0" + regenerator-runtime "^0.13.7" + stable "^0.1.8" + store2 "^2.12.0" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + "@storybook/client-api@6.3.7": version "6.3.7" resolved "https://registry.npmjs.org/@storybook/client-api/-/client-api-6.3.7.tgz#cb1dca05467d777bd09aadbbdd1dd22ca537ce14" @@ -5885,6 +5922,36 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" +"@storybook/components@6.3.12": + version "6.3.12" + resolved "https://registry.npmjs.org/@storybook/components/-/components-6.3.12.tgz#0c7967c60354c84afa20dfab4753105e49b1927d" + integrity sha512-kdQt8toUjynYAxDLrJzuG7YSNL6as1wJoyzNUaCfG06YPhvIAlKo7le9tS2mThVFN5e9nbKrW3N1V1sp6ypZXQ== + dependencies: + "@popperjs/core" "^2.6.0" + "@storybook/client-logger" "6.3.12" + "@storybook/csf" "0.0.1" + "@storybook/theming" "6.3.12" + "@types/color-convert" "^2.0.0" + "@types/overlayscrollbars" "^1.12.0" + "@types/react-syntax-highlighter" "11.0.5" + color-convert "^2.0.1" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + lodash "^4.17.20" + markdown-to-jsx "^7.1.3" + memoizerific "^1.11.3" + overlayscrollbars "^1.13.1" + polished "^4.0.5" + prop-types "^15.7.2" + react-colorful "^5.1.2" + react-popper-tooltip "^3.1.1" + react-syntax-highlighter "^13.5.3" + react-textarea-autosize "^8.3.0" + regenerator-runtime "^0.13.7" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + "@storybook/components@6.3.7": version "6.3.7" resolved "https://registry.npmjs.org/@storybook/components/-/components-6.3.7.tgz#42b1ca6d24e388e02eab82aa9ed3365db2266ecc" From bd74a61eaabca24e8cdcedefe70beb21e6bff135 Mon Sep 17 00:00:00 2001 From: Harry Hogg Date: Mon, 8 Nov 2021 11:03:58 +0000 Subject: [PATCH 079/196] Updated to run on a separate workflow every 4 hours. Signed-off-by: Harry Hogg --- .github/workflows/snyk-github-issue-sync.yml | 23 ++++++++++++++++++++ .github/workflows/snyk-monitor.yml | 5 ----- 2 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/snyk-github-issue-sync.yml diff --git a/.github/workflows/snyk-github-issue-sync.yml b/.github/workflows/snyk-github-issue-sync.yml new file mode 100644 index 0000000000..34eb5c1ca4 --- /dev/null +++ b/.github/workflows/snyk-github-issue-sync.yml @@ -0,0 +1,23 @@ +name: 'Snyk Github Issue Sync' + +on: + schedule: + - cron: '0 */4 * * *' + +jobs: + sync: + steps: + - uses: actions/checkout@v2 + - name: Create Snyk report + uses: snyk/actions/node@master + with: + args: > + --yarn-workspaces + --org=backstage-dgh + --strict-out-of-sync=false + --json-file-output=snyk.json + json: true + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + - name: Update Github issues + run: yarn ts-node scripts/snyk-github-issue-sync.ts diff --git a/.github/workflows/snyk-monitor.yml b/.github/workflows/snyk-monitor.yml index 32df37f35b..0adb6c5bcb 100644 --- a/.github/workflows/snyk-monitor.yml +++ b/.github/workflows/snyk-monitor.yml @@ -43,14 +43,9 @@ jobs: --org=backstage-dgh --strict-out-of-sync=false --sarif-file-output=snyk.sarif - --json-file-output=snyk.json - json: true env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - name: Upload Snyk report uses: github/codeql-action/upload-sarif@v1 with: sarif_file: snyk.sarif - - - name: Update Github issues - run: yarn ts-node scripts/snyk-github-issue-sync.ts From 26c5659c97b6d57859f8e3fca68f66c1663a76d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 8 Nov 2021 15:02:07 +0100 Subject: [PATCH 080/196] Bump msw to the same version as the rest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/odd-rats-walk.md | 5 +++++ packages/config-loader/package.json | 2 +- yarn.lock | 28 ---------------------------- 3 files changed, 6 insertions(+), 29 deletions(-) create mode 100644 .changeset/odd-rats-walk.md diff --git a/.changeset/odd-rats-walk.md b/.changeset/odd-rats-walk.md new file mode 100644 index 0000000000..3a8710cc32 --- /dev/null +++ b/.changeset/odd-rats-walk.md @@ -0,0 +1,5 @@ +--- +'@backstage/config-loader': patch +--- + +Bump msw to the same version as the rest diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 910dd87108..22fcedf486 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -53,7 +53,7 @@ "@types/node": "^14.14.32", "@types/yup": "^0.29.13", "mock-fs": "^5.1.0", - "msw": "^0.29.0" + "msw": "^0.35.0" }, "files": [ "dist" diff --git a/yarn.lock b/yarn.lock index 0d88beceb5..6dc17c5577 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14206,11 +14206,6 @@ fecha@^4.2.0: resolved "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz#3ffb6395453e3f3efff850404f0a59b6747f5f41" integrity sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg== -fetch-blob@2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-2.1.2.tgz#a7805db1361bd44c1ef62bb57fb5fe8ea173ef3c" - integrity sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow== - fetch-readablestream@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/fetch-readablestream/-/fetch-readablestream-0.2.0.tgz#eaa6d1a76b12de2d4731a343393c6ccdcfe2c795" @@ -28357,11 +28352,6 @@ webidl-conversions@^3.0.0: resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= -webidl-conversions@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" - integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== - webidl-conversions@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" @@ -28585,24 +28575,6 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -whatwg-url@^6.4.1: - version "6.5.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" - integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" - -whatwg-url@^7.0.0: - version "7.1.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" - integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" - whatwg-url@^8.0.0, whatwg-url@^8.4.0: version "8.4.0" resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz#50fb9615b05469591d2b2bd6dfaed2942ed72837" From 0557332ec60fb9a8a322768fddb20e6385561106 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 8 Nov 2021 15:05:56 +0100 Subject: [PATCH 081/196] chore: missing ADR in sidebars Signed-off-by: blam --- microsite/sidebars.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsite/sidebars.json b/microsite/sidebars.json index bee9cd7180..23cc6135c0 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -275,7 +275,8 @@ "architecture-decisions/adrs-adr008", "architecture-decisions/adrs-adr009", "architecture-decisions/adrs-adr010", - "architecture-decisions/adrs-adr011" + "architecture-decisions/adrs-adr011", + "architecture-decisions/adrs-adr012" ], "FAQ": ["FAQ"] } From 9c76bf58b7c31f632700f8033f7b4bf10bf88c97 Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Mon, 8 Nov 2021 14:06:44 +0000 Subject: [PATCH 082/196] feat: Created new `@backstage/plugin-azure-devops-common` package for common types. Signed-off-by: Marley Powell --- .changeset/tough-buckets-explain.md | 3 +- plugins/azure-devops-common/.eslintrc.js | 3 + plugins/azure-devops-common/package.json | 37 +++++++++ plugins/azure-devops-common/src/index.ts | 17 ++++ plugins/azure-devops-common/src/types.ts | 82 +++++++++++++++++++ plugins/azure-devops/package.json | 2 +- .../BuildTable/BuildTable.stories.tsx | 2 +- 7 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 plugins/azure-devops-common/.eslintrc.js create mode 100644 plugins/azure-devops-common/package.json create mode 100644 plugins/azure-devops-common/src/index.ts create mode 100644 plugins/azure-devops-common/src/types.ts diff --git a/.changeset/tough-buckets-explain.md b/.changeset/tough-buckets-explain.md index a2ca37d516..53d397a4ef 100644 --- a/.changeset/tough-buckets-explain.md +++ b/.changeset/tough-buckets-explain.md @@ -1,5 +1,6 @@ --- '@backstage/plugin-azure-devops': patch +'@backstage/plugin-azure-devops-common': patch --- -Simplified queue time calculation in `BuildTable`. +Simplified queue time calculation in `BuildTable`. Created new `@backstage/plugin-azure-devops-common` package. diff --git a/plugins/azure-devops-common/.eslintrc.js b/plugins/azure-devops-common/.eslintrc.js new file mode 100644 index 0000000000..13573efa9c --- /dev/null +++ b/plugins/azure-devops-common/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint')], +}; diff --git a/plugins/azure-devops-common/package.json b/plugins/azure-devops-common/package.json new file mode 100644 index 0000000000..e1e5d00894 --- /dev/null +++ b/plugins/azure-devops-common/package.json @@ -0,0 +1,37 @@ +{ + "name": "@backstage/plugin-azure-devops-common", + "version": "0.0.1", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": false, + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "module": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/azure-devops-common" + }, + "keywords": [ + "backstage" + ], + "scripts": { + "build": "backstage-cli build", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "devDependencies": { + "@backstage/cli": "^0.8.1" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/azure-devops-common/src/index.ts b/plugins/azure-devops-common/src/index.ts new file mode 100644 index 0000000000..26a854de89 --- /dev/null +++ b/plugins/azure-devops-common/src/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './types'; diff --git a/plugins/azure-devops-common/src/types.ts b/plugins/azure-devops-common/src/types.ts new file mode 100644 index 0000000000..d7736d03e1 --- /dev/null +++ b/plugins/azure-devops-common/src/types.ts @@ -0,0 +1,82 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export enum BuildResult { + /** + * No result + */ + None = 0, + /** + * The build completed successfully. + */ + Succeeded = 2, + /** + * The build completed compilation successfully but had other errors. + */ + PartiallySucceeded = 4, + /** + * The build completed unsuccessfully. + */ + Failed = 8, + /** + * The build was canceled before starting. + */ + Canceled = 32, +} + +export enum BuildStatus { + /** + * No status. + */ + None = 0, + /** + * The build is currently in progress. + */ + InProgress = 1, + /** + * The build has completed. + */ + Completed = 2, + /** + * The build is cancelling + */ + Cancelling = 4, + /** + * The build is inactive in the queue. + */ + Postponed = 8, + /** + * The build has not yet started. + */ + NotStarted = 32, + /** + * All status. + */ + All = 47, +} + +export type RepoBuild = { + id?: number; + title: string; + link?: string; + status?: BuildStatus; + result?: BuildResult; + queueTime?: Date; + startTime?: Date; + finishTime?: Date; + source: string; + uniqueName?: string; +}; diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index 1c5e0bba84..1ad5b07ebb 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -31,7 +31,7 @@ "@backstage/core-components": "^0.7.2", "@backstage/core-plugin-api": "^0.1.12", "@backstage/errors": "^0.1.4", - "@backstage/plugin-azure-devops-backend": "^0.1.4", + "@backstage/plugin-azure-devops-common": "^0.0.1", "@backstage/plugin-catalog-react": "^0.6.2", "@backstage/theme": "^0.2.12", "@material-ui/core": "^4.12.2", diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.stories.tsx b/plugins/azure-devops/src/components/BuildTable/BuildTable.stories.tsx index 609466769b..76a19155ba 100644 --- a/plugins/azure-devops/src/components/BuildTable/BuildTable.stories.tsx +++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.stories.tsx @@ -18,7 +18,7 @@ import { BuildResult, BuildStatus, RepoBuild, -} from '@backstage/plugin-azure-devops-backend'; +} from '@backstage/plugin-azure-devops-common'; import { BuildTable } from './BuildTable'; import { MemoryRouter } from 'react-router'; From a209473de859a5d58a28f7da5dc665fec56d6dca Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Mon, 8 Nov 2021 14:08:25 +0000 Subject: [PATCH 083/196] revert: Removed unnecessary changes in changeset. Signed-off-by: Marley Powell --- .changeset/tough-buckets-explain.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.changeset/tough-buckets-explain.md b/.changeset/tough-buckets-explain.md index 53d397a4ef..a2ca37d516 100644 --- a/.changeset/tough-buckets-explain.md +++ b/.changeset/tough-buckets-explain.md @@ -1,6 +1,5 @@ --- '@backstage/plugin-azure-devops': patch -'@backstage/plugin-azure-devops-common': patch --- -Simplified queue time calculation in `BuildTable`. Created new `@backstage/plugin-azure-devops-common` package. +Simplified queue time calculation in `BuildTable`. From 4c780d86514c0b4354357cda21ffead175202a4b Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 8 Nov 2021 15:14:38 +0100 Subject: [PATCH 084/196] chore: use named exports instead Signed-off-by: blam --- plugins/catalog-react/src/hooks/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/catalog-react/src/hooks/index.ts b/plugins/catalog-react/src/hooks/index.ts index 17c15e4ebb..bbbca3a501 100644 --- a/plugins/catalog-react/src/hooks/index.ts +++ b/plugins/catalog-react/src/hooks/index.ts @@ -37,4 +37,8 @@ export { useOwnUser } from './useOwnUser'; export { useRelatedEntities } from './useRelatedEntities'; export { useStarredEntities } from './useStarredEntities'; export { useStarredEntity } from './useStarredEntity'; -export * from './useEntityOwnership'; +export { + loadCatalogOwnerRefs, + useEntityOwnership, + loadIdentityOwnerRefs, +} from './useEntityOwnership'; From 236756d0f204eb55cd3fd4e40a480e38f720ca99 Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Mon, 8 Nov 2021 14:21:39 +0000 Subject: [PATCH 085/196] refactor: Consume types from `@backstage/plugin-azure-devops-common`. Signed-off-by: Marley Powell --- .changeset/many-mayflies-notice.md | 2 +- plugins/azure-devops/package.json | 2 +- .../azure-devops/src/api/AzureDevOpsApi.ts | 6 +++- .../azure-devops/src/api/AzureDevOpsClient.ts | 8 +++-- plugins/azure-devops/src/api/types.ts | 34 ------------------- .../components/BuildTable/BuildTable.test.ts | 2 +- .../src/components/BuildTable/BuildTable.tsx | 4 +-- 7 files changed, 16 insertions(+), 42 deletions(-) delete mode 100644 plugins/azure-devops/src/api/types.ts diff --git a/.changeset/many-mayflies-notice.md b/.changeset/many-mayflies-notice.md index b8d551f4fb..b3d3b3adcd 100644 --- a/.changeset/many-mayflies-notice.md +++ b/.changeset/many-mayflies-notice.md @@ -2,4 +2,4 @@ '@backstage/plugin-azure-devops': patch --- -refactor(`@backstage/plugin-azure-devops`): Consume re-exported types from `@backstage/plugin-azure-devops-backend`. +refactor(`@backstage/plugin-azure-devops`): Consume types from `@backstage/plugin-azure-devops-common`. diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index 5bde285aa0..5c08f998fb 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -37,7 +37,7 @@ "@backstage/core-components": "^0.7.2", "@backstage/core-plugin-api": "^0.1.12", "@backstage/errors": "^0.1.4", - "@backstage/plugin-azure-devops-backend": "^0.1.4", + "@backstage/plugin-azure-devops-common": "^0.0.1", "@backstage/plugin-catalog-react": "^0.6.2", "@backstage/theme": "^0.2.12", "@material-ui/core": "^4.12.2", diff --git a/plugins/azure-devops/src/api/AzureDevOpsApi.ts b/plugins/azure-devops/src/api/AzureDevOpsApi.ts index f0bad53b43..05377b0b7b 100644 --- a/plugins/azure-devops/src/api/AzureDevOpsApi.ts +++ b/plugins/azure-devops/src/api/AzureDevOpsApi.ts @@ -14,7 +14,11 @@ * limitations under the License. */ -import { RepoBuild, RepoBuildOptions } from './types'; +import { + RepoBuild, + RepoBuildOptions, +} from '@backstage/plugin-azure-devops-common'; + import { createApiRef } from '@backstage/core-plugin-api'; export const azureDevOpsApiRef = createApiRef({ diff --git a/plugins/azure-devops/src/api/AzureDevOpsClient.ts b/plugins/azure-devops/src/api/AzureDevOpsClient.ts index e03428e511..4fd4e781c4 100644 --- a/plugins/azure-devops/src/api/AzureDevOpsClient.ts +++ b/plugins/azure-devops/src/api/AzureDevOpsClient.ts @@ -14,9 +14,13 @@ * limitations under the License. */ -import { AzureDevOpsApi } from './AzureDevOpsApi'; -import { RepoBuild, RepoBuildOptions } from './types'; import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api'; +import { + RepoBuild, + RepoBuildOptions, +} from '@backstage/plugin-azure-devops-common'; + +import { AzureDevOpsApi } from './AzureDevOpsApi'; import { ResponseError } from '@backstage/errors'; export class AzureDevOpsClient implements AzureDevOpsApi { diff --git a/plugins/azure-devops/src/api/types.ts b/plugins/azure-devops/src/api/types.ts deleted file mode 100644 index 46a9212aca..0000000000 --- a/plugins/azure-devops/src/api/types.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - BuildResult, - BuildStatus, -} from '@backstage/plugin-azure-devops-backend'; - -export type RepoBuild = { - id?: number; - title: string; - link?: string; - status?: BuildStatus; - result?: BuildResult; - queueTime?: Date; - source: string; -}; - -export type RepoBuildOptions = { - top?: number; -}; diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts b/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts index 81942b72ec..cb240aef42 100644 --- a/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts +++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts @@ -17,7 +17,7 @@ import { BuildResult, BuildStatus, -} from '@backstage/plugin-azure-devops-backend'; +} from '@backstage/plugin-azure-devops-common'; import { getBuildResultComponent, getBuildStateComponent } from './BuildTable'; import { renderInTestApp } from '@backstage/test-utils'; diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx index 4833370cf6..7e260be6e9 100644 --- a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx +++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx @@ -18,7 +18,8 @@ import { Box, Typography } from '@material-ui/core'; import { BuildResult, BuildStatus, -} from '@backstage/plugin-azure-devops-backend'; + RepoBuild, +} from '@backstage/plugin-azure-devops-common'; import { Link, ResponseErrorPanel, @@ -34,7 +35,6 @@ import { import { DateTime } from 'luxon'; import React from 'react'; -import { RepoBuild } from '../../api/types'; export const getBuildResultComponent = (result: number | undefined) => { switch (result) { From 4634588de330ca63e68ff8af136a423bc03b3303 Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Mon, 8 Nov 2021 14:29:50 +0000 Subject: [PATCH 086/196] chore: generated API report. Signed-off-by: Marley Powell --- plugins/azure-devops-common/api-report.md | 47 +++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 plugins/azure-devops-common/api-report.md diff --git a/plugins/azure-devops-common/api-report.md b/plugins/azure-devops-common/api-report.md new file mode 100644 index 0000000000..0bc6568189 --- /dev/null +++ b/plugins/azure-devops-common/api-report.md @@ -0,0 +1,47 @@ +## API Report File for "@backstage/plugin-azure-devops-common" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +// Warning: (ae-missing-release-tag) "BuildResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export enum BuildResult { + Canceled = 32, + Failed = 8, + None = 0, + PartiallySucceeded = 4, + Succeeded = 2, +} + +// Warning: (ae-missing-release-tag) "BuildStatus" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export enum BuildStatus { + All = 47, + Cancelling = 4, + Completed = 2, + InProgress = 1, + None = 0, + NotStarted = 32, + Postponed = 8, +} + +// Warning: (ae-missing-release-tag) "RepoBuild" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type RepoBuild = { + id?: number; + title: string; + link?: string; + status?: BuildStatus; + result?: BuildResult; + queueTime?: Date; + startTime?: Date; + finishTime?: Date; + source: string; + uniqueName?: string; +}; + +// (No @packageDocumentation comment for this package) +``` From b4ab81cad9ed51d6fe5f985c3a467b5312374c6a Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Mon, 8 Nov 2021 14:55:07 +0000 Subject: [PATCH 087/196] refactor: Stop re-exporting types from `@backstage/plugin-azure-devops-backend`. Signed-off-by: Marley Powell --- .changeset/many-mayflies-notice.md | 3 +- plugins/azure-devops-backend/api-report.md | 45 +------------ plugins/azure-devops-backend/package.json | 1 + .../src/api/AzureDevOpsApi.test.ts | 12 ++-- .../src/api/AzureDevOpsApi.ts | 12 ++-- plugins/azure-devops-backend/src/api/index.ts | 2 - plugins/azure-devops-backend/src/api/types.ts | 67 ------------------- plugins/azure-devops-backend/src/index.ts | 3 +- .../src/service/router.test.ts | 26 +++---- .../src/service/router.ts | 5 +- .../azure-devops/src/hooks/useRepoBuilds.ts | 5 +- 11 files changed, 43 insertions(+), 138 deletions(-) delete mode 100644 plugins/azure-devops-backend/src/api/types.ts diff --git a/.changeset/many-mayflies-notice.md b/.changeset/many-mayflies-notice.md index b3d3b3adcd..cd4f91fa93 100644 --- a/.changeset/many-mayflies-notice.md +++ b/.changeset/many-mayflies-notice.md @@ -1,5 +1,6 @@ --- '@backstage/plugin-azure-devops': patch +'@backstage/plugin-azure-devops-backend': minor --- -refactor(`@backstage/plugin-azure-devops`): Consume types from `@backstage/plugin-azure-devops-common`. +refactor(`@backstage/plugin-azure-devops`): Consume types from `@backstage/plugin-azure-devops-common`. Stop re-exporting types from `@backstage/plugin-azure-devops-backend`. diff --git a/plugins/azure-devops-backend/api-report.md b/plugins/azure-devops-backend/api-report.md index fac6261a39..1eabccc720 100644 --- a/plugins/azure-devops-backend/api-report.md +++ b/plugins/azure-devops-backend/api-report.md @@ -4,13 +4,13 @@ ```ts import { Build } from 'azure-devops-node-api/interfaces/BuildInterfaces'; -import { BuildResult } from 'azure-devops-node-api/interfaces/BuildInterfaces'; -import { BuildStatus } from 'azure-devops-node-api/interfaces/BuildInterfaces'; import { Config } from '@backstage/config'; import express from 'express'; import { GitRepository } from 'azure-devops-node-api/interfaces/GitInterfaces'; import { Logger as Logger_2 } from 'winston'; -import { PullRequestStatus } from 'azure-devops-node-api/interfaces/GitInterfaces'; +import { PullRequest } from '@backstage/plugin-azure-devops-common'; +import { PullRequestOptions } from '@backstage/plugin-azure-devops-common'; +import { RepoBuild } from '@backstage/plugin-azure-devops-common'; import { WebApi } from 'azure-devops-node-api'; // Warning: (ae-missing-release-tag) "AzureDevOpsApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -29,8 +29,6 @@ export class AzureDevOpsApi { projectName: string, repoName: string, ): Promise; - // Warning: (ae-forgotten-export) The symbol "PullRequestOptions" needs to be exported by the entry point index.d.ts - // // (undocumented) getPullRequests( projectName: string, @@ -45,48 +43,11 @@ export class AzureDevOpsApi { ): Promise; } -export { BuildResult }; - -export { BuildStatus }; - // Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export function createRouter(options: RouterOptions): Promise; -// Warning: (ae-missing-release-tag) "PullRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type PullRequest = { - pullRequestId?: number; - repoName?: string; - title?: string; - uniqueName?: string; - createdBy?: string; - creationDate?: Date; - sourceRefName?: string; - targetRefName?: string; - status?: PullRequestStatus; - isDraft?: boolean; - link: string; -}; - -// Warning: (ae-missing-release-tag) "RepoBuild" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type RepoBuild = { - id?: number; - title: string; - link?: string; - status?: BuildStatus; - result?: BuildResult; - queueTime?: Date; - startTime?: Date; - finishTime?: Date; - source: string; - uniqueName?: string; -}; - // Warning: (ae-missing-release-tag) "RouterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/plugins/azure-devops-backend/package.json b/plugins/azure-devops-backend/package.json index 8a468b0cc9..9dd79e9f8e 100644 --- a/plugins/azure-devops-backend/package.json +++ b/plugins/azure-devops-backend/package.json @@ -22,6 +22,7 @@ "dependencies": { "@backstage/backend-common": "^0.9.8", "@backstage/config": "^0.1.11", + "@backstage/plugin-azure-devops-common": "^0.0.1", "@types/express": "^4.17.6", "azure-devops-node-api": "^11.0.1", "express": "^4.17.1", diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts index fdd49c0440..429d608bd6 100644 --- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts +++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts @@ -16,17 +16,21 @@ import { Build, + DefinitionReference, +} from 'azure-devops-node-api/interfaces/BuildInterfaces'; +import { BuildResult, BuildStatus, - GitPullRequest, - GitRepository, PullRequest, PullRequestStatus, RepoBuild, -} from './types'; +} from '@backstage/plugin-azure-devops-common'; +import { + GitPullRequest, + GitRepository, +} from 'azure-devops-node-api/interfaces/GitInterfaces'; import { mappedPullRequest, mappedRepoBuild } from './AzureDevOpsApi'; -import { DefinitionReference } from 'azure-devops-node-api/interfaces/BuildInterfaces'; import { IdentityRef } from 'azure-devops-node-api/interfaces/common/VSSInterfaces'; describe('AzureDevOpsApi', () => { diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts index 26378496ea..1ed1f1d5f5 100644 --- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts +++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts @@ -15,17 +15,19 @@ */ import { - Build, BuildResult, BuildStatus, - GitPullRequest, - GitPullRequestSearchCriteria, - GitRepository, PullRequest, PullRequestOptions, RepoBuild, -} from './types'; +} from '@backstage/plugin-azure-devops-common'; +import { + GitPullRequest, + GitPullRequestSearchCriteria, + GitRepository, +} from 'azure-devops-node-api/interfaces/GitInterfaces'; +import { Build } from 'azure-devops-node-api/interfaces/BuildInterfaces'; import { Logger } from 'winston'; import { WebApi } from 'azure-devops-node-api'; diff --git a/plugins/azure-devops-backend/src/api/index.ts b/plugins/azure-devops-backend/src/api/index.ts index 97c48cb0bf..903c1bdb6a 100644 --- a/plugins/azure-devops-backend/src/api/index.ts +++ b/plugins/azure-devops-backend/src/api/index.ts @@ -15,5 +15,3 @@ */ export { AzureDevOpsApi } from './AzureDevOpsApi'; -export { BuildResult, BuildStatus } from './types'; -export type { RepoBuild, PullRequest } from './types'; diff --git a/plugins/azure-devops-backend/src/api/types.ts b/plugins/azure-devops-backend/src/api/types.ts deleted file mode 100644 index fab61d26c9..0000000000 --- a/plugins/azure-devops-backend/src/api/types.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - Build, - BuildResult, - BuildStatus, -} from 'azure-devops-node-api/interfaces/BuildInterfaces'; -import { - GitPullRequest, - GitPullRequestSearchCriteria, - GitRepository, - PullRequestStatus, -} from 'azure-devops-node-api/interfaces/GitInterfaces'; - -export { BuildResult, BuildStatus, PullRequestStatus }; -export type { - Build, - GitPullRequest, - GitPullRequestSearchCriteria, - GitRepository, -}; - -export type RepoBuild = { - id?: number; - title: string; - link?: string; - status?: BuildStatus; - result?: BuildResult; - queueTime?: Date; - startTime?: Date; - finishTime?: Date; - source: string; - uniqueName?: string; -}; - -export type PullRequest = { - pullRequestId?: number; - repoName?: string; - title?: string; - uniqueName?: string; - createdBy?: string; - creationDate?: Date; - sourceRefName?: string; - targetRefName?: string; - status?: PullRequestStatus; - isDraft?: boolean; - link: string; -}; - -export type PullRequestOptions = { - top: number; - status: PullRequestStatus; -}; diff --git a/plugins/azure-devops-backend/src/index.ts b/plugins/azure-devops-backend/src/index.ts index 7a2a347b65..6977d3580f 100644 --- a/plugins/azure-devops-backend/src/index.ts +++ b/plugins/azure-devops-backend/src/index.ts @@ -13,6 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { AzureDevOpsApi, BuildResult, BuildStatus } from './api'; -export type { RepoBuild, PullRequest } from './api'; +export { AzureDevOpsApi } from './api'; export * from './service/router'; diff --git a/plugins/azure-devops-backend/src/service/router.test.ts b/plugins/azure-devops-backend/src/service/router.test.ts index 4143291409..f5d533d2d8 100644 --- a/plugins/azure-devops-backend/src/service/router.test.ts +++ b/plugins/azure-devops-backend/src/service/router.test.ts @@ -14,22 +14,22 @@ * limitations under the License. */ -import { getVoidLogger } from '@backstage/backend-common'; -import { ConfigReader } from '@backstage/config'; -import express from 'express'; -import request from 'supertest'; -import { AzureDevOpsApi } from '../api'; -import { createRouter } from './router'; -import { PullRequest, RepoBuild } from '../api/types'; import { - GitRepository, - PullRequestStatus, -} from 'azure-devops-node-api/interfaces/GitInterfaces'; -import { - Build, BuildResult, BuildStatus, -} from 'azure-devops-node-api/interfaces/BuildInterfaces'; + PullRequest, + PullRequestStatus, + RepoBuild, +} from '@backstage/plugin-azure-devops-common'; + +import { AzureDevOpsApi } from '../api'; +import { Build } from 'azure-devops-node-api/interfaces/BuildInterfaces'; +import { ConfigReader } from '@backstage/config'; +import { GitRepository } from 'azure-devops-node-api/interfaces/GitInterfaces'; +import { createRouter } from './router'; +import express from 'express'; +import { getVoidLogger } from '@backstage/backend-common'; +import request from 'supertest'; describe('createRouter', () => { let azureDevOpsApi: jest.Mocked; diff --git a/plugins/azure-devops-backend/src/service/router.ts b/plugins/azure-devops-backend/src/service/router.ts index b3505eae1c..d61148b4ff 100644 --- a/plugins/azure-devops-backend/src/service/router.ts +++ b/plugins/azure-devops-backend/src/service/router.ts @@ -14,7 +14,10 @@ * limitations under the License. */ -import { PullRequestOptions, PullRequestStatus } from '../api/types'; +import { + PullRequestOptions, + PullRequestStatus, +} from '@backstage/plugin-azure-devops-common'; import { WebApi, getPersonalAccessTokenHandler } from 'azure-devops-node-api'; import { AzureDevOpsApi } from '../api'; diff --git a/plugins/azure-devops/src/hooks/useRepoBuilds.ts b/plugins/azure-devops/src/hooks/useRepoBuilds.ts index df4f76c57e..af2d181c34 100644 --- a/plugins/azure-devops/src/hooks/useRepoBuilds.ts +++ b/plugins/azure-devops/src/hooks/useRepoBuilds.ts @@ -14,7 +14,10 @@ * limitations under the License. */ -import { RepoBuild, RepoBuildOptions } from '../api/types'; +import { + RepoBuild, + RepoBuildOptions, +} from '@backstage/plugin-azure-devops-common'; import { AZURE_DEVOPS_DEFAULT_TOP } from '../constants'; import { Entity } from '@backstage/catalog-model'; From 0c9dd8df5143f3ee906de63a6b351fe3b0d3be40 Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Mon, 8 Nov 2021 14:56:16 +0000 Subject: [PATCH 088/196] fix: Added `--passWithNoTests` flag to `package.json`. Signed-off-by: Marley Powell --- plugins/azure-devops-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/azure-devops-common/package.json b/plugins/azure-devops-common/package.json index e1e5d00894..05f70f6596 100644 --- a/plugins/azure-devops-common/package.json +++ b/plugins/azure-devops-common/package.json @@ -23,7 +23,7 @@ "scripts": { "build": "backstage-cli build", "lint": "backstage-cli lint", - "test": "backstage-cli test", + "test": "backstage-cli test --passWithNoTests", "prepack": "backstage-cli prepack", "postpack": "backstage-cli postpack", "clean": "backstage-cli clean" From 18b6c1543ce003d5e3839374b12706370df0323e Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Mon, 8 Nov 2021 14:58:31 +0000 Subject: [PATCH 089/196] fix: Fixed merge issue in `package.json`. Signed-off-by: Marley Powell --- plugins/azure-devops/package.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index 5c08f998fb..25328d9e81 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -27,12 +27,6 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.3", - "@backstage/core-components": "^0.7.0", - "@backstage/core-plugin-api": "^0.1.10", - "@backstage/errors": "^0.1.2", - "@backstage/plugin-catalog-react": "^0.6.0", - "@backstage/theme": "^0.2.11", "@backstage/catalog-model": "^0.9.6", "@backstage/core-components": "^0.7.2", "@backstage/core-plugin-api": "^0.1.12", From 97eed9f67f12359429e260e74cbe32adc35e6815 Mon Sep 17 00:00:00 2001 From: Colton Padden Date: Mon, 8 Nov 2021 10:16:55 -0500 Subject: [PATCH 090/196] use diff codeblock in changeset markdown Signed-off-by: Colton Padden --- .changeset/metal-impalas-allow.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.changeset/metal-impalas-allow.md b/.changeset/metal-impalas-allow.md index 6f18969f3d..d3ca3b6e49 100644 --- a/.changeset/metal-impalas-allow.md +++ b/.changeset/metal-impalas-allow.md @@ -6,7 +6,7 @@ 2. The `LocalPublish` publisher can now be created using `fromConfig`: -``` ---- const publisher = new LocalPublish(config, logger, discovery); -+++ const publisher = LocalPublish.fromConfig(config, logger, discovery); +```diff +- const publisher = new LocalPublish(config, logger, discovery); ++ const publisher = LocalPublish.fromConfig(config, logger, discovery); ``` From 86bef79ad1eaa499c0cd9afe443d4cd1029f22f4 Mon Sep 17 00:00:00 2001 From: Joon Park Date: Fri, 29 Oct 2021 15:31:46 +0100 Subject: [PATCH 091/196] Allow nested EntityFilters This makes the format of EntityFilters more flexible, and paves the way for the permissions system, which requires composing multiple _collections_ of filters. Signed-off-by: Joon Park --- .changeset/slimy-days-leave.md | 5 + plugins/catalog-backend/api-report.md | 2 +- plugins/catalog-backend/src/catalog/types.ts | 2 +- .../src/legacy/database/CommonDatabase.ts | 24 ++- .../src/service/NextEntitiesCatalog.test.ts | 155 ++++++++++++++++++ .../src/service/NextEntitiesCatalog.ts | 97 +++++++---- 6 files changed, 242 insertions(+), 43 deletions(-) create mode 100644 .changeset/slimy-days-leave.md diff --git a/.changeset/slimy-days-leave.md b/.changeset/slimy-days-leave.md new file mode 100644 index 0000000000..250bf32962 --- /dev/null +++ b/.changeset/slimy-days-leave.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Allow nested EntityFilters diff --git a/plugins/catalog-backend/api-report.md b/plugins/catalog-backend/api-report.md index 6d21884c12..e83fa68cad 100644 --- a/plugins/catalog-backend/api-report.md +++ b/plugins/catalog-backend/api-report.md @@ -872,7 +872,7 @@ export type EntityAncestryResponse = { // @public export type EntityFilter = { anyOf: { - allOf: EntitiesSearchFilter[]; + allOf: (EntitiesSearchFilter | EntityFilter)[]; }[]; }; diff --git a/plugins/catalog-backend/src/catalog/types.ts b/plugins/catalog-backend/src/catalog/types.ts index 0bc0ec4311..b0b06c563c 100644 --- a/plugins/catalog-backend/src/catalog/types.ts +++ b/plugins/catalog-backend/src/catalog/types.ts @@ -23,7 +23,7 @@ import { Entity, EntityRelationSpec } from '@backstage/catalog-model'; * individual filters must match. */ export type EntityFilter = { - anyOf: { allOf: EntitiesSearchFilter[] }[]; + anyOf: { allOf: (EntitiesSearchFilter | EntityFilter)[] }[]; }; /** diff --git a/plugins/catalog-backend/src/legacy/database/CommonDatabase.ts b/plugins/catalog-backend/src/legacy/database/CommonDatabase.ts index 5cc31b5be5..81b0612989 100644 --- a/plugins/catalog-backend/src/legacy/database/CommonDatabase.ts +++ b/plugins/catalog-backend/src/legacy/database/CommonDatabase.ts @@ -46,7 +46,11 @@ import { DbPageInfo, Transaction, } from './types'; -import { EntityPagination } from '../../catalog/types'; +import { + EntityPagination, + EntityFilter, + EntitiesSearchFilter, +} from '../../catalog/types'; // The number of items that are sent per batch to the database layer, when // doing .batchInsert calls to knex. This needs to be low enough to not cause @@ -219,11 +223,13 @@ export class CommonDatabase implements Database { for (const singleFilter of request?.filter?.anyOf ?? []) { entitiesQuery = entitiesQuery.orWhere(function singleFilterFn() { - for (const { - key, - matchValueIn, - matchValueExists, - } of singleFilter.allOf) { + for (const filter of singleFilter.allOf) { + if (isEntityFilter(filter)) { + throw new Error( + 'Nested filters are not supported in the legacy CommonDatabase', + ); + } + const { key, matchValueIn, matchValueExists } = filter; // NOTE(freben): This used to be a set of OUTER JOIN, which may seem to // make a lot of sense. However, it had abysmal performance on sqlite // when datasets grew large, so we're using IN instead. @@ -606,3 +612,9 @@ function deduplicateRelations( r => `${r.source_full_name}:${r.target_full_name}:${r.type}`, ); } + +function isEntityFilter( + filter: EntitiesSearchFilter | EntityFilter, +): filter is EntityFilter { + return filter.hasOwnProperty('anyOf'); +} diff --git a/plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts index 082aa47df5..0c805e1b17 100644 --- a/plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts @@ -23,6 +23,7 @@ import { DbFinalEntitiesRow, DbRefreshStateReferencesRow, DbRefreshStateRow, + DbSearchRow, } from '../database/tables'; import { NextEntitiesCatalog } from './NextEntitiesCatalog'; @@ -73,6 +74,52 @@ describe('NextEntitiesCatalog', () => { } } + async function addEntityToSearch(knex: Knex, entity: Entity) { + const id = uuid(); + const entityRef = stringifyEntityRef(entity); + const entityJson = JSON.stringify(entity); + + await knex('refresh_state').insert({ + entity_id: id, + entity_ref: entityRef, + unprocessed_entity: entityJson, + errors: '[]', + next_update_at: '2031-01-01 23:00:00', + last_discovery_at: '2021-04-01 13:37:00', + }); + + await knex('final_entities').insert({ + entity_id: id, + final_entity: entityJson, + hash: 'h', + stitch_ticket: '', + }); + + await insertSearchRow(knex, id, null, entity); + } + + async function insertSearchRow( + knex: Knex, + id: string, + previousKey: string | null, + previousValue: Object, + ) { + return Promise.all( + Object.entries(previousValue).map(async ([key, value]) => { + const currentKey = `${previousKey ? `${previousKey}.` : ``}${key}`; + if (typeof value === 'object') { + await insertSearchRow(knex, id, currentKey, value); + } else { + await knex('search').insert({ + entity_id: id, + key: currentKey, + value: value, + }); + } + }), + ); + } + describe('entityAncestry', () => { it.each(databases.eachSupportedId())( 'should return the ancestry with one parent, %p', @@ -209,4 +256,112 @@ describe('NextEntitiesCatalog', () => { 60_000, ); }); + + describe('entities', () => { + it.each(databases.eachSupportedId())( + 'should return correct entity for simple filter', + async databaseId => { + const { knex } = await createDatabase(databaseId); + const entity1: Entity = { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'one' }, + spec: {}, + }; + const entity2: Entity = { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'two' }, + spec: { + test: 'test value', + }, + }; + await addEntityToSearch(knex, entity1); + await addEntityToSearch(knex, entity2); + const catalog = new NextEntitiesCatalog(knex); + + const testFilter = { + key: 'spec.test', + matchValueExists: true, + }; + const request = { + filter: { anyOf: [{ allOf: [testFilter] }] }, + }; + const { entities } = await catalog.entities(request); + + expect(entities.length).toBe(1); + expect(entities[0]).toEqual(entity2); + }, + ); + + it.each(databases.eachSupportedId())( + 'should return correct entity for nested filter', + async databaseId => { + const { knex } = await createDatabase(databaseId); + const entity1: Entity = { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'one', org: 'a', desc: 'description' }, + spec: {}, + }; + const entity2: Entity = { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'two', org: 'b', desc: 'description' }, + spec: {}, + }; + const entity3: Entity = { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'three', org: 'b', color: 'red' }, + spec: {}, + }; + const entity4: Entity = { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'four', org: 'b', color: 'blue' }, + spec: {}, + }; + await addEntityToSearch(knex, entity1); + await addEntityToSearch(knex, entity2); + await addEntityToSearch(knex, entity3); + await addEntityToSearch(knex, entity4); + const catalog = new NextEntitiesCatalog(knex); + + const testFilter1 = { + key: 'metadata.org', + matchValueExists: true, + matchValueIn: ['b'], + }; + const testFilter2 = { + key: 'metadata.desc', + matchValueExists: true, + }; + const testFilter3 = { + key: 'metadata.color', + matchValueExists: true, + matchValueIn: ['blue'], + }; + const request = { + filter: { + anyOf: [ + { + allOf: [ + testFilter1, + { + anyOf: [{ allOf: [testFilter2] }, { allOf: [testFilter3] }], + }, + ], + }, + ], + }, + }; + const { entities } = await catalog.entities(request); + + expect(entities.length).toBe(2); + expect(entities).toContainEqual(entity2); + expect(entities).toContainEqual(entity4); + }, + ); + }); }); diff --git a/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts b/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts index 0891be045d..52d6ed1bdd 100644 --- a/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts @@ -23,6 +23,8 @@ import { EntitiesResponse, EntityAncestryResponse, EntityPagination, + EntityFilter, + EntitiesSearchFilter, } from '../catalog/types'; import { DbFinalEntitiesRow, @@ -73,6 +75,64 @@ function stringifyPagination(input: { limit: number; offset: number }) { return base64; } +function addCondition( + queryBuilder: Knex.QueryBuilder, + db: Knex, + { key, matchValueIn, matchValueExists }: EntitiesSearchFilter, +) { + // NOTE(freben): This used to be a set of OUTER JOIN, which may seem to + // make a lot of sense. However, it had abysmal performance on sqlite + // when datasets grew large, so we're using IN instead. + const matchQuery = db('search') + .select('entity_id') + .where(function keyFilter() { + this.andWhere({ key: key.toLowerCase() }); + if (matchValueExists !== false && matchValueIn) { + if (matchValueIn.length === 1) { + this.andWhere({ value: matchValueIn[0].toLowerCase() }); + } else if (matchValueIn.length > 1) { + this.andWhere( + 'value', + 'in', + matchValueIn.map(v => v.toLowerCase()), + ); + } + } + }); + // Explicitly evaluate matchValueExists as a boolean since it may be undefined + queryBuilder.andWhere( + 'entity_id', + matchValueExists === false ? 'not in' : 'in', + matchQuery, + ); +} + +function isEntityFilter( + filter: EntitiesSearchFilter | EntityFilter, +): filter is EntityFilter { + return filter.hasOwnProperty('anyOf'); +} + +function parseFilter( + filters: EntityFilter, + query: Knex.QueryBuilder, + db: Knex, +): Knex.QueryBuilder { + let cumulativeQuery = query; + for (const singleFilter of filters?.anyOf ?? []) { + cumulativeQuery = cumulativeQuery.orWhere(function singleFilterFn() { + for (const filter of singleFilter.allOf) { + if (isEntityFilter(filter)) { + this.andWhere(subQuery => parseFilter(filter, subQuery, db)); + } else { + addCondition(this, db, filter); + } + } + }); + } + return cumulativeQuery; +} + export class NextEntitiesCatalog implements EntitiesCatalog { constructor(private readonly database: Knex) {} @@ -80,41 +140,8 @@ export class NextEntitiesCatalog implements EntitiesCatalog { const db = this.database; let entitiesQuery = db('final_entities'); - - for (const singleFilter of request?.filter?.anyOf ?? []) { - entitiesQuery = entitiesQuery.orWhere(function singleFilterFn() { - for (const { - key, - matchValueIn, - matchValueExists, - } of singleFilter.allOf) { - // NOTE(freben): This used to be a set of OUTER JOIN, which may seem to - // make a lot of sense. However, it had abysmal performance on sqlite - // when datasets grew large, so we're using IN instead. - const matchQuery = db('search') - .select('entity_id') - .where(function keyFilter() { - this.andWhere({ key: key.toLowerCase() }); - if (matchValueExists !== false && matchValueIn) { - if (matchValueIn.length === 1) { - this.andWhere({ value: matchValueIn[0].toLowerCase() }); - } else if (matchValueIn.length > 1) { - this.andWhere( - 'value', - 'in', - matchValueIn.map(v => v.toLowerCase()), - ); - } - } - }); - // Explicitly evaluate matchValueExists as a boolean since it may be undefined - this.andWhere( - 'entity_id', - matchValueExists === false ? 'not in' : 'in', - matchQuery, - ); - } - }); + if (request?.filter) { + entitiesQuery = parseFilter(request.filter, entitiesQuery, db); } // TODO: move final_entities to use entity_ref From c74834a62d9e5208d16754eaaf42df3c228f97e4 Mon Sep 17 00:00:00 2001 From: Joon Park Date: Mon, 8 Nov 2021 12:31:28 +0000 Subject: [PATCH 092/196] Allow singleton and flexible EntityFilters. Signed-off-by: Joon Park --- .changeset/slimy-days-leave.md | 2 +- plugins/catalog-backend/api-report.md | 13 +++-- plugins/catalog-backend/src/catalog/types.ts | 7 +-- .../src/legacy/database/CommonDatabase.ts | 33 +++++++----- .../src/service/NextEntitiesCatalog.test.ts | 14 ++--- .../src/service/NextEntitiesCatalog.ts | 54 ++++++++++++++----- 6 files changed, 77 insertions(+), 46 deletions(-) diff --git a/.changeset/slimy-days-leave.md b/.changeset/slimy-days-leave.md index 250bf32962..df772d80d4 100644 --- a/.changeset/slimy-days-leave.md +++ b/.changeset/slimy-days-leave.md @@ -2,4 +2,4 @@ '@backstage/plugin-catalog-backend': patch --- -Allow nested EntityFilters +Allow singleton and flexibly nested EntityFilters diff --git a/plugins/catalog-backend/api-report.md b/plugins/catalog-backend/api-report.md index e83fa68cad..143e31bc21 100644 --- a/plugins/catalog-backend/api-report.md +++ b/plugins/catalog-backend/api-report.md @@ -870,11 +870,14 @@ export type EntityAncestryResponse = { // Warning: (ae-missing-release-tag) "EntityFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export type EntityFilter = { - anyOf: { - allOf: (EntitiesSearchFilter | EntityFilter)[]; - }[]; -}; +export type EntityFilter = + | { + allOf: EntityFilter[]; + } + | { + anyOf: EntityFilter[]; + } + | EntitiesSearchFilter; // Warning: (ae-missing-release-tag) "EntityPagination" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/plugins/catalog-backend/src/catalog/types.ts b/plugins/catalog-backend/src/catalog/types.ts index b0b06c563c..df693bfd42 100644 --- a/plugins/catalog-backend/src/catalog/types.ts +++ b/plugins/catalog-backend/src/catalog/types.ts @@ -22,9 +22,10 @@ import { Entity, EntityRelationSpec } from '@backstage/catalog-model'; * Any (at least one) of the outer sets must match, within which all of the * individual filters must match. */ -export type EntityFilter = { - anyOf: { allOf: (EntitiesSearchFilter | EntityFilter)[] }[]; -}; +export type EntityFilter = + | { allOf: EntityFilter[] } + | { anyOf: EntityFilter[] } + | EntitiesSearchFilter; /** * A pagination rule for entities. diff --git a/plugins/catalog-backend/src/legacy/database/CommonDatabase.ts b/plugins/catalog-backend/src/legacy/database/CommonDatabase.ts index 81b0612989..6a150f7583 100644 --- a/plugins/catalog-backend/src/legacy/database/CommonDatabase.ts +++ b/plugins/catalog-backend/src/legacy/database/CommonDatabase.ts @@ -46,11 +46,11 @@ import { DbPageInfo, Transaction, } from './types'; -import { - EntityPagination, - EntityFilter, - EntitiesSearchFilter, -} from '../../catalog/types'; +import { EntityPagination, EntitiesSearchFilter } from '../../catalog/types'; + +type LegacyEntityFilter = { + anyOf: { allOf: EntitiesSearchFilter[] }[]; +}; // The number of items that are sent per batch to the database layer, when // doing .batchInsert calls to knex. This needs to be low enough to not cause @@ -221,10 +221,23 @@ export class CommonDatabase implements Database { let entitiesQuery = tx('entities'); - for (const singleFilter of request?.filter?.anyOf ?? []) { + if ( + request?.filter && + (request.filter.hasOwnProperty('key') || + request.filter.hasOwnProperty('allOf')) + ) { + throw new Error( + 'Filters for the legacy CommonDatabase must obey the { anyOf: [{ allOf: [] }] } format.', + ); + } + for (const singleFilter of (request?.filter as LegacyEntityFilter)?.anyOf ?? + []) { entitiesQuery = entitiesQuery.orWhere(function singleFilterFn() { for (const filter of singleFilter.allOf) { - if (isEntityFilter(filter)) { + if ( + filter.hasOwnProperty('anyOf') || + filter.hasOwnProperty('allOf') + ) { throw new Error( 'Nested filters are not supported in the legacy CommonDatabase', ); @@ -612,9 +625,3 @@ function deduplicateRelations( r => `${r.source_full_name}:${r.target_full_name}:${r.type}`, ); } - -function isEntityFilter( - filter: EntitiesSearchFilter | EntityFilter, -): filter is EntityFilter { - return filter.hasOwnProperty('anyOf'); -} diff --git a/plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts index 0c805e1b17..32d5fa843e 100644 --- a/plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts @@ -284,9 +284,7 @@ describe('NextEntitiesCatalog', () => { key: 'spec.test', matchValueExists: true, }; - const request = { - filter: { anyOf: [{ allOf: [testFilter] }] }, - }; + const request = { filter: testFilter }; const { entities } = await catalog.entities(request); expect(entities.length).toBe(1); @@ -344,14 +342,10 @@ describe('NextEntitiesCatalog', () => { }; const request = { filter: { - anyOf: [ + allOf: [ + testFilter1, { - allOf: [ - testFilter1, - { - anyOf: [{ allOf: [testFilter2] }, { allOf: [testFilter3] }], - }, - ], + anyOf: [testFilter2, testFilter3], }, ], }, diff --git a/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts b/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts index 52d6ed1bdd..841e57c921 100644 --- a/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts @@ -107,30 +107,56 @@ function addCondition( ); } -function isEntityFilter( +function isEntitiesSearchFilter( filter: EntitiesSearchFilter | EntityFilter, -): filter is EntityFilter { +): filter is EntitiesSearchFilter { + return filter.hasOwnProperty('key'); +} + +function isAndEntityFilter( + filter: { allOf: EntityFilter[] } | EntityFilter, +): filter is { allOf: EntityFilter[] } { + return filter.hasOwnProperty('allOf'); +} + +function isOrEntityFilter( + filter: { anyOf: EntityFilter[] } | EntityFilter, +): filter is { anyOf: EntityFilter[] } { return filter.hasOwnProperty('anyOf'); } function parseFilter( - filters: EntityFilter, + filter: EntityFilter, query: Knex.QueryBuilder, db: Knex, ): Knex.QueryBuilder { - let cumulativeQuery = query; - for (const singleFilter of filters?.anyOf ?? []) { - cumulativeQuery = cumulativeQuery.orWhere(function singleFilterFn() { - for (const filter of singleFilter.allOf) { - if (isEntityFilter(filter)) { - this.andWhere(subQuery => parseFilter(filter, subQuery, db)); - } else { - addCondition(this, db, filter); - } - } + if (isEntitiesSearchFilter(filter)) { + return query.where(function filterFunction() { + addCondition(this, db, filter); }); } - return cumulativeQuery; + + if (isOrEntityFilter(filter)) { + let cumulativeQuery = query; + for (const subFilter of filter.anyOf ?? []) { + cumulativeQuery = cumulativeQuery.orWhere(subQuery => + parseFilter(subFilter, subQuery, db), + ); + } + return cumulativeQuery; + } + + if (isAndEntityFilter(filter)) { + let cumulativeQuery = query; + for (const subFilter of filter.allOf ?? []) { + cumulativeQuery = cumulativeQuery.andWhere(subQuery => + parseFilter(subFilter, subQuery, db), + ); + } + return cumulativeQuery; + } + + return query; } export class NextEntitiesCatalog implements EntitiesCatalog { From e9803242acc5d8ee555dc21eca8f0f7dabc44559 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Nov 2021 15:29:23 +0000 Subject: [PATCH 093/196] build(deps): bump passport-oauth2 from 1.6.0 to 1.6.1 Bumps [passport-oauth2](https://github.com/jaredhanson/passport-oauth2) from 1.6.0 to 1.6.1. - [Release notes](https://github.com/jaredhanson/passport-oauth2/releases) - [Changelog](https://github.com/jaredhanson/passport-oauth2/blob/master/CHANGELOG.md) - [Commits](https://github.com/jaredhanson/passport-oauth2/compare/v1.6.0...v1.6.1) --- updated-dependencies: - dependency-name: passport-oauth2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5c0a15392e..d318bcbb3d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22051,9 +22051,9 @@ passport-oauth2@1.2.0: uid2 "0.0.x" passport-oauth2@1.x.x, passport-oauth2@^1.1.2, passport-oauth2@^1.4.0, passport-oauth2@^1.5.0: - version "1.6.0" - resolved "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.6.0.tgz#5f599735e0ea40ea3027643785f81a3a9b4feb50" - integrity sha512-emXPLqLcVEcLFR/QvQXZcwLmfK8e9CqvMgmOFJxcNT3okSFMtUbRRKpY20x5euD+01uHsjjCa07DYboEeLXYiw== + version "1.6.1" + resolved "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.6.1.tgz#c5aee8f849ce8bd436c7f81d904a3cd1666f181b" + integrity sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ== dependencies: base64url "3.x.x" oauth "0.9.x" From 4de24185c85304fcb57a4142f4c61d4a7c4eb4c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Nov 2021 15:38:21 +0000 Subject: [PATCH 094/196] build(deps): bump @spotify/eslint-config-typescript Bumps [@spotify/eslint-config-typescript](https://github.com/spotify/web-scripts) from 10.0.0 to 12.0.0. - [Release notes](https://github.com/spotify/web-scripts/releases) - [Changelog](https://github.com/spotify/web-scripts/blob/master/CHANGELOG.md) - [Commits](https://github.com/spotify/web-scripts/compare/v10.0.0...v12.0.0) --- updated-dependencies: - dependency-name: "@spotify/eslint-config-typescript" dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- packages/cli/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index c023313fe7..23a2bef027 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -43,7 +43,7 @@ "@rollup/plugin-yaml": "^3.0.0", "@spotify/eslint-config-base": "^12.0.0", "@spotify/eslint-config-react": "^10.0.0", - "@spotify/eslint-config-typescript": "^10.0.0", + "@spotify/eslint-config-typescript": "^12.0.0", "@sucrase/jest-plugin": "^2.1.1", "@sucrase/webpack-loader": "^2.0.0", "@svgr/plugin-jsx": "5.5.x", diff --git a/yarn.lock b/yarn.lock index 5c0a15392e..aa7333288b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5433,10 +5433,10 @@ resolved "https://registry.npmjs.org/@spotify/eslint-config-react/-/eslint-config-react-10.0.0.tgz#6f83ada05f79b49c1f9def5b8815e3231ed24969" integrity sha512-MozX6W3aMp7EQPliuUQYI58Ni5vh65mItXMG0CgZBj0v1ZEeZVM5XS/nqhsCaIHYXskmZM2O1qqLFaEg5PqGdg== -"@spotify/eslint-config-typescript@^10.0.0": - version "10.0.0" - resolved "https://registry.npmjs.org/@spotify/eslint-config-typescript/-/eslint-config-typescript-10.0.0.tgz#4df7074f3f4ef31d76c617e55d335f9a36cfed5b" - integrity sha512-qR4WOU3gJrpz26O8BlNbXas4Yj93NeVH7yvULVYO2j9bCAEZJu2sfl1BGfOy4qAsYGutZhJtNwMqK0Rl4DcFHQ== +"@spotify/eslint-config-typescript@^12.0.0": + version "12.0.0" + resolved "https://registry.npmjs.org/@spotify/eslint-config-typescript/-/eslint-config-typescript-12.0.0.tgz#4c7af3f74a47668bec0c860b72e2a0103e78a138" + integrity sha512-nMVll8ZkN/W8+IHn6Iz3YzCKW0qhrn3TVfyxkAr3qmXm5cex+GzyUdZEuxb8rdN2inZL6A1Il2NFfO5p/UKxog== "@spotify/prettier-config@^11.0.0": version "11.0.0" From 367e3bf130a5cfa9ee0c31de32d5440250636113 Mon Sep 17 00:00:00 2001 From: Jeremy Guarini Date: Mon, 8 Nov 2021 09:29:21 -0800 Subject: [PATCH 095/196] updated timestamp conversion to use Luxon and adrs-adr012 guideline, added luxon as dependency Signed-off-by: Jeremy Guarini --- plugins/code-coverage/package.json | 1 + .../CoverageHistoryChart/CoverageHistoryChart.tsx | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/code-coverage/package.json b/plugins/code-coverage/package.json index 2b09b4d99f..8b6b4aef09 100644 --- a/plugins/code-coverage/package.json +++ b/plugins/code-coverage/package.json @@ -33,6 +33,7 @@ "@material-ui/lab": "4.0.0-alpha.57", "@material-ui/styles": "^4.11.0", "highlight.js": "^10.6.0", + "luxon": "^2.1.0", "react": "^16.13.1", "react-dom": "^16.13.1", "react-router": "6.0.0-beta.0", diff --git a/plugins/code-coverage/src/components/CoverageHistoryChart/CoverageHistoryChart.tsx b/plugins/code-coverage/src/components/CoverageHistoryChart/CoverageHistoryChart.tsx index a251bb1a68..4291599098 100644 --- a/plugins/code-coverage/src/components/CoverageHistoryChart/CoverageHistoryChart.tsx +++ b/plugins/code-coverage/src/components/CoverageHistoryChart/CoverageHistoryChart.tsx @@ -46,6 +46,8 @@ import { codeCoverageApiRef } from '../../api'; import { Progress, ResponseErrorPanel } from '@backstage/core-components'; import { useApi } from '@backstage/core-plugin-api'; +import { DateTime } from 'luxon'; + type Coverage = 'line' | 'branch'; const useStyles = makeStyles(theme => ({ @@ -71,7 +73,9 @@ const getTrendIcon = (trend: number, classes: ClassNameMap) => { // convert timestamp to human friendly form function formatDateToHuman(timeStamp: string | number) { - return new Date(timeStamp).toUTCString(); + return DateTime.fromMillis(Number(timeStamp)).toLocaleString( + DateTime.DATETIME_MED, + ); } export const CoverageHistoryChart = () => { From 564d392580b6d629788951cd458ec3bdd380960d Mon Sep 17 00:00:00 2001 From: Jeremy Guarini Date: Mon, 8 Nov 2021 09:40:10 -0800 Subject: [PATCH 096/196] Add Palo Alto Networks to Adopters page Signed-off-by: Jeremy Guarini --- ADOPTERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ADOPTERS.md b/ADOPTERS.md index 06501a2a10..f0b2fa16f6 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -63,3 +63,4 @@ | [Splunk](https://www.splunk.com) | [@tonytamsf](https://github.com/tonytamsf) | Developer portal as a centralized place to find people, services, documentation, escalation policies and give bravos. This portal is also being used as a centralized search engine for engineering specific documentation. | | [SoundCloud](https://www.soundcloud.com) | [Julio Zynger](https://github.com/julioz) | Developer portal as a [humane registry](https://martinfowler.com/bliki/HumaneRegistry.html) for the organization: catalog of people, services, documentation, feature toggles, escalation policies, etc. | | [Volvofinans Bank](https://www.volvofinans.se) | [Johan Hammar](https://github.com/johanhammar) | Developer portal enabling engineers to manage and explore software and documentation. | +| [Palo Alto Networks](https://www.paloaltonetworks.com) | [Jeremy Guarini](https://github.com/jeremyguarini), [Brian Lomeland](https://github.com/bbbmmmlll), [Palo Alto Networks](https://github.com/PaloAltoNetworks) | Developer portal, service catalog, documentation and tooling | From 1a915b49d44d04f73792d2c60f9820d5c98b26ba Mon Sep 17 00:00:00 2001 From: Jeremy Guarini Date: Mon, 8 Nov 2021 09:58:17 -0800 Subject: [PATCH 097/196] change luxon version to match others Signed-off-by: Jeremy Guarini --- plugins/code-coverage/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/code-coverage/package.json b/plugins/code-coverage/package.json index 8b6b4aef09..d01e12dff5 100644 --- a/plugins/code-coverage/package.json +++ b/plugins/code-coverage/package.json @@ -33,7 +33,7 @@ "@material-ui/lab": "4.0.0-alpha.57", "@material-ui/styles": "^4.11.0", "highlight.js": "^10.6.0", - "luxon": "^2.1.0", + "luxon": "^2.0.2", "react": "^16.13.1", "react-dom": "^16.13.1", "react-router": "6.0.0-beta.0", From 0ef78fcd750df361f64d780a43df0c16b2837e51 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Nov 2021 18:09:15 +0000 Subject: [PATCH 098/196] build(deps): bump graphiql from 1.4.2 to 1.4.7 Bumps [graphiql](https://github.com/graphql/graphiql) from 1.4.2 to 1.4.7. - [Release notes](https://github.com/graphql/graphiql/releases) - [Changelog](https://github.com/graphql/graphiql/blob/main/CHANGELOG.md) - [Commits](https://github.com/graphql/graphiql/compare/graphiql@1.4.2...graphiql@1.4.7) --- updated-dependencies: - dependency-name: graphiql dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yarn.lock | 134 +++++++++++++++++++++++++++++------------------------- 1 file changed, 73 insertions(+), 61 deletions(-) diff --git a/yarn.lock b/yarn.lock index 086ecc5f1a..b5786626dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2968,16 +2968,14 @@ stream-events "^1.0.1" xdg-basedir "^4.0.0" -"@graphiql/toolkit@^0.2.0": - version "0.2.2" - resolved "https://registry.npmjs.org/@graphiql/toolkit/-/toolkit-0.2.2.tgz#193d570afcf686c9ee61c92054c1782b9f3c1255" - integrity sha512-kDgYhqnS4p4LqSo1KvLd3tbX8Hhdj0ZrgQuGsosjjEnahiPYmmylxUL1p9lj6348OsypcTlCncGpEjeb9S3TiQ== +"@graphiql/toolkit@^0.3.2": + version "0.3.2" + resolved "https://registry.npmjs.org/@graphiql/toolkit/-/toolkit-0.3.2.tgz#551753436ada2bc27ea870b7668e5199a958ccfb" + integrity sha512-IweIT9VC8uDovg7kuCO9YqZcnIuWU8IGzrpUisXv6CUNK2Ed1ke8yERDTMmF/rjvLd2DeVZwM8iEOjEs4sUJQw== dependencies: - "@n1ru4l/push-pull-async-iterable-iterator" "^2.1.4" - graphql-ws "^4.3.2" + "@n1ru4l/push-pull-async-iterable-iterator" "^3.0.0" + graphql-ws "^4.9.0" meros "^1.1.4" - optionalDependencies: - subscriptions-transport-ws "^0.9.18" "@graphql-codegen/cli@^1.21.3": version "1.21.6" @@ -4635,10 +4633,10 @@ outvariant "^1.2.0" strict-event-emitter "^0.2.0" -"@n1ru4l/push-pull-async-iterable-iterator@^2.1.4": - version "2.1.4" - resolved "https://registry.npmjs.org/@n1ru4l/push-pull-async-iterable-iterator/-/push-pull-async-iterable-iterator-2.1.4.tgz#a90225474352f9f159bff979905f707b9c6bcf04" - integrity sha512-qLIvoOUJ+zritv+BlzcBMePKNjKQzH9Rb2i9W98YXxf/M62Lye8qH0peyiU8yJ1tL0kfulWi31BoK10E6BKJeA== +"@n1ru4l/push-pull-async-iterable-iterator@^3.0.0": + version "3.1.0" + resolved "https://registry.npmjs.org/@n1ru4l/push-pull-async-iterable-iterator/-/push-pull-async-iterable-iterator-3.1.0.tgz#be450c97d1c7cd6af1a992d53232704454345df9" + integrity sha512-K4scWxGhdQM0masHHy4gIQs2iGiLEXCrXttumknyPJqtdl4J179BjpibWSSQ1fxKdCcHgIlCTKXJU6cMM6D6Wg== "@nodelib/fs.scandir@2.1.3": version "2.1.3" @@ -11136,18 +11134,18 @@ code-point-at@^1.0.0: resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= -codemirror-graphql@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/codemirror-graphql/-/codemirror-graphql-1.0.0.tgz#ba8db60dc42b87768d643b3d19bf088f43dc5380" - integrity sha512-6LnSeRldL7psIBfjDr4xXKxCqPVYfQE4Yj04p2VpIyAIpc4MVE4VOjzvILgnmAW8X93ou5/s5gQXvB4huDwTUQ== +codemirror-graphql@^1.0.3: + version "1.1.0" + resolved "https://registry.npmjs.org/codemirror-graphql/-/codemirror-graphql-1.1.0.tgz#dd22ddf7761efa9131fa99a70a4a85fe653484e5" + integrity sha512-bp2XUg7epL07kJcylM8VCISK6X+rFsHL2lUkPQAw2v721MVhn+80FgjMP8tiZCOfJgHn1+JgsA71L5nOHWgUdA== dependencies: - graphql-language-service-interface "^2.8.2" - graphql-language-service-parser "^1.9.0" + graphql-language-service-interface "^2.9.0" + graphql-language-service-parser "^1.10.0" -codemirror@^5.54.0: - version "5.59.4" - resolved "https://registry.npmjs.org/codemirror/-/codemirror-5.59.4.tgz#bfc11c8ce32b04818e8d661bbd790a94f4b3a6f3" - integrity sha512-achw5JBgx8QPcACDDn+EUUXmCYzx/zxEtOGXyjvLEvYY8GleUrnfm5D+Zb+UjShHggXKDT9AXrbkBZX6a0YSQg== +codemirror@^5.58.2: + version "5.63.3" + resolved "https://registry.npmjs.org/codemirror/-/codemirror-5.63.3.tgz#97042a242027fe0c87c09b36bc01931d37b76527" + integrity sha512-1C+LELr+5grgJYqwZKqxrcbPsHFHapVaVAloBsFBASbpLnQqLw1U8yXJ3gT5D+rhxIiSpo+kTqN+hQ+9ialIXw== codeowners-utils@^1.0.2: version "1.0.2" @@ -13418,7 +13416,12 @@ entities@^1.1.1, entities@^1.1.2: resolved "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== -entities@^2.0.0, entities@~2.0.0: +entities@^2.0.0, entities@~2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" + integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== + +entities@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== @@ -15541,18 +15544,19 @@ grapheme-splitter@^1.0.4: integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== graphiql@^1.0.0-alpha.10: - version "1.4.2" - resolved "https://registry.npmjs.org/graphiql/-/graphiql-1.4.2.tgz#a1dc1a4d8d35f60c90d6d8a9eb62a99756e9fd9b" - integrity sha512-TQDuuU/ZqTWV1yQDpVEiKskg0IYA+Wck37DYrrFzLlpgZWRbWiyab1PyHKiRep7J540CgScBg6C/gGCymKyO3g== + version "1.4.7" + resolved "https://registry.npmjs.org/graphiql/-/graphiql-1.4.7.tgz#6a35acf0786d7518fbb986b75bf0a3d752c19c1a" + integrity sha512-oHsBTzdWTbRJhqazbjrC6wY7YInViErAeXLqetCxdFFu2Zk5FV3V3rs7KPrCyr7kM6lW0nfXMzIfKuIgxAqx7g== dependencies: - "@graphiql/toolkit" "^0.2.0" - codemirror "^5.54.0" - codemirror-graphql "^1.0.0" + "@graphiql/toolkit" "^0.3.2" + codemirror "^5.58.2" + codemirror-graphql "^1.0.3" copy-to-clipboard "^3.2.0" dset "^3.1.0" entities "^2.0.0" - graphql-language-service "^3.1.2" - markdown-it "^10.0.0" + escape-html "^1.0.3" + graphql-language-service "^3.1.6" + markdown-it "^12.2.0" graphlib@^2.1.8: version "2.1.8" @@ -15587,7 +15591,7 @@ graphql-extensions@^0.15.0: apollo-server-env "^3.1.0" apollo-server-types "^0.9.0" -graphql-language-service-interface@2.8.2, graphql-language-service-interface@^2.8.2: +graphql-language-service-interface@2.8.2, graphql-language-service-interface@^2.9.0: version "2.8.2" resolved "https://registry.npmjs.org/graphql-language-service-interface/-/graphql-language-service-interface-2.8.2.tgz#b3bb2aef7eaf0dff0b4ea419fa412c5f66fa268b" integrity sha512-otbOQmhgkAJU1QJgQkMztNku6SbJLu/uodoFOYOOtJsizTjrMs93vkYaHCcYnLA3oi1Goj27XcHjMnRCYQOZXQ== @@ -15597,7 +15601,7 @@ graphql-language-service-interface@2.8.2, graphql-language-service-interface@^2. graphql-language-service-utils "^2.5.1" vscode-languageserver-types "^3.15.1" -graphql-language-service-parser@1.9.0, graphql-language-service-parser@^1.9.0: +graphql-language-service-parser@1.9.0, graphql-language-service-parser@^1.10.0, graphql-language-service-parser@^1.9.0: version "1.9.0" resolved "https://registry.npmjs.org/graphql-language-service-parser/-/graphql-language-service-parser-1.9.0.tgz#79af21294119a0a7e81b6b994a1af36833bab724" integrity sha512-B5xPZLbBmIp0kHvpY1Z35I5DtPoDK9wGxQVRDIzcBaiIvAmlTrDvjo3bu7vKREdjFbYKvWNgrEWENuprMbF17Q== @@ -15609,6 +15613,11 @@ graphql-language-service-types@^1.8.0: resolved "https://registry.npmjs.org/graphql-language-service-types/-/graphql-language-service-types-1.8.1.tgz#963810010924f2b5eaea415d5b8eb0b7d42c479b" integrity sha512-IpYS0mEHEmRsFlq+loWCpSYYYizAID7Alri6GoFN1QqUdux+8rp1Tkp2NGsGDpDmm3Dbz5ojmJWzNWQGpuwveA== +graphql-language-service-types@^1.8.2: + version "1.8.2" + resolved "https://registry.npmjs.org/graphql-language-service-types/-/graphql-language-service-types-1.8.2.tgz#50ae56f69cc24fcfc3daa129b68b0eb9421e8578" + integrity sha512-Sj07RHnMwAhEvAt7Jdt1l/x56ZpoNh+V6g+T58CF6GiYqI5l4vXqqRB4d4xHDcNQX98GpJfnf3o8BqPgP3C5Sw== + graphql-language-service-utils@^2.5.1: version "2.5.1" resolved "https://registry.npmjs.org/graphql-language-service-utils/-/graphql-language-service-utils-2.5.1.tgz#832ad4b0a9da03fdded756932c27e057ccf71302" @@ -15617,13 +15626,23 @@ graphql-language-service-utils@^2.5.1: graphql-language-service-types "^1.8.0" nullthrows "^1.0.0" -graphql-language-service@^3.1.2: - version "3.1.2" - resolved "https://registry.npmjs.org/graphql-language-service/-/graphql-language-service-3.1.2.tgz#6f50d5d824ea09c402cb02902b10e54b9da899d5" - integrity sha512-OiOH8mVE+uotrl3jGA2Pgt9k7rrI8lgw/8p+Cf6nwyEHbmIZj37vX9KoOWgpdFhuQlw824nNxWHSbz6k90xjWQ== +graphql-language-service-utils@^2.5.3: + version "2.5.3" + resolved "https://registry.npmjs.org/graphql-language-service-utils/-/graphql-language-service-utils-2.5.3.tgz#185f4f65cf8c010871eb9405452a3a0bfdf88748" + integrity sha512-ydevEZ0AgzEKQF3hiCbLXuS0o7189Ww/T30WtCKCLaRHDYk9Yyb2PZWdhSTWLxYZTaX2TccV6NtFWvzIC7UP3g== dependencies: - graphql-language-service-interface "^2.8.2" graphql-language-service-types "^1.8.0" + nullthrows "^1.0.0" + +graphql-language-service@^3.1.6: + version "3.2.0" + resolved "https://registry.npmjs.org/graphql-language-service/-/graphql-language-service-3.2.0.tgz#e0eb6d5dea2cab92549a253d7a6b4fa0cce178b7" + integrity sha512-xM5Ua5p7ttG/oEaDy2zk35FP2O2I9qD2N0DOrjCDUVDRC06FNDG+/CvF4qX9+i8DWOI65xch5vAhSQEfS2jFsA== + dependencies: + graphql-language-service-interface "^2.9.0" + graphql-language-service-parser "^1.10.0" + graphql-language-service-types "^1.8.2" + graphql-language-service-utils "^2.5.3" graphql-request@^3.3.0: version "3.4.0" @@ -15678,16 +15697,16 @@ graphql-type-json@^0.3.2: resolved "https://registry.npmjs.org/graphql-type-json/-/graphql-type-json-0.3.2.tgz#f53a851dbfe07bd1c8157d24150064baab41e115" integrity sha512-J+vjof74oMlCWXSvt0DOf2APEdZOCdubEvGDUAlqH//VBYcOYsGgRW7Xzorr44LvkjiuvecWc8fChxuZZbChtg== -graphql-ws@^4.3.2: - version "4.9.0" - resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-4.9.0.tgz#5cfd8bb490b35e86583d8322f5d5d099c26e365c" - integrity sha512-sHkK9+lUm20/BGawNEWNtVAeJzhZeBg21VmvmLoT5NdGVeZWv5PdIhkcayQIAgjSyyQ17WMKmbDijIPG2On+Ag== - graphql-ws@^4.4.1: version "4.7.0" resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-4.7.0.tgz#b323fbf35a3736eed85dac24c0054d6d10c93e62" integrity sha512-Md8SsmC9ZlsogFPd3Ot8HbIAAqsHh8Xoq7j4AmcIat1Bh6k91tjVyQvA0Au1/BolXSYq+RDvib6rATU2Hcf1Xw== +graphql-ws@^4.9.0: + version "4.9.0" + resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-4.9.0.tgz#5cfd8bb490b35e86583d8322f5d5d099c26e365c" + integrity sha512-sHkK9+lUm20/BGawNEWNtVAeJzhZeBg21VmvmLoT5NdGVeZWv5PdIhkcayQIAgjSyyQ17WMKmbDijIPG2On+Ag== + graphql@15.5.0: version "15.5.0" resolved "https://registry.npmjs.org/graphql/-/graphql-15.5.0.tgz#39d19494dbe69d1ea719915b578bf920344a69d5" @@ -18816,13 +18835,6 @@ lines-and-columns@^1.1.6: resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= -linkify-it@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf" - integrity sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw== - dependencies: - uc.micro "^1.0.1" - linkify-it@^3.0.1: version "3.0.2" resolved "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.2.tgz#f55eeb8bc1d3ae754049e124ab3bb56d97797fb8" @@ -19518,17 +19530,6 @@ markdown-escapes@^1.0.0: resolved "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== -markdown-it@^10.0.0: - version "10.0.0" - resolved "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc" - integrity sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg== - dependencies: - argparse "^1.0.7" - entities "~2.0.0" - linkify-it "^2.0.0" - mdurl "^1.0.1" - uc.micro "^1.0.5" - markdown-it@^11.0.1: version "11.0.1" resolved "https://registry.npmjs.org/markdown-it/-/markdown-it-11.0.1.tgz#b54f15ec2a2193efa66dda1eb4173baea08993d6" @@ -19540,6 +19541,17 @@ markdown-it@^11.0.1: mdurl "^1.0.1" uc.micro "^1.0.5" +markdown-it@^12.2.0: + version "12.2.0" + resolved "https://registry.npmjs.org/markdown-it/-/markdown-it-12.2.0.tgz#091f720fd5db206f80de7a8d1f1a7035fd0d38db" + integrity sha512-Wjws+uCrVQRqOoJvze4HCqkKl1AsSh95iFAeQDwnyfxM09divCBSXlDR1uTvyUP3Grzpn4Ru8GeCxYPM8vkCQg== + dependencies: + argparse "^2.0.1" + entities "~2.1.0" + linkify-it "^3.0.1" + mdurl "^1.0.1" + uc.micro "^1.0.5" + markdown-table@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz#194a90ced26d31fe753d8b9434430214c011865b" From 1b8efd52f0b1aab5c7860c0ef71675bdd49ce95e Mon Sep 17 00:00:00 2001 From: Jeremy Guarini Date: Mon, 8 Nov 2021 10:33:40 -0800 Subject: [PATCH 099/196] address vale issue with href Signed-off-by: Jeremy Guarini --- .changeset/eight-months-agree.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/eight-months-agree.md b/.changeset/eight-months-agree.md index 372c0b624c..0f117d8a40 100644 --- a/.changeset/eight-months-agree.md +++ b/.changeset/eight-months-agree.md @@ -14,6 +14,6 @@ Adds the following to the project list page: Makes breadcrumb a link back to project list for the project details and new project views. -In project list page, updates New project button to use RouterLink instead of href to avoid login prompt. +In project list page, updates New project button to use RouterLink instead of `href` to avoid login prompt. In project details view, links to project details and logs now work, clicking on these will open the project or logs in GCP in new tab. From 5725f87e4c7a138e95758ee913f12f3db623759c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 8 Nov 2021 20:56:03 +0100 Subject: [PATCH 100/196] create-app: default to making new plugins private Signed-off-by: Patrik Oldsberg --- .changeset/nine-dots-suffer.md | 14 ++++++++++++++ .../templates/default-app/package.json.hbs | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 .changeset/nine-dots-suffer.md diff --git a/.changeset/nine-dots-suffer.md b/.changeset/nine-dots-suffer.md new file mode 100644 index 0000000000..3efd307a10 --- /dev/null +++ b/.changeset/nine-dots-suffer.md @@ -0,0 +1,14 @@ +--- +'@backstage/create-app': patch +--- + +Updated the app template to no longer include the `--no-private` flag for the `create-plugin` command. + +To apply this change to an existing application, remove the `--no-private` flag from the `create-plugin` command in the root `package.json`: + +```diff + "prettier:check": "prettier --check .", +- "create-plugin": "backstage-cli create-plugin --scope internal --no-private", ++ "create-plugin": "backstage-cli create-plugin --scope internal", + "remove-plugin": "backstage-cli remove-plugin" +``` diff --git a/packages/create-app/templates/default-app/package.json.hbs b/packages/create-app/templates/default-app/package.json.hbs index 5ed066a30b..1db9a98c41 100644 --- a/packages/create-app/templates/default-app/package.json.hbs +++ b/packages/create-app/templates/default-app/package.json.hbs @@ -20,7 +20,7 @@ "lint": "lerna run lint --since origin/master --", "lint:all": "lerna run lint --", "prettier:check": "prettier --check .", - "create-plugin": "backstage-cli create-plugin --scope internal --no-private", + "create-plugin": "backstage-cli create-plugin --scope internal", "remove-plugin": "backstage-cli remove-plugin" }, "resolutions": { From 01df5bc4362b3ddc96e88d6070994ecdb9bf825c Mon Sep 17 00:00:00 2001 From: Tim Jacomb Date: Fri, 5 Nov 2021 14:24:43 +0000 Subject: [PATCH 101/196] Additional normalisation Signed-off-by: Tim Jacomb --- .../src/microsoftGraph/helper.test.ts | 8 ++++++++ .../src/microsoftGraph/helper.ts | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/helper.test.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/helper.test.ts index cb9c73d7b1..bf13859c41 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/helper.test.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/helper.test.ts @@ -21,6 +21,14 @@ describe('normalizeEntityName', () => { expect(normalizeEntityName('User Name')).toBe('user_name'); }); + it('should normalize complex name to valid entity name', () => { + expect(normalizeEntityName('User (Name)')).toBe('user_name'); + }); + + it('should normalize complex name to valid entity name without extra underscore', () => { + expect(normalizeEntityName('User :(Name:)')).toBe('user_name'); + }); + it('should normalize e-mail to valid entity name', () => { expect(normalizeEntityName('user.name@example.com')).toBe( 'user.name_example.com', diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/helper.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/helper.ts index c1d2606018..41dbc1aed8 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/helper.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/helper.ts @@ -15,8 +15,21 @@ */ export function normalizeEntityName(name: string): string { - return name + let cleaned = name .trim() .toLocaleLowerCase() .replace(/[^a-zA-Z0-9_\-\.]/g, '_'); + + // invalid to end with _ + while (cleaned.endsWith('_')) { + cleaned = cleaned.substring(0, cleaned.length - 1); + } + + // cleans up format for groups like 'my group (Reader)' + while (cleaned.includes('__')) { + // replaceAll from node.js >= 15 + cleaned = cleaned.replace('__', '_'); + } + + return cleaned; } From d393a3bd2a190daba0dedc7c4c62beba961a6646 Mon Sep 17 00:00:00 2001 From: Jason Nguyen Date: Wed, 3 Nov 2021 14:16:19 -0600 Subject: [PATCH 102/196] [docs] clarify example showing that parameters types propagate Signed-off-by: Jason Nguyen --- .../software-templates/migrating-from-v1beta2-to-v1beta3.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/features/software-templates/migrating-from-v1beta2-to-v1beta3.md b/docs/features/software-templates/migrating-from-v1beta2-to-v1beta3.md index 280eab083d..4cc9a6a8e6 100644 --- a/docs/features/software-templates/migrating-from-v1beta2-to-v1beta3.md +++ b/docs/features/software-templates/migrating-from-v1beta2-to-v1beta3.md @@ -125,8 +125,8 @@ input schema. input: - address: '{{ json parameters.address }}' + address: ${{ parameters.address }} -- number: '{{ parameters.number }}' -+ number: ${{ parameters.number }} # this will now make sure that the type of number is a number 🙏 +- test: '{{ parameters.test }}' ++ test: ${{ parameters.test }} # this will now make sure that the type of test is a number 🙏 ``` ## `parseRepoUrl` is now a `filter` From d44f3414e8a876af2f07f350107dda79417b6ef6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Nov 2021 21:51:52 +0000 Subject: [PATCH 103/196] build(deps): bump apollo-server from 2.21.0 to 2.25.3 Bumps [apollo-server](https://github.com/apollographql/apollo-server/tree/HEAD/packages/apollo-server) from 2.21.0 to 2.25.3. - [Release notes](https://github.com/apollographql/apollo-server/releases) - [Changelog](https://github.com/apollographql/apollo-server/blob/main/CHANGELOG.md) - [Commits](https://github.com/apollographql/apollo-server/commits/apollo-server@2.25.3/packages/apollo-server) --- updated-dependencies: - dependency-name: apollo-server dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yarn.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/yarn.lock b/yarn.lock index b5786626dd..98e8a242cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9011,10 +9011,10 @@ apollo-server-caching@^0.7.0: dependencies: lru-cache "^6.0.0" -apollo-server-core@^2.21.0, apollo-server-core@^2.25.2: - version "2.25.2" - resolved "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-2.25.2.tgz#ff65da5e512d9b5ca54c8e5e8c78ee28b5987247" - integrity sha512-lrohEjde2TmmDTO7FlOs8x5QQbAS0Sd3/t0TaK2TWaodfzi92QAvIsq321Mol6p6oEqmjm8POIDHW1EuJd7XMA== +apollo-server-core@^2.25.3: + version "2.25.3" + resolved "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-2.25.3.tgz#1a649fd14b3928f5b6e65f0002b380fcfde56862" + integrity sha512-Midow3uZoJ9TjFNeCNSiWElTVZlvmB7G7tG6PPoxIR9Px90/v16Q6EzunDIO0rTJHRC3+yCwZkwtf8w2AcP0sA== dependencies: "@apollographql/apollo-tools" "^0.5.0" "@apollographql/graphql-playground-html" "1.6.27" @@ -9055,10 +9055,10 @@ apollo-server-errors@^2.5.0: resolved "https://registry.npmjs.org/apollo-server-errors/-/apollo-server-errors-2.5.0.tgz#5d1024117c7496a2979e3e34908b5685fe112b68" integrity sha512-lO5oTjgiC3vlVg2RKr3RiXIIQ5pGXBFxYGGUkKDhTud3jMIhs+gel8L8zsEjKaKxkjHhCQAA/bcEfYiKkGQIvA== -apollo-server-express@^2.16.1, apollo-server-express@^2.21.0: - version "2.25.2" - resolved "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-2.25.2.tgz#58cd819694ff4c2dec6945a95c5dff6aa2719ef6" - integrity sha512-A2gF2e85vvDugPlajbhr0A14cDFDIGX0mteNOJ8P3Z3cIM0D4hwrWxJidI+SzobefDIyIHu1dynFedJVhV0euQ== +apollo-server-express@^2.16.1, apollo-server-express@^2.25.3: + version "2.25.3" + resolved "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-2.25.3.tgz#33fe0dae27fa71c8710e714efd93451bf2eb105f" + integrity sha512-tTFYn0oKH2qqLwVj7Ez2+MiKleXACODiGh5IxsB7VuYCPMAi9Yl8iUSlwTjQUvgCWfReZjnf0vFL2k5YhDlrtQ== dependencies: "@apollographql/graphql-playground-html" "1.6.27" "@types/accepts" "^1.3.5" @@ -9067,7 +9067,7 @@ apollo-server-express@^2.16.1, apollo-server-express@^2.21.0: "@types/express" "^4.17.12" "@types/express-serve-static-core" "^4.17.21" accepts "^1.3.5" - apollo-server-core "^2.25.2" + apollo-server-core "^2.25.3" apollo-server-types "^0.9.0" body-parser "^1.18.3" cors "^2.8.5" @@ -9095,12 +9095,12 @@ apollo-server-types@^0.9.0: apollo-server-env "^3.1.0" apollo-server@^2.16.1: - version "2.21.0" - resolved "https://registry.npmjs.org/apollo-server/-/apollo-server-2.21.0.tgz#4e62131885b4a8a26bb8b5e77177bd0d4d210852" - integrity sha512-OqngjOSB0MEH6VKGWHcrqt4y39HlhYh9CrMvn4PhadTt53IPYRmBglk5qSRA8xMorGqy60iKrOReqj5YfCjTOg== + version "2.25.3" + resolved "https://registry.npmjs.org/apollo-server/-/apollo-server-2.25.3.tgz#2e5db9ce5217389625ac5014551dcbdeeedcd1d8" + integrity sha512-+eUY2//DLkU7RkJLn6CTl1P89/ZMHuUQnWqv8La2iJ2hLT7Me+nMx+hgHl3LqlT/qDstQ8qA45T85FuCayplmQ== dependencies: - apollo-server-core "^2.21.0" - apollo-server-express "^2.21.0" + apollo-server-core "^2.25.3" + apollo-server-express "^2.25.3" express "^4.0.0" graphql-subscriptions "^1.0.0" graphql-tools "^4.0.8" From 5a85b802f33c5575453cf3fd1740102bc8d114b7 Mon Sep 17 00:00:00 2001 From: Jeremy Guarini Date: Mon, 8 Nov 2021 15:18:32 -0800 Subject: [PATCH 104/196] display X-axis in ascending order Signed-off-by: Jeremy Guarini --- .changeset/perfect-avocados-mate.md | 5 +++++ .../CoverageHistoryChart/CoverageHistoryChart.tsx | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 .changeset/perfect-avocados-mate.md diff --git a/.changeset/perfect-avocados-mate.md b/.changeset/perfect-avocados-mate.md new file mode 100644 index 0000000000..9394b57449 --- /dev/null +++ b/.changeset/perfect-avocados-mate.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-code-coverage': patch +--- + +Make dates in X-Axis sort in ascending order diff --git a/plugins/code-coverage/src/components/CoverageHistoryChart/CoverageHistoryChart.tsx b/plugins/code-coverage/src/components/CoverageHistoryChart/CoverageHistoryChart.tsx index 4291599098..e713eb7c6e 100644 --- a/plugins/code-coverage/src/components/CoverageHistoryChart/CoverageHistoryChart.tsx +++ b/plugins/code-coverage/src/components/CoverageHistoryChart/CoverageHistoryChart.tsx @@ -158,7 +158,11 @@ export const CoverageHistoryChart = () => { margin={{ right: 48, top: 32 }} > - + From 9f3dd76bc7ba5a5db8305f952f1c4d7f1921a24c Mon Sep 17 00:00:00 2001 From: rgorkovenko Date: Tue, 9 Nov 2021 10:07:22 +0300 Subject: [PATCH 105/196] fix: kuber plugin pass id token on getClusters req Signed-off-by: rgorkovenko --- .changeset/clean-fans-change.md | 5 +++++ plugins/kubernetes/src/api/KubernetesBackendClient.ts | 4 ++++ 2 files changed, 9 insertions(+) create mode 100644 .changeset/clean-fans-change.md diff --git a/.changeset/clean-fans-change.md b/.changeset/clean-fans-change.md new file mode 100644 index 0000000000..fad4a9cfa6 --- /dev/null +++ b/.changeset/clean-fans-change.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes': patch +--- + +fix: kubernetes plugin shall pass id token on get clusters request if possible diff --git a/plugins/kubernetes/src/api/KubernetesBackendClient.ts b/plugins/kubernetes/src/api/KubernetesBackendClient.ts index 3f6a0394d3..4d44c021c4 100644 --- a/plugins/kubernetes/src/api/KubernetesBackendClient.ts +++ b/plugins/kubernetes/src/api/KubernetesBackendClient.ts @@ -79,10 +79,14 @@ export class KubernetesBackendClient implements KubernetesApi { } async getClusters(): Promise<{ name: string; authProvider: string }[]> { + const idToken = await this.identityApi.getIdToken(); const url = `${await this.discoveryApi.getBaseUrl('kubernetes')}/clusters`; const response = await fetch(url, { method: 'GET', + headers: { + ...(idToken && { Authorization: `Bearer ${idToken}` }), + }, }); return (await this.handleResponse(response)).items; From 910a6a435e4226474fab557120e8c70b06281aef Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Tue, 9 Nov 2021 08:04:51 +0000 Subject: [PATCH 106/196] feat: Added new types to `@backstage/plugin-azure-devops-common`. Signed-off-by: Marley Powell --- .changeset/many-mayflies-notice.md | 5 ++- plugins/azure-devops-common/api-report.md | 43 +++++++++++++++++++++ plugins/azure-devops-common/src/types.ts | 46 +++++++++++++++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/.changeset/many-mayflies-notice.md b/.changeset/many-mayflies-notice.md index cd4f91fa93..a96d1fcce5 100644 --- a/.changeset/many-mayflies-notice.md +++ b/.changeset/many-mayflies-notice.md @@ -1,6 +1,9 @@ --- '@backstage/plugin-azure-devops': patch '@backstage/plugin-azure-devops-backend': minor +'@backstage/plugin-azure-devops-common': patch --- -refactor(`@backstage/plugin-azure-devops`): Consume types from `@backstage/plugin-azure-devops-common`. Stop re-exporting types from `@backstage/plugin-azure-devops-backend`. +refactor(`@backstage/plugin-azure-devops`): Consume types from `@backstage/plugin-azure-devops-common`. +Stop re-exporting types from `@backstage/plugin-azure-devops-backend`. +Added new types to `@backstage/plugin-azure-devops-common`. diff --git a/plugins/azure-devops-common/api-report.md b/plugins/azure-devops-common/api-report.md index 0bc6568189..f03c0b4059 100644 --- a/plugins/azure-devops-common/api-report.md +++ b/plugins/azure-devops-common/api-report.md @@ -27,6 +27,42 @@ export enum BuildStatus { Postponed = 8, } +// Warning: (ae-missing-release-tag) "PullRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PullRequest = { + pullRequestId?: number; + repoName?: string; + title?: string; + uniqueName?: string; + createdBy?: string; + creationDate?: Date; + sourceRefName?: string; + targetRefName?: string; + status?: PullRequestStatus; + isDraft?: boolean; + link: string; +}; + +// Warning: (ae-missing-release-tag) "PullRequestOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PullRequestOptions = { + top: number; + status: PullRequestStatus; +}; + +// Warning: (ae-missing-release-tag) "PullRequestStatus" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export enum PullRequestStatus { + Abandoned = 2, + Active = 1, + All = 4, + Completed = 3, + NotSet = 0, +} + // Warning: (ae-missing-release-tag) "RepoBuild" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -43,5 +79,12 @@ export type RepoBuild = { uniqueName?: string; }; +// Warning: (ae-missing-release-tag) "RepoBuildOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type RepoBuildOptions = { + top?: number; +}; + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/azure-devops-common/src/types.ts b/plugins/azure-devops-common/src/types.ts index d7736d03e1..eacc2af30d 100644 --- a/plugins/azure-devops-common/src/types.ts +++ b/plugins/azure-devops-common/src/types.ts @@ -80,3 +80,49 @@ export type RepoBuild = { source: string; uniqueName?: string; }; + +export type RepoBuildOptions = { + top?: number; +}; + +export enum PullRequestStatus { + /** + * Status not set. Default state. + */ + NotSet = 0, + /** + * Pull request is active. + */ + Active = 1, + /** + * Pull request is abandoned. + */ + Abandoned = 2, + /** + * Pull request is completed. + */ + Completed = 3, + /** + * Used in pull request search criteria to include all statuses. + */ + All = 4, +} + +export type PullRequest = { + pullRequestId?: number; + repoName?: string; + title?: string; + uniqueName?: string; + createdBy?: string; + creationDate?: Date; + sourceRefName?: string; + targetRefName?: string; + status?: PullRequestStatus; + isDraft?: boolean; + link: string; +}; + +export type PullRequestOptions = { + top: number; + status: PullRequestStatus; +}; From e059aea7b9b5a93616b35216cefadfb7f35e5c79 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Tue, 9 Nov 2021 09:36:42 +0100 Subject: [PATCH 107/196] core-plugin-api: Deprecate unused types Signed-off-by: Johan Haals --- .changeset/young-steaks-punch.md | 5 +++++ packages/core-plugin-api/src/apis/system/types.ts | 2 ++ 2 files changed, 7 insertions(+) create mode 100644 .changeset/young-steaks-punch.md diff --git a/.changeset/young-steaks-punch.md b/.changeset/young-steaks-punch.md new file mode 100644 index 0000000000..55997af565 --- /dev/null +++ b/.changeset/young-steaks-punch.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-plugin-api': patch +--- + +Deprecate unused ApiRef types diff --git a/packages/core-plugin-api/src/apis/system/types.ts b/packages/core-plugin-api/src/apis/system/types.ts index d7801ea5c1..a449e83450 100644 --- a/packages/core-plugin-api/src/apis/system/types.ts +++ b/packages/core-plugin-api/src/apis/system/types.ts @@ -35,6 +35,7 @@ export type AnyApiRef = ApiRef; * Transforms ApiRef type into its inner API type. * * @public + * @deprecated unused type. */ export type ApiRefType = T extends ApiRef ? U : never; @@ -51,6 +52,7 @@ export type TypesToApiRefs = { [key in keyof T]: ApiRef }; * Reverse type transform of {@link TypesToApiRefs}. * * @public + * @deprecated unused type. */ export type ApiRefsToTypes }> = { [key in keyof T]: ApiRefType; From de527c6e33f1a7ddb9d12a3c280fdbf62c86c48e Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Tue, 9 Nov 2021 09:49:55 +0100 Subject: [PATCH 108/196] api report Signed-off-by: Johan Haals --- packages/core-plugin-api/api-report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core-plugin-api/api-report.md b/packages/core-plugin-api/api-report.md index 3225c1a28f..acfc8d4a45 100644 --- a/packages/core-plugin-api/api-report.md +++ b/packages/core-plugin-api/api-report.md @@ -142,7 +142,7 @@ export type ApiRefConfig = { description?: string; }; -// @public +// @public @deprecated export type ApiRefsToTypes< T extends { [key in string]: ApiRef; @@ -151,7 +151,7 @@ export type ApiRefsToTypes< [key in keyof T]: ApiRefType; }; -// @public +// @public @deprecated export type ApiRefType = T extends ApiRef ? U : never; // @public From 6160f89865d19005b6a59531c0e8b098b1f69300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 9 Nov 2021 10:10:13 +0100 Subject: [PATCH 109/196] fake the test config to get rid of the timeouts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- plugins/proxy-backend/src/service/router.test.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/plugins/proxy-backend/src/service/router.test.ts b/plugins/proxy-backend/src/service/router.test.ts index 11a1068b68..df999dbe2a 100644 --- a/plugins/proxy-backend/src/service/router.test.ts +++ b/plugins/proxy-backend/src/service/router.test.ts @@ -14,11 +14,8 @@ * limitations under the License. */ -import { - getVoidLogger, - loadBackendConfig, - SingleHostDiscovery, -} from '@backstage/backend-common'; +import { getVoidLogger, SingleHostDiscovery } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; import { Request, Response } from 'express'; import * as http from 'http'; import { createProxyMiddleware, Options } from 'http-proxy-middleware'; @@ -35,7 +32,14 @@ const mockCreateProxyMiddleware = createProxyMiddleware as jest.MockedFunction< describe('createRouter', () => { it('works', async () => { const logger = getVoidLogger(); - const config = await loadBackendConfig({ logger, argv: [] }); + const config = new ConfigReader({ + backend: { + baseUrl: 'https://example.com:7000', + listen: { + port: 7000, + }, + }, + }); const discovery = SingleHostDiscovery.fromConfig(config); const router = await createRouter({ config, From 9df71040e347bb13ead3f54a600db9af6ab2a41c Mon Sep 17 00:00:00 2001 From: Tim Jacomb Date: Tue, 9 Nov 2021 09:20:49 +0000 Subject: [PATCH 110/196] Remove mailEnabled filter Signed-off-by: Tim Jacomb --- .../catalog-backend-module-msgraph/src/microsoftGraph/read.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts index 77bea268ee..7dd6f83e2e 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts @@ -252,7 +252,7 @@ export async function readMicrosoftGraphOrganization( } function extractGroupName(group: MicrosoftGraph.Group): string { - if (group.securityEnabled && !group.mailEnabled) { + if (group.securityEnabled) { return group.displayName as string; } return (group.mailNickname || group.displayName) as string; From afe864b872f3d93654639b44d2f1a652e001c843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 9 Nov 2021 10:35:13 +0100 Subject: [PATCH 111/196] get rid of config loading here too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../src/service/KubernetesBuilder.test.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts b/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts index 1b90e65fd7..de0dcdf373 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { Config } from '@backstage/config'; -import { getVoidLogger, loadBackendConfig } from '@backstage/backend-common'; +import { getVoidLogger } from '@backstage/backend-common'; +import { Config, ConfigReader } from '@backstage/config'; +import { ObjectsByEntityResponse } from '@backstage/plugin-kubernetes-common'; import express from 'express'; import request from 'supertest'; -import { KubernetesFanOutHandler } from './KubernetesFanOutHandler'; import { ClusterDetails, FetchResponseWrapper, @@ -27,8 +27,8 @@ import { KubernetesServiceLocator, ObjectFetchParams, } from '../types/types'; -import { ObjectsByEntityResponse } from '@backstage/plugin-kubernetes-common'; import { KubernetesBuilder } from './KubernetesBuilder'; +import { KubernetesFanOutHandler } from './KubernetesFanOutHandler'; describe('KubernetesBuilder', () => { let app: express.Express; @@ -37,7 +37,12 @@ describe('KubernetesBuilder', () => { beforeAll(async () => { const logger = getVoidLogger(); - config = await loadBackendConfig({ logger, argv: [] }); + config = new ConfigReader({ + kubernetes: { + serviceLocatorMethod: { type: 'multiTenant' }, + clusterLocatorMethods: [{ type: 'config', clusters: [] }], + }, + }); const clusters: ClusterDetails[] = [ { From 00af71a8fc924d603b93ce91387e53cbc1cffff9 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Tue, 9 Nov 2021 10:42:21 +0100 Subject: [PATCH 112/196] Filter out undefined keys from settings Signed-off-by: Marcus Eide --- .../auth-backend/src/identity/KeyStores.ts | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/plugins/auth-backend/src/identity/KeyStores.ts b/plugins/auth-backend/src/identity/KeyStores.ts index 74e60a0502..3d238adba9 100644 --- a/plugins/auth-backend/src/identity/KeyStores.ts +++ b/plugins/auth-backend/src/identity/KeyStores.ts @@ -15,6 +15,7 @@ */ import { Logger } from 'winston'; +import { pickBy } from 'lodash'; import { PluginDatabaseManager } from '@backstage/backend-common'; import { Config } from '@backstage/config'; @@ -64,16 +65,20 @@ export class KeyStores { if (provider === 'firestore') { const settings = ks?.getConfig(provider); - const keyStore = await FirestoreKeyStore.create({ - projectId: settings?.getOptionalString('projectId'), - keyFilename: settings?.getOptionalString('keyFilename'), - host: settings?.getOptionalString('host'), - port: settings?.getOptionalNumber('port'), - ssl: settings?.getOptionalBoolean('ssl'), - path: settings?.getOptionalString('path'), - timeout: settings?.getOptionalNumber('timeout'), - }); - + const keyStore = await FirestoreKeyStore.create( + pickBy( + { + projectId: settings?.getOptionalString('projectId'), + keyFilename: settings?.getOptionalString('keyFilename'), + host: settings?.getOptionalString('host'), + port: settings?.getOptionalNumber('port'), + ssl: settings?.getOptionalBoolean('ssl'), + path: settings?.getOptionalString('path'), + timeout: settings?.getOptionalNumber('timeout'), + }, + value => value !== undefined, + ), + ); await FirestoreKeyStore.verifyConnection(keyStore, logger); return keyStore; From 5ee31f860b412566ffaddd1c49115cb47ffe34ff Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Tue, 9 Nov 2021 10:44:26 +0100 Subject: [PATCH 113/196] Add changeset Signed-off-by: Marcus Eide --- .changeset/modern-beers-tickle.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/modern-beers-tickle.md diff --git a/.changeset/modern-beers-tickle.md b/.changeset/modern-beers-tickle.md new file mode 100644 index 0000000000..6ad9cc872d --- /dev/null +++ b/.changeset/modern-beers-tickle.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Only use settings that have a value when creating a new FirestoreKeyStore instance From 4fa7934710a162b24e730a2655d60fee52c4418e Mon Sep 17 00:00:00 2001 From: Harry Hogg Date: Tue, 9 Nov 2021 10:02:36 +0000 Subject: [PATCH 114/196] Added runson command to fix the workflow config Signed-off-by: Harry Hogg --- .github/workflows/snyk-github-issue-sync.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/snyk-github-issue-sync.yml b/.github/workflows/snyk-github-issue-sync.yml index 34eb5c1ca4..4f29e43215 100644 --- a/.github/workflows/snyk-github-issue-sync.yml +++ b/.github/workflows/snyk-github-issue-sync.yml @@ -6,6 +6,7 @@ on: jobs: sync: + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Create Snyk report From ce4457bb305088eca9700c03bb4fd04cfcbbc93d Mon Sep 17 00:00:00 2001 From: Harry Hogg Date: Tue, 9 Nov 2021 12:36:01 +0000 Subject: [PATCH 115/196] Fixes the Snyk github issue workflow to continue when the Snyk CLI exits with an error Signed-off-by: Harry Hogg --- .github/workflows/snyk-github-issue-sync.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/snyk-github-issue-sync.yml b/.github/workflows/snyk-github-issue-sync.yml index 4f29e43215..5f49be4e1a 100644 --- a/.github/workflows/snyk-github-issue-sync.yml +++ b/.github/workflows/snyk-github-issue-sync.yml @@ -11,6 +11,7 @@ jobs: - uses: actions/checkout@v2 - name: Create Snyk report uses: snyk/actions/node@master + continue-on-error: true # Snyk CLI exits with error when vulnerabilities are found with: args: > --yarn-workspaces From 327e79c2b85718f1dbc38b1292e2fa9c1b04d173 Mon Sep 17 00:00:00 2001 From: Elliot Greenwood Date: Tue, 9 Nov 2021 13:26:28 +0000 Subject: [PATCH 116/196] Allow for material UI cellStyle function from the backstage Table component Signed-off-by: Elliot Greenwood --- .../src/components/Table/Table.test.tsx | 115 ++++++++++++++++-- .../src/components/Table/Table.tsx | 20 ++- 2 files changed, 122 insertions(+), 13 deletions(-) diff --git a/packages/core-components/src/components/Table/Table.test.tsx b/packages/core-components/src/components/Table/Table.test.tsx index fb231fde31..12431dea59 100644 --- a/packages/core-components/src/components/Table/Table.test.tsx +++ b/packages/core-components/src/components/Table/Table.test.tsx @@ -18,17 +18,18 @@ import React from 'react'; import { renderInTestApp } from '@backstage/test-utils'; import { Table } from './Table'; +const column1 = { + title: 'Column 1', + field: 'col1', +}; + +const column2 = { + title: 'Column 2', + field: 'col2', +}; + const minProps = { - columns: [ - { - title: 'Column 1', - field: 'col1', - }, - { - title: 'Column 2', - field: 'col2', - }, - ], + columns: [column1, column2], data: [ { col1: 'first value, first row', @@ -47,6 +48,100 @@ describe('', () => { expect(rendered.getByText('second value, second row')).toBeInTheDocument(); }); + describe('with style rows', () => { + describe('with CSS Properties object', () => { + const styledColumn2 = { + ...column2, + cellStyle: { + color: 'blue', + }, + }; + + it('renders non-highlighted correctly', async () => { + const columns = [column1, styledColumn2]; + + const rendered = await renderInTestApp( +
, + ); + expect(rendered.getByText('second value, first row')).toHaveStyle({ + color: 'blue', + }); + }); + + it('renders highlighted column correctly', async () => { + const columns = [ + column1, + { + ...styledColumn2, + highlight: true, + }, + ]; + + const rendered = await renderInTestApp( +
, + ); + expect(rendered.getByText('second value, first row')).toHaveStyle({ + color: 'blue', + 'font-weight': 700, + }); + }); + }); + + describe('with CSS Properties function', () => { + const styledColumn2 = { + ...column2, + cellStyle: ( + _data: any, + rowData: any & { tableData: { id: number } }, + ) => { + return rowData.tableData.id % 2 === 0 + ? { + color: 'green', + } + : { + color: 'red', + }; + }, + }; + + it('renders non-highlighted columns correctly', async () => { + const columns = [column1, styledColumn2]; + + const rendered = await renderInTestApp( +
, + ); + expect(rendered.getByText('second value, first row')).toHaveStyle({ + color: 'green', + }); + expect(rendered.getByText('second value, second row')).toHaveStyle({ + color: 'red', + }); + }); + + it('renders highlighted columns correctly', async () => { + const columns = [ + column1, + { + ...styledColumn2, + highlight: true, + }, + ]; + + const rendered = await renderInTestApp( +
, + ); + expect(rendered.getByText('second value, first row')).toHaveStyle({ + color: 'green', + 'font-weight': 700, + }); + expect(rendered.getByText('second value, second row')).toHaveStyle({ + color: 'red', + 'font-weight': 700, + }); + }); + }); + }); + it('renders with subtitle', async () => { const rendered = await renderInTestApp(
, diff --git a/packages/core-components/src/components/Table/Table.tsx b/packages/core-components/src/components/Table/Table.tsx index 234b64ec54..fe722f225a 100644 --- a/packages/core-components/src/components/Table/Table.tsx +++ b/packages/core-components/src/components/Table/Table.tsx @@ -168,12 +168,26 @@ function convertColumns( ): TableColumn[] { return columns.map(column => { const headerStyle: React.CSSProperties = {}; - const cellStyle: React.CSSProperties = - typeof column.cellStyle === 'object' ? column.cellStyle : {}; + + let cellStyle = column.cellStyle || {}; if (column.highlight) { headerStyle.color = theme.palette.textContrast; - cellStyle.fontWeight = theme.typography.fontWeightBold; + + if (typeof cellStyle === 'object') { + (cellStyle as React.CSSProperties).fontWeight = + theme.typography.fontWeightBold; + } else { + const cellStyleFn = cellStyle as ( + data: any, + rowData: T, + column?: Column, + ) => React.CSSProperties; + cellStyle = (data, rowData, rowColumn) => { + const style = cellStyleFn(data, rowData, rowColumn); + return { ...style, fontWeight: theme.typography.fontWeightBold }; + }; + } } return { From cf090d25e7da6d180b437888c5dcfdb9fa6e6a49 Mon Sep 17 00:00:00 2001 From: Elliot Greenwood Date: Tue, 9 Nov 2021 13:27:07 +0000 Subject: [PATCH 117/196] Add StyledTable to the Table stories Signed-off-by: Elliot Greenwood --- .../src/components/Table/Table.stories.tsx | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/packages/core-components/src/components/Table/Table.stories.tsx b/packages/core-components/src/components/Table/Table.stories.tsx index 7785764b22..b057d0a12c 100644 --- a/packages/core-components/src/components/Table/Table.stories.tsx +++ b/packages/core-components/src/components/Table/Table.stories.tsx @@ -326,3 +326,49 @@ export const FilterTable = () => { ); }; + +export const StyledTable = () => { + const classes = useStyles(); + const columns: TableColumn[] = [ + { + title: 'Column 1', + field: 'col1', + highlight: true, + cellStyle: (_, rowData: any & { tableData: { id: number } }) => { + return rowData.tableData.id % 2 === 0 + ? { + color: '#6CD75F', + } + : { + color: '#DC3D5A', + }; + }, + }, + { + title: 'Column 2', + field: 'col2', + cellStyle: { color: '#2FA5DC' }, + }, + { + title: 'Numeric value', + field: 'number', + type: 'numeric', + }, + { + title: 'A Date', + field: 'date', + type: 'date', + }, + ]; + + return ( +
+
+ + ); +}; From 5826c17b7d769e2a499dcff9b5dd7dc05ffcfcfc Mon Sep 17 00:00:00 2001 From: Elliot Greenwood Date: Tue, 9 Nov 2021 13:44:15 +0000 Subject: [PATCH 118/196] Add changeset Signed-off-by: Elliot Greenwood --- .changeset/four-years-develop.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/four-years-develop.md diff --git a/.changeset/four-years-develop.md b/.changeset/four-years-develop.md new file mode 100644 index 0000000000..cfd3704f2f --- /dev/null +++ b/.changeset/four-years-develop.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Allow for `cellStyle` property on `TableColumn` to be a function as well as `React.CSSProperties` as per the Material UI Table component From 1921f70aa744b386b45eb53862f21efbe2c222cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 9 Nov 2021 14:50:21 +0100 Subject: [PATCH 119/196] revert the graphql package pinning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/seven-zoos-sparkle.md | 14 +++++++ package.json | 4 +- .../templates/default-app/package.json.hbs | 4 -- yarn.lock | 41 +++++++++++-------- 4 files changed, 38 insertions(+), 25 deletions(-) create mode 100644 .changeset/seven-zoos-sparkle.md diff --git a/.changeset/seven-zoos-sparkle.md b/.changeset/seven-zoos-sparkle.md new file mode 100644 index 0000000000..1399c3254e --- /dev/null +++ b/.changeset/seven-zoos-sparkle.md @@ -0,0 +1,14 @@ +--- +'@backstage/create-app': patch +--- + +Removed the version pinning of the packages `graphql-language-service-interface` and `graphql-language-service-parser`. This should no longer be necessary. + +You can apply the same change in your repository by ensuring that the following does _NOT_ appear in your root `package.json`. + +```json +"resolutions": { + "graphql-language-service-interface": "2.8.2", + "graphql-language-service-parser": "1.9.0" + }, +``` diff --git a/package.json b/package.json index 62a8a0924e..03dd289653 100644 --- a/package.json +++ b/package.json @@ -46,9 +46,7 @@ "resolutions": { "**/@graphql-codegen/cli/**/ws": "^7.4.6", "**/@roadiehq/**/@backstage/plugin-catalog": "*", - "**/@roadiehq/**/@backstage/catalog-model": "*", - "graphql-language-service-interface": "2.8.2", - "graphql-language-service-parser": "1.9.0" + "**/@roadiehq/**/@backstage/catalog-model": "*" }, "version": "1.0.0", "dependencies": { diff --git a/packages/create-app/templates/default-app/package.json.hbs b/packages/create-app/templates/default-app/package.json.hbs index 5ed066a30b..638caf6a2f 100644 --- a/packages/create-app/templates/default-app/package.json.hbs +++ b/packages/create-app/templates/default-app/package.json.hbs @@ -23,10 +23,6 @@ "create-plugin": "backstage-cli create-plugin --scope internal --no-private", "remove-plugin": "backstage-cli remove-plugin" }, - "resolutions": { - "graphql-language-service-interface": "2.8.2", - "graphql-language-service-parser": "1.9.0" - }, "workspaces": { "packages": [ "packages/*", diff --git a/yarn.lock b/yarn.lock index b5786626dd..34421fdae7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15591,20 +15591,20 @@ graphql-extensions@^0.15.0: apollo-server-env "^3.1.0" apollo-server-types "^0.9.0" -graphql-language-service-interface@2.8.2, graphql-language-service-interface@^2.9.0: - version "2.8.2" - resolved "https://registry.npmjs.org/graphql-language-service-interface/-/graphql-language-service-interface-2.8.2.tgz#b3bb2aef7eaf0dff0b4ea419fa412c5f66fa268b" - integrity sha512-otbOQmhgkAJU1QJgQkMztNku6SbJLu/uodoFOYOOtJsizTjrMs93vkYaHCcYnLA3oi1Goj27XcHjMnRCYQOZXQ== +graphql-language-service-interface@^2.9.0: + version "2.9.1" + resolved "https://registry.npmjs.org/graphql-language-service-interface/-/graphql-language-service-interface-2.9.1.tgz#be0b11b06b78730ea9d250e0e2290e7ed9c8d283" + integrity sha512-yGsE67fxJBXxY82+rLDMvUpmzpOUM8XFB+k+xOTUyABWs27osKaoGiuDDXAVGg1adhm+cpunWbipe763ZJkAVA== dependencies: - graphql-language-service-parser "^1.9.0" - graphql-language-service-types "^1.8.0" - graphql-language-service-utils "^2.5.1" + graphql-language-service-parser "^1.10.0" + graphql-language-service-types "^1.8.3" + graphql-language-service-utils "^2.6.0" vscode-languageserver-types "^3.15.1" -graphql-language-service-parser@1.9.0, graphql-language-service-parser@^1.10.0, graphql-language-service-parser@^1.9.0: - version "1.9.0" - resolved "https://registry.npmjs.org/graphql-language-service-parser/-/graphql-language-service-parser-1.9.0.tgz#79af21294119a0a7e81b6b994a1af36833bab724" - integrity sha512-B5xPZLbBmIp0kHvpY1Z35I5DtPoDK9wGxQVRDIzcBaiIvAmlTrDvjo3bu7vKREdjFbYKvWNgrEWENuprMbF17Q== +graphql-language-service-parser@^1.10.0: + version "1.10.0" + resolved "https://registry.npmjs.org/graphql-language-service-parser/-/graphql-language-service-parser-1.10.0.tgz#116f4be849754f6afb4c196421a43fe96d87b278" + integrity sha512-cLExv0EjqT2hsKdwVTPmKU6eMfjZAjxqywgCPnWD48eJn6tyuePMyG7ye+jpX1PRPPx/cDHfFJGf8sUclchvng== dependencies: graphql-language-service-types "^1.8.0" @@ -15618,13 +15618,10 @@ graphql-language-service-types@^1.8.2: resolved "https://registry.npmjs.org/graphql-language-service-types/-/graphql-language-service-types-1.8.2.tgz#50ae56f69cc24fcfc3daa129b68b0eb9421e8578" integrity sha512-Sj07RHnMwAhEvAt7Jdt1l/x56ZpoNh+V6g+T58CF6GiYqI5l4vXqqRB4d4xHDcNQX98GpJfnf3o8BqPgP3C5Sw== -graphql-language-service-utils@^2.5.1: - version "2.5.1" - resolved "https://registry.npmjs.org/graphql-language-service-utils/-/graphql-language-service-utils-2.5.1.tgz#832ad4b0a9da03fdded756932c27e057ccf71302" - integrity sha512-Lzz723cYrYlVN4WVzIyFGg3ogoe+QYAIBfdtDboiIILoy0FTmqbyC2TOErqbmWKqO4NK9xDA95cSRFbWiHYj0g== - dependencies: - graphql-language-service-types "^1.8.0" - nullthrows "^1.0.0" +graphql-language-service-types@^1.8.3: + version "1.8.3" + resolved "https://registry.npmjs.org/graphql-language-service-types/-/graphql-language-service-types-1.8.3.tgz#d7d688d74c122c4d9cc4cceae761a1f2a3c396a1" + integrity sha512-m+RHnlGkKDcesW/gC4M7I2pSmWJB84uWS6LtnjplO/07JN312nJCJYCwV/DBny2m1fmSOxN7H/o+JW0l56KwBA== graphql-language-service-utils@^2.5.3: version "2.5.3" @@ -15634,6 +15631,14 @@ graphql-language-service-utils@^2.5.3: graphql-language-service-types "^1.8.0" nullthrows "^1.0.0" +graphql-language-service-utils@^2.6.0: + version "2.6.0" + resolved "https://registry.npmjs.org/graphql-language-service-utils/-/graphql-language-service-utils-2.6.0.tgz#d04904641248167ccbb381d8705ba97daa784954" + integrity sha512-idqwmbREixhDuQMcYp8WH0btQT02xZny8MO/HduNTVjnPrmTYnZUbpZ9AejdflmaKoS0o8nNvgXQ0GpIOzbG5g== + dependencies: + graphql-language-service-types "^1.8.3" + nullthrows "^1.0.0" + graphql-language-service@^3.1.6: version "3.2.0" resolved "https://registry.npmjs.org/graphql-language-service/-/graphql-language-service-3.2.0.tgz#e0eb6d5dea2cab92549a253d7a6b4fa0cce178b7" From b152c2a1e4233a2f1ddc1ad7fef0f7f1c6fe29d9 Mon Sep 17 00:00:00 2001 From: Jonas Thomsen Date: Tue, 9 Nov 2021 15:25:48 +0100 Subject: [PATCH 120/196] Update ADOPTERS.md --- ADOPTERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ADOPTERS.md b/ADOPTERS.md index f0b2fa16f6..6736538fa0 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -64,3 +64,4 @@ | [SoundCloud](https://www.soundcloud.com) | [Julio Zynger](https://github.com/julioz) | Developer portal as a [humane registry](https://martinfowler.com/bliki/HumaneRegistry.html) for the organization: catalog of people, services, documentation, feature toggles, escalation policies, etc. | | [Volvofinans Bank](https://www.volvofinans.se) | [Johan Hammar](https://github.com/johanhammar) | Developer portal enabling engineers to manage and explore software and documentation. | | [Palo Alto Networks](https://www.paloaltonetworks.com) | [Jeremy Guarini](https://github.com/jeremyguarini), [Brian Lomeland](https://github.com/bbbmmmlll), [Palo Alto Networks](https://github.com/PaloAltoNetworks) | Developer portal, service catalog, documentation and tooling | +| [Signal Iduna Group](https://www.signal-iduna.de/) | [Jonas Thomsen](https://github.com/JoThomsen) | Developer Portal, documentation, monitoring, service catalog for our insurance ecosystem From 3e0e2f09d516022e8c510bed36baa4a6f8652679 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 9 Nov 2021 16:25:34 +0100 Subject: [PATCH 121/196] auth-backend: add forwarding of the SAML audience option Signed-off-by: Patrik Oldsberg --- .changeset/selfish-bags-punch.md | 5 +++++ plugins/auth-backend/config.d.ts | 1 + plugins/auth-backend/src/providers/saml/provider.ts | 1 + 3 files changed, 7 insertions(+) create mode 100644 .changeset/selfish-bags-punch.md diff --git a/.changeset/selfish-bags-punch.md b/.changeset/selfish-bags-punch.md new file mode 100644 index 0000000000..1950da9357 --- /dev/null +++ b/.changeset/selfish-bags-punch.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Added forwarding of the `audience` option for the SAML provider, making it possible to enable `audience` verification. diff --git a/plugins/auth-backend/config.d.ts b/plugins/auth-backend/config.d.ts index 25da7fa332..791bd3606b 100644 --- a/plugins/auth-backend/config.d.ts +++ b/plugins/auth-backend/config.d.ts @@ -75,6 +75,7 @@ export interface Config { logoutUrl?: string; issuer: string; cert: string; + audience?: string; privateKey?: string; authnContext?: string[]; identifierFormat?: string; diff --git a/plugins/auth-backend/src/providers/saml/provider.ts b/plugins/auth-backend/src/providers/saml/provider.ts index 07fc77e4f7..8929a1dbf0 100644 --- a/plugins/auth-backend/src/providers/saml/provider.ts +++ b/plugins/auth-backend/src/providers/saml/provider.ts @@ -125,6 +125,7 @@ export const createSamlProvider = ( callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`, entryPoint: config.getString('entryPoint'), logoutUrl: config.getOptionalString('logoutUrl'), + audience: config.getOptionalString('audience'), issuer: config.getString('issuer'), cert: config.getString('cert'), privateCert: config.getOptionalString('privateKey'), From a197708da96d8db13fbb45df5f1f99b2854feb91 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 9 Nov 2021 18:20:26 +0100 Subject: [PATCH 122/196] add changeset for @spotify/eslint-config-typescript bump Signed-off-by: Patrik Oldsberg --- .changeset/tall-lions-return.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tall-lions-return.md diff --git a/.changeset/tall-lions-return.md b/.changeset/tall-lions-return.md new file mode 100644 index 0000000000..56bb0fc3dd --- /dev/null +++ b/.changeset/tall-lions-return.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Bumped `@spotify/eslint-config-typescript` from `v10` to `v12`, dropping support for Node.js v12. From 6d4f06699b977a7a91bbe95916177b70459a78cd Mon Sep 17 00:00:00 2001 From: Colton Padden Date: Tue, 9 Nov 2021 13:45:26 -0500 Subject: [PATCH 123/196] note internal refactor of LocalPublish changes and impact on usage Signed-off-by: Colton Padden --- .changeset/metal-impalas-allow.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.changeset/metal-impalas-allow.md b/.changeset/metal-impalas-allow.md index d3ca3b6e49..8ad727f759 100644 --- a/.changeset/metal-impalas-allow.md +++ b/.changeset/metal-impalas-allow.md @@ -2,9 +2,12 @@ '@backstage/techdocs-common': patch --- -1. Techdocs publishers constructors now use parameter objects when being instantiated +1. Techdocs publisher constructors now use parameter objects when being + instantiated -2. The `LocalPublish` publisher can now be created using `fromConfig`: +2. Internal refactor of `LocalPublish` publisher to use `fromConfig` for + creation to be aligned with other publishers; this does not impact + `LocalPublish` usage. ```diff - const publisher = new LocalPublish(config, logger, discovery); From fdc35dbff7e2226b9230ec8a52552ddb36f2bb73 Mon Sep 17 00:00:00 2001 From: Jeremy Guarini Date: Tue, 9 Nov 2021 14:18:54 -0800 Subject: [PATCH 124/196] swap out data-grid for material-table Signed-off-by: Jeremy Guarini --- plugins/gcp-projects/package.json | 2 +- .../ProjectListPage/ProjectListPage.tsx | 156 +++++++++--------- 2 files changed, 80 insertions(+), 78 deletions(-) diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 71b212a698..48205e4fd8 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -35,9 +35,9 @@ "@backstage/core-plugin-api": "^0.1.12", "@backstage/theme": "^0.2.12", "@material-ui/core": "^4.12.2", - "@material-ui/data-grid": "^4.0.0-alpha.37", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", + "material-table": "^1.69.3", "react": "^16.13.1", "react-dom": "^16.13.1", "react-router-dom": "^6.0.0-beta.0", diff --git a/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx b/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx index e4137f96c7..2797d17d0d 100644 --- a/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx +++ b/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx @@ -16,8 +16,17 @@ // NEEDS WORK import { Button, LinearProgress, Tooltip, Typography } from '@material-ui/core'; -import { DataGrid, GridColDef, GridCellParams } from '@material-ui/data-grid'; -import React from 'react'; +import MaterialTable from 'material-table'; +import ArrowDownward from '@material-ui/icons/ArrowDownward'; +import ChevronLeft from '@material-ui/icons/ChevronLeft'; +import ChevronRight from '@material-ui/icons/ChevronRight'; +import Clear from '@material-ui/icons/Clear'; +import FilterList from '@material-ui/icons/FilterList'; +import FirstPage from '@material-ui/icons/FirstPage'; +import LastPage from '@material-ui/icons/LastPage'; +import Search from '@material-ui/icons/Search'; +import React, { forwardRef } from 'react'; + import { useAsync } from 'react-use'; import { gcpApiRef, Project } from '../../api'; @@ -59,18 +68,6 @@ const PageContents = () => { const { loading, error, value } = useAsync(() => api.listProjects()); - const [page, setPage] = React.useState(0); - const [rowsPerPage, setRowsPerPage] = React.useState(10); - - const handleChangePage = (newPage: number) => { - setPage(newPage); - }; - - const handleChangeRowsPerPage = (pageSize: number) => { - setRowsPerPage(pageSize); - setPage(0); - }; - if (loading) { return ; } else if (error) { @@ -81,78 +78,83 @@ const PageContents = () => { ); } - function renderLink(params: GridCellParams) { + function renderLink(id: string) { return ( - + - + ); } - const columns: GridColDef[] = [ - { - align: 'left', - field: 'name', - flex: 1, - headerAlign: 'left', - headerName: 'Name', - }, - { - align: 'left', - field: 'projectNumber', - flex: 0.6, - headerAlign: 'left', - headerName: 'Project Number', - }, - { - align: 'left', - field: 'projectID', - flex: 1, - headerAlign: 'left', - headerName: 'Project ID', - renderCell: renderLink, - }, - { - align: 'left', - field: 'state', - flex: 0.6, - headerAlign: 'left', - headerName: 'State', - }, - { - align: 'left', - field: 'creationTime', - flex: 0.7, - headerAlign: 'left', - headerName: 'Creation Time', - }, - ]; - - const rows = - value?.map((project: Project) => ({ - id: project.projectId, - name: project.name, - projectNumber: project?.projectNumber || 'Error', - projectID: project.projectId, - state: project?.lifecycleState || 'Error', - creationTime: project?.createTime || 'Error', - })) || []; - return (
- ( + + )), + FirstPage: forwardRef((props, ref) => ( + + )), + LastPage: forwardRef((props, ref) => ( + + )), + NextPage: forwardRef((props, ref) => ( + + )), + PreviousPage: forwardRef((props, ref) => ( + + )), + ResetSearch: forwardRef((props, ref) => ( + + )), + Search: forwardRef((props, ref) => ), + SortArrow: forwardRef((props, ref) => ( + + )), + }} + columns={[ + { + field: 'name', + title: 'Name', + defaultSort: 'asc', + }, + { + field: 'projectNumber', + title: 'Project Number', + }, + { + field: 'projectID', + title: 'Project ID', + render: (rowData: { id: string }) => renderLink(rowData.id), + }, + { + field: 'state', + title: 'State', + }, + { + field: 'creationTime', + title: 'Creation Time', + }, + ]} + data={ + value?.map((project: Project) => ({ + id: project.projectId, + name: project.name, + projectNumber: project?.projectNumber || 'Error', + projectID: project.projectId, + state: project?.lifecycleState || 'Error', + creationTime: project?.createTime || 'Error', + })) || [] + } + options={{ + filtering: true, + pageSize: 5, + pageSizeOptions: [5, 10, 25, 50, 100], + showTitle: false, + }} />
); From 4a336fd292944b642f57e891cc8e9fbb5a48a5ee Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Wed, 10 Nov 2021 09:21:02 +0100 Subject: [PATCH 125/196] core-plugin-api: Deprecate use of extensions without name Signed-off-by: Johan Haals --- .changeset/chilly-emus-roll.md | 5 +++++ .changeset/mighty-gifts-visit.md | 5 +++++ .changeset/quiet-clocks-push.md | 5 +++++ .../software-catalog/catalog-customization.md | 1 + docs/plugins/composability.md | 2 ++ docs/plugins/plugin-development.md | 1 + docs/plugins/structure-of-a-plugin.md | 1 + packages/core-app-api/src/app/App.test.tsx | 1 + .../src/extensions/extensions.test.tsx | 2 ++ .../src/extensions/extensions.tsx | 19 +++++++++++++------ plugins/bazaar/src/plugin.ts | 1 + plugins/home/src/extensions.tsx | 3 +++ plugins/home/src/plugin.ts | 2 ++ 13 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 .changeset/chilly-emus-roll.md create mode 100644 .changeset/mighty-gifts-visit.md create mode 100644 .changeset/quiet-clocks-push.md diff --git a/.changeset/chilly-emus-roll.md b/.changeset/chilly-emus-roll.md new file mode 100644 index 0000000000..1569820980 --- /dev/null +++ b/.changeset/chilly-emus-roll.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': patch +--- + +Add name option to `createCardExtension` to remove deprecation warnings for extensions without name. Name will be required for extensions in a future release of `core-plugin-api` and therefore also in `createCardExtension`. diff --git a/.changeset/mighty-gifts-visit.md b/.changeset/mighty-gifts-visit.md new file mode 100644 index 0000000000..8ceab2906e --- /dev/null +++ b/.changeset/mighty-gifts-visit.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-bazaar': patch +--- + +Name extension to remove deprecation warning diff --git a/.changeset/quiet-clocks-push.md b/.changeset/quiet-clocks-push.md new file mode 100644 index 0000000000..3289e1886b --- /dev/null +++ b/.changeset/quiet-clocks-push.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-plugin-api': patch +--- + +Deprecate use of extensions without name. Adds a warning to the developer console to opt prompt integrators to provide names for extensions. diff --git a/docs/features/software-catalog/catalog-customization.md b/docs/features/software-catalog/catalog-customization.md index 8f855776a0..017285719a 100644 --- a/docs/features/software-catalog/catalog-customization.md +++ b/docs/features/software-catalog/catalog-customization.md @@ -172,6 +172,7 @@ This page itself can be exported as a routable extension in the plugin: ```ts export const CustomCatalogIndexPage = myPlugin.provide( createRoutableExtension({ + name: 'CustomCatalogPage', component: () => import('./components/CustomCatalogPage').then(m => m.CustomCatalogPage), mountPoint: catalogRouteRef, diff --git a/docs/plugins/composability.md b/docs/plugins/composability.md index 8d22c05a79..33568ac561 100644 --- a/docs/plugins/composability.md +++ b/docs/plugins/composability.md @@ -136,6 +136,7 @@ a component: ```ts export const FooPage = plugin.provide( createRoutableExtension({ + name: 'FooPage', component: () => import('./components/FooPage').then(m => m.FooPage), mountPoint: fooPageRouteRef, }), @@ -417,6 +418,7 @@ export const myPlugin = createPlugin({ export const MyPage = myPlugin.provide( createRoutableExtension({ + name: 'MyPage', component: () => import('./components/MyPage').then(m => m.MyPage), mountPoint: rootRouteRef, }), diff --git a/docs/plugins/plugin-development.md b/docs/plugins/plugin-development.md index f61fae0968..362ecc5d5a 100644 --- a/docs/plugins/plugin-development.md +++ b/docs/plugins/plugin-development.md @@ -65,6 +65,7 @@ export const examplePlugin = createPlugin({ // Each extension should also be exported from your plugin package. export const ExamplePage = examplePlugin.provide( createRoutableExtension({ + name: 'ExamplePage', // The component needs to be lazy-loaded. It's what will actually be rendered in the end. component: () => import('./components/ExampleComponent').then(m => m.ExampleComponent), diff --git a/docs/plugins/structure-of-a-plugin.md b/docs/plugins/structure-of-a-plugin.md index 83fcfbddba..9e8b67710a 100644 --- a/docs/plugins/structure-of-a-plugin.md +++ b/docs/plugins/structure-of-a-plugin.md @@ -73,6 +73,7 @@ export const examplePlugin = createPlugin({ export const ExamplePage = examplePlugin.provide( createRoutableExtension({ + name: 'ExamplePage', component: () => import('./components/ExampleComponent').then(m => m.ExampleComponent), mountPoint: rootRouteRef, diff --git a/packages/core-app-api/src/app/App.test.tsx b/packages/core-app-api/src/app/App.test.tsx index ab287a3ece..6ed5d45b86 100644 --- a/packages/core-app-api/src/app/App.test.tsx +++ b/packages/core-app-api/src/app/App.test.tsx @@ -169,6 +169,7 @@ describe('Integration Test', () => { const NavigateComponent = plugin1.provide( createRoutableExtension({ + name: 'NavigateComponent', component: () => Promise.resolve((_: PropsWithChildren<{ path?: string }>) => { return ; diff --git a/packages/core-plugin-api/src/extensions/extensions.test.tsx b/packages/core-plugin-api/src/extensions/extensions.test.tsx index 29c696c71f..dcc92d9d47 100644 --- a/packages/core-plugin-api/src/extensions/extensions.test.tsx +++ b/packages/core-plugin-api/src/extensions/extensions.test.tsx @@ -41,6 +41,7 @@ describe('extensions', () => { const Component = () =>
; const extension = createReactExtension({ + name: 'Extension', component: { sync: Component, }, @@ -67,6 +68,7 @@ describe('extensions', () => { }); const extension2 = createRoutableExtension({ + name: 'Extension2', component: () => Promise.resolve(Component), mountPoint: routeRef, }); diff --git a/packages/core-plugin-api/src/extensions/extensions.tsx b/packages/core-plugin-api/src/extensions/extensions.tsx index f85570a35b..ca5ada8478 100644 --- a/packages/core-plugin-api/src/extensions/extensions.tsx +++ b/packages/core-plugin-api/src/extensions/extensions.tsx @@ -55,7 +55,7 @@ export function createRoutableExtension< mountPoint: RouteRef; name?: string; }): Extension { - const { component, mountPoint } = options; + const { component, mountPoint, name } = options; return createReactExtension({ component: { lazy: () => @@ -85,7 +85,7 @@ export function createRoutableExtension< }; const componentName = - options.name || + name || (InnerComponent as { displayName?: string }).displayName || InnerComponent.name || 'LazyComponent'; @@ -108,7 +108,7 @@ export function createRoutableExtension< data: { 'core.mountPoint': mountPoint, }, - name: options.name, + name, }); } @@ -152,7 +152,14 @@ export function createReactExtension< data?: Record; name?: string; }): Extension { - const { data = {} } = options; + const { data = {}, name } = options; + if (!name) { + // eslint-disable-next-line no-console + console.warn( + 'Declaring extensions without name is DEPRECATED. ' + + 'Make sure that all usages of createReactExtension, createComponentExtension and createRoutableExtension provide a name.', + ); + } let Component: T; if ('lazy' in options.component) { @@ -164,7 +171,7 @@ export function createReactExtension< Component = options.component.sync; } const componentName = - options.name || + name || (Component as { displayName?: string }).displayName || Component.name || 'Component'; @@ -186,7 +193,7 @@ export function createReactExtension< diff --git a/plugins/bazaar/src/plugin.ts b/plugins/bazaar/src/plugin.ts index f96eaf522d..5eb5e2aacd 100644 --- a/plugins/bazaar/src/plugin.ts +++ b/plugins/bazaar/src/plugin.ts @@ -44,6 +44,7 @@ export const bazaarPlugin = createPlugin({ export const BazaarPage = bazaarPlugin.provide( createRoutableExtension({ + name: 'BazaarPage', component: () => import('./components/HomePage').then(m => m.HomePage), mountPoint: rootRouteRef, }), diff --git a/plugins/home/src/extensions.tsx b/plugins/home/src/extensions.tsx index 93fe4ceb4f..82394cf1ea 100644 --- a/plugins/home/src/extensions.tsx +++ b/plugins/home/src/extensions.tsx @@ -37,11 +37,14 @@ type RendererProps = { title: string } & ComponentParts; export function createCardExtension({ title, components, + name, }: { title: string; components: () => Promise; + name?: string; }) { return createReactExtension({ + name, component: { lazy: () => components().then(({ Content, Actions, Settings, ContextProvider }) => { diff --git a/plugins/home/src/plugin.ts b/plugins/home/src/plugin.ts index d67ea846a5..b4b9f9b6ee 100644 --- a/plugins/home/src/plugin.ts +++ b/plugins/home/src/plugin.ts @@ -71,6 +71,7 @@ export const ComponentTab = homePlugin.provide( */ export const WelcomeTitle = homePlugin.provide( createComponentExtension({ + name: 'WelcomeTitle', component: { lazy: () => import('./homePageComponents/WelcomeTitle').then(m => m.WelcomeTitle), @@ -80,6 +81,7 @@ export const WelcomeTitle = homePlugin.provide( export const HomePageRandomJoke = homePlugin.provide( createCardExtension<{ defaultCategory?: 'any' | 'programming' }>({ + name: 'HomePageRandomJoke', title: 'Random Joke', components: () => import('./homePageComponents/RandomJoke'), }), From c41b6ef904650a7880f064a887ca0deb56b15a25 Mon Sep 17 00:00:00 2001 From: Harry Hogg Date: Wed, 10 Nov 2021 08:36:58 +0000 Subject: [PATCH 126/196] Added ts-node as a dependency for the Snyk Github issues sync script Signed-off-by: Harry Hogg --- package.json | 1 + yarn.lock | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 03dd289653..3e6d376c46 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "minimist": "^1.2.5", "prettier": "^2.2.1", "shx": "^0.3.2", + "ts-node": "^10.4.0", "yarn-lock-check": "^1.0.5" }, "prettier": "@spotify/prettier-config", diff --git a/yarn.lock b/yarn.lock index e2e7c13672..42c43fe4b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -27465,7 +27465,7 @@ ts-node@^10.0.0: source-map-support "^0.5.17" yn "3.1.1" -ts-node@^10.2.1: +ts-node@^10.2.1, ts-node@^10.4.0: version "10.4.0" resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz#680f88945885f4e6cf450e7f0d6223dd404895f7" integrity sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A== From 3487920e5e7b705ae96365052ff2b6ff8144b4b5 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Wed, 10 Nov 2021 09:49:44 +0100 Subject: [PATCH 127/196] api report Signed-off-by: Johan Haals --- plugins/home/api-report.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index 57b0b899e2..99d88fa4b5 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -71,9 +71,11 @@ export const ComponentTabs: ({ export function createCardExtension({ title, components, + name, }: { title: string; components: () => Promise; + name?: string; }): Extension< ({ Renderer, From 598057f348028332f2d55dcd59a9f4ac42bdd557 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Wed, 10 Nov 2021 10:59:38 +0100 Subject: [PATCH 128/196] Update docs/features/software-catalog/catalog-customization.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Johan Haals Co-authored-by: Fredrik Adelöw --- docs/features/software-catalog/catalog-customization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/software-catalog/catalog-customization.md b/docs/features/software-catalog/catalog-customization.md index 017285719a..e80e8a1842 100644 --- a/docs/features/software-catalog/catalog-customization.md +++ b/docs/features/software-catalog/catalog-customization.md @@ -172,7 +172,7 @@ This page itself can be exported as a routable extension in the plugin: ```ts export const CustomCatalogIndexPage = myPlugin.provide( createRoutableExtension({ - name: 'CustomCatalogPage', + name: 'CustomCatalogIndexPage', component: () => import('./components/CustomCatalogPage').then(m => m.CustomCatalogPage), mountPoint: catalogRouteRef, From f1403fac6437a2726d226d8ebe4eedcba93e0d14 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Wed, 10 Nov 2021 11:01:16 +0100 Subject: [PATCH 129/196] Update .changeset/quiet-clocks-push.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Johan Haals Co-authored-by: Fredrik Adelöw --- .changeset/quiet-clocks-push.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/quiet-clocks-push.md b/.changeset/quiet-clocks-push.md index 3289e1886b..2b469a5fc5 100644 --- a/.changeset/quiet-clocks-push.md +++ b/.changeset/quiet-clocks-push.md @@ -2,4 +2,4 @@ '@backstage/core-plugin-api': patch --- -Deprecate use of extensions without name. Adds a warning to the developer console to opt prompt integrators to provide names for extensions. +Deprecate use of extensions without name. Adds a warning to the developer console to prompt integrators to provide names for extensions. From 1d0e5c55f828d25395ad44a14f0428af8ffe06ca Mon Sep 17 00:00:00 2001 From: Harry Hogg Date: Wed, 10 Nov 2021 12:36:50 +0000 Subject: [PATCH 130/196] Install dependeices before trying to use them in the Snyk github issue workflow Signed-off-by: Harry Hogg --- .github/workflows/snyk-github-issue-sync.yml | 34 ++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/.github/workflows/snyk-github-issue-sync.yml b/.github/workflows/snyk-github-issue-sync.yml index 5f49be4e1a..77604a126d 100644 --- a/.github/workflows/snyk-github-issue-sync.yml +++ b/.github/workflows/snyk-github-issue-sync.yml @@ -7,8 +7,42 @@ on: jobs: sync: runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [14.x] + steps: - uses: actions/checkout@v2 + + # Beginning of yarn setup, keep in sync between all workflows, see ci.yml + - name: use node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + registry-url: https://registry.npmjs.org/ # Needed for auth + - name: cache all node_modules + id: cache-modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-v${{ matrix.node-version }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} + - name: find location of global yarn cache + id: yarn-cache + if: steps.cache-modules.outputs.cache-hit != 'true' + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + if: steps.cache-modules.outputs.cache-hit != 'true' + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: yarn install + run: yarn install --frozen-lockfile + # End of yarn setup + - name: Create Snyk report uses: snyk/actions/node@master continue-on-error: true # Snyk CLI exits with error when vulnerabilities are found From 7e18ed7f29d1dfb4dbc8a290386ecb806619db40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 10 Nov 2021 14:29:35 +0100 Subject: [PATCH 131/196] Remove the unused UserFlags type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/sharp-meals-fetch.md | 5 +++++ packages/core-plugin-api/api-report.md | 3 --- .../src/apis/definitions/FeatureFlagsApi.ts | 7 ------- 3 files changed, 5 insertions(+), 10 deletions(-) create mode 100644 .changeset/sharp-meals-fetch.md diff --git a/.changeset/sharp-meals-fetch.md b/.changeset/sharp-meals-fetch.md new file mode 100644 index 0000000000..1d3df65ba4 --- /dev/null +++ b/.changeset/sharp-meals-fetch.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-plugin-api': minor +--- + +Removed the unused `UserFlags` type. diff --git a/packages/core-plugin-api/api-report.md b/packages/core-plugin-api/api-report.md index acfc8d4a45..f0cee2a42c 100644 --- a/packages/core-plugin-api/api-report.md +++ b/packages/core-plugin-api/api-report.md @@ -799,9 +799,6 @@ export function useElementFilter( dependencies?: any[], ): T; -// @public -export type UserFlags = {}; - // @public export function useRouteRef( routeRef: ExternalRouteRef, diff --git a/packages/core-plugin-api/src/apis/definitions/FeatureFlagsApi.ts b/packages/core-plugin-api/src/apis/definitions/FeatureFlagsApi.ts index c669710895..ac43bbebe8 100644 --- a/packages/core-plugin-api/src/apis/definitions/FeatureFlagsApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/FeatureFlagsApi.ts @@ -61,13 +61,6 @@ export type FeatureFlagsSaveOptions = { merge?: boolean; }; -/** - * User flags alias. - * - * @public - */ -export type UserFlags = {}; - /** * The feature flags API is used to toggle functionality to users across plugins and Backstage. * From e0861b92ffd3435deb02288e1cf3d34ea309a121 Mon Sep 17 00:00:00 2001 From: James Turley Date: Thu, 4 Nov 2021 16:35:15 +0000 Subject: [PATCH 132/196] Add prop to override color selection in gauge components Signed-off-by: James Turley --- .changeset/grumpy-pugs-report.md | 23 +++++++++++++++++++ .../components/ProgressBars/Gauge.stories.tsx | 6 +++++ .../src/components/ProgressBars/Gauge.tsx | 20 ++++++++++++++-- .../ProgressBars/GaugeCard.stories.tsx | 21 +++++++++++++++++ .../src/components/ProgressBars/GaugeCard.tsx | 14 ++++++++--- .../ProgressBars/LinearGauge.stories.tsx | 6 +++++ .../components/ProgressBars/LinearGauge.tsx | 14 +++++++---- 7 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 .changeset/grumpy-pugs-report.md diff --git a/.changeset/grumpy-pugs-report.md b/.changeset/grumpy-pugs-report.md new file mode 100644 index 0000000000..6ee56f2bf7 --- /dev/null +++ b/.changeset/grumpy-pugs-report.md @@ -0,0 +1,23 @@ +--- +'@backstage/core-components': patch +--- + +Add new way to override color selection to progress bar/gauge components. + +`Gauge`, `LinearGauge` and `GaugeCard` all accept a `getColor` prop, +which is a function of the type: + +```ts +export type GetColor = (args: { + palette: Palette; + value: number; + inverse?: boolean; + max?: number; +}) => string | PaletteColor; +``` + +Either return a standard Material UI palette color object or a CSS color +string (e.g. "red", "#f02020"), and the gauge will be set to that color. + +If the prop is omitted, the default implementation is unchanged from previous +versions. diff --git a/packages/core-components/src/components/ProgressBars/Gauge.stories.tsx b/packages/core-components/src/components/ProgressBars/Gauge.stories.tsx index 3481c48cef..401f502697 100644 --- a/packages/core-components/src/components/ProgressBars/Gauge.stories.tsx +++ b/packages/core-components/src/components/ProgressBars/Gauge.stories.tsx @@ -53,3 +53,9 @@ export const AbsoluteProgress = () => (
); + +export const StaticColor = () => ( +
+ '#f0f'} value={0.5} /> +
+); diff --git a/packages/core-components/src/components/ProgressBars/Gauge.tsx b/packages/core-components/src/components/ProgressBars/Gauge.tsx index 83e8d05b16..097fc9419d 100644 --- a/packages/core-components/src/components/ProgressBars/Gauge.tsx +++ b/packages/core-components/src/components/ProgressBars/Gauge.tsx @@ -52,8 +52,16 @@ type Props = { inverse?: boolean; unit?: string; max?: number; + getColor?: GetColor; }; +export type GetColor = (args: { + palette: BackstageTheme['palette']; + value: number; + inverse?: boolean; + max?: number; +}) => string | BackstageTheme['palette']['error']; + const defaultProps = { fractional: true, inverse: false, @@ -83,10 +91,18 @@ export function getProgressColor( return palette.status.ok; } +export const defaultGetProgressColor: GetColor = ({ + palette, + value, + inverse, + max, +}) => getProgressColor(palette, value, inverse, max); + /** @public */ export function Gauge(props: Props) { + const { getColor = defaultGetProgressColor } = props; const classes = useStyles(props); - const theme = useTheme(); + const { palette } = useTheme(); const { value, fractional, inverse, unit, max } = { ...defaultProps, ...props, @@ -102,7 +118,7 @@ export function Gauge(props: Props) { percent={asPercentage} strokeWidth={12} trailWidth={12} - strokeColor={getProgressColor(theme.palette, asActual, inverse, max)} + strokeColor={getColor({ palette, value: asActual, inverse, max })} className={classes.circle} />
diff --git a/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx b/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx index ad3323f8d3..f2aad14cd9 100644 --- a/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx +++ b/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx @@ -97,3 +97,24 @@ export const LinkInFooter = () => ( ); + +export const StaticColor = () => ( + + + '#f00'} title="Red" progress={0.5} /> + + + '#0f0'} title="Green" progress={0.5} /> + + + '#00f'} title="Blue" progress={0.5} /> + + + palette.error} + title="palette.error" + progress={0.5} + /> + + +); diff --git a/packages/core-components/src/components/ProgressBars/GaugeCard.tsx b/packages/core-components/src/components/ProgressBars/GaugeCard.tsx index fe17de30cd..f1fd281b35 100644 --- a/packages/core-components/src/components/ProgressBars/GaugeCard.tsx +++ b/packages/core-components/src/components/ProgressBars/GaugeCard.tsx @@ -18,7 +18,7 @@ import React from 'react'; import { makeStyles } from '@material-ui/core/styles'; import { InfoCard, InfoCardVariants } from '../../layout/InfoCard'; import { BottomLinkProps } from '../../layout/BottomLink'; -import { Gauge } from './Gauge'; +import { Gauge, GetColor } from './Gauge'; type Props = { title: string; @@ -28,6 +28,7 @@ type Props = { progress: number; inverse?: boolean; deepLink?: BottomLinkProps; + getColor?: GetColor; }; /** @public */ @@ -46,7 +47,14 @@ const useStyles = makeStyles( /** @public */ export function GaugeCard(props: Props) { const classes = useStyles(props); - const { title, subheader, progress, inverse, deepLink, variant } = props; + const { title, subheader, progress, inverse, deepLink, variant, getColor } = + props; + + const gaugeProps = { + inverse, + getColor, + value: progress, + }; return (
@@ -56,7 +64,7 @@ export function GaugeCard(props: Props) { deepLink={deepLink} variant={variant} > - +
); diff --git a/packages/core-components/src/components/ProgressBars/LinearGauge.stories.tsx b/packages/core-components/src/components/ProgressBars/LinearGauge.stories.tsx index ee2ed2eae6..5d36ea9178 100644 --- a/packages/core-components/src/components/ProgressBars/LinearGauge.stories.tsx +++ b/packages/core-components/src/components/ProgressBars/LinearGauge.stories.tsx @@ -41,3 +41,9 @@ export const LowProgress = () => (
); + +export const StaticColor = () => ( +
+ '#f0f'} value={0.5} /> +
+); diff --git a/packages/core-components/src/components/ProgressBars/LinearGauge.tsx b/packages/core-components/src/components/ProgressBars/LinearGauge.tsx index afc93fd337..ac6097195d 100644 --- a/packages/core-components/src/components/ProgressBars/LinearGauge.tsx +++ b/packages/core-components/src/components/ProgressBars/LinearGauge.tsx @@ -20,18 +20,19 @@ import Tooltip from '@material-ui/core/Tooltip'; // @ts-ignore import { Line } from 'rc-progress'; import { BackstageTheme } from '@backstage/theme'; -import { getProgressColor } from './Gauge'; +import { defaultGetProgressColor, GetColor } from './Gauge'; type Props = { /** * Progress value between 0.0 - 1.0. */ value: number; + getColor?: GetColor; }; export function LinearGauge(props: Props) { - const { value } = props; - const theme = useTheme(); + const { value, getColor = defaultGetProgressColor } = props; + const { palette } = useTheme(); if (isNaN(value)) { return null; } @@ -39,7 +40,12 @@ export function LinearGauge(props: Props) { if (percent > 100) { percent = 100; } - const strokeColor = getProgressColor(theme.palette, percent, false, 100); + const strokeColor = getColor({ + palette, + value: percent, + inverse: false, + max: 100, + }); return ( From 6df5e58f7b86ce476a768794492f482fff5c72fe Mon Sep 17 00:00:00 2001 From: James Turley Date: Fri, 5 Nov 2021 12:46:49 +0000 Subject: [PATCH 133/196] Modify exported getProgressColor to accept arguments object Signed-off-by: James Turley --- .../components/ProgressBars/Gauge.test.tsx | 26 +++++++++++----- .../src/components/ProgressBars/Gauge.tsx | 31 +++++++++---------- .../components/ProgressBars/LinearGauge.tsx | 4 +-- 3 files changed, 34 insertions(+), 27 deletions(-) diff --git a/packages/core-components/src/components/ProgressBars/Gauge.test.tsx b/packages/core-components/src/components/ProgressBars/Gauge.test.tsx index 066e1233a0..2d9e852e6c 100644 --- a/packages/core-components/src/components/ProgressBars/Gauge.test.tsx +++ b/packages/core-components/src/components/ProgressBars/Gauge.test.tsx @@ -56,16 +56,26 @@ describe('', () => { }; it('colors the progress correctly', () => { - 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); + expect(getProgressColor({ palette, value: 'Not a Number' as any })).toBe( + '#ddd', + ); + expect(getProgressColor({ palette, value: 10 })).toBe(error); + expect(getProgressColor({ palette, value: 50 })).toBe(warning); + expect(getProgressColor({ palette, value: 90 })).toBe(ok); }); it('colors the inverse progress correctly', () => { - 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); + expect( + getProgressColor({ + palette, + value: 'Not a Number' as any, + inverse: true, + }), + ).toBe('#ddd'); + expect(getProgressColor({ palette, value: 10, inverse: true })).toBe(ok); + expect(getProgressColor({ palette, value: 50, inverse: true })).toBe( + warning, + ); + expect(getProgressColor({ palette, value: 90, inverse: true })).toBe(error); }); }); diff --git a/packages/core-components/src/components/ProgressBars/Gauge.tsx b/packages/core-components/src/components/ProgressBars/Gauge.tsx index 097fc9419d..2bcc0c3976 100644 --- a/packages/core-components/src/components/ProgressBars/Gauge.tsx +++ b/packages/core-components/src/components/ProgressBars/Gauge.tsx @@ -55,12 +55,16 @@ type Props = { getColor?: GetColor; }; -export type GetColor = (args: { +type GetColorArgs = { palette: BackstageTheme['palette']; value: number; inverse?: boolean; max?: number; -}) => string | BackstageTheme['palette']['error']; +}; + +export type GetColor = ( + args: GetColorArgs, +) => string | BackstageTheme['palette']['error']; const defaultProps = { fractional: true, @@ -69,12 +73,12 @@ const defaultProps = { max: 100, }; -export function getProgressColor( - palette: BackstageTheme['palette'], - value: number, - inverse?: boolean, - max?: number, -) { +export const getProgressColor: GetColor = ({ + palette, + value, + inverse, + max, +}) => { if (isNaN(value)) { return '#ddd'; } @@ -89,18 +93,11 @@ export function getProgressColor( } return palette.status.ok; -} - -export const defaultGetProgressColor: GetColor = ({ - palette, - value, - inverse, - max, -}) => getProgressColor(palette, value, inverse, max); +}; /** @public */ export function Gauge(props: Props) { - const { getColor = defaultGetProgressColor } = props; + const { getColor = getProgressColor } = props; const classes = useStyles(props); const { palette } = useTheme(); const { value, fractional, inverse, unit, max } = { diff --git a/packages/core-components/src/components/ProgressBars/LinearGauge.tsx b/packages/core-components/src/components/ProgressBars/LinearGauge.tsx index ac6097195d..e331ed1fa5 100644 --- a/packages/core-components/src/components/ProgressBars/LinearGauge.tsx +++ b/packages/core-components/src/components/ProgressBars/LinearGauge.tsx @@ -20,7 +20,7 @@ import Tooltip from '@material-ui/core/Tooltip'; // @ts-ignore import { Line } from 'rc-progress'; import { BackstageTheme } from '@backstage/theme'; -import { defaultGetProgressColor, GetColor } from './Gauge'; +import { getProgressColor, GetColor } from './Gauge'; type Props = { /** @@ -31,7 +31,7 @@ type Props = { }; export function LinearGauge(props: Props) { - const { value, getColor = defaultGetProgressColor } = props; + const { value, getColor = getProgressColor } = props; const { palette } = useTheme(); if (isNaN(value)) { return null; From 60f5d96953210a320934c8b248b0f0f41a02a42e Mon Sep 17 00:00:00 2001 From: James Turley Date: Wed, 10 Nov 2021 14:43:24 +0000 Subject: [PATCH 134/196] Rename gauge props to GaugeProps and export Signed-off-by: James Turley --- packages/core-components/api-report.md | 24 +++++++++---------- .../src/components/ProgressBars/Gauge.tsx | 10 ++++---- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index cc884c9e8f..13929db7a3 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -100,7 +100,7 @@ export type BottomLinkProps = { // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // // @public (undocumented) -export function Breadcrumbs(props: Props_21): JSX.Element; +export function Breadcrumbs(props: Props_20): JSX.Element; // @public (undocumented) export type BreadcrumbsClickableTextClassKey = 'root'; @@ -157,7 +157,7 @@ export interface CodeSnippetProps { // Warning: (ae-missing-release-tag) "Content" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function Content(props: PropsWithChildren): JSX.Element; +export function Content(props: PropsWithChildren): JSX.Element; // Warning: (ae-forgotten-export) The symbol "ContentHeaderProps" needs to be exported by the entry point index.d.ts // @@ -368,10 +368,10 @@ export function FeatureCalloutCircular( // @public (undocumented) export type FiltersContainerClassKey = 'root' | 'title'; -// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "GaugeProps" needs to be exported by the entry point index.d.ts // // @public (undocumented) -export function Gauge(props: Props_11): JSX.Element; +export function Gauge(props: GaugeProps): JSX.Element; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // @@ -393,7 +393,7 @@ export function GroupIcon(props: IconComponentProps): JSX.Element; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // // @public (undocumented) -export function Header(props: PropsWithChildren): JSX.Element; +export function Header(props: PropsWithChildren): JSX.Element; // @public (undocumented) export type HeaderClassKey = @@ -484,7 +484,7 @@ export type IconLinkVerticalProps = { // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // // @public (undocumented) -export function InfoCard(props: Props_16): JSX.Element; +export function InfoCard(props: Props_15): JSX.Element; // @public (undocumented) export type InfoCardClassKey = @@ -565,7 +565,7 @@ export type LifecycleClassKey = 'alpha' | 'beta'; // Warning: (ae-missing-release-tag) "LinearGauge" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function LinearGauge(props: Props_12): JSX.Element | null; +export function LinearGauge(props: Props_11): JSX.Element | null; // Warning: (ae-missing-release-tag) "LinkType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -666,7 +666,7 @@ export type OverflowTooltipClassKey = 'container'; // Warning: (ae-missing-release-tag) "Page" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function Page(props: PropsWithChildren): JSX.Element; +export function Page(props: PropsWithChildren): JSX.Element; // Warning: (ae-missing-release-tag) "PageClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -771,7 +771,7 @@ export type SelectInputBaseClassKey = 'root' | 'input'; // Warning: (ae-missing-release-tag) "Sidebar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function Sidebar(props: PropsWithChildren): JSX.Element; +export function Sidebar(props: PropsWithChildren): JSX.Element; // Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1961,7 +1961,7 @@ export const SidebarSpacer: React_2.ComponentType< // Warning: (ae-missing-release-tag) "SignInPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function SignInPage(props: Props_19): JSX.Element; +export function SignInPage(props: Props_18): JSX.Element; // Warning: (ae-missing-release-tag) "SignInPageClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2050,7 +2050,7 @@ export function StatusWarning(props: PropsWithChildren<{}>): JSX.Element; // Warning: (ae-missing-release-tag) "StructuredMetadataTable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function StructuredMetadataTable(props: Props_13): JSX.Element; +export function StructuredMetadataTable(props: Props_12): JSX.Element; // Warning: (ae-missing-release-tag) "StructuredMetadataTableListClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2132,7 +2132,7 @@ export type TabBarClassKey = 'indicator' | 'flexContainer' | 'root'; // Warning: (ae-missing-release-tag) "TabbedCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function TabbedCard(props: PropsWithChildren): JSX.Element; +export function TabbedCard(props: PropsWithChildren): JSX.Element; // Warning: (ae-missing-release-tag) "TabbedCardClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/packages/core-components/src/components/ProgressBars/Gauge.tsx b/packages/core-components/src/components/ProgressBars/Gauge.tsx index 2bcc0c3976..98d5a8a000 100644 --- a/packages/core-components/src/components/ProgressBars/Gauge.tsx +++ b/packages/core-components/src/components/ProgressBars/Gauge.tsx @@ -46,7 +46,7 @@ const useStyles = makeStyles( { name: 'BackstageGauge' }, ); -type Props = { +export type GaugeProps = { value: number; fractional?: boolean; inverse?: boolean; @@ -66,7 +66,7 @@ export type GetColor = ( args: GetColorArgs, ) => string | BackstageTheme['palette']['error']; -const defaultProps = { +const defaultGaugeProps = { fractional: true, inverse: false, unit: '%', @@ -83,7 +83,7 @@ export const getProgressColor: GetColor = ({ return '#ddd'; } - const actualMax = max ? max : defaultProps.max; + const actualMax = max ? max : defaultGaugeProps.max; const actualValue = inverse ? actualMax - value : value; if (actualValue < actualMax / 3) { @@ -96,12 +96,12 @@ export const getProgressColor: GetColor = ({ }; /** @public */ -export function Gauge(props: Props) { +export function Gauge(props: GaugeProps) { const { getColor = getProgressColor } = props; const classes = useStyles(props); const { palette } = useTheme(); const { value, fractional, inverse, unit, max } = { - ...defaultProps, + ...defaultGaugeProps, ...props, }; From 2e641b622b02b256240f5664a782a820e20cc617 Mon Sep 17 00:00:00 2001 From: James Turley Date: Wed, 10 Nov 2021 14:49:15 +0000 Subject: [PATCH 135/196] Only return string from color getter type Signed-off-by: James Turley --- .../core-components/src/components/ProgressBars/Gauge.tsx | 4 +--- .../src/components/ProgressBars/GaugeCard.stories.tsx | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/core-components/src/components/ProgressBars/Gauge.tsx b/packages/core-components/src/components/ProgressBars/Gauge.tsx index 98d5a8a000..ef39a81d4e 100644 --- a/packages/core-components/src/components/ProgressBars/Gauge.tsx +++ b/packages/core-components/src/components/ProgressBars/Gauge.tsx @@ -62,9 +62,7 @@ type GetColorArgs = { max?: number; }; -export type GetColor = ( - args: GetColorArgs, -) => string | BackstageTheme['palette']['error']; +export type GetColor = (args: GetColorArgs) => string; const defaultGaugeProps = { fractional: true, diff --git a/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx b/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx index f2aad14cd9..64a6cc1b16 100644 --- a/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx +++ b/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx @@ -111,8 +111,8 @@ export const StaticColor = () => ( palette.error} - title="palette.error" + getColor={({ palette }) => palette.status.error} + title="palette.status.error" progress={0.5} /> From 1e3388a0f47843fabbb1b80515514145f5879845 Mon Sep 17 00:00:00 2001 From: James Turley Date: Wed, 10 Nov 2021 15:13:03 +0000 Subject: [PATCH 136/196] Fixup and rename Gauge color getter function types Signed-off-by: James Turley --- .../src/components/ProgressBars/Gauge.tsx | 12 ++++++------ .../src/components/ProgressBars/GaugeCard.tsx | 4 ++-- .../src/components/ProgressBars/LinearGauge.tsx | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/core-components/src/components/ProgressBars/Gauge.tsx b/packages/core-components/src/components/ProgressBars/Gauge.tsx index ef39a81d4e..ef8ec08378 100644 --- a/packages/core-components/src/components/ProgressBars/Gauge.tsx +++ b/packages/core-components/src/components/ProgressBars/Gauge.tsx @@ -15,7 +15,7 @@ */ import { makeStyles, useTheme } from '@material-ui/core/styles'; -import { BackstageTheme } from '@backstage/theme'; +import { BackstagePalette, BackstageTheme } from '@backstage/theme'; import { Circle } from 'rc-progress'; import React from 'react'; @@ -52,17 +52,17 @@ export type GaugeProps = { inverse?: boolean; unit?: string; max?: number; - getColor?: GetColor; + getColor?: GaugePropsGetColor; }; -type GetColorArgs = { - palette: BackstageTheme['palette']; +export type GaugePropsGetColorOptions = { + palette: BackstagePalette; value: number; inverse?: boolean; max?: number; }; -export type GetColor = (args: GetColorArgs) => string; +export type GaugePropsGetColor = (args: GaugePropsGetColorOptions) => string; const defaultGaugeProps = { fractional: true, @@ -71,7 +71,7 @@ const defaultGaugeProps = { max: 100, }; -export const getProgressColor: GetColor = ({ +export const getProgressColor: GaugePropsGetColor = ({ palette, value, inverse, diff --git a/packages/core-components/src/components/ProgressBars/GaugeCard.tsx b/packages/core-components/src/components/ProgressBars/GaugeCard.tsx index f1fd281b35..0fa51b3919 100644 --- a/packages/core-components/src/components/ProgressBars/GaugeCard.tsx +++ b/packages/core-components/src/components/ProgressBars/GaugeCard.tsx @@ -18,7 +18,7 @@ import React from 'react'; import { makeStyles } from '@material-ui/core/styles'; import { InfoCard, InfoCardVariants } from '../../layout/InfoCard'; import { BottomLinkProps } from '../../layout/BottomLink'; -import { Gauge, GetColor } from './Gauge'; +import { Gauge, GaugePropsGetColor } from './Gauge'; type Props = { title: string; @@ -28,7 +28,7 @@ type Props = { progress: number; inverse?: boolean; deepLink?: BottomLinkProps; - getColor?: GetColor; + getColor?: GaugePropsGetColor; }; /** @public */ diff --git a/packages/core-components/src/components/ProgressBars/LinearGauge.tsx b/packages/core-components/src/components/ProgressBars/LinearGauge.tsx index e331ed1fa5..f1e4fb3334 100644 --- a/packages/core-components/src/components/ProgressBars/LinearGauge.tsx +++ b/packages/core-components/src/components/ProgressBars/LinearGauge.tsx @@ -20,14 +20,14 @@ import Tooltip from '@material-ui/core/Tooltip'; // @ts-ignore import { Line } from 'rc-progress'; import { BackstageTheme } from '@backstage/theme'; -import { getProgressColor, GetColor } from './Gauge'; +import { getProgressColor, GaugePropsGetColor } from './Gauge'; type Props = { /** * Progress value between 0.0 - 1.0. */ value: number; - getColor?: GetColor; + getColor?: GaugePropsGetColor; }; export function LinearGauge(props: Props) { From f5e5c297ceb396cb87a3ad0fba734d461da225fb Mon Sep 17 00:00:00 2001 From: James Turley Date: Wed, 10 Nov 2021 15:19:58 +0000 Subject: [PATCH 137/196] Update changeset to reflect naming changes Signed-off-by: James Turley --- .changeset/grumpy-pugs-report.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.changeset/grumpy-pugs-report.md b/.changeset/grumpy-pugs-report.md index 6ee56f2bf7..db915b7a79 100644 --- a/.changeset/grumpy-pugs-report.md +++ b/.changeset/grumpy-pugs-report.md @@ -8,16 +8,16 @@ Add new way to override color selection to progress bar/gauge components. which is a function of the type: ```ts -export type GetColor = (args: { +export type GaugePropsGetColor = (args: { palette: Palette; value: number; inverse?: boolean; max?: number; -}) => string | PaletteColor; +}) => string; ``` -Either return a standard Material UI palette color object or a CSS color -string (e.g. "red", "#f02020"), and the gauge will be set to that color. +Return a standard CSS color string (e.g. "red", "#f02020"), and the gauge will +be set to that color. If the prop is omitted, the default implementation is unchanged from previous versions. From b393c4d4be0ddbf4ce8c343ea95b8a57479b07c6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 10 Nov 2021 14:57:56 +0100 Subject: [PATCH 138/196] cli: fix for config:check only validating frontend config Signed-off-by: Patrik Oldsberg --- .changeset/chilled-pandas-leave.md | 5 +++++ docs/local-dev/cli-commands.md | 3 ++- packages/cli/src/commands/config/print.ts | 1 + packages/cli/src/commands/config/validate.ts | 1 + packages/cli/src/commands/index.ts | 1 + packages/cli/src/lib/config.ts | 5 ++++- 6 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 .changeset/chilled-pandas-leave.md diff --git a/.changeset/chilled-pandas-leave.md b/.changeset/chilled-pandas-leave.md new file mode 100644 index 0000000000..7ad5953f7f --- /dev/null +++ b/.changeset/chilled-pandas-leave.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Fixed the `config:check` command that was incorrectly only validating frontend configuration. Also added a `--frontend` flag to the command which maintains that behavior. diff --git a/docs/local-dev/cli-commands.md b/docs/local-dev/cli-commands.md index 0397ffa09c..0b14a2e136 100644 --- a/docs/local-dev/cli-commands.md +++ b/docs/local-dev/cli-commands.md @@ -512,7 +512,8 @@ Usage: backstage-cli config:check [options] Options: --package <name> Only load config schema that applies to the given package - --lax Do not require environment variables to be set + --lax Do not require environment variables to be set + --frontend Only validate the frontend configuration --config <path> Config files to load instead of app-config.yaml (default: []) -h, --help display help for command ``` diff --git a/packages/cli/src/commands/config/print.ts b/packages/cli/src/commands/config/print.ts index 6bd575f9bf..b640ee4f37 100644 --- a/packages/cli/src/commands/config/print.ts +++ b/packages/cli/src/commands/config/print.ts @@ -25,6 +25,7 @@ export default async (cmd: Command) => { args: cmd.config, fromPackage: cmd.package, mockEnv: cmd.lax, + fullVisibility: !cmd.frontend, }); const visibility = getVisibilityOption(cmd); const data = serializeConfigData(appConfigs, schema, visibility); diff --git a/packages/cli/src/commands/config/validate.ts b/packages/cli/src/commands/config/validate.ts index 9041272c5a..2d3ce60366 100644 --- a/packages/cli/src/commands/config/validate.ts +++ b/packages/cli/src/commands/config/validate.ts @@ -22,5 +22,6 @@ export default async (cmd: Command) => { args: cmd.config, fromPackage: cmd.package, mockEnv: cmd.lax, + fullVisibility: !cmd.frontend, }); }; diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 302f76cc5d..4e01a8ca9f 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -172,6 +172,7 @@ export function registerCommands(program: CommanderStatic) { 'Only load config schema that applies to the given package', ) .option('--lax', 'Do not require environment variables to be set') + .option('--frontend', 'Only validate the frontend configuration') .option(...configOption) .description( 'Validate that the given configuration loads and matches schema', diff --git a/packages/cli/src/lib/config.ts b/packages/cli/src/lib/config.ts index 80b374039f..6f93318ead 100644 --- a/packages/cli/src/lib/config.ts +++ b/packages/cli/src/lib/config.ts @@ -28,6 +28,7 @@ type Options = { fromPackage?: string; mockEnv?: boolean; withFilteredKeys?: boolean; + fullVisibility?: boolean; }; export async function loadCliConfig(options: Options) { @@ -70,7 +71,9 @@ export async function loadCliConfig(options: Options) { try { const frontendAppConfigs = schema.process(appConfigs, { - visibility: ['frontend'], + visibility: options.fullVisibility + ? ['frontend', 'backend', 'secret'] + : ['frontend'], withFilteredKeys: options.withFilteredKeys, }); const frontendConfig = ConfigReader.fromConfigs(frontendAppConfigs); From 145913e6a0bfe4cf4df7f955b262d65b0aac5d7c Mon Sep 17 00:00:00 2001 From: Tim Hansen Date: Wed, 10 Nov 2021 08:25:58 -0700 Subject: [PATCH 139/196] Add missing link to Gitlab discovery Signed-off-by: Tim Hansen --- microsite/sidebars.json | 5 ++++- mkdocs.yml | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 23cc6135c0..91525d4938 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -151,7 +151,10 @@ { "type": "subcategory", "label": "GitLab", - "ids": ["integrations/gitlab/locations"] + "ids": [ + "integrations/gitlab/locations", + "integrations/gitlab/discovery" + ] }, { "type": "subcategory", diff --git a/mkdocs.yml b/mkdocs.yml index 2f4aa4203f..0ddae446e6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -100,6 +100,7 @@ nav: - Org Data: 'integrations/github/org.md' - GitLab: - Locations: 'integrations/gitlab/locations.md' + - Discovery: 'integrations/gitlab/discovery.md' - Google GCS: - Locations: 'integrations/google-cloud-storage/locations.md' - LDAP: From 36099bf941649d26b591007978337a7d15a7b7f3 Mon Sep 17 00:00:00 2001 From: James Turley Date: Wed, 10 Nov 2021 16:18:34 +0000 Subject: [PATCH 140/196] Update API report Signed-off-by: James Turley --- packages/core-components/api-report.md | 24 +++++++++++++++++-- .../src/components/ProgressBars/Gauge.tsx | 3 +++ .../src/components/ProgressBars/index.ts | 5 ++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 13929db7a3..0f1e3510ae 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -7,6 +7,7 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstageIdentityApi } from '@backstage/core-plugin-api'; +import { BackstagePalette } from '@backstage/theme'; import { BackstageTheme } from '@backstage/theme'; import { ButtonProps as ButtonProps_2 } from '@material-ui/core/Button'; import { CardHeaderProps } from '@material-ui/core/CardHeader'; @@ -368,8 +369,6 @@ export function FeatureCalloutCircular( // @public (undocumented) export type FiltersContainerClassKey = 'root' | 'title'; -// Warning: (ae-forgotten-export) The symbol "GaugeProps" needs to be exported by the entry point index.d.ts -// // @public (undocumented) export function Gauge(props: GaugeProps): JSX.Element; @@ -384,6 +383,27 @@ export type GaugeCardClassKey = 'root'; // @public (undocumented) export type GaugeClassKey = 'root' | 'overlay' | 'circle' | 'colorUnknown'; +// @public (undocumented) +export type GaugeProps = { + value: number; + fractional?: boolean; + inverse?: boolean; + unit?: string; + max?: number; + getColor?: GaugePropsGetColor; +}; + +// @public (undocumented) +export type GaugePropsGetColor = (args: GaugePropsGetColorOptions) => string; + +// @public (undocumented) +export type GaugePropsGetColorOptions = { + palette: BackstagePalette; + value: number; + inverse?: boolean; + max?: number; +}; + // @public (undocumented) export function GitHubIcon(props: IconComponentProps): JSX.Element; diff --git a/packages/core-components/src/components/ProgressBars/Gauge.tsx b/packages/core-components/src/components/ProgressBars/Gauge.tsx index ef8ec08378..00e7b3257b 100644 --- a/packages/core-components/src/components/ProgressBars/Gauge.tsx +++ b/packages/core-components/src/components/ProgressBars/Gauge.tsx @@ -46,6 +46,7 @@ const useStyles = makeStyles( { name: 'BackstageGauge' }, ); +/** @public */ export type GaugeProps = { value: number; fractional?: boolean; @@ -55,6 +56,7 @@ export type GaugeProps = { getColor?: GaugePropsGetColor; }; +/** @public */ export type GaugePropsGetColorOptions = { palette: BackstagePalette; value: number; @@ -62,6 +64,7 @@ export type GaugePropsGetColorOptions = { max?: number; }; +/** @public */ export type GaugePropsGetColor = (args: GaugePropsGetColorOptions) => string; const defaultGaugeProps = { diff --git a/packages/core-components/src/components/ProgressBars/index.ts b/packages/core-components/src/components/ProgressBars/index.ts index 3d821eaac2..5e588ea696 100644 --- a/packages/core-components/src/components/ProgressBars/index.ts +++ b/packages/core-components/src/components/ProgressBars/index.ts @@ -17,5 +17,10 @@ export { GaugeCard } from './GaugeCard'; export type { GaugeCardClassKey } from './GaugeCard'; export { Gauge } from './Gauge'; +export type { + GaugeProps, + GaugePropsGetColor, + GaugePropsGetColorOptions, +} from './Gauge'; export type { GaugeClassKey } from './Gauge'; export { LinearGauge } from './LinearGauge'; From 88677aee18aeb59b2b2e1665bc61aa08a57bf20a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Nov 2021 04:09:19 +0000 Subject: [PATCH 141/196] build(deps): bump @spotify/eslint-config-react from 10.0.0 to 12.0.0 Bumps [@spotify/eslint-config-react](https://github.com/spotify/web-scripts) from 10.0.0 to 12.0.0. - [Release notes](https://github.com/spotify/web-scripts/releases) - [Changelog](https://github.com/spotify/web-scripts/blob/master/CHANGELOG.md) - [Commits](https://github.com/spotify/web-scripts/compare/v10.0.0...v12.0.0) --- updated-dependencies: - dependency-name: "@spotify/eslint-config-react" dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- packages/cli/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 23a2bef027..0fca97f317 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -42,7 +42,7 @@ "@rollup/plugin-node-resolve": "^13.0.0", "@rollup/plugin-yaml": "^3.0.0", "@spotify/eslint-config-base": "^12.0.0", - "@spotify/eslint-config-react": "^10.0.0", + "@spotify/eslint-config-react": "^12.0.0", "@spotify/eslint-config-typescript": "^12.0.0", "@sucrase/jest-plugin": "^2.1.1", "@sucrase/webpack-loader": "^2.0.0", diff --git a/yarn.lock b/yarn.lock index 42c43fe4b7..6c23c706b1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5481,10 +5481,10 @@ resolved "https://registry.npmjs.org/@spotify/eslint-config-base/-/eslint-config-base-12.0.0.tgz#0b1e41bb436d5c1c20714703629514d64c3c0f06" integrity sha512-5Uud/TmzakqmdUNCZpD8JFQRa2VG3dVd3DanSMpU/nVdu6K5LyX8EMU3Tz1vGP18Wih8iAu/sBSJhntNzw7e6w== -"@spotify/eslint-config-react@^10.0.0": - version "10.0.0" - resolved "https://registry.npmjs.org/@spotify/eslint-config-react/-/eslint-config-react-10.0.0.tgz#6f83ada05f79b49c1f9def5b8815e3231ed24969" - integrity sha512-MozX6W3aMp7EQPliuUQYI58Ni5vh65mItXMG0CgZBj0v1ZEeZVM5XS/nqhsCaIHYXskmZM2O1qqLFaEg5PqGdg== +"@spotify/eslint-config-react@^12.0.0": + version "12.0.0" + resolved "https://registry.npmjs.org/@spotify/eslint-config-react/-/eslint-config-react-12.0.0.tgz#5b8d4bc3b81a8ec2824648f482f1f6c3cf711893" + integrity sha512-lNHZRtJesNA273OJHBVUGAg2JYyVDZ+bsT7h3OwnX1HYgejJ3YcKPSziPM8TGFAN8DruH3tHFfaM63uAIA1+uw== "@spotify/eslint-config-typescript@^12.0.0": version "12.0.0" From 8af66229e7535daa0269f8bf979d942163b457b1 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 9 Nov 2021 19:38:12 +0100 Subject: [PATCH 142/196] add changeset for @spotify/eslint-config-react bump Signed-off-by: Patrik Oldsberg --- .changeset/rich-meals-switch.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/rich-meals-switch.md diff --git a/.changeset/rich-meals-switch.md b/.changeset/rich-meals-switch.md new file mode 100644 index 0000000000..8a9467a84e --- /dev/null +++ b/.changeset/rich-meals-switch.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Bumped `@spotify/eslint-config-react` from `v10` to `v12`, dropping support for Node.js v12. From 79dc3f9a29603dc7866a2907acb8f01bc0e55564 Mon Sep 17 00:00:00 2001 From: Harry Hogg Date: Thu, 11 Nov 2021 08:09:01 +0000 Subject: [PATCH 143/196] Pass the GH token as an ENV to the Github step of the Snyk workflow Signed-off-by: Harry Hogg --- .github/workflows/snyk-github-issue-sync.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/snyk-github-issue-sync.yml b/.github/workflows/snyk-github-issue-sync.yml index 77604a126d..0c50b2c505 100644 --- a/.github/workflows/snyk-github-issue-sync.yml +++ b/.github/workflows/snyk-github-issue-sync.yml @@ -57,3 +57,5 @@ jobs: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - name: Update Github issues run: yarn ts-node scripts/snyk-github-issue-sync.ts + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 5dcea2586c79f1d41609d70c9139fdc204fdc2d1 Mon Sep 17 00:00:00 2001 From: Rod Machen Date: Thu, 11 Nov 2021 02:26:15 -0600 Subject: [PATCH 144/196] Add Search Modal component (#7316) Signed-off-by: rodmachen --- .changeset/flat-tomatoes-drop.md | 28 +++ .changeset/proud-eels-work.md | 17 ++ docs/features/search/getting-started.md | 19 ++ packages/app/src/components/Root/Root.tsx | 4 +- .../packages/app/src/components/Root/Root.tsx | 4 +- plugins/search/api-report.md | 23 +++ .../SearchModal/SearchModal.test.tsx | 77 +++++++++ .../components/SearchModal/SearchModal.tsx | 162 ++++++++++++++++++ .../src/components/SearchModal/index.tsx | 17 ++ .../SidebarSearchModal/SidebarSearchModal.tsx | 36 ++++ .../components/SidebarSearchModal/index.ts | 16 ++ plugins/search/src/components/index.tsx | 2 + plugins/search/src/index.ts | 5 +- plugins/search/src/plugin.ts | 12 ++ 14 files changed, 417 insertions(+), 5 deletions(-) create mode 100644 .changeset/flat-tomatoes-drop.md create mode 100644 .changeset/proud-eels-work.md create mode 100644 plugins/search/src/components/SearchModal/SearchModal.test.tsx create mode 100644 plugins/search/src/components/SearchModal/SearchModal.tsx create mode 100644 plugins/search/src/components/SearchModal/index.tsx create mode 100644 plugins/search/src/components/SidebarSearchModal/SidebarSearchModal.tsx create mode 100644 plugins/search/src/components/SidebarSearchModal/index.ts diff --git a/.changeset/flat-tomatoes-drop.md b/.changeset/flat-tomatoes-drop.md new file mode 100644 index 0000000000..97ea32d8fd --- /dev/null +++ b/.changeset/flat-tomatoes-drop.md @@ -0,0 +1,28 @@ +--- +'@backstage/create-app': patch +--- + +Integrated `SidebarSearchModal` component into default-app to use the `SearchModal`. + +The `SidebarSearchModal` component can also be used in other generated apps: + +```diff +import { +- SidebarSearch, ++ SidebarSearchModal +} from '@backstage/plugin-search'; +... + + + +- ++ + +... +``` + +If you only want to use the `SearchModal` you can import it from `'@backstage/plugin-search'`: + +```js +import { SearchModal } from '@backstage/plugin-search'; +``` diff --git a/.changeset/proud-eels-work.md b/.changeset/proud-eels-work.md new file mode 100644 index 0000000000..a4f2c86eb4 --- /dev/null +++ b/.changeset/proud-eels-work.md @@ -0,0 +1,17 @@ +--- +'@backstage/plugin-search': patch +--- + +Added `SearchModal` component. + +Now you can import `SearchModal` in your apps: + +```js +import { SearchModal } from '@backstage/plugin-search'; +``` + +You can also use the `SidebarSearchModal` component to integrate it into the sidebar of your sample apps: + +```js +import { SidebarSearchModal } from '@backstage/plugin-search'; +``` diff --git a/docs/features/search/getting-started.md b/docs/features/search/getting-started.md index aa40c1e539..27219e529a 100644 --- a/docs/features/search/getting-started.md +++ b/docs/features/search/getting-started.md @@ -109,6 +109,25 @@ const routes = ( ); ``` +### Using the Search Modal + +In `Root.tsx`, add the `SidebarSearchModal` component: + +```bash +import { SidebarSearchModal } from '@backstage/plugin-search'; + +export const Root = ({ children }: PropsWithChildren<{}>) => ( + + + + + +... +``` + +For more information about using `Root.tsx`, please see +[the changelog](https://github.com/backstage/backstage/blob/master/packages/create-app/CHANGELOG.md#0315). + ## Adding Search to the Backend Add the following plugins into your backend app: diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 7a666a020a..f365310595 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -29,7 +29,7 @@ import LogoIcon from './LogoIcon'; import { NavLink } from 'react-router-dom'; import { GraphiQLIcon } from '@backstage/plugin-graphiql'; import { Settings as SidebarSettings } from '@backstage/plugin-user-settings'; -import { SidebarSearch } from '@backstage/plugin-search'; +import { SidebarSearchModal } from '@backstage/plugin-search'; import { Shortcuts } from '@backstage/plugin-shortcuts'; import { Sidebar, @@ -79,7 +79,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( - + {/* Global nav, not org-specific */} diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx index 198e7ec2b1..ec59b0b116 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx @@ -25,7 +25,7 @@ import LogoFull from './LogoFull'; import LogoIcon from './LogoIcon'; import { NavLink } from 'react-router-dom'; import { Settings as SidebarSettings } from '@backstage/plugin-user-settings'; -import { SidebarSearch } from '@backstage/plugin-search'; +import { SidebarSearchModal } from '@backstage/plugin-search'; import { Sidebar, SidebarPage, @@ -74,7 +74,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( - + {/* Global nav, not org-specific */} diff --git a/plugins/search/api-report.md b/plugins/search/api-report.md index c638fb4af6..0e0ad01fca 100644 --- a/plugins/search/api-report.md +++ b/plugins/search/api-report.md @@ -126,6 +126,24 @@ export const SearchFilterNext: { Select(props: Omit & Component): JSX.Element; }; +// Warning: (ae-missing-release-tag) "SearchModal" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const SearchModal: ({ + open, + toggleModal, +}: SearchModalProps) => JSX.Element; + +// Warning: (ae-missing-release-tag) "SearchModalProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface SearchModalProps { + // (undocumented) + open?: boolean; + // (undocumented) + toggleModal: () => void; +} + // Warning: (ae-missing-release-tag) "SearchPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -179,6 +197,11 @@ export const SearchType: ({ // @public (undocumented) export const SidebarSearch: () => JSX.Element; +// Warning: (ae-missing-release-tag) "SidebarSearchModal" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const SidebarSearchModal: () => JSX.Element; + // Warning: (ae-forgotten-export) The symbol "SearchContextValue" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "useSearch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/plugins/search/src/components/SearchModal/SearchModal.test.tsx b/plugins/search/src/components/SearchModal/SearchModal.test.tsx new file mode 100644 index 0000000000..d7f873abf3 --- /dev/null +++ b/plugins/search/src/components/SearchModal/SearchModal.test.tsx @@ -0,0 +1,77 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { screen } from '@testing-library/react'; +import { renderInTestApp } from '@backstage/test-utils'; +import userEvent from '@testing-library/user-event'; +import { configApiRef } from '@backstage/core-plugin-api'; +import { + ApiProvider, + ApiRegistry, + ConfigReader, +} from '@backstage/core-app-api'; +import { rootRouteRef } from '../../plugin'; +import { searchApiRef } from '../../apis'; + +import { SearchModal } from './SearchModal'; + +jest.mock('../SearchContext', () => ({ + ...jest.requireActual('../SearchContext'), + useSearch: jest.fn().mockReturnValue({ + result: {}, + }), +})); + +describe('SearchModal', () => { + const query = jest.fn().mockResolvedValue({}); + + const apiRegistry = ApiRegistry.from([ + [configApiRef, new ConfigReader({ app: { title: 'Mock app' } })], + [searchApiRef, { query }], + ]); + + const toggleModal = jest.fn(); + + it('Should render the Modal correctly', async () => { + await renderInTestApp( + + + , + { + mountedRoutes: { + '/search': rootRouteRef, + }, + }, + ); + + expect(screen.getByRole('dialog')).toBeInTheDocument(); + }); + + it('Calls toggleModal handler', async () => { + await renderInTestApp( + + + , + { + mountedRoutes: { + '/search': rootRouteRef, + }, + }, + ); + userEvent.keyboard('{esc}'); + expect(toggleModal).toHaveBeenCalledTimes(1); + }); +}); diff --git a/plugins/search/src/components/SearchModal/SearchModal.tsx b/plugins/search/src/components/SearchModal/SearchModal.tsx new file mode 100644 index 0000000000..feabbc55af --- /dev/null +++ b/plugins/search/src/components/SearchModal/SearchModal.tsx @@ -0,0 +1,162 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { useEffect, useState } from 'react'; +import { + Dialog, + DialogActions, + DialogContent, + DialogTitle, + Divider, + Grid, + List, + Paper, +} from '@material-ui/core'; +import { Launch } from '@material-ui/icons/'; +import { makeStyles } from '@material-ui/core/styles'; +import { SearchBarBase } from '../SearchBar'; +import { DefaultResultListItem } from '../DefaultResultListItem'; +import { SearchResult } from '../SearchResult'; +import { SearchContextProvider, useSearch } from '../SearchContext'; +import { SearchResultPager } from '../SearchResultPager'; +import { useRouteRef } from '@backstage/core-plugin-api'; +import { Link } from '@backstage/core-components'; +import { rootRouteRef } from '../../plugin'; + +import { useDebounce } from 'react-use'; + +export interface SearchModalProps { + open?: boolean; + toggleModal: () => void; +} + +const useStyles = makeStyles(theme => ({ + container: { + borderRadius: 30, + display: 'flex', + height: '2.4em', + }, + input: { + flex: 1, + }, + // Reduces default height of the modal, keeping a gap of 128px between the top and bottom of the page. + paperFullWidth: { height: 'calc(100% - 128px)' }, + dialogActionsContainer: { padding: theme.spacing(1, 3) }, + viewResultsLink: { verticalAlign: '0.5em' }, +})); + +export const Modal = ({ open = true, toggleModal }: SearchModalProps) => { + const getSearchLink = useRouteRef(rootRouteRef); + const classes = useStyles(); + + const { term, setTerm } = useSearch(); + const [value, setValue] = useState(term); + + useEffect(() => { + setValue(prevValue => (prevValue !== term ? term : prevValue)); + }, [term]); + + useDebounce(() => setTerm(value), 1000, [value]); + + const handleQuery = (newValue: string) => { + setValue(newValue); + }; + + const handleClear = () => setValue(''); + + const handleResultClick = () => { + toggleModal(); + handleClear(); + }; + + const handleKeyPress = () => { + handleResultClick(); + }; + + return ( + + + + + + + + + + + View Full Results + + + + + + + {({ results }) => ( + + {results.map(({ document }) => ( +
+ +
+ ))} +
+ )} +
+
+ + + + + + + +
+ ); +}; + +export const SearchModal = ({ open = true, toggleModal }: SearchModalProps) => { + return ( + + + + ); +}; diff --git a/plugins/search/src/components/SearchModal/index.tsx b/plugins/search/src/components/SearchModal/index.tsx new file mode 100644 index 0000000000..ec0053951d --- /dev/null +++ b/plugins/search/src/components/SearchModal/index.tsx @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { SearchModal } from './SearchModal'; +export type { SearchModalProps } from './SearchModal'; diff --git a/plugins/search/src/components/SidebarSearchModal/SidebarSearchModal.tsx b/plugins/search/src/components/SidebarSearchModal/SidebarSearchModal.tsx new file mode 100644 index 0000000000..4d4fa1a9fc --- /dev/null +++ b/plugins/search/src/components/SidebarSearchModal/SidebarSearchModal.tsx @@ -0,0 +1,36 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { useState } from 'react'; +import SearchIcon from '@material-ui/icons/Search'; +import { SearchModal } from '../SearchModal'; +import { SidebarItem } from '@backstage/core-components'; + +export const SidebarSearchModal = () => { + const [open, setOpen] = useState(false); + const toggleModal = (): void => setOpen(prevState => !prevState); + + return ( + <> + + + + ); +}; diff --git a/plugins/search/src/components/SidebarSearchModal/index.ts b/plugins/search/src/components/SidebarSearchModal/index.ts new file mode 100644 index 0000000000..849180878d --- /dev/null +++ b/plugins/search/src/components/SidebarSearchModal/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { SidebarSearchModal } from './SidebarSearchModal'; diff --git a/plugins/search/src/components/index.tsx b/plugins/search/src/components/index.tsx index 8b6493ae9b..6a4eae4001 100644 --- a/plugins/search/src/components/index.tsx +++ b/plugins/search/src/components/index.tsx @@ -19,8 +19,10 @@ export * from './Filters'; export * from './SearchBar'; export * from './SearchContext'; export * from './SearchFilter'; +export * from './SearchModal'; export * from './SearchPage'; export * from './SearchResult'; export * from './SearchResultPager'; export * from './SearchType'; export * from './SidebarSearch'; +export * from './SidebarSearchModal'; diff --git a/plugins/search/src/index.ts b/plugins/search/src/index.ts index 07e4443f04..09cf204017 100644 --- a/plugins/search/src/index.ts +++ b/plugins/search/src/index.ts @@ -28,20 +28,23 @@ export { SearchContextProvider, SearchFilter, SearchFilterNext, + SearchModal, SearchPage as Router, SearchResultPager, SearchType, SidebarSearch, useSearch, } from './components'; +export type { SearchModalProps } from './components'; export type { FiltersState } from './components'; export { DefaultResultListItem, + HomePageSearchBar, SearchBarNext, SearchPage, SearchPageNext, searchPlugin as plugin, searchPlugin, SearchResult, - HomePageSearchBar, + SidebarSearchModal, } from './plugin'; diff --git a/plugins/search/src/plugin.ts b/plugins/search/src/plugin.ts index 491582aead..b8c016fb36 100644 --- a/plugins/search/src/plugin.ts +++ b/plugins/search/src/plugin.ts @@ -122,6 +122,18 @@ export const SearchResultNext = searchPlugin.provide( }), ); +export const SidebarSearchModal = searchPlugin.provide( + createComponentExtension({ + name: 'SidebarSearchModal', + component: { + lazy: () => + import('./components/SidebarSearchModal').then( + m => m.SidebarSearchModal, + ), + }, + }), +); + export const DefaultResultListItem = searchPlugin.provide( createComponentExtension({ name: 'DefaultResultListItem', From fe5738fe1c8dd286f374bb0fe8f81e2009210e56 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Mon, 1 Nov 2021 16:25:46 +0100 Subject: [PATCH 145/196] Lazy load `LazyLog` as it is rarely used Signed-off-by: Oliver Sand --- .changeset/gold-cobras-stare.md | 6 ++ .../src/components/TaskPage/TaskPage.tsx | 60 +++++++++++-------- .../components/TechDocsBuildLogs.test.tsx | 21 ++++--- .../reader/components/TechDocsBuildLogs.tsx | 27 +++++---- 4 files changed, 66 insertions(+), 48 deletions(-) create mode 100644 .changeset/gold-cobras-stare.md diff --git a/.changeset/gold-cobras-stare.md b/.changeset/gold-cobras-stare.md new file mode 100644 index 0000000000..d3265c865a --- /dev/null +++ b/.changeset/gold-cobras-stare.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder': patch +'@backstage/plugin-techdocs': patch +--- + +Lazy load `LazyLog` as it is rarely used. diff --git a/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx b/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx index aeb47b2789..8d22293cb1 100644 --- a/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx +++ b/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx @@ -14,38 +14,40 @@ * limitations under the License. */ -import React, { useState, useEffect, memo, useMemo } from 'react'; -import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; -import Stepper from '@material-ui/core/Stepper'; -import Step from '@material-ui/core/Step'; -import StepLabel from '@material-ui/core/StepLabel'; -import Grid from '@material-ui/core/Grid'; -import Typography from '@material-ui/core/Typography'; -import { useParams } from 'react-router'; -import { useTaskEventStream } from '../hooks/useEventStream'; -import LazyLog from 'react-lazylog/build/LazyLog'; +import { + Content, + ErrorPage, + Header, + Lifecycle, + Page, +} from '@backstage/core-components'; +import { BackstageTheme } from '@backstage/theme'; import { CircularProgress, + LinearProgress, Paper, StepButton, StepIconProps, } from '@material-ui/core'; -import { Status, TaskOutput } from '../../types'; -import { DateTime, Interval } from 'luxon'; -import { useInterval } from 'react-use'; -import Check from '@material-ui/icons/Check'; +import Grid from '@material-ui/core/Grid'; +import Step from '@material-ui/core/Step'; +import StepLabel from '@material-ui/core/StepLabel'; +import Stepper from '@material-ui/core/Stepper'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; import Cancel from '@material-ui/icons/Cancel'; +import Check from '@material-ui/icons/Check'; import FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord'; import classNames from 'classnames'; -import { BackstageTheme } from '@backstage/theme'; +import { DateTime, Interval } from 'luxon'; +import React, { memo, Suspense, useEffect, useMemo, useState } from 'react'; +import { useParams } from 'react-router'; +import { useInterval } from 'react-use'; +import { Status, TaskOutput } from '../../types'; +import { useTaskEventStream } from '../hooks/useEventStream'; import { TaskPageLinks } from './TaskPageLinks'; -import { - Page, - Header, - Lifecycle, - Content, - ErrorPage, -} from '@backstage/core-components'; + +const LazyLog = React.lazy(() => import('react-lazylog/build/LazyLog')); // typings are wrong for this library, so fallback to not parsing types. const humanizeDuration = require('humanize-duration'); @@ -213,9 +215,17 @@ export const TaskStatusStepper = memo( const TaskLogger = memo(({ log }: { log: string }) => { return ( -
- -
+ }> +
+ +
+
); }); diff --git a/plugins/techdocs/src/reader/components/TechDocsBuildLogs.test.tsx b/plugins/techdocs/src/reader/components/TechDocsBuildLogs.test.tsx index d1bb99b76c..fa82527282 100644 --- a/plugins/techdocs/src/reader/components/TechDocsBuildLogs.test.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsBuildLogs.test.tsx @@ -23,9 +23,9 @@ import { // react-lazylog is based on a react-virtualized component which doesn't // write the content to the dom, so we mock it. -jest.mock('react-lazylog', () => { +jest.mock('react-lazylog/build/LazyLog', () => { return { - LazyLog: ({ text }: { text: string }) => { + default: ({ text }: { text: string }) => { return

{text}

; }, }; @@ -46,18 +46,20 @@ describe('', () => { }); describe('', () => { - it('should render with empty log', () => { + it('should render with empty log', async () => { const onClose = jest.fn(); const rendered = render( , ); expect(rendered.getByText(/Build Details/i)).toBeInTheDocument(); - expect(rendered.getByText(/Waiting for logs.../i)).toBeInTheDocument(); + expect( + await rendered.findByText(/Waiting for logs.../i), + ).toBeInTheDocument(); expect(onClose).toBeCalledTimes(0); }); - it('should render with empty logs', () => { + it('should render logs', async () => { const onClose = jest.fn(); const rendered = render( ', () => { />, ); expect(rendered.getByText(/Build Details/i)).toBeInTheDocument(); - expect( - rendered.queryByText(/Waiting for logs.../i), - ).not.toBeInTheDocument(); - expect(rendered.getByText(/Line 1/i)).toBeInTheDocument(); - expect(rendered.getByText(/Line 2/i)).toBeInTheDocument(); + expect(await rendered.findByText(/Line 1/i)).toBeInTheDocument(); + expect(await rendered.findByText(/Line 2/i)).toBeInTheDocument(); expect(onClose).toBeCalledTimes(0); }); @@ -80,7 +79,7 @@ describe('', () => { const rendered = render( , ); - rendered.getByRole('button').click(); + rendered.getByTitle('Close the drawer').click(); expect(onClose).toBeCalledTimes(1); }); diff --git a/plugins/techdocs/src/reader/components/TechDocsBuildLogs.tsx b/plugins/techdocs/src/reader/components/TechDocsBuildLogs.tsx index 4283932fb3..0850c0e291 100644 --- a/plugins/techdocs/src/reader/components/TechDocsBuildLogs.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsBuildLogs.tsx @@ -20,14 +20,15 @@ import { Drawer, Grid, IconButton, + LinearProgress, makeStyles, Theme, Typography, } from '@material-ui/core'; import Close from '@material-ui/icons/Close'; -import * as React from 'react'; -import { useState } from 'react'; -import { LazyLog } from 'react-lazylog'; +import React, { Suspense, useState } from 'react'; + +const LazyLog = React.lazy(() => import('react-lazylog/build/LazyLog')); const useDrawerStyles = makeStyles((theme: Theme) => createStyles({ @@ -83,15 +84,17 @@ export const TechDocsBuildLogsDrawerContent = ({ - + }> + + ); }; From 7e5bd18cf7774f07eb54c072d2e48de73ebee899 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Tue, 2 Nov 2021 09:58:11 +0100 Subject: [PATCH 146/196] Adjust to review comments Signed-off-by: Oliver Sand --- plugins/api-docs/api-report.md | 44 +++++++++++++------ .../AsyncApiDefinitionWidget.tsx | 8 ++-- .../AsyncApiDefinitionWidget/index.ts | 1 + .../GraphQlDefinitionWidget.tsx | 8 ++-- .../GraphQlDefinitionWidget/index.ts | 1 + .../OpenApiDefinitionWidget.tsx | 8 ++-- .../OpenApiDefinitionWidget/index.ts | 1 + .../PlainApiDefinitionWidget.tsx | 12 +++-- .../PlainApiDefinitionWidget/index.ts | 1 + .../src/components/TaskPage/TaskPage.tsx | 4 +- .../reader/components/TechDocsBuildLogs.tsx | 4 +- 11 files changed, 60 insertions(+), 32 deletions(-) diff --git a/plugins/api-docs/api-report.md b/plugins/api-docs/api-report.md index 8ec6175a6d..111660ae07 100644 --- a/plugins/api-docs/api-report.md +++ b/plugins/api-docs/api-report.md @@ -84,13 +84,19 @@ export const ApiTypeTitle: ({ apiEntity: ApiEntity; }) => JSX.Element; -// Warning: (ae-forgotten-export) The symbol "AsyncApiDefinitionWidgetProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "AsyncApiDefinitionWidget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const AsyncApiDefinitionWidget: ({ - definition, -}: AsyncApiDefinitionWidgetProps) => JSX.Element; +export const AsyncApiDefinitionWidget: ( + props: AsyncApiDefinitionWidgetProps, +) => JSX.Element; + +// Warning: (ae-missing-release-tag) "AsyncApiDefinitionWidgetProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type AsyncApiDefinitionWidgetProps = { + definition: string; +}; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ConsumedApisCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -171,22 +177,34 @@ export const EntityProvidingComponentsCard: ({ // @public (undocumented) export const HasApisCard: ({ variant }: Props_3) => JSX.Element; -// Warning: (ae-forgotten-export) The symbol "OpenApiDefinitionWidgetProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "OpenApiDefinitionWidget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const OpenApiDefinitionWidget: ({ - definition, -}: OpenApiDefinitionWidgetProps) => JSX.Element; +export const OpenApiDefinitionWidget: ( + props: OpenApiDefinitionWidgetProps, +) => JSX.Element; + +// Warning: (ae-missing-release-tag) "OpenApiDefinitionWidgetProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type OpenApiDefinitionWidgetProps = { + definition: string; +}; -// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "PlainApiDefinitionWidget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const PlainApiDefinitionWidget: ({ - definition, - language, -}: Props_7) => JSX.Element; +export const PlainApiDefinitionWidget: ( + props: PlainApiDefinitionWidgetProps, +) => JSX.Element; + +// Warning: (ae-missing-release-tag) "PlainApiDefinitionWidgetProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PlainApiDefinitionWidgetProps = { + definition: any; + language: string; +}; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ProvidedApisCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx index a3d9bc1f64..03bc6fa8a6 100644 --- a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx +++ b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx @@ -29,12 +29,12 @@ export type AsyncApiDefinitionWidgetProps = { definition: string; }; -export const AsyncApiDefinitionWidget = ({ - definition, -}: AsyncApiDefinitionWidgetProps) => { +export const AsyncApiDefinitionWidget = ( + props: AsyncApiDefinitionWidgetProps, +) => { return ( }> - + ); }; diff --git a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/index.ts b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/index.ts index dcca901be2..7cc49e3138 100644 --- a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/index.ts +++ b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/index.ts @@ -15,3 +15,4 @@ */ export { AsyncApiDefinitionWidget } from './AsyncApiDefinitionWidget'; +export type { AsyncApiDefinitionWidgetProps } from './AsyncApiDefinitionWidget'; diff --git a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.tsx b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.tsx index e91387bffa..1f8a796249 100644 --- a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.tsx +++ b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.tsx @@ -29,12 +29,12 @@ export type GraphQlDefinitionWidgetProps = { definition: string; }; -export const GraphQlDefinitionWidget = ({ - definition, -}: GraphQlDefinitionWidgetProps) => { +export const GraphQlDefinitionWidget = ( + props: GraphQlDefinitionWidgetProps, +) => { return ( }> - + ); }; diff --git a/plugins/api-docs/src/components/GraphQlDefinitionWidget/index.ts b/plugins/api-docs/src/components/GraphQlDefinitionWidget/index.ts index 9173f492b0..8645b3c372 100644 --- a/plugins/api-docs/src/components/GraphQlDefinitionWidget/index.ts +++ b/plugins/api-docs/src/components/GraphQlDefinitionWidget/index.ts @@ -15,3 +15,4 @@ */ export { GraphQlDefinitionWidget } from './GraphQlDefinitionWidget'; +export type { GraphQlDefinitionWidgetProps } from './GraphQlDefinitionWidget'; diff --git a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.tsx b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.tsx index 2c223243d1..606e476454 100644 --- a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.tsx +++ b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.tsx @@ -29,12 +29,12 @@ export type OpenApiDefinitionWidgetProps = { definition: string; }; -export const OpenApiDefinitionWidget = ({ - definition, -}: OpenApiDefinitionWidgetProps) => { +export const OpenApiDefinitionWidget = ( + props: OpenApiDefinitionWidgetProps, +) => { return ( }> - + ); }; diff --git a/plugins/api-docs/src/components/OpenApiDefinitionWidget/index.ts b/plugins/api-docs/src/components/OpenApiDefinitionWidget/index.ts index 94401c92a1..e317d42a59 100644 --- a/plugins/api-docs/src/components/OpenApiDefinitionWidget/index.ts +++ b/plugins/api-docs/src/components/OpenApiDefinitionWidget/index.ts @@ -15,3 +15,4 @@ */ export { OpenApiDefinitionWidget } from './OpenApiDefinitionWidget'; +export type { OpenApiDefinitionWidgetProps } from './OpenApiDefinitionWidget'; diff --git a/plugins/api-docs/src/components/PlainApiDefinitionWidget/PlainApiDefinitionWidget.tsx b/plugins/api-docs/src/components/PlainApiDefinitionWidget/PlainApiDefinitionWidget.tsx index efdd882564..b7f1e8afc0 100644 --- a/plugins/api-docs/src/components/PlainApiDefinitionWidget/PlainApiDefinitionWidget.tsx +++ b/plugins/api-docs/src/components/PlainApiDefinitionWidget/PlainApiDefinitionWidget.tsx @@ -17,13 +17,19 @@ import React from 'react'; import { CodeSnippet } from '@backstage/core-components'; -type Props = { +export type PlainApiDefinitionWidgetProps = { definition: any; language: string; }; -export const PlainApiDefinitionWidget = ({ definition, language }: Props) => { +export const PlainApiDefinitionWidget = ( + props: PlainApiDefinitionWidgetProps, +) => { return ( - + ); }; diff --git a/plugins/api-docs/src/components/PlainApiDefinitionWidget/index.ts b/plugins/api-docs/src/components/PlainApiDefinitionWidget/index.ts index c233652981..562a3e67c1 100644 --- a/plugins/api-docs/src/components/PlainApiDefinitionWidget/index.ts +++ b/plugins/api-docs/src/components/PlainApiDefinitionWidget/index.ts @@ -15,3 +15,4 @@ */ export { PlainApiDefinitionWidget } from './PlainApiDefinitionWidget'; +export type { PlainApiDefinitionWidgetProps } from './PlainApiDefinitionWidget'; diff --git a/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx b/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx index 8d22293cb1..11d12282d2 100644 --- a/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx +++ b/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx @@ -20,11 +20,11 @@ import { Header, Lifecycle, Page, + Progress, } from '@backstage/core-components'; import { BackstageTheme } from '@backstage/theme'; import { CircularProgress, - LinearProgress, Paper, StepButton, StepIconProps, @@ -215,7 +215,7 @@ export const TaskStatusStepper = memo( const TaskLogger = memo(({ log }: { log: string }) => { return ( - }> + }>
- }> + }> Date: Tue, 2 Nov 2021 10:00:10 +0100 Subject: [PATCH 147/196] Use `` instead of `` in combination with `Suspense` Signed-off-by: Oliver Sand --- .changeset/dull-mangos-count.md | 6 ++++++ .../lib/ActionOutput/ActionOutput.tsx | 4 ++-- .../WorkflowRunLogs/WorkflowRunLogs.tsx | 19 +++++++++---------- 3 files changed, 17 insertions(+), 12 deletions(-) create mode 100644 .changeset/dull-mangos-count.md diff --git a/.changeset/dull-mangos-count.md b/.changeset/dull-mangos-count.md new file mode 100644 index 0000000000..64a01c121d --- /dev/null +++ b/.changeset/dull-mangos-count.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-circleci': patch +'@backstage/plugin-github-actions': patch +--- + +Use `` instead of `` in combination with `Suspense`. diff --git a/plugins/circleci/src/components/BuildWithStepsPage/lib/ActionOutput/ActionOutput.tsx b/plugins/circleci/src/components/BuildWithStepsPage/lib/ActionOutput/ActionOutput.tsx index bbfc1e468c..a4c2af241f 100644 --- a/plugins/circleci/src/components/BuildWithStepsPage/lib/ActionOutput/ActionOutput.tsx +++ b/plugins/circleci/src/components/BuildWithStepsPage/lib/ActionOutput/ActionOutput.tsx @@ -14,11 +14,11 @@ * limitations under the License. */ +import { Progress } from '@backstage/core-components'; import { Accordion, AccordionDetails, AccordionSummary, - LinearProgress, Typography, } from '@material-ui/core'; import { makeStyles } from '@material-ui/core/styles'; @@ -85,7 +85,7 @@ export const ActionOutput = ({ {messages.length === 0 ? ( 'Nothing here...' ) : ( - }> + }>
diff --git a/plugins/github-actions/src/components/WorkflowRunLogs/WorkflowRunLogs.tsx b/plugins/github-actions/src/components/WorkflowRunLogs/WorkflowRunLogs.tsx index ce33ae3781..399fed9557 100644 --- a/plugins/github-actions/src/components/WorkflowRunLogs/WorkflowRunLogs.tsx +++ b/plugins/github-actions/src/components/WorkflowRunLogs/WorkflowRunLogs.tsx @@ -14,12 +14,15 @@ * limitations under the License. */ +import { Entity } from '@backstage/catalog-model'; +import { Progress } from '@backstage/core-components'; +import { configApiRef, useApi } from '@backstage/core-plugin-api'; +import { readGitHubIntegrationConfigs } from '@backstage/integration'; import { Accordion, AccordionSummary, CircularProgress, Fade, - LinearProgress, makeStyles, Modal, Theme, @@ -27,15 +30,11 @@ import { Typography, Zoom, } from '@material-ui/core'; - -import React, { Suspense } from 'react'; -import { useDownloadWorkflowRunLogs } from './useDownloadWorkflowRunLogs'; -import { useProjectName } from '../useProjectName'; -import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import DescriptionIcon from '@material-ui/icons/Description'; -import { Entity } from '@backstage/catalog-model'; -import { readGitHubIntegrationConfigs } from '@backstage/integration'; -import { configApiRef, useApi } from '@backstage/core-plugin-api'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import React, { Suspense } from 'react'; +import { useProjectName } from '../useProjectName'; +import { useDownloadWorkflowRunLogs } from './useDownloadWorkflowRunLogs'; const LazyLog = React.lazy(() => import('react-lazylog/build/LazyLog')); const LinePart = React.lazy(() => import('react-lazylog/build/LinePart')); @@ -72,7 +71,7 @@ const DisplayLog = ({ className: string; }) => { return ( - }> + }>
Date: Thu, 11 Nov 2021 11:33:12 +0100 Subject: [PATCH 148/196] Fix styling issues of swagger-ui Signed-off-by: Oliver Sand --- .../OpenApiDefinition.tsx | 108 ++++++++++++------ 1 file changed, 74 insertions(+), 34 deletions(-) diff --git a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.tsx b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.tsx index cc3df40ae5..4a74b238f7 100644 --- a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.tsx +++ b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.tsx @@ -21,58 +21,98 @@ import 'swagger-ui-react/swagger-ui.css'; const useStyles = makeStyles(theme => ({ root: { - '& .swagger-ui, .info h1, .info h2, .info h3, .info h4, .info h': { - 'font-family': 'inherit', + '& .swagger-ui': { + fontFamily: 'inherit', color: theme.palette.text.primary, - }, - '& .scheme-container': { - 'background-color': theme.palette.background.default, - }, - '& .opblock-tag, .opblock-tag small, table thead tr td, table thead tr th': - { + + [`& .info h1, + .info h2, + .info h3, + .info h4, + .info h5, + .info h6`]: { + fontFamily: 'inherit', color: theme.palette.text.primary, - 'border-color': theme.palette.divider, }, - '& section.models, section.models.is-open h4': { - 'border-color': theme.palette.divider, - }, - '& .opblock .opblock-summary-description, .parameter__type, table.headers td, .model-title, .model .property.primitive, section h3': - { + [`& .scheme-container`]: { + backgroundColor: theme.palette.background.default, + }, + [`& .opblock-tag, + .opblock-tag small, + table thead tr td, + table thead tr th`]: { + fontFamily: 'inherit', + color: theme.palette.text.primary, + borderColor: theme.palette.divider, + }, + [`& section.models, + section.models.is-open h4`]: { + borderColor: theme.palette.divider, + }, + [`& .opblock .opblock-summary-description, + .parameter__type, + table.headers td, + .model-title, + .model .property.primitive, + section h3`]: { + fontFamily: 'inherit', color: theme.palette.text.secondary, }, - '& .opblock .opblock-summary-operation-id, .opblock .opblock-summary-path, .opblock .opblock-summary-path__deprecated, .opblock .opblock-section-header h4, .parameter__name, .response-col_status, .response-col_links, .responses-inner h4, .swagger-ui .responses-inner h5, .opblock-section-header .btn, .tab li, .info li, .info p, .info table, section.models h4, .info .title, table.model tr.description, .property-row': - { + [`& .opblock .opblock-summary-operation-id, + .opblock .opblock-summary-path, + .opblock .opblock-summary-path__deprecated, + .opblock .opblock-section-header h4, + .parameter__name, + .response-col_status, + .response-col_links, + .responses-inner h4, + .responses-inner h5, + .opblock-section-header .btn, + .tab li, + .info li, + .info p, + .info table, + section.models h4, + .info .title, + table.model tr.description, + .property-row`]: { + fontFamily: 'inherit', color: theme.palette.text.primary, }, - '& .opblock .opblock-section-header, .model-box, section.models .model-container': - { + [`& .opblock .opblock-section-header, + .model-box, + section.models .model-container`]: { background: theme.palette.background.default, }, - '& .prop-format, .parameter__in': { - color: theme.palette.text.disabled, - }, - '& ': { - color: theme.palette.text.primary, - 'border-color': theme.palette.divider, - }, - '& .opblock-description-wrapper p, .opblock-external-docs-wrapper p, .opblock-title_normal p, .response-control-media-type__accept-message, .opblock .opblock-section-header>label, .scheme-container .schemes>label, .info .base-url, .model': - { + [`& .prop-format, + .parameter__in`]: { + color: theme.palette.text.disabled, + }, + [`& .opblock-description-wrapper p, + .opblock-external-docs-wrapper p, + .opblock-title_normal p, + .response-control-media-type__accept-message, + .opblock .opblock-section-header>label, + .scheme-container .schemes>label, + .info .base-url, + .model`]: { color: theme.palette.text.hint, }, - '& .parameter__name.required:after': { - color: theme.palette.warning.dark, - }, - '& .prop-type': { - color: theme.palette.primary.main, + [`& .parameter__name.required:after`]: { + color: theme.palette.warning.dark, + }, + [`& .prop-type`]: { + color: theme.palette.primary.main, + }, }, }, })); -type Props = { +export type OpenApiDefinitionProps = { definition: string; }; -export const OpenApiDefinition = ({ definition }: Props) => { +export const OpenApiDefinition = ({ definition }: OpenApiDefinitionProps) => { const classes = useStyles(); // Due to a bug in the swagger-ui-react component, the component needs From 53c9ad7e04ae49bd17a0ef86f18b6c31cf6cec3b Mon Sep 17 00:00:00 2001 From: Kim Jin <28945404+kim5566@users.noreply.github.com> Date: Thu, 11 Nov 2021 21:45:40 +1100 Subject: [PATCH 149/196] Update TechDocs Headings css font weight (#7931) * update headings style Signed-off-by: kim5566 <28945404+kim5566@users.noreply.github.com> * add changeset Signed-off-by: kim5566 <28945404+kim5566@users.noreply.github.com> * update changeset Signed-off-by: kim5566 <28945404+kim5566@users.noreply.github.com> * rename changeset Signed-off-by: kim5566 <28945404+kim5566@users.noreply.github.com> --- .changeset/techdocs-friendly-chairs-wait.md | 5 +++++ plugins/techdocs/src/reader/components/Reader.tsx | 1 + 2 files changed, 6 insertions(+) create mode 100644 .changeset/techdocs-friendly-chairs-wait.md diff --git a/.changeset/techdocs-friendly-chairs-wait.md b/.changeset/techdocs-friendly-chairs-wait.md new file mode 100644 index 0000000000..b4881ade11 --- /dev/null +++ b/.changeset/techdocs-friendly-chairs-wait.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Update font weight for headings in TechDocs diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx index 9bb0a7bbad..4e12ac18d1 100644 --- a/plugins/techdocs/src/reader/components/Reader.tsx +++ b/plugins/techdocs/src/reader/components/Reader.tsx @@ -198,6 +198,7 @@ export const useTechDocsReaderDom = (): Element | null => { .md-footer-nav__link { width: 20rem;} .md-content { margin-left: 20rem; max-width: calc(100% - 20rem * 2 - 3rem); } .md-typeset { font-size: 1rem; } + .md-typeset h1, .md-typeset h2, .md-typeset h3 { font-weight: bold; } .md-nav { font-size: 1rem; } .md-grid { max-width: 90vw; margin: 0 } .md-typeset table:not([class]) { From 3369ade8c09b3000a723ae40fa37eca8e924db00 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 11 Nov 2021 11:07:58 +0000 Subject: [PATCH 150/196] Version Packages --- .changeset/angry-countries-cheat.md | 5 -- .changeset/chilled-pandas-leave.md | 5 -- .changeset/chilly-emus-roll.md | 5 -- .changeset/clean-fans-change.md | 5 -- .changeset/dry-spies-cover.md | 8 --- .changeset/dull-mangos-count.md | 6 --- .changeset/early-trains-hammer.md | 7 --- .changeset/few-waves-dream.md | 5 -- .changeset/flat-dodos-bake.md | 5 -- .changeset/flat-tomatoes-drop.md | 28 ---------- .changeset/four-years-develop.md | 5 -- .changeset/giant-years-help.md | 7 --- .changeset/gold-cobras-stare.md | 6 --- .changeset/grumpy-pugs-report.md | 23 --------- .changeset/late-walls-cry.md | 5 -- .changeset/light-knives-camp.md | 5 -- .changeset/long-bugs-kiss.md | 5 -- .changeset/loud-bats-flow.md | 5 -- .changeset/lovely-cars-sneeze.md | 5 -- .changeset/many-mayflies-notice.md | 9 ---- .changeset/mean-cars-wash.md | 5 -- .changeset/metal-impalas-allow.md | 15 ------ .changeset/mighty-gifts-visit.md | 5 -- .changeset/modern-beers-tickle.md | 5 -- .changeset/nasty-actors-push.md | 5 -- .changeset/neat-pugs-wait.md | 5 -- .changeset/nice-tools-cheat.md | 5 -- .changeset/nine-dots-suffer.md | 14 ----- .changeset/odd-rats-walk.md | 5 -- .changeset/perfect-avocados-mate.md | 5 -- .changeset/proud-eels-work.md | 17 ------- .changeset/quiet-clocks-push.md | 5 -- .changeset/real-mails-add.md | 5 -- .changeset/rich-meals-switch.md | 5 -- .changeset/selfish-bags-punch.md | 5 -- .changeset/selfish-countries-prove.md | 42 --------------- .changeset/seven-zoos-sparkle.md | 14 ----- .changeset/slimy-days-leave.md | 5 -- .changeset/smart-penguins-compete.md | 5 -- .changeset/spicy-panthers-thank.md | 5 -- .changeset/swift-turtles-provide.md | 7 --- .changeset/tall-boxes-sit.md | 5 -- .changeset/tall-lions-return.md | 5 -- .changeset/techdocs-buh-bump-ts.md | 5 -- .changeset/techdocs-friendly-chairs-wait.md | 5 -- .changeset/techdocs-tender-bugs-sort.md | 5 -- .changeset/tender-chefs-approve.md | 6 --- .changeset/tough-buckets-explain.md | 5 -- .changeset/wet-pears-thank.md | 5 -- .changeset/wicked-boats-lie.md | 7 --- .changeset/young-steaks-punch.md | 5 -- packages/backend-common/CHANGELOG.md | 9 ++++ packages/backend-common/package.json | 8 +-- packages/backend/CHANGELOG.md | 15 ++++++ packages/backend/package.json | 22 ++++---- packages/catalog-client/CHANGELOG.md | 6 +++ packages/catalog-client/package.json | 4 +- packages/cli/CHANGELOG.md | 13 +++++ packages/cli/package.json | 16 +++--- packages/codemods/CHANGELOG.md | 9 ++++ packages/codemods/package.json | 2 +- packages/config-loader/CHANGELOG.md | 7 +++ packages/config-loader/package.json | 2 +- packages/core-app-api/CHANGELOG.md | 11 ++++ packages/core-app-api/package.json | 12 ++--- packages/core-components/CHANGELOG.md | 34 +++++++++++++ packages/core-components/package.json | 12 ++--- packages/core-plugin-api/CHANGELOG.md | 10 ++++ packages/core-plugin-api/package.json | 10 ++-- packages/create-app/CHANGELOG.md | 51 +++++++++++++++++++ packages/create-app/package.json | 2 +- packages/techdocs-common/CHANGELOG.md | 21 ++++++++ packages/techdocs-common/package.json | 6 +-- packages/test-utils/CHANGELOG.md | 10 ++++ packages/test-utils/package.json | 10 ++-- packages/theme/CHANGELOG.md | 8 +++ packages/theme/package.json | 4 +- plugins/allure/package.json | 12 ++--- plugins/analytics-module-ga/package.json | 12 ++--- plugins/api-docs/CHANGELOG.md | 13 +++++ plugins/api-docs/package.json | 16 +++--- plugins/auth-backend/CHANGELOG.md | 11 ++++ plugins/auth-backend/package.json | 10 ++-- plugins/azure-devops-backend/CHANGELOG.md | 14 +++++ plugins/azure-devops-backend/package.json | 8 +-- plugins/azure-devops-common/CHANGELOG.md | 9 ++++ plugins/azure-devops-common/package.json | 4 +- plugins/azure-devops/CHANGELOG.md | 15 ++++++ plugins/azure-devops/package.json | 18 +++---- plugins/badges/package.json | 12 ++--- plugins/bazaar/CHANGELOG.md | 11 ++++ plugins/bazaar/package.json | 12 ++--- plugins/bitrise/package.json | 12 ++--- .../CHANGELOG.md | 11 ++++ .../package.json | 10 ++-- plugins/catalog-backend/CHANGELOG.md | 9 ++++ plugins/catalog-backend/package.json | 10 ++-- plugins/catalog-graph/package.json | 12 ++--- plugins/catalog-import/package.json | 10 ++-- plugins/catalog-react/CHANGELOG.md | 11 ++++ plugins/catalog-react/package.json | 14 ++--- plugins/catalog/package.json | 12 ++--- plugins/circleci/CHANGELOG.md | 11 ++++ plugins/circleci/package.json | 16 +++--- plugins/cloudbuild/package.json | 12 ++--- plugins/code-coverage/CHANGELOG.md | 12 +++++ plugins/code-coverage/package.json | 16 +++--- plugins/config-schema/package.json | 12 ++--- plugins/cost-insights/package.json | 12 ++--- plugins/explore/package.json | 12 ++--- plugins/firehydrant/package.json | 12 ++--- plugins/fossa/package.json | 12 ++--- plugins/gcp-projects/package.json | 12 ++--- plugins/git-release-manager/package.json | 12 ++--- plugins/github-actions/CHANGELOG.md | 11 ++++ plugins/github-actions/package.json | 16 +++--- plugins/github-deployments/package.json | 12 ++--- plugins/gitops-profiles/package.json | 12 ++--- plugins/graphiql/package.json | 12 ++--- plugins/home/CHANGELOG.md | 10 ++++ plugins/home/package.json | 14 ++--- plugins/ilert/CHANGELOG.md | 11 ++++ plugins/ilert/package.json | 16 +++--- plugins/jenkins-backend/CHANGELOG.md | 9 ++++ plugins/jenkins-backend/package.json | 8 +-- plugins/jenkins/package.json | 12 ++--- plugins/kafka/CHANGELOG.md | 11 ++++ plugins/kafka/package.json | 16 +++--- plugins/kubernetes/CHANGELOG.md | 11 ++++ plugins/kubernetes/package.json | 16 +++--- plugins/lighthouse/package.json | 12 ++--- plugins/newrelic/package.json | 12 ++--- plugins/org/package.json | 12 ++--- plugins/pagerduty/package.json | 12 ++--- plugins/rollbar/package.json | 12 ++--- .../CHANGELOG.md | 9 ++++ .../package.json | 8 +-- .../CHANGELOG.md | 9 ++++ .../package.json | 8 +-- .../CHANGELOG.md | 8 +++ .../package.json | 6 +-- plugins/scaffolder-backend/CHANGELOG.md | 50 ++++++++++++++++++ plugins/scaffolder-backend/package.json | 14 ++--- plugins/scaffolder/CHANGELOG.md | 50 ++++++++++++++++++ plugins/scaffolder/package.json | 18 +++---- .../CHANGELOG.md | 6 +++ .../package.json | 6 +-- plugins/search/CHANGELOG.md | 24 +++++++++ plugins/search/package.json | 16 +++--- plugins/sentry/CHANGELOG.md | 11 ++++ plugins/sentry/package.json | 16 +++--- plugins/shortcuts/package.json | 12 ++--- plugins/sonarqube/package.json | 12 ++--- plugins/splunk-on-call/package.json | 12 ++--- plugins/tech-radar/package.json | 12 ++--- plugins/techdocs/CHANGELOG.md | 13 +++++ plugins/techdocs/package.json | 18 +++---- plugins/todo/package.json | 12 ++--- plugins/user-settings/package.json | 12 ++--- plugins/xcmetrics/package.json | 12 ++--- 160 files changed, 979 insertions(+), 796 deletions(-) delete mode 100644 .changeset/angry-countries-cheat.md delete mode 100644 .changeset/chilled-pandas-leave.md delete mode 100644 .changeset/chilly-emus-roll.md delete mode 100644 .changeset/clean-fans-change.md delete mode 100644 .changeset/dry-spies-cover.md delete mode 100644 .changeset/dull-mangos-count.md delete mode 100644 .changeset/early-trains-hammer.md delete mode 100644 .changeset/few-waves-dream.md delete mode 100644 .changeset/flat-dodos-bake.md delete mode 100644 .changeset/flat-tomatoes-drop.md delete mode 100644 .changeset/four-years-develop.md delete mode 100644 .changeset/giant-years-help.md delete mode 100644 .changeset/gold-cobras-stare.md delete mode 100644 .changeset/grumpy-pugs-report.md delete mode 100644 .changeset/late-walls-cry.md delete mode 100644 .changeset/light-knives-camp.md delete mode 100644 .changeset/long-bugs-kiss.md delete mode 100644 .changeset/loud-bats-flow.md delete mode 100644 .changeset/lovely-cars-sneeze.md delete mode 100644 .changeset/many-mayflies-notice.md delete mode 100644 .changeset/mean-cars-wash.md delete mode 100644 .changeset/metal-impalas-allow.md delete mode 100644 .changeset/mighty-gifts-visit.md delete mode 100644 .changeset/modern-beers-tickle.md delete mode 100644 .changeset/nasty-actors-push.md delete mode 100644 .changeset/neat-pugs-wait.md delete mode 100644 .changeset/nice-tools-cheat.md delete mode 100644 .changeset/nine-dots-suffer.md delete mode 100644 .changeset/odd-rats-walk.md delete mode 100644 .changeset/perfect-avocados-mate.md delete mode 100644 .changeset/proud-eels-work.md delete mode 100644 .changeset/quiet-clocks-push.md delete mode 100644 .changeset/real-mails-add.md delete mode 100644 .changeset/rich-meals-switch.md delete mode 100644 .changeset/selfish-bags-punch.md delete mode 100644 .changeset/selfish-countries-prove.md delete mode 100644 .changeset/seven-zoos-sparkle.md delete mode 100644 .changeset/slimy-days-leave.md delete mode 100644 .changeset/smart-penguins-compete.md delete mode 100644 .changeset/spicy-panthers-thank.md delete mode 100644 .changeset/swift-turtles-provide.md delete mode 100644 .changeset/tall-boxes-sit.md delete mode 100644 .changeset/tall-lions-return.md delete mode 100644 .changeset/techdocs-buh-bump-ts.md delete mode 100644 .changeset/techdocs-friendly-chairs-wait.md delete mode 100644 .changeset/techdocs-tender-bugs-sort.md delete mode 100644 .changeset/tender-chefs-approve.md delete mode 100644 .changeset/tough-buckets-explain.md delete mode 100644 .changeset/wet-pears-thank.md delete mode 100644 .changeset/wicked-boats-lie.md delete mode 100644 .changeset/young-steaks-punch.md create mode 100644 plugins/azure-devops-common/CHANGELOG.md diff --git a/.changeset/angry-countries-cheat.md b/.changeset/angry-countries-cheat.md deleted file mode 100644 index e97e0ae56c..0000000000 --- a/.changeset/angry-countries-cheat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Switched to dynamically determining the packages that are unsafe to repack when executing the CLI within the Backstage main repo. diff --git a/.changeset/chilled-pandas-leave.md b/.changeset/chilled-pandas-leave.md deleted file mode 100644 index 7ad5953f7f..0000000000 --- a/.changeset/chilled-pandas-leave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Fixed the `config:check` command that was incorrectly only validating frontend configuration. Also added a `--frontend` flag to the command which maintains that behavior. diff --git a/.changeset/chilly-emus-roll.md b/.changeset/chilly-emus-roll.md deleted file mode 100644 index 1569820980..0000000000 --- a/.changeset/chilly-emus-roll.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-home': patch ---- - -Add name option to `createCardExtension` to remove deprecation warnings for extensions without name. Name will be required for extensions in a future release of `core-plugin-api` and therefore also in `createCardExtension`. diff --git a/.changeset/clean-fans-change.md b/.changeset/clean-fans-change.md deleted file mode 100644 index fad4a9cfa6..0000000000 --- a/.changeset/clean-fans-change.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-kubernetes': patch ---- - -fix: kubernetes plugin shall pass id token on get clusters request if possible diff --git a/.changeset/dry-spies-cover.md b/.changeset/dry-spies-cover.md deleted file mode 100644 index aae683faaf..0000000000 --- a/.changeset/dry-spies-cover.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/core-components': patch -'@backstage/theme': patch ---- - -Added a warning variant to `DismissableBanner` component. If you are using a -custom theme, you will need to add the optional `palette.banner.warning` color, -otherwise this variant will fall back to the `palette.banner.error` color. diff --git a/.changeset/dull-mangos-count.md b/.changeset/dull-mangos-count.md deleted file mode 100644 index 64a01c121d..0000000000 --- a/.changeset/dull-mangos-count.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-circleci': patch -'@backstage/plugin-github-actions': patch ---- - -Use `` instead of `` in combination with `Suspense`. diff --git a/.changeset/early-trains-hammer.md b/.changeset/early-trains-hammer.md deleted file mode 100644 index 853788e015..0000000000 --- a/.changeset/early-trains-hammer.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-msgraph': patch ---- - -Tweak logic for msgraph catalog ingesting for display names with security groups - -Previously security groups that weren't mail enabled were imported with UUIDs, now they use the display name. diff --git a/.changeset/few-waves-dream.md b/.changeset/few-waves-dream.md deleted file mode 100644 index 72123171b8..0000000000 --- a/.changeset/few-waves-dream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -AWSS3UrlReader now throws a `NotModifiedError` (exported from @backstage/backend-common) when s3 returns a 304 response. diff --git a/.changeset/flat-dodos-bake.md b/.changeset/flat-dodos-bake.md deleted file mode 100644 index f6b52aac1a..0000000000 --- a/.changeset/flat-dodos-bake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-jenkins-backend': patch ---- - -Don't require a validation pattern for the Jenkins base URL. diff --git a/.changeset/flat-tomatoes-drop.md b/.changeset/flat-tomatoes-drop.md deleted file mode 100644 index 97ea32d8fd..0000000000 --- a/.changeset/flat-tomatoes-drop.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Integrated `SidebarSearchModal` component into default-app to use the `SearchModal`. - -The `SidebarSearchModal` component can also be used in other generated apps: - -```diff -import { -- SidebarSearch, -+ SidebarSearchModal -} from '@backstage/plugin-search'; -... - - - -- -+ - -... -``` - -If you only want to use the `SearchModal` you can import it from `'@backstage/plugin-search'`: - -```js -import { SearchModal } from '@backstage/plugin-search'; -``` diff --git a/.changeset/four-years-develop.md b/.changeset/four-years-develop.md deleted file mode 100644 index cfd3704f2f..0000000000 --- a/.changeset/four-years-develop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -Allow for `cellStyle` property on `TableColumn` to be a function as well as `React.CSSProperties` as per the Material UI Table component diff --git a/.changeset/giant-years-help.md b/.changeset/giant-years-help.md deleted file mode 100644 index c6ca0def30..0000000000 --- a/.changeset/giant-years-help.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/backend-common': patch -'@backstage/cli': patch -'@backstage/config-loader': patch ---- - -Reading app config from a remote server diff --git a/.changeset/gold-cobras-stare.md b/.changeset/gold-cobras-stare.md deleted file mode 100644 index d3265c865a..0000000000 --- a/.changeset/gold-cobras-stare.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch -'@backstage/plugin-techdocs': patch ---- - -Lazy load `LazyLog` as it is rarely used. diff --git a/.changeset/grumpy-pugs-report.md b/.changeset/grumpy-pugs-report.md deleted file mode 100644 index db915b7a79..0000000000 --- a/.changeset/grumpy-pugs-report.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -Add new way to override color selection to progress bar/gauge components. - -`Gauge`, `LinearGauge` and `GaugeCard` all accept a `getColor` prop, -which is a function of the type: - -```ts -export type GaugePropsGetColor = (args: { - palette: Palette; - value: number; - inverse?: boolean; - max?: number; -}) => string; -``` - -Return a standard CSS color string (e.g. "red", "#f02020"), and the gauge will -be set to that color. - -If the prop is omitted, the default implementation is unchanged from previous -versions. diff --git a/.changeset/late-walls-cry.md b/.changeset/late-walls-cry.md deleted file mode 100644 index c9fcb3112a..0000000000 --- a/.changeset/late-walls-cry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-search-backend-module-elasticsearch': patch ---- - -Pinning version of elastic search client to 7.13.0 to prevent breaking change towards third party ElasticSearch clusters on 7.14.0. diff --git a/.changeset/light-knives-camp.md b/.changeset/light-knives-camp.md deleted file mode 100644 index 1e7fa37082..0000000000 --- a/.changeset/light-knives-camp.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-ilert': patch ---- - -Change the version of `@date-io/luxon` from 2.x to 1.x to make it compatible with material-ui-pickers diff --git a/.changeset/long-bugs-kiss.md b/.changeset/long-bugs-kiss.md deleted file mode 100644 index 49e0edae72..0000000000 --- a/.changeset/long-bugs-kiss.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-kafka': patch ---- - -Use IdentityApi to provide Auth Token for KafkaBackendClient Api calls diff --git a/.changeset/loud-bats-flow.md b/.changeset/loud-bats-flow.md deleted file mode 100644 index af4837dbbe..0000000000 --- a/.changeset/loud-bats-flow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/test-utils': patch ---- - -Update Keyboard deprecation with a link to the recommended successor diff --git a/.changeset/lovely-cars-sneeze.md b/.changeset/lovely-cars-sneeze.md deleted file mode 100644 index 8e6bfd691c..0000000000 --- a/.changeset/lovely-cars-sneeze.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Expose some classes and interfaces public so TaskWorkers can run externally from the scaffolder API. diff --git a/.changeset/many-mayflies-notice.md b/.changeset/many-mayflies-notice.md deleted file mode 100644 index a96d1fcce5..0000000000 --- a/.changeset/many-mayflies-notice.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/plugin-azure-devops': patch -'@backstage/plugin-azure-devops-backend': minor -'@backstage/plugin-azure-devops-common': patch ---- - -refactor(`@backstage/plugin-azure-devops`): Consume types from `@backstage/plugin-azure-devops-common`. -Stop re-exporting types from `@backstage/plugin-azure-devops-backend`. -Added new types to `@backstage/plugin-azure-devops-common`. diff --git a/.changeset/mean-cars-wash.md b/.changeset/mean-cars-wash.md deleted file mode 100644 index 187f5d7e21..0000000000 --- a/.changeset/mean-cars-wash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/catalog-client': patch ---- - -Improved API documentation for catalog-client. diff --git a/.changeset/metal-impalas-allow.md b/.changeset/metal-impalas-allow.md deleted file mode 100644 index 8ad727f759..0000000000 --- a/.changeset/metal-impalas-allow.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'@backstage/techdocs-common': patch ---- - -1. Techdocs publisher constructors now use parameter objects when being - instantiated - -2. Internal refactor of `LocalPublish` publisher to use `fromConfig` for - creation to be aligned with other publishers; this does not impact - `LocalPublish` usage. - -```diff -- const publisher = new LocalPublish(config, logger, discovery); -+ const publisher = LocalPublish.fromConfig(config, logger, discovery); -``` diff --git a/.changeset/mighty-gifts-visit.md b/.changeset/mighty-gifts-visit.md deleted file mode 100644 index 8ceab2906e..0000000000 --- a/.changeset/mighty-gifts-visit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-bazaar': patch ---- - -Name extension to remove deprecation warning diff --git a/.changeset/modern-beers-tickle.md b/.changeset/modern-beers-tickle.md deleted file mode 100644 index 6ad9cc872d..0000000000 --- a/.changeset/modern-beers-tickle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': patch ---- - -Only use settings that have a value when creating a new FirestoreKeyStore instance diff --git a/.changeset/nasty-actors-push.md b/.changeset/nasty-actors-push.md deleted file mode 100644 index 8f4d0d92a8..0000000000 --- a/.changeset/nasty-actors-push.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-sentry': patch ---- - -fix: sentry-plugin can forward identity token to backend (for case when it requires authorization) diff --git a/.changeset/neat-pugs-wait.md b/.changeset/neat-pugs-wait.md deleted file mode 100644 index 8902f44c9d..0000000000 --- a/.changeset/neat-pugs-wait.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-code-coverage': patch ---- - -Change represented test date from epoch to something more human friendly. Round test coverage to 2 decimal places. diff --git a/.changeset/nice-tools-cheat.md b/.changeset/nice-tools-cheat.md deleted file mode 100644 index c4f0e162bf..0000000000 --- a/.changeset/nice-tools-cheat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Make `ExitCodeError` call `super` early to avoid compiler warnings diff --git a/.changeset/nine-dots-suffer.md b/.changeset/nine-dots-suffer.md deleted file mode 100644 index 3efd307a10..0000000000 --- a/.changeset/nine-dots-suffer.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Updated the app template to no longer include the `--no-private` flag for the `create-plugin` command. - -To apply this change to an existing application, remove the `--no-private` flag from the `create-plugin` command in the root `package.json`: - -```diff - "prettier:check": "prettier --check .", -- "create-plugin": "backstage-cli create-plugin --scope internal --no-private", -+ "create-plugin": "backstage-cli create-plugin --scope internal", - "remove-plugin": "backstage-cli remove-plugin" -``` diff --git a/.changeset/odd-rats-walk.md b/.changeset/odd-rats-walk.md deleted file mode 100644 index 3a8710cc32..0000000000 --- a/.changeset/odd-rats-walk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/config-loader': patch ---- - -Bump msw to the same version as the rest diff --git a/.changeset/perfect-avocados-mate.md b/.changeset/perfect-avocados-mate.md deleted file mode 100644 index 9394b57449..0000000000 --- a/.changeset/perfect-avocados-mate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-code-coverage': patch ---- - -Make dates in X-Axis sort in ascending order diff --git a/.changeset/proud-eels-work.md b/.changeset/proud-eels-work.md deleted file mode 100644 index a4f2c86eb4..0000000000 --- a/.changeset/proud-eels-work.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -'@backstage/plugin-search': patch ---- - -Added `SearchModal` component. - -Now you can import `SearchModal` in your apps: - -```js -import { SearchModal } from '@backstage/plugin-search'; -``` - -You can also use the `SidebarSearchModal` component to integrate it into the sidebar of your sample apps: - -```js -import { SidebarSearchModal } from '@backstage/plugin-search'; -``` diff --git a/.changeset/quiet-clocks-push.md b/.changeset/quiet-clocks-push.md deleted file mode 100644 index 2b469a5fc5..0000000000 --- a/.changeset/quiet-clocks-push.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-plugin-api': patch ---- - -Deprecate use of extensions without name. Adds a warning to the developer console to prompt integrators to provide names for extensions. diff --git a/.changeset/real-mails-add.md b/.changeset/real-mails-add.md deleted file mode 100644 index 30a9100699..0000000000 --- a/.changeset/real-mails-add.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -fixed route resolving (issue #7741) when user cannot select a tab in any of the tabbed pages (like the Catalog page) if it shares the same initial letters as a preceding tab. (i.e. where tab with a path of /ci is followed by a path of /ci-2, user cannot select /ci-2 as /ci will always be selected first). diff --git a/.changeset/rich-meals-switch.md b/.changeset/rich-meals-switch.md deleted file mode 100644 index 8a9467a84e..0000000000 --- a/.changeset/rich-meals-switch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Bumped `@spotify/eslint-config-react` from `v10` to `v12`, dropping support for Node.js v12. diff --git a/.changeset/selfish-bags-punch.md b/.changeset/selfish-bags-punch.md deleted file mode 100644 index 1950da9357..0000000000 --- a/.changeset/selfish-bags-punch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': patch ---- - -Added forwarding of the `audience` option for the SAML provider, making it possible to enable `audience` verification. diff --git a/.changeset/selfish-countries-prove.md b/.changeset/selfish-countries-prove.md deleted file mode 100644 index 03e36f6d80..0000000000 --- a/.changeset/selfish-countries-prove.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch -'@backstage/plugin-scaffolder-backend': patch ---- - -Adds a new endpoint for consuming logs from the Scaffolder that uses long polling instead of Server Sent Events. - -This is useful if Backstage is accessed from an environment that doesn't support SSE correctly, which happens in combination with certain enterprise HTTP Proxy servers. - -It is intended to switch the endpoint globally for the whole instance. -If you want to use it, you can provide a reconfigured API to the `scaffolderApiRef`: - -```tsx -// packages/app/src/apis.ts - -// ... -import { - scaffolderApiRef, - ScaffolderClient, -} from '@backstage/plugin-scaffolder'; - -export const apis: AnyApiFactory[] = [ - // ... - - createApiFactory({ - api: scaffolderApiRef, - deps: { - discoveryApi: discoveryApiRef, - identityApi: identityApiRef, - scmIntegrationsApi: scmIntegrationsApiRef, - }, - factory: ({ discoveryApi, identityApi, scmIntegrationsApi }) => - new ScaffolderClient({ - discoveryApi, - identityApi, - scmIntegrationsApi, - // use long polling instead of an eventsource - useLongPollingLogs: true, - }), - }), -]; -``` diff --git a/.changeset/seven-zoos-sparkle.md b/.changeset/seven-zoos-sparkle.md deleted file mode 100644 index 1399c3254e..0000000000 --- a/.changeset/seven-zoos-sparkle.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Removed the version pinning of the packages `graphql-language-service-interface` and `graphql-language-service-parser`. This should no longer be necessary. - -You can apply the same change in your repository by ensuring that the following does _NOT_ appear in your root `package.json`. - -```json -"resolutions": { - "graphql-language-service-interface": "2.8.2", - "graphql-language-service-parser": "1.9.0" - }, -``` diff --git a/.changeset/slimy-days-leave.md b/.changeset/slimy-days-leave.md deleted file mode 100644 index df772d80d4..0000000000 --- a/.changeset/slimy-days-leave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Allow singleton and flexibly nested EntityFilters diff --git a/.changeset/smart-penguins-compete.md b/.changeset/smart-penguins-compete.md deleted file mode 100644 index d3518600a6..0000000000 --- a/.changeset/smart-penguins-compete.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-react': patch ---- - -export `loadIdentityOwnerRefs` and `loadCatalogOwnerRefs` all the way diff --git a/.changeset/spicy-panthers-thank.md b/.changeset/spicy-panthers-thank.md deleted file mode 100644 index 6c28b002f4..0000000000 --- a/.changeset/spicy-panthers-thank.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-app-api': patch ---- - -I have added default icons for the catalog, scaffolder, techdocs, and search. diff --git a/.changeset/swift-turtles-provide.md b/.changeset/swift-turtles-provide.md deleted file mode 100644 index 52cf9a7c8f..0000000000 --- a/.changeset/swift-turtles-provide.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-api-docs': patch ---- - -Lazy load all API definition widgets. The widgets use libraries like -`swagger-ui`, `graphiql`, and `@asyncapi/react-component` which are quite heavy -weight. To improve initial load times, the widgets are only loaded once used. diff --git a/.changeset/tall-boxes-sit.md b/.changeset/tall-boxes-sit.md deleted file mode 100644 index a74e0400bd..0000000000 --- a/.changeset/tall-boxes-sit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -Add Theme Overrides for Sidebar diff --git a/.changeset/tall-lions-return.md b/.changeset/tall-lions-return.md deleted file mode 100644 index 56bb0fc3dd..0000000000 --- a/.changeset/tall-lions-return.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Bumped `@spotify/eslint-config-typescript` from `v10` to `v12`, dropping support for Node.js v12. diff --git a/.changeset/techdocs-buh-bump-ts.md b/.changeset/techdocs-buh-bump-ts.md deleted file mode 100644 index 438843e04b..0000000000 --- a/.changeset/techdocs-buh-bump-ts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/techdocs-common': patch ---- - -Default TechDocs container used at docs generation-time is now [v0.3.5](https://github.com/backstage/techdocs-container/releases/tag/v0.3.5). diff --git a/.changeset/techdocs-friendly-chairs-wait.md b/.changeset/techdocs-friendly-chairs-wait.md deleted file mode 100644 index b4881ade11..0000000000 --- a/.changeset/techdocs-friendly-chairs-wait.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs': patch ---- - -Update font weight for headings in TechDocs diff --git a/.changeset/techdocs-tender-bugs-sort.md b/.changeset/techdocs-tender-bugs-sort.md deleted file mode 100644 index be670b06ff..0000000000 --- a/.changeset/techdocs-tender-bugs-sort.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/techdocs-common': patch ---- - -Updated to properly join URL segments under any OS for both AWS S3 and GCP diff --git a/.changeset/tender-chefs-approve.md b/.changeset/tender-chefs-approve.md deleted file mode 100644 index 1cc618507c..0000000000 --- a/.changeset/tender-chefs-approve.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/core-app-api': patch -'@backstage/core-plugin-api': patch ---- - -Improve API documentation for @backstage/core-plugin-api diff --git a/.changeset/tough-buckets-explain.md b/.changeset/tough-buckets-explain.md deleted file mode 100644 index a2ca37d516..0000000000 --- a/.changeset/tough-buckets-explain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-azure-devops': patch ---- - -Simplified queue time calculation in `BuildTable`. diff --git a/.changeset/wet-pears-thank.md b/.changeset/wet-pears-thank.md deleted file mode 100644 index f9b5576fb3..0000000000 --- a/.changeset/wet-pears-thank.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Fix a bug where only file mode 775 is considered an executable diff --git a/.changeset/wicked-boats-lie.md b/.changeset/wicked-boats-lie.md deleted file mode 100644 index 8c5da92d9c..0000000000 --- a/.changeset/wicked-boats-lie.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-cookiecutter': patch -'@backstage/plugin-scaffolder-backend-module-rails': patch -'@backstage/plugin-scaffolder-backend-module-yeoman': patch ---- - -Add missing API docs to scaffolder action plugins diff --git a/.changeset/young-steaks-punch.md b/.changeset/young-steaks-punch.md deleted file mode 100644 index 55997af565..0000000000 --- a/.changeset/young-steaks-punch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-plugin-api': patch ---- - -Deprecate unused ApiRef types diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index f0368b55d4..f3464917aa 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/backend-common +## 0.9.9 + +### Patch Changes + +- 8c4cad0bf2: AWSS3UrlReader now throws a `NotModifiedError` (exported from @backstage/backend-common) when s3 returns a 304 response. +- 0611f3b3e2: Reading app config from a remote server +- Updated dependencies + - @backstage/config-loader@0.7.2 + ## 0.9.8 ### Patch Changes diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index a174862b4a..03ae1fbe62 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-common", "description": "Common functionality library for Backstage backends", - "version": "0.9.8", + "version": "0.9.9", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -31,7 +31,7 @@ "dependencies": { "@backstage/cli-common": "^0.1.5", "@backstage/config": "^0.1.11", - "@backstage/config-loader": "^0.7.1", + "@backstage/config-loader": "^0.7.2", "@backstage/errors": "^0.1.4", "@backstage/integration": "^0.6.9", "@backstage/types": "^0.1.1", @@ -79,8 +79,8 @@ } }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/test-utils": "^0.1.20", + "@backstage/cli": "^0.8.2", + "@backstage/test-utils": "^0.1.21", "@types/archiver": "^5.1.0", "@types/compression": "^1.7.0", "@types/concat-stream": "^1.6.0", diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 19e9bcdd67..7cf7d9ddaa 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,20 @@ # example-backend +## 0.2.52 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.9.9 + - @backstage/plugin-jenkins-backend@0.1.7 + - @backstage/plugin-search-backend-module-elasticsearch@0.0.5 + - @backstage/plugin-scaffolder-backend@0.15.12 + - @backstage/plugin-azure-devops-backend@0.2.0 + - @backstage/catalog-client@0.5.1 + - @backstage/plugin-auth-backend@0.4.7 + - @backstage/plugin-catalog-backend@0.17.3 + - @backstage/plugin-scaffolder-backend-module-rails@0.1.7 + ## 0.2.50 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index f1d58252a4..4c854241d4 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.2.50", + "version": "0.2.52", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,28 +24,28 @@ "migrate:create": "knex migrate:make -x ts" }, "dependencies": { - "@backstage/backend-common": "^0.9.7", - "@backstage/catalog-client": "^0.5.0", + "@backstage/backend-common": "^0.9.9", + "@backstage/catalog-client": "^0.5.1", "@backstage/catalog-model": "^0.9.5", "@backstage/config": "^0.1.10", "@backstage/integration": "^0.6.8", "@backstage/plugin-app-backend": "^0.3.17", - "@backstage/plugin-auth-backend": "^0.4.5", - "@backstage/plugin-azure-devops-backend": "^0.1.3", + "@backstage/plugin-auth-backend": "^0.4.7", + "@backstage/plugin-azure-devops-backend": "^0.2.0", "@backstage/plugin-badges-backend": "^0.1.11", - "@backstage/plugin-catalog-backend": "^0.17.1", + "@backstage/plugin-catalog-backend": "^0.17.3", "@backstage/plugin-code-coverage-backend": "^0.1.14", "@backstage/plugin-graphql-backend": "^0.1.9", - "@backstage/plugin-jenkins-backend": "^0.1.6", + "@backstage/plugin-jenkins-backend": "^0.1.7", "@backstage/plugin-kubernetes-backend": "^0.3.18", "@backstage/plugin-kafka-backend": "^0.2.10", "@backstage/plugin-proxy-backend": "^0.2.13", "@backstage/plugin-rollbar-backend": "^0.1.15", - "@backstage/plugin-scaffolder-backend": "^0.15.10", - "@backstage/plugin-scaffolder-backend-module-rails": "^0.1.5", + "@backstage/plugin-scaffolder-backend": "^0.15.12", + "@backstage/plugin-scaffolder-backend-module-rails": "^0.1.7", "@backstage/plugin-search-backend": "^0.2.6", "@backstage/plugin-search-backend-node": "^0.4.2", - "@backstage/plugin-search-backend-module-elasticsearch": "^0.0.4", + "@backstage/plugin-search-backend-module-elasticsearch": "^0.0.5", "@backstage/plugin-search-backend-module-pg": "^0.2.1", "@backstage/plugin-techdocs-backend": "^0.10.5", "@backstage/plugin-tech-insights-backend": "^0.1.0", @@ -68,7 +68,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.8.0", + "@backstage/cli": "^0.8.2", "@types/dockerode": "^3.2.1", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5" diff --git a/packages/catalog-client/CHANGELOG.md b/packages/catalog-client/CHANGELOG.md index 8243d09486..f5ec2116f7 100644 --- a/packages/catalog-client/CHANGELOG.md +++ b/packages/catalog-client/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/catalog-client +## 0.5.1 + +### Patch Changes + +- 39e92897e4: Improved API documentation for catalog-client. + ## 0.5.0 ### Minor Changes diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index d4ba2c20f6..efdaacc1e0 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/catalog-client", "description": "An isomorphic client for the catalog backend", - "version": "0.5.0", + "version": "0.5.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -35,7 +35,7 @@ "cross-fetch": "^3.0.6" }, "devDependencies": { - "@backstage/cli": "^0.8.0", + "@backstage/cli": "^0.8.2", "@types/jest": "^26.0.7", "msw": "^0.35.0" }, diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 48f38cbc8a..0fc702851d 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/cli +## 0.8.2 + +### Patch Changes + +- dd355bca46: Switched to dynamically determining the packages that are unsafe to repack when executing the CLI within the Backstage main repo. +- b393c4d4be: Fixed the `config:check` command that was incorrectly only validating frontend configuration. Also added a `--frontend` flag to the command which maintains that behavior. +- 0611f3b3e2: Reading app config from a remote server +- ec64d9590c: Make `ExitCodeError` call `super` early to avoid compiler warnings +- 8af66229e7: Bumped `@spotify/eslint-config-react` from `v10` to `v12`, dropping support for Node.js v12. +- a197708da9: Bumped `@spotify/eslint-config-typescript` from `v10` to `v12`, dropping support for Node.js v12. +- Updated dependencies + - @backstage/config-loader@0.7.2 + ## 0.8.1 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 0fca97f317..546a1597e1 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli", "description": "CLI for developing Backstage plugins and apps", - "version": "0.8.1", + "version": "0.8.2", "private": false, "publishConfig": { "access": "public" @@ -30,7 +30,7 @@ "dependencies": { "@backstage/cli-common": "^0.1.5", "@backstage/config": "^0.1.11", - "@backstage/config-loader": "^0.7.1", + "@backstage/config-loader": "^0.7.2", "@backstage/errors": "^0.1.4", "@backstage/types": "^0.1.1", "@hot-loader/react-dom": "^16.13.0", @@ -117,14 +117,14 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-common": "^0.9.8", + "@backstage/backend-common": "^0.9.9", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", - "@backstage/core-app-api": "^0.1.19", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", - "@backstage/theme": "^0.2.12", + "@backstage/test-utils": "^0.1.21", + "@backstage/theme": "^0.2.13", "@types/diff": "^5.0.0", "@types/express": "^4.17.6", "@types/fs-extra": "^9.0.1", diff --git a/packages/codemods/CHANGELOG.md b/packages/codemods/CHANGELOG.md index fbb2076167..1043d9718f 100644 --- a/packages/codemods/CHANGELOG.md +++ b/packages/codemods/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/codemods +## 0.1.21 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.7.3 + - @backstage/core-plugin-api@0.1.13 + - @backstage/core-app-api@0.1.20 + ## 0.1.20 ### Patch Changes diff --git a/packages/codemods/package.json b/packages/codemods/package.json index 0dc0e9a0d5..17e89c86f0 100644 --- a/packages/codemods/package.json +++ b/packages/codemods/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/codemods", "description": "A collection of codemods for Backstage projects", - "version": "0.1.20", + "version": "0.1.21", "private": false, "publishConfig": { "access": "public", diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md index dcaec00816..0f1bc27996 100644 --- a/packages/config-loader/CHANGELOG.md +++ b/packages/config-loader/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/config-loader +## 0.7.2 + +### Patch Changes + +- 0611f3b3e2: Reading app config from a remote server +- 26c5659c97: Bump msw to the same version as the rest + ## 0.7.1 ### Patch Changes diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 22fcedf486..2e25d91856 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config-loader", "description": "Config loading functionality used by Backstage backend, and CLI", - "version": "0.7.1", + "version": "0.7.2", "private": false, "publishConfig": { "access": "public", diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index ff75f5bf81..b9e60b7dab 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/core-app-api +## 0.1.20 + +### Patch Changes + +- 78c512ce8f: I have added default icons for the catalog, scaffolder, techdocs, and search. +- 8b4284cd5c: Improve API documentation for @backstage/core-plugin-api +- Updated dependencies + - @backstage/core-components@0.7.3 + - @backstage/theme@0.2.13 + - @backstage/core-plugin-api@0.1.13 + ## 0.1.19 ### Patch Changes diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index f40e3d5eb7..a0877cf933 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-app-api", "description": "Core app API used by Backstage apps", - "version": "0.1.19", + "version": "0.1.20", "private": false, "publishConfig": { "access": "public", @@ -29,10 +29,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.2", + "@backstage/core-components": "^0.7.3", "@backstage/config": "^0.1.11", - "@backstage/core-plugin-api": "^0.1.12", - "@backstage/theme": "^0.2.12", + "@backstage/core-plugin-api": "^0.1.13", + "@backstage/theme": "^0.2.13", "@backstage/types": "^0.1.1", "@backstage/version-bridge": "^0.1.0", "@material-ui/core": "^4.12.2", @@ -46,8 +46,8 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/test-utils": "^0.1.20", + "@backstage/cli": "^0.8.2", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index af03762094..1e62df337d 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,39 @@ # @backstage/core-components +## 0.7.3 + +### Patch Changes + +- c11a37710a: Added a warning variant to `DismissableBanner` component. If you are using a + custom theme, you will need to add the optional `palette.banner.warning` color, + otherwise this variant will fall back to the `palette.banner.error` color. +- 5826c17b7d: Allow for `cellStyle` property on `TableColumn` to be a function as well as `React.CSSProperties` as per the Material UI Table component +- e0861b92ff: Add new way to override color selection to progress bar/gauge components. + + `Gauge`, `LinearGauge` and `GaugeCard` all accept a `getColor` prop, + which is a function of the type: + + ```ts + export type GaugePropsGetColor = (args: { + palette: Palette; + value: number; + inverse?: boolean; + max?: number; + }) => string; + ``` + + Return a standard CSS color string (e.g. "red", "#f02020"), and the gauge will + be set to that color. + + If the prop is omitted, the default implementation is unchanged from previous + versions. + +- 021986e8a3: fixed route resolving (issue #7741) when user cannot select a tab in any of the tabbed pages (like the Catalog page) if it shares the same initial letters as a preceding tab. (i.e. where tab with a path of /ci is followed by a path of /ci-2, user cannot select /ci-2 as /ci will always be selected first). +- a39a2105ef: Add Theme Overrides for Sidebar +- Updated dependencies + - @backstage/theme@0.2.13 + - @backstage/core-plugin-api@0.1.13 + ## 0.7.2 ### Patch Changes diff --git a/packages/core-components/package.json b/packages/core-components/package.json index 7f50af7488..ffbde0072a 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-components", "description": "Core components used by Backstage plugins and apps", - "version": "0.7.2", + "version": "0.7.3", "private": false, "publishConfig": { "access": "public", @@ -30,9 +30,9 @@ }, "dependencies": { "@backstage/config": "^0.1.11", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/errors": "^0.1.4", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-table/core": "^3.1.0", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -67,9 +67,9 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/core-app-api": "^0.1.19", - "@backstage/cli": "^0.8.1", - "@backstage/test-utils": "^0.1.20", + "@backstage/core-app-api": "^0.1.20", + "@backstage/cli": "^0.8.2", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/packages/core-plugin-api/CHANGELOG.md b/packages/core-plugin-api/CHANGELOG.md index 9d8246f931..d92922ddb4 100644 --- a/packages/core-plugin-api/CHANGELOG.md +++ b/packages/core-plugin-api/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/core-plugin-api +## 0.1.13 + +### Patch Changes + +- 4a336fd292: Deprecate use of extensions without name. Adds a warning to the developer console to prompt integrators to provide names for extensions. +- 8b4284cd5c: Improve API documentation for @backstage/core-plugin-api +- e059aea7b9: Deprecate unused ApiRef types +- Updated dependencies + - @backstage/theme@0.2.13 + ## 0.1.12 ### Patch Changes diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index 23bf0fc8b0..6679e89645 100644 --- a/packages/core-plugin-api/package.json +++ b/packages/core-plugin-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-plugin-api", "description": "Core API used by Backstage plugins", - "version": "0.1.12", + "version": "0.1.13", "private": false, "publishConfig": { "access": "public", @@ -30,7 +30,7 @@ }, "dependencies": { "@backstage/config": "^0.1.11", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@backstage/types": "^0.1.1", "@backstage/version-bridge": "^0.1.0", "@material-ui/core": "^4.12.2", @@ -43,9 +43,9 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", - "@backstage/test-utils": "^0.1.20", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 95d99d0532..7e53d0cde5 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,56 @@ # @backstage/create-app +## 0.4.3 + +### Patch Changes + +- 5dcea2586c: Integrated `SidebarSearchModal` component into default-app to use the `SearchModal`. + + The `SidebarSearchModal` component can also be used in other generated apps: + + ```diff + import { + - SidebarSearch, + + SidebarSearchModal + } from '@backstage/plugin-search'; + ... + + + + - + + + + ... + ``` + + If you only want to use the `SearchModal` you can import it from `'@backstage/plugin-search'`: + + ```js + import { SearchModal } from '@backstage/plugin-search'; + ``` + +- 5725f87e4c: Updated the app template to no longer include the `--no-private` flag for the `create-plugin` command. + + To apply this change to an existing application, remove the `--no-private` flag from the `create-plugin` command in the root `package.json`: + + ```diff + "prettier:check": "prettier --check .", + - "create-plugin": "backstage-cli create-plugin --scope internal --no-private", + + "create-plugin": "backstage-cli create-plugin --scope internal", + "remove-plugin": "backstage-cli remove-plugin" + ``` + +- 1921f70aa7: Removed the version pinning of the packages `graphql-language-service-interface` and `graphql-language-service-parser`. This should no longer be necessary. + + You can apply the same change in your repository by ensuring that the following does _NOT_ appear in your root `package.json`. + + ```json + "resolutions": { + "graphql-language-service-interface": "2.8.2", + "graphql-language-service-parser": "1.9.0" + }, + ``` + ## 0.4.2 ## 0.4.1 diff --git a/packages/create-app/package.json b/packages/create-app/package.json index ad4ab8dabd..2820d41ed6 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "A CLI that helps you create your own Backstage app", - "version": "0.4.2", + "version": "0.4.3", "private": false, "publishConfig": { "access": "public" diff --git a/packages/techdocs-common/CHANGELOG.md b/packages/techdocs-common/CHANGELOG.md index b540f74549..87e9e12193 100644 --- a/packages/techdocs-common/CHANGELOG.md +++ b/packages/techdocs-common/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/techdocs-common +## 0.10.6 + +### Patch Changes + +- a2d4389587: 1. Techdocs publisher constructors now use parameter objects when being + instantiated + + 2. Internal refactor of `LocalPublish` publisher to use `fromConfig` for + creation to be aligned with other publishers; this does not impact + `LocalPublish` usage. + + ```diff + - const publisher = new LocalPublish(config, logger, discovery); + + const publisher = LocalPublish.fromConfig(config, logger, discovery); + ``` + +- 6129c89a47: Default TechDocs container used at docs generation-time is now [v0.3.5](https://github.com/backstage/techdocs-container/releases/tag/v0.3.5). +- f3c7eec64b: Updated to properly join URL segments under any OS for both AWS S3 and GCP +- Updated dependencies + - @backstage/backend-common@0.9.9 + ## 0.10.5 ### Patch Changes diff --git a/packages/techdocs-common/package.json b/packages/techdocs-common/package.json index 152b9b04f1..25577df1dd 100644 --- a/packages/techdocs-common/package.json +++ b/packages/techdocs-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/techdocs-common", "description": "Common functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", - "version": "0.10.5", + "version": "0.10.6", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -38,7 +38,7 @@ "dependencies": { "@azure/identity": "^1.5.0", "@azure/storage-blob": "^12.5.0", - "@backstage/backend-common": "^0.9.8", + "@backstage/backend-common": "^0.9.9", "@backstage/catalog-model": "^0.9.6", "@backstage/config": "^0.1.11", "@backstage/errors": "^0.1.4", @@ -60,7 +60,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.8.1", + "@backstage/cli": "^0.8.2", "@types/fs-extra": "^9.0.5", "@types/js-yaml": "^4.0.0", "@types/mime-types": "^2.1.0", diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index a1fa9d3b6b..6fcacc30fa 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/test-utils +## 0.1.21 + +### Patch Changes + +- 71fd5cd735: Update Keyboard deprecation with a link to the recommended successor +- Updated dependencies + - @backstage/theme@0.2.13 + - @backstage/core-plugin-api@0.1.13 + - @backstage/core-app-api@0.1.20 + ## 0.1.20 ### Patch Changes diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 269b7b2d38..ab78eab683 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/test-utils", "description": "Utilities to test Backstage plugins and apps.", - "version": "0.1.20", + "version": "0.1.21", "private": false, "publishConfig": { "access": "public", @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-app-api": "^0.1.19", - "@backstage/core-plugin-api": "^0.1.12", - "@backstage/theme": "^0.2.12", + "@backstage/core-app-api": "^0.1.20", + "@backstage/core-plugin-api": "^0.1.13", + "@backstage/theme": "^0.2.13", "@backstage/types": "^0.1.1", "@material-ui/core": "^4.12.2", "@testing-library/jest-dom": "^5.10.1", @@ -45,7 +45,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.8.1", + "@backstage/cli": "^0.8.2", "@types/jest": "^26.0.7", "@types/node": "^14.14.32" }, diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index 8709484c79..1c8661d7cc 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/theme +## 0.2.13 + +### Patch Changes + +- c11a37710a: Added a warning variant to `DismissableBanner` component. If you are using a + custom theme, you will need to add the optional `palette.banner.warning` color, + otherwise this variant will fall back to the `palette.banner.error` color. + ## 0.2.12 ### Patch Changes diff --git a/packages/theme/package.json b/packages/theme/package.json index 1acc91de79..d9e89c969f 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/theme", "description": "material-ui theme for use with Backstage.", - "version": "0.2.12", + "version": "0.2.13", "private": false, "publishConfig": { "access": "public", @@ -31,7 +31,7 @@ "@material-ui/core": "^4.12.2" }, "devDependencies": { - "@backstage/cli": "^0.8.1" + "@backstage/cli": "^0.8.2" }, "files": [ "dist" diff --git a/plugins/allure/package.json b/plugins/allure/package.json index 63864a7239..b157795eec 100644 --- a/plugins/allure/package.json +++ b/plugins/allure/package.json @@ -23,10 +23,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -36,10 +36,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/analytics-module-ga/package.json b/plugins/analytics-module-ga/package.json index 25534e9577..4970f50c04 100644 --- a/plugins/analytics-module-ga/package.json +++ b/plugins/analytics-module-ga/package.json @@ -22,9 +22,9 @@ }, "dependencies": { "@backstage/config": "^0.1.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", - "@backstage/theme": "^0.2.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -34,10 +34,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 188d81fcd1..78f4b08bd7 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-api-docs +## 0.6.13 + +### Patch Changes + +- 044c38e739: Lazy load all API definition widgets. The widgets use libraries like + `swagger-ui`, `graphiql`, and `@asyncapi/react-component` which are quite heavy + weight. To improve initial load times, the widgets are only loaded once used. +- Updated dependencies + - @backstage/core-components@0.7.3 + - @backstage/theme@0.2.13 + - @backstage/core-plugin-api@0.1.13 + - @backstage/plugin-catalog-react@0.6.3 + ## 0.6.12 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 67045c2043..61ae26c776 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-api-docs", "description": "A Backstage plugin that helps represent API entities in the frontend", - "version": "0.6.12", + "version": "0.6.13", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,11 +32,11 @@ "dependencies": { "@asyncapi/react-component": "^0.23.0", "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/plugin-catalog": "^0.7.2", - "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -53,10 +53,10 @@ "swagger-ui-react": "^4.0.0-rc.3" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index c1ba2038aa..a11f561986 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-auth-backend +## 0.4.7 + +### Patch Changes + +- 5ee31f860b: Only use settings that have a value when creating a new FirestoreKeyStore instance +- 3e0e2f09d5: Added forwarding of the `audience` option for the SAML provider, making it possible to enable `audience` verification. +- Updated dependencies + - @backstage/backend-common@0.9.9 + - @backstage/test-utils@0.1.21 + - @backstage/catalog-client@0.5.1 + ## 0.4.6 ### Patch Changes diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 5c9d70ea10..e9ec29970b 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend", "description": "A Backstage backend plugin that handles authentication", - "version": "0.4.6", + "version": "0.4.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,12 +30,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.8", - "@backstage/catalog-client": "^0.5.0", + "@backstage/backend-common": "^0.9.9", + "@backstage/catalog-client": "^0.5.1", "@backstage/catalog-model": "^0.9.6", "@backstage/config": "^0.1.11", "@backstage/errors": "^0.1.4", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@google-cloud/firestore": "^4.15.1", "@types/express": "^4.17.6", "@types/passport": "^1.0.3", @@ -73,7 +73,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.1", + "@backstage/cli": "^0.8.2", "@types/body-parser": "^1.19.0", "@types/cookie-parser": "^1.4.2", "@types/express-session": "^1.17.2", diff --git a/plugins/azure-devops-backend/CHANGELOG.md b/plugins/azure-devops-backend/CHANGELOG.md index b756e7ce4f..d2fe981eb0 100644 --- a/plugins/azure-devops-backend/CHANGELOG.md +++ b/plugins/azure-devops-backend/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-azure-devops-backend +## 0.2.0 + +### Minor Changes + +- b85acc8c35: refactor(`@backstage/plugin-azure-devops`): Consume types from `@backstage/plugin-azure-devops-common`. + Stop re-exporting types from `@backstage/plugin-azure-devops-backend`. + Added new types to `@backstage/plugin-azure-devops-common`. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.9.9 + - @backstage/plugin-azure-devops-common@0.0.2 + ## 0.1.4 ### Patch Changes diff --git a/plugins/azure-devops-backend/package.json b/plugins/azure-devops-backend/package.json index 9dd79e9f8e..901ad0b960 100644 --- a/plugins/azure-devops-backend/package.json +++ b/plugins/azure-devops-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops-backend", - "version": "0.1.4", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,9 +20,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.8", + "@backstage/backend-common": "^0.9.9", "@backstage/config": "^0.1.11", - "@backstage/plugin-azure-devops-common": "^0.0.1", + "@backstage/plugin-azure-devops-common": "^0.0.2", "@types/express": "^4.17.6", "azure-devops-node-api": "^11.0.1", "express": "^4.17.1", @@ -31,7 +31,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.1", + "@backstage/cli": "^0.8.2", "@types/supertest": "^2.0.8", "supertest": "^4.0.2", "msw": "^0.35.0" diff --git a/plugins/azure-devops-common/CHANGELOG.md b/plugins/azure-devops-common/CHANGELOG.md new file mode 100644 index 0000000000..b414cd3ce6 --- /dev/null +++ b/plugins/azure-devops-common/CHANGELOG.md @@ -0,0 +1,9 @@ +# @backstage/plugin-azure-devops-common + +## 0.0.2 + +### Patch Changes + +- b85acc8c35: refactor(`@backstage/plugin-azure-devops`): Consume types from `@backstage/plugin-azure-devops-common`. + Stop re-exporting types from `@backstage/plugin-azure-devops-backend`. + Added new types to `@backstage/plugin-azure-devops-common`. diff --git a/plugins/azure-devops-common/package.json b/plugins/azure-devops-common/package.json index 05f70f6596..a1ddddcbe0 100644 --- a/plugins/azure-devops-common/package.json +++ b/plugins/azure-devops-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops-common", - "version": "0.0.1", + "version": "0.0.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,7 +29,7 @@ "clean": "backstage-cli clean" }, "devDependencies": { - "@backstage/cli": "^0.8.1" + "@backstage/cli": "^0.8.2" }, "files": [ "dist" diff --git a/plugins/azure-devops/CHANGELOG.md b/plugins/azure-devops/CHANGELOG.md index 71904265dd..2f75f64236 100644 --- a/plugins/azure-devops/CHANGELOG.md +++ b/plugins/azure-devops/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-azure-devops +## 0.1.3 + +### Patch Changes + +- b85acc8c35: refactor(`@backstage/plugin-azure-devops`): Consume types from `@backstage/plugin-azure-devops-common`. + Stop re-exporting types from `@backstage/plugin-azure-devops-backend`. + Added new types to `@backstage/plugin-azure-devops-common`. +- 84ace9a29c: Simplified queue time calculation in `BuildTable`. +- Updated dependencies + - @backstage/core-components@0.7.3 + - @backstage/theme@0.2.13 + - @backstage/plugin-azure-devops-common@0.0.2 + - @backstage/core-plugin-api@0.1.13 + - @backstage/plugin-catalog-react@0.6.3 + ## 0.1.2 ### Patch Changes diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index 25328d9e81..235304c9fa 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops", - "version": "0.1.2", + "version": "0.1.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -28,12 +28,12 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.6", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/errors": "^0.1.4", - "@backstage/plugin-azure-devops-common": "^0.0.1", - "@backstage/plugin-catalog-react": "^0.6.2", - "@backstage/theme": "^0.2.12", + "@backstage/plugin-azure-devops-common": "^0.0.2", + "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -44,10 +44,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/badges/package.json b/plugins/badges/package.json index 0cc3a06509..0c792808bc 100644 --- a/plugins/badges/package.json +++ b/plugins/badges/package.json @@ -28,11 +28,11 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/errors": "^0.1.3", "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -42,10 +42,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/bazaar/CHANGELOG.md b/plugins/bazaar/CHANGELOG.md index 3cd13cfa8a..731f62b9e9 100644 --- a/plugins/bazaar/CHANGELOG.md +++ b/plugins/bazaar/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-bazaar +## 0.1.3 + +### Patch Changes + +- 4a336fd292: Name extension to remove deprecation warning +- Updated dependencies + - @backstage/cli@0.8.2 + - @backstage/core-components@0.7.3 + - @backstage/core-plugin-api@0.1.13 + - @backstage/plugin-catalog-react@0.6.3 + ## 0.1.2 ### Patch Changes diff --git a/plugins/bazaar/package.json b/plugins/bazaar/package.json index 5a98abfc3a..4d18784d2d 100644 --- a/plugins/bazaar/package.json +++ b/plugins/bazaar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bazaar", - "version": "0.1.2", + "version": "0.1.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,11 +22,11 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.5", - "@backstage/cli": "^0.8.0", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/cli": "^0.8.2", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/plugin-catalog": "^0.7.2", - "@backstage/plugin-catalog-react": "^0.6.1", + "@backstage/plugin-catalog-react": "^0.6.3", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -39,7 +39,7 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", + "@backstage/cli": "^0.8.2", "@backstage/dev-utils": "^0.2.12", "@testing-library/jest-dom": "^5.10.1", "cross-fetch": "^3.0.6" diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index c4403830f1..5ebfd453a2 100644 --- a/plugins/bitrise/package.json +++ b/plugins/bitrise/package.json @@ -22,10 +22,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -39,10 +39,10 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/catalog-backend-module-msgraph/CHANGELOG.md b/plugins/catalog-backend-module-msgraph/CHANGELOG.md index 7f3b8657f9..ee8aa49065 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.2.9 + +### Patch Changes + +- 779d7a2304: Tweak logic for msgraph catalog ingesting for display names with security groups + + Previously security groups that weren't mail enabled were imported with UUIDs, now they use the display name. + +- Updated dependencies + - @backstage/plugin-catalog-backend@0.17.3 + ## 0.2.8 ### Patch Changes diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index 68e58d922d..b3c9494f78 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-msgraph", "description": "A Backstage catalog backend modules that helps integrate towards Microsoft Graph", - "version": "0.2.8", + "version": "0.2.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,7 +32,7 @@ "@azure/msal-node": "^1.1.0", "@backstage/catalog-model": "^0.9.5", "@backstage/config": "^0.1.10", - "@backstage/plugin-catalog-backend": "^0.17.1", + "@backstage/plugin-catalog-backend": "^0.17.3", "@microsoft/microsoft-graph-types": "^2.6.0", "cross-fetch": "^3.0.6", "lodash": "^4.17.21", @@ -41,9 +41,9 @@ "qs": "^6.9.4" }, "devDependencies": { - "@backstage/backend-common": "^0.9.7", - "@backstage/cli": "^0.8.0", - "@backstage/test-utils": "^0.1.19", + "@backstage/backend-common": "^0.9.9", + "@backstage/cli": "^0.8.2", + "@backstage/test-utils": "^0.1.21", "@types/lodash": "^4.14.151", "msw": "^0.35.0" }, diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index a6bdc8407d..abb50a345c 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-catalog-backend +## 0.17.3 + +### Patch Changes + +- 86bef79ad1: Allow singleton and flexibly nested EntityFilters +- Updated dependencies + - @backstage/backend-common@0.9.9 + - @backstage/catalog-client@0.5.1 + ## 0.17.2 ### Patch Changes diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 121dd933f9..f13ff2d66c 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend", "description": "The Backstage backend plugin that provides the Backstage catalog", - "version": "0.17.2", + "version": "0.17.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,8 +30,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.8", - "@backstage/catalog-client": "^0.5.0", + "@backstage/backend-common": "^0.9.9", + "@backstage/catalog-client": "^0.5.1", "@backstage/catalog-model": "^0.9.6", "@backstage/config": "^0.1.11", "@backstage/errors": "^0.1.4", @@ -63,8 +63,8 @@ }, "devDependencies": { "@backstage/backend-test-utils": "^0.1.8", - "@backstage/cli": "^0.8.1", - "@backstage/test-utils": "^0.1.20", + "@backstage/cli": "^0.8.2", + "@backstage/test-utils": "^0.1.21", "@types/core-js": "^2.5.4", "@types/git-url-parse": "^9.0.0", "@types/lodash": "^4.14.151", diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index d5cacc12a6..e58d66b1f2 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -23,10 +23,10 @@ "dependencies": { "@backstage/catalog-client": "^0.5.0", "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -41,10 +41,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 0287be8e06..242231bf81 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -33,8 +33,8 @@ "dependencies": { "@backstage/catalog-client": "^0.5.0", "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/errors": "^0.1.3", "@backstage/integration": "^0.6.8", "@backstage/integration-react": "^0.1.12", @@ -55,10 +55,10 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index cdb80ceac4..79dfa5e72a 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-react +## 0.6.3 + +### Patch Changes + +- 03b47a476d: export `loadIdentityOwnerRefs` and `loadCatalogOwnerRefs` all the way +- Updated dependencies + - @backstage/core-components@0.7.3 + - @backstage/catalog-client@0.5.1 + - @backstage/core-plugin-api@0.1.13 + - @backstage/core-app-api@0.1.20 + ## 0.6.2 ### Patch Changes diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index 40c0d9c739..9778163391 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-react", "description": "A frontend library that helps other Backstage plugins interact with the catalog", - "version": "0.6.2", + "version": "0.6.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,11 +29,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-client": "^0.5.0", + "@backstage/catalog-client": "^0.5.1", "@backstage/catalog-model": "^0.9.6", - "@backstage/core-app-api": "^0.1.19", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-app-api": "^0.1.20", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/errors": "^0.1.4", "@backstage/integration": "^0.6.9", "@backstage/types": "^0.1.1", @@ -51,8 +51,8 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/test-utils": "^0.1.20", + "@backstage/cli": "^0.8.2", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 0bc7ac4034..9697741dde 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -33,12 +33,12 @@ "dependencies": { "@backstage/catalog-client": "^0.5.0", "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/errors": "^0.1.3", "@backstage/integration-react": "^0.1.12", "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -51,10 +51,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/circleci/CHANGELOG.md b/plugins/circleci/CHANGELOG.md index a501d77ff3..78cd6d9711 100644 --- a/plugins/circleci/CHANGELOG.md +++ b/plugins/circleci/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-circleci +## 0.2.28 + +### Patch Changes + +- e849f9cc0a: Use `` instead of `` in combination with `Suspense`. +- Updated dependencies + - @backstage/core-components@0.7.3 + - @backstage/theme@0.2.13 + - @backstage/core-plugin-api@0.1.13 + - @backstage/plugin-catalog-react@0.6.3 + ## 0.2.27 ### Patch Changes diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index 9a7c428d33..c57d19ea00 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-circleci", "description": "A Backstage plugin that integrates towards Circle CI", - "version": "0.2.27", + "version": "0.2.28", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,10 +33,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", - "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", + "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -52,10 +52,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index 376dd9f091..6521816f73 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -32,10 +32,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -49,10 +49,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/code-coverage/CHANGELOG.md b/plugins/code-coverage/CHANGELOG.md index 8a8c91a111..572e0f7800 100644 --- a/plugins/code-coverage/CHANGELOG.md +++ b/plugins/code-coverage/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-code-coverage +## 0.1.17 + +### Patch Changes + +- a5512851a0: Change represented test date from epoch to something more human friendly. Round test coverage to 2 decimal places. +- 5a85b802f3: Make dates in X-Axis sort in ascending order +- Updated dependencies + - @backstage/core-components@0.7.3 + - @backstage/theme@0.2.13 + - @backstage/core-plugin-api@0.1.13 + - @backstage/plugin-catalog-react@0.6.3 + ## 0.1.16 ### Patch Changes diff --git a/plugins/code-coverage/package.json b/plugins/code-coverage/package.json index d01e12dff5..0d6e998b69 100644 --- a/plugins/code-coverage/package.json +++ b/plugins/code-coverage/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-code-coverage", "description": "A Backstage plugin that helps you keep track of your code coverage", - "version": "0.1.16", + "version": "0.1.17", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,11 +23,11 @@ "dependencies": { "@backstage/catalog-model": "^0.9.6", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/errors": "^0.1.4", - "@backstage/plugin-catalog-react": "^0.6.2", - "@backstage/theme": "^0.2.12", + "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -42,10 +42,10 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index e460878691..0708115270 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -22,10 +22,10 @@ }, "dependencies": { "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/errors": "^0.1.4", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@backstage/types": "^0.1.1", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -37,10 +37,10 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index 010498a272..064b24bd0c 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -32,9 +32,9 @@ }, "dependencies": { "@backstage/config": "^0.1.10", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", - "@backstage/theme": "^0.2.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -55,10 +55,10 @@ "yup": "^0.32.9" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/explore/package.json b/plugins/explore/package.json index 66bd3fdf7a..30890c7176 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -32,11 +32,11 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/plugin-catalog-react": "^0.6.1", "@backstage/plugin-explore-react": "^0.0.6", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -49,10 +49,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/firehydrant/package.json b/plugins/firehydrant/package.json index 0738980613..4e3fbdd3c8 100644 --- a/plugins/firehydrant/package.json +++ b/plugins/firehydrant/package.json @@ -22,10 +22,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -35,10 +35,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/fossa/package.json b/plugins/fossa/package.json index c5b6b8b62f..94528a3126 100644 --- a/plugins/fossa/package.json +++ b/plugins/fossa/package.json @@ -33,11 +33,11 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/errors": "^0.1.3", "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -49,10 +49,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 65458ea88b..471b9d80f1 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -31,9 +31,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", - "@backstage/theme": "^0.2.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -43,10 +43,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/git-release-manager/package.json b/plugins/git-release-manager/package.json index 11f6655c5d..b1b2c11732 100644 --- a/plugins/git-release-manager/package.json +++ b/plugins/git-release-manager/package.json @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/integration": "^0.6.8", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -39,10 +39,10 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/github-actions/CHANGELOG.md b/plugins/github-actions/CHANGELOG.md index 222bc4c851..c8fce8e501 100644 --- a/plugins/github-actions/CHANGELOG.md +++ b/plugins/github-actions/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-github-actions +## 0.4.23 + +### Patch Changes + +- e849f9cc0a: Use `` instead of `` in combination with `Suspense`. +- Updated dependencies + - @backstage/core-components@0.7.3 + - @backstage/theme@0.2.13 + - @backstage/core-plugin-api@0.1.13 + - @backstage/plugin-catalog-react@0.6.3 + ## 0.4.22 ### Patch Changes diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 087a596c64..1a4079a5ca 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-github-actions", "description": "A Backstage plugin that integrates towards GitHub Actions", - "version": "0.4.22", + "version": "0.4.23", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,11 +34,11 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/integration": "^0.6.8", - "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -52,10 +52,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/github-deployments/package.json b/plugins/github-deployments/package.json index a405800387..78061951f5 100644 --- a/plugins/github-deployments/package.json +++ b/plugins/github-deployments/package.json @@ -22,13 +22,13 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/errors": "^0.1.3", "@backstage/integration": "^0.6.8", "@backstage/integration-react": "^0.1.12", "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -39,10 +39,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index cdebce653f..c290599c2c 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -32,9 +32,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", - "@backstage/theme": "^0.2.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -44,10 +44,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index b21281ff80..df1ad6f887 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -31,9 +31,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", - "@backstage/theme": "^0.2.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -44,10 +44,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/home/CHANGELOG.md b/plugins/home/CHANGELOG.md index aa35cdf968..06beb827a6 100644 --- a/plugins/home/CHANGELOG.md +++ b/plugins/home/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-home +## 0.4.5 + +### Patch Changes + +- 4a336fd292: Add name option to `createCardExtension` to remove deprecation warnings for extensions without name. Name will be required for extensions in a future release of `core-plugin-api` and therefore also in `createCardExtension`. +- Updated dependencies + - @backstage/core-components@0.7.3 + - @backstage/theme@0.2.13 + - @backstage/core-plugin-api@0.1.13 + ## 0.4.4 ### Patch Changes diff --git a/plugins/home/package.json b/plugins/home/package.json index e100dba9ca..8020d7af03 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-home", "description": "A Backstage plugin that helps you build a home page", - "version": "0.4.4", + "version": "0.4.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", - "@backstage/theme": "^0.2.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -35,10 +35,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/ilert/CHANGELOG.md b/plugins/ilert/CHANGELOG.md index efa2d5d8f6..f43f2add09 100644 --- a/plugins/ilert/CHANGELOG.md +++ b/plugins/ilert/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-ilert +## 0.1.16 + +### Patch Changes + +- fa325fd89d: Change the version of `@date-io/luxon` from 2.x to 1.x to make it compatible with material-ui-pickers +- Updated dependencies + - @backstage/core-components@0.7.3 + - @backstage/theme@0.2.13 + - @backstage/core-plugin-api@0.1.13 + - @backstage/plugin-catalog-react@0.6.3 + ## 0.1.15 ### Patch Changes diff --git a/plugins/ilert/package.json b/plugins/ilert/package.json index f93a5b4870..69c4025181 100644 --- a/plugins/ilert/package.json +++ b/plugins/ilert/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-ilert", "description": "A Backstage plugin that integrates towards iLert", - "version": "0.1.15", + "version": "0.1.16", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,11 +22,11 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/errors": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/theme": "^0.2.13", "@date-io/luxon": "1.x", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -39,10 +39,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/jenkins-backend/CHANGELOG.md b/plugins/jenkins-backend/CHANGELOG.md index 635fa080ce..48f4461f7f 100644 --- a/plugins/jenkins-backend/CHANGELOG.md +++ b/plugins/jenkins-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-jenkins-backend +## 0.1.7 + +### Patch Changes + +- e8a1c1afe2: Don't require a validation pattern for the Jenkins base URL. +- Updated dependencies + - @backstage/backend-common@0.9.9 + - @backstage/catalog-client@0.5.1 + ## 0.1.6 ### Patch Changes diff --git a/plugins/jenkins-backend/package.json b/plugins/jenkins-backend/package.json index 93f5fa84d2..7049fc0a32 100644 --- a/plugins/jenkins-backend/package.json +++ b/plugins/jenkins-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-jenkins-backend", "description": "A Backstage backend plugin that integrates towards Jenkins", - "version": "0.1.6", + "version": "0.1.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,8 +22,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.7", - "@backstage/catalog-client": "^0.5.0", + "@backstage/backend-common": "^0.9.9", + "@backstage/catalog-client": "^0.5.1", "@backstage/catalog-model": "^0.9.5", "@backstage/config": "^0.1.10", "@types/express": "^4.17.6", @@ -35,7 +35,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.0", + "@backstage/cli": "^0.8.2", "@types/jenkins": "^0.23.1", "@types/supertest": "^2.0.8", "msw": "^0.35.0", diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index 1bd6388fba..80d8b40322 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -33,10 +33,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -48,10 +48,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/kafka/CHANGELOG.md b/plugins/kafka/CHANGELOG.md index 3697f8de28..2fe42565da 100644 --- a/plugins/kafka/CHANGELOG.md +++ b/plugins/kafka/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-kafka +## 0.2.20 + +### Patch Changes + +- 91f8644f17: Use IdentityApi to provide Auth Token for KafkaBackendClient Api calls +- Updated dependencies + - @backstage/core-components@0.7.3 + - @backstage/theme@0.2.13 + - @backstage/core-plugin-api@0.1.13 + - @backstage/plugin-catalog-react@0.6.3 + ## 0.2.19 ### Patch Changes diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json index e9fe6bc4bf..888c79db59 100644 --- a/plugins/kafka/package.json +++ b/plugins/kafka/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kafka", "description": "A Backstage plugin that integrates towards Kafka", - "version": "0.2.19", + "version": "0.2.20", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,10 +22,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", - "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", + "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -35,10 +35,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index ac7b19a1d9..a757041b32 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-kubernetes +## 0.4.19 + +### Patch Changes + +- 9f3dd76bc7: fix: kubernetes plugin shall pass id token on get clusters request if possible +- Updated dependencies + - @backstage/core-components@0.7.3 + - @backstage/theme@0.2.13 + - @backstage/core-plugin-api@0.1.13 + - @backstage/plugin-catalog-react@0.6.3 + ## 0.4.18 ### Patch Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 5bce077278..9dbca8aff8 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes", "description": "A Backstage plugin that integrates towards Kubernetes", - "version": "0.4.18", + "version": "0.4.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,11 +33,11 @@ "dependencies": { "@backstage/catalog-model": "^0.9.6", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", - "@backstage/plugin-catalog-react": "^0.6.2", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", + "@backstage/plugin-catalog-react": "^0.6.3", "@backstage/plugin-kubernetes-common": "^0.1.5", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@kubernetes/client-node": "^0.15.0", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -51,10 +51,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index 1f3dd91d32..f0dca639fc 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -34,10 +34,10 @@ "dependencies": { "@backstage/catalog-model": "^0.9.5", "@backstage/config": "^0.1.10", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -47,10 +47,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index c60f1ee9a5..271daa566e 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -32,9 +32,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", - "@backstage/theme": "^0.2.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -43,10 +43,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/org/package.json b/plugins/org/package.json index dc91b59816..5348f67523 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -22,10 +22,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -37,10 +37,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index 2efc824746..7b42a15bc6 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -32,10 +32,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -48,10 +48,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 38e295a37b..2e2da0a284 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -33,10 +33,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -49,10 +49,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md index 9e870a9e0b..43a522482f 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-scaffolder-backend-module-cookiecutter +## 0.1.4 + +### Patch Changes + +- 290fbb3ec2: Add missing API docs to scaffolder action plugins +- Updated dependencies + - @backstage/backend-common@0.9.9 + - @backstage/plugin-scaffolder-backend@0.15.12 + ## 0.1.3 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index 38c2eb493c..36a6310aa2 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/package.json +++ b/plugins/scaffolder-backend-module-cookiecutter/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-cookiecutter", "description": "A module for the scaffolder backend that lets you template projects using cookiecutter", - "version": "0.1.3", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,10 +20,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.8", + "@backstage/backend-common": "^0.9.9", "@backstage/errors": "^0.1.4", "@backstage/integration": "^0.6.9", - "@backstage/plugin-scaffolder-backend": "^0.15.11", + "@backstage/plugin-scaffolder-backend": "^0.15.12", "@backstage/config": "^0.1.11", "@backstage/types": "^0.1.1", "command-exists": "^1.2.9", @@ -33,7 +33,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.1", + "@backstage/cli": "^0.8.2", "@types/fs-extra": "^9.0.1", "@types/mock-fs": "^4.13.0", "@types/jest": "^26.0.7", diff --git a/plugins/scaffolder-backend-module-rails/CHANGELOG.md b/plugins/scaffolder-backend-module-rails/CHANGELOG.md index f1e53abc5b..9dcd6bcb88 100644 --- a/plugins/scaffolder-backend-module-rails/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-rails/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-scaffolder-backend-module-rails +## 0.1.7 + +### Patch Changes + +- 290fbb3ec2: Add missing API docs to scaffolder action plugins +- Updated dependencies + - @backstage/backend-common@0.9.9 + - @backstage/plugin-scaffolder-backend@0.15.12 + ## 0.1.6 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index e79b7fb818..f4ace02eca 100644 --- a/plugins/scaffolder-backend-module-rails/package.json +++ b/plugins/scaffolder-backend-module-rails/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-rails", "description": "A module for the scaffolder backend that lets you template projects using Rails", - "version": "0.1.6", + "version": "0.1.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.8", - "@backstage/plugin-scaffolder-backend": "^0.15.11", + "@backstage/backend-common": "^0.9.9", + "@backstage/plugin-scaffolder-backend": "^0.15.12", "@backstage/config": "^0.1.11", "@backstage/errors": "^0.1.4", "@backstage/integration": "^0.6.9", @@ -31,7 +31,7 @@ "fs-extra": "^9.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.1", + "@backstage/cli": "^0.8.2", "@types/jest": "^26.0.7", "@types/node": "^14.14.32", "@types/command-exists": "^1.2.0", diff --git a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md index b15614b0ba..9373564b1c 100644 --- a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-scaffolder-backend-module-yeoman +## 0.1.2 + +### Patch Changes + +- 290fbb3ec2: Add missing API docs to scaffolder action plugins +- Updated dependencies + - @backstage/plugin-scaffolder-backend@0.15.12 + ## 0.1.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-yeoman/package.json b/plugins/scaffolder-backend-module-yeoman/package.json index b853309c56..8d17df0239 100644 --- a/plugins/scaffolder-backend-module-yeoman/package.json +++ b/plugins/scaffolder-backend-module-yeoman/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-yeoman", - "version": "0.1.1", + "version": "0.1.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,13 +21,13 @@ }, "dependencies": { "@backstage/config": "^0.1.11", - "@backstage/plugin-scaffolder-backend": "^0.15.11", + "@backstage/plugin-scaffolder-backend": "^0.15.12", "@backstage/types": "^0.1.1", "winston": "^3.2.1", "yeoman-environment": "^3.6.0" }, "devDependencies": { - "@backstage/backend-common": "^0.9.8", + "@backstage/backend-common": "^0.9.9", "@types/jest": "^26.0.7" }, "files": [ diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index 689cb51d24..de033eabbd 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,55 @@ # @backstage/plugin-scaffolder-backend +## 0.15.12 + +### Patch Changes + +- 9990df8a1f: Expose some classes and interfaces public so TaskWorkers can run externally from the scaffolder API. +- b45a34fb15: Adds a new endpoint for consuming logs from the Scaffolder that uses long polling instead of Server Sent Events. + + This is useful if Backstage is accessed from an environment that doesn't support SSE correctly, which happens in combination with certain enterprise HTTP Proxy servers. + + It is intended to switch the endpoint globally for the whole instance. + If you want to use it, you can provide a reconfigured API to the `scaffolderApiRef`: + + ```tsx + // packages/app/src/apis.ts + + // ... + import { + scaffolderApiRef, + ScaffolderClient, + } from '@backstage/plugin-scaffolder'; + + export const apis: AnyApiFactory[] = [ + // ... + + createApiFactory({ + api: scaffolderApiRef, + deps: { + discoveryApi: discoveryApiRef, + identityApi: identityApiRef, + scmIntegrationsApi: scmIntegrationsApiRef, + }, + factory: ({ discoveryApi, identityApi, scmIntegrationsApi }) => + new ScaffolderClient({ + discoveryApi, + identityApi, + scmIntegrationsApi, + // use long polling instead of an eventsource + useLongPollingLogs: true, + }), + }), + ]; + ``` + +- a794c341ca: Fix a bug where only file mode 775 is considered an executable +- Updated dependencies + - @backstage/backend-common@0.9.9 + - @backstage/catalog-client@0.5.1 + - @backstage/plugin-catalog-backend@0.17.3 + - @backstage/plugin-scaffolder-backend-module-cookiecutter@0.1.4 + ## 0.15.11 ### Patch Changes diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index fe5d61d4d3..e5d13801ba 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend", "description": "The Backstage backend plugin that helps you create new things", - "version": "0.15.11", + "version": "0.15.12", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,15 +30,15 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.8", - "@backstage/catalog-client": "^0.5.0", + "@backstage/backend-common": "^0.9.9", + "@backstage/catalog-client": "^0.5.1", "@backstage/catalog-model": "^0.9.6", "@backstage/config": "^0.1.11", "@backstage/errors": "^0.1.4", "@backstage/integration": "^0.6.9", - "@backstage/plugin-catalog-backend": "^0.17.2", + "@backstage/plugin-catalog-backend": "^0.17.3", "@backstage/plugin-scaffolder-common": "^0.1.1", - "@backstage/plugin-scaffolder-backend-module-cookiecutter": "^0.1.3", + "@backstage/plugin-scaffolder-backend-module-cookiecutter": "^0.1.4", "@backstage/types": "^0.1.1", "@gitbeaker/core": "^30.2.0", "@gitbeaker/node": "^30.2.0", @@ -71,8 +71,8 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/test-utils": "^0.1.20", + "@backstage/cli": "^0.8.2", + "@backstage/test-utils": "^0.1.21", "@types/command-exists": "^1.2.0", "@types/fs-extra": "^9.0.1", "@types/git-url-parse": "^9.0.0", diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index b9873b5357..dd5653240a 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,55 @@ # @backstage/plugin-scaffolder +## 0.11.10 + +### Patch Changes + +- fe5738fe1c: Lazy load `LazyLog` as it is rarely used. +- b45a34fb15: Adds a new endpoint for consuming logs from the Scaffolder that uses long polling instead of Server Sent Events. + + This is useful if Backstage is accessed from an environment that doesn't support SSE correctly, which happens in combination with certain enterprise HTTP Proxy servers. + + It is intended to switch the endpoint globally for the whole instance. + If you want to use it, you can provide a reconfigured API to the `scaffolderApiRef`: + + ```tsx + // packages/app/src/apis.ts + + // ... + import { + scaffolderApiRef, + ScaffolderClient, + } from '@backstage/plugin-scaffolder'; + + export const apis: AnyApiFactory[] = [ + // ... + + createApiFactory({ + api: scaffolderApiRef, + deps: { + discoveryApi: discoveryApiRef, + identityApi: identityApiRef, + scmIntegrationsApi: scmIntegrationsApiRef, + }, + factory: ({ discoveryApi, identityApi, scmIntegrationsApi }) => + new ScaffolderClient({ + discoveryApi, + identityApi, + scmIntegrationsApi, + // use long polling instead of an eventsource + useLongPollingLogs: true, + }), + }), + ]; + ``` + +- Updated dependencies + - @backstage/core-components@0.7.3 + - @backstage/theme@0.2.13 + - @backstage/catalog-client@0.5.1 + - @backstage/core-plugin-api@0.1.13 + - @backstage/plugin-catalog-react@0.6.3 + ## 0.11.9 ### Patch Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 58bee32201..1d8bc4a0ea 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder", "description": "The Backstage plugin that helps you create new things", - "version": "0.11.9", + "version": "0.11.10", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,16 +31,16 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-client": "^0.5.0", + "@backstage/catalog-client": "^0.5.1", "@backstage/catalog-model": "^0.9.6", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/errors": "^0.1.4", "@backstage/integration": "^0.6.9", "@backstage/integration-react": "^0.1.13", - "@backstage/plugin-catalog-react": "^0.6.2", - "@backstage/theme": "^0.2.12", + "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/theme": "^0.2.13", "@backstage/types": "^0.1.1", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -66,10 +66,10 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/search-backend-module-elasticsearch/CHANGELOG.md b/plugins/search-backend-module-elasticsearch/CHANGELOG.md index e950678cef..6bd39200c0 100644 --- a/plugins/search-backend-module-elasticsearch/CHANGELOG.md +++ b/plugins/search-backend-module-elasticsearch/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/plugin-search-backend-module-elasticsearch +## 0.0.5 + +### Patch Changes + +- 36350bf8b3: Pinning version of elastic search client to 7.13.0 to prevent breaking change towards third party ElasticSearch clusters on 7.14.0. + ## 0.0.4 ### Patch Changes diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index 2d5cabf725..df0a783396 100644 --- a/plugins/search-backend-module-elasticsearch/package.json +++ b/plugins/search-backend-module-elasticsearch/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-elasticsearch", "description": "A module for the search backend that implements search using ElasticSearch", - "version": "0.0.4", + "version": "0.0.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,8 +30,8 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/backend-common": "^0.9.7", - "@backstage/cli": "^0.8.0", + "@backstage/backend-common": "^0.9.9", + "@backstage/cli": "^0.8.2", "@elastic/elasticsearch-mock": "^0.3.0" }, "files": [ diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index 66c4a61ead..808c847914 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-search +## 0.4.17 + +### Patch Changes + +- 5dcea2586c: Added `SearchModal` component. + + Now you can import `SearchModal` in your apps: + + ```js + import { SearchModal } from '@backstage/plugin-search'; + ``` + + You can also use the `SidebarSearchModal` component to integrate it into the sidebar of your sample apps: + + ```js + import { SidebarSearchModal } from '@backstage/plugin-search'; + ``` + +- Updated dependencies + - @backstage/core-components@0.7.3 + - @backstage/theme@0.2.13 + - @backstage/core-plugin-api@0.1.13 + - @backstage/plugin-catalog-react@0.6.3 + ## 0.4.16 ### Patch Changes diff --git a/plugins/search/package.json b/plugins/search/package.json index 60e8a8eb3c..9d25ec723d 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search", "description": "The Backstage plugin that provides your backstage app with search", - "version": "0.4.16", + "version": "0.4.17", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,12 +32,12 @@ "dependencies": { "@backstage/catalog-model": "^0.9.6", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/errors": "^0.1.4", - "@backstage/plugin-catalog-react": "^0.6.2", + "@backstage/plugin-catalog-react": "^0.6.3", "@backstage/search-common": "^0.2.1", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@backstage/types": "^0.1.1", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -51,10 +51,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/sentry/CHANGELOG.md b/plugins/sentry/CHANGELOG.md index 2f44314100..b0bcaad261 100644 --- a/plugins/sentry/CHANGELOG.md +++ b/plugins/sentry/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-sentry +## 0.3.28 + +### Patch Changes + +- 33d7bd0baa: fix: sentry-plugin can forward identity token to backend (for case when it requires authorization) +- Updated dependencies + - @backstage/core-components@0.7.3 + - @backstage/theme@0.2.13 + - @backstage/core-plugin-api@0.1.13 + - @backstage/plugin-catalog-react@0.6.3 + ## 0.3.27 ### Patch Changes diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 005bfb2b3a..ee2421bf7f 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-sentry", "description": "A Backstage plugin that integrates towards Sentry", - "version": "0.3.27", + "version": "0.3.28", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,10 +33,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.6", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", - "@backstage/plugin-catalog-react": "^0.6.2", - "@backstage/theme": "^0.2.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", + "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -48,10 +48,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index 8b6f57bc52..e73e047bc1 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -21,9 +21,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", - "@backstage/theme": "^0.2.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", + "@backstage/theme": "^0.2.13", "@backstage/types": "^0.1.1", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -38,10 +38,10 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index 30cdd83368..bb55e68954 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -34,10 +34,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -49,10 +49,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/splunk-on-call/package.json b/plugins/splunk-on-call/package.json index 632de7e413..9d9ffdb370 100644 --- a/plugins/splunk-on-call/package.json +++ b/plugins/splunk-on-call/package.json @@ -32,10 +32,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -47,10 +47,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index 31b12a97a9..839ad1b399 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -31,9 +31,9 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", - "@backstage/theme": "^0.2.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -45,10 +45,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index a825b9a008..be100b9dea 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-techdocs +## 0.12.5 + +### Patch Changes + +- fe5738fe1c: Lazy load `LazyLog` as it is rarely used. +- 53c9ad7e04: Update font weight for headings in TechDocs +- Updated dependencies + - @backstage/core-components@0.7.3 + - @backstage/theme@0.2.13 + - @backstage/plugin-search@0.4.17 + - @backstage/core-plugin-api@0.1.13 + - @backstage/plugin-catalog-react@0.6.3 + ## 0.12.4 ### Patch Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index c72bcab499..79841a0621 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs", "description": "The Backstage plugin that renders technical documentation for your components", - "version": "0.12.4", + "version": "0.12.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,15 +34,15 @@ "dependencies": { "@backstage/catalog-model": "^0.9.6", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/errors": "^0.1.4", "@backstage/integration": "^0.6.9", "@backstage/integration-react": "^0.1.13", "@backstage/plugin-catalog": "^0.7.2", - "@backstage/plugin-catalog-react": "^0.6.2", - "@backstage/plugin-search": "^0.4.16", - "@backstage/theme": "^0.2.12", + "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/plugin-search": "^0.4.17", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -61,10 +61,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/todo/package.json b/plugins/todo/package.json index 9f27679615..5e32d84497 100644 --- a/plugins/todo/package.json +++ b/plugins/todo/package.json @@ -28,11 +28,11 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/errors": "^0.1.3", "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -41,10 +41,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 9ec642892b..dc63957cb9 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -31,9 +31,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", - "@backstage/theme": "^0.2.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -43,10 +43,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/xcmetrics/package.json b/plugins/xcmetrics/package.json index 1ef31ecd6d..bbab66e455 100644 --- a/plugins/xcmetrics/package.json +++ b/plugins/xcmetrics/package.json @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-plugin-api": "^0.1.13", "@backstage/errors": "^0.1.3", - "@backstage/theme": "^0.2.12", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -36,10 +36,10 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/core-app-api": "^0.1.19", + "@backstage/cli": "^0.8.2", + "@backstage/core-app-api": "^0.1.20", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", From aae27a554e4a56b02a05c9905786e4424e3eb3a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 11 Nov 2021 13:38:56 +0100 Subject: [PATCH 151/196] bump uglify-js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- yarn.lock | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6c23c706b1..19c9f7e627 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11309,7 +11309,7 @@ commander@7.1.0: resolved "https://registry.npmjs.org/commander/-/commander-7.1.0.tgz#f2eaecf131f10e36e07d894698226e36ae0eb5ff" integrity sha512-pRxBna3MJe6HKnBGsDyMv8ETbptw3axEdYHoqNh7gu5oDcew8fs0xnivZGm06Ogk8zGAJ9VX+OPEr2GXEQK4dg== -commander@^2.19.0, commander@^2.20.0, commander@^2.20.3, commander@^2.7.1, commander@~2.20.3: +commander@^2.19.0, commander@^2.20.0, commander@^2.20.3, commander@^2.7.1: version "2.20.3" resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -27718,12 +27718,9 @@ uc.micro@^1.0.1, uc.micro@^1.0.5: integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== uglify-js@^3.1.4: - version "3.8.0" - resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.0.tgz#f3541ae97b2f048d7e7e3aa4f39fd8a1f5d7a805" - integrity sha512-ugNSTT8ierCsDHso2jkBHXYrU8Y5/fY2ZUprfrJUiD7YpuFvV4jODLFmb3h4btQjqr5Nh4TX4XtgDfCU1WdioQ== - dependencies: - commander "~2.20.3" - source-map "~0.6.1" + version "3.14.3" + resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.3.tgz#c0f25dfea1e8e5323eccf59610be08b6043c15cf" + integrity sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g== uid-number@0.0.6: version "0.0.6" From 33fa4487e6201fac5449100a0a10e7a4b3e04bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 11 Nov 2021 13:46:34 +0100 Subject: [PATCH 152/196] bump refractor and, hence, prismjs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6c23c706b1..57bbaa382e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23148,10 +23148,10 @@ printj@~1.1.0: resolved "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== -prismjs@^1.21.0, prismjs@^1.22.0, prismjs@~1.24.0: - version "1.24.1" - resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.24.1.tgz#c4d7895c4d6500289482fa8936d9cdd192684036" - integrity sha512-mNPsedLuk90RVJioIky8ANZEwYm5w9LcvCXrxHlwf4fNVSn8jEipMybMkWUyyF0JhnC+C4VcOVSBuHRKs1L5Ow== +prismjs@^1.21.0, prismjs@^1.22.0, prismjs@~1.25.0: + version "1.25.0" + resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz#6f822df1bdad965734b310b315a23315cf999756" + integrity sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg== private@^0.1.8: version "0.1.8" @@ -24448,13 +24448,13 @@ reflect-metadata@^0.1.13: integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== refractor@^3.1.0, refractor@^3.2.0: - version "3.4.0" - resolved "https://registry.npmjs.org/refractor/-/refractor-3.4.0.tgz#62bd274b06c942041f390c371b676eb67cb0a678" - integrity sha512-dBeD02lC5eytm9Gld2Mx0cMcnR+zhSnsTfPpWqFaMgUMJfC9A6bcN3Br/NaXrnBJcuxnLFR90k1jrkaSyV8umg== + version "3.5.0" + resolved "https://registry.npmjs.org/refractor/-/refractor-3.5.0.tgz#334586f352dda4beaf354099b48c2d18e0819aec" + integrity sha512-QwPJd3ferTZ4cSPPjdP5bsYHMytwWYnAN5EEnLtGvkqp/FCCnGsBgxrm9EuIDnjUC3Uc/kETtvVi7fSIVC74Dg== dependencies: hastscript "^6.0.0" parse-entities "^2.0.0" - prismjs "~1.24.0" + prismjs "~1.25.0" regenerate-unicode-properties@^8.2.0: version "8.2.0" From 71264599e142516b7b35b83a8867e29417007bbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 11 Nov 2021 14:05:45 +0100 Subject: [PATCH 153/196] bump xss MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6c23c706b1..ba46cc3425 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29161,9 +29161,9 @@ xpath@0.0.32: integrity sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw== xss@^1.0.8: - version "1.0.9" - resolved "https://registry.npmjs.org/xss/-/xss-1.0.9.tgz#3ffd565571ff60d2e40db7f3b80b4677bec770d2" - integrity sha512-2t7FahYnGJys6DpHLhajusId7R0Pm2yTmuL0GV9+mV0ZlaLSnb2toBmppATfg5sWIhZQGlsTLoecSzya+l4EAQ== + version "1.0.10" + resolved "https://registry.npmjs.org/xss/-/xss-1.0.10.tgz#5cd63a9b147a755a14cb0455c7db8866120eb4d2" + integrity sha512-qmoqrRksmzqSKvgqzN0055UFWY7OKx1/9JWeRswwEVX9fCG5jcYRxa/A2DHcmZX6VJvjzHRQ2STeeVcQkrmLSw== dependencies: commander "^2.20.3" cssfilter "0.0.10" From c416ae18517795d97bc870118bd70cf5864f043a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 11 Nov 2021 14:14:44 +0100 Subject: [PATCH 154/196] bump @npmcli/git MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- yarn.lock | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6c23c706b1..87327f96f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4714,22 +4714,7 @@ "@gar/promisify" "^1.0.1" semver "^7.3.5" -"@npmcli/git@^2.0.1": - version "2.0.6" - resolved "https://registry.npmjs.org/@npmcli/git/-/git-2.0.6.tgz#47b97e96b2eede3f38379262fa3bdfa6eae57bf2" - integrity sha512-a1MnTfeRPBaKbFY07fd+6HugY1WAkKJzdiJvlRub/9o5xz2F/JtPacZZapx5zRJUQFIzSL677vmTSxEcDMrDbg== - dependencies: - "@npmcli/promise-spawn" "^1.1.0" - lru-cache "^6.0.0" - mkdirp "^1.0.3" - npm-pick-manifest "^6.0.0" - promise-inflight "^1.0.1" - promise-retry "^2.0.1" - semver "^7.3.2" - unique-filename "^1.1.1" - which "^2.0.2" - -"@npmcli/git@^2.1.0": +"@npmcli/git@^2.0.1", "@npmcli/git@^2.1.0": version "2.1.0" resolved "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz#2fbd77e147530247d37f325930d457b3ebe894f6" integrity sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw== @@ -4802,7 +4787,7 @@ dependencies: json-parse-even-better-errors "^2.3.1" -"@npmcli/promise-spawn@^1.1.0", "@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": +"@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": version "1.3.2" resolved "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz#42d4e56a8e9274fba180dabc0aea6e38f29274f5" integrity sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg== From a1c4c8de3df2cd2987bafc5dcd3fb0cd7a130c7a Mon Sep 17 00:00:00 2001 From: Harry Hogg Date: Thu, 11 Nov 2021 13:25:38 +0000 Subject: [PATCH 155/196] Fixes a minor markdown foobar in the body output Signed-off-by: Harry Hogg --- scripts/snyk-github-issue-sync.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/snyk-github-issue-sync.ts b/scripts/snyk-github-issue-sync.ts index d7f64cf1ff..f1015f8139 100644 --- a/scripts/snyk-github-issue-sync.ts +++ b/scripts/snyk-github-issue-sync.ts @@ -82,9 +82,9 @@ const fetchSnykGithubIssueMap = async (): Promise> => { const generateIssueBody = (vulnerability: Vulnerability) => ` ## Affecting Packages/Plugins -${Array.from(vulnerability.packages).map( - ({ name, target }) => `* [${name}](${target})\n`, -)} +${Array.from(vulnerability.packages) + .map(({ name, target }) => `* [${name}](${target})`) + .join('\n')} ${vulnerability.description} `; From 03fdccacb7825955f8fd79b3d164968f62de60d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 11 Nov 2021 14:26:49 +0100 Subject: [PATCH 156/196] partially bump nth-check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- yarn.lock | 136 +++++++++++++++++++++++++++++------------------------- 1 file changed, 74 insertions(+), 62 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6c23c706b1..4bd65274fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12000,16 +12000,6 @@ css-select-base-adapter@^0.1.1: resolved "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== -css-select@^1.1.0: - version "1.2.0" - resolved "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" - integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= - dependencies: - boolbase "~1.0.0" - css-what "2.1" - domutils "1.5.1" - nth-check "~1.0.1" - css-select@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" @@ -12020,6 +12010,17 @@ css-select@^2.0.0: domutils "^1.7.0" nth-check "^1.0.2" +css-select@^4.1.3: + version "4.1.3" + resolved "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz#a70440f70317f2669118ad74ff105e65849c7067" + integrity sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA== + dependencies: + boolbase "^1.0.0" + css-what "^5.0.0" + domhandler "^4.2.0" + domutils "^2.6.0" + nth-check "^2.0.0" + css-tree@1.0.0-alpha.37: version "1.0.0-alpha.37" resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" @@ -12049,16 +12050,16 @@ css-vendor@^2.0.8: "@babel/runtime" "^7.8.3" is-in-browser "^1.0.2" -css-what@2.1: - version "2.1.3" - resolved "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" - integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== - css-what@^3.2.1: version "3.2.1" resolved "https://registry.npmjs.org/css-what/-/css-what-3.2.1.tgz#f4a8f12421064621b456755e34a03a2c22df5da1" integrity sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw== +css-what@^5.0.0: + version "5.1.0" + resolved "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe" + integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw== + css.escape@1.5.1, css.escape@^1.5.1: version "1.5.1" resolved "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" @@ -13016,7 +13017,7 @@ dom-accessibility-api@^0.5.4, dom-accessibility-api@^0.5.6: resolved "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.6.tgz#3f5d43b52c7a3bd68b5fb63fa47b4e4c1fdf65a9" integrity sha512-DplGLZd8L1lN64jlT27N9TVSESFR5STaEJvX+thCby7fuCHonfPpAlodYc3vuUYbDuDec5w8AMP7oCM5TWFsqw== -dom-converter@^0.2: +dom-converter@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== @@ -13065,7 +13066,7 @@ domain-browser@^1.1.1: resolved "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== -domelementtype@1, domelementtype@^1.3.1: +domelementtype@1: version "1.3.1" resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== @@ -13075,6 +13076,11 @@ domelementtype@^2.0.1, domelementtype@^2.1.0: resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== +domelementtype@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" + integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== + domexception@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" @@ -13082,13 +13088,6 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" -domhandler@^2.3.0: - version "2.4.2" - resolved "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" - integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== - dependencies: - domelementtype "1" - domhandler@^3.0, domhandler@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/domhandler/-/domhandler-3.0.0.tgz#51cd13efca31da95bbb0c5bee3a48300e333b3e9" @@ -13103,20 +13102,19 @@ domhandler@^4.0.0: dependencies: domelementtype "^2.1.0" +domhandler@^4.2.0: + version "4.2.2" + resolved "https://registry.npmjs.org/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f" + integrity sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w== + dependencies: + domelementtype "^2.2.0" + dompurify@^2.1.1, dompurify@^2.2.9: version "2.3.3" resolved "https://registry.npmjs.org/dompurify/-/dompurify-2.3.3.tgz#c1af3eb88be47324432964d8abc75cf4b98d634c" integrity sha512-dqnqRkPMAjOZE0FogZ+ceJNM2dZ3V/yNOuFB7+39qpO93hHhfRpHw3heYQC7DPK9FqbQTfBKUJhiSfz4MvXYwg== -domutils@1.5.1: - version "1.5.1" - resolved "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" - integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= - dependencies: - dom-serializer "0" - domelementtype "1" - -domutils@^1.5.1, domutils@^1.7.0: +domutils@^1.7.0: version "1.7.0" resolved "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== @@ -13133,6 +13131,15 @@ domutils@^2.0.0: domelementtype "^2.0.1" domhandler "^4.0.0" +domutils@^2.5.2, domutils@^2.6.0: + version "2.8.0" + resolved "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + dot-case@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" @@ -13411,7 +13418,7 @@ ent@^2.2.0: resolved "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= -entities@^1.1.1, entities@^1.1.2: +entities@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== @@ -16167,18 +16174,6 @@ html-webpack-plugin@^5.3.1: pretty-error "^2.1.1" tapable "^2.0.0" -htmlparser2@^3.3.0: - version "3.10.1" - resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" - integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== - dependencies: - domelementtype "^1.3.1" - domhandler "^2.3.0" - domutils "^1.5.1" - entities "^1.1.1" - inherits "^2.0.1" - readable-stream "^3.1.1" - htmlparser2@^4.0: version "4.1.0" resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz#9a4ef161f2e4625ebf7dfbe6c0a2f52d18a59e78" @@ -16189,6 +16184,16 @@ htmlparser2@^4.0: domutils "^2.0.0" entities "^2.0.0" +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" @@ -21312,13 +21317,20 @@ npm-run-path@^4.0.0, npm-run-path@^4.0.1: gauge "~2.7.3" set-blocking "~2.0.0" -nth-check@^1.0.2, nth-check@~1.0.1: +nth-check@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== dependencies: boolbase "~1.0.0" +nth-check@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2" + integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w== + dependencies: + boolbase "^1.0.0" + nullthrows@^1.0.0, nullthrows@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" @@ -23111,12 +23123,12 @@ pretty-bytes@^5.3.0, pretty-bytes@^5.6.0: integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== pretty-error@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3" - integrity sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM= + version "2.1.2" + resolved "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz#be89f82d81b1c86ec8fdfbc385045882727f93b6" + integrity sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw== dependencies: - renderkid "^2.0.1" - utila "~0.4" + lodash "^4.17.20" + renderkid "^2.0.4" pretty-format@^24.9.0: version "24.9.0" @@ -24705,16 +24717,16 @@ remove-trailing-spaces@^1.0.6: resolved "https://registry.npmjs.org/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz#4354d22f3236374702f58ee373168f6d6887ada7" integrity sha512-O3vsMYfWighyFbTd8hk8VaSj9UAGENxAtX+//ugIst2RMk5e03h6RoIS+0ylsFxY1gvmPuAY/PO4It+gPEeySA== -renderkid@^2.0.1: - version "2.0.3" - resolved "https://registry.npmjs.org/renderkid/-/renderkid-2.0.3.tgz#380179c2ff5ae1365c522bf2fcfcff01c5b74149" - integrity sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA== +renderkid@^2.0.4: + version "2.0.7" + resolved "https://registry.npmjs.org/renderkid/-/renderkid-2.0.7.tgz#464f276a6bdcee606f4a15993f9b29fc74ca8609" + integrity sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ== dependencies: - css-select "^1.1.0" - dom-converter "^0.2" - htmlparser2 "^3.3.0" - strip-ansi "^3.0.0" - utila "^0.4.0" + css-select "^4.1.3" + dom-converter "^0.2.0" + htmlparser2 "^6.1.0" + lodash "^4.17.21" + strip-ansi "^3.0.1" repeat-element@^1.1.2: version "1.1.3" @@ -28281,7 +28293,7 @@ util@^0.12.3: safe-buffer "^5.1.2" which-typed-array "^1.1.2" -utila@^0.4.0, utila@~0.4: +utila@~0.4: version "0.4.0" resolved "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= From ec786075419e200d7b8e37b7aeebc51919e8d184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 11 Nov 2021 14:34:42 +0100 Subject: [PATCH 157/196] bump prompts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- yarn.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6c23c706b1..ebd1998bae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23245,7 +23245,7 @@ promise@^7.1.1: dependencies: asap "~2.0.3" -prompts@2.4.0, prompts@^2.0.1: +prompts@2.4.0: version "2.4.0" resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz#4aa5de0723a231d1ee9121c40fdf663df73f61d7" integrity sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ== @@ -23253,10 +23253,10 @@ prompts@2.4.0, prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -prompts@^2.4.0: - version "2.4.1" - resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61" - integrity sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ== +prompts@^2.0.1, prompts@^2.4.0: + version "2.4.2" + resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== dependencies: kleur "^3.0.3" sisteransi "^1.0.5" From 014cbf8cb9f91bbf5961aca0a5b4839c806729dd Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 11 Oct 2021 22:10:47 +0200 Subject: [PATCH 158/196] core-app-api: deprecate dependency on core-components Signed-off-by: Patrik Oldsberg --- .changeset/giant-drinks-wave.md | 5 ++ .changeset/hot-walls-fail.md | 33 ++++++++ .changeset/late-rice-sit.md | 5 ++ .changeset/twenty-swans-matter.md | 16 ++++ packages/app/src/App.tsx | 2 + .../core-app-api/src/app/createApp.test.tsx | 28 +------ packages/core-app-api/src/app/createApp.tsx | 84 ++++++------------- packages/core-components/api-report.md | 4 + .../src/defaultAppComponents.test.tsx | 38 +++++++++ .../src/defaultAppComponents.tsx | 83 ++++++++++++++++++ packages/core-components/src/index.ts | 1 + .../default-app/packages/app/src/App.tsx | 3 +- packages/dev-utils/src/devApp/render.tsx | 2 + 13 files changed, 219 insertions(+), 85 deletions(-) create mode 100644 .changeset/giant-drinks-wave.md create mode 100644 .changeset/hot-walls-fail.md create mode 100644 .changeset/late-rice-sit.md create mode 100644 .changeset/twenty-swans-matter.md create mode 100644 packages/core-components/src/defaultAppComponents.test.tsx create mode 100644 packages/core-components/src/defaultAppComponents.tsx diff --git a/.changeset/giant-drinks-wave.md b/.changeset/giant-drinks-wave.md new file mode 100644 index 0000000000..ab9df136fc --- /dev/null +++ b/.changeset/giant-drinks-wave.md @@ -0,0 +1,5 @@ +--- +'@backstage/dev-utils': patch +--- + +Migrated to explicit passing of components to `createApp`. diff --git a/.changeset/hot-walls-fail.md b/.changeset/hot-walls-fail.md new file mode 100644 index 0000000000..055613934d --- /dev/null +++ b/.changeset/hot-walls-fail.md @@ -0,0 +1,33 @@ +--- +'@backstage/create-app': patch +--- + +Migrated the app template to pass on explicit `components` to the `createApp` options, as not doing this has been deprecated and will need to be done in the future. + +To migrate an existing application, make the following change to `packages/app/src/App.tsx`: + +```diff ++import { defaultAppComponents } from '@backstage/core-components'; + + // ... + + const app = createApp({ + apis, ++ components: defaultAppComponents(), + bindRoutes({ bind }) { +``` + +If you already supply custom app components, you can use the following: + +```diff + + // ... + + const app = createApp({ + apis, ++ components: { + ...defaultAppComponents(), ++ Progress: MyCustomProgressComponent, + }, + bindRoutes({ bind }) { +``` diff --git a/.changeset/late-rice-sit.md b/.changeset/late-rice-sit.md new file mode 100644 index 0000000000..eb074cbb18 --- /dev/null +++ b/.changeset/late-rice-sit.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Added a new `defaultAppComponents` method that creates a minimal set of components to pass on to `createApp` from `@backstage/core-app-api`. diff --git a/.changeset/twenty-swans-matter.md b/.changeset/twenty-swans-matter.md new file mode 100644 index 0000000000..0722913e05 --- /dev/null +++ b/.changeset/twenty-swans-matter.md @@ -0,0 +1,16 @@ +--- +'@backstage/core-app-api': patch +--- + +Deprecated the defaulting of the `components` options of `createApp`, meaning it will become required in the future. When not passing the required components options a deprecation warning is currently logged, and it will become required in a future release. + +The keep the existing components intact, migrate to using `defaultAppComponents` from `@backstage/core-components`: + +```ts +const app = createApp({ + components: { + ...defaultAppComponents(), + // Place any custom components here + }, +}); +``` diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 109ae7922e..f0b31fb4d0 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -31,6 +31,7 @@ import { AlertDisplay, OAuthRequestDialog, SignInPage, + defaultAppComponents, } from '@backstage/core-components'; import { apiDocsPlugin, ApiExplorerPage } from '@backstage/plugin-api-docs'; import { @@ -95,6 +96,7 @@ const app = createApp({ }, components: { + ...defaultAppComponents(), SignInPage: props => { return ( { }); }); -describe('OptionallyWrapInRouter', () => { - it('should wrap with router if not yet inside a router', async () => { - const { getByText } = render( - Test, - ); - - expect(getByText('Test')).toBeInTheDocument(); - }); - - it('should not wrap with router if already inside a router', async () => { - const { getByText } = render( - - Test - , - ); - - expect(getByText('Test')).toBeInTheDocument(); - }); -}); - describe('Optional ThemeProvider', () => { it('should render app with user-provided ThemeProvider', async () => { const components = { diff --git a/packages/core-app-api/src/app/createApp.tsx b/packages/core-app-api/src/app/createApp.tsx index 65aa15b50c..1e53103152 100644 --- a/packages/core-app-api/src/app/createApp.tsx +++ b/packages/core-app-api/src/app/createApp.tsx @@ -16,28 +16,25 @@ import { AppConfig } from '@backstage/config'; import { JsonObject } from '@backstage/types'; -import { Button } from '@material-ui/core'; -import { ErrorPage, ErrorPanel, Progress } from '@backstage/core-components'; +import { defaultAppComponents } from '@backstage/core-components'; import { darkTheme, lightTheme } from '@backstage/theme'; import DarkIcon from '@material-ui/icons/Brightness2'; import LightIcon from '@material-ui/icons/WbSunny'; -import React, { PropsWithChildren } from 'react'; -import { - BrowserRouter, - MemoryRouter, - useInRouterContext, -} from 'react-router-dom'; +import React from 'react'; +import { BrowserRouter } from 'react-router-dom'; import { PrivateAppImpl } from './App'; import { AppThemeProvider } from './AppThemeProvider'; import { defaultApis } from './defaultApis'; import { defaultAppIcons } from './icons'; -import { - AppConfigLoader, - AppOptions, - BootErrorPageProps, - ErrorBoundaryFallbackProps, -} from './types'; -import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { AppConfigLoader, AppOptions } from './types'; +import { AppComponents, BackstagePlugin } from '@backstage/core-plugin-api'; + +const REQUIRED_APP_COMPONENTS: Array = [ + 'Progress', + 'NotFoundErrorPage', + 'BootErrorPage', + 'ErrorBoundaryFallback', +]; /** * The default config loader, which expects that config is available at compile-time @@ -93,63 +90,34 @@ export const defaultConfigLoader: AppConfigLoader = async ( return configs; }; -export function OptionallyWrapInRouter({ children }: PropsWithChildren<{}>) { - if (useInRouterContext()) { - return <>{children}; - } - return {children}; -} - /** * Creates a new Backstage App. * * @public */ export function createApp(options?: AppOptions) { - const DefaultNotFoundPage = () => ( - + const missingRequiredComponents = REQUIRED_APP_COMPONENTS.filter( + name => !options?.components?.[name], ); - const DefaultBootErrorPage = ({ step, error }: BootErrorPageProps) => { - let message = ''; - if (step === 'load-config') { - message = `The configuration failed to load, someone should have a look at this error: ${error.message}`; - } else if (step === 'load-chunk') { - message = `Lazy loaded chunk failed to load, try to reload the page: ${error.message}`; - } - // TODO: figure out a nicer way to handle routing on the error page, when it can be done. - return ( - - - + if (missingRequiredComponents.length > 0) { + // eslint-disable-next-line no-console + console.warn( + 'DEPRECATION WARNING: The createApp options will soon require a minimal set of ' + + 'components to be provided in the components option. These components can be ' + + 'created using defaultAppComponents from @backstage/core-components and ' + + 'passed along like this: createApp({ components: defaultAppComponents() }). ' + + `The following components are missing: ${missingRequiredComponents.join( + ', ', + )}`, ); - }; - const DefaultErrorBoundaryFallback = ({ - error, - resetError, - plugin, - }: ErrorBoundaryFallbackProps) => { - return ( - - - - ); - }; + } const apis = options?.apis ?? []; const icons = { ...defaultAppIcons, ...options?.icons }; const plugins = options?.plugins ?? []; const components = { - NotFoundErrorPage: DefaultNotFoundPage, - BootErrorPage: DefaultBootErrorPage, - Progress: Progress, + ...defaultAppComponents(), Router: BrowserRouter, - ErrorBoundaryFallback: DefaultErrorBoundaryFallback, ThemeProvider: AppThemeProvider, ...options?.components, }; diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 0f1e3510ae..63f35873f7 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -6,6 +6,7 @@ /// import { ApiRef } from '@backstage/core-plugin-api'; +import { AppComponents } from '@backstage/core-plugin-api'; import { BackstageIdentityApi } from '@backstage/core-plugin-api'; import { BackstagePalette } from '@backstage/theme'; import { BackstageTheme } from '@backstage/theme'; @@ -199,6 +200,9 @@ export type CustomProviderClassKey = 'form' | 'button'; // @public (undocumented) export function DashboardIcon(props: IconComponentProps): JSX.Element; +// @public +export function defaultAppComponents(): Omit; + // @public type DependencyEdge = T & { from: string; diff --git a/packages/core-components/src/defaultAppComponents.test.tsx b/packages/core-components/src/defaultAppComponents.test.tsx new file mode 100644 index 0000000000..db0e2d50c1 --- /dev/null +++ b/packages/core-components/src/defaultAppComponents.test.tsx @@ -0,0 +1,38 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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 { render, screen } from '@testing-library/react'; +import React from 'react'; +import { MemoryRouter } from 'react-router-dom'; +import { OptionallyWrapInRouter } from './defaultAppComponents'; + +describe('OptionallyWrapInRouter', () => { + it('should wrap with router if not yet inside a router', async () => { + render(Test); + + expect(screen.getByText('Test')).toBeInTheDocument(); + }); + + it('should not wrap with router if already inside a router', async () => { + render( + + Test + , + ); + + expect(screen.getByText('Test')).toBeInTheDocument(); + }); +}); diff --git a/packages/core-components/src/defaultAppComponents.tsx b/packages/core-components/src/defaultAppComponents.tsx new file mode 100644 index 0000000000..fb810a6272 --- /dev/null +++ b/packages/core-components/src/defaultAppComponents.tsx @@ -0,0 +1,83 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { ReactNode } from 'react'; +import Button from '@material-ui/core/Button'; +import { + AppComponents, + BootErrorPageProps, + ErrorBoundaryFallbackProps, +} from '@backstage/core-plugin-api'; +import { ErrorPanel, Progress } from './components'; +import { ErrorPage } from './layout'; +import { MemoryRouter, useInRouterContext } from 'react-router'; + +export function OptionallyWrapInRouter({ children }: { children: ReactNode }) { + if (useInRouterContext()) { + return <>{children}; + } + return {children}; +} + +const DefaultNotFoundPage = () => ( + +); + +const DefaultBootErrorPage = ({ step, error }: BootErrorPageProps) => { + let message = ''; + if (step === 'load-config') { + message = `The configuration failed to load, someone should have a look at this error: ${error.message}`; + } else if (step === 'load-chunk') { + message = `Lazy loaded chunk failed to load, try to reload the page: ${error.message}`; + } + // TODO: figure out a nicer way to handle routing on the error page, when it can be done. + return ( + + + + ); +}; +const DefaultErrorBoundaryFallback = ({ + error, + resetError, + plugin, +}: ErrorBoundaryFallbackProps) => { + return ( + + + + ); +}; + +/** + * Creates a set of default components to pass along to {@link @backstage/core-app-api#createApp}. + * + * @public + */ +export function defaultAppComponents(): Omit { + return { + Progress, + NotFoundErrorPage: DefaultNotFoundPage, + BootErrorPage: DefaultBootErrorPage, + ErrorBoundaryFallback: DefaultErrorBoundaryFallback, + }; +} diff --git a/packages/core-components/src/index.ts b/packages/core-components/src/index.ts index 3c5e708360..9bc2436559 100644 --- a/packages/core-components/src/index.ts +++ b/packages/core-components/src/index.ts @@ -25,3 +25,4 @@ export * from './hooks'; export * from './icons'; export * from './layout'; export * from './overridableComponents'; +export { defaultAppComponents } from './defaultAppComponents'; diff --git a/packages/create-app/templates/default-app/packages/app/src/App.tsx b/packages/create-app/templates/default-app/packages/app/src/App.tsx index 4cd83685a6..7117b3ea07 100644 --- a/packages/create-app/templates/default-app/packages/app/src/App.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/App.tsx @@ -25,11 +25,12 @@ import { entityPage } from './components/catalog/EntityPage'; import { searchPage } from './components/search/SearchPage'; import { Root } from './components/Root'; -import { AlertDisplay, OAuthRequestDialog } from '@backstage/core-components'; +import { AlertDisplay, defaultAppComponents, OAuthRequestDialog } from '@backstage/core-components'; import { createApp, FlatRoutes } from '@backstage/core-app-api'; const app = createApp({ apis, + components: defaultAppComponents(), bindRoutes({ bind }) { bind(catalogPlugin.externalRoutes, { createComponent: scaffolderPlugin.routes.root, diff --git a/packages/dev-utils/src/devApp/render.tsx b/packages/dev-utils/src/devApp/render.tsx index 755289a060..31b6ac6169 100644 --- a/packages/dev-utils/src/devApp/render.tsx +++ b/packages/dev-utils/src/devApp/render.tsx @@ -27,6 +27,7 @@ import { Route } from 'react-router'; import { AlertDisplay, + defaultAppComponents, OAuthRequestDialog, Sidebar, SidebarItem, @@ -177,6 +178,7 @@ export class DevAppBuilder { apis, plugins: this.plugins, themes: this.themes, + components: defaultAppComponents(), bindRoutes: ({ bind }) => { for (const plugin of this.plugins ?? []) { const targets: Record> = {}; From cb77f4599a2f9433bf0e87cc4350d912913ba216 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 22 Oct 2021 13:59:41 +0200 Subject: [PATCH 159/196] core-app-api: deprecate not passing in icons + add defaultAppIcons() Co-authored-by: Johan Haals Signed-off-by: Patrik Oldsberg --- packages/app/src/App.tsx | 2 + packages/core-app-api/src/app/createApp.tsx | 24 ++++++-- packages/core-app-api/src/app/icons.tsx | 46 ++++++++-------- packages/core-app-api/src/app/index.ts | 1 + .../core-components/src/defaultAppIcons.tsx | 55 +++++++++++++++++++ packages/core-components/src/index.ts | 1 + 6 files changed, 102 insertions(+), 27 deletions(-) create mode 100644 packages/core-components/src/defaultAppIcons.tsx diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index f0b31fb4d0..c2273156ac 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -31,6 +31,7 @@ import { AlertDisplay, OAuthRequestDialog, SignInPage, + defaultAppIcons, defaultAppComponents, } from '@backstage/core-components'; import { apiDocsPlugin, ApiExplorerPage } from '@backstage/plugin-api-docs'; @@ -91,6 +92,7 @@ const app = createApp({ apis, plugins: Object.values(plugins), icons: { + ...defaultAppIcons(), // Custom icon example alert: AlarmIcon, }, diff --git a/packages/core-app-api/src/app/createApp.tsx b/packages/core-app-api/src/app/createApp.tsx index 1e53103152..7b93daf40f 100644 --- a/packages/core-app-api/src/app/createApp.tsx +++ b/packages/core-app-api/src/app/createApp.tsx @@ -16,7 +16,10 @@ import { AppConfig } from '@backstage/config'; import { JsonObject } from '@backstage/types'; -import { defaultAppComponents } from '@backstage/core-components'; +import { + defaultAppComponents, + defaultAppIcons, +} from '@backstage/core-components'; import { darkTheme, lightTheme } from '@backstage/theme'; import DarkIcon from '@material-ui/icons/Brightness2'; import LightIcon from '@material-ui/icons/WbSunny'; @@ -25,7 +28,6 @@ import { BrowserRouter } from 'react-router-dom'; import { PrivateAppImpl } from './App'; import { AppThemeProvider } from './AppThemeProvider'; import { defaultApis } from './defaultApis'; -import { defaultAppIcons } from './icons'; import { AppConfigLoader, AppOptions } from './types'; import { AppComponents, BackstagePlugin } from '@backstage/core-plugin-api'; @@ -112,8 +114,22 @@ export function createApp(options?: AppOptions) { ); } + const appIcons = defaultAppIcons(); + const providedIconKeys = Object.keys(options?.icons ?? {}); + const missingIconKeys = Object.keys(appIcons).filter( + key => !providedIconKeys.includes(key), + ); + if (missingIconKeys.length > 0) { + // eslint-disable-next-line no-console + console.warn( + 'DEPRECATION WARNING: The createApp options will soon require all app icons to be provided.' + + 'These icons can be created using defaultAppIcons from @backstage/core-components ' + + 'and then passed along like this: createApp({ icons: ...defaultAppIcons() })' + + `The following icons are missing: ${missingIconKeys.join(', ')}`, + ); + } + const apis = options?.apis ?? []; - const icons = { ...defaultAppIcons, ...options?.icons }; const plugins = options?.plugins ?? []; const components = { ...defaultAppComponents(), @@ -141,7 +157,7 @@ export function createApp(options?: AppOptions) { return new PrivateAppImpl({ apis, - icons, + icons: { ...appIcons, ...options?.icons }, plugins: plugins as BackstagePlugin[], components, themes, diff --git a/packages/core-app-api/src/app/icons.tsx b/packages/core-app-api/src/app/icons.tsx index b234a292b1..f06c9da989 100644 --- a/packages/core-app-api/src/app/icons.tsx +++ b/packages/core-app-api/src/app/icons.tsx @@ -35,30 +35,30 @@ import MuiPeopleIcon from '@material-ui/icons/People'; import MuiPersonIcon from '@material-ui/icons/Person'; import MuiWarningIcon from '@material-ui/icons/Warning'; -type AppIconsKey = - | 'brokenImage' - | 'catalog' - | 'scaffolder' - | 'techdocs' - | 'search' - | 'chat' - | 'dashboard' - | 'docs' - | 'email' - | 'github' - | 'group' - | 'help' - | 'kind:api' - | 'kind:component' - | 'kind:domain' - | 'kind:group' - | 'kind:location' - | 'kind:system' - | 'kind:user' - | 'user' - | 'warning'; +export type AppIcons = { + 'kind:api': IconComponent; + 'kind:component': IconComponent; + 'kind:domain': IconComponent; + 'kind:group': IconComponent; + 'kind:location': IconComponent; + 'kind:system': IconComponent; + 'kind:user': IconComponent; -export type AppIcons = { [key in AppIconsKey]: IconComponent }; + brokenImage: IconComponent; + catalog: IconComponent; + chat: IconComponent; + dashboard: IconComponent; + docs: IconComponent; + email: IconComponent; + github: IconComponent; + group: IconComponent; + help: IconComponent; + scaffolder: IconComponent; + search: IconComponent; + techdocs: IconComponent; + user: IconComponent; + warning: IconComponent; +}; export const defaultAppIcons: AppIcons = { brokenImage: MuiBrokenImageIcon, diff --git a/packages/core-app-api/src/app/index.ts b/packages/core-app-api/src/app/index.ts index ab82774cad..2a16a90bfa 100644 --- a/packages/core-app-api/src/app/index.ts +++ b/packages/core-app-api/src/app/index.ts @@ -15,4 +15,5 @@ */ export { createApp, defaultConfigLoader } from './createApp'; +export type { AppIcons } from './icons'; export * from './types'; diff --git a/packages/core-components/src/defaultAppIcons.tsx b/packages/core-components/src/defaultAppIcons.tsx new file mode 100644 index 0000000000..1d02dd5ad2 --- /dev/null +++ b/packages/core-components/src/defaultAppIcons.tsx @@ -0,0 +1,55 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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 { IconComponent } from '@backstage/core-plugin-api'; +import MuiApartmentIcon from '@material-ui/icons/Apartment'; +import MuiBrokenImageIcon from '@material-ui/icons/BrokenImage'; +import MuiCategoryIcon from '@material-ui/icons/Category'; +import MuiChatIcon from '@material-ui/icons/Chat'; +import MuiDashboardIcon from '@material-ui/icons/Dashboard'; +import MuiDocsIcon from '@material-ui/icons/Description'; +import MuiEmailIcon from '@material-ui/icons/Email'; +import MuiExtensionIcon from '@material-ui/icons/Extension'; +import MuiGitHubIcon from '@material-ui/icons/GitHub'; +import MuiHelpIcon from '@material-ui/icons/Help'; +import MuiLocationOnIcon from '@material-ui/icons/LocationOn'; +import MuiMemoryIcon from '@material-ui/icons/Memory'; +import MuiMenuBookIcon from '@material-ui/icons/MenuBook'; +import MuiPeopleIcon from '@material-ui/icons/People'; +import MuiPersonIcon from '@material-ui/icons/Person'; +import MuiWarningIcon from '@material-ui/icons/Warning'; + +export const defaultAppIcons = () => ({ + brokenImage: MuiBrokenImageIcon as IconComponent, + // To be confirmed: see https://github.com/backstage/backstage/issues/4970 + catalog: MuiMenuBookIcon as IconComponent, + chat: MuiChatIcon as IconComponent, + dashboard: MuiDashboardIcon as IconComponent, + docs: MuiDocsIcon as IconComponent, + email: MuiEmailIcon as IconComponent, + github: MuiGitHubIcon as IconComponent, + group: MuiPeopleIcon as IconComponent, + help: MuiHelpIcon as IconComponent, + 'kind:api': MuiExtensionIcon as IconComponent, + 'kind:component': MuiMemoryIcon as IconComponent, + 'kind:domain': MuiApartmentIcon as IconComponent, + 'kind:group': MuiPeopleIcon as IconComponent, + 'kind:location': MuiLocationOnIcon as IconComponent, + 'kind:system': MuiCategoryIcon as IconComponent, + 'kind:user': MuiPersonIcon as IconComponent, + user: MuiPersonIcon as IconComponent, + warning: MuiWarningIcon as IconComponent, +}); diff --git a/packages/core-components/src/index.ts b/packages/core-components/src/index.ts index 9bc2436559..3538cd4186 100644 --- a/packages/core-components/src/index.ts +++ b/packages/core-components/src/index.ts @@ -26,3 +26,4 @@ export * from './icons'; export * from './layout'; export * from './overridableComponents'; export { defaultAppComponents } from './defaultAppComponents'; +export { defaultAppIcons } from './defaultAppIcons'; From 9ddec7eaf14cfc77bc8244fd93023efecf25a86d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 22 Oct 2021 14:46:33 +0200 Subject: [PATCH 160/196] core-{app,plugin}-api: refactor AppTheme to use provider + defaultAppThemes Co-authored-by: Johan Haals Signed-off-by: Patrik Oldsberg --- packages/core-app-api/src/app/App.tsx | 4 +- .../core-app-api/src/app/AppThemeProvider.tsx | 11 ++++ packages/core-app-api/src/app/createApp.tsx | 32 ++++-------- packages/core-app-api/src/app/types.ts | 14 +++-- .../core-components/src/defaultAppThemes.tsx | 52 +++++++++++++++++++ packages/core-components/src/index.ts | 1 + .../src/apis/definitions/AppThemeApi.ts | 4 ++ 7 files changed, 92 insertions(+), 26 deletions(-) create mode 100644 packages/core-components/src/defaultAppThemes.tsx diff --git a/packages/core-app-api/src/app/App.tsx b/packages/core-app-api/src/app/App.tsx index 8cb3f167fb..6fadd22808 100644 --- a/packages/core-app-api/src/app/App.tsx +++ b/packages/core-app-api/src/app/App.tsx @@ -126,7 +126,7 @@ type FullAppOptions = { icons: NonNullable; plugins: BackstagePlugin[]; components: AppComponents; - themes: AppTheme[]; + themes: (Partial & Omit)[]; configLoader?: AppConfigLoader; defaultApis: Iterable; bindRoutes?: AppOptions['bindRoutes']; @@ -206,7 +206,7 @@ export class PrivateAppImpl implements BackstageApp { this.icons = options.icons; this.plugins = new Set(options.plugins); this.components = options.components; - this.themes = options.themes; + this.themes = options.themes as AppTheme[]; this.configLoader = options.configLoader; this.defaultApis = options.defaultApis; this.bindRoutes = options.bindRoutes; diff --git a/packages/core-app-api/src/app/AppThemeProvider.tsx b/packages/core-app-api/src/app/AppThemeProvider.tsx index 4e3a8487a6..aa48a2187d 100644 --- a/packages/core-app-api/src/app/AppThemeProvider.tsx +++ b/packages/core-app-api/src/app/AppThemeProvider.tsx @@ -90,6 +90,17 @@ export function AppThemeProvider({ children }: PropsWithChildren<{}>) { throw new Error('App has no themes'); } + if (appTheme.Provider) { + return ; + } + + // eslint-disable-next-line no-console + console.warn( + "DEPRECATION WARNING: A provided app theme is using the deprecated 'theme' property " + + 'and should be migrated to use a Provider instead. ' + + 'See https://backstage.io/docs/deprecations/TODO for more info.', + ); + return ( {children} diff --git a/packages/core-app-api/src/app/createApp.tsx b/packages/core-app-api/src/app/createApp.tsx index 7b93daf40f..f5989219e8 100644 --- a/packages/core-app-api/src/app/createApp.tsx +++ b/packages/core-app-api/src/app/createApp.tsx @@ -19,11 +19,8 @@ import { JsonObject } from '@backstage/types'; import { defaultAppComponents, defaultAppIcons, + defaultAppThemes, } from '@backstage/core-components'; -import { darkTheme, lightTheme } from '@backstage/theme'; -import DarkIcon from '@material-ui/icons/Brightness2'; -import LightIcon from '@material-ui/icons/WbSunny'; -import React from 'react'; import { BrowserRouter } from 'react-router-dom'; import { PrivateAppImpl } from './App'; import { AppThemeProvider } from './AppThemeProvider'; @@ -129,6 +126,15 @@ export function createApp(options?: AppOptions) { ); } + if (!options?.themes) { + // eslint-disable-next-line no-console + console.warn( + 'DEPRECATION WARNING: The createApp options will soon require themes to be provided. ' + + 'Themes can be created using defaultAppThemes from @backstage/core-components ' + + 'and then passed along like this: createApp({ theme: defaultAppThemes() })', + ); + } + const apis = options?.apis ?? []; const plugins = options?.plugins ?? []; const components = { @@ -137,22 +143,6 @@ export function createApp(options?: AppOptions) { ThemeProvider: AppThemeProvider, ...options?.components, }; - const themes = options?.themes ?? [ - { - id: 'light', - title: 'Light Theme', - variant: 'light', - theme: lightTheme, - icon: , - }, - { - id: 'dark', - title: 'Dark Theme', - variant: 'dark', - theme: darkTheme, - icon: , - }, - ]; const configLoader = options?.configLoader ?? defaultConfigLoader; return new PrivateAppImpl({ @@ -160,7 +150,7 @@ export function createApp(options?: AppOptions) { icons: { ...appIcons, ...options?.icons }, plugins: plugins as BackstagePlugin[], components, - themes, + themes: options?.themes ?? defaultAppThemes(), configLoader, defaultApis, bindRoutes: options?.bindRoutes, diff --git a/packages/core-app-api/src/app/types.ts b/packages/core-app-api/src/app/types.ts index 407018111b..78ef277c90 100644 --- a/packages/core-app-api/src/app/types.ts +++ b/packages/core-app-api/src/app/types.ts @@ -226,18 +226,26 @@ export type AppOptions = { * id: 'light', * title: 'Light Theme', * variant: 'light', - * theme: lightTheme, * icon: , + * Provider: ({ children }) => ( + * + * {children} + * + * ), * }, { * id: 'dark', * title: 'Dark Theme', * variant: 'dark', - * theme: darkTheme, * icon: , + * Provider: ({ children }) => ( + * + * {children} + * + * ), * }] * ``` */ - themes?: AppTheme[]; + themes?: (Partial & Omit)[]; /** * A function that loads in App configuration that will be accessible via diff --git a/packages/core-components/src/defaultAppThemes.tsx b/packages/core-components/src/defaultAppThemes.tsx new file mode 100644 index 0000000000..894716942b --- /dev/null +++ b/packages/core-components/src/defaultAppThemes.tsx @@ -0,0 +1,52 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { darkTheme, lightTheme } from '@backstage/theme'; +import DarkIcon from '@material-ui/icons/Brightness2'; +import LightIcon from '@material-ui/icons/WbSunny'; +import { ThemeProvider } from '@material-ui/core/styles'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import { AppTheme } from '@backstage/core-plugin-api'; + +export function defaultAppThemes(): AppTheme[] { + return [ + { + id: 'light', + title: 'Light Theme', + variant: 'light', + icon: , + theme: lightTheme, + Provider: ({ children }) => ( + + {children} + + ), + }, + { + id: 'dark', + title: 'Dark Theme', + variant: 'dark', + icon: , + theme: darkTheme, + Provider: ({ children }) => ( + + {children} + + ), + }, + ]; +} diff --git a/packages/core-components/src/index.ts b/packages/core-components/src/index.ts index 3538cd4186..305783dec8 100644 --- a/packages/core-components/src/index.ts +++ b/packages/core-components/src/index.ts @@ -27,3 +27,4 @@ export * from './layout'; export * from './overridableComponents'; export { defaultAppComponents } from './defaultAppComponents'; export { defaultAppIcons } from './defaultAppIcons'; +export { defaultAppThemes } from './defaultAppThemes'; diff --git a/packages/core-plugin-api/src/apis/definitions/AppThemeApi.ts b/packages/core-plugin-api/src/apis/definitions/AppThemeApi.ts index de523663f2..52cf202422 100644 --- a/packages/core-plugin-api/src/apis/definitions/AppThemeApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/AppThemeApi.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { ReactNode } from 'react'; import { ApiRef, createApiRef } from '../system'; import { BackstageTheme } from '@backstage/theme'; import { Observable } from '@backstage/types'; @@ -41,6 +42,7 @@ export type AppTheme = { /** * The specialized MaterialUI theme instance. + * @deprecated use Provider instead, see https://backstage.io/docs/deprecations/TODO */ theme: BackstageTheme; @@ -48,6 +50,8 @@ export type AppTheme = { * An Icon for the theme mode setting. */ icon?: React.ReactElement; + + Provider?(props: { children: ReactNode }): JSX.Element | null; }; /** From e551a44c65bb06a2e45c08706850925ba2e50de1 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 26 Oct 2021 19:08:50 +0200 Subject: [PATCH 161/196] core-components: provide withDefaults and work around type duplication Co-authored-by: Johan Haals Signed-off-by: Patrik Oldsberg --- packages/app/src/App.tsx | 82 ++++++------ packages/core-app-api/src/app/createApp.tsx | 40 +++--- .../src/appDefaults/AppThemeProvider.tsx | 109 ++++++++++++++++ .../defaultAppComponents.test.tsx | 0 .../defaultAppComponents.tsx | 12 +- .../src/{ => appDefaults}/defaultAppIcons.tsx | 0 .../{ => appDefaults}/defaultAppThemes.tsx | 0 .../core-components/src/appDefaults/index.ts | 17 +++ .../src/appDefaults/withDefaults.tsx | 64 ++++++++++ packages/core-components/src/index.ts | 4 +- packages/core-plugin-api/src/app/types.ts | 119 ++---------------- 11 files changed, 265 insertions(+), 182 deletions(-) create mode 100644 packages/core-components/src/appDefaults/AppThemeProvider.tsx rename packages/core-components/src/{ => appDefaults}/defaultAppComponents.test.tsx (100%) rename packages/core-components/src/{ => appDefaults}/defaultAppComponents.tsx (88%) rename packages/core-components/src/{ => appDefaults}/defaultAppIcons.tsx (100%) rename packages/core-components/src/{ => appDefaults}/defaultAppThemes.tsx (100%) create mode 100644 packages/core-components/src/appDefaults/index.ts create mode 100644 packages/core-components/src/appDefaults/withDefaults.tsx diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index c2273156ac..d3ab0649e9 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -31,8 +31,7 @@ import { AlertDisplay, OAuthRequestDialog, SignInPage, - defaultAppIcons, - defaultAppComponents, + withDefaults, } from '@backstage/core-components'; import { apiDocsPlugin, ApiExplorerPage } from '@backstage/plugin-api-docs'; import { @@ -88,47 +87,46 @@ import * as plugins from './plugins'; import { techDocsPage } from './components/techdocs/TechDocsPage'; -const app = createApp({ - apis, - plugins: Object.values(plugins), - icons: { - ...defaultAppIcons(), - // Custom icon example - alert: AlarmIcon, - }, - - components: { - ...defaultAppComponents(), - SignInPage: props => { - return ( - - ); +const app = createApp( + withDefaults({ + apis, + plugins: Object.values(plugins), + icons: { + // Custom icon example + alert: AlarmIcon, }, - }, - bindRoutes({ bind }) { - bind(catalogPlugin.externalRoutes, { - createComponent: scaffolderPlugin.routes.root, - viewTechDoc: techdocsPlugin.routes.docRoot, - }); - bind(catalogGraphPlugin.externalRoutes, { - catalogEntity: catalogPlugin.routes.catalogEntity, - }); - bind(apiDocsPlugin.externalRoutes, { - createComponent: scaffolderPlugin.routes.root, - }); - bind(explorePlugin.externalRoutes, { - catalogEntity: catalogPlugin.routes.catalogEntity, - }); - bind(scaffolderPlugin.externalRoutes, { - registerComponent: catalogImportPlugin.routes.importPage, - }); - }, -}); + components: { + SignInPage: props => { + return ( + + ); + }, + }, + bindRoutes({ bind }) { + bind(catalogPlugin.externalRoutes, { + createComponent: scaffolderPlugin.routes.root, + viewTechDoc: techdocsPlugin.routes.docRoot, + }); + bind(catalogGraphPlugin.externalRoutes, { + catalogEntity: catalogPlugin.routes.catalogEntity, + }); + bind(apiDocsPlugin.externalRoutes, { + createComponent: scaffolderPlugin.routes.root, + }); + bind(explorePlugin.externalRoutes, { + catalogEntity: catalogPlugin.routes.catalogEntity, + }); + bind(scaffolderPlugin.externalRoutes, { + registerComponent: catalogImportPlugin.routes.importPage, + }); + }, + }), +); const AppProvider = app.getProvider(); const AppRouter = app.getRouter(); diff --git a/packages/core-app-api/src/app/createApp.tsx b/packages/core-app-api/src/app/createApp.tsx index f5989219e8..c1e9741ff1 100644 --- a/packages/core-app-api/src/app/createApp.tsx +++ b/packages/core-app-api/src/app/createApp.tsx @@ -16,20 +16,15 @@ import { AppConfig } from '@backstage/config'; import { JsonObject } from '@backstage/types'; -import { - defaultAppComponents, - defaultAppIcons, - defaultAppThemes, -} from '@backstage/core-components'; -import { BrowserRouter } from 'react-router-dom'; +import { withDefaults } from '@backstage/core-components'; import { PrivateAppImpl } from './App'; -import { AppThemeProvider } from './AppThemeProvider'; +import { AppComponents, AppConfigLoader, AppOptions } from './types'; import { defaultApis } from './defaultApis'; -import { AppConfigLoader, AppOptions } from './types'; -import { AppComponents, BackstagePlugin } from '@backstage/core-plugin-api'; +import { BackstagePlugin } from '@backstage/core-plugin-api'; const REQUIRED_APP_COMPONENTS: Array = [ 'Progress', + 'Router', 'NotFoundErrorPage', 'BootErrorPage', 'ErrorBoundaryFallback', @@ -95,6 +90,8 @@ export const defaultConfigLoader: AppConfigLoader = async ( * @public */ export function createApp(options?: AppOptions) { + const optionsWithDefaults = withDefaults(options); + const missingRequiredComponents = REQUIRED_APP_COMPONENTS.filter( name => !options?.components?.[name], ); @@ -111,9 +108,8 @@ export function createApp(options?: AppOptions) { ); } - const appIcons = defaultAppIcons(); const providedIconKeys = Object.keys(options?.icons ?? {}); - const missingIconKeys = Object.keys(appIcons).filter( + const missingIconKeys = Object.keys(optionsWithDefaults.icons!).filter( key => !providedIconKeys.includes(key), ); if (missingIconKeys.length > 0) { @@ -135,24 +131,16 @@ export function createApp(options?: AppOptions) { ); } - const apis = options?.apis ?? []; - const plugins = options?.plugins ?? []; - const components = { - ...defaultAppComponents(), - Router: BrowserRouter, - ThemeProvider: AppThemeProvider, - ...options?.components, - }; - const configLoader = options?.configLoader ?? defaultConfigLoader; + const { icons, themes, components } = optionsWithDefaults; return new PrivateAppImpl({ - apis, - icons: { ...appIcons, ...options?.icons }, - plugins: plugins as BackstagePlugin[], - components, - themes: options?.themes ?? defaultAppThemes(), - configLoader, + icons: icons!, + themes: themes!, + components: components! as AppComponents, defaultApis, + apis: options?.apis ?? [], bindRoutes: options?.bindRoutes, + plugins: (options?.plugins as BackstagePlugin[]) ?? [], + configLoader: options?.configLoader ?? defaultConfigLoader, }); } diff --git a/packages/core-components/src/appDefaults/AppThemeProvider.tsx b/packages/core-components/src/appDefaults/AppThemeProvider.tsx new file mode 100644 index 0000000000..aa48a2187d --- /dev/null +++ b/packages/core-components/src/appDefaults/AppThemeProvider.tsx @@ -0,0 +1,109 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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, { useMemo, useEffect, useState, PropsWithChildren } from 'react'; +import { ThemeProvider } from '@material-ui/core/styles'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import { useApi, appThemeApiRef, AppTheme } from '@backstage/core-plugin-api'; +import { useObservable } from 'react-use'; + +// This tries to find the most accurate match, but also falls back to less +// accurate results in order to avoid errors. +function resolveTheme( + themeId: string | undefined, + shouldPreferDark: boolean, + themes: AppTheme[], +) { + if (themeId !== undefined) { + const selectedTheme = themes.find(theme => theme.id === themeId); + if (selectedTheme) { + return selectedTheme; + } + } + + if (shouldPreferDark) { + const darkTheme = themes.find(theme => theme.variant === 'dark'); + if (darkTheme) { + return darkTheme; + } + } + + const lightTheme = themes.find(theme => theme.variant === 'light'); + if (lightTheme) { + return lightTheme; + } + + return themes[0]; +} + +const useShouldPreferDarkTheme = () => { + const mediaQuery = useMemo( + () => window.matchMedia('(prefers-color-scheme: dark)'), + [], + ); + const [shouldPreferDark, setPrefersDark] = useState(mediaQuery.matches); + + useEffect(() => { + const listener = (event: MediaQueryListEvent) => { + setPrefersDark(event.matches); + }; + mediaQuery.addListener(listener); + return () => { + mediaQuery.removeListener(listener); + }; + }, [mediaQuery]); + + return shouldPreferDark; +}; + +export function AppThemeProvider({ children }: PropsWithChildren<{}>) { + const appThemeApi = useApi(appThemeApiRef); + const themeId = useObservable( + appThemeApi.activeThemeId$(), + appThemeApi.getActiveThemeId(), + ); + + // Browser feature detection won't change over time, so ignore lint rule + const shouldPreferDark = Boolean(window.matchMedia) + ? useShouldPreferDarkTheme() // eslint-disable-line react-hooks/rules-of-hooks + : false; + + const appTheme = resolveTheme( + themeId, + shouldPreferDark, + appThemeApi.getInstalledThemes(), + ); + if (!appTheme) { + throw new Error('App has no themes'); + } + + if (appTheme.Provider) { + return ; + } + + // eslint-disable-next-line no-console + console.warn( + "DEPRECATION WARNING: A provided app theme is using the deprecated 'theme' property " + + 'and should be migrated to use a Provider instead. ' + + 'See https://backstage.io/docs/deprecations/TODO for more info.', + ); + + return ( + + {children} + + ); +} diff --git a/packages/core-components/src/defaultAppComponents.test.tsx b/packages/core-components/src/appDefaults/defaultAppComponents.test.tsx similarity index 100% rename from packages/core-components/src/defaultAppComponents.test.tsx rename to packages/core-components/src/appDefaults/defaultAppComponents.test.tsx diff --git a/packages/core-components/src/defaultAppComponents.tsx b/packages/core-components/src/appDefaults/defaultAppComponents.tsx similarity index 88% rename from packages/core-components/src/defaultAppComponents.tsx rename to packages/core-components/src/appDefaults/defaultAppComponents.tsx index fb810a6272..ab55a25840 100644 --- a/packages/core-components/src/defaultAppComponents.tsx +++ b/packages/core-components/src/appDefaults/defaultAppComponents.tsx @@ -16,14 +16,16 @@ import React, { ReactNode } from 'react'; import Button from '@material-ui/core/Button'; +import { ErrorPanel, Progress } from '../components'; +import { ErrorPage } from '../layout'; +import { MemoryRouter, useInRouterContext } from 'react-router'; +import { BrowserRouter } from 'react-router-dom'; import { AppComponents, BootErrorPageProps, ErrorBoundaryFallbackProps, } from '@backstage/core-plugin-api'; -import { ErrorPanel, Progress } from './components'; -import { ErrorPage } from './layout'; -import { MemoryRouter, useInRouterContext } from 'react-router'; +import { AppThemeProvider } from './AppThemeProvider'; export function OptionallyWrapInRouter({ children }: { children: ReactNode }) { if (useInRouterContext()) { @@ -73,9 +75,11 @@ const DefaultErrorBoundaryFallback = ({ * * @public */ -export function defaultAppComponents(): Omit { +export function defaultAppComponents(): AppComponents { return { Progress, + Router: BrowserRouter, + ThemeProvider: AppThemeProvider, NotFoundErrorPage: DefaultNotFoundPage, BootErrorPage: DefaultBootErrorPage, ErrorBoundaryFallback: DefaultErrorBoundaryFallback, diff --git a/packages/core-components/src/defaultAppIcons.tsx b/packages/core-components/src/appDefaults/defaultAppIcons.tsx similarity index 100% rename from packages/core-components/src/defaultAppIcons.tsx rename to packages/core-components/src/appDefaults/defaultAppIcons.tsx diff --git a/packages/core-components/src/defaultAppThemes.tsx b/packages/core-components/src/appDefaults/defaultAppThemes.tsx similarity index 100% rename from packages/core-components/src/defaultAppThemes.tsx rename to packages/core-components/src/appDefaults/defaultAppThemes.tsx diff --git a/packages/core-components/src/appDefaults/index.ts b/packages/core-components/src/appDefaults/index.ts new file mode 100644 index 0000000000..287ad3cac6 --- /dev/null +++ b/packages/core-components/src/appDefaults/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { withDefaults } from './withDefaults'; diff --git a/packages/core-components/src/appDefaults/withDefaults.tsx b/packages/core-components/src/appDefaults/withDefaults.tsx new file mode 100644 index 0000000000..51adaf22ef --- /dev/null +++ b/packages/core-components/src/appDefaults/withDefaults.tsx @@ -0,0 +1,64 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AppTheme, IconComponent } from '@backstage/core-plugin-api'; +// This is a bit of a hack that we use to avoid having to redeclare these types +// within this package or have an explicit dependency on core-app-api. +// These types end up being inlined and duplicated into this package at build time. +// eslint-disable-next-line no-restricted-imports +import { + AppIcons, + AppComponents, + AppOptions, +} from '../../../core-app-api/src/app'; +import { defaultAppComponents } from './defaultAppComponents'; +import { defaultAppIcons } from './defaultAppIcons'; +import { defaultAppThemes } from './defaultAppThemes'; + +interface OptionalAppOptions { + icons?: Partial & { + [key in string]: IconComponent; + }; + themes?: (Partial & Omit)[]; + components?: Partial; +} + +/** + * The options required by {@link @backstage/core-app-api#createApp}, but with + * many of the fields being optional + * + * @public + */ +type DefaultAppOptions = Omit & + OptionalAppOptions; + +/** + * Provides a set of default App options with the ability to override specific options. + * + * These options populate the theme, icons and components options of {@link @backstage/core-app-api#AppOptions}. + * + * @public + */ +export function withDefaults(options?: DefaultAppOptions): AppOptions { + const { themes, icons, components } = options ?? {}; + + return { + ...options, + themes: themes ?? defaultAppThemes(), + icons: { ...defaultAppIcons(), ...icons }, + components: { ...defaultAppComponents(), ...components }, + }; +} diff --git a/packages/core-components/src/index.ts b/packages/core-components/src/index.ts index 305783dec8..ea1fea9d5c 100644 --- a/packages/core-components/src/index.ts +++ b/packages/core-components/src/index.ts @@ -25,6 +25,4 @@ export * from './hooks'; export * from './icons'; export * from './layout'; export * from './overridableComponents'; -export { defaultAppComponents } from './defaultAppComponents'; -export { defaultAppIcons } from './defaultAppIcons'; -export { defaultAppThemes } from './defaultAppThemes'; +export * from './appDefaults'; diff --git a/packages/core-plugin-api/src/app/types.ts b/packages/core-plugin-api/src/app/types.ts index d072f0a2e6..df78728a21 100644 --- a/packages/core-plugin-api/src/app/types.ts +++ b/packages/core-plugin-api/src/app/types.ts @@ -14,110 +14,15 @@ * limitations under the License. */ -import { ComponentType } from 'react'; -import { ProfileInfo } from '../apis/definitions'; -import { IconComponent } from '../icons'; -import { BackstagePlugin } from '../plugin/types'; - -/** - * Props for the BootErrorPage. - * - * @public - */ -export type BootErrorPageProps = { - step: 'load-config' | 'load-chunk'; - error: Error; -}; - -/** - * Data and handlers associated with the user sign in event. - * - * @public - */ -export type SignInResult = { - /** - * User ID that will be returned by the IdentityApi - */ - userId: string; - - profile: ProfileInfo; - - /** - * Function used to retrieve an ID token for the signed in user. - */ - getIdToken?: () => Promise; - - /** - * Sign out handler that will be called if the user requests to sign out. - */ - signOut?: () => Promise; -}; - -/** - * Props for the SignInPage. - * - * @public - */ -export type SignInPageProps = { - /** - * Set the sign-in result for the app. This should only be called once. - */ - onResult(result: SignInResult): void; -}; - -/** - * Props for the ErrorBoundaryFallback. - * - * @public - */ -export type ErrorBoundaryFallbackProps = { - plugin?: BackstagePlugin; - error: Error; - resetError: () => void; -}; - -/** - * Basic app components. - * - * @public - */ -export type AppComponents = { - NotFoundErrorPage: ComponentType<{}>; - BootErrorPage: ComponentType; - Progress: ComponentType<{}>; - Router: ComponentType<{}>; - ErrorBoundaryFallback: ComponentType; - - /** - * An optional sign-in page that will be rendered instead of the AppRouter at startup. - * - * If a sign-in page is set, it will always be shown before the app, and it is up - * to the sign-in page to handle e.g. saving of login methods for subsequent visits. - * - * The sign-in page will be displayed until it has passed up a result to the parent, - * and which point the AppRouter and all of its children will be rendered instead. - */ - SignInPage?: ComponentType; -}; - -/** - * Provides plugins and components registered in the app. - * - * @public - */ -export type AppContext = { - /** - * Get a list of all plugins that are installed in the app. - */ - getPlugins(): BackstagePlugin[]; - - /** - * Get a common or custom icon for this app. - */ - getSystemIcon(key: string): IconComponent | undefined; - - /** - * Get the components registered for various purposes in the app. - */ - getComponents(): AppComponents; -}; +// This is a bit of a hack that we use to avoid having to redeclare these types +// within this package or have an explicit dependency on core-app-api. +// These types end up being inlined and duplicated into this package at build time. +// eslint-disable-next-line no-restricted-imports +export type { + BootErrorPageProps, + SignInResult, + SignInPageProps, + ErrorBoundaryFallbackProps, + AppComponents, + AppContext, +} from '../../../core-app-api/src/app/types'; From 44976c138cc491d577386a551ccd58e747e112ab Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 26 Oct 2021 19:46:26 +0200 Subject: [PATCH 162/196] core-app-api, create-app, dev-utils: updates to use withDefaults Signed-off-by: Patrik Oldsberg --- packages/core-app-api/src/app/createApp.tsx | 19 +++++---- .../default-app/packages/app/src/App.tsx | 39 +++++++++++-------- packages/dev-utils/src/devApp/render.tsx | 31 ++++++++------- 3 files changed, 47 insertions(+), 42 deletions(-) diff --git a/packages/core-app-api/src/app/createApp.tsx b/packages/core-app-api/src/app/createApp.tsx index c1e9741ff1..c3f477035f 100644 --- a/packages/core-app-api/src/app/createApp.tsx +++ b/packages/core-app-api/src/app/createApp.tsx @@ -98,10 +98,9 @@ export function createApp(options?: AppOptions) { if (missingRequiredComponents.length > 0) { // eslint-disable-next-line no-console console.warn( - 'DEPRECATION WARNING: The createApp options will soon require a minimal set of ' + - 'components to be provided in the components option. These components can be ' + - 'created using defaultAppComponents from @backstage/core-components and ' + - 'passed along like this: createApp({ components: defaultAppComponents() }). ' + + 'DEPRECATION WARNING: The createApp options will soon require a minimal set of components to ' + + 'be provided. You can use the default components by using withDefaults from @backstage/core-components ' + + 'like this: createApp(withDefaults({ ... })), or you can provide the components yourself. ' + `The following components are missing: ${missingRequiredComponents.join( ', ', )}`, @@ -115,9 +114,9 @@ export function createApp(options?: AppOptions) { if (missingIconKeys.length > 0) { // eslint-disable-next-line no-console console.warn( - 'DEPRECATION WARNING: The createApp options will soon require all app icons to be provided.' + - 'These icons can be created using defaultAppIcons from @backstage/core-components ' + - 'and then passed along like this: createApp({ icons: ...defaultAppIcons() })' + + 'DEPRECATION WARNING: The createApp options will soon require a minimal set of icons to ' + + 'be provided. You can use the default icons by using withDefaults from @backstage/core-components ' + + 'like this: createApp(withDefaults({ ... })), or you can provide the icons yourself. ' + `The following icons are missing: ${missingIconKeys.join(', ')}`, ); } @@ -125,9 +124,9 @@ export function createApp(options?: AppOptions) { if (!options?.themes) { // eslint-disable-next-line no-console console.warn( - 'DEPRECATION WARNING: The createApp options will soon require themes to be provided. ' + - 'Themes can be created using defaultAppThemes from @backstage/core-components ' + - 'and then passed along like this: createApp({ theme: defaultAppThemes() })', + 'DEPRECATION WARNING: The createApp options will soon require the themes to be provided. ' + + 'You can use the default themes by using withDefaults from @backstage/core-components ' + + 'like this: createApp(withDefaults({ ... })), or you can provide the themes yourself. ', ); } diff --git a/packages/create-app/templates/default-app/packages/app/src/App.tsx b/packages/create-app/templates/default-app/packages/app/src/App.tsx index 7117b3ea07..a6d34e6687 100644 --- a/packages/create-app/templates/default-app/packages/app/src/App.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/App.tsx @@ -25,25 +25,30 @@ import { entityPage } from './components/catalog/EntityPage'; import { searchPage } from './components/search/SearchPage'; import { Root } from './components/Root'; -import { AlertDisplay, defaultAppComponents, OAuthRequestDialog } from '@backstage/core-components'; +import { + AlertDisplay, + withDefaults, + OAuthRequestDialog, +} from '@backstage/core-components'; import { createApp, FlatRoutes } from '@backstage/core-app-api'; -const app = createApp({ - apis, - components: defaultAppComponents(), - bindRoutes({ bind }) { - bind(catalogPlugin.externalRoutes, { - createComponent: scaffolderPlugin.routes.root, - viewTechDoc: techdocsPlugin.routes.docRoot, - }); - bind(apiDocsPlugin.externalRoutes, { - createComponent: scaffolderPlugin.routes.root, - }); - bind(scaffolderPlugin.externalRoutes, { - registerComponent: catalogImportPlugin.routes.importPage, - }); - }, -}); +const app = createApp( + withDefaults({ + apis, + bindRoutes({ bind }) { + bind(catalogPlugin.externalRoutes, { + createComponent: scaffolderPlugin.routes.root, + viewTechDoc: techdocsPlugin.routes.docRoot, + }); + bind(apiDocsPlugin.externalRoutes, { + createComponent: scaffolderPlugin.routes.root, + }); + bind(scaffolderPlugin.externalRoutes, { + registerComponent: catalogImportPlugin.routes.importPage, + }); + }, + }), +); const AppProvider = app.getProvider(); const AppRouter = app.getRouter(); diff --git a/packages/dev-utils/src/devApp/render.tsx b/packages/dev-utils/src/devApp/render.tsx index 31b6ac6169..bcaac891fe 100644 --- a/packages/dev-utils/src/devApp/render.tsx +++ b/packages/dev-utils/src/devApp/render.tsx @@ -27,12 +27,12 @@ import { Route } from 'react-router'; import { AlertDisplay, - defaultAppComponents, OAuthRequestDialog, Sidebar, SidebarItem, SidebarPage, SidebarSpacer, + withDefaults, } from '@backstage/core-components'; import { @@ -174,21 +174,22 @@ export class DevAppBuilder { ); } - const app = createApp({ - apis, - plugins: this.plugins, - themes: this.themes, - components: defaultAppComponents(), - bindRoutes: ({ bind }) => { - for (const plugin of this.plugins ?? []) { - const targets: Record> = {}; - for (const routeKey of Object.keys(plugin.externalRoutes)) { - targets[routeKey] = dummyRouteRef; + const app = createApp( + withDefaults({ + apis, + plugins: this.plugins, + themes: this.themes, + bindRoutes: ({ bind }) => { + for (const plugin of this.plugins ?? []) { + const targets: Record> = {}; + for (const routeKey of Object.keys(plugin.externalRoutes)) { + targets[routeKey] = dummyRouteRef; + } + bind(plugin.externalRoutes, targets); } - bind(plugin.externalRoutes, targets); - } - }, - }); + }, + }), + ); const AppProvider = app.getProvider(); const AppRouter = app.getRouter(); From 2db99234966ab3e7c964de3d8acb4f21241ed350 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 26 Oct 2021 19:45:52 +0200 Subject: [PATCH 163/196] changesets: updated changesets to use withDefaults Signed-off-by: Patrik Oldsberg --- .changeset/giant-drinks-wave.md | 2 +- .changeset/hot-walls-fail.md | 25 +++++++------------------ .changeset/late-rice-sit.md | 2 +- .changeset/twenty-swans-matter.md | 15 +++++++-------- 4 files changed, 16 insertions(+), 28 deletions(-) diff --git a/.changeset/giant-drinks-wave.md b/.changeset/giant-drinks-wave.md index ab9df136fc..4c2f754c2c 100644 --- a/.changeset/giant-drinks-wave.md +++ b/.changeset/giant-drinks-wave.md @@ -2,4 +2,4 @@ '@backstage/dev-utils': patch --- -Migrated to explicit passing of components to `createApp`. +Migrated to using `withDefaults` to pass defaults to `createApp`. diff --git a/.changeset/hot-walls-fail.md b/.changeset/hot-walls-fail.md index 055613934d..c814354a56 100644 --- a/.changeset/hot-walls-fail.md +++ b/.changeset/hot-walls-fail.md @@ -2,32 +2,21 @@ '@backstage/create-app': patch --- -Migrated the app template to pass on explicit `components` to the `createApp` options, as not doing this has been deprecated and will need to be done in the future. +Migrated the app template use the new `withDefaults` to construct the `createApp` options, as not doing this has been deprecated and will need to be done in the future. To migrate an existing application, make the following change to `packages/app/src/App.tsx`: ```diff -+import { defaultAppComponents } from '@backstage/core-components'; ++import { withDefaults } from '@backstage/core-components'; // ... - const app = createApp({ +-const app = createApp({ ++const app = createApp(withDefaults({ apis, -+ components: defaultAppComponents(), bindRoutes({ bind }) { -``` - -If you already supply custom app components, you can use the following: - -```diff - - // ... - - const app = createApp({ - apis, -+ components: { - ...defaultAppComponents(), -+ Progress: MyCustomProgressComponent, + ... }, - bindRoutes({ bind }) { +-}); ++})); ``` diff --git a/.changeset/late-rice-sit.md b/.changeset/late-rice-sit.md index eb074cbb18..778215038f 100644 --- a/.changeset/late-rice-sit.md +++ b/.changeset/late-rice-sit.md @@ -2,4 +2,4 @@ '@backstage/core-components': patch --- -Added a new `defaultAppComponents` method that creates a minimal set of components to pass on to `createApp` from `@backstage/core-app-api`. +Added a new `withDefaults` method that accepts a set of `AppOptions` and add the default components, themes and icons. It is intended to be used together with `createApp` from `@backstage/core-app-api` like this: `createApp(withDefaults({ ... }))`. diff --git a/.changeset/twenty-swans-matter.md b/.changeset/twenty-swans-matter.md index 0722913e05..6dea7a089f 100644 --- a/.changeset/twenty-swans-matter.md +++ b/.changeset/twenty-swans-matter.md @@ -2,15 +2,14 @@ '@backstage/core-app-api': patch --- -Deprecated the defaulting of the `components` options of `createApp`, meaning it will become required in the future. When not passing the required components options a deprecation warning is currently logged, and it will become required in a future release. +Deprecated the defaulting of the `components`, `icons` and `themes` options of `createApp`, meaning it will become required in the future. When not passing the required options a deprecation warning is currently logged, and they will become required in a future release. -The keep the existing components intact, migrate to using `defaultAppComponents` from `@backstage/core-components`: +The keep using the default set of options, migrate to using `withDefaults` from `@backstage/core-components`: ```ts -const app = createApp({ - components: { - ...defaultAppComponents(), - // Place any custom components here - }, -}); +const app = createApp( + withDefaults({ + // ... + }), +); ``` From ab73a5216796a7ee62ca8af078b8b79a34292208 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 26 Oct 2021 19:59:47 +0200 Subject: [PATCH 164/196] core-*: update API reports Signed-off-by: Patrik Oldsberg --- packages/core-app-api/api-report.md | 27 ++++++++++++++++++++++++-- packages/core-components/api-report.md | 20 +++++++++++++++---- packages/core-plugin-api/api-report.md | 13 +++++++++---- 3 files changed, 50 insertions(+), 10 deletions(-) diff --git a/packages/core-app-api/api-report.md b/packages/core-app-api/api-report.md index 8fb3d5c788..f517f284b4 100644 --- a/packages/core-app-api/api-report.md +++ b/packages/core-app-api/api-report.md @@ -164,6 +164,30 @@ export type AppContext = { getComponents(): AppComponents; }; +// Warning: (ae-missing-release-tag) "AppIcons" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type AppIcons = { + 'kind:api': IconComponent; + 'kind:component': IconComponent; + 'kind:domain': IconComponent; + 'kind:group': IconComponent; + 'kind:location': IconComponent; + 'kind:system': IconComponent; + 'kind:user': IconComponent; + brokenImage: IconComponent; + catalog: IconComponent; + chat: IconComponent; + dashboard: IconComponent; + docs: IconComponent; + email: IconComponent; + github: IconComponent; + group: IconComponent; + help: IconComponent; + user: IconComponent; + warning: IconComponent; +}; + // @public export type AppOptions = { apis?: Iterable; @@ -172,7 +196,7 @@ export type AppOptions = { }; plugins?: BackstagePluginWithAnyOutput[]; components?: Partial; - themes?: AppTheme[]; + themes?: (Partial & Omit)[]; configLoader?: AppConfigLoader; bindRoutes?(context: { bind: AppRouteBinder }): void; }; @@ -608,5 +632,4 @@ export class WebStorage implements StorageApi { // Warnings were encountered during analysis: // // src/apis/system/ApiProvider.d.ts:15:5 - (ae-forgotten-export) The symbol "ApiProviderProps" needs to be exported by the entry point index.d.ts -// src/app/types.d.ts:152:5 - (ae-forgotten-export) The symbol "AppIcons" needs to be exported by the entry point index.d.ts ``` diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 63f35873f7..b0c3e2d9b2 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -5,21 +5,26 @@ ```ts /// +import { AnyApiFactory } from '@backstage/core-plugin-api'; import { ApiRef } from '@backstage/core-plugin-api'; -import { AppComponents } from '@backstage/core-plugin-api'; +import { AppConfig } from '@backstage/config'; +import { AppTheme } from '@backstage/core-plugin-api'; import { BackstageIdentityApi } from '@backstage/core-plugin-api'; import { BackstagePalette } from '@backstage/theme'; +import { BackstagePlugin } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; import { ButtonProps as ButtonProps_2 } from '@material-ui/core/Button'; import { CardHeaderProps } from '@material-ui/core/CardHeader'; import { Column } from '@material-table/core'; import { ComponentClass } from 'react'; import { ComponentProps } from 'react'; +import { ComponentType } from 'react'; import { Context } from 'react'; import { default as CSS_2 } from 'csstype'; import { CSSProperties } from 'react'; import { ElementType } from 'react'; import { ErrorInfo } from 'react'; +import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { IconComponent } from '@backstage/core-plugin-api'; import { LinearProgressProps } from '@material-ui/core/LinearProgress'; import { LinkProps as LinkProps_2 } from '@material-ui/core/Link'; @@ -28,12 +33,15 @@ import MaterialBreadcrumbs from '@material-ui/core/Breadcrumbs'; import { MaterialTableProps } from '@material-table/core'; import { NavLinkProps } from 'react-router-dom'; import { Overrides } from '@material-ui/core/styles/overrides'; +import { PluginOutput } from '@backstage/core-plugin-api'; +import { ProfileInfo } from '@backstage/core-plugin-api'; import { ProfileInfoApi } from '@backstage/core-plugin-api'; import { PropsWithChildren } from 'react'; import { default as React_2 } from 'react'; import * as React_3 from 'react'; import { ReactElement } from 'react'; import { ReactNode } from 'react'; +import { RouteRef } from '@backstage/core-plugin-api'; import { SessionApi } from '@backstage/core-plugin-api'; import { SignInPageProps } from '@backstage/core-plugin-api'; import { SparklinesLineProps } from 'react-sparklines'; @@ -41,6 +49,7 @@ import { SparklinesProps } from 'react-sparklines'; import { StyledComponentProps } from '@material-ui/core/styles'; import { StyleRules } from '@material-ui/styles'; import { StyleRules as StyleRules_2 } from '@material-ui/core/styles/withStyles'; +import { SubRouteRef } from '@backstage/core-plugin-api'; import { TabProps } from '@material-ui/core/Tab'; import { TextTruncateProps } from 'react-text-truncate'; import { Theme } from '@material-ui/core/styles'; @@ -200,9 +209,6 @@ export type CustomProviderClassKey = 'form' | 'button'; // @public (undocumented) export function DashboardIcon(props: IconComponentProps): JSX.Element; -// @public -export function defaultAppComponents(): Omit; - // @public type DependencyEdge = T & { from: string; @@ -2322,6 +2328,12 @@ export type WarningPanelClassKey = | 'message' | 'details'; +// Warning: (ae-forgotten-export) The symbol "DefaultAppOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "AppOptions" needs to be exported by the entry point index.d.ts +// +// @public +export function withDefaults(options?: DefaultAppOptions): AppOptions; + // Warnings were encountered during analysis: // // src/components/DependencyGraph/types.d.ts:16:5 - (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/core-components" does not have an export "DependencyNode" diff --git a/packages/core-plugin-api/api-report.md b/packages/core-plugin-api/api-report.md index f0cee2a42c..433edc57ff 100644 --- a/packages/core-plugin-api/api-report.md +++ b/packages/core-plugin-api/api-report.md @@ -5,11 +5,14 @@ ```ts /// +import { BackstagePlugin as BackstagePlugin_2 } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; import { ComponentType } from 'react'; import { Config } from '@backstage/config'; +import { IconComponent as IconComponent_2 } from '@backstage/core-plugin-api'; import { Observable as Observable_2 } from '@backstage/types'; import { Observer as Observer_2 } from '@backstage/types'; +import { ProfileInfo as ProfileInfo_2 } from '@backstage/core-plugin-api'; import { default as React_2 } from 'react'; import { ReactElement } from 'react'; import { ReactNode } from 'react'; @@ -161,13 +164,14 @@ export type AppComponents = { Progress: ComponentType<{}>; Router: ComponentType<{}>; ErrorBoundaryFallback: ComponentType; + ThemeProvider: ComponentType<{}>; SignInPage?: ComponentType; }; // @public export type AppContext = { - getPlugins(): BackstagePlugin[]; - getSystemIcon(key: string): IconComponent | undefined; + getPlugins(): BackstagePlugin_2[]; + getSystemIcon(key: string): IconComponent_2 | undefined; getComponents(): AppComponents; }; @@ -178,6 +182,7 @@ export type AppTheme = { variant: 'light' | 'dark'; theme: BackstageTheme; icon?: React.ReactElement; + Provider?(props: { children: ReactNode }): JSX.Element | null; }; // @public @@ -421,7 +426,7 @@ export const errorApiRef: ApiRef; // @public export type ErrorBoundaryFallbackProps = { - plugin?: BackstagePlugin; + plugin?: BackstagePlugin_2; error: Error; resetError: () => void; }; @@ -741,7 +746,7 @@ export type SignInPageProps = { // @public export type SignInResult = { userId: string; - profile: ProfileInfo; + profile: ProfileInfo_2; getIdToken?: () => Promise; signOut?: () => Promise; }; From 7e0b26dec09f87b535406b9e99c8a082fc5b60b3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 26 Oct 2021 21:02:52 +0200 Subject: [PATCH 165/196] core-app-api: remove duplicate AppThemeProvider Signed-off-by: Patrik Oldsberg --- packages/core-app-api/src/app/App.test.tsx | 6 +- .../core-app-api/src/app/AppThemeProvider.tsx | 109 ------------------ 2 files changed, 3 insertions(+), 112 deletions(-) delete mode 100644 packages/core-app-api/src/app/AppThemeProvider.tsx diff --git a/packages/core-app-api/src/app/App.test.tsx b/packages/core-app-api/src/app/App.test.tsx index 6ed5d45b86..198048f8cf 100644 --- a/packages/core-app-api/src/app/App.test.tsx +++ b/packages/core-app-api/src/app/App.test.tsx @@ -38,7 +38,7 @@ import { analyticsApiRef, } from '@backstage/core-plugin-api'; import { generateBoundRoutes, PrivateAppImpl } from './App'; -import { AppThemeProvider } from './AppThemeProvider'; +import { AppComponents } from './types'; describe('generateBoundRoutes', () => { it('runs happy path', () => { @@ -178,13 +178,13 @@ describe('Integration Test', () => { }), ); - const components = { + const components: AppComponents = { NotFoundErrorPage: () => null, BootErrorPage: () => null, Progress: () => null, Router: BrowserRouter, ErrorBoundaryFallback: () => null, - ThemeProvider: AppThemeProvider, + ThemeProvider: ({ children }) => <>{children}, }; it('runs happy paths', async () => { diff --git a/packages/core-app-api/src/app/AppThemeProvider.tsx b/packages/core-app-api/src/app/AppThemeProvider.tsx deleted file mode 100644 index aa48a2187d..0000000000 --- a/packages/core-app-api/src/app/AppThemeProvider.tsx +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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, { useMemo, useEffect, useState, PropsWithChildren } from 'react'; -import { ThemeProvider } from '@material-ui/core/styles'; -import CssBaseline from '@material-ui/core/CssBaseline'; -import { useApi, appThemeApiRef, AppTheme } from '@backstage/core-plugin-api'; -import { useObservable } from 'react-use'; - -// This tries to find the most accurate match, but also falls back to less -// accurate results in order to avoid errors. -function resolveTheme( - themeId: string | undefined, - shouldPreferDark: boolean, - themes: AppTheme[], -) { - if (themeId !== undefined) { - const selectedTheme = themes.find(theme => theme.id === themeId); - if (selectedTheme) { - return selectedTheme; - } - } - - if (shouldPreferDark) { - const darkTheme = themes.find(theme => theme.variant === 'dark'); - if (darkTheme) { - return darkTheme; - } - } - - const lightTheme = themes.find(theme => theme.variant === 'light'); - if (lightTheme) { - return lightTheme; - } - - return themes[0]; -} - -const useShouldPreferDarkTheme = () => { - const mediaQuery = useMemo( - () => window.matchMedia('(prefers-color-scheme: dark)'), - [], - ); - const [shouldPreferDark, setPrefersDark] = useState(mediaQuery.matches); - - useEffect(() => { - const listener = (event: MediaQueryListEvent) => { - setPrefersDark(event.matches); - }; - mediaQuery.addListener(listener); - return () => { - mediaQuery.removeListener(listener); - }; - }, [mediaQuery]); - - return shouldPreferDark; -}; - -export function AppThemeProvider({ children }: PropsWithChildren<{}>) { - const appThemeApi = useApi(appThemeApiRef); - const themeId = useObservable( - appThemeApi.activeThemeId$(), - appThemeApi.getActiveThemeId(), - ); - - // Browser feature detection won't change over time, so ignore lint rule - const shouldPreferDark = Boolean(window.matchMedia) - ? useShouldPreferDarkTheme() // eslint-disable-line react-hooks/rules-of-hooks - : false; - - const appTheme = resolveTheme( - themeId, - shouldPreferDark, - appThemeApi.getInstalledThemes(), - ); - if (!appTheme) { - throw new Error('App has no themes'); - } - - if (appTheme.Provider) { - return ; - } - - // eslint-disable-next-line no-console - console.warn( - "DEPRECATION WARNING: A provided app theme is using the deprecated 'theme' property " + - 'and should be migrated to use a Provider instead. ' + - 'See https://backstage.io/docs/deprecations/TODO for more info.', - ); - - return ( - - {children} - - ); -} From 8921944da11d2f1d90c480c0e62a4d96455a6f1a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 26 Oct 2021 21:31:28 +0200 Subject: [PATCH 166/196] core-components: tweak exports and update api reports Signed-off-by: Patrik Oldsberg --- packages/core-app-api/api-report.md | 2 -- packages/core-app-api/src/app/icons.tsx | 1 + packages/core-components/api-report.md | 21 +++++++++++++-- .../core-components/src/appDefaults/index.ts | 1 + .../src/appDefaults/withDefaults.tsx | 26 ++++++++++--------- 5 files changed, 35 insertions(+), 16 deletions(-) diff --git a/packages/core-app-api/api-report.md b/packages/core-app-api/api-report.md index f517f284b4..b4b1b07ebe 100644 --- a/packages/core-app-api/api-report.md +++ b/packages/core-app-api/api-report.md @@ -164,8 +164,6 @@ export type AppContext = { getComponents(): AppComponents; }; -// Warning: (ae-missing-release-tag) "AppIcons" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export type AppIcons = { 'kind:api': IconComponent; diff --git a/packages/core-app-api/src/app/icons.tsx b/packages/core-app-api/src/app/icons.tsx index f06c9da989..1d9bb12923 100644 --- a/packages/core-app-api/src/app/icons.tsx +++ b/packages/core-app-api/src/app/icons.tsx @@ -35,6 +35,7 @@ import MuiPeopleIcon from '@material-ui/icons/People'; import MuiPersonIcon from '@material-ui/icons/Person'; import MuiWarningIcon from '@material-ui/icons/Warning'; +/** @public */ export type AppIcons = { 'kind:api': IconComponent; 'kind:component': IconComponent; diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index b0c3e2d9b2..f517b1673b 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -681,6 +681,22 @@ export type OAuthRequestDialogClassKey = // @public (undocumented) export type OpenedDropdownClassKey = 'icon'; +// @public +export interface OptionalAppOptions { + // Warning: (ae-forgotten-export) The symbol "AppComponents" needs to be exported by the entry point index.d.ts + // + // (undocumented) + components?: Partial; + // Warning: (ae-forgotten-export) The symbol "AppIcons" needs to be exported by the entry point index.d.ts + // + // (undocumented) + icons?: Partial & { + [key in string]: IconComponent; + }; + // (undocumented) + themes?: (Partial & Omit)[]; +} + // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "OverflowTooltip" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2328,11 +2344,12 @@ export type WarningPanelClassKey = | 'message' | 'details'; -// Warning: (ae-forgotten-export) The symbol "DefaultAppOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "AppOptions" needs to be exported by the entry point index.d.ts // // @public -export function withDefaults(options?: DefaultAppOptions): AppOptions; +export function withDefaults( + options?: Omit & OptionalAppOptions, +): AppOptions; // Warnings were encountered during analysis: // diff --git a/packages/core-components/src/appDefaults/index.ts b/packages/core-components/src/appDefaults/index.ts index 287ad3cac6..6ccc6a6257 100644 --- a/packages/core-components/src/appDefaults/index.ts +++ b/packages/core-components/src/appDefaults/index.ts @@ -15,3 +15,4 @@ */ export { withDefaults } from './withDefaults'; +export type { OptionalAppOptions } from './withDefaults'; diff --git a/packages/core-components/src/appDefaults/withDefaults.tsx b/packages/core-components/src/appDefaults/withDefaults.tsx index 51adaf22ef..9254053404 100644 --- a/packages/core-components/src/appDefaults/withDefaults.tsx +++ b/packages/core-components/src/appDefaults/withDefaults.tsx @@ -28,22 +28,22 @@ import { defaultAppComponents } from './defaultAppComponents'; import { defaultAppIcons } from './defaultAppIcons'; import { defaultAppThemes } from './defaultAppThemes'; -interface OptionalAppOptions { - icons?: Partial & { - [key in string]: IconComponent; - }; - themes?: (Partial & Omit)[]; - components?: Partial; -} +// NOTE: we don't re-export any of the types imported from core-app-api, as we +// want them to be imported from there rather than core-components. /** - * The options required by {@link @backstage/core-app-api#createApp}, but with - * many of the fields being optional + * The set of app options that will be populated by {@link withDefaults} if they + * are not passed in explicitly. * * @public */ -type DefaultAppOptions = Omit & - OptionalAppOptions; +export interface OptionalAppOptions { + icons?: Partial & { + [key in string]: IconComponent; + }; + themes?: (Partial & Omit)[]; // TODO: simplify once AppTheme is updated + components?: Partial; +} /** * Provides a set of default App options with the ability to override specific options. @@ -52,7 +52,9 @@ type DefaultAppOptions = Omit & * * @public */ -export function withDefaults(options?: DefaultAppOptions): AppOptions { +export function withDefaults( + options?: Omit & OptionalAppOptions, +): AppOptions { const { themes, icons, components } = options ?? {}; return { From a82aa54e272b03fb8b8d713909d5895f2b45e5f0 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 27 Oct 2021 19:10:40 +0200 Subject: [PATCH 167/196] packages: new app-defaults package + move over withDefaults Signed-off-by: Patrik Oldsberg --- packages/app-defaults/.eslintrc.js | 3 + packages/app-defaults/README.md | 17 + packages/app-defaults/package.json | 64 ++++ .../src/createApp}/AppThemeProvider.tsx | 0 .../src/createApp/createApp.test.tsx | 119 +++++++ .../app-defaults/src/createApp/createApp.tsx | 145 ++++++++ .../app-defaults/src/createApp/defaultApis.ts | 264 ++++++++++++++ .../createApp}/defaultAppComponents.test.tsx | 0 .../src/createApp}/defaultAppComponents.tsx | 0 .../src/createApp}/defaultAppIcons.tsx | 0 .../src/createApp}/defaultAppThemes.tsx | 0 packages/app-defaults/src/createApp/icons.tsx | 78 ++++ packages/app-defaults/src/createApp/index.ts | 19 + packages/app-defaults/src/createApp/types.ts | 332 ++++++++++++++++++ .../src/createApp}/withDefaults.tsx | 0 packages/app-defaults/src/index.ts | 23 ++ .../src/setupTests.ts} | 6 +- packages/core-components/src/index.ts | 1 - 18 files changed, 1067 insertions(+), 4 deletions(-) create mode 100644 packages/app-defaults/.eslintrc.js create mode 100644 packages/app-defaults/README.md create mode 100644 packages/app-defaults/package.json rename packages/{core-components/src/appDefaults => app-defaults/src/createApp}/AppThemeProvider.tsx (100%) create mode 100644 packages/app-defaults/src/createApp/createApp.test.tsx create mode 100644 packages/app-defaults/src/createApp/createApp.tsx create mode 100644 packages/app-defaults/src/createApp/defaultApis.ts rename packages/{core-components/src/appDefaults => app-defaults/src/createApp}/defaultAppComponents.test.tsx (100%) rename packages/{core-components/src/appDefaults => app-defaults/src/createApp}/defaultAppComponents.tsx (100%) rename packages/{core-components/src/appDefaults => app-defaults/src/createApp}/defaultAppIcons.tsx (100%) rename packages/{core-components/src/appDefaults => app-defaults/src/createApp}/defaultAppThemes.tsx (100%) create mode 100644 packages/app-defaults/src/createApp/icons.tsx create mode 100644 packages/app-defaults/src/createApp/index.ts create mode 100644 packages/app-defaults/src/createApp/types.ts rename packages/{core-components/src/appDefaults => app-defaults/src/createApp}/withDefaults.tsx (100%) create mode 100644 packages/app-defaults/src/index.ts rename packages/{core-components/src/appDefaults/index.ts => app-defaults/src/setupTests.ts} (79%) diff --git a/packages/app-defaults/.eslintrc.js b/packages/app-defaults/.eslintrc.js new file mode 100644 index 0000000000..13573efa9c --- /dev/null +++ b/packages/app-defaults/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint')], +}; diff --git a/packages/app-defaults/README.md b/packages/app-defaults/README.md new file mode 100644 index 0000000000..15a0bd3d24 --- /dev/null +++ b/packages/app-defaults/README.md @@ -0,0 +1,17 @@ +# @backstage/app-defaults + +This package provides a default wiring of a Backstage app that avoids boilerplate when setting up a standard Backstage app. + +## Installation + +Install the package via Yarn: + +```sh +cd packages/app +yarn add @backstage/app-defaults +``` + +## Documentation + +- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) +- [Backstage Documentation](https://backstage.io/docs) diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json new file mode 100644 index 0000000000..c7594470d6 --- /dev/null +++ b/packages/app-defaults/package.json @@ -0,0 +1,64 @@ +{ + "name": "@backstage/app-defaults", + "description": "Provides the default wiring of a Backstage App", + "version": "0.1.0", + "private": false, + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "packages/app-defaults" + }, + "keywords": [ + "backstage" + ], + "license": "Apache-2.0", + "main": "src/index.ts", + "types": "src/index.ts", + "scripts": { + "build": "backstage-cli build --outputs types,esm", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "dependencies": { + "@backstage/core-components": "^0.7.1", + "@backstage/config": "^0.1.10", + "@backstage/core-plugin-api": "^0.1.11", + "@backstage/theme": "^0.2.11", + "@backstage/types": "^0.1.1", + "@backstage/version-bridge": "^0.1.0", + "@material-ui/core": "^4.12.2", + "@material-ui/icons": "^4.9.1", + "@types/react": "*", + "@types/prop-types": "^15.7.3", + "prop-types": "^15.7.2", + "react": "^16.12.0", + "react-router-dom": "6.0.0-beta.0", + "react-use": "^17.2.4", + "zen-observable": "^0.8.15" + }, + "devDependencies": { + "@backstage/cli": "^0.8.0", + "@backstage/test-utils": "^0.1.19", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^11.2.5", + "@testing-library/react-hooks": "^7.0.2", + "@testing-library/user-event": "^13.1.8", + "@types/jest": "^26.0.7", + "@types/node": "^14.14.32", + "@types/zen-observable": "^0.8.0", + "cross-fetch": "^3.0.6", + "msw": "^0.29.0" + }, + "files": [ + "dist" + ] +} diff --git a/packages/core-components/src/appDefaults/AppThemeProvider.tsx b/packages/app-defaults/src/createApp/AppThemeProvider.tsx similarity index 100% rename from packages/core-components/src/appDefaults/AppThemeProvider.tsx rename to packages/app-defaults/src/createApp/AppThemeProvider.tsx diff --git a/packages/app-defaults/src/createApp/createApp.test.tsx b/packages/app-defaults/src/createApp/createApp.test.tsx new file mode 100644 index 0000000000..0bf90987db --- /dev/null +++ b/packages/app-defaults/src/createApp/createApp.test.tsx @@ -0,0 +1,119 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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 { screen } from '@testing-library/react'; +import { renderWithEffects } from '@backstage/test-utils'; +import React, { PropsWithChildren } from 'react'; +import { MemoryRouter } from 'react-router-dom'; +import { defaultConfigLoader, createApp } from './createApp'; + +(process as any).env = { NODE_ENV: 'test' }; +const anyEnv = process.env as any; +const anyWindow = window as any; + +describe('defaultConfigLoader', () => { + afterEach(() => { + delete anyEnv.APP_CONFIG; + delete anyWindow.__APP_CONFIG__; + }); + + it('loads static config', async () => { + anyEnv.APP_CONFIG = [ + { data: { my: 'config' }, context: 'a' }, + { data: { my: 'override-config' }, context: 'b' }, + ]; + + const configs = await defaultConfigLoader(); + expect(configs).toEqual([ + { data: { my: 'config' }, context: 'a' }, + { data: { my: 'override-config' }, context: 'b' }, + ]); + }); + + it('loads runtime config', async () => { + anyEnv.APP_CONFIG = [ + { data: { my: 'override-config' }, context: 'a' }, + { data: { my: 'config' }, context: 'b' }, + ]; + + const configs = await (defaultConfigLoader as any)( + '{"my":"runtime-config"}', + ); + expect(configs).toEqual([ + { data: { my: 'override-config' }, context: 'a' }, + { data: { my: 'config' }, context: 'b' }, + { data: { my: 'runtime-config' }, context: 'env' }, + ]); + }); + + it('fails to load invalid missing config', async () => { + await expect(defaultConfigLoader()).rejects.toThrow( + 'No static configuration provided', + ); + }); + + it('fails to load invalid static config', async () => { + anyEnv.APP_CONFIG = { my: 'invalid-config' }; + await expect(defaultConfigLoader()).rejects.toThrow( + 'Static configuration has invalid format', + ); + }); + + it('fails to load bad runtime config', async () => { + anyEnv.APP_CONFIG = [{ data: { my: 'config' }, context: 'a' }]; + + await expect((defaultConfigLoader as any)('}')).rejects.toThrow( + 'Failed to load runtime configuration, SyntaxError: Unexpected token } in JSON at position 0', + ); + }); + + it('loads config from window.__APP_CONFIG__', async () => { + anyEnv.APP_CONFIG = [ + { data: { my: 'config' }, context: 'a' }, + { data: { my: 'override-config' }, context: 'b' }, + ]; + const windowConfig = { app: { configKey: 'config-value' } }; + anyWindow.__APP_CONFIG__ = windowConfig; + + const configs = await defaultConfigLoader(); + + expect(configs).toEqual([ + ...anyEnv.APP_CONFIG, + { context: 'window', data: windowConfig }, + ]); + }); +}); + +describe('Optional ThemeProvider', () => { + it('should render app with user-provided ThemeProvider', async () => { + const components = { + NotFoundErrorPage: () => null, + BootErrorPage: () => null, + Progress: () => null, + Router: MemoryRouter, + ErrorBoundaryFallback: () => null, + ThemeProvider: ({ children }: PropsWithChildren<{}>) => ( +
{children}
+ ), + }; + + const App = createApp({ components }).getProvider(); + + await renderWithEffects(); + + expect(screen.getByRole('main')).toBeInTheDocument(); + }); +}); diff --git a/packages/app-defaults/src/createApp/createApp.tsx b/packages/app-defaults/src/createApp/createApp.tsx new file mode 100644 index 0000000000..c3f477035f --- /dev/null +++ b/packages/app-defaults/src/createApp/createApp.tsx @@ -0,0 +1,145 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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 { AppConfig } from '@backstage/config'; +import { JsonObject } from '@backstage/types'; +import { withDefaults } from '@backstage/core-components'; +import { PrivateAppImpl } from './App'; +import { AppComponents, AppConfigLoader, AppOptions } from './types'; +import { defaultApis } from './defaultApis'; +import { BackstagePlugin } from '@backstage/core-plugin-api'; + +const REQUIRED_APP_COMPONENTS: Array = [ + 'Progress', + 'Router', + 'NotFoundErrorPage', + 'BootErrorPage', + 'ErrorBoundaryFallback', +]; + +/** + * The default config loader, which expects that config is available at compile-time + * in `process.env.APP_CONFIG`. APP_CONFIG should be an array of config objects as + * returned by the config loader. + * + * It will also load runtime config from the __APP_INJECTED_RUNTIME_CONFIG__ string, + * which can be rewritten at runtime to contain an additional JSON config object. + * If runtime config is present, it will be placed first in the config array, overriding + * other config values. + * + * @public + */ +export const defaultConfigLoader: AppConfigLoader = async ( + // This string may be replaced at runtime to provide additional config. + // It should be replaced by a JSON-serialized config object. + // It's a param so we can test it, but at runtime this will always fall back to default. + runtimeConfigJson: string = '__APP_INJECTED_RUNTIME_CONFIG__', +) => { + const appConfig = process.env.APP_CONFIG; + if (!appConfig) { + throw new Error('No static configuration provided'); + } + if (!Array.isArray(appConfig)) { + throw new Error('Static configuration has invalid format'); + } + const configs = appConfig.slice() as unknown as AppConfig[]; + + // Avoiding this string also being replaced at runtime + if ( + runtimeConfigJson !== + '__app_injected_runtime_config__'.toLocaleUpperCase('en-US') + ) { + try { + const data = JSON.parse(runtimeConfigJson) as JsonObject; + if (Array.isArray(data)) { + configs.push(...data); + } else { + configs.push({ data, context: 'env' }); + } + } catch (error) { + throw new Error(`Failed to load runtime configuration, ${error}`); + } + } + + const windowAppConfig = (window as any).__APP_CONFIG__; + if (windowAppConfig) { + configs.push({ + context: 'window', + data: windowAppConfig, + }); + } + return configs; +}; + +/** + * Creates a new Backstage App. + * + * @public + */ +export function createApp(options?: AppOptions) { + const optionsWithDefaults = withDefaults(options); + + const missingRequiredComponents = REQUIRED_APP_COMPONENTS.filter( + name => !options?.components?.[name], + ); + if (missingRequiredComponents.length > 0) { + // eslint-disable-next-line no-console + console.warn( + 'DEPRECATION WARNING: The createApp options will soon require a minimal set of components to ' + + 'be provided. You can use the default components by using withDefaults from @backstage/core-components ' + + 'like this: createApp(withDefaults({ ... })), or you can provide the components yourself. ' + + `The following components are missing: ${missingRequiredComponents.join( + ', ', + )}`, + ); + } + + const providedIconKeys = Object.keys(options?.icons ?? {}); + const missingIconKeys = Object.keys(optionsWithDefaults.icons!).filter( + key => !providedIconKeys.includes(key), + ); + if (missingIconKeys.length > 0) { + // eslint-disable-next-line no-console + console.warn( + 'DEPRECATION WARNING: The createApp options will soon require a minimal set of icons to ' + + 'be provided. You can use the default icons by using withDefaults from @backstage/core-components ' + + 'like this: createApp(withDefaults({ ... })), or you can provide the icons yourself. ' + + `The following icons are missing: ${missingIconKeys.join(', ')}`, + ); + } + + if (!options?.themes) { + // eslint-disable-next-line no-console + console.warn( + 'DEPRECATION WARNING: The createApp options will soon require the themes to be provided. ' + + 'You can use the default themes by using withDefaults from @backstage/core-components ' + + 'like this: createApp(withDefaults({ ... })), or you can provide the themes yourself. ', + ); + } + + const { icons, themes, components } = optionsWithDefaults; + + return new PrivateAppImpl({ + icons: icons!, + themes: themes!, + components: components! as AppComponents, + defaultApis, + apis: options?.apis ?? [], + bindRoutes: options?.bindRoutes, + plugins: (options?.plugins as BackstagePlugin[]) ?? [], + configLoader: options?.configLoader ?? defaultConfigLoader, + }); +} diff --git a/packages/app-defaults/src/createApp/defaultApis.ts b/packages/app-defaults/src/createApp/defaultApis.ts new file mode 100644 index 0000000000..fb02274cf0 --- /dev/null +++ b/packages/app-defaults/src/createApp/defaultApis.ts @@ -0,0 +1,264 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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 { + AlertApiForwarder, + NoOpAnalyticsApi, + ErrorApiForwarder, + ErrorAlerter, + GoogleAuth, + GithubAuth, + OAuth2, + OktaAuth, + GitlabAuth, + Auth0Auth, + MicrosoftAuth, + BitbucketAuth, + OAuthRequestManager, + WebStorage, + UrlPatternDiscovery, + SamlAuth, + OneLoginAuth, + UnhandledErrorForwarder, + AtlassianAuth, +} from '../apis'; + +import { + createApiFactory, + alertApiRef, + analyticsApiRef, + errorApiRef, + discoveryApiRef, + oauthRequestApiRef, + googleAuthApiRef, + githubAuthApiRef, + oauth2ApiRef, + oktaAuthApiRef, + gitlabAuthApiRef, + auth0AuthApiRef, + microsoftAuthApiRef, + storageApiRef, + configApiRef, + samlAuthApiRef, + oneloginAuthApiRef, + oidcAuthApiRef, + bitbucketAuthApiRef, + atlassianAuthApiRef, +} from '@backstage/core-plugin-api'; + +import OAuth2Icon from '@material-ui/icons/AcUnit'; + +export const defaultApis = [ + createApiFactory({ + api: discoveryApiRef, + deps: { configApi: configApiRef }, + factory: ({ configApi }) => + UrlPatternDiscovery.compile( + `${configApi.getString('backend.baseUrl')}/api/{{ pluginId }}`, + ), + }), + createApiFactory(alertApiRef, new AlertApiForwarder()), + createApiFactory(analyticsApiRef, new NoOpAnalyticsApi()), + createApiFactory({ + api: errorApiRef, + deps: { alertApi: alertApiRef }, + factory: ({ alertApi }) => { + const errorApi = new ErrorAlerter(alertApi, new ErrorApiForwarder()); + UnhandledErrorForwarder.forward(errorApi, { hidden: false }); + return errorApi; + }, + }), + createApiFactory({ + api: storageApiRef, + deps: { errorApi: errorApiRef }, + factory: ({ errorApi }) => WebStorage.create({ errorApi }), + }), + createApiFactory(oauthRequestApiRef, new OAuthRequestManager()), + createApiFactory({ + api: googleAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + GoogleAuth.create({ + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: microsoftAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + MicrosoftAuth.create({ + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: githubAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + GithubAuth.create({ + discoveryApi, + oauthRequestApi, + defaultScopes: ['read:user'], + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: oktaAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OktaAuth.create({ + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: gitlabAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + GitlabAuth.create({ + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: auth0AuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + Auth0Auth.create({ + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: oauth2ApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OAuth2.create({ + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: samlAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, configApi }) => + SamlAuth.create({ + discoveryApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: oneloginAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OneLoginAuth.create({ + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: oidcAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OAuth2.create({ + discoveryApi, + oauthRequestApi, + provider: { + id: 'oidc', + title: 'Your Identity Provider', + icon: OAuth2Icon, + }, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: bitbucketAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + BitbucketAuth.create({ + discoveryApi, + oauthRequestApi, + defaultScopes: ['team'], + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: atlassianAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => { + return AtlassianAuth.create({ + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }); + }, + }), +]; diff --git a/packages/core-components/src/appDefaults/defaultAppComponents.test.tsx b/packages/app-defaults/src/createApp/defaultAppComponents.test.tsx similarity index 100% rename from packages/core-components/src/appDefaults/defaultAppComponents.test.tsx rename to packages/app-defaults/src/createApp/defaultAppComponents.test.tsx diff --git a/packages/core-components/src/appDefaults/defaultAppComponents.tsx b/packages/app-defaults/src/createApp/defaultAppComponents.tsx similarity index 100% rename from packages/core-components/src/appDefaults/defaultAppComponents.tsx rename to packages/app-defaults/src/createApp/defaultAppComponents.tsx diff --git a/packages/core-components/src/appDefaults/defaultAppIcons.tsx b/packages/app-defaults/src/createApp/defaultAppIcons.tsx similarity index 100% rename from packages/core-components/src/appDefaults/defaultAppIcons.tsx rename to packages/app-defaults/src/createApp/defaultAppIcons.tsx diff --git a/packages/core-components/src/appDefaults/defaultAppThemes.tsx b/packages/app-defaults/src/createApp/defaultAppThemes.tsx similarity index 100% rename from packages/core-components/src/appDefaults/defaultAppThemes.tsx rename to packages/app-defaults/src/createApp/defaultAppThemes.tsx diff --git a/packages/app-defaults/src/createApp/icons.tsx b/packages/app-defaults/src/createApp/icons.tsx new file mode 100644 index 0000000000..df1e69da6a --- /dev/null +++ b/packages/app-defaults/src/createApp/icons.tsx @@ -0,0 +1,78 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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 { IconComponent } from '@backstage/core-plugin-api'; +import MuiApartmentIcon from '@material-ui/icons/Apartment'; +import MuiBrokenImageIcon from '@material-ui/icons/BrokenImage'; +import MuiCategoryIcon from '@material-ui/icons/Category'; +import MuiChatIcon from '@material-ui/icons/Chat'; +import MuiDashboardIcon from '@material-ui/icons/Dashboard'; +import MuiDocsIcon from '@material-ui/icons/Description'; +import MuiEmailIcon from '@material-ui/icons/Email'; +import MuiExtensionIcon from '@material-ui/icons/Extension'; +import MuiGitHubIcon from '@material-ui/icons/GitHub'; +import MuiHelpIcon from '@material-ui/icons/Help'; +import MuiLocationOnIcon from '@material-ui/icons/LocationOn'; +import MuiMemoryIcon from '@material-ui/icons/Memory'; +import MuiMenuBookIcon from '@material-ui/icons/MenuBook'; +import MuiPeopleIcon from '@material-ui/icons/People'; +import MuiPersonIcon from '@material-ui/icons/Person'; +import MuiWarningIcon from '@material-ui/icons/Warning'; + +/** @public */ +export type AppIcons = { + 'kind:api': IconComponent; + 'kind:component': IconComponent; + 'kind:domain': IconComponent; + 'kind:group': IconComponent; + 'kind:location': IconComponent; + 'kind:system': IconComponent; + 'kind:user': IconComponent; + + brokenImage: IconComponent; + catalog: IconComponent; + chat: IconComponent; + dashboard: IconComponent; + docs: IconComponent; + email: IconComponent; + github: IconComponent; + group: IconComponent; + help: IconComponent; + user: IconComponent; + warning: IconComponent; +}; + +export const defaultAppIcons: AppIcons = { + brokenImage: MuiBrokenImageIcon, + // To be confirmed: see https://github.com/backstage/backstage/issues/4970 + catalog: MuiMenuBookIcon, + chat: MuiChatIcon, + dashboard: MuiDashboardIcon, + docs: MuiDocsIcon, + email: MuiEmailIcon, + github: MuiGitHubIcon, + group: MuiPeopleIcon, + help: MuiHelpIcon, + 'kind:api': MuiExtensionIcon, + 'kind:component': MuiMemoryIcon, + 'kind:domain': MuiApartmentIcon, + 'kind:group': MuiPeopleIcon, + 'kind:location': MuiLocationOnIcon, + 'kind:system': MuiCategoryIcon, + 'kind:user': MuiPersonIcon, + user: MuiPersonIcon, + warning: MuiWarningIcon, +}; diff --git a/packages/app-defaults/src/createApp/index.ts b/packages/app-defaults/src/createApp/index.ts new file mode 100644 index 0000000000..2a16a90bfa --- /dev/null +++ b/packages/app-defaults/src/createApp/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { createApp, defaultConfigLoader } from './createApp'; +export type { AppIcons } from './icons'; +export * from './types'; diff --git a/packages/app-defaults/src/createApp/types.ts b/packages/app-defaults/src/createApp/types.ts new file mode 100644 index 0000000000..78ef277c90 --- /dev/null +++ b/packages/app-defaults/src/createApp/types.ts @@ -0,0 +1,332 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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 { ComponentType } from 'react'; +import { + AnyApiFactory, + AppTheme, + ProfileInfo, + IconComponent, + BackstagePlugin, + RouteRef, + SubRouteRef, + ExternalRouteRef, + PluginOutput, +} from '@backstage/core-plugin-api'; +import { AppConfig } from '@backstage/config'; +import { AppIcons } from './icons'; + +/** + * Props for the `BootErrorPage` component of {@link AppComponents}. + * + * @public + */ +export type BootErrorPageProps = { + step: 'load-config' | 'load-chunk'; + error: Error; +}; + +/** + * The outcome of signing in on the sign-in page. + * + * @public + */ +export type SignInResult = { + /** + * User ID that will be returned by the IdentityApi + */ + userId: string; + + profile: ProfileInfo; + + /** + * Function used to retrieve an ID token for the signed in user. + */ + getIdToken?: () => Promise; + + /** + * Sign out handler that will be called if the user requests to sign out. + */ + signOut?: () => Promise; +}; + +/** + * Props for the `SignInPage` component of {@link AppComponents}. + * + * @public + */ +export type SignInPageProps = { + /** + * Set the sign-in result for the app. This should only be called once. + */ + onResult(result: SignInResult): void; +}; + +/** + * Props for the fallback error boundary. + * + * @public + */ +export type ErrorBoundaryFallbackProps = { + plugin?: BackstagePlugin; + error: Error; + resetError: () => void; +}; + +/** + * A set of replaceable core components that are part of every Backstage app. + * + * @public + */ +export type AppComponents = { + NotFoundErrorPage: ComponentType<{}>; + BootErrorPage: ComponentType; + Progress: ComponentType<{}>; + Router: ComponentType<{}>; + ErrorBoundaryFallback: ComponentType; + ThemeProvider: ComponentType<{}>; + + /** + * An optional sign-in page that will be rendered instead of the AppRouter at startup. + * + * If a sign-in page is set, it will always be shown before the app, and it is up + * to the sign-in page to handle e.g. saving of login methods for subsequent visits. + * + * The sign-in page will be displayed until it has passed up a result to the parent, + * and which point the AppRouter and all of its children will be rendered instead. + */ + SignInPage?: ComponentType; +}; + +/** + * A function that loads in the App config that will be accessible via the ConfigApi. + * + * If multiple config objects are returned in the array, values in the earlier configs + * will override later ones. + * + * @public + */ +export type AppConfigLoader = () => Promise; + +/** + * Extracts a union of the keys in a map whose value extends the given type + */ +type KeysWithType = { + [key in keyof Obj]: Obj[key] extends Type ? key : never; +}[keyof Obj]; + +/** + * Takes a map Map required values and makes all keys matching Keys optional + */ +type PartialKeys< + Map extends { [name in string]: any }, + Keys extends keyof Map, +> = Partial> & Required>; + +/** + * Creates a map of target routes with matching parameters based on a map of external routes. + */ +type TargetRouteMap< + ExternalRoutes extends { [name: string]: ExternalRouteRef }, +> = { + [name in keyof ExternalRoutes]: ExternalRoutes[name] extends ExternalRouteRef< + infer Params, + any + > + ? RouteRef | SubRouteRef + : never; +}; + +/** + * A function that can bind from external routes of a given plugin, to concrete + * routes of other plugins. See {@link createApp}. + * + * @public + */ +export type AppRouteBinder = < + ExternalRoutes extends { [name: string]: ExternalRouteRef }, +>( + externalRoutes: ExternalRoutes, + targetRoutes: PartialKeys< + TargetRouteMap, + KeysWithType> + >, +) => void; + +/** + * Internal helper type that represents a plugin with any type of output. + * + * @public + * @remarks + * + * The `type: string` type is there to handle output from newer or older plugin + * API versions that might not be supported by this version of the app API, but + * we don't want to break at the type checking level. We only use this more + * permissive type for the `createApp` options, as we otherwise want to stick + * to using the type for the outputs that we know about in this version of the + * app api. + * + * TODO(freben): This should be marked internal but that's not supported by the api report generation tools yet + */ +export type BackstagePluginWithAnyOutput = Omit< + BackstagePlugin, + 'output' +> & { + output(): (PluginOutput | { type: string })[]; +}; + +/** + * The options accepted by {@link createApp}. + * + * @public + */ +export type AppOptions = { + /** + * A collection of ApiFactories to register in the application to either + * add add new ones, or override factories provided by default or by plugins. + */ + apis?: Iterable; + + /** + * Supply icons to override the default ones. + */ + icons?: Partial & { [key in string]: IconComponent }; + + /** + * A list of all plugins to include in the app. + */ + plugins?: BackstagePluginWithAnyOutput[]; + + /** + * Supply components to the app to override the default ones. + */ + components?: Partial; + + /** + * Themes provided as a part of the app. By default two themes are included, one + * light variant of the default backstage theme, and one dark. + * + * This is the default config: + * + * ``` + * [{ + * id: 'light', + * title: 'Light Theme', + * variant: 'light', + * icon: , + * Provider: ({ children }) => ( + * + * {children} + * + * ), + * }, { + * id: 'dark', + * title: 'Dark Theme', + * variant: 'dark', + * icon: , + * Provider: ({ children }) => ( + * + * {children} + * + * ), + * }] + * ``` + */ + themes?: (Partial & Omit)[]; + + /** + * A function that loads in App configuration that will be accessible via + * the ConfigApi. + * + * Defaults to an empty config. + * + * TODO(Rugvip): Omitting this should instead default to loading in configuration + * that was packaged by the backstage-cli and default docker container boot script. + */ + configLoader?: AppConfigLoader; + + /** + * A function that is used to register associations between cross-plugin route + * references, enabling plugins to navigate between each other. + * + * The `bind` function that is passed in should be used to bind all external + * routes of all used plugins. + * + * ```ts + * bindRoutes({ bind }) { + * bind(docsPlugin.externalRoutes, { + * homePage: managePlugin.routes.managePage, + * }) + * bind(homePagePlugin.externalRoutes, { + * settingsPage: settingsPlugin.routes.settingsPage, + * }) + * } + * ``` + */ + bindRoutes?(context: { bind: AppRouteBinder }): void; +}; + +/** + * The public API of the output of {@link createApp}. + * + * @public + */ +export type BackstageApp = { + /** + * Returns all plugins registered for the app. + */ + getPlugins(): BackstagePlugin[]; + + /** + * Get a common or custom icon for this app. + */ + getSystemIcon(key: string): IconComponent | undefined; + + /** + * Provider component that should wrap the Router created with getRouter() + * and any other components that need to be within the app context. + */ + getProvider(): ComponentType<{}>; + + /** + * Router component that should wrap the App Routes create with getRoutes() + * and any other components that should only be available while signed in. + */ + getRouter(): ComponentType<{}>; +}; + +/** + * The central context providing runtime app specific state that plugin views + * want to consume. + * + * @public + */ +export type AppContext = { + /** + * Get a list of all plugins that are installed in the app. + */ + getPlugins(): BackstagePlugin[]; + + /** + * Get a common or custom icon for this app. + */ + getSystemIcon(key: string): IconComponent | undefined; + + /** + * Get the components registered for various purposes in the app. + */ + getComponents(): AppComponents; +}; diff --git a/packages/core-components/src/appDefaults/withDefaults.tsx b/packages/app-defaults/src/createApp/withDefaults.tsx similarity index 100% rename from packages/core-components/src/appDefaults/withDefaults.tsx rename to packages/app-defaults/src/createApp/withDefaults.tsx diff --git a/packages/app-defaults/src/index.ts b/packages/app-defaults/src/index.ts new file mode 100644 index 0000000000..62ef4d9077 --- /dev/null +++ b/packages/app-defaults/src/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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. + */ + +/** + * Provides the default wiring of a Backstage App + * + * @packageDocumentation + */ + +export * from './createApp'; diff --git a/packages/core-components/src/appDefaults/index.ts b/packages/app-defaults/src/setupTests.ts similarity index 79% rename from packages/core-components/src/appDefaults/index.ts rename to packages/app-defaults/src/setupTests.ts index 6ccc6a6257..c1d649f2ad 100644 --- a/packages/core-components/src/appDefaults/index.ts +++ b/packages/app-defaults/src/setupTests.ts @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * Copyright 2020 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,5 +14,5 @@ * limitations under the License. */ -export { withDefaults } from './withDefaults'; -export type { OptionalAppOptions } from './withDefaults'; +import '@testing-library/jest-dom'; +import 'cross-fetch/polyfill'; diff --git a/packages/core-components/src/index.ts b/packages/core-components/src/index.ts index ea1fea9d5c..3c5e708360 100644 --- a/packages/core-components/src/index.ts +++ b/packages/core-components/src/index.ts @@ -25,4 +25,3 @@ export * from './hooks'; export * from './icons'; export * from './layout'; export * from './overridableComponents'; -export * from './appDefaults'; From 60d1b30c215d6a0e7843447b7833229d7a7769e2 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 27 Oct 2021 19:35:39 +0200 Subject: [PATCH 168/196] app-defaults: refactor things into folders and move things closer to home Signed-off-by: Patrik Oldsberg --- packages/app-defaults/package.json | 1 + .../{ => components}/AppThemeProvider.tsx | 0 .../src/createApp/createApp.test.tsx | 79 +---- .../app-defaults/src/createApp/createApp.tsx | 183 ++++------ .../{defaultApis.ts => defaults/apis.ts} | 4 +- .../components.test.tsx} | 2 +- .../components.tsx} | 31 +- .../createApp/defaults/configLoader.test.ts | 92 +++++ .../src/createApp/defaults/configLoader.ts | 73 ++++ .../icons.tsx} | 2 +- .../src/createApp/defaults/index.ts | 21 ++ .../themes.tsx} | 54 ++- packages/app-defaults/src/createApp/icons.tsx | 78 ---- packages/app-defaults/src/createApp/index.ts | 4 +- packages/app-defaults/src/createApp/types.ts | 332 ------------------ .../src/createApp/withDefaults.tsx | 66 ---- 16 files changed, 301 insertions(+), 721 deletions(-) rename packages/app-defaults/src/createApp/{ => components}/AppThemeProvider.tsx (100%) rename packages/app-defaults/src/createApp/{defaultApis.ts => defaults/apis.ts} (99%) rename packages/app-defaults/src/createApp/{defaultAppComponents.test.tsx => defaults/components.test.tsx} (95%) rename packages/app-defaults/src/createApp/{defaultAppComponents.tsx => defaults/components.tsx} (79%) create mode 100644 packages/app-defaults/src/createApp/defaults/configLoader.test.ts create mode 100644 packages/app-defaults/src/createApp/defaults/configLoader.ts rename packages/app-defaults/src/createApp/{defaultAppIcons.tsx => defaults/icons.tsx} (98%) create mode 100644 packages/app-defaults/src/createApp/defaults/index.ts rename packages/app-defaults/src/createApp/{defaultAppThemes.tsx => defaults/themes.tsx} (59%) delete mode 100644 packages/app-defaults/src/createApp/icons.tsx delete mode 100644 packages/app-defaults/src/createApp/types.ts delete mode 100644 packages/app-defaults/src/createApp/withDefaults.tsx diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json index c7594470d6..19f4e1432e 100644 --- a/packages/app-defaults/package.json +++ b/packages/app-defaults/package.json @@ -31,6 +31,7 @@ "dependencies": { "@backstage/core-components": "^0.7.1", "@backstage/config": "^0.1.10", + "@backstage/core-app-api": "^0.1.11", "@backstage/core-plugin-api": "^0.1.11", "@backstage/theme": "^0.2.11", "@backstage/types": "^0.1.1", diff --git a/packages/app-defaults/src/createApp/AppThemeProvider.tsx b/packages/app-defaults/src/createApp/components/AppThemeProvider.tsx similarity index 100% rename from packages/app-defaults/src/createApp/AppThemeProvider.tsx rename to packages/app-defaults/src/createApp/components/AppThemeProvider.tsx diff --git a/packages/app-defaults/src/createApp/createApp.test.tsx b/packages/app-defaults/src/createApp/createApp.test.tsx index 0bf90987db..45988a4840 100644 --- a/packages/app-defaults/src/createApp/createApp.test.tsx +++ b/packages/app-defaults/src/createApp/createApp.test.tsx @@ -18,84 +18,7 @@ import { screen } from '@testing-library/react'; import { renderWithEffects } from '@backstage/test-utils'; import React, { PropsWithChildren } from 'react'; import { MemoryRouter } from 'react-router-dom'; -import { defaultConfigLoader, createApp } from './createApp'; - -(process as any).env = { NODE_ENV: 'test' }; -const anyEnv = process.env as any; -const anyWindow = window as any; - -describe('defaultConfigLoader', () => { - afterEach(() => { - delete anyEnv.APP_CONFIG; - delete anyWindow.__APP_CONFIG__; - }); - - it('loads static config', async () => { - anyEnv.APP_CONFIG = [ - { data: { my: 'config' }, context: 'a' }, - { data: { my: 'override-config' }, context: 'b' }, - ]; - - const configs = await defaultConfigLoader(); - expect(configs).toEqual([ - { data: { my: 'config' }, context: 'a' }, - { data: { my: 'override-config' }, context: 'b' }, - ]); - }); - - it('loads runtime config', async () => { - anyEnv.APP_CONFIG = [ - { data: { my: 'override-config' }, context: 'a' }, - { data: { my: 'config' }, context: 'b' }, - ]; - - const configs = await (defaultConfigLoader as any)( - '{"my":"runtime-config"}', - ); - expect(configs).toEqual([ - { data: { my: 'override-config' }, context: 'a' }, - { data: { my: 'config' }, context: 'b' }, - { data: { my: 'runtime-config' }, context: 'env' }, - ]); - }); - - it('fails to load invalid missing config', async () => { - await expect(defaultConfigLoader()).rejects.toThrow( - 'No static configuration provided', - ); - }); - - it('fails to load invalid static config', async () => { - anyEnv.APP_CONFIG = { my: 'invalid-config' }; - await expect(defaultConfigLoader()).rejects.toThrow( - 'Static configuration has invalid format', - ); - }); - - it('fails to load bad runtime config', async () => { - anyEnv.APP_CONFIG = [{ data: { my: 'config' }, context: 'a' }]; - - await expect((defaultConfigLoader as any)('}')).rejects.toThrow( - 'Failed to load runtime configuration, SyntaxError: Unexpected token } in JSON at position 0', - ); - }); - - it('loads config from window.__APP_CONFIG__', async () => { - anyEnv.APP_CONFIG = [ - { data: { my: 'config' }, context: 'a' }, - { data: { my: 'override-config' }, context: 'b' }, - ]; - const windowConfig = { app: { configKey: 'config-value' } }; - anyWindow.__APP_CONFIG__ = windowConfig; - - const configs = await defaultConfigLoader(); - - expect(configs).toEqual([ - ...anyEnv.APP_CONFIG, - { context: 'window', data: windowConfig }, - ]); - }); -}); +import { createApp } from './createApp'; describe('Optional ThemeProvider', () => { it('should render app with user-provided ThemeProvider', async () => { diff --git a/packages/app-defaults/src/createApp/createApp.tsx b/packages/app-defaults/src/createApp/createApp.tsx index c3f477035f..417c41d3af 100644 --- a/packages/app-defaults/src/createApp/createApp.tsx +++ b/packages/app-defaults/src/createApp/createApp.tsx @@ -14,132 +14,81 @@ * limitations under the License. */ -import { AppConfig } from '@backstage/config'; -import { JsonObject } from '@backstage/types'; -import { withDefaults } from '@backstage/core-components'; -import { PrivateAppImpl } from './App'; -import { AppComponents, AppConfigLoader, AppOptions } from './types'; -import { defaultApis } from './defaultApis'; -import { BackstagePlugin } from '@backstage/core-plugin-api'; - -const REQUIRED_APP_COMPONENTS: Array = [ - 'Progress', - 'Router', - 'NotFoundErrorPage', - 'BootErrorPage', - 'ErrorBoundaryFallback', -]; +import { apis, components, configLoader, icons, themes } from './defaults'; +import { + AppTheme, + BackstagePlugin, + IconComponent, +} from '@backstage/core-plugin-api'; +import { + AppComponents, + AppOptions, + AppIcons, + PrivateAppImpl, +} from '@backstage/core-app-api'; /** - * The default config loader, which expects that config is available at compile-time - * in `process.env.APP_CONFIG`. APP_CONFIG should be an array of config objects as - * returned by the config loader. - * - * It will also load runtime config from the __APP_INJECTED_RUNTIME_CONFIG__ string, - * which can be rewritten at runtime to contain an additional JSON config object. - * If runtime config is present, it will be placed first in the config array, overriding - * other config values. - * - * @public - */ -export const defaultConfigLoader: AppConfigLoader = async ( - // This string may be replaced at runtime to provide additional config. - // It should be replaced by a JSON-serialized config object. - // It's a param so we can test it, but at runtime this will always fall back to default. - runtimeConfigJson: string = '__APP_INJECTED_RUNTIME_CONFIG__', -) => { - const appConfig = process.env.APP_CONFIG; - if (!appConfig) { - throw new Error('No static configuration provided'); - } - if (!Array.isArray(appConfig)) { - throw new Error('Static configuration has invalid format'); - } - const configs = appConfig.slice() as unknown as AppConfig[]; - - // Avoiding this string also being replaced at runtime - if ( - runtimeConfigJson !== - '__app_injected_runtime_config__'.toLocaleUpperCase('en-US') - ) { - try { - const data = JSON.parse(runtimeConfigJson) as JsonObject; - if (Array.isArray(data)) { - configs.push(...data); - } else { - configs.push({ data, context: 'env' }); - } - } catch (error) { - throw new Error(`Failed to load runtime configuration, ${error}`); - } - } - - const windowAppConfig = (window as any).__APP_CONFIG__; - if (windowAppConfig) { - configs.push({ - context: 'window', - data: windowAppConfig, - }); - } - return configs; -}; - -/** - * Creates a new Backstage App. + * Creates a new Backstage App using a default set of components, icons and themes unless + * they are explicitly provided. * * @public */ export function createApp(options?: AppOptions) { - const optionsWithDefaults = withDefaults(options); - - const missingRequiredComponents = REQUIRED_APP_COMPONENTS.filter( - name => !options?.components?.[name], - ); - if (missingRequiredComponents.length > 0) { - // eslint-disable-next-line no-console - console.warn( - 'DEPRECATION WARNING: The createApp options will soon require a minimal set of components to ' + - 'be provided. You can use the default components by using withDefaults from @backstage/core-components ' + - 'like this: createApp(withDefaults({ ... })), or you can provide the components yourself. ' + - `The following components are missing: ${missingRequiredComponents.join( - ', ', - )}`, - ); - } - - const providedIconKeys = Object.keys(options?.icons ?? {}); - const missingIconKeys = Object.keys(optionsWithDefaults.icons!).filter( - key => !providedIconKeys.includes(key), - ); - if (missingIconKeys.length > 0) { - // eslint-disable-next-line no-console - console.warn( - 'DEPRECATION WARNING: The createApp options will soon require a minimal set of icons to ' + - 'be provided. You can use the default icons by using withDefaults from @backstage/core-components ' + - 'like this: createApp(withDefaults({ ... })), or you can provide the icons yourself. ' + - `The following icons are missing: ${missingIconKeys.join(', ')}`, - ); - } - - if (!options?.themes) { - // eslint-disable-next-line no-console - console.warn( - 'DEPRECATION WARNING: The createApp options will soon require the themes to be provided. ' + - 'You can use the default themes by using withDefaults from @backstage/core-components ' + - 'like this: createApp(withDefaults({ ... })), or you can provide the themes yourself. ', - ); - } - - const { icons, themes, components } = optionsWithDefaults; - return new PrivateAppImpl({ - icons: icons!, - themes: themes!, - components: components! as AppComponents, - defaultApis, + ...options, apis: options?.apis ?? [], bindRoutes: options?.bindRoutes, + components: { + ...components, + ...options?.components, + }, + configLoader: options?.configLoader ?? configLoader, + defaultApis: apis, + icons: { + ...icons, + ...options?.icons, + }, plugins: (options?.plugins as BackstagePlugin[]) ?? [], - configLoader: options?.configLoader ?? defaultConfigLoader, + themes: options?.themes ?? themes, }); } + +// NOTE: we don't re-export any of the types imported from core-app-api, as we +// want them to be imported from there rather than core-components. + +/** + * The set of app options that will be populated by {@link withDefaults} if they + * are not passed in explicitly. + * + * @public + */ +export interface OptionalAppOptions { + /** + * A set of icons to override the default icons with. + * + * The override is applied for each icon individually. + * + * @public + */ + icons?: Partial & { + [key in string]: IconComponent; + }; + + /** + * A set of themes that override all of the default app themes. + * + * If this option is provided none of the default themes will be used. + * + * @public + */ + themes?: (Partial & Omit)[]; // TODO: simplify once AppTheme is updated + + /** + * A set of components to override the default components with. + * + * The override is applied for each icon individually. + * + * @public + */ + components?: Partial; +} diff --git a/packages/app-defaults/src/createApp/defaultApis.ts b/packages/app-defaults/src/createApp/defaults/apis.ts similarity index 99% rename from packages/app-defaults/src/createApp/defaultApis.ts rename to packages/app-defaults/src/createApp/defaults/apis.ts index fb02274cf0..2596ac2593 100644 --- a/packages/app-defaults/src/createApp/defaultApis.ts +++ b/packages/app-defaults/src/createApp/defaults/apis.ts @@ -34,7 +34,7 @@ import { OneLoginAuth, UnhandledErrorForwarder, AtlassianAuth, -} from '../apis'; +} from '@backstage/core-app-api'; import { createApiFactory, @@ -61,7 +61,7 @@ import { import OAuth2Icon from '@material-ui/icons/AcUnit'; -export const defaultApis = [ +export const apis = [ createApiFactory({ api: discoveryApiRef, deps: { configApi: configApiRef }, diff --git a/packages/app-defaults/src/createApp/defaultAppComponents.test.tsx b/packages/app-defaults/src/createApp/defaults/components.test.tsx similarity index 95% rename from packages/app-defaults/src/createApp/defaultAppComponents.test.tsx rename to packages/app-defaults/src/createApp/defaults/components.test.tsx index db0e2d50c1..865eeed8a7 100644 --- a/packages/app-defaults/src/createApp/defaultAppComponents.test.tsx +++ b/packages/app-defaults/src/createApp/defaults/components.test.tsx @@ -17,7 +17,7 @@ import { render, screen } from '@testing-library/react'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; -import { OptionallyWrapInRouter } from './defaultAppComponents'; +import { OptionallyWrapInRouter } from './components'; describe('OptionallyWrapInRouter', () => { it('should wrap with router if not yet inside a router', async () => { diff --git a/packages/app-defaults/src/createApp/defaultAppComponents.tsx b/packages/app-defaults/src/createApp/defaults/components.tsx similarity index 79% rename from packages/app-defaults/src/createApp/defaultAppComponents.tsx rename to packages/app-defaults/src/createApp/defaults/components.tsx index ab55a25840..659af21cd7 100644 --- a/packages/app-defaults/src/createApp/defaultAppComponents.tsx +++ b/packages/app-defaults/src/createApp/defaults/components.tsx @@ -16,16 +16,18 @@ import React, { ReactNode } from 'react'; import Button from '@material-ui/core/Button'; -import { ErrorPanel, Progress } from '../components'; -import { ErrorPage } from '../layout'; -import { MemoryRouter, useInRouterContext } from 'react-router'; -import { BrowserRouter } from 'react-router-dom'; +import { ErrorPanel, Progress, ErrorPage } from '@backstage/core-components'; +import { + MemoryRouter, + useInRouterContext, + BrowserRouter, +} from 'react-router-dom'; import { AppComponents, BootErrorPageProps, ErrorBoundaryFallbackProps, } from '@backstage/core-plugin-api'; -import { AppThemeProvider } from './AppThemeProvider'; +import { AppThemeProvider } from '../components/AppThemeProvider'; export function OptionallyWrapInRouter({ children }: { children: ReactNode }) { if (useInRouterContext()) { @@ -52,6 +54,7 @@ const DefaultBootErrorPage = ({ step, error }: BootErrorPageProps) => { ); }; + const DefaultErrorBoundaryFallback = ({ error, resetError, @@ -75,13 +78,11 @@ const DefaultErrorBoundaryFallback = ({ * * @public */ -export function defaultAppComponents(): AppComponents { - return { - Progress, - Router: BrowserRouter, - ThemeProvider: AppThemeProvider, - NotFoundErrorPage: DefaultNotFoundPage, - BootErrorPage: DefaultBootErrorPage, - ErrorBoundaryFallback: DefaultErrorBoundaryFallback, - }; -} +export const components: AppComponents = { + Progress, + Router: BrowserRouter, + ThemeProvider: AppThemeProvider, + NotFoundErrorPage: DefaultNotFoundPage, + BootErrorPage: DefaultBootErrorPage, + ErrorBoundaryFallback: DefaultErrorBoundaryFallback, +}; diff --git a/packages/app-defaults/src/createApp/defaults/configLoader.test.ts b/packages/app-defaults/src/createApp/defaults/configLoader.test.ts new file mode 100644 index 0000000000..0246a1cf11 --- /dev/null +++ b/packages/app-defaults/src/createApp/defaults/configLoader.test.ts @@ -0,0 +1,92 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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 { configLoader } from './configLoader'; + +(process as any).env = { NODE_ENV: 'test' }; +const anyEnv = process.env as any; +const anyWindow = window as any; + +describe('configLoader', () => { + afterEach(() => { + delete anyEnv.APP_CONFIG; + delete anyWindow.__APP_CONFIG__; + }); + + it('loads static config', async () => { + anyEnv.APP_CONFIG = [ + { data: { my: 'config' }, context: 'a' }, + { data: { my: 'override-config' }, context: 'b' }, + ]; + + const configs = await configLoader(); + expect(configs).toEqual([ + { data: { my: 'config' }, context: 'a' }, + { data: { my: 'override-config' }, context: 'b' }, + ]); + }); + + it('loads runtime config', async () => { + anyEnv.APP_CONFIG = [ + { data: { my: 'override-config' }, context: 'a' }, + { data: { my: 'config' }, context: 'b' }, + ]; + + const configs = await (configLoader as any)('{"my":"runtime-config"}'); + expect(configs).toEqual([ + { data: { my: 'override-config' }, context: 'a' }, + { data: { my: 'config' }, context: 'b' }, + { data: { my: 'runtime-config' }, context: 'env' }, + ]); + }); + + it('fails to load invalid missing config', async () => { + await expect(configLoader()).rejects.toThrow( + 'No static configuration provided', + ); + }); + + it('fails to load invalid static config', async () => { + anyEnv.APP_CONFIG = { my: 'invalid-config' }; + await expect(configLoader()).rejects.toThrow( + 'Static configuration has invalid format', + ); + }); + + it('fails to load bad runtime config', async () => { + anyEnv.APP_CONFIG = [{ data: { my: 'config' }, context: 'a' }]; + + await expect((configLoader as any)('}')).rejects.toThrow( + 'Failed to load runtime configuration, SyntaxError: Unexpected token } in JSON at position 0', + ); + }); + + it('loads config from window.__APP_CONFIG__', async () => { + anyEnv.APP_CONFIG = [ + { data: { my: 'config' }, context: 'a' }, + { data: { my: 'override-config' }, context: 'b' }, + ]; + const windowConfig = { app: { configKey: 'config-value' } }; + anyWindow.__APP_CONFIG__ = windowConfig; + + const configs = await configLoader(); + + expect(configs).toEqual([ + ...anyEnv.APP_CONFIG, + { context: 'window', data: windowConfig }, + ]); + }); +}); diff --git a/packages/app-defaults/src/createApp/defaults/configLoader.ts b/packages/app-defaults/src/createApp/defaults/configLoader.ts new file mode 100644 index 0000000000..3cf10ef72f --- /dev/null +++ b/packages/app-defaults/src/createApp/defaults/configLoader.ts @@ -0,0 +1,73 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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 { AppConfig } from '@backstage/config'; +import { JsonObject } from '@backstage/types'; +import { AppConfigLoader } from '@backstage/core-app-api'; + +/** + * The default config loader, which expects that config is available at compile-time + * in `process.env.APP_CONFIG`. APP_CONFIG should be an array of config objects as + * returned by the config loader. + * + * It will also load runtime config from the __APP_INJECTED_RUNTIME_CONFIG__ string, + * which can be rewritten at runtime to contain an additional JSON config object. + * If runtime config is present, it will be placed first in the config array, overriding + * other config values. + * + * @public + */ +export const configLoader: AppConfigLoader = async ( + // This string may be replaced at runtime to provide additional config. + // It should be replaced by a JSON-serialized config object. + // It's a param so we can test it, but at runtime this will always fall back to default. + runtimeConfigJson: string = '__APP_INJECTED_RUNTIME_CONFIG__', +) => { + const appConfig = process.env.APP_CONFIG; + if (!appConfig) { + throw new Error('No static configuration provided'); + } + if (!Array.isArray(appConfig)) { + throw new Error('Static configuration has invalid format'); + } + const configs = appConfig.slice() as unknown as AppConfig[]; + + // Avoiding this string also being replaced at runtime + if ( + runtimeConfigJson !== + '__app_injected_runtime_config__'.toLocaleUpperCase('en-US') + ) { + try { + const data = JSON.parse(runtimeConfigJson) as JsonObject; + if (Array.isArray(data)) { + configs.push(...data); + } else { + configs.push({ data, context: 'env' }); + } + } catch (error) { + throw new Error(`Failed to load runtime configuration, ${error}`); + } + } + + const windowAppConfig = (window as any).__APP_CONFIG__; + if (windowAppConfig) { + configs.push({ + context: 'window', + data: windowAppConfig, + }); + } + return configs; +}; diff --git a/packages/app-defaults/src/createApp/defaultAppIcons.tsx b/packages/app-defaults/src/createApp/defaults/icons.tsx similarity index 98% rename from packages/app-defaults/src/createApp/defaultAppIcons.tsx rename to packages/app-defaults/src/createApp/defaults/icons.tsx index 1d02dd5ad2..ba2531213d 100644 --- a/packages/app-defaults/src/createApp/defaultAppIcons.tsx +++ b/packages/app-defaults/src/createApp/defaults/icons.tsx @@ -32,7 +32,7 @@ import MuiPeopleIcon from '@material-ui/icons/People'; import MuiPersonIcon from '@material-ui/icons/Person'; import MuiWarningIcon from '@material-ui/icons/Warning'; -export const defaultAppIcons = () => ({ +export const icons = () => ({ brokenImage: MuiBrokenImageIcon as IconComponent, // To be confirmed: see https://github.com/backstage/backstage/issues/4970 catalog: MuiMenuBookIcon as IconComponent, diff --git a/packages/app-defaults/src/createApp/defaults/index.ts b/packages/app-defaults/src/createApp/defaults/index.ts new file mode 100644 index 0000000000..a8a11e78ac --- /dev/null +++ b/packages/app-defaults/src/createApp/defaults/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { apis } from './apis'; +export { components } from './components'; +export { configLoader } from './configLoader'; +export { icons } from './icons'; +export { themes } from './themes'; diff --git a/packages/app-defaults/src/createApp/defaultAppThemes.tsx b/packages/app-defaults/src/createApp/defaults/themes.tsx similarity index 59% rename from packages/app-defaults/src/createApp/defaultAppThemes.tsx rename to packages/app-defaults/src/createApp/defaults/themes.tsx index 894716942b..90a4a0f47f 100644 --- a/packages/app-defaults/src/createApp/defaultAppThemes.tsx +++ b/packages/app-defaults/src/createApp/defaults/themes.tsx @@ -22,31 +22,29 @@ import { ThemeProvider } from '@material-ui/core/styles'; import CssBaseline from '@material-ui/core/CssBaseline'; import { AppTheme } from '@backstage/core-plugin-api'; -export function defaultAppThemes(): AppTheme[] { - return [ - { - id: 'light', - title: 'Light Theme', - variant: 'light', - icon: , - theme: lightTheme, - Provider: ({ children }) => ( - - {children} - - ), - }, - { - id: 'dark', - title: 'Dark Theme', - variant: 'dark', - icon: , - theme: darkTheme, - Provider: ({ children }) => ( - - {children} - - ), - }, - ]; -} +export const themes: AppTheme[] = [ + { + id: 'light', + title: 'Light Theme', + variant: 'light', + icon: , + theme: lightTheme, + Provider: ({ children }) => ( + + {children} + + ), + }, + { + id: 'dark', + title: 'Dark Theme', + variant: 'dark', + icon: , + theme: darkTheme, + Provider: ({ children }) => ( + + {children} + + ), + }, +]; diff --git a/packages/app-defaults/src/createApp/icons.tsx b/packages/app-defaults/src/createApp/icons.tsx deleted file mode 100644 index df1e69da6a..0000000000 --- a/packages/app-defaults/src/createApp/icons.tsx +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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 { IconComponent } from '@backstage/core-plugin-api'; -import MuiApartmentIcon from '@material-ui/icons/Apartment'; -import MuiBrokenImageIcon from '@material-ui/icons/BrokenImage'; -import MuiCategoryIcon from '@material-ui/icons/Category'; -import MuiChatIcon from '@material-ui/icons/Chat'; -import MuiDashboardIcon from '@material-ui/icons/Dashboard'; -import MuiDocsIcon from '@material-ui/icons/Description'; -import MuiEmailIcon from '@material-ui/icons/Email'; -import MuiExtensionIcon from '@material-ui/icons/Extension'; -import MuiGitHubIcon from '@material-ui/icons/GitHub'; -import MuiHelpIcon from '@material-ui/icons/Help'; -import MuiLocationOnIcon from '@material-ui/icons/LocationOn'; -import MuiMemoryIcon from '@material-ui/icons/Memory'; -import MuiMenuBookIcon from '@material-ui/icons/MenuBook'; -import MuiPeopleIcon from '@material-ui/icons/People'; -import MuiPersonIcon from '@material-ui/icons/Person'; -import MuiWarningIcon from '@material-ui/icons/Warning'; - -/** @public */ -export type AppIcons = { - 'kind:api': IconComponent; - 'kind:component': IconComponent; - 'kind:domain': IconComponent; - 'kind:group': IconComponent; - 'kind:location': IconComponent; - 'kind:system': IconComponent; - 'kind:user': IconComponent; - - brokenImage: IconComponent; - catalog: IconComponent; - chat: IconComponent; - dashboard: IconComponent; - docs: IconComponent; - email: IconComponent; - github: IconComponent; - group: IconComponent; - help: IconComponent; - user: IconComponent; - warning: IconComponent; -}; - -export const defaultAppIcons: AppIcons = { - brokenImage: MuiBrokenImageIcon, - // To be confirmed: see https://github.com/backstage/backstage/issues/4970 - catalog: MuiMenuBookIcon, - chat: MuiChatIcon, - dashboard: MuiDashboardIcon, - docs: MuiDocsIcon, - email: MuiEmailIcon, - github: MuiGitHubIcon, - group: MuiPeopleIcon, - help: MuiHelpIcon, - 'kind:api': MuiExtensionIcon, - 'kind:component': MuiMemoryIcon, - 'kind:domain': MuiApartmentIcon, - 'kind:group': MuiPeopleIcon, - 'kind:location': MuiLocationOnIcon, - 'kind:system': MuiCategoryIcon, - 'kind:user': MuiPersonIcon, - user: MuiPersonIcon, - warning: MuiWarningIcon, -}; diff --git a/packages/app-defaults/src/createApp/index.ts b/packages/app-defaults/src/createApp/index.ts index 2a16a90bfa..dea36bb3e7 100644 --- a/packages/app-defaults/src/createApp/index.ts +++ b/packages/app-defaults/src/createApp/index.ts @@ -14,6 +14,4 @@ * limitations under the License. */ -export { createApp, defaultConfigLoader } from './createApp'; -export type { AppIcons } from './icons'; -export * from './types'; +export { createApp } from './createApp'; diff --git a/packages/app-defaults/src/createApp/types.ts b/packages/app-defaults/src/createApp/types.ts deleted file mode 100644 index 78ef277c90..0000000000 --- a/packages/app-defaults/src/createApp/types.ts +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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 { ComponentType } from 'react'; -import { - AnyApiFactory, - AppTheme, - ProfileInfo, - IconComponent, - BackstagePlugin, - RouteRef, - SubRouteRef, - ExternalRouteRef, - PluginOutput, -} from '@backstage/core-plugin-api'; -import { AppConfig } from '@backstage/config'; -import { AppIcons } from './icons'; - -/** - * Props for the `BootErrorPage` component of {@link AppComponents}. - * - * @public - */ -export type BootErrorPageProps = { - step: 'load-config' | 'load-chunk'; - error: Error; -}; - -/** - * The outcome of signing in on the sign-in page. - * - * @public - */ -export type SignInResult = { - /** - * User ID that will be returned by the IdentityApi - */ - userId: string; - - profile: ProfileInfo; - - /** - * Function used to retrieve an ID token for the signed in user. - */ - getIdToken?: () => Promise; - - /** - * Sign out handler that will be called if the user requests to sign out. - */ - signOut?: () => Promise; -}; - -/** - * Props for the `SignInPage` component of {@link AppComponents}. - * - * @public - */ -export type SignInPageProps = { - /** - * Set the sign-in result for the app. This should only be called once. - */ - onResult(result: SignInResult): void; -}; - -/** - * Props for the fallback error boundary. - * - * @public - */ -export type ErrorBoundaryFallbackProps = { - plugin?: BackstagePlugin; - error: Error; - resetError: () => void; -}; - -/** - * A set of replaceable core components that are part of every Backstage app. - * - * @public - */ -export type AppComponents = { - NotFoundErrorPage: ComponentType<{}>; - BootErrorPage: ComponentType; - Progress: ComponentType<{}>; - Router: ComponentType<{}>; - ErrorBoundaryFallback: ComponentType; - ThemeProvider: ComponentType<{}>; - - /** - * An optional sign-in page that will be rendered instead of the AppRouter at startup. - * - * If a sign-in page is set, it will always be shown before the app, and it is up - * to the sign-in page to handle e.g. saving of login methods for subsequent visits. - * - * The sign-in page will be displayed until it has passed up a result to the parent, - * and which point the AppRouter and all of its children will be rendered instead. - */ - SignInPage?: ComponentType; -}; - -/** - * A function that loads in the App config that will be accessible via the ConfigApi. - * - * If multiple config objects are returned in the array, values in the earlier configs - * will override later ones. - * - * @public - */ -export type AppConfigLoader = () => Promise; - -/** - * Extracts a union of the keys in a map whose value extends the given type - */ -type KeysWithType = { - [key in keyof Obj]: Obj[key] extends Type ? key : never; -}[keyof Obj]; - -/** - * Takes a map Map required values and makes all keys matching Keys optional - */ -type PartialKeys< - Map extends { [name in string]: any }, - Keys extends keyof Map, -> = Partial> & Required>; - -/** - * Creates a map of target routes with matching parameters based on a map of external routes. - */ -type TargetRouteMap< - ExternalRoutes extends { [name: string]: ExternalRouteRef }, -> = { - [name in keyof ExternalRoutes]: ExternalRoutes[name] extends ExternalRouteRef< - infer Params, - any - > - ? RouteRef | SubRouteRef - : never; -}; - -/** - * A function that can bind from external routes of a given plugin, to concrete - * routes of other plugins. See {@link createApp}. - * - * @public - */ -export type AppRouteBinder = < - ExternalRoutes extends { [name: string]: ExternalRouteRef }, ->( - externalRoutes: ExternalRoutes, - targetRoutes: PartialKeys< - TargetRouteMap, - KeysWithType> - >, -) => void; - -/** - * Internal helper type that represents a plugin with any type of output. - * - * @public - * @remarks - * - * The `type: string` type is there to handle output from newer or older plugin - * API versions that might not be supported by this version of the app API, but - * we don't want to break at the type checking level. We only use this more - * permissive type for the `createApp` options, as we otherwise want to stick - * to using the type for the outputs that we know about in this version of the - * app api. - * - * TODO(freben): This should be marked internal but that's not supported by the api report generation tools yet - */ -export type BackstagePluginWithAnyOutput = Omit< - BackstagePlugin, - 'output' -> & { - output(): (PluginOutput | { type: string })[]; -}; - -/** - * The options accepted by {@link createApp}. - * - * @public - */ -export type AppOptions = { - /** - * A collection of ApiFactories to register in the application to either - * add add new ones, or override factories provided by default or by plugins. - */ - apis?: Iterable; - - /** - * Supply icons to override the default ones. - */ - icons?: Partial & { [key in string]: IconComponent }; - - /** - * A list of all plugins to include in the app. - */ - plugins?: BackstagePluginWithAnyOutput[]; - - /** - * Supply components to the app to override the default ones. - */ - components?: Partial; - - /** - * Themes provided as a part of the app. By default two themes are included, one - * light variant of the default backstage theme, and one dark. - * - * This is the default config: - * - * ``` - * [{ - * id: 'light', - * title: 'Light Theme', - * variant: 'light', - * icon: , - * Provider: ({ children }) => ( - * - * {children} - * - * ), - * }, { - * id: 'dark', - * title: 'Dark Theme', - * variant: 'dark', - * icon: , - * Provider: ({ children }) => ( - * - * {children} - * - * ), - * }] - * ``` - */ - themes?: (Partial & Omit)[]; - - /** - * A function that loads in App configuration that will be accessible via - * the ConfigApi. - * - * Defaults to an empty config. - * - * TODO(Rugvip): Omitting this should instead default to loading in configuration - * that was packaged by the backstage-cli and default docker container boot script. - */ - configLoader?: AppConfigLoader; - - /** - * A function that is used to register associations between cross-plugin route - * references, enabling plugins to navigate between each other. - * - * The `bind` function that is passed in should be used to bind all external - * routes of all used plugins. - * - * ```ts - * bindRoutes({ bind }) { - * bind(docsPlugin.externalRoutes, { - * homePage: managePlugin.routes.managePage, - * }) - * bind(homePagePlugin.externalRoutes, { - * settingsPage: settingsPlugin.routes.settingsPage, - * }) - * } - * ``` - */ - bindRoutes?(context: { bind: AppRouteBinder }): void; -}; - -/** - * The public API of the output of {@link createApp}. - * - * @public - */ -export type BackstageApp = { - /** - * Returns all plugins registered for the app. - */ - getPlugins(): BackstagePlugin[]; - - /** - * Get a common or custom icon for this app. - */ - getSystemIcon(key: string): IconComponent | undefined; - - /** - * Provider component that should wrap the Router created with getRouter() - * and any other components that need to be within the app context. - */ - getProvider(): ComponentType<{}>; - - /** - * Router component that should wrap the App Routes create with getRoutes() - * and any other components that should only be available while signed in. - */ - getRouter(): ComponentType<{}>; -}; - -/** - * The central context providing runtime app specific state that plugin views - * want to consume. - * - * @public - */ -export type AppContext = { - /** - * Get a list of all plugins that are installed in the app. - */ - getPlugins(): BackstagePlugin[]; - - /** - * Get a common or custom icon for this app. - */ - getSystemIcon(key: string): IconComponent | undefined; - - /** - * Get the components registered for various purposes in the app. - */ - getComponents(): AppComponents; -}; diff --git a/packages/app-defaults/src/createApp/withDefaults.tsx b/packages/app-defaults/src/createApp/withDefaults.tsx deleted file mode 100644 index 9254053404..0000000000 --- a/packages/app-defaults/src/createApp/withDefaults.tsx +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { AppTheme, IconComponent } from '@backstage/core-plugin-api'; -// This is a bit of a hack that we use to avoid having to redeclare these types -// within this package or have an explicit dependency on core-app-api. -// These types end up being inlined and duplicated into this package at build time. -// eslint-disable-next-line no-restricted-imports -import { - AppIcons, - AppComponents, - AppOptions, -} from '../../../core-app-api/src/app'; -import { defaultAppComponents } from './defaultAppComponents'; -import { defaultAppIcons } from './defaultAppIcons'; -import { defaultAppThemes } from './defaultAppThemes'; - -// NOTE: we don't re-export any of the types imported from core-app-api, as we -// want them to be imported from there rather than core-components. - -/** - * The set of app options that will be populated by {@link withDefaults} if they - * are not passed in explicitly. - * - * @public - */ -export interface OptionalAppOptions { - icons?: Partial & { - [key in string]: IconComponent; - }; - themes?: (Partial & Omit)[]; // TODO: simplify once AppTheme is updated - components?: Partial; -} - -/** - * Provides a set of default App options with the ability to override specific options. - * - * These options populate the theme, icons and components options of {@link @backstage/core-app-api#AppOptions}. - * - * @public - */ -export function withDefaults( - options?: Omit & OptionalAppOptions, -): AppOptions { - const { themes, icons, components } = options ?? {}; - - return { - ...options, - themes: themes ?? defaultAppThemes(), - icons: { ...defaultAppIcons(), ...icons }, - components: { ...defaultAppComponents(), ...components }, - }; -} From 5bfe8917c79531f2f1ab7f969390819f7ad6e48b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 27 Oct 2021 21:20:57 +0200 Subject: [PATCH 169/196] app-defaults: move AppThemeProvider back to core-app-api Signed-off-by: Patrik Oldsberg --- packages/app-defaults/src/createApp/defaults/components.tsx | 2 -- packages/core-app-api/src/app/App.tsx | 5 +++-- .../components => core-app-api/src/app}/AppThemeProvider.tsx | 0 packages/core-app-api/src/app/types.ts | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) rename packages/{app-defaults/src/createApp/components => core-app-api/src/app}/AppThemeProvider.tsx (100%) diff --git a/packages/app-defaults/src/createApp/defaults/components.tsx b/packages/app-defaults/src/createApp/defaults/components.tsx index 659af21cd7..139bea4862 100644 --- a/packages/app-defaults/src/createApp/defaults/components.tsx +++ b/packages/app-defaults/src/createApp/defaults/components.tsx @@ -27,7 +27,6 @@ import { BootErrorPageProps, ErrorBoundaryFallbackProps, } from '@backstage/core-plugin-api'; -import { AppThemeProvider } from '../components/AppThemeProvider'; export function OptionallyWrapInRouter({ children }: { children: ReactNode }) { if (useInRouterContext()) { @@ -81,7 +80,6 @@ const DefaultErrorBoundaryFallback = ({ export const components: AppComponents = { Progress, Router: BrowserRouter, - ThemeProvider: AppThemeProvider, NotFoundErrorPage: DefaultNotFoundPage, BootErrorPage: DefaultBootErrorPage, ErrorBoundaryFallback: DefaultErrorBoundaryFallback, diff --git a/packages/core-app-api/src/app/App.tsx b/packages/core-app-api/src/app/App.tsx index 6fadd22808..81da370b6c 100644 --- a/packages/core-app-api/src/app/App.tsx +++ b/packages/core-app-api/src/app/App.tsx @@ -77,6 +77,7 @@ import { SignInPageProps, SignInResult, } from './types'; +import { AppThemeProvider } from './AppThemeProvider'; export function generateBoundRoutes(bindRoutes: AppOptions['bindRoutes']) { const result = new Map(); @@ -151,7 +152,7 @@ function useConfigLoader( noConfigNode = ; } - const { ThemeProvider } = components; + const { ThemeProvider = AppThemeProvider } = components; // Before the config is loaded we can't use a router, so exit early if (noConfigNode) { @@ -307,7 +308,7 @@ export class PrivateAppImpl implements BackstageApp { return loadedConfig.node; } - const { ThemeProvider } = this.components; + const { ThemeProvider = AppThemeProvider } = this.components; return ( diff --git a/packages/app-defaults/src/createApp/components/AppThemeProvider.tsx b/packages/core-app-api/src/app/AppThemeProvider.tsx similarity index 100% rename from packages/app-defaults/src/createApp/components/AppThemeProvider.tsx rename to packages/core-app-api/src/app/AppThemeProvider.tsx diff --git a/packages/core-app-api/src/app/types.ts b/packages/core-app-api/src/app/types.ts index 78ef277c90..41e997e179 100644 --- a/packages/core-app-api/src/app/types.ts +++ b/packages/core-app-api/src/app/types.ts @@ -97,7 +97,7 @@ export type AppComponents = { Progress: ComponentType<{}>; Router: ComponentType<{}>; ErrorBoundaryFallback: ComponentType; - ThemeProvider: ComponentType<{}>; + ThemeProvider?: ComponentType<{}>; /** * An optional sign-in page that will be rendered instead of the AppRouter at startup. From 3fb98cfcbf63d7b71dba14df33513dd20ca319ec Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 27 Oct 2021 21:21:58 +0200 Subject: [PATCH 170/196] app-defaults: move defaultConfigLoader back to core-app-api Signed-off-by: Patrik Oldsberg --- .../src/createApp/defaults/index.ts | 1 - .../src/app/defaultConfigLoader.test.ts} | 18 ++++++++++-------- .../src/app/defaultConfigLoader.ts} | 4 ++-- packages/core-app-api/src/app/index.ts | 3 ++- 4 files changed, 14 insertions(+), 12 deletions(-) rename packages/{app-defaults/src/createApp/defaults/configLoader.test.ts => core-app-api/src/app/defaultConfigLoader.test.ts} (83%) rename packages/{app-defaults/src/createApp/defaults/configLoader.ts => core-app-api/src/app/defaultConfigLoader.ts} (95%) diff --git a/packages/app-defaults/src/createApp/defaults/index.ts b/packages/app-defaults/src/createApp/defaults/index.ts index a8a11e78ac..d9ff18bc8c 100644 --- a/packages/app-defaults/src/createApp/defaults/index.ts +++ b/packages/app-defaults/src/createApp/defaults/index.ts @@ -16,6 +16,5 @@ export { apis } from './apis'; export { components } from './components'; -export { configLoader } from './configLoader'; export { icons } from './icons'; export { themes } from './themes'; diff --git a/packages/app-defaults/src/createApp/defaults/configLoader.test.ts b/packages/core-app-api/src/app/defaultConfigLoader.test.ts similarity index 83% rename from packages/app-defaults/src/createApp/defaults/configLoader.test.ts rename to packages/core-app-api/src/app/defaultConfigLoader.test.ts index 0246a1cf11..aebba92c43 100644 --- a/packages/app-defaults/src/createApp/defaults/configLoader.test.ts +++ b/packages/core-app-api/src/app/defaultConfigLoader.test.ts @@ -14,13 +14,13 @@ * limitations under the License. */ -import { configLoader } from './configLoader'; +import { defaultConfigLoader } from './defaultConfigLoader'; (process as any).env = { NODE_ENV: 'test' }; const anyEnv = process.env as any; const anyWindow = window as any; -describe('configLoader', () => { +describe('defaultConfigLoader', () => { afterEach(() => { delete anyEnv.APP_CONFIG; delete anyWindow.__APP_CONFIG__; @@ -32,7 +32,7 @@ describe('configLoader', () => { { data: { my: 'override-config' }, context: 'b' }, ]; - const configs = await configLoader(); + const configs = await defaultConfigLoader(); expect(configs).toEqual([ { data: { my: 'config' }, context: 'a' }, { data: { my: 'override-config' }, context: 'b' }, @@ -45,7 +45,9 @@ describe('configLoader', () => { { data: { my: 'config' }, context: 'b' }, ]; - const configs = await (configLoader as any)('{"my":"runtime-config"}'); + const configs = await (defaultConfigLoader as any)( + '{"my":"runtime-config"}', + ); expect(configs).toEqual([ { data: { my: 'override-config' }, context: 'a' }, { data: { my: 'config' }, context: 'b' }, @@ -54,14 +56,14 @@ describe('configLoader', () => { }); it('fails to load invalid missing config', async () => { - await expect(configLoader()).rejects.toThrow( + await expect(defaultConfigLoader()).rejects.toThrow( 'No static configuration provided', ); }); it('fails to load invalid static config', async () => { anyEnv.APP_CONFIG = { my: 'invalid-config' }; - await expect(configLoader()).rejects.toThrow( + await expect(defaultConfigLoader()).rejects.toThrow( 'Static configuration has invalid format', ); }); @@ -69,7 +71,7 @@ describe('configLoader', () => { it('fails to load bad runtime config', async () => { anyEnv.APP_CONFIG = [{ data: { my: 'config' }, context: 'a' }]; - await expect((configLoader as any)('}')).rejects.toThrow( + await expect((defaultConfigLoader as any)('}')).rejects.toThrow( 'Failed to load runtime configuration, SyntaxError: Unexpected token } in JSON at position 0', ); }); @@ -82,7 +84,7 @@ describe('configLoader', () => { const windowConfig = { app: { configKey: 'config-value' } }; anyWindow.__APP_CONFIG__ = windowConfig; - const configs = await configLoader(); + const configs = await defaultConfigLoader(); expect(configs).toEqual([ ...anyEnv.APP_CONFIG, diff --git a/packages/app-defaults/src/createApp/defaults/configLoader.ts b/packages/core-app-api/src/app/defaultConfigLoader.ts similarity index 95% rename from packages/app-defaults/src/createApp/defaults/configLoader.ts rename to packages/core-app-api/src/app/defaultConfigLoader.ts index 3cf10ef72f..00aee2d367 100644 --- a/packages/app-defaults/src/createApp/defaults/configLoader.ts +++ b/packages/core-app-api/src/app/defaultConfigLoader.ts @@ -16,7 +16,7 @@ import { AppConfig } from '@backstage/config'; import { JsonObject } from '@backstage/types'; -import { AppConfigLoader } from '@backstage/core-app-api'; +import { AppConfigLoader } from './types'; /** * The default config loader, which expects that config is available at compile-time @@ -30,7 +30,7 @@ import { AppConfigLoader } from '@backstage/core-app-api'; * * @public */ -export const configLoader: AppConfigLoader = async ( +export const defaultConfigLoader: AppConfigLoader = async ( // This string may be replaced at runtime to provide additional config. // It should be replaced by a JSON-serialized config object. // It's a param so we can test it, but at runtime this will always fall back to default. diff --git a/packages/core-app-api/src/app/index.ts b/packages/core-app-api/src/app/index.ts index 2a16a90bfa..5514d7ee76 100644 --- a/packages/core-app-api/src/app/index.ts +++ b/packages/core-app-api/src/app/index.ts @@ -14,6 +14,7 @@ * limitations under the License. */ -export { createApp, defaultConfigLoader } from './createApp'; +export { createApp } from './createApp'; +export { defaultConfigLoader } from './defaultConfigLoader'; export type { AppIcons } from './icons'; export * from './types'; From 42175496486ed1f79bb4f1a0adf5eba4a1f484ef Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 27 Oct 2021 21:26:32 +0200 Subject: [PATCH 171/196] core-app-api: rename PrivateAppImpl to AppManager Signed-off-by: Patrik Oldsberg --- .../src/app/{App.test.tsx => AppManager.test.tsx} | 12 ++++++------ .../core-app-api/src/app/{App.tsx => AppManager.tsx} | 4 ++-- packages/core-app-api/src/app/createApp.tsx | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) rename packages/core-app-api/src/app/{App.test.tsx => AppManager.test.tsx} (98%) rename packages/core-app-api/src/app/{App.tsx => AppManager.tsx} (99%) diff --git a/packages/core-app-api/src/app/App.test.tsx b/packages/core-app-api/src/app/AppManager.test.tsx similarity index 98% rename from packages/core-app-api/src/app/App.test.tsx rename to packages/core-app-api/src/app/AppManager.test.tsx index 198048f8cf..934ca3295f 100644 --- a/packages/core-app-api/src/app/App.test.tsx +++ b/packages/core-app-api/src/app/AppManager.test.tsx @@ -37,7 +37,7 @@ import { createRoutableExtension, analyticsApiRef, } from '@backstage/core-plugin-api'; -import { generateBoundRoutes, PrivateAppImpl } from './App'; +import { generateBoundRoutes, AppManager } from './AppManager'; import { AppComponents } from './types'; describe('generateBoundRoutes', () => { @@ -188,7 +188,7 @@ describe('Integration Test', () => { }; it('runs happy paths', async () => { - const app = new PrivateAppImpl({ + const app = new AppManager({ apis: [noOpAnalyticsApi], defaultApis: [], themes: [ @@ -242,7 +242,7 @@ describe('Integration Test', () => { }); it('runs happy paths without optional routes', async () => { - const app = new PrivateAppImpl({ + const app = new AppManager({ apis: [noOpAnalyticsApi], defaultApis: [], themes: [ @@ -299,7 +299,7 @@ describe('Integration Test', () => { }), ]; - const app = new PrivateAppImpl({ + const app = new AppManager({ apis, defaultApis: [], themes: [ @@ -349,7 +349,7 @@ describe('Integration Test', () => { it('should track route changes via analytics api', async () => { const mockAnalyticsApi = new MockAnalyticsApi(); const apis = [createApiFactory(analyticsApiRef, mockAnalyticsApi)]; - const app = new PrivateAppImpl({ + const app = new AppManager({ apis, defaultApis: [], themes: [ @@ -409,7 +409,7 @@ describe('Integration Test', () => { }); it('should throw some error when the route has duplicate params', () => { - const app = new PrivateAppImpl({ + const app = new AppManager({ apis: [], defaultApis: [], themes: [ diff --git a/packages/core-app-api/src/app/App.tsx b/packages/core-app-api/src/app/AppManager.tsx similarity index 99% rename from packages/core-app-api/src/app/App.tsx rename to packages/core-app-api/src/app/AppManager.tsx index 81da370b6c..ff501aa100 100644 --- a/packages/core-app-api/src/app/App.tsx +++ b/packages/core-app-api/src/app/AppManager.tsx @@ -171,7 +171,7 @@ function useConfigLoader( } class AppContextImpl implements AppContext { - constructor(private readonly app: PrivateAppImpl) {} + constructor(private readonly app: AppManager) {} getPlugins(): BackstagePlugin[] { return this.app.getPlugins(); @@ -186,7 +186,7 @@ class AppContextImpl implements AppContext { } } -export class PrivateAppImpl implements BackstageApp { +export class AppManager implements BackstageApp { private apiHolder?: ApiHolder; private configApi?: ConfigApi; diff --git a/packages/core-app-api/src/app/createApp.tsx b/packages/core-app-api/src/app/createApp.tsx index c3f477035f..5bb5607a37 100644 --- a/packages/core-app-api/src/app/createApp.tsx +++ b/packages/core-app-api/src/app/createApp.tsx @@ -17,7 +17,7 @@ import { AppConfig } from '@backstage/config'; import { JsonObject } from '@backstage/types'; import { withDefaults } from '@backstage/core-components'; -import { PrivateAppImpl } from './App'; +import { AppManager } from './AppManager'; import { AppComponents, AppConfigLoader, AppOptions } from './types'; import { defaultApis } from './defaultApis'; import { BackstagePlugin } from '@backstage/core-plugin-api'; @@ -132,7 +132,7 @@ export function createApp(options?: AppOptions) { const { icons, themes, components } = optionsWithDefaults; - return new PrivateAppImpl({ + return new AppManager({ icons: icons!, themes: themes!, components: components! as AppComponents, From 1c3413bab07bdb45ea8ddca78aaf2a3f9c4e074c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 28 Oct 2021 13:49:25 +0200 Subject: [PATCH 172/196] app-defaults,core-app-api: refactor into createApp/createSpecializedApp split Signed-off-by: Patrik Oldsberg --- .../src/{createApp => }/createApp.test.tsx | 0 .../src/{createApp => }/createApp.tsx | 23 +- .../src/{createApp => }/defaults/apis.ts | 0 .../defaults/components.test.tsx | 0 .../{createApp => }/defaults/components.tsx | 0 .../src/{createApp => }/defaults/icons.tsx | 4 +- .../src/{createApp => }/defaults/index.ts | 0 .../src/{createApp => }/defaults/themes.tsx | 0 packages/app-defaults/src/index.ts | 3 +- packages/core-app-api/package.json | 1 + packages/core-app-api/src/app/AppManager.tsx | 24 +- .../core-app-api/src/app/createApp.test.tsx | 119 -------- packages/core-app-api/src/app/createApp.tsx | 135 +-------- .../src/app/createSpecializedApp.tsx} | 13 +- packages/core-app-api/src/app/defaultApis.ts | 264 ------------------ packages/core-app-api/src/app/index.ts | 1 + packages/core-app-api/src/app/types.ts | 15 +- 17 files changed, 63 insertions(+), 539 deletions(-) rename packages/app-defaults/src/{createApp => }/createApp.test.tsx (100%) rename packages/app-defaults/src/{createApp => }/createApp.tsx (78%) rename packages/app-defaults/src/{createApp => }/defaults/apis.ts (100%) rename packages/app-defaults/src/{createApp => }/defaults/components.test.tsx (100%) rename packages/app-defaults/src/{createApp => }/defaults/components.tsx (100%) rename packages/app-defaults/src/{createApp => }/defaults/icons.tsx (98%) rename packages/app-defaults/src/{createApp => }/defaults/index.ts (100%) rename packages/app-defaults/src/{createApp => }/defaults/themes.tsx (100%) delete mode 100644 packages/core-app-api/src/app/createApp.test.tsx rename packages/{app-defaults/src/createApp/index.ts => core-app-api/src/app/createSpecializedApp.tsx} (64%) delete mode 100644 packages/core-app-api/src/app/defaultApis.ts diff --git a/packages/app-defaults/src/createApp/createApp.test.tsx b/packages/app-defaults/src/createApp.test.tsx similarity index 100% rename from packages/app-defaults/src/createApp/createApp.test.tsx rename to packages/app-defaults/src/createApp.test.tsx diff --git a/packages/app-defaults/src/createApp/createApp.tsx b/packages/app-defaults/src/createApp.tsx similarity index 78% rename from packages/app-defaults/src/createApp/createApp.tsx rename to packages/app-defaults/src/createApp.tsx index 417c41d3af..d85dfa04eb 100644 --- a/packages/app-defaults/src/createApp/createApp.tsx +++ b/packages/app-defaults/src/createApp.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { apis, components, configLoader, icons, themes } from './defaults'; +import { apis, components, icons, themes } from './defaults'; import { AppTheme, BackstagePlugin, @@ -24,7 +24,7 @@ import { AppComponents, AppOptions, AppIcons, - PrivateAppImpl, + createSpecializedApp, } from '@backstage/core-app-api'; /** @@ -33,8 +33,10 @@ import { * * @public */ -export function createApp(options?: AppOptions) { - return new PrivateAppImpl({ +export function createApp( + options?: Omit & OptionalAppOptions, +) { + return createSpecializedApp({ ...options, apis: options?.apis ?? [], bindRoutes: options?.bindRoutes, @@ -42,7 +44,7 @@ export function createApp(options?: AppOptions) { ...components, ...options?.components, }, - configLoader: options?.configLoader ?? configLoader, + configLoader: options?.configLoader, defaultApis: apis, icons: { ...icons, @@ -53,16 +55,13 @@ export function createApp(options?: AppOptions) { }); } -// NOTE: we don't re-export any of the types imported from core-app-api, as we -// want them to be imported from there rather than core-components. - /** - * The set of app options that will be populated by {@link withDefaults} if they - * are not passed in explicitly. + * The set of app options that {@link createApp} will provide defaults for + * if they are not passed in explicitly. * * @public */ -export interface OptionalAppOptions { +export type OptionalAppOptions = { /** * A set of icons to override the default icons with. * @@ -91,4 +90,4 @@ export interface OptionalAppOptions { * @public */ components?: Partial; -} +}; diff --git a/packages/app-defaults/src/createApp/defaults/apis.ts b/packages/app-defaults/src/defaults/apis.ts similarity index 100% rename from packages/app-defaults/src/createApp/defaults/apis.ts rename to packages/app-defaults/src/defaults/apis.ts diff --git a/packages/app-defaults/src/createApp/defaults/components.test.tsx b/packages/app-defaults/src/defaults/components.test.tsx similarity index 100% rename from packages/app-defaults/src/createApp/defaults/components.test.tsx rename to packages/app-defaults/src/defaults/components.test.tsx diff --git a/packages/app-defaults/src/createApp/defaults/components.tsx b/packages/app-defaults/src/defaults/components.tsx similarity index 100% rename from packages/app-defaults/src/createApp/defaults/components.tsx rename to packages/app-defaults/src/defaults/components.tsx diff --git a/packages/app-defaults/src/createApp/defaults/icons.tsx b/packages/app-defaults/src/defaults/icons.tsx similarity index 98% rename from packages/app-defaults/src/createApp/defaults/icons.tsx rename to packages/app-defaults/src/defaults/icons.tsx index ba2531213d..fa578a263a 100644 --- a/packages/app-defaults/src/createApp/defaults/icons.tsx +++ b/packages/app-defaults/src/defaults/icons.tsx @@ -32,7 +32,7 @@ import MuiPeopleIcon from '@material-ui/icons/People'; import MuiPersonIcon from '@material-ui/icons/Person'; import MuiWarningIcon from '@material-ui/icons/Warning'; -export const icons = () => ({ +export const icons = { brokenImage: MuiBrokenImageIcon as IconComponent, // To be confirmed: see https://github.com/backstage/backstage/issues/4970 catalog: MuiMenuBookIcon as IconComponent, @@ -52,4 +52,4 @@ export const icons = () => ({ 'kind:user': MuiPersonIcon as IconComponent, user: MuiPersonIcon as IconComponent, warning: MuiWarningIcon as IconComponent, -}); +}; diff --git a/packages/app-defaults/src/createApp/defaults/index.ts b/packages/app-defaults/src/defaults/index.ts similarity index 100% rename from packages/app-defaults/src/createApp/defaults/index.ts rename to packages/app-defaults/src/defaults/index.ts diff --git a/packages/app-defaults/src/createApp/defaults/themes.tsx b/packages/app-defaults/src/defaults/themes.tsx similarity index 100% rename from packages/app-defaults/src/createApp/defaults/themes.tsx rename to packages/app-defaults/src/defaults/themes.tsx diff --git a/packages/app-defaults/src/index.ts b/packages/app-defaults/src/index.ts index 62ef4d9077..16a6a693a5 100644 --- a/packages/app-defaults/src/index.ts +++ b/packages/app-defaults/src/index.ts @@ -20,4 +20,5 @@ * @packageDocumentation */ -export * from './createApp'; +export { createApp } from './createApp'; +export type { OptionalAppOptions } from './createApp'; diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index a0877cf933..fc6c103109 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -29,6 +29,7 @@ "clean": "backstage-cli clean" }, "dependencies": { + "@backstage/app-defaults": "^0.1.0", "@backstage/core-components": "^0.7.3", "@backstage/config": "^0.1.11", "@backstage/core-plugin-api": "^0.1.13", diff --git a/packages/core-app-api/src/app/AppManager.tsx b/packages/core-app-api/src/app/AppManager.tsx index ff501aa100..950f57bb7d 100644 --- a/packages/core-app-api/src/app/AppManager.tsx +++ b/packages/core-app-api/src/app/AppManager.tsx @@ -78,6 +78,7 @@ import { SignInResult, } from './types'; import { AppThemeProvider } from './AppThemeProvider'; +import { defaultConfigLoader } from './defaultConfigLoader'; export function generateBoundRoutes(bindRoutes: AppOptions['bindRoutes']) { const result = new Map(); @@ -122,17 +123,6 @@ function getBasePath(configApi: Config) { return pathname; } -type FullAppOptions = { - apis: Iterable; - icons: NonNullable; - plugins: BackstagePlugin[]; - components: AppComponents; - themes: (Partial & Omit)[]; - configLoader?: AppConfigLoader; - defaultApis: Iterable; - bindRoutes?: AppOptions['bindRoutes']; -}; - function useConfigLoader( configLoader: AppConfigLoader | undefined, components: AppComponents, @@ -202,14 +192,16 @@ export class AppManager implements BackstageApp { private readonly identityApi = new AppIdentity(); private readonly apiFactoryRegistry: ApiFactoryRegistry; - constructor(options: FullAppOptions) { - this.apis = options.apis; + constructor(options: AppOptions) { + this.apis = options.apis ?? []; this.icons = options.icons; - this.plugins = new Set(options.plugins); + this.plugins = new Set( + (options.plugins as BackstagePlugin[]) ?? [], + ); this.components = options.components; this.themes = options.themes as AppTheme[]; - this.configLoader = options.configLoader; - this.defaultApis = options.defaultApis; + this.configLoader = options.configLoader ?? defaultConfigLoader; + this.defaultApis = options.defaultApis ?? []; this.bindRoutes = options.bindRoutes; this.apiFactoryRegistry = new ApiFactoryRegistry(); } diff --git a/packages/core-app-api/src/app/createApp.test.tsx b/packages/core-app-api/src/app/createApp.test.tsx deleted file mode 100644 index 0bf90987db..0000000000 --- a/packages/core-app-api/src/app/createApp.test.tsx +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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 { screen } from '@testing-library/react'; -import { renderWithEffects } from '@backstage/test-utils'; -import React, { PropsWithChildren } from 'react'; -import { MemoryRouter } from 'react-router-dom'; -import { defaultConfigLoader, createApp } from './createApp'; - -(process as any).env = { NODE_ENV: 'test' }; -const anyEnv = process.env as any; -const anyWindow = window as any; - -describe('defaultConfigLoader', () => { - afterEach(() => { - delete anyEnv.APP_CONFIG; - delete anyWindow.__APP_CONFIG__; - }); - - it('loads static config', async () => { - anyEnv.APP_CONFIG = [ - { data: { my: 'config' }, context: 'a' }, - { data: { my: 'override-config' }, context: 'b' }, - ]; - - const configs = await defaultConfigLoader(); - expect(configs).toEqual([ - { data: { my: 'config' }, context: 'a' }, - { data: { my: 'override-config' }, context: 'b' }, - ]); - }); - - it('loads runtime config', async () => { - anyEnv.APP_CONFIG = [ - { data: { my: 'override-config' }, context: 'a' }, - { data: { my: 'config' }, context: 'b' }, - ]; - - const configs = await (defaultConfigLoader as any)( - '{"my":"runtime-config"}', - ); - expect(configs).toEqual([ - { data: { my: 'override-config' }, context: 'a' }, - { data: { my: 'config' }, context: 'b' }, - { data: { my: 'runtime-config' }, context: 'env' }, - ]); - }); - - it('fails to load invalid missing config', async () => { - await expect(defaultConfigLoader()).rejects.toThrow( - 'No static configuration provided', - ); - }); - - it('fails to load invalid static config', async () => { - anyEnv.APP_CONFIG = { my: 'invalid-config' }; - await expect(defaultConfigLoader()).rejects.toThrow( - 'Static configuration has invalid format', - ); - }); - - it('fails to load bad runtime config', async () => { - anyEnv.APP_CONFIG = [{ data: { my: 'config' }, context: 'a' }]; - - await expect((defaultConfigLoader as any)('}')).rejects.toThrow( - 'Failed to load runtime configuration, SyntaxError: Unexpected token } in JSON at position 0', - ); - }); - - it('loads config from window.__APP_CONFIG__', async () => { - anyEnv.APP_CONFIG = [ - { data: { my: 'config' }, context: 'a' }, - { data: { my: 'override-config' }, context: 'b' }, - ]; - const windowConfig = { app: { configKey: 'config-value' } }; - anyWindow.__APP_CONFIG__ = windowConfig; - - const configs = await defaultConfigLoader(); - - expect(configs).toEqual([ - ...anyEnv.APP_CONFIG, - { context: 'window', data: windowConfig }, - ]); - }); -}); - -describe('Optional ThemeProvider', () => { - it('should render app with user-provided ThemeProvider', async () => { - const components = { - NotFoundErrorPage: () => null, - BootErrorPage: () => null, - Progress: () => null, - Router: MemoryRouter, - ErrorBoundaryFallback: () => null, - ThemeProvider: ({ children }: PropsWithChildren<{}>) => ( -
{children}
- ), - }; - - const App = createApp({ components }).getProvider(); - - await renderWithEffects(); - - expect(screen.getByRole('main')).toBeInTheDocument(); - }); -}); diff --git a/packages/core-app-api/src/app/createApp.tsx b/packages/core-app-api/src/app/createApp.tsx index 5bb5607a37..a020308d91 100644 --- a/packages/core-app-api/src/app/createApp.tsx +++ b/packages/core-app-api/src/app/createApp.tsx @@ -14,132 +14,25 @@ * limitations under the License. */ -import { AppConfig } from '@backstage/config'; -import { JsonObject } from '@backstage/types'; -import { withDefaults } from '@backstage/core-components'; -import { AppManager } from './AppManager'; -import { AppComponents, AppConfigLoader, AppOptions } from './types'; -import { defaultApis } from './defaultApis'; -import { BackstagePlugin } from '@backstage/core-plugin-api'; - -const REQUIRED_APP_COMPONENTS: Array = [ - 'Progress', - 'Router', - 'NotFoundErrorPage', - 'BootErrorPage', - 'ErrorBoundaryFallback', -]; - -/** - * The default config loader, which expects that config is available at compile-time - * in `process.env.APP_CONFIG`. APP_CONFIG should be an array of config objects as - * returned by the config loader. - * - * It will also load runtime config from the __APP_INJECTED_RUNTIME_CONFIG__ string, - * which can be rewritten at runtime to contain an additional JSON config object. - * If runtime config is present, it will be placed first in the config array, overriding - * other config values. - * - * @public - */ -export const defaultConfigLoader: AppConfigLoader = async ( - // This string may be replaced at runtime to provide additional config. - // It should be replaced by a JSON-serialized config object. - // It's a param so we can test it, but at runtime this will always fall back to default. - runtimeConfigJson: string = '__APP_INJECTED_RUNTIME_CONFIG__', -) => { - const appConfig = process.env.APP_CONFIG; - if (!appConfig) { - throw new Error('No static configuration provided'); - } - if (!Array.isArray(appConfig)) { - throw new Error('Static configuration has invalid format'); - } - const configs = appConfig.slice() as unknown as AppConfig[]; - - // Avoiding this string also being replaced at runtime - if ( - runtimeConfigJson !== - '__app_injected_runtime_config__'.toLocaleUpperCase('en-US') - ) { - try { - const data = JSON.parse(runtimeConfigJson) as JsonObject; - if (Array.isArray(data)) { - configs.push(...data); - } else { - configs.push({ data, context: 'env' }); - } - } catch (error) { - throw new Error(`Failed to load runtime configuration, ${error}`); - } - } - - const windowAppConfig = (window as any).__APP_CONFIG__; - if (windowAppConfig) { - configs.push({ - context: 'window', - data: windowAppConfig, - }); - } - return configs; -}; +import { + createApp as createDefaultApp, + OptionalAppOptions, +} from '@backstage/app-defaults'; /** * Creates a new Backstage App. * + * @deprecated Use createApp from @backstage/app-defaults instead + * @param options - A set of options for creating the app * @public */ -export function createApp(options?: AppOptions) { - const optionsWithDefaults = withDefaults(options); - - const missingRequiredComponents = REQUIRED_APP_COMPONENTS.filter( - name => !options?.components?.[name], +export function createApp(options?: OptionalAppOptions) { + // eslint-disable-next-line no-console + console.warn( + 'DEPRECATION WARNING: The createApp function from @backstage/core-app-api will soon be removed, ' + + 'migrate to importing createApp from the @backstage/app-defaults package instead. ' + + 'If you do not wish to use a standard app configuration but instead supply all options yourself ' + + ' you can use createSpecializedApp from @backstage/core-app-api instead.', ); - if (missingRequiredComponents.length > 0) { - // eslint-disable-next-line no-console - console.warn( - 'DEPRECATION WARNING: The createApp options will soon require a minimal set of components to ' + - 'be provided. You can use the default components by using withDefaults from @backstage/core-components ' + - 'like this: createApp(withDefaults({ ... })), or you can provide the components yourself. ' + - `The following components are missing: ${missingRequiredComponents.join( - ', ', - )}`, - ); - } - - const providedIconKeys = Object.keys(options?.icons ?? {}); - const missingIconKeys = Object.keys(optionsWithDefaults.icons!).filter( - key => !providedIconKeys.includes(key), - ); - if (missingIconKeys.length > 0) { - // eslint-disable-next-line no-console - console.warn( - 'DEPRECATION WARNING: The createApp options will soon require a minimal set of icons to ' + - 'be provided. You can use the default icons by using withDefaults from @backstage/core-components ' + - 'like this: createApp(withDefaults({ ... })), or you can provide the icons yourself. ' + - `The following icons are missing: ${missingIconKeys.join(', ')}`, - ); - } - - if (!options?.themes) { - // eslint-disable-next-line no-console - console.warn( - 'DEPRECATION WARNING: The createApp options will soon require the themes to be provided. ' + - 'You can use the default themes by using withDefaults from @backstage/core-components ' + - 'like this: createApp(withDefaults({ ... })), or you can provide the themes yourself. ', - ); - } - - const { icons, themes, components } = optionsWithDefaults; - - return new AppManager({ - icons: icons!, - themes: themes!, - components: components! as AppComponents, - defaultApis, - apis: options?.apis ?? [], - bindRoutes: options?.bindRoutes, - plugins: (options?.plugins as BackstagePlugin[]) ?? [], - configLoader: options?.configLoader ?? defaultConfigLoader, - }); + return createDefaultApp(options); } diff --git a/packages/app-defaults/src/createApp/index.ts b/packages/core-app-api/src/app/createSpecializedApp.tsx similarity index 64% rename from packages/app-defaults/src/createApp/index.ts rename to packages/core-app-api/src/app/createSpecializedApp.tsx index dea36bb3e7..54e8d6cddf 100644 --- a/packages/app-defaults/src/createApp/index.ts +++ b/packages/core-app-api/src/app/createSpecializedApp.tsx @@ -14,4 +14,15 @@ * limitations under the License. */ -export { createApp } from './createApp'; +import { AppManager } from './AppManager'; +import { AppOptions } from './types'; + +/** + * Creates a new Backstage App where the full set of options are required. + * + * @param options - A set of options for creating the app + * @returns + */ +export function createSpecializedApp(options: AppOptions) { + return new AppManager(options); +} diff --git a/packages/core-app-api/src/app/defaultApis.ts b/packages/core-app-api/src/app/defaultApis.ts deleted file mode 100644 index fb02274cf0..0000000000 --- a/packages/core-app-api/src/app/defaultApis.ts +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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 { - AlertApiForwarder, - NoOpAnalyticsApi, - ErrorApiForwarder, - ErrorAlerter, - GoogleAuth, - GithubAuth, - OAuth2, - OktaAuth, - GitlabAuth, - Auth0Auth, - MicrosoftAuth, - BitbucketAuth, - OAuthRequestManager, - WebStorage, - UrlPatternDiscovery, - SamlAuth, - OneLoginAuth, - UnhandledErrorForwarder, - AtlassianAuth, -} from '../apis'; - -import { - createApiFactory, - alertApiRef, - analyticsApiRef, - errorApiRef, - discoveryApiRef, - oauthRequestApiRef, - googleAuthApiRef, - githubAuthApiRef, - oauth2ApiRef, - oktaAuthApiRef, - gitlabAuthApiRef, - auth0AuthApiRef, - microsoftAuthApiRef, - storageApiRef, - configApiRef, - samlAuthApiRef, - oneloginAuthApiRef, - oidcAuthApiRef, - bitbucketAuthApiRef, - atlassianAuthApiRef, -} from '@backstage/core-plugin-api'; - -import OAuth2Icon from '@material-ui/icons/AcUnit'; - -export const defaultApis = [ - createApiFactory({ - api: discoveryApiRef, - deps: { configApi: configApiRef }, - factory: ({ configApi }) => - UrlPatternDiscovery.compile( - `${configApi.getString('backend.baseUrl')}/api/{{ pluginId }}`, - ), - }), - createApiFactory(alertApiRef, new AlertApiForwarder()), - createApiFactory(analyticsApiRef, new NoOpAnalyticsApi()), - createApiFactory({ - api: errorApiRef, - deps: { alertApi: alertApiRef }, - factory: ({ alertApi }) => { - const errorApi = new ErrorAlerter(alertApi, new ErrorApiForwarder()); - UnhandledErrorForwarder.forward(errorApi, { hidden: false }); - return errorApi; - }, - }), - createApiFactory({ - api: storageApiRef, - deps: { errorApi: errorApiRef }, - factory: ({ errorApi }) => WebStorage.create({ errorApi }), - }), - createApiFactory(oauthRequestApiRef, new OAuthRequestManager()), - createApiFactory({ - api: googleAuthApiRef, - deps: { - discoveryApi: discoveryApiRef, - oauthRequestApi: oauthRequestApiRef, - configApi: configApiRef, - }, - factory: ({ discoveryApi, oauthRequestApi, configApi }) => - GoogleAuth.create({ - discoveryApi, - oauthRequestApi, - environment: configApi.getOptionalString('auth.environment'), - }), - }), - createApiFactory({ - api: microsoftAuthApiRef, - deps: { - discoveryApi: discoveryApiRef, - oauthRequestApi: oauthRequestApiRef, - configApi: configApiRef, - }, - factory: ({ discoveryApi, oauthRequestApi, configApi }) => - MicrosoftAuth.create({ - discoveryApi, - oauthRequestApi, - environment: configApi.getOptionalString('auth.environment'), - }), - }), - createApiFactory({ - api: githubAuthApiRef, - deps: { - discoveryApi: discoveryApiRef, - oauthRequestApi: oauthRequestApiRef, - configApi: configApiRef, - }, - factory: ({ discoveryApi, oauthRequestApi, configApi }) => - GithubAuth.create({ - discoveryApi, - oauthRequestApi, - defaultScopes: ['read:user'], - environment: configApi.getOptionalString('auth.environment'), - }), - }), - createApiFactory({ - api: oktaAuthApiRef, - deps: { - discoveryApi: discoveryApiRef, - oauthRequestApi: oauthRequestApiRef, - configApi: configApiRef, - }, - factory: ({ discoveryApi, oauthRequestApi, configApi }) => - OktaAuth.create({ - discoveryApi, - oauthRequestApi, - environment: configApi.getOptionalString('auth.environment'), - }), - }), - createApiFactory({ - api: gitlabAuthApiRef, - deps: { - discoveryApi: discoveryApiRef, - oauthRequestApi: oauthRequestApiRef, - configApi: configApiRef, - }, - factory: ({ discoveryApi, oauthRequestApi, configApi }) => - GitlabAuth.create({ - discoveryApi, - oauthRequestApi, - environment: configApi.getOptionalString('auth.environment'), - }), - }), - createApiFactory({ - api: auth0AuthApiRef, - deps: { - discoveryApi: discoveryApiRef, - oauthRequestApi: oauthRequestApiRef, - configApi: configApiRef, - }, - factory: ({ discoveryApi, oauthRequestApi, configApi }) => - Auth0Auth.create({ - discoveryApi, - oauthRequestApi, - environment: configApi.getOptionalString('auth.environment'), - }), - }), - createApiFactory({ - api: oauth2ApiRef, - deps: { - discoveryApi: discoveryApiRef, - oauthRequestApi: oauthRequestApiRef, - configApi: configApiRef, - }, - factory: ({ discoveryApi, oauthRequestApi, configApi }) => - OAuth2.create({ - discoveryApi, - oauthRequestApi, - environment: configApi.getOptionalString('auth.environment'), - }), - }), - createApiFactory({ - api: samlAuthApiRef, - deps: { - discoveryApi: discoveryApiRef, - configApi: configApiRef, - }, - factory: ({ discoveryApi, configApi }) => - SamlAuth.create({ - discoveryApi, - environment: configApi.getOptionalString('auth.environment'), - }), - }), - createApiFactory({ - api: oneloginAuthApiRef, - deps: { - discoveryApi: discoveryApiRef, - oauthRequestApi: oauthRequestApiRef, - configApi: configApiRef, - }, - factory: ({ discoveryApi, oauthRequestApi, configApi }) => - OneLoginAuth.create({ - discoveryApi, - oauthRequestApi, - environment: configApi.getOptionalString('auth.environment'), - }), - }), - createApiFactory({ - api: oidcAuthApiRef, - deps: { - discoveryApi: discoveryApiRef, - oauthRequestApi: oauthRequestApiRef, - configApi: configApiRef, - }, - factory: ({ discoveryApi, oauthRequestApi, configApi }) => - OAuth2.create({ - discoveryApi, - oauthRequestApi, - provider: { - id: 'oidc', - title: 'Your Identity Provider', - icon: OAuth2Icon, - }, - environment: configApi.getOptionalString('auth.environment'), - }), - }), - createApiFactory({ - api: bitbucketAuthApiRef, - deps: { - discoveryApi: discoveryApiRef, - oauthRequestApi: oauthRequestApiRef, - configApi: configApiRef, - }, - factory: ({ discoveryApi, oauthRequestApi, configApi }) => - BitbucketAuth.create({ - discoveryApi, - oauthRequestApi, - defaultScopes: ['team'], - environment: configApi.getOptionalString('auth.environment'), - }), - }), - createApiFactory({ - api: atlassianAuthApiRef, - deps: { - discoveryApi: discoveryApiRef, - oauthRequestApi: oauthRequestApiRef, - configApi: configApiRef, - }, - factory: ({ discoveryApi, oauthRequestApi, configApi }) => { - return AtlassianAuth.create({ - discoveryApi, - oauthRequestApi, - environment: configApi.getOptionalString('auth.environment'), - }); - }, - }), -]; diff --git a/packages/core-app-api/src/app/index.ts b/packages/core-app-api/src/app/index.ts index 5514d7ee76..ba6a8917a4 100644 --- a/packages/core-app-api/src/app/index.ts +++ b/packages/core-app-api/src/app/index.ts @@ -15,6 +15,7 @@ */ export { createApp } from './createApp'; +export { createSpecializedApp } from './createSpecializedApp'; export { defaultConfigLoader } from './defaultConfigLoader'; export type { AppIcons } from './icons'; export * from './types'; diff --git a/packages/core-app-api/src/app/types.ts b/packages/core-app-api/src/app/types.ts index 41e997e179..89a70ed566 100644 --- a/packages/core-app-api/src/app/types.ts +++ b/packages/core-app-api/src/app/types.ts @@ -200,10 +200,19 @@ export type AppOptions = { */ apis?: Iterable; + /** + * A collection of ApiFactories to register in the application as default APIs. + * Theses APIs can not be overridden by plugin factories, but can be overridden + * by plugin APIs provided through the + * A collection of ApiFactories to register in the application to either + * add add new ones, or override factories provided by default or by plugins. + */ + defaultApis?: Iterable; + /** * Supply icons to override the default ones. */ - icons?: Partial & { [key in string]: IconComponent }; + icons: AppIcons & { [key in string]: IconComponent }; /** * A list of all plugins to include in the app. @@ -213,7 +222,7 @@ export type AppOptions = { /** * Supply components to the app to override the default ones. */ - components?: Partial; + components: AppComponents; /** * Themes provided as a part of the app. By default two themes are included, one @@ -245,7 +254,7 @@ export type AppOptions = { * }] * ``` */ - themes?: (Partial & Omit)[]; + themes: (Partial & Omit)[]; /** * A function that loads in App configuration that will be accessible via From 0fb57d28e36d6c26be419e431c61665daf4eb944 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 28 Oct 2021 15:31:59 +0200 Subject: [PATCH 173/196] dev-utils,test-utils,app: update to use app-defaults Signed-off-by: Patrik Oldsberg --- packages/app/package.json | 1 + packages/app/src/App.tsx | 80 +++++++++---------- packages/dev-utils/package.json | 1 + packages/dev-utils/src/devApp/render.tsx | 32 ++++---- .../test-utils/src/testUtils/appWrappers.tsx | 27 ++++++- 5 files changed, 81 insertions(+), 60 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index 833e146d45..9a1fb108f6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -4,6 +4,7 @@ "private": true, "bundled": true, "dependencies": { + "@backstage/app-defaults": "^0.1.0", "@backstage/catalog-model": "^0.9.5", "@backstage/cli": "^0.8.0", "@backstage/core-app-api": "^0.1.18", diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index d3ab0649e9..401665b62e 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -26,12 +26,12 @@ import { RELATION_PART_OF, RELATION_PROVIDES_API, } from '@backstage/catalog-model'; -import { createApp, FlatRoutes } from '@backstage/core-app-api'; +import { createApp } from '@backstage/app-defaults'; +import { FlatRoutes } from '@backstage/core-app-api'; import { AlertDisplay, OAuthRequestDialog, SignInPage, - withDefaults, } from '@backstage/core-components'; import { apiDocsPlugin, ApiExplorerPage } from '@backstage/plugin-api-docs'; import { @@ -87,46 +87,44 @@ import * as plugins from './plugins'; import { techDocsPage } from './components/techdocs/TechDocsPage'; -const app = createApp( - withDefaults({ - apis, - plugins: Object.values(plugins), - icons: { - // Custom icon example - alert: AlarmIcon, +const app = createApp({ + apis, + plugins: Object.values(plugins), + icons: { + // Custom icon example + alert: AlarmIcon, + }, + components: { + SignInPage: props => { + return ( + + ); }, - components: { - SignInPage: props => { - return ( - - ); - }, - }, - bindRoutes({ bind }) { - bind(catalogPlugin.externalRoutes, { - createComponent: scaffolderPlugin.routes.root, - viewTechDoc: techdocsPlugin.routes.docRoot, - }); - bind(catalogGraphPlugin.externalRoutes, { - catalogEntity: catalogPlugin.routes.catalogEntity, - }); - bind(apiDocsPlugin.externalRoutes, { - createComponent: scaffolderPlugin.routes.root, - }); - bind(explorePlugin.externalRoutes, { - catalogEntity: catalogPlugin.routes.catalogEntity, - }); - bind(scaffolderPlugin.externalRoutes, { - registerComponent: catalogImportPlugin.routes.importPage, - }); - }, - }), -); + }, + bindRoutes({ bind }) { + bind(catalogPlugin.externalRoutes, { + createComponent: scaffolderPlugin.routes.root, + viewTechDoc: techdocsPlugin.routes.docRoot, + }); + bind(catalogGraphPlugin.externalRoutes, { + catalogEntity: catalogPlugin.routes.catalogEntity, + }); + bind(apiDocsPlugin.externalRoutes, { + createComponent: scaffolderPlugin.routes.root, + }); + bind(explorePlugin.externalRoutes, { + catalogEntity: catalogPlugin.routes.catalogEntity, + }); + bind(scaffolderPlugin.externalRoutes, { + registerComponent: catalogImportPlugin.routes.importPage, + }); + }, +}); const AppProvider = app.getProvider(); const AppRouter = app.getRouter(); diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index e01390e169..0846ee8e4f 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -29,6 +29,7 @@ "clean": "backstage-cli clean" }, "dependencies": { + "@backstage/app-defaults": "^0.1.0", "@backstage/core-app-api": "^0.1.18", "@backstage/core-components": "^0.7.1", "@backstage/core-plugin-api": "^0.1.11", diff --git a/packages/dev-utils/src/devApp/render.tsx b/packages/dev-utils/src/devApp/render.tsx index bcaac891fe..d4b1c8db49 100644 --- a/packages/dev-utils/src/devApp/render.tsx +++ b/packages/dev-utils/src/devApp/render.tsx @@ -32,7 +32,6 @@ import { SidebarItem, SidebarPage, SidebarSpacer, - withDefaults, } from '@backstage/core-components'; import { @@ -48,7 +47,8 @@ import { BackstagePlugin, } from '@backstage/core-plugin-api'; -import { createApp, FlatRoutes } from '@backstage/core-app-api'; +import { createApp } from '@backstage/app-defaults'; +import { FlatRoutes } from '@backstage/core-app-api'; const GatheringRoute: (props: { path: string; @@ -174,22 +174,20 @@ export class DevAppBuilder { ); } - const app = createApp( - withDefaults({ - apis, - plugins: this.plugins, - themes: this.themes, - bindRoutes: ({ bind }) => { - for (const plugin of this.plugins ?? []) { - const targets: Record> = {}; - for (const routeKey of Object.keys(plugin.externalRoutes)) { - targets[routeKey] = dummyRouteRef; - } - bind(plugin.externalRoutes, targets); + const app = createApp({ + apis, + plugins: this.plugins, + themes: this.themes, + bindRoutes: ({ bind }) => { + for (const plugin of this.plugins ?? []) { + const targets: Record> = {}; + for (const routeKey of Object.keys(plugin.externalRoutes)) { + targets[routeKey] = dummyRouteRef; } - }, - }), - ); + bind(plugin.externalRoutes, targets); + } + }, + }); const AppProvider = app.getProvider(); const AppRouter = app.getRouter(); diff --git a/packages/test-utils/src/testUtils/appWrappers.tsx b/packages/test-utils/src/testUtils/appWrappers.tsx index 6f55cc6190..890d89e3fe 100644 --- a/packages/test-utils/src/testUtils/appWrappers.tsx +++ b/packages/test-utils/src/testUtils/appWrappers.tsx @@ -18,7 +18,7 @@ import React, { ComponentType, ReactNode, ReactElement } from 'react'; import { MemoryRouter } from 'react-router'; import { Route } from 'react-router-dom'; import { lightTheme } from '@backstage/theme'; -import { createApp } from '@backstage/core-app-api'; +import { createSpecializedApp } from '@backstage/core-app-api'; import { BootErrorPageProps, RouteRef, @@ -30,6 +30,28 @@ import { RenderResult } from '@testing-library/react'; import { renderWithEffects } from './testingLibrary'; import { mockApis } from './mockApis'; +const mockIcons = { + 'kind:api': () => , + 'kind:component': () => , + 'kind:domain': () => , + 'kind:group': () => , + 'kind:location': () => , + 'kind:system': () => , + 'kind:user': () => , + + brokenImage: () => , + catalog: () => , + chat: () => , + dashboard: () => , + docs: () => , + email: () => , + github: () => , + group: () => , + help: () => , + user: () => , + warning: () => , +}; + const ErrorBoundaryFallback = ({ error }: { error: Error }) => { throw new Error(`Reached ErrorBoundaryFallback Page with error, ${error}`); }; @@ -90,7 +112,7 @@ export function wrapInTestApp( const { routeEntries = ['/'] } = options; const boundRoutes = new Map(); - const app = createApp({ + const app = createSpecializedApp({ apis: mockApis, // Bit of a hack to make sure that the default config loader isn't used // as that would force every single test to wait for config loading. @@ -104,6 +126,7 @@ export function wrapInTestApp( ), }, + icons: mockIcons, plugins: [], themes: [ { From 687fca4ff76d9a7bf8fb9edf20f3ce39d421ee0b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 29 Oct 2021 16:06:42 +0200 Subject: [PATCH 174/196] changesets,create-app: update for createApp split Signed-off-by: Patrik Oldsberg --- .changeset/giant-drinks-wave.md | 2 +- .changeset/hot-walls-fail.md | 17 ++------ .changeset/late-rice-sit.md | 5 --- .changeset/twenty-swans-matter.md | 12 ++---- .../default-app/packages/app/src/App.tsx | 41 ++++++++----------- 5 files changed, 26 insertions(+), 51 deletions(-) delete mode 100644 .changeset/late-rice-sit.md diff --git a/.changeset/giant-drinks-wave.md b/.changeset/giant-drinks-wave.md index 4c2f754c2c..ecf4c8fbdd 100644 --- a/.changeset/giant-drinks-wave.md +++ b/.changeset/giant-drinks-wave.md @@ -2,4 +2,4 @@ '@backstage/dev-utils': patch --- -Migrated to using `withDefaults` to pass defaults to `createApp`. +Migrated to using `@backstage/app-defaults`. diff --git a/.changeset/hot-walls-fail.md b/.changeset/hot-walls-fail.md index c814354a56..b0854e868c 100644 --- a/.changeset/hot-walls-fail.md +++ b/.changeset/hot-walls-fail.md @@ -2,21 +2,12 @@ '@backstage/create-app': patch --- -Migrated the app template use the new `withDefaults` to construct the `createApp` options, as not doing this has been deprecated and will need to be done in the future. +Migrated the app template use the new `@backstage/app-defaults` for the `createApp` import, since the `createApp` exported by `@backstage/app-core-api` will be removed in the future. To migrate an existing application, make the following change to `packages/app/src/App.tsx`: ```diff -+import { withDefaults } from '@backstage/core-components'; - - // ... - --const app = createApp({ -+const app = createApp(withDefaults({ - apis, - bindRoutes({ bind }) { - ... - }, --}); -+})); +-import { createApp, FlatRoutes } from '@backstage/core-app-api'; ++import { createApp } from '@backstage/app-defaults'; ++import { FlatRoutes } from '@backstage/core-app-api'; ``` diff --git a/.changeset/late-rice-sit.md b/.changeset/late-rice-sit.md deleted file mode 100644 index 778215038f..0000000000 --- a/.changeset/late-rice-sit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -Added a new `withDefaults` method that accepts a set of `AppOptions` and add the default components, themes and icons. It is intended to be used together with `createApp` from `@backstage/core-app-api` like this: `createApp(withDefaults({ ... }))`. diff --git a/.changeset/twenty-swans-matter.md b/.changeset/twenty-swans-matter.md index 6dea7a089f..55cd3d6dc3 100644 --- a/.changeset/twenty-swans-matter.md +++ b/.changeset/twenty-swans-matter.md @@ -2,14 +2,8 @@ '@backstage/core-app-api': patch --- -Deprecated the defaulting of the `components`, `icons` and `themes` options of `createApp`, meaning it will become required in the future. When not passing the required options a deprecation warning is currently logged, and they will become required in a future release. +The `createApp` function from `@backstage/core-app-api` has been deprecated, with two new options being provided as a replacement. -The keep using the default set of options, migrate to using `withDefaults` from `@backstage/core-components`: +The first and most commonly used one is `createApp` from the new `@backstage/app-defaults` package, which behaves just like the existing `createApp`. In the future this method is likely to be expanded to add more APIs and other pieces into the default setup, for example the Utility APIs from `@backstage/integration-react`. -```ts -const app = createApp( - withDefaults({ - // ... - }), -); -``` +The other option that we now provide is to use `createSpecializedApp` from `@backstage/core-app-api`. This is a more low-level API where you need to provide a full set of options, including your own `components`, `icons`, `defaultApis`, and `themes`. The `createSpecializedApp` way of creating an app is particularly useful if you are not using `@backstage/core-components` or MUI, as it allows you to avoid those dependencies completely. diff --git a/packages/create-app/templates/default-app/packages/app/src/App.tsx b/packages/create-app/templates/default-app/packages/app/src/App.tsx index a6d34e6687..8a535835b4 100644 --- a/packages/create-app/templates/default-app/packages/app/src/App.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/App.tsx @@ -25,30 +25,25 @@ import { entityPage } from './components/catalog/EntityPage'; import { searchPage } from './components/search/SearchPage'; import { Root } from './components/Root'; -import { - AlertDisplay, - withDefaults, - OAuthRequestDialog, -} from '@backstage/core-components'; -import { createApp, FlatRoutes } from '@backstage/core-app-api'; +import { AlertDisplay, OAuthRequestDialog } from '@backstage/core-components'; +import { createApp } from '@backstage/app-defaults'; +import { FlatRoutes } from '@backstage/core-app-api'; -const app = createApp( - withDefaults({ - apis, - bindRoutes({ bind }) { - bind(catalogPlugin.externalRoutes, { - createComponent: scaffolderPlugin.routes.root, - viewTechDoc: techdocsPlugin.routes.docRoot, - }); - bind(apiDocsPlugin.externalRoutes, { - createComponent: scaffolderPlugin.routes.root, - }); - bind(scaffolderPlugin.externalRoutes, { - registerComponent: catalogImportPlugin.routes.importPage, - }); - }, - }), -); +const app = createApp({ + apis, + bindRoutes({ bind }) { + bind(catalogPlugin.externalRoutes, { + createComponent: scaffolderPlugin.routes.root, + viewTechDoc: techdocsPlugin.routes.docRoot, + }); + bind(apiDocsPlugin.externalRoutes, { + createComponent: scaffolderPlugin.routes.root, + }); + bind(scaffolderPlugin.externalRoutes, { + registerComponent: catalogImportPlugin.routes.importPage, + }); + }, +}); const AppProvider = app.getProvider(); const AppRouter = app.getRouter(); From b7958c36dbc2608fb18c75b2f87ed7823b064b7f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 29 Oct 2021 16:46:37 +0200 Subject: [PATCH 175/196] core-components: wrap page stories in test app Signed-off-by: Patrik Oldsberg --- .../src/layout/Page/Page.stories.tsx | 82 ++++++++----------- 1 file changed, 36 insertions(+), 46 deletions(-) diff --git a/packages/core-components/src/layout/Page/Page.stories.tsx b/packages/core-components/src/layout/Page/Page.stories.tsx index 7386d198a3..12ddd51d04 100644 --- a/packages/core-components/src/layout/Page/Page.stories.tsx +++ b/packages/core-components/src/layout/Page/Page.stories.tsx @@ -20,8 +20,7 @@ import Grid from '@material-ui/core/Grid'; import Link from '@material-ui/core/Link'; import Typography from '@material-ui/core/Typography'; import React, { useState } from 'react'; -import { MemoryRouter } from 'react-router'; -import { createApp } from '@backstage/core-app-api'; +import { wrapInTestApp } from '@backstage/test-utils'; import { Content, ContentHeader, @@ -197,53 +196,44 @@ const ExampleContentHeader = ({ selectedTab }: { selectedTab?: number }) => ( ); -const app = createApp({ configLoader: async () => [] }); -const AppProvider = app.getProvider(); - export const PluginWithData = () => { const [selectedTab, setSelectedTab] = useState(2); - return ( - - -
- - - setSelectedTab(index)} - tabs={tabs.map(({ label }, index) => ({ - id: index.toString(), - label, - }))} - /> - - - - - -
-
-
- ); + return wrapInTestApp(() => ( +
+ + + setSelectedTab(index)} + tabs={tabs.map(({ label }, index) => ({ + id: index.toString(), + label, + }))} + /> + + + + + +
+ )); }; export const PluginWithTable = () => { - return ( - -
- - - - -
- - - - - ); + return wrapInTestApp(() => ( +
+ + + + +
+ + + + )); }; From c1578d609b1daf42f3aaece1eb69a9fbdf391f15 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 29 Oct 2021 16:49:45 +0200 Subject: [PATCH 176/196] update API reports for createApp split Signed-off-by: Patrik Oldsberg --- packages/app-defaults/api-report.md | 26 ++++++++++++++++++++ packages/core-app-api/api-report.md | 20 +++++++++++----- packages/core-components/api-report.md | 33 -------------------------- packages/core-plugin-api/api-report.md | 2 +- 4 files changed, 41 insertions(+), 40 deletions(-) create mode 100644 packages/app-defaults/api-report.md diff --git a/packages/app-defaults/api-report.md b/packages/app-defaults/api-report.md new file mode 100644 index 0000000000..6b35dab81b --- /dev/null +++ b/packages/app-defaults/api-report.md @@ -0,0 +1,26 @@ +## API Report File for "@backstage/app-defaults" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { AppComponents } from '@backstage/core-app-api'; +import { AppIcons } from '@backstage/core-app-api'; +import { AppManager } from '@backstage/core-app-api/src/app/AppManager'; +import { AppOptions } from '@backstage/core-app-api'; +import { AppTheme } from '@backstage/core-plugin-api'; +import { IconComponent } from '@backstage/core-plugin-api'; + +// @public +export function createApp( + options?: Omit & OptionalAppOptions, +): AppManager; + +// @public +export type OptionalAppOptions = { + icons?: Partial & { + [key in string]: IconComponent; + }; + themes?: (Partial & Omit)[]; + components?: Partial; +}; +``` diff --git a/packages/core-app-api/api-report.md b/packages/core-app-api/api-report.md index b4b1b07ebe..b3b54ff1a0 100644 --- a/packages/core-app-api/api-report.md +++ b/packages/core-app-api/api-report.md @@ -45,6 +45,7 @@ import { Observable } from '@backstage/types'; import { oktaAuthApiRef } from '@backstage/core-plugin-api'; import { oneloginAuthApiRef } from '@backstage/core-plugin-api'; import { OpenIdConnectApi } from '@backstage/core-plugin-api'; +import { OptionalAppOptions } from '@backstage/app-defaults'; import { PendingAuthRequest } from '@backstage/core-plugin-api'; import { PluginOutput } from '@backstage/core-plugin-api'; import { ProfileInfo } from '@backstage/core-plugin-api'; @@ -150,7 +151,7 @@ export type AppComponents = { Progress: ComponentType<{}>; Router: ComponentType<{}>; ErrorBoundaryFallback: ComponentType; - ThemeProvider: ComponentType<{}>; + ThemeProvider?: ComponentType<{}>; SignInPage?: ComponentType; }; @@ -189,12 +190,13 @@ export type AppIcons = { // @public export type AppOptions = { apis?: Iterable; - icons?: Partial & { + defaultApis?: Iterable; + icons: AppIcons & { [key in string]: IconComponent; }; plugins?: BackstagePluginWithAnyOutput[]; - components?: Partial; - themes?: (Partial & Omit)[]; + components: AppComponents; + themes: (Partial & Omit)[]; configLoader?: AppConfigLoader; bindRoutes?(context: { bind: AppRouteBinder }): void; }; @@ -308,10 +310,16 @@ export type BootErrorPageProps = { export { ConfigReader }; -// Warning: (ae-forgotten-export) The symbol "PrivateAppImpl" needs to be exported by the entry point index.d.ts +// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" +// Warning: (ae-forgotten-export) The symbol "AppManager" needs to be exported by the entry point index.d.ts +// +// @public @deprecated +export function createApp(options?: OptionalAppOptions): AppManager; + +// Warning: (ae-missing-release-tag) "createSpecializedApp" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export function createApp(options?: AppOptions): PrivateAppImpl; +export function createSpecializedApp(options: AppOptions): AppManager; // @public export const defaultConfigLoader: AppConfigLoader; diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index f517b1673b..0f1e3510ae 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -5,26 +5,20 @@ ```ts /// -import { AnyApiFactory } from '@backstage/core-plugin-api'; import { ApiRef } from '@backstage/core-plugin-api'; -import { AppConfig } from '@backstage/config'; -import { AppTheme } from '@backstage/core-plugin-api'; import { BackstageIdentityApi } from '@backstage/core-plugin-api'; import { BackstagePalette } from '@backstage/theme'; -import { BackstagePlugin } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; import { ButtonProps as ButtonProps_2 } from '@material-ui/core/Button'; import { CardHeaderProps } from '@material-ui/core/CardHeader'; import { Column } from '@material-table/core'; import { ComponentClass } from 'react'; import { ComponentProps } from 'react'; -import { ComponentType } from 'react'; import { Context } from 'react'; import { default as CSS_2 } from 'csstype'; import { CSSProperties } from 'react'; import { ElementType } from 'react'; import { ErrorInfo } from 'react'; -import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { IconComponent } from '@backstage/core-plugin-api'; import { LinearProgressProps } from '@material-ui/core/LinearProgress'; import { LinkProps as LinkProps_2 } from '@material-ui/core/Link'; @@ -33,15 +27,12 @@ import MaterialBreadcrumbs from '@material-ui/core/Breadcrumbs'; import { MaterialTableProps } from '@material-table/core'; import { NavLinkProps } from 'react-router-dom'; import { Overrides } from '@material-ui/core/styles/overrides'; -import { PluginOutput } from '@backstage/core-plugin-api'; -import { ProfileInfo } from '@backstage/core-plugin-api'; import { ProfileInfoApi } from '@backstage/core-plugin-api'; import { PropsWithChildren } from 'react'; import { default as React_2 } from 'react'; import * as React_3 from 'react'; import { ReactElement } from 'react'; import { ReactNode } from 'react'; -import { RouteRef } from '@backstage/core-plugin-api'; import { SessionApi } from '@backstage/core-plugin-api'; import { SignInPageProps } from '@backstage/core-plugin-api'; import { SparklinesLineProps } from 'react-sparklines'; @@ -49,7 +40,6 @@ import { SparklinesProps } from 'react-sparklines'; import { StyledComponentProps } from '@material-ui/core/styles'; import { StyleRules } from '@material-ui/styles'; import { StyleRules as StyleRules_2 } from '@material-ui/core/styles/withStyles'; -import { SubRouteRef } from '@backstage/core-plugin-api'; import { TabProps } from '@material-ui/core/Tab'; import { TextTruncateProps } from 'react-text-truncate'; import { Theme } from '@material-ui/core/styles'; @@ -681,22 +671,6 @@ export type OAuthRequestDialogClassKey = // @public (undocumented) export type OpenedDropdownClassKey = 'icon'; -// @public -export interface OptionalAppOptions { - // Warning: (ae-forgotten-export) The symbol "AppComponents" needs to be exported by the entry point index.d.ts - // - // (undocumented) - components?: Partial; - // Warning: (ae-forgotten-export) The symbol "AppIcons" needs to be exported by the entry point index.d.ts - // - // (undocumented) - icons?: Partial & { - [key in string]: IconComponent; - }; - // (undocumented) - themes?: (Partial & Omit)[]; -} - // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "OverflowTooltip" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2344,13 +2318,6 @@ export type WarningPanelClassKey = | 'message' | 'details'; -// Warning: (ae-forgotten-export) The symbol "AppOptions" needs to be exported by the entry point index.d.ts -// -// @public -export function withDefaults( - options?: Omit & OptionalAppOptions, -): AppOptions; - // Warnings were encountered during analysis: // // src/components/DependencyGraph/types.d.ts:16:5 - (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/core-components" does not have an export "DependencyNode" diff --git a/packages/core-plugin-api/api-report.md b/packages/core-plugin-api/api-report.md index 433edc57ff..2765b02bca 100644 --- a/packages/core-plugin-api/api-report.md +++ b/packages/core-plugin-api/api-report.md @@ -164,7 +164,7 @@ export type AppComponents = { Progress: ComponentType<{}>; Router: ComponentType<{}>; ErrorBoundaryFallback: ComponentType; - ThemeProvider: ComponentType<{}>; + ThemeProvider?: ComponentType<{}>; SignInPage?: ComponentType; }; From 60e5d356cbab4f5049111cacd57e374bca1ec74f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 10 Nov 2021 01:30:07 +0100 Subject: [PATCH 177/196] core-app-api: add missing core feature icons + move icon things around a bit Signed-off-by: Patrik Oldsberg --- packages/app-defaults/src/defaults/icons.tsx | 6 ++ packages/core-app-api/api-report.md | 5 +- .../core-app-api/src/app/AppManager.test.tsx | 15 ++-- packages/core-app-api/src/app/icons.tsx | 87 ------------------- packages/core-app-api/src/app/index.ts | 1 - packages/core-app-api/src/app/types.ts | 31 ++++++- .../test-utils/src/testUtils/appWrappers.tsx | 3 + 7 files changed, 51 insertions(+), 97 deletions(-) delete mode 100644 packages/core-app-api/src/app/icons.tsx diff --git a/packages/app-defaults/src/defaults/icons.tsx b/packages/app-defaults/src/defaults/icons.tsx index fa578a263a..c4f2d2b7e4 100644 --- a/packages/app-defaults/src/defaults/icons.tsx +++ b/packages/app-defaults/src/defaults/icons.tsx @@ -18,6 +18,9 @@ import { IconComponent } from '@backstage/core-plugin-api'; import MuiApartmentIcon from '@material-ui/icons/Apartment'; import MuiBrokenImageIcon from '@material-ui/icons/BrokenImage'; import MuiCategoryIcon from '@material-ui/icons/Category'; +import MuiCreateNewFolderIcon from '@material-ui/icons/CreateNewFolder'; +import MuiSubjectIcon from '@material-ui/icons/Subject'; +import MuiSearchIcon from '@material-ui/icons/Search'; import MuiChatIcon from '@material-ui/icons/Chat'; import MuiDashboardIcon from '@material-ui/icons/Dashboard'; import MuiDocsIcon from '@material-ui/icons/Description'; @@ -36,6 +39,9 @@ export const icons = { brokenImage: MuiBrokenImageIcon as IconComponent, // To be confirmed: see https://github.com/backstage/backstage/issues/4970 catalog: MuiMenuBookIcon as IconComponent, + scaffolder: MuiCreateNewFolderIcon as IconComponent, + techdocs: MuiSubjectIcon as IconComponent, + search: MuiSearchIcon as IconComponent, chat: MuiChatIcon as IconComponent, dashboard: MuiDashboardIcon as IconComponent, docs: MuiDocsIcon as IconComponent, diff --git a/packages/core-app-api/api-report.md b/packages/core-app-api/api-report.md index b3b54ff1a0..f45fc349b2 100644 --- a/packages/core-app-api/api-report.md +++ b/packages/core-app-api/api-report.md @@ -165,7 +165,7 @@ export type AppContext = { getComponents(): AppComponents; }; -// @public (undocumented) +// @public export type AppIcons = { 'kind:api': IconComponent; 'kind:component': IconComponent; @@ -183,6 +183,9 @@ export type AppIcons = { github: IconComponent; group: IconComponent; help: IconComponent; + scaffolder: IconComponent; + search: IconComponent; + techdocs: IconComponent; user: IconComponent; warning: IconComponent; }; diff --git a/packages/core-app-api/src/app/AppManager.test.tsx b/packages/core-app-api/src/app/AppManager.test.tsx index 934ca3295f..71df5219d1 100644 --- a/packages/core-app-api/src/app/AppManager.test.tsx +++ b/packages/core-app-api/src/app/AppManager.test.tsx @@ -24,7 +24,6 @@ import { lightTheme } from '@backstage/theme'; import { render, screen } from '@testing-library/react'; import React, { PropsWithChildren } from 'react'; import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom'; -import { defaultAppIcons } from './icons'; import { configApiRef, createApiFactory, @@ -38,7 +37,7 @@ import { analyticsApiRef, } from '@backstage/core-plugin-api'; import { generateBoundRoutes, AppManager } from './AppManager'; -import { AppComponents } from './types'; +import { AppComponents, AppIcons } from './types'; describe('generateBoundRoutes', () => { it('runs happy path', () => { @@ -187,6 +186,8 @@ describe('Integration Test', () => { ThemeProvider: ({ children }) => <>{children}, }; + const icons = {} as AppIcons; + it('runs happy paths', async () => { const app = new AppManager({ apis: [noOpAnalyticsApi], @@ -199,7 +200,7 @@ describe('Integration Test', () => { theme: lightTheme, }, ], - icons: defaultAppIcons, + icons, plugins: [], components, bindRoutes: ({ bind }) => { @@ -253,7 +254,7 @@ describe('Integration Test', () => { theme: lightTheme, }, ], - icons: defaultAppIcons, + icons, plugins: [], components, bindRoutes: ({ bind }) => { @@ -310,7 +311,7 @@ describe('Integration Test', () => { theme: lightTheme, }, ], - icons: defaultAppIcons, + icons, plugins: [ createPlugin({ id: 'test', @@ -360,7 +361,7 @@ describe('Integration Test', () => { theme: lightTheme, }, ], - icons: defaultAppIcons, + icons, plugins: [], components, bindRoutes: ({ bind }) => { @@ -420,7 +421,7 @@ describe('Integration Test', () => { theme: lightTheme, }, ], - icons: defaultAppIcons, + icons, plugins: [], components, bindRoutes: ({ bind }) => { diff --git a/packages/core-app-api/src/app/icons.tsx b/packages/core-app-api/src/app/icons.tsx deleted file mode 100644 index 1d9bb12923..0000000000 --- a/packages/core-app-api/src/app/icons.tsx +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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 { IconComponent } from '@backstage/core-plugin-api'; -import MuiApartmentIcon from '@material-ui/icons/Apartment'; -import MuiBrokenImageIcon from '@material-ui/icons/BrokenImage'; -import MuiCategoryIcon from '@material-ui/icons/Category'; -import MuiCreateNewFolderIcon from '@material-ui/icons/CreateNewFolder'; -import MuiSubjectIcon from '@material-ui/icons/Subject'; -import MuiSearchIcon from '@material-ui/icons/Search'; -import MuiChatIcon from '@material-ui/icons/Chat'; -import MuiDashboardIcon from '@material-ui/icons/Dashboard'; -import MuiDocsIcon from '@material-ui/icons/Description'; -import MuiEmailIcon from '@material-ui/icons/Email'; -import MuiExtensionIcon from '@material-ui/icons/Extension'; -import MuiGitHubIcon from '@material-ui/icons/GitHub'; -import MuiHelpIcon from '@material-ui/icons/Help'; -import MuiLocationOnIcon from '@material-ui/icons/LocationOn'; -import MuiMemoryIcon from '@material-ui/icons/Memory'; -import MuiMenuBookIcon from '@material-ui/icons/MenuBook'; -import MuiPeopleIcon from '@material-ui/icons/People'; -import MuiPersonIcon from '@material-ui/icons/Person'; -import MuiWarningIcon from '@material-ui/icons/Warning'; - -/** @public */ -export type AppIcons = { - 'kind:api': IconComponent; - 'kind:component': IconComponent; - 'kind:domain': IconComponent; - 'kind:group': IconComponent; - 'kind:location': IconComponent; - 'kind:system': IconComponent; - 'kind:user': IconComponent; - - brokenImage: IconComponent; - catalog: IconComponent; - chat: IconComponent; - dashboard: IconComponent; - docs: IconComponent; - email: IconComponent; - github: IconComponent; - group: IconComponent; - help: IconComponent; - scaffolder: IconComponent; - search: IconComponent; - techdocs: IconComponent; - user: IconComponent; - warning: IconComponent; -}; - -export const defaultAppIcons: AppIcons = { - brokenImage: MuiBrokenImageIcon, - // To be confirmed: see https://github.com/backstage/backstage/issues/4970 - catalog: MuiMenuBookIcon, - scaffolder: MuiCreateNewFolderIcon, - techdocs: MuiSubjectIcon, - search: MuiSearchIcon, - chat: MuiChatIcon, - dashboard: MuiDashboardIcon, - docs: MuiDocsIcon, - email: MuiEmailIcon, - github: MuiGitHubIcon, - group: MuiPeopleIcon, - help: MuiHelpIcon, - 'kind:api': MuiExtensionIcon, - 'kind:component': MuiMemoryIcon, - 'kind:domain': MuiApartmentIcon, - 'kind:group': MuiPeopleIcon, - 'kind:location': MuiLocationOnIcon, - 'kind:system': MuiCategoryIcon, - 'kind:user': MuiPersonIcon, - user: MuiPersonIcon, - warning: MuiWarningIcon, -}; diff --git a/packages/core-app-api/src/app/index.ts b/packages/core-app-api/src/app/index.ts index ba6a8917a4..5ea5405632 100644 --- a/packages/core-app-api/src/app/index.ts +++ b/packages/core-app-api/src/app/index.ts @@ -17,5 +17,4 @@ export { createApp } from './createApp'; export { createSpecializedApp } from './createSpecializedApp'; export { defaultConfigLoader } from './defaultConfigLoader'; -export type { AppIcons } from './icons'; export * from './types'; diff --git a/packages/core-app-api/src/app/types.ts b/packages/core-app-api/src/app/types.ts index 89a70ed566..86389a0105 100644 --- a/packages/core-app-api/src/app/types.ts +++ b/packages/core-app-api/src/app/types.ts @@ -27,7 +27,6 @@ import { PluginOutput, } from '@backstage/core-plugin-api'; import { AppConfig } from '@backstage/config'; -import { AppIcons } from './icons'; /** * Props for the `BootErrorPage` component of {@link AppComponents}. @@ -111,6 +110,36 @@ export type AppComponents = { SignInPage?: ComponentType; }; +/** + * A set of well-known icons that should be available within an app. + * + * @public + */ +export type AppIcons = { + 'kind:api': IconComponent; + 'kind:component': IconComponent; + 'kind:domain': IconComponent; + 'kind:group': IconComponent; + 'kind:location': IconComponent; + 'kind:system': IconComponent; + 'kind:user': IconComponent; + + brokenImage: IconComponent; + catalog: IconComponent; + chat: IconComponent; + dashboard: IconComponent; + docs: IconComponent; + email: IconComponent; + github: IconComponent; + group: IconComponent; + help: IconComponent; + scaffolder: IconComponent; + search: IconComponent; + techdocs: IconComponent; + user: IconComponent; + warning: IconComponent; +}; + /** * A function that loads in the App config that will be accessible via the ConfigApi. * diff --git a/packages/test-utils/src/testUtils/appWrappers.tsx b/packages/test-utils/src/testUtils/appWrappers.tsx index 890d89e3fe..b84c06c77b 100644 --- a/packages/test-utils/src/testUtils/appWrappers.tsx +++ b/packages/test-utils/src/testUtils/appWrappers.tsx @@ -41,6 +41,9 @@ const mockIcons = { brokenImage: () => , catalog: () => , + scaffolder: () => , + techdocs: () => , + search: () => , chat: () => , dashboard: () => , docs: () => , From a3b6707eb2f6a6e793ccd27906ebd62c2beba800 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 10 Nov 2021 10:25:29 +0100 Subject: [PATCH 178/196] app-defaults: bump internal deps Signed-off-by: Patrik Oldsberg --- packages/app-defaults/package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json index 19f4e1432e..6c06927daf 100644 --- a/packages/app-defaults/package.json +++ b/packages/app-defaults/package.json @@ -29,11 +29,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.1", - "@backstage/config": "^0.1.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/core-plugin-api": "^0.1.11", - "@backstage/theme": "^0.2.11", + "@backstage/core-components": "^0.7.2", + "@backstage/config": "^0.1.11", + "@backstage/core-app-api": "^0.1.19", + "@backstage/core-plugin-api": "^0.1.12", + "@backstage/theme": "^0.2.12", "@backstage/types": "^0.1.1", "@backstage/version-bridge": "^0.1.0", "@material-ui/core": "^4.12.2", @@ -47,8 +47,8 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.8.0", - "@backstage/test-utils": "^0.1.19", + "@backstage/cli": "^0.8.1", + "@backstage/test-utils": "^0.1.20", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", From 7ca03b85ab02792e2af5c5909c01610de0166123 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 10 Nov 2021 10:25:53 +0100 Subject: [PATCH 179/196] core-app-api: createSpecializedApp returns a BackstageApp Signed-off-by: Patrik Oldsberg --- packages/core-app-api/src/app/createSpecializedApp.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core-app-api/src/app/createSpecializedApp.tsx b/packages/core-app-api/src/app/createSpecializedApp.tsx index 54e8d6cddf..a437835f39 100644 --- a/packages/core-app-api/src/app/createSpecializedApp.tsx +++ b/packages/core-app-api/src/app/createSpecializedApp.tsx @@ -15,7 +15,7 @@ */ import { AppManager } from './AppManager'; -import { AppOptions } from './types'; +import { AppOptions, BackstageApp } from './types'; /** * Creates a new Backstage App where the full set of options are required. @@ -23,6 +23,6 @@ import { AppOptions } from './types'; * @param options - A set of options for creating the app * @returns */ -export function createSpecializedApp(options: AppOptions) { +export function createSpecializedApp(options: AppOptions): BackstageApp { return new AppManager(options); } From c98c5c3d000ad5452a85b8054afca1e1fd3b0f1d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 10 Nov 2021 10:40:41 +0100 Subject: [PATCH 180/196] core-app-api: switch auth provider icons to be empty by default Signed-off-by: Patrik Oldsberg --- packages/app-defaults/src/defaults/apis.ts | 4 +--- .../implementations/OAuthRequestApi/MockOAuthApi.test.ts | 9 ++++----- .../OAuthRequestApi/OAuthRequestManager.test.ts | 3 +-- .../apis/implementations/auth/atlassian/AtlassianAuth.ts | 3 +-- .../src/apis/implementations/auth/auth0/Auth0Auth.ts | 3 +-- .../apis/implementations/auth/bitbucket/BitbucketAuth.ts | 3 +-- .../src/apis/implementations/auth/github/GithubAuth.ts | 3 +-- .../src/apis/implementations/auth/gitlab/GitlabAuth.ts | 3 +-- .../src/apis/implementations/auth/google/GoogleAuth.ts | 3 +-- .../apis/implementations/auth/microsoft/MicrosoftAuth.ts | 3 +-- .../src/apis/implementations/auth/oauth2/OAuth2.ts | 3 +-- .../src/apis/implementations/auth/okta/OktaAuth.ts | 3 +-- .../apis/implementations/auth/onelogin/OneLoginAuth.ts | 3 +-- .../src/apis/implementations/auth/saml/SamlAuth.ts | 3 +-- .../src/lib/AuthConnector/DefaultAuthConnector.test.ts | 3 +-- 15 files changed, 18 insertions(+), 34 deletions(-) diff --git a/packages/app-defaults/src/defaults/apis.ts b/packages/app-defaults/src/defaults/apis.ts index 2596ac2593..85c004eece 100644 --- a/packages/app-defaults/src/defaults/apis.ts +++ b/packages/app-defaults/src/defaults/apis.ts @@ -59,8 +59,6 @@ import { atlassianAuthApiRef, } from '@backstage/core-plugin-api'; -import OAuth2Icon from '@material-ui/icons/AcUnit'; - export const apis = [ createApiFactory({ api: discoveryApiRef, @@ -226,7 +224,7 @@ export const apis = [ provider: { id: 'oidc', title: 'Your Identity Provider', - icon: OAuth2Icon, + icon: () => null, }, environment: configApi.getOptionalString('auth.environment'), }), diff --git a/packages/core-app-api/src/apis/implementations/OAuthRequestApi/MockOAuthApi.test.ts b/packages/core-app-api/src/apis/implementations/OAuthRequestApi/MockOAuthApi.test.ts index d0f137f0be..76cf01d20a 100644 --- a/packages/core-app-api/src/apis/implementations/OAuthRequestApi/MockOAuthApi.test.ts +++ b/packages/core-app-api/src/apis/implementations/OAuthRequestApi/MockOAuthApi.test.ts @@ -15,7 +15,6 @@ */ import MockOAuthApi from './MockOAuthApi'; -import PowerIcon from '@material-ui/icons/Power'; describe('MockOAuthApi', () => { it('should trigger all requests', async () => { @@ -24,13 +23,13 @@ describe('MockOAuthApi', () => { const authHandler1 = jest.fn().mockImplementation(() => authResult); const requester1 = mock.createAuthRequester({ - provider: { icon: PowerIcon, title: 'Test' }, + provider: { icon: () => null, title: 'Test' }, onAuthRequest: authHandler1, }); const authHandler2 = jest.fn().mockResolvedValue('other'); const requester2 = mock.createAuthRequester({ - provider: { icon: PowerIcon, title: 'Test' }, + provider: { icon: () => null, title: 'Test' }, onAuthRequest: authHandler2, }); @@ -67,13 +66,13 @@ describe('MockOAuthApi', () => { const authHandler1 = jest.fn(); const requester1 = mock.createAuthRequester({ - provider: { icon: PowerIcon, title: 'Test' }, + provider: { icon: () => null, title: 'Test' }, onAuthRequest: authHandler1, }); const authHandler2 = jest.fn(); const requester2 = mock.createAuthRequester({ - provider: { icon: PowerIcon, title: 'Test' }, + provider: { icon: () => null, title: 'Test' }, onAuthRequest: authHandler2, }); diff --git a/packages/core-app-api/src/apis/implementations/OAuthRequestApi/OAuthRequestManager.test.ts b/packages/core-app-api/src/apis/implementations/OAuthRequestApi/OAuthRequestManager.test.ts index d8faf90229..3110197ad9 100644 --- a/packages/core-app-api/src/apis/implementations/OAuthRequestApi/OAuthRequestManager.test.ts +++ b/packages/core-app-api/src/apis/implementations/OAuthRequestApi/OAuthRequestManager.test.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import ProviderIcon from '@material-ui/icons/AcUnit'; import { OAuthRequestManager } from './OAuthRequestManager'; describe('OAuthRequestManager', () => { @@ -27,7 +26,7 @@ describe('OAuthRequestManager', () => { const requester = manager.createAuthRequester({ provider: { title: 'My Provider', - icon: ProviderIcon, + icon: () => null, }, onAuthRequest: async () => 'hello', }); diff --git a/packages/core-app-api/src/apis/implementations/auth/atlassian/AtlassianAuth.ts b/packages/core-app-api/src/apis/implementations/auth/atlassian/AtlassianAuth.ts index 227d70e494..caad40ddbf 100644 --- a/packages/core-app-api/src/apis/implementations/auth/atlassian/AtlassianAuth.ts +++ b/packages/core-app-api/src/apis/implementations/auth/atlassian/AtlassianAuth.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import AtlassianIcon from '@material-ui/icons/AcUnit'; import { atlassianAuthApiRef } from '@backstage/core-plugin-api'; import { OAuth2 } from '../oauth2'; import { OAuthApiCreateOptions } from '../types'; @@ -22,7 +21,7 @@ import { OAuthApiCreateOptions } from '../types'; const DEFAULT_PROVIDER = { id: 'atlassian', title: 'Atlassian', - icon: AtlassianIcon, + icon: () => null, }; /** diff --git a/packages/core-app-api/src/apis/implementations/auth/auth0/Auth0Auth.ts b/packages/core-app-api/src/apis/implementations/auth/auth0/Auth0Auth.ts index 69462942e4..0a158ccb9e 100644 --- a/packages/core-app-api/src/apis/implementations/auth/auth0/Auth0Auth.ts +++ b/packages/core-app-api/src/apis/implementations/auth/auth0/Auth0Auth.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import Auth0Icon from '@material-ui/icons/AcUnit'; import { auth0AuthApiRef } from '@backstage/core-plugin-api'; import { OAuth2 } from '../oauth2'; import { OAuthApiCreateOptions } from '../types'; @@ -22,7 +21,7 @@ import { OAuthApiCreateOptions } from '../types'; const DEFAULT_PROVIDER = { id: 'auth0', title: 'Auth0', - icon: Auth0Icon, + icon: () => null, }; /** diff --git a/packages/core-app-api/src/apis/implementations/auth/bitbucket/BitbucketAuth.ts b/packages/core-app-api/src/apis/implementations/auth/bitbucket/BitbucketAuth.ts index c9db93906a..e488580c4d 100644 --- a/packages/core-app-api/src/apis/implementations/auth/bitbucket/BitbucketAuth.ts +++ b/packages/core-app-api/src/apis/implementations/auth/bitbucket/BitbucketAuth.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import BitbucketIcon from '@material-ui/icons/FormatBold'; import { BackstageIdentity, bitbucketAuthApiRef, @@ -37,7 +36,7 @@ export type BitbucketAuthResponse = { const DEFAULT_PROVIDER = { id: 'bitbucket', title: 'Bitbucket', - icon: BitbucketIcon, + icon: () => null, }; /** diff --git a/packages/core-app-api/src/apis/implementations/auth/github/GithubAuth.ts b/packages/core-app-api/src/apis/implementations/auth/github/GithubAuth.ts index 01e7e77b8e..885e80da7c 100644 --- a/packages/core-app-api/src/apis/implementations/auth/github/GithubAuth.ts +++ b/packages/core-app-api/src/apis/implementations/auth/github/GithubAuth.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import GithubIcon from '@material-ui/icons/AcUnit'; import { DefaultAuthConnector } from '../../../../lib/AuthConnector'; import { GithubSession } from './types'; import { @@ -48,7 +47,7 @@ export type GithubAuthResponse = { const DEFAULT_PROVIDER = { id: 'github', title: 'GitHub', - icon: GithubIcon, + icon: () => null, }; /** diff --git a/packages/core-app-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts b/packages/core-app-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts index af581d0018..f46f2ef72e 100644 --- a/packages/core-app-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts +++ b/packages/core-app-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import GitlabIcon from '@material-ui/icons/AcUnit'; import { gitlabAuthApiRef } from '@backstage/core-plugin-api'; import { OAuth2 } from '../oauth2'; import { OAuthApiCreateOptions } from '../types'; @@ -22,7 +21,7 @@ import { OAuthApiCreateOptions } from '../types'; const DEFAULT_PROVIDER = { id: 'gitlab', title: 'GitLab', - icon: GitlabIcon, + icon: () => null, }; /** diff --git a/packages/core-app-api/src/apis/implementations/auth/google/GoogleAuth.ts b/packages/core-app-api/src/apis/implementations/auth/google/GoogleAuth.ts index 916c52382e..a6f37b250f 100644 --- a/packages/core-app-api/src/apis/implementations/auth/google/GoogleAuth.ts +++ b/packages/core-app-api/src/apis/implementations/auth/google/GoogleAuth.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import GoogleIcon from '@material-ui/icons/AcUnit'; import { googleAuthApiRef } from '@backstage/core-plugin-api'; import { OAuth2 } from '../oauth2'; import { OAuthApiCreateOptions } from '../types'; @@ -22,7 +21,7 @@ import { OAuthApiCreateOptions } from '../types'; const DEFAULT_PROVIDER = { id: 'google', title: 'Google', - icon: GoogleIcon, + icon: () => null, }; const SCOPE_PREFIX = 'https://www.googleapis.com/auth/'; diff --git a/packages/core-app-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts b/packages/core-app-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts index 09bd7f67a2..148be66873 100644 --- a/packages/core-app-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts +++ b/packages/core-app-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import MicrosoftIcon from '@material-ui/icons/AcUnit'; import { microsoftAuthApiRef } from '@backstage/core-plugin-api'; import { OAuth2 } from '../oauth2'; import { OAuthApiCreateOptions } from '../types'; @@ -22,7 +21,7 @@ import { OAuthApiCreateOptions } from '../types'; const DEFAULT_PROVIDER = { id: 'microsoft', title: 'Microsoft', - icon: MicrosoftIcon, + icon: () => null, }; /** diff --git a/packages/core-app-api/src/apis/implementations/auth/oauth2/OAuth2.ts b/packages/core-app-api/src/apis/implementations/auth/oauth2/OAuth2.ts index 6c0759d967..45937a68f8 100644 --- a/packages/core-app-api/src/apis/implementations/auth/oauth2/OAuth2.ts +++ b/packages/core-app-api/src/apis/implementations/auth/oauth2/OAuth2.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import OAuth2Icon from '@material-ui/icons/AcUnit'; import { DefaultAuthConnector } from '../../../../lib/AuthConnector'; import { RefreshingAuthSessionManager } from '../../../../lib/AuthSessionManager'; import { SessionManager } from '../../../../lib/AuthSessionManager/types'; @@ -51,7 +50,7 @@ export type OAuth2Response = { const DEFAULT_PROVIDER = { id: 'oauth2', title: 'Your Identity Provider', - icon: OAuth2Icon, + icon: () => null, }; /** diff --git a/packages/core-app-api/src/apis/implementations/auth/okta/OktaAuth.ts b/packages/core-app-api/src/apis/implementations/auth/okta/OktaAuth.ts index 294fb496af..465a124051 100644 --- a/packages/core-app-api/src/apis/implementations/auth/okta/OktaAuth.ts +++ b/packages/core-app-api/src/apis/implementations/auth/okta/OktaAuth.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import OktaIcon from '@material-ui/icons/AcUnit'; import { oktaAuthApiRef } from '@backstage/core-plugin-api'; import { OAuth2 } from '../oauth2'; import { OAuthApiCreateOptions } from '../types'; @@ -22,7 +21,7 @@ import { OAuthApiCreateOptions } from '../types'; const DEFAULT_PROVIDER = { id: 'okta', title: 'Okta', - icon: OktaIcon, + icon: () => null, }; const OKTA_OIDC_SCOPES: Set = new Set([ diff --git a/packages/core-app-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts b/packages/core-app-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts index f49ea4cde8..5f933b9c6e 100644 --- a/packages/core-app-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts +++ b/packages/core-app-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import OneLoginIcon from '@material-ui/icons/AcUnit'; import { oneloginAuthApiRef, OAuthRequestApi, @@ -33,7 +32,7 @@ type CreateOptions = { const DEFAULT_PROVIDER = { id: 'onelogin', title: 'onelogin', - icon: OneLoginIcon, + icon: () => null, }; const OIDC_SCOPES: Set = new Set([ diff --git a/packages/core-app-api/src/apis/implementations/auth/saml/SamlAuth.ts b/packages/core-app-api/src/apis/implementations/auth/saml/SamlAuth.ts index f2668dedd2..63985979de 100644 --- a/packages/core-app-api/src/apis/implementations/auth/saml/SamlAuth.ts +++ b/packages/core-app-api/src/apis/implementations/auth/saml/SamlAuth.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import SamlIcon from '@material-ui/icons/AcUnit'; import { DirectAuthConnector } from '../../../../lib/AuthConnector'; import { SessionManager } from '../../../../lib/AuthSessionManager/types'; import { @@ -42,7 +41,7 @@ export type SamlAuthResponse = { const DEFAULT_PROVIDER = { id: 'saml', title: 'SAML', - icon: SamlIcon, + icon: () => null, }; /** diff --git a/packages/core-app-api/src/lib/AuthConnector/DefaultAuthConnector.test.ts b/packages/core-app-api/src/lib/AuthConnector/DefaultAuthConnector.test.ts index 7e448586e3..baf89a59d8 100644 --- a/packages/core-app-api/src/lib/AuthConnector/DefaultAuthConnector.test.ts +++ b/packages/core-app-api/src/lib/AuthConnector/DefaultAuthConnector.test.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import ProviderIcon from '@material-ui/icons/AcUnit'; import { DefaultAuthConnector } from './DefaultAuthConnector'; import MockOAuthApi from '../../apis/implementations/OAuthRequestApi/MockOAuthApi'; import * as loginPopup from '../loginPopup'; @@ -29,7 +28,7 @@ const defaultOptions = { provider: { id: 'my-provider', title: 'My Provider', - icon: ProviderIcon, + icon: () => null, }, oauthRequestApi: new MockOAuthApi(), sessionTransform: ({ expiresInSeconds, ...res }: any) => ({ From 03da4c09469ddf94760925d5b9d20f233eedfa78 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 10 Nov 2021 11:23:46 +0100 Subject: [PATCH 181/196] create-app: add app-defaults dependency to app + notice in changeset Signed-off-by: Patrik Oldsberg --- .changeset/hot-walls-fail.md | 2 +- packages/create-app/src/lib/versions.ts | 2 ++ .../templates/default-app/packages/app/package.json.hbs | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.changeset/hot-walls-fail.md b/.changeset/hot-walls-fail.md index b0854e868c..3728e10ff6 100644 --- a/.changeset/hot-walls-fail.md +++ b/.changeset/hot-walls-fail.md @@ -4,7 +4,7 @@ Migrated the app template use the new `@backstage/app-defaults` for the `createApp` import, since the `createApp` exported by `@backstage/app-core-api` will be removed in the future. -To migrate an existing application, make the following change to `packages/app/src/App.tsx`: +To migrate an existing application, add the latest version of `@backstage/app-defaults` as a dependency in `packages/app/package.json`, and make the following change to `packages/app/src/App.tsx`: ```diff -import { createApp, FlatRoutes } from '@backstage/core-app-api'; diff --git a/packages/create-app/src/lib/versions.ts b/packages/create-app/src/lib/versions.ts index 97904cc15b..6c85652c64 100644 --- a/packages/create-app/src/lib/versions.ts +++ b/packages/create-app/src/lib/versions.ts @@ -30,6 +30,7 @@ Rollup will extract the value of the version field in each package at build time leaving any imports in place. */ +import { version as appDefaults } from '../../../app-defaults/package.json'; import { version as backendCommon } from '../../../backend-common/package.json'; import { version as catalogClient } from '../../../catalog-client/package.json'; import { version as catalogModel } from '../../../catalog-model/package.json'; @@ -68,6 +69,7 @@ import { version as pluginTechdocsBackend } from '../../../../plugins/techdocs-b import { version as pluginUserSettings } from '../../../../plugins/user-settings/package.json'; export const packageVersions = { + '@backstage/app-defaults': appDefaults, '@backstage/backend-common': backendCommon, '@backstage/catalog-client': catalogClient, '@backstage/catalog-model': catalogModel, diff --git a/packages/create-app/templates/default-app/packages/app/package.json.hbs b/packages/create-app/templates/default-app/packages/app/package.json.hbs index c443b45dc7..9e15608a01 100644 --- a/packages/create-app/templates/default-app/packages/app/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/app/package.json.hbs @@ -4,6 +4,7 @@ "private": true, "bundled": true, "dependencies": { + "@backstage/app-defaults": "^{{version '@backstage/app-defaults'}}", "@backstage/catalog-model": "^{{version '@backstage/catalog-model'}}", "@backstage/cli": "^{{version '@backstage/cli'}}", "@backstage/core-app-api": "^{{version '@backstage/core-app-api'}}", From f2bd2419726a0025b1af699f437f628556bb1e3b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 10 Nov 2021 11:41:31 +0100 Subject: [PATCH 182/196] docs: introduce deprecation docs + app-theme deprecation Signed-off-by: Patrik Oldsberg --- docs/api/deprecations.md | 56 +++++++++++++++++++ microsite/sidebars.json | 3 +- mkdocs.yml | 1 + .../core-app-api/src/app/AppThemeProvider.tsx | 2 +- .../src/apis/definitions/AppThemeApi.ts | 2 +- 5 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 docs/api/deprecations.md diff --git a/docs/api/deprecations.md b/docs/api/deprecations.md new file mode 100644 index 0000000000..774c8ef03d --- /dev/null +++ b/docs/api/deprecations.md @@ -0,0 +1,56 @@ +--- +id: deprecations +title: Deprecations +description: A list of active and past deprecations +--- + +## Introduction + +This page contains extended documentation for some of the deprecations in +various parts of Backstage. It is not an exhaustive list as most deprecation +only come in the form of a changelog notice and a console warning. The +deprecations listed here are the ones that need a bit more guidance than what +fits in a console message. + +### App Theme + +`Released 2021-11-12 in @backstage/core-plugin-api v0.1.13` + +In order to provide more flexibility in what types of themes can be used and how +they are applied, the `theme` property on the `AppTheme` type is being +deprecated and replaced by a `Provider` property instead. The `Provider` +property is a React component that will be mounted at the root of the app +whenever that theme is active. This also removes the tight connection to MUI and +opens up for other type of themes, and removes the hardcoded usage of +``. + +To migrate an existing theme, remove the `theme` property and move it over to a +new `Provider` component, using `ThemeProvider` from MUI to provide the new +theme, along with ``. For example a theme that currently looks like +this: + +```tsx +const darkTheme = { + id: 'dark', + title: 'Dark Theme', + variant: 'dark', + icon: , + theme: darkTheme, +}; +``` + +Would be migrated to the following: + +```tsx +const darkTheme = { + id: 'dark', + title: 'Dark Theme', + variant: 'dark', + icon: , + Provider: ({ children }) => ( + + {children} + + ), +}; +``` diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 91525d4938..ec5c5d8d68 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -256,7 +256,8 @@ "type": "subcategory", "label": "API Reference", "ids": ["reference/index"] - } + }, + "api/deprecations" ], "Tutorials": [ "tutorials/journey", diff --git a/mkdocs.yml b/mkdocs.yml index 0ddae446e6..8c7114abbc 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -164,6 +164,7 @@ nav: - API Reference: - Guides: - Utility APIs: 'api/utility-apis.md' + - Deprecations: 'api/deprecations.md' - Tutorials: - Future developer journey: 'tutorials/journey.md' - Migrating away from @backstage/core: 'tutorials/migrating-away-from-core.md' diff --git a/packages/core-app-api/src/app/AppThemeProvider.tsx b/packages/core-app-api/src/app/AppThemeProvider.tsx index aa48a2187d..2e145025d5 100644 --- a/packages/core-app-api/src/app/AppThemeProvider.tsx +++ b/packages/core-app-api/src/app/AppThemeProvider.tsx @@ -98,7 +98,7 @@ export function AppThemeProvider({ children }: PropsWithChildren<{}>) { console.warn( "DEPRECATION WARNING: A provided app theme is using the deprecated 'theme' property " + 'and should be migrated to use a Provider instead. ' + - 'See https://backstage.io/docs/deprecations/TODO for more info.', + 'See https://backstage.io/docs/api/deprecations#app-theme for more info.', ); return ( diff --git a/packages/core-plugin-api/src/apis/definitions/AppThemeApi.ts b/packages/core-plugin-api/src/apis/definitions/AppThemeApi.ts index 52cf202422..8053c449a1 100644 --- a/packages/core-plugin-api/src/apis/definitions/AppThemeApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/AppThemeApi.ts @@ -42,7 +42,7 @@ export type AppTheme = { /** * The specialized MaterialUI theme instance. - * @deprecated use Provider instead, see https://backstage.io/docs/deprecations/TODO + * @deprecated use Provider instead, see https://backstage.io/docs/api/deprecations#app-theme */ theme: BackstageTheme; From 465dffb05c704d8922acd2ea58383b357be93a41 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 10 Nov 2021 11:48:52 +0100 Subject: [PATCH 183/196] core-app-api,app-defaults: API report and doc fixups Signed-off-by: Patrik Oldsberg --- packages/app-defaults/api-report.md | 4 ++-- packages/core-app-api/api-report.md | 9 ++------- packages/core-app-api/src/app/createApp.tsx | 2 +- packages/core-app-api/src/app/createSpecializedApp.tsx | 1 + 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/app-defaults/api-report.md b/packages/app-defaults/api-report.md index 6b35dab81b..57ea61ad8b 100644 --- a/packages/app-defaults/api-report.md +++ b/packages/app-defaults/api-report.md @@ -5,15 +5,15 @@ ```ts import { AppComponents } from '@backstage/core-app-api'; import { AppIcons } from '@backstage/core-app-api'; -import { AppManager } from '@backstage/core-app-api/src/app/AppManager'; import { AppOptions } from '@backstage/core-app-api'; import { AppTheme } from '@backstage/core-plugin-api'; +import { BackstageApp } from '@backstage/core-app-api'; import { IconComponent } from '@backstage/core-plugin-api'; // @public export function createApp( options?: Omit & OptionalAppOptions, -): AppManager; +): BackstageApp; // @public export type OptionalAppOptions = { diff --git a/packages/core-app-api/api-report.md b/packages/core-app-api/api-report.md index f45fc349b2..d98b5ef5f8 100644 --- a/packages/core-app-api/api-report.md +++ b/packages/core-app-api/api-report.md @@ -313,16 +313,11 @@ export type BootErrorPageProps = { export { ConfigReader }; -// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" -// Warning: (ae-forgotten-export) The symbol "AppManager" needs to be exported by the entry point index.d.ts -// // @public @deprecated -export function createApp(options?: OptionalAppOptions): AppManager; +export function createApp(options?: OptionalAppOptions): BackstageApp; -// Warning: (ae-missing-release-tag) "createSpecializedApp" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public -export function createSpecializedApp(options: AppOptions): AppManager; +export function createSpecializedApp(options: AppOptions): BackstageApp; // @public export const defaultConfigLoader: AppConfigLoader; diff --git a/packages/core-app-api/src/app/createApp.tsx b/packages/core-app-api/src/app/createApp.tsx index a020308d91..e11076099b 100644 --- a/packages/core-app-api/src/app/createApp.tsx +++ b/packages/core-app-api/src/app/createApp.tsx @@ -22,7 +22,7 @@ import { /** * Creates a new Backstage App. * - * @deprecated Use createApp from @backstage/app-defaults instead + * @deprecated Use {@link @backstage/app-defaults#createApp} from `@backstage/app-defaults` instead * @param options - A set of options for creating the app * @public */ diff --git a/packages/core-app-api/src/app/createSpecializedApp.tsx b/packages/core-app-api/src/app/createSpecializedApp.tsx index a437835f39..9bda223722 100644 --- a/packages/core-app-api/src/app/createSpecializedApp.tsx +++ b/packages/core-app-api/src/app/createSpecializedApp.tsx @@ -20,6 +20,7 @@ import { AppOptions, BackstageApp } from './types'; /** * Creates a new Backstage App where the full set of options are required. * + * @public * @param options - A set of options for creating the app * @returns */ From cdbddf196f6ac9e4aeca2bc854ffc425b8ff937d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 10 Nov 2021 11:56:29 +0100 Subject: [PATCH 184/196] app-defaults: clean up dependencies Signed-off-by: Patrik Oldsberg --- packages/app-defaults/package.json | 16 ++-------------- packages/app-defaults/src/setupTests.ts | 1 - 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json index 6c06927daf..76101f0ad4 100644 --- a/packages/app-defaults/package.json +++ b/packages/app-defaults/package.json @@ -30,34 +30,22 @@ }, "dependencies": { "@backstage/core-components": "^0.7.2", - "@backstage/config": "^0.1.11", "@backstage/core-app-api": "^0.1.19", "@backstage/core-plugin-api": "^0.1.12", "@backstage/theme": "^0.2.12", - "@backstage/types": "^0.1.1", - "@backstage/version-bridge": "^0.1.0", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@types/react": "*", - "@types/prop-types": "^15.7.3", - "prop-types": "^15.7.2", "react": "^16.12.0", - "react-router-dom": "6.0.0-beta.0", - "react-use": "^17.2.4", - "zen-observable": "^0.8.15" + "react-router-dom": "6.0.0-beta.0" }, "devDependencies": { "@backstage/cli": "^0.8.1", "@backstage/test-utils": "^0.1.20", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", - "@testing-library/react-hooks": "^7.0.2", - "@testing-library/user-event": "^13.1.8", "@types/jest": "^26.0.7", "@types/node": "^14.14.32", - "@types/zen-observable": "^0.8.0", - "cross-fetch": "^3.0.6", - "msw": "^0.29.0" + "@types/react": "*" }, "files": [ "dist" diff --git a/packages/app-defaults/src/setupTests.ts b/packages/app-defaults/src/setupTests.ts index c1d649f2ad..963c0f188b 100644 --- a/packages/app-defaults/src/setupTests.ts +++ b/packages/app-defaults/src/setupTests.ts @@ -15,4 +15,3 @@ */ import '@testing-library/jest-dom'; -import 'cross-fetch/polyfill'; From 2dd2a7b2ccc2cd063d086b93ae2b7b8d68661104 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 10 Nov 2021 12:04:27 +0100 Subject: [PATCH 185/196] changesets: add changesets for test-utils createApp and AppTheme deprecation Signed-off-by: Patrik Oldsberg --- .changeset/sharp-moons-jog.md | 5 +++++ .changeset/slow-moles-act.md | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .changeset/sharp-moons-jog.md create mode 100644 .changeset/slow-moles-act.md diff --git a/.changeset/sharp-moons-jog.md b/.changeset/sharp-moons-jog.md new file mode 100644 index 0000000000..d3329af08e --- /dev/null +++ b/.changeset/sharp-moons-jog.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-plugin-api': patch +--- + +Deprecated the `theme` property on `AppTheme`, replacing it with `Provider`. See https://backstage.io/docs/api/deprecations#app-theme for more details. diff --git a/.changeset/slow-moles-act.md b/.changeset/slow-moles-act.md new file mode 100644 index 0000000000..ab920bfa5d --- /dev/null +++ b/.changeset/slow-moles-act.md @@ -0,0 +1,5 @@ +--- +'@backstage/test-utils': patch +--- + +Migrated to using `createSpecializedApp`. From 29eb8d7dbee876056be94268ea68ff152a083612 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 10 Nov 2021 15:08:32 +0100 Subject: [PATCH 186/196] test-utils: switch mock icons to be plain svg elements Signed-off-by: Patrik Oldsberg --- .../test-utils/src/testUtils/appWrappers.tsx | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/test-utils/src/testUtils/appWrappers.tsx b/packages/test-utils/src/testUtils/appWrappers.tsx index b84c06c77b..7a8d913273 100644 --- a/packages/test-utils/src/testUtils/appWrappers.tsx +++ b/packages/test-utils/src/testUtils/appWrappers.tsx @@ -31,28 +31,28 @@ import { renderWithEffects } from './testingLibrary'; import { mockApis } from './mockApis'; const mockIcons = { - 'kind:api': () => , - 'kind:component': () => , - 'kind:domain': () => , - 'kind:group': () => , - 'kind:location': () => , - 'kind:system': () => , - 'kind:user': () => , + 'kind:api': () => , + 'kind:component': () => , + 'kind:domain': () => , + 'kind:group': () => , + 'kind:location': () => , + 'kind:system': () => , + 'kind:user': () => , - brokenImage: () => , - catalog: () => , - scaffolder: () => , - techdocs: () => , - search: () => , - chat: () => , - dashboard: () => , - docs: () => , - email: () => , - github: () => , - group: () => , - help: () => , - user: () => , - warning: () => , + brokenImage: () => , + catalog: () => , + scaffolder: () => , + techdocs: () => , + search: () => , + chat: () => , + dashboard: () => , + docs: () => , + email: () => , + github: () => , + group: () => , + help: () => , + user: () => , + warning: () => , }; const ErrorBoundaryFallback = ({ error }: { error: Error }) => { From 569acdb8f8bd7b352d02274466390044c4085725 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 10 Nov 2021 15:19:45 +0100 Subject: [PATCH 187/196] core-app-api: fix fix docs docs Signed-off-by: Patrik Oldsberg --- packages/core-app-api/src/app/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core-app-api/src/app/types.ts b/packages/core-app-api/src/app/types.ts index 86389a0105..f2d1976f87 100644 --- a/packages/core-app-api/src/app/types.ts +++ b/packages/core-app-api/src/app/types.ts @@ -225,7 +225,7 @@ export type BackstagePluginWithAnyOutput = Omit< export type AppOptions = { /** * A collection of ApiFactories to register in the application to either - * add add new ones, or override factories provided by default or by plugins. + * add new ones, or override factories provided by default or by plugins. */ apis?: Iterable; @@ -234,7 +234,7 @@ export type AppOptions = { * Theses APIs can not be overridden by plugin factories, but can be overridden * by plugin APIs provided through the * A collection of ApiFactories to register in the application to either - * add add new ones, or override factories provided by default or by plugins. + * add new ones, or override factories provided by default or by plugins. */ defaultApis?: Iterable; From 6d790f0f637c9c12e1f0ab4a38dc675aeaeefc14 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 10 Nov 2021 15:21:08 +0100 Subject: [PATCH 188/196] test-utils: reintroduce defaultApis Signed-off-by: Patrik Oldsberg --- .../test-utils/src/testUtils/appWrappers.tsx | 2 + .../test-utils/src/testUtils/defaultApis.ts | 264 ++++++++++++++++++ 2 files changed, 266 insertions(+) create mode 100644 packages/test-utils/src/testUtils/defaultApis.ts diff --git a/packages/test-utils/src/testUtils/appWrappers.tsx b/packages/test-utils/src/testUtils/appWrappers.tsx index 7a8d913273..ab96887a78 100644 --- a/packages/test-utils/src/testUtils/appWrappers.tsx +++ b/packages/test-utils/src/testUtils/appWrappers.tsx @@ -28,6 +28,7 @@ import { } from '@backstage/core-plugin-api'; import { RenderResult } from '@testing-library/react'; import { renderWithEffects } from './testingLibrary'; +import { defaultApis } from './defaultApis'; import { mockApis } from './mockApis'; const mockIcons = { @@ -117,6 +118,7 @@ export function wrapInTestApp( const app = createSpecializedApp({ apis: mockApis, + defaultApis, // Bit of a hack to make sure that the default config loader isn't used // as that would force every single test to wait for config loading. configLoader: false as unknown as undefined, diff --git a/packages/test-utils/src/testUtils/defaultApis.ts b/packages/test-utils/src/testUtils/defaultApis.ts new file mode 100644 index 0000000000..f06e1ba6b7 --- /dev/null +++ b/packages/test-utils/src/testUtils/defaultApis.ts @@ -0,0 +1,264 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + AlertApiForwarder, + NoOpAnalyticsApi, + ErrorApiForwarder, + ErrorAlerter, + GoogleAuth, + GithubAuth, + OAuth2, + OktaAuth, + GitlabAuth, + Auth0Auth, + MicrosoftAuth, + BitbucketAuth, + OAuthRequestManager, + WebStorage, + UrlPatternDiscovery, + SamlAuth, + OneLoginAuth, + UnhandledErrorForwarder, + AtlassianAuth, +} from '@backstage/core-app-api'; + +import { + createApiFactory, + alertApiRef, + analyticsApiRef, + errorApiRef, + discoveryApiRef, + oauthRequestApiRef, + googleAuthApiRef, + githubAuthApiRef, + oauth2ApiRef, + oktaAuthApiRef, + gitlabAuthApiRef, + auth0AuthApiRef, + microsoftAuthApiRef, + storageApiRef, + configApiRef, + samlAuthApiRef, + oneloginAuthApiRef, + oidcAuthApiRef, + bitbucketAuthApiRef, + atlassianAuthApiRef, +} from '@backstage/core-plugin-api'; + +// TODO(Rugvip): This is just a copy of the createApp default APIs for now, but +// we should clean up this list a bit move more things over to mocks. +export const defaultApis = [ + createApiFactory({ + api: discoveryApiRef, + deps: { configApi: configApiRef }, + factory: ({ configApi }) => + UrlPatternDiscovery.compile( + `${configApi.getString('backend.baseUrl')}/api/{{ pluginId }}`, + ), + }), + createApiFactory(alertApiRef, new AlertApiForwarder()), + createApiFactory(analyticsApiRef, new NoOpAnalyticsApi()), + createApiFactory({ + api: errorApiRef, + deps: { alertApi: alertApiRef }, + factory: ({ alertApi }) => { + const errorApi = new ErrorAlerter(alertApi, new ErrorApiForwarder()); + UnhandledErrorForwarder.forward(errorApi, { hidden: false }); + return errorApi; + }, + }), + createApiFactory({ + api: storageApiRef, + deps: { errorApi: errorApiRef }, + factory: ({ errorApi }) => WebStorage.create({ errorApi }), + }), + createApiFactory(oauthRequestApiRef, new OAuthRequestManager()), + createApiFactory({ + api: googleAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + GoogleAuth.create({ + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: microsoftAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + MicrosoftAuth.create({ + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: githubAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + GithubAuth.create({ + discoveryApi, + oauthRequestApi, + defaultScopes: ['read:user'], + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: oktaAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OktaAuth.create({ + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: gitlabAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + GitlabAuth.create({ + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: auth0AuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + Auth0Auth.create({ + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: oauth2ApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OAuth2.create({ + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: samlAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, configApi }) => + SamlAuth.create({ + discoveryApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: oneloginAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OneLoginAuth.create({ + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: oidcAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OAuth2.create({ + discoveryApi, + oauthRequestApi, + provider: { + id: 'oidc', + title: 'Your Identity Provider', + icon: () => null, + }, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: bitbucketAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + BitbucketAuth.create({ + discoveryApi, + oauthRequestApi, + defaultScopes: ['team'], + environment: configApi.getOptionalString('auth.environment'), + }), + }), + createApiFactory({ + api: atlassianAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => { + return AtlassianAuth.create({ + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }); + }, + }), +]; From eb489db0c1169dcc356d39683eb096fcbe888535 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 10 Nov 2021 17:31:15 +0100 Subject: [PATCH 189/196] app-defaults: lazier api initialization to work around import cycle Signed-off-by: Patrik Oldsberg --- packages/app-defaults/src/defaults/apis.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/app-defaults/src/defaults/apis.ts b/packages/app-defaults/src/defaults/apis.ts index 85c004eece..d3c76d52bd 100644 --- a/packages/app-defaults/src/defaults/apis.ts +++ b/packages/app-defaults/src/defaults/apis.ts @@ -68,8 +68,16 @@ export const apis = [ `${configApi.getString('backend.baseUrl')}/api/{{ pluginId }}`, ), }), - createApiFactory(alertApiRef, new AlertApiForwarder()), - createApiFactory(analyticsApiRef, new NoOpAnalyticsApi()), + createApiFactory({ + api: alertApiRef, + deps: {}, + factory: () => new AlertApiForwarder(), + }), + createApiFactory({ + api: analyticsApiRef, + deps: {}, + factory: () => new NoOpAnalyticsApi(), + }), createApiFactory({ api: errorApiRef, deps: { alertApi: alertApiRef }, @@ -84,7 +92,11 @@ export const apis = [ deps: { errorApi: errorApiRef }, factory: ({ errorApi }) => WebStorage.create({ errorApi }), }), - createApiFactory(oauthRequestApiRef, new OAuthRequestManager()), + createApiFactory({ + api: oauthRequestApiRef, + deps: {}, + factory: () => new OAuthRequestManager(), + }), createApiFactory({ api: googleAuthApiRef, deps: { From 1be02e09dc73fe1e747101d9bf802816cd9e762c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 10 Nov 2021 18:20:47 +0100 Subject: [PATCH 190/196] core-app-api: fix AppManager tests Signed-off-by: Patrik Oldsberg --- .../core-app-api/src/app/AppManager.test.tsx | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/packages/core-app-api/src/app/AppManager.test.tsx b/packages/core-app-api/src/app/AppManager.test.tsx index 71df5219d1..07401660e8 100644 --- a/packages/core-app-api/src/app/AppManager.test.tsx +++ b/packages/core-app-api/src/app/AppManager.test.tsx @@ -20,7 +20,6 @@ import { renderWithEffects, withLogCollector, } from '@backstage/test-utils'; -import { lightTheme } from '@backstage/theme'; import { render, screen } from '@testing-library/react'; import React, { PropsWithChildren } from 'react'; import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom'; @@ -127,7 +126,7 @@ describe('Integration Test', () => { const HiddenComponent = plugin2.provide( createRoutableExtension({ name: 'HiddenComponent', - component: () => Promise.resolve((_: { path?: string }) =>
), + component: () => Promise.resolve(() =>
), mountPoint: plugin2RouteRef, }), ); @@ -136,7 +135,7 @@ describe('Integration Test', () => { createRoutableExtension({ name: 'ExposedComponent', component: () => - Promise.resolve((_: PropsWithChildren<{ path?: string }>) => { + Promise.resolve(() => { const link1 = useRouteRef(plugin1RouteRef); const link2 = useRouteRef(plugin2RouteRef); const subLink1 = useRouteRef(subRouteRef1); @@ -197,12 +196,13 @@ describe('Integration Test', () => { id: 'light', title: 'Light Theme', variant: 'light', - theme: lightTheme, + Provider: ({ children }) => <>{children}, }, ], icons, plugins: [], components, + configLoader: async () => [], bindRoutes: ({ bind }) => { bind(plugin1.externalRoutes, { extRouteRef1: plugin1RouteRef, @@ -220,8 +220,8 @@ describe('Integration Test', () => { - - + } /> + } /> , @@ -251,12 +251,13 @@ describe('Integration Test', () => { id: 'light', title: 'Light Theme', variant: 'light', - theme: lightTheme, + Provider: ({ children }) => <>{children}, }, ], icons, plugins: [], components, + configLoader: async () => [], bindRoutes: ({ bind }) => { bind(plugin1.externalRoutes, { extRouteRef1: plugin1RouteRef, @@ -272,8 +273,8 @@ describe('Integration Test', () => { - - + } /> + } /> , @@ -308,7 +309,7 @@ describe('Integration Test', () => { id: 'light', title: 'Light Theme', variant: 'light', - theme: lightTheme, + Provider: ({ children }) => <>{children}, }, ], icons, @@ -319,6 +320,7 @@ describe('Integration Test', () => { }), ], components, + configLoader: async () => [], bindRoutes: ({ bind }) => { bind(plugin1.externalRoutes, { extRouteRef1: plugin1RouteRef, @@ -334,8 +336,8 @@ describe('Integration Test', () => { - - + } /> + } /> , @@ -358,12 +360,13 @@ describe('Integration Test', () => { id: 'light', title: 'Light Theme', variant: 'light', - theme: lightTheme, + Provider: ({ children }) => <>{children}, }, ], icons, plugins: [], components, + configLoader: async () => [], bindRoutes: ({ bind }) => { bind(plugin1.externalRoutes, { extRouteRef1: plugin1RouteRef, @@ -380,7 +383,7 @@ describe('Integration Test', () => { } /> - } /> + } /> , @@ -418,12 +421,13 @@ describe('Integration Test', () => { id: 'light', title: 'Light Theme', variant: 'light', - theme: lightTheme, + Provider: ({ children }) => <>{children}, }, ], icons, plugins: [], components, + configLoader: async () => [], bindRoutes: ({ bind }) => { bind(plugin1.externalRoutes, { extRouteRef1: plugin1RouteRef, @@ -440,9 +444,9 @@ describe('Integration Test', () => { - - - + }> + } /> + , From 46570fecbc1076cf9b2d1c13241c05924e2c20d6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 10 Nov 2021 18:50:22 +0100 Subject: [PATCH 191/196] test-utils: migrate theme usage and switch to actual MUI icons Signed-off-by: Patrik Oldsberg --- packages/test-utils/package.json | 1 + .../test-utils/src/testUtils/appWrappers.tsx | 51 +++++++++++-------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index ab78eab683..6f5f78de8c 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -34,6 +34,7 @@ "@backstage/theme": "^0.2.13", "@backstage/types": "^0.1.1", "@material-ui/core": "^4.12.2", + "@material-ui/icons": "^4.11.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/packages/test-utils/src/testUtils/appWrappers.tsx b/packages/test-utils/src/testUtils/appWrappers.tsx index ab96887a78..cad1df4ecc 100644 --- a/packages/test-utils/src/testUtils/appWrappers.tsx +++ b/packages/test-utils/src/testUtils/appWrappers.tsx @@ -18,6 +18,9 @@ import React, { ComponentType, ReactNode, ReactElement } from 'react'; import { MemoryRouter } from 'react-router'; import { Route } from 'react-router-dom'; import { lightTheme } from '@backstage/theme'; +import { ThemeProvider } from '@material-ui/core/styles'; +import { CssBaseline } from '@material-ui/core'; +import MockIcon from '@material-ui/icons/AcUnit'; import { createSpecializedApp } from '@backstage/core-app-api'; import { BootErrorPageProps, @@ -32,28 +35,28 @@ import { defaultApis } from './defaultApis'; import { mockApis } from './mockApis'; const mockIcons = { - 'kind:api': () => , - 'kind:component': () => , - 'kind:domain': () => , - 'kind:group': () => , - 'kind:location': () => , - 'kind:system': () => , - 'kind:user': () => , + 'kind:api': MockIcon, + 'kind:component': MockIcon, + 'kind:domain': MockIcon, + 'kind:group': MockIcon, + 'kind:location': MockIcon, + 'kind:system': MockIcon, + 'kind:user': MockIcon, - brokenImage: () => , - catalog: () => , - scaffolder: () => , - techdocs: () => , - search: () => , - chat: () => , - dashboard: () => , - docs: () => , - email: () => , - github: () => , - group: () => , - help: () => , - user: () => , - warning: () => , + brokenImage: MockIcon, + catalog: MockIcon, + scaffolder: MockIcon, + techdocs: MockIcon, + search: MockIcon, + chat: MockIcon, + dashboard: MockIcon, + docs: MockIcon, + email: MockIcon, + github: MockIcon, + group: MockIcon, + help: MockIcon, + user: MockIcon, + warning: MockIcon, }; const ErrorBoundaryFallback = ({ error }: { error: Error }) => { @@ -136,9 +139,13 @@ export function wrapInTestApp( themes: [ { id: 'light', - theme: lightTheme, title: 'Test App Theme', variant: 'light', + Provider: ({ children }) => ( + + {children} + + ), }, ], bindRoutes: ({ bind }) => { From b85ccce84df919c10d01cacce536211bb3e46a02 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 11 Nov 2021 17:28:52 +0100 Subject: [PATCH 192/196] app-defaults: update internal deps Signed-off-by: Patrik Oldsberg --- packages/app-defaults/package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json index 76101f0ad4..9438966b3b 100644 --- a/packages/app-defaults/package.json +++ b/packages/app-defaults/package.json @@ -29,18 +29,18 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.2", - "@backstage/core-app-api": "^0.1.19", - "@backstage/core-plugin-api": "^0.1.12", - "@backstage/theme": "^0.2.12", + "@backstage/core-components": "^0.7.3", + "@backstage/core-app-api": "^0.1.20", + "@backstage/core-plugin-api": "^0.1.13", + "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "react": "^16.12.0", "react-router-dom": "6.0.0-beta.0" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/test-utils": "^0.1.20", + "@backstage/cli": "^0.8.2", + "@backstage/test-utils": "^0.1.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@types/jest": "^26.0.7", From 25f637f39f5066b2c7d3bc21118f712b7ac2a974 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 11 Nov 2021 18:15:39 +0100 Subject: [PATCH 193/196] cli: make sure JSS styles receive the highest priority Signed-off-by: Patrik Oldsberg --- .changeset/moody-snails-admire.md | 5 ++++ packages/cli/src/lib/bundler/transforms.ts | 27 +++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 .changeset/moody-snails-admire.md diff --git a/.changeset/moody-snails-admire.md b/.changeset/moody-snails-admire.md new file mode 100644 index 0000000000..7622546989 --- /dev/null +++ b/.changeset/moody-snails-admire.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': minor +--- + +Tweaked style insertion logic to make sure that JSS stylesheets always receive the highest priority. diff --git a/packages/cli/src/lib/bundler/transforms.ts b/packages/cli/src/lib/bundler/transforms.ts index cf9e873a66..863728b8f8 100644 --- a/packages/cli/src/lib/bundler/transforms.ts +++ b/packages/cli/src/lib/bundler/transforms.ts @@ -32,6 +32,23 @@ export const transforms = (options: TransformOptions): Transforms => { const extraTransforms = isDev ? ['react-hot-loader'] : []; + // This ensures that styles inserted from the style-loader and any + // async style chunks are always given lower priority than JSS styles. + // Note that this function is stringified and executed in the browser + // after transpilation, so stick to simple syntax + function insertBeforeJssStyles(element: any) { + const head = document.head; + // This makes sure that any style elements we insert get put before the + // dynamic styles from JSS, such as the ones from `makeStyles()`. + // TODO(Rugvip): This will likely break in material-ui v5, keep an eye on it. + const firstJssNode = head.querySelector('style[data-jss]'); + if (!firstJssNode) { + head.appendChild(element); + } else { + head.insertBefore(element, firstJssNode); + } + } + const loaders = [ { test: /\.(tsx?)$/, @@ -112,7 +129,14 @@ export const transforms = (options: TransformOptions): Transforms => { { test: /\.css$/i, use: [ - isDev ? require.resolve('style-loader') : MiniCssExtractPlugin.loader, + isDev + ? { + loader: require.resolve('style-loader'), + options: { + insert: insertBeforeJssStyles, + }, + } + : MiniCssExtractPlugin.loader, { loader: require.resolve('css-loader'), options: { @@ -132,6 +156,7 @@ export const transforms = (options: TransformOptions): Transforms => { new MiniCssExtractPlugin({ filename: 'static/[name].[contenthash:8].css', chunkFilename: 'static/[name].[id].[contenthash:8].css', + insert: insertBeforeJssStyles, // Only applies to async chunks }), ); } From 9010cdc4d718f6ab46c88681b9e8e5188117a08d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 11 Nov 2021 15:48:26 +0100 Subject: [PATCH 194/196] add snyk ignores for the node-gyp tar vuln MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- packages/backend-test-utils/.snyk | 47 +++++++++++++++++++++++++++++++ packages/backend/.snyk | 47 +++++++++++++++++++++++++++++++ plugins/catalog-backend/.snyk | 47 +++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+) create mode 100644 packages/backend-test-utils/.snyk create mode 100644 packages/backend/.snyk create mode 100644 plugins/catalog-backend/.snyk diff --git a/packages/backend-test-utils/.snyk b/packages/backend-test-utils/.snyk new file mode 100644 index 0000000000..ad62599e66 --- /dev/null +++ b/packages/backend-test-utils/.snyk @@ -0,0 +1,47 @@ +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.22.1 +# ignores vulnerabilities until expiry date; change duration by modifying expiry date +ignore: + SNYK-JS-TAR-1579155: + - 'sqlite3 > node-gyp > tar': + reason: >- + The only usage is via node-gyp; there is no unpacking of untrusted tar + files + expires: 2022-11-11T14:30:05.581Z + created: 2021-11-11T14:30:05.582Z + SNYK-JS-TAR-1579152: + - 'sqlite3 > node-gyp > tar': + reason: >- + The only usage is via node-gyp; there is no unpacking of untrusted tar + files + expires: 2022-11-11T14:30:05.581Z + created: 2021-11-11T14:30:05.582Z + SNYK-JS-TAR-1579147: + - 'sqlite3 > node-gyp > tar': + reason: >- + The only usage is via node-gyp; there is no unpacking of untrusted tar + files + expires: 2022-11-11T14:30:05.581Z + created: 2021-11-11T14:30:05.582Z + SNYK-JS-TAR-1536758: + - 'sqlite3 > node-gyp > tar': + reason: >- + The only usage is via node-gyp; there is no unpacking of untrusted tar + files + expires: 2022-11-11T14:30:05.581Z + created: 2021-11-11T14:30:05.582Z + SNYK-JS-TAR-1536531: + - 'sqlite3 > node-gyp > tar': + reason: >- + The only usage is via node-gyp; there is no unpacking of untrusted tar + files + expires: 2022-11-11T14:30:05.581Z + created: 2021-11-11T14:30:05.582Z + SNYK-JS-TAR-1536528: + - 'sqlite3 > node-gyp > tar': + reason: >- + The only usage is via node-gyp; there is no unpacking of untrusted tar + files + expires: 2022-11-11T14:30:05.581Z + created: 2021-11-11T14:30:05.582Z +patch: {} diff --git a/packages/backend/.snyk b/packages/backend/.snyk new file mode 100644 index 0000000000..ad62599e66 --- /dev/null +++ b/packages/backend/.snyk @@ -0,0 +1,47 @@ +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.22.1 +# ignores vulnerabilities until expiry date; change duration by modifying expiry date +ignore: + SNYK-JS-TAR-1579155: + - 'sqlite3 > node-gyp > tar': + reason: >- + The only usage is via node-gyp; there is no unpacking of untrusted tar + files + expires: 2022-11-11T14:30:05.581Z + created: 2021-11-11T14:30:05.582Z + SNYK-JS-TAR-1579152: + - 'sqlite3 > node-gyp > tar': + reason: >- + The only usage is via node-gyp; there is no unpacking of untrusted tar + files + expires: 2022-11-11T14:30:05.581Z + created: 2021-11-11T14:30:05.582Z + SNYK-JS-TAR-1579147: + - 'sqlite3 > node-gyp > tar': + reason: >- + The only usage is via node-gyp; there is no unpacking of untrusted tar + files + expires: 2022-11-11T14:30:05.581Z + created: 2021-11-11T14:30:05.582Z + SNYK-JS-TAR-1536758: + - 'sqlite3 > node-gyp > tar': + reason: >- + The only usage is via node-gyp; there is no unpacking of untrusted tar + files + expires: 2022-11-11T14:30:05.581Z + created: 2021-11-11T14:30:05.582Z + SNYK-JS-TAR-1536531: + - 'sqlite3 > node-gyp > tar': + reason: >- + The only usage is via node-gyp; there is no unpacking of untrusted tar + files + expires: 2022-11-11T14:30:05.581Z + created: 2021-11-11T14:30:05.582Z + SNYK-JS-TAR-1536528: + - 'sqlite3 > node-gyp > tar': + reason: >- + The only usage is via node-gyp; there is no unpacking of untrusted tar + files + expires: 2022-11-11T14:30:05.581Z + created: 2021-11-11T14:30:05.582Z +patch: {} diff --git a/plugins/catalog-backend/.snyk b/plugins/catalog-backend/.snyk new file mode 100644 index 0000000000..ad62599e66 --- /dev/null +++ b/plugins/catalog-backend/.snyk @@ -0,0 +1,47 @@ +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.22.1 +# ignores vulnerabilities until expiry date; change duration by modifying expiry date +ignore: + SNYK-JS-TAR-1579155: + - 'sqlite3 > node-gyp > tar': + reason: >- + The only usage is via node-gyp; there is no unpacking of untrusted tar + files + expires: 2022-11-11T14:30:05.581Z + created: 2021-11-11T14:30:05.582Z + SNYK-JS-TAR-1579152: + - 'sqlite3 > node-gyp > tar': + reason: >- + The only usage is via node-gyp; there is no unpacking of untrusted tar + files + expires: 2022-11-11T14:30:05.581Z + created: 2021-11-11T14:30:05.582Z + SNYK-JS-TAR-1579147: + - 'sqlite3 > node-gyp > tar': + reason: >- + The only usage is via node-gyp; there is no unpacking of untrusted tar + files + expires: 2022-11-11T14:30:05.581Z + created: 2021-11-11T14:30:05.582Z + SNYK-JS-TAR-1536758: + - 'sqlite3 > node-gyp > tar': + reason: >- + The only usage is via node-gyp; there is no unpacking of untrusted tar + files + expires: 2022-11-11T14:30:05.581Z + created: 2021-11-11T14:30:05.582Z + SNYK-JS-TAR-1536531: + - 'sqlite3 > node-gyp > tar': + reason: >- + The only usage is via node-gyp; there is no unpacking of untrusted tar + files + expires: 2022-11-11T14:30:05.581Z + created: 2021-11-11T14:30:05.582Z + SNYK-JS-TAR-1536528: + - 'sqlite3 > node-gyp > tar': + reason: >- + The only usage is via node-gyp; there is no unpacking of untrusted tar + files + expires: 2022-11-11T14:30:05.581Z + created: 2021-11-11T14:30:05.582Z +patch: {} From 1e33ce7407ce162bf706cc805aa3cd87173feded Mon Sep 17 00:00:00 2001 From: Jeremy Guarini Date: Thu, 11 Nov 2021 12:41:30 -0800 Subject: [PATCH 195/196] swapout material-table for backstage table Signed-off-by: Jeremy Guarini --- plugins/gcp-projects/package.json | 1 - .../ProjectListPage/ProjectListPage.tsx | 40 ++----------------- 2 files changed, 3 insertions(+), 38 deletions(-) diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 48205e4fd8..65458ea88b 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -37,7 +37,6 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", - "material-table": "^1.69.3", "react": "^16.13.1", "react-dom": "^16.13.1", "react-router-dom": "^6.0.0-beta.0", diff --git a/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx b/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx index 2797d17d0d..b37dc2fa50 100644 --- a/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx +++ b/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx @@ -16,16 +16,7 @@ // NEEDS WORK import { Button, LinearProgress, Tooltip, Typography } from '@material-ui/core'; -import MaterialTable from 'material-table'; -import ArrowDownward from '@material-ui/icons/ArrowDownward'; -import ChevronLeft from '@material-ui/icons/ChevronLeft'; -import ChevronRight from '@material-ui/icons/ChevronRight'; -import Clear from '@material-ui/icons/Clear'; -import FilterList from '@material-ui/icons/FilterList'; -import FirstPage from '@material-ui/icons/FirstPage'; -import LastPage from '@material-ui/icons/LastPage'; -import Search from '@material-ui/icons/Search'; -import React, { forwardRef } from 'react'; +import React from 'react'; import { useAsync } from 'react-use'; import { gcpApiRef, Project } from '../../api'; @@ -37,6 +28,7 @@ import { HeaderLabel, Link, Page, + Table, SupportButton, WarningPanel, } from '@backstage/core-components'; @@ -90,31 +82,7 @@ const PageContents = () => { return (
- ( - - )), - FirstPage: forwardRef((props, ref) => ( - - )), - LastPage: forwardRef((props, ref) => ( - - )), - NextPage: forwardRef((props, ref) => ( - - )), - PreviousPage: forwardRef((props, ref) => ( - - )), - ResetSearch: forwardRef((props, ref) => ( - - )), - Search: forwardRef((props, ref) => ), - SortArrow: forwardRef((props, ref) => ( - - )), - }} +
{ })) || [] } options={{ - filtering: true, pageSize: 5, pageSizeOptions: [5, 10, 25, 50, 100], - showTitle: false, }} /> From 4f3ba47bf362446f47ec29c05507d351724dee84 Mon Sep 17 00:00:00 2001 From: Jeremy Guarini Date: Thu, 11 Nov 2021 14:49:40 -0800 Subject: [PATCH 196/196] ran yarn install to clear out data-grid dependency Signed-off-by: Jeremy Guarini --- yarn.lock | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/yarn.lock b/yarn.lock index fc66a02031..517d66ba0c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4416,16 +4416,6 @@ react-is "^16.8.0 || ^17.0.0" react-transition-group "^4.4.0" -"@material-ui/data-grid@^4.0.0-alpha.37": - version "4.0.0-alpha.37" - resolved "https://registry.npmjs.org/@material-ui/data-grid/-/data-grid-4.0.0-alpha.37.tgz#89d907c4e94e6a0db4e89e4f59160f7811546ca2" - integrity sha512-3T2AG31aad/lWLMLwn1XUP4mUf3H9YZES17dGuYByzkRLCXbBZHBTPEnCctWukajzwm+v0KGg3QpwitGoiDAjA== - dependencies: - "@material-ui/utils" "^5.0.0-alpha.14" - clsx "^1.0.4" - prop-types "^15.7.2" - reselect "^4.0.0" - "@material-ui/icons@^4.11.2", "@material-ui/icons@^4.9.1": version "4.11.2" resolved "https://registry.npmjs.org/@material-ui/icons/-/icons-4.11.2.tgz#b3a7353266519cd743b6461ae9fdfcb1b25eb4c5" @@ -4513,17 +4503,6 @@ prop-types "^15.7.2" react-is "^16.8.0 || ^17.0.0" -"@material-ui/utils@^5.0.0-alpha.14": - version "5.0.0-beta.5" - resolved "https://registry.npmjs.org/@material-ui/utils/-/utils-5.0.0-beta.5.tgz#de492037e1f1f0910fda32e6f11b66dfcde2a1c2" - integrity sha512-wtJ3ovXWZdTAz5eLBqvMpYH/IBJb3qMQbGCyL1i00+sf7AUlAuv4QLx+QtX/siA6L7IpxUQVfqpoCpQH1eYRpQ== - dependencies: - "@babel/runtime" "^7.14.8" - "@types/prop-types" "^15.7.4" - "@types/react-is" "^16.7.1 || ^17.0.0" - prop-types "^15.7.2" - react-is "^17.0.2" - "@mattiasbuelens/web-streams-polyfill@^0.2.0": version "0.2.1" resolved "https://registry.npmjs.org/@mattiasbuelens/web-streams-polyfill/-/web-streams-polyfill-0.2.1.tgz#d7c4aa94f98084ec0787be084d47167d62ea5f67" @@ -7455,7 +7434,7 @@ resolved "https://registry.npmjs.org/@types/pretty-hrtime/-/pretty-hrtime-1.0.1.tgz#72a26101dc567b0d68fd956cf42314556e42d601" integrity sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ== -"@types/prop-types@*", "@types/prop-types@^15.7.3", "@types/prop-types@^15.7.4": +"@types/prop-types@*", "@types/prop-types@^15.7.3": version "15.7.4" resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== @@ -7496,13 +7475,6 @@ dependencies: "@types/react" "*" -"@types/react-is@^16.7.1 || ^17.0.0": - version "17.0.3" - resolved "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz#2d855ba575f2fc8d17ef9861f084acc4b90a137a" - integrity sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw== - dependencies: - "@types/react" "*" - "@types/react-lazylog@^4.5.0": version "4.5.1" resolved "https://registry.npmjs.org/@types/react-lazylog/-/react-lazylog-4.5.1.tgz#babb5d814f7035b5434518769975e12f299356a8"