From 38b17fe361dadd9a98a7ca95bd5c6e39bfb3f2ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2020 04:45:38 +0000 Subject: [PATCH 001/252] chore(deps-dev): bump @testing-library/cypress from 6.0.1 to 7.0.1 Bumps [@testing-library/cypress](https://github.com/kentcdodds/cypress-testing-library) from 6.0.1 to 7.0.1. - [Release notes](https://github.com/kentcdodds/cypress-testing-library/releases) - [Changelog](https://github.com/testing-library/cypress-testing-library/blob/master/CHANGELOG.md) - [Commits](https://github.com/kentcdodds/cypress-testing-library/compare/v6.0.1...v7.0.1) Signed-off-by: dependabot[bot] --- packages/app/package.json | 2 +- yarn.lock | 21 ++++++--------------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index e8dfb94046..a6875ba86f 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -46,7 +46,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@testing-library/cypress": "^6.0.0", + "@testing-library/cypress": "^7.0.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/yarn.lock b/yarn.lock index a3b64f0650..d17878a727 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4467,16 +4467,15 @@ dependencies: defer-to-connect "^2.0.0" -"@testing-library/cypress@^6.0.0": - version "6.0.1" - resolved "https://registry.npmjs.org/@testing-library/cypress/-/cypress-6.0.1.tgz#c924a69617db6a403c498abcb63759b79318fc76" - integrity sha512-hcPu2OnVuSTX1yDubEe7TBRD6mP2VgyopP1WTBIrJP79INPZdgOAX+TMNH68uZ/r5b4C7100IB4hjPIEQ+/Xog== +"@testing-library/cypress@^7.0.1": + version "7.0.1" + resolved "https://registry.npmjs.org/@testing-library/cypress/-/cypress-7.0.1.tgz#2843033acaefe96cb4cf789f16e98d957383e59d" + integrity sha512-LtggqG/7Hdc1EiKdmqXQwxWOO3ET1dkZtq0S8mIe8o+xaOtaVLrdCn0dE8Bi4Aj7z3w51w6wN9STdYymnUPlnQ== dependencies: "@babel/runtime" "^7.11.2" "@testing-library/dom" "^7.22.2" - "@types/testing-library__cypress" "^5.0.6" -"@testing-library/dom@^7.11.0", "@testing-library/dom@^7.17.1", "@testing-library/dom@^7.22.2": +"@testing-library/dom@^7.17.1", "@testing-library/dom@^7.22.2": version "7.23.0" resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-7.23.0.tgz#c54c0fa53705ad867bcefb52fc0c96487fbc10f6" integrity sha512-H5m090auYH+obdZmsaYLrSWC5OauWD2CvNbz88KBxQJoXgkJzbU0DpAG8BS7Evj5WqCC3nAAKrLS6vw0ljUYLg== @@ -5600,14 +5599,6 @@ dependencies: "@types/estree" "*" -"@types/testing-library__cypress@^5.0.6": - version "5.0.6" - resolved "https://registry.npmjs.org/@types/testing-library__cypress/-/testing-library__cypress-5.0.6.tgz#9015f575c1a98f05996a4fe769071134ee488c26" - integrity sha512-TUp5wfanU7zUZigKqIeQDChnHQ1MEzbYqrI5iCQMFiesWNOASWm/el1lFBh1JPqmd6GkdDdDiHYJnkqd9le2ww== - dependencies: - "@testing-library/dom" "^7.11.0" - cypress "*" - "@types/testing-library__jest-dom@^5.9.1": version "5.9.1" resolved "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.1.tgz#aba5ee062b7880f69c212ef769389f30752806e5" @@ -9338,7 +9329,7 @@ cyclist@^1.0.1: resolved "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= -cypress@*, cypress@^4.2.0: +cypress@^4.2.0: version "4.12.1" resolved "https://registry.npmjs.org/cypress/-/cypress-4.12.1.tgz#0ead1b9f4c0917d69d8b57f996b6e01fe693b6ec" integrity sha512-9SGIPEmqU8vuRA6xst2CMTYd9sCFCxKSzrHt0wr+w2iAQMCIIsXsQ5Gplns1sT6LDbZcmLv6uehabAOl3fhc9Q== From 53eb5e61e31ed470d5c8acf5c33328aeae1c9198 Mon Sep 17 00:00:00 2001 From: Marvin9 Date: Tue, 13 Oct 2020 19:13:05 +0530 Subject: [PATCH 002/252] feat: Implement CatalogEntityClient, mainly to get Template entity from template name --- .../src/lib/catalog/CatalogEntityClient.ts | 70 +++++++++++++++++++ .../src/lib/catalog/index.ts | 16 +++++ 2 files changed, 86 insertions(+) create mode 100644 plugins/scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts create mode 100644 plugins/scaffolder-backend/src/lib/catalog/index.ts diff --git a/plugins/scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts b/plugins/scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts new file mode 100644 index 0000000000..48856e7783 --- /dev/null +++ b/plugins/scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts @@ -0,0 +1,70 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fetch from 'node-fetch'; +import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; +import { + ConflictError, + NotFoundError, + PluginEndpointDiscovery, +} from '@backstage/backend-common'; + +/** + * A catalog client tailored for reading out entity data from the catalog. + */ +export class CatalogEntityClient { + private readonly discovery: PluginEndpointDiscovery; + + constructor(options: { discovery: PluginEndpointDiscovery }) { + this.discovery = options.discovery; + } + + /** + * Looks up a single template using a template name. + * + * Throws a NotFoundError or ConflictError if 0 or multiple templates are found. + */ + async findTemplate(templateName: string): Promise { + const params = new URLSearchParams(); + params.append('kind', 'Template'); + + params.append('metadata.name', templateName); + + const baseUrl = await this.discovery.getBaseUrl('catalog'); + const response = await fetch(`${baseUrl}/entities?${params}`); + + if (!response.ok) { + const text = await response.text(); + throw new Error( + `Request failed with ${response.status} ${response.statusText}, ${text}`, + ); + } + + const templates: TemplateEntityV1alpha1[] = await response.json(); + + if (templates.length !== 1) { + if (templates.length > 1) { + throw new ConflictError( + 'Templates lookup resulted in multiple matches', + ); + } else { + throw new NotFoundError('Template not found'); + } + } + + return templates[0]; + } +} diff --git a/plugins/scaffolder-backend/src/lib/catalog/index.ts b/plugins/scaffolder-backend/src/lib/catalog/index.ts new file mode 100644 index 0000000000..a8de546e00 --- /dev/null +++ b/plugins/scaffolder-backend/src/lib/catalog/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { CatalogEntityClient } from './CatalogEntityClient'; From a24b6577212edb80009ac79b8121de12a86278b5 Mon Sep 17 00:00:00 2001 From: Marvin9 Date: Tue, 13 Oct 2020 19:14:49 +0530 Subject: [PATCH 003/252] feat: use CatalogEntityClient --- plugins/scaffolder-backend/package.json | 3 ++- .../scaffolder-backend/src/service/router.ts | 23 +++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 66d6457333..fac30df196 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -44,7 +44,8 @@ "nodegit": "0.27.0", "uuid": "^8.2.0", "winston": "^3.2.1", - "yaml": "^1.10.0" + "yaml": "^1.10.0", + "node-fetch": "^2.6.1" }, "devDependencies": { "@backstage/cli": "^0.1.1-alpha.25", diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index f61d2566b9..a556916df0 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -14,8 +14,12 @@ * limitations under the License. */ +import { + loadBackendConfig, + SingleHostDiscovery, +} from '@backstage/backend-common'; +import { JsonValue, ConfigReader } from '@backstage/config'; import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; -import { JsonValue } from '@backstage/config'; import Docker from 'dockerode'; import express from 'express'; import Router from 'express-promise-router'; @@ -28,6 +32,7 @@ import { TemplaterBuilder, PublisherBuilder, } from '../scaffolder'; +import { CatalogEntityClient } from '../lib/catalog'; import { validate, ValidatorResult } from 'jsonschema'; export interface RouterOptions { @@ -56,6 +61,10 @@ export async function createRouter( const logger = parentLogger.child({ plugin: 'scaffolder' }); const jobProcessor = new JobProcessor(); + const config = ConfigReader.fromConfigs(await loadBackendConfig()); + const discovery = SingleHostDiscovery.fromConfig(config); + const entityClient = new CatalogEntityClient({ discovery }); + router .get('/v1/job/:jobId', ({ params }, res) => { const job = jobProcessor.get(params.jobId); @@ -81,14 +90,24 @@ export async function createRouter( }); }) .post('/v1/jobs', async (req, res) => { - const template: TemplateEntityV1alpha1 = req.body.template; + const templateName: string = req.body.templateName; const values: RequiredTemplateValues & Record = req.body.values; + let template: TemplateEntityV1alpha1; + try { + template = await entityClient.findTemplate(templateName); + } catch (e) { + // help me here + res.status(400).json({ errors: e }); + return; + } + const validationResult: ValidatorResult = validate( values, template.spec.schema, ); + if (!validationResult.valid) { res.status(400).json({ errors: validationResult.errors }); return; From 91e425fdabb5a024d81114ab643655422e71fb38 Mon Sep 17 00:00:00 2001 From: Marvin9 Date: Tue, 13 Oct 2020 19:16:22 +0530 Subject: [PATCH 004/252] feat: Pass entity unique id to POST body of /v1/jobs instead of entire entity --- plugins/scaffolder/src/api.ts | 10 +++------- .../src/components/TemplatePage/TemplatePage.tsx | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/plugins/scaffolder/src/api.ts b/plugins/scaffolder/src/api.ts index f58c8e68d1..ca52418e92 100644 --- a/plugins/scaffolder/src/api.ts +++ b/plugins/scaffolder/src/api.ts @@ -15,7 +15,6 @@ */ import { createApiRef, DiscoveryApi } from '@backstage/core'; -import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; export const scaffolderApiRef = createApiRef({ id: 'plugin.scaffolder.service', @@ -33,20 +32,17 @@ export class ScaffolderApi { * Executes the scaffolding of a component, given a template and its * parameter values. * - * @param template Template entity for the scaffolder to use. New project is going to be created out of this template. + * @param templateName Template name for the scaffolder to use. New project is going to be created out of this template. * @param values Parameters for the template, e.g. name, description */ - async scaffold( - template: TemplateEntityV1alpha1, - values: Record, - ) { + async scaffold(templateName: string, values: Record) { const url = `${await this.discoveryApi.getBaseUrl('scaffolder')}/v1/jobs`; const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ template, values: { ...values } }), + body: JSON.stringify({ templateName, values: { ...values } }), }); if (response.status !== 201) { diff --git a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx index e68c795bd6..6c71a2eab1 100644 --- a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx +++ b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx @@ -97,7 +97,7 @@ export const TemplatePage = () => { const handleCreate = async () => { try { - const job = await scaffolderApi.scaffold(template!, formState); + const job = await scaffolderApi.scaffold(templateName, formState); setJobId(job); } catch (e) { errorApi.post(e); From 34a94c9a583dbbbd2ec156ac3769c114dedb4415 Mon Sep 17 00:00:00 2001 From: Jesko Steinberg Date: Mon, 12 Oct 2020 15:06:27 +0200 Subject: [PATCH 005/252] feat: make default scopes of Auth APIs configurable --- .../apis/implementations/auth/auth0/Auth0Auth.ts | 4 +++- .../implementations/auth/gitlab/GitlabAuth.ts | 4 +++- .../implementations/auth/google/GoogleAuth.ts | 16 +++++++++------- .../auth/microsoft/MicrosoftAuth.ts | 16 +++++++++------- .../apis/implementations/auth/okta/OktaAuth.ts | 4 +++- 5 files changed, 27 insertions(+), 17 deletions(-) diff --git a/packages/core-api/src/apis/implementations/auth/auth0/Auth0Auth.ts b/packages/core-api/src/apis/implementations/auth/auth0/Auth0Auth.ts index 40e537169c..793d9f90d3 100644 --- a/packages/core-api/src/apis/implementations/auth/auth0/Auth0Auth.ts +++ b/packages/core-api/src/apis/implementations/auth/auth0/Auth0Auth.ts @@ -27,6 +27,7 @@ type CreateOptions = { discoveryApi: DiscoveryApi; oauthRequestApi: OAuthRequestApi; + defaultScopes?: string[]; environment?: string; provider?: AuthProvider & { id: string }; }; @@ -43,13 +44,14 @@ class Auth0Auth { environment = 'development', provider = DEFAULT_PROVIDER, oauthRequestApi, + defaultScopes = ['openid', `email`, `profile`], }: CreateOptions): typeof auth0AuthApiRef.T { return OAuth2.create({ discoveryApi, oauthRequestApi, provider, environment, - defaultScopes: ['openid', `email`, `profile`], + defaultScopes, }); } } diff --git a/packages/core-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts b/packages/core-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts index 9e2acd4537..b6a2757143 100644 --- a/packages/core-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts +++ b/packages/core-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts @@ -27,6 +27,7 @@ type CreateOptions = { discoveryApi: DiscoveryApi; oauthRequestApi: OAuthRequestApi; + defaultScopes?: string[]; environment?: string; provider?: AuthProvider & { id: string }; }; @@ -43,13 +44,14 @@ class GitlabAuth { environment = 'development', provider = DEFAULT_PROVIDER, oauthRequestApi, + defaultScopes = ['read_user'], }: CreateOptions): typeof gitlabAuthApiRef.T { return OAuth2.create({ discoveryApi, oauthRequestApi, provider, environment, - defaultScopes: ['read_user'], + defaultScopes, }); } } diff --git a/packages/core-api/src/apis/implementations/auth/google/GoogleAuth.ts b/packages/core-api/src/apis/implementations/auth/google/GoogleAuth.ts index 7e84226508..2d2ff3ede7 100644 --- a/packages/core-api/src/apis/implementations/auth/google/GoogleAuth.ts +++ b/packages/core-api/src/apis/implementations/auth/google/GoogleAuth.ts @@ -27,6 +27,7 @@ type CreateOptions = { discoveryApi: DiscoveryApi; oauthRequestApi: OAuthRequestApi; + defaultScopes?: string[]; environment?: string; provider?: AuthProvider & { id: string }; }; @@ -37,25 +38,26 @@ const DEFAULT_PROVIDER = { icon: GoogleIcon, }; +const SCOPE_PREFIX = 'https://www.googleapis.com/auth/'; + class GoogleAuth { static create({ discoveryApi, oauthRequestApi, environment = 'development', provider = DEFAULT_PROVIDER, + defaultScopes = [ + 'openid', + `${SCOPE_PREFIX}userinfo.email`, + `${SCOPE_PREFIX}userinfo.profile`, + ], }: CreateOptions): typeof googleAuthApiRef.T { - const SCOPE_PREFIX = 'https://www.googleapis.com/auth/'; - return OAuth2.create({ discoveryApi, oauthRequestApi, provider, environment, - defaultScopes: [ - 'openid', - `${SCOPE_PREFIX}userinfo.email`, - `${SCOPE_PREFIX}userinfo.profile`, - ], + defaultScopes, scopeTransform(scopes: string[]) { return scopes.map(scope => { if (scope === 'openid') { diff --git a/packages/core-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts b/packages/core-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts index 241ac7b802..db9180b31c 100644 --- a/packages/core-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts +++ b/packages/core-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts @@ -28,6 +28,7 @@ type CreateOptions = { discoveryApi: DiscoveryApi; oauthRequestApi: OAuthRequestApi; + defaultScopes?: string[]; environment?: string; provider?: AuthProvider & { id: string }; }; @@ -44,19 +45,20 @@ class MicrosoftAuth { provider = DEFAULT_PROVIDER, oauthRequestApi, discoveryApi, + defaultScopes = [ + 'openid', + 'offline_access', + 'profile', + 'email', + 'User.Read', + ], }: CreateOptions): typeof microsoftAuthApiRef.T { return OAuth2.create({ discoveryApi, oauthRequestApi, provider, environment, - defaultScopes: [ - 'openid', - 'offline_access', - 'profile', - 'email', - 'User.Read', - ], + defaultScopes, }); } } diff --git a/packages/core-api/src/apis/implementations/auth/okta/OktaAuth.ts b/packages/core-api/src/apis/implementations/auth/okta/OktaAuth.ts index 7e9ff77678..7382d24d62 100644 --- a/packages/core-api/src/apis/implementations/auth/okta/OktaAuth.ts +++ b/packages/core-api/src/apis/implementations/auth/okta/OktaAuth.ts @@ -27,6 +27,7 @@ type CreateOptions = { discoveryApi: DiscoveryApi; oauthRequestApi: OAuthRequestApi; + defaultScopes?: string[]; environment?: string; provider?: AuthProvider & { id: string }; }; @@ -55,13 +56,14 @@ class OktaAuth { environment = 'development', provider = DEFAULT_PROVIDER, oauthRequestApi, + defaultScopes = ['openid', 'email', 'profile', 'offline_access'], }: CreateOptions): typeof oktaAuthApiRef.T { return OAuth2.create({ discoveryApi, oauthRequestApi, provider, environment, - defaultScopes: ['openid', 'email', 'profile', 'offline_access'], + defaultScopes, scopeTransform(scopes) { return scopes.map(scope => { if (OKTA_OIDC_SCOPES.has(scope)) { From c5acbbb8c42114338747935d7c044898f6578bd0 Mon Sep 17 00:00:00 2001 From: Marvin9 Date: Fri, 23 Oct 2020 23:32:10 +0530 Subject: [PATCH 006/252] chore: replace node-fetch => cross-fetch --- plugins/scaffolder-backend/package.json | 2 +- .../scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts | 2 +- yarn.lock | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index fac30df196..e87f86961e 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -45,7 +45,7 @@ "uuid": "^8.2.0", "winston": "^3.2.1", "yaml": "^1.10.0", - "node-fetch": "^2.6.1" + "cross-fetch": "^3.0.6" }, "devDependencies": { "@backstage/cli": "^0.1.1-alpha.25", diff --git a/plugins/scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts b/plugins/scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts index 48856e7783..84676b63fe 100644 --- a/plugins/scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts +++ b/plugins/scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import fetch from 'node-fetch'; +import fetch from 'cross-fetch'; import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; import { ConflictError, diff --git a/yarn.lock b/yarn.lock index 4832a549b8..2c7c609835 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9021,7 +9021,7 @@ cross-env@^7.0.0: dependencies: cross-spawn "^7.0.1" -cross-fetch@3.0.6, cross-fetch@^3.0.4, cross-fetch@^3.0.5: +cross-fetch@3.0.6, cross-fetch@^3.0.4, cross-fetch@^3.0.5, cross-fetch@^3.0.6: version "3.0.6" resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c" integrity sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ== From 8440f8f91be240cf96c7447262547adb653ba410 Mon Sep 17 00:00:00 2001 From: Marvin9 Date: Tue, 27 Oct 2020 10:02:56 +0530 Subject: [PATCH 007/252] feat: load config directly, #2976 --- plugins/scaffolder-backend/src/service/router.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index a556916df0..bf1eeae8dd 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -17,8 +17,9 @@ import { loadBackendConfig, SingleHostDiscovery, + getRootLogger, } from '@backstage/backend-common'; -import { JsonValue, ConfigReader } from '@backstage/config'; +import { JsonValue } from '@backstage/config'; import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; import Docker from 'dockerode'; import express from 'express'; @@ -61,7 +62,10 @@ export async function createRouter( const logger = parentLogger.child({ plugin: 'scaffolder' }); const jobProcessor = new JobProcessor(); - const config = ConfigReader.fromConfigs(await loadBackendConfig()); + const config = await loadBackendConfig({ + argv: process.argv, + logger: getRootLogger(), + }); const discovery = SingleHostDiscovery.fromConfig(config); const entityClient = new CatalogEntityClient({ discovery }); From f2a9bba5e27ad2e2ec29d3fdccec303d8de1e808 Mon Sep 17 00:00:00 2001 From: Marvin9 Date: Tue, 27 Oct 2020 10:53:40 +0530 Subject: [PATCH 008/252] feat: update related to #3066 --- .../src/lib/catalog/CatalogEntityClient.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugins/scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts b/plugins/scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts index 84676b63fe..4541f03a4a 100644 --- a/plugins/scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts +++ b/plugins/scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts @@ -38,13 +38,15 @@ export class CatalogEntityClient { * Throws a NotFoundError or ConflictError if 0 or multiple templates are found. */ async findTemplate(templateName: string): Promise { - const params = new URLSearchParams(); - params.append('kind', 'Template'); - - params.append('metadata.name', templateName); + const conditions = [ + 'kind=template', + `metadata.name=${encodeURIComponent(templateName)}`, + ]; const baseUrl = await this.discovery.getBaseUrl('catalog'); - const response = await fetch(`${baseUrl}/entities?${params}`); + const response = await fetch( + `${baseUrl}/entities?filter=${conditions.join(',')}`, + ); if (!response.ok) { const text = await response.text(); From 097e3d222a61889c029c109d9693f8ec298ada0d Mon Sep 17 00:00:00 2001 From: Marvin9 Date: Wed, 28 Oct 2020 10:54:38 +0530 Subject: [PATCH 009/252] fix: error handling --- plugins/scaffolder-backend/src/service/router.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 83a0cbd398..efe8ee46f6 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -115,10 +115,8 @@ export async function createRouter( let template: TemplateEntityV1alpha1; try { template = await entityClient.findTemplate(templateName); - } catch (e) { - // help me here - res.status(400).json({ errors: e }); - return; + } catch (err) { + throw err; } const validationResult: ValidatorResult = validate( From 833a652d055a8dafa21a5bda57b8a404fab81696 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 28 Oct 2020 10:20:43 +0100 Subject: [PATCH 010/252] v0.1.1 --- lerna.json | 2 +- packages/app/package.json | 56 ++++++++++++------------- packages/backend-common/package.json | 12 +++--- packages/backend/package.json | 32 +++++++------- packages/catalog-model/package.json | 6 +-- packages/cli-common/package.json | 2 +- packages/cli/package.json | 20 ++++----- packages/config-loader/package.json | 4 +- packages/config/package.json | 2 +- packages/core-api/package.json | 12 +++--- packages/core/package.json | 12 +++--- packages/create-app/package.json | 52 +++++++++++------------ packages/dev-utils/package.json | 10 ++--- packages/docgen/package.json | 2 +- packages/e2e-test/package.json | 4 +- packages/storybook/package.json | 4 +- packages/techdocs-cli/package.json | 4 +- packages/test-utils-core/package.json | 2 +- packages/test-utils/package.json | 10 ++--- packages/theme/package.json | 4 +- plugins/api-docs/package.json | 16 +++---- plugins/app-backend/package.json | 8 ++-- plugins/auth-backend/package.json | 10 ++--- plugins/catalog-backend/package.json | 12 +++--- plugins/catalog-graphql/package.json | 12 +++--- plugins/catalog/package.json | 18 ++++---- plugins/circleci/package.json | 16 +++---- plugins/cloudbuild/package.json | 16 +++---- plugins/cost-insights/package.json | 16 +++---- plugins/explore/package.json | 12 +++--- plugins/gcp-projects/package.json | 12 +++--- plugins/github-actions/package.json | 18 ++++---- plugins/gitops-profiles/package.json | 12 +++--- plugins/graphiql/package.json | 12 +++--- plugins/graphql/package.json | 10 ++--- plugins/jenkins/package.json | 16 +++---- plugins/kubernetes-backend/package.json | 8 ++-- plugins/kubernetes/package.json | 18 ++++---- plugins/lighthouse/package.json | 20 ++++----- plugins/newrelic/package.json | 12 +++--- plugins/proxy-backend/package.json | 8 ++-- plugins/register-component/package.json | 16 +++---- plugins/rollbar-backend/package.json | 8 ++-- plugins/rollbar/package.json | 16 +++---- plugins/scaffolder-backend/package.json | 10 ++--- plugins/scaffolder/package.json | 16 +++---- plugins/sentry-backend/package.json | 6 +-- plugins/sentry/package.json | 14 +++---- plugins/tech-radar/package.json | 14 +++---- plugins/techdocs-backend/package.json | 10 ++--- plugins/techdocs/package.json | 20 ++++----- plugins/user-settings/package.json | 12 +++--- plugins/welcome/package.json | 12 +++--- 53 files changed, 344 insertions(+), 344 deletions(-) diff --git a/lerna.json b/lerna.json index 5871423f78..dd2dd884eb 100644 --- a/lerna.json +++ b/lerna.json @@ -2,5 +2,5 @@ "packages": ["packages/*", "plugins/*"], "npmClient": "yarn", "useWorkspaces": true, - "version": "0.1.1-alpha.26" + "version": "0.1.1" } diff --git a/packages/app/package.json b/packages/app/package.json index 594c12b5e5..5c0b90f0fe 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,36 +1,36 @@ { "name": "example-app", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "private": true, "bundled": true, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/plugin-api-docs": "^0.1.1-alpha.26", - "@backstage/plugin-catalog": "^0.1.1-alpha.26", - "@backstage/plugin-circleci": "^0.1.1-alpha.26", - "@backstage/plugin-cloudbuild": "^0.1.1-alpha.26", - "@backstage/plugin-cost-insights": "^0.1.1-alpha.26", - "@backstage/plugin-explore": "^0.1.1-alpha.26", - "@backstage/plugin-gcp-projects": "^0.1.1-alpha.26", - "@backstage/plugin-github-actions": "^0.1.1-alpha.26", - "@backstage/plugin-gitops-profiles": "^0.1.1-alpha.26", - "@backstage/plugin-graphiql": "^0.1.1-alpha.26", - "@backstage/plugin-jenkins": "^0.1.1-alpha.26", - "@backstage/plugin-kubernetes": "^0.1.1-alpha.26", - "@backstage/plugin-lighthouse": "^0.1.1-alpha.26", - "@backstage/plugin-newrelic": "^0.1.1-alpha.26", - "@backstage/plugin-register-component": "^0.1.1-alpha.26", - "@backstage/plugin-rollbar": "^0.1.1-alpha.26", - "@backstage/plugin-scaffolder": "^0.1.1-alpha.26", - "@backstage/plugin-sentry": "^0.1.1-alpha.26", - "@backstage/plugin-tech-radar": "^0.1.1-alpha.26", - "@backstage/plugin-techdocs": "^0.1.1-alpha.26", - "@backstage/plugin-user-settings": "^0.1.1-alpha.26", - "@backstage/plugin-welcome": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/catalog-model": "^0.1.1", + "@backstage/cli": "^0.1.1", + "@backstage/core": "^0.1.1", + "@backstage/plugin-api-docs": "^0.1.1", + "@backstage/plugin-catalog": "^0.1.1", + "@backstage/plugin-circleci": "^0.1.1", + "@backstage/plugin-cloudbuild": "^0.1.1", + "@backstage/plugin-cost-insights": "^0.1.1", + "@backstage/plugin-explore": "^0.1.1", + "@backstage/plugin-gcp-projects": "^0.1.1", + "@backstage/plugin-github-actions": "^0.1.1", + "@backstage/plugin-gitops-profiles": "^0.1.1", + "@backstage/plugin-graphiql": "^0.1.1", + "@backstage/plugin-jenkins": "^0.1.1", + "@backstage/plugin-kubernetes": "^0.1.1", + "@backstage/plugin-lighthouse": "^0.1.1", + "@backstage/plugin-newrelic": "^0.1.1", + "@backstage/plugin-register-component": "^0.1.1", + "@backstage/plugin-rollbar": "^0.1.1", + "@backstage/plugin-scaffolder": "^0.1.1", + "@backstage/plugin-sentry": "^0.1.1", + "@backstage/plugin-tech-radar": "^0.1.1", + "@backstage/plugin-techdocs": "^0.1.1", + "@backstage/plugin-user-settings": "^0.1.1", + "@backstage/plugin-welcome": "^0.1.1", + "@backstage/test-utils": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@octokit/rest": "^18.0.0", diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index dea2f16bae..990c8597ce 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.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -29,10 +29,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli-common": "^0.1.1-alpha.26", - "@backstage/config": "^0.1.1-alpha.26", - "@backstage/config-loader": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli-common": "^0.1.1", + "@backstage/config": "^0.1.1", + "@backstage/config-loader": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@types/cors": "^2.8.6", "@types/express": "^4.17.6", "compression": "^1.7.4", @@ -62,7 +62,7 @@ } }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", "@types/compression": "^1.7.0", "@types/http-errors": "^1.6.3", "@types/minimist": "^1.2.0", diff --git a/packages/backend/package.json b/packages/backend/package.json index 9f8e66bc61..76d8861717 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "dist/index.cjs.js", "types": "src/index.ts", "private": true, @@ -18,24 +18,24 @@ "migrate:create": "knex migrate:make -x ts" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.26", - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/config": "^0.1.1-alpha.26", - "@backstage/plugin-app-backend": "^0.1.1-alpha.26", - "@backstage/plugin-auth-backend": "^0.1.1-alpha.26", - "@backstage/plugin-catalog-backend": "^0.1.1-alpha.26", - "@backstage/plugin-graphql-backend": "^0.1.1-alpha.26", - "@backstage/plugin-kubernetes-backend": "^0.1.1-alpha.26", - "@backstage/plugin-proxy-backend": "^0.1.1-alpha.26", - "@backstage/plugin-rollbar-backend": "^0.1.1-alpha.26", - "@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.26", - "@backstage/plugin-sentry-backend": "^0.1.1-alpha.26", - "@backstage/plugin-techdocs-backend": "^0.1.1-alpha.26", + "@backstage/backend-common": "^0.1.1", + "@backstage/catalog-model": "^0.1.1", + "@backstage/config": "^0.1.1", + "@backstage/plugin-app-backend": "^0.1.1", + "@backstage/plugin-auth-backend": "^0.1.1", + "@backstage/plugin-catalog-backend": "^0.1.1", + "@backstage/plugin-graphql-backend": "^0.1.1", + "@backstage/plugin-kubernetes-backend": "^0.1.1", + "@backstage/plugin-proxy-backend": "^0.1.1", + "@backstage/plugin-rollbar-backend": "^0.1.1", + "@backstage/plugin-scaffolder-backend": "^0.1.1", + "@backstage/plugin-sentry-backend": "^0.1.1", + "@backstage/plugin-techdocs-backend": "^0.1.1", "@gitbeaker/node": "^23.5.0", "@octokit/rest": "^18.0.0", "azure-devops-node-api": "^10.1.1", "dockerode": "^3.2.0", - "example-app": "^0.1.1-alpha.26", + "example-app": "^0.1.1", "express": "^4.17.1", "express-promise-router": "^3.0.3", "knex": "^0.21.1", @@ -45,7 +45,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", "@types/dockerode": "^2.5.32", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5", diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index c97aea9572..0d555ef5d2 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-model", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.1-alpha.26", + "@backstage/config": "^0.1.1", "@types/json-schema": "^7.0.5", "@types/yup": "^0.29.8", "json-schema": "^0.2.5", @@ -29,7 +29,7 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", "@types/express": "^4.17.6", "@types/jest": "^26.0.7", "@types/lodash": "^4.14.151", diff --git a/packages/cli-common/package.json b/packages/cli-common/package.json index 56fb0d3417..2186f3d045 100644 --- a/packages/cli-common/package.json +++ b/packages/cli-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli-common", "description": "Common functionality used by cli, backend, and create-app", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "private": false, "main": "src/index.ts", "types": "src/index.ts", diff --git a/packages/cli/package.json b/packages/cli/package.json index 668ec5e000..4ad33c997b 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.1.1-alpha.26", + "version": "0.1.1", "private": false, "publishConfig": { "access": "public" @@ -28,9 +28,9 @@ "backstage-cli": "bin/backstage-cli" }, "dependencies": { - "@backstage/cli-common": "^0.1.1-alpha.26", - "@backstage/config": "^0.1.1-alpha.26", - "@backstage/config-loader": "^0.1.1-alpha.26", + "@backstage/cli-common": "^0.1.1", + "@backstage/config": "^0.1.1", + "@backstage/config-loader": "^0.1.1", "@hot-loader/react-dom": "^16.13.0", "@lerna/package-graph": "^3.18.5", "@lerna/project": "^3.18.0", @@ -108,12 +108,12 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-common": "^0.1.1-alpha.26", - "@backstage/config": "^0.1.1-alpha.26", - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/backend-common": "^0.1.1", + "@backstage/config": "^0.1.1", + "@backstage/core": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", + "@backstage/theme": "^0.1.1", "@types/diff": "^4.0.2", "@types/fs-extra": "^9.0.1", "@types/html-webpack-plugin": "^3.2.2", diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 2eef7db027..8c43832ab6 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.1.1-alpha.26", + "version": "0.1.1", "private": false, "publishConfig": { "access": "public", @@ -30,7 +30,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.1-alpha.26", + "@backstage/config": "^0.1.1", "fs-extra": "^9.0.0", "yaml": "^1.9.2", "yup": "^0.29.3" diff --git a/packages/config/package.json b/packages/config/package.json index d8ee50e97c..4bd4bedf5d 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config", "description": "Config API used by Backstage core, backend, and CLI", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "private": false, "publishConfig": { "access": "public", diff --git a/packages/core-api/package.json b/packages/core-api/package.json index 34949e4315..d183ce9433 100644 --- a/packages/core-api/package.json +++ b/packages/core-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-api", "description": "Internal Core API used by Backstage plugins and apps", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "private": false, "publishConfig": { "access": "public", @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/config": "^0.1.1", + "@backstage/test-utils": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@types/react": "^16.9", @@ -42,8 +42,8 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/test-utils-core": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/test-utils-core": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/packages/core/package.json b/packages/core/package.json index ec4411b390..535fb856cf 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core", "description": "Core API used by Backstage plugins and apps", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "private": false, "publishConfig": { "access": "public", @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.1-alpha.26", - "@backstage/core-api": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/config": "^0.1.1", + "@backstage/core-api": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -55,8 +55,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/packages/create-app/package.json b/packages/create-app/package.json index bb321dc504..4362fc7cff 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "Create app package for Backstage", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "private": false, "publishConfig": { "access": "public" @@ -27,7 +27,7 @@ "start": "nodemon --" }, "dependencies": { - "@backstage/cli-common": "^0.1.1-alpha.26", + "@backstage/cli-common": "^0.1.1", "chalk": "^4.0.0", "commander": "^6.1.0", "fs-extra": "^9.0.0", @@ -37,30 +37,30 @@ "recursive-readdir": "^2.2.2" }, "devDependencies": { - "@backstage/backend-common": "^0.1.1-alpha.26", - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/config": "^0.1.1-alpha.26", - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/plugin-api-docs": "^0.1.1-alpha.26", - "@backstage/plugin-auth-backend": "^0.1.1-alpha.26", - "@backstage/plugin-catalog": "^0.1.1-alpha.26", - "@backstage/plugin-catalog-backend": "^0.1.1-alpha.26", - "@backstage/plugin-circleci": "^0.1.1-alpha.26", - "@backstage/plugin-explore": "^0.1.1-alpha.26", - "@backstage/plugin-github-actions": "^0.1.1-alpha.26", - "@backstage/plugin-lighthouse": "^0.1.1-alpha.26", - "@backstage/plugin-proxy-backend": "^0.1.1-alpha.26", - "@backstage/plugin-register-component": "^0.1.1-alpha.26", - "@backstage/plugin-rollbar-backend": "^0.1.1-alpha.26", - "@backstage/plugin-scaffolder": "^0.1.1-alpha.26", - "@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.26", - "@backstage/plugin-tech-radar": "^0.1.1-alpha.26", - "@backstage/plugin-techdocs": "^0.1.1-alpha.26", - "@backstage/plugin-techdocs-backend": "^0.1.1-alpha.26", - "@backstage/plugin-user-settings": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/backend-common": "^0.1.1", + "@backstage/catalog-model": "^0.1.1", + "@backstage/cli": "^0.1.1", + "@backstage/config": "^0.1.1", + "@backstage/core": "^0.1.1", + "@backstage/plugin-api-docs": "^0.1.1", + "@backstage/plugin-auth-backend": "^0.1.1", + "@backstage/plugin-catalog": "^0.1.1", + "@backstage/plugin-catalog-backend": "^0.1.1", + "@backstage/plugin-circleci": "^0.1.1", + "@backstage/plugin-explore": "^0.1.1", + "@backstage/plugin-github-actions": "^0.1.1", + "@backstage/plugin-lighthouse": "^0.1.1", + "@backstage/plugin-proxy-backend": "^0.1.1", + "@backstage/plugin-register-component": "^0.1.1", + "@backstage/plugin-rollbar-backend": "^0.1.1", + "@backstage/plugin-scaffolder": "^0.1.1", + "@backstage/plugin-scaffolder-backend": "^0.1.1", + "@backstage/plugin-tech-radar": "^0.1.1", + "@backstage/plugin-techdocs": "^0.1.1", + "@backstage/plugin-techdocs-backend": "^0.1.1", + "@backstage/plugin-user-settings": "^0.1.1", + "@backstage/test-utils": "^0.1.1", + "@backstage/theme": "^0.1.1", "@types/fs-extra": "^9.0.1", "@types/inquirer": "^7.3.1", "@types/ora": "^3.2.0", diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index d61336465f..b8420d17b1 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/dev-utils", "description": "Utilities for developing Backstage plugins.", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "private": false, "publishConfig": { "access": "public", @@ -29,10 +29,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/core": "^0.1.1", + "@backstage/test-utils": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@testing-library/jest-dom": "^5.10.1", diff --git a/packages/docgen/package.json b/packages/docgen/package.json index 18f26d6941..db045baedb 100644 --- a/packages/docgen/package.json +++ b/packages/docgen/package.json @@ -1,7 +1,7 @@ { "name": "docgen", "description": "Tool for generating API Documentation for itself", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "private": true, "homepage": "https://backstage.io", "repository": { diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index 9098b3cd07..0420b426c6 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -1,7 +1,7 @@ { "name": "e2e-test", "description": "E2E test for verifying Backstage packages", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "private": true, "homepage": "https://backstage.io", "repository": { @@ -24,7 +24,7 @@ "e2e-test": "bin/e2e-test" }, "devDependencies": { - "@backstage/cli-common": "^0.1.1-alpha.26", + "@backstage/cli-common": "^0.1.1", "@types/fs-extra": "^9.0.1", "@types/node": "^13.7.2", "chalk": "^4.0.0", diff --git a/packages/storybook/package.json b/packages/storybook/package.json index 9eefd85128..4c9cc88dd0 100644 --- a/packages/storybook/package.json +++ b/packages/storybook/package.json @@ -1,6 +1,6 @@ { "name": "storybook", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "description": "Storybook build for core package", "private": true, "scripts": { @@ -14,7 +14,7 @@ ] }, "dependencies": { - "@backstage/theme": "^0.1.1-alpha.26" + "@backstage/theme": "^0.1.1" }, "devDependencies": { "@storybook/addon-actions": "^6.0.21", diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index c111a2973a..a81d4b9044 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,7 +1,7 @@ { "name": "@techdocs/cli", "description": "CLI for running TechDocs locally.", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "private": false, "publishConfig": { "access": "public" @@ -40,7 +40,7 @@ "ext": "ts" }, "dependencies": { - "@backstage/cli": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", "commander": "^6.1.0", "fs-extra": "^9.0.1", "http-proxy": "^1.18.1", diff --git a/packages/test-utils-core/package.json b/packages/test-utils-core/package.json index 1db8f6f023..1b69f48b08 100644 --- a/packages/test-utils-core/package.json +++ b/packages/test-utils-core/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/test-utils-core", "description": "Utilities to test Backstage core", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "private": false, "publishConfig": { "access": "public", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 74e0c8dcf7..cf05dd583c 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.1-alpha.26", + "version": "0.1.1", "private": false, "publishConfig": { "access": "public", @@ -29,10 +29,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/core-api": "^0.1.1-alpha.26", - "@backstage/test-utils-core": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/core-api": "^0.1.1", + "@backstage/test-utils-core": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/packages/theme/package.json b/packages/theme/package.json index ddec0e7981..47b0c497eb 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.1.1-alpha.26", + "version": "0.1.1", "private": false, "publishConfig": { "access": "public", @@ -31,7 +31,7 @@ "@material-ui/core": "^4.11.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26" + "@backstage/cli": "^0.1.1" }, "files": [ "dist" diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index baf574d5b5..23c072201e 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-api-docs", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,10 +20,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/plugin-catalog": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/catalog-model": "^0.1.1", + "@backstage/core": "^0.1.1", + "@backstage/plugin-catalog": "^0.1.1", + "@backstage/theme": "^0.1.1", "@kyma-project/asyncapi-react": "^0.13.1", "@material-icons/font": "^1.0.2", "@material-ui/core": "^4.11.0", @@ -39,9 +39,9 @@ "swagger-ui-react": "^3.31.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 8297b466af..4e91bf2161 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-backend", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.26", - "@backstage/config-loader": "^0.1.1-alpha.26", + "@backstage/backend-common": "^0.1.1", + "@backstage/config-loader": "^0.1.1", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^3.0.3", @@ -30,7 +30,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", "@types/supertest": "^2.0.8", "msw": "^0.20.5", "supertest": "^4.0.2" diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 6489c8547d..217aaa9e58 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "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.1.1-alpha.26", - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/config": "^0.1.1-alpha.26", + "@backstage/backend-common": "^0.1.1", + "@backstage/catalog-model": "^0.1.1", + "@backstage/config": "^0.1.1", "@types/express": "^4.17.6", "compression": "^1.7.4", "cookie-parser": "^1.4.5", @@ -51,7 +51,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", "@types/body-parser": "^1.19.0", "@types/cookie-parser": "^1.4.2", "@types/jwt-decode": "2.2.1", diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index cd8e363901..ef1401d4d3 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "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.1.1-alpha.26", - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/config": "^0.1.1-alpha.26", + "@backstage/backend-common": "^0.1.1", + "@backstage/catalog-model": "^0.1.1", + "@backstage/config": "^0.1.1", "@octokit/graphql": "^4.5.6", "@types/express": "^4.17.6", "codeowners-utils": "^1.0.2", @@ -45,8 +45,8 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@types/core-js": "^2.5.4", "@types/git-url-parse": "^9.0.0", "@types/ldapjs": "^1.0.9", diff --git a/plugins/catalog-graphql/package.json b/plugins/catalog-graphql/package.json index f74aaf7b57..a43f512856 100644 --- a/plugins/catalog-graphql/package.json +++ b/plugins/catalog-graphql/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graphql", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "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.1.1-alpha.26", - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/config": "^0.1.1-alpha.26", + "@backstage/backend-common": "^0.1.1", + "@backstage/catalog-model": "^0.1.1", + "@backstage/config": "^0.1.1", "@graphql-modules/core": "^0.7.17", "apollo-server": "^2.16.1", "cross-fetch": "^3.0.6", @@ -32,8 +32,8 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@graphql-codegen/cli": "^1.17.7", "@graphql-codegen/typescript": "^1.17.7", "@graphql-codegen/typescript-resolvers": "^1.17.7", diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 663ac7554b..da03d1a584 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,11 +21,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/plugin-scaffolder": "^0.1.1-alpha.26", - "@backstage/plugin-techdocs": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/catalog-model": "^0.1.1", + "@backstage/core": "^0.1.1", + "@backstage/plugin-scaffolder": "^0.1.1", + "@backstage/plugin-techdocs": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -41,9 +41,9 @@ "swr": "^0.3.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/react-hooks": "^3.3.0", diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index 7733d713ab..dc09713024 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-circleci", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "postpack": "backstage-cli postpack" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/plugin-catalog": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/catalog-model": "^0.1.1", + "@backstage/core": "^0.1.1", + "@backstage/plugin-catalog": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -38,9 +38,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index 29bfd1b261..f29f30f255 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-cloudbuild", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,10 +20,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/plugin-catalog": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/catalog-model": "^0.1.1", + "@backstage/core": "^0.1.1", + "@backstage/plugin-catalog": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -39,9 +39,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index abefb601d2..f978e13a8c 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-cost-insights", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.1-alpha.26", - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/config": "^0.1.1", + "@backstage/core": "^0.1.1", + "@backstage/test-utils": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -46,9 +46,9 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/explore/package.json b/plugins/explore/package.json index 4db68909b9..fa682e8dde 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-explore", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/core": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -33,9 +33,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 4c6375ebf6..86b525f1c9 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gcp-projects", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/core": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -31,9 +31,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 75fed63465..ea5d8c4278 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-github-actions", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,11 +21,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/core-api": "^0.1.1-alpha.26", - "@backstage/plugin-catalog": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/catalog-model": "^0.1.1", + "@backstage/core": "^0.1.1", + "@backstage/core-api": "^0.1.1", + "@backstage/plugin-catalog": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -40,9 +40,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index 18486bc2b7..714c63da20 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gitops-profiles", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/core": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -32,9 +32,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index 9e1ffd5ef1..ea61845e18 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphiql", "description": "Backstage plugin for browsing GraphQL APIs", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "private": false, "publishConfig": { "access": "public", @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/core": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -43,9 +43,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/graphql/package.json b/plugins/graphql/package.json index 58b6c763c6..16c3572251 100644 --- a/plugins/graphql/package.json +++ b/plugins/graphql/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-graphql-backend", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -19,9 +19,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.26", - "@backstage/config": "^0.1.1-alpha.26", - "@backstage/plugin-catalog-graphql": "^0.1.1-alpha.26", + "@backstage/backend-common": "^0.1.1", + "@backstage/config": "^0.1.1", + "@backstage/plugin-catalog-graphql": "^0.1.1", "@graphql-modules/core": "^0.7.17", "@types/express": "^4.17.6", "apollo-server": "^2.16.1", @@ -35,7 +35,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", "@types/supertest": "^2.0.8", "eslint-plugin-graphql": "^4.0.0", "msw": "^0.20.5", diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index 194ae71429..cbf9791809 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-jenkins", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/plugin-catalog": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/catalog-model": "^0.1.1", + "@backstage/core": "^0.1.1", + "@backstage/plugin-catalog": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -36,9 +36,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 26cc842db9..c66c364871 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-backend", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.26", - "@backstage/config": "^0.1.1-alpha.26", + "@backstage/backend-common": "^0.1.1", + "@backstage/config": "^0.1.1", "@kubernetes/client-node": "^0.12.1", "@types/express": "^4.17.6", "compression": "^1.7.4", @@ -37,7 +37,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", "supertest": "^4.0.2" }, "files": [ diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 6544d751e1..850dd4cada 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,11 +20,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/config": "^0.1.1-alpha.26", - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/plugin-kubernetes-backend": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/catalog-model": "^0.1.1", + "@backstage/config": "^0.1.1", + "@backstage/core": "^0.1.1", + "@backstage/plugin-kubernetes-backend": "^0.1.1", + "@backstage/theme": "^0.1.1", "@kubernetes/client-node": "^0.12.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -35,9 +35,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index 8285d29635..587d826d9d 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-lighthouse", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,12 +21,12 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/config": "^0.1.1-alpha.26", - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/core-api": "^0.1.1-alpha.26", - "@backstage/plugin-catalog": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/catalog-model": "^0.1.1", + "@backstage/config": "^0.1.1", + "@backstage/core": "^0.1.1", + "@backstage/core-api": "^0.1.1", + "@backstage/plugin-catalog": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -39,9 +39,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index b9a12cc648..0b52220239 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-newrelic", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/core": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -31,9 +31,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index fcb68dcd78..57594cfe3f 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-proxy-backend", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -19,8 +19,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.26", - "@backstage/config": "^0.1.1-alpha.26", + "@backstage/backend-common": "^0.1.1", + "@backstage/config": "^0.1.1", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^3.0.3", @@ -33,7 +33,7 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", "@types/http-proxy-middleware": "^0.19.3", "@types/supertest": "^2.0.8", "@types/uuid": "^8.0.0", diff --git a/plugins/register-component/package.json b/plugins/register-component/package.json index 5c3f985b4d..58d3b122ca 100644 --- a/plugins/register-component/package.json +++ b/plugins/register-component/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-register-component", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/plugin-catalog": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/catalog-model": "^0.1.1", + "@backstage/core": "^0.1.1", + "@backstage/plugin-catalog": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -36,9 +36,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/rollbar-backend/package.json b/plugins/rollbar-backend/package.json index cb553d859b..91e0000d6c 100644 --- a/plugins/rollbar-backend/package.json +++ b/plugins/rollbar-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-rollbar-backend", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.26", - "@backstage/config": "^0.1.1-alpha.26", + "@backstage/backend-common": "^0.1.1", + "@backstage/config": "^0.1.1", "@types/express": "^4.17.6", "axios": "^0.20.0", "camelcase-keys": "^6.2.2", @@ -37,7 +37,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", "@types/supertest": "^2.0.8", "supertest": "^4.0.2" }, diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 27421cfd4d..a3b11ace55 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-rollbar", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/plugin-catalog": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/catalog-model": "^0.1.1", + "@backstage/core": "^0.1.1", + "@backstage/plugin-catalog": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -37,9 +37,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/react-hooks": "^3.3.0", diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 1c85adc23a..676ddc9b8a 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "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.1.1-alpha.26", - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/config": "^0.1.1-alpha.26", + "@backstage/backend-common": "^0.1.1", + "@backstage/catalog-model": "^0.1.1", + "@backstage/config": "^0.1.1", "@gitbeaker/core": "^23.5.0", "@gitbeaker/node": "^23.5.0", "@octokit/rest": "^18.0.0", @@ -47,7 +47,7 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", "@octokit/types": "^5.4.1", "@types/fs-extra": "^9.0.1", "@types/git-url-parse": "^9.0.0", diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 751f4075b2..4f7dbdae20 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/plugin-catalog": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/catalog-model": "^0.1.1", + "@backstage/core": "^0.1.1", + "@backstage/plugin-catalog": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -41,9 +41,9 @@ "swr": "^0.3.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/sentry-backend/package.json b/plugins/sentry-backend/package.json index b51eb1509a..cf9bf7886f 100644 --- a/plugins/sentry-backend/package.json +++ b/plugins/sentry-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sentry-backend", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.26", + "@backstage/backend-common": "^0.1.1", "@types/express": "^4.17.6", "axios": "^0.20.0", "compression": "^1.7.4", @@ -34,7 +34,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26" + "@backstage/cli": "^0.1.1" }, "files": [ "dist" diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 4d9cd59bc3..69c6f130eb 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sentry", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/catalog-model": "^0.1.1", + "@backstage/core": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -36,9 +36,9 @@ "timeago.js": "^4.0.2" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index f27f4c1784..c7163cc918 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-radar", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/core": "^0.1.1", + "@backstage/test-utils": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -35,9 +35,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 5bdc7ba282..b65540be97 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "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.1.1-alpha.26", - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/config": "^0.1.1-alpha.26", + "@backstage/backend-common": "^0.1.1", + "@backstage/catalog-model": "^0.1.1", + "@backstage/config": "^0.1.1", "@types/dockerode": "^2.5.34", "@types/express": "^4.17.6", "command-exists-promise": "^2.0.2", @@ -37,7 +37,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", "supertest": "^4.0.2" }, "files": [ diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 9d14f0a919..551932369c 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,12 +22,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/core-api": "^0.1.1-alpha.26", - "@backstage/plugin-catalog": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/catalog-model": "^0.1.1", + "@backstage/core": "^0.1.1", + "@backstage/core-api": "^0.1.1", + "@backstage/plugin-catalog": "^0.1.1", + "@backstage/test-utils": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -40,9 +40,9 @@ "sanitize-html": "^1.27.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 6f96d7b2fd..3e67c8cae6 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/core": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -32,9 +32,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index 380a4b0a46..846388eaec 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-welcome", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -21,8 +21,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/core": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -32,9 +32,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", From a3ae6ddfdbeab25c9d5437dd47a3656e98f5a010 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 28 Oct 2020 22:50:37 +0100 Subject: [PATCH 011/252] docgen: add support for picking up the type from the declaration instead of initialization --- packages/docgen/src/docgen/TypeLocator.ts | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/docgen/src/docgen/TypeLocator.ts b/packages/docgen/src/docgen/TypeLocator.ts index 6030258112..a5a0b1fc5b 100644 --- a/packages/docgen/src/docgen/TypeLocator.ts +++ b/packages/docgen/src/docgen/TypeLocator.ts @@ -76,13 +76,7 @@ export default class TypeLocator { return; } - docMap.get(decl.constructorType)!.push({ - node, - name: decl.name, - source, - args: Array.from(decl.initializer.arguments || []), - typeArgs: Array.from(decl.initializer.typeArguments || []), - }); + docMap.get(decl.constructorType)!.push({ node, source, ...decl }); }); }); @@ -103,7 +97,8 @@ export default class TypeLocator { ): | { constructorType: ts.Type; - initializer: ts.CallExpression | ts.NewExpression; + args: ts.Expression[]; + typeArgs: ts.TypeNode[]; name: string; } | undefined { @@ -137,6 +132,14 @@ export default class TypeLocator { const constructorType = this.checker.getTypeAtLocation( initializer.expression, ); - return { constructorType, initializer, name: name.text }; + const args = Array.from(initializer.arguments ?? []); + const typeNode = declaration.type; + const typeArgs = Array.from( + (typeNode && ts.isTypeReferenceNode(typeNode) + ? typeNode.typeArguments + : initializer.typeArguments) ?? [], + ); + + return { constructorType, args, typeArgs, name: name.text }; } } From 55af83a78a9ce8c0591728c2fec13dbdd2d39e9a Mon Sep 17 00:00:00 2001 From: Marvin9 Date: Thu, 29 Oct 2020 14:33:09 +0530 Subject: [PATCH 012/252] chore: move entityClient to createPlugin, mock entityClient in tests --- packages/backend/src/plugins/scaffolder.ts | 6 ++ plugins/scaffolder-backend/src/index.ts | 1 + .../src/service/router.test.ts | 91 ++++++++++--------- .../scaffolder-backend/src/service/router.ts | 5 +- 4 files changed, 59 insertions(+), 44 deletions(-) diff --git a/packages/backend/src/plugins/scaffolder.ts b/packages/backend/src/plugins/scaffolder.ts index 6eacd143c1..5d36d508a5 100644 --- a/packages/backend/src/plugins/scaffolder.ts +++ b/packages/backend/src/plugins/scaffolder.ts @@ -21,7 +21,9 @@ import { Publishers, CreateReactAppTemplater, Templaters, + CatalogEntityClient, } from '@backstage/plugin-scaffolder-backend'; +import { SingleHostDiscovery } from '@backstage/backend-common'; import type { PluginEnvironment } from '../types'; import Docker from 'dockerode'; @@ -40,6 +42,9 @@ export default async function createPlugin({ const dockerClient = new Docker(); + const discovery = SingleHostDiscovery.fromConfig(config); + const entityClient = new CatalogEntityClient({ discovery }); + return await createRouter({ preparers, templaters, @@ -47,5 +52,6 @@ export default async function createPlugin({ logger, config, dockerClient, + entityClient, }); } diff --git a/plugins/scaffolder-backend/src/index.ts b/plugins/scaffolder-backend/src/index.ts index c461bfede6..28e6a2b24c 100644 --- a/plugins/scaffolder-backend/src/index.ts +++ b/plugins/scaffolder-backend/src/index.ts @@ -16,3 +16,4 @@ export * from './scaffolder'; export * from './service/router'; +export * from './lib/catalog'; diff --git a/plugins/scaffolder-backend/src/service/router.test.ts b/plugins/scaffolder-backend/src/service/router.test.ts index fae7ee8beb..c2b60a2040 100644 --- a/plugins/scaffolder-backend/src/service/router.test.ts +++ b/plugins/scaffolder-backend/src/service/router.test.ts @@ -35,6 +35,10 @@ import Docker from 'dockerode'; jest.mock('dockerode'); +const generateEntityClient: any = (template: any) => ({ + findTemplate: () => Promise.resolve(template), +}); + describe('createRouter - working directory', () => { const mockPrepare = jest.fn(); const mockPreparers = new Preparers(); @@ -74,6 +78,8 @@ describe('createRouter - working directory', () => { }, }; + const mockedEntityClient = generateEntityClient(template); + it('should throw an error when working directory does not exist or is not writable', async () => { mockAccess.mockImplementation(() => { throw new Error('access error'); @@ -87,6 +93,7 @@ describe('createRouter - working directory', () => { publishers: new Publishers(), config: ConfigReader.fromConfigs([workDirConfig('/path')]), dockerClient: new Docker(), + entityClient: mockedEntityClient, }), ).rejects.toThrow('access error'); }); @@ -99,11 +106,12 @@ describe('createRouter - working directory', () => { publishers: new Publishers(), config: ConfigReader.fromConfigs([workDirConfig('/path')]), dockerClient: new Docker(), + entityClient: mockedEntityClient, }); const app = express().use(router); await request(app).post('/v1/jobs').send({ - template, + templateName: '', values: {}, }); @@ -121,11 +129,12 @@ describe('createRouter - working directory', () => { publishers: new Publishers(), config: ConfigReader.fromConfigs([]), dockerClient: new Docker(), + entityClient: mockedEntityClient, }); const app = express().use(router); await request(app).post('/v1/jobs').send({ - template, + templateName: '', values: {}, }); @@ -137,6 +146,43 @@ describe('createRouter - working directory', () => { describe('createRouter', () => { let app: express.Express; + const template = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Template', + metadata: { + description: 'Create a new CRA website project', + name: 'create-react-app-template', + tags: ['experimental', 'react', 'cra'], + title: 'Create React App Template', + }, + spec: { + owner: 'web@example.com', + path: '.', + schema: { + properties: { + component_id: { + description: 'Unique name of the component', + title: 'Name', + type: 'string', + }, + description: { + description: 'Description of the component', + title: 'Description', + type: 'string', + }, + use_typescript: { + default: true, + description: 'Include typescript', + title: 'Use Typescript', + type: 'boolean', + }, + }, + required: ['component_id', 'use_typescript'], + }, + templater: 'cra', + type: 'website', + }, + }; beforeAll(async () => { const router = await createRouter({ @@ -146,6 +192,7 @@ describe('createRouter', () => { publishers: new Publishers(), config: ConfigReader.fromConfigs([]), dockerClient: new Docker(), + entityClient: generateEntityClient(template), }); app = express().use(router); }); @@ -155,47 +202,9 @@ describe('createRouter', () => { }); describe('POST /v1/jobs', () => { - const template = { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Template', - metadata: { - description: 'Create a new CRA website project', - name: 'create-react-app-template', - tags: ['experimental', 'react', 'cra'], - title: 'Create React App Template', - }, - spec: { - owner: 'web@example.com', - path: '.', - schema: { - properties: { - component_id: { - description: 'Unique name of the component', - title: 'Name', - type: 'string', - }, - description: { - description: 'Description of the component', - title: 'Description', - type: 'string', - }, - use_typescript: { - default: true, - description: 'Include typescript', - title: 'Use Typescript', - type: 'boolean', - }, - }, - required: ['component_id', 'use_typescript'], - }, - templater: 'cra', - type: 'website', - }, - }; - it('rejects template values which do not match the template schema definition', async () => { const response = await request(app).post('/v1/jobs').send({ - template, + templateName: '', values: {}, }); diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index efe8ee46f6..e4d3d5c45a 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { SingleHostDiscovery } from '@backstage/backend-common'; import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; import { Config, JsonValue } from '@backstage/config'; import fs from 'fs-extra'; @@ -41,6 +40,7 @@ export interface RouterOptions { logger: Logger; config: Config; dockerClient: Docker; + entityClient: CatalogEntityClient; } export async function createRouter( @@ -56,13 +56,12 @@ export async function createRouter( logger: parentLogger, config, dockerClient, + entityClient, } = options; const logger = parentLogger.child({ plugin: 'scaffolder' }); const jobProcessor = new JobProcessor(); - const discovery = SingleHostDiscovery.fromConfig(config); - const entityClient = new CatalogEntityClient({ discovery }); let workingDirectory: string; if (config.has('backend.workingDirectory')) { workingDirectory = config.getString('backend.workingDirectory'); From 1ddcf191dbf5f0280383e3bb20f251016ed14889 Mon Sep 17 00:00:00 2001 From: Marvin9 Date: Thu, 29 Oct 2020 14:46:55 +0530 Subject: [PATCH 013/252] fix: use entityClient in create-app package --- .../default-app/packages/backend/src/plugins/scaffolder.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/create-app/templates/default-app/packages/backend/src/plugins/scaffolder.ts b/packages/create-app/templates/default-app/packages/backend/src/plugins/scaffolder.ts index 8de15920e9..2dc69feb45 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/plugins/scaffolder.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/plugins/scaffolder.ts @@ -11,7 +11,9 @@ import { CreateReactAppTemplater, Templaters, RepoVisibilityOptions, + CatalogEntityClient, } from '@backstage/plugin-scaffolder-backend'; +import { SingleHostDiscovery } from '@backstage/backend-common'; import { Octokit } from '@octokit/rest'; import { Gitlab } from '@gitbeaker/node'; import type { PluginEnvironment } from '../types'; @@ -97,6 +99,10 @@ export default async function createPlugin({ } const dockerClient = new Docker(); + + const discovery = SingleHostDiscovery.fromConfig(config); + const entityClient = new CatalogEntityClient({ discovery }); + return await createRouter({ preparers, templaters, @@ -104,5 +110,6 @@ export default async function createPlugin({ logger, config, dockerClient, + entityClient, }); } From 364939e5e3dee6511e62185a6d492a2f904e8f4a Mon Sep 17 00:00:00 2001 From: Marvin9 Date: Thu, 29 Oct 2020 14:54:30 +0530 Subject: [PATCH 014/252] chore: remove try/catch --- plugins/scaffolder-backend/src/service/router.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index e4d3d5c45a..2dc6a794fb 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; import { Config, JsonValue } from '@backstage/config'; import fs from 'fs-extra'; import Docker from 'dockerode'; @@ -111,12 +110,7 @@ export async function createRouter( const values: RequiredTemplateValues & Record = req.body.values; - let template: TemplateEntityV1alpha1; - try { - template = await entityClient.findTemplate(templateName); - } catch (err) { - throw err; - } + const template = await entityClient.findTemplate(templateName); const validationResult: ValidatorResult = validate( values, From 21e071fa6c26ebec4dcca013d55351d56e024d59 Mon Sep 17 00:00:00 2001 From: Forrest Waters Date: Wed, 28 Oct 2020 18:23:13 -0500 Subject: [PATCH 015/252] working onelogin auth implementation --- app-config.yaml | 8 + packages/app/src/identityProviders.ts | 7 + .../core-api/src/apis/definitions/auth.ts | 11 ++ .../src/apis/implementations/auth/index.ts | 1 + .../auth/onelogin/OktaAuth.test.ts | 61 +++++++ .../auth/onelogin/OneLoginAuth.ts | 82 +++++++++ .../implementations/auth/onelogin/index.ts | 17 ++ packages/core/src/api-wrappers/defaultApis.ts | 11 ++ .../lib/passport/PassportStrategyHelper.ts | 1 - .../auth-backend/src/providers/factories.ts | 2 + .../src/providers/onelogin/index.ts | 1 + .../src/providers/onelogin/provider.ts | 160 ++++++++++++++++++ .../src/providers/onelogin/types.d.ts | 20 +++ 13 files changed, 381 insertions(+), 1 deletion(-) create mode 100644 packages/core-api/src/apis/implementations/auth/onelogin/OktaAuth.test.ts create mode 100644 packages/core-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts create mode 100644 packages/core-api/src/apis/implementations/auth/onelogin/index.ts create mode 100644 plugins/auth-backend/src/providers/onelogin/index.ts create mode 100644 plugins/auth-backend/src/providers/onelogin/provider.ts create mode 100644 plugins/auth-backend/src/providers/onelogin/types.d.ts diff --git a/app-config.yaml b/app-config.yaml index 4746e822da..a495df0263 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -232,6 +232,14 @@ auth: $env: AUTH_MICROSOFT_CLIENT_SECRET tenantId: $env: AUTH_MICROSOFT_TENANT_ID + onelogin: + development: + clientId: + $env: AUTH_ONELOGIN_CLIENT_ID + clientSecret: + $env: AUTH_ONELOGIN_CLIENT_SECRET + issuer: + $env: AUTH_ONELOGIN_ISSUER costInsights: engineerCost: 200000 products: diff --git a/packages/app/src/identityProviders.ts b/packages/app/src/identityProviders.ts index 388bc8d8b6..06ff6b07d3 100644 --- a/packages/app/src/identityProviders.ts +++ b/packages/app/src/identityProviders.ts @@ -21,6 +21,7 @@ import { githubAuthApiRef, samlAuthApiRef, microsoftAuthApiRef, + oneloginAuthApiRef, } from '@backstage/core'; export const providers = [ @@ -60,4 +61,10 @@ export const providers = [ message: 'Sign In using SAML', apiRef: samlAuthApiRef, }, + { + id: 'onelogin-auth-provider', + title: 'OneLogin', + message: 'Sign In using OneLogin', + apiRef: oneloginAuthApiRef, + }, ]; diff --git a/packages/core-api/src/apis/definitions/auth.ts b/packages/core-api/src/apis/definitions/auth.ts index 946baeabd9..c538065f3a 100644 --- a/packages/core-api/src/apis/definitions/auth.ts +++ b/packages/core-api/src/apis/definitions/auth.ts @@ -320,3 +320,14 @@ export const samlAuthApiRef: ApiRef< id: 'core.auth.saml', description: 'Example of how to use SAML custom provider', }); + +export const oneloginAuthApiRef: ApiRef< + OAuthApi & + OpenIdConnectApi & + ProfileInfoApi & + BackstageIdentityApi & + SessionApi +> = createApiRef({ + id: 'core.auth.onelogin', + description: 'Provides authentication towards OneLogin APIs and identities', +}); diff --git a/packages/core-api/src/apis/implementations/auth/index.ts b/packages/core-api/src/apis/implementations/auth/index.ts index 39223e8e15..7ef78d19cf 100644 --- a/packages/core-api/src/apis/implementations/auth/index.ts +++ b/packages/core-api/src/apis/implementations/auth/index.ts @@ -22,3 +22,4 @@ export * from './okta'; export * from './saml'; export * from './auth0'; export * from './microsoft'; +export * from './onelogin'; diff --git a/packages/core-api/src/apis/implementations/auth/onelogin/OktaAuth.test.ts b/packages/core-api/src/apis/implementations/auth/onelogin/OktaAuth.test.ts new file mode 100644 index 0000000000..d6b1a07d9d --- /dev/null +++ b/packages/core-api/src/apis/implementations/auth/onelogin/OktaAuth.test.ts @@ -0,0 +1,61 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import OktaAuth from './OktaAuth'; +import MockOAuthApi from '../../OAuthRequestApi/MockOAuthApi'; +import { UrlPatternDiscovery } from '../../DiscoveryApi'; + +const PREFIX = 'okta.'; + +const getSession = jest.fn(); + +jest.mock('../../../../lib/AuthSessionManager', () => ({ + ...(jest.requireActual('../../../../lib/AuthSessionManager') as any), + RefreshingAuthSessionManager: class { + getSession = getSession; + }, +})); + +describe('OktaAuth', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + it.each([ + ['openid', ['openid']], + ['profile email', ['profile', 'email']], + [`${PREFIX}groups.manage`, [`${PREFIX}groups.manage`]], + ['groups.read', [`${PREFIX}groups.read`]], + [ + `${PREFIX}groups.manage groups.read, openid`, + [`${PREFIX}groups.manage`, `${PREFIX}groups.read`, 'openid'], + ], + [`email\t ${PREFIX}groups.read`, ['email', `${PREFIX}groups.read`]], + + // Some incorrect scopes that we don't try to fix + [`${PREFIX}email`, [`${PREFIX}email`]], + [`${PREFIX}profile`, [`${PREFIX}profile`]], + [`${PREFIX}openid`, [`${PREFIX}openid`]], + ])(`should normalize scopes correctly - %p`, (scope, scopes) => { + const auth = OktaAuth.create({ + oauthRequestApi: new MockOAuthApi(), + discoveryApi: UrlPatternDiscovery.compile('http://example.com'), + }); + + auth.getAccessToken(scope); + expect(getSession).toHaveBeenCalledWith({ scopes: new Set(scopes) }); + }); +}); diff --git a/packages/core-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts b/packages/core-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts new file mode 100644 index 0000000000..7420b21510 --- /dev/null +++ b/packages/core-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts @@ -0,0 +1,82 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import OneLoginIcon from '@material-ui/icons/AcUnit'; +import { oneloginAuthApiRef } from '@backstage/core-api/src/apis/definitions/auth'; +import { + OAuthRequestApi, + AuthProvider, + DiscoveryApi, +} from '@backstage/core-api/src/apis/definitions'; +import { OAuth2 } from '@backstage/core-api/src/apis/implementations/auth/oauth2'; + +type CreateOptions = { + discoveryApi: DiscoveryApi; + oauthRequestApi: OAuthRequestApi; + + environment?: string; + provider?: AuthProvider & { id: string }; +}; + +const DEFAULT_PROVIDER = { + id: 'onelogin', + title: 'onelogin', + icon: OneLoginIcon, +}; + +const OIDC_SCOPES: Set = new Set([ + 'openid', + 'profile', + 'email', + 'phone', + 'address', + 'groups', + 'offline_access', +]); + +const SCOPE_PREFIX: string = 'onelogin.'; + +class OneLoginAuth { + static create({ + discoveryApi, + environment = 'development', + provider = DEFAULT_PROVIDER, + oauthRequestApi, + }: CreateOptions): typeof oneloginAuthApiRef.T { + return OAuth2.create({ + discoveryApi, + oauthRequestApi, + provider, + environment, + defaultScopes: ['openid', 'email', 'profile', 'offline_access'], + scopeTransform(scopes) { + return scopes.map(scope => { + if (OIDC_SCOPES.has(scope)) { + return scope; + } + + if (scope.startsWith(SCOPE_PREFIX)) { + return scope; + } + + return `${SCOPE_PREFIX}${scope}`; + }); + }, + }); + } +} + +export default OneLoginAuth; diff --git a/packages/core-api/src/apis/implementations/auth/onelogin/index.ts b/packages/core-api/src/apis/implementations/auth/onelogin/index.ts new file mode 100644 index 0000000000..1d163207db --- /dev/null +++ b/packages/core-api/src/apis/implementations/auth/onelogin/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { default as OneLoginAuth } from './OneLoginAuth'; diff --git a/packages/core/src/api-wrappers/defaultApis.ts b/packages/core/src/api-wrappers/defaultApis.ts index d0a777dc94..61d4f3a7e1 100644 --- a/packages/core/src/api-wrappers/defaultApis.ts +++ b/packages/core/src/api-wrappers/defaultApis.ts @@ -44,6 +44,8 @@ import { UrlPatternDiscovery, samlAuthApiRef, SamlAuth, + oneloginAuthApiRef, + OneLoginAuth, } from '@backstage/core-api'; export const defaultApis = [ @@ -142,4 +144,13 @@ export const defaultApis = [ }, factory: ({ discoveryApi }) => SamlAuth.create({ discoveryApi }), }), + createApiFactory({ + api: oneloginAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi }) => + OneLoginAuth.create({ discoveryApi, oauthRequestApi }), + }), ]; diff --git a/plugins/auth-backend/src/lib/passport/PassportStrategyHelper.ts b/plugins/auth-backend/src/lib/passport/PassportStrategyHelper.ts index 3264fd8faa..28f15ee9e8 100644 --- a/plugins/auth-backend/src/lib/passport/PassportStrategyHelper.ts +++ b/plugins/auth-backend/src/lib/passport/PassportStrategyHelper.ts @@ -116,7 +116,6 @@ export const executeFrameHandlerStrategy = async ( strategy.redirect = () => { reject(new Error('Unexpected redirect')); }; - strategy.authenticate(req, {}); }, ); diff --git a/plugins/auth-backend/src/providers/factories.ts b/plugins/auth-backend/src/providers/factories.ts index 37761c2305..a764d0b50e 100644 --- a/plugins/auth-backend/src/providers/factories.ts +++ b/plugins/auth-backend/src/providers/factories.ts @@ -22,6 +22,7 @@ import { createOktaProvider } from './okta'; import { createSamlProvider } from './saml'; import { createAuth0Provider } from './auth0'; import { createMicrosoftProvider } from './microsoft'; +import { createOneLoginProvider } from './onelogin'; import { AuthProviderFactory, AuthProviderFactoryOptions } from './types'; const factories: { [providerId: string]: AuthProviderFactory } = { @@ -33,6 +34,7 @@ const factories: { [providerId: string]: AuthProviderFactory } = { auth0: createAuth0Provider, microsoft: createMicrosoftProvider, oauth2: createOAuth2Provider, + onelogin: createOneLoginProvider, }; export function createAuthProvider( diff --git a/plugins/auth-backend/src/providers/onelogin/index.ts b/plugins/auth-backend/src/providers/onelogin/index.ts new file mode 100644 index 0000000000..4c110c56ac --- /dev/null +++ b/plugins/auth-backend/src/providers/onelogin/index.ts @@ -0,0 +1 @@ +export { createOneLoginProvider } from './provider'; \ No newline at end of file diff --git a/plugins/auth-backend/src/providers/onelogin/provider.ts b/plugins/auth-backend/src/providers/onelogin/provider.ts new file mode 100644 index 0000000000..a9b43fc8d4 --- /dev/null +++ b/plugins/auth-backend/src/providers/onelogin/provider.ts @@ -0,0 +1,160 @@ +//import { Strategy as OneLoginStrategy } from 'passport-openidconnect'; +import { Strategy as OneLoginStrategy } from 'passport-onelogin'; +import express from 'express'; +import { + OAuthAdapter, + OAuthProviderOptions, + OAuthHandlers, + OAuthResponse, + OAuthEnvironmentHandler, + OAuthStartRequest, + encodeState, + OAuthRefreshRequest, +} from '../../lib/oauth'; +import passport from 'passport'; +import { + executeFrameHandlerStrategy, + executeRedirectStrategy, + executeRefreshTokenStrategy, + makeProfileInfo, + executeFetchUserProfileStrategy, + PassportDoneCallback, +} from '../../lib/passport'; +import { RedirectInfo, AuthProviderFactory } from '../types'; + +type PrivateInfo = { + refreshToken: string; +}; + +export type OneLoginProviderOptions = OAuthProviderOptions & { + issuer: string +} + +export class OneLoginProvider implements OAuthHandlers { + private readonly _strategy: any; + + constructor(options: OneLoginProviderOptions) { + this._strategy = new OneLoginStrategy( + { + issuer: options.issuer, + clientID: options.clientId, + clientSecret: options.clientSecret, + callbackURL: options.callbackUrl, + passReqToCallback: false as true, + }, + ( + accessToken: any, + refreshToken: any, + params: any, + rawProfile: passport.Profile, + done: PassportDoneCallback, + ) => { + const profile = makeProfileInfo(rawProfile, params.id_token); + + done( + undefined, + { + providerInfo: { + idToken: params.id_token, + accessToken, + scope: params.scope, + //scope: 'profile', + expiresInSeconds: params.expires_in, + }, + profile, + }, + { + refreshToken, + }, + ); + }, + ); + } + async start(req: OAuthStartRequest): Promise { + return await executeRedirectStrategy(req, this._strategy, { + accessType: 'offline', + prompt: 'consent', + scope: 'openid', + state: encodeState(req.state), + }); + } + + async handler( + req: express.Request, + ): Promise<{ response: OAuthResponse; refreshToken: string }> { + const { response, privateInfo } = await executeFrameHandlerStrategy< + OAuthResponse, + PrivateInfo + >(req, this._strategy); + + return { + response: await this.populateIdentity(response), + refreshToken: privateInfo.refreshToken, + }; + } + + async refresh(req: OAuthRefreshRequest): Promise { + const { accessToken, params } = await executeRefreshTokenStrategy( + this._strategy, + req.refreshToken, + req.scope, + ); + + const profile = await executeFetchUserProfileStrategy( + this._strategy, + accessToken, + params.id_token, + ); + + return this.populateIdentity({ + providerInfo: { + accessToken, + idToken: params.id_token, + expiresInSeconds: params.expires_in, + scope: params.scope, + }, + profile, + }); + } + + private async populateIdentity( + response: OAuthResponse, + ): Promise { + const { profile } = response; + + if (!profile.email) { + throw new Error('OIDC profile contained no email'); + } + + const id = profile.email.split('@')[0]; + + return { ...response, backstageIdentity: { id } }; + } +} + + +export const createOneLoginProvider: AuthProviderFactory = ({ + globalConfig, + config, + tokenIssuer, +}) => + OAuthEnvironmentHandler.mapConfig(config, envConfig => { + const providerId = 'onelogin'; + const clientId = envConfig.getString('clientId'); + const clientSecret = envConfig.getString('clientSecret'); + const issuer = envConfig.getString('issuer'); + const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`; + + const provider = new OneLoginProvider({ + clientId, + clientSecret, + callbackUrl, + issuer, + }); + + return OAuthAdapter.fromConfig(globalConfig, provider, { + disableRefresh: false, + providerId, + tokenIssuer, + }); + }); diff --git a/plugins/auth-backend/src/providers/onelogin/types.d.ts b/plugins/auth-backend/src/providers/onelogin/types.d.ts new file mode 100644 index 0000000000..bff5a7e33c --- /dev/null +++ b/plugins/auth-backend/src/providers/onelogin/types.d.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +declare module 'passport-openidconnect' { + export class Strategy { + constructor(options: any, verify: any); + } +} From 159f5ee77c5042eb84acce2d5563eef9ca214df0 Mon Sep 17 00:00:00 2001 From: Forrest Waters Date: Wed, 28 Oct 2020 19:18:13 -0500 Subject: [PATCH 016/252] add passport-onelogin dependency --- .../auth/onelogin/OktaAuth.test.ts | 61 ------------------- plugins/auth-backend/package.json | 1 + yarn.lock | 9 +++ 3 files changed, 10 insertions(+), 61 deletions(-) delete mode 100644 packages/core-api/src/apis/implementations/auth/onelogin/OktaAuth.test.ts diff --git a/packages/core-api/src/apis/implementations/auth/onelogin/OktaAuth.test.ts b/packages/core-api/src/apis/implementations/auth/onelogin/OktaAuth.test.ts deleted file mode 100644 index d6b1a07d9d..0000000000 --- a/packages/core-api/src/apis/implementations/auth/onelogin/OktaAuth.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import OktaAuth from './OktaAuth'; -import MockOAuthApi from '../../OAuthRequestApi/MockOAuthApi'; -import { UrlPatternDiscovery } from '../../DiscoveryApi'; - -const PREFIX = 'okta.'; - -const getSession = jest.fn(); - -jest.mock('../../../../lib/AuthSessionManager', () => ({ - ...(jest.requireActual('../../../../lib/AuthSessionManager') as any), - RefreshingAuthSessionManager: class { - getSession = getSession; - }, -})); - -describe('OktaAuth', () => { - afterEach(() => { - jest.resetAllMocks(); - }); - - it.each([ - ['openid', ['openid']], - ['profile email', ['profile', 'email']], - [`${PREFIX}groups.manage`, [`${PREFIX}groups.manage`]], - ['groups.read', [`${PREFIX}groups.read`]], - [ - `${PREFIX}groups.manage groups.read, openid`, - [`${PREFIX}groups.manage`, `${PREFIX}groups.read`, 'openid'], - ], - [`email\t ${PREFIX}groups.read`, ['email', `${PREFIX}groups.read`]], - - // Some incorrect scopes that we don't try to fix - [`${PREFIX}email`, [`${PREFIX}email`]], - [`${PREFIX}profile`, [`${PREFIX}profile`]], - [`${PREFIX}openid`, [`${PREFIX}openid`]], - ])(`should normalize scopes correctly - %p`, (scope, scopes) => { - const auth = OktaAuth.create({ - oauthRequestApi: new MockOAuthApi(), - discoveryApi: UrlPatternDiscovery.compile('http://example.com'), - }); - - auth.getAccessToken(scope); - expect(getSession).toHaveBeenCalledWith({ scopes: new Set(scopes) }); - }); -}); diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 6489c8547d..7266395c78 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -45,6 +45,7 @@ "passport-microsoft": "^0.1.0", "passport-oauth2": "^1.5.0", "passport-okta-oauth": "^0.0.1", + "passport-onelogin-oauth": "^0.0.1", "passport-saml": "^1.3.3", "uuid": "^8.0.0", "winston": "^3.2.1", diff --git a/yarn.lock b/yarn.lock index b2b87be5e0..4d1a4cd856 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17915,6 +17915,15 @@ passport-okta-oauth@^0.0.1: pkginfo "0.2.x" uid2 "0.0.3" +passport-onelogin-oauth@^0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/passport-onelogin-oauth/-/passport-onelogin-oauth-0.0.1.tgz#6e991ac6720783fdd80d4caa08c36cc71ef19962" + integrity sha512-EXFBqlJdHf5AX4QaiZsLfhgQUOR6z3zGA5479SUJF4I4rnAt7yasZEbs27pg8MRiQh/uLZEWLGMoVXr6LHV9mQ== + dependencies: + passport-oauth "1.0.0" + pkginfo "0.2.x" + uid2 "0.0.3" + passport-saml@^1.3.3: version "1.3.3" resolved "https://registry.npmjs.org/passport-saml/-/passport-saml-1.3.3.tgz#cbea1a2b21ff32b3bc4bfd84dc39c3a370df9935" From 11664e3a99c79e95f92ec60ad681ca8849e282ea Mon Sep 17 00:00:00 2001 From: Forrest Waters Date: Wed, 28 Oct 2020 19:34:02 -0500 Subject: [PATCH 017/252] run prettier --- plugins/auth-backend/src/providers/onelogin/index.ts | 2 +- plugins/auth-backend/src/providers/onelogin/provider.ts | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/auth-backend/src/providers/onelogin/index.ts b/plugins/auth-backend/src/providers/onelogin/index.ts index 4c110c56ac..c356ef2d6d 100644 --- a/plugins/auth-backend/src/providers/onelogin/index.ts +++ b/plugins/auth-backend/src/providers/onelogin/index.ts @@ -1 +1 @@ -export { createOneLoginProvider } from './provider'; \ No newline at end of file +export { createOneLoginProvider } from './provider'; diff --git a/plugins/auth-backend/src/providers/onelogin/provider.ts b/plugins/auth-backend/src/providers/onelogin/provider.ts index a9b43fc8d4..bafc0c61ca 100644 --- a/plugins/auth-backend/src/providers/onelogin/provider.ts +++ b/plugins/auth-backend/src/providers/onelogin/provider.ts @@ -27,8 +27,8 @@ type PrivateInfo = { }; export type OneLoginProviderOptions = OAuthProviderOptions & { - issuer: string -} + issuer: string; +}; export class OneLoginProvider implements OAuthHandlers { private readonly _strategy: any; @@ -132,7 +132,6 @@ export class OneLoginProvider implements OAuthHandlers { } } - export const createOneLoginProvider: AuthProviderFactory = ({ globalConfig, config, From b652bf2cc58ae56e34a73d4f74f25904d53ab61e Mon Sep 17 00:00:00 2001 From: Forrest Waters Date: Wed, 28 Oct 2020 19:48:21 -0500 Subject: [PATCH 018/252] add changeset for onelogin --- .changeset/beige-pandas-thank.md | 5 +++++ .../src/providers/onelogin/index.ts | 16 ++++++++++++++++ .../src/providers/onelogin/provider.ts | 19 +++++++++++++++++-- .../src/providers/onelogin/types.d.ts | 2 +- 4 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 .changeset/beige-pandas-thank.md diff --git a/.changeset/beige-pandas-thank.md b/.changeset/beige-pandas-thank.md new file mode 100644 index 0000000000..b2c52842fc --- /dev/null +++ b/.changeset/beige-pandas-thank.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Add OneLogin Identity Provider to Auth Backend diff --git a/plugins/auth-backend/src/providers/onelogin/index.ts b/plugins/auth-backend/src/providers/onelogin/index.ts index c356ef2d6d..79af226cd1 100644 --- a/plugins/auth-backend/src/providers/onelogin/index.ts +++ b/plugins/auth-backend/src/providers/onelogin/index.ts @@ -1 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + export { createOneLoginProvider } from './provider'; diff --git a/plugins/auth-backend/src/providers/onelogin/provider.ts b/plugins/auth-backend/src/providers/onelogin/provider.ts index bafc0c61ca..d11baa605d 100644 --- a/plugins/auth-backend/src/providers/onelogin/provider.ts +++ b/plugins/auth-backend/src/providers/onelogin/provider.ts @@ -1,5 +1,20 @@ -//import { Strategy as OneLoginStrategy } from 'passport-openidconnect'; -import { Strategy as OneLoginStrategy } from 'passport-onelogin'; +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Strategy as OneLoginStrategy } from 'passport-onelogin-oauth'; import express from 'express'; import { OAuthAdapter, diff --git a/plugins/auth-backend/src/providers/onelogin/types.d.ts b/plugins/auth-backend/src/providers/onelogin/types.d.ts index bff5a7e33c..d418f1bcd4 100644 --- a/plugins/auth-backend/src/providers/onelogin/types.d.ts +++ b/plugins/auth-backend/src/providers/onelogin/types.d.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -declare module 'passport-openidconnect' { +declare module 'passport-onelogin-oauth' { export class Strategy { constructor(options: any, verify: any); } From 842acf4f5d95915bae902c89650a22573aafd596 Mon Sep 17 00:00:00 2001 From: Forrest Waters Date: Wed, 28 Oct 2020 19:56:05 -0500 Subject: [PATCH 019/252] remove commented code --- plugins/auth-backend/src/providers/onelogin/provider.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/auth-backend/src/providers/onelogin/provider.ts b/plugins/auth-backend/src/providers/onelogin/provider.ts index d11baa605d..97cf182e5a 100644 --- a/plugins/auth-backend/src/providers/onelogin/provider.ts +++ b/plugins/auth-backend/src/providers/onelogin/provider.ts @@ -73,7 +73,6 @@ export class OneLoginProvider implements OAuthHandlers { idToken: params.id_token, accessToken, scope: params.scope, - //scope: 'profile', expiresInSeconds: params.expires_in, }, profile, From 05b0ed81f543a2eac67606bb4ad225b0d2b95d30 Mon Sep 17 00:00:00 2001 From: Forrest Waters Date: Thu, 29 Oct 2020 12:02:29 -0500 Subject: [PATCH 020/252] fix imports --- .../src/apis/implementations/auth/onelogin/OneLoginAuth.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts b/packages/core-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts index 7420b21510..ff5c7c1990 100644 --- a/packages/core-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts +++ b/packages/core-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts @@ -15,13 +15,13 @@ */ import OneLoginIcon from '@material-ui/icons/AcUnit'; -import { oneloginAuthApiRef } from '@backstage/core-api/src/apis/definitions/auth'; +import { oneloginAuthApiRef } from '../../../definitions/auth'; import { OAuthRequestApi, AuthProvider, DiscoveryApi, -} from '@backstage/core-api/src/apis/definitions'; -import { OAuth2 } from '@backstage/core-api/src/apis/implementations/auth/oauth2'; +} from '../../../definitions'; +import { OAuth2 } from '../oauth2'; type CreateOptions = { discoveryApi: DiscoveryApi; From 8fc93d46967e6f15c48b9c402058a09d619583eb Mon Sep 17 00:00:00 2001 From: Alan Crosswell Date: Thu, 29 Oct 2020 16:57:16 -0400 Subject: [PATCH 021/252] Gitlab Enterprise documentation Most importantly documents the Gitlab callback URL but also the various scopes that seem appropriate. --- plugins/auth-backend/README.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/plugins/auth-backend/README.md b/plugins/auth-backend/README.md index 355fcc1536..4f5a9ef6c7 100644 --- a/plugins/auth-backend/README.md +++ b/plugins/auth-backend/README.md @@ -52,9 +52,27 @@ export AUTH_GITHUB_ENTERPRISE_INSTANCE_URL=https://x ### Gitlab +#### Creating a Gitlab Enterprise OAuth application + +Follow this link, substituting `gitlab.example.com` for your Gitlab enterprise domain, https://gitlab.example.com/profile/applications + +1. Set Application Name to `backstage-dev` or something along those lines. +1. The Authorization Callback URL should match the redirect URI set in Backstage. + 1. Set this to `http://localhost:7000/api/auth/gitlab/handler/frame` for local development. + 1. Set this to `http://{APP_FQDN}:{APP_BACKEND_PORT}/api/auth/gitlab/handler/frame` for non-local deployments. + 1. Select the checked scopes from this list: + - [ ] `api` Grants complete read/write access to the API, including all groups and projects. + - [x] `read_user` Grants read-only access to the authenticated user's profile through the /user API endpoint, which includes username, public email, and full name. Also grants access to read-only API endpoints under /users. + - [x] `read_repository` Grants read-only access to repositories on private projects using Git-over-HTTP (not using the API). + - [x] `write_repository` Grants read-write access to repositories on private projects using Git-over-HTTP (not using the API). + - [ ] `sudo` Grants permission to perform API actions as any user in the system, when authenticated as an admin user. + - [x] `openid` Grants permission to authenticate with GitLab using OpenID Connect. Also gives read-only access to the user's profile and group memberships. + - [x] `profile` Grants read-only access to the user's profile data using OpenID Connect. + - [x] `email` Grants read-only access to the user's primary email address using OpenID Connect. + ```bash -export GITLAB_BASE_URL=x # default is https://gitlab.com -export AUTH_GITLAB_CLIENT_ID=x +export GITLAB_BASE_URL=https://gitlab.example.com # for GitLab Enterprise. default is https://gitlab.com +export AUTH_GITLAB_CLIENT_ID=x # Gitlab calls this the Application ID export AUTH_GITLAB_CLIENT_SECRET=x ``` From efff67c205aed75997a1badca2e91385f236e662 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Fri, 30 Oct 2020 09:49:53 +0100 Subject: [PATCH 022/252] TechDocs: Update docs around docker-in-docker installation Fix python package name to mkdocs-techdocs-core Refer to the original Dockerfile where users can seek inspiration from --- docs/features/techdocs/getting-started.md | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/docs/features/techdocs/getting-started.md b/docs/features/techdocs/getting-started.md index 72efb6eee0..288ddb74ae 100644 --- a/docs/features/techdocs/getting-started.md +++ b/docs/features/techdocs/getting-started.md @@ -73,10 +73,10 @@ required. ### Disable Docker in Docker situation (Optional) -The TechDocs backend plugin runs a docker container with mkdocs to generate the -frontend of the docs from source files (Markdown). If you are deploying -Backstage using Docker, this will mean that your Backstage Docker container will -try to run another Docker container for TechDocs backend. +The TechDocs backend plugin runs a docker container with mkdocs installed to +generate the frontend of the docs from source files (Markdown). If you are +deploying Backstage using Docker, this will mean that your Backstage Docker +container will try to run another Docker container for TechDocs backend. To avoid this problem, we have a configuration available. You can set a value in your `app-config.yaml` that tells the techdocs generator if it should run the @@ -90,10 +90,16 @@ techdocs: ``` Setting `generators.techdocs` to `local` means you will have to make sure your -environment is compatible with techdocs. You will have to install the -`mkdocs-techdocs-container` and 'mkdocs' package from pip, as well as graphviz -and plantuml from your package manager. This has only been tested with python -3.7 and python 3.8. +environment is compatible with techdocs. + +You will have to install the `mkdocs-techdocs-core` and `mkdocs` package from +pip, as well as `graphviz` and `plantuml` from your OS package manager (e.g. +apt). See our +[Dockerfile](https://github.com/spotify/backstage/blob/master/packages/techdocs-container/Dockerfile) +for the latest requirements. You should be trying to match your Dockerfile with +this one. + +It is worth mentioning that we recommend Python version 3.7 or higher. ## Run Backstage locally From 6018855f34e1d1cb19bf904285ed52984243d1cb Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Fri, 30 Oct 2020 10:28:31 +0100 Subject: [PATCH 023/252] TechDocs: Add caveat around Markdown version --- docs/features/techdocs/getting-started.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/features/techdocs/getting-started.md b/docs/features/techdocs/getting-started.md index 288ddb74ae..cbbbc1e6cd 100644 --- a/docs/features/techdocs/getting-started.md +++ b/docs/features/techdocs/getting-started.md @@ -92,14 +92,21 @@ techdocs: Setting `generators.techdocs` to `local` means you will have to make sure your environment is compatible with techdocs. -You will have to install the `mkdocs-techdocs-core` and `mkdocs` package from +You will have to install the `mkdocs` and `mkdocs-techdocs-core` package from pip, as well as `graphviz` and `plantuml` from your OS package manager (e.g. apt). See our [Dockerfile](https://github.com/spotify/backstage/blob/master/packages/techdocs-container/Dockerfile) for the latest requirements. You should be trying to match your Dockerfile with this one. -It is worth mentioning that we recommend Python version 3.7 or higher. +Note: We recommend Python version 3.7 or higher. + +Caveat: Please install the `mkdocs-techdocs-core` package after all other Python +packages. The order is important to make sure we get correct version of some of +the dependencies. For example, we want `Markdown` version to be +[3.2.2](https://github.com/spotify/backstage/blob/f9f70c225548017b6a14daea75b00fbd399c11eb/packages/techdocs-container/techdocs-core/requirements.txt#L11). +You can also explicitly install `Markdown==3.2.2` after installing all other +Python packages. ## Run Backstage locally From 28a5c9b5649f60aad3570c2774c33f9b5811b9a8 Mon Sep 17 00:00:00 2001 From: Abhishek Jakhar Date: Wed, 28 Oct 2020 22:19:16 +0530 Subject: [PATCH 024/252] make export of HeaderTabs consistent with other components export --- .../core/src/layout/HeaderTabs/HeaderTabs.tsx | 90 +++++++++++++++++++ packages/core/src/layout/HeaderTabs/index.tsx | 75 +--------------- 2 files changed, 91 insertions(+), 74 deletions(-) create mode 100644 packages/core/src/layout/HeaderTabs/HeaderTabs.tsx diff --git a/packages/core/src/layout/HeaderTabs/HeaderTabs.tsx b/packages/core/src/layout/HeaderTabs/HeaderTabs.tsx new file mode 100644 index 0000000000..b509cae14f --- /dev/null +++ b/packages/core/src/layout/HeaderTabs/HeaderTabs.tsx @@ -0,0 +1,90 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// TODO(blam): Remove this implementation when the Tabs are ready +// This is just a temporary solution to implementing tabs for now + +import React, { useState, useEffect } from 'react'; +import { makeStyles, Tabs, Tab } from '@material-ui/core'; + +const useStyles = makeStyles(theme => ({ + tabsWrapper: { + gridArea: 'pageSubheader', + backgroundColor: theme.palette.background.paper, + paddingLeft: theme.spacing(3), + }, + defaultTab: { + padding: theme.spacing(3, 3), + ...theme.typography.caption, + textTransform: 'uppercase', + fontWeight: 'bold', + color: theme.palette.text.secondary, + }, + selected: { + color: theme.palette.text.primary, + }, +})); + +export type Tab = { + id: string; + label: string; +}; + +export const HeaderTabs: React.FC<{ + tabs: Tab[]; + onChange?: (index: number) => void; + selectedIndex?: number; +}> = ({ tabs, onChange, selectedIndex }) => { + const [selectedTab, setSelectedTab] = useState(selectedIndex ?? 0); + const styles = useStyles(); + + const handleChange = (_: React.ChangeEvent<{}>, index: number) => { + if (selectedIndex === undefined) { + setSelectedTab(index); + } + if (onChange) onChange(index); + }; + + useEffect(() => { + if (selectedIndex !== undefined) { + setSelectedTab(selectedIndex); + } + }, [selectedIndex]); + + return ( +
+ + {tabs.map((tab, index) => ( + + ))} + +
+ ); +}; diff --git a/packages/core/src/layout/HeaderTabs/index.tsx b/packages/core/src/layout/HeaderTabs/index.tsx index b509cae14f..9a37f20af9 100644 --- a/packages/core/src/layout/HeaderTabs/index.tsx +++ b/packages/core/src/layout/HeaderTabs/index.tsx @@ -14,77 +14,4 @@ * limitations under the License. */ -// TODO(blam): Remove this implementation when the Tabs are ready -// This is just a temporary solution to implementing tabs for now - -import React, { useState, useEffect } from 'react'; -import { makeStyles, Tabs, Tab } from '@material-ui/core'; - -const useStyles = makeStyles(theme => ({ - tabsWrapper: { - gridArea: 'pageSubheader', - backgroundColor: theme.palette.background.paper, - paddingLeft: theme.spacing(3), - }, - defaultTab: { - padding: theme.spacing(3, 3), - ...theme.typography.caption, - textTransform: 'uppercase', - fontWeight: 'bold', - color: theme.palette.text.secondary, - }, - selected: { - color: theme.palette.text.primary, - }, -})); - -export type Tab = { - id: string; - label: string; -}; - -export const HeaderTabs: React.FC<{ - tabs: Tab[]; - onChange?: (index: number) => void; - selectedIndex?: number; -}> = ({ tabs, onChange, selectedIndex }) => { - const [selectedTab, setSelectedTab] = useState(selectedIndex ?? 0); - const styles = useStyles(); - - const handleChange = (_: React.ChangeEvent<{}>, index: number) => { - if (selectedIndex === undefined) { - setSelectedTab(index); - } - if (onChange) onChange(index); - }; - - useEffect(() => { - if (selectedIndex !== undefined) { - setSelectedTab(selectedIndex); - } - }, [selectedIndex]); - - return ( -
- - {tabs.map((tab, index) => ( - - ))} - -
- ); -}; +export { HeaderTabs } from './HeaderTabs'; From 805626336f8fa755b8d93a9bd2f1639668bf817e Mon Sep 17 00:00:00 2001 From: Abhishek Jakhar Date: Wed, 28 Oct 2020 22:30:23 +0530 Subject: [PATCH 025/252] export type Tab from HeaderTabs index.tsx --- packages/core/src/layout/HeaderTabs/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/src/layout/HeaderTabs/index.tsx b/packages/core/src/layout/HeaderTabs/index.tsx index 9a37f20af9..1097698265 100644 --- a/packages/core/src/layout/HeaderTabs/index.tsx +++ b/packages/core/src/layout/HeaderTabs/index.tsx @@ -14,4 +14,5 @@ * limitations under the License. */ +export type { Tab } from './HeaderTabs'; export { HeaderTabs } from './HeaderTabs'; From 357f42b6a105d7ce1ed93b66abe10bb1e8e464f7 Mon Sep 17 00:00:00 2001 From: Jesko Steinberg Date: Thu, 29 Oct 2020 13:14:15 +0100 Subject: [PATCH 026/252] feat: add MarkdownContent component --- .changeset/wet-tools-exercise.md | 15 +++ packages/core/package.json | 4 +- .../MarkdownContent.stories.tsx | 120 ++++++++++++++++++ .../MarkdownContent/MarkdownContent.test.tsx | 43 +++++++ .../MarkdownContent/MarkdownContent.tsx | 74 +++++++++++ .../src/components/MarkdownContent/index.ts | 17 +++ packages/core/src/components/index.ts | 1 + 7 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 .changeset/wet-tools-exercise.md create mode 100644 packages/core/src/components/MarkdownContent/MarkdownContent.stories.tsx create mode 100644 packages/core/src/components/MarkdownContent/MarkdownContent.test.tsx create mode 100644 packages/core/src/components/MarkdownContent/MarkdownContent.tsx create mode 100644 packages/core/src/components/MarkdownContent/index.ts diff --git a/.changeset/wet-tools-exercise.md b/.changeset/wet-tools-exercise.md new file mode 100644 index 0000000000..ba6eb8314f --- /dev/null +++ b/.changeset/wet-tools-exercise.md @@ -0,0 +1,15 @@ +--- +'@backstage/core': minor +--- + +Adds the MarkdownContent component to render and display markdown input. + +``` + +``` + +Render and display the github flavored markdown [GFM](https://github.github.com/gfm/) input: + +``` + +``` diff --git a/packages/core/package.json b/packages/core/package.json index ec4411b390..a78fcc0a82 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -48,11 +48,13 @@ "react-dom": "^16.12.0", "react-helmet": "6.1.0", "react-hook-form": "^6.6.0", + "react-markdown": "^5.0.2", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-sparklines": "^1.7.0", "react-syntax-highlighter": "^13.5.1", - "react-use": "^15.3.3" + "react-use": "^15.3.3", + "remark-gfm": "^1.0.0" }, "devDependencies": { "@backstage/cli": "^0.1.1-alpha.26", diff --git a/packages/core/src/components/MarkdownContent/MarkdownContent.stories.tsx b/packages/core/src/components/MarkdownContent/MarkdownContent.stories.tsx new file mode 100644 index 0000000000..b3747971a7 --- /dev/null +++ b/packages/core/src/components/MarkdownContent/MarkdownContent.stories.tsx @@ -0,0 +1,120 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { MarkdownContent } from './MarkdownContent'; + +export default { + title: 'Data Display/MarkdownContent', + component: MarkdownContent, +}; + +const markdownGithubFlavored = + '# GFM\n' + + '\n' + + '## Autolink literals\n' + + '\n' + + 'www.example.com, https://example.com, and contact@example.com.\n' + + '\n' + + '## Strikethrough\n' + + '\n' + + '~one~ or ~~two~~ tildes.\n' + + '\n' + + '## Table\n' + + '\n' + + '| foo | bar |\n' + + '| --- | --- |\n' + + '| baz | bim |\n' + + '| buz | bum |\n' + + '| biz | bim |\n' + + '\n' + + '## Tasklist\n' + + '\n' + + '* [ ] to do\n' + + '* [x] done'; + +const markdown = + '# Choreas Iovis\n' + + '\n' + + '## Incedere retenta\n' + + '\n' + + 'Lorem markdownum velamina [nupta amici aequoreis](http://est-quae.org/sic)\n' + + 'desertum factum premunt: falcato parvos nihil. Facietque vulnus tum dumque\n' + + 'reserato Maeandros insignia solidis, tot longi causa et nimium arcuerat altera\n' + + 'unus, in quis.\n' + + '\n' + + '1. Est qui dixere nullus\n' + + '2. Fuit obicit\n' + + '3. Vim patrem portae materiem ulla quod crater\n' + + '4. Rigido est magis raptor quid crepitante aequa\n' + + '5. Imago quis ignis tamen\n' + + '\n' + + '## Vix posse vestem\n' + + '\n' + + 'Nec deos robora visa pater toris remittit *crimina* utque, ora ego lacerae quae\n' + + 'laboris laturus silvas audax terrae. Qua fuisse patrio inlaesas [sine\n' + + 'seque](http://ambitvictore.org/), nondum et tamen annis, nec. Poscimur magnum,\n' + + 'Hesperium dedisti, ait ipse et fides terras scalas.\n' + + '\n' + + '- Quas superis satyri adloquitur natura hausimus\n' + + '- Dux suspicere siccare\n' + + '- Cape huc quid videor\n' + + '- Foret vivit concolor\n' + + '- Occupat morte oblectamina minuunt quaeque placidis nate\n' + + '- Non posset' + + '\n' + + 'Licet movitque dederat potest in sorores in sola pendere luce pro quod, sit.\n' + + 'Inpia ut in opibus flores uno quam quo multifidasque fera anhelitus retorsit.\n' + + 'Sustinui premebat puppe somnos. Dicit genu sic qualia excussit facunde parvae in\n' + + 'robur, Ianthe Interea. Superis victorque ponat puta cum: est enim.\n' + + '\n' + + '## Tacetve est in nullis Cerberus silvani luminibus\n' + + '\n' + + 'Divulsere *summissoque esse manes*; artus ausus conatoque utque: illo\n' + + 'Phaestiadas quod pascat et referentem, nec. In seris, iubebat iam nomina:\n' + + 'tergoque occidit ingenii.\n' + + '\n' + + ' mouseDdl(tablet_definition * phishing_icann_mamp);\n' + + ' vector += 20 + key_ram.source_isa(hard_tunneling_zone(w_wireless_page));\n' + + ' if (rate_client_direct) {\n' + + ' textXDpi += sql_cloud_class.sdk.speakers_wired_warm(pcZettabyteGis(\n' + + ' market_bezel, 1), 1);\n' + + ' tag_scraping = format_ppi;\n' + + ' }\n' + + '\n' + + 'Per quem, nec formosior qui cum Peliden me interea **ornatos**! Te facit\n' + + 'instimulat sequentia in flumina exilium te vulnere, sola. Coetum nec amnes.\n' + + 'Protinus nam Caras cava, *a* vocantem dicta inevitabile, nata nulla.\n' + + '\n' + + '## Piscem Iunoni maius\n' + + '\n' + + 'Prece fallere arduus, *ad Athamantis laticem* simillima in ante Temesesque opus!\n' + + 'Ausim quoslibet crede Tyria: Medusa [muneris Aeneaden\n' + + 'tutaque](http://cragon-aequoribus.io/) genitor fistula cogeris abstrahere nati,\n' + + 'relevare videri *non*.\n' + + '\n' + + '> Promissas ulterius senectae Desinet his ait pedum! Libet *sublime* vibrantia\n' + + '> si *dicta quod* pectora cupidine hastam dominoque.\n' + + '\n' + + 'Pedis hic, est bis quod, adhaeret et reditum. Fixa sic vel pugnare **forte est**\n' + + 'parte in quaerite generisque repugnat; de quod, creatos.'; +export const MarkdownContentCommonMark = () => ( + +); + +export const MarkdownContentGithubFlavoredCommonMark = () => ( + +); diff --git a/packages/core/src/components/MarkdownContent/MarkdownContent.test.tsx b/packages/core/src/components/MarkdownContent/MarkdownContent.test.tsx new file mode 100644 index 0000000000..a96525a3f6 --- /dev/null +++ b/packages/core/src/components/MarkdownContent/MarkdownContent.test.tsx @@ -0,0 +1,43 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; +import { MarkdownContent } from './MarkdownContent'; + +describe('', () => { + it('render MarkdownContent component with common mark', async () => { + const rendered = await renderWithEffects( + wrapInTestApp( + , + ), + ); + expect(rendered.getByText('H1', { selector: 'h1' })).toBeInTheDocument(); + expect(rendered.getByText('H2', { selector: 'h2' })).toBeInTheDocument(); + expect(rendered.getByText('H3', { selector: 'h3' })).toBeInTheDocument(); + }); + + it('render MarkdownContent component with common mark github flavored', async () => { + const rendered = await renderWithEffects( + wrapInTestApp( + , + ), + ); + expect( + rendered.getByText('https://example.com', { selector: 'a' }), + ).toBeInTheDocument(); + }); +}); diff --git a/packages/core/src/components/MarkdownContent/MarkdownContent.tsx b/packages/core/src/components/MarkdownContent/MarkdownContent.tsx new file mode 100644 index 0000000000..1449c5b1be --- /dev/null +++ b/packages/core/src/components/MarkdownContent/MarkdownContent.tsx @@ -0,0 +1,74 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { makeStyles } from '@material-ui/core'; +import ReactMarkdown from 'react-markdown'; +import gfm from 'remark-gfm'; +import React from 'react'; + +const useStyles = makeStyles(theme => ({ + markdown: { + '& table': { + borderCollapse: 'collapse', + border: '1px solid #dfe2e5', + color: 'rgb(36, 41, 46)', + }, + '& th, & td': { + border: '1px solid #dfe2e5', + padding: theme.spacing(1), + }, + '& tr': { + backgroundColor: '#fff', + }, + '& tr:nth-child(2n)': { + backgroundColor: '#f6f8fa', + }, + '& pre': { + padding: '16px', + overflow: 'auto', + fontSize: '85%', + lineHeight: 1.45, + backgroundColor: '#f6f8fa', + borderRadius: '6px', + color: 'rgba(0, 0, 0, 0.87)', + }, + '& a': { + color: '#2E77D0', + }, + '& img': { + maxWidth: '100%', + }, + }, +})); + +/** + * MarkdownContent. Renders markdown (CommonMark, optionally with [GFM](https://github.com/remarkjs/remark-gfm)) to formatted HTML. + */ +type Props = { + content: string; + enableGfm?: boolean; +}; + +export const MarkdownContent = ({ content, enableGfm = false }: Props) => { + const classes = useStyles(); + return ( + + ); +}; diff --git a/packages/core/src/components/MarkdownContent/index.ts b/packages/core/src/components/MarkdownContent/index.ts new file mode 100644 index 0000000000..7267ff191c --- /dev/null +++ b/packages/core/src/components/MarkdownContent/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { MarkdownContent } from './MarkdownContent'; diff --git a/packages/core/src/components/index.ts b/packages/core/src/components/index.ts index 4f085ffd6a..40c4ef0c11 100644 --- a/packages/core/src/components/index.ts +++ b/packages/core/src/components/index.ts @@ -35,3 +35,4 @@ export * from './Tabs'; export * from './TrendLine'; export * from './WarningPanel'; export * from './EmptyState'; +export * from './MarkdownContent'; From 7b37d65fd8ffc3e06737a6815a4beadf08ec5f12 Mon Sep 17 00:00:00 2001 From: Jesko Steinberg Date: Fri, 30 Oct 2020 11:02:57 +0100 Subject: [PATCH 027/252] fix: update github-insights-plugin to resolve conflicts --- .changeset/3157.md | 9 +++++++++ .changeset/wet-tools-exercise.md | 15 --------------- 2 files changed, 9 insertions(+), 15 deletions(-) create mode 100644 .changeset/3157.md delete mode 100644 .changeset/wet-tools-exercise.md diff --git a/.changeset/3157.md b/.changeset/3157.md new file mode 100644 index 0000000000..d6c0120d8a --- /dev/null +++ b/.changeset/3157.md @@ -0,0 +1,9 @@ +--- +'@backstage/core': minor +--- + +Adds the MarkdownContent component to render and display markdown input. + +``` + +``` diff --git a/.changeset/wet-tools-exercise.md b/.changeset/wet-tools-exercise.md deleted file mode 100644 index ba6eb8314f..0000000000 --- a/.changeset/wet-tools-exercise.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'@backstage/core': minor ---- - -Adds the MarkdownContent component to render and display markdown input. - -``` - -``` - -Render and display the github flavored markdown [GFM](https://github.github.com/gfm/) input: - -``` - -``` From ef8e3c20ac52626ced7b2ddabbd0235f0d9a7703 Mon Sep 17 00:00:00 2001 From: Jesko Steinberg Date: Fri, 30 Oct 2020 12:08:12 +0100 Subject: [PATCH 028/252] feat: add code block to backstage theme --- packages/theme/src/themes.ts | 8 ++++++++ packages/theme/src/types.ts | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/packages/theme/src/themes.ts b/packages/theme/src/themes.ts index 5b233ac303..48142d48b9 100644 --- a/packages/theme/src/themes.ts +++ b/packages/theme/src/themes.ts @@ -48,6 +48,10 @@ export const lightTheme = createTheme({ text: '#FFFFFF', link: '#000000', }, + code: { + background: '#333333', + text: '#FFFFFF', + }, border: '#E6E6E6', textContrast: '#000000', textVerySubtle: '#DDD', @@ -110,6 +114,10 @@ export const darkTheme = createTheme({ text: '#FFFFFF', link: '#000000', }, + code: { + background: '#F8F8F8', + text: '#333333', + }, border: '#E6E6E6', textContrast: '#FFFFFF', textVerySubtle: '#727272', diff --git a/packages/theme/src/types.ts b/packages/theme/src/types.ts index 5acc0f75e9..c87c5c26f4 100644 --- a/packages/theme/src/types.ts +++ b/packages/theme/src/types.ts @@ -69,6 +69,10 @@ type PaletteAdditions = { text: string; link: string; }; + code: { + background: string; + text: string; + }; }; export type BackstagePalette = Palette & PaletteAdditions; From bbc15f5112f5982d333c1a4991d426bdf5e85f05 Mon Sep 17 00:00:00 2001 From: Jesko Steinberg Date: Fri, 30 Oct 2020 12:08:36 +0100 Subject: [PATCH 029/252] fix: use backstage theme for MarkdownContent --- .changeset/3157.md | 6 ++++ .../MarkdownContent/MarkdownContent.tsx | 33 +++++++++++++------ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/.changeset/3157.md b/.changeset/3157.md index d6c0120d8a..ba6eb8314f 100644 --- a/.changeset/3157.md +++ b/.changeset/3157.md @@ -7,3 +7,9 @@ Adds the MarkdownContent component to render and display markdown input. ``` ``` + +Render and display the github flavored markdown [GFM](https://github.github.com/gfm/) input: + +``` + +``` diff --git a/packages/core/src/components/MarkdownContent/MarkdownContent.tsx b/packages/core/src/components/MarkdownContent/MarkdownContent.tsx index 1449c5b1be..baa3f45862 100644 --- a/packages/core/src/components/MarkdownContent/MarkdownContent.tsx +++ b/packages/core/src/components/MarkdownContent/MarkdownContent.tsx @@ -18,35 +18,48 @@ import { makeStyles } from '@material-ui/core'; import ReactMarkdown from 'react-markdown'; import gfm from 'remark-gfm'; import React from 'react'; +import { BackstageTheme } from '@backstage/theme'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles(theme => ({ markdown: { '& table': { borderCollapse: 'collapse', - border: '1px solid #dfe2e5', - color: 'rgb(36, 41, 46)', + border: `1px solid ${theme.palette.border}`, }, '& th, & td': { - border: '1px solid #dfe2e5', + border: `1px solid ${theme.palette.border}`, padding: theme.spacing(1), }, + '& td': { + wordBreak: 'break-word', + overflow: 'hidden', + verticalAlign: 'middle', + lineHeight: '1', + margin: 0, + padding: theme.spacing(3, 2, 3, 2.5), + borderBottom: 0, + }, + '& th': { + backgroundColor: theme.palette.background.paper, + }, '& tr': { - backgroundColor: '#fff', + backgroundColor: theme.palette.background.paper, }, - '& tr:nth-child(2n)': { - backgroundColor: '#f6f8fa', + '& tr:nth-child(odd)': { + backgroundColor: theme.palette.background.default, }, + '& pre': { padding: '16px', overflow: 'auto', fontSize: '85%', lineHeight: 1.45, - backgroundColor: '#f6f8fa', + backgroundColor: theme.palette.code.background, borderRadius: '6px', - color: 'rgba(0, 0, 0, 0.87)', + color: theme.palette.code.text, }, '& a': { - color: '#2E77D0', + color: theme.palette.link, }, '& img': { maxWidth: '100%', From 992072cc9455430ad44cf8fd2d022754f15522af Mon Sep 17 00:00:00 2001 From: Alan Crosswell Date: Sat, 31 Oct 2020 10:37:00 -0400 Subject: [PATCH 030/252] add lowercase gitlab to styles vocab --- .github/styles/vocab.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt index 0cbd0ffa05..e05c5f8623 100644 --- a/.github/styles/vocab.txt +++ b/.github/styles/vocab.txt @@ -62,6 +62,7 @@ Firekube Fredrik github Github +gitlab Gitlab graphql graphviz From 13e6a6d337d3076e92f134b9e03f0d0fb52f3f0d Mon Sep 17 00:00:00 2001 From: Alan Crosswell Date: Sat, 31 Oct 2020 14:41:07 -0400 Subject: [PATCH 031/252] update GitLab docs to include both gitlab.com and GitLab Enterprise configuration --- plugins/auth-backend/README.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/plugins/auth-backend/README.md b/plugins/auth-backend/README.md index 4f5a9ef6c7..affaca9987 100644 --- a/plugins/auth-backend/README.md +++ b/plugins/auth-backend/README.md @@ -52,30 +52,41 @@ export AUTH_GITHUB_ENTERPRISE_INSTANCE_URL=https://x ### Gitlab -#### Creating a Gitlab Enterprise OAuth application +#### Creating a GitLab OAuth application -Follow this link, substituting `gitlab.example.com` for your Gitlab enterprise domain, https://gitlab.example.com/profile/applications +Follow this link, [Add new application](https://gitlab.com/-/profile/applications). 1. Set Application Name to `backstage-dev` or something along those lines. 1. The Authorization Callback URL should match the redirect URI set in Backstage. 1. Set this to `http://localhost:7000/api/auth/gitlab/handler/frame` for local development. 1. Set this to `http://{APP_FQDN}:{APP_BACKEND_PORT}/api/auth/gitlab/handler/frame` for non-local deployments. - 1. Select the checked scopes from this list: - - [ ] `api` Grants complete read/write access to the API, including all groups and projects. + 1. Select the following scopes from the list: - [x] `read_user` Grants read-only access to the authenticated user's profile through the /user API endpoint, which includes username, public email, and full name. Also grants access to read-only API endpoints under /users. - [x] `read_repository` Grants read-only access to repositories on private projects using Git-over-HTTP (not using the API). - [x] `write_repository` Grants read-write access to repositories on private projects using Git-over-HTTP (not using the API). - - [ ] `sudo` Grants permission to perform API actions as any user in the system, when authenticated as an admin user. - [x] `openid` Grants permission to authenticate with GitLab using OpenID Connect. Also gives read-only access to the user's profile and group memberships. - [x] `profile` Grants read-only access to the user's profile data using OpenID Connect. - [x] `email` Grants read-only access to the user's primary email address using OpenID Connect. ```bash -export GITLAB_BASE_URL=https://gitlab.example.com # for GitLab Enterprise. default is https://gitlab.com +export GITLAB_BASE_URL=https://gitlab.com export AUTH_GITLAB_CLIENT_ID=x # Gitlab calls this the Application ID export AUTH_GITLAB_CLIENT_SECRET=x ``` +#### Creating a GitLab Enterprise OAuth application + +If you have GitLab Enterprise, perform the steps above, replacing `gitlab.example.com` in the following +with the domain of your GitLab Enterprise host: + +Create new application at: `https://gitlab.example.com/profile/applications`. + +```bash +export GITLAB_BASE_URL=https://gitlab.example.com +export AUTH_GITLAB_CLIENT_ID=x # GitLab calls this the Application ID +export AUTH_GITLAB_CLIENT_SECRET=x +``` + ### Okta ```bash From 478e17bcf4e12f370ef597c47501a62fb34bc6d1 Mon Sep 17 00:00:00 2001 From: David Tuite Date: Mon, 2 Nov 2020 08:05:57 +0000 Subject: [PATCH 032/252] Apply development config in Helm charts (#3161) This isn't the best fix because we're still using NODE_ENV=development in a "production" environment. Ideally we would stop doing this. Right now, the Helm charts are broken because they deploy the backend straight into Error state.. This change will unbreak them for the moment. --- contrib/docker/kubernetes-example-backend/Dockerfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contrib/docker/kubernetes-example-backend/Dockerfile b/contrib/docker/kubernetes-example-backend/Dockerfile index df617decf5..b7d7a9631e 100644 --- a/contrib/docker/kubernetes-example-backend/Dockerfile +++ b/contrib/docker/kubernetes-example-backend/Dockerfile @@ -30,6 +30,4 @@ RUN yarn install --frozen-lockfile --production # Do not use this Dockerfile outside of that command, as it will copy in the source code instead. COPY . . -CMD ["node", "packages/backend"] - - +CMD ["node", "packages/backend", "--config", "app-config.yaml", "--config", "app-config.development.yaml"] From eb976e8e0f32dbd16eee255a30cfa912c930a531 Mon Sep 17 00:00:00 2001 From: Althaf Hameez Date: Mon, 2 Nov 2020 16:24:17 +0800 Subject: [PATCH 033/252] Remove tech-radar owner and lifecycle from header (#3061) * Remove tech-radar owner and lifecycle from header * Run prettier * Fix prettier warning --- plugins/tech-radar/src/components/RadarPage.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/tech-radar/src/components/RadarPage.tsx b/plugins/tech-radar/src/components/RadarPage.tsx index 779d1fa088..992266a77b 100644 --- a/plugins/tech-radar/src/components/RadarPage.tsx +++ b/plugins/tech-radar/src/components/RadarPage.tsx @@ -21,7 +21,6 @@ import { ContentHeader, Page, Header, - HeaderLabel, SupportButton, } from '@backstage/core'; import RadarComponent from '../components/RadarComponent'; @@ -48,10 +47,7 @@ export const RadarPage = ({ const classes = useStyles(); return ( -
- - -
+
From f82ec384cb94da3120836e1b387474bbf12ab8ba Mon Sep 17 00:00:00 2001 From: Jesko Steinberg Date: Fri, 30 Oct 2020 15:32:17 +0100 Subject: [PATCH 034/252] feat(plugin-sonarqube): add links to each rating --- plugins/sonarqube/src/api/index.test.ts | 77 +++++++++++-------- plugins/sonarqube/src/api/index.ts | 17 +++- plugins/sonarqube/src/api/types.ts | 2 + .../components/SonarQubeCard/RatingCard.tsx | 30 ++++---- .../SonarQubeCard/SonarQubeCard.tsx | 5 ++ 5 files changed, 86 insertions(+), 45 deletions(-) diff --git a/plugins/sonarqube/src/api/index.test.ts b/plugins/sonarqube/src/api/index.test.ts index 272d021493..2d96fc2135 100644 --- a/plugins/sonarqube/src/api/index.test.ts +++ b/plugins/sonarqube/src/api/index.test.ts @@ -114,22 +114,29 @@ describe('SonarQubeApi', () => { const client = new SonarQubeApi({ discoveryApi }); const summary = await client.getFindingSummary('our-service'); - - expect(summary).toEqual({ - lastAnalysis: '2020-01-01T00:00:00Z', - metrics: { - alert_status: 'OK', - bugs: '2', - reliability_rating: '3.0', - vulnerabilities: '4', - security_rating: '1.0', - code_smells: '100', - sqale_rating: '2.0', - coverage: '55.5', - duplicated_lines_density: '1.0', - }, - projectUrl: 'https://sonarcloud.io/dashboard?id=our-service', - } as FindingSummary); + expect(summary).toEqual( + expect.objectContaining({ + lastAnalysis: '2020-01-01T00:00:00Z', + metrics: { + alert_status: 'OK', + bugs: '2', + reliability_rating: '3.0', + vulnerabilities: '4', + security_rating: '1.0', + code_smells: '100', + sqale_rating: '2.0', + coverage: '55.5', + duplicated_lines_density: '1.0', + }, + projectUrl: 'https://sonarcloud.io/dashboard?id=our-service', + }), + ); + expect(summary?.getIssuesUrl('CODE_SMELL')).toEqual( + 'https://sonarcloud.io/project/issues?id=our-service&types=CODE_SMELL&resolved=false', + ); + expect(summary?.getComponentMeasuresUrl('COVERAGE')).toEqual( + 'https://sonarcloud.io/component_measures?id=our-service&metric=coverage&resolved=false&view=list', + ); }); it('should report finding summary (custom baseUrl)', async () => { @@ -142,20 +149,28 @@ describe('SonarQubeApi', () => { const summary = await client.getFindingSummary('our-service'); - expect(summary).toEqual({ - lastAnalysis: '2020-01-01T00:00:00Z', - metrics: { - alert_status: 'OK', - bugs: '2', - reliability_rating: '3.0', - vulnerabilities: '4', - security_rating: '1.0', - code_smells: '100', - sqale_rating: '2.0', - coverage: '55.5', - duplicated_lines_density: '1.0', - }, - projectUrl: 'http://a.instance.local/dashboard?id=our-service', - } as FindingSummary); + expect(summary).toEqual( + expect.objectContaining({ + lastAnalysis: '2020-01-01T00:00:00Z', + metrics: { + alert_status: 'OK', + bugs: '2', + reliability_rating: '3.0', + vulnerabilities: '4', + security_rating: '1.0', + code_smells: '100', + sqale_rating: '2.0', + coverage: '55.5', + duplicated_lines_density: '1.0', + }, + projectUrl: 'http://a.instance.local/dashboard?id=our-service', + }) as FindingSummary, + ); + expect(summary?.getIssuesUrl('CODE_SMELL')).toEqual( + 'http://a.instance.local/project/issues?id=our-service&types=CODE_SMELL&resolved=false', + ); + expect(summary?.getComponentMeasuresUrl('COVERAGE')).toEqual( + 'http://a.instance.local/component_measures?id=our-service&metric=coverage&resolved=false&view=list', + ); }); }); diff --git a/plugins/sonarqube/src/api/index.ts b/plugins/sonarqube/src/api/index.ts index 01f6f74fe3..f2c616f53c 100644 --- a/plugins/sonarqube/src/api/index.ts +++ b/plugins/sonarqube/src/api/index.ts @@ -16,7 +16,12 @@ import { createApiRef, DiscoveryApi } from '@backstage/core'; import fetch from 'cross-fetch'; -import { ComponentWrapper, MeasuresWrapper, MetricKey } from './types'; +import { + ComponentWrapper, + MeasuresWrapper, + MetricKey, + SonarUrlProcessorFunc, +} from './types'; /** * Define a type to make sure that all metrics are used @@ -29,6 +34,8 @@ export interface FindingSummary { lastAnalysis: string; metrics: Metrics; projectUrl: string; + getIssuesUrl: SonarUrlProcessorFunc; + getComponentMeasuresUrl: SonarUrlProcessorFunc; } export const sonarQubeApiRef = createApiRef({ @@ -105,6 +112,14 @@ export class SonarQubeApi { lastAnalysis: component.component.analysisDate, metrics, projectUrl: `${this.baseUrl}dashboard?id=${componentKey}`, + getIssuesUrl: identifier => + `${ + this.baseUrl + }project/issues?id=${componentKey}&types=${identifier.toUpperCase()}&resolved=false`, + getComponentMeasuresUrl: (identifier: string) => + `${ + this.baseUrl + }component_measures?id=${componentKey}&metric=${identifier.toLowerCase()}&resolved=false&view=list`, }; } } diff --git a/plugins/sonarqube/src/api/types.ts b/plugins/sonarqube/src/api/types.ts index 17539070cb..fc99b2d87d 100644 --- a/plugins/sonarqube/src/api/types.ts +++ b/plugins/sonarqube/src/api/types.ts @@ -53,3 +53,5 @@ export interface Measure { value: string; component: string; } + +export type SonarUrlProcessorFunc = (identifier: string) => string; diff --git a/plugins/sonarqube/src/components/SonarQubeCard/RatingCard.tsx b/plugins/sonarqube/src/components/SonarQubeCard/RatingCard.tsx index cd678b7366..6a94f7e8f4 100644 --- a/plugins/sonarqube/src/components/SonarQubeCard/RatingCard.tsx +++ b/plugins/sonarqube/src/components/SonarQubeCard/RatingCard.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Grid, Typography } from '@material-ui/core'; +import { Grid, Typography, Link } from '@material-ui/core'; import { makeStyles } from '@material-ui/core/styles'; import React, { ReactNode } from 'react'; @@ -50,29 +50,33 @@ export const RatingCard = ({ rightSlot, title, titleIcon, + link, }: { leftSlot: ReactNode; rightSlot: ReactNode; title: string; titleIcon?: ReactNode; + link: string; }) => { const classes = useStyles(); return ( - - - - {leftSlot} + + + + + {leftSlot} + + + {rightSlot} + - - {rightSlot} + + + {titleIcon} {title} + - - - {titleIcon} {title} - - - + ); }; diff --git a/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx b/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx index 8e6cc817a1..2b836d714b 100644 --- a/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx +++ b/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx @@ -181,29 +181,34 @@ export const SonarQubeCard = ({ } title="Bugs" + link={value.getIssuesUrl('BUG')} leftSlot={} rightSlot={} /> } title="Vulnerabilities" + link={value.getIssuesUrl('VULNERABILITY')} leftSlot={} rightSlot={} /> } title="Code Smells" + link={value.getIssuesUrl('CODE_SMELL')} leftSlot={} rightSlot={} />
} rightSlot={} /> } rightSlot={ From 65792dd070455c2993b23cb65f5ab35042bb45b5 Mon Sep 17 00:00:00 2001 From: Iain Billett Date: Sat, 31 Oct 2020 15:44:07 +0000 Subject: [PATCH 035/252] Use discovery api to build URLs in techdocs Use the discovery api to build catalog API URLs and URLs to locally published metadata. --- plugins/techdocs-backend/src/service/router.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/plugins/techdocs-backend/src/service/router.ts b/plugins/techdocs-backend/src/service/router.ts index 2254e721bd..1d932a1fb9 100644 --- a/plugins/techdocs-backend/src/service/router.ts +++ b/plugins/techdocs-backend/src/service/router.ts @@ -62,7 +62,13 @@ export async function createRouter({ const router = Router(); router.get('/metadata/mkdocs/*', async (req, res) => { - const storageUrl = config.getString('techdocs.storageUrl'); + let storageUrl = config.getString('techdocs.storageUrl'); + if (publisher instanceof LocalPublish) { + storageUrl = new URL( + new URL(storageUrl).pathname, + await discovery.getBaseUrl('techdocs'), + ).toString(); + } const { '0': path } = req.params; const metadataURL = `${storageUrl}/${path}/techdocs_metadata.json`; @@ -77,13 +83,14 @@ export async function createRouter({ }); router.get('/metadata/entity/:namespace/:kind/:name', async (req, res) => { - const baseUrl = config.getString('backend.baseUrl'); + const catalogUrl = await discovery.getBaseUrl('catalog'); + const { kind, namespace, name } = req.params; try { const entity = (await ( await fetch( - `${baseUrl}/api/catalog/entities/by-name/${kind}/${namespace}/${name}`, + `${catalogUrl}/entities/by-name/${kind}/${namespace}/${name}`, ) ).json()) as Entity; From d20a481942eefe1278eb508c9df1fd14537234ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 26 Oct 2020 17:53:27 +0100 Subject: [PATCH 036/252] fix(catalog-backend): simplify the filtering logic --- .../catalog/DatabaseEntitiesCatalog.test.ts | 66 +++++----- .../src/catalog/DatabaseEntitiesCatalog.ts | 40 +++--- plugins/catalog-backend/src/catalog/types.ts | 4 +- .../src/database/CommonDatabase.test.ts | 64 ++-------- .../src/database/CommonDatabase.ts | 67 +++------- plugins/catalog-backend/src/database/index.ts | 2 +- plugins/catalog-backend/src/database/types.ts | 46 ++++--- .../src/ingestion/HigherOrderOperations.ts | 2 +- .../src/ingestion/processors/ldap/config.ts | 2 +- .../ingestion/processors/ldap/read.test.ts | 2 +- .../ingestion/processors/ldap/util.test.ts | 16 +-- .../src/ingestion/processors/ldap/util.ts | 11 -- .../src/service/EntityFilters.test.ts | 113 +++++++++++++++++ .../src/service/EntityFilters.ts | 120 ++++++++++++++++++ .../src/service/filterQuery.test.ts | 61 +-------- .../src/service/filterQuery.ts | 57 +-------- .../src/service/router.test.ts | 55 ++++---- plugins/catalog-backend/src/service/router.ts | 32 ++--- .../src/util/RecursivePartial.test.ts | 31 +++++ .../src/util/RecursivePartial.ts | 26 ++++ plugins/catalog-backend/src/util/index.ts | 2 + 21 files changed, 455 insertions(+), 364 deletions(-) create mode 100644 plugins/catalog-backend/src/service/EntityFilters.test.ts create mode 100644 plugins/catalog-backend/src/service/EntityFilters.ts create mode 100644 plugins/catalog-backend/src/util/RecursivePartial.test.ts create mode 100644 plugins/catalog-backend/src/util/RecursivePartial.ts diff --git a/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.test.ts b/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.test.ts index f3db4c8dc6..00898b33a6 100644 --- a/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.test.ts @@ -17,6 +17,7 @@ import { getVoidLogger } from '@backstage/backend-common'; import type { Entity } from '@backstage/catalog-model'; import { Database, DatabaseManager, Transaction } from '../database'; +import { EntityFilters } from '../service/EntityFilters'; import { DatabaseEntitiesCatalog } from './DatabaseEntitiesCatalog'; import { EntityUpsertRequest } from './types'; @@ -73,13 +74,12 @@ describe('DatabaseEntitiesCatalog', () => { ]); expect(db.entities).toHaveBeenCalledTimes(1); - expect(db.entities).toHaveBeenCalledWith(expect.anything(), [ - { - kind: 'b', - 'metadata.namespace': 'd', - 'metadata.name': ['c'], - }, - ]); + expect(db.entities).toHaveBeenCalledWith( + expect.anything(), + EntityFilters.ofFilterString( + 'kind=b,metadata.namespace=d,metadata.name=c', + ), + ); expect(db.setRelations).toHaveBeenCalledTimes(1); expect(db.setRelations).toHaveBeenCalledWith(expect.anything(), 'u', []); expect(db.addEntities).toHaveBeenCalledTimes(1); @@ -107,13 +107,12 @@ describe('DatabaseEntitiesCatalog', () => { ); expect(db.entities).toHaveBeenCalledTimes(1); - expect(db.entities).toHaveBeenCalledWith(expect.anything(), [ - { - kind: 'b', - 'metadata.namespace': 'd', - 'metadata.name': ['c'], - }, - ]); + expect(db.entities).toHaveBeenCalledWith( + expect.anything(), + EntityFilters.ofFilterString( + 'kind=b,metadata.namespace=d,metadata.name=c', + ), + ); expect(db.setRelations).toHaveBeenCalledTimes(1); expect(db.setRelations).toHaveBeenCalledWith(expect.anything(), 'u', []); expect(db.addEntities).toHaveBeenCalledTimes(1); @@ -204,13 +203,12 @@ describe('DatabaseEntitiesCatalog', () => { ]); expect(db.entities).toHaveBeenCalledTimes(1); - expect(db.entities).toHaveBeenCalledWith(expect.anything(), [ - { - kind: 'b', - 'metadata.namespace': 'd', - 'metadata.name': ['c'], - }, - ]); + expect(db.entities).toHaveBeenCalledWith( + expect.anything(), + EntityFilters.ofFilterString( + 'kind=b,metadata.namespace=d,metadata.name=c', + ), + ); expect(db.entityByName).not.toHaveBeenCalled(); expect(db.entityByUid).toHaveBeenCalledTimes(1); expect(db.entityByUid).toHaveBeenCalledWith(transaction, 'u'); @@ -280,13 +278,12 @@ describe('DatabaseEntitiesCatalog', () => { ]); expect(db.entities).toHaveBeenCalledTimes(1); - expect(db.entities).toHaveBeenCalledWith(expect.anything(), [ - { - kind: 'b', - 'metadata.namespace': 'd', - 'metadata.name': ['c'], - }, - ]); + expect(db.entities).toHaveBeenCalledWith( + expect.anything(), + EntityFilters.ofFilterString( + 'kind=b,metadata.namespace=d,metadata.name=c', + ), + ); expect(db.entityByName).toHaveBeenCalledTimes(1); expect(db.entityByName).toHaveBeenCalledWith(transaction, { kind: 'b', @@ -342,13 +339,12 @@ describe('DatabaseEntitiesCatalog', () => { ]); expect(db.entities).toHaveBeenCalledTimes(1); - expect(db.entities).toHaveBeenCalledWith(expect.anything(), [ - { - kind: 'b', - 'metadata.namespace': 'd', - 'metadata.name': ['c'], - }, - ]); + expect(db.entities).toHaveBeenCalledWith( + expect.anything(), + EntityFilters.ofFilterString( + 'kind=b,metadata.namespace=d,metadata.name=c', + ), + ); expect(db.entityByName).not.toHaveBeenCalled(); expect(db.entityByUid).not.toHaveBeenCalled(); expect(db.updateEntity).not.toHaveBeenCalled(); diff --git a/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.ts b/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.ts index c97ee023e6..5c55a30a55 100644 --- a/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.ts @@ -16,13 +16,13 @@ import { ConflictError, NotFoundError } from '@backstage/backend-common'; import { + Entity, entityHasChanges, + EntityRelationSpec, generateUpdatedEntity, getEntityName, LOCATION_ANNOTATION, serializeEntityRef, - Entity, - EntityRelationSpec, } from '@backstage/catalog-model'; import { chunk, groupBy } from 'lodash'; import limiterFactory from 'p-limit'; @@ -30,9 +30,10 @@ import { Logger } from 'winston'; import type { Database, DbEntityResponse, - EntityFilters, + EntityFilter, Transaction, } from '../database'; +import { EntityFilters } from '../service/EntityFilters'; import { durationText } from '../util/timing'; import type { EntitiesCatalog, @@ -65,9 +66,9 @@ export class DatabaseEntitiesCatalog implements EntitiesCatalog { private readonly logger: Logger, ) {} - async entities(filters?: EntityFilters[]): Promise { + async entities(filter?: EntityFilter): Promise { const items = await this.database.transaction(tx => - this.database.entities(tx, filters), + this.database.entities(tx, filter), ); return items.map(i => i.entity); } @@ -113,9 +114,12 @@ export class DatabaseEntitiesCatalog implements EntitiesCatalog { const location = entityResponse.entity.metadata.annotations?.[LOCATION_ANNOTATION]; const colocatedEntities = location - ? await this.database.entities(tx, [ - { [`metadata.annotations.${LOCATION_ANNOTATION}`]: location }, - ]) + ? await this.database.entities( + tx, + EntityFilters.ofMatchers({ + [`metadata.annotations.${LOCATION_ANNOTATION}`]: location, + }), + ) : [entityResponse]; for (const dbResponse of colocatedEntities) { await this.database.removeEntityByUid( @@ -219,9 +223,12 @@ export class DatabaseEntitiesCatalog implements EntitiesCatalog { } if (options?.outputEntities) { - const writtenEntities = await this.database.entities(tx, [ - { 'metadata.uid': modifiedEntityIds.map(e => e.entityId) }, - ]); + const writtenEntities = await this.database.entities( + tx, + EntityFilters.ofMatchers({ + 'metadata.uid': modifiedEntityIds.map(e => e.entityId), + }), + ); modifiedEntityIds = writtenEntities.map(e => ({ entityId: e.entity.metadata.uid!, @@ -241,7 +248,7 @@ export class DatabaseEntitiesCatalog implements EntitiesCatalog { // If this is only a dry run, cancel the database transaction even if it was successful. await tx.rollback(); - this.logger.debug(`Perfomed successful dry run of adding entities`); + this.logger.debug(`Performed successful dry run of adding entities`); } return entityUpserts; @@ -273,13 +280,14 @@ export class DatabaseEntitiesCatalog implements EntitiesCatalog { const markTimestamp = process.hrtime(); const names = requests.map(({ entity }) => entity.metadata.name); - const oldEntities = await this.database.entities(tx, [ - { + const oldEntities = await this.database.entities( + tx, + EntityFilters.ofMatchers({ kind: kind, 'metadata.namespace': namespace, 'metadata.name': names, - }, - ]); + }), + ); const oldEntitiesByName = new Map( oldEntities.map(e => [e.entity.metadata.name, e.entity]), diff --git a/plugins/catalog-backend/src/catalog/types.ts b/plugins/catalog-backend/src/catalog/types.ts index edc6ed2772..52bb6e95ab 100644 --- a/plugins/catalog-backend/src/catalog/types.ts +++ b/plugins/catalog-backend/src/catalog/types.ts @@ -15,7 +15,7 @@ */ import { Entity, EntityRelationSpec, Location } from '@backstage/catalog-model'; -import type { EntityFilters } from '../database'; +import type { EntityFilter } from '../database'; // // Entities @@ -32,7 +32,7 @@ export type EntityUpsertResponse = { }; export type EntitiesCatalog = { - entities(filters?: EntityFilters[]): Promise; + entities(filter?: EntityFilter): Promise; removeEntityByUid(uid: string): Promise; /** diff --git a/plugins/catalog-backend/src/database/CommonDatabase.test.ts b/plugins/catalog-backend/src/database/CommonDatabase.test.ts index b2a238b111..ffffd85f04 100644 --- a/plugins/catalog-backend/src/database/CommonDatabase.test.ts +++ b/plugins/catalog-backend/src/database/CommonDatabase.test.ts @@ -16,6 +16,7 @@ import { ConflictError } from '@backstage/backend-common'; import { Entity, Location, parseEntityRef } from '@backstage/catalog-model'; +import { EntityFilters } from '../service/EntityFilters'; import { DatabaseManager } from './DatabaseManager'; import type { DbEntityRequest, @@ -348,7 +349,7 @@ describe('CommonDatabase', () => { await db.transaction(async tx => { await db.addEntities(tx, [{ entity: e1 }, { entity: e2 }]); }); - const result = await db.transaction(async tx => db.entities(tx, [])); + const result = await db.transaction(async tx => db.entities(tx)); expect(result.length).toEqual(2); expect(result).toEqual( expect.arrayContaining([ @@ -390,7 +391,7 @@ describe('CommonDatabase', () => { await expect( db.transaction(async tx => - db.entities(tx, [{ kind: 'k2', 'spec.c': 'some' }]), + db.entities(tx, EntityFilters.ofFilterString('kind=k2,spec.c=some')), ), ).resolves.toEqual([ { @@ -400,56 +401,14 @@ describe('CommonDatabase', () => { ]); }); - it('can get all specific entities for matching filters with nulls (both missing and literal null value)', async () => { + it('can get all specific entities for matching filters case insensitively', async () => { const entities: Entity[] = [ - { apiVersion: 'a', kind: 'k1', metadata: { name: 'n' } }, { - apiVersion: 'a', - kind: 'k2', - metadata: { name: 'n' }, - spec: { c: 'some' }, + apiVersion: 'A', + kind: 'K1', + metadata: { name: 'N' }, + spec: { c: 'SOME' }, }, - { - apiVersion: 'a', - kind: 'k3', - metadata: { name: 'n' }, - spec: { c: null }, - }, - ]; - - await db.transaction(async tx => { - await db.addEntities( - tx, - entities.map(entity => ({ entity })), - ); - }); - - const rows = await db.transaction(async tx => - db.entities(tx, [{ apiVersion: 'a', 'spec.c': [null, 'some'] }]), - ); - - expect(rows.length).toEqual(3); - expect(rows).toEqual( - expect.arrayContaining([ - { - locationId: undefined, - entity: expect.objectContaining({ kind: 'k1' }), - }, - { - locationId: undefined, - entity: expect.objectContaining({ kind: 'k2' }), - }, - { - locationId: undefined, - entity: expect.objectContaining({ kind: 'k3' }), - }, - ]), - ); - }); - - it('can get all specific entities for matching filters case insensitively)', async () => { - const entities: Entity[] = [ - { apiVersion: 'A', kind: 'K1', metadata: { name: 'N' } }, { apiVersion: 'a', kind: 'k2', @@ -460,7 +419,7 @@ describe('CommonDatabase', () => { apiVersion: 'a', kind: 'k3', metadata: { name: 'n' }, - spec: { c: null }, + spec: { c: 'somE' }, }, ]; @@ -472,7 +431,10 @@ describe('CommonDatabase', () => { }); const rows = await db.transaction(async tx => - db.entities(tx, [{ ApiVersioN: 'A', 'spEc.C': [null, 'some'] }]), + db.entities( + tx, + EntityFilters.ofFilterString('ApiVersioN=A,spEc.C=some'), + ), ); expect(rows.length).toEqual(3); diff --git a/plugins/catalog-backend/src/database/CommonDatabase.ts b/plugins/catalog-backend/src/database/CommonDatabase.ts index bd6dc29abc..f86ab9e598 100644 --- a/plugins/catalog-backend/src/database/CommonDatabase.ts +++ b/plugins/catalog-backend/src/database/CommonDatabase.ts @@ -45,7 +45,7 @@ import { DbEntityResponse, DbLocationsRow, DbLocationsRowWithStatus, - EntityFilters, + EntityFilter, Transaction, } from './types'; @@ -233,71 +233,36 @@ export class CommonDatabase implements Database { async entities( txOpaque: Transaction, - filters?: EntityFilters[], + filter?: EntityFilter, ): Promise { const tx = txOpaque as Knex.Transaction; let entitiesQuery = tx('entities'); - for (const singleFilter of filters ?? []) { + for (const singleFilter of filter?.anyOf ?? []) { entitiesQuery = entitiesQuery.orWhere(function singleFilterFn() { - for (const [matchKey, matchVal] of Object.entries(singleFilter)) { - const key = matchKey.toLowerCase().replace(/[*]/g, '%'); - const keyOp = key.includes('%') ? 'like' : '='; - const values = Array.isArray(matchVal) ? matchVal : [matchVal]; - - let matchNulls = false; - const matchIn: string[] = []; - const matchLike: string[] = []; - - for (const value of values) { - if (!value) { - matchNulls = true; - } else if (value.includes('*')) { - matchLike.push(value.toLowerCase().replace(/[*]/g, '%')); - } else { - matchIn.push(value.toLowerCase()); - } - } - + for (const { key, matchValueIn } 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 = tx('entities_search') .select('entity_id') .where(function keyFilter() { - this.andWhere('key', keyOp, key); - this.andWhere(function valueFilter() { - if (matchIn.length === 1) { - this.orWhere({ value: matchIn[0] }); - } else if (matchIn.length > 1) { - this.orWhereIn('value', matchIn); + this.andWhere({ key: key.toLowerCase() }); + if (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()), + ); } - if (matchLike.length) { - for (const x of matchLike) { - this.orWhere('value', 'like', tx.raw('?', [x])); - } - } - if (matchNulls) { - // Match explicit nulls, and then handle absence separately - // below - this.orWhereNull('value'); - } - }); + } }); - // Handle absence as nulls as well - this.andWhere(function match() { - this.whereIn('id', matchQuery); - if (matchNulls) { - this.orWhereNotIn( - 'id', - tx('entities_search') - .select('entity_id') - .where('key', keyOp, key), - ); - } - }); + this.andWhere('id', 'in', matchQuery); } }); } diff --git a/plugins/catalog-backend/src/database/index.ts b/plugins/catalog-backend/src/database/index.ts index 0d84106815..c2cc07788a 100644 --- a/plugins/catalog-backend/src/database/index.ts +++ b/plugins/catalog-backend/src/database/index.ts @@ -20,7 +20,7 @@ export type { Database, DbEntityRequest, DbEntityResponse, + EntitiesSearchFilter, EntityFilter, - EntityFilters, Transaction, } from './types'; diff --git a/plugins/catalog-backend/src/database/types.ts b/plugins/catalog-backend/src/database/types.ts index 62b2d55b88..1307b063f2 100644 --- a/plugins/catalog-backend/src/database/types.ts +++ b/plugins/catalog-backend/src/database/types.ts @@ -17,8 +17,8 @@ import type { Entity, EntityName, - Location, EntityRelationSpec, + Location, } from '@backstage/catalog-model'; export type DbEntitiesRow = { @@ -80,26 +80,35 @@ export type DatabaseLocationUpdateLogEvent = { }; /** - * Filter matcher for a single entity field. - * - * Can be either null or a string, or an array of those. Null and the empty - * string are treated equally, and match both a present field with a null or - * empty value, as well as an absent field. - * - * A filter may contain asterisks (*) that are treated as wildcards for zero - * or more arbitrary characters. + * Matches rows in the entities_search table. */ -export type EntityFilter = null | string | (null | string)[]; +export type EntitiesSearchFilter = { + /** + * The key to match on. + * + * Matches are always case insensitive. + */ + key: string; + + /** + * Match on plain equality of values. + * + * If undefined, this factor is not taken into account. Otherwise, match on + * values that are equal to any of the given array items. Matches are always + * case insensitive. + */ + matchValueIn?: string[]; +}; /** - * A set of filter matchers used for filtering entities. + * A filter expression for entities. * - * The keys are full dot-separated paths into the structure of an entity, for - * example "metadata.name". You can also address any item in an array the same - * way, e.g. "a.b.c": "x" works if b is an array of objects that have a c field - * and any of those have the value x. + * Any (at least one) of the outer sets must match, within which all of the + * individual filters must match. */ -export type EntityFilters = Record; +export type EntityFilter = { + anyOf: { allOf: EntitiesSearchFilter[] }[]; +}; /** * An abstraction for transactions of the underlying database technology. @@ -160,10 +169,7 @@ export type Database = { matchingGeneration?: number, ): Promise; - entities( - tx: Transaction, - filters?: EntityFilters[], - ): Promise; + entities(tx: Transaction, filter?: EntityFilter): Promise; entityByName( tx: Transaction, diff --git a/plugins/catalog-backend/src/ingestion/HigherOrderOperations.ts b/plugins/catalog-backend/src/ingestion/HigherOrderOperations.ts index 902e26e0e3..f352e8d3fd 100644 --- a/plugins/catalog-backend/src/ingestion/HigherOrderOperations.ts +++ b/plugins/catalog-backend/src/ingestion/HigherOrderOperations.ts @@ -18,7 +18,7 @@ import { Location, LocationSpec } from '@backstage/catalog-model'; import { v4 as uuidv4 } from 'uuid'; import { Logger } from 'winston'; import { EntitiesCatalog, LocationsCatalog } from '../catalog'; -import { durationText } from '../util/timing'; +import { durationText } from '../util'; import { AddLocationResult, HigherOrderOperation, diff --git a/plugins/catalog-backend/src/ingestion/processors/ldap/config.ts b/plugins/catalog-backend/src/ingestion/processors/ldap/config.ts index 74c02e3f84..2a63b20619 100644 --- a/plugins/catalog-backend/src/ingestion/processors/ldap/config.ts +++ b/plugins/catalog-backend/src/ingestion/processors/ldap/config.ts @@ -17,7 +17,7 @@ import { Config, JsonValue } from '@backstage/config'; import { SearchOptions } from 'ldapjs'; import mergeWith from 'lodash/mergeWith'; -import { RecursivePartial } from './util'; +import { RecursivePartial } from '../../../util'; /** * The configuration parameters for a single LDAP provider. diff --git a/plugins/catalog-backend/src/ingestion/processors/ldap/read.test.ts b/plugins/catalog-backend/src/ingestion/processors/ldap/read.test.ts index e7a7d1c567..e644bab22a 100644 --- a/plugins/catalog-backend/src/ingestion/processors/ldap/read.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/ldap/read.test.ts @@ -17,6 +17,7 @@ import { GroupEntity, UserEntity } from '@backstage/catalog-model'; import { SearchEntry } from 'ldapjs'; import merge from 'lodash/merge'; +import { RecursivePartial } from '../../../util'; import { LdapClient } from './client'; import { GroupConfig, UserConfig } from './config'; import { @@ -25,7 +26,6 @@ import { LDAP_UUID_ANNOTATION, } from './constants'; import { readLdapGroups, readLdapUsers, resolveRelations } from './read'; -import { RecursivePartial } from './util'; function user(data: RecursivePartial): UserEntity { return merge( diff --git a/plugins/catalog-backend/src/ingestion/processors/ldap/util.test.ts b/plugins/catalog-backend/src/ingestion/processors/ldap/util.test.ts index 2def6ad960..63c7253411 100644 --- a/plugins/catalog-backend/src/ingestion/processors/ldap/util.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/ldap/util.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { errorString, RecursivePartial } from './util'; +import { errorString } from './util'; describe('errorString', () => { it('formats', () => { @@ -22,17 +22,3 @@ describe('errorString', () => { expect(errorString(e)).toEqual('1 n: m'); }); }); - -describe('RecursivePartial', () => { - it('is recursive', () => { - type X = { - required: { - required: string; - }; - }; - const x: RecursivePartial = { - required: {}, - }; - expect(x).toEqual({ required: {} }); - }); -}); diff --git a/plugins/catalog-backend/src/ingestion/processors/ldap/util.ts b/plugins/catalog-backend/src/ingestion/processors/ldap/util.ts index 29159dc219..f5751b1e9f 100644 --- a/plugins/catalog-backend/src/ingestion/processors/ldap/util.ts +++ b/plugins/catalog-backend/src/ingestion/processors/ldap/util.ts @@ -24,14 +24,3 @@ import { Error as LDAPError } from 'ldapjs'; export function errorString(error: LDAPError) { return `${error.code} ${error.name}: ${error.message}`; } - -/** - * Makes all keys of an entire hierarchy optional. - */ -export type RecursivePartial = { - [P in keyof T]?: T[P] extends (infer U)[] - ? RecursivePartial[] - : T[P] extends object - ? RecursivePartial - : T[P]; -}; diff --git a/plugins/catalog-backend/src/service/EntityFilters.test.ts b/plugins/catalog-backend/src/service/EntityFilters.test.ts new file mode 100644 index 0000000000..bfc20c625a --- /dev/null +++ b/plugins/catalog-backend/src/service/EntityFilters.test.ts @@ -0,0 +1,113 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EntityFilters } from './EntityFilters'; + +describe('EntityFilters', () => { + describe('ofQuery', () => { + it('translates empty query to empty list', () => { + const result = EntityFilters.ofQuery({}); + expect(result).toEqual(undefined); + }); + + it('supports single-string format', () => { + const result = EntityFilters.ofQuery({ filter: 'a=1' })!; + expect(result).toEqual({ + anyOf: [{ allOf: [{ key: 'a', matchValueIn: ['1'] }] }], + }); + }); + + it('supports array-of-strings format', () => { + const result = EntityFilters.ofQuery({ filter: ['a=1', 'b=2'] }); + expect(result).toEqual({ + anyOf: [ + { allOf: [{ key: 'a', matchValueIn: ['1'] }] }, + { allOf: [{ key: 'b', matchValueIn: ['2'] }] }, + ], + }); + }); + + it('throws for non-strings', () => { + expect(() => EntityFilters.ofQuery({ filter: [3] })).toThrow(/string/); + }); + }); + + describe('ofFilterString', () => { + it('runs the happy path', () => { + const result = EntityFilters.ofFilterString('a=1,b=2'); + expect(result).toEqual({ + anyOf: [ + { + allOf: [ + { key: 'a', matchValueIn: ['1'] }, + { key: 'b', matchValueIn: ['2'] }, + ], + }, + ], + }); + }); + + it('ignores empty', () => { + const result = EntityFilters.ofFilterString('a=1,,b=2,'); + expect(result).toEqual({ + anyOf: [ + { + allOf: [ + { key: 'a', matchValueIn: ['1'] }, + { key: 'b', matchValueIn: ['2'] }, + ], + }, + ], + }); + }); + + it('trims', () => { + const result = EntityFilters.ofFilterString(' a = 1 ,, b=2 ,'); + expect(result).toEqual({ + anyOf: [ + { + allOf: [ + { key: 'a', matchValueIn: ['1'] }, + { key: 'b', matchValueIn: ['2'] }, + ], + }, + ], + }); + }); + + it('merges multiple of the same key', () => { + const result = EntityFilters.ofFilterString('a=1,a=2,b=3'); + expect(result).toEqual({ + anyOf: [ + { + allOf: [ + { key: 'a', matchValueIn: ['1', '2'] }, + { key: 'b', matchValueIn: ['3'] }, + ], + }, + ], + }); + }); + + it('throws on missing equal sign', () => { + expect(() => EntityFilters.ofFilterString('a,b=2')).toThrow(); + }); + + it('throws on misplaced equal sign', () => { + expect(() => EntityFilters.ofFilterString('a=,b=2')).toThrow(); + }); + }); +}); diff --git a/plugins/catalog-backend/src/service/EntityFilters.ts b/plugins/catalog-backend/src/service/EntityFilters.ts new file mode 100644 index 0000000000..5a93407424 --- /dev/null +++ b/plugins/catalog-backend/src/service/EntityFilters.ts @@ -0,0 +1,120 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { InputError } from '@backstage/backend-common'; +import { EntitiesSearchFilter, EntityFilter } from '../database'; + +/** + * A builder that assists in creating entity filter instances. + */ +export class EntityFilters { + // Builds a filter from the value of a filter=a=1,b=2 type filter string + static ofFilterString(filterString: string): EntityFilter | undefined { + const builder = new EntityFilters(); + builder.addFilterString(filterString); + return builder.build(); + } + + // Builds a filter from an entire query params structure + static ofQuery(query: Record): EntityFilter | undefined { + const filterStrings = [query.filter || []].flat(); + + if (filterStrings.some(f => typeof f !== 'string')) { + throw new InputError( + 'Only string type filter query parameters are supported', + ); + } + + const builder = new EntityFilters(); + for (const filterString of filterStrings) { + builder.addFilterString(filterString); + } + + return builder.build(); + } + + // Builds a filter from a { key: value } matcher object + static ofMatchers( + matchers: Record, + ): EntityFilter | undefined { + const builder = new EntityFilters(); + builder.addMatchers(matchers); + return builder.build(); + } + + private filters: EntitiesSearchFilter[][]; + + constructor() { + this.filters = []; + } + + addFilterString(filterString: string): EntityFilters { + const filtersByKey: Record = {}; + + const addFilter = (key: string, value: string) => { + const f = + key in filtersByKey + ? filtersByKey[key] + : (filtersByKey[key] = { key, matchValueIn: [] }); + f.matchValueIn!.push(value); + }; + + const statements = filterString + .split(',') + .map(s => s.trim()) + .filter(Boolean); + + for (const statement of statements) { + const equalsIndex = statement.indexOf('='); + if (equalsIndex < 1) { + throw new InputError('Malformed filter query'); + } else { + const key = statement.substr(0, equalsIndex).trim(); + const value = statement.substr(equalsIndex + 1).trim(); + if (!key || !value) { + throw new InputError('Malformed filter query'); + } + addFilter(key, value); + } + } + + this.filters.push(Object.values(filtersByKey)); + + return this; + } + + addMatchers(matchers: Record): EntityFilters { + const filters: EntitiesSearchFilter[] = []; + + for (const [key, value] of Object.entries(matchers)) { + filters.push({ key, matchValueIn: [value].flat() }); + } + + if (filters.length) { + this.filters.push(filters); + } + + return this; + } + + build(): EntityFilter | undefined { + if (!this.filters.length) { + return undefined; + } + + return { anyOf: this.filters.map(f => ({ allOf: f })) }; + } +} diff --git a/plugins/catalog-backend/src/service/filterQuery.test.ts b/plugins/catalog-backend/src/service/filterQuery.test.ts index bfb58568b5..a4b3d9c671 100644 --- a/plugins/catalog-backend/src/service/filterQuery.test.ts +++ b/plugins/catalog-backend/src/service/filterQuery.test.ts @@ -15,66 +15,7 @@ */ import { Entity } from '@backstage/catalog-model'; -import { - translateFilterQueryEntryToEntityFilters, - translateQueryToEntityFilters, - translateQueryToFieldMapper, -} from './filterQuery'; - -describe('translateQueryToEntityFilters', () => { - it('translates empty query to empty list', () => { - const result = translateQueryToEntityFilters({}); - expect(result).toEqual([]); - }); - - it('supports single-string format', () => { - const result = translateQueryToEntityFilters({ filter: 'a=1' }); - expect(result).toEqual([{ a: ['1'] }]); - }); - - it('supports array-of-strings format', () => { - const result = translateQueryToEntityFilters({ filter: ['a=1', 'b=2'] }); - expect(result).toEqual([{ a: ['1'] }, { b: ['2'] }]); - }); - - it('throws for non-strings', () => { - expect(() => translateQueryToEntityFilters({ filter: [3] })).toThrow( - /string/, - ); - }); -}); - -describe('translateFilterQueryEntryToEntityFilters', () => { - it('runs the happy path', () => { - const result = translateFilterQueryEntryToEntityFilters('a=1,b=2'); - expect(result).toEqual({ a: ['1'], b: ['2'] }); - }); - - it('ignores empty', () => { - const result = translateFilterQueryEntryToEntityFilters('a=1,,b=2,'); - expect(result).toEqual({ a: ['1'], b: ['2'] }); - }); - - it('trims', () => { - const result = translateFilterQueryEntryToEntityFilters(' a = 1 ,, b=2 ,'); - expect(result).toEqual({ a: ['1'], b: ['2'] }); - }); - - it('merges multiple of the same key', () => { - const result = translateFilterQueryEntryToEntityFilters('a=1,a=2,b=3'); - expect(result).toEqual({ a: ['1', '2'], b: ['3'] }); - }); - - it('treats missing equal signs as presence', () => { - const result = translateFilterQueryEntryToEntityFilters('a,b=2'); - expect(result).toEqual({ a: ['*'], b: ['2'] }); - }); - - it('treats empty value as null/absence', () => { - const result = translateFilterQueryEntryToEntityFilters('a=,b=2'); - expect(result).toEqual({ a: [null], b: ['2'] }); - }); -}); +import { translateQueryToFieldMapper } from './filterQuery'; describe('translateQueryToFieldMapper', () => { const entity: Entity = { diff --git a/plugins/catalog-backend/src/service/filterQuery.ts b/plugins/catalog-backend/src/service/filterQuery.ts index 4ab471ffcd..e9b6ef6062 100644 --- a/plugins/catalog-backend/src/service/filterQuery.ts +++ b/plugins/catalog-backend/src/service/filterQuery.ts @@ -17,62 +17,7 @@ import { InputError } from '@backstage/backend-common'; import { Entity } from '@backstage/catalog-model'; import lodash from 'lodash'; -import { EntityFilters } from '../database'; -import { RecursivePartial } from '../ingestion/processors/ldap/util'; - -export function translateQueryToEntityFilters( - query: Record, -): EntityFilters[] { - if (!query.filter) { - return []; - } - - const filterStrings = [query.filter].flat(); - - if (filterStrings.some(s => typeof s !== 'string')) { - throw new InputError( - 'Only string type filter query parameters are supported', - ); - } - - return filterStrings - .filter(Boolean) - .map(translateFilterQueryEntryToEntityFilters); -} - -// Parses the value of a filter=a=1,b=2 type query param -export function translateFilterQueryEntryToEntityFilters( - filterString: string, -): EntityFilters { - const filters: Record = {}; - - const addFilter = (key: string, value: string | null) => { - const matchers = key in filters ? filters[key] : (filters[key] = []); - matchers.push(value || null); - }; - - const statements = filterString - .split(',') - .map(s => s.trim()) - .filter(Boolean); - - for (const statement of statements) { - const equalsIndex = statement.indexOf('='); - if (equalsIndex < 0) { - // Check presence, any value - addFilter(statement, '*'); - } else { - const key = statement.substr(0, equalsIndex).trim(); - const value = statement.substr(equalsIndex + 1).trim(); - if (!key) { - throw new InputError('Malformed filter query'); - } - addFilter(key, value); - } - } - - return filters; -} +import { RecursivePartial } from '../util'; type FieldMapper = (entity: Entity) => Entity; diff --git a/plugins/catalog-backend/src/service/router.test.ts b/plugins/catalog-backend/src/service/router.test.ts index 1cf64945ee..cef40a9aac 100644 --- a/plugins/catalog-backend/src/service/router.test.ts +++ b/plugins/catalog-backend/src/service/router.test.ts @@ -21,6 +21,7 @@ import request from 'supertest'; import { EntitiesCatalog, LocationsCatalog } from '../catalog'; import { LocationResponse } from '../catalog/types'; import { HigherOrderOperation } from '../ingestion/types'; +import { EntityFilters } from './EntityFilters'; import { createRouter } from './router'; describe('createRouter', () => { @@ -78,18 +79,22 @@ describe('createRouter', () => { it('parses single and multiple request parameters and passes them down', async () => { entitiesCatalog.entities.mockResolvedValueOnce([]); const response = await request(app).get( - '/entities?filter=a=1,a=,a=3,b=4&filter=c=', + '/entities?filter=a=1,a=2,b=3&filter=c=4', ); expect(response.status).toEqual(200); expect(entitiesCatalog.entities).toHaveBeenCalledTimes(1); - expect(entitiesCatalog.entities).toHaveBeenCalledWith([ - { - a: ['1', null, '3'], - b: ['4'], - }, - { c: [null] }, - ]); + expect(entitiesCatalog.entities).toHaveBeenCalledWith({ + anyOf: [ + { + allOf: [ + { key: 'a', matchValueIn: ['1', '2'] }, + { key: 'b', matchValueIn: ['3'] }, + ], + }, + { allOf: [{ key: 'c', matchValueIn: ['4'] }] }, + ], + }); }); }); @@ -107,9 +112,9 @@ describe('createRouter', () => { const response = await request(app).get('/entities/by-uid/zzz'); expect(entitiesCatalog.entities).toHaveBeenCalledTimes(1); - expect(entitiesCatalog.entities).toHaveBeenCalledWith([ - { 'metadata.uid': 'zzz' }, - ]); + expect(entitiesCatalog.entities).toHaveBeenCalledWith( + EntityFilters.ofMatchers({ 'metadata.uid': 'zzz' }), + ); expect(response.status).toEqual(200); expect(response.body).toEqual(expect.objectContaining(entity)); }); @@ -120,9 +125,9 @@ describe('createRouter', () => { const response = await request(app).get('/entities/by-uid/zzz'); expect(entitiesCatalog.entities).toHaveBeenCalledTimes(1); - expect(entitiesCatalog.entities).toHaveBeenCalledWith([ - { 'metadata.uid': 'zzz' }, - ]); + expect(entitiesCatalog.entities).toHaveBeenCalledWith( + EntityFilters.ofMatchers({ 'metadata.uid': 'zzz' }), + ); expect(response.status).toEqual(404); expect(response.text).toMatch(/uid/); }); @@ -143,13 +148,13 @@ describe('createRouter', () => { const response = await request(app).get('/entities/by-name/k/ns/n'); expect(entitiesCatalog.entities).toHaveBeenCalledTimes(1); - expect(entitiesCatalog.entities).toHaveBeenCalledWith([ - { + expect(entitiesCatalog.entities).toHaveBeenCalledWith( + EntityFilters.ofMatchers({ kind: 'k', 'metadata.namespace': 'ns', 'metadata.name': 'n', - }, - ]); + }), + ); expect(response.status).toEqual(200); expect(response.body).toEqual(expect.objectContaining(entity)); }); @@ -160,13 +165,13 @@ describe('createRouter', () => { const response = await request(app).get('/entities/by-name/b/d/c'); expect(entitiesCatalog.entities).toHaveBeenCalledTimes(1); - expect(entitiesCatalog.entities).toHaveBeenCalledWith([ - { + expect(entitiesCatalog.entities).toHaveBeenCalledWith( + EntityFilters.ofMatchers({ kind: 'b', 'metadata.namespace': 'd', 'metadata.name': 'c', - }, - ]); + }), + ); expect(response.status).toEqual(404); expect(response.text).toMatch(/name/); }); @@ -209,9 +214,9 @@ describe('createRouter', () => { { entity, relations: [] }, ]); expect(entitiesCatalog.entities).toHaveBeenCalledTimes(1); - expect(entitiesCatalog.entities).toHaveBeenCalledWith([ - { 'metadata.uid': 'u' }, - ]); + expect(entitiesCatalog.entities).toHaveBeenCalledWith( + EntityFilters.ofMatchers({ 'metadata.uid': 'u' }), + ); expect(response.status).toEqual(200); expect(response.body).toEqual(entity); }); diff --git a/plugins/catalog-backend/src/service/router.ts b/plugins/catalog-backend/src/service/router.ts index 96b343a184..ebad544bef 100644 --- a/plugins/catalog-backend/src/service/router.ts +++ b/plugins/catalog-backend/src/service/router.ts @@ -23,10 +23,8 @@ import { Logger } from 'winston'; import yn from 'yn'; import { EntitiesCatalog, LocationsCatalog } from '../catalog'; import { HigherOrderOperation } from '../ingestion/types'; -import { - translateQueryToEntityFilters, - translateQueryToFieldMapper, -} from './filterQuery'; +import { EntityFilters } from './EntityFilters'; +import { translateQueryToFieldMapper } from './filterQuery'; import { requireRequestBody, validateRequestBody } from './util'; export interface RouterOptions { @@ -47,9 +45,9 @@ export async function createRouter( if (entitiesCatalog) { router .get('/entities', async (req, res) => { - const filters = translateQueryToEntityFilters(req.query); + const filter = EntityFilters.ofQuery(req.query); const fieldMapper = translateQueryToFieldMapper(req.query); - const entities = await entitiesCatalog.entities(filters); + const entities = await entitiesCatalog.entities(filter); res.status(200).send(entities.map(fieldMapper)); }) .post('/entities', async (req, res) => { @@ -57,18 +55,16 @@ export async function createRouter( const [result] = await entitiesCatalog.batchAddOrUpdateEntities([ { entity: body as Entity, relations: [] }, ]); - const [entity] = await entitiesCatalog.entities([ - { 'metadata.uid': result.entityId }, - ]); + const [entity] = await entitiesCatalog.entities( + EntityFilters.ofMatchers({ 'metadata.uid': result.entityId }), + ); res.status(200).send(entity); }) .get('/entities/by-uid/:uid', async (req, res) => { const { uid } = req.params; - const entities = await entitiesCatalog.entities([ - { - 'metadata.uid': uid, - }, - ]); + const entities = await entitiesCatalog.entities( + EntityFilters.ofMatchers({ 'metadata.uid': uid }), + ); if (!entities.length) { res.status(404).send(`No entity with uid ${uid}`); } @@ -81,13 +77,13 @@ export async function createRouter( }) .get('/entities/by-name/:kind/:namespace/:name', async (req, res) => { const { kind, namespace, name } = req.params; - const entities = await entitiesCatalog.entities([ - { + const entities = await entitiesCatalog.entities( + EntityFilters.ofMatchers({ kind: kind, 'metadata.namespace': namespace, 'metadata.name': name, - }, - ]); + }), + ); if (!entities.length) { res .status(404) diff --git a/plugins/catalog-backend/src/util/RecursivePartial.test.ts b/plugins/catalog-backend/src/util/RecursivePartial.test.ts new file mode 100644 index 0000000000..7f16226188 --- /dev/null +++ b/plugins/catalog-backend/src/util/RecursivePartial.test.ts @@ -0,0 +1,31 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RecursivePartial } from './RecursivePartial'; + +describe('RecursivePartial', () => { + it('is recursive', () => { + type X = { + required: { + required: string; + }; + }; + const x: RecursivePartial = { + required: {}, + }; + expect(x).toEqual({ required: {} }); + }); +}); diff --git a/plugins/catalog-backend/src/util/RecursivePartial.ts b/plugins/catalog-backend/src/util/RecursivePartial.ts new file mode 100644 index 0000000000..0fa468b4c4 --- /dev/null +++ b/plugins/catalog-backend/src/util/RecursivePartial.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Makes all keys of an entire hierarchy optional. + */ +export type RecursivePartial = { + [P in keyof T]?: T[P] extends (infer U)[] + ? RecursivePartial[] + : T[P] extends object + ? RecursivePartial + : T[P]; +}; diff --git a/plugins/catalog-backend/src/util/index.ts b/plugins/catalog-backend/src/util/index.ts index 6f0505d8c5..8364819f99 100644 --- a/plugins/catalog-backend/src/util/index.ts +++ b/plugins/catalog-backend/src/util/index.ts @@ -15,3 +15,5 @@ */ export * from './runPeriodically'; +export * from './RecursivePartial'; +export * from './timing'; From 153670832e1795140ee45347dc5094ce27aba773 Mon Sep 17 00:00:00 2001 From: Raghunandan Date: Mon, 2 Nov 2020 14:24:56 +0100 Subject: [PATCH 037/252] techdocs: override default mkdocs config with user config from mkdocs.yaml --- .../techdocs-core/src/core.py | 28 +++++++++-- .../techdocs-core/src/test_core.py | 48 +++++++++++++++++++ 2 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 packages/techdocs-container/techdocs-core/src/test_core.py diff --git a/packages/techdocs-container/techdocs-core/src/core.py b/packages/techdocs-container/techdocs-core/src/core.py index ed1eb7c6f6..aa94d870bc 100644 --- a/packages/techdocs-container/techdocs-core/src/core.py +++ b/packages/techdocs-container/techdocs-core/src/core.py @@ -30,6 +30,10 @@ class TechDocsCore(BasePlugin): '{\n "site_name": "{{ config.site_name }}",\n "site_description": "{{ config.site_description }}"\n}' ) + mdx_configs_override = {} + if "mdx_configs" in config.keys(): + mdx_configs_override = config["mdx_configs"].copy() + # Theme config["theme"] = Theme( name="material", static_templates=["techdocs_metadata.json",], @@ -44,15 +48,16 @@ class TechDocsCore(BasePlugin): monorepo_plugin = MonorepoPlugin() monorepo_plugin.load_config({}) - config["plugins"]["search"] = search_plugin config["plugins"]["monorepo"] = monorepo_plugin - search_plugin = SearchPlugin() - search_plugin.load_config({}) - config["plugins"]["search"] = search_plugin - # Markdown Extensions + if "markdown_extensions" not in config.keys(): + config["markdown_extensions"] = [] + + if "mdx_configs" not in config.keys(): + config["mdx_configs"] = {} + config["markdown_extensions"].append("admonition") config["markdown_extensions"].append("toc") config["mdx_configs"]["toc"] = { @@ -90,4 +95,17 @@ class TechDocsCore(BasePlugin): config["markdown_extensions"].append("markdown_inline_graphviz") config["markdown_extensions"].append("plantuml_markdown") + # merge or add config supplied by user in the mkdocs.yml + mdx_configs_keys = config["mdx_configs"].keys() + mdx_configs_override_keys = mdx_configs_override.keys() + for key in mdx_configs_override_keys: + if key in mdx_configs_keys: + default_config = config["mdx_configs"][key] + override_config = mdx_configs_override[key] + default_config.update(override_config) + config["mdx_configs"][key] = default_config + else: + config["mdx_configs"].append(key) + config["mdx_configs"][key] = mdx_configs_override[key] + return config diff --git a/packages/techdocs-container/techdocs-core/src/test_core.py b/packages/techdocs-container/techdocs-core/src/test_core.py new file mode 100644 index 0000000000..7f34d28d38 --- /dev/null +++ b/packages/techdocs-container/techdocs-core/src/test_core.py @@ -0,0 +1,48 @@ +import unittest +import mkdocs.config as config +import mkdocs.plugins as plugins +from .core import TechDocsCore + +class DummyTechDocsCorePlugin(plugins.BasePlugin): + pass + +class TestTechDocsCoreConfig(unittest.TestCase): + def setUp(self): + self.techdocscore = TechDocsCore() + self.plugin_collection = plugins.PluginCollection() + plugin = DummyTechDocsCorePlugin() + self.plugin_collection['techdocs-core'] = plugin + self.mkdocs_yaml_config = {"plugins": self.plugin_collection} + + def test_removes_techdocs_core_plugin_from_config(self): + final_config = self.techdocscore.on_config(self.mkdocs_yaml_config) + self.assertTrue("techdocs-core" not in final_config["plugins"]) + + def test_merge_default_config_and_user_config(self): + self.mkdocs_yaml_config["markdown_extension"] = [] + self.mkdocs_yaml_config["mdx_configs"] = {} + self.mkdocs_yaml_config["markdown_extension"].append(["toc"]) + self.mkdocs_yaml_config["mdx_configs"]["toc"] = {"toc_depth": 3} + final_config = self.techdocscore.on_config(self.mkdocs_yaml_config) + self.assertTrue("toc" in final_config["mdx_configs"]) + self.assertTrue("permalink" in final_config["mdx_configs"]["toc"]) + self.assertTrue("toc_depth" in final_config["mdx_configs"]["toc"]) + + def test_override_default_config_with_user_config(self): + self.mkdocs_yaml_config["markdown_extension"] = [] + self.mkdocs_yaml_config["mdx_configs"] = {} + self.mkdocs_yaml_config["markdown_extension"].append(["toc"]) + self.mkdocs_yaml_config["mdx_configs"]["toc"] = {"permalink": False} + final_config = self.techdocscore.on_config(self.mkdocs_yaml_config) + self.assertTrue("toc" in final_config["mdx_configs"]) + self.assertTrue("permalink" in final_config["mdx_configs"]["toc"]) + self.assertFalse(final_config["mdx_configs"]["toc"]["permalink"]) + + def test_add_user_config_to_default_config(self): + self.mkdocs_yaml_config["markdown_extension"] = [] + self.mkdocs_yaml_config["mdx_configs"] = {} + self.mkdocs_yaml_config["markdown_extension"].append(["abc"]) + self.mkdocs_yaml_config["mdx_configs"]["abc"] = {"testkey": "testvalue"} + final_config = self.techdocscore.on_config(self.mkdocs_yaml_config) + self.assertTrue("abc" in final_config["mdx_configs"]) + self.assertTrue("testkey" in final_config["mdx_configs"]["abc"]) From 833d8feae76625d73ab8a3c507a410206008340c Mon Sep 17 00:00:00 2001 From: Raghunandan Date: Mon, 2 Nov 2020 14:37:57 +0100 Subject: [PATCH 038/252] only override config keys that exist --- packages/techdocs-container/techdocs-core/src/core.py | 6 +----- .../techdocs-container/techdocs-core/src/test_core.py | 9 --------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/packages/techdocs-container/techdocs-core/src/core.py b/packages/techdocs-container/techdocs-core/src/core.py index aa94d870bc..7e5385e9f5 100644 --- a/packages/techdocs-container/techdocs-core/src/core.py +++ b/packages/techdocs-container/techdocs-core/src/core.py @@ -95,7 +95,7 @@ class TechDocsCore(BasePlugin): config["markdown_extensions"].append("markdown_inline_graphviz") config["markdown_extensions"].append("plantuml_markdown") - # merge or add config supplied by user in the mkdocs.yml + # merge config supplied by user in the mkdocs.yml mdx_configs_keys = config["mdx_configs"].keys() mdx_configs_override_keys = mdx_configs_override.keys() for key in mdx_configs_override_keys: @@ -103,9 +103,5 @@ class TechDocsCore(BasePlugin): default_config = config["mdx_configs"][key] override_config = mdx_configs_override[key] default_config.update(override_config) - config["mdx_configs"][key] = default_config - else: - config["mdx_configs"].append(key) - config["mdx_configs"][key] = mdx_configs_override[key] return config diff --git a/packages/techdocs-container/techdocs-core/src/test_core.py b/packages/techdocs-container/techdocs-core/src/test_core.py index 7f34d28d38..0ccdc713e6 100644 --- a/packages/techdocs-container/techdocs-core/src/test_core.py +++ b/packages/techdocs-container/techdocs-core/src/test_core.py @@ -37,12 +37,3 @@ class TestTechDocsCoreConfig(unittest.TestCase): self.assertTrue("toc" in final_config["mdx_configs"]) self.assertTrue("permalink" in final_config["mdx_configs"]["toc"]) self.assertFalse(final_config["mdx_configs"]["toc"]["permalink"]) - - def test_add_user_config_to_default_config(self): - self.mkdocs_yaml_config["markdown_extension"] = [] - self.mkdocs_yaml_config["mdx_configs"] = {} - self.mkdocs_yaml_config["markdown_extension"].append(["abc"]) - self.mkdocs_yaml_config["mdx_configs"]["abc"] = {"testkey": "testvalue"} - final_config = self.techdocscore.on_config(self.mkdocs_yaml_config) - self.assertTrue("abc" in final_config["mdx_configs"]) - self.assertTrue("testkey" in final_config["mdx_configs"]["abc"]) From d76b4659755b9d5e987a7a0efa2d184e1c4de0dc Mon Sep 17 00:00:00 2001 From: Jesko Steinberg Date: Fri, 30 Oct 2020 14:25:03 +0100 Subject: [PATCH 039/252] fix: use code snippet for code rendering --- .../MarkdownContent/MarkdownContent.test.tsx | 13 +++++++++++++ .../MarkdownContent/MarkdownContent.tsx | 17 ++++++++--------- packages/theme/src/themes.ts | 8 -------- packages/theme/src/types.ts | 4 ---- yarn.lock | 2 +- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/core/src/components/MarkdownContent/MarkdownContent.test.tsx b/packages/core/src/components/MarkdownContent/MarkdownContent.test.tsx index a96525a3f6..2fb40d2f4f 100644 --- a/packages/core/src/components/MarkdownContent/MarkdownContent.test.tsx +++ b/packages/core/src/components/MarkdownContent/MarkdownContent.test.tsx @@ -40,4 +40,17 @@ describe('', () => { rendered.getByText('https://example.com', { selector: 'a' }), ).toBeInTheDocument(); }); + + it('render MarkdownContent component with CodeSnippet for code blocks', async () => { + const rendered = await renderWithEffects( + wrapInTestApp(), + ); + const fp1 = rendered.getByText('jest', { selector: 'span' }); + expect(fp1).toBeInTheDocument(); + expect(fp1.className).toEqual('hljs-function'); + const fp2 = rendered.getByText('(test: string)', { selector: 'span' }); + expect(fp2).toBeInTheDocument(); + expect(fp2.className).toEqual('hljs-function'); + expect(rendered.getByText(';', { selector: 'span' })).toBeInTheDocument(); + }); }); diff --git a/packages/core/src/components/MarkdownContent/MarkdownContent.tsx b/packages/core/src/components/MarkdownContent/MarkdownContent.tsx index baa3f45862..4f234565d2 100644 --- a/packages/core/src/components/MarkdownContent/MarkdownContent.tsx +++ b/packages/core/src/components/MarkdownContent/MarkdownContent.tsx @@ -19,6 +19,7 @@ import ReactMarkdown from 'react-markdown'; import gfm from 'remark-gfm'; import React from 'react'; import { BackstageTheme } from '@backstage/theme'; +import { CodeSnippet } from '../CodeSnippet'; const useStyles = makeStyles(theme => ({ markdown: { @@ -49,15 +50,6 @@ const useStyles = makeStyles(theme => ({ backgroundColor: theme.palette.background.default, }, - '& pre': { - padding: '16px', - overflow: 'auto', - fontSize: '85%', - lineHeight: 1.45, - backgroundColor: theme.palette.code.background, - borderRadius: '6px', - color: theme.palette.code.text, - }, '& a': { color: theme.palette.link, }, @@ -75,6 +67,12 @@ type Props = { enableGfm?: boolean; }; +const renderers = { + code: ({ language, value }: { language: string; value: string }) => { + return ; + }, +}; + export const MarkdownContent = ({ content, enableGfm = false }: Props) => { const classes = useStyles(); return ( @@ -82,6 +80,7 @@ export const MarkdownContent = ({ content, enableGfm = false }: Props) => { plugins={enableGfm ? [gfm] : []} className={classes.markdown} children={content} + renderers={renderers} /> ); }; diff --git a/packages/theme/src/themes.ts b/packages/theme/src/themes.ts index 48142d48b9..5b233ac303 100644 --- a/packages/theme/src/themes.ts +++ b/packages/theme/src/themes.ts @@ -48,10 +48,6 @@ export const lightTheme = createTheme({ text: '#FFFFFF', link: '#000000', }, - code: { - background: '#333333', - text: '#FFFFFF', - }, border: '#E6E6E6', textContrast: '#000000', textVerySubtle: '#DDD', @@ -114,10 +110,6 @@ export const darkTheme = createTheme({ text: '#FFFFFF', link: '#000000', }, - code: { - background: '#F8F8F8', - text: '#333333', - }, border: '#E6E6E6', textContrast: '#FFFFFF', textVerySubtle: '#727272', diff --git a/packages/theme/src/types.ts b/packages/theme/src/types.ts index c87c5c26f4..5acc0f75e9 100644 --- a/packages/theme/src/types.ts +++ b/packages/theme/src/types.ts @@ -69,10 +69,6 @@ type PaletteAdditions = { text: string; link: string; }; - code: { - background: string; - text: string; - }; }; export type BackstagePalette = Palette & PaletteAdditions; diff --git a/yarn.lock b/yarn.lock index 80d96ad134..3615ad7f7d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19682,7 +19682,7 @@ react-markdown@^4.3.1: unist-util-visit "^1.3.0" xtend "^4.0.1" -react-markdown@^5.0.0: +react-markdown@^5.0.0, react-markdown@^5.0.2: version "5.0.2" resolved "https://registry.npmjs.org/react-markdown/-/react-markdown-5.0.2.tgz#d15a8beb37b4ec34fc23dd892e7755eb7040b8db" integrity sha512-kmkB4JbV7LqkDAjvaKRKtodB3n3Id76/DalaDun1U8FuLB0SenPfvH+jAQ5Pcpo54cACRQc1LB1yXmuuuIVecw== From 4c8ac3daf1149ba3102e7a8197b89e3e297cf0dc Mon Sep 17 00:00:00 2001 From: Raghunandan Date: Mon, 2 Nov 2020 15:23:14 +0100 Subject: [PATCH 040/252] Fix formatting --- .../techdocs-container/techdocs-core/src/core.py | 5 ++++- .../techdocs-core/src/test_core.py | 14 ++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/techdocs-container/techdocs-core/src/core.py b/packages/techdocs-container/techdocs-core/src/core.py index 7e5385e9f5..c29715c283 100644 --- a/packages/techdocs-container/techdocs-core/src/core.py +++ b/packages/techdocs-container/techdocs-core/src/core.py @@ -36,7 +36,10 @@ class TechDocsCore(BasePlugin): # Theme config["theme"] = Theme( - name="material", static_templates=["techdocs_metadata.json",], + name="material", + static_templates=[ + "techdocs_metadata.json", + ], ) config["theme"].dirs.append(tempfile.gettempdir()) diff --git a/packages/techdocs-container/techdocs-core/src/test_core.py b/packages/techdocs-container/techdocs-core/src/test_core.py index 0ccdc713e6..621570356c 100644 --- a/packages/techdocs-container/techdocs-core/src/test_core.py +++ b/packages/techdocs-container/techdocs-core/src/test_core.py @@ -3,15 +3,17 @@ import mkdocs.config as config import mkdocs.plugins as plugins from .core import TechDocsCore + class DummyTechDocsCorePlugin(plugins.BasePlugin): pass + class TestTechDocsCoreConfig(unittest.TestCase): def setUp(self): self.techdocscore = TechDocsCore() self.plugin_collection = plugins.PluginCollection() plugin = DummyTechDocsCorePlugin() - self.plugin_collection['techdocs-core'] = plugin + self.plugin_collection["techdocs-core"] = plugin self.mkdocs_yaml_config = {"plugins": self.plugin_collection} def test_removes_techdocs_core_plugin_from_config(self): @@ -24,9 +26,9 @@ class TestTechDocsCoreConfig(unittest.TestCase): self.mkdocs_yaml_config["markdown_extension"].append(["toc"]) self.mkdocs_yaml_config["mdx_configs"]["toc"] = {"toc_depth": 3} final_config = self.techdocscore.on_config(self.mkdocs_yaml_config) - self.assertTrue("toc" in final_config["mdx_configs"]) - self.assertTrue("permalink" in final_config["mdx_configs"]["toc"]) - self.assertTrue("toc_depth" in final_config["mdx_configs"]["toc"]) + self.assertTrue("toc" in final_config["mdx_configs"]) + self.assertTrue("permalink" in final_config["mdx_configs"]["toc"]) + self.assertTrue("toc_depth" in final_config["mdx_configs"]["toc"]) def test_override_default_config_with_user_config(self): self.mkdocs_yaml_config["markdown_extension"] = [] @@ -34,6 +36,6 @@ class TestTechDocsCoreConfig(unittest.TestCase): self.mkdocs_yaml_config["markdown_extension"].append(["toc"]) self.mkdocs_yaml_config["mdx_configs"]["toc"] = {"permalink": False} final_config = self.techdocscore.on_config(self.mkdocs_yaml_config) - self.assertTrue("toc" in final_config["mdx_configs"]) - self.assertTrue("permalink" in final_config["mdx_configs"]["toc"]) + self.assertTrue("toc" in final_config["mdx_configs"]) + self.assertTrue("permalink" in final_config["mdx_configs"]["toc"]) self.assertFalse(final_config["mdx_configs"]["toc"]["permalink"]) From f0cf95b0cc7f43d55f052b958f5135fdab10336f Mon Sep 17 00:00:00 2001 From: Raghunandan Date: Mon, 2 Nov 2020 16:02:32 +0100 Subject: [PATCH 041/252] Update black linter version --- packages/techdocs-container/techdocs-core/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/techdocs-container/techdocs-core/requirements.txt b/packages/techdocs-container/techdocs-core/requirements.txt index fc56ae6aee..279a391454 100644 --- a/packages/techdocs-container/techdocs-core/requirements.txt +++ b/packages/techdocs-container/techdocs-core/requirements.txt @@ -13,4 +13,4 @@ Markdown==3.2.2 # The linter using for Python # Note: This requires Python 3.6+ to run, but can format Python 2 code too. # https://github.com/psf/black -black==19.10b0 +black==20.8b1 From 46ca3c9aa51d5cc45f010049f1b9afd6dd9c0839 Mon Sep 17 00:00:00 2001 From: Raghunandan Date: Mon, 2 Nov 2020 16:10:00 +0100 Subject: [PATCH 042/252] Revert "Update black linter version" This reverts commit f0cf95b0cc7f43d55f052b958f5135fdab10336f. --- packages/techdocs-container/techdocs-core/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/techdocs-container/techdocs-core/requirements.txt b/packages/techdocs-container/techdocs-core/requirements.txt index 279a391454..fc56ae6aee 100644 --- a/packages/techdocs-container/techdocs-core/requirements.txt +++ b/packages/techdocs-container/techdocs-core/requirements.txt @@ -13,4 +13,4 @@ Markdown==3.2.2 # The linter using for Python # Note: This requires Python 3.6+ to run, but can format Python 2 code too. # https://github.com/psf/black -black==20.8b1 +black==19.10b0 From 653c882a2c879a1300d3aa0e26b624320293ffd4 Mon Sep 17 00:00:00 2001 From: Raghunandan Date: Mon, 2 Nov 2020 16:11:08 +0100 Subject: [PATCH 043/252] one more formatting fix --- packages/techdocs-container/techdocs-core/src/core.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/techdocs-container/techdocs-core/src/core.py b/packages/techdocs-container/techdocs-core/src/core.py index c29715c283..7e5385e9f5 100644 --- a/packages/techdocs-container/techdocs-core/src/core.py +++ b/packages/techdocs-container/techdocs-core/src/core.py @@ -36,10 +36,7 @@ class TechDocsCore(BasePlugin): # Theme config["theme"] = Theme( - name="material", - static_templates=[ - "techdocs_metadata.json", - ], + name="material", static_templates=["techdocs_metadata.json",], ) config["theme"].dirs.append(tempfile.gettempdir()) From 5de4914f54cda4c0b84d7f04a46111bf9b8a1092 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 2 Nov 2020 16:15:25 +0100 Subject: [PATCH 044/252] core-api: text fix, throughout --- packages/core-api/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-api/src/types.ts b/packages/core-api/src/types.ts index 09f0bc11ae..13a8a72570 100644 --- a/packages/core-api/src/types.ts +++ b/packages/core-api/src/types.ts @@ -15,7 +15,7 @@ */ /** - * This file contains non-react related core types used throught Backstage. + * This file contains non-react related core types used throughout Backstage. */ /** From e2fb37db0a66c98725b2c97b8c93e837d6929abd Mon Sep 17 00:00:00 2001 From: Raghunandan Date: Mon, 2 Nov 2020 16:21:47 +0100 Subject: [PATCH 045/252] PR review fixes --- packages/techdocs-container/techdocs-core/src/core.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/techdocs-container/techdocs-core/src/core.py b/packages/techdocs-container/techdocs-core/src/core.py index 7e5385e9f5..b78ff24cdf 100644 --- a/packages/techdocs-container/techdocs-core/src/core.py +++ b/packages/techdocs-container/techdocs-core/src/core.py @@ -96,10 +96,8 @@ class TechDocsCore(BasePlugin): config["markdown_extensions"].append("plantuml_markdown") # merge config supplied by user in the mkdocs.yml - mdx_configs_keys = config["mdx_configs"].keys() - mdx_configs_override_keys = mdx_configs_override.keys() - for key in mdx_configs_override_keys: - if key in mdx_configs_keys: + for key in mdx_configs_override: + if key in config["mdx_configs"]: default_config = config["mdx_configs"][key] override_config = mdx_configs_override[key] default_config.update(override_config) From 3c8ad0a06a34ad51a40b17c278869ff55be9ae61 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 2 Nov 2020 16:32:31 +0100 Subject: [PATCH 046/252] core-api: more text fixes --- packages/core-api/src/apis/definitions/OAuthRequestApi.ts | 4 ++-- packages/core-api/src/apis/definitions/StorageApi.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core-api/src/apis/definitions/OAuthRequestApi.ts b/packages/core-api/src/apis/definitions/OAuthRequestApi.ts index 7514a88dc1..b9776ed037 100644 --- a/packages/core-api/src/apis/definitions/OAuthRequestApi.ts +++ b/packages/core-api/src/apis/definitions/OAuthRequestApi.ts @@ -99,7 +99,7 @@ export type PendingAuthRequest = { export type OAuthRequestApi = { /** * A utility for showing login popups or similar things, and merging together multiple requests for - * different scopes into one request that inclues all scopes. + * different scopes into one request that includes all scopes. * * The passed in options provide information about the login provider, and how to handle auth requests. * @@ -114,7 +114,7 @@ export type OAuthRequestApi = { ): AuthRequester; /** - * Observers panding auth requests. The returned observable will emit all + * Observers pending auth requests. The returned observable will emit all * current active auth request, at most one for each created auth requester. * * Each request has its own info about the login provider, forwarded from the auth requester options. diff --git a/packages/core-api/src/apis/definitions/StorageApi.ts b/packages/core-api/src/apis/definitions/StorageApi.ts index d0f71ea3a4..2482af6d63 100644 --- a/packages/core-api/src/apis/definitions/StorageApi.ts +++ b/packages/core-api/src/apis/definitions/StorageApi.ts @@ -52,7 +52,7 @@ export interface StorageApi { remove(key: string): Promise; /** - * Save persistant data, and emit messages to anyone that is using observe$ for this key + * Save persistent data, and emit messages to anyone that is using observe$ for this key * * @param {String} key Unique key associated with the data. */ From bb400beea0e64871549829064b361195f882cc0c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 28 Oct 2020 22:51:00 +0100 Subject: [PATCH 047/252] docs: regenerate api reference docs --- docs/reference/utility-apis/AlertApi.md | 12 +- docs/reference/utility-apis/AppThemeApi.md | 57 ++++++++-- .../utility-apis/BackstageIdentityApi.md | 8 +- docs/reference/utility-apis/Config.md | 10 +- docs/reference/utility-apis/DiscoveryApi.md | 2 +- docs/reference/utility-apis/ErrorApi.md | 14 +-- .../reference/utility-apis/FeatureFlagsApi.md | 106 +++++++++++++++--- docs/reference/utility-apis/IdentityApi.md | 4 +- docs/reference/utility-apis/OAuthApi.md | 6 +- .../reference/utility-apis/OAuthRequestApi.md | 18 +-- .../utility-apis/OpenIdConnectApi.md | 4 +- docs/reference/utility-apis/ProfileInfoApi.md | 8 +- docs/reference/utility-apis/README.md | 42 ++++--- docs/reference/utility-apis/SessionApi.md | 14 ++- docs/reference/utility-apis/StorageApi.md | 14 +-- 15 files changed, 227 insertions(+), 92 deletions(-) diff --git a/docs/reference/utility-apis/AlertApi.md b/docs/reference/utility-apis/AlertApi.md index 6219f520f5..7d3167fdb7 100644 --- a/docs/reference/utility-apis/AlertApi.md +++ b/docs/reference/utility-apis/AlertApi.md @@ -1,7 +1,7 @@ # AlertApi The AlertApi type is defined at -[packages/core-api/src/apis/definitions/AlertApi.ts:29](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/AlertApi.ts#L29). +[packages/core-api/src/apis/definitions/AlertApi.ts:29](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/AlertApi.ts#L29). The following Utility API implements this type: [alertApiRef](./README.md#alert) @@ -38,7 +38,7 @@ export type AlertMessage = { Defined at -[packages/core-api/src/apis/definitions/AlertApi.ts:19](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/AlertApi.ts#L19). +[packages/core-api/src/apis/definitions/AlertApi.ts:19](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/AlertApi.ts#L19). Referenced by: [post](#post), [alert\$](#alert). @@ -67,13 +67,13 @@ export type Observable<T> = { Defined at -[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/types.ts#L53). +[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L53). Referenced by: [alert\$](#alert). ### Observer -This file contains non-react related core types used through Backstage. +This file contains non-react related core types used throughout Backstage. Observer interface for consuming an Observer, see TC39. @@ -86,7 +86,7 @@ export type Observer<T> = { Defined at -[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/types.ts#L24). +[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L24). Referenced by: [Observable](#observable). @@ -109,6 +109,6 @@ export type Subscription = { Defined at -[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/types.ts#L33). +[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L33). Referenced by: [Observable](#observable). diff --git a/docs/reference/utility-apis/AppThemeApi.md b/docs/reference/utility-apis/AppThemeApi.md index eb35eb38ef..e103511246 100644 --- a/docs/reference/utility-apis/AppThemeApi.md +++ b/docs/reference/utility-apis/AppThemeApi.md @@ -1,7 +1,7 @@ # AppThemeApi The AppThemeApi type is defined at -[packages/core-api/src/apis/definitions/AppThemeApi.ts:50](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/AppThemeApi.ts#L50). +[packages/core-api/src/apis/definitions/AppThemeApi.ts:56](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/AppThemeApi.ts#L56). The following Utility API implements this type: [appThemeApiRef](./README.md#apptheme) @@ -72,11 +72,16 @@ export type AppTheme = { * The specialized MaterialUI theme instance. */ theme: BackstageTheme; + + /** + * An Icon for the theme mode setting. + */ + icon?: React.ReactElement<SvgIconProps>; } Defined at -[packages/core-api/src/apis/definitions/AppThemeApi.ts:24](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/AppThemeApi.ts#L24). +[packages/core-api/src/apis/definitions/AppThemeApi.ts:25](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/AppThemeApi.ts#L25). Referenced by: [getInstalledThemes](#getinstalledthemes). @@ -87,7 +92,7 @@ export type BackstagePalette = Palette & Palette Defined at -[packages/theme/src/types.ts:70](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/theme/src/types.ts#L70). +[packages/theme/src/types.ts:74](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/theme/src/types.ts#L74). Referenced by: [BackstageTheme](#backstagetheme). @@ -96,11 +101,13 @@ Referenced by: [BackstageTheme](#backstagetheme).
 export interface BackstageTheme extends Theme {
   palette: BackstagePalette;
+  page: PageTheme;
+  getPageTheme: ({ themeId }: PageThemeSelector) => PageTheme;
 }
 
Defined at -[packages/theme/src/types.ts:73](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/theme/src/types.ts#L73). +[packages/theme/src/types.ts:81](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/theme/src/types.ts#L81). Referenced by: [AppTheme](#apptheme). @@ -129,13 +136,13 @@ export type Observable<T> = { Defined at -[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/types.ts#L53). +[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L53). Referenced by: [activeThemeId\$](#activethemeid). ### Observer -This file contains non-react related core types used through Backstage. +This file contains non-react related core types used throughout Backstage. Observer interface for consuming an Observer, see TC39. @@ -148,10 +155,38 @@ export type Observer<T> = { Defined at -[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/types.ts#L24). +[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L24). Referenced by: [Observable](#observable). +### PageTheme + +
+export type PageTheme = {
+  colors: string[];
+  shape: string;
+  backgroundImage: string;
+}
+
+ +Defined at +[packages/theme/src/types.ts:103](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/theme/src/types.ts#L103). + +Referenced by: [BackstageTheme](#backstagetheme). + +### PageThemeSelector + +
+export type PageThemeSelector = {
+  themeId: string;
+}
+
+ +Defined at +[packages/theme/src/types.ts:77](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/theme/src/types.ts#L77). + +Referenced by: [BackstageTheme](#backstagetheme). + ### PaletteAdditions
@@ -181,6 +216,8 @@ type PaletteAdditions = {
   navigation: {
     background: string;
     indicator: string;
+    color: string;
+    selectedColor: string;
   };
   tabbar: {
     indicator: string;
@@ -199,12 +236,14 @@ type PaletteAdditions = {
   banner: {
     info: string;
     error: string;
+    text: string;
+    link: string;
   };
 }
 
Defined at -[packages/theme/src/types.ts:23](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/theme/src/types.ts#L23). +[packages/theme/src/types.ts:23](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/theme/src/types.ts#L23). Referenced by: [BackstagePalette](#backstagepalette). @@ -227,6 +266,6 @@ export type Subscription = { Defined at -[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/types.ts#L33). +[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L33). Referenced by: [Observable](#observable). diff --git a/docs/reference/utility-apis/BackstageIdentityApi.md b/docs/reference/utility-apis/BackstageIdentityApi.md index 529b73c576..4e86717101 100644 --- a/docs/reference/utility-apis/BackstageIdentityApi.md +++ b/docs/reference/utility-apis/BackstageIdentityApi.md @@ -1,7 +1,7 @@ # BackstageIdentityApi The BackstageIdentityApi type is defined at -[packages/core-api/src/apis/definitions/auth.ts:134](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L134). +[packages/core-api/src/apis/definitions/auth.ts:134](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L134). The following Utility APIs implement this type: @@ -19,6 +19,8 @@ The following Utility APIs implement this type: - [oktaAuthApiRef](./README.md#oktaauth) +- [samlAuthApiRef](./README.md#samlauth) + ## Members ### getBackstageIdentity() @@ -68,7 +70,7 @@ export type AuthRequestOptions = { Defined at -[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L40). +[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L40). Referenced by: [getBackstageIdentity](#getbackstageidentity). @@ -89,6 +91,6 @@ export type BackstageIdentity = { Defined at -[packages/core-api/src/apis/definitions/auth.ts:147](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L147). +[packages/core-api/src/apis/definitions/auth.ts:147](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L147). Referenced by: [getBackstageIdentity](#getbackstageidentity). diff --git a/docs/reference/utility-apis/Config.md b/docs/reference/utility-apis/Config.md index b54e122c03..9d184dd56a 100644 --- a/docs/reference/utility-apis/Config.md +++ b/docs/reference/utility-apis/Config.md @@ -1,7 +1,7 @@ # Config The Config type is defined at -[packages/config/src/types.ts:32](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/config/src/types.ts#L32). +[packages/config/src/types.ts:32](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/config/src/types.ts#L32). The following Utility API implements this type: [configApiRef](./README.md#config) @@ -140,7 +140,7 @@ export type Config = { Defined at -[packages/config/src/types.ts:32](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/config/src/types.ts#L32). +[packages/config/src/types.ts:32](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/config/src/types.ts#L32). Referenced by: [getConfig](#getconfig), [getOptionalConfig](#getoptionalconfig), [getConfigArray](#getconfigarray), @@ -153,7 +153,7 @@ export type JsonArray = JsonValue[] Defined at -[packages/config/src/types.ts:18](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/config/src/types.ts#L18). +[packages/config/src/types.ts:18](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/config/src/types.ts#L18). Referenced by: [JsonValue](#jsonvalue). @@ -164,7 +164,7 @@ export type JsonObject = { [key in string]?: JsonValue Defined at -[packages/config/src/types.ts:17](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/config/src/types.ts#L17). +[packages/config/src/types.ts:17](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/config/src/types.ts#L17). Referenced by: [JsonValue](#jsonvalue). @@ -181,7 +181,7 @@ export type JsonValue = Defined at -[packages/config/src/types.ts:19](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/config/src/types.ts#L19). +[packages/config/src/types.ts:19](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/config/src/types.ts#L19). Referenced by: [get](#get), [getOptional](#getoptional), [JsonObject](#jsonobject), [JsonArray](#jsonarray), [Config](#config). diff --git a/docs/reference/utility-apis/DiscoveryApi.md b/docs/reference/utility-apis/DiscoveryApi.md index 24371c1729..72f37dc58d 100644 --- a/docs/reference/utility-apis/DiscoveryApi.md +++ b/docs/reference/utility-apis/DiscoveryApi.md @@ -1,7 +1,7 @@ # DiscoveryApi The DiscoveryApi type is defined at -[packages/core-api/src/apis/definitions/DiscoveryApi.ts:30](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/DiscoveryApi.ts#L30). +[packages/core-api/src/apis/definitions/DiscoveryApi.ts:30](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/DiscoveryApi.ts#L30). The following Utility API implements this type: [discoveryApiRef](./README.md#discovery) diff --git a/docs/reference/utility-apis/ErrorApi.md b/docs/reference/utility-apis/ErrorApi.md index 762d2016d5..2234aaa543 100644 --- a/docs/reference/utility-apis/ErrorApi.md +++ b/docs/reference/utility-apis/ErrorApi.md @@ -1,7 +1,7 @@ # ErrorApi The ErrorApi type is defined at -[packages/core-api/src/apis/definitions/ErrorApi.ts:53](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/ErrorApi.ts#L53). +[packages/core-api/src/apis/definitions/ErrorApi.ts:53](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/ErrorApi.ts#L53). The following Utility API implements this type: [errorApiRef](./README.md#error) @@ -41,7 +41,7 @@ type Error = { Defined at -[packages/core-api/src/apis/definitions/ErrorApi.ts:24](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/ErrorApi.ts#L24). +[packages/core-api/src/apis/definitions/ErrorApi.ts:24](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/ErrorApi.ts#L24). Referenced by: [post](#post), [error\$](#error). @@ -58,7 +58,7 @@ export type ErrorContext = { Defined at -[packages/core-api/src/apis/definitions/ErrorApi.ts:33](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/ErrorApi.ts#L33). +[packages/core-api/src/apis/definitions/ErrorApi.ts:33](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/ErrorApi.ts#L33). Referenced by: [post](#post), [error\$](#error). @@ -87,13 +87,13 @@ export type Observable<T> = { Defined at -[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/types.ts#L53). +[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L53). Referenced by: [error\$](#error). ### Observer -This file contains non-react related core types used through Backstage. +This file contains non-react related core types used throughout Backstage. Observer interface for consuming an Observer, see TC39. @@ -106,7 +106,7 @@ export type Observer<T> = { Defined at -[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/types.ts#L24). +[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L24). Referenced by: [Observable](#observable). @@ -129,6 +129,6 @@ export type Subscription = { Defined at -[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/types.ts#L33). +[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L33). Referenced by: [Observable](#observable). diff --git a/docs/reference/utility-apis/FeatureFlagsApi.md b/docs/reference/utility-apis/FeatureFlagsApi.md index 8fbcb794fb..ed04ecfd8d 100644 --- a/docs/reference/utility-apis/FeatureFlagsApi.md +++ b/docs/reference/utility-apis/FeatureFlagsApi.md @@ -1,27 +1,20 @@ # FeatureFlagsApi The FeatureFlagsApi type is defined at -[packages/core-api/src/apis/definitions/FeatureFlagsApi.ts:41](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L41). +[packages/core-api/src/apis/definitions/FeatureFlagsApi.ts:60](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L60). The following Utility API implements this type: [featureFlagsApiRef](./README.md#featureflags) ## Members -### registeredFeatureFlags +### registerFlag() -Store a list of registered feature flags. +Registers a new feature flag. Once a feature flag has been registered it can be +toggled by users, and read back to enable or disable features.
-registeredFeatureFlags: FeatureFlagsRegistryItem[]
-
- -### getFlags() - -Get a list of all feature flags from the current user. - -
-getFlags(): UserFlags
+registerFlag(flag: FeatureFlag): void
 
### getRegisteredFlags() @@ -29,5 +22,92 @@ getFlags(): UserFlags Get a list of all registered flags.
-getRegisteredFlags(): FeatureFlagsRegistry
+getRegisteredFlags(): FeatureFlag[]
 
+ +### isActive() + +Whether the feature flag with the given name is currently activated for the +user. + +
+isActive(name: string): boolean
+
+ +### save() + +Save the user's choice of feature flag states. + +
+save(options: FeatureFlagsSaveOptions): void
+
+ +## Supporting types + +These types are part of the API declaration, but may not be unique to this API. + +### FeatureFlag + +The feature flags API is used to toggle functionality to users across plugins +and Backstage. + +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. + +
+export type FeatureFlag = {
+  name: string;
+  pluginId: string;
+}
+
+ +Defined at +[packages/core-api/src/apis/definitions/FeatureFlagsApi.ts:31](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L31). + +Referenced by: [registerFlag](#registerflag), +[getRegisteredFlags](#getregisteredflags). + +### FeatureFlagState + +
+export enum FeatureFlagState {
+  None = 0,
+  Active = 1,
+}
+
+ +Defined at +[packages/core-api/src/apis/definitions/FeatureFlagsApi.ts:36](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L36). + +Referenced by: [FeatureFlagsSaveOptions](#featureflagssaveoptions). + +### FeatureFlagsSaveOptions + +Options to use when saving feature flags. + +
+export type FeatureFlagsSaveOptions = {
+  /**
+   * The new feature flag states to save.
+   */
+  states: Record<string, FeatureFlagState>;
+
+  /**
+   * Whether the saves states should be merged into the existing ones, or replace them.
+   *
+   * Defaults to false.
+   */
+  merge?: boolean;
+}
+
+ +Defined at +[packages/core-api/src/apis/definitions/FeatureFlagsApi.ts:44](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L44). + +Referenced by: [save](#save). diff --git a/docs/reference/utility-apis/IdentityApi.md b/docs/reference/utility-apis/IdentityApi.md index 5ee5c582b6..51ea146eec 100644 --- a/docs/reference/utility-apis/IdentityApi.md +++ b/docs/reference/utility-apis/IdentityApi.md @@ -1,7 +1,7 @@ # IdentityApi The IdentityApi type is defined at -[packages/core-api/src/apis/definitions/IdentityApi.ts:22](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/IdentityApi.ts#L22). +[packages/core-api/src/apis/definitions/IdentityApi.ts:22](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/IdentityApi.ts#L22). The following Utility API implements this type: [identityApiRef](./README.md#identity) @@ -76,6 +76,6 @@ export type ProfileInfo = { Defined at -[packages/core-api/src/apis/definitions/auth.ts:162](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L162). +[packages/core-api/src/apis/definitions/auth.ts:162](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L162). Referenced by: [getProfile](#getprofile). diff --git a/docs/reference/utility-apis/OAuthApi.md b/docs/reference/utility-apis/OAuthApi.md index a489db76c5..42517410fa 100644 --- a/docs/reference/utility-apis/OAuthApi.md +++ b/docs/reference/utility-apis/OAuthApi.md @@ -1,7 +1,7 @@ # OAuthApi The OAuthApi type is defined at -[packages/core-api/src/apis/definitions/auth.ts:67](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L67). +[packages/core-api/src/apis/definitions/auth.ts:67](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L67). The following Utility APIs implement this type: @@ -82,7 +82,7 @@ export type AuthRequestOptions = { Defined at -[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L40). +[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L40). Referenced by: [getAccessToken](#getaccesstoken). @@ -108,6 +108,6 @@ export type OAuthScope = string | string[] Defined at -[packages/core-api/src/apis/definitions/auth.ts:38](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L38). +[packages/core-api/src/apis/definitions/auth.ts:38](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L38). Referenced by: [getAccessToken](#getaccesstoken). diff --git a/docs/reference/utility-apis/OAuthRequestApi.md b/docs/reference/utility-apis/OAuthRequestApi.md index 1328aabeec..690df9dbff 100644 --- a/docs/reference/utility-apis/OAuthRequestApi.md +++ b/docs/reference/utility-apis/OAuthRequestApi.md @@ -1,7 +1,7 @@ # OAuthRequestApi The OAuthRequestApi type is defined at -[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:99](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L99). +[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:99](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L99). The following Utility API implements this type: [oauthRequestApiRef](./README.md#oauthrequest) @@ -73,7 +73,7 @@ export type AuthProvider = { Defined at -[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:27](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L27). +[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:27](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L27). Referenced by: [AuthRequesterOptions](#authrequesteroptions), [PendingAuthRequest](#pendingauthrequest). @@ -97,7 +97,7 @@ export type AuthRequester<AuthResponse> = ( Defined at -[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:66](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L66). +[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:66](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L66). Referenced by: [createAuthRequester](#createauthrequester). @@ -122,7 +122,7 @@ export type AuthRequesterOptions<AuthResponse> = { Defined at -[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:43](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L43). +[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:43](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L43). Referenced by: [createAuthRequester](#createauthrequester). @@ -151,13 +151,13 @@ export type Observable<T> = { Defined at -[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/types.ts#L53). +[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L53). Referenced by: [authRequest\$](#authrequest). ### Observer -This file contains non-react related core types used through Backstage. +This file contains non-react related core types used throughout Backstage. Observer interface for consuming an Observer, see TC39. @@ -170,7 +170,7 @@ export type Observer<T> = { Defined at -[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/types.ts#L24). +[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L24). Referenced by: [Observable](#observable). @@ -205,7 +205,7 @@ export type PendingAuthRequest = { Defined at -[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:77](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L77). +[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:77](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L77). Referenced by: [authRequest\$](#authrequest). @@ -228,6 +228,6 @@ export type Subscription = { Defined at -[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/types.ts#L33). +[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L33). Referenced by: [Observable](#observable). diff --git a/docs/reference/utility-apis/OpenIdConnectApi.md b/docs/reference/utility-apis/OpenIdConnectApi.md index efd79593d6..11ac589ef1 100644 --- a/docs/reference/utility-apis/OpenIdConnectApi.md +++ b/docs/reference/utility-apis/OpenIdConnectApi.md @@ -1,7 +1,7 @@ # OpenIdConnectApi The OpenIdConnectApi type is defined at -[packages/core-api/src/apis/definitions/auth.ts:99](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L99). +[packages/core-api/src/apis/definitions/auth.ts:99](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L99). The following Utility APIs implement this type: @@ -66,6 +66,6 @@ export type AuthRequestOptions = { Defined at -[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L40). +[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L40). Referenced by: [getIdToken](#getidtoken). diff --git a/docs/reference/utility-apis/ProfileInfoApi.md b/docs/reference/utility-apis/ProfileInfoApi.md index 402b5ba504..c5b2ae456b 100644 --- a/docs/reference/utility-apis/ProfileInfoApi.md +++ b/docs/reference/utility-apis/ProfileInfoApi.md @@ -1,7 +1,7 @@ # ProfileInfoApi The ProfileInfoApi type is defined at -[packages/core-api/src/apis/definitions/auth.ts:117](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L117). +[packages/core-api/src/apis/definitions/auth.ts:117](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L117). The following Utility APIs implement this type: @@ -19,6 +19,8 @@ The following Utility APIs implement this type: - [oktaAuthApiRef](./README.md#oktaauth) +- [samlAuthApiRef](./README.md#samlauth) + ## Members ### getProfile() @@ -65,7 +67,7 @@ export type AuthRequestOptions = { Defined at -[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L40). +[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L40). Referenced by: [getProfile](#getprofile). @@ -93,6 +95,6 @@ export type ProfileInfo = { Defined at -[packages/core-api/src/apis/definitions/auth.ts:162](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L162). +[packages/core-api/src/apis/definitions/auth.ts:162](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L162). Referenced by: [getProfile](#getprofile). diff --git a/docs/reference/utility-apis/README.md b/docs/reference/utility-apis/README.md index 71931a5d5a..84674d9fff 100644 --- a/docs/reference/utility-apis/README.md +++ b/docs/reference/utility-apis/README.md @@ -12,7 +12,7 @@ Used to report alerts and forward them to the app Implemented type: [AlertApi](./AlertApi.md) ApiRef: -[alertApiRef](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/AlertApi.ts#L41) +[alertApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/AlertApi.ts#L41) ### appTheme @@ -21,7 +21,7 @@ API Used to configure the app theme, and enumerate options Implemented type: [AppThemeApi](./AppThemeApi.md) ApiRef: -[appThemeApiRef](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/AppThemeApi.ts#L74) +[appThemeApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/AppThemeApi.ts#L80) ### auth0Auth @@ -32,7 +32,7 @@ Implemented types: [OpenIdConnectApi](./OpenIdConnectApi.md), [BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) ApiRef: -[auth0AuthApiRef](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L275) +[auth0AuthApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L275) ### config @@ -41,7 +41,7 @@ Used to access runtime configuration Implemented type: [Config](./Config.md) ApiRef: -[configApiRef](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/ConfigApi.ts#L22) +[configApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/ConfigApi.ts#L22) ### discovery @@ -50,7 +50,7 @@ Provides service discovery of backend plugins Implemented type: [DiscoveryApi](./DiscoveryApi.md) ApiRef: -[discoveryApiRef](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/DiscoveryApi.ts#L44) +[discoveryApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/DiscoveryApi.ts#L44) ### error @@ -59,7 +59,7 @@ Used to report errors and forward them to the app Implemented type: [ErrorApi](./ErrorApi.md) ApiRef: -[errorApiRef](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/ErrorApi.ts#L65) +[errorApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/ErrorApi.ts#L65) ### featureFlags @@ -68,7 +68,7 @@ Used to toggle functionality in features across Backstage Implemented type: [FeatureFlagsApi](./FeatureFlagsApi.md) ApiRef: -[featureFlagsApiRef](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L58) +[featureFlagsApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L83) ### githubAuth @@ -79,7 +79,7 @@ Implemented types: [OAuthApi](./OAuthApi.md), [BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) ApiRef: -[githubAuthApiRef](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L232) +[githubAuthApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L232) ### gitlabAuth @@ -90,7 +90,7 @@ Implemented types: [OAuthApi](./OAuthApi.md), [BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) ApiRef: -[gitlabAuthApiRef](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L262) +[gitlabAuthApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L262) ### googleAuth @@ -102,7 +102,7 @@ Implemented types: [OAuthApi](./OAuthApi.md), [BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) ApiRef: -[googleAuthApiRef](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L215) +[googleAuthApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L215) ### identity @@ -111,7 +111,7 @@ Provides access to the identity of the signed in user Implemented type: [IdentityApi](./IdentityApi.md) ApiRef: -[identityApiRef](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/IdentityApi.ts#L54) +[identityApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/IdentityApi.ts#L54) ### microsoftAuth @@ -123,7 +123,7 @@ Implemented types: [OAuthApi](./OAuthApi.md), [BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) ApiRef: -[microsoftAuthApiRef](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L289) +[microsoftAuthApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L289) ### oauth2 @@ -135,7 +135,7 @@ Implemented types: [OAuthApi](./OAuthApi.md), [BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) ApiRef: -[oauth2ApiRef](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L303) +[oauth2ApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L303) ### oauthRequest @@ -144,7 +144,7 @@ An API for implementing unified OAuth flows in Backstage Implemented type: [OAuthRequestApi](./OAuthRequestApi.md) ApiRef: -[oauthRequestApiRef](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L130) +[oauthRequestApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L130) ### oktaAuth @@ -156,7 +156,17 @@ Implemented types: [OAuthApi](./OAuthApi.md), [BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) ApiRef: -[oktaAuthApiRef](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L245) +[oktaAuthApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L245) + +### samlAuth + +Example of how to use SAML custom provider + +Implemented types: [ProfileInfoApi](./ProfileInfoApi.md), +[BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) + +ApiRef: +[samlAuthApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L317) ### storage @@ -165,4 +175,4 @@ Provides the ability to store data which is unique to the user Implemented type: [StorageApi](./StorageApi.md) ApiRef: -[storageApiRef](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/StorageApi.ts#L68) +[storageApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/StorageApi.ts#L68) diff --git a/docs/reference/utility-apis/SessionApi.md b/docs/reference/utility-apis/SessionApi.md index e7a9e58c59..f2e9097143 100644 --- a/docs/reference/utility-apis/SessionApi.md +++ b/docs/reference/utility-apis/SessionApi.md @@ -1,7 +1,7 @@ # SessionApi The SessionApi type is defined at -[packages/core-api/src/apis/definitions/auth.ts:190](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L190). +[packages/core-api/src/apis/definitions/auth.ts:190](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L190). The following Utility APIs implement this type: @@ -19,6 +19,8 @@ The following Utility APIs implement this type: - [oktaAuthApiRef](./README.md#oktaauth) +- [samlAuthApiRef](./README.md#samlauth) + ## Members ### signIn() @@ -75,13 +77,13 @@ export type Observable<T> = { Defined at -[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/types.ts#L53). +[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L53). Referenced by: [sessionState\$](#sessionstate). ### Observer -This file contains non-react related core types used through Backstage. +This file contains non-react related core types used throughout Backstage. Observer interface for consuming an Observer, see TC39. @@ -94,7 +96,7 @@ export type Observer<T> = { Defined at -[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/types.ts#L24). +[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L24). Referenced by: [Observable](#observable). @@ -110,7 +112,7 @@ export enum SessionState { Defined at -[packages/core-api/src/apis/definitions/auth.ts:182](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/auth.ts#L182). +[packages/core-api/src/apis/definitions/auth.ts:182](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L182). Referenced by: [sessionState\$](#sessionstate). @@ -133,6 +135,6 @@ export type Subscription = { Defined at -[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/types.ts#L33). +[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L33). Referenced by: [Observable](#observable). diff --git a/docs/reference/utility-apis/StorageApi.md b/docs/reference/utility-apis/StorageApi.md index e7d5131ff9..9fe10e358b 100644 --- a/docs/reference/utility-apis/StorageApi.md +++ b/docs/reference/utility-apis/StorageApi.md @@ -1,7 +1,7 @@ # StorageApi The StorageApi type is defined at -[packages/core-api/src/apis/definitions/StorageApi.ts:31](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/StorageApi.ts#L31). +[packages/core-api/src/apis/definitions/StorageApi.ts:31](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/StorageApi.ts#L31). The following Utility API implements this type: [storageApiRef](./README.md#storage) @@ -79,13 +79,13 @@ export type Observable<T> = { Defined at -[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/types.ts#L53). +[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L53). Referenced by: [observe\$](#observe), [StorageApi](#storageapi). ### Observer -This file contains non-react related core types used through Backstage. +This file contains non-react related core types used throughout Backstage. Observer interface for consuming an Observer, see TC39. @@ -98,7 +98,7 @@ export type Observer<T> = { Defined at -[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/types.ts#L24). +[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L24). Referenced by: [Observable](#observable). @@ -144,7 +144,7 @@ export interface StorageApi { Defined at -[packages/core-api/src/apis/definitions/StorageApi.ts:31](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/StorageApi.ts#L31). +[packages/core-api/src/apis/definitions/StorageApi.ts:31](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/StorageApi.ts#L31). Referenced by: [forBucket](#forbucket). @@ -158,7 +158,7 @@ export type StorageValueChange<T = any> = { Defined at -[packages/core-api/src/apis/definitions/StorageApi.ts:21](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/apis/definitions/StorageApi.ts#L21). +[packages/core-api/src/apis/definitions/StorageApi.ts:21](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/StorageApi.ts#L21). Referenced by: [observe\$](#observe), [StorageApi](#storageapi). @@ -181,6 +181,6 @@ export type Subscription = { Defined at -[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/0406ace29aba7332a98ff9ef9feedd65adc75223/packages/core-api/src/types.ts#L33). +[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L33). Referenced by: [Observable](#observable). From e2b4aea8eb76172fac7cfd018d34b9390998dba7 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Mon, 2 Nov 2020 16:50:14 +0100 Subject: [PATCH 048/252] TechDocs: Remove explicit .keys for dictionaries --- packages/techdocs-container/techdocs-core/src/core.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/techdocs-container/techdocs-core/src/core.py b/packages/techdocs-container/techdocs-core/src/core.py index b78ff24cdf..7d32b69ad8 100644 --- a/packages/techdocs-container/techdocs-core/src/core.py +++ b/packages/techdocs-container/techdocs-core/src/core.py @@ -31,7 +31,7 @@ class TechDocsCore(BasePlugin): ) mdx_configs_override = {} - if "mdx_configs" in config.keys(): + if "mdx_configs" in config: mdx_configs_override = config["mdx_configs"].copy() # Theme @@ -52,10 +52,10 @@ class TechDocsCore(BasePlugin): config["plugins"]["monorepo"] = monorepo_plugin # Markdown Extensions - if "markdown_extensions" not in config.keys(): + if "markdown_extensions" not in config: config["markdown_extensions"] = [] - if "mdx_configs" not in config.keys(): + if "mdx_configs" not in config: config["mdx_configs"] = {} config["markdown_extensions"].append("admonition") From 59166e5ec2b98e6389ba19c1353122c32c150a84 Mon Sep 17 00:00:00 2001 From: Marvin9 Date: Mon, 2 Nov 2020 22:19:56 +0530 Subject: [PATCH 049/252] chore: run changeset --- .changeset/weak-otters-love.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/weak-otters-love.md diff --git a/.changeset/weak-otters-love.md b/.changeset/weak-otters-love.md new file mode 100644 index 0000000000..fb4ac19d3a --- /dev/null +++ b/.changeset/weak-otters-love.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder': minor +'@backstage/plugin-scaffolder-backend': minor +--- + +Scaffolder's API `/v1/jobs` will accept `templateName` instead of `template` Entity. From 240036496013d4cb0c52dce9d47d3548705e7a8f Mon Sep 17 00:00:00 2001 From: Marvin9 Date: Mon, 2 Nov 2020 22:48:44 +0530 Subject: [PATCH 050/252] chore: mention entityClient as an additional option of createRouter --- .changeset/weak-otters-love.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.changeset/weak-otters-love.md b/.changeset/weak-otters-love.md index fb4ac19d3a..2c273e067b 100644 --- a/.changeset/weak-otters-love.md +++ b/.changeset/weak-otters-love.md @@ -3,4 +3,14 @@ '@backstage/plugin-scaffolder-backend': minor --- -Scaffolder's API `/v1/jobs` will accept `templateName` instead of `template` Entity. +`createRouter` of scaffolder backend will now require additional option as `entityClient` which could be generated by `CatalogEntityClient` in `plugin-scaffolder-backend` package. Here is example to generate `entityClient`. + +```js +import { CatalogEntityClient } from '@backstage/plugin-scaffolder-backend'; +import { SingleHostDiscovery } from '@backstage/backend-common'; + +const discovery = SingleHostDiscovery.fromConfig(config); +const entityClient = new CatalogEntityClient({ discovery }); +``` + +- Scaffolder's API `/v1/jobs` will accept `templateName` instead of `template` Entity. From 4a5fcc0e0136f2c338799dd19f90bc628b936d03 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Mon, 2 Nov 2020 12:28:28 -0500 Subject: [PATCH 051/252] Fix wording --- docs/api/utility-apis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/utility-apis.md b/docs/api/utility-apis.md index 1ebf2ae88b..d9a71c92cd 100644 --- a/docs/api/utility-apis.md +++ b/docs/api/utility-apis.md @@ -22,7 +22,7 @@ during their entire life cycle. Each Utility API is tied to an `ApiRef` instance, which is a global singleton object without any additional state or functionality, its only purpose is to -reference Utility APIs. `ApiRef`s are create using `createApiRef`, which is +reference Utility APIs. `ApiRef`s are created using `createApiRef`, which is exported by `@backstage/core`. There are many [predefined Utility APIs](../reference/utility-apis/README.md) defined in `@backstage/core`, and they're all exported with a name of the pattern From 8a497983411af61e74101f016a37c44007fd7f7a Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Mon, 2 Nov 2020 12:29:34 -0500 Subject: [PATCH 052/252] Link to baking, update chapter header, remove old footer --- docs/dls/design.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/dls/design.md b/docs/dls/design.md index fa3a0c159e..3fe9a0adb1 100644 --- a/docs/dls/design.md +++ b/docs/dls/design.md @@ -99,9 +99,10 @@ issues in GitHub with ‘design’ and/or ‘storybook’ - so feel free to brow tackle the tasks that interest you. If you have any questions regarding an issue, you can ask them in the comments section of the issue or on [Discord](https://discord.gg/EBHEGzX). We absolutely adore our external -contributors and will send you virtual semlas for your contributions! +contributors and will send you virtual +[semlas](https://en.wikipedia.org/wiki/Semla) for your contributions! -### Request a component. +### Request a component Create an issue (label it design and assign it to katz95) or send us a message on [Discord](https://discord.gg/EBHEGzX) (_#design_ channel) with details of @@ -135,5 +136,3 @@ contributing to Backstage as a designer is easy. From styling guidelines to UX principles to Figma documents, we’ll make sure you’re equipped to chip in on this project. We’re excited to work with you! In the meantime, we’d love to hear from you on [Discord](https://discord.gg/EBHEGzX). - -[Back to Docs](../README.md) From 9a3786309fa248e7135e76404d41dfe5c9784af4 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Mon, 2 Nov 2020 12:29:47 -0500 Subject: [PATCH 053/252] Remove extraneous footer --- docs/plugins/publishing.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/plugins/publishing.md b/docs/plugins/publishing.md index fbeab6340c..71c24a2c07 100644 --- a/docs/plugins/publishing.md +++ b/docs/plugins/publishing.md @@ -56,5 +56,3 @@ $ git reset --hard master $ yarn release $ git push --force ``` - -[Back to Docs](../README.md) From 1344f481294d03cc940971905bd52deb61552c16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ca=C5=82us?= Date: Mon, 2 Nov 2020 19:00:35 +0100 Subject: [PATCH 054/252] Bump travis-ci plugin version Due to a bug fix, travis-ci plugin version has to be updated. --- packages/app/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/package.json b/packages/app/package.json index f36806b93a..8d1e5a6289 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -36,7 +36,7 @@ "@octokit/rest": "^18.0.0", "@roadiehq/backstage-plugin-github-insights": "^0.2.10", "@roadiehq/backstage-plugin-github-pull-requests": "^0.6.0", - "@roadiehq/backstage-plugin-travis-ci": "^0.2.5", + "@roadiehq/backstage-plugin-travis-ci": "^0.2.6", "history": "^5.0.0", "prop-types": "^15.7.2", "react": "^16.12.0", From 8635aec73449a567ee7f5b59153614ea7ef459f6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 2 Nov 2020 19:16:12 +0100 Subject: [PATCH 055/252] cli: bump @spotify/eslint-* packages to v9 --- packages/cli/package.json | 6 +++--- yarn.lock | 24 ++++++++++++------------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 9f8ad850e0..fdc0acddbf 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -38,9 +38,9 @@ "@rollup/plugin-json": "^4.0.2", "@rollup/plugin-node-resolve": "^9.0.0", "@rollup/plugin-yaml": "^2.1.1", - "@spotify/eslint-config-base": "^8.0.0", - "@spotify/eslint-config-react": "^8.0.0", - "@spotify/eslint-config-typescript": "^8.0.0", + "@spotify/eslint-config-base": "^9.0.0", + "@spotify/eslint-config-react": "^9.0.0", + "@spotify/eslint-config-typescript": "^9.0.0", "@sucrase/webpack-loader": "^2.0.0", "@svgr/plugin-jsx": "5.4.x", "@svgr/plugin-svgo": "5.4.x", diff --git a/yarn.lock b/yarn.lock index 38a37b8ab3..90d2657f0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3812,10 +3812,10 @@ dependencies: "@sinonjs/commons" "^1.7.0" -"@spotify/eslint-config-base@^8.0.0": - version "8.0.0" - resolved "https://registry.npmjs.org/@spotify/eslint-config-base/-/eslint-config-base-8.0.0.tgz#c3d10d8a05ad9129d579952cb80063e4c4fa216e" - integrity sha512-07Fo+KoTMl4AaZJPD6deFnYmG634wzgeqfFcWdFlIrOLAasvWSH8Lp6ZE9cJBKuffOAy8kuEE1SwFo34+5X8bQ== +"@spotify/eslint-config-base@^9.0.0": + version "9.0.0" + resolved "https://registry.npmjs.org/@spotify/eslint-config-base/-/eslint-config-base-9.0.0.tgz#e122b2e6d1a11750170badc5be6774ded95953d2" + integrity sha512-LNGY6bBM0IlosvMIx9Ic5gqatpa5LPvrJhN4RnrUnP9oaXNLgPTKDE89p3eC5Tnh78ADdX4HD+rbHDSyP2tBgg== "@spotify/eslint-config-oss@^1.0.1": version "1.0.2" @@ -3824,15 +3824,15 @@ dependencies: eslint-plugin-notice "^0.9.10" -"@spotify/eslint-config-react@^8.0.0": - version "8.0.2" - resolved "https://registry.npmjs.org/@spotify/eslint-config-react/-/eslint-config-react-8.0.2.tgz#91af3c58b22d49a96795336ed66927e722cda430" - integrity sha512-V0gzO/jWmjhODLINAfeunHj+OBdj/cQeVDw+92ZUlkonAg6BZLjX6Vy8jeIUFjfgmVOtI9eKbcApxaurj1jN6g== +"@spotify/eslint-config-react@^9.0.0": + version "9.0.0" + resolved "https://registry.npmjs.org/@spotify/eslint-config-react/-/eslint-config-react-9.0.0.tgz#9c032e257090a5b3f5b08b28c1311485bf0fbae3" + integrity sha512-3w+cmbUljBLxZGLk4dn1oJD8v4uxW7bKackkcpZhac6VkQmg01EDN04Bx9qmbNe5S/2uHwrOJGcWlge5R8Ml1Q== -"@spotify/eslint-config-typescript@^8.0.0": - version "8.1.1" - resolved "https://registry.npmjs.org/@spotify/eslint-config-typescript/-/eslint-config-typescript-8.1.1.tgz#a8c6f74ef9445fe740008ae3a26003a2eef78f93" - integrity sha512-0NkE6ZiSRf9IBWwCxzn/l7UdwcL/KZsbzjjOSweafyYueajEn+eIpKjOgDGUL/ebxSrOtjEp+YeFc4lSPkP0qg== +"@spotify/eslint-config-typescript@^9.0.0": + version "9.0.0" + resolved "https://registry.npmjs.org/@spotify/eslint-config-typescript/-/eslint-config-typescript-9.0.0.tgz#be68cfaf212599f0bfeb6536c7c58ec05d2b6fba" + integrity sha512-ZsXTwMA68ZCz943U4N8XwprdWcc7ErOO/IW8PewLK5lycCZtLnmRkOvAbae7O5qNJPD8b/l0iUMTLaZuwjXWwg== "@spotify/prettier-config@^8.0.0": version "8.0.0" From 3cbebc9c13dbaed9eb01d821b8d582848bb87fc5 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Mon, 2 Nov 2020 19:20:23 +0100 Subject: [PATCH 056/252] fix: fix edge case of sonarqube plugin if no quality gate was computed Initially no quality gate is available, till the first analysis. Previously this was handled as "failed". Now it has its own state. --- .../SonarQubeCard/SonarQubeCard.tsx | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx b/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx index 8e6cc817a1..677ce4f7eb 100644 --- a/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx +++ b/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx @@ -51,6 +51,9 @@ const useStyles = makeStyles(theme => ({ margin: 0, backgroundColor: theme.palette.success.main, }, + badgeUnknown: { + backgroundColor: theme.palette.grey[500], + }, header: { padding: theme.spacing(2, 2, 2, 2.5), }, @@ -104,17 +107,20 @@ export const SonarQubeCard = ({ } : undefined; - const gatePassed = value && value.metrics.alert_status === 'OK'; - const classes = useStyles(); + let gateLabel = 'Not computed'; + let gateColor = classes.badgeUnknown; + + if (value?.metrics.alert_status) { + const gatePassed = value.metrics.alert_status === 'OK'; + gateLabel = gatePassed ? 'Gate passed' : 'Gate failed'; + gateColor = gatePassed ? classes.badgeSuccess : classes.badgeError; + } const qualityBadge = !loading && value && ( ); From 1249cbb07a46b1d5ff1d3a76c241a663d7b8822f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 3 Nov 2020 00:29:09 +0100 Subject: [PATCH 057/252] cli: disable lint rules that depend on @typescript-eslint v4 --- packages/cli/config/eslint.backend.js | 4 ++++ packages/cli/config/eslint.js | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/packages/cli/config/eslint.backend.js b/packages/cli/config/eslint.backend.js index 2c0b05c196..67f0a49b9f 100644 --- a/packages/cli/config/eslint.backend.js +++ b/packages/cli/config/eslint.backend.js @@ -37,6 +37,10 @@ module.exports = { }, ignorePatterns: ['.eslintrc.js', '**/dist/**', '**/dist-types/**'], rules: { + // TODO(Rugvip): We need to bump @typescript-eslint to v4 to enable these + '@typescript-eslint/no-shadow': 0, + '@typescript-eslint/no-redeclare': 0, + 'no-console': 0, // Permitted in console programs 'new-cap': ['error', { capIsNew: false }], // Because Express constructs things e.g. like 'const r = express.Router()' 'import/newline-after-import': 'error', diff --git a/packages/cli/config/eslint.js b/packages/cli/config/eslint.js index 3e171d1f82..9b1880c1db 100644 --- a/packages/cli/config/eslint.js +++ b/packages/cli/config/eslint.js @@ -41,6 +41,10 @@ module.exports = { }, ignorePatterns: ['.eslintrc.js', '**/dist/**', '**/dist-types/**'], rules: { + // TODO(Rugvip): We need to bump @typescript-eslint to v4 to enable these + '@typescript-eslint/no-shadow': 0, + '@typescript-eslint/no-redeclare': 0, + 'import/newline-after-import': 'error', 'import/no-duplicates': 'warn', 'import/no-extraneous-dependencies': [ From d53ecd6dc6f68817283fe25a720a351504514922 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 3 Nov 2020 00:39:44 +0100 Subject: [PATCH 058/252] core-api: fix outdated eslint ignore --- packages/core-api/src/apis/system/ApiRegistry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-api/src/apis/system/ApiRegistry.ts b/packages/core-api/src/apis/system/ApiRegistry.ts index 14f58268b4..b105633c51 100644 --- a/packages/core-api/src/apis/system/ApiRegistry.ts +++ b/packages/core-api/src/apis/system/ApiRegistry.ts @@ -27,7 +27,7 @@ class ApiRegistryBuilder { } build(): ApiRegistry { - // eslint-disable-next-line no-use-before-define + // eslint-disable-next-line @typescript-eslint/no-use-before-define return new ApiRegistry(new Map(this.apis)); } } From eef09cbd013dbcf02d60bd6a348029b0d04c57ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Nov 2020 04:28:22 +0000 Subject: [PATCH 059/252] chore(deps): bump pg from 8.3.0 to 8.4.2 Bumps [pg](https://github.com/brianc/node-postgres) from 8.3.0 to 8.4.2. - [Release notes](https://github.com/brianc/node-postgres/releases) - [Changelog](https://github.com/brianc/node-postgres/blob/master/CHANGELOG.md) - [Commits](https://github.com/brianc/node-postgres/compare/pg@8.3.0...pg@8.4.2) Signed-off-by: dependabot[bot] --- yarn.lock | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/yarn.lock b/yarn.lock index 38a37b8ab3..a438e1bf05 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18253,11 +18253,16 @@ pg-connection-string@0.1.3, pg-connection-string@^0.1.3: resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-0.1.3.tgz#da1847b20940e42ee1492beaf65d49d91b245df7" integrity sha1-2hhHsglA5C7hSSvq9l1J2RskXfc= -pg-connection-string@2.3.0, pg-connection-string@^2.3.0: +pg-connection-string@2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.3.0.tgz#c13fcb84c298d0bfa9ba12b40dd6c23d946f55d6" integrity sha512-ukMTJXLI7/hZIwTW7hGMZJ0Lj0S2XQBCJ4Shv4y1zgQ/vqVea+FLhzywvPj0ujSuofu+yA4MYHGZPTsgjBgJ+w== +pg-connection-string@^2.3.0, pg-connection-string@^2.4.0: + version "2.4.0" + resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.4.0.tgz#c979922eb47832999a204da5dbe1ebf2341b6a10" + integrity sha512-3iBXuv7XKvxeMrIgym7njT+HlZkwZqqGX4Bu9cci8xHZNT+Um1gWKqCsAzcC0d95rcKMU5WBg6YRUcHyV0HZKQ== + pg-int8@1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" @@ -18271,15 +18276,15 @@ pg-pool@1.*: generic-pool "2.4.3" object-assign "4.1.0" -pg-pool@^3.2.1: - version "3.2.1" - resolved "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.1.tgz#5f4afc0f58063659aeefa952d36af49fa28b30e0" - integrity sha512-BQDPWUeKenVrMMDN9opfns/kZo4lxmSWhIqo+cSAF7+lfi9ZclQbr9vfnlNaPr8wYF3UYjm5X0yPAhbcgqNOdA== +pg-pool@^3.2.2: + version "3.2.2" + resolved "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.2.tgz#a560e433443ed4ad946b84d774b3f22452694dff" + integrity sha512-ORJoFxAlmmros8igi608iVEbQNNZlp89diFVx6yV5v+ehmpMY9sK6QgpmgoXbmkNaBAx8cOOZh9g80kJv1ooyA== -pg-protocol@^1.2.5: - version "1.2.5" - resolved "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.2.5.tgz#28a1492cde11646ff2d2d06bdee42a3ba05f126c" - integrity sha512-1uYCckkuTfzz/FCefvavRywkowa6M5FohNMF5OjKrqo9PSR8gYc8poVmwwYQaBxhmQdBjhtP514eXy9/Us2xKg== +pg-protocol@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.3.0.tgz#3c8fb7ca34dbbfcc42776ce34ac5f537d6e34770" + integrity sha512-64/bYByMrhWULUaCd+6/72c9PMWhiVFs3EVxl9Ct6a3v/U8+rKgqP2w+kKg/BIGgMJyB+Bk/eNivT32Al+Jghw== pg-types@1.*: version "1.13.0" @@ -18318,18 +18323,17 @@ pg@^6.1.0: semver "4.3.2" pg@^8.3.0: - version "8.3.0" - resolved "https://registry.npmjs.org/pg/-/pg-8.3.0.tgz#941383300d38eef51ecb88a0188cec441ab64d81" - integrity sha512-jQPKWHWxbI09s/Z9aUvoTbvGgoj98AU7FDCcQ7kdejupn/TcNpx56v2gaOTzXkzOajmOEJEdi9eTh9cA2RVAjQ== + version "8.4.2" + resolved "https://registry.npmjs.org/pg/-/pg-8.4.2.tgz#2aa58166a23391e91d56a7ea57c6d99931c0642a" + integrity sha512-E9FlUrrc7w3+sbRmL1CSw99vifACzB2TjhMM9J5w9D1LIg+6un0jKkpHS1EQf2CWhKhec2bhrBLVMmUBDbjPRQ== dependencies: buffer-writer "2.0.0" packet-reader "1.0.0" - pg-connection-string "^2.3.0" - pg-pool "^3.2.1" - pg-protocol "^1.2.5" + pg-connection-string "^2.4.0" + pg-pool "^3.2.2" + pg-protocol "^1.3.0" pg-types "^2.1.0" pgpass "1.x" - semver "4.3.2" pgpass@1.*, pgpass@1.x: version "1.0.2" From 7c12fc656f1c358661c4ad6d47585f1f1c0d3360 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 3 Nov 2020 10:34:10 +0100 Subject: [PATCH 060/252] Update CODEOWNERS to fit new org --- .github/CODEOWNERS | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d161a8e1a9..7cce08798a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,14 +4,14 @@ # The last matching pattern takes precedence. # https://help.github.com/articles/about-codeowners/ -* @spotify/backstage-core -/docs/features/techdocs @spotify/techdocs-core -/plugins/cost-insights @spotify/silver-lining +* @backstage/maintainers +/docs/features/techdocs @backstage/techdocs-core +/plugins/cost-insights @backstage/silver-lining /plugins/cloudbuild @trivago/ebarrios -/plugins/techdocs @spotify/techdocs-core -/plugins/techdocs-backend @spotify/techdocs-core -/packages/techdocs-cli @spotify/techdocs-core -/packages/techdocs-container @spotify/techdocs-core -/.github/workflows/techdocs.yml @spotify/techdocs-core -/.github/workflows/techdocs-pypi.yml @spotify/techdocs-core -/.changeset/cost-insights-* @spotify/silver-lining +/plugins/techdocs @backstage/techdocs-core +/plugins/techdocs-backend @backstage/techdocs-core +/packages/techdocs-cli @backstage/techdocs-core +/packages/techdocs-container @backstage/techdocs-core +/.github/workflows/techdocs.yml @backstage/techdocs-core +/.github/workflows/techdocs-pypi.yml @backstage/techdocs-core +/.changeset/cost-insights-* @backstage/silver-lining From 4703305bc6b46b9c497fb53a9e97f3888a536773 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 3 Nov 2020 10:51:43 +0100 Subject: [PATCH 061/252] Release new version of mkdocs-techdocs-core (#3207) --- packages/techdocs-container/techdocs-core/README.md | 4 ++++ packages/techdocs-container/techdocs-core/setup.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/techdocs-container/techdocs-core/README.md b/packages/techdocs-container/techdocs-core/README.md index 423a076cbf..7bd6fe4187 100644 --- a/packages/techdocs-container/techdocs-core/README.md +++ b/packages/techdocs-container/techdocs-core/README.md @@ -82,6 +82,10 @@ Extensions: ## Changelog +### 0.0.11 + +- Any MkDocs plugin configurations from mkdocs.yml will now work and override the default configuration. See https://github.com/spotify/backstage/issues/3017 + ### 0.0.10 - Pin Markdown version to fix issue with Graphviz diff --git a/packages/techdocs-container/techdocs-core/setup.py b/packages/techdocs-container/techdocs-core/setup.py index 44ff653d72..b70805a3d0 100644 --- a/packages/techdocs-container/techdocs-core/setup.py +++ b/packages/techdocs-container/techdocs-core/setup.py @@ -23,7 +23,7 @@ with open(path.join(this_dir, "README.md"), encoding="utf-8") as file: setup( name="mkdocs-techdocs-core", - version="0.0.10", + version="0.0.11", description="A Mkdocs package that contains TechDocs defaults", long_description=long_description, long_description_content_type="text/markdown", From af141c0766db5536045a2f849d43214f151f9430 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 3 Nov 2020 10:37:24 +0100 Subject: [PATCH 062/252] rewrite repo location urls --- .changeset/3066.md | 2 +- .github/PULL_REQUEST_TEMPLATE.md | 2 +- .github/workflows/techdocs-project-board.yml | 10 +- CHANGELOG.md | 58 ++++----- CONTRIBUTING.md | 18 +-- README.md | 10 +- app-config.yaml | 12 +- catalog-info.yaml | 2 +- contrib/chart/backstage/Chart.yaml | 2 +- contrib/chart/backstage/README.md | 2 +- .../files/app-config.development.yaml.tpl | 10 +- docs/FAQ.md | 30 ++--- .../adr002-default-catalog-file-format.md | 4 +- ...adr007-use-msw-to-mock-service-requests.md | 2 +- .../adr008-default-catalog-file-name.md | 2 +- .../adr009-entity-references.md | 2 +- docs/architecture-decisions/index.md | 4 +- docs/auth/index.md | 2 +- docs/auth/oauth.md | 8 +- docs/conf/reading.md | 4 +- .../software-catalog/configuration.md | 2 +- .../software-catalog/extending-the-model.md | 2 +- docs/features/software-catalog/index.md | 8 +- .../features/software-catalog/installation.md | 18 +-- .../features/software-catalog/system-model.md | 4 +- .../well-known-annotations.md | 4 +- .../extending/create-your-own-preparer.md | 6 +- .../extending/create-your-own-publisher.md | 4 +- .../extending/create-your-own-templater.md | 4 +- .../software-templates/extending/index.md | 2 +- .../software-templates/installation.md | 6 +- docs/features/techdocs/FAQ.md | 4 +- docs/features/techdocs/README.md | 16 +-- docs/features/techdocs/concepts.md | 12 +- docs/getting-started/app-custom-theme.md | 2 +- docs/getting-started/deployment-helm.md | 6 +- docs/getting-started/deployment-k8s.md | 2 +- .../development-environment.md | 4 +- docs/getting-started/index.md | 2 +- .../running-backstage-locally.md | 10 +- docs/openapi/definitions/auth.yaml | 4 +- docs/overview/architecture-overview.md | 10 +- docs/overview/logos.md | 2 +- docs/overview/roadmap.md | 50 ++++---- docs/overview/what-is-backstage.md | 2 +- docs/plugins/add-to-marketplace.md | 2 +- docs/plugins/call-existing-api.md | 2 +- docs/plugins/index.md | 2 +- docs/plugins/publishing.md | 2 +- docs/plugins/structure-of-a-plugin.md | 2 +- docs/reference/utility-apis/AlertApi.md | 10 +- docs/reference/utility-apis/AppThemeApi.md | 20 ++-- .../utility-apis/BackstageIdentityApi.md | 6 +- docs/reference/utility-apis/Config.md | 10 +- docs/reference/utility-apis/DiscoveryApi.md | 2 +- docs/reference/utility-apis/ErrorApi.md | 12 +- .../reference/utility-apis/FeatureFlagsApi.md | 8 +- docs/reference/utility-apis/IdentityApi.md | 4 +- docs/reference/utility-apis/OAuthApi.md | 6 +- .../reference/utility-apis/OAuthRequestApi.md | 16 +-- .../utility-apis/OpenIdConnectApi.md | 4 +- docs/reference/utility-apis/ProfileInfoApi.md | 6 +- docs/reference/utility-apis/README.md | 36 +++--- docs/reference/utility-apis/SessionApi.md | 10 +- .../reference/utility-apis/SessionStateApi.md | 10 +- docs/reference/utility-apis/StorageApi.md | 12 +- docs/support/project-structure.md | 113 +++++++++--------- docs/support/support.md | 4 +- docs/tutorials/quickstart-app-plugin.md | 6 +- .../blog/2020-03-18-what-is-backstage.md | 4 +- .../blog/2020-04-06-lighthouse-plugin.md | 2 +- ...0-04-30-how-to-quickly-set-up-backstage.md | 2 +- .../blog/2020-05-14-tech-radar-plugin.md | 4 +- .../2020-05-22-phase-2-service-catalog.md | 10 +- ...0-06-22-backstage-service-catalog-alpha.md | 4 +- ...hentication-in-backstage-using-passport.md | 4 +- ...announcing-backstage-software-templates.md | 2 +- .../blog/2020-09-08-announcing-tech-docs.md | 6 +- .../blog/2020-09-23-backstage-cncf-sandbox.md | 2 +- .../2020-09-30-backstage-design-system.md | 4 +- .../blog/2020-09-30-plugin-marketplace.md | 4 +- .../blog/2020-10-22-cost-insights-plugin.md | 8 +- microsite/core/Footer.js | 2 +- microsite/data/plugins/api-docs.yaml | 2 +- microsite/data/plugins/circleci.yaml | 2 +- microsite/data/plugins/cloud-build.yaml | 2 +- microsite/data/plugins/cost-insights.yaml | 2 +- microsite/data/plugins/gcp-projects.yaml | 2 +- microsite/data/plugins/github-actions.yaml | 2 +- microsite/data/plugins/gitops-cluster.yaml | 2 +- microsite/data/plugins/graphiql.yaml | 2 +- microsite/data/plugins/jenkins.yaml | 2 +- microsite/data/plugins/lighthouse.yaml | 2 +- microsite/data/plugins/new-relic.yaml | 2 +- microsite/data/plugins/rollbar.yaml | 2 +- microsite/data/plugins/sentry.yaml | 2 +- microsite/data/plugins/sonarqube.yaml | 2 +- microsite/data/plugins/tech-radar.yaml | 2 +- microsite/pages/en/index.js | 4 +- microsite/pages/en/plugins.js | 4 +- microsite/siteConfig.js | 6 +- packages/backend-common/README.md | 4 +- packages/backend-common/package.json | 2 +- packages/backend/README.md | 6 +- packages/catalog-model/README.md | 4 +- .../catalog-model/examples/acme-corp.yaml | 2 +- packages/catalog-model/examples/acme/org.yaml | 14 +-- packages/catalog-model/examples/all-apis.yaml | 10 +- .../examples/all-components.yaml | 18 +-- .../policies/FieldFormatEntityPolicy.ts | 2 +- .../src/kinds/LocationEntityV1alpha1.test.ts | 6 +- packages/cli-common/README.md | 4 +- packages/cli-common/package.json | 2 +- packages/cli/README.md | 4 +- packages/cli/package.json | 2 +- packages/config-loader/README.md | 4 +- packages/config-loader/package.json | 2 +- packages/config/README.md | 4 +- packages/config/package.json | 2 +- packages/core-api/README.md | 4 +- packages/core-api/package.json | 2 +- packages/core/README.md | 4 +- packages/core/package.json | 2 +- .../core/src/layout/ErrorPage/ErrorPage.tsx | 2 +- packages/create-app/README.md | 4 +- packages/create-app/package.json | 2 +- .../templates/default-app/app-config.yaml.hbs | 12 +- .../default-app/catalog-info.yaml.hbs | 10 +- .../default-app/packages/backend/README.md | 8 +- packages/dev-utils/README.md | 4 +- packages/dev-utils/package.json | 2 +- packages/docgen/package.json | 2 +- packages/docgen/src/docgen/ApiDocsPrinter.ts | 2 +- .../src/docgen/GitHubMarkdownPrinter.ts | 2 +- .../src/docgen/TechdocsMarkdownPrinter.ts | 2 +- packages/e2e-test/README.md | 4 +- packages/e2e-test/package.json | 2 +- .../webpack-plugin-fail-build-on-warning.js | 2 +- packages/techdocs-cli/README.md | 4 +- packages/techdocs-cli/package.json | 2 +- packages/techdocs-container/README.md | 4 +- .../techdocs-container/techdocs-core/setup.py | 2 +- packages/test-utils-core/README.md | 4 +- packages/test-utils-core/package.json | 2 +- packages/test-utils/README.md | 4 +- packages/test-utils/package.json | 2 +- packages/theme/README.md | 4 +- packages/theme/package.json | 2 +- plugins/README.md | 6 +- plugins/auth-backend/README.md | 2 +- plugins/catalog-backend/README.md | 2 +- .../processors/CodeOwnersProcessor.test.ts | 14 ++- .../processors/PlaceholderProcessor.test.ts | 30 +++-- plugins/catalog/README.md | 2 +- .../components/AboutCard/AboutCard.test.tsx | 4 +- .../src/components/AboutCard/AboutCard.tsx | 2 +- plugins/cost-insights/README.md | 6 +- plugins/github-actions/examples/sample.yaml | 2 +- plugins/graphiql/package.json | 4 +- plugins/lighthouse/README.md | 6 +- .../lighthouse/src/components/Intro/index.tsx | 4 +- plugins/rollbar-backend/README.md | 2 +- plugins/rollbar/README.md | 4 +- .../sample-templates/all-templates.yaml | 8 +- plugins/scaffolder/README.md | 2 +- plugins/techdocs-backend/README.md | 4 +- .../documented-component.yaml | 2 +- .../techdocs/stages/prepare/commonGit.test.ts | 2 +- .../src/techdocs/stages/prepare/dir.test.ts | 2 +- .../components/WelcomePage/WelcomePage.tsx | 8 +- 170 files changed, 549 insertions(+), 542 deletions(-) diff --git a/.changeset/3066.md b/.changeset/3066.md index 7bb67e70d1..7c157c94c3 100644 --- a/.changeset/3066.md +++ b/.changeset/3066.md @@ -6,7 +6,7 @@ Filters passed to the `/entities` endpoint of the catalog has changed format. The old way was to pass things on the form `?a=b&c=d`; the new way is to pass things on the form `?filter=a=b,c=d`. See discussion in -[#2910](https://github.com/spotify/backstage/issues/2910) for details. +[#2910](https://github.com/backstage/backstage/issues/2910) for details. The comma separated items within a single filter have an AND between them. If multiple such filters are passed, they have an OR between those item groups. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index de7ce773bb..d68af23491 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -7,7 +7,7 @@ -- [ ] A changeset describing the change and affected packages. ([more info](https://github.com/spotify/backstage/blob/master/CONTRIBUTING.md#creating-changesets)) +- [ ] A changeset describing the change and affected packages. ([more info](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md#creating-changesets)) - [ ] Added or updated documentation - [ ] Tests for new functionality and regression tests for bug fixes - [ ] Screenshots attached (for UI changes) diff --git a/.github/workflows/techdocs-project-board.yml b/.github/workflows/techdocs-project-board.yml index b389cf6bf5..a0691f2a79 100644 --- a/.github/workflows/techdocs-project-board.yml +++ b/.github/workflows/techdocs-project-board.yml @@ -1,5 +1,5 @@ name: Automatically add new TechDocs Issues and PRs to the GitHub project board -# Development of TechDocs in Backstage is managed by this Kanban board - https://github.com/spotify/backstage/projects/5 +# Development of TechDocs in Backstage is managed by this Kanban board - https://github.com/backstage/backstage/projects/5 # New issues with TechDocs in their title or docs-like-code label will be added to the board. on: @@ -23,7 +23,7 @@ jobs: contains(github.event.issue.title, 'techdocs') || contains(github.event.issue.title, 'Techdocs') with: - project: 'https://github.com/spotify/backstage/projects/5' + project: 'https://github.com/backstage/backstage/projects/5' column_name: 'Incoming' - name: Assign new issue to Incoming based on its label. @@ -31,7 +31,7 @@ jobs: if: | contains(github.event.issue.labels.*.name, 'docs-like-code') with: - project: 'https://github.com/spotify/backstage/projects/5' + project: 'https://github.com/backstage/backstage/projects/5' column_name: 'Incoming' - name: Assign new PR to Incoming based on its title. @@ -41,7 +41,7 @@ jobs: contains(github.event.pull_request.title, 'techdocs') || contains(github.event.pull_request.title, 'Techdocs') with: - project: 'https://github.com/spotify/backstage/projects/5' + project: 'https://github.com/backstage/backstage/projects/5' column_name: 'Incoming' - name: Assign new PR to Incoming based on its label. @@ -49,5 +49,5 @@ jobs: if: | contains(github.event.pull_request.labels.*.name, 'docs-like-code') with: - project: 'https://github.com/spotify/backstage/projects/5' + project: 'https://github.com/backstage/backstage/projects/5' column_name: 'Incoming' diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ab3459628..1ac33ca7f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ This is a best-effort changelog where we manually collect breaking changes. It is not an exhaustive list of all changes or even features added. -If you encounter issues while upgrading to a newer version, don't hesitate to reach out on [Discord](https://discord.gg/EBHEGzX) or [open an issue](https://github.com/spotify/backstage/issues/new/choose)! +If you encounter issues while upgrading to a newer version, don't hesitate to reach out on [Discord](https://discord.gg/EBHEGzX) or [open an issue](https://github.com/backstage/backstage/issues/new/choose)! ## Next Release @@ -26,18 +26,18 @@ If you encounter issues while upgrading to a newer version, don't hesitate to re ### Backend (example-backend, or backends created with @backstage/create-app) -- A plugin database manager has been created, and plugins can now accept that interface as an argument during initialisation. Notably, the `auth` plugin has a [`createRouter` signature change](./plugins/auth-backend/src/service/router.ts). See [packages/backend/src/index.ts](./packages/backend/src/index.ts) on how to set it up. [#2697](https://github.com/spotify/backstage/pull/2697) +- A plugin database manager has been created, and plugins can now accept that interface as an argument during initialisation. Notably, the `auth` plugin has a [`createRouter` signature change](./plugins/auth-backend/src/service/router.ts). See [packages/backend/src/index.ts](./packages/backend/src/index.ts) on how to set it up. [#2697](https://github.com/backstage/backstage/pull/2697) ## v0.1.1-alpha.24 ### Backend (example-backend, or backends created with @backstage/create-app) -- The default mount point for backend plugins have been changed to `/api`. These changes are done in the backend package itself, so it is recommended that you sync up existing backend packages with this new pattern. [#2562](https://github.com/spotify/backstage/pull/2562) -- A service discovery mechanism for backend plugins has been added, and is now a requirement for several backend plugins. See [packages/backend/src/index.ts](./packages/backend/src/index.ts) for how to set it up using `SingleHostDiscovery` from `@backstage/backend-common`. Note that the default base path for plugins is set to `/api` to that change, but it can be set to use the old behavior via the `basePath` option. [#2600](https://github.com/spotify/backstage/pull/2600) +- The default mount point for backend plugins have been changed to `/api`. These changes are done in the backend package itself, so it is recommended that you sync up existing backend packages with this new pattern. [#2562](https://github.com/backstage/backstage/pull/2562) +- A service discovery mechanism for backend plugins has been added, and is now a requirement for several backend plugins. See [packages/backend/src/index.ts](./packages/backend/src/index.ts) for how to set it up using `SingleHostDiscovery` from `@backstage/backend-common`. Note that the default base path for plugins is set to `/api` to that change, but it can be set to use the old behavior via the `basePath` option. [#2600](https://github.com/backstage/backstage/pull/2600) ### @backstage/auth-backend -- The default mount path of backend plugins was changed to `/api/:pluginId`, and as part of that it was needed to enable configuration of the base path of the auth backend, so that it can construct redirect URLs correctly. Note that you will also need to reconfigure any allowed redirect URLs to include `/api` if you switch to the new recommended pattern. [#2562](https://github.com/spotify/backstage/pull/2562) +- The default mount path of backend plugins was changed to `/api/:pluginId`, and as part of that it was needed to enable configuration of the base path of the auth backend, so that it can construct redirect URLs correctly. Note that you will also need to reconfigure any allowed redirect URLs to include `/api` if you switch to the new recommended pattern. [#2562](https://github.com/backstage/backstage/pull/2562) - The auth backend now requires an implementation of `PluginEndpointDiscovery` from `@backstage/backend-common` to be passed in as `discovery`. See the changes to `@backstage/backend`. ### @backstage/proxy-backend @@ -50,28 +50,28 @@ If you encounter issues while upgrading to a newer version, don't hesitate to re ### @backstage/plugin-identity-backend -- This plugin was removed, remove it from your backend if it's there. [#2616](https://github.com/spotify/backstage/pull/2616) +- This plugin was removed, remove it from your backend if it's there. [#2616](https://github.com/backstage/backstage/pull/2616) ## v0.1.1-alpha.23 ### @backstage/core -- Renamed `SessionStateApi` to `SessionApi` and `logout` to `signOut`. Custom implementations of the `SingInPage` app-component will need to rename their `logout` function. The different auth provider items for the `UserSettingsMenu` have been consolidated into a single `ProviderSettingsItem`, meaning you need to replace existing usages of `OAuthProviderSettings` and `OIDCProviderSettings`. [#2555](https://github.com/spotify/backstage/pull/2555). +- Renamed `SessionStateApi` to `SessionApi` and `logout` to `signOut`. Custom implementations of the `SingInPage` app-component will need to rename their `logout` function. The different auth provider items for the `UserSettingsMenu` have been consolidated into a single `ProviderSettingsItem`, meaning you need to replace existing usages of `OAuthProviderSettings` and `OIDCProviderSettings`. [#2555](https://github.com/backstage/backstage/pull/2555). ## v0.1.1-alpha.22 ### @backstage/core -- Introduced initial version of an inverted app/plugin relationship, where plugins export components for apps to use, instead registering themselves directly into the app. This enables more fine-grained control of plugin features, and also composition of plugins such as catalog pages with additional cards and tabs. This breaks the use of `RouteRef`s, and there will be more changes related to this in the future, but this change lays the initial foundation. See `packages/app` and followup PRs for how to update plugins for this change. [#2076](https://github.com/spotify/backstage/pull/2076) -- Switch to an automatic dependency injection mechanism for all Utility APIs, allowing plugins to ship default implementations of their APIs. See [https://backstage.io/docs/api/utility-apis](https://backstage.io/docs/api/utility-apis). [#2285](https://github.com/spotify/backstage/pull/2285) +- Introduced initial version of an inverted app/plugin relationship, where plugins export components for apps to use, instead registering themselves directly into the app. This enables more fine-grained control of plugin features, and also composition of plugins such as catalog pages with additional cards and tabs. This breaks the use of `RouteRef`s, and there will be more changes related to this in the future, but this change lays the initial foundation. See `packages/app` and followup PRs for how to update plugins for this change. [#2076](https://github.com/backstage/backstage/pull/2076) +- Switch to an automatic dependency injection mechanism for all Utility APIs, allowing plugins to ship default implementations of their APIs. See [https://backstage.io/docs/api/utility-apis](https://backstage.io/docs/api/utility-apis). [#2285](https://github.com/backstage/backstage/pull/2285) ### @backstage/cli -- Change `backstage-cli backend:build-image` to forward all args to `docker image build`, instead of just tag. Also add `--build` flag for building all dependent packages before packaging the workspace for the docker build. [#2299](https://github.com/spotify/backstage/pull/2299) +- Change `backstage-cli backend:build-image` to forward all args to `docker image build`, instead of just tag. Also add `--build` flag for building all dependent packages before packaging the workspace for the docker build. [#2299](https://github.com/backstage/backstage/pull/2299) ### @backstage/create-app -- Change root `tsc` output dir to `dist-types`, in order to allow for standalone plugin repos. [#2278](https://github.com/spotify/backstage/pull/2278) +- Change root `tsc` output dir to `dist-types`, in order to allow for standalone plugin repos. [#2278](https://github.com/backstage/backstage/pull/2278) ### @backstage/catalog-backend @@ -79,7 +79,7 @@ If you encounter issues while upgrading to a newer version, don't hesitate to re ## v0.1.1-alpha.21 -- Added many more frontend plugins to the template along with the sidebar. [#1942](https://github.com/spotify/backstage/pull/1942), [#2084](https://github.com/spotify/backstage/pull/2084) +- Added many more frontend plugins to the template along with the sidebar. [#1942](https://github.com/backstage/backstage/pull/1942), [#2084](https://github.com/backstage/backstage/pull/2084) ### @backstage/core @@ -90,14 +90,14 @@ If you encounter issues while upgrading to a newer version, don't hesitate to re ### @backstage/cli -- Set `NODE_ENV` to `test` when running test. [#2214](https://github.com/spotify/backstage/pull/2214) +- Set `NODE_ENV` to `test` when running test. [#2214](https://github.com/backstage/backstage/pull/2214) -- Fix for backend plugins names requiring to be prefixed with `@backstage` to build. [#2224](https://github.com/spotify/backstage/pull/2224) +- Fix for backend plugins names requiring to be prefixed with `@backstage` to build. [#2224](https://github.com/backstage/backstage/pull/2224) ### @backstage/backend-common - The backend plugin - [service builder](https://github.com/spotify/backstage/blob/master/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts) + [service builder](https://github.com/backstage/backstage/blob/master/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts) no longer adds `express.json()` automatically to all routes. While convenient in a lot of cases, it also led to problems where for example the proxy middleware could hang because the body had already been altered and could not @@ -107,61 +107,61 @@ If you encounter issues while upgrading to a newer version, don't hesitate to re ### @backstage/catalog-backend -- Add rules configuration for catalog location and entity kinds. The default rules should cover most use-cases, but you may need to allow specific entity kinds when using things like Template or Group entities. [#2118](https://github.com/spotify/backstage/pull/2118) +- Add rules configuration for catalog location and entity kinds. The default rules should cover most use-cases, but you may need to allow specific entity kinds when using things like Template or Group entities. [#2118](https://github.com/backstage/backstage/pull/2118) ## v0.1.1-alpha.20 ### @backstage/cli -- Use config files according to `NODE_ENV` when serving and building frontend packages. [#2077](https://github.com/spotify/backstage/pull/2077) +- Use config files according to `NODE_ENV` when serving and building frontend packages. [#2077](https://github.com/backstage/backstage/pull/2077) -- Pin `rollup-plugin-dts` to avoid a later broken version. [#2097](https://github.com/spotify/backstage/pull/2097) +- Pin `rollup-plugin-dts` to avoid a later broken version. [#2097](https://github.com/backstage/backstage/pull/2097) ## v0.1.1-alpha.19 ### @backstage/backend-common -- Allow listen host and port to be configured separately, in order to support PORT environment variables. [#1950](https://github.com/spotify/backstage/pull/1950) +- Allow listen host and port to be configured separately, in order to support PORT environment variables. [#1950](https://github.com/backstage/backstage/pull/1950) ### @backstage/core -- Added new `DiscoveryApi` for discovering backend endpoint in the frontend, and use in most plugins. See [packages/app/src/apis.ts](https://github.com/spotify/backstage/blob/master/packages/app/src/apis.ts) for how to register in your app. [#2074](https://github.com/spotify/backstage/pull/2074) +- Added new `DiscoveryApi` for discovering backend endpoint in the frontend, and use in most plugins. See [packages/app/src/apis.ts](https://github.com/backstage/backstage/blob/master/packages/app/src/apis.ts) for how to register in your app. [#2074](https://github.com/backstage/backstage/pull/2074) ### @backstage/create-app -- Added catalog and scaffolder frontend plugins to the template along with the sidebar. [#1942](https://github.com/spotify/backstage/pull/1942), [#2084](https://github.com/spotify/backstage/pull/2084) -- Many plugins have been added to the catalog and will for now be required to be added to separate apps as well. This will be solved as [#1536](https://github.com/spotify/backstage/issues/1536) gets sorted out, but for now you may need to install some plugins just to get pages to work. +- Added catalog and scaffolder frontend plugins to the template along with the sidebar. [#1942](https://github.com/backstage/backstage/pull/1942), [#2084](https://github.com/backstage/backstage/pull/2084) +- Many plugins have been added to the catalog and will for now be required to be added to separate apps as well. This will be solved as [#1536](https://github.com/backstage/backstage/issues/1536) gets sorted out, but for now you may need to install some plugins just to get pages to work. ### @backstage/catalog-backend -- Added the possibility to add static locations via `app-config.yaml`. This changed the signature of `new LocationReaders(logger)` inside `packages/backend/src/plugins/catalog.ts` to `new LocationReaders({config, logger})`. [#1890](https://github.com/spotify/backstage/pull/1890) +- Added the possibility to add static locations via `app-config.yaml`. This changed the signature of `new LocationReaders(logger)` inside `packages/backend/src/plugins/catalog.ts` to `new LocationReaders({config, logger})`. [#1890](https://github.com/backstage/backstage/pull/1890) ### @backstage/theme -- Changed the type signature of the palette, removing `sidebar: string` and adding `navigation: { background: string; indicator: string}`. [#1880](https://github.com/spotify/backstage/pull/1880) +- Changed the type signature of the palette, removing `sidebar: string` and adding `navigation: { background: string; indicator: string}`. [#1880](https://github.com/backstage/backstage/pull/1880) ## v0.1.1-alpha.18 ### @backstage/catalog-backend -- Fixed an issue with duplicated location logs. Applying the database migrations from this fix will clear the existing migration logs. [#1836](https://github.com/spotify/backstage/pull/1836) +- Fixed an issue with duplicated location logs. Applying the database migrations from this fix will clear the existing migration logs. [#1836](https://github.com/backstage/backstage/pull/1836) ### @backstage/auth-backend This version fixes a breakage in CSP policies set by the auth backend. If you're facing trouble with auth in alpha.17, upgrade to alpha.18. -- OAuth redirect URLs no longer receive the `env` parameter, as it is now passed through state instead. This will likely require a reconfiguration of the OAuth app, where a redirect URL like `http://localhost:7000/auth/google/handler/frame?env=development` should now be configured as `http://localhost:7000/auth/google/handler/frame`. [#1812](https://github.com/spotify/backstage/pull/1812) +- OAuth redirect URLs no longer receive the `env` parameter, as it is now passed through state instead. This will likely require a reconfiguration of the OAuth app, where a redirect URL like `http://localhost:7000/auth/google/handler/frame?env=development` should now be configured as `http://localhost:7000/auth/google/handler/frame`. [#1812](https://github.com/backstage/backstage/pull/1812) ### @backstage/core -- `SignInPage` props have been changed to receive a list of provider objects instead of simple string identifiers for all but the `'guest'` and `'custom'` providers. This opens up for configuration of custom providers, but may break existing configurations. See [packages/app/src/App.tsx](https://github.com/spotify/backstage/blob/032ba401af36a760efdac41668d7000ccf09bc57/packages/app/src/App.tsx#L36) and [packages/app/src/identityProviders.ts](https://github.com/spotify/backstage/blob/032ba401af36a760efdac41668d7000ccf09bc57/packages/app/src/identityProviders.ts#L24) for how to bring back the existing providers. [#1816](https://github.com/spotify/backstage/pull/1816) +- `SignInPage` props have been changed to receive a list of provider objects instead of simple string identifiers for all but the `'guest'` and `'custom'` providers. This opens up for configuration of custom providers, but may break existing configurations. See [packages/app/src/App.tsx](https://github.com/backstage/backstage/blob/032ba401af36a760efdac41668d7000ccf09bc57/packages/app/src/App.tsx#L36) and [packages/app/src/identityProviders.ts](https://github.com/backstage/backstage/blob/032ba401af36a760efdac41668d7000ccf09bc57/packages/app/src/identityProviders.ts#L24) for how to bring back the existing providers. [#1816](https://github.com/backstage/backstage/pull/1816) ## v0.1.1-alpha.17 ### @backstage/techdocs-backend -- The techdocs backend now requires more configuration to be supplied when creating the router. See [packages/backend/src/plugins/techdocs.ts](https://github.com/spotify/backstage/blob/0201fd9b4a52429519dd59e9184106ba69456deb/packages/backend/src/plugins/techdocs.ts#L42) for an example. [#1736](https://github.com/spotify/backstage/pull/1736) +- The techdocs backend now requires more configuration to be supplied when creating the router. See [packages/backend/src/plugins/techdocs.ts](https://github.com/backstage/backstage/blob/0201fd9b4a52429519dd59e9184106ba69456deb/packages/backend/src/plugins/techdocs.ts#L42) for an example. [#1736](https://github.com/backstage/backstage/pull/1736) ### @backstage/cli -- The `create-app` command was moved out from the CLI to a standalone package. It's now invoked with `npx @backstage/create-app` instead. [#1745](https://github.com/spotify/backstage/pull/1745) +- The `create-app` command was moved out from the CLI to a standalone package. It's now invoked with `npx @backstage/create-app` instead. [#1745](https://github.com/backstage/backstage/pull/1745) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7edbc6d17b..b7157d9f05 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,11 +12,11 @@ Backstage is released under the Apache2.0 License, and original creations contri ## Report bugs -No one likes bugs. Report bugs as an issue [here](https://github.com/spotify/backstage/issues/new?template=bug_template.md). +No one likes bugs. Report bugs as an issue [here](https://github.com/backstage/backstage/issues/new?template=bug_template.md). ## Fix bugs or build new features -Look through the GitHub issues for [bugs](https://github.com/spotify/backstage/labels/bugs), [good first issues](https://github.com/spotify/backstage/labels/good%20first%20issue) or [help wanted](https://github.com/spotify/backstage/labels/help%20wanted). +Look through the GitHub issues for [bugs](https://github.com/backstage/backstage/labels/bugs), [good first issues](https://github.com/backstage/backstage/labels/good%20first%20issue) or [help wanted](https://github.com/backstage/backstage/labels/help%20wanted). ## Build a plugin @@ -24,17 +24,17 @@ The value of Backstage grows with every new plugin that gets added. Wouldn't it A great reference example of a plugin can be found on [our blog](https://backstage.io/blog/2020/04/06/lighthouse-plugin) (thanks [@fastfrwrd](https://github.com/fastfrwrd)!) -What kind of plugins should/could be created? Some inspiration from the 120+ plugins that we have developed inside Spotify can be found [here](https://backstage.io/demos), but we will keep a running list of suggestions labeled with [[plugin]](https://github.com/spotify/backstage/labels/plugin). +What kind of plugins should/could be created? Some inspiration from the 120+ plugins that we have developed inside Spotify can be found [here](https://backstage.io/demos), but we will keep a running list of suggestions labeled with [[plugin]](https://github.com/backstage/backstage/labels/plugin). ## Suggesting a plugin -If you start developing a plugin that you aim to release as open source, we suggest that you create a [new Issue](https://github.com/spotify/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME). This helps the community know what plugins are in development. +If you start developing a plugin that you aim to release as open source, we suggest that you create a [new Issue](https://github.com/backstage/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME). This helps the community know what plugins are in development. You can also use this process if you have an idea for a good plugin but you hope that someone else will pick up the work. ## Adding Non-code Contributions -Since there is such a large landscape of possible development, build, and deployment environments, we welcome community contributions in these areas in the [`/contrib`](https://github.com/spotify/backstage/tree/master/contrib) folder of the project. This is an excellent place to put things that help out the community at large, but which may not fit within the scope of the core product to support natively. Here, you will find Helm charts, alternative Docker images, and much more. +Since there is such a large landscape of possible development, build, and deployment environments, we welcome community contributions in these areas in the [`/contrib`](https://github.com/backstage/backstage/tree/master/contrib) folder of the project. This is an excellent place to put things that help out the community at large, but which may not fit within the scope of the core product to support natively. Here, you will find Helm charts, alternative Docker images, and much more. ## Write Documentation @@ -44,11 +44,11 @@ The current documentation is very limited. Help us make the `/docs` folder come We think the best way to ensure different plugins provide a consistent experience is through a solid set of reusable UI/UX components. Backstage uses [Storybook](http://backstage.io/storybook). -Either help us [create new components](https://github.com/spotify/backstage/labels/help%20wanted) or improve stories for the existing ones (look for files with `*.stories.tsx`). +Either help us [create new components](https://github.com/backstage/backstage/labels/help%20wanted) or improve stories for the existing ones (look for files with `*.stories.tsx`). ## Submit Feedback -The best way to send feedback is to file [an issue](https://github.com/spotify/backstage/issues). +The best way to send feedback is to file [an issue](https://github.com/backstage/backstage/issues). If you are proposing a feature: @@ -74,7 +74,7 @@ All code is formatted with `prettier` using the configuration in the repo. If po If you're contributing to the backend or CLI tooling, be mindful of cross-platform support. [This](https://shapeshed.com/writing-cross-platform-node/) blog post is a good guide of what to keep in mind when writing cross-platform NodeJS. -Also be sure to skim through our [ADRs](https://github.com/spotify/backstage/tree/master/docs/architecture-decisions) to see if they cover what you're working on. In particular [ADR006: Avoid React.FC and React.SFC](https://github.com/spotify/backstage/blob/master/docs/architecture-decisions/adr006-avoid-react-fc.md) is one to look out for. +Also be sure to skim through our [ADRs](https://github.com/backstage/backstage/tree/master/docs/architecture-decisions) to see if they cover what you're working on. In particular [ADR006: Avoid React.FC and React.SFC](https://github.com/backstage/backstage/blob/master/docs/architecture-decisions/adr006-avoid-react-fc.md) is one to look out for. If there are any updates in `markdown` file please make sure to run `yarn run lint:docs`. Though it is checked on `lint-staged`. It is required to install [vale](https://docs.errata.ai/vale/install) separately and make sure it is accessed by global command. @@ -97,7 +97,7 @@ For more information, checkout [adding a changeset](https://github.com/atlassian This project adheres to the [Spotify FOSS Code of Conduct][code-of-conduct]. By participating, you are expected to honor this code. -[code-of-conduct]: https://github.com/spotify/backstage/blob/master/CODE_OF_CONDUCT.md +[code-of-conduct]: https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md # Security Issues? diff --git a/README.md b/README.md index 70badd5321..b1926f070e 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ [![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/spotify/backstage/workflows/Main%20Master%20Build/badge.svg)](https://github.com/spotify/backstage/actions?query=workflow%3A%22Main+Master+Build%22) +[![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) [![Discord](https://img.shields.io/discord/687207715902193673)](https://discord.gg/EBHEGzX) ![Code style](https://img.shields.io/badge/code_style-prettier-ff69b4.svg) [![Codecov](https://img.shields.io/codecov/c/github/spotify/backstage)](https://codecov.io/gh/spotify/backstage) -[![](https://img.shields.io/npm/v/@backstage/core?label=Version)](https://github.com/spotify/backstage/releases) +[![](https://img.shields.io/npm/v/@backstage/core?label=Version)](https://github.com/backstage/backstage/releases) ## What is Backstage? @@ -23,7 +23,7 @@ Out of the box, Backstage includes: - [Backstage Service Catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview) for managing all your software (microservices, libraries, data pipelines, websites, ML models, etc.) - [Backstage Software Templates](https://backstage.io/docs/features/software-templates/software-templates-index) for quickly spinning up new projects and standardizing your tooling with your organization’s best practices - [Backstage TechDocs](https://backstage.io/docs/features/techdocs/techdocs-overview) for making it easy to create, maintain, find, and use technical documentation, using a "docs like code" approach -- Plus, a growing ecosystem of [open source plugins](https://github.com/spotify/backstage/tree/master/plugins) that further expand Backstage’s customizability and functionality +- Plus, a growing ecosystem of [open source plugins](https://github.com/backstage/backstage/tree/master/plugins) that further expand Backstage’s customizability and functionality Backstage was created by Spotify but is now hosted by the [Cloud Native Computing Foundation (CNCF)](https://www.cncf.io) as a Sandbox level project. Read the announcement [here](https://backstage.io/blog/2020/09/23/backstage-cncf-sandbox). @@ -46,8 +46,8 @@ Check out [the documentation](https://backstage.io/docs/getting-started) on how ## Community - [Discord chatroom](https://discord.gg/MUpMjP2) - Get support or discuss the project -- [Good First Issues](https://github.com/spotify/backstage/contribute) - Start here if you want to contribute -- [RFCs](https://github.com/spotify/backstage/labels/rfc) - Help shape the technical direction +- [Good First Issues](https://github.com/backstage/backstage/contribute) - Start here if you want to contribute +- [RFCs](https://github.com/backstage/backstage/labels/rfc) - Help shape the technical direction - [FAQ](https://backstage.io/docs/FAQ) - Frequently Asked Questions - [Code of Conduct](CODE_OF_CONDUCT.md) - This is how we roll - [Adopters](ADOPTERS.md) - Companies already using Backstage diff --git a/app-config.yaml b/app-config.yaml index a495df0263..bc25b7d71e 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -138,22 +138,22 @@ catalog: locations: # Backstage example components - type: url - target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/all-components.yaml + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all-components.yaml # Example component for github-actions - type: url - target: https://github.com/spotify/backstage/blob/master/plugins/github-actions/examples/sample.yaml + target: https://github.com/backstage/backstage/blob/master/plugins/github-actions/examples/sample.yaml # Example component for techdocs - type: url - target: https://github.com/spotify/backstage/blob/master/plugins/techdocs-backend/examples/documented-component/documented-component.yaml + target: https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend/examples/documented-component/documented-component.yaml # Backstage example APIs - type: url - target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/all-apis.yaml + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all-apis.yaml # Backstage example templates - type: url - target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/all-templates.yaml + target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/all-templates.yaml # Backstage example groups and users - type: url - target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/acme-corp.yaml + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/acme-corp.yaml scaffolder: github: diff --git a/catalog-info.yaml b/catalog-info.yaml index 405c3f31b4..9c7e5309dd 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -6,7 +6,7 @@ metadata: Backstage is an open-source developer portal that puts the developer experience first. annotations: github.com/project-slug: spotify/backstage - backstage.io/techdocs-ref: github:https://github.com/spotify/backstage.git + backstage.io/techdocs-ref: github:https://github.com/backstage/backstage.git lighthouse.com/website-url: https://backstage.io spec: type: library diff --git a/contrib/chart/backstage/Chart.yaml b/contrib/chart/backstage/Chart.yaml index 24c6f2a4d7..be5c20af82 100644 --- a/contrib/chart/backstage/Chart.yaml +++ b/contrib/chart/backstage/Chart.yaml @@ -12,7 +12,7 @@ version: 0.1.1 appVersion: v0.1.1-alpha.23 sources: - - https://github.com/spotify/backstage + - https://github.com/backstage/backstage - https://github.com/spotify/lighthouse-audit-service dependencies: diff --git a/contrib/chart/backstage/README.md b/contrib/chart/backstage/README.md index 9d1c93765b..8b922a6b56 100644 --- a/contrib/chart/backstage/README.md +++ b/contrib/chart/backstage/README.md @@ -36,7 +36,7 @@ appConfig: Then use it to run: ``` -git clone https://github.com/spotify/backstage.git +git clone https://github.com/backstage/backstage.git cd contrib/chart/backstage helm dependency update helm install -f backstage-mydomain.yaml backstage . diff --git a/contrib/chart/backstage/files/app-config.development.yaml.tpl b/contrib/chart/backstage/files/app-config.development.yaml.tpl index 76d6499145..1e0a41ad63 100644 --- a/contrib/chart/backstage/files/app-config.development.yaml.tpl +++ b/contrib/chart/backstage/files/app-config.development.yaml.tpl @@ -18,19 +18,19 @@ catalog: locations: # Backstage example components - type: github - target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/all-components.yaml + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all-components.yaml # Example component for github-actions - type: github - target: https://github.com/spotify/backstage/blob/master/plugins/github-actions/examples/sample.yaml + target: https://github.com/backstage/backstage/blob/master/plugins/github-actions/examples/sample.yaml # Example component for techdocs - type: github - target: https://github.com/spotify/backstage/blob/master/plugins/techdocs-backend/examples/documented-component/documented-component.yaml + target: https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend/examples/documented-component/documented-component.yaml # Backstage example APIs - type: github - target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/all-apis.yaml + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all-apis.yaml # Backstage example templates - type: github - target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/all-templates.yaml + target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/all-templates.yaml {{- else }} locations: [] {{- end }} diff --git a/docs/FAQ.md b/docs/FAQ.md index 7163d955c7..7b906a341b 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -48,11 +48,11 @@ source candidates. (And we'll probably end up writing some brand new ones, too.) ### What's the roadmap for Backstage? We envision three phases, which you can learn about in -[our project roadmap](https://github.com/spotify/backstage#project-roadmap). +[our project roadmap](https://github.com/backstage/backstage#project-roadmap). Even though the open source version of Backstage is relatively new compared to our internal version, we have already begun work on various aspects of all three phases. Looking at the -[milestones for active issues](https://github.com/spotify/backstage/milestones) +[milestones for active issues](https://github.com/backstage/backstage/milestones) will also give you a sense of our progress. ### My company doesn't have thousands of developers or services. Is Backstage overkill? @@ -87,8 +87,8 @@ well-known tech and a large flora of components. ### What technology does Backstage use? The codebase is a large-scale React application that uses TypeScript. For -[Phase 2](https://github.com/spotify/backstage#project-roadmap), we plan to use -Node.js and GraphQL. +[Phase 2](https://github.com/backstage/backstage#project-roadmap), we plan to +use Node.js and GraphQL. ### What is the end-to-end user flow? The happy path story. @@ -116,7 +116,7 @@ components. Plugins can fetch data either from the backend or an API exposed through the proxy. Learn more about -[the different components](https://github.com/spotify/backstage#overview) that +[the different components](https://github.com/backstage/backstage#overview) that make up Backstage. ### Do I have to write plugins in TypeScript? @@ -127,10 +127,10 @@ APIs in TypeScript, but aren't forcing it on individual plugins. ### How do I find out if a plugin already exists? Before you write a plugin, -[search the plugin issues](https://github.com/spotify/backstage/issues?q=is%3Aissue+label%3Aplugin+) +[search the plugin issues](https://github.com/backstage/backstage/issues?q=is%3Aissue+label%3Aplugin+) to see if it already exists or is in the works. If no one's thought of it yet, great! Open a new issue as -[a plugin suggestion](https://github.com/spotify/backstage/issues/new/choose) +[a plugin suggestion](https://github.com/backstage/backstage/issues/new/choose) and describe what your plugin will do. This will help coordinate our contributors' efforts and avoid duplicating existing functionality. @@ -151,7 +151,7 @@ above) ### Are you planning to have plugins baked into the repo? Or should they be developed in separate repos? Contributors can add open source plugins to the plugins directory in -[this monorepo](https://github.com/spotify/backstage). Integrators can then +[this monorepo](https://github.com/backstage/backstage). Integrators can then configure which open source plugins are available to use in their instance of the app. Open source plugins are downloaded as npm packages published in the open source repository. While we encourage using the open source model, we know @@ -166,7 +166,7 @@ We chose GitHub because it is the tool that we are most familiar with, so that will naturally lead to integrations for GitHub being developed at an early stage. Hosting this project on GitHub does not exclude integrations with alternatives, such as -[GitLab](https://github.com/spotify/backstage/issues?q=is%3Aissue+is%3Aopen+GitLab) +[GitLab](https://github.com/backstage/backstage/issues?q=is%3Aissue+is%3Aopen+GitLab) or Bitbucket. We believe that in time there will be plugins that will provide functionality for these tools as well. Hopefully, contributed by the community! Also note, implementations of Backstage can be hosted wherever you feel suits @@ -215,17 +215,17 @@ data is shared with. Yes. The core frontend framework could be used for building any large-scale web application where (1) multiple teams are building separate parts of the app, and (2) you want the overall experience to be consistent. That being said, in -[Phase 2](https://github.com/spotify/backstage#project-roadmap) of the project +[Phase 2](https://github.com/backstage/backstage#project-roadmap) of the project we will add features that are needed for developer portals and systems for managing software ecosystems. Our ambition will be to keep Backstage modular. ### How can I get involved? Jump right in! Come help us fix some of the -[early bugs and first issues](https://github.com/spotify/backstage/labels/good%20first%20issue) -or reach [a new milestone](https://github.com/spotify/backstage/milestones). Or -write an open source plugin for Backstage, like this -[Lighthouse plugin](https://github.com/spotify/backstage/tree/master/plugins/lighthouse). +[early bugs and first issues](https://github.com/backstage/backstage/labels/good%20first%20issue) +or reach [a new milestone](https://github.com/backstage/backstage/milestones). +Or write an open source plugin for Backstage, like this +[Lighthouse plugin](https://github.com/backstage/backstage/tree/master/plugins/lighthouse). See all the ways you can -[contribute here](https://github.com/spotify/backstage/blob/master/CONTRIBUTING.md). +[contribute here](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md). We'd love to have you as part of the community. diff --git a/docs/architecture-decisions/adr002-default-catalog-file-format.md b/docs/architecture-decisions/adr002-default-catalog-file-format.md index d831581d5b..699f7af8ed 100644 --- a/docs/architecture-decisions/adr002-default-catalog-file-format.md +++ b/docs/architecture-decisions/adr002-default-catalog-file-format.md @@ -39,7 +39,7 @@ inside Backstage, or by push events from a CI/CD pipelines, or by webhook triggers from the version control system, etc. Each file describes one or more entities in accordance with the -[Backstage System Model](https://github.com/spotify/backstage/issues/390). All +[Backstage System Model](https://github.com/backstage/backstage/issues/390). All of these entities have a common structure and nomenclature, and they are stored in the software catalog from which they then can be queried. @@ -87,7 +87,7 @@ The root envelope object has the following structure. ### `apiVersion` and `kind` The `kind` is the high level entity type being described, typically from the -[Backstage system model](https://github.com/spotify/backstage/issues/390). The +[Backstage system model](https://github.com/backstage/backstage/issues/390). The first versions of the catalog will focus on the `Component` kind. The `apiVersion`is the version of specification format for that particular diff --git a/docs/architecture-decisions/adr007-use-msw-to-mock-service-requests.md b/docs/architecture-decisions/adr007-use-msw-to-mock-service-requests.md index 045588f505..68394de17c 100644 --- a/docs/architecture-decisions/adr007-use-msw-to-mock-service-requests.md +++ b/docs/architecture-decisions/adr007-use-msw-to-mock-service-requests.md @@ -41,7 +41,7 @@ worker.start(); ``` and in a more real life scenario, taken from -[CatalogClient.test.ts](https://github.com/spotify/backstage/blob/f3245c4f8f0b6b2625c4a6d5d50161b612fb4757/plugins/catalog/src/api/CatalogClient.test.ts) +[CatalogClient.test.ts](https://github.com/backstage/backstage/blob/f3245c4f8f0b6b2625c4a6d5d50161b612fb4757/plugins/catalog/src/api/CatalogClient.test.ts) ```ts beforeEach(() => { diff --git a/docs/architecture-decisions/adr008-default-catalog-file-name.md b/docs/architecture-decisions/adr008-default-catalog-file-name.md index d4716318bc..6794dc87cc 100644 --- a/docs/architecture-decisions/adr008-default-catalog-file-name.md +++ b/docs/architecture-decisions/adr008-default-catalog-file-name.md @@ -11,7 +11,7 @@ While the spec for the catalog file format is well described in to the name of the catalog file. Following discussion in -[Issue 1822](https://github.com/spotify/backstage/pull/1822#pullrequestreview-461253670), +[Issue 1822](https://github.com/backstage/backstage/pull/1822#pullrequestreview-461253670), a decision was made. ## Name diff --git a/docs/architecture-decisions/adr009-entity-references.md b/docs/architecture-decisions/adr009-entity-references.md index 8b7984ea49..a6fe3ee583 100644 --- a/docs/architecture-decisions/adr009-entity-references.md +++ b/docs/architecture-decisions/adr009-entity-references.md @@ -13,7 +13,7 @@ There was also some confusion on how to reference entities in URLs in the Backstage frontend. Following discussion in -[Issue 1947](https://github.com/spotify/backstage/issues/1947), a decision was +[Issue 1947](https://github.com/backstage/backstage/issues/1947), a decision was made. ## Entity References in YAML files diff --git a/docs/architecture-decisions/index.md b/docs/architecture-decisions/index.md index ddf2805e75..3211f37550 100644 --- a/docs/architecture-decisions/index.md +++ b/docs/architecture-decisions/index.md @@ -25,9 +25,9 @@ Records should be stored under the `architecture-decisions` directory. - Address and integrate feedback from the community - Eventually, assign a number - Add the path of the ADR to the microsite sidebar in - [`sidebars.json`](https://github.com/spotify/backstage/blob/master/microsite/sidebars.json) + [`sidebars.json`](https://github.com/backstage/backstage/blob/master/microsite/sidebars.json) - Add the path of the ADR to the - [`mkdocs.yml`](https://github.com/spotify/backstage/blob/master/mkdocs.yml) + [`mkdocs.yml`](https://github.com/backstage/backstage/blob/master/mkdocs.yml) - Merge the pull request ## Superseding an ADR diff --git a/docs/auth/index.md b/docs/auth/index.md index d7fb5d8c59..3f95efaf61 100644 --- a/docs/auth/index.md +++ b/docs/auth/index.md @@ -58,7 +58,7 @@ token used to make authenticated calls within Backstage. The middleware that will be provided by `@backstage/backend-common` allows verification of Backstage ID tokens, and optionally loading additional information about the user. The progress is tracked in -https://github.com/spotify/backstage/issues/1435. +https://github.com/backstage/backstage/issues/1435. #### Identity for App Developers diff --git a/docs/auth/oauth.md b/docs/auth/oauth.md index 178b99b153..0655175b58 100644 --- a/docs/auth/oauth.md +++ b/docs/auth/oauth.md @@ -15,10 +15,10 @@ to various third party APIs. There are occasions when the user wants to perform actions towards third party services that require authorization via OAuth. Backstage provides standardized [Utility APIs](../api/utility-apis.md) such as the -[GoogleAuthApi](https://github.com/spotify/backstage/blob/master/packages/core-api/src/apis/definitions/auth.ts) +[GoogleAuthApi](https://github.com/backstage/backstage/blob/master/packages/core-api/src/apis/definitions/auth.ts) for that use-case. Backstage also includes a set of implementations of these APIs that integrate with the -[auth-backend](https://github.com/spotify/backstage/tree/master/plugins/auth-backend) +[auth-backend](https://github.com/backstage/backstage/tree/master/plugins/auth-backend) plugin to provide a popup-based OAuth flow. ## Background @@ -58,9 +58,9 @@ easier to make authenticated requests inside a plugin. ## OAuth Flow The following describes the OAuth flow implemented by the -[auth-backend](https://github.com/spotify/backstage/tree/master/plugins/auth-backend) +[auth-backend](https://github.com/backstage/backstage/tree/master/plugins/auth-backend) and -[DefaultAuthConnector](https://github.com/spotify/backstage/blob/master/packages/core-api/src/lib/AuthConnector/DefaultAuthConnector.ts) +[DefaultAuthConnector](https://github.com/backstage/backstage/blob/master/packages/core-api/src/lib/AuthConnector/DefaultAuthConnector.ts) in `@backstage/core-api`. Component and APIs can request Access or ID Tokens from any available Auth diff --git a/docs/conf/reading.md b/docs/conf/reading.md index a43ec8c026..6d4b61224d 100644 --- a/docs/conf/reading.md +++ b/docs/conf/reading.md @@ -117,7 +117,7 @@ The [ConfigApi](../reference/utility-apis/Config.md) in the frontend is a Depending on the config api in another API is slightly different though, as the `ConfigApi` implementation is supplied via the App itself and not instantiated like other APIs. See -[packages/app/src/apis.ts](https://github.com/spotify/backstage/blob/244eef851f5aa19f91c7c9b5c12d5df95cf482ca/packages/app/src/apis.ts#L66) +[packages/app/src/apis.ts](https://github.com/backstage/backstage/blob/244eef851f5aa19f91c7c9b5c12d5df95cf482ca/packages/app/src/apis.ts#L66) for an example of how this wiring is done. For standalone plugin setups in `dev/index.ts`, register a factory with a @@ -129,4 +129,4 @@ from `@backstage/core`. In backend plugins the configuration is passed in via options from the main backend package. See for example -[packages/backend/src/plugins/auth.ts](https://github.com/spotify/backstage/blob/244eef851f5aa19f91c7c9b5c12d5df95cf482ca/packages/backend/src/plugins/auth.ts#L23). +[packages/backend/src/plugins/auth.ts](https://github.com/backstage/backstage/blob/244eef851f5aa19f91c7c9b5c12d5df95cf482ca/packages/backend/src/plugins/auth.ts#L23). diff --git a/docs/features/software-catalog/configuration.md b/docs/features/software-catalog/configuration.md index dd19d4cd51..d10fb29e30 100644 --- a/docs/features/software-catalog/configuration.md +++ b/docs/features/software-catalog/configuration.md @@ -89,7 +89,7 @@ the catalog under the `catalog.locations` key, for example: catalog: locations: - type: url - target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml ``` The locations added through static configuration can not be removed through the diff --git a/docs/features/software-catalog/extending-the-model.md b/docs/features/software-catalog/extending-the-model.md index b045660202..72170fb576 100644 --- a/docs/features/software-catalog/extending-the-model.md +++ b/docs/features/software-catalog/extending-the-model.md @@ -36,6 +36,6 @@ through plugins. Different plugins are used for managing different types of components. For example, the -[Lighthouse plugin](https://github.com/spotify/backstage/tree/master/plugins/lighthouse) +[Lighthouse plugin](https://github.com/backstage/backstage/tree/master/plugins/lighthouse) only makes sense for Websites. The more specific you can be in how you model your software, the easier it is to provide plugins that are contextual. diff --git a/docs/features/software-catalog/index.md b/docs/features/software-catalog/index.md index 953540227c..a7c80d45cc 100644 --- a/docs/features/software-catalog/index.md +++ b/docs/features/software-catalog/index.md @@ -62,11 +62,11 @@ Users can register new components by going to `/create` and clicking the Backstage expects the full URL to the YAML in your source control. Example: ```bash -https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml +https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml ``` _More examples can be found -[here](https://github.com/spotify/backstage/tree/master/packages/catalog-model/examples)._ +[here](https://github.com/backstage/backstage/tree/master/packages/catalog-model/examples)._ ![](../../assets/software-catalog/bsc-register-2.png) @@ -91,7 +91,7 @@ above example can be added using the following configuration: catalog: locations: - type: url - target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml ``` More information about catalog configuration can be found @@ -135,7 +135,7 @@ in the catalog. ![tools](https://backstage.io/blog/assets/20-05-20/tabs.png) The Backstage platform can be customized by incorporating -[existing open source plugins](https://github.com/spotify/backstage/tree/master/plugins), +[existing open source plugins](https://github.com/backstage/backstage/tree/master/plugins), or by [building your own](../../plugins/index.md). ## Links diff --git a/docs/features/software-catalog/installation.md b/docs/features/software-catalog/installation.md index 1e037461b4..b2afd62878 100644 --- a/docs/features/software-catalog/installation.md +++ b/docs/features/software-catalog/installation.md @@ -56,7 +56,7 @@ The catalog components depend on a number of other [Utility APIs](../../api/utility-apis.md) to function, including at least the `ErrorApi` and `StorageApi`. You can find an example of how to install these in your app -[here](https://github.com/spotify/backstage/blob/61c3a7e5b750dc7c059ef16b188594d31b2c04c2/packages/app/src/apis.ts#L80). +[here](https://github.com/backstage/backstage/blob/61c3a7e5b750dc7c059ef16b188594d31b2c04c2/packages/app/src/apis.ts#L80). ## Gotchas that we will fix @@ -168,21 +168,21 @@ catalog: locations: # Backstage Example Component - type: url - target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml - type: url - target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/playback-order-component.yaml + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/playback-order-component.yaml - type: url - target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/podcast-api-component.yaml + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/podcast-api-component.yaml - type: url - target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/queue-proxy-component.yaml + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/queue-proxy-component.yaml - type: url - target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/searcher-component.yaml + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/searcher-component.yaml - type: url - target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/playback-lib-component.yaml + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/playback-lib-component.yaml - type: url - target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/www-artist-component.yaml + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/www-artist-component.yaml - type: url - target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/shuffle-api-component.yaml + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/shuffle-api-component.yaml ``` ### Running the Backend diff --git a/docs/features/software-catalog/system-model.md b/docs/features/software-catalog/system-model.md index 44dac13d65..53f49d5df9 100644 --- a/docs/features/software-catalog/system-model.md +++ b/docs/features/software-catalog/system-model.md @@ -8,7 +8,7 @@ We believe that a strong shared understanding and terminology around software and resources leads to a better Backstage experience. _This description originates from -[this RFC](https://github.com/spotify/backstage/issues/390). Note that some of +[this RFC](https://github.com/backstage/backstage/issues/390). Note that some of the concepts are not yet supported in Backstage._ ## Core Entities @@ -113,5 +113,5 @@ Backstage currently supports Components and APIs. ## Links -- [Original RFC](https://github.com/spotify/backstage/issues/390) +- [Original RFC](https://github.com/backstage/backstage/issues/390) - [YAML file format](../../architecture-decisions/adr002-default-catalog-file-format.md) diff --git a/docs/features/software-catalog/well-known-annotations.md b/docs/features/software-catalog/well-known-annotations.md index 47b1d5885d..f1ea9347a7 100644 --- a/docs/features/software-catalog/well-known-annotations.md +++ b/docs/features/software-catalog/well-known-annotations.md @@ -23,7 +23,7 @@ use. # Example: metadata: annotations: - backstage.io/managed-by-location: github:http://github.com/spotify/backstage/catalog-info.yaml + backstage.io/managed-by-location: github:http://github.com/backstage/backstage/catalog-info.yaml ``` The value of this annotation is a so called location reference string, that @@ -47,7 +47,7 @@ colon is always present. # Example: metadata: annotations: - backstage.io/techdocs-ref: github:https://github.com/spotify/backstage.git + backstage.io/techdocs-ref: github:https://github.com/backstage/backstage.git ``` The value of this annotation is a location reference string (see above). If this diff --git a/docs/features/software-templates/extending/create-your-own-preparer.md b/docs/features/software-templates/extending/create-your-own-preparer.md index ef9d709bce..186b2a3c0a 100644 --- a/docs/features/software-templates/extending/create-your-own-preparer.md +++ b/docs/features/software-templates/extending/create-your-own-preparer.md @@ -18,7 +18,7 @@ These two are added to the `PreparersBuilder` and then passed into the `createRouter` function of the `@spotify/plugin-scaffolder-backend`. A full example backend can be found in -[`scaffolder.ts`](https://github.com/spotify/backstage/blob/d91c10f654475a60829fa33a5c81018e517a319a/packages/backend/src/plugins/scaffolder.ts), +[`scaffolder.ts`](https://github.com/backstage/backstage/blob/d91c10f654475a60829fa33a5c81018e517a319a/packages/backend/src/plugins/scaffolder.ts), but it looks something like the following ```ts @@ -85,8 +85,8 @@ and put the contents into a temporary directory and return that directory path. Some good examples exist here: -- https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/src/scaffolder/stages/prepare/file.ts -- https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.ts +- https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/src/scaffolder/stages/prepare/file.ts +- https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.ts ### Registering your own Preparer diff --git a/docs/features/software-templates/extending/create-your-own-publisher.md b/docs/features/software-templates/extending/create-your-own-publisher.md index 546980f033..f076640ebe 100644 --- a/docs/features/software-templates/extending/create-your-own-publisher.md +++ b/docs/features/software-templates/extending/create-your-own-publisher.md @@ -22,7 +22,7 @@ This publisher is passed through to the `createRouter` function of the supported, but PR's are always welcome. An full example backend can be found -[here](https://github.com/spotify/backstage/blob/d91c10f654475a60829fa33a5c81018e517a319a/packages/backend/src/plugins/scaffolder.ts), +[here](https://github.com/backstage/backstage/blob/d91c10f654475a60829fa33a5c81018e517a319a/packages/backend/src/plugins/scaffolder.ts), but it looks something like the following ```ts @@ -82,7 +82,7 @@ Now it's up to you to implement the `publish` function and return Some good examples exist here: -- https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.ts +- https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.ts ### Registering your own Publisher diff --git a/docs/features/software-templates/extending/create-your-own-templater.md b/docs/features/software-templates/extending/create-your-own-templater.md index d4a6eef5d2..26e01e2782 100644 --- a/docs/features/software-templates/extending/create-your-own-templater.md +++ b/docs/features/software-templates/extending/create-your-own-templater.md @@ -20,7 +20,7 @@ This templater is added to the `TemplaterBuilder` and then passed into the `createRouter` function of the `@spotify/plugin-scaffolder-backend` An full example backend can be found -[here](https://github.com/spotify/backstage/blob/d91c10f654475a60829fa33a5c81018e517a319a/packages/backend/src/plugins/scaffolder.ts), +[here](https://github.com/backstage/backstage/blob/d91c10f654475a60829fa33a5c81018e517a319a/packages/backend/src/plugins/scaffolder.ts), but it looks something like the following ```ts @@ -96,7 +96,7 @@ Now it's up to you to implement the `run` function, and then return a Some good examples exist here: -- https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.ts +- https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.ts ### Registering your own Templater diff --git a/docs/features/software-templates/extending/index.md b/docs/features/software-templates/extending/index.md index 944bf0e3ca..aaf42d6b12 100644 --- a/docs/features/software-templates/extending/index.md +++ b/docs/features/software-templates/extending/index.md @@ -51,7 +51,7 @@ passed through to the scaffolder backend. ### How it works Most of the heavy lifting is done in the -[router.ts](https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/src/service/router.ts#L93) +[router.ts](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/src/service/router.ts#L93) file in the `scaffolder-backend` plugin. There are two routes defined in the router: `POST /v1/jobs` and diff --git a/docs/features/software-templates/installation.md b/docs/features/software-templates/installation.md index bac5f60789..724b86aed1 100644 --- a/docs/features/software-templates/installation.md +++ b/docs/features/software-templates/installation.md @@ -156,11 +156,11 @@ catalog: locations: # Backstage Example Templates - type: url - target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml + target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml - type: url - target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml + target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml - type: url - target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml + target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml - type: url target: https://github.com/spotify/cookiecutter-golang/blob/master/template.yaml ``` diff --git a/docs/features/techdocs/FAQ.md b/docs/features/techdocs/FAQ.md index e81e9d82c7..080346b9d0 100644 --- a/docs/features/techdocs/FAQ.md +++ b/docs/features/techdocs/FAQ.md @@ -17,14 +17,14 @@ This page answers frequently asked questions about [TechDocs](README.md). TechDocs is using [MkDocs](https://www.mkdocs.org/) to build project documentation under the hood. Documentation built with the -[techdocs-container](https://github.com/spotify/backstage/blob/master/packages/techdocs-container/README.md) +[techdocs-container](https://github.com/backstage/backstage/blob/master/packages/techdocs-container/README.md) is using the MkDocs [Material Theme](https://github.com/squidfunk/mkdocs-material). #### What is the mkdocs-techdocs-core plugin? The -[mkdocs-techdocs-core](https://github.com/spotify/backstage/blob/master/packages/techdocs-container/techdocs-core/README.md) +[mkdocs-techdocs-core](https://github.com/backstage/backstage/blob/master/packages/techdocs-container/techdocs-core/README.md) package is a MkDocs Plugin that works like a wrapper around multiple MkDocs plugins (e.g. [MkDocs Monorepo Plugin](https://github.com/spotify/mkdocs-monorepo-plugin)) as diff --git a/docs/features/techdocs/README.md b/docs/features/techdocs/README.md index e2b81b4a53..a16776868a 100644 --- a/docs/features/techdocs/README.md +++ b/docs/features/techdocs/README.md @@ -40,10 +40,10 @@ about TechDocs and the philosophy in its | [TechDocs V.2 🔮⌛][v2] | Easy adoption of TechDocs (whatever environment you have) [See V.2 Use Cases.](#techdocs-v2) | | [TechDocs V.3 🔮⌛][v3] | Build a widget (plugin) framework so that contributors can easily contribute features to TechDocs - that others can use. [See V.3 Use Cases.](#techdocs-v3) | -[v0]: https://github.com/spotify/backstage/milestone/15 -[v1]: https://github.com/spotify/backstage/milestone/16 -[v2]: https://github.com/spotify/backstage/milestone/22 -[v3]: https://github.com/spotify/backstage/milestone/17 +[v0]: https://github.com/backstage/backstage/milestone/15 +[v1]: https://github.com/backstage/backstage/milestone/16 +[v2]: https://github.com/backstage/backstage/milestone/22 +[v3]: https://github.com/backstage/backstage/milestone/17 @@ -117,10 +117,10 @@ migrate Spotify's existing TechDocs features to open source. [creating and publishing documentation]: creating-and-publishing.md [faq]: FAQ.md 'Frequently asked questions' [techdocs/frontend]: - https://github.com/spotify/backstage/blob/master/plugins/techdocs + https://github.com/backstage/backstage/blob/master/plugins/techdocs [techdocs/backend]: - https://github.com/spotify/backstage/blob/master/plugins/techdocs-backend + https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend [techdocs/container]: - https://github.com/spotify/backstage/blob/master/packages/techdocs-container + https://github.com/backstage/backstage/blob/master/packages/techdocs-container [techdocs/cli]: - https://github.com/spotify/backstage/blob/master/packages/techdocs-cli + https://github.com/backstage/backstage/blob/master/packages/techdocs-cli diff --git a/docs/features/techdocs/concepts.md b/docs/features/techdocs/concepts.md index 13144f8df0..13b780524b 100644 --- a/docs/features/techdocs/concepts.md +++ b/docs/features/techdocs/concepts.md @@ -14,7 +14,7 @@ The TechDocs Core Plugin is an [MkDocs](https://www.mkdocs.org/) plugin created as a wrapper around multiple MkDocs plugins and Python Markdown extensions to standardize the configuration of MkDocs used for TechDocs. -[TechDocs Core](https://github.com/spotify/backstage/blob/master/packages/techdocs-container/techdocs-core/README.md) +[TechDocs Core](https://github.com/backstage/backstage/blob/master/packages/techdocs-container/techdocs-core/README.md) ### TechDocs container @@ -23,14 +23,14 @@ The TechDocs container is a Docker container available at pages, including stylesheets and scripts from Python flavored Markdown, through MkDocs. -[TechDocs Container](https://github.com/spotify/backstage/blob/master/packages/techdocs-container/README.md) +[TechDocs Container](https://github.com/backstage/backstage/blob/master/packages/techdocs-container/README.md) ### TechDocs publisher The `techdocs-backend` plugin currently comes with one publisher - `LocalPublish`. -[TechDocs Backend](https://github.com/spotify/backstage/tree/master/plugins/techdocs-backend) +[TechDocs Backend](https://github.com/backstage/backstage/tree/master/plugins/techdocs-backend) More standalone publishers will come in the near future... @@ -41,7 +41,7 @@ documentation for publishing. Currently it mostly acts as a wrapper around the TechDocs container and provides an easy-to-use interface for our docker container. -[TechDocs CLI](https://github.com/spotify/backstage/blob/master/packages/techdocs-cli/README.md) +[TechDocs CLI](https://github.com/backstage/backstage/blob/master/packages/techdocs-cli/README.md) ### TechDocs Reader @@ -53,7 +53,7 @@ The TechDocs Reader purpose is also to open up the opportunity to integrate TechDocs widgets for a customized full-featured TechDocs experience. ([coming soon V.3](./README.md#project-roadmap)) -[TechDocs Reader](https://github.com/spotify/backstage/blob/master/plugins/techdocs/src/reader/README.md) +[TechDocs Reader](https://github.com/backstage/backstage/blob/master/plugins/techdocs/src/reader/README.md) ### Transformers @@ -62,4 +62,4 @@ Reader. The reason why transformers were introduced was to provide a way to transform the HTML content on pre and post render (e.g. rewrite docs links or modify css). -[Transformers API docs](https://github.com/spotify/backstage/blob/master/plugins/techdocs/src/reader/README.md) +[Transformers API docs](https://github.com/backstage/backstage/blob/master/plugins/techdocs/src/reader/README.md) diff --git a/docs/getting-started/app-custom-theme.md b/docs/getting-started/app-custom-theme.md index 97876c2ba1..79cd405198 100644 --- a/docs/getting-started/app-custom-theme.md +++ b/docs/getting-started/app-custom-theme.md @@ -37,7 +37,7 @@ in combination with [createMuiTheme](https://material-ui.com/customization/theming/#createmuitheme-options-args-theme) from [@material-ui/core](https://www.npmjs.com/package/@material-ui/core). See the -[@backstage/theme source](https://github.com/spotify/backstage/tree/master/packages/theme/src) +[@backstage/theme source](https://github.com/backstage/backstage/tree/master/packages/theme/src) and the implementation of the `createTheme` function for how this is done. You can also create a theme from scratch that matches the `BackstageTheme` type diff --git a/docs/getting-started/deployment-helm.md b/docs/getting-started/deployment-helm.md index c062ed3951..e1f3ea7629 100644 --- a/docs/getting-started/deployment-helm.md +++ b/docs/getting-started/deployment-helm.md @@ -8,7 +8,7 @@ sidebar_label: Kubernetes and Helm # Helm charts An example Backstage app can be deployed in Kubernetes using the -[Backstage Helm charts](https://github.com/spotify/backstage/tree/master/contrib/chart/backstage). +[Backstage Helm charts](https://github.com/backstage/backstage/tree/master/contrib/chart/backstage). First, choose a DNS name where Backstage will be hosted, and create a YAML file for your custom configuration. @@ -32,7 +32,7 @@ appConfig: Then use it to run: ```bash -git clone https://github.com/spotify/backstage.git +git clone https://github.com/backstage/backstage.git cd contrib/chart/backstage helm dependency update helm install -f backstage-mydomain.yaml backstage . @@ -61,4 +61,4 @@ backstage-ingress * 123.1.2.3 80 17m > **NOTE**: this is not a production ready deployment. For more information on how to customize the deployment check the -[README](https://github.com/spotify/backstage/tree/master/contrib/chart/backstage/README.md). +[README](https://github.com/backstage/backstage/tree/master/contrib/chart/backstage/README.md). diff --git a/docs/getting-started/deployment-k8s.md b/docs/getting-started/deployment-k8s.md index f123dd2548..5b7ce80051 100644 --- a/docs/getting-started/deployment-k8s.md +++ b/docs/getting-started/deployment-k8s.md @@ -9,5 +9,5 @@ Beyond that point we do not have an opinionated way to deploy Backstage within Kubernetes, as each cluster has its own unique set of tooling and patterns. We do provide examples to help you get started though. Check out -[this example](https://github.com/spotify/backstage/tree/master/contrib/kubernetes/plain_single_backend_deplyoment/) +[this example](https://github.com/backstage/backstage/tree/master/contrib/kubernetes/plain_single_backend_deplyoment/) for a basic single-deployment setup. diff --git a/docs/getting-started/development-environment.md b/docs/getting-started/development-environment.md index aa9aa0bde1..c5275114ce 100644 --- a/docs/getting-started/development-environment.md +++ b/docs/getting-started/development-environment.md @@ -56,7 +56,7 @@ system resources and slow things down. ## Package Scripts There are many commands to be found in the root -[package.json](https://github.com/spotify/backstage/blob/master/package.json), +[package.json](https://github.com/backstage/backstage/blob/master/package.json), here are some useful ones: ```python @@ -86,7 +86,7 @@ yarn create-plugin # Create a new plugin ``` > See -> [package.json](https://github.com/spotify/backstage/blob/master/package.json) +> [package.json](https://github.com/backstage/backstage/blob/master/package.json) > for other yarn commands/options. ## Local configuration diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index 855c8b0634..eb199f4bc8 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -44,7 +44,7 @@ look something like this. You can read more about this process in ### Contributing to Backstage You can read more in our -[CONTRIBUTING](https://github.com/spotify/backstage/blob/master/CONTRIBUTING.md) +[CONTRIBUTING](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) guide, which can help you get setup with a Backstage development environment. ### Next steps diff --git a/docs/getting-started/running-backstage-locally.md b/docs/getting-started/running-backstage-locally.md index 1a9097afd3..de3dbe93b5 100644 --- a/docs/getting-started/running-backstage-locally.md +++ b/docs/getting-started/running-backstage-locally.md @@ -84,19 +84,19 @@ exploring. But you can also set up any of the available authentication methods. The easiest option will be GitHub. To setup GitHub authentication in Backstage, see -[these instructions](https://github.com/spotify/backstage/tree/master/plugins/auth-backend#github). +[these instructions](https://github.com/backstage/backstage/tree/master/plugins/auth-backend#github). --- Congratulations! That should be it. Let us know how it went [on discord](https://discord.gg/EBHEGzX), file issues for any -[feature](https://github.com/spotify/backstage/issues/new?labels=help+wanted&template=feature_template.md) +[feature](https://github.com/backstage/backstage/issues/new?labels=help+wanted&template=feature_template.md) or -[plugin suggestions](https://github.com/spotify/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME), +[plugin suggestions](https://github.com/backstage/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME), or -[bugs](https://github.com/spotify/backstage/issues/new?labels=bug&template=bug_template.md) +[bugs](https://github.com/backstage/backstage/issues/new?labels=bug&template=bug_template.md) you have, and feel free to -[contribute](https://github.com/spotify/backstage/blob/master/CONTRIBUTING.md)! +[contribute](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md)! ## Creating a Plugin diff --git a/docs/openapi/definitions/auth.yaml b/docs/openapi/definitions/auth.yaml index cb736fd2a9..411ce69253 100644 --- a/docs/openapi/definitions/auth.yaml +++ b/docs/openapi/definitions/auth.yaml @@ -12,14 +12,14 @@ info: The API is supplied with a list of providers - such as `Google` or `Github` - and will add the endpoints described below to each of those providers. - Read more about [User Authentication and Authorization in Backstage](https://github.com/spotify/backstage/blob/master/docs/auth/overview.md). + Read more about [User Authentication and Authorization in Backstage](https://github.com/backstage/backstage/blob/master/docs/auth/overview.md). license: name: Apache 2.0 url: http://www.apache.org/licenses/LICENSE-2.0.html version: 0.1.1-alpha.8 externalDocs: description: Backstage official documentation - url: https://github.com/spotify/backstage/blob/master/docs/README.md + url: https://github.com/backstage/backstage/blob/master/docs/README.md servers: - url: http://localhost:7000/api/auth/ tags: diff --git a/docs/overview/architecture-overview.md b/docs/overview/architecture-overview.md index 6f3c6b1a71..ddf7980cbc 100644 --- a/docs/overview/architecture-overview.md +++ b/docs/overview/architecture-overview.md @@ -63,17 +63,17 @@ Each plugin is a client side application which mounts itself on the UI. Plugins are written in TypeScript or JavaScript. They each live in their own directory in `backstage/plugins`. For example, the source code for the lighthouse plugin is available at -[backstage/plugins/lighthouse](https://github.com/spotify/backstage/tree/master/plugins/lighthouse). +[backstage/plugins/lighthouse](https://github.com/backstage/backstage/tree/master/plugins/lighthouse). ### Installing plugins Plugins are typically loaded by the UI in your Backstage applications `plugins.ts` file. For example, -[here](https://github.com/spotify/backstage/blob/master/packages/app/src/plugins.ts) +[here](https://github.com/backstage/backstage/blob/master/packages/app/src/plugins.ts) is that file in the Backstage sample app. Plugins can be enabled, and passed configuration in `apis.ts`. For example, -[here](https://github.com/spotify/backstage/blob/master/packages/app/src/apis.ts) +[here](https://github.com/backstage/backstage/blob/master/packages/app/src/apis.ts) is that file in the Backstage sample app. This is how the Lighthouse plugin would be enabled in a typical Backstage @@ -169,8 +169,8 @@ in the future. To learn more about the future of databases and Backstage, see the following GitHub issues. -- [Knex + Plugins (Multiple vs Single Database) · Issue #1598 · spotify/backstage](https://github.com/spotify/backstage/issues/1598) -- [Update migrations to support postgres by dariddler · Pull Request #1527 · spotify/backstage](https://github.com/spotify/backstage/pull/1527#discussion_r450374145) +- [Knex + Plugins (Multiple vs Single Database) · Issue #1598 · spotify/backstage](https://github.com/backstage/backstage/issues/1598) +- [Update migrations to support postgres by dariddler · Pull Request #1527 · spotify/backstage](https://github.com/backstage/backstage/pull/1527#discussion_r450374145) ## Containerization diff --git a/docs/overview/logos.md b/docs/overview/logos.md index 2a684420bc..4c0c345f36 100644 --- a/docs/overview/logos.md +++ b/docs/overview/logos.md @@ -8,7 +8,7 @@ description: Guidelines for how to use the Backstage logos and icons Guidelines for how to use the Backstage logo and icon can be found [here](/logo_assets/Backstage_Identity_Assets_Overview.pdf). The assets below are all in `.svg` format. Other formats are available in the -[repository](https://github.com/spotify/backstage/tree/master/microsite/static/logo_assets). +[repository](https://github.com/backstage/backstage/tree/master/microsite/static/logo_assets). ## Backstage logo diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index 51f09347ce..5ff4d49a7c 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -9,8 +9,8 @@ description: Roadmap of Backstage Project > Backstage is currently under rapid development. This means that you can expect > APIs and features to evolve. It is also recommended that teams who adopt > Backstage today upgrade their installation as new -> [releases](https://github.com/spotify/backstage/releases) become available, as -> Backwards compatibility is not yet guaranteed. +> [releases](https://github.com/backstage/backstage/releases) become available, +> as Backwards compatibility is not yet guaranteed. ## Phases @@ -37,34 +37,34 @@ We have divided the project into three high-level _phases_: If you have questions about the roadmap or want to provide feedback, we would love to hear from you! Please create an -[Issue](https://github.com/spotify/backstage/issues/new/choose), ping us on +[Issue](https://github.com/backstage/backstage/issues/new/choose), ping us on [Discord](https://discord.gg/EBHEGzX) or reach out directly at [alund@spotify.com](mailto:alund@spotify.com). Want to help out? Awesome ❤️ Head over to -[CONTRIBUTING](https://github.com/spotify/backstage/blob/master/CONTRIBUTING.md) +[CONTRIBUTING](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) guidelines to get started. ### Ongoing work 🚧 -- **[Plugins for managing micro services end-2-end](https://github.com/spotify/backstage/milestone/14)** +- **[Plugins for managing micro services end-2-end](https://github.com/backstage/backstage/milestone/14)** Out of the box Backstage will ship with a set of plugins (Overview, CI, API and Docs) that will demonstrate how a user can manage a micro service and follow a change all the way out in production. Completing this work will make it much easier to see how a plugin can be built that integrates with the Backstage Service Catalog. -- **[Users and teams](https://github.com/spotify/backstage/issues/1807)** +- **[Users and teams](https://github.com/backstage/backstage/issues/1807)** Ownership is a central concept in Backstage. It should be easy to import your existing organizational data, such as users and groups/teams, into Backstage. A user the logs into Backstage should see software components owned by the team(s) they are in. -- **[Backstage platform is stable](https://github.com/spotify/backstage/milestone/19)** - +- **[Backstage platform is stable](https://github.com/backstage/backstage/milestone/19)** - The platform APIs and features are stable and can be depended on for production use. After this plugins will require little to no maintenance. -* **[Improved Kubernetes plugin](https://github.com/spotify/backstage/issues/2857)** - +* **[Improved Kubernetes plugin](https://github.com/backstage/backstage/issues/2857)** - Native support for Kubernetes, making it easier for developers to see and manage their services running in k8s. @@ -77,21 +77,21 @@ guidelines to get started. deployment available publicly so that people can click around and get a feel for the product without having to install anything. -- **[Global search](https://github.com/spotify/backstage/issues/1499)** - Extend - the basic search available in the Backstage Service Catalog with a global - search experience. Long term this search solution should be extensible, making - it possible for you add custom search results. +- **[Global search](https://github.com/backstage/backstage/issues/1499)** - + Extend the basic search available in the Backstage Service Catalog with a + global search experience. Long term this search solution should be extensible, + making it possible for you add custom search results. -- **[[TechDocs V.2] Stabilization release](https://github.com/spotify/backstage/milestone/17)** - +- **[[TechDocs V.2] Stabilization release](https://github.com/backstage/backstage/milestone/17)** - Platform stability and compatibility improvements. - **Additional auth providers** - Backstage should work for most (all!) auth solutions. Since Backstage can be used by companies regardless of what cloud (or on prem) you are using we are especially keen to get auth support for - [AWS](https://github.com/spotify/backstage/issues/290), - [Azure](https://github.com/spotify/backstage/issues/348) and others. + [AWS](https://github.com/backstage/backstage/issues/290), + [Azure](https://github.com/backstage/backstage/issues/348) and others. -- **[Initial GraphQL API](https://github.com/spotify/backstage/milestone/13)** - +- **[Initial GraphQL API](https://github.com/backstage/backstage/milestone/13)** - A GraphQL API will open up the rich metadata provided by Backstage in a single query. Plugins can easily query this API as well as extend the model where needed. @@ -102,18 +102,18 @@ Building and maintaining [plugins](https://backstage.io/plugins) is the work of the entire Backstage community. A list of plugins that are in development is -[available here](https://github.com/spotify/backstage/issues?q=is%3Aissue+is%3Aopen+label%3Aplugin+sort%3Areactions-%2B1-desc). +[available here](https://github.com/backstage/backstage/issues?q=is%3Aissue+is%3Aopen+label%3Aplugin+sort%3Areactions-%2B1-desc). We strongly recommend to upvote 👍 plugins you are interested in. This helps us and the community prioritize what plugins to build. Are you missing a plugin for your favorite tool? Please -[suggest a new one](https://github.com/spotify/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME). +[suggest a new one](https://github.com/backstage/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME). Chances are that someone will jump in and help build it. ### Completed milestones ✅ -- [Kubernetes plugin - v1](https://github.com/spotify/backstage/tree/master/plugins/kubernetes) -- [Helm charts](https://github.com/spotify/backstage/tree/master/contrib/chart/backstage) +- [Kubernetes plugin - v1](https://github.com/backstage/backstage/tree/master/plugins/kubernetes) +- [Helm charts](https://github.com/backstage/backstage/tree/master/contrib/chart/backstage) - [Backstage Design System 💅](https://backstage.io/blog/2020/09/30/backstage-design-system) - [Cost Insights plugin 💸](https://engineering.atspotify.com/2020/09/29/managing-clouds-from-the-ground-up-cost-engineering-at-spotify/) - [Donate Backstage to the CNCF 🎉](https://backstage.io/blog/2020/09/23/backstage-cncf-sandbox) @@ -123,11 +123,11 @@ Chances are that someone will jump in and help build it. - [Backstage Service Catalog (alpha)](https://backstage.io/blog/2020/06/22/backstage-service-catalog-alpha) - [Backstage Software Templates (alpha)](https://backstage.io/blog/2020/08/05/announcing-backstage-software-templates) - [Make it possible to add custom auth providers](https://backstage.io/blog/2020/07/01/how-to-enable-authentication-in-backstage-using-passport) -- [TechDocs v0](https://github.com/spotify/backstage/milestone/15) +- [TechDocs v0](https://github.com/backstage/backstage/milestone/15) - CI plugins: CircleCI, Jenkins, GitHub Actions and TravisCI -- [Service API documentation](https://github.com/spotify/backstage/pull/1737) +- [Service API documentation](https://github.com/backstage/backstage/pull/1737) - Backstage Service Catalog can read from: GitHub, GitLab, - [Bitbucket](https://github.com/spotify/backstage/pull/1938) + [Bitbucket](https://github.com/backstage/backstage/pull/1938) - Support auth providers: Google, Okta, GitHub, GitLab, - [auth0](https://github.com/spotify/backstage/pull/1611), - [AWS](https://github.com/spotify/backstage/pull/1990) + [auth0](https://github.com/backstage/backstage/pull/1611), + [AWS](https://github.com/backstage/backstage/pull/1990) diff --git a/docs/overview/what-is-backstage.md b/docs/overview/what-is-backstage.md index 3d41c714ee..5439b838bd 100644 --- a/docs/overview/what-is-backstage.md +++ b/docs/overview/what-is-backstage.md @@ -30,7 +30,7 @@ Out of the box, Backstage includes: code" approach - Plus, a growing ecosystem of - [open source plugins](https://github.com/spotify/backstage/tree/master/plugins) + [open source plugins](https://github.com/backstage/backstage/tree/master/plugins) that further expand Backstage’s customizability and functionality ## Backstage and the CNCF diff --git a/docs/plugins/add-to-marketplace.md b/docs/plugins/add-to-marketplace.md index 59cca07bff..790512e6aa 100644 --- a/docs/plugins/add-to-marketplace.md +++ b/docs/plugins/add-to-marketplace.md @@ -8,7 +8,7 @@ description: Documentation on Adding Plugin to Marketplace To add a new plugin to the [plugin marketplace](https://backstage.io/plugins) create a file in -[`microsite/data/plugins`](https://github.com/spotify/backstage/tree/master/microsite/data/plugins) +[`microsite/data/plugins`](https://github.com/backstage/backstage/tree/master/microsite/data/plugins) with your plugin's information. Example: ```yaml diff --git a/docs/plugins/call-existing-api.md b/docs/plugins/call-existing-api.md index 3fff90c3f6..75e054d2b9 100644 --- a/docs/plugins/call-existing-api.md +++ b/docs/plugins/call-existing-api.md @@ -137,7 +137,7 @@ router.use('/summary', async (req, res) => { ``` For a more detailed example, see -[the lighthouse plugin](https://github.com/spotify/backstage/tree/master/plugins/lighthouse) +[the lighthouse plugin](https://github.com/backstage/backstage/tree/master/plugins/lighthouse) that stores some state in a database and adds new capabilities to the underlying API. diff --git a/docs/plugins/index.md b/docs/plugins/index.md index 8836c1cd42..dcef3f995c 100644 --- a/docs/plugins/index.md +++ b/docs/plugins/index.md @@ -22,7 +22,7 @@ To create a plugin, follow the steps outlined [here](create-a-plugin.md). If you start developing a plugin that you aim to release as open source, we suggest that you create a -[new Issue](https://github.com/spotify/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME). +[new Issue](https://github.com/backstage/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME). This helps the community know what plugins are in development. You can also use this process if you have an idea for a good plugin but you hope diff --git a/docs/plugins/publishing.md b/docs/plugins/publishing.md index fbeab6340c..1a94daf8a5 100644 --- a/docs/plugins/publishing.md +++ b/docs/plugins/publishing.md @@ -7,7 +7,7 @@ description: Documentation on Publishing NPM packages ## NPM NPM packages are published through CI/CD in the -[.github/workflows/master.yml](https://github.com/spotify/backstage/blob/master/.github/workflows/master.yml) +[.github/workflows/master.yml](https://github.com/backstage/backstage/blob/master/.github/workflows/master.yml) workflow. Every commit that is merged to master will be checked for new versions of all public packages, and any new versions will automatically be published to NPM. diff --git a/docs/plugins/structure-of-a-plugin.md b/docs/plugins/structure-of-a-plugin.md index 0f11896f7a..3f631a9a46 100644 --- a/docs/plugins/structure-of-a-plugin.md +++ b/docs/plugins/structure-of-a-plugin.md @@ -106,6 +106,6 @@ environment you will probably face challenges like CORS policies and/or backend-side authorization. To smooth this process out you can use proxy - either the one you already have (like Nginx, HAProxy, etc.) or the proxy-backend plugin that we provide for the Backstage backend. -[Read more](https://github.com/spotify/backstage/blob/master/plugins/proxy-backend/README.md) +[Read more](https://github.com/backstage/backstage/blob/master/plugins/proxy-backend/README.md) [Back to Getting Started](../README.md) diff --git a/docs/reference/utility-apis/AlertApi.md b/docs/reference/utility-apis/AlertApi.md index 7d3167fdb7..4116e964ae 100644 --- a/docs/reference/utility-apis/AlertApi.md +++ b/docs/reference/utility-apis/AlertApi.md @@ -1,7 +1,7 @@ # AlertApi The AlertApi type is defined at -[packages/core-api/src/apis/definitions/AlertApi.ts:29](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/AlertApi.ts#L29). +[packages/core-api/src/apis/definitions/AlertApi.ts:29](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/AlertApi.ts#L29). The following Utility API implements this type: [alertApiRef](./README.md#alert) @@ -38,7 +38,7 @@ export type AlertMessage = { Defined at -[packages/core-api/src/apis/definitions/AlertApi.ts:19](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/AlertApi.ts#L19). +[packages/core-api/src/apis/definitions/AlertApi.ts:19](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/AlertApi.ts#L19). Referenced by: [post](#post), [alert\$](#alert). @@ -67,7 +67,7 @@ export type Observable<T> = { Defined at -[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L53). +[packages/core-api/src/types.ts:53](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L53). Referenced by: [alert\$](#alert). @@ -86,7 +86,7 @@ export type Observer<T> = { Defined at -[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L24). +[packages/core-api/src/types.ts:24](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L24). Referenced by: [Observable](#observable). @@ -109,6 +109,6 @@ export type Subscription = { Defined at -[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L33). +[packages/core-api/src/types.ts:33](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L33). Referenced by: [Observable](#observable). diff --git a/docs/reference/utility-apis/AppThemeApi.md b/docs/reference/utility-apis/AppThemeApi.md index e103511246..0f5c095825 100644 --- a/docs/reference/utility-apis/AppThemeApi.md +++ b/docs/reference/utility-apis/AppThemeApi.md @@ -1,7 +1,7 @@ # AppThemeApi The AppThemeApi type is defined at -[packages/core-api/src/apis/definitions/AppThemeApi.ts:56](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/AppThemeApi.ts#L56). +[packages/core-api/src/apis/definitions/AppThemeApi.ts:56](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/AppThemeApi.ts#L56). The following Utility API implements this type: [appThemeApiRef](./README.md#apptheme) @@ -81,7 +81,7 @@ export type AppTheme = { Defined at -[packages/core-api/src/apis/definitions/AppThemeApi.ts:25](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/AppThemeApi.ts#L25). +[packages/core-api/src/apis/definitions/AppThemeApi.ts:25](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/AppThemeApi.ts#L25). Referenced by: [getInstalledThemes](#getinstalledthemes). @@ -92,7 +92,7 @@ export type BackstagePalette = Palette & Palette Defined at -[packages/theme/src/types.ts:74](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/theme/src/types.ts#L74). +[packages/theme/src/types.ts:74](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/theme/src/types.ts#L74). Referenced by: [BackstageTheme](#backstagetheme). @@ -107,7 +107,7 @@ export interface BackstageTheme extends Theme { Defined at -[packages/theme/src/types.ts:81](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/theme/src/types.ts#L81). +[packages/theme/src/types.ts:81](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/theme/src/types.ts#L81). Referenced by: [AppTheme](#apptheme). @@ -136,7 +136,7 @@ export type Observable<T> = { Defined at -[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L53). +[packages/core-api/src/types.ts:53](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L53). Referenced by: [activeThemeId\$](#activethemeid). @@ -155,7 +155,7 @@ export type Observer<T> = { Defined at -[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L24). +[packages/core-api/src/types.ts:24](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L24). Referenced by: [Observable](#observable). @@ -170,7 +170,7 @@ export type PageTheme = { Defined at -[packages/theme/src/types.ts:103](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/theme/src/types.ts#L103). +[packages/theme/src/types.ts:103](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/theme/src/types.ts#L103). Referenced by: [BackstageTheme](#backstagetheme). @@ -183,7 +183,7 @@ export type PageThemeSelector = { Defined at -[packages/theme/src/types.ts:77](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/theme/src/types.ts#L77). +[packages/theme/src/types.ts:77](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/theme/src/types.ts#L77). Referenced by: [BackstageTheme](#backstagetheme). @@ -243,7 +243,7 @@ type PaletteAdditions = { Defined at -[packages/theme/src/types.ts:23](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/theme/src/types.ts#L23). +[packages/theme/src/types.ts:23](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/theme/src/types.ts#L23). Referenced by: [BackstagePalette](#backstagepalette). @@ -266,6 +266,6 @@ export type Subscription = { Defined at -[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L33). +[packages/core-api/src/types.ts:33](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L33). Referenced by: [Observable](#observable). diff --git a/docs/reference/utility-apis/BackstageIdentityApi.md b/docs/reference/utility-apis/BackstageIdentityApi.md index 4e86717101..dafdb7d55f 100644 --- a/docs/reference/utility-apis/BackstageIdentityApi.md +++ b/docs/reference/utility-apis/BackstageIdentityApi.md @@ -1,7 +1,7 @@ # BackstageIdentityApi The BackstageIdentityApi type is defined at -[packages/core-api/src/apis/definitions/auth.ts:134](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L134). +[packages/core-api/src/apis/definitions/auth.ts:134](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L134). The following Utility APIs implement this type: @@ -70,7 +70,7 @@ export type AuthRequestOptions = { Defined at -[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L40). +[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L40). Referenced by: [getBackstageIdentity](#getbackstageidentity). @@ -91,6 +91,6 @@ export type BackstageIdentity = { Defined at -[packages/core-api/src/apis/definitions/auth.ts:147](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L147). +[packages/core-api/src/apis/definitions/auth.ts:147](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L147). Referenced by: [getBackstageIdentity](#getbackstageidentity). diff --git a/docs/reference/utility-apis/Config.md b/docs/reference/utility-apis/Config.md index 9d184dd56a..2ae047a700 100644 --- a/docs/reference/utility-apis/Config.md +++ b/docs/reference/utility-apis/Config.md @@ -1,7 +1,7 @@ # Config The Config type is defined at -[packages/config/src/types.ts:32](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/config/src/types.ts#L32). +[packages/config/src/types.ts:32](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/config/src/types.ts#L32). The following Utility API implements this type: [configApiRef](./README.md#config) @@ -140,7 +140,7 @@ export type Config = { Defined at -[packages/config/src/types.ts:32](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/config/src/types.ts#L32). +[packages/config/src/types.ts:32](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/config/src/types.ts#L32). Referenced by: [getConfig](#getconfig), [getOptionalConfig](#getoptionalconfig), [getConfigArray](#getconfigarray), @@ -153,7 +153,7 @@ export type JsonArray = JsonValue[] Defined at -[packages/config/src/types.ts:18](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/config/src/types.ts#L18). +[packages/config/src/types.ts:18](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/config/src/types.ts#L18). Referenced by: [JsonValue](#jsonvalue). @@ -164,7 +164,7 @@ export type JsonObject = { [key in string]?: JsonValue Defined at -[packages/config/src/types.ts:17](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/config/src/types.ts#L17). +[packages/config/src/types.ts:17](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/config/src/types.ts#L17). Referenced by: [JsonValue](#jsonvalue). @@ -181,7 +181,7 @@ export type JsonValue = Defined at -[packages/config/src/types.ts:19](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/config/src/types.ts#L19). +[packages/config/src/types.ts:19](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/config/src/types.ts#L19). Referenced by: [get](#get), [getOptional](#getoptional), [JsonObject](#jsonobject), [JsonArray](#jsonarray), [Config](#config). diff --git a/docs/reference/utility-apis/DiscoveryApi.md b/docs/reference/utility-apis/DiscoveryApi.md index 72f37dc58d..ac278fdd97 100644 --- a/docs/reference/utility-apis/DiscoveryApi.md +++ b/docs/reference/utility-apis/DiscoveryApi.md @@ -1,7 +1,7 @@ # DiscoveryApi The DiscoveryApi type is defined at -[packages/core-api/src/apis/definitions/DiscoveryApi.ts:30](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/DiscoveryApi.ts#L30). +[packages/core-api/src/apis/definitions/DiscoveryApi.ts:30](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/DiscoveryApi.ts#L30). The following Utility API implements this type: [discoveryApiRef](./README.md#discovery) diff --git a/docs/reference/utility-apis/ErrorApi.md b/docs/reference/utility-apis/ErrorApi.md index 2234aaa543..93f4f9cd48 100644 --- a/docs/reference/utility-apis/ErrorApi.md +++ b/docs/reference/utility-apis/ErrorApi.md @@ -1,7 +1,7 @@ # ErrorApi The ErrorApi type is defined at -[packages/core-api/src/apis/definitions/ErrorApi.ts:53](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/ErrorApi.ts#L53). +[packages/core-api/src/apis/definitions/ErrorApi.ts:53](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/ErrorApi.ts#L53). The following Utility API implements this type: [errorApiRef](./README.md#error) @@ -41,7 +41,7 @@ type Error = { Defined at -[packages/core-api/src/apis/definitions/ErrorApi.ts:24](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/ErrorApi.ts#L24). +[packages/core-api/src/apis/definitions/ErrorApi.ts:24](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/ErrorApi.ts#L24). Referenced by: [post](#post), [error\$](#error). @@ -58,7 +58,7 @@ export type ErrorContext = { Defined at -[packages/core-api/src/apis/definitions/ErrorApi.ts:33](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/ErrorApi.ts#L33). +[packages/core-api/src/apis/definitions/ErrorApi.ts:33](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/ErrorApi.ts#L33). Referenced by: [post](#post), [error\$](#error). @@ -87,7 +87,7 @@ export type Observable<T> = { Defined at -[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L53). +[packages/core-api/src/types.ts:53](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L53). Referenced by: [error\$](#error). @@ -106,7 +106,7 @@ export type Observer<T> = { Defined at -[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L24). +[packages/core-api/src/types.ts:24](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L24). Referenced by: [Observable](#observable). @@ -129,6 +129,6 @@ export type Subscription = { Defined at -[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L33). +[packages/core-api/src/types.ts:33](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L33). Referenced by: [Observable](#observable). diff --git a/docs/reference/utility-apis/FeatureFlagsApi.md b/docs/reference/utility-apis/FeatureFlagsApi.md index ed04ecfd8d..529d2ac5dc 100644 --- a/docs/reference/utility-apis/FeatureFlagsApi.md +++ b/docs/reference/utility-apis/FeatureFlagsApi.md @@ -1,7 +1,7 @@ # FeatureFlagsApi The FeatureFlagsApi type is defined at -[packages/core-api/src/apis/definitions/FeatureFlagsApi.ts:60](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L60). +[packages/core-api/src/apis/definitions/FeatureFlagsApi.ts:60](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L60). The following Utility API implements this type: [featureFlagsApiRef](./README.md#featureflags) @@ -68,7 +68,7 @@ export type FeatureFlag = { Defined at -[packages/core-api/src/apis/definitions/FeatureFlagsApi.ts:31](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L31). +[packages/core-api/src/apis/definitions/FeatureFlagsApi.ts:31](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L31). Referenced by: [registerFlag](#registerflag), [getRegisteredFlags](#getregisteredflags). @@ -83,7 +83,7 @@ export enum FeatureFlagState { Defined at -[packages/core-api/src/apis/definitions/FeatureFlagsApi.ts:36](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L36). +[packages/core-api/src/apis/definitions/FeatureFlagsApi.ts:36](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L36). Referenced by: [FeatureFlagsSaveOptions](#featureflagssaveoptions). @@ -108,6 +108,6 @@ export type FeatureFlagsSaveOptions = { Defined at -[packages/core-api/src/apis/definitions/FeatureFlagsApi.ts:44](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L44). +[packages/core-api/src/apis/definitions/FeatureFlagsApi.ts:44](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L44). Referenced by: [save](#save). diff --git a/docs/reference/utility-apis/IdentityApi.md b/docs/reference/utility-apis/IdentityApi.md index 51ea146eec..a88aaf0b8a 100644 --- a/docs/reference/utility-apis/IdentityApi.md +++ b/docs/reference/utility-apis/IdentityApi.md @@ -1,7 +1,7 @@ # IdentityApi The IdentityApi type is defined at -[packages/core-api/src/apis/definitions/IdentityApi.ts:22](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/IdentityApi.ts#L22). +[packages/core-api/src/apis/definitions/IdentityApi.ts:22](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/IdentityApi.ts#L22). The following Utility API implements this type: [identityApiRef](./README.md#identity) @@ -76,6 +76,6 @@ export type ProfileInfo = { Defined at -[packages/core-api/src/apis/definitions/auth.ts:162](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L162). +[packages/core-api/src/apis/definitions/auth.ts:162](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L162). Referenced by: [getProfile](#getprofile). diff --git a/docs/reference/utility-apis/OAuthApi.md b/docs/reference/utility-apis/OAuthApi.md index 42517410fa..79b55812ef 100644 --- a/docs/reference/utility-apis/OAuthApi.md +++ b/docs/reference/utility-apis/OAuthApi.md @@ -1,7 +1,7 @@ # OAuthApi The OAuthApi type is defined at -[packages/core-api/src/apis/definitions/auth.ts:67](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L67). +[packages/core-api/src/apis/definitions/auth.ts:67](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L67). The following Utility APIs implement this type: @@ -82,7 +82,7 @@ export type AuthRequestOptions = { Defined at -[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L40). +[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L40). Referenced by: [getAccessToken](#getaccesstoken). @@ -108,6 +108,6 @@ export type OAuthScope = string | string[] Defined at -[packages/core-api/src/apis/definitions/auth.ts:38](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L38). +[packages/core-api/src/apis/definitions/auth.ts:38](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L38). Referenced by: [getAccessToken](#getaccesstoken). diff --git a/docs/reference/utility-apis/OAuthRequestApi.md b/docs/reference/utility-apis/OAuthRequestApi.md index 690df9dbff..f1c2311ce9 100644 --- a/docs/reference/utility-apis/OAuthRequestApi.md +++ b/docs/reference/utility-apis/OAuthRequestApi.md @@ -1,7 +1,7 @@ # OAuthRequestApi The OAuthRequestApi type is defined at -[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:99](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L99). +[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:99](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L99). The following Utility API implements this type: [oauthRequestApiRef](./README.md#oauthrequest) @@ -73,7 +73,7 @@ export type AuthProvider = { Defined at -[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:27](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L27). +[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:27](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L27). Referenced by: [AuthRequesterOptions](#authrequesteroptions), [PendingAuthRequest](#pendingauthrequest). @@ -97,7 +97,7 @@ export type AuthRequester<AuthResponse> = ( Defined at -[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:66](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L66). +[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:66](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L66). Referenced by: [createAuthRequester](#createauthrequester). @@ -122,7 +122,7 @@ export type AuthRequesterOptions<AuthResponse> = { Defined at -[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:43](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L43). +[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:43](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L43). Referenced by: [createAuthRequester](#createauthrequester). @@ -151,7 +151,7 @@ export type Observable<T> = { Defined at -[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L53). +[packages/core-api/src/types.ts:53](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L53). Referenced by: [authRequest\$](#authrequest). @@ -170,7 +170,7 @@ export type Observer<T> = { Defined at -[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L24). +[packages/core-api/src/types.ts:24](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L24). Referenced by: [Observable](#observable). @@ -205,7 +205,7 @@ export type PendingAuthRequest = { Defined at -[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:77](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L77). +[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:77](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L77). Referenced by: [authRequest\$](#authrequest). @@ -228,6 +228,6 @@ export type Subscription = { Defined at -[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L33). +[packages/core-api/src/types.ts:33](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L33). Referenced by: [Observable](#observable). diff --git a/docs/reference/utility-apis/OpenIdConnectApi.md b/docs/reference/utility-apis/OpenIdConnectApi.md index 11ac589ef1..eaea530708 100644 --- a/docs/reference/utility-apis/OpenIdConnectApi.md +++ b/docs/reference/utility-apis/OpenIdConnectApi.md @@ -1,7 +1,7 @@ # OpenIdConnectApi The OpenIdConnectApi type is defined at -[packages/core-api/src/apis/definitions/auth.ts:99](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L99). +[packages/core-api/src/apis/definitions/auth.ts:99](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L99). The following Utility APIs implement this type: @@ -66,6 +66,6 @@ export type AuthRequestOptions = { Defined at -[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L40). +[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L40). Referenced by: [getIdToken](#getidtoken). diff --git a/docs/reference/utility-apis/ProfileInfoApi.md b/docs/reference/utility-apis/ProfileInfoApi.md index c5b2ae456b..76d04045e8 100644 --- a/docs/reference/utility-apis/ProfileInfoApi.md +++ b/docs/reference/utility-apis/ProfileInfoApi.md @@ -1,7 +1,7 @@ # ProfileInfoApi The ProfileInfoApi type is defined at -[packages/core-api/src/apis/definitions/auth.ts:117](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L117). +[packages/core-api/src/apis/definitions/auth.ts:117](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L117). The following Utility APIs implement this type: @@ -67,7 +67,7 @@ export type AuthRequestOptions = { Defined at -[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L40). +[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L40). Referenced by: [getProfile](#getprofile). @@ -95,6 +95,6 @@ export type ProfileInfo = { Defined at -[packages/core-api/src/apis/definitions/auth.ts:162](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L162). +[packages/core-api/src/apis/definitions/auth.ts:162](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L162). Referenced by: [getProfile](#getprofile). diff --git a/docs/reference/utility-apis/README.md b/docs/reference/utility-apis/README.md index 84674d9fff..1ef5b6197e 100644 --- a/docs/reference/utility-apis/README.md +++ b/docs/reference/utility-apis/README.md @@ -3,7 +3,7 @@ The following is a list of all Utility APIs defined by `@backstage/core`. They are available to use by plugins and components, and can be accessed using the `useApi` hook, also provided by `@backstage/core`. For more information, see -https://github.com/spotify/backstage/blob/master/docs/api/utility-apis.md. +https://github.com/backstage/backstage/blob/master/docs/api/utility-apis.md. ### alert @@ -12,7 +12,7 @@ Used to report alerts and forward them to the app Implemented type: [AlertApi](./AlertApi.md) ApiRef: -[alertApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/AlertApi.ts#L41) +[alertApiRef](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/AlertApi.ts#L41) ### appTheme @@ -21,7 +21,7 @@ API Used to configure the app theme, and enumerate options Implemented type: [AppThemeApi](./AppThemeApi.md) ApiRef: -[appThemeApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/AppThemeApi.ts#L80) +[appThemeApiRef](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/AppThemeApi.ts#L80) ### auth0Auth @@ -32,7 +32,7 @@ Implemented types: [OpenIdConnectApi](./OpenIdConnectApi.md), [BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) ApiRef: -[auth0AuthApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L275) +[auth0AuthApiRef](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L275) ### config @@ -41,7 +41,7 @@ Used to access runtime configuration Implemented type: [Config](./Config.md) ApiRef: -[configApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/ConfigApi.ts#L22) +[configApiRef](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/ConfigApi.ts#L22) ### discovery @@ -50,7 +50,7 @@ Provides service discovery of backend plugins Implemented type: [DiscoveryApi](./DiscoveryApi.md) ApiRef: -[discoveryApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/DiscoveryApi.ts#L44) +[discoveryApiRef](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/DiscoveryApi.ts#L44) ### error @@ -59,7 +59,7 @@ Used to report errors and forward them to the app Implemented type: [ErrorApi](./ErrorApi.md) ApiRef: -[errorApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/ErrorApi.ts#L65) +[errorApiRef](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/ErrorApi.ts#L65) ### featureFlags @@ -68,7 +68,7 @@ Used to toggle functionality in features across Backstage Implemented type: [FeatureFlagsApi](./FeatureFlagsApi.md) ApiRef: -[featureFlagsApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L83) +[featureFlagsApiRef](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L83) ### githubAuth @@ -79,7 +79,7 @@ Implemented types: [OAuthApi](./OAuthApi.md), [BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) ApiRef: -[githubAuthApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L232) +[githubAuthApiRef](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L232) ### gitlabAuth @@ -90,7 +90,7 @@ Implemented types: [OAuthApi](./OAuthApi.md), [BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) ApiRef: -[gitlabAuthApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L262) +[gitlabAuthApiRef](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L262) ### googleAuth @@ -102,7 +102,7 @@ Implemented types: [OAuthApi](./OAuthApi.md), [BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) ApiRef: -[googleAuthApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L215) +[googleAuthApiRef](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L215) ### identity @@ -111,7 +111,7 @@ Provides access to the identity of the signed in user Implemented type: [IdentityApi](./IdentityApi.md) ApiRef: -[identityApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/IdentityApi.ts#L54) +[identityApiRef](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/IdentityApi.ts#L54) ### microsoftAuth @@ -123,7 +123,7 @@ Implemented types: [OAuthApi](./OAuthApi.md), [BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) ApiRef: -[microsoftAuthApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L289) +[microsoftAuthApiRef](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L289) ### oauth2 @@ -135,7 +135,7 @@ Implemented types: [OAuthApi](./OAuthApi.md), [BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) ApiRef: -[oauth2ApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L303) +[oauth2ApiRef](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L303) ### oauthRequest @@ -144,7 +144,7 @@ An API for implementing unified OAuth flows in Backstage Implemented type: [OAuthRequestApi](./OAuthRequestApi.md) ApiRef: -[oauthRequestApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L130) +[oauthRequestApiRef](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L130) ### oktaAuth @@ -156,7 +156,7 @@ Implemented types: [OAuthApi](./OAuthApi.md), [BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) ApiRef: -[oktaAuthApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L245) +[oktaAuthApiRef](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L245) ### samlAuth @@ -166,7 +166,7 @@ Implemented types: [ProfileInfoApi](./ProfileInfoApi.md), [BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) ApiRef: -[samlAuthApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L317) +[samlAuthApiRef](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L317) ### storage @@ -175,4 +175,4 @@ Provides the ability to store data which is unique to the user Implemented type: [StorageApi](./StorageApi.md) ApiRef: -[storageApiRef](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/StorageApi.ts#L68) +[storageApiRef](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/StorageApi.ts#L68) diff --git a/docs/reference/utility-apis/SessionApi.md b/docs/reference/utility-apis/SessionApi.md index f2e9097143..7d271558d6 100644 --- a/docs/reference/utility-apis/SessionApi.md +++ b/docs/reference/utility-apis/SessionApi.md @@ -1,7 +1,7 @@ # SessionApi The SessionApi type is defined at -[packages/core-api/src/apis/definitions/auth.ts:190](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L190). +[packages/core-api/src/apis/definitions/auth.ts:190](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L190). The following Utility APIs implement this type: @@ -77,7 +77,7 @@ export type Observable<T> = { Defined at -[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L53). +[packages/core-api/src/types.ts:53](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L53). Referenced by: [sessionState\$](#sessionstate). @@ -96,7 +96,7 @@ export type Observer<T> = { Defined at -[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L24). +[packages/core-api/src/types.ts:24](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L24). Referenced by: [Observable](#observable). @@ -112,7 +112,7 @@ export enum SessionState { Defined at -[packages/core-api/src/apis/definitions/auth.ts:182](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L182). +[packages/core-api/src/apis/definitions/auth.ts:182](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/auth.ts#L182). Referenced by: [sessionState\$](#sessionstate). @@ -135,6 +135,6 @@ export type Subscription = { Defined at -[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L33). +[packages/core-api/src/types.ts:33](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L33). Referenced by: [Observable](#observable). diff --git a/docs/reference/utility-apis/SessionStateApi.md b/docs/reference/utility-apis/SessionStateApi.md index 81f25aa349..a8a4c3bc3d 100644 --- a/docs/reference/utility-apis/SessionStateApi.md +++ b/docs/reference/utility-apis/SessionStateApi.md @@ -1,7 +1,7 @@ # SessionStateApi The SessionStateApi type is defined at -[packages/core-api/src/apis/definitions/auth.ts:201](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L201). +[packages/core-api/src/apis/definitions/auth.ts:201](https://github.com/backstage/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L201). The following Utility APIs implement this type: @@ -56,7 +56,7 @@ export type Observable<T> = { Defined at -[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L53). +[packages/core-api/src/types.ts:53](https://github.com/backstage/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L53). Referenced by: [sessionState\$](#sessionstate). @@ -75,7 +75,7 @@ export type Observer<T> = { Defined at -[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L24). +[packages/core-api/src/types.ts:24](https://github.com/backstage/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L24). Referenced by: [Observable](#observable). @@ -91,7 +91,7 @@ export enum SessionState { Defined at -[packages/core-api/src/apis/definitions/auth.ts:192](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L192). +[packages/core-api/src/apis/definitions/auth.ts:192](https://github.com/backstage/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L192). Referenced by: [sessionState\$](#sessionstate). @@ -114,6 +114,6 @@ export type Subscription = { Defined at -[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L33). +[packages/core-api/src/types.ts:33](https://github.com/backstage/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L33). Referenced by: [Observable](#observable). diff --git a/docs/reference/utility-apis/StorageApi.md b/docs/reference/utility-apis/StorageApi.md index 9fe10e358b..6c5595d3d8 100644 --- a/docs/reference/utility-apis/StorageApi.md +++ b/docs/reference/utility-apis/StorageApi.md @@ -1,7 +1,7 @@ # StorageApi The StorageApi type is defined at -[packages/core-api/src/apis/definitions/StorageApi.ts:31](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/StorageApi.ts#L31). +[packages/core-api/src/apis/definitions/StorageApi.ts:31](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/StorageApi.ts#L31). The following Utility API implements this type: [storageApiRef](./README.md#storage) @@ -79,7 +79,7 @@ export type Observable<T> = { Defined at -[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L53). +[packages/core-api/src/types.ts:53](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L53). Referenced by: [observe\$](#observe), [StorageApi](#storageapi). @@ -98,7 +98,7 @@ export type Observer<T> = { Defined at -[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L24). +[packages/core-api/src/types.ts:24](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L24). Referenced by: [Observable](#observable). @@ -144,7 +144,7 @@ export interface StorageApi { Defined at -[packages/core-api/src/apis/definitions/StorageApi.ts:31](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/StorageApi.ts#L31). +[packages/core-api/src/apis/definitions/StorageApi.ts:31](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/StorageApi.ts#L31). Referenced by: [forBucket](#forbucket). @@ -158,7 +158,7 @@ export type StorageValueChange<T = any> = { Defined at -[packages/core-api/src/apis/definitions/StorageApi.ts:21](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/StorageApi.ts#L21). +[packages/core-api/src/apis/definitions/StorageApi.ts:21](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/apis/definitions/StorageApi.ts#L21). Referenced by: [observe\$](#observe), [StorageApi](#storageapi). @@ -181,6 +181,6 @@ export type Subscription = { Defined at -[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L33). +[packages/core-api/src/types.ts:33](https://github.com/backstage/backstage/blob/ca535f2f66c3a4980c80f4b1a049dfd07569010e/packages/core-api/src/types.ts#L33). Referenced by: [Observable](#observable). diff --git a/docs/support/project-structure.md b/docs/support/project-structure.md index 9720c4db3e..9bf143e0f2 100644 --- a/docs/support/project-structure.md +++ b/docs/support/project-structure.md @@ -15,48 +15,48 @@ In the project root, there are a set of files and folders which are not part of the project as such, and may or may not be familiar to someone looking through the code. -- [`.changeset/`](https://github.com/spotify/backstage/tree/master/.changeset) - +- [`.changeset/`](https://github.com/backstage/backstage/tree/master/.changeset) - This folder contains files outlining which changes occurred in the project since the last release. These files are added manually, but managed by [changesets](https://github.com/atlassian/changesets) and will be removed at every new release. They are essentially building-blocks of a CHANGELOG. -- [`.github/`](https://github.com/spotify/backstage/tree/master/.github) - +- [`.github/`](https://github.com/backstage/backstage/tree/master/.github) - Standard GitHub folder. It contains - amongst other things - our workflow definitions and templates. Worth noting is the - [styles](https://github.com/spotify/backstage/tree/master/.github/styles) + [styles](https://github.com/backstage/backstage/tree/master/.github/styles) folder which is used for a markdown spellchecker. -- [`.yarn/`](https://github.com/spotify/backstage/tree/master/.yarn) - Backstage - ships with it's own `yarn` implementation. This allows us to have better - control over our `yarn.lock` file and hopefully avoid problems due to yarn - versioning differences. +- [`.yarn/`](https://github.com/backstage/backstage/tree/master/.yarn) - + Backstage ships with it's own `yarn` implementation. This allows us to have + better control over our `yarn.lock` file and hopefully avoid problems due to + yarn versioning differences. -- [`docker/`](https://github.com/spotify/backstage/tree/master/docker) - Files +- [`docker/`](https://github.com/backstage/backstage/tree/master/docker) - Files related to our root Dockerfile. We are planning to refactor this, so expect this folder to be moved in the future. -- [`contrib/`](https://github.com/spotify/backstage/tree/master/contrib) - +- [`contrib/`](https://github.com/backstage/backstage/tree/master/contrib) - Collection of examples or resources provided by the community. We really appreciate contributions in here and encourage them being kept up to date. -- [`docs/`](https://github.com/spotify/backstage/tree/master/docs) - This is +- [`docs/`](https://github.com/backstage/backstage/tree/master/docs) - This is where we keep all of our documentation Markdown files. These ends up on http://backstage.io/docs. Just keep in mind that changes to the - [`sidebars.json`](https://github.com/spotify/backstage/blob/master/microsite/sidebars.json) + [`sidebars.json`](https://github.com/backstage/backstage/blob/master/microsite/sidebars.json) file may be needed as sections are added/removed. -- [`.editorconfig`](https://github.com/spotify/backstage/tree/master/.editorconfig) - +- [`.editorconfig`](https://github.com/backstage/backstage/tree/master/.editorconfig) - A configuration file used by most common code editors. -- [`.imgbotconfig`](https://github.com/spotify/backstage/tree/master/.imgbotconfig) - +- [`.imgbotconfig`](https://github.com/backstage/backstage/tree/master/.imgbotconfig) - Configuration for a [bot](https://imgbot.net/) ## Monorepo packages Every folder in both `packages/` and `plugins/` is within our monorepo setup, as defined in -[`package.json`](https://github.com/spotify/backstage/blob/master/package.json): +[`package.json`](https://github.com/backstage/backstage/blob/master/package.json): ```json "workspaces": { @@ -74,30 +74,30 @@ Let's look at them individually. These are all the packages that we use within the project. [Plugins](#plugins) are separated out into their own folder, see further down. -- [`app/`](https://github.com/spotify/backstage/tree/master/packages/app) - This - is our take on how an App could look like, bringing together a set of packages - and plugins into a working Backstage App. This is not a published package, and - the main goals are to provide a demo of what an App could look like and to - enable local development. +- [`app/`](https://github.com/backstage/backstage/tree/master/packages/app) - + This is our take on how an App could look like, bringing together a set of + packages and plugins into a working Backstage App. This is not a published + package, and the main goals are to provide a demo of what an App could look + like and to enable local development. -- [`backend/`](https://github.com/spotify/backstage/tree/master/packages/backend) - +- [`backend/`](https://github.com/backstage/backstage/tree/master/packages/backend) - Every standalone Backstage project will have both an `app` _and_ a `backend` package. The `backend` uses plugins to construct a working backend that the frontend (`app`) can use. -- [`backend-common/`](https://github.com/spotify/backstage/tree/master/packages/backend-common) - +- [`backend-common/`](https://github.com/backstage/backstage/tree/master/packages/backend-common) - There are no "core" packages in the backend. Instead we have `backend-common` which contains helper middleware and other utils. -- [`catalog-model/`](https://github.com/spotify/backstage/tree/master/packages/catalog-model) - +- [`catalog-model/`](https://github.com/backstage/backstage/tree/master/packages/catalog-model) - You can consider this to be a library for working with the catalog of sorts. It contains the definition of an [Entity](https://backstage.io/docs/features/software-catalog/references#docsNav), as well as validation and other logic related to it. This package can be used in both the frontend and the backend. -- [`cli/`](https://github.com/spotify/backstage/tree/master/packages/cli) - One - of the biggest packages in our project, the `cli` is used to build, serve, +- [`cli/`](https://github.com/backstage/backstage/tree/master/packages/cli) - + One of the biggest packages in our project, the `cli` is used to build, serve, diff, create-plugins and more. In the early days of this project, we started out with calling tools directly - such as `eslint` - through `package.json`. But as it was tricky to have a good development experience around that when we @@ -105,86 +105,87 @@ are separated out into their own folder, see further down. everything looks the same in `package.json`. Much like [react-scripts](https://github.com/facebook/create-react-app/tree/master/packages/react-scripts). -- [`cli-common/`](https://github.com/spotify/backstage/tree/master/packages/cli-common) - +- [`cli-common/`](https://github.com/backstage/backstage/tree/master/packages/cli-common) - This package mainly handles path resolving. It is a separate package to reduce - bugs in [cli](https://github.com/spotify/backstage/tree/master/packages/cli). - We also want as few dependencies as possible to reduce download time when - running the cli which is another reason this is a separate package. + bugs in + [cli](https://github.com/backstage/backstage/tree/master/packages/cli). We + also want as few dependencies as possible to reduce download time when running + the cli which is another reason this is a separate package. -- [`config/`](https://github.com/spotify/backstage/tree/master/packages/config) - +- [`config/`](https://github.com/backstage/backstage/tree/master/packages/config) - The way we read configuration data. This package can take a bunch of config objects and merge them together. - [app-config.yaml](https://github.com/spotify/backstage/blob/master/app-config.yaml) + [app-config.yaml](https://github.com/backstage/backstage/blob/master/app-config.yaml) is an example of an config object. -- [`config-loader/`](https://github.com/spotify/backstage/tree/master/packages/config-loader) - +- [`config-loader/`](https://github.com/backstage/backstage/tree/master/packages/config-loader) - This package is used to read config objects. It does not know how to merge, but only reads files and passes them on to the config. As this part is only used by the backend, we chose to separate `config` and `config-loader` into two different packages. -- [`core/`](https://github.com/spotify/backstage/tree/master/packages/core) - +- [`core/`](https://github.com/backstage/backstage/tree/master/packages/core) - This package contains our visual React components, some of which you can find in [plugin examples](https://backstage.io/storybook/?path=/story/plugins-examples--plugin-with-data). Apart from that it re-exports everything from [`core-api`] so that users only need to rely on one package. -- [`core-api/`](https://github.com/spotify/backstage/tree/master/packages/core-api) - +- [`core-api/`](https://github.com/backstage/backstage/tree/master/packages/core-api) - This package contains APIs and definitions of such. It is it's own package because we needed to split our `test-utils` package. It's an implementation detail that we try to hide from our users, and no one should have to depend on it directly. -- [`test-utils/`](https://github.com/spotify/backstage/tree/master/packages/test-utils) - +- [`test-utils/`](https://github.com/backstage/backstage/tree/master/packages/test-utils) - This package contains specific testing facilities used when testing `core-api`. -- [`test-utils-core/`](https://github.com/spotify/backstage/tree/master/packages/test-utils-core) - +- [`test-utils-core/`](https://github.com/backstage/backstage/tree/master/packages/test-utils-core) - This package contains more general purpose testing facilities for testing a Backstage App. -- [`create-app/`](https://github.com/spotify/backstage/tree/master/packages/create-app) - +- [`create-app/`](https://github.com/backstage/backstage/tree/master/packages/create-app) - An CLI to specifically scaffold a new Backstage App. It does so by using a - [template](https://github.com/spotify/backstage/tree/master/packages/create-app/templates/default-app). + [template](https://github.com/backstage/backstage/tree/master/packages/create-app/templates/default-app). -- [`dev-utils/`](https://github.com/spotify/backstage/tree/master/packages/dev-utils) - +- [`dev-utils/`](https://github.com/backstage/backstage/tree/master/packages/dev-utils) - Helps you setup a plugin for isolated development so that it can be served separately. -- [`docgen/`](https://github.com/spotify/backstage/tree/master/packages/docgen) - +- [`docgen/`](https://github.com/backstage/backstage/tree/master/packages/docgen) - Uses the [Typescript Compiler API](https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API) to read out definitions and generate documentation for it. -- [`e2e-test/`](https://github.com/spotify/backstage/tree/master/packages/e2e-test) - +- [`e2e-test/`](https://github.com/backstage/backstage/tree/master/packages/e2e-test) - Another CLI that can be run to try out what would happen if you built all the packages, publish them, created a new app, and the run it. CI uses this for e2e-tests. -- [`storybook/`](https://github.com/spotify/backstage/tree/master/packages/storybook) - +- [`storybook/`](https://github.com/backstage/backstage/tree/master/packages/storybook) - This folder contains only the storybook config. Stories are within the core package. The Backstage Storybook is found [here](https://backstage.io/storybook) -- [`techdocs-cli/`](https://github.com/spotify/backstage/tree/master/packages/techdocs-cli) - +- [`techdocs-cli/`](https://github.com/backstage/backstage/tree/master/packages/techdocs-cli) - Used for verifying TechDocs locally. -- [`techdocs-container/`](https://github.com/spotify/backstage/tree/master/packages/techdocs-container) - +- [`techdocs-container/`](https://github.com/backstage/backstage/tree/master/packages/techdocs-container) - Used by the `techdocs-cli` -- [`test-utils-core/`](https://github.com/spotify/backstage/tree/master/packages/test-utils-core) +- [`test-utils-core/`](https://github.com/backstage/backstage/tree/master/packages/test-utils-core) -- [`test-utils/`](https://github.com/spotify/backstage/tree/master/packages/test-utils) +- [`test-utils/`](https://github.com/backstage/backstage/tree/master/packages/test-utils) -- [`theme/`](https://github.com/spotify/backstage/tree/master/packages/theme) - +- [`theme/`](https://github.com/backstage/backstage/tree/master/packages/theme) - Holds the Backstage Theme. ### `plugins/` Most of the functionality of a Backstage App comes from plugins. Even core features can be plugins, take the -[catalog](https://github.com/spotify/backstage/tree/master/plugins/catalog) as +[catalog](https://github.com/backstage/backstage/tree/master/plugins/catalog) as an example. We can categorize plugins into three different types; **Frontend**, **Backend** @@ -196,18 +197,18 @@ reason is for clear separation of concerns. Take a look at our [Plugin Gallery](https://backstage.io/plugins) or browse through the -[`plugins/`](https://github.com/spotify/backstage/tree/master/plugins) folder. +[`plugins/`](https://github.com/backstage/backstage/tree/master/plugins) folder. ## Packages outside of the monorepo For convenience we include packages in our project that are not part of our monorepo setup. -- [`microsite/`](https://github.com/spotify/backstage/blob/master/microsite) - +- [`microsite/`](https://github.com/backstage/backstage/blob/master/microsite) - This folder contains the source code for backstage.io. It is built with [Docusaurus](https://docusaurus.io/). This folder is not part of the monorepo due to dependency reasons. Look at the - [README](https://github.com/spotify/backstage/blob/master/microsite/README.md) + [README](https://github.com/backstage/backstage/blob/master/microsite/README.md) for instructions on how to run it locally. ## Root files specifically used by the `app` @@ -216,22 +217,22 @@ These files are kept in the root of the project mostly by historical reasons. Some of these files may be subject to be moved out of the root sometime in the future. -- [`.npmrc`](https://github.com/spotify/backstage/tree/master/.npmrc) - It's +- [`.npmrc`](https://github.com/backstage/backstage/tree/master/.npmrc) - It's common for companies to have their own npm registry, this files makes sure that this folder use the public registry. -- [`.vale.ini`](https://github.com/spotify/backstage/tree/master/.vale.ini) - +- [`.vale.ini`](https://github.com/backstage/backstage/tree/master/.vale.ini) - [Spell checker](https://github.com/errata-ai/vale) for Markdown files. -- [`.yarnrc`](https://github.com/spotify/backstage/tree/master/.yarnrc) - +- [`.yarnrc`](https://github.com/backstage/backstage/tree/master/.yarnrc) - Enforces "our" version of Yarn. -- [`app-config.yaml`](https://github.com/spotify/backstage/tree/master/app-config.yaml) - +- [`app-config.yaml`](https://github.com/backstage/backstage/tree/master/app-config.yaml) - Configuration for the app, both frontend and backend. -- [`catalog-info.yaml`](https://github.com/spotify/backstage/tree/master/catalog-info.yaml) - +- [`catalog-info.yaml`](https://github.com/backstage/backstage/tree/master/catalog-info.yaml) - Description of Backstage in the Backstage Entity format. -- [`lerna.json`](https://github.com/spotify/backstage/tree/master/lerna.json) - +- [`lerna.json`](https://github.com/backstage/backstage/tree/master/lerna.json) - [Lerna](https://github.com/lerna/lerna) monorepo config. We are using `yarn workspaces`, so this will only be used for executing scripts. diff --git a/docs/support/support.md b/docs/support/support.md index 12bc7cfaf9..797aca041f 100644 --- a/docs/support/support.md +++ b/docs/support/support.md @@ -6,9 +6,9 @@ description: Support and Community Details and Links - [Discord chatroom](https://discord.gg/MUpMjP2) - Get support or discuss the project -- [Good First Issues](https://github.com/spotify/backstage/contribute) - Start +- [Good First Issues](https://github.com/backstage/backstage/contribute) - Start here if you want to contribute -- [RFCs](https://github.com/spotify/backstage/labels/rfc) - Help shape the +- [RFCs](https://github.com/backstage/backstage/labels/rfc) - Help shape the technical direction - [FAQ](../FAQ.md) - Frequently Asked Questions - [Code of Conduct](../../CODE_OF_CONDUCT.md) - This is how we roll diff --git a/docs/tutorials/quickstart-app-plugin.md b/docs/tutorials/quickstart-app-plugin.md index daf76aa1cd..216ff9659f 100644 --- a/docs/tutorials/quickstart-app-plugin.md +++ b/docs/tutorials/quickstart-app-plugin.md @@ -120,10 +120,10 @@ If everything is saved, you should see your name, id, and email on the github-playground page. Our data accessed is synchronous. So we just grab and go. -https://github.com/spotify/backstage/tree/master/contrib +https://github.com/backstage/backstage/tree/master/contrib 6. Here is the entire file for reference - [ExampleComponent.tsx](https://github.com/spotify/backstage/tree/master/contrib/docs/tutorials/quickstart-app-plugin/ExampleComponent.md) + [ExampleComponent.tsx](https://github.com/backstage/backstage/tree/master/contrib/docs/tutorials/quickstart-app-plugin/ExampleComponent.md) # The Wipe @@ -302,7 +302,7 @@ return ( 8. After saving that, and given we don't have any errors, you should see a table with basic information on your repositories. 9. Here is the entire file for reference - [ExampleFetchComponent.tsx](https://github.com/spotify/backstage/tree/master/contrib/docs/tutorials/quickstart-app-plugin/ExampleFetchComponent.md) + [ExampleFetchComponent.tsx](https://github.com/backstage/backstage/tree/master/contrib/docs/tutorials/quickstart-app-plugin/ExampleFetchComponent.md) 10. We finished! You should see your own GitHub repository's information displayed in a basic table. If you run into issues, you can compare the repo that backs this document, diff --git a/microsite/blog/2020-03-18-what-is-backstage.md b/microsite/blog/2020-03-18-what-is-backstage.md index 8b0a0fcbe6..0a0ff9e0b1 100644 --- a/microsite/blog/2020-03-18-what-is-backstage.md +++ b/microsite/blog/2020-03-18-what-is-backstage.md @@ -81,8 +81,8 @@ Similar to how Backstage ties together all of Spotify’s infrastructure, our am ## What’s next? -We are envisioning [three phases](https://github.com/spotify/backstage/milestones) of the project (so far), and we have already begun work on various aspects of these phases. The best way to track the work and see where you can jump in and help out is: +We are envisioning [three phases](https://github.com/backstage/backstage/milestones) of the project (so far), and we have already begun work on various aspects of these phases. The best way to track the work and see where you can jump in and help out is: -https://github.com/spotify/backstage/milestones +https://github.com/backstage/backstage/milestones Want to discuss the project or need support? Join us on [Discord](https://discord.gg/MUpMjP2) or reach out on [alund@spotify.com](mailto:alund@spotify.com). diff --git a/microsite/blog/2020-04-06-lighthouse-plugin.md b/microsite/blog/2020-04-06-lighthouse-plugin.md index 2d5c7b96b7..b8fd68e783 100644 --- a/microsite/blog/2020-04-06-lighthouse-plugin.md +++ b/microsite/blog/2020-04-06-lighthouse-plugin.md @@ -33,7 +33,7 @@ Trigger an audit directly from Backstage, or trigger audits programmatically wit ## Using Lighthouse in Backstage -To learn how you can enable Lighthouse auditing within Backstage, head over to the [README](https://github.com/spotify/backstage/tree/master/plugins/lighthouse) for the plugin to get started. +To learn how you can enable Lighthouse auditing within Backstage, head over to the [README](https://github.com/backstage/backstage/tree/master/plugins/lighthouse) for the plugin to get started. ## A personal note diff --git a/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.md b/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.md index dff567eaf3..85f40dd9ea 100644 --- a/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.md +++ b/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.md @@ -132,4 +132,4 @@ If you are developing a plugin that might be useful for others, consider releasi ## Ready to get started? -Head over to GitHub and check out the [project](https://github.com/spotify/backstage) or download our [CLI](https://www.npmjs.com/package/@backstage/cli). If you have more questions, join us on [Discord](https://discord.gg/MUpMjP2) or [create an issue](https://github.com/spotify/backstage/issues/new/choose). +Head over to GitHub and check out the [project](https://github.com/backstage/backstage) or download our [CLI](https://www.npmjs.com/package/@backstage/cli). If you have more questions, join us on [Discord](https://discord.gg/MUpMjP2) or [create an issue](https://github.com/backstage/backstage/issues/new/choose). diff --git a/microsite/blog/2020-05-14-tech-radar-plugin.md b/microsite/blog/2020-05-14-tech-radar-plugin.md index d78d5d67fa..80c1eb8b5a 100644 --- a/microsite/blog/2020-05-14-tech-radar-plugin.md +++ b/microsite/blog/2020-05-14-tech-radar-plugin.md @@ -34,11 +34,11 @@ To learn about how you can bring the Tech Radar to your Backstage installation, I want to thank both the Backstage team and Spotify. Firstly, I’ve been working with our internal version of Backstage for over a year, and the developer experience since open sourcing has been even more of a joy to work with. Secondly, the 10% hack time that Spotify generously provides to all engineers enabled me to open source the Tech Radar plugin. -Since open sourcing it, the community has shown great interest in yet another powerful use case of Backstage. There was also an enthusiastic open source contributor who volunteered to migrate the plugin to TypeScript and React Hooks [in just 29 minutes](https://github.com/spotify/backstage/issues/661) of opening the issue! +Since open sourcing it, the community has shown great interest in yet another powerful use case of Backstage. There was also an enthusiastic open source contributor who volunteered to migrate the plugin to TypeScript and React Hooks [in just 29 minutes](https://github.com/backstage/backstage/issues/661) of opening the issue! I can’t wait to see how others benefit from the Tech Radar in their organizations! [lighthouse website audits]: https://backstage.io/blog/2020/04/06/lighthouse-plugin -[tech radar plugin]: https://github.com/spotify/backstage/tree/master/plugins/tech-radar +[tech radar plugin]: https://github.com/backstage/backstage/tree/master/plugins/tech-radar [thoughtworks]: https://www.thoughtworks.com/radar [zalando]: https://opensource.zalando.com/tech-radar/ diff --git a/microsite/blog/2020-05-22-phase-2-service-catalog.md b/microsite/blog/2020-05-22-phase-2-service-catalog.md index 5c988c03aa..103750dcc6 100644 --- a/microsite/blog/2020-05-22-phase-2-service-catalog.md +++ b/microsite/blog/2020-05-22-phase-2-service-catalog.md @@ -7,7 +7,7 @@ authorImageURL: https://pbs.twimg.com/profile_images/121166861/6919c047c0d0edaac **TL;DR** Thanks to the help from the Backstage community, we’ve made excellent progress and are now moving into Phase 2 of Backstage — building out a Service Catalog and the surrounding systems that will help unify the tools you use to manage your software. -We released the open source version of Backstage a little less than two months ago, and have been thrilled to see so many people jumping in and contributing to the project in its early stages. We’re excited to see what the community can build together as we progress through [each phase of Backstage](https://github.com/spotify/backstage#project-roadmap). +We released the open source version of Backstage a little less than two months ago, and have been thrilled to see so many people jumping in and contributing to the project in its early stages. We’re excited to see what the community can build together as we progress through [each phase of Backstage](https://github.com/backstage/backstage#project-roadmap). ![img](assets/20-05-20/Service_Catalog_MVP.png) @@ -15,7 +15,7 @@ We released the open source version of Backstage a little less than two months a ## Progress so far -Phase 1 was all about building an extensible frontend platform, enabling teams to start creating a single, consistent UI layer for your internal infrastructure and tools in the form of [plugins](https://github.com/spotify/backstage/labels/plugin). In fact, thanks to our amazing (30+) [contributors](https://github.com/spotify/backstage/graphs/contributors), we were able to complete most of Phase 1 earlier than expected. 🎉 +Phase 1 was all about building an extensible frontend platform, enabling teams to start creating a single, consistent UI layer for your internal infrastructure and tools in the form of [plugins](https://github.com/backstage/backstage/labels/plugin). In fact, thanks to our amazing (30+) [contributors](https://github.com/backstage/backstage/graphs/contributors), we were able to complete most of Phase 1 earlier than expected. 🎉 Today, we are happy to announce that we are shifting our focus to Phase 2! @@ -27,7 +27,7 @@ Quote from [Platform Nuts & Bolts: Extendable Data Models](https://www.kislayver Entities, or what we refer to as “components” in Backstage, represent all software, including services, websites, libraries, data pipelines, and so forth. The focus of Phase 2 will be on adding an entity model in Backstage that makes it easy for engineers to create and manage the software components they own. -With the ability to create a plethora of components in Backstage, how does one keep track of all the software in the ecosystem? Therein lies the highlight feature of Phase 2: the [Service Catalog](https://github.com/spotify/backstage/milestone/4). The service catalog — or software catalog — is a centralized system that keeps track of ownership and metadata about all software in your ecosystem. The catalog is built around the concept of [metadata yaml files](/docs/architecture-decisions/adrs-adr002) stored together with the code, which are then harvested and visualized in Backstage. +With the ability to create a plethora of components in Backstage, how does one keep track of all the software in the ecosystem? Therein lies the highlight feature of Phase 2: the [Service Catalog](https://github.com/backstage/backstage/milestone/4). The service catalog — or software catalog — is a centralized system that keeps track of ownership and metadata about all software in your ecosystem. The catalog is built around the concept of [metadata yaml files](/docs/architecture-decisions/adrs-adr002) stored together with the code, which are then harvested and visualized in Backstage. ![img](assets/20-05-20/Service_Catalog_MVP.png) @@ -39,13 +39,13 @@ On top of that, we have found that the service catalog is a great way to organis ![img](assets/20-05-20/tabs.png) -More concretely, having this structure in place will allow plugins such as [CircleCI](https://github.com/spotify/backstage/tree/master/plugins/circleci) to show only the builds for the specific service you are viewing, or a [Spinnaker](https://github.com/spotify/backstage/issues/631) plugin to show running deployments, or an Open API plugin to [show documentation](https://github.com/spotify/backstage/issues/627) for endpoints exposed by the service, or the [Lighthouse](https://github.com/spotify/backstage/tree/master/plugins/lighthouse) plugin to show audit reports for your website. You get the point. +More concretely, having this structure in place will allow plugins such as [CircleCI](https://github.com/backstage/backstage/tree/master/plugins/circleci) to show only the builds for the specific service you are viewing, or a [Spinnaker](https://github.com/backstage/backstage/issues/631) plugin to show running deployments, or an Open API plugin to [show documentation](https://github.com/backstage/backstage/issues/627) for endpoints exposed by the service, or the [Lighthouse](https://github.com/backstage/backstage/tree/master/plugins/lighthouse) plugin to show audit reports for your website. You get the point. ## Timeline Our estimated timeline has us delivering these pieces in increments leading up to June 22. But with the support of the community we wouldn’t be surprised if things land earlier than that. 🙏 -If you are interested in joining us, check out our [Milestones](https://github.com/spotify/backstage/milestones) and connected Issues. +If you are interested in joining us, check out our [Milestones](https://github.com/backstage/backstage/milestones) and connected Issues. ## Long-term vision diff --git a/microsite/blog/2020-06-22-backstage-service-catalog-alpha.md b/microsite/blog/2020-06-22-backstage-service-catalog-alpha.md index 167cc753f8..eeed43b713 100644 --- a/microsite/blog/2020-06-22-backstage-service-catalog-alpha.md +++ b/microsite/blog/2020-06-22-backstage-service-catalog-alpha.md @@ -40,7 +40,7 @@ You’ll be able to see many of these virtues in action with this alpha release Alpha is our shorthand for "we don’t yet think Backstage is ready for production, but we’d love for you to test it and provide us with feedback". However, you should be able to try out the functionality of the service catalog: -1. Register software components ([examples](https://github.com/spotify/backstage/tree/master/packages/catalog-model/examples)) +1. Register software components ([examples](https://github.com/backstage/backstage/tree/master/packages/catalog-model/examples)) 2. See all components represented in the catalog 3. Search across all components 4. Get an overview of the metadata of the components @@ -49,6 +49,6 @@ Alpha is our shorthand for "we don’t yet think Backstage is ready for producti As with most alpha releases, you should expect things to change quite a lot until we reach the beta stage (we’re targeting the end of summer). There are obviously many things missing as well, but we wanted to start collecting feedback early and make it easier to see the end-to-end flow. -If you have feedback or questions, please open a [GitHub issue](https://github.com/spotify/backstage/issues), ping us on [Discord chat](https://discord.gg/EBHEGzX) or send me an email at [alund@spotify.com](mailto:alund@spotify.com) 🙏 +If you have feedback or questions, please open a [GitHub issue](https://github.com/backstage/backstage/issues), ping us on [Discord chat](https://discord.gg/EBHEGzX) or send me an email at [alund@spotify.com](mailto:alund@spotify.com) 🙏 To get regular product updates and news about the Backstage community, sign up for the [Backstage newsletter](https://mailchi.mp/spotify/backstage-community). diff --git a/microsite/blog/2020-07-01-how-to-enable-authentication-in-backstage-using-passport.md b/microsite/blog/2020-07-01-how-to-enable-authentication-in-backstage-using-passport.md index b2a944bf26..9d41a21d05 100644 --- a/microsite/blog/2020-07-01-how-to-enable-authentication-in-backstage-using-passport.md +++ b/microsite/blog/2020-07-01-how-to-enable-authentication-in-backstage-using-passport.md @@ -21,7 +21,7 @@ Passport has allowed us to leverage an existing open-source authentication frame ![auth-landing-page](assets/20-07-01/auth-sidebar.png) -First, check out the provided Google and GitHub implementations! [Spin up a local copy of Backstage](https://backstage.io/blog/2020/04/30/how-to-quickly-set-up-backstage) along with our example-backend. You can find more documentation on setting up the example backend [here](https://github.com/spotify/backstage/tree/master/packages/backend), but be sure to include the relevant client IDs and secrets when running `yarn start`: +First, check out the provided Google and GitHub implementations! [Spin up a local copy of Backstage](https://backstage.io/blog/2020/04/30/how-to-quickly-set-up-backstage) along with our example-backend. You can find more documentation on setting up the example backend [here](https://github.com/backstage/backstage/tree/master/packages/backend), but be sure to include the relevant client IDs and secrets when running `yarn start`: ``` AUTH_GOOGLE_CLIENT_ID=x AUTH_GOOGLE_CLIENT_SECRET=x AUTH_GITHUB_CLIENT_ID=x AUTH_GITHUB_CLIENT_SECRET=x SENTRY_TOKEN=x LOG_LEVEL=debug yarn start @@ -43,4 +43,4 @@ For full details, take a look at our [“Adding authentication providers” docu ## Interested in contributing to the next steps for authentication? -We’ve already seen both GitLab and Okta contributions from the community — and we’re thinking about a few more providers we’d like to add to Backstage, too. You can find those, and other authentication-related issues, in our repository by filtering with the [auth label](https://github.com/spotify/backstage/issues?q=is%3Aissue+is%3Aopen+label%3Aauth). +We’ve already seen both GitLab and Okta contributions from the community — and we’re thinking about a few more providers we’d like to add to Backstage, too. You can find those, and other authentication-related issues, in our repository by filtering with the [auth label](https://github.com/backstage/backstage/issues?q=is%3Aissue+is%3Aopen+label%3Aauth). diff --git a/microsite/blog/2020-08-05-announcing-backstage-software-templates.md b/microsite/blog/2020-08-05-announcing-backstage-software-templates.md index 81d495e1c3..c216d7e398 100644 --- a/microsite/blog/2020-08-05-announcing-backstage-software-templates.md +++ b/microsite/blog/2020-08-05-announcing-backstage-software-templates.md @@ -73,6 +73,6 @@ Backstage ships with four example templates, but since these are likely not the We have learned that one of the keys to getting these standards adopted is to keep an open process. Templates are code. By making it clear to your engineers that you are open to pull requests, and that teams with different needs can add their own templates, you are on the path of striking a good balance between autonomy and standardization. -If you have feedback or questions, please open a [GitHub issue](https://github.com/spotify/backstage/issues), ping us on [Discord chat](https://discord.gg/EBHEGzX) or send me an email at [alund@spotify.com](mailto:alund@spotify.com) 🙏 +If you have feedback or questions, please open a [GitHub issue](https://github.com/backstage/backstage/issues), ping us on [Discord chat](https://discord.gg/EBHEGzX) or send me an email at [alund@spotify.com](mailto:alund@spotify.com) 🙏 To get regular product updates and news about the Backstage community, sign up for the [Backstage newsletter](https://mailchi.mp/spotify/backstage-community). diff --git a/microsite/blog/2020-09-08-announcing-tech-docs.md b/microsite/blog/2020-09-08-announcing-tech-docs.md index 48b6eeb7cc..ecf678a5bd 100644 --- a/microsite/blog/2020-09-08-announcing-tech-docs.md +++ b/microsite/blog/2020-09-08-announcing-tech-docs.md @@ -21,11 +21,11 @@ But this is just one way to do it. Today we’re most excited for what the open ## Okay, let’s start collaborating -If you go to [GitHub](https://github.com/spotify/backstage/tree/master/plugins) now, you’ll find everything you need to start collaborating with us to build out the docs-like-code Backstage plugin — we’ll call it TechDocs in the open as well. +If you go to [GitHub](https://github.com/backstage/backstage/tree/master/plugins) now, you’ll find everything you need to start collaborating with us to build out the docs-like-code Backstage plugin — we’ll call it TechDocs in the open as well. -You’ll find the code in [techdocs](https://github.com/spotify/backstage/tree/master/plugins/techdocs) (frontend) and [techdocs-backend](https://github.com/spotify/backstage/tree/master/plugins/techdocs-backend). (There are also two separate packages [techdocs-cli](https://github.com/spotify/backstage/tree/master/packages/techdocs-cli) and [techdocs-container](https://github.com/spotify/backstage/tree/master/packages/techdocs-container).) +You’ll find the code in [techdocs](https://github.com/backstage/backstage/tree/master/plugins/techdocs) (frontend) and [techdocs-backend](https://github.com/backstage/backstage/tree/master/plugins/techdocs-backend). (There are also two separate packages [techdocs-cli](https://github.com/backstage/backstage/tree/master/packages/techdocs-cli) and [techdocs-container](https://github.com/backstage/backstage/tree/master/packages/techdocs-container).) -You’ll find issues to work on in the [issues queue](https://github.com/spotify/backstage/issues?q=is%3Aissue+is%3Aopen+label%3A%22docs-like-code%22+label%3A%22help+wanted%22), typically starting with TechDocs: and labeled with docs-like-code, some labeled good first issue. Feel free to add your own issues, of course. +You’ll find issues to work on in the [issues queue](https://github.com/backstage/backstage/issues?q=is%3Aissue+is%3Aopen+label%3A%22docs-like-code%22+label%3A%22help+wanted%22), typically starting with TechDocs: and labeled with docs-like-code, some labeled good first issue. Feel free to add your own issues, of course. ![available-templates](assets/announcing-techdocs/github-issues.png) diff --git a/microsite/blog/2020-09-23-backstage-cncf-sandbox.md b/microsite/blog/2020-09-23-backstage-cncf-sandbox.md index bcfd7c6245..fbf47a39c3 100644 --- a/microsite/blog/2020-09-23-backstage-cncf-sandbox.md +++ b/microsite/blog/2020-09-23-backstage-cncf-sandbox.md @@ -14,7 +14,7 @@ Backstage garnered quite a bit of interest from developers and organizations whe Backstage’s ability to simplify tooling and standardize engineering practices has attracted interest from other major tech companies, as well as airlines, auto manufacturers, investment firms, and global retailers. We know that Backstage solves a problem — infrastructure complexity — that’s common to a lot of large and growing companies today. But different companies work differently, use particular toolsets, and have unique use cases. By making Backstage open source, we can build it with people working inside a variety of engineering organizations all over the world. It makes for a better product that serves a wider group of users (beyond that of Spotify’s) and their needs. -The Backstage community is healthy and growing quickly. Over [130 people](https://github.com/spotify/backstage/graphs/contributors) have contributed to the project, and roughly 40% of pull requests are now coming from external, non-Spotify, contributors. With companies now deciding to [adopt Backstage](https://github.com/spotify/backstage/blob/master/ADOPTERS.md) we are also seeing a shift in the kinds of contributions we are getting from the community. It is truly amazing to see contributions to core parts of the platform as well as significant functionality additions through working [plugins](https://backstage.io/plugins). +The Backstage community is healthy and growing quickly. Over [130 people](https://github.com/backstage/backstage/graphs/contributors) have contributed to the project, and roughly 40% of pull requests are now coming from external, non-Spotify, contributors. With companies now deciding to [adopt Backstage](https://github.com/backstage/backstage/blob/master/ADOPTERS.md) we are also seeing a shift in the kinds of contributions we are getting from the community. It is truly amazing to see contributions to core parts of the platform as well as significant functionality additions through working [plugins](https://backstage.io/plugins). We’re excited to embark on this journey with the CNCF community. There’s so much great tech being built here, and it’s about time we share it to build even greater products, together. Entering into the CNCF Sandbox is just the first step. We are committed to working with the community to bring Backstage through the Incubation step, and finally all the way to becoming a Graduated, top-level project. diff --git a/microsite/blog/2020-09-30-backstage-design-system.md b/microsite/blog/2020-09-30-backstage-design-system.md index 4faea98782..bb12a26100 100644 --- a/microsite/blog/2020-09-30-backstage-design-system.md +++ b/microsite/blog/2020-09-30-backstage-design-system.md @@ -68,9 +68,9 @@ To keep up with our latest design guidelines, go to [Designing for Backstage](ht ![img](assets/backstage-github-ds.png) -### [GitHub](https://github.com/spotify/backstage) +### [GitHub](https://github.com/backstage/backstage) -Join in on the action [at spotify/backstage on GitHub](https://github.com/spotify/backstage) by submitting issues and opening pull requests for all things related to components and patterns in Backstage. +Join in on the action [at spotify/backstage on GitHub](https://github.com/backstage/backstage) by submitting issues and opening pull requests for all things related to components and patterns in Backstage. ![img](assets/backstage-discord-DS.png) diff --git a/microsite/blog/2020-09-30-plugin-marketplace.md b/microsite/blog/2020-09-30-plugin-marketplace.md index 0b96abbc60..10928112b8 100644 --- a/microsite/blog/2020-09-30-plugin-marketplace.md +++ b/microsite/blog/2020-09-30-plugin-marketplace.md @@ -34,10 +34,10 @@ This grand vision is actually not that far off. Already today there is a growing Not all plugins you need will be open source. Every company has their own homegrown tooling. Building internal plugins lets you tailor your version of Backstage to be a perfect fit for your infrastructure and software development needs. If you end up [building plugins](https://backstage.io/docs/plugins/create-a-plugin) that could be useful for other companies, please consider releasing them as open source and [add them to the Marketplace](https://backstage.io/docs/plugins/add-to-marketplace). -If you start developing a plugin that you aim to release as open source, we suggest that you create a [new plugin Issue](https://github.com/spotify/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME). This helps the community know what plugins are in development and opens up opportunities for collaboration. You can also use this process if you have an idea for a good plugin, but you need help building it. +If you start developing a plugin that you aim to release as open source, we suggest that you create a [new plugin Issue](https://github.com/backstage/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME). This helps the community know what plugins are in development and opens up opportunities for collaboration. You can also use this process if you have an idea for a good plugin, but you need help building it. We are really excited to see all the amazing plugins that have already been built, and look forward to seeing even more ideas and collaboration as the Backstage community continues to grow. -What plugins would you like to see in the Plugin Marketplace? [Tell us](https://github.com/spotify/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME)! +What plugins would you like to see in the Plugin Marketplace? [Tell us](https://github.com/backstage/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME)! _Special shout-out to community member [Iain Billett](https://github.com/iain-b) from [Roadie](https://roadie.io) for helping build and contribute the [Plugin Marketplace page](https://backstage.io/plugins) (as his first PR no less!)._ diff --git a/microsite/blog/2020-10-22-cost-insights-plugin.md b/microsite/blog/2020-10-22-cost-insights-plugin.md index 421ebacdd2..15647f8418 100644 --- a/microsite/blog/2020-10-22-cost-insights-plugin.md +++ b/microsite/blog/2020-10-22-cost-insights-plugin.md @@ -4,7 +4,7 @@ author: Janisa Anandamohan authorURL: https://twitter.com/janisa_a --- -How did Spotify save millions on cloud costs within a matter of months?? We made cost optimization just another part of the daily development process. Our newly open sourced [Cost Insights plugin](https://github.com/spotify/backstage/tree/master/plugins/cost-insights) makes a team’s cloud costs visible — and actionable — right inside Backstage. So engineers can see the impact of their cloud usage (down to a product and resource level) and make optimizations wherever and whenever it makes sense. By managing cloud costs from the ground up, you can make smarter decisions that let you continue to build and scale quickly, without wasting resources. +How did Spotify save millions on cloud costs within a matter of months?? We made cost optimization just another part of the daily development process. Our newly open sourced [Cost Insights plugin](https://github.com/backstage/backstage/tree/master/plugins/cost-insights) makes a team’s cloud costs visible — and actionable — right inside Backstage. So engineers can see the impact of their cloud usage (down to a product and resource level) and make optimizations wherever and whenever it makes sense. By managing cloud costs from the ground up, you can make smarter decisions that let you continue to build and scale quickly, without wasting resources. @@ -59,7 +59,7 @@ Engineers can then determine for themselves if the time invested in an optimizat ## Getting started -You can begin working with the Cost Insights plugin today on [GitHub](https://github.com/spotify/backstage/tree/master/plugins/cost-insights). We include an example client with static data in the expected format. The `CostInsightsApi` should talk with a cloud billing backend that aggregates billing data from your cloud provider. +You can begin working with the Cost Insights plugin today on [GitHub](https://github.com/backstage/backstage/tree/master/plugins/cost-insights). We include an example client with static data in the expected format. The `CostInsightsApi` should talk with a cloud billing backend that aggregates billing data from your cloud provider. The current release of Cost Insights includes: @@ -72,8 +72,8 @@ The current release of Cost Insights includes: Our hope is to help other companies translate their cloud cost in a relatable way for their engineers to better understand their impact and accurately identify their opportunities for optimizations. -And if you’re interested in contributing to our outstanding issues, you can find them in the issues queue, filtered under the [‘cost-insights’ label](https://github.com/spotify/backstage/labels/cost-insights). +And if you’re interested in contributing to our outstanding issues, you can find them in the issues queue, filtered under the [‘cost-insights’ label](https://github.com/backstage/backstage/labels/cost-insights). ## Ready for DevSecCostOpsPlus (and whatever’s next) -There’s DevOps, there’s DevSecOps, and then there’s Backstage: one frontend for all your infrastructure. From building, testing, and deploying to monitoring and security — Backstage helps you manage your entire tech organization and provides a seamless developer experience for engineers, from end to end to end. And now that also extends to cost management for your cloud infrastructure and tooling. Happy building and [happy optimizing](https://github.com/spotify/backstage/tree/master/plugins/cost-insights). +There’s DevOps, there’s DevSecOps, and then there’s Backstage: one frontend for all your infrastructure. From building, testing, and deploying to monitoring and security — Backstage helps you manage your entire tech organization and provides a seamless developer experience for engineers, from end to end to end. And now that also extends to cost management for your cloud infrastructure and tooling. Happy building and [happy optimizing](https://github.com/backstage/backstage/tree/master/plugins/cost-insights). diff --git a/microsite/core/Footer.js b/microsite/core/Footer.js index d41b15b74c..e48c86acce 100644 --- a/microsite/core/Footer.js +++ b/microsite/core/Footer.js @@ -41,7 +41,7 @@ class Footer extends React.Component {
Community
Support chatroom - + Contributing diff --git a/microsite/data/plugins/api-docs.yaml b/microsite/data/plugins/api-docs.yaml index babcbf74f1..3343a46693 100644 --- a/microsite/data/plugins/api-docs.yaml +++ b/microsite/data/plugins/api-docs.yaml @@ -4,6 +4,6 @@ author: SDA SE authorUrl: https://sda.se/ category: Discovery description: Components to discover and display API entities as an extension to the catalog plugin. -documentation: https://github.com/spotify/backstage/blob/master/plugins/api-docs/README.md +documentation: https://github.com/backstage/backstage/blob/master/plugins/api-docs/README.md iconUrl: https://thecoders.io/wp-content/uploads/2019/11/tech-swagger.svg npmPackageName: '@backstage/plugin-api-docs' diff --git a/microsite/data/plugins/circleci.yaml b/microsite/data/plugins/circleci.yaml index 5f7b4b08e2..467da9fb1e 100644 --- a/microsite/data/plugins/circleci.yaml +++ b/microsite/data/plugins/circleci.yaml @@ -4,7 +4,7 @@ author: Spotify authorUrl: https://github.com/spotify category: CI description: Automate your development process with CI hosted in the cloud or on a private server. -documentation: https://github.com/spotify/backstage/tree/master/plugins/circleci +documentation: https://github.com/backstage/backstage/tree/master/plugins/circleci iconUrl: https://www.saaves.com/storage/brochure/logo-circleci-icon1583764538.png npmPackageName: '@backstage/plugin-circleci' tags: diff --git a/microsite/data/plugins/cloud-build.yaml b/microsite/data/plugins/cloud-build.yaml index cbd3a3cb6c..2b40d02de2 100644 --- a/microsite/data/plugins/cloud-build.yaml +++ b/microsite/data/plugins/cloud-build.yaml @@ -4,7 +4,7 @@ author: Trivago authorUrl: https://www.trivago.com category: CI description: Build, test, and deploy on Google's serverless CI/CD platform. -documentation: https://github.com/spotify/backstage/tree/master/plugins/cloudbuild +documentation: https://github.com/backstage/backstage/tree/master/plugins/cloudbuild iconUrl: https://avatars2.githubusercontent.com/u/38220399?s=400&v=4 npmPackageName: '@backstage/plugin-cloudbuild' tags: diff --git a/microsite/data/plugins/cost-insights.yaml b/microsite/data/plugins/cost-insights.yaml index 7950879833..daa4f55a02 100644 --- a/microsite/data/plugins/cost-insights.yaml +++ b/microsite/data/plugins/cost-insights.yaml @@ -4,7 +4,7 @@ author: Spotify authorUrl: https://github.com/spotify category: Discovery description: Visualize, understand and optimize your team's cloud costs. -documentation: https://github.com/spotify/backstage/tree/master/plugins/cost-insights +documentation: https://github.com/backstage/backstage/tree/master/plugins/cost-insights iconUrl: https://www.materialui.co/materialIcons/editor/monetization_on_white_192x192.png npmPackageName: '@backstage/plugin-cost-insights' tags: diff --git a/microsite/data/plugins/gcp-projects.yaml b/microsite/data/plugins/gcp-projects.yaml index 7eff42af98..1c015821b2 100644 --- a/microsite/data/plugins/gcp-projects.yaml +++ b/microsite/data/plugins/gcp-projects.yaml @@ -4,7 +4,7 @@ author: Trivago authorUrl: https://www.trivago.com category: Cloud description: Create, list and manage your Google Cloud Projects. -documentation: https://github.com/spotify/backstage/tree/master/plugins/gcp-projects +documentation: https://github.com/backstage/backstage/tree/master/plugins/gcp-projects iconUrl: https://avatars1.githubusercontent.com/u/2810941?s=280&v=4 npmPackageName: '@backstage/plugin-gcp-projects' tags: diff --git a/microsite/data/plugins/github-actions.yaml b/microsite/data/plugins/github-actions.yaml index 062d0fff61..aeccdc9b86 100644 --- a/microsite/data/plugins/github-actions.yaml +++ b/microsite/data/plugins/github-actions.yaml @@ -4,7 +4,7 @@ author: Spotify authorUrl: https://github.com/spotify category: CI description: GitHub Actions makes it easy to automate all your software workflows, now with world-class CI/CD. Build, test, and deploy your code right from GitHub. -documentation: https://github.com/spotify/backstage/tree/master/plugins/github-actions +documentation: https://github.com/backstage/backstage/tree/master/plugins/github-actions iconUrl: https://avatars2.githubusercontent.com/u/44036562?s=400&v=4 npmPackageName: '@backstage/plugin-github-actions' tags: diff --git a/microsite/data/plugins/gitops-cluster.yaml b/microsite/data/plugins/gitops-cluster.yaml index 6f8ab6b097..ad6e7979aa 100644 --- a/microsite/data/plugins/gitops-cluster.yaml +++ b/microsite/data/plugins/gitops-cluster.yaml @@ -4,7 +4,7 @@ author: Weaveworks authorUrl: https://www.weave.works/ category: Kubernetes description: Create GitOps-managed Kubernetes clusters. Currently, it supports provisioning EKS clusters on GitHub via GitHub Actions. -documentation: https://github.com/spotify/backstage/tree/master/plugins/gitops-profiles +documentation: https://github.com/backstage/backstage/tree/master/plugins/gitops-profiles iconUrl: https://res-5.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_256,w_256,f_auto,q_auto:eco/v1462316670/i9d3delzvx1erzjhmcws.png npmPackageName: '@backstage/plugin-gitops-profiles' tags: diff --git a/microsite/data/plugins/graphiql.yaml b/microsite/data/plugins/graphiql.yaml index e997360b04..eda91206eb 100644 --- a/microsite/data/plugins/graphiql.yaml +++ b/microsite/data/plugins/graphiql.yaml @@ -4,7 +4,7 @@ author: Spotify authorUrl: https://github.com/spotify category: Debugging description: Integrates GraphiQL as a tool to browse GraphQL API endpoints inside Backstage. -documentation: https://github.com/spotify/backstage/tree/master/plugins/graphiql +documentation: https://github.com/backstage/backstage/tree/master/plugins/graphiql iconUrl: https://upload.wikimedia.org/wikipedia/commons/thumb/1/17/GraphQL_Logo.svg/1024px-GraphQL_Logo.svg.png npmPackageName: '@backstage/plugin-graphiql' tags: diff --git a/microsite/data/plugins/jenkins.yaml b/microsite/data/plugins/jenkins.yaml index 78b3616595..638758d54d 100644 --- a/microsite/data/plugins/jenkins.yaml +++ b/microsite/data/plugins/jenkins.yaml @@ -4,7 +4,7 @@ author: '@timja' authorUrl: https://github.com/timja category: CI description: Jenkins offers a simple way to set up a continuous integration and continuous delivery environment. -documentation: https://github.com/spotify/backstage/tree/master/plugins/jenkins +documentation: https://github.com/backstage/backstage/tree/master/plugins/jenkins iconUrl: https://img.icons8.com/color/1600/jenkins.png npmPackageName: '@backstage/plugin-jenkins' tags: diff --git a/microsite/data/plugins/lighthouse.yaml b/microsite/data/plugins/lighthouse.yaml index 0819455ed2..88d444e0ef 100644 --- a/microsite/data/plugins/lighthouse.yaml +++ b/microsite/data/plugins/lighthouse.yaml @@ -4,7 +4,7 @@ author: Spotify authorUrl: https://github.com/spotify category: Accessibility description: Google's Lighthouse tool is a great resource for benchmarking and improving the accessibility, performance, SEO, and best practices of your website. -documentation: https://github.com/spotify/backstage/tree/master/plugins/lighthouse +documentation: https://github.com/backstage/backstage/tree/master/plugins/lighthouse iconUrl: https://seeklogo.com/images/G/google-lighthouse-logo-1C7FA08580-seeklogo.com.png npmPackageName: '@backstage/plugin-lighthouse' tags: diff --git a/microsite/data/plugins/new-relic.yaml b/microsite/data/plugins/new-relic.yaml index e3ddf18652..d728a9d1c7 100644 --- a/microsite/data/plugins/new-relic.yaml +++ b/microsite/data/plugins/new-relic.yaml @@ -4,7 +4,7 @@ author: '@timwheelercom' authorUrl: https://github.com/timwheelercom category: Monitoring description: Observability platform built to help engineers create and monitor their software. -documentation: https://github.com/spotify/backstage/tree/master/plugins/newrelic +documentation: https://github.com/backstage/backstage/tree/master/plugins/newrelic iconUrl: https://www.mulesoft.com/sites/default/files/2018-10/New_relic.png npmPackageName: '@backstage/plugin-newrelic' tags: diff --git a/microsite/data/plugins/rollbar.yaml b/microsite/data/plugins/rollbar.yaml index 8ef3d8d66a..05f0d4ef78 100644 --- a/microsite/data/plugins/rollbar.yaml +++ b/microsite/data/plugins/rollbar.yaml @@ -4,6 +4,6 @@ author: '@andrewthauer' authorUrl: https://github.com/andrewthauer category: Monitoring description: View Rollbar errors for your services in Backstage. -documentation: https://github.com/spotify/backstage/tree/master/plugins/rollbar +documentation: https://github.com/backstage/backstage/tree/master/plugins/rollbar iconUrl: https://rollbar.com/assets/media/rollbar-mark-color.png npmPackageName: '@backstage/plugin-rollbar' diff --git a/microsite/data/plugins/sentry.yaml b/microsite/data/plugins/sentry.yaml index 8da488f727..3445d6abb7 100644 --- a/microsite/data/plugins/sentry.yaml +++ b/microsite/data/plugins/sentry.yaml @@ -4,6 +4,6 @@ author: Spotify authorUrl: https://github.com/spotify category: Monitoring description: View Sentry issues in Backstage. -documentation: https://github.com/spotify/backstage/tree/master/plugins/sentry +documentation: https://github.com/backstage/backstage/tree/master/plugins/sentry iconUrl: https://sentry-brand.storage.googleapis.com/sentry-glyph-white.png npmPackageName: '@backstage/plugin-sentry' diff --git a/microsite/data/plugins/sonarqube.yaml b/microsite/data/plugins/sonarqube.yaml index a2982a4a58..e9e22d18a4 100644 --- a/microsite/data/plugins/sonarqube.yaml +++ b/microsite/data/plugins/sonarqube.yaml @@ -4,6 +4,6 @@ author: SDA SE authorUrl: https://sda.se/ category: Quality description: Components to display code quality metrics from SonarCloud and SonarQube. -documentation: https://github.com/spotify/backstage/blob/master/plugins/sonarqube/README.md +documentation: https://github.com/backstage/backstage/blob/master/plugins/sonarqube/README.md iconUrl: img/sonarqube-icon.svg npmPackageName: '@backstage/plugin-sonarqube' diff --git a/microsite/data/plugins/tech-radar.yaml b/microsite/data/plugins/tech-radar.yaml index b1d4f5cc17..f2d5b12663 100644 --- a/microsite/data/plugins/tech-radar.yaml +++ b/microsite/data/plugins/tech-radar.yaml @@ -4,6 +4,6 @@ author: Spotify authorUrl: https://github.com/spotify category: Discovery description: Visualize the your company's official guidelines of different areas of software development. -documentation: https://github.com/spotify/backstage/tree/master/plugins/tech-radar +documentation: https://github.com/backstage/backstage/tree/master/plugins/tech-radar iconUrl: https://www.materialui.co/materialIcons/action/track_changes_white_192x192.png npmPackageName: '@backstage/plugin-tech-radar' diff --git a/microsite/pages/en/index.js b/microsite/pages/en/index.js index 6e649aa574..0422bfab63 100644 --- a/microsite/pages/en/index.js +++ b/microsite/pages/en/index.js @@ -31,7 +31,7 @@ class Index extends React.Component { high-quality code quickly — without compromising autonomy. GitHub @@ -282,7 +282,7 @@ class Index extends React.Component { Build your own software templates Contribute diff --git a/microsite/pages/en/plugins.js b/microsite/pages/en/plugins.js index 36957c2b4e..b1854135dc 100644 --- a/microsite/pages/en/plugins.js +++ b/microsite/pages/en/plugins.js @@ -98,11 +98,11 @@ const Plugins = () => (

See what plugins are already{' '} - + in progress {' '} and 👍. Missing a plugin for your favorite tool? Please{' '} - + suggest {' '} a new one. diff --git a/microsite/siteConfig.js b/microsite/siteConfig.js index 06ae8453a0..6e8f8edddf 100644 --- a/microsite/siteConfig.js +++ b/microsite/siteConfig.js @@ -17,7 +17,7 @@ const siteConfig = { url: 'https://backstage.io', // Your website URL cname: 'backstage.io', baseUrl: '/', // Base URL for your project */ - editUrl: 'https://github.com/spotify/backstage/edit/master/docs/', + editUrl: 'https://github.com/backstage/backstage/edit/master/docs/', // Used for publishing and more projectName: 'backstage', @@ -30,7 +30,7 @@ const siteConfig = { // For no header links in the top nav bar -> headerLinks: [], headerLinks: [ { - href: 'https://github.com/spotify/backstage', + href: 'https://github.com/backstage/backstage', label: 'GitHub', }, { @@ -111,7 +111,7 @@ const siteConfig = { // You may provide arbitrary config keys to be used as needed by your // template. For example, if you need your repo's URL... - repoUrl: 'https://github.com/spotify/backstage', + repoUrl: 'https://github.com/backstage/backstage', twitterUsername: 'SpotifyEng', stylesheets: [ diff --git a/packages/backend-common/README.md b/packages/backend-common/README.md index 82db0454d2..dc08d89416 100644 --- a/packages/backend-common/README.md +++ b/packages/backend-common/README.md @@ -34,5 +34,5 @@ app.listen(PORT, () => { ## Documentation -- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md) +- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 076e644afd..828db8538a 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -13,7 +13,7 @@ "homepage": "https://backstage.io", "repository": { "type": "git", - "url": "https://github.com/spotify/backstage", + "url": "https://github.com/backstage/backstage", "directory": "packages/backend-common" }, "keywords": [ diff --git a/packages/backend/README.md b/packages/backend/README.md index c45a0d28d9..d458268846 100644 --- a/packages/backend/README.md +++ b/packages/backend/README.md @@ -55,9 +55,9 @@ in `app-config.yaml` under `catalog.locations`. For local development you can ov We chose [Passport](http://www.passportjs.org/) as authentication platform due to its comprehensive set of supported authentication [strategies](http://www.passportjs.org/packages/). -Read more about the [auth-backend](https://github.com/spotify/backstage/blob/master/plugins/auth-backend/README.md) and [how to add a new provider](https://github.com/spotify/backstage/blob/master/docs/auth/add-auth-provider.md) +Read more about the [auth-backend](https://github.com/backstage/backstage/blob/master/plugins/auth-backend/README.md) and [how to add a new provider](https://github.com/backstage/backstage/blob/master/docs/auth/add-auth-provider.md) ## Documentation -- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md) +- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) diff --git a/packages/catalog-model/README.md b/packages/catalog-model/README.md index 6dab6e7cae..8218508be9 100644 --- a/packages/catalog-model/README.md +++ b/packages/catalog-model/README.md @@ -7,6 +7,6 @@ as well as by others that want to consume catalog data. ## Links -- [Default frontend part of the catalog](https://github.com/spotify/backstage/tree/master/plugins/catalog) -- [Default backend part of the catalog](https://github.com/spotify/backstage/tree/master/plugins/catalog-backend) +- [Default frontend part of the catalog](https://github.com/backstage/backstage/tree/master/plugins/catalog) +- [Default backend part of the catalog](https://github.com/backstage/backstage/tree/master/plugins/catalog-backend) - [The Backstage homepage](https://backstage.io) diff --git a/packages/catalog-model/examples/acme-corp.yaml b/packages/catalog-model/examples/acme-corp.yaml index 7e5c5ebc61..e8047fc143 100644 --- a/packages/catalog-model/examples/acme-corp.yaml +++ b/packages/catalog-model/examples/acme-corp.yaml @@ -6,4 +6,4 @@ metadata: spec: type: github targets: - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/acme/org.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/acme/org.yaml diff --git a/packages/catalog-model/examples/acme/org.yaml b/packages/catalog-model/examples/acme/org.yaml index 2d6a6a2666..8c562aaf89 100644 --- a/packages/catalog-model/examples/acme/org.yaml +++ b/packages/catalog-model/examples/acme/org.yaml @@ -18,10 +18,10 @@ metadata: spec: type: github targets: - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/acme/infrastructure-group.yaml - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/acme/boxoffice-group.yaml - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/acme/backstage-group.yaml - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/acme/team-a-group.yaml - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/acme/team-b-group.yaml - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/acme/team-c-group.yaml - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/acme/team-d-group.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/acme/infrastructure-group.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/acme/boxoffice-group.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/acme/backstage-group.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/acme/team-a-group.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/acme/team-b-group.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/acme/team-c-group.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/acme/team-d-group.yaml diff --git a/packages/catalog-model/examples/all-apis.yaml b/packages/catalog-model/examples/all-apis.yaml index 64586fdddd..0278fc3078 100644 --- a/packages/catalog-model/examples/all-apis.yaml +++ b/packages/catalog-model/examples/all-apis.yaml @@ -6,8 +6,8 @@ metadata: spec: type: github targets: - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/apis/hello-world-api.yaml - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/apis/petstore-api.yaml - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/apis/spotify-api.yaml - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/apis/streetlights-api.yaml - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/apis/swapi-graphql.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/apis/hello-world-api.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/apis/petstore-api.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/apis/spotify-api.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/apis/streetlights-api.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/apis/swapi-graphql.yaml diff --git a/packages/catalog-model/examples/all-components.yaml b/packages/catalog-model/examples/all-components.yaml index bef38df0b1..06c44b59d7 100644 --- a/packages/catalog-model/examples/all-components.yaml +++ b/packages/catalog-model/examples/all-components.yaml @@ -6,12 +6,12 @@ metadata: spec: type: github targets: - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/components/artist-lookup-component.yaml - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/components/petstore-component.yaml - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/components/playback-order-component.yaml - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/components/podcast-api-component.yaml - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/components/queue-proxy-component.yaml - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/components/searcher-component.yaml - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/components/playback-lib-component.yaml - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/components/www-artist-component.yaml - - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/components/shuffle-api-component.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/artist-lookup-component.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/petstore-component.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/playback-order-component.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/podcast-api-component.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/queue-proxy-component.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/searcher-component.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/playback-lib-component.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/www-artist-component.yaml + - https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/shuffle-api-component.yaml diff --git a/packages/catalog-model/src/entity/policies/FieldFormatEntityPolicy.ts b/packages/catalog-model/src/entity/policies/FieldFormatEntityPolicy.ts index acd4469fcb..0aabbd9e48 100644 --- a/packages/catalog-model/src/entity/policies/FieldFormatEntityPolicy.ts +++ b/packages/catalog-model/src/entity/policies/FieldFormatEntityPolicy.ts @@ -94,7 +94,7 @@ export class FieldFormatEntityPolicy implements EntityPolicy { : ''; throw new Error( - `"${field}" is not valid;${message} To learn more about catalog file format, visit: https://github.com/spotify/backstage/blob/master/docs/architecture-decisions/adr002-default-catalog-file-format.md`, + `"${field}" is not valid;${message} To learn more about catalog file format, visit: https://github.com/backstage/backstage/blob/master/docs/architecture-decisions/adr002-default-catalog-file-format.md`, ); } } diff --git a/packages/catalog-model/src/kinds/LocationEntityV1alpha1.test.ts b/packages/catalog-model/src/kinds/LocationEntityV1alpha1.test.ts index daa18b00cc..d9c1e9185f 100644 --- a/packages/catalog-model/src/kinds/LocationEntityV1alpha1.test.ts +++ b/packages/catalog-model/src/kinds/LocationEntityV1alpha1.test.ts @@ -71,7 +71,7 @@ describe('LocationV1alpha1Validator', () => { it('accepts good target', async () => { (entity as any).spec.target = - 'https://github.com/spotify/backstage/blob/master/plugins/catalog-backend/examples/artist-lookup-component.yaml'; + 'https://github.com/backstage/backstage/blob/master/plugins/catalog-backend/examples/artist-lookup-component.yaml'; await expect(validator.check(entity)).resolves.toBe(true); }); @@ -87,8 +87,8 @@ describe('LocationV1alpha1Validator', () => { it('accepts good targets', async () => { (entity as any).spec.targets = [ - 'https://github.com/spotify/backstage/blob/master/plugins/catalog-backend/examples/artist-lookup-component.yaml', - 'https://github.com/spotify/backstage/blob/master/plugins/catalog-backend/examples/playback-order-component.yaml', + 'https://github.com/backstage/backstage/blob/master/plugins/catalog-backend/examples/artist-lookup-component.yaml', + 'https://github.com/backstage/backstage/blob/master/plugins/catalog-backend/examples/playback-order-component.yaml', ]; await expect(validator.check(entity)).resolves.toBe(true); }); diff --git a/packages/cli-common/README.md b/packages/cli-common/README.md index 3488003a76..91b774145e 100644 --- a/packages/cli-common/README.md +++ b/packages/cli-common/README.md @@ -8,5 +8,5 @@ Do not install this package directly, it is an internal package used by [@backst ## Documentation -- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md) +- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) diff --git a/packages/cli-common/package.json b/packages/cli-common/package.json index 56fb0d3417..29f5f436c3 100644 --- a/packages/cli-common/package.json +++ b/packages/cli-common/package.json @@ -13,7 +13,7 @@ "homepage": "https://backstage.io", "repository": { "type": "git", - "url": "https://github.com/spotify/backstage", + "url": "https://github.com/backstage/backstage", "directory": "packages/cli-common" }, "keywords": [ diff --git a/packages/cli/README.md b/packages/cli/README.md index 8868e699c8..9eac5cdfe4 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -30,5 +30,5 @@ To try out the command locally, you can execute the following from the parent di ## Documentation -- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md) +- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) diff --git a/packages/cli/package.json b/packages/cli/package.json index fdc0acddbf..9f2a664e7f 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -9,7 +9,7 @@ "homepage": "https://backstage.io", "repository": { "type": "git", - "url": "https://github.com/spotify/backstage", + "url": "https://github.com/backstage/backstage", "directory": "packages/cli" }, "keywords": [ diff --git a/packages/config-loader/README.md b/packages/config-loader/README.md index 595241e0d5..261394a6a5 100644 --- a/packages/config-loader/README.md +++ b/packages/config-loader/README.md @@ -8,5 +8,5 @@ Do not install this package directly, it is an internal package used by [@backst ## Documentation -- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md) +- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 2eef7db027..caf3520a63 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -12,7 +12,7 @@ "homepage": "https://backstage.io", "repository": { "type": "git", - "url": "https://github.com/spotify/backstage", + "url": "https://github.com/backstage/backstage", "directory": "packages/config-loader" }, "keywords": [ diff --git a/packages/config/README.md b/packages/config/README.md index f532c682be..9866b1bce9 100644 --- a/packages/config/README.md +++ b/packages/config/README.md @@ -8,5 +8,5 @@ Do not install this package directly, it is an internal package used by [@backst ## Documentation -- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md) +- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) diff --git a/packages/config/package.json b/packages/config/package.json index d8ee50e97c..3609839f68 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -12,7 +12,7 @@ "homepage": "https://backstage.io", "repository": { "type": "git", - "url": "https://github.com/spotify/backstage", + "url": "https://github.com/backstage/backstage", "directory": "packages/config" }, "keywords": [ diff --git a/packages/core-api/README.md b/packages/core-api/README.md index 5732b5ef23..f7b8b6c337 100644 --- a/packages/core-api/README.md +++ b/packages/core-api/README.md @@ -18,5 +18,5 @@ $ yarn add @backstage/core ## Documentation -- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md) +- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) diff --git a/packages/core-api/package.json b/packages/core-api/package.json index 34949e4315..b03772184b 100644 --- a/packages/core-api/package.json +++ b/packages/core-api/package.json @@ -11,7 +11,7 @@ "homepage": "https://backstage.io", "repository": { "type": "git", - "url": "https://github.com/spotify/backstage", + "url": "https://github.com/backstage/backstage", "directory": "packages/core-api" }, "keywords": [ diff --git a/packages/core/README.md b/packages/core/README.md index a22f66ecd9..0d0063c9fd 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -18,5 +18,5 @@ $ yarn add @backstage/core ## Documentation -- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md) +- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) diff --git a/packages/core/package.json b/packages/core/package.json index ec4411b390..5f574c5119 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -11,7 +11,7 @@ "homepage": "https://backstage.io", "repository": { "type": "git", - "url": "https://github.com/spotify/backstage", + "url": "https://github.com/backstage/backstage", "directory": "packages/core" }, "keywords": [ diff --git a/packages/core/src/layout/ErrorPage/ErrorPage.tsx b/packages/core/src/layout/ErrorPage/ErrorPage.tsx index b41308dd6d..776ed41614 100644 --- a/packages/core/src/layout/ErrorPage/ErrorPage.tsx +++ b/packages/core/src/layout/ErrorPage/ErrorPage.tsx @@ -73,7 +73,7 @@ export const ErrorPage = ({ ... or if you think this is a bug, please file an{' '} issue. diff --git a/packages/create-app/README.md b/packages/create-app/README.md index b5f231a326..7533110372 100644 --- a/packages/create-app/README.md +++ b/packages/create-app/README.md @@ -13,5 +13,5 @@ $ npx @backstage/create-app ## Documentation -- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md) +- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) diff --git a/packages/create-app/package.json b/packages/create-app/package.json index bb321dc504..2a2ed28e1c 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -9,7 +9,7 @@ "homepage": "https://backstage.io", "repository": { "type": "git", - "url": "https://github.com/spotify/backstage", + "url": "https://github.com/backstage/backstage", "directory": "packages/create-app" }, "keywords": [ diff --git a/packages/create-app/templates/default-app/app-config.yaml.hbs b/packages/create-app/templates/default-app/app-config.yaml.hbs index 3c7bc40c2b..595812dec3 100644 --- a/packages/create-app/templates/default-app/app-config.yaml.hbs +++ b/packages/create-app/templates/default-app/app-config.yaml.hbs @@ -81,23 +81,23 @@ catalog: locations: # Backstage example components - type: url - target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/all-components.yaml + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all-components.yaml # Backstage example APIs - type: url - target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/all-apis.yaml + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all-apis.yaml # Backstage example templates - type: github - target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml + target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml rules: - allow: [Template] - type: github - target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml + target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml rules: - allow: [Template] - type: github - target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml + target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml rules: - allow: [Template] - type: github @@ -105,6 +105,6 @@ catalog: rules: - allow: [Template] - type: github - target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml + target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml rules: - allow: [Template] diff --git a/packages/create-app/templates/default-app/catalog-info.yaml.hbs b/packages/create-app/templates/default-app/catalog-info.yaml.hbs index a370902020..b3fbbda5ce 100644 --- a/packages/create-app/templates/default-app/catalog-info.yaml.hbs +++ b/packages/create-app/templates/default-app/catalog-info.yaml.hbs @@ -1,13 +1,13 @@ -apiVersion: backstage.io/v1alpha1 +apiVersion: backstage.io/v1alpha1 kind: Component metadata: - name: {{name}} + name: {{name}} description: An example of a Backstage application. # Example for optional annotations - # annotations: + # annotations: # github.com/project-slug: spotify/backstage - # backstage.io/techdocs-ref: github:https://github.com/spotify/backstage.git + # backstage.io/techdocs-ref: github:https://github.com/backstage/backstage.git spec: type: website owner: john@example.com - lifecycle: experimental \ No newline at end of file + lifecycle: experimental diff --git a/packages/create-app/templates/default-app/packages/backend/README.md b/packages/create-app/templates/default-app/packages/backend/README.md index 5583bff625..81e0f80535 100644 --- a/packages/create-app/templates/default-app/packages/backend/README.md +++ b/packages/create-app/templates/default-app/packages/backend/README.md @@ -56,11 +56,11 @@ to its comprehensive set of supported authentication [strategies](http://www.passportjs.org/packages/). Read more about the -[auth-backend](https://github.com/spotify/backstage/blob/master/plugins/auth-backend/README.md) +[auth-backend](https://github.com/backstage/backstage/blob/master/plugins/auth-backend/README.md) and -[how to add a new provider](https://github.com/spotify/backstage/blob/master/docs/auth/add-auth-provider.md) +[how to add a new provider](https://github.com/backstage/backstage/blob/master/docs/auth/add-auth-provider.md) ## Documentation -- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md) +- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) diff --git a/packages/dev-utils/README.md b/packages/dev-utils/README.md index 04f8404f64..1aa86ba51c 100644 --- a/packages/dev-utils/README.md +++ b/packages/dev-utils/README.md @@ -20,5 +20,5 @@ $ yarn add -D @backstage/dev-utils ## Documentation -- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md) +- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index d61336465f..4059516d14 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -11,7 +11,7 @@ "homepage": "https://backstage.io", "repository": { "type": "git", - "url": "https://github.com/spotify/backstage", + "url": "https://github.com/backstage/backstage", "directory": "packages/dev-utils" }, "keywords": [ diff --git a/packages/docgen/package.json b/packages/docgen/package.json index 18f26d6941..a3ebc16ecb 100644 --- a/packages/docgen/package.json +++ b/packages/docgen/package.json @@ -6,7 +6,7 @@ "homepage": "https://backstage.io", "repository": { "type": "git", - "url": "https://github.com/spotify/backstage", + "url": "https://github.com/backstage/backstage", "directory": "packages/docgen" }, "keywords": [ diff --git a/packages/docgen/src/docgen/ApiDocsPrinter.ts b/packages/docgen/src/docgen/ApiDocsPrinter.ts index fdd53afc8e..6f6528ec12 100644 --- a/packages/docgen/src/docgen/ApiDocsPrinter.ts +++ b/packages/docgen/src/docgen/ApiDocsPrinter.ts @@ -40,7 +40,7 @@ export default class ApiDocPrinter { 'The following is a list of all Utility APIs defined by `@backstage/core`.', 'They are available to use by plugins and components, and can be accessed ', 'using the `useApi` hook, also provided by `@backstage/core`.', - 'For more information, see https://github.com/spotify/backstage/blob/master/docs/api/utility-apis.md.', + 'For more information, see https://github.com/backstage/backstage/blob/master/docs/api/utility-apis.md.', ); for (const api of apiDocs) { diff --git a/packages/docgen/src/docgen/GitHubMarkdownPrinter.ts b/packages/docgen/src/docgen/GitHubMarkdownPrinter.ts index 64d38d8b15..08e493600d 100644 --- a/packages/docgen/src/docgen/GitHubMarkdownPrinter.ts +++ b/packages/docgen/src/docgen/GitHubMarkdownPrinter.ts @@ -20,7 +20,7 @@ import { MarkdownPrinter, TypeLink } from './types'; import { execSync } from 'child_process'; // TODO(Rugvip): provide through options? -const GH_BASE_URL = 'https://github.com/spotify/backstage'; +const GH_BASE_URL = 'https://github.com/backstage/backstage'; const COMMIT_SHA = process.env.COMMIT_SHA || diff --git a/packages/docgen/src/docgen/TechdocsMarkdownPrinter.ts b/packages/docgen/src/docgen/TechdocsMarkdownPrinter.ts index f2453f314f..bd83699660 100644 --- a/packages/docgen/src/docgen/TechdocsMarkdownPrinter.ts +++ b/packages/docgen/src/docgen/TechdocsMarkdownPrinter.ts @@ -19,7 +19,7 @@ import { Highlighter, MarkdownPrinter, TypeLink } from './types'; import { execSync } from 'child_process'; // TODO(Rugvip): provide through options? -const GH_BASE_URL = 'https://github.com/spotify/backstage'; +const GH_BASE_URL = 'https://github.com/backstage/backstage'; const COMMIT_SHA = process.env.COMMIT_SHA || execSync('git rev-parse HEAD').toString('utf8'); diff --git a/packages/e2e-test/README.md b/packages/e2e-test/README.md index e3706b6131..139213c0c1 100644 --- a/packages/e2e-test/README.md +++ b/packages/e2e-test/README.md @@ -20,5 +20,5 @@ If you make changes to other packages you will need to rerun `yarn tsc && yarn b ## Documentation -- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md) +- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index 9098b3cd07..13129782f2 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -6,7 +6,7 @@ "homepage": "https://backstage.io", "repository": { "type": "git", - "url": "https://github.com/spotify/backstage", + "url": "https://github.com/backstage/backstage", "directory": "packages/e2e-test" }, "keywords": [ diff --git a/packages/storybook/.storybook/webpack-plugin-fail-build-on-warning.js b/packages/storybook/.storybook/webpack-plugin-fail-build-on-warning.js index b812b6bac9..04b6d62e03 100644 --- a/packages/storybook/.storybook/webpack-plugin-fail-build-on-warning.js +++ b/packages/storybook/.storybook/webpack-plugin-fail-build-on-warning.js @@ -16,7 +16,7 @@ /** * When building storybook, we can have warnings which may cause issues in the future. One of the example case is - * https://github.com/spotify/backstage/issues/718. To make sure new warnings are not introduced with new PRs, we + * https://github.com/backstage/backstage/issues/718. To make sure new warnings are not introduced with new PRs, we * want to fail CI builds if there are warnings when building storybook. * * This webpack plugin makes sure the CI builds fail on Webpack warnings. We also have an allowlist of warnings here diff --git a/packages/techdocs-cli/README.md b/packages/techdocs-cli/README.md index 5d8a3869c7..031f26fd93 100644 --- a/packages/techdocs-cli/README.md +++ b/packages/techdocs-cli/README.md @@ -1,8 +1,8 @@ # TechDocs CLI -Check out the [TechDocs README](https://github.com/spotify/backstage/blob/master/plugins/techdocs/README.md) to learn more. +Check out the [TechDocs README](https://github.com/backstage/backstage/blob/master/plugins/techdocs/README.md) to learn more. -**WIP: This cli is a work in progress. It is not ready for use yet. Follow our progress on [the Backstage Discord](https://discord.gg/MUpMjP2) under #docs-like-code or on [our GitHub Milestone](https://github.com/spotify/backstage/milestone/15).** +**WIP: This cli is a work in progress. It is not ready for use yet. Follow our progress on [the Backstage Discord](https://discord.gg/MUpMjP2) under #docs-like-code or on [our GitHub Milestone](https://github.com/backstage/backstage/milestone/15).** ## Prerequisities diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index c111a2973a..5b751e97dc 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -8,7 +8,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/spotify/backstage", + "url": "https://github.com/backstage/backstage", "directory": "packages/techdocs-cli" }, "keywords": [ diff --git a/packages/techdocs-container/README.md b/packages/techdocs-container/README.md index cc92071f5d..7f1350031b 100644 --- a/packages/techdocs-container/README.md +++ b/packages/techdocs-container/README.md @@ -1,6 +1,6 @@ # techdocs-container -This is the Docker container that powers the creation of static documentation sites that are supported by [TechDocs](https://github.com/spotify/backstage/blob/master/plugins/techdocs). +This is the Docker container that powers the creation of static documentation sites that are supported by [TechDocs](https://github.com/backstage/backstage/blob/master/plugins/techdocs). ## Getting Started @@ -26,6 +26,6 @@ This container is published on DockerHub - https://hub.docker.com/r/spotify/tech The publishing is configured by [Automated Builds](https://hub.docker.com/repository/docker/spotify/techdocs/builds/edit) feature on Docker Hub which is triggered from GitHub (on new commits and releases). @spotify/techdocs-core team has access to the settings. -The `latest` tag on Docker Hub points to the recent commits in the `master` branch. The [version tags](https://hub.docker.com/r/spotify/techdocs/tags) (e.g. v0.1.1-alpha.24) point to the GitHub tags created from [releases](https://github.com/spotify/backstage/releases) of this GitHub repository. +The `latest` tag on Docker Hub points to the recent commits in the `master` branch. The [version tags](https://hub.docker.com/r/spotify/techdocs/tags) (e.g. v0.1.1-alpha.24) point to the GitHub tags created from [releases](https://github.com/backstage/backstage/releases) of this GitHub repository. Note: We recommend using a specific version of the container instead of `latest` release for stability and avoiding unexpected changes. diff --git a/packages/techdocs-container/techdocs-core/setup.py b/packages/techdocs-container/techdocs-core/setup.py index 44ff653d72..4c016991a3 100644 --- a/packages/techdocs-container/techdocs-core/setup.py +++ b/packages/techdocs-container/techdocs-core/setup.py @@ -28,7 +28,7 @@ setup( long_description=long_description, long_description_content_type="text/markdown", keywords="mkdocs", - url="https://github.com/spotify/backstage", + url="https://github.com/backstage/backstage", author="TechDocs Core", author_email="pulp-fiction@spotify.com", license="Apache-2.0", diff --git a/packages/test-utils-core/README.md b/packages/test-utils-core/README.md index b99250857d..63b6ece0a6 100644 --- a/packages/test-utils-core/README.md +++ b/packages/test-utils-core/README.md @@ -11,5 +11,5 @@ The reason this package exists is to allow the Backstage core packages to use th ## Documentation -- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md) +- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) diff --git a/packages/test-utils-core/package.json b/packages/test-utils-core/package.json index 1db8f6f023..32af4b880a 100644 --- a/packages/test-utils-core/package.json +++ b/packages/test-utils-core/package.json @@ -11,7 +11,7 @@ "homepage": "https://backstage.io", "repository": { "type": "git", - "url": "https://github.com/spotify/backstage", + "url": "https://github.com/backstage/backstage", "directory": "packages/test-utils-core" }, "keywords": [ diff --git a/packages/test-utils/README.md b/packages/test-utils/README.md index 46a2e2dab3..38bf1e83cf 100644 --- a/packages/test-utils/README.md +++ b/packages/test-utils/README.md @@ -18,5 +18,5 @@ $ yarn add -D @backstage/test-utils ## Documentation -- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md) +- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 74e0c8dcf7..5b08d3becc 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -11,7 +11,7 @@ "homepage": "https://backstage.io", "repository": { "type": "git", - "url": "https://github.com/spotify/backstage", + "url": "https://github.com/backstage/backstage", "directory": "packages/test-utils" }, "keywords": [ diff --git a/packages/theme/README.md b/packages/theme/README.md index a1b6f81f92..4b29738193 100644 --- a/packages/theme/README.md +++ b/packages/theme/README.md @@ -18,5 +18,5 @@ $ yarn add @backstage/theme ## Documentation -- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md) +- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) diff --git a/packages/theme/package.json b/packages/theme/package.json index ddec0e7981..46377cac19 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -11,7 +11,7 @@ "homepage": "https://backstage.io", "repository": { "type": "git", - "url": "https://github.com/spotify/backstage", + "url": "https://github.com/backstage/backstage", "directory": "packages/theme" }, "keywords": [ diff --git a/plugins/README.md b/plugins/README.md index d56758b8f0..6651ba079f 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -2,16 +2,16 @@ Backstage is a single-page application composed of a set of plugins. -Our goal for the plugin ecosystem is that the definition of a plugin is flexible enough to allow you to expose pretty much any kind of infrastructure or software development tool as a plugin in Backstage. By following strong [design guidelines](https://github.com/spotify/backstage/blob/master/docs/dls/design.md) we ensure the overall user experience stays consistent between plugins. +Our goal for the plugin ecosystem is that the definition of a plugin is flexible enough to allow you to expose pretty much any kind of infrastructure or software development tool as a plugin in Backstage. By following strong [design guidelines](https://github.com/backstage/backstage/blob/master/docs/dls/design.md) we ensure the overall user experience stays consistent between plugins. ![plugin](../docs/assets/my-plugin_screenshot.png) ## Creating a plugin -To create a plugin, follow the steps outlined [here](https://github.com/spotify/backstage/blob/master/docs/plugins/create-a-plugin.md). +To create a plugin, follow the steps outlined [here](https://github.com/backstage/backstage/blob/master/docs/plugins/create-a-plugin.md). ## Suggesting a plugin -If you start developing a plugin that you aim to release as open source, we suggest that you create a [new Issue](https://github.com/spotify/backstage/issues/new?template=plugin_template.md). This helps the community know what plugins are in development. +If you start developing a plugin that you aim to release as open source, we suggest that you create a [new Issue](https://github.com/backstage/backstage/issues/new?template=plugin_template.md). This helps the community know what plugins are in development. You can also use this process if you have an idea for a good plugin but you hope that someone else will pick up the work. diff --git a/plugins/auth-backend/README.md b/plugins/auth-backend/README.md index 355fcc1536..4fcd19ecb7 100644 --- a/plugins/auth-backend/README.md +++ b/plugins/auth-backend/README.md @@ -119,7 +119,7 @@ To try out SAML, you can use the mock identity provider: ## Authentication providers -[How to add an auth provider](https://github.com/spotify/backstage/blob/master/docs/auth/add-auth-provider.md) +[How to add an auth provider](https://github.com/backstage/backstage/blob/master/docs/auth/add-auth-provider.md) ## Links diff --git a/plugins/catalog-backend/README.md b/plugins/catalog-backend/README.md index 1b4a653f4a..2f06b9c062 100644 --- a/plugins/catalog-backend/README.md +++ b/plugins/catalog-backend/README.md @@ -27,5 +27,5 @@ This will launch the full example backend, populated some example entities. ## Links -- [Frontend part of the plugin](https://github.com/spotify/backstage/tree/master/plugins/catalog) +- [Frontend part of the plugin](https://github.com/backstage/backstage/tree/master/plugins/catalog) - [The Backstage homepage](https://backstage.io) diff --git a/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts index b0e857b2bb..e4ddb249d6 100644 --- a/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts @@ -29,7 +29,7 @@ import { describe('CodeOwnersProcessor', () => { const mockUrl = ({ basePath = '' } = {}): string => - `https://github.com/spotify/backstage/blob/master/${basePath}catalog-info.yaml`; + `https://github.com/backstage/backstage/blob/master/${basePath}catalog-info.yaml`; const mockLocation = ({ basePath = '', type = 'github', @@ -39,12 +39,12 @@ describe('CodeOwnersProcessor', () => { }); const mockReadUrl = (basePath = '') => - `https://github.com/spotify/backstage/blob/master/${basePath}CODEOWNERS`; + `https://github.com/backstage/backstage/blob/master/${basePath}CODEOWNERS`; const mockGitUri = (codeOwnersPath: string = '') => { return { source: 'github.com', - owner: 'spotify', + owner: 'backstage', name: 'backstage', codeOwnersPath, }; @@ -94,7 +94,7 @@ describe('CodeOwnersProcessor', () => { codeOwnersPath: '/.github/CODEOWNERS', }), ).toBe( - 'https://github.com/spotify/backstage/blob/master/.github/CODEOWNERS', + 'https://github.com/backstage/backstage/blob/master/.github/CODEOWNERS', ); }); }); @@ -102,7 +102,7 @@ describe('CodeOwnersProcessor', () => { describe('buildCodeOwnerUrl', () => { it('should build a location spec to the codeowners', () => { expect(buildCodeOwnerUrl(mockUrl(), '/docs/CODEOWNERS')).toEqual( - 'https://github.com/spotify/backstage/blob/master/docs/CODEOWNERS', + 'https://github.com/backstage/backstage/blob/master/docs/CODEOWNERS', ); }); @@ -112,7 +112,9 @@ describe('CodeOwnersProcessor', () => { mockUrl({ basePath: 'packages/foo/' }), '/CODEOWNERS', ), - ).toEqual('https://github.com/spotify/backstage/blob/master/CODEOWNERS'); + ).toEqual( + 'https://github.com/backstage/backstage/blob/master/CODEOWNERS', + ); }); }); diff --git a/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts index 44f3163a97..48cd2dae01 100644 --- a/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts @@ -152,7 +152,8 @@ describe('PlaceholderProcessor', () => { }, { type: 'github', - target: 'https://github.com/spotify/backstage/a/b/catalog-info.yaml', + target: + 'https://github.com/backstage/backstage/a/b/catalog-info.yaml', }, ), ).resolves.toEqual({ @@ -163,7 +164,7 @@ describe('PlaceholderProcessor', () => { }); expect(read).toBeCalledWith( - 'https://github.com/spotify/backstage/a/file.txt', + 'https://github.com/backstage/backstage/a/file.txt', ); }); @@ -186,7 +187,8 @@ describe('PlaceholderProcessor', () => { }, { type: 'github', - target: 'https://github.com/spotify/backstage/a/b/catalog-info.yaml', + target: + 'https://github.com/backstage/backstage/a/b/catalog-info.yaml', }, ), ).resolves.toEqual({ @@ -197,7 +199,7 @@ describe('PlaceholderProcessor', () => { }); expect(read).toBeCalledWith( - 'https://github.com/spotify/backstage/a/b/file.json', + 'https://github.com/backstage/backstage/a/b/file.json', ); }); @@ -218,7 +220,8 @@ describe('PlaceholderProcessor', () => { }, { type: 'github', - target: 'https://github.com/spotify/backstage/a/b/catalog-info.yaml', + target: + 'https://github.com/backstage/backstage/a/b/catalog-info.yaml', }, ), ).resolves.toEqual({ @@ -229,7 +232,7 @@ describe('PlaceholderProcessor', () => { }); expect(read).toBeCalledWith( - 'https://github.com/spotify/backstage/a/file.yaml', + 'https://github.com/backstage/backstage/a/file.yaml', ); }); @@ -248,13 +251,14 @@ describe('PlaceholderProcessor', () => { metadata: { name: 'n' }, spec: { data: { - $text: 'https://github.com/spotify/backstage/catalog-info.yaml', + $text: 'https://github.com/backstage/backstage/catalog-info.yaml', }, }, }, { type: 'github', - target: 'https://github.com/spotify/backstage/a/b/catalog-info.yaml', + target: + 'https://github.com/backstage/backstage/a/b/catalog-info.yaml', }, ), ).resolves.toEqual({ @@ -265,7 +269,7 @@ describe('PlaceholderProcessor', () => { }); expect(read).toBeCalledWith( - 'https://github.com/spotify/backstage/catalog-info.yaml', + 'https://github.com/backstage/backstage/catalog-info.yaml', ); }); @@ -284,7 +288,7 @@ describe('PlaceholderProcessor', () => { metadata: { name: 'n' }, spec: { data: { - $text: 'https://github.com/spotify/backstage/catalog-info.yaml', + $text: 'https://github.com/backstage/backstage/catalog-info.yaml', }, }, }, @@ -301,7 +305,7 @@ describe('PlaceholderProcessor', () => { }); expect(read).toBeCalledWith( - 'https://github.com/spotify/backstage/catalog-info.yaml', + 'https://github.com/backstage/backstage/catalog-info.yaml', ); }); @@ -345,7 +349,7 @@ describe('yamlPlaceholderResolver', () => { const params: ResolverParams = { key: 'a', value: './file.yaml', - baseUrl: 'https://github.com/spotify/backstage/a/b/catalog-info.yaml', + baseUrl: 'https://github.com/backstage/backstage/a/b/catalog-info.yaml', read, }; @@ -389,7 +393,7 @@ describe('jsonPlaceholderResolver', () => { const params: ResolverParams = { key: 'a', value: './file.json', - baseUrl: 'https://github.com/spotify/backstage/a/b/catalog-info.yaml', + baseUrl: 'https://github.com/backstage/backstage/a/b/catalog-info.yaml', read, }; diff --git a/plugins/catalog/README.md b/plugins/catalog/README.md index cff632cb24..e9405a41f5 100644 --- a/plugins/catalog/README.md +++ b/plugins/catalog/README.md @@ -9,5 +9,5 @@ supply the base views to show and manage them. ## Links -- [Backend part of the plugin](https://github.com/spotify/backstage/tree/master/plugins/catalog-backend) +- [Backend part of the plugin](https://github.com/backstage/backstage/tree/master/plugins/catalog-backend) - [The Backstage homepage](https://backstage.io) diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx index bf69818995..2854af2f26 100644 --- a/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx @@ -27,7 +27,7 @@ describe('', () => { name: 'software', annotations: { 'backstage.io/managed-by-location': - 'github:https://github.com/spotify/backstage/blob/master/software.yaml', + 'github:https://github.com/backstage/backstage/blob/master/software.yaml', }, }, spec: { @@ -40,7 +40,7 @@ describe('', () => { expect(getByText('service')).toBeInTheDocument(); expect(getByText('View Source').closest('a')).toHaveAttribute( 'href', - 'https://github.com/spotify/backstage/blob/master/software.yaml', + 'https://github.com/backstage/backstage/blob/master/software.yaml', ); }); }); diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.tsx index fbbf5fa11c..74877b5a65 100644 --- a/plugins/catalog/src/components/AboutCard/AboutCard.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutCard.tsx @@ -87,7 +87,7 @@ function getCodeLinkInfo(entity: Entity): CodeLinkInfo { if (location) { // split by first `:` - // e.g. "github:https://github.com/spotify/backstage/blob/master/software.yaml" + // e.g. "github:https://github.com/backstage/backstage/blob/master/software.yaml" const [type, target] = location.split(/:(.+)/); return { icon: iconMap[type], href: target }; diff --git a/plugins/cost-insights/README.md b/plugins/cost-insights/README.md index 3b6e1e4fd5..886efa8eb6 100644 --- a/plugins/cost-insights/README.md +++ b/plugins/cost-insights/README.md @@ -21,7 +21,7 @@ yarn add @backstage/plugin-cost-insights 1. Configure `app-config.yaml`. See [Configuration](#configuration). -2. Create a CostInsights client. Clients must implement the CostInsightsApi interface. See the [API file](https://github.com/spotify/backstage/blob/master/plugins/cost-insights/src/api/CostInsightsApi.ts) for required methods and documentation. +2. Create a CostInsights client. Clients must implement the CostInsightsApi interface. See the [API file](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/api/CostInsightsApi.ts) for required methods and documentation. ```ts // path/to/CostInsightsClient.ts @@ -58,7 +58,7 @@ export { plugin as CostInsights } from '@backstage/plugin-cost-insights'; Cost Insights has only two required configuration fields: a map of cloud `products` for showing cost breakdowns and `engineerCost` - the average yearly cost of an engineer including benefits. Products must be defined as keys on the `products` field. -You can optionally supply a product `icon` to display in Cost Insights navigation. See the [type file](https://github.com/spotify/backstage/blob/master/plugins/cost-insights/src/types/Icon.ts) for supported types and Material UI icon [mappings](https://github.com/spotify/backstage/blob/master/plugins/cost-insights/src/utils/navigation.tsx). +You can optionally supply a product `icon` to display in Cost Insights navigation. See the [type file](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/types/Icon.ts) for supported types and Material UI icon [mappings](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/utils/navigation.tsx). **Note:** Product keys should be unique and camelCased. Backstage does not support underscores in configuration keys. @@ -106,7 +106,7 @@ costInsights: ## Alerts -The CostInsightsApi `getAlerts` method may return any type of alert or recommendation (called collectively "Action Items" in Cost Insights) that implements the [Alert type](https://github.com/spotify/backstage/blob/master/plugins/cost-insights/src/types/Alert.ts). This allows you to deliver any alerts or recommendations specific to your infrastructure or company migrations. +The CostInsightsApi `getAlerts` method may return any type of alert or recommendation (called collectively "Action Items" in Cost Insights) that implements the [Alert type](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/types/Alert.ts). This allows you to deliver any alerts or recommendations specific to your infrastructure or company migrations. The Alert type includes an `element` field to supply the JSX Element that will be rendered in the Cost Insights "Action Items" section; we recommend using Backstage's [InfoCard](https://backstage.io/storybook/?path=/story/layout-information-card--default) and [Recharts](http://recharts.org/en-US/) to show actionable visualizations. diff --git a/plugins/github-actions/examples/sample.yaml b/plugins/github-actions/examples/sample.yaml index 7d9de50463..af12b6bb04 100644 --- a/plugins/github-actions/examples/sample.yaml +++ b/plugins/github-actions/examples/sample.yaml @@ -5,7 +5,7 @@ metadata: description: backstage.io annotations: github.com/project-slug: 'spotify/backstage' - backstage.io/techdocs-ref: github:https://github.com/spotify/backstage.git + backstage.io/techdocs-ref: github:https://github.com/backstage/backstage.git spec: type: website lifecycle: production diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index 9e1ffd5ef1..fe88c92ea7 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -8,10 +8,10 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, - "homepage": "https://github.com/spotify/backstage/tree/master/plugins/graphiql#readme", + "homepage": "https://github.com/backstage/backstage/tree/master/plugins/graphiql#readme", "repository": { "type": "git", - "url": "https://github.com/spotify/backstage", + "url": "https://github.com/backstage/backstage", "directory": "plugins/graphiql" }, "keywords": [ diff --git a/plugins/lighthouse/README.md b/plugins/lighthouse/README.md index 19244f3cf0..cdcc2fe2c4 100644 --- a/plugins/lighthouse/README.md +++ b/plugins/lighthouse/README.md @@ -23,7 +23,7 @@ _It's likely you will need to enable CORS when running lighthouse-audit-service. with the environment variable `LAS_CORS` set to `true`._ When you have an instance running that Backstage can hook into, make sure to export the plugin in -your app's [`plugins.ts`](https://github.com/spotify/backstage/blob/master/packages/app/src/plugins.ts) +your app's [`plugins.ts`](https://github.com/backstage/backstage/blob/master/packages/app/src/plugins.ts) to enable the plugin: ```js @@ -32,7 +32,7 @@ export LighthousePlugin; ``` Then, you need to use the `lighthouseApiRef` exported from the plugin to initialize the Rest API in -your [`apis.ts`](https://github.com/spotify/backstage/blob/master/packages/app/src/apis.ts). +your [`apis.ts`](https://github.com/backstage/backstage/blob/master/packages/app/src/apis.ts). ```js import { ApiHolder, ApiRegistry } from '@backstage/core'; @@ -51,7 +51,7 @@ export const apis = (config: ConfigApi) => { } ``` -Then configure the lighthouse service url in your [`app-config.yaml`](https://github.com/spotify/backstage/blob/master/app-config.yaml). +Then configure the lighthouse service url in your [`app-config.yaml`](https://github.com/backstage/backstage/blob/master/app-config.yaml). ```yaml lighthouse: diff --git a/plugins/lighthouse/src/components/Intro/index.tsx b/plugins/lighthouse/src/components/Intro/index.tsx index ee5d5d66af..dc2a8ea283 100644 --- a/plugins/lighthouse/src/components/Intro/index.tsx +++ b/plugins/lighthouse/src/components/Intro/index.tsx @@ -43,7 +43,7 @@ _It's likely you will need to enable CORS when running lighthouse-audit-service. with the environment variable \`LAS_CORS\` set to \`true\`._ When you have an instance running that Backstage can hook into, make sure to export the plugin in -your app's [\`plugins.ts\`](https://github.com/spotify/backstage/blob/master/packages/app/src/plugins.ts) +your app's [\`plugins.ts\`](https://github.com/backstage/backstage/blob/master/packages/app/src/plugins.ts) to enable the plugin: \`\`\`js @@ -52,7 +52,7 @@ export LighthousePlugin; \`\`\` Then, you need to use the \`lighthouseApiRef\` exported from the plugin to initialize the Rest API in -your [\`apis.ts\`](https://github.com/spotify/backstage/blob/master/packages/app/src/apis.ts). +your [\`apis.ts\`](https://github.com/backstage/backstage/blob/master/packages/app/src/apis.ts). \`\`\`js import { ApiHolder, ApiRegistry } from '@backstage/core'; diff --git a/plugins/rollbar-backend/README.md b/plugins/rollbar-backend/README.md index f441efa416..efa715a2d3 100644 --- a/plugins/rollbar-backend/README.md +++ b/plugins/rollbar-backend/README.md @@ -18,5 +18,5 @@ access account token._ ## Links -- [Frontend part of the plugin](https://github.com/spotify/backstage/tree/master/plugins/rollbar) +- [Frontend part of the plugin](https://github.com/backstage/backstage/tree/master/plugins/rollbar) - [The Backstage homepage](https://backstage.io) diff --git a/plugins/rollbar/README.md b/plugins/rollbar/README.md index 98f2272508..0af117e07c 100644 --- a/plugins/rollbar/README.md +++ b/plugins/rollbar/README.md @@ -4,7 +4,7 @@ Website: [https://rollbar.com/](https://rollbar.com/) ## Setup -1. Configure the [rollbar backend plugin](https://github.com/spotify/backstage/tree/master/plugins/rollbar-backend/README.md) +1. Configure the [rollbar backend plugin](https://github.com/backstage/backstage/tree/master/plugins/rollbar-backend/README.md) 2. If you have standalone app (you didn't clone this repo), then do @@ -83,5 +83,5 @@ metadata: ## Links -- [Backend part of the plugin](https://github.com/spotify/backstage/tree/master/plugins/rollbar-backend) +- [Backend part of the plugin](https://github.com/backstage/backstage/tree/master/plugins/rollbar-backend) - [The Backstage homepage](https://backstage.io) diff --git a/plugins/scaffolder-backend/sample-templates/all-templates.yaml b/plugins/scaffolder-backend/sample-templates/all-templates.yaml index 1eb619a072..6866543ab0 100644 --- a/plugins/scaffolder-backend/sample-templates/all-templates.yaml +++ b/plugins/scaffolder-backend/sample-templates/all-templates.yaml @@ -6,8 +6,8 @@ metadata: spec: type: github targets: - - https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml - - https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml - - https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml + - https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml + - https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml + - https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml - https://github.com/spotify/cookiecutter-golang/blob/master/template.yaml - - https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml + - https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml diff --git a/plugins/scaffolder/README.md b/plugins/scaffolder/README.md index a356cb8ec9..05a68dafdd 100644 --- a/plugins/scaffolder/README.md +++ b/plugins/scaffolder/README.md @@ -6,5 +6,5 @@ This is the frontend part of the default scaffolder plugin. ## Links -- [Backend part of the plugin](https://github.com/spotify/backstage/tree/master/plugins/scaffolder-backend) +- [Backend part of the plugin](https://github.com/backstage/backstage/tree/master/plugins/scaffolder-backend) - [The Backstage homepage](https://backstage.io) diff --git a/plugins/techdocs-backend/README.md b/plugins/techdocs-backend/README.md index b7eca2c5cb..625f0910b5 100644 --- a/plugins/techdocs-backend/README.md +++ b/plugins/techdocs-backend/README.md @@ -33,7 +33,7 @@ Currently the build process of techdocs-backend is split up in these three stage - Generators - Publishers -Preparers read your entity data and creates a working directory with your documentation source code. For example if you have set your `backstage.io/techdocs-ref` to `github:https://github.com/spotify/backstage.git` it will clone that repository to a temp folder and pass that on to the generator. +Preparers read your entity data and creates a working directory with your documentation source code. For example if you have set your `backstage.io/techdocs-ref` to `github:https://github.com/backstage/backstage.git` it will clone that repository to a temp folder and pass that on to the generator. Generators takes the prepared source and runs the `techdocs-container` on it. It then passes on the output folder of that build to the publisher. @@ -43,5 +43,5 @@ Any of these can be extended. If we want to publish to a external static file se ## Links -- [Frontend part of the plugin](https://github.com/spotify/backstage/tree/master/plugins/techdocs) +- [Frontend part of the plugin](https://github.com/backstage/backstage/tree/master/plugins/techdocs) - [The Backstage homepage](https://backstage.io) diff --git a/plugins/techdocs-backend/examples/documented-component/documented-component.yaml b/plugins/techdocs-backend/examples/documented-component/documented-component.yaml index 045ff89013..800116344f 100644 --- a/plugins/techdocs-backend/examples/documented-component/documented-component.yaml +++ b/plugins/techdocs-backend/examples/documented-component/documented-component.yaml @@ -4,7 +4,7 @@ metadata: name: documented-component description: A Service with TechDocs documentation annotations: - backstage.io/techdocs-ref: 'github:https://github.com/spotify/backstage/blob/master/plugins/techdocs-backend/examples/documented-component' + backstage.io/techdocs-ref: 'github:https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend/examples/documented-component' spec: type: service lifecycle: experimental diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/commonGit.test.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/commonGit.test.ts index cec1110f97..843df476e9 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/commonGit.test.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/commonGit.test.ts @@ -51,7 +51,7 @@ describe('commonGit preparer', () => { const mockEntity = createMockEntity({ 'backstage.io/techdocs-ref': - 'github:https://github.com/spotify/backstage/blob/master/plugins/techdocs-backend/examples/documented-component', + 'github:https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend/examples/documented-component', }); const tempDocsPath = await preparer.prepare(mockEntity); diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.test.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.test.ts index 4412e6bf73..dc2b1d7d48 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.test.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.test.ts @@ -78,7 +78,7 @@ describe('directory preparer', () => { const mockEntity = createMockEntity({ 'backstage.io/managed-by-location': - 'github:https://github.com/spotify/backstage/blob/master/catalog-info.yaml', + 'github:https://github.com/backstage/backstage/blob/master/catalog-info.yaml', 'backstage.io/techdocs-ref': 'dir:./docs', }); diff --git a/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx b/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx index 7b94024948..4cd2c34ec1 100644 --- a/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx +++ b/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx @@ -69,7 +69,7 @@ const WelcomePage = () => { of these phases. The best way to keep track of the progress is through the  Milestones @@ -116,7 +116,7 @@ const WelcomePage = () => { We suggest you either check out the documentation for{' '} creating a plugin @@ -127,7 +127,7 @@ const WelcomePage = () => { {' '} in the directory{' '} plugins/ @@ -144,7 +144,7 @@ const WelcomePage = () => { Create a plugin From 7450c5eabbe9c1c0349283a7a038275a8ca3d1d3 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 3 Nov 2020 11:06:26 +0100 Subject: [PATCH 063/252] Release new version of techdocs-container (#3208) Includes upgrade of mkdocs-techdocs-core package from 0.0.10 to 0.0.11: --- packages/techdocs-container/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/techdocs-container/Dockerfile b/packages/techdocs-container/Dockerfile index 1c1ca264db..c30db043cd 100644 --- a/packages/techdocs-container/Dockerfile +++ b/packages/techdocs-container/Dockerfile @@ -18,7 +18,7 @@ FROM python:3.8-alpine RUN apk update && apk --no-cache add gcc musl-dev openjdk11-jdk curl graphviz ttf-dejavu fontconfig RUN curl -o plantuml.jar -L http://sourceforge.net/projects/plantuml/files/plantuml.1.2020.16.jar/download && echo "c789ace48347c43073232b1458badc5810c01fe8 plantuml.jar" | sha1sum -c - && mv plantuml.jar /opt/plantuml.jar -RUN pip install --upgrade pip && pip install mkdocs-techdocs-core==0.0.10 +RUN pip install --upgrade pip && pip install mkdocs-techdocs-core==0.0.11 # Create script to call plantuml.jar from a location in path From 474bb5b57f31eaf5868fb5efcae275ea64d9182e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 3 Nov 2020 11:39:12 +0100 Subject: [PATCH 064/252] rewrite other references to spotify/backstage and sorrounding things --- README.md | 2 +- catalog-info.yaml | 4 ++-- contrib/chart/backstage/Chart.yaml | 2 +- contrib/chart/backstage/values.yaml | 4 ++-- .../backstage/Chart.yaml | 2 +- docs/conf/writing.md | 2 +- docs/dls/design.md | 12 ++++++------ .../software-catalog/well-known-annotations.md | 6 +++--- .../extending/create-your-own-preparer.md | 2 +- .../extending/create-your-own-publisher.md | 2 +- .../extending/create-your-own-templater.md | 2 +- .../running-backstage-locally.md | 2 +- docs/overview/architecture-overview.md | 4 ++-- .../blog/2020-09-30-backstage-design-system.md | 2 +- microsite/core/Footer.js | 2 +- packages/cli/src/lib/codeowners/codeowners.ts | 2 +- .../default-app/catalog-info.yaml.hbs | 2 +- packages/techdocs-container/README.md | 2 +- .../processors/GithubOrgReaderProcessor.ts | 2 +- .../ingestion/processors/util/parse.test.ts | 4 ++-- plugins/github-actions/README.md | 3 +-- plugins/github-actions/examples/sample.yaml | 2 +- plugins/rollbar-backend/README.md | 2 +- plugins/rollbar/README.md | 2 +- .../docs-template/template.yaml | 7 +++---- .../src/scaffolder/jobs/processor.test.ts | 2 +- .../stages/prepare/preparers.test.ts | 2 +- .../stages/templater/cookiecutter.test.ts | 18 +++++++++--------- .../stages/templater/templaters.test.ts | 2 +- plugins/sonarqube/README.md | 2 +- scripts/verify-links.js | 4 ++-- 31 files changed, 53 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index b1926f070e..d2ed3c6e96 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![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) [![Discord](https://img.shields.io/discord/687207715902193673)](https://discord.gg/EBHEGzX) ![Code style](https://img.shields.io/badge/code_style-prettier-ff69b4.svg) -[![Codecov](https://img.shields.io/codecov/c/github/spotify/backstage)](https://codecov.io/gh/spotify/backstage) +[![Codecov](https://img.shields.io/codecov/c/github/backstage/backstage)](https://codecov.io/gh/backstage/backstage) [![](https://img.shields.io/npm/v/@backstage/core?label=Version)](https://github.com/backstage/backstage/releases) ## What is Backstage? diff --git a/catalog-info.yaml b/catalog-info.yaml index 9c7e5309dd..617d01093e 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -5,10 +5,10 @@ metadata: description: | Backstage is an open-source developer portal that puts the developer experience first. annotations: - github.com/project-slug: spotify/backstage + github.com/project-slug: backstage/backstage backstage.io/techdocs-ref: github:https://github.com/backstage/backstage.git lighthouse.com/website-url: https://backstage.io spec: type: library - owner: Spotify + owner: CNCF lifecycle: experimental diff --git a/contrib/chart/backstage/Chart.yaml b/contrib/chart/backstage/Chart.yaml index be5c20af82..8aeab9be3e 100644 --- a/contrib/chart/backstage/Chart.yaml +++ b/contrib/chart/backstage/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 name: backstage -description: A Helm chart for Spotify Backstage +description: A Helm chart for Backstage type: application # This is the chart version. This version number should be incremented each time you make changes diff --git a/contrib/chart/backstage/values.yaml b/contrib/chart/backstage/values.yaml index eb06422d34..a4a0fadcc2 100644 --- a/contrib/chart/backstage/values.yaml +++ b/contrib/chart/backstage/values.yaml @@ -105,14 +105,14 @@ appConfig: rejectUnauthorized: false ca: sentry: - organization: spotify + organization: example-org-name techdocs: storageUrl: https://demo.example.com/api/techdocs/static/docs requestUrl: https://demo.example.com/api/techdocs lighthouse: baseUrl: https://demo.example.com/lighthouse-api rollbar: - organization: roadie + organization: example-org-name # Auth config has recently moved into the app config file in upstream Backstage. However, # most of this config simply mandates that items like the client id and client secret should diff --git a/contrib/kubernetes/basic_kubernetes_example_with_helm/backstage/Chart.yaml b/contrib/kubernetes/basic_kubernetes_example_with_helm/backstage/Chart.yaml index efc3635a7f..6cbb7ea3cb 100644 --- a/contrib/kubernetes/basic_kubernetes_example_with_helm/backstage/Chart.yaml +++ b/contrib/kubernetes/basic_kubernetes_example_with_helm/backstage/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v1 appVersion: '1.0' -description: A Helm chart for Spotify Backstage +description: A Helm chart for Backstage name: backstage version: 0.1.1-alpha.12 diff --git a/docs/conf/writing.md b/docs/conf/writing.md index 06d4b52f3f..4eb92bb2ff 100644 --- a/docs/conf/writing.md +++ b/docs/conf/writing.md @@ -19,7 +19,7 @@ backend: baseUrl: http://localhost:7000 organization: - name: Spotify + name: CNCF proxy: /my/api: diff --git a/docs/dls/design.md b/docs/dls/design.md index fa3a0c159e..a108343a42 100644 --- a/docs/dls/design.md +++ b/docs/dls/design.md @@ -46,9 +46,9 @@ referencing Figma documents to share specs and prototypes with the community. ### Creating a New Design Component -| Step 1 | Step 2 | Step 3 | Step 4 | Step 5 | Step 6 | -| :------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------- | :--------------------------------------------------------------------------- | :------------------------------------------------------------------------------------- | -| Platform design team submits an issue to **spotify/Backstage GitHub** with a potential component. | Backstage community offers feedback or approval on **spotify/Backstage GitHub**. | Platform design team adjusts accordingly (as they see fit) and update the Figma DLS document. | Designed component is added to **spotify/Backstage GitHub** as an issue. | External or internal Backstage open source contributors build the component. | External or internal contributors add the component to the **Backstage Storybook**. 🎉 | +| Step 1 | Step 2 | Step 3 | Step 4 | Step 5 | Step 6 | +| :-------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------- | :--------------------------------------------------------------------------- | :------------------------------------------------------------------------------------- | +| Platform design team submits an issue to **backstage/backstage GitHub** with a potential component. | Backstage community offers feedback or approval on **backstage/backstage GitHub**. | Platform design team adjusts accordingly (as they see fit) and update the Figma DLS document. | Designed component is added to **backstage/backstage GitHub** as an issue. | External or internal Backstage open source contributors build the component. | External or internal contributors add the component to the **Backstage Storybook**. 🎉 | ### Building for Backstage @@ -56,9 +56,9 @@ referencing Figma documents to share specs and prototypes with the community. | :------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------- | | External or internal contributors use Backstage and come up with an idea of an entity to build for Backstage. | External or internal contributors refer to the Backstage Open Source design system documentation in the Figma DLS document. | External or internal contributors leverage the components and tokens from the Backstage Storybook. | External or internal contributors build their Backstage entity. | -| Step 5 | Step 6 | Step 7 | Step 8 | -| :------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------ | -| External or internal contributors make a pull request for their entity on spotify/Backstage GitHub for review. | Platform designers and devs review the entity and submit feedback or approval on spotify/Backstage GitHub. | External or internal contributors make the changes, pull request is approved and the entity is merged. It’s live on Backstage! 🎉 | If the entity happens to be or include a UX component, it’s added to Backstage Storybook as well. | +| Step 5 | Step 6 | Step 7 | Step 8 | +| :--------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------ | +| External or internal contributors make a pull request for their entity on backstage/backstage GitHub for review. | Platform designers and devs review the entity and submit feedback or approval on backstage/backstage GitHub. | External or internal contributors make the changes, pull request is approved and the entity is merged. It’s live on Backstage! 🎉 | If the entity happens to be or include a UX component, it’s added to Backstage Storybook as well. | The following diagram shows the relationship between the Backstage Design System and our foundation, which comprises of [Material UI](https://material-ui.com/) diff --git a/docs/features/software-catalog/well-known-annotations.md b/docs/features/software-catalog/well-known-annotations.md index f1ea9347a7..25489b89d9 100644 --- a/docs/features/software-catalog/well-known-annotations.md +++ b/docs/features/software-catalog/well-known-annotations.md @@ -75,7 +75,7 @@ that entity. # Example: metadata: annotations: - github.com/project-slug: spotify/backstage + github.com/project-slug: backstage/backstage ``` The value of this annotation is the so-called slug that identifies a project on @@ -93,7 +93,7 @@ that entity. # Example: metadata: annotations: - github.com/team-slug: spotify/backstage-core + github.com/team-slug: backstage/maintainers ``` The value of this annotation is the so-called slug that identifies a team on @@ -146,7 +146,7 @@ that entity. # Example: metadata: annotations: - rollbar.com/project-slug: spotify/pump-station + rollbar.com/project-slug: backstage/pump-station ``` The value of this annotation is the so-called slug (or alternatively, the ID) of diff --git a/docs/features/software-templates/extending/create-your-own-preparer.md b/docs/features/software-templates/extending/create-your-own-preparer.md index 186b2a3c0a..5419748c3a 100644 --- a/docs/features/software-templates/extending/create-your-own-preparer.md +++ b/docs/features/software-templates/extending/create-your-own-preparer.md @@ -15,7 +15,7 @@ location protocols: - `github://` These two are added to the `PreparersBuilder` and then passed into the -`createRouter` function of the `@spotify/plugin-scaffolder-backend`. +`createRouter` function of the `@backstage/plugin-scaffolder-backend`. A full example backend can be found in [`scaffolder.ts`](https://github.com/backstage/backstage/blob/d91c10f654475a60829fa33a5c81018e517a319a/packages/backend/src/plugins/scaffolder.ts), diff --git a/docs/features/software-templates/extending/create-your-own-publisher.md b/docs/features/software-templates/extending/create-your-own-publisher.md index f076640ebe..97854c999a 100644 --- a/docs/features/software-templates/extending/create-your-own-publisher.md +++ b/docs/features/software-templates/extending/create-your-own-publisher.md @@ -18,7 +18,7 @@ Currently we provide the following `publishers`: - `github` This publisher is passed through to the `createRouter` function of the -`@spotify/plugin-scaffolder-backend`. Currently, only one publisher is +`@backstage/plugin-scaffolder-backend`. Currently, only one publisher is supported, but PR's are always welcome. An full example backend can be found diff --git a/docs/features/software-templates/extending/create-your-own-templater.md b/docs/features/software-templates/extending/create-your-own-templater.md index 26e01e2782..63acd38286 100644 --- a/docs/features/software-templates/extending/create-your-own-templater.md +++ b/docs/features/software-templates/extending/create-your-own-templater.md @@ -17,7 +17,7 @@ Currently we provide the following templaters: - `cookiecutter` This templater is added to the `TemplaterBuilder` and then passed into the -`createRouter` function of the `@spotify/plugin-scaffolder-backend` +`createRouter` function of the `@backstage/plugin-scaffolder-backend` An full example backend can be found [here](https://github.com/backstage/backstage/blob/d91c10f654475a60829fa33a5c81018e517a319a/packages/backend/src/plugins/scaffolder.ts), diff --git a/docs/getting-started/running-backstage-locally.md b/docs/getting-started/running-backstage-locally.md index de3dbe93b5..a30a918fd0 100644 --- a/docs/getting-started/running-backstage-locally.md +++ b/docs/getting-started/running-backstage-locally.md @@ -42,7 +42,7 @@ of GitHub and run an initial build. ```bash # Start from your local development folder -git clone --depth 1 git@github.com:spotify/backstage.git +git clone --depth 1 git@github.com:backstage/backstage.git cd backstage # Fetch our dependencies and run an initial build diff --git a/docs/overview/architecture-overview.md b/docs/overview/architecture-overview.md index ddf7980cbc..2d65e9e430 100644 --- a/docs/overview/architecture-overview.md +++ b/docs/overview/architecture-overview.md @@ -169,8 +169,8 @@ in the future. To learn more about the future of databases and Backstage, see the following GitHub issues. -- [Knex + Plugins (Multiple vs Single Database) · Issue #1598 · spotify/backstage](https://github.com/backstage/backstage/issues/1598) -- [Update migrations to support postgres by dariddler · Pull Request #1527 · spotify/backstage](https://github.com/backstage/backstage/pull/1527#discussion_r450374145) +- [Knex + Plugins (Multiple vs Single Database) · Issue #1598 · backstage/backstage](https://github.com/backstage/backstage/issues/1598) +- [Update migrations to support postgres by dariddler · Pull Request #1527 · backstage/backstage](https://github.com/backstage/backstage/pull/1527#discussion_r450374145) ## Containerization diff --git a/microsite/blog/2020-09-30-backstage-design-system.md b/microsite/blog/2020-09-30-backstage-design-system.md index bb12a26100..a1d087f755 100644 --- a/microsite/blog/2020-09-30-backstage-design-system.md +++ b/microsite/blog/2020-09-30-backstage-design-system.md @@ -70,7 +70,7 @@ To keep up with our latest design guidelines, go to [Designing for Backstage](ht ### [GitHub](https://github.com/backstage/backstage) -Join in on the action [at spotify/backstage on GitHub](https://github.com/backstage/backstage) by submitting issues and opening pull requests for all things related to components and patterns in Backstage. +Join in on the action [at backstage/backstage on GitHub](https://github.com/backstage/backstage) by submitting issues and opening pull requests for all things related to components and patterns in Backstage. ![img](assets/backstage-discord-DS.png) diff --git a/microsite/core/Footer.js b/microsite/core/Footer.js index e48c86acce..6ce3c3dd84 100644 --- a/microsite/core/Footer.js +++ b/microsite/core/Footer.js @@ -59,7 +59,7 @@ class Footer extends React.Component { className="github-button" href={this.props.config.repoUrl} data-icon="octicon-star" - data-count-href="/spotify/backstage/stargazers" + data-count-href="/backstage/backstage/stargazers" data-show-count="true" data-count-aria-label="# stargazers on GitHub" aria-label="Star this project on GitHub" diff --git a/packages/cli/src/lib/codeowners/codeowners.ts b/packages/cli/src/lib/codeowners/codeowners.ts index 91b0e19782..aa24f91aac 100644 --- a/packages/cli/src/lib/codeowners/codeowners.ts +++ b/packages/cli/src/lib/codeowners/codeowners.ts @@ -20,7 +20,7 @@ import path from 'path'; const TEAM_ID_RE = /^@[-\w]+\/[-\w]+$/; const USER_ID_RE = /^@[-\w]+$/; const EMAIL_RE = /^[^@]+@[-.\w]+\.[-\w]+$/i; -const DEFAULT_OWNER = '@spotify/backstage-core'; +const DEFAULT_OWNER = '@backstage/maintainers'; type CodeownersEntry = { ownedPath: string; diff --git a/packages/create-app/templates/default-app/catalog-info.yaml.hbs b/packages/create-app/templates/default-app/catalog-info.yaml.hbs index b3fbbda5ce..0f8e4f5aeb 100644 --- a/packages/create-app/templates/default-app/catalog-info.yaml.hbs +++ b/packages/create-app/templates/default-app/catalog-info.yaml.hbs @@ -5,7 +5,7 @@ metadata: description: An example of a Backstage application. # Example for optional annotations # annotations: - # github.com/project-slug: spotify/backstage + # github.com/project-slug: backstage/backstage # backstage.io/techdocs-ref: github:https://github.com/backstage/backstage.git spec: type: website diff --git a/packages/techdocs-container/README.md b/packages/techdocs-container/README.md index 7f1350031b..53a518fa97 100644 --- a/packages/techdocs-container/README.md +++ b/packages/techdocs-container/README.md @@ -24,7 +24,7 @@ Then open up `http://localhost:8000` on your local machine. This container is published on DockerHub - https://hub.docker.com/r/spotify/techdocs -The publishing is configured by [Automated Builds](https://hub.docker.com/repository/docker/spotify/techdocs/builds/edit) feature on Docker Hub which is triggered from GitHub (on new commits and releases). @spotify/techdocs-core team has access to the settings. +The publishing is configured by [Automated Builds](https://hub.docker.com/repository/docker/spotify/techdocs/builds/edit) feature on Docker Hub which is triggered from GitHub (on new commits and releases). @backstage/techdocs-core team has access to the settings. The `latest` tag on Docker Hub points to the recent commits in the `master` branch. The [version tags](https://hub.docker.com/r/spotify/techdocs/tags) (e.g. v0.1.1-alpha.24) point to the GitHub tags created from [releases](https://github.com/backstage/backstage/releases) of this GitHub repository. diff --git a/plugins/catalog-backend/src/ingestion/processors/GithubOrgReaderProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/GithubOrgReaderProcessor.ts index bef57a6e5c..9aecb8b86e 100644 --- a/plugins/catalog-backend/src/ingestion/processors/GithubOrgReaderProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/GithubOrgReaderProcessor.ts @@ -182,7 +182,7 @@ export function readConfig(config: Config): ProviderConfig[] { export function parseUrl(urlString: string): { org: string } { const path = new URL(urlString).pathname.substr(1).split('/'); - // /spotify + // /backstage if (path.length === 1 && path[0].length) { return { org: decodeURIComponent(path[0]) }; } diff --git a/plugins/catalog-backend/src/ingestion/processors/util/parse.test.ts b/plugins/catalog-backend/src/ingestion/processors/util/parse.test.ts index 1400a066cd..8cb91fb2ce 100644 --- a/plugins/catalog-backend/src/ingestion/processors/util/parse.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/util/parse.test.ts @@ -34,7 +34,7 @@ describe('parseEntityYaml', () => { name: backstage description: backstage.io annotations: - github.com/project-slug: 'spotify/backstage' + github.com/project-slug: 'backstage/backstage' spec: type: website lifecycle: production @@ -54,7 +54,7 @@ describe('parseEntityYaml', () => { name: 'backstage', description: 'backstage.io', annotations: { - 'github.com/project-slug': 'spotify/backstage', + 'github.com/project-slug': 'backstage/backstage', }, }, spec: { diff --git a/plugins/github-actions/README.md b/plugins/github-actions/README.md index 07a69cdfad..1a46022c58 100644 --- a/plugins/github-actions/README.md +++ b/plugins/github-actions/README.md @@ -26,8 +26,7 @@ TBD name: backstage description: backstage.io annotations: - github.com/project-slug: 'spotify/backstage' - + github.com/project-slug: 'backstage/backstage' spec: type: website lifecycle: production diff --git a/plugins/github-actions/examples/sample.yaml b/plugins/github-actions/examples/sample.yaml index af12b6bb04..beda3c58ed 100644 --- a/plugins/github-actions/examples/sample.yaml +++ b/plugins/github-actions/examples/sample.yaml @@ -4,7 +4,7 @@ metadata: name: backstage description: backstage.io annotations: - github.com/project-slug: 'spotify/backstage' + github.com/project-slug: 'backstage/backstage' backstage.io/techdocs-ref: github:https://github.com/backstage/backstage.git spec: type: website diff --git a/plugins/rollbar-backend/README.md b/plugins/rollbar-backend/README.md index efa715a2d3..8f13eb5f26 100644 --- a/plugins/rollbar-backend/README.md +++ b/plugins/rollbar-backend/README.md @@ -8,7 +8,7 @@ The following values are read from the configuration file. ```yaml rollbar: - organization: spotify + organization: organization-name accountToken: $env: ROLLBAR_ACCOUNT_TOKEN ``` diff --git a/plugins/rollbar/README.md b/plugins/rollbar/README.md index 0af117e07c..a232462dde 100644 --- a/plugins/rollbar/README.md +++ b/plugins/rollbar/README.md @@ -53,7 +53,7 @@ const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( ```yaml # app.config.yaml rollbar: - organization: spotify + organization: organization-name accountToken: $env: ROLLBAR_ACCOUNT_TOKEN ``` diff --git a/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml b/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml index 31b57135ad..36404f088c 100644 --- a/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml +++ b/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml @@ -9,13 +9,13 @@ metadata: - techdocs - mkdocs spec: - owner: spotify/techdocs-core + owner: backstage/techdocs-core templater: cookiecutter type: documentation path: '.' - + schema: - required: + required: - component_id - description properties: @@ -27,4 +27,3 @@ spec: title: Description type: string description: Help others understand what these docs are about. - diff --git a/plugins/scaffolder-backend/src/scaffolder/jobs/processor.test.ts b/plugins/scaffolder-backend/src/scaffolder/jobs/processor.test.ts index 8a875e4e2b..d23db937bb 100644 --- a/plugins/scaffolder-backend/src/scaffolder/jobs/processor.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/jobs/processor.test.ts @@ -61,7 +61,7 @@ describe('JobProcessor', () => { const mockValues: RequiredTemplateValues = { owner: 'blobby', - storePath: 'spotify/mock-repo', + storePath: 'backstage/mock-repo', }; describe('create', () => { diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/preparers.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/preparers.test.ts index 0aaef2633d..7c2d7e0a47 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/preparers.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/preparers.test.ts @@ -24,7 +24,7 @@ describe('Preparers', () => { metadata: { annotations: { 'backstage.io/managed-by-location': - 'file:/Users/bingo/spotify/backstage/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml', + 'file:/Users/bingo/backstage/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml', }, name: 'react-ssr-template', title: 'React SSR Template', diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.test.ts index 106a448d7c..568c95a073 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.test.ts @@ -51,12 +51,12 @@ describe('CookieCutter Templater', () => { return await fs.promises.mkdtemp(path.join(tempDir, 'temp')); }; - it('should write a cookiecutter.json file with the values from the entitiy', async () => { + it('should write a cookiecutter.json file with the values from the entity', async () => { const tempdir = await mkTemp(); const values = { owner: 'blobby', - storePath: 'spotify/end-repo', + storePath: 'backstage/end-repo', description: 'description', component_id: 'newthing', }; @@ -78,7 +78,7 @@ describe('CookieCutter Templater', () => { const values = { owner: 'blobby', - storePath: 'spotify/end-repo', + storePath: 'backstage/end-repo', component_id: 'something', }; @@ -96,7 +96,7 @@ describe('CookieCutter Templater', () => { const values = { owner: 'blobby', - storePath: 'spotify/end-repo', + storePath: 'backstage/end-repo', }; await expect( @@ -109,7 +109,7 @@ describe('CookieCutter Templater', () => { const values = { owner: 'blobby', - storePath: 'spotify/end-repo', + storePath: 'backstage/end-repo', component_id: 'newthing', }; @@ -137,7 +137,7 @@ describe('CookieCutter Templater', () => { const values = { owner: 'blobby', - storePath: 'spotify/end-repo', + storePath: 'backstage/end-repo', component_id: 'newthing', }; @@ -157,7 +157,7 @@ describe('CookieCutter Templater', () => { const values = { owner: 'blobby', - storePath: 'spotify/end-repo', + storePath: 'backstage/end-repo', component_id: 'newthing', }; @@ -203,7 +203,7 @@ describe('CookieCutter Templater', () => { const values = { owner: 'blobby', - storePath: 'spotify/end-repo', + storePath: 'backstage/end-repo', component_id: 'newthing', }; @@ -243,7 +243,7 @@ describe('CookieCutter Templater', () => { directory: tempdir, values: { owner: 'blobby', - storePath: 'spotify/end-repo', + storePath: 'backstage/end-repo', }, logStream: stream, dockerClient: mockDocker, diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/templater/templaters.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/templater/templaters.test.ts index 1c4fe90207..9ef20b7740 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/templater/templaters.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/templater/templaters.test.ts @@ -24,7 +24,7 @@ describe('Templaters', () => { metadata: { annotations: { 'backstage.io/managed-by-location': - 'file:/Users/bingo/spotify/backstage/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml', + 'file:/Users/bingo/backstage/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml', }, name: 'react-ssr-template', title: 'React SSR Template', diff --git a/plugins/sonarqube/README.md b/plugins/sonarqube/README.md index 3d3c1f725b..af18017359 100644 --- a/plugins/sonarqube/README.md +++ b/plugins/sonarqube/README.md @@ -90,6 +90,6 @@ metadata: sonarqube.org/project-key: YOUR_PROJECT_KEY spec: type: library - owner: Spotify + owner: CNCF lifecycle: experimental ``` diff --git a/scripts/verify-links.js b/scripts/verify-links.js index 6126f693e4..0422352d4e 100755 --- a/scripts/verify-links.js +++ b/scripts/verify-links.js @@ -27,7 +27,7 @@ async function verifyUrl(basePath, absUrl, docPages) { // Avoid having absolute URL links within docs/, so that links work on the site if ( absUrl.match( - /https:\/\/github.com\/spotify\/backstage\/(tree|blob)\/master\/docs\//, + /https:\/\/github.com\/backstage\/backstage\/(tree|blob)\/master\/docs\//, ) && basePath.match(/^(?:docs|microsite)\//) ) { @@ -37,7 +37,7 @@ async function verifyUrl(basePath, absUrl, docPages) { const url = absUrl .replace(/#.*$/, '') .replace( - /https:\/\/github.com\/spotify\/backstage\/(tree|blob)\/master/, + /https:\/\/github.com\/backstage\/backstage\/(tree|blob)\/master/, '', ); if (!url) { From c840f49c7e17cb8090fbd2c82f3cb28bea3b676c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 3 Nov 2020 11:43:59 +0100 Subject: [PATCH 065/252] README: add notice to update git remote --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index b1926f070e..cd33a0a6de 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,10 @@ [![Codecov](https://img.shields.io/codecov/c/github/spotify/backstage)](https://codecov.io/gh/spotify/backstage) [![](https://img.shields.io/npm/v/@backstage/core?label=Version)](https://github.com/backstage/backstage/releases) +> We recently moved from `spotify/backstage`, update your remotes! +> +> `git remote set-url origin git@github.com:backstage/backstage.git` + ## What is Backstage? [Backstage](https://backstage.io/) is an open platform for building developer portals. Powered by a centralized service catalog, Backstage restores order to your microservices and infrastructure. So your product teams can ship high-quality code quickly — without compromising autonomy. From b8e67816c95ce10b13d55cfa562545f8c5294a5a Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 3 Nov 2020 11:45:43 +0100 Subject: [PATCH 066/252] Remove mkdocs-techdocs-core from backstage mono-repo New home: https://github.com/backstage/mkdocs-techdocs-core --- docs/features/techdocs/concepts.md | 2 +- .../techdocs-core/.gitignore | 2 - .../techdocs-core/README.md | 149 ------------------ .../techdocs-core/requirements.txt | 16 -- .../techdocs-container/techdocs-core/setup.py | 57 ------- .../techdocs-core/src/__init__.py | 0 .../techdocs-core/src/core.py | 105 ------------ .../techdocs-core/src/test_core.py | 41 ----- 8 files changed, 1 insertion(+), 371 deletions(-) delete mode 100644 packages/techdocs-container/techdocs-core/.gitignore delete mode 100644 packages/techdocs-container/techdocs-core/README.md delete mode 100644 packages/techdocs-container/techdocs-core/requirements.txt delete mode 100644 packages/techdocs-container/techdocs-core/setup.py delete mode 100644 packages/techdocs-container/techdocs-core/src/__init__.py delete mode 100644 packages/techdocs-container/techdocs-core/src/core.py delete mode 100644 packages/techdocs-container/techdocs-core/src/test_core.py diff --git a/docs/features/techdocs/concepts.md b/docs/features/techdocs/concepts.md index 13b780524b..ed1fc2220c 100644 --- a/docs/features/techdocs/concepts.md +++ b/docs/features/techdocs/concepts.md @@ -14,7 +14,7 @@ The TechDocs Core Plugin is an [MkDocs](https://www.mkdocs.org/) plugin created as a wrapper around multiple MkDocs plugins and Python Markdown extensions to standardize the configuration of MkDocs used for TechDocs. -[TechDocs Core](https://github.com/backstage/backstage/blob/master/packages/techdocs-container/techdocs-core/README.md) +[TechDocs Core](https://github.com/backstage/mkdocs-techdocs-core) ### TechDocs container diff --git a/packages/techdocs-container/techdocs-core/.gitignore b/packages/techdocs-container/techdocs-core/.gitignore deleted file mode 100644 index cd5c4a44f3..0000000000 --- a/packages/techdocs-container/techdocs-core/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.tox -*.egg-info diff --git a/packages/techdocs-container/techdocs-core/README.md b/packages/techdocs-container/techdocs-core/README.md deleted file mode 100644 index 7bd6fe4187..0000000000 --- a/packages/techdocs-container/techdocs-core/README.md +++ /dev/null @@ -1,149 +0,0 @@ -# techdocs-core - -This is the base [Mkdocs](https://mkdocs.org) plugin used when using Mkdocs with Spotify's TechDocs. It is written in Python and packages all of our Mkdocs defaults, such as theming, plugins, etc in a single plugin. - -[Python Package](https://pypi.org/project/mkdocs-techdocs-core/) - -## Usage - -```bash -$ pip install mkdocs-techdocs-core -``` - -Once you have installed the `mkdocs-techdocs-core` plugin, you'll need to add it to your `mkdocs.yml`. - -```yaml -site_name: Backstage Docs - -nav: - - Home: index.md - - Developing a Plugin: developing-a-plugin.md - -plugins: - - techdocs-core -``` - -## Running Locally - -You can install this package locally using `pip` and the `--editable` flag used for making developing Python packages. - -```bash -pip install --editable . -``` - -You'll then have the `techdocs-core` package available to use in Mkdocs and `pip` will point the dependency to this folder. - -## Running with Docker - -In the parent `Dockerfile` we add this folder to the build and install the package locally in the container. In the future, we'll probably move away from this approach and have it download directly from a Python registry (and this folder will publish to one). - -See the `README.md` located in the `techdocs-container/` folder for more details on how to build and run the Docker container. - -## Linting - -```bash -pip install -r requirements.txt -python -m black src/ -``` - -**Note:** This will write to all Python files in `src/` with the formatted code. If you would like to only check to see if it passes, simply append the `--check` flag. - -## MkDocs plugins and extensions - -The TechDocs Core MkDocs plugin comes with a set of extensions and plugins that mkdocs supports. Below you can find a list of all extensions and plugins that are included in the -TechDocs Core plugin: - -Plugins: - -- [search](https://www.mkdocs.org/user-guide/configuration/#search) -- [mkdocs-monorepo-plugin](https://github.com/spotify/mkdocs-monorepo-plugin) - -Extensions: - -- [admonition](https://squidfunk.github.io/mkdocs-material/reference/admonitions/#admonitions) -- [toc](https://python-markdown.github.io/extensions/toc/) -- [pymdown](https://facelessuser.github.io/pymdown-extensions/) - - caret - - critic - - details - - emoji - - superfences - - inlinehilite - - magiclink - - mark - - smartsymobls - - highlight - - extra - - tabbed - - tasklist - - tilde -- [markdown_inline_graphviz](https://pypi.org/project/markdown-inline-graphviz/) -- [plantuml_markdown](https://pypi.org/project/plantuml-markdown/) - -## Changelog - -### 0.0.11 - -- Any MkDocs plugin configurations from mkdocs.yml will now work and override the default configuration. See https://github.com/spotify/backstage/issues/3017 - -### 0.0.10 - -- Pin Markdown version to fix issue with Graphviz - -### 0.0.9 - -- Change development status to 3 - Alpha - -### 0.0.8 - -- Superfences and Codehilite doesn't work very well together (squidfunk/mkdocs-material#1604) so therefore the codehilite extension is replaced by pymdownx.highlight - -* Uses pymdownx extensions v.7.1 instead of 8.0.0 to allow legacy_tab_classes config. This makes the techdocs core plugin compatible with the usage of tabs for grouping markdown with the following syntax: - -```` - ```java tab="java 2" - public void function() { - .... - } - ``` -```` - -as well as the new - -```` - === "Java" - - ```java - public void function() { - .... - } - ``` -```` - -The pymdownx extension will be bumped too 8.0.0 in the near future. - -- pymdownx.tabbed is added to support tabs to group markdown content, such as codeblocks. - -- "PyMdown Extensions includes three extensions that are meant to replace their counterpart in the default Python Markdown extensions." Therefore some extensions has been taken away in this version that comes by default from pymdownx.extra which is added now (https://facelessuser.github.io/pymdown-extensions/usage_notes/#incompatible-extensions) - -### 0.0.7 - -- Fix an issue with configuration of emoji support - -### 0.0.6 - -- Further adjustments to versions to find ones that are compatible - -### 0.0.5 - -- Downgrade some versions of markdown extensions to versions that are more stable - -### 0.0.4 - -- Added support for more mkdocs extensions - - mkdocs-material - - mkdocs-monorepo-plugin - - plantuml-markdown - - markdown_inline_graphviz_extension - - pygments - - pymdown-extensions diff --git a/packages/techdocs-container/techdocs-core/requirements.txt b/packages/techdocs-container/techdocs-core/requirements.txt deleted file mode 100644 index fc56ae6aee..0000000000 --- a/packages/techdocs-container/techdocs-core/requirements.txt +++ /dev/null @@ -1,16 +0,0 @@ -# The "base" version of the Mkdocs project. -# Note: if you update this, also update `install_requires` in setup.py -# https://github.com/mkdocs/mkdocs -mkdocs==1.1.2 -mkdocs-material==5.3.2 -mkdocs-monorepo-plugin==0.4.5 -plantuml-markdown==3.1.2 -markdown_inline_graphviz_extension==1.1 -pygments==2.6.1 -pymdown-extensions==7.1 -Markdown==3.2.2 - -# The linter using for Python -# Note: This requires Python 3.6+ to run, but can format Python 2 code too. -# https://github.com/psf/black -black==19.10b0 diff --git a/packages/techdocs-container/techdocs-core/setup.py b/packages/techdocs-container/techdocs-core/setup.py deleted file mode 100644 index bc3e6c7082..0000000000 --- a/packages/techdocs-container/techdocs-core/setup.py +++ /dev/null @@ -1,57 +0,0 @@ -""" -Copyright 2020 Spotify AB - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -from setuptools import setup, find_packages -from os import path - -# read the contents of the README file in the current directory -this_dir = path.abspath(path.dirname(__file__)) -with open(path.join(this_dir, "README.md"), encoding="utf-8") as file: - long_description = file.read() - -setup( - name="mkdocs-techdocs-core", - version="0.0.11", - description="A Mkdocs package that contains TechDocs defaults", - long_description=long_description, - long_description_content_type="text/markdown", - keywords="mkdocs", - url="https://github.com/backstage/backstage", - author="TechDocs Core", - author_email="pulp-fiction@spotify.com", - license="Apache-2.0", - python_requires=">=3.7", - install_requires=[ - "mkdocs>=1.1.2", - "mkdocs-material==5.3.2", - "mkdocs-monorepo-plugin==0.4.5", - "plantuml-markdown==3.1.2", - "markdown_inline_graphviz_extension==1.1", - "pygments==2.6.1", - "pymdown-extensions==7.1", - "Markdown==3.2.2", - ], - classifiers=[ - "Development Status :: 3 - Alpha", - "Intended Audience :: Developers", - "Intended Audience :: Information Technology", - "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.7", - ], - packages=find_packages(), - entry_points={"mkdocs.plugins": ["techdocs-core = src.core:TechDocsCore"]}, -) diff --git a/packages/techdocs-container/techdocs-core/src/__init__.py b/packages/techdocs-container/techdocs-core/src/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/techdocs-container/techdocs-core/src/core.py b/packages/techdocs-container/techdocs-core/src/core.py deleted file mode 100644 index 7d32b69ad8..0000000000 --- a/packages/techdocs-container/techdocs-core/src/core.py +++ /dev/null @@ -1,105 +0,0 @@ -""" - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -""" - -from mkdocs.plugins import BasePlugin -from mkdocs.theme import Theme -from mkdocs.contrib.search import SearchPlugin -from mkdocs_monorepo_plugin.plugin import MonorepoPlugin -from pymdownx.emoji import to_svg -import tempfile -import os - - -class TechDocsCore(BasePlugin): - def on_config(self, config): - fp = open(os.path.join(tempfile.gettempdir(), "techdocs_metadata.json"), "w+") - fp.write( - '{\n "site_name": "{{ config.site_name }}",\n "site_description": "{{ config.site_description }}"\n}' - ) - - mdx_configs_override = {} - if "mdx_configs" in config: - mdx_configs_override = config["mdx_configs"].copy() - - # Theme - config["theme"] = Theme( - name="material", static_templates=["techdocs_metadata.json",], - ) - config["theme"].dirs.append(tempfile.gettempdir()) - - # Plugins - del config["plugins"]["techdocs-core"] - - search_plugin = SearchPlugin() - search_plugin.load_config({}) - - monorepo_plugin = MonorepoPlugin() - monorepo_plugin.load_config({}) - config["plugins"]["search"] = search_plugin - config["plugins"]["monorepo"] = monorepo_plugin - - # Markdown Extensions - if "markdown_extensions" not in config: - config["markdown_extensions"] = [] - - if "mdx_configs" not in config: - config["mdx_configs"] = {} - - config["markdown_extensions"].append("admonition") - config["markdown_extensions"].append("toc") - config["mdx_configs"]["toc"] = { - "permalink": True, - } - - config["markdown_extensions"].append("pymdownx.caret") - config["markdown_extensions"].append("pymdownx.critic") - config["markdown_extensions"].append("pymdownx.details") - config["markdown_extensions"].append("pymdownx.emoji") - config["mdx_configs"]["pymdownx.emoji"] = {"emoji_generator": to_svg} - config["markdown_extensions"].append("pymdownx.inlinehilite") - config["markdown_extensions"].append("pymdownx.magiclink") - config["markdown_extensions"].append("pymdownx.mark") - config["markdown_extensions"].append("pymdownx.smartsymbols") - config["markdown_extensions"].append("pymdownx.superfences") - config["mdx_configs"]["pymdownx.superfences"] = { - "legacy_tab_classes": True, - } - config["markdown_extensions"].append("pymdownx.highlight") - config["mdx_configs"]["pymdownx.highlight"] = { - "linenums": True, - } - config["markdown_extensions"].append("pymdownx.extra") - config["mdx_configs"]["pymdownx.betterem"] = { - "smart_enable": "all", - } - config["markdown_extensions"].append("pymdownx.tabbed") - config["markdown_extensions"].append("pymdownx.tasklist") - config["mdx_configs"]["pymdownx.tasklist"] = { - "custom_checkbox": True, - } - config["markdown_extensions"].append("pymdownx.tilde") - - config["markdown_extensions"].append("markdown_inline_graphviz") - config["markdown_extensions"].append("plantuml_markdown") - - # merge config supplied by user in the mkdocs.yml - for key in mdx_configs_override: - if key in config["mdx_configs"]: - default_config = config["mdx_configs"][key] - override_config = mdx_configs_override[key] - default_config.update(override_config) - - return config diff --git a/packages/techdocs-container/techdocs-core/src/test_core.py b/packages/techdocs-container/techdocs-core/src/test_core.py deleted file mode 100644 index 621570356c..0000000000 --- a/packages/techdocs-container/techdocs-core/src/test_core.py +++ /dev/null @@ -1,41 +0,0 @@ -import unittest -import mkdocs.config as config -import mkdocs.plugins as plugins -from .core import TechDocsCore - - -class DummyTechDocsCorePlugin(plugins.BasePlugin): - pass - - -class TestTechDocsCoreConfig(unittest.TestCase): - def setUp(self): - self.techdocscore = TechDocsCore() - self.plugin_collection = plugins.PluginCollection() - plugin = DummyTechDocsCorePlugin() - self.plugin_collection["techdocs-core"] = plugin - self.mkdocs_yaml_config = {"plugins": self.plugin_collection} - - def test_removes_techdocs_core_plugin_from_config(self): - final_config = self.techdocscore.on_config(self.mkdocs_yaml_config) - self.assertTrue("techdocs-core" not in final_config["plugins"]) - - def test_merge_default_config_and_user_config(self): - self.mkdocs_yaml_config["markdown_extension"] = [] - self.mkdocs_yaml_config["mdx_configs"] = {} - self.mkdocs_yaml_config["markdown_extension"].append(["toc"]) - self.mkdocs_yaml_config["mdx_configs"]["toc"] = {"toc_depth": 3} - final_config = self.techdocscore.on_config(self.mkdocs_yaml_config) - self.assertTrue("toc" in final_config["mdx_configs"]) - self.assertTrue("permalink" in final_config["mdx_configs"]["toc"]) - self.assertTrue("toc_depth" in final_config["mdx_configs"]["toc"]) - - def test_override_default_config_with_user_config(self): - self.mkdocs_yaml_config["markdown_extension"] = [] - self.mkdocs_yaml_config["mdx_configs"] = {} - self.mkdocs_yaml_config["markdown_extension"].append(["toc"]) - self.mkdocs_yaml_config["mdx_configs"]["toc"] = {"permalink": False} - final_config = self.techdocscore.on_config(self.mkdocs_yaml_config) - self.assertTrue("toc" in final_config["mdx_configs"]) - self.assertTrue("permalink" in final_config["mdx_configs"]["toc"]) - self.assertFalse(final_config["mdx_configs"]["toc"]["permalink"]) From 9d800c49ccfb7ed8044d609c193f64122d824e27 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 3 Nov 2020 11:47:35 +0100 Subject: [PATCH 067/252] TechDocs: Remove unnecessary GitHub workflow steps --- .github/workflows/techdocs-pypi.yml | 37 ----------------------------- .github/workflows/techdocs.yml | 11 --------- 2 files changed, 48 deletions(-) delete mode 100644 .github/workflows/techdocs-pypi.yml diff --git a/.github/workflows/techdocs-pypi.yml b/.github/workflows/techdocs-pypi.yml deleted file mode 100644 index 7680cb98b0..0000000000 --- a/.github/workflows/techdocs-pypi.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Master Build TechDocs PyPI Publish - -on: - push: - branches: [master] - paths: - - '.github/workflows/techdocs-pypi.yml' - - 'packages/techdocs-container/**' - -jobs: - build: - runs-on: ${{ matrix.os }} - - strategy: - matrix: - os: [ubuntu-latest] - python-version: [3.7] - - steps: - # Publish techdocs-core to PyPI - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@master - with: - python-version: 3.7 - - name: Build Python distribution - working-directory: ./packages/techdocs-container/techdocs-core - run: | - pip install wheel - rm -rf dist - python setup.py bdist_wheel sdist --formats gztar - - name: Publish a Python distribution to PyPI - uses: pypa/gh-action-pypi-publish@master - with: - user: __token__ - password: ${{ secrets.PYPI_API_KEY }} - packages_dir: ./packages/techdocs-container/techdocs-core/dist diff --git a/.github/workflows/techdocs.yml b/.github/workflows/techdocs.yml index 95b68c9376..9b5ea50217 100644 --- a/.github/workflows/techdocs.yml +++ b/.github/workflows/techdocs.yml @@ -31,14 +31,3 @@ jobs: with: path: packages/techdocs-container push: false - - # Lint Python code for techdocs-core package - - name: Prepare Python environment - run: | - python3 -m pip install --index-url https://pypi.org/simple/ setuptools - python3 -m pip install --upgrade pip - python3 -m pip install --index-url https://pypi.org/simple/ -r $TECHDOCS_CORE_PATH/requirements.txt - - - name: Lint techdocs-core package - run: | - python3 -m black --check $TECHDOCS_CORE_PATH/src From 878eb6095895797c3207be79d466b5a19a3c58c1 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 3 Nov 2020 11:49:01 +0100 Subject: [PATCH 068/252] TechDocs: Update repo URL in documentation: --- docs/features/techdocs/FAQ.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/features/techdocs/FAQ.md b/docs/features/techdocs/FAQ.md index 080346b9d0..257d6e6f7a 100644 --- a/docs/features/techdocs/FAQ.md +++ b/docs/features/techdocs/FAQ.md @@ -23,8 +23,7 @@ is using the MkDocs #### What is the mkdocs-techdocs-core plugin? -The -[mkdocs-techdocs-core](https://github.com/backstage/backstage/blob/master/packages/techdocs-container/techdocs-core/README.md) +The [mkdocs-techdocs-core](https://github.com/backstage/mkdocs-techdocs-core) package is a MkDocs Plugin that works like a wrapper around multiple MkDocs plugins (e.g. [MkDocs Monorepo Plugin](https://github.com/spotify/mkdocs-monorepo-plugin)) as From 0672a3ecb16e9479ab1dcd41e64692595e8dd88c Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 3 Nov 2020 12:36:42 +0100 Subject: [PATCH 069/252] TechDocs: Remove unnecessary CODEOWNERS config --- .github/CODEOWNERS | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7cce08798a..cf28c55cb3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -13,5 +13,4 @@ /packages/techdocs-cli @backstage/techdocs-core /packages/techdocs-container @backstage/techdocs-core /.github/workflows/techdocs.yml @backstage/techdocs-core -/.github/workflows/techdocs-pypi.yml @backstage/techdocs-core /.changeset/cost-insights-* @backstage/silver-lining From cf87e35e7fab42de3ce80fcdcb48caac9583a798 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 3 Nov 2020 14:11:10 +0100 Subject: [PATCH 070/252] plugins/sonarqube: bump to 0.1.1 --- plugins/sonarqube/package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index fc14805c06..b3d90b2ca7 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sonarqube", - "version": "0.1.1-alpha.26", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.26", - "@backstage/core": "^0.1.1-alpha.26", - "@backstage/theme": "^0.1.1-alpha.26", + "@backstage/catalog-model": "^0.1.1", + "@backstage/core": "^0.1.1", + "@backstage/theme": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -35,9 +35,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.26", - "@backstage/dev-utils": "^0.1.1-alpha.26", - "@backstage/test-utils": "^0.1.1-alpha.26", + "@backstage/cli": "^0.1.1", + "@backstage/dev-utils": "^0.1.1", + "@backstage/test-utils": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", From 1914a3e88b100c4593e2c75228569366d434409b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 3 Nov 2020 13:56:26 +0100 Subject: [PATCH 071/252] workflows: run prettier as part of changeset versioning --- .github/workflows/changeset.yml | 3 +++ package.json | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/changeset.yml b/.github/workflows/changeset.yml index ac7872ea93..592a7421aa 100644 --- a/.github/workflows/changeset.yml +++ b/.github/workflows/changeset.yml @@ -15,5 +15,8 @@ jobs: run: yarn --frozen-lockfile - name: Create Release Pull Request uses: changesets/action@master + with: + # Calls out to `changeset version`, but also runs prettier + version: yarn version env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/package.json b/package.json index 96e02e33f9..e49d7438c0 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,8 @@ "prettier:check": "prettier --check .", "lerna": "lerna", "storybook": "yarn workspace storybook start", - "build-storybook": "yarn workspace storybook build-storybook" + "build-storybook": "yarn workspace storybook build-storybook", + "version": "changeset version && yarn prettier --write '{packages,plugins}/*/{package.json,CHANGELOG.md}'" }, "workspaces": { "packages": [ From a963b59b0441960d23244788f1e520c27086b3f8 Mon Sep 17 00:00:00 2001 From: Jesko Steinberg Date: Tue, 3 Nov 2020 14:39:39 +0100 Subject: [PATCH 072/252] refactor: use markdown dialects (gfm as default) --- .changeset/3157.md | 11 ++++++----- .../MarkdownContent.stories.tsx | 4 ++-- .../MarkdownContent/MarkdownContent.test.tsx | 19 ++++++++++++++----- .../MarkdownContent/MarkdownContent.tsx | 15 +++++++++------ 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/.changeset/3157.md b/.changeset/3157.md index ba6eb8314f..f15d05c46a 100644 --- a/.changeset/3157.md +++ b/.changeset/3157.md @@ -1,15 +1,16 @@ --- -'@backstage/core': minor +'@backstage/core': patch --- -Adds the MarkdownContent component to render and display markdown input. +Adds the MarkdownContent component to render and display Markdown content with the default +[GFM](https://github.github.com/gfm/) (Github flavored Markdown) dialect. ``` - + ``` -Render and display the github flavored markdown [GFM](https://github.github.com/gfm/) input: +To render the Markdown content with plain [CommonMark](https://commonmark.org/), set the dialect to `common-mark` ``` - + ( - + ); export const MarkdownContentGithubFlavoredCommonMark = () => ( - + ); diff --git a/packages/core/src/components/MarkdownContent/MarkdownContent.test.tsx b/packages/core/src/components/MarkdownContent/MarkdownContent.test.tsx index 2fb40d2f4f..dcbe421dcb 100644 --- a/packages/core/src/components/MarkdownContent/MarkdownContent.test.tsx +++ b/packages/core/src/components/MarkdownContent/MarkdownContent.test.tsx @@ -19,7 +19,7 @@ import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; import { MarkdownContent } from './MarkdownContent'; describe('', () => { - it('render MarkdownContent component with common mark', async () => { + it('render MarkdownContent component', async () => { const rendered = await renderWithEffects( wrapInTestApp( , @@ -30,17 +30,26 @@ describe('', () => { expect(rendered.getByText('H3', { selector: 'h3' })).toBeInTheDocument(); }); - it('render MarkdownContent component with common mark github flavored', async () => { + it('render MarkdownContent component with GitHub flavored Markdown dialect', async () => { const rendered = await renderWithEffects( - wrapInTestApp( - , - ), + wrapInTestApp(), ); expect( rendered.getByText('https://example.com', { selector: 'a' }), ).toBeInTheDocument(); }); + it('Render MarkdownContent component with common mark dialect', async () => { + const rendered = await renderWithEffects( + wrapInTestApp( + , + ), + ); + expect( + rendered.getByText('https://example.com', { selector: 'p' }), + ).toBeInTheDocument(); + }); + it('render MarkdownContent component with CodeSnippet for code blocks', async () => { const rendered = await renderWithEffects( wrapInTestApp(), diff --git a/packages/core/src/components/MarkdownContent/MarkdownContent.tsx b/packages/core/src/components/MarkdownContent/MarkdownContent.tsx index 4f234565d2..0000e54a5f 100644 --- a/packages/core/src/components/MarkdownContent/MarkdownContent.tsx +++ b/packages/core/src/components/MarkdownContent/MarkdownContent.tsx @@ -59,12 +59,9 @@ const useStyles = makeStyles(theme => ({ }, })); -/** - * MarkdownContent. Renders markdown (CommonMark, optionally with [GFM](https://github.com/remarkjs/remark-gfm)) to formatted HTML. - */ type Props = { content: string; - enableGfm?: boolean; + dialect?: 'gfm' | 'common-mark'; }; const renderers = { @@ -73,11 +70,17 @@ const renderers = { }, }; -export const MarkdownContent = ({ content, enableGfm = false }: Props) => { +/** + * MarkdownContent + * -- + * Renders markdown with the default dialect [gfm - GitHub flavored Markdown](https://github.github.com/gfm/) to backstage theme styled HTML. + * If you just want to render to plain [CommonMark](https://commonmark.org/), set the dialect to `'common-mark'` + */ +export const MarkdownContent = ({ content, dialect = 'gfm' }: Props) => { const classes = useStyles(); return ( Date: Tue, 3 Nov 2020 15:22:49 +0100 Subject: [PATCH 073/252] workflows: fix changeset release script naming --- .github/workflows/changeset.yml | 2 +- package.json | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/changeset.yml b/.github/workflows/changeset.yml index 592a7421aa..a557b4921b 100644 --- a/.github/workflows/changeset.yml +++ b/.github/workflows/changeset.yml @@ -17,6 +17,6 @@ jobs: uses: changesets/action@master with: # Calls out to `changeset version`, but also runs prettier - version: yarn version + version: yarn release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/package.json b/package.json index e49d7438c0..505ee0caad 100644 --- a/package.json +++ b/package.json @@ -24,12 +24,11 @@ "docker-build": "yarn tsc && yarn workspace example-backend build-image", "create-plugin": "backstage-cli create-plugin --scope backstage --no-private", "remove-plugin": "backstage-cli remove-plugin", - "release": "if [ \"$(git symbolic-ref --short HEAD)\" = master ]; then echo \"don't try to release master\"; exit 1; else lerna version --no-push --force-publish; fi", + "release": "changeset version && yarn prettier --write '{packages,plugins}/*/{package.json,CHANGELOG.md}'", "prettier:check": "prettier --check .", "lerna": "lerna", "storybook": "yarn workspace storybook start", - "build-storybook": "yarn workspace storybook build-storybook", - "version": "changeset version && yarn prettier --write '{packages,plugins}/*/{package.json,CHANGELOG.md}'" + "build-storybook": "yarn workspace storybook build-storybook" }, "workspaces": { "packages": [ From 9bf610b109757d68c0714bcd49e648ed9e410c2b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 3 Nov 2020 17:41:03 +0100 Subject: [PATCH 074/252] docs/api: fix link in utility APIs --- docs/api/utility-apis.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/api/utility-apis.md b/docs/api/utility-apis.md index 1ebf2ae88b..0adafc020a 100644 --- a/docs/api/utility-apis.md +++ b/docs/api/utility-apis.md @@ -160,7 +160,8 @@ const app = createApp({ ``` A common pattern is to export a list of all APIs from `apis.ts`, next to -`App.tsx`. See the [example app in this repo](../../packages/app/src/apis.ts) +`App.tsx`. See the +[example app in this repo](https://github.com/backstage/backstage/blob/master/packages/app/src/apis.ts) for an example. ## Custom implementations of Utility APIs From cff0a558e5f0cee605963a32892e3adad5f9cea6 Mon Sep 17 00:00:00 2001 From: Brenda Sukh Date: Tue, 3 Nov 2020 13:52:39 -0500 Subject: [PATCH 075/252] Expose alerts for internal use --- plugins/cost-insights/src/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/cost-insights/src/index.ts b/plugins/cost-insights/src/index.ts index 1e6172d568..cefdefb9c9 100644 --- a/plugins/cost-insights/src/index.ts +++ b/plugins/cost-insights/src/index.ts @@ -18,7 +18,8 @@ export { plugin } from './plugin'; export * from './client'; export * from './api'; export * from './components'; -export { useCurrency } from './hooks'; +export { useCurrency, CurrencyContext } from './hooks'; export * from './types'; export * from './utils/tests'; export * from './utils/duration'; +export * from './utils/alerts'; From 02f3e571ade91ce49aca0def49df3b70e5b3a2d7 Mon Sep 17 00:00:00 2001 From: Brenda Sukh Date: Tue, 3 Nov 2020 13:52:57 -0500 Subject: [PATCH 076/252] Make lastBillingDate a param for product insights --- plugins/cost-insights/src/api/CostInsightsApi.ts | 2 ++ plugins/cost-insights/src/client.ts | 3 ++- .../src/components/ProductInsightsCard/ProductInsightsCard.tsx | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/cost-insights/src/api/CostInsightsApi.ts b/plugins/cost-insights/src/api/CostInsightsApi.ts index be5cd54fdb..a9048dedb8 100644 --- a/plugins/cost-insights/src/api/CostInsightsApi.ts +++ b/plugins/cost-insights/src/api/CostInsightsApi.ts @@ -112,12 +112,14 @@ export type CostInsightsApi = { * @param group * @param duration A time duration, such as P1M. See the Duration type for a detailed explanation * of how the durations are interpreted in Cost Insights. + * @param lastBillingDate The most current date for which billing data is complete, in YYYY-MM-DD format. * @param project (optional) The project id from getGroupProjects or query parameters */ getProductInsights( product: string, group: string, duration: Duration, + lastBillingDate: string, project: Maybe, ): Promise; diff --git a/plugins/cost-insights/src/client.ts b/plugins/cost-insights/src/client.ts index f7671bcce0..69822d7a63 100644 --- a/plugins/cost-insights/src/client.ts +++ b/plugins/cost-insights/src/client.ts @@ -197,10 +197,11 @@ export class ExampleCostInsightsClient implements CostInsightsApi { product: string, group: string, duration: Duration, + lastBillingDate: string, project: Maybe, ): Promise { const projectProductInsights = await this.request( - { product, group, duration, project }, + { product, group, duration, lastBillingDate, project }, { aggregation: [80_000, 110_000], change: { diff --git a/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.tsx b/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.tsx index 840c99522a..abf56d91db 100644 --- a/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.tsx +++ b/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.tsx @@ -86,6 +86,7 @@ export const ProductInsightsCard = ({ product }: ProductInsightsCardProps) => { product.kind, group!, productFilter!.duration, + lastCompleteBillingDate, project, ); setResource(p); @@ -107,6 +108,7 @@ export const ProductInsightsCard = ({ product }: ProductInsightsCardProps) => { group, product.kind, project, + lastCompleteBillingDate, ]); const onPeriodSelect = (duration: Duration) => { From 522132e5e8a9c2d9b0aaef9634cc9cac8cb945e9 Mon Sep 17 00:00:00 2001 From: Brenda Sukh Date: Tue, 3 Nov 2020 13:53:05 -0500 Subject: [PATCH 077/252] Add changeset --- .changeset/rich-dodos-smell.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/rich-dodos-smell.md diff --git a/.changeset/rich-dodos-smell.md b/.changeset/rich-dodos-smell.md new file mode 100644 index 0000000000..a579797437 --- /dev/null +++ b/.changeset/rich-dodos-smell.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-cost-insights': patch +--- + +expose alerts utilities for export From db3d03228350f97cde1925ca7add53f2ddd1eeab Mon Sep 17 00:00:00 2001 From: Brenda Sukh Date: Tue, 3 Nov 2020 15:25:01 -0500 Subject: [PATCH 078/252] Update getProductInsights to accept a single object with fields --- .../cost-insights/src/api/CostInsightsApi.ts | 44 +++++++++---- plugins/cost-insights/src/client.ts | 66 +++++++++---------- .../ProductInsightsCard.tsx | 10 +-- plugins/cost-insights/src/index.ts | 2 +- 4 files changed, 66 insertions(+), 56 deletions(-) diff --git a/plugins/cost-insights/src/api/CostInsightsApi.ts b/plugins/cost-insights/src/api/CostInsightsApi.ts index a9048dedb8..bedbf8319d 100644 --- a/plugins/cost-insights/src/api/CostInsightsApi.ts +++ b/plugins/cost-insights/src/api/CostInsightsApi.ts @@ -26,6 +26,34 @@ import { MetricData, } from '../types'; +export type ProductInsightsOptions = { + /** + * The product from the cost-insights configuration in app-config.yaml + */ + product: string; + + /** + * The group id from getUserGroups or query parameters + */ + group: string; + + /** + * A time duration, such as P1M. See the Duration type for a detailed explanation + * of how the durations are interpreted in Cost Insights. + */ + duration: Duration; + + /** + * The most current date for which billing data is complete, in YYYY-MM-DD format. + */ + lastCompleteBillingDate: string; + + /** + * (optional) The project id from getGroupProjects or query parameters + */ + project: Maybe; +}; + export type CostInsightsApi = { /** * Get the most current date for which billing data is complete, in YYYY-MM-DD format. This helps @@ -108,21 +136,9 @@ export type CostInsightsApi = { * The time period is supplied as a Duration rather than intervals, since this is always expected * to return data for two bucketed time period (e.g. month vs month, or quarter vs quarter). * - * @param product The product from the cost-insights configuration in app-config.yaml - * @param group - * @param duration A time duration, such as P1M. See the Duration type for a detailed explanation - * of how the durations are interpreted in Cost Insights. - * @param lastBillingDate The most current date for which billing data is complete, in YYYY-MM-DD format. - * @param project (optional) The project id from getGroupProjects or query parameters + * @param options Options to use when fetching insights for a particular cloud product and interval timeframe. */ - getProductInsights( - product: string, - group: string, - duration: Duration, - lastBillingDate: string, - project: Maybe, - ): Promise; - + getProductInsights(options: ProductInsightsOptions): Promise; /** * Get current cost alerts for a given group. These show up as Action Items for the group on the * Cost Insights page. Alerts may include cost-saving recommendations, such as infrastructure diff --git a/plugins/cost-insights/src/client.ts b/plugins/cost-insights/src/client.ts index 69822d7a63..1fa57c624a 100644 --- a/plugins/cost-insights/src/client.ts +++ b/plugins/cost-insights/src/client.ts @@ -17,7 +17,7 @@ import dayjs from 'dayjs'; import regression, { DataPoint } from 'regression'; -import { CostInsightsApi } from '../src/api'; +import { CostInsightsApi, ProductInsightsOptions } from '../src/api'; import { Alert, ChangeStatistic, @@ -26,7 +26,6 @@ import { DEFAULT_DATE_FORMAT, Duration, Group, - Maybe, MetricData, ProductCost, Project, @@ -194,42 +193,35 @@ export class ExampleCostInsightsClient implements CostInsightsApi { } async getProductInsights( - product: string, - group: string, - duration: Duration, - lastBillingDate: string, - project: Maybe, + productInsightsOptions: ProductInsightsOptions, ): Promise { - const projectProductInsights = await this.request( - { product, group, duration, lastBillingDate, project }, - { - aggregation: [80_000, 110_000], - change: { - ratio: 0.375, - amount: 30_000, - }, - entities: [ - { - id: null, // entities with null ids will be appear as "Unlabeled" in product panels - aggregation: [45_000, 50_000], - }, - { - id: 'entity-a', - aggregation: [15_000, 20_000], - }, - { - id: 'entity-b', - aggregation: [20_000, 30_000], - }, - { - id: 'entity-e', - aggregation: [0, 10_000], - }, - ], + const projectProductInsights = await this.request(productInsightsOptions, { + aggregation: [80_000, 110_000], + change: { + ratio: 0.375, + amount: 30_000, }, - ); + entities: [ + { + id: null, // entities with null ids will be appear as "Unlabeled" in product panels + aggregation: [45_000, 50_000], + }, + { + id: 'entity-a', + aggregation: [15_000, 20_000], + }, + { + id: 'entity-b', + aggregation: [20_000, 30_000], + }, + { + id: 'entity-e', + aggregation: [0, 10_000], + }, + ], + }); const productInsights: ProductCost = await this.request( - { product, group, duration, project }, + productInsightsOptions, { aggregation: [200_000, 250_000], change: { @@ -277,7 +269,9 @@ export class ExampleCostInsightsClient implements CostInsightsApi { }, ); - return project ? projectProductInsights : productInsights; + return productInsightsOptions.project + ? projectProductInsights + : productInsights; } async getAlerts(group: string): Promise { diff --git a/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.tsx b/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.tsx index abf56d91db..17b1624c05 100644 --- a/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.tsx +++ b/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.tsx @@ -82,13 +82,13 @@ export const ProductInsightsCard = ({ product }: ProductInsightsCardProps) => { async function load() { if (loadingProduct) { try { - const p: ProductCost = await client.getProductInsights( - product.kind, - group!, - productFilter!.duration, + const p: ProductCost = await client.getProductInsights({ + product: product.kind, + group: group!, + duration: productFilter!.duration, lastCompleteBillingDate, project, - ); + }); setResource(p); } catch (e) { setError(e); diff --git a/plugins/cost-insights/src/index.ts b/plugins/cost-insights/src/index.ts index cefdefb9c9..28bdb1a8b3 100644 --- a/plugins/cost-insights/src/index.ts +++ b/plugins/cost-insights/src/index.ts @@ -18,7 +18,7 @@ export { plugin } from './plugin'; export * from './client'; export * from './api'; export * from './components'; -export { useCurrency, CurrencyContext } from './hooks'; +export { useCurrency } from './hooks'; export * from './types'; export * from './utils/tests'; export * from './utils/duration'; From 5c70f3d353be1e848a277cddcef19293753ceecb Mon Sep 17 00:00:00 2001 From: Brenda Sukh Date: Tue, 3 Nov 2020 15:25:24 -0500 Subject: [PATCH 079/252] Update changeset filename --- .../{rich-dodos-smell.md => cost-insights-rich-dodos-smell.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .changeset/{rich-dodos-smell.md => cost-insights-rich-dodos-smell.md} (100%) diff --git a/.changeset/rich-dodos-smell.md b/.changeset/cost-insights-rich-dodos-smell.md similarity index 100% rename from .changeset/rich-dodos-smell.md rename to .changeset/cost-insights-rich-dodos-smell.md From 715bb76fb545063109f630ff64b7f579c655af55 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 3 Nov 2020 20:50:42 +0000 Subject: [PATCH 080/252] Version Packages --- .changeset/1724.md | 27 --- .changeset/2284.md | 11 -- .changeset/2515.md | 5 - .changeset/2532.md | 5 - .changeset/2535.md | 14 -- .changeset/2541.md | 5 - .changeset/2543.md | 9 - .changeset/2562.md | 7 - .changeset/2563.md | 5 - .changeset/2565.md | 59 ------- .changeset/2575.md | 5 - .changeset/2586.md | 6 - .changeset/2587.md | 9 - .changeset/2597.md | 6 - .changeset/2598.md | 11 -- .changeset/2600.md | 19 --- .changeset/2603.md | 8 - .changeset/2606.md | 5 - .changeset/2609.md | 5 - .changeset/2610.md | 5 - .changeset/2611.md | 5 - .changeset/2612.md | 5 - .changeset/2613.md | 12 -- .changeset/2614.md | 5 - .changeset/2615.md | 5 - .changeset/2616.md | 7 - .changeset/2623.md | 7 - .changeset/2624.md | 17 -- .changeset/2625-catalog-backend.md | 9 - .changeset/2625-cli.md | 7 - .changeset/2628.md | 22 --- .changeset/2630.md | 9 - .changeset/2637.md | 21 --- .changeset/2639.md | 5 - .changeset/2641.md | 5 - .changeset/2656.md | 5 - .changeset/2657.md | 5 - .changeset/2660.md | 5 - .changeset/2661.md | 5 - .changeset/2669-catalog-backend.md | 7 - .changeset/2669-catalog-model.md | 5 - .changeset/2669-create-app.md | 7 - .changeset/2674.md | 12 -- .changeset/2686.md | 5 - .changeset/2689.md | 5 - .changeset/2722.md | 11 -- .changeset/2798.md | 7 - .changeset/2800.md | 15 -- .changeset/2803.md | 26 --- .changeset/2804.md | 15 -- .changeset/2826.md | 20 --- .changeset/2874.md | 12 -- .changeset/3030.md | 5 - .changeset/3066.md | 12 -- .changeset/3113.md | 11 -- .changeset/3130.md | 5 - .changeset/alchemists-on-ice.md | 11 -- .changeset/angry-crabs-relate.md | 9 - .changeset/beige-apes-serve.md | 5 - .changeset/beige-pandas-thank.md | 5 - .changeset/blue-donkeys-exercise.md | 5 - .changeset/carpal-tunnel-driver.md | 13 -- .changeset/chilly-emus-fetch.md | 5 - .changeset/clean-phones-vanish.md | 5 - .changeset/cli-parallel-build-options.md | 12 -- .changeset/codeowner-processor-url-reader.md | 5 - .changeset/cost-insights-kind-trees-occur.md | 5 - .changeset/cost-insights-rich-dodos-smell.md | 5 - .changeset/cost-insights-sparkle.md | 5 - .../cost-insights-swift-carrots-bake.md | 5 - .changeset/create-app-url-reader-update.md | 6 - .changeset/cyan-plants-dress.md | 5 - .changeset/dull-icons-share.md | 21 --- .changeset/eighty-lamps-smell.md | 5 - .changeset/eighty-stingrays-type.md | 5 - .changeset/flat-bugs-do.md | 8 - .changeset/flat-yaks-march.md | 5 - .changeset/fresh-maps-complain.md | 6 - .changeset/funny-readers-breathe.md | 5 - .changeset/fuzzy-falcons-happen.md | 5 - .changeset/good-islands-cheer.md | 5 - .changeset/great-apples-flash.md | 5 - .changeset/happy-ads-behave.md | 5 - .changeset/heavy-berries-hear.md | 5 - .changeset/hungry-parrots-work.md | 5 - .changeset/little-wasps-pull.md | 5 - .changeset/long-ways-count.md | 57 ------- .changeset/loud-lamps-visit.md | 5 - .changeset/lovely-suits-flash.md | 5 - .changeset/metal-fishes-learn.md | 5 - .changeset/mighty-starfishes-taste.md | 5 - .changeset/nervous-drinks-notice.md | 8 - .changeset/new-url-reader.md | 12 -- .changeset/nice-candles-argue.md | 5 - .changeset/ninety-ads-dance.md | 5 - .changeset/ninety-pens-poke.md | 5 - .changeset/old-eagles-admire.md | 6 - .changeset/old-falcons-jump.md | 25 --- .changeset/old-jeans-dance.md | 5 - .changeset/olive-bikes-jog.md | 5 - .changeset/pink-spies-rule.md | 5 - .changeset/polite-laws-think.md | 5 - .changeset/polite-moose-cough.md | 5 - .changeset/popular-ghosts-remain.md | 5 - .changeset/popular-jars-serve.md | 5 - .changeset/pretty-cups-joke.md | 5 - .changeset/proud-eagles-grow.md | 5 - .changeset/raghu-is-great.md | 10 -- .changeset/red-games-float.md | 10 -- .changeset/remove-location-processor-read.md | 6 - .changeset/rocket-lizards-congregate.md | 10 -- .changeset/selfish-bats-perform.md | 6 - .changeset/selfish-mugs-itch.md | 5 - .changeset/seven-humans-check.md | 9 - .changeset/short-secrets.md | 5 - .changeset/slimy-garlics-eat.md | 5 - .changeset/slow-experts-peel.md | 5 - .changeset/spicy-moles-yell.md | 5 - .changeset/spicy-rockets-ring.md | 10 -- .changeset/spotty-apples-visit.md | 5 - .changeset/sweet-bikes-battle.md | 5 - .changeset/swift-emus-mate.md | 5 - .changeset/swift-peas-argue.md | 5 - .changeset/ten-bees-wash.md | 5 - .changeset/three-horses-juggle.md | 5 - .changeset/tough-weeks-pull.md | 5 - .changeset/url-reader-processor.md | 5 - .changeset/weak-ducks-tan.md | 8 - packages/app/CHANGELOG.md | 121 ++++++++++++++ packages/app/package.json | 56 +++---- packages/backend-common/CHANGELOG.md | 64 ++++++++ packages/backend-common/package.json | 8 +- packages/backend/CHANGELOG.md | 97 +++++++++++ packages/backend/package.json | 30 ++-- packages/catalog-model/CHANGELOG.md | 28 ++++ packages/catalog-model/package.json | 4 +- packages/cli/CHANGELOG.md | 46 ++++++ packages/cli/package.json | 14 +- packages/config-loader/CHANGELOG.md | 21 +++ packages/config-loader/package.json | 2 +- packages/core-api/CHANGELOG.md | 90 ++++++++++ packages/core-api/package.json | 8 +- packages/core/CHANGELOG.md | 70 ++++++++ packages/core/package.json | 10 +- packages/create-app/CHANGELOG.md | 132 +++++++++++++++ packages/create-app/package.json | 48 +++--- packages/dev-utils/CHANGELOG.md | 40 +++++ packages/dev-utils/package.json | 10 +- packages/e2e-test/CHANGELOG.md | 7 + packages/e2e-test/package.json | 2 +- packages/storybook/CHANGELOG.md | 9 + packages/storybook/package.json | 4 +- packages/techdocs-cli/CHANGELOG.md | 17 ++ packages/techdocs-cli/package.json | 4 +- packages/test-utils/CHANGELOG.md | 26 +++ packages/test-utils/package.json | 8 +- packages/theme/CHANGELOG.md | 19 +++ packages/theme/package.json | 4 +- plugins/api-docs/CHANGELOG.md | 59 +++++++ plugins/api-docs/package.json | 16 +- plugins/app-backend/CHANGELOG.md | 22 +++ plugins/app-backend/package.json | 8 +- plugins/auth-backend/CHANGELOG.md | 61 +++++++ plugins/auth-backend/package.json | 8 +- plugins/catalog-backend/CHANGELOG.md | 155 ++++++++++++++++++ plugins/catalog-backend/package.json | 10 +- plugins/catalog-graphql/CHANGELOG.md | 29 ++++ plugins/catalog-graphql/package.json | 10 +- plugins/catalog/CHANGELOG.md | 79 +++++++++ plugins/catalog/package.json | 18 +- plugins/circleci/CHANGELOG.md | 52 ++++++ plugins/circleci/package.json | 16 +- plugins/cloudbuild/CHANGELOG.md | 51 ++++++ plugins/cloudbuild/package.json | 16 +- plugins/cost-insights/CHANGELOG.md | 65 ++++++++ plugins/cost-insights/package.json | 14 +- plugins/explore/CHANGELOG.md | 48 ++++++ plugins/explore/package.json | 12 +- plugins/gcp-projects/CHANGELOG.md | 32 ++++ plugins/gcp-projects/package.json | 12 +- plugins/github-actions/CHANGELOG.md | 66 ++++++++ plugins/github-actions/package.json | 18 +- plugins/gitops-profiles/CHANGELOG.md | 32 ++++ plugins/gitops-profiles/package.json | 12 +- plugins/graphiql/CHANGELOG.md | 33 ++++ plugins/graphiql/package.json | 12 +- plugins/graphql/CHANGELOG.md | 18 ++ plugins/graphql/package.json | 8 +- plugins/jenkins/CHANGELOG.md | 62 +++++++ plugins/jenkins/package.json | 16 +- plugins/kubernetes-backend/CHANGELOG.md | 16 ++ plugins/kubernetes-backend/package.json | 6 +- plugins/kubernetes/CHANGELOG.md | 42 +++++ plugins/kubernetes/package.json | 16 +- plugins/lighthouse/CHANGELOG.md | 65 ++++++++ plugins/lighthouse/package.json | 18 +- plugins/newrelic/CHANGELOG.md | 53 ++++++ plugins/newrelic/package.json | 12 +- plugins/proxy-backend/CHANGELOG.md | 42 +++++ plugins/proxy-backend/package.json | 6 +- plugins/register-component/CHANGELOG.md | 55 +++++++ plugins/register-component/package.json | 16 +- plugins/rollbar-backend/CHANGELOG.md | 16 ++ plugins/rollbar-backend/package.json | 6 +- plugins/rollbar/CHANGELOG.md | 51 ++++++ plugins/rollbar/package.json | 16 +- plugins/scaffolder-backend/CHANGELOG.md | 46 ++++++ plugins/scaffolder-backend/package.json | 8 +- plugins/scaffolder/CHANGELOG.md | 67 ++++++++ plugins/scaffolder/package.json | 16 +- plugins/sentry-backend/CHANGELOG.md | 16 ++ plugins/sentry-backend/package.json | 6 +- plugins/sentry/CHANGELOG.md | 41 +++++ plugins/sentry/package.json | 14 +- plugins/sonarqube/CHANGELOG.md | 37 +++++ plugins/sonarqube/package.json | 14 +- plugins/tech-radar/CHANGELOG.md | 35 ++++ plugins/tech-radar/package.json | 14 +- plugins/techdocs-backend/CHANGELOG.md | 94 +++++++++++ plugins/techdocs-backend/package.json | 8 +- plugins/techdocs/CHANGELOG.md | 65 ++++++++ plugins/techdocs/package.json | 20 +-- plugins/user-settings/CHANGELOG.md | 32 ++++ plugins/user-settings/package.json | 12 +- plugins/welcome/CHANGELOG.md | 32 ++++ plugins/welcome/package.json | 12 +- 226 files changed, 2775 insertions(+), 1404 deletions(-) delete mode 100644 .changeset/1724.md delete mode 100644 .changeset/2284.md delete mode 100644 .changeset/2515.md delete mode 100644 .changeset/2532.md delete mode 100644 .changeset/2535.md delete mode 100644 .changeset/2541.md delete mode 100644 .changeset/2543.md delete mode 100644 .changeset/2562.md delete mode 100644 .changeset/2563.md delete mode 100644 .changeset/2565.md delete mode 100644 .changeset/2575.md delete mode 100644 .changeset/2586.md delete mode 100644 .changeset/2587.md delete mode 100644 .changeset/2597.md delete mode 100644 .changeset/2598.md delete mode 100644 .changeset/2600.md delete mode 100644 .changeset/2603.md delete mode 100644 .changeset/2606.md delete mode 100644 .changeset/2609.md delete mode 100644 .changeset/2610.md delete mode 100644 .changeset/2611.md delete mode 100644 .changeset/2612.md delete mode 100644 .changeset/2613.md delete mode 100644 .changeset/2614.md delete mode 100644 .changeset/2615.md delete mode 100644 .changeset/2616.md delete mode 100644 .changeset/2623.md delete mode 100644 .changeset/2624.md delete mode 100644 .changeset/2625-catalog-backend.md delete mode 100644 .changeset/2625-cli.md delete mode 100644 .changeset/2628.md delete mode 100644 .changeset/2630.md delete mode 100644 .changeset/2637.md delete mode 100644 .changeset/2639.md delete mode 100644 .changeset/2641.md delete mode 100644 .changeset/2656.md delete mode 100644 .changeset/2657.md delete mode 100644 .changeset/2660.md delete mode 100644 .changeset/2661.md delete mode 100644 .changeset/2669-catalog-backend.md delete mode 100644 .changeset/2669-catalog-model.md delete mode 100644 .changeset/2669-create-app.md delete mode 100644 .changeset/2674.md delete mode 100644 .changeset/2686.md delete mode 100644 .changeset/2689.md delete mode 100644 .changeset/2722.md delete mode 100644 .changeset/2798.md delete mode 100644 .changeset/2800.md delete mode 100644 .changeset/2803.md delete mode 100644 .changeset/2804.md delete mode 100644 .changeset/2826.md delete mode 100644 .changeset/2874.md delete mode 100644 .changeset/3030.md delete mode 100644 .changeset/3066.md delete mode 100644 .changeset/3113.md delete mode 100644 .changeset/3130.md delete mode 100644 .changeset/alchemists-on-ice.md delete mode 100644 .changeset/angry-crabs-relate.md delete mode 100644 .changeset/beige-apes-serve.md delete mode 100644 .changeset/beige-pandas-thank.md delete mode 100644 .changeset/blue-donkeys-exercise.md delete mode 100644 .changeset/carpal-tunnel-driver.md delete mode 100644 .changeset/chilly-emus-fetch.md delete mode 100644 .changeset/clean-phones-vanish.md delete mode 100644 .changeset/cli-parallel-build-options.md delete mode 100644 .changeset/codeowner-processor-url-reader.md delete mode 100644 .changeset/cost-insights-kind-trees-occur.md delete mode 100644 .changeset/cost-insights-rich-dodos-smell.md delete mode 100644 .changeset/cost-insights-sparkle.md delete mode 100644 .changeset/cost-insights-swift-carrots-bake.md delete mode 100644 .changeset/create-app-url-reader-update.md delete mode 100644 .changeset/cyan-plants-dress.md delete mode 100644 .changeset/dull-icons-share.md delete mode 100644 .changeset/eighty-lamps-smell.md delete mode 100644 .changeset/eighty-stingrays-type.md delete mode 100644 .changeset/flat-bugs-do.md delete mode 100644 .changeset/flat-yaks-march.md delete mode 100644 .changeset/fresh-maps-complain.md delete mode 100644 .changeset/funny-readers-breathe.md delete mode 100644 .changeset/fuzzy-falcons-happen.md delete mode 100644 .changeset/good-islands-cheer.md delete mode 100644 .changeset/great-apples-flash.md delete mode 100644 .changeset/happy-ads-behave.md delete mode 100644 .changeset/heavy-berries-hear.md delete mode 100644 .changeset/hungry-parrots-work.md delete mode 100644 .changeset/little-wasps-pull.md delete mode 100644 .changeset/long-ways-count.md delete mode 100644 .changeset/loud-lamps-visit.md delete mode 100644 .changeset/lovely-suits-flash.md delete mode 100644 .changeset/metal-fishes-learn.md delete mode 100644 .changeset/mighty-starfishes-taste.md delete mode 100644 .changeset/nervous-drinks-notice.md delete mode 100644 .changeset/new-url-reader.md delete mode 100644 .changeset/nice-candles-argue.md delete mode 100644 .changeset/ninety-ads-dance.md delete mode 100644 .changeset/ninety-pens-poke.md delete mode 100644 .changeset/old-eagles-admire.md delete mode 100644 .changeset/old-falcons-jump.md delete mode 100644 .changeset/old-jeans-dance.md delete mode 100644 .changeset/olive-bikes-jog.md delete mode 100644 .changeset/pink-spies-rule.md delete mode 100644 .changeset/polite-laws-think.md delete mode 100644 .changeset/polite-moose-cough.md delete mode 100644 .changeset/popular-ghosts-remain.md delete mode 100644 .changeset/popular-jars-serve.md delete mode 100644 .changeset/pretty-cups-joke.md delete mode 100644 .changeset/proud-eagles-grow.md delete mode 100644 .changeset/raghu-is-great.md delete mode 100644 .changeset/red-games-float.md delete mode 100644 .changeset/remove-location-processor-read.md delete mode 100644 .changeset/rocket-lizards-congregate.md delete mode 100644 .changeset/selfish-bats-perform.md delete mode 100644 .changeset/selfish-mugs-itch.md delete mode 100644 .changeset/seven-humans-check.md delete mode 100644 .changeset/short-secrets.md delete mode 100644 .changeset/slimy-garlics-eat.md delete mode 100644 .changeset/slow-experts-peel.md delete mode 100644 .changeset/spicy-moles-yell.md delete mode 100644 .changeset/spicy-rockets-ring.md delete mode 100644 .changeset/spotty-apples-visit.md delete mode 100644 .changeset/sweet-bikes-battle.md delete mode 100644 .changeset/swift-emus-mate.md delete mode 100644 .changeset/swift-peas-argue.md delete mode 100644 .changeset/ten-bees-wash.md delete mode 100644 .changeset/three-horses-juggle.md delete mode 100644 .changeset/tough-weeks-pull.md delete mode 100644 .changeset/url-reader-processor.md delete mode 100644 .changeset/weak-ducks-tan.md create mode 100644 packages/app/CHANGELOG.md create mode 100644 packages/backend-common/CHANGELOG.md create mode 100644 packages/backend/CHANGELOG.md create mode 100644 packages/catalog-model/CHANGELOG.md create mode 100644 packages/cli/CHANGELOG.md create mode 100644 packages/config-loader/CHANGELOG.md create mode 100644 packages/core-api/CHANGELOG.md create mode 100644 packages/core/CHANGELOG.md create mode 100644 packages/create-app/CHANGELOG.md create mode 100644 packages/dev-utils/CHANGELOG.md create mode 100644 packages/e2e-test/CHANGELOG.md create mode 100644 packages/storybook/CHANGELOG.md create mode 100644 packages/techdocs-cli/CHANGELOG.md create mode 100644 packages/test-utils/CHANGELOG.md create mode 100644 packages/theme/CHANGELOG.md create mode 100644 plugins/api-docs/CHANGELOG.md create mode 100644 plugins/app-backend/CHANGELOG.md create mode 100644 plugins/auth-backend/CHANGELOG.md create mode 100644 plugins/catalog-backend/CHANGELOG.md create mode 100644 plugins/catalog-graphql/CHANGELOG.md create mode 100644 plugins/catalog/CHANGELOG.md create mode 100644 plugins/circleci/CHANGELOG.md create mode 100644 plugins/cloudbuild/CHANGELOG.md create mode 100644 plugins/cost-insights/CHANGELOG.md create mode 100644 plugins/explore/CHANGELOG.md create mode 100644 plugins/gcp-projects/CHANGELOG.md create mode 100644 plugins/github-actions/CHANGELOG.md create mode 100644 plugins/gitops-profiles/CHANGELOG.md create mode 100644 plugins/graphiql/CHANGELOG.md create mode 100644 plugins/graphql/CHANGELOG.md create mode 100644 plugins/jenkins/CHANGELOG.md create mode 100644 plugins/kubernetes-backend/CHANGELOG.md create mode 100644 plugins/kubernetes/CHANGELOG.md create mode 100644 plugins/lighthouse/CHANGELOG.md create mode 100644 plugins/newrelic/CHANGELOG.md create mode 100644 plugins/proxy-backend/CHANGELOG.md create mode 100644 plugins/register-component/CHANGELOG.md create mode 100644 plugins/rollbar-backend/CHANGELOG.md create mode 100644 plugins/rollbar/CHANGELOG.md create mode 100644 plugins/scaffolder-backend/CHANGELOG.md create mode 100644 plugins/scaffolder/CHANGELOG.md create mode 100644 plugins/sentry-backend/CHANGELOG.md create mode 100644 plugins/sentry/CHANGELOG.md create mode 100644 plugins/sonarqube/CHANGELOG.md create mode 100644 plugins/tech-radar/CHANGELOG.md create mode 100644 plugins/techdocs-backend/CHANGELOG.md create mode 100644 plugins/techdocs/CHANGELOG.md create mode 100644 plugins/user-settings/CHANGELOG.md create mode 100644 plugins/welcome/CHANGELOG.md diff --git a/.changeset/1724.md b/.changeset/1724.md deleted file mode 100644 index 2ac03f94e9..0000000000 --- a/.changeset/1724.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -'@backstage/cli': minor -'@backstage/plugin-api-docs': minor -'@backstage/plugin-app-backend': minor -'@backstage/plugin-auth-backend': minor -'@backstage/plugin-catalog-graphql': minor -'@backstage/plugin-catalog': minor -'@backstage/plugin-circleci': minor -'@backstage/plugin-explore': minor -'@backstage/plugin-gcp-projects': minor -'@backstage/plugin-github-actions': minor -'@backstage/plugin-gitops-profiles': minor -'@backstage/plugin-graphiql': minor -'@backstage/plugin-jenkins': minor -'@backstage/plugin-kubernetes': minor -'@backstage/plugin-lighthouse': minor -'@backstage/plugin-newrelic': minor -'@backstage/plugin-register-component': minor -'@backstage/plugin-rollbar': minor -'@backstage/plugin-scaffolder': minor -'@backstage/plugin-sentry': minor -'@backstage/plugin-tech-radar': minor -'@backstage/plugin-techdocs': minor -'@backstage/plugin-welcome': minor ---- - -Create backend plugin through CLI diff --git a/.changeset/2284.md b/.changeset/2284.md deleted file mode 100644 index 655cc5a3e8..0000000000 --- a/.changeset/2284.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@backstage/core-api': minor -'@backstage/core': minor -'@backstage/plugin-auth-backend': minor ---- - -Add SAML login to backstage - -![](https://user-images.githubusercontent.com/872486/92251660-bb9e3400-eeff-11ea-86fe-1f2a0262cd31.png) - -![](https://user-images.githubusercontent.com/872486/93851658-1a76f200-fce3-11ea-990b-26ca1a327a15.png) diff --git a/.changeset/2515.md b/.changeset/2515.md deleted file mode 100644 index 32f5edd6b7..0000000000 --- a/.changeset/2515.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-cloudbuild': minor ---- - -Releasing Google Cloud Build Plugin diff --git a/.changeset/2532.md b/.changeset/2532.md deleted file mode 100644 index 14307f6c08..0000000000 --- a/.changeset/2532.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/catalog-model': minor ---- - -Add handling and docs for entity references diff --git a/.changeset/2535.md b/.changeset/2535.md deleted file mode 100644 index bd28f3ce03..0000000000 --- a/.changeset/2535.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'@backstage/core': patch -'@backstage/theme': patch ---- - -Fix banner position and color - -This PR closes: #2245 - -The "fixed" props added to control the position of the banner. When it is set to true the banner will be shown in bottom of that page and the width will be based on the content of the message. - -![](https://user-images.githubusercontent.com/15106494/93765685-999df480-fc15-11ea-8fa5-11cac5836cf1.png) - -![](https://user-images.githubusercontent.com/15106494/93765697-9e62a880-fc15-11ea-92af-b6a7fee4bb21.png) diff --git a/.changeset/2541.md b/.changeset/2541.md deleted file mode 100644 index 1ba29da040..0000000000 --- a/.changeset/2541.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/theme': minor ---- - -Tweak dark mode colors diff --git a/.changeset/2543.md b/.changeset/2543.md deleted file mode 100644 index 00311ade45..0000000000 --- a/.changeset/2543.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': minor ---- - -Add Azure DevOps support to the scaffolder backend - -This adds support for Azure DevOps to the scaffolder (preparer & publisher). I thought I should get this in there now since #2426 has been merged. I had a previous PR with only the preparer but I closed that in favor of this one. - -I stayed with the 'azure/api' structure but I guess we should try and go the same way as with GitHub here #2501 diff --git a/.changeset/2562.md b/.changeset/2562.md deleted file mode 100644 index 716f370ad9..0000000000 --- a/.changeset/2562.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/create-app': minor -'@backstage/plugin-auth-backend': minor -'@backstage/plugin-techdocs-backend': minor ---- - -Change the default backend plugin mount point to /api diff --git a/.changeset/2563.md b/.changeset/2563.md deleted file mode 100644 index 318d5cf5ef..0000000000 --- a/.changeset/2563.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-github-actions': minor ---- - -Adds a widget to show recent git workflow runs to the github actions plugin. The default setting is the last 5 runs across all branches but both branch and the number of runs are configurable. diff --git a/.changeset/2565.md b/.changeset/2565.md deleted file mode 100644 index 92253101ee..0000000000 --- a/.changeset/2565.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -'@backstage/core-api': patch ---- - -Add initial RouteRefRegistry - -Starting out some work to bring routing back and working as part of the work towards finalizing #1536 - -This is some of the groundwork of an experiment we're working on to enable routing via RouteRefs, while letting the app itself look something like this: - -```jsx -const App = () => ( - - - - {' '} - // catalogRouteRef - - - - - - - // statusRouteRef - - - - - - - - - - // sentryRouteRef - - - - - - - - - - - - - - - -); -``` - -As part of inverting the composition of the app, route refs and routing in general was somewhat broken, intentionally. Right now it's not really possible to easily route to different parts of the app from a plugin, or even different parts of the plugin that are not within the same router. - -The core part of the experiment is to construct a map of ApiRef[] -> path overrides. Each key in the map is the list of route refs to traversed to reach a leaf in the routing tree, and the value is the path override at that point. For example, the above tree would add entries like [techDocsRouteRef] -> '/docs', and [entityRouteRef, apiDocsRouteRef] -> '/api'. By mapping out the entire app in this structure, the idea is that we can navigate to any point in the app using RouteRefs. - -The RouteRefRegistry is an implementation of such a map, and the idea is to add it in master to make it a bit easier to experiment and iterate. This is not an exposed API at this point. - -We've explored a couple of alternatives for how to enable routing, but it's boiled down to either a solution centred around the route map mentioned above, or treating all routes as static and globally unique, with no room for flexibility, customization or conflicts between different plugins. We're starting out pursuing this options 😁. We also expect that a the app-wide routing table will make things like dynamic loading a lot cleaner, as there would be a much more clear handoff between the main chunk and dynamic chunks. diff --git a/.changeset/2575.md b/.changeset/2575.md deleted file mode 100644 index b08f50ed13..0000000000 --- a/.changeset/2575.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': minor ---- - -Fix dense in Structured Metadata Table diff --git a/.changeset/2586.md b/.changeset/2586.md deleted file mode 100644 index 568adb8a3e..0000000000 --- a/.changeset/2586.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/catalog-model': minor -'@backstage/plugin-catalog-backend': minor ---- - -Entirely case insensitive read path of entities diff --git a/.changeset/2587.md b/.changeset/2587.md deleted file mode 100644 index 910de1ca04..0000000000 --- a/.changeset/2587.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/catalog-model': minor ---- - -Add the User & Group entities - -A user describes a person, such as an employee, a contractor, or similar. Users belong to Group entities in the catalog. - -A group describes an organizational entity, such as for example a team, a business unit, or a loose collection of people in an interest group. Members of these groups are modeled in the catalog as kind User. diff --git a/.changeset/2597.md b/.changeset/2597.md deleted file mode 100644 index 9db25e8d0e..0000000000 --- a/.changeset/2597.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/catalog-model': minor -'@backstage/plugin-catalog-backend': minor ---- - -Add ApiDefinitionAtLocationProcessor that allows to load a API definition from another location diff --git a/.changeset/2598.md b/.changeset/2598.md deleted file mode 100644 index 03e10e7db8..0000000000 --- a/.changeset/2598.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@backstage/plugin-techdocs': minor ---- - -Add a message if techdocs takes long time to load - -Fixes #2416. - -The UI after the change should look like this: - -![techdocs-progress-bar](https://user-images.githubusercontent.com/33940798/94189286-296ac980-fec8-11ea-9051-1b3db938d12f.gif) diff --git a/.changeset/2600.md b/.changeset/2600.md deleted file mode 100644 index 6fc9775fa1..0000000000 --- a/.changeset/2600.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -'@backstage/plugin-techdocs-backend': minor -'@backstage/plugin-proxy-backend': minor -'@backstage/plugin-auth-backend': minor -'@backstage/create-app': minor -'@backstage/backend-common': minor ---- - -Add service discovery interface and implement for single host deployments - -Fixes #1847, #2596 - -Went with an interface similar to the frontend DiscoveryApi, since it's dead simple but still provides a lot of flexibility in the implementation. - -Also ended up with two different methods, one for internal endpoint discovery and one for external. The two use-cases are explained a bit more in the docs, but basically it's service-to-service vs callback URLs. - -This did get me thinking about uniqueness and that we're heading towards a global namespace for backend plugin IDs. That's probably fine, but if we're happy with that we should leverage it a bit more to simplify the backend setup. For example we'd have each plugin provide its own ID and not manually mount on paths in the backend. - -Draft until we're happy with the implementation, then I can add more docs and changelog entry. Also didn't go on a thorough hunt for places where discovery can be used, but I don't think there are many since it's been pretty awkward to do service-to-service communication. diff --git a/.changeset/2603.md b/.changeset/2603.md deleted file mode 100644 index c24d2ea033..0000000000 --- a/.changeset/2603.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/backend-common': minor -'@backstage/create-app': minor ---- - -Make CSP configurable to fix app-backend served app not being able to fetch - -See discussion [here on discord](https://discordapp.com/channels/687207715902193673/687235481154617364/758721460163575850) diff --git a/.changeset/2606.md b/.changeset/2606.md deleted file mode 100644 index f6059e7705..0000000000 --- a/.changeset/2606.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': patch ---- - -Move auth provider router creation to router diff --git a/.changeset/2609.md b/.changeset/2609.md deleted file mode 100644 index 6bb8ab7a40..0000000000 --- a/.changeset/2609.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Sync scaffolded backend with example diff --git a/.changeset/2610.md b/.changeset/2610.md deleted file mode 100644 index 92ec8dcc0c..0000000000 --- a/.changeset/2610.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-circleci': minor ---- - -Refactor to use DiscoveryApi diff --git a/.changeset/2611.md b/.changeset/2611.md deleted file mode 100644 index cbf3129038..0000000000 --- a/.changeset/2611.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Remove discovery api override diff --git a/.changeset/2612.md b/.changeset/2612.md deleted file mode 100644 index bf093d8f77..0000000000 --- a/.changeset/2612.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-jenkins': patch ---- - -Refactor to use DiscoveryApi diff --git a/.changeset/2613.md b/.changeset/2613.md deleted file mode 100644 index cf8c1a155a..0000000000 --- a/.changeset/2613.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@backstage/plugin-auth-backend': minor ---- - -Initial implementation of catalog user lookup - -This adds a basic catalog client + method for the Google provider to look up users in the catalog. It expects to find a single user entity in the catalog with a google.com/email annotation that matches the email of the Google profile. - -Right now it falls back to the old behavior of splitting the email, since I don't wanna break the sign-in flow for existing apps, not yet anyway x). - -- Added "@backstage/catalog-model@^0.1.1-alpha.23" as a dependency -- Added "node-fetch@^2.6.1" as a dependency diff --git a/.changeset/2614.md b/.changeset/2614.md deleted file mode 100644 index 7e2660bc80..0000000000 --- a/.changeset/2614.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/create-app': minor ---- - -Default to using internal scope for new plugins diff --git a/.changeset/2615.md b/.changeset/2615.md deleted file mode 100644 index 412b8127c4..0000000000 --- a/.changeset/2615.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': minor ---- - -Use localhost to fall back to IPv4 if IPv6 isn't available diff --git a/.changeset/2616.md b/.changeset/2616.md deleted file mode 100644 index 9a70c0e79f..0000000000 --- a/.changeset/2616.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/create-app': minor ---- - -Remove identity-backend - -Not used, and we're heading down the route of identities in the catalog diff --git a/.changeset/2623.md b/.changeset/2623.md deleted file mode 100644 index abd5ce7a4c..0000000000 --- a/.changeset/2623.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-catalog': minor ---- - -Created EntityNotFound component for catalog which displays the 404 page when entity is not found. - -Fixes #2266 diff --git a/.changeset/2624.md b/.changeset/2624.md deleted file mode 100644 index dcfc800713..0000000000 --- a/.changeset/2624.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -Make title meaningful after component creation - -Fixes #2458. - -After the change, the UX should look like this: - -### If the component creation was successful: - -![successfully-created-component](https://user-images.githubusercontent.com/33940798/94339294-8bd1e000-0016-11eb-885b-7936fcc23b63.gif) - -### If the component creation failed: - -![failed-to-create-component](https://user-images.githubusercontent.com/33940798/94339296-90969400-0016-11eb-9a74-ce16b3dd8d88.gif) diff --git a/.changeset/2625-catalog-backend.md b/.changeset/2625-catalog-backend.md deleted file mode 100644 index bcd49dddb2..0000000000 --- a/.changeset/2625-catalog-backend.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Add codeowners processor - -- Add `codeowners-utils@^1.0.2` as a dependency -- Add `core-js@^3.6.5` as a dependency -- Added new CodeOwnersProcessor diff --git a/.changeset/2625-cli.md b/.changeset/2625-cli.md deleted file mode 100644 index c5a5437b69..0000000000 --- a/.changeset/2625-cli.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Add codeowners processor - -- Include ESNext.Promise in TypeScript compilation diff --git a/.changeset/2628.md b/.changeset/2628.md deleted file mode 100644 index 0ac36d456a..0000000000 --- a/.changeset/2628.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -This feature works the same as \$secret does in config - it allows programmatic substitution of values into a document. - -This is particularly useful e.g. for API type entities where you do not want to repeat your entire API spec document inside the catalog-info.yaml file. For those cases, you can instead do something like - -``` -apiVersion: backstage.io/v1alpha1 -kind: API -metadata: - name: my-federated-service -spec: - type: graphql - definition: - $text: ./schema.graphql -``` - -The textual content of that file will be injected as the value of definition, during each refresh loop. Both relative and absolute paths are supported, as well as any HTTP/HTTPS URL pointing to a service that returns the relevant data. - -The initial version supports injection of text file data, and structured data from JSON and YAML files. You can add any handler of your own in addition to these. diff --git a/.changeset/2630.md b/.changeset/2630.md deleted file mode 100644 index 933204e292..0000000000 --- a/.changeset/2630.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/create-app': minor -'@backstage/plugin-catalog-backend': minor ---- - -Allow node v14 and add to master build matrix - -- Upgrade sqlite3@^5.0.0 in @backstage/plugin-catalog-backend -- Add Node 14 to engines in @backstage/create-app diff --git a/.changeset/2637.md b/.changeset/2637.md deleted file mode 100644 index 9d368634b6..0000000000 --- a/.changeset/2637.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -'@backstage/plugin-cost-insights': minor -'@backstage/plugin-explore': minor ---- - -This PR adds Spotify's Cost Insights Tool. Cost Insights explains costs from cloud services in an understandable way, using software terms familiar to your engineers. This tool helps you and your team make trade-offs between cost optimization efforts and your other priorities. - -Cost Insights features: - -Daily cost graph by team or billing account -Cost comparison against configurable business metrics -Insights panels for configurable cloud products your company uses -Cost alerts and recommendations -Selectable time periods for month over month, or quarter over quarter cost comparison -Conversion of cost growth into average engineer cost (configurable) to help optimization trade-off decisions - -![plugin-cost-insights](https://user-images.githubusercontent.com/3030003/94430416-e166d380-0161-11eb-891c-9ce10187683e.gif) - -This PR adds the Cost Insights frontend React plugin with a defined CostInsightsApi. We include an example client with static data in the expected format. This API should talk with a cloud billing backend that aggregates billing data from your cloud provider. - -Fixes #688 💵 diff --git a/.changeset/2639.md b/.changeset/2639.md deleted file mode 100644 index e6a623088e..0000000000 --- a/.changeset/2639.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': minor ---- - -Upgrade dependency `esbuild@0.7.7` diff --git a/.changeset/2641.md b/.changeset/2641.md deleted file mode 100644 index 3b06358e81..0000000000 --- a/.changeset/2641.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -Simplify the read function in processors diff --git a/.changeset/2656.md b/.changeset/2656.md deleted file mode 100644 index e253b6b1f6..0000000000 --- a/.changeset/2656.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': minor ---- - -Lookup user in Google Auth Provider diff --git a/.changeset/2657.md b/.changeset/2657.md deleted file mode 100644 index 9aa645d65f..0000000000 --- a/.changeset/2657.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': minor ---- - -Added EmptyState component diff --git a/.changeset/2660.md b/.changeset/2660.md deleted file mode 100644 index 758d523f07..0000000000 --- a/.changeset/2660.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-api-docs': patch ---- - -Resolve some dark mode styling issues in asyncAPI specs diff --git a/.changeset/2661.md b/.changeset/2661.md deleted file mode 100644 index e73b0abf26..0000000000 --- a/.changeset/2661.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': patch ---- - -Fixed banner component position in DismissableBanner component diff --git a/.changeset/2669-catalog-backend.md b/.changeset/2669-catalog-backend.md deleted file mode 100644 index 32ee5c0f4d..0000000000 --- a/.changeset/2669-catalog-backend.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -Add the ability to import users from GitHub Organization into the catalog. - -The token needs to have the scopes `user:email`, `read:user`, and `read:org`. diff --git a/.changeset/2669-catalog-model.md b/.changeset/2669-catalog-model.md deleted file mode 100644 index b9a2841a1b..0000000000 --- a/.changeset/2669-catalog-model.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/catalog-model': minor ---- - -Add the ability to import users from GitHub Organization into the catalog. diff --git a/.changeset/2669-create-app.md b/.changeset/2669-create-app.md deleted file mode 100644 index e7075ffeb8..0000000000 --- a/.changeset/2669-create-app.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/create-app': minor ---- - -Add the ability to import users from GitHub Organization into the catalog. - -The token needs to have the scopes `user:email`, `read:user`, and `read:org`. diff --git a/.changeset/2674.md b/.changeset/2674.md deleted file mode 100644 index 15a7a0e849..0000000000 --- a/.changeset/2674.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@backstage/backend-common': minor -'@backstage/create-app': minor ---- - -Auto-create plugin databases - -Relates to #1598. - -This creates databases for plugins before handing off control to plugins. - -The list of plugins currently need to be hard-coded depending on the installed plugins. A later PR will properly refactor the code to provide a factory pattern where plugins specify what they need, and Knex instances will be provided based on the input. diff --git a/.changeset/2686.md b/.changeset/2686.md deleted file mode 100644 index 8831c1309b..0000000000 --- a/.changeset/2686.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Update SSR template to pass CI diff --git a/.changeset/2689.md b/.changeset/2689.md deleted file mode 100644 index be75dd261b..0000000000 --- a/.changeset/2689.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Upgrade dependency rollup-plugin-typescript2 to ^0.27.3 diff --git a/.changeset/2722.md b/.changeset/2722.md deleted file mode 100644 index 69642b5797..0000000000 --- a/.changeset/2722.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@backstage/plugin-api-docs': minor ---- - -There were some missing features and markdown was not rendered properly, but this is fixed now. - -Details: - -- [`asyncapi/asyncapi-react#149`](https://github.com/asyncapi/asyncapi-react/pull/149) - fix: improve markdown rendering of nested fields -- [`asyncapi/asyncapi-react#150`](https://github.com/asyncapi/asyncapi-react/pull/150) - feat: display the description of channels and operations -- [`asyncapi/asyncapi-react#153`](https://github.com/asyncapi/asyncapi-react/pull/153) - fix: let the list of `enums` break into multiple lines diff --git a/.changeset/2798.md b/.changeset/2798.md deleted file mode 100644 index 2b202142ef..0000000000 --- a/.changeset/2798.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -Use the new `UrlReader` in `PlaceholderProcessor`. -This allows to use the placeholder processor to include API definitions in API entities. -Previously it was only possible to do this if the definition comes from the same location type as the entity itself. diff --git a/.changeset/2800.md b/.changeset/2800.md deleted file mode 100644 index 0e051b0557..0000000000 --- a/.changeset/2800.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'@backstage/core': minor -'@backstage/core-api': minor ---- - -Updated the `GithubAuth.create` method to configure the default scope of the Github Auth Api. As a result the -default scope is configurable when overwriting the Core Api in the app. - -``` -GithubAuth.create({ - discoveryApi, - oauthRequestApi, - defaultScopes: ['read:user', 'repo'], -}), -``` diff --git a/.changeset/2803.md b/.changeset/2803.md deleted file mode 100644 index 3a4773757f..0000000000 --- a/.changeset/2803.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -Remove the backstage.io/definition-at-location annotation. -The annotation was superseded by the placeholder processor. - -```yaml -apiVersion: backstage.io/v1alpha1 -kind: API -metadata: - name: spotify - description: The Spotify web API - tags: - - spotify - - rest - annotations: - # Don't use this annotation, but the placeholder $text instead (see below). - backstage.io/definition-at-location: 'url:https://raw.githubusercontent.com/APIs-guru/openapi-directory/master/APIs/spotify.com/v1/swagger.yaml' -spec: - type: openapi - lifecycle: production - owner: spotify@example.com - definition: - $text: https://raw.githubusercontent.com/APIs-guru/openapi-directory/master/APIs/spotify.com/v1/swagger.yaml -``` diff --git a/.changeset/2804.md b/.changeset/2804.md deleted file mode 100644 index 5223a24791..0000000000 --- a/.changeset/2804.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'@backstage/plugin-proxy-backend': minor ---- - -Limit the http headers that are forwarded from the request to a safe set of defaults. -A user can configure additional headers that should be forwarded if the specific applications needs that. - -```yaml -proxy: - '/my-api': - target: 'https://my-api.com/get' - allowedHeaders: - # We need to forward the Authorization header that was provided by the caller - - Authorization -``` diff --git a/.changeset/2826.md b/.changeset/2826.md deleted file mode 100644 index fb2dbb6877..0000000000 --- a/.changeset/2826.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -'example-app': minor -'@backstage/core': minor -'@backstage/create-app': minor -'@backstage/plugin-catalog': minor -'@backstage/plugin-github-actions': minor -'@backstage/plugin-jenkins': minor -'@backstage/plugin-lighthouse': minor ---- - -The InfoCard variant `'height100'` is deprecated. Use variant `'gridItem'` instead. - -When the InfoCard is displayed as a grid item within a grid, you may want items to have the same height for all items. -Set to the `'gridItem'` variant to display the InfoCard with full height suitable for Grid: -`...` - -Changed the InfoCards in '@backstage/plugin-github-actions', '@backstage/plugin-jenkins', '@backstage/plugin-lighthouse' -to pass an optional variant to the corresponding card of the plugin. - -As a result the overview content of the EntityPage shows cards with full height suitable for Grid. diff --git a/.changeset/2874.md b/.changeset/2874.md deleted file mode 100644 index 693962314f..0000000000 --- a/.changeset/2874.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -The way that wiring together a catalog happens, has changed drastically. Now -there is a new class `CatalogBuilder` that does almost all of the heavy lifting -of how to augment/replace pieces of catalog functionality, such as adding -support for custom entities or adding additional processors. - -As the builder was added, a lot of the static methods and builders for default -setups have been removed from classes deep in the hierarchy. Instead, the -builder contains the knowledge of what the defaults are. diff --git a/.changeset/3030.md b/.changeset/3030.md deleted file mode 100644 index 89730dc14c..0000000000 --- a/.changeset/3030.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': minor ---- - -Add the ability to import components from Bitbucket Server to the service catalog diff --git a/.changeset/3066.md b/.changeset/3066.md deleted file mode 100644 index 7c157c94c3..0000000000 --- a/.changeset/3066.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -Filters passed to the `/entities` endpoint of the catalog has changed format. - -The old way was to pass things on the form `?a=b&c=d`; the new way is to pass -things on the form `?filter=a=b,c=d`. See discussion in -[#2910](https://github.com/backstage/backstage/issues/2910) for details. - -The comma separated items within a single filter have an AND between them. If -multiple such filters are passed, they have an OR between those item groups. diff --git a/.changeset/3113.md b/.changeset/3113.md deleted file mode 100644 index 7c53d01c16..0000000000 --- a/.changeset/3113.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@backstage/catalog-model': minor -'@backstage/plugin-catalog-backend': minor ---- - -Changes the various kind policies into a new type `KindValidator`. - -Adds `CatalogProcessor#validateEntityKind` that makes use of the above -validators. This moves entity schema validity checking away from entity -policies and into processors, centralizing the extension points into the -processor chain. diff --git a/.changeset/3130.md b/.changeset/3130.md deleted file mode 100644 index 6edac9372b..0000000000 --- a/.changeset/3130.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Fix `CatalogBuilder#addProcessor`. diff --git a/.changeset/alchemists-on-ice.md b/.changeset/alchemists-on-ice.md deleted file mode 100644 index 24f41730d1..0000000000 --- a/.changeset/alchemists-on-ice.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Add support for `fields` sub-selection of just parts of an entity when listing -entities in the catalog backend. - -Example: `.../entities?fields=metadata.name,spec.type` will return partial -entity objects with only those exact fields present and the rest cut out. -Fields do not have to be simple scalars - you can for example do -`fields=metadata`. diff --git a/.changeset/angry-crabs-relate.md b/.changeset/angry-crabs-relate.md deleted file mode 100644 index 678093c1ac..0000000000 --- a/.changeset/angry-crabs-relate.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -Add ability to dry run adding a new location to the catalog API. - -The location is now added in a transaction and afterwards rolled back. -This allows users to dry run this operation to see if there entity has issues. -This is probably done by automated tools in the CI/CD pipeline. diff --git a/.changeset/beige-apes-serve.md b/.changeset/beige-apes-serve.md deleted file mode 100644 index 54ee39a85e..0000000000 --- a/.changeset/beige-apes-serve.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'example-app': patch ---- - -Add Pull Request tab to components view. diff --git a/.changeset/beige-pandas-thank.md b/.changeset/beige-pandas-thank.md deleted file mode 100644 index b2c52842fc..0000000000 --- a/.changeset/beige-pandas-thank.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': patch ---- - -Add OneLogin Identity Provider to Auth Backend diff --git a/.changeset/blue-donkeys-exercise.md b/.changeset/blue-donkeys-exercise.md deleted file mode 100644 index 4f714e3c97..0000000000 --- a/.changeset/blue-donkeys-exercise.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog': minor ---- - -Add client side paging for catalog table diff --git a/.changeset/carpal-tunnel-driver.md b/.changeset/carpal-tunnel-driver.md deleted file mode 100644 index 2e6a814533..0000000000 --- a/.changeset/carpal-tunnel-driver.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -- The `CatalogProcessor` API was updated to have `preProcessEntity` and - `postProcessEntity` methods, instead of just one `processEntity`. This makes - it easier to make processors that have several stages in one, and to make - different processors more position independent in the list of processors. -- The `EntityPolicy` is now given directly to the `LocationReaders`, instead of - being enforced inside a policy. We have decided to separate out the act of - validating an entity to be outside of the processing flow, to make it - possible to apply more liberally and to evolve it as a separate concept. -- Because of the above, the `EntityPolicyProcessor` has been removed. diff --git a/.changeset/chilly-emus-fetch.md b/.changeset/chilly-emus-fetch.md deleted file mode 100644 index ad77c90f4e..0000000000 --- a/.changeset/chilly-emus-fetch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Added .fromConfig static factories for Preparers and Publishers + read integrations config to support url location types diff --git a/.changeset/clean-phones-vanish.md b/.changeset/clean-phones-vanish.md deleted file mode 100644 index 4c80b82f24..0000000000 --- a/.changeset/clean-phones-vanish.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-cost-insights': patch ---- - -export test utilities for mocking context diff --git a/.changeset/cli-parallel-build-options.md b/.changeset/cli-parallel-build-options.md deleted file mode 100644 index 95f9360738..0000000000 --- a/.changeset/cli-parallel-build-options.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@backstage/cli': minor ---- - -Adds a new `BACKSTAGE_CLI_BUILD_PARELLEL` environment variable to control -parallelism for some build steps. - -This is useful in CI to help avoid out of memory issues when using `terser`. The -`BACKSTAGE_CLI_BUILD_PARELLEL` environment variable can be set to -`true | false | [integer]` to override the default behaviour. See -[terser-webpack-plugin](https://github.com/webpack-contrib/terser-webpack-plugin#parallel) -for more details. diff --git a/.changeset/codeowner-processor-url-reader.md b/.changeset/codeowner-processor-url-reader.md deleted file mode 100644 index af4eb0997f..0000000000 --- a/.changeset/codeowner-processor-url-reader.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -Use the new `UrlReader` in the `CodeOwnersProcessor`. diff --git a/.changeset/cost-insights-kind-trees-occur.md b/.changeset/cost-insights-kind-trees-occur.md deleted file mode 100644 index 600cc3b3b4..0000000000 --- a/.changeset/cost-insights-kind-trees-occur.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-cost-insights': patch ---- - -migrate type utilities out of type definition files diff --git a/.changeset/cost-insights-rich-dodos-smell.md b/.changeset/cost-insights-rich-dodos-smell.md deleted file mode 100644 index a579797437..0000000000 --- a/.changeset/cost-insights-rich-dodos-smell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-cost-insights': patch ---- - -expose alerts utilities for export diff --git a/.changeset/cost-insights-sparkle.md b/.changeset/cost-insights-sparkle.md deleted file mode 100644 index c332502476..0000000000 --- a/.changeset/cost-insights-sparkle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-cost-insights': minor ---- - -Added getLastCompleteBillingDate to the CostInsightsApi to reason about completeness of billing data diff --git a/.changeset/cost-insights-swift-carrots-bake.md b/.changeset/cost-insights-swift-carrots-bake.md deleted file mode 100644 index bc378ad247..0000000000 --- a/.changeset/cost-insights-swift-carrots-bake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-cost-insights': patch ---- - -prefer named exports diff --git a/.changeset/create-app-url-reader-update.md b/.changeset/create-app-url-reader-update.md deleted file mode 100644 index eb2e538b77..0000000000 --- a/.changeset/create-app-url-reader-update.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'example-backend': patch -'@backstage/create-app': patch ---- - -Bump @backstage/catalog-backend and pass the now required UrlReader interface to the plugin diff --git a/.changeset/cyan-plants-dress.md b/.changeset/cyan-plants-dress.md deleted file mode 100644 index 19ebc63e53..0000000000 --- a/.changeset/cyan-plants-dress.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'example-backend': patch ---- - -Pass GitHub token into Scaffolder GitHub Preparer diff --git a/.changeset/dull-icons-share.md b/.changeset/dull-icons-share.md deleted file mode 100644 index aa4c01e422..0000000000 --- a/.changeset/dull-icons-share.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -'@backstage/backend-common': minor -'@backstage/cli': minor -'@backstage/config-loader': minor -'example-backend': patch -'@backstage/create-app': patch ---- - -**BREAKING CHANGE** - -The existing loading of additional config files like `app-config.development.yaml` using APP_ENV or NODE_ENV has been removed. -Instead, the CLI and backend process now accept one or more `--config` flags to load config files. - -Without passing any flags, `app-config.yaml` and, if it exists, `app-config.local.yaml` will be loaded. -If passing any `--config ` flags, only those files will be loaded, **NOT** the default `app-config.yaml` one. - -The old behaviour of for example `APP_ENV=development` can be replicated using the following flags: - -```bash ---config ../../app-config.yaml --config ../../app-config.development.yaml -``` diff --git a/.changeset/eighty-lamps-smell.md b/.changeset/eighty-lamps-smell.md deleted file mode 100644 index 85dfba4f82..0000000000 --- a/.changeset/eighty-lamps-smell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog': patch ---- - -handle the case where no entities are available to show diff --git a/.changeset/eighty-stingrays-type.md b/.changeset/eighty-stingrays-type.md deleted file mode 100644 index 731c401b77..0000000000 --- a/.changeset/eighty-stingrays-type.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': patch ---- - -Add forwardRef to the SidebarItem diff --git a/.changeset/flat-bugs-do.md b/.changeset/flat-bugs-do.md deleted file mode 100644 index 672daf657d..0000000000 --- a/.changeset/flat-bugs-do.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/core': patch -'@backstage/plugin-graphiql': patch -'@backstage/plugin-tech-radar': patch -'@backstage/plugin-techdocs': patch ---- - -add test case for Progress component diff --git a/.changeset/flat-yaks-march.md b/.changeset/flat-yaks-march.md deleted file mode 100644 index 6b26206eb8..0000000000 --- a/.changeset/flat-yaks-march.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -fix the accordion details design when job stage fail diff --git a/.changeset/fresh-maps-complain.md b/.changeset/fresh-maps-complain.md deleted file mode 100644 index ef2021df37..0000000000 --- a/.changeset/fresh-maps-complain.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-catalog': minor -'@backstage/plugin-register-component': patch ---- - -Locations registered through the catalog client now default to the 'url' type. The type selection dropdown in the register-component form has been removed. diff --git a/.changeset/funny-readers-breathe.md b/.changeset/funny-readers-breathe.md deleted file mode 100644 index 8897b24cf6..0000000000 --- a/.changeset/funny-readers-breathe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': patch ---- - -fix the warning of all the core components test cases diff --git a/.changeset/fuzzy-falcons-happen.md b/.changeset/fuzzy-falcons-happen.md deleted file mode 100644 index 461ac195d9..0000000000 --- a/.changeset/fuzzy-falcons-happen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'e2e-test': minor ---- - -Converted into a CLI, use `yarn e2e-test run` to run diff --git a/.changeset/good-islands-cheer.md b/.changeset/good-islands-cheer.md deleted file mode 100644 index 2e7109b312..0000000000 --- a/.changeset/good-islands-cheer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-cost-insights': minor ---- - -Enable custom alert types in Cost Insights diff --git a/.changeset/great-apples-flash.md b/.changeset/great-apples-flash.md deleted file mode 100644 index 78ad89dee0..0000000000 --- a/.changeset/great-apples-flash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Fix CodeOwnersProcessor to handle non team users diff --git a/.changeset/happy-ads-behave.md b/.changeset/happy-ads-behave.md deleted file mode 100644 index f8b889f85c..0000000000 --- a/.changeset/happy-ads-behave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Allow templates to be located on non-default branch diff --git a/.changeset/heavy-berries-hear.md b/.changeset/heavy-berries-hear.md deleted file mode 100644 index 66aa6d93e1..0000000000 --- a/.changeset/heavy-berries-hear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': patch ---- - -Update ItemCard headers to pass color contrast standards. diff --git a/.changeset/hungry-parrots-work.md b/.changeset/hungry-parrots-work.md deleted file mode 100644 index a18218b542..0000000000 --- a/.changeset/hungry-parrots-work.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': patch ---- - -rename stories folder top Chip diff --git a/.changeset/little-wasps-pull.md b/.changeset/little-wasps-pull.md deleted file mode 100644 index 732c7506b0..0000000000 --- a/.changeset/little-wasps-pull.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog': patch ---- - -Remove "in default" in component name diff --git a/.changeset/long-ways-count.md b/.changeset/long-ways-count.md deleted file mode 100644 index e04c1225cc..0000000000 --- a/.changeset/long-ways-count.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -'@backstage/plugin-techdocs-backend': minor -'@backstage/create-app': patch ---- - -Updated naming of environment variables. New pattern [NAME]\_TOKEN for Github, Gitlab, Azure & Github enterprise access tokens. - -### Detail: - -- Previously we have to export same token for both, catalog & scaffolder - -```bash -export GITHUB_ACCESS_TOKEN=foo -export GITHUB_PRIVATE_TOKEN=foo -``` - -with latest changes, only single export is sufficient. - -```bash -export GITHUB_TOKEN=foo -export GITLAB_TOKEN=foo -export GHE_TOKEN=foo -export AZURE_TOKEN=foo -``` - -### list: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Old nameNew name
GITHUB_ACCESS_TOKENGITHUB_TOKEN
GITHUB_PRIVATE_TOKENGITHUB_TOKEN
GITLAB_ACCESS_TOKENGITLAB_TOKEN
GITLAB_PRIVATE_TOKENGITLAB_TOKEN
AZURE_PRIVATE_TOKENAZURE_TOKEN
GHE_PRIVATE_TOKENGHE_TOKEN
diff --git a/.changeset/loud-lamps-visit.md b/.changeset/loud-lamps-visit.md deleted file mode 100644 index 9023061643..0000000000 --- a/.changeset/loud-lamps-visit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Including source maps with all packages diff --git a/.changeset/lovely-suits-flash.md b/.changeset/lovely-suits-flash.md deleted file mode 100644 index f093290c85..0000000000 --- a/.changeset/lovely-suits-flash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': patch ---- - -update the test cases of CodeSnippet component diff --git a/.changeset/metal-fishes-learn.md b/.changeset/metal-fishes-learn.md deleted file mode 100644 index d22af83ca0..0000000000 --- a/.changeset/metal-fishes-learn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/create-app': minor ---- - -The default mount point for backend plugins have been changed to /api. These changes are done in the backend package itself, so it is recommended that you sync up existing backend packages with this new pattern. diff --git a/.changeset/mighty-starfishes-taste.md b/.changeset/mighty-starfishes-taste.md deleted file mode 100644 index eb43ce69b8..0000000000 --- a/.changeset/mighty-starfishes-taste.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -Removed the parseData step from catalog processors. Locations readers should emit full entities instead. diff --git a/.changeset/nervous-drinks-notice.md b/.changeset/nervous-drinks-notice.md deleted file mode 100644 index 7c6af21f64..0000000000 --- a/.changeset/nervous-drinks-notice.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/plugin-catalog': minor -'@backstage/plugin-register-component': minor ---- - -Add a validate button to the register-component page - -This allows the user to validate his location before adding it. diff --git a/.changeset/new-url-reader.md b/.changeset/new-url-reader.md deleted file mode 100644 index 94758c5bc1..0000000000 --- a/.changeset/new-url-reader.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Added new UrlReader interface for reading opaque data from URLs with different providers. - -This new URL reading system is intended as a replacement for the various integrations towards -external systems in the catalog, scaffolder, and techdocs. It is configured via a new top-level -config section called 'integrations'. - -Along with the UrlReader interface is a new UrlReaders class, which exposes static factory -methods for instantiating readers that can read from many different integrations simultaneously. diff --git a/.changeset/nice-candles-argue.md b/.changeset/nice-candles-argue.md deleted file mode 100644 index 971000410a..0000000000 --- a/.changeset/nice-candles-argue.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-user-settings': minor ---- - -Add settings button to sidebar diff --git a/.changeset/ninety-ads-dance.md b/.changeset/ninety-ads-dance.md deleted file mode 100644 index a03298fd7f..0000000000 --- a/.changeset/ninety-ads-dance.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/catalog-model': patch ---- - -Fix documentation and validation message for tags diff --git a/.changeset/ninety-pens-poke.md b/.changeset/ninety-pens-poke.md deleted file mode 100644 index 94bcb06749..0000000000 --- a/.changeset/ninety-pens-poke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Fix for configured templates using 'url' locations even though it's not supported yet diff --git a/.changeset/old-eagles-admire.md b/.changeset/old-eagles-admire.md deleted file mode 100644 index 0729b6439d..0000000000 --- a/.changeset/old-eagles-admire.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/backend-common': minor -'@backstage/cli': minor ---- - -Use APP_ENV before NODE_ENV for determining what config to load diff --git a/.changeset/old-falcons-jump.md b/.changeset/old-falcons-jump.md deleted file mode 100644 index 107d75ff88..0000000000 --- a/.changeset/old-falcons-jump.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -'@backstage/plugin-newrelic': minor ---- - -The New Relic plugin now uses the Backstage proxy to communicate with New Relic's API. - -Please update your `app-config.yaml` as follows: - -```yaml -# Old Config -newrelic: - api: - baseUrl: 'https://api.newrelic.com/v2' - key: NEW_RELIC_REST_API_KEY -``` - -```yaml -# New Config -proxy: - '/newrelic/apm/api': - target: https://api.newrelic.com/v2 - headers: - X-Api-Key: - $env: NEW_RELIC_REST_API_KEY -``` diff --git a/.changeset/old-jeans-dance.md b/.changeset/old-jeans-dance.md deleted file mode 100644 index 1bd4a3136a..0000000000 --- a/.changeset/old-jeans-dance.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -The CodeOwnersProcessor now handles 'url' locations diff --git a/.changeset/olive-bikes-jog.md b/.changeset/olive-bikes-jog.md deleted file mode 100644 index fdbf1e0d9f..0000000000 --- a/.changeset/olive-bikes-jog.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': patch ---- - -add test cases for Status components diff --git a/.changeset/pink-spies-rule.md b/.changeset/pink-spies-rule.md deleted file mode 100644 index 71653a1841..0000000000 --- a/.changeset/pink-spies-rule.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs-backend': patch ---- - -Replacing the hard coded `baseApiUrl` by reading the value from configuration to enable private GitHub setup for TechDocs. diff --git a/.changeset/polite-laws-think.md b/.changeset/polite-laws-think.md deleted file mode 100644 index a5eb7f9883..0000000000 --- a/.changeset/polite-laws-think.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -Removed support for deprecated `catalog.providers` config that have been moved to `integrations` diff --git a/.changeset/polite-moose-cough.md b/.changeset/polite-moose-cough.md deleted file mode 100644 index b66227bb24..0000000000 --- a/.changeset/polite-moose-cough.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': patch ---- - -update ItemCard component and it's story diff --git a/.changeset/popular-ghosts-remain.md b/.changeset/popular-ghosts-remain.md deleted file mode 100644 index 4a170c7618..0000000000 --- a/.changeset/popular-ghosts-remain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': patch ---- - -Proper render boolean values on StructuredMetadataTable component diff --git a/.changeset/popular-jars-serve.md b/.changeset/popular-jars-serve.md deleted file mode 100644 index 6ee40195fc..0000000000 --- a/.changeset/popular-jars-serve.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/catalog-model': minor ---- - -Enable adding locations for config files that does not yet exist by adding a flag to api request diff --git a/.changeset/pretty-cups-joke.md b/.changeset/pretty-cups-joke.md deleted file mode 100644 index 49a1db2a75..0000000000 --- a/.changeset/pretty-cups-joke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Add authentication token to Scaffolder GitHub Preparer diff --git a/.changeset/proud-eagles-grow.md b/.changeset/proud-eagles-grow.md deleted file mode 100644 index ef83a4ad26..0000000000 --- a/.changeset/proud-eagles-grow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Fixed duplicate help output, and print help on invalid command diff --git a/.changeset/raghu-is-great.md b/.changeset/raghu-is-great.md deleted file mode 100644 index 805e49f3e8..0000000000 --- a/.changeset/raghu-is-great.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -Renamed the `LocationProcessor` class to `CatalogProcessor`. - -Likewise, renamed `LocationProcessorResult`, `LocationProcessorLocationResult`, -`LocationProcessorDataResult`, `LocationProcessorEntityResult`, -`LocationProcessorErrorResult`, and `LocationProcessorEmit` to their `Catalog*` -counterparts. diff --git a/.changeset/red-games-float.md b/.changeset/red-games-float.md deleted file mode 100644 index 6eabba26a4..0000000000 --- a/.changeset/red-games-float.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@backstage/plugin-cost-insights': minor ---- - -- getProjectDailyCost and getGroupDailyCost no longer accept a metric as a parameter -- getDailyMetricData added to API for fetching daily metric data for given interval -- dailyCost removed as configurable metric -- default field added to metric configuration for displaying comparison metric data in top panel -- Metric.kind can no longer be null -- MetricData type added diff --git a/.changeset/remove-location-processor-read.md b/.changeset/remove-location-processor-read.md deleted file mode 100644 index 78aea6d820..0000000000 --- a/.changeset/remove-location-processor-read.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -Remove the `read` argument of `LocationProcessor.processEntity`. -Instead, pass the `UrlReader` into the constructor of your `LocationProcessor`. diff --git a/.changeset/rocket-lizards-congregate.md b/.changeset/rocket-lizards-congregate.md deleted file mode 100644 index f4cd50aa36..0000000000 --- a/.changeset/rocket-lizards-congregate.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@backstage/plugin-catalog': minor ---- - -The URL path for a catalog entity has changed, - -- from: `/catalog/:kind/:optionalNamespaceAndName` -- to: `/catalog/:namespace/:kind/:name` - -Redirects are in place, so disruptions for users should not happen. diff --git a/.changeset/selfish-bats-perform.md b/.changeset/selfish-bats-perform.md deleted file mode 100644 index e4d05c088c..0000000000 --- a/.changeset/selfish-bats-perform.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch -'@backstage/plugin-scaffolder-backend': patch ---- - -The new `scaffolder.github.baseUrl` config property allows to specify a custom base url for GitHub enterprise instances diff --git a/.changeset/selfish-mugs-itch.md b/.changeset/selfish-mugs-itch.md deleted file mode 100644 index 39ed236352..0000000000 --- a/.changeset/selfish-mugs-itch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-cost-insights': minor ---- - -Remove product filters from query parameters diff --git a/.changeset/seven-humans-check.md b/.changeset/seven-humans-check.md deleted file mode 100644 index f2fb784cc0..0000000000 --- a/.changeset/seven-humans-check.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'example-app': patch -'@backstage/core-api': patch -'@backstage/plugin-cost-insights': patch ---- - -Remove cost insights example client from demo app and export from plugin -Create cost insights dev plugin using example client -Make PluginConfig and dependent types public diff --git a/.changeset/short-secrets.md b/.changeset/short-secrets.md deleted file mode 100644 index 72cb059979..0000000000 --- a/.changeset/short-secrets.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/config-loader': minor ---- - -Added support for new shorthand when defining secrets, where `$env: ENV` can be used instead of `$secret: { env: ENV }` etc. diff --git a/.changeset/slimy-garlics-eat.md b/.changeset/slimy-garlics-eat.md deleted file mode 100644 index 3ecc0ce495..0000000000 --- a/.changeset/slimy-garlics-eat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog': patch ---- - -update the EntityNotFound component diff --git a/.changeset/slow-experts-peel.md b/.changeset/slow-experts-peel.md deleted file mode 100644 index ed9c7af21e..0000000000 --- a/.changeset/slow-experts-peel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-tech-radar': patch ---- - -fix the horizontal scrolling issue in the RadarPage component diff --git a/.changeset/spicy-moles-yell.md b/.changeset/spicy-moles-yell.md deleted file mode 100644 index 72b648b30b..0000000000 --- a/.changeset/spicy-moles-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': patch ---- - -make ErrorPage responsive + fix the test case diff --git a/.changeset/spicy-rockets-ring.md b/.changeset/spicy-rockets-ring.md deleted file mode 100644 index b39b433c1b..0000000000 --- a/.changeset/spicy-rockets-ring.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Added support for configuring the working directory of the Scaffolder: - -```yaml -backend: - workingDirectory: /some-dir # Use this to configure a working directory for the scaffolder, defaults to the OS temp-dir -``` diff --git a/.changeset/spotty-apples-visit.md b/.changeset/spotty-apples-visit.md deleted file mode 100644 index ba481fa052..0000000000 --- a/.changeset/spotty-apples-visit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': patch ---- - -Better presentation of authentication errors diff --git a/.changeset/sweet-bikes-battle.md b/.changeset/sweet-bikes-battle.md deleted file mode 100644 index 68ed8caeeb..0000000000 --- a/.changeset/sweet-bikes-battle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'example-app': patch ---- - -cleaning up because external plugins have already implemented new api for creating diff --git a/.changeset/swift-emus-mate.md b/.changeset/swift-emus-mate.md deleted file mode 100644 index 1903742115..0000000000 --- a/.changeset/swift-emus-mate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-api': minor ---- - -Refactored the FeatureFlagsApi to make it easier to re-implement. Existing usage of particularly getUserFlags can be replaced with isActive() or save(). diff --git a/.changeset/swift-peas-argue.md b/.changeset/swift-peas-argue.md deleted file mode 100644 index d78d844d45..0000000000 --- a/.changeset/swift-peas-argue.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': patch ---- - -give aria-label attribute to Status Ok, Warning and Error diff --git a/.changeset/ten-bees-wash.md b/.changeset/ten-bees-wash.md deleted file mode 100644 index 179dbf87f4..0000000000 --- a/.changeset/ten-bees-wash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs': patch ---- - -While techdocs fetches site name and metadata for the component, the page title was displayed as '[object Object] | Backstage'. This has now been fixed to display the component ID if site name is not present or being fetched. diff --git a/.changeset/three-horses-juggle.md b/.changeset/three-horses-juggle.md deleted file mode 100644 index bebcf77e43..0000000000 --- a/.changeset/three-horses-juggle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Introduce PreparerOptions for PreparerBase diff --git a/.changeset/tough-weeks-pull.md b/.changeset/tough-weeks-pull.md deleted file mode 100644 index e79355de90..0000000000 --- a/.changeset/tough-weeks-pull.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': patch ---- - -fix the responsive of page story diff --git a/.changeset/url-reader-processor.md b/.changeset/url-reader-processor.md deleted file mode 100644 index 40fef3c106..0000000000 --- a/.changeset/url-reader-processor.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -The catalog backend UrlReaderProcessor now uses a UrlReader from @backstage/backend-common, which must now be supplied to the constructor. diff --git a/.changeset/weak-ducks-tan.md b/.changeset/weak-ducks-tan.md deleted file mode 100644 index 64b1c3f117..0000000000 --- a/.changeset/weak-ducks-tan.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/backend-common': minor -'example-backend': patch -'@backstage/cli': patch -'@backstage/create-app': patch ---- - -Change loadBackendConfig to return the config directly diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md new file mode 100644 index 0000000000..458f5a5e5c --- /dev/null +++ b/packages/app/CHANGELOG.md @@ -0,0 +1,121 @@ +# example-app + +## 0.2.0 + +### Minor Changes + +- 6d97d2d6f: The InfoCard variant `'height100'` is deprecated. Use variant `'gridItem'` instead. + + When the InfoCard is displayed as a grid item within a grid, you may want items to have the same height for all items. + Set to the `'gridItem'` variant to display the InfoCard with full height suitable for Grid: + `...` + + Changed the InfoCards in '@backstage/plugin-github-actions', '@backstage/plugin-jenkins', '@backstage/plugin-lighthouse' + to pass an optional variant to the corresponding card of the plugin. + + As a result the overview content of the EntityPage shows cards with full height suitable for Grid. + +### Patch Changes + +- 65d722455: Add Pull Request tab to components view. +- 26e69ab1a: Remove cost insights example client from demo app and export from plugin + Create cost insights dev plugin using example client + Make PluginConfig and dependent types public +- e7f5471fd: cleaning up because external plugins have already implemented new api for creating +- Updated dependencies [28edd7d29] +- Updated dependencies [819a70229] +- Updated dependencies [2846ef95c] +- Updated dependencies [3a4236570] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [d67c529ab] +- Updated dependencies [482b6313d] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [8351ad79b] +- Updated dependencies [30dd11122] +- Updated dependencies [1297dcb3a] +- Updated dependencies [368fd8243] +- Updated dependencies [fb74f1db6] +- Updated dependencies [3472c8be7] +- Updated dependencies [cab473771] +- Updated dependencies [1d0aec70f] +- Updated dependencies [1c60f716e] +- Updated dependencies [a73979d45] +- Updated dependencies [144c66d50] +- Updated dependencies [a768a07fb] +- Updated dependencies [a3840bed2] +- Updated dependencies [339668995] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [5adfc005e] +- Updated dependencies [f0aa01bcc] +- Updated dependencies [8d1360aa9] +- Updated dependencies [72f6cda35] +- Updated dependencies [0ee9e9f66] +- Updated dependencies [5c70f3d35] +- Updated dependencies [bb48b9833] +- Updated dependencies [fd8384d7e] +- Updated dependencies [8c2b76e45] +- Updated dependencies [0aecfded0] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [c5ef12926] +- Updated dependencies [8b9c8196f] +- Updated dependencies [2713f28f4] +- Updated dependencies [6a84cb072] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [60d40892c] +- Updated dependencies [cba4e4d97] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [2ebcfac8d] +- Updated dependencies [4fc1d440e] +- Updated dependencies [fa56f4615] +- Updated dependencies [8afce088a] +- Updated dependencies [4512b9967] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [b3d57961c] +- Updated dependencies [9a3b3dbf1] +- Updated dependencies [e7d4ac7ce] +- Updated dependencies [0b956f21b] +- Updated dependencies [1c8c43756] +- Updated dependencies [0e67c6b40] +- Updated dependencies [26e69ab1a] +- Updated dependencies [97c2cb19b] +- Updated dependencies [02c60b5f8] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [57b54c8ed] +- Updated dependencies [1611c6dbc] +- Updated dependencies [7bbeb049f] + - @backstage/cli@0.2.0 + - @backstage/plugin-api-docs@0.2.0 + - @backstage/plugin-catalog@0.2.0 + - @backstage/plugin-circleci@0.2.0 + - @backstage/plugin-explore@0.2.0 + - @backstage/plugin-gcp-projects@0.2.0 + - @backstage/plugin-github-actions@0.2.0 + - @backstage/plugin-gitops-profiles@0.2.0 + - @backstage/plugin-graphiql@0.2.0 + - @backstage/plugin-jenkins@0.2.0 + - @backstage/plugin-kubernetes@0.2.0 + - @backstage/plugin-lighthouse@0.2.0 + - @backstage/plugin-newrelic@0.2.0 + - @backstage/plugin-register-component@0.2.0 + - @backstage/plugin-rollbar@0.2.0 + - @backstage/plugin-scaffolder@0.2.0 + - @backstage/plugin-sentry@0.2.0 + - @backstage/plugin-tech-radar@0.2.0 + - @backstage/plugin-techdocs@0.2.0 + - @backstage/plugin-welcome@0.2.0 + - @backstage/core@0.2.0 + - @backstage/plugin-cloudbuild@0.2.0 + - @backstage/catalog-model@0.2.0 + - @backstage/theme@0.2.0 + - @backstage/plugin-cost-insights@0.2.0 + - @backstage/plugin-user-settings@0.2.0 + - @backstage/test-utils@0.1.2 diff --git a/packages/app/package.json b/packages/app/package.json index 2e7b15cf23..bd497857ab 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,36 +1,36 @@ { "name": "example-app", - "version": "0.1.1", + "version": "0.2.0", "private": true, "bundled": true, "dependencies": { - "@backstage/catalog-model": "^0.1.1", - "@backstage/cli": "^0.1.1", - "@backstage/core": "^0.1.1", - "@backstage/plugin-api-docs": "^0.1.1", - "@backstage/plugin-catalog": "^0.1.1", - "@backstage/plugin-circleci": "^0.1.1", - "@backstage/plugin-cloudbuild": "^0.1.1", - "@backstage/plugin-cost-insights": "^0.1.1", - "@backstage/plugin-explore": "^0.1.1", - "@backstage/plugin-gcp-projects": "^0.1.1", - "@backstage/plugin-github-actions": "^0.1.1", - "@backstage/plugin-gitops-profiles": "^0.1.1", - "@backstage/plugin-graphiql": "^0.1.1", - "@backstage/plugin-jenkins": "^0.1.1", - "@backstage/plugin-kubernetes": "^0.1.1", - "@backstage/plugin-lighthouse": "^0.1.1", - "@backstage/plugin-newrelic": "^0.1.1", - "@backstage/plugin-register-component": "^0.1.1", - "@backstage/plugin-rollbar": "^0.1.1", - "@backstage/plugin-scaffolder": "^0.1.1", - "@backstage/plugin-sentry": "^0.1.1", - "@backstage/plugin-tech-radar": "^0.1.1", - "@backstage/plugin-techdocs": "^0.1.1", - "@backstage/plugin-user-settings": "^0.1.1", - "@backstage/plugin-welcome": "^0.1.1", - "@backstage/test-utils": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/catalog-model": "^0.2.0", + "@backstage/cli": "^0.2.0", + "@backstage/core": "^0.2.0", + "@backstage/plugin-api-docs": "^0.2.0", + "@backstage/plugin-catalog": "^0.2.0", + "@backstage/plugin-circleci": "^0.2.0", + "@backstage/plugin-cloudbuild": "^0.2.0", + "@backstage/plugin-cost-insights": "^0.2.0", + "@backstage/plugin-explore": "^0.2.0", + "@backstage/plugin-gcp-projects": "^0.2.0", + "@backstage/plugin-github-actions": "^0.2.0", + "@backstage/plugin-gitops-profiles": "^0.2.0", + "@backstage/plugin-graphiql": "^0.2.0", + "@backstage/plugin-jenkins": "^0.2.0", + "@backstage/plugin-kubernetes": "^0.2.0", + "@backstage/plugin-lighthouse": "^0.2.0", + "@backstage/plugin-newrelic": "^0.2.0", + "@backstage/plugin-register-component": "^0.2.0", + "@backstage/plugin-rollbar": "^0.2.0", + "@backstage/plugin-scaffolder": "^0.2.0", + "@backstage/plugin-sentry": "^0.2.0", + "@backstage/plugin-tech-radar": "^0.2.0", + "@backstage/plugin-techdocs": "^0.2.0", + "@backstage/plugin-user-settings": "^0.2.0", + "@backstage/plugin-welcome": "^0.2.0", + "@backstage/test-utils": "^0.1.2", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@octokit/rest": "^18.0.0", diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md new file mode 100644 index 0000000000..2b8b38b9b0 --- /dev/null +++ b/packages/backend-common/CHANGELOG.md @@ -0,0 +1,64 @@ +# @backstage/backend-common + +## 0.2.0 + +### Minor Changes + +- 5249594c5: Add service discovery interface and implement for single host deployments + + Fixes #1847, #2596 + + Went with an interface similar to the frontend DiscoveryApi, since it's dead simple but still provides a lot of flexibility in the implementation. + + Also ended up with two different methods, one for internal endpoint discovery and one for external. The two use-cases are explained a bit more in the docs, but basically it's service-to-service vs callback URLs. + + This did get me thinking about uniqueness and that we're heading towards a global namespace for backend plugin IDs. That's probably fine, but if we're happy with that we should leverage it a bit more to simplify the backend setup. For example we'd have each plugin provide its own ID and not manually mount on paths in the backend. + + Draft until we're happy with the implementation, then I can add more docs and changelog entry. Also didn't go on a thorough hunt for places where discovery can be used, but I don't think there are many since it's been pretty awkward to do service-to-service communication. + +- 56e4eb589: Make CSP configurable to fix app-backend served app not being able to fetch + + See discussion [here on discord](https://discordapp.com/channels/687207715902193673/687235481154617364/758721460163575850) + +- e37c0a005: Use localhost to fall back to IPv4 if IPv6 isn't available +- f00ca3cb8: Auto-create plugin databases + + Relates to #1598. + + This creates databases for plugins before handing off control to plugins. + + The list of plugins currently need to be hard-coded depending on the installed plugins. A later PR will properly refactor the code to provide a factory pattern where plugins specify what they need, and Knex instances will be provided based on the input. + +- 6579769df: Add the ability to import components from Bitbucket Server to the service catalog +- 8c2b76e45: **BREAKING CHANGE** + + The existing loading of additional config files like `app-config.development.yaml` using APP_ENV or NODE_ENV has been removed. + Instead, the CLI and backend process now accept one or more `--config` flags to load config files. + + Without passing any flags, `app-config.yaml` and, if it exists, `app-config.local.yaml` will be loaded. + If passing any `--config ` flags, only those files will be loaded, **NOT** the default `app-config.yaml` one. + + The old behaviour of for example `APP_ENV=development` can be replicated using the following flags: + + ```bash + --config ../../app-config.yaml --config ../../app-config.development.yaml + ``` + +- 8afce088a: Use APP_ENV before NODE_ENV for determining what config to load +- 7bbeb049f: Change loadBackendConfig to return the config directly + +### Patch Changes + +- 440a17b39: Added new UrlReader interface for reading opaque data from URLs with different providers. + + This new URL reading system is intended as a replacement for the various integrations towards + external systems in the catalog, scaffolder, and techdocs. It is configured via a new top-level + config section called 'integrations'. + + Along with the UrlReader interface is a new UrlReaders class, which exposes static factory + methods for instantiating readers that can read from many different integrations simultaneously. + +- Updated dependencies [8c2b76e45] +- Updated dependencies [ce5512bc0] + - @backstage/config-loader@0.2.0 + - @backstage/test-utils@0.1.2 diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index fb213a476d..c004eaac28 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.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -31,8 +31,8 @@ "dependencies": { "@backstage/cli-common": "^0.1.1", "@backstage/config": "^0.1.1", - "@backstage/config-loader": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/config-loader": "^0.2.0", + "@backstage/test-utils": "^0.1.2", "@types/cors": "^2.8.6", "@types/express": "^4.17.6", "compression": "^1.7.4", @@ -62,7 +62,7 @@ } }, "devDependencies": { - "@backstage/cli": "^0.1.1", + "@backstage/cli": "^0.2.0", "@types/compression": "^1.7.0", "@types/http-errors": "^1.6.3", "@types/minimist": "^1.2.0", diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md new file mode 100644 index 0000000000..a3e5535e40 --- /dev/null +++ b/packages/backend/CHANGELOG.md @@ -0,0 +1,97 @@ +# example-backend + +## 0.2.0 + +### Patch Changes + +- 440a17b39: Bump @backstage/catalog-backend and pass the now required UrlReader interface to the plugin +- 6840a68df: Pass GitHub token into Scaffolder GitHub Preparer +- 8c2b76e45: **BREAKING CHANGE** + + The existing loading of additional config files like `app-config.development.yaml` using APP_ENV or NODE_ENV has been removed. + Instead, the CLI and backend process now accept one or more `--config` flags to load config files. + + Without passing any flags, `app-config.yaml` and, if it exists, `app-config.local.yaml` will be loaded. + If passing any `--config ` flags, only those files will be loaded, **NOT** the default `app-config.yaml` one. + + The old behaviour of for example `APP_ENV=development` can be replicated using the following flags: + + ```bash + --config ../../app-config.yaml --config ../../app-config.development.yaml + ``` + +- 7bbeb049f: Change loadBackendConfig to return the config directly +- Updated dependencies [28edd7d29] +- Updated dependencies [819a70229] +- Updated dependencies [3a4236570] +- Updated dependencies [3e254503d] +- Updated dependencies [6d29605db] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [5249594c5] +- Updated dependencies [56e4eb589] +- Updated dependencies [b4e5466e1] +- Updated dependencies [6f1768c0f] +- Updated dependencies [e37c0a005] +- Updated dependencies [3472c8be7] +- Updated dependencies [57d555eb2] +- Updated dependencies [61db1ddc6] +- Updated dependencies [81cb94379] +- Updated dependencies [1687b8fbb] +- Updated dependencies [a768a07fb] +- Updated dependencies [a768a07fb] +- Updated dependencies [f00ca3cb8] +- Updated dependencies [0c370c979] +- Updated dependencies [ce1f55398] +- Updated dependencies [e6b00e3af] +- Updated dependencies [9226c2aaa] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [99710b102] +- Updated dependencies [6579769df] +- Updated dependencies [002860e7a] +- Updated dependencies [5adfc005e] +- Updated dependencies [33454c0f2] +- Updated dependencies [183e2a30d] +- Updated dependencies [948052cbb] +- Updated dependencies [65d722455] +- Updated dependencies [b652bf2cc] +- Updated dependencies [4036ff59d] +- Updated dependencies [991a950e0] +- Updated dependencies [512d70973] +- Updated dependencies [8c2b76e45] +- Updated dependencies [8bdf0bcf5] +- Updated dependencies [c926765a2] +- Updated dependencies [5a920c6e4] +- Updated dependencies [2f62e1804] +- Updated dependencies [440a17b39] +- Updated dependencies [fa56f4615] +- Updated dependencies [8afce088a] +- Updated dependencies [4c4eab81b] +- Updated dependencies [22ff8fba5] +- Updated dependencies [36a71d278] +- Updated dependencies [b3d57961c] +- Updated dependencies [6840a68df] +- Updated dependencies [a5cb46bac] +- Updated dependencies [49d70ccab] +- Updated dependencies [1c8c43756] +- Updated dependencies [26e69ab1a] +- Updated dependencies [5e4551e3a] +- Updated dependencies [e142a2767] +- Updated dependencies [e7f5471fd] +- Updated dependencies [e3d063ffa] +- Updated dependencies [440a17b39] +- Updated dependencies [7bbeb049f] + - @backstage/plugin-app-backend@0.2.0 + - @backstage/plugin-auth-backend@0.2.0 + - @backstage/catalog-model@0.2.0 + - @backstage/plugin-scaffolder-backend@0.2.0 + - @backstage/plugin-techdocs-backend@0.2.0 + - @backstage/plugin-catalog-backend@0.2.0 + - @backstage/plugin-proxy-backend@0.2.0 + - @backstage/backend-common@0.2.0 + - example-app@0.2.0 + - @backstage/plugin-graphql-backend@0.1.2 + - @backstage/plugin-kubernetes-backend@0.1.2 + - @backstage/plugin-rollbar-backend@0.1.2 + - @backstage/plugin-sentry-backend@0.1.2 diff --git a/packages/backend/package.json b/packages/backend/package.json index cdc938331a..67b2a42fee 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.1.1", + "version": "0.2.0", "main": "dist/index.cjs.js", "types": "src/index.ts", "private": true, @@ -18,24 +18,24 @@ "migrate:create": "knex migrate:make -x ts" }, "dependencies": { - "@backstage/backend-common": "^0.1.1", - "@backstage/catalog-model": "^0.1.1", + "@backstage/backend-common": "^0.2.0", + "@backstage/catalog-model": "^0.2.0", "@backstage/config": "^0.1.1", - "@backstage/plugin-app-backend": "^0.1.1", - "@backstage/plugin-auth-backend": "^0.1.1", - "@backstage/plugin-catalog-backend": "^0.1.1", - "@backstage/plugin-graphql-backend": "^0.1.1", - "@backstage/plugin-kubernetes-backend": "^0.1.1", - "@backstage/plugin-proxy-backend": "^0.1.1", - "@backstage/plugin-rollbar-backend": "^0.1.1", - "@backstage/plugin-scaffolder-backend": "^0.1.1", - "@backstage/plugin-sentry-backend": "^0.1.1", - "@backstage/plugin-techdocs-backend": "^0.1.1", + "@backstage/plugin-app-backend": "^0.2.0", + "@backstage/plugin-auth-backend": "^0.2.0", + "@backstage/plugin-catalog-backend": "^0.2.0", + "@backstage/plugin-graphql-backend": "^0.1.2", + "@backstage/plugin-kubernetes-backend": "^0.1.2", + "@backstage/plugin-proxy-backend": "^0.2.0", + "@backstage/plugin-rollbar-backend": "^0.1.2", + "@backstage/plugin-scaffolder-backend": "^0.2.0", + "@backstage/plugin-sentry-backend": "^0.1.2", + "@backstage/plugin-techdocs-backend": "^0.2.0", "@gitbeaker/node": "^23.5.0", "@octokit/rest": "^18.0.0", "azure-devops-node-api": "^10.1.1", "dockerode": "^3.2.0", - "example-app": "^0.1.1", + "example-app": "^0.2.0", "express": "^4.17.1", "express-promise-router": "^3.0.3", "knex": "^0.21.6", @@ -45,7 +45,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1", + "@backstage/cli": "^0.2.0", "@types/dockerode": "^2.5.32", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5", diff --git a/packages/catalog-model/CHANGELOG.md b/packages/catalog-model/CHANGELOG.md new file mode 100644 index 0000000000..051aa82c80 --- /dev/null +++ b/packages/catalog-model/CHANGELOG.md @@ -0,0 +1,28 @@ +# @backstage/catalog-model + +## 0.2.0 + +### Minor Changes + +- 3a4236570: Add handling and docs for entity references +- e0be86b6f: Entirely case insensitive read path of entities +- f70a52868: Add the User & Group entities + + A user describes a person, such as an employee, a contractor, or similar. Users belong to Group entities in the catalog. + + A group describes an organizational entity, such as for example a team, a business unit, or a loose collection of people in an interest group. Members of these groups are modeled in the catalog as kind User. + +- 12b5fe940: Add ApiDefinitionAtLocationProcessor that allows to load a API definition from another location +- a768a07fb: Add the ability to import users from GitHub Organization into the catalog. +- 5adfc005e: Changes the various kind policies into a new type `KindValidator`. + + Adds `CatalogProcessor#validateEntityKind` that makes use of the above + validators. This moves entity schema validity checking away from entity + policies and into processors, centralizing the extension points into the + processor chain. + +- b3d57961c: Enable adding locations for config files that does not yet exist by adding a flag to api request + +### Patch Changes + +- fa56f4615: Fix documentation and validation message for tags diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 0d555ef5d2..47d047fe9a 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-model", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,7 +29,7 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", + "@backstage/cli": "^0.2.0", "@types/express": "^4.17.6", "@types/jest": "^26.0.7", "@types/lodash": "^4.14.151", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md new file mode 100644 index 0000000000..387f5e416f --- /dev/null +++ b/packages/cli/CHANGELOG.md @@ -0,0 +1,46 @@ +# @backstage/cli + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI +- 1d0aec70f: Upgrade dependency `esbuild@0.7.7` +- 72f6cda35: Adds a new `BACKSTAGE_CLI_BUILD_PARELLEL` environment variable to control + parallelism for some build steps. + + This is useful in CI to help avoid out of memory issues when using `terser`. The + `BACKSTAGE_CLI_BUILD_PARELLEL` environment variable can be set to + `true | false | [integer]` to override the default behaviour. See + [terser-webpack-plugin](https://github.com/webpack-contrib/terser-webpack-plugin#parallel) + for more details. + +- 8c2b76e45: **BREAKING CHANGE** + + The existing loading of additional config files like `app-config.development.yaml` using APP_ENV or NODE_ENV has been removed. + Instead, the CLI and backend process now accept one or more `--config` flags to load config files. + + Without passing any flags, `app-config.yaml` and, if it exists, `app-config.local.yaml` will be loaded. + If passing any `--config ` flags, only those files will be loaded, **NOT** the default `app-config.yaml` one. + + The old behaviour of for example `APP_ENV=development` can be replicated using the following flags: + + ```bash + --config ../../app-config.yaml --config ../../app-config.development.yaml + ``` + +- 8afce088a: Use APP_ENV before NODE_ENV for determining what config to load + +### Patch Changes + +- 3472c8be7: Add codeowners processor + + - Include ESNext.Promise in TypeScript compilation + +- a3840bed2: Upgrade dependency rollup-plugin-typescript2 to ^0.27.3 +- cba4e4d97: Including source maps with all packages +- 9a3b3dbf1: Fixed duplicate help output, and print help on invalid command +- 7bbeb049f: Change loadBackendConfig to return the config directly +- Updated dependencies [8c2b76e45] +- Updated dependencies [ce5512bc0] + - @backstage/config-loader@0.2.0 diff --git a/packages/cli/package.json b/packages/cli/package.json index 0e10b1d3d0..7ae244866d 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.1.1", + "version": "0.2.0", "private": false, "publishConfig": { "access": "public" @@ -30,7 +30,7 @@ "dependencies": { "@backstage/cli-common": "^0.1.1", "@backstage/config": "^0.1.1", - "@backstage/config-loader": "^0.1.1", + "@backstage/config-loader": "^0.2.0", "@hot-loader/react-dom": "^16.13.0", "@lerna/package-graph": "^3.18.5", "@lerna/project": "^3.18.0", @@ -108,12 +108,12 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-common": "^0.1.1", + "@backstage/backend-common": "^0.2.0", "@backstage/config": "^0.1.1", - "@backstage/core": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/core": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", + "@backstage/theme": "^0.2.0", "@types/diff": "^4.0.2", "@types/fs-extra": "^9.0.1", "@types/html-webpack-plugin": "^3.2.2", diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md new file mode 100644 index 0000000000..9c6bee077f --- /dev/null +++ b/packages/config-loader/CHANGELOG.md @@ -0,0 +1,21 @@ +# @backstage/config-loader + +## 0.2.0 + +### Minor Changes + +- 8c2b76e45: **BREAKING CHANGE** + + The existing loading of additional config files like `app-config.development.yaml` using APP_ENV or NODE_ENV has been removed. + Instead, the CLI and backend process now accept one or more `--config` flags to load config files. + + Without passing any flags, `app-config.yaml` and, if it exists, `app-config.local.yaml` will be loaded. + If passing any `--config ` flags, only those files will be loaded, **NOT** the default `app-config.yaml` one. + + The old behaviour of for example `APP_ENV=development` can be replicated using the following flags: + + ```bash + --config ../../app-config.yaml --config ../../app-config.development.yaml + ``` + +- ce5512bc0: Added support for new shorthand when defining secrets, where `$env: ENV` can be used instead of `$secret: { env: ENV }` etc. diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 4444366658..2d3474f3af 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.1.1", + "version": "0.2.0", "private": false, "publishConfig": { "access": "public", diff --git a/packages/core-api/CHANGELOG.md b/packages/core-api/CHANGELOG.md new file mode 100644 index 0000000000..0e84d14aea --- /dev/null +++ b/packages/core-api/CHANGELOG.md @@ -0,0 +1,90 @@ +# @backstage/core-api + +## 0.2.0 + +### Minor Changes + +- 819a70229: Add SAML login to backstage + + ![](https://user-images.githubusercontent.com/872486/92251660-bb9e3400-eeff-11ea-86fe-1f2a0262cd31.png) + + ![](https://user-images.githubusercontent.com/872486/93851658-1a76f200-fce3-11ea-990b-26ca1a327a15.png) + +- b79017fd3: Updated the `GithubAuth.create` method to configure the default scope of the Github Auth Api. As a result the + default scope is configurable when overwriting the Core Api in the app. + + ``` + GithubAuth.create({ + discoveryApi, + oauthRequestApi, + defaultScopes: ['read:user', 'repo'], + }), + ``` + +- cbab5bbf8: Refactored the FeatureFlagsApi to make it easier to re-implement. Existing usage of particularly getUserFlags can be replaced with isActive() or save(). + +### Patch Changes + +- cbbd271c4: Add initial RouteRefRegistry + + Starting out some work to bring routing back and working as part of the work towards finalizing #1536 + + This is some of the groundwork of an experiment we're working on to enable routing via RouteRefs, while letting the app itself look something like this: + + ```jsx + const App = () => ( + + + + {' '} + // catalogRouteRef + + + + + + + // statusRouteRef + + + + + + + + + + // sentryRouteRef + + + + + + + + + + + + + + + + ); + ``` + + As part of inverting the composition of the app, route refs and routing in general was somewhat broken, intentionally. Right now it's not really possible to easily route to different parts of the app from a plugin, or even different parts of the plugin that are not within the same router. + + The core part of the experiment is to construct a map of ApiRef[] -> path overrides. Each key in the map is the list of route refs to traversed to reach a leaf in the routing tree, and the value is the path override at that point. For example, the above tree would add entries like [techDocsRouteRef] -> '/docs', and [entityRouteRef, apiDocsRouteRef] -> '/api'. By mapping out the entire app in this structure, the idea is that we can navigate to any point in the app using RouteRefs. + + The RouteRefRegistry is an implementation of such a map, and the idea is to add it in master to make it a bit easier to experiment and iterate. This is not an exposed API at this point. + + We've explored a couple of alternatives for how to enable routing, but it's boiled down to either a solution centred around the route map mentioned above, or treating all routes as static and globally unique, with no room for flexibility, customization or conflicts between different plugins. We're starting out pursuing this options 😁. We also expect that a the app-wide routing table will make things like dynamic loading a lot cleaner, as there would be a much more clear handoff between the main chunk and dynamic chunks. + +- 26e69ab1a: Remove cost insights example client from demo app and export from plugin + Create cost insights dev plugin using example client + Make PluginConfig and dependent types public +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] + - @backstage/theme@0.2.0 + - @backstage/test-utils@0.1.2 diff --git a/packages/core-api/package.json b/packages/core-api/package.json index 4c09f30113..42ba1c2080 100644 --- a/packages/core-api/package.json +++ b/packages/core-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-api", "description": "Internal Core API used by Backstage plugins and apps", - "version": "0.1.1", + "version": "0.2.0", "private": false, "publishConfig": { "access": "public", @@ -30,8 +30,8 @@ }, "dependencies": { "@backstage/config": "^0.1.1", - "@backstage/test-utils": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/test-utils": "^0.1.2", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@types/react": "^16.9", @@ -42,7 +42,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.1.1", + "@backstage/cli": "^0.2.0", "@backstage/test-utils-core": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md new file mode 100644 index 0000000000..10110a7b24 --- /dev/null +++ b/packages/core/CHANGELOG.md @@ -0,0 +1,70 @@ +# @backstage/core + +## 0.2.0 + +### Minor Changes + +- 819a70229: Add SAML login to backstage + + ![](https://user-images.githubusercontent.com/872486/92251660-bb9e3400-eeff-11ea-86fe-1f2a0262cd31.png) + + ![](https://user-images.githubusercontent.com/872486/93851658-1a76f200-fce3-11ea-990b-26ca1a327a15.png) + +- 482b6313d: Fix dense in Structured Metadata Table +- 1c60f716e: Added EmptyState component +- b79017fd3: Updated the `GithubAuth.create` method to configure the default scope of the Github Auth Api. As a result the + default scope is configurable when overwriting the Core Api in the app. + + ``` + GithubAuth.create({ + discoveryApi, + oauthRequestApi, + defaultScopes: ['read:user', 'repo'], + }), + ``` + +- 6d97d2d6f: The InfoCard variant `'height100'` is deprecated. Use variant `'gridItem'` instead. + + When the InfoCard is displayed as a grid item within a grid, you may want items to have the same height for all items. + Set to the `'gridItem'` variant to display the InfoCard with full height suitable for Grid: + `...` + + Changed the InfoCards in '@backstage/plugin-github-actions', '@backstage/plugin-jenkins', '@backstage/plugin-lighthouse' + to pass an optional variant to the corresponding card of the plugin. + + As a result the overview content of the EntityPage shows cards with full height suitable for Grid. + +### Patch Changes + +- ae5983387: Fix banner position and color + + This PR closes: #2245 + + The "fixed" props added to control the position of the banner. When it is set to true the banner will be shown in bottom of that page and the width will be based on the content of the message. + + ![](https://user-images.githubusercontent.com/15106494/93765685-999df480-fc15-11ea-8fa5-11cac5836cf1.png) + + ![](https://user-images.githubusercontent.com/15106494/93765697-9e62a880-fc15-11ea-92af-b6a7fee4bb21.png) + +- 144c66d50: Fixed banner component position in DismissableBanner component +- 93a3fa3ae: Add forwardRef to the SidebarItem +- 782f3b354: add test case for Progress component +- 2713f28f4: fix the warning of all the core components test cases +- 406015b0d: Update ItemCard headers to pass color contrast standards. +- 82759d3e4: rename stories folder top Chip +- ac8d5d5c7: update the test cases of CodeSnippet component +- ebca83d48: add test cases for Status components +- aca79334f: update ItemCard component and it's story +- c0d5242a0: Proper render boolean values on StructuredMetadataTable component +- 3beb5c9fc: make ErrorPage responsive + fix the test case +- 754e31db5: give aria-label attribute to Status Ok, Warning and Error +- 1611c6dbc: fix the responsive of page story +- Updated dependencies [819a70229] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [cbbd271c4] +- Updated dependencies [b79017fd3] +- Updated dependencies [26e69ab1a] +- Updated dependencies [cbab5bbf8] + - @backstage/core-api@0.2.0 + - @backstage/theme@0.2.0 diff --git a/packages/core/package.json b/packages/core/package.json index 59e9c4d6fc..76db8834ad 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core", "description": "Core API used by Backstage plugins and apps", - "version": "0.1.1", + "version": "0.2.0", "private": false, "publishConfig": { "access": "public", @@ -30,8 +30,8 @@ }, "dependencies": { "@backstage/config": "^0.1.1", - "@backstage/core-api": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/core-api": "^0.2.0", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -55,8 +55,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md new file mode 100644 index 0000000000..fa0d8f817b --- /dev/null +++ b/packages/create-app/CHANGELOG.md @@ -0,0 +1,132 @@ +# @backstage/create-app + +## 0.2.0 + +### Minor Changes + +- 6d29605db: Change the default backend plugin mount point to /api +- 5249594c5: Add service discovery interface and implement for single host deployments + + Fixes #1847, #2596 + + Went with an interface similar to the frontend DiscoveryApi, since it's dead simple but still provides a lot of flexibility in the implementation. + + Also ended up with two different methods, one for internal endpoint discovery and one for external. The two use-cases are explained a bit more in the docs, but basically it's service-to-service vs callback URLs. + + This did get me thinking about uniqueness and that we're heading towards a global namespace for backend plugin IDs. That's probably fine, but if we're happy with that we should leverage it a bit more to simplify the backend setup. For example we'd have each plugin provide its own ID and not manually mount on paths in the backend. + + Draft until we're happy with the implementation, then I can add more docs and changelog entry. Also didn't go on a thorough hunt for places where discovery can be used, but I don't think there are many since it's been pretty awkward to do service-to-service communication. + +- 56e4eb589: Make CSP configurable to fix app-backend served app not being able to fetch + + See discussion [here on discord](https://discordapp.com/channels/687207715902193673/687235481154617364/758721460163575850) + +- d7873e1aa: Default to using internal scope for new plugins +- 6f447b3fc: Remove identity-backend + + Not used, and we're heading down the route of identities in the catalog + +- 61db1ddc6: Allow node v14 and add to master build matrix + + - Upgrade sqlite3@^5.0.0 in @backstage/plugin-catalog-backend + - Add Node 14 to engines in @backstage/create-app + +- a768a07fb: Add the ability to import users from GitHub Organization into the catalog. + + The token needs to have the scopes `user:email`, `read:user`, and `read:org`. + +- f00ca3cb8: Auto-create plugin databases + + Relates to #1598. + + This creates databases for plugins before handing off control to plugins. + + The list of plugins currently need to be hard-coded depending on the installed plugins. A later PR will properly refactor the code to provide a factory pattern where plugins specify what they need, and Knex instances will be provided based on the input. + +- 6d97d2d6f: The InfoCard variant `'height100'` is deprecated. Use variant `'gridItem'` instead. + + When the InfoCard is displayed as a grid item within a grid, you may want items to have the same height for all items. + Set to the `'gridItem'` variant to display the InfoCard with full height suitable for Grid: + `...` + + Changed the InfoCards in '@backstage/plugin-github-actions', '@backstage/plugin-jenkins', '@backstage/plugin-lighthouse' + to pass an optional variant to the corresponding card of the plugin. + + As a result the overview content of the EntityPage shows cards with full height suitable for Grid. + +- 7aff112af: The default mount point for backend plugins have been changed to /api. These changes are done in the backend package itself, so it is recommended that you sync up existing backend packages with this new pattern. + +### Patch Changes + +- e67d49bf5: Sync scaffolded backend with example +- 961414d55: Remove discovery api override +- 440a17b39: Bump @backstage/catalog-backend and pass the now required UrlReader interface to the plugin +- 8c2b76e45: **BREAKING CHANGE** + + The existing loading of additional config files like `app-config.development.yaml` using APP_ENV or NODE_ENV has been removed. + Instead, the CLI and backend process now accept one or more `--config` flags to load config files. + + Without passing any flags, `app-config.yaml` and, if it exists, `app-config.local.yaml` will be loaded. + If passing any `--config ` flags, only those files will be loaded, **NOT** the default `app-config.yaml` one. + + The old behaviour of for example `APP_ENV=development` can be replicated using the following flags: + + ```bash + --config ../../app-config.yaml --config ../../app-config.development.yaml + ``` + +- 5a920c6e4: Updated naming of environment variables. New pattern [NAME]\_TOKEN for Github, Gitlab, Azure & Github enterprise access tokens. + + ### Detail: + + - Previously we have to export same token for both, catalog & scaffolder + + ```bash + export GITHUB_ACCESS_TOKEN=foo + export GITHUB_PRIVATE_TOKEN=foo + ``` + + with latest changes, only single export is sufficient. + + ```bash + export GITHUB_TOKEN=foo + export GITLAB_TOKEN=foo + export GHE_TOKEN=foo + export AZURE_TOKEN=foo + ``` + + ### list: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Old nameNew name
GITHUB_ACCESS_TOKENGITHUB_TOKEN
GITHUB_PRIVATE_TOKENGITHUB_TOKEN
GITLAB_ACCESS_TOKENGITLAB_TOKEN
GITLAB_PRIVATE_TOKENGITLAB_TOKEN
AZURE_PRIVATE_TOKENAZURE_TOKEN
GHE_PRIVATE_TOKENGHE_TOKEN
+ +- 67d76b419: Fix for configured templates using 'url' locations even though it's not supported yet +- 7bbeb049f: Change loadBackendConfig to return the config directly diff --git a/packages/create-app/package.json b/packages/create-app/package.json index adb2cba8f7..2653185c32 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "Create app package for Backstage", - "version": "0.1.1", + "version": "0.2.0", "private": false, "publishConfig": { "access": "public" @@ -37,30 +37,30 @@ "recursive-readdir": "^2.2.2" }, "devDependencies": { - "@backstage/backend-common": "^0.1.1", - "@backstage/catalog-model": "^0.1.1", - "@backstage/cli": "^0.1.1", + "@backstage/backend-common": "^0.2.0", + "@backstage/catalog-model": "^0.2.0", + "@backstage/cli": "^0.2.0", "@backstage/config": "^0.1.1", - "@backstage/core": "^0.1.1", - "@backstage/plugin-api-docs": "^0.1.1", - "@backstage/plugin-auth-backend": "^0.1.1", - "@backstage/plugin-catalog": "^0.1.1", - "@backstage/plugin-catalog-backend": "^0.1.1", - "@backstage/plugin-circleci": "^0.1.1", - "@backstage/plugin-explore": "^0.1.1", - "@backstage/plugin-github-actions": "^0.1.1", - "@backstage/plugin-lighthouse": "^0.1.1", - "@backstage/plugin-proxy-backend": "^0.1.1", - "@backstage/plugin-register-component": "^0.1.1", - "@backstage/plugin-rollbar-backend": "^0.1.1", - "@backstage/plugin-scaffolder": "^0.1.1", - "@backstage/plugin-scaffolder-backend": "^0.1.1", - "@backstage/plugin-tech-radar": "^0.1.1", - "@backstage/plugin-techdocs": "^0.1.1", - "@backstage/plugin-techdocs-backend": "^0.1.1", - "@backstage/plugin-user-settings": "^0.1.1", - "@backstage/test-utils": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/core": "^0.2.0", + "@backstage/plugin-api-docs": "^0.2.0", + "@backstage/plugin-auth-backend": "^0.2.0", + "@backstage/plugin-catalog": "^0.2.0", + "@backstage/plugin-catalog-backend": "^0.2.0", + "@backstage/plugin-circleci": "^0.2.0", + "@backstage/plugin-explore": "^0.2.0", + "@backstage/plugin-github-actions": "^0.2.0", + "@backstage/plugin-lighthouse": "^0.2.0", + "@backstage/plugin-proxy-backend": "^0.2.0", + "@backstage/plugin-register-component": "^0.2.0", + "@backstage/plugin-rollbar-backend": "^0.1.2", + "@backstage/plugin-scaffolder": "^0.2.0", + "@backstage/plugin-scaffolder-backend": "^0.2.0", + "@backstage/plugin-tech-radar": "^0.2.0", + "@backstage/plugin-techdocs": "^0.2.0", + "@backstage/plugin-techdocs-backend": "^0.2.0", + "@backstage/plugin-user-settings": "^0.2.0", + "@backstage/test-utils": "^0.1.2", + "@backstage/theme": "^0.2.0", "@types/fs-extra": "^9.0.1", "@types/inquirer": "^7.3.1", "@types/ora": "^3.2.0", diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md new file mode 100644 index 0000000000..20152c16ef --- /dev/null +++ b/packages/dev-utils/CHANGELOG.md @@ -0,0 +1,40 @@ +# @backstage/dev-utils + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [28edd7d29] +- Updated dependencies [819a70229] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [3472c8be7] +- Updated dependencies [1d0aec70f] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [a3840bed2] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [72f6cda35] +- Updated dependencies [8c2b76e45] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [cba4e4d97] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [8afce088a] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [9a3b3dbf1] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] +- Updated dependencies [7bbeb049f] + - @backstage/cli@0.2.0 + - @backstage/core@0.2.0 + - @backstage/theme@0.2.0 + - @backstage/test-utils@0.1.2 diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index a2f3bb24a0..60cb5adfd5 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/dev-utils", "description": "Utilities for developing Backstage plugins.", - "version": "0.1.1", + "version": "0.1.2", "private": false, "publishConfig": { "access": "public", @@ -29,10 +29,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/core": "^0.1.1", - "@backstage/test-utils": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/core": "^0.2.0", + "@backstage/test-utils": "^0.1.2", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@testing-library/jest-dom": "^5.10.1", diff --git a/packages/e2e-test/CHANGELOG.md b/packages/e2e-test/CHANGELOG.md new file mode 100644 index 0000000000..d872d34664 --- /dev/null +++ b/packages/e2e-test/CHANGELOG.md @@ -0,0 +1,7 @@ +# e2e-test + +## 0.2.0 + +### Minor Changes + +- 7de1004f0: Converted into a CLI, use `yarn e2e-test run` to run diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index 28c59a79c9..ea1dcc8ea2 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -1,7 +1,7 @@ { "name": "e2e-test", "description": "E2E test for verifying Backstage packages", - "version": "0.1.1", + "version": "0.2.0", "private": true, "homepage": "https://backstage.io", "repository": { diff --git a/packages/storybook/CHANGELOG.md b/packages/storybook/CHANGELOG.md new file mode 100644 index 0000000000..f5e44f780c --- /dev/null +++ b/packages/storybook/CHANGELOG.md @@ -0,0 +1,9 @@ +# storybook + +## 0.2.0 + +### Patch Changes + +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] + - @backstage/theme@0.2.0 diff --git a/packages/storybook/package.json b/packages/storybook/package.json index 4c9cc88dd0..f143596a9a 100644 --- a/packages/storybook/package.json +++ b/packages/storybook/package.json @@ -1,6 +1,6 @@ { "name": "storybook", - "version": "0.1.1", + "version": "0.2.0", "description": "Storybook build for core package", "private": true, "scripts": { @@ -14,7 +14,7 @@ ] }, "dependencies": { - "@backstage/theme": "^0.1.1" + "@backstage/theme": "^0.2.0" }, "devDependencies": { "@storybook/addon-actions": "^6.0.21", diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md new file mode 100644 index 0000000000..9add3bb173 --- /dev/null +++ b/packages/techdocs-cli/CHANGELOG.md @@ -0,0 +1,17 @@ +# @techdocs/cli + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [28edd7d29] +- Updated dependencies [3472c8be7] +- Updated dependencies [1d0aec70f] +- Updated dependencies [a3840bed2] +- Updated dependencies [72f6cda35] +- Updated dependencies [8c2b76e45] +- Updated dependencies [cba4e4d97] +- Updated dependencies [8afce088a] +- Updated dependencies [9a3b3dbf1] +- Updated dependencies [7bbeb049f] + - @backstage/cli@0.2.0 diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index 9772483923..ee3785b0cc 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,7 +1,7 @@ { "name": "@techdocs/cli", "description": "CLI for running TechDocs locally.", - "version": "0.1.1", + "version": "0.1.2", "private": false, "publishConfig": { "access": "public" @@ -40,7 +40,7 @@ "ext": "ts" }, "dependencies": { - "@backstage/cli": "^0.1.1", + "@backstage/cli": "^0.2.0", "commander": "^6.1.0", "fs-extra": "^9.0.1", "http-proxy": "^1.18.1", diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md new file mode 100644 index 0000000000..0d71e2ddd3 --- /dev/null +++ b/packages/test-utils/CHANGELOG.md @@ -0,0 +1,26 @@ +# @backstage/test-utils + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [28edd7d29] +- Updated dependencies [819a70229] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [cbbd271c4] +- Updated dependencies [3472c8be7] +- Updated dependencies [1d0aec70f] +- Updated dependencies [a3840bed2] +- Updated dependencies [b79017fd3] +- Updated dependencies [72f6cda35] +- Updated dependencies [8c2b76e45] +- Updated dependencies [cba4e4d97] +- Updated dependencies [8afce088a] +- Updated dependencies [9a3b3dbf1] +- Updated dependencies [26e69ab1a] +- Updated dependencies [cbab5bbf8] +- Updated dependencies [7bbeb049f] + - @backstage/cli@0.2.0 + - @backstage/core-api@0.2.0 + - @backstage/theme@0.2.0 diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 8245848567..9a7682c76b 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.1", + "version": "0.1.2", "private": false, "publishConfig": { "access": "public", @@ -29,10 +29,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/core-api": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/core-api": "^0.2.0", "@backstage/test-utils-core": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md new file mode 100644 index 0000000000..d8ba8deeba --- /dev/null +++ b/packages/theme/CHANGELOG.md @@ -0,0 +1,19 @@ +# @backstage/theme + +## 0.2.0 + +### Minor Changes + +- 0d4459c08: Tweak dark mode colors + +### Patch Changes + +- ae5983387: Fix banner position and color + + This PR closes: #2245 + + The "fixed" props added to control the position of the banner. When it is set to true the banner will be shown in bottom of that page and the width will be based on the content of the message. + + ![](https://user-images.githubusercontent.com/15106494/93765685-999df480-fc15-11ea-8fa5-11cac5836cf1.png) + + ![](https://user-images.githubusercontent.com/15106494/93765697-9e62a880-fc15-11ea-92af-b6a7fee4bb21.png) diff --git a/packages/theme/package.json b/packages/theme/package.json index 7033d9e1a5..378bd9db57 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.1.1", + "version": "0.2.0", "private": false, "publishConfig": { "access": "public", @@ -31,7 +31,7 @@ "@material-ui/core": "^4.11.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1" + "@backstage/cli": "^0.2.0" }, "files": [ "dist" diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md new file mode 100644 index 0000000000..73e6d6a941 --- /dev/null +++ b/plugins/api-docs/CHANGELOG.md @@ -0,0 +1,59 @@ +# @backstage/plugin-api-docs + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI +- 339668995: There were some missing features and markdown was not rendered properly, but this is fixed now. + + Details: + + - [`asyncapi/asyncapi-react#149`](https://github.com/asyncapi/asyncapi-react/pull/149) - fix: improve markdown rendering of nested fields + - [`asyncapi/asyncapi-react#150`](https://github.com/asyncapi/asyncapi-react/pull/150) - feat: display the description of channels and operations + - [`asyncapi/asyncapi-react#153`](https://github.com/asyncapi/asyncapi-react/pull/153) - fix: let the list of `enums` break into multiple lines + +### Patch Changes + +- a73979d45: Resolve some dark mode styling issues in asyncAPI specs +- Updated dependencies [28edd7d29] +- Updated dependencies [819a70229] +- Updated dependencies [3a4236570] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [368fd8243] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [a768a07fb] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [5adfc005e] +- Updated dependencies [f0aa01bcc] +- Updated dependencies [0aecfded0] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [8b9c8196f] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [60d40892c] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [2ebcfac8d] +- Updated dependencies [fa56f4615] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [b3d57961c] +- Updated dependencies [0b956f21b] +- Updated dependencies [97c2cb19b] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/plugin-catalog@0.2.0 + - @backstage/core@0.2.0 + - @backstage/catalog-model@0.2.0 + - @backstage/theme@0.2.0 diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 23c072201e..3e949bd5cc 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-api-docs", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,10 +20,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1", - "@backstage/core": "^0.1.1", - "@backstage/plugin-catalog": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/catalog-model": "^0.2.0", + "@backstage/core": "^0.2.0", + "@backstage/plugin-catalog": "^0.2.0", + "@backstage/theme": "^0.2.0", "@kyma-project/asyncapi-react": "^0.13.1", "@material-icons/font": "^1.0.2", "@material-ui/core": "^4.11.0", @@ -39,9 +39,9 @@ "swagger-ui-react": "^3.31.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md new file mode 100644 index 0000000000..03ecafd574 --- /dev/null +++ b/plugins/app-backend/CHANGELOG.md @@ -0,0 +1,22 @@ +# @backstage/plugin-app-backend + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI + +### Patch Changes + +- Updated dependencies [5249594c5] +- Updated dependencies [56e4eb589] +- Updated dependencies [e37c0a005] +- Updated dependencies [f00ca3cb8] +- Updated dependencies [6579769df] +- Updated dependencies [8c2b76e45] +- Updated dependencies [440a17b39] +- Updated dependencies [8afce088a] +- Updated dependencies [ce5512bc0] +- Updated dependencies [7bbeb049f] + - @backstage/backend-common@0.2.0 + - @backstage/config-loader@0.2.0 diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 4e91bf2161..c9b82cf6f1 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-backend", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1", - "@backstage/config-loader": "^0.1.1", + "@backstage/backend-common": "^0.2.0", + "@backstage/config-loader": "^0.2.0", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^3.0.3", @@ -30,7 +30,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1", + "@backstage/cli": "^0.2.0", "@types/supertest": "^2.0.8", "msw": "^0.20.5", "supertest": "^4.0.2" diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md new file mode 100644 index 0000000000..344ccd8312 --- /dev/null +++ b/plugins/auth-backend/CHANGELOG.md @@ -0,0 +1,61 @@ +# @backstage/plugin-auth-backend + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI +- 819a70229: Add SAML login to backstage + + ![](https://user-images.githubusercontent.com/872486/92251660-bb9e3400-eeff-11ea-86fe-1f2a0262cd31.png) + + ![](https://user-images.githubusercontent.com/872486/93851658-1a76f200-fce3-11ea-990b-26ca1a327a15.png) + +- 6d29605db: Change the default backend plugin mount point to /api +- 5249594c5: Add service discovery interface and implement for single host deployments + + Fixes #1847, #2596 + + Went with an interface similar to the frontend DiscoveryApi, since it's dead simple but still provides a lot of flexibility in the implementation. + + Also ended up with two different methods, one for internal endpoint discovery and one for external. The two use-cases are explained a bit more in the docs, but basically it's service-to-service vs callback URLs. + + This did get me thinking about uniqueness and that we're heading towards a global namespace for backend plugin IDs. That's probably fine, but if we're happy with that we should leverage it a bit more to simplify the backend setup. For example we'd have each plugin provide its own ID and not manually mount on paths in the backend. + + Draft until we're happy with the implementation, then I can add more docs and changelog entry. Also didn't go on a thorough hunt for places where discovery can be used, but I don't think there are many since it's been pretty awkward to do service-to-service communication. + +- 6f1768c0f: Initial implementation of catalog user lookup + + This adds a basic catalog client + method for the Google provider to look up users in the catalog. It expects to find a single user entity in the catalog with a google.com/email annotation that matches the email of the Google profile. + + Right now it falls back to the old behavior of splitting the email, since I don't wanna break the sign-in flow for existing apps, not yet anyway x). + + - Added "@backstage/catalog-model@^0.1.1-alpha.23" as a dependency + - Added "node-fetch@^2.6.1" as a dependency + +- 1687b8fbb: Lookup user in Google Auth Provider + +### Patch Changes + +- b4e5466e1: Move auth provider router creation to router +- b652bf2cc: Add OneLogin Identity Provider to Auth Backend +- e142a2767: Better presentation of authentication errors +- Updated dependencies [3a4236570] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [5249594c5] +- Updated dependencies [56e4eb589] +- Updated dependencies [e37c0a005] +- Updated dependencies [a768a07fb] +- Updated dependencies [f00ca3cb8] +- Updated dependencies [6579769df] +- Updated dependencies [5adfc005e] +- Updated dependencies [8c2b76e45] +- Updated dependencies [440a17b39] +- Updated dependencies [fa56f4615] +- Updated dependencies [8afce088a] +- Updated dependencies [b3d57961c] +- Updated dependencies [7bbeb049f] + - @backstage/catalog-model@0.2.0 + - @backstage/backend-common@0.2.0 diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index e7ff0289d7..bfeacdf74b 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1", - "@backstage/catalog-model": "^0.1.1", + "@backstage/backend-common": "^0.2.0", + "@backstage/catalog-model": "^0.2.0", "@backstage/config": "^0.1.1", "@types/express": "^4.17.6", "compression": "^1.7.4", @@ -52,7 +52,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1", + "@backstage/cli": "^0.2.0", "@types/body-parser": "^1.19.0", "@types/cookie-parser": "^1.4.2", "@types/jwt-decode": "2.2.1", diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md new file mode 100644 index 0000000000..e45da37691 --- /dev/null +++ b/plugins/catalog-backend/CHANGELOG.md @@ -0,0 +1,155 @@ +# @backstage/plugin-catalog-backend + +## 0.2.0 + +### Minor Changes + +- e0be86b6f: Entirely case insensitive read path of entities +- 12b5fe940: Add ApiDefinitionAtLocationProcessor that allows to load a API definition from another location +- 57d555eb2: This feature works the same as \$secret does in config - it allows programmatic substitution of values into a document. + + This is particularly useful e.g. for API type entities where you do not want to repeat your entire API spec document inside the catalog-info.yaml file. For those cases, you can instead do something like + + ``` + apiVersion: backstage.io/v1alpha1 + kind: API + metadata: + name: my-federated-service + spec: + type: graphql + definition: + $text: ./schema.graphql + ``` + + The textual content of that file will be injected as the value of definition, during each refresh loop. Both relative and absolute paths are supported, as well as any HTTP/HTTPS URL pointing to a service that returns the relevant data. + + The initial version supports injection of text file data, and structured data from JSON and YAML files. You can add any handler of your own in addition to these. + +- 61db1ddc6: Allow node v14 and add to master build matrix + + - Upgrade sqlite3@^5.0.0 in @backstage/plugin-catalog-backend + - Add Node 14 to engines in @backstage/create-app + +- 81cb94379: Simplify the read function in processors +- a768a07fb: Add the ability to import users from GitHub Organization into the catalog. + + The token needs to have the scopes `user:email`, `read:user`, and `read:org`. + +- ce1f55398: Use the new `UrlReader` in `PlaceholderProcessor`. + This allows to use the placeholder processor to include API definitions in API entities. + Previously it was only possible to do this if the definition comes from the same location type as the entity itself. +- e6b00e3af: Remove the backstage.io/definition-at-location annotation. + The annotation was superseded by the placeholder processor. + + ```yaml + apiVersion: backstage.io/v1alpha1 + kind: API + metadata: + name: spotify + description: The Spotify web API + tags: + - spotify + - rest + annotations: + # Don't use this annotation, but the placeholder $text instead (see below). + backstage.io/definition-at-location: 'url:https://raw.githubusercontent.com/APIs-guru/openapi-directory/master/APIs/spotify.com/v1/swagger.yaml' + spec: + type: openapi + lifecycle: production + owner: spotify@example.com + definition: + $text: https://raw.githubusercontent.com/APIs-guru/openapi-directory/master/APIs/spotify.com/v1/swagger.yaml + ``` + +- 99710b102: The way that wiring together a catalog happens, has changed drastically. Now + there is a new class `CatalogBuilder` that does almost all of the heavy lifting + of how to augment/replace pieces of catalog functionality, such as adding + support for custom entities or adding additional processors. + + As the builder was added, a lot of the static methods and builders for default + setups have been removed from classes deep in the hierarchy. Instead, the + builder contains the knowledge of what the defaults are. + +- 002860e7a: Filters passed to the `/entities` endpoint of the catalog has changed format. + + The old way was to pass things on the form `?a=b&c=d`; the new way is to pass + things on the form `?filter=a=b,c=d`. See discussion in + [#2910](https://github.com/backstage/backstage/issues/2910) for details. + + The comma separated items within a single filter have an AND between them. If + multiple such filters are passed, they have an OR between those item groups. + +- 5adfc005e: Changes the various kind policies into a new type `KindValidator`. + + Adds `CatalogProcessor#validateEntityKind` that makes use of the above + validators. This moves entity schema validity checking away from entity + policies and into processors, centralizing the extension points into the + processor chain. + +- 948052cbb: Add ability to dry run adding a new location to the catalog API. + + The location is now added in a transaction and afterwards rolled back. + This allows users to dry run this operation to see if there entity has issues. + This is probably done by automated tools in the CI/CD pipeline. + +- 4036ff59d: - The `CatalogProcessor` API was updated to have `preProcessEntity` and + `postProcessEntity` methods, instead of just one `processEntity`. This makes + it easier to make processors that have several stages in one, and to make + different processors more position independent in the list of processors. + - The `EntityPolicy` is now given directly to the `LocationReaders`, instead of + being enforced inside a policy. We have decided to separate out the act of + validating an entity to be outside of the processing flow, to make it + possible to apply more liberally and to evolve it as a separate concept. + - Because of the above, the `EntityPolicyProcessor` has been removed. +- 512d70973: Use the new `UrlReader` in the `CodeOwnersProcessor`. +- 2f62e1804: Removed the parseData step from catalog processors. Locations readers should emit full entities instead. +- 36a71d278: Removed support for deprecated `catalog.providers` config that have been moved to `integrations` +- a5cb46bac: Renamed the `LocationProcessor` class to `CatalogProcessor`. + + Likewise, renamed `LocationProcessorResult`, `LocationProcessorLocationResult`, + `LocationProcessorDataResult`, `LocationProcessorEntityResult`, + `LocationProcessorErrorResult`, and `LocationProcessorEmit` to their `Catalog*` + counterparts. + +- 49d70ccab: Remove the `read` argument of `LocationProcessor.processEntity`. + Instead, pass the `UrlReader` into the constructor of your `LocationProcessor`. +- 440a17b39: The catalog backend UrlReaderProcessor now uses a UrlReader from @backstage/backend-common, which must now be supplied to the constructor. + +### Patch Changes + +- 3472c8be7: Add codeowners processor + + - Add `codeowners-utils@^1.0.2` as a dependency + - Add `core-js@^3.6.5` as a dependency + - Added new CodeOwnersProcessor + +- 33454c0f2: Fix `CatalogBuilder#addProcessor`. +- 183e2a30d: Add support for `fields` sub-selection of just parts of an entity when listing + entities in the catalog backend. + + Example: `.../entities?fields=metadata.name,spec.type` will return partial + entity objects with only those exact fields present and the rest cut out. + Fields do not have to be simple scalars - you can for example do + `fields=metadata`. + +- 8bdf0bcf5: Fix CodeOwnersProcessor to handle non team users +- 4c4eab81b: The CodeOwnersProcessor now handles 'url' locations +- Updated dependencies [3a4236570] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [5249594c5] +- Updated dependencies [56e4eb589] +- Updated dependencies [e37c0a005] +- Updated dependencies [a768a07fb] +- Updated dependencies [f00ca3cb8] +- Updated dependencies [6579769df] +- Updated dependencies [5adfc005e] +- Updated dependencies [8c2b76e45] +- Updated dependencies [440a17b39] +- Updated dependencies [fa56f4615] +- Updated dependencies [8afce088a] +- Updated dependencies [b3d57961c] +- Updated dependencies [7bbeb049f] + - @backstage/catalog-model@0.2.0 + - @backstage/backend-common@0.2.0 diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 0d18d860d5..390c91551f 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1", - "@backstage/catalog-model": "^0.1.1", + "@backstage/backend-common": "^0.2.0", + "@backstage/catalog-model": "^0.2.0", "@backstage/config": "^0.1.1", "@octokit/graphql": "^4.5.6", "@types/express": "^4.17.6", @@ -45,8 +45,8 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/test-utils": "^0.1.2", "@types/core-js": "^2.5.4", "@types/git-url-parse": "^9.0.0", "@types/ldapjs": "^1.0.9", diff --git a/plugins/catalog-graphql/CHANGELOG.md b/plugins/catalog-graphql/CHANGELOG.md new file mode 100644 index 0000000000..30414d6998 --- /dev/null +++ b/plugins/catalog-graphql/CHANGELOG.md @@ -0,0 +1,29 @@ +# @backstage/plugin-catalog-graphql + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI + +### Patch Changes + +- Updated dependencies [3a4236570] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [5249594c5] +- Updated dependencies [56e4eb589] +- Updated dependencies [e37c0a005] +- Updated dependencies [a768a07fb] +- Updated dependencies [f00ca3cb8] +- Updated dependencies [6579769df] +- Updated dependencies [5adfc005e] +- Updated dependencies [8c2b76e45] +- Updated dependencies [440a17b39] +- Updated dependencies [fa56f4615] +- Updated dependencies [8afce088a] +- Updated dependencies [b3d57961c] +- Updated dependencies [7bbeb049f] + - @backstage/catalog-model@0.2.0 + - @backstage/backend-common@0.2.0 diff --git a/plugins/catalog-graphql/package.json b/plugins/catalog-graphql/package.json index a43f512856..aaa5faac36 100644 --- a/plugins/catalog-graphql/package.json +++ b/plugins/catalog-graphql/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graphql", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1", - "@backstage/catalog-model": "^0.1.1", + "@backstage/backend-common": "^0.2.0", + "@backstage/catalog-model": "^0.2.0", "@backstage/config": "^0.1.1", "@graphql-modules/core": "^0.7.17", "apollo-server": "^2.16.1", @@ -32,8 +32,8 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/test-utils": "^0.1.2", "@graphql-codegen/cli": "^1.17.7", "@graphql-codegen/typescript": "^1.17.7", "@graphql-codegen/typescript-resolvers": "^1.17.7", diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md new file mode 100644 index 0000000000..7fe67ffba4 --- /dev/null +++ b/plugins/catalog/CHANGELOG.md @@ -0,0 +1,79 @@ +# @backstage/plugin-catalog + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI +- 368fd8243: Created EntityNotFound component for catalog which displays the 404 page when entity is not found. + + Fixes #2266 + +- 6d97d2d6f: The InfoCard variant `'height100'` is deprecated. Use variant `'gridItem'` instead. + + When the InfoCard is displayed as a grid item within a grid, you may want items to have the same height for all items. + Set to the `'gridItem'` variant to display the InfoCard with full height suitable for Grid: + `...` + + Changed the InfoCards in '@backstage/plugin-github-actions', '@backstage/plugin-jenkins', '@backstage/plugin-lighthouse' + to pass an optional variant to the corresponding card of the plugin. + + As a result the overview content of the EntityPage shows cards with full height suitable for Grid. + +- f0aa01bcc: Add client side paging for catalog table +- 8b9c8196f: Locations registered through the catalog client now default to the 'url' type. The type selection dropdown in the register-component form has been removed. +- 2ebcfac8d: Add a validate button to the register-component page + + This allows the user to validate his location before adding it. + +- 0b956f21b: The URL path for a catalog entity has changed, + + - from: `/catalog/:kind/:optionalNamespaceAndName` + - to: `/catalog/:namespace/:kind/:name` + + Redirects are in place, so disruptions for users should not happen. + +### Patch Changes + +- 0aecfded0: handle the case where no entities are available to show +- 60d40892c: Remove "in default" in component name +- 97c2cb19b: update the EntityNotFound component +- Updated dependencies [28edd7d29] +- Updated dependencies [819a70229] +- Updated dependencies [3a4236570] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [8351ad79b] +- Updated dependencies [fb74f1db6] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [a768a07fb] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [5adfc005e] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [c5ef12926] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [fa56f4615] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [b3d57961c] +- Updated dependencies [1c8c43756] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [57b54c8ed] +- Updated dependencies [1611c6dbc] + - @backstage/plugin-scaffolder@0.2.0 + - @backstage/plugin-techdocs@0.2.0 + - @backstage/core@0.2.0 + - @backstage/catalog-model@0.2.0 + - @backstage/theme@0.2.0 diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index da03d1a584..66f7a681c5 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,11 +21,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1", - "@backstage/core": "^0.1.1", - "@backstage/plugin-scaffolder": "^0.1.1", - "@backstage/plugin-techdocs": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/catalog-model": "^0.2.0", + "@backstage/core": "^0.2.0", + "@backstage/plugin-scaffolder": "^0.2.0", + "@backstage/plugin-techdocs": "^0.2.0", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -41,9 +41,9 @@ "swr": "^0.3.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/react-hooks": "^3.3.0", diff --git a/plugins/circleci/CHANGELOG.md b/plugins/circleci/CHANGELOG.md new file mode 100644 index 0000000000..065d2e6526 --- /dev/null +++ b/plugins/circleci/CHANGELOG.md @@ -0,0 +1,52 @@ +# @backstage/plugin-circleci + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI +- 30dd11122: Refactor to use DiscoveryApi + +### Patch Changes + +- Updated dependencies [28edd7d29] +- Updated dependencies [819a70229] +- Updated dependencies [3a4236570] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [368fd8243] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [a768a07fb] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [5adfc005e] +- Updated dependencies [f0aa01bcc] +- Updated dependencies [0aecfded0] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [8b9c8196f] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [60d40892c] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [2ebcfac8d] +- Updated dependencies [fa56f4615] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [b3d57961c] +- Updated dependencies [0b956f21b] +- Updated dependencies [97c2cb19b] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/plugin-catalog@0.2.0 + - @backstage/core@0.2.0 + - @backstage/catalog-model@0.2.0 + - @backstage/theme@0.2.0 diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index dc09713024..cd849cbd31 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-circleci", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "postpack": "backstage-cli postpack" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1", - "@backstage/core": "^0.1.1", - "@backstage/plugin-catalog": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/catalog-model": "^0.2.0", + "@backstage/core": "^0.2.0", + "@backstage/plugin-catalog": "^0.2.0", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -38,9 +38,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/cloudbuild/CHANGELOG.md b/plugins/cloudbuild/CHANGELOG.md new file mode 100644 index 0000000000..a9df03e8e2 --- /dev/null +++ b/plugins/cloudbuild/CHANGELOG.md @@ -0,0 +1,51 @@ +# @backstage/plugin-cloudbuild + +## 0.2.0 + +### Minor Changes + +- 2846ef95c: Releasing Google Cloud Build Plugin + +### Patch Changes + +- Updated dependencies [28edd7d29] +- Updated dependencies [819a70229] +- Updated dependencies [3a4236570] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [368fd8243] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [a768a07fb] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [5adfc005e] +- Updated dependencies [f0aa01bcc] +- Updated dependencies [0aecfded0] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [8b9c8196f] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [60d40892c] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [2ebcfac8d] +- Updated dependencies [fa56f4615] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [b3d57961c] +- Updated dependencies [0b956f21b] +- Updated dependencies [97c2cb19b] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/plugin-catalog@0.2.0 + - @backstage/core@0.2.0 + - @backstage/catalog-model@0.2.0 + - @backstage/theme@0.2.0 diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index f29f30f255..14a7c5091c 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-cloudbuild", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,10 +20,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1", - "@backstage/core": "^0.1.1", - "@backstage/plugin-catalog": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/catalog-model": "^0.2.0", + "@backstage/core": "^0.2.0", + "@backstage/plugin-catalog": "^0.2.0", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -39,9 +39,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/cost-insights/CHANGELOG.md b/plugins/cost-insights/CHANGELOG.md new file mode 100644 index 0000000000..2d478c5717 --- /dev/null +++ b/plugins/cost-insights/CHANGELOG.md @@ -0,0 +1,65 @@ +# @backstage/plugin-cost-insights + +## 0.2.0 + +### Minor Changes + +- cab473771: This PR adds Spotify's Cost Insights Tool. Cost Insights explains costs from cloud services in an understandable way, using software terms familiar to your engineers. This tool helps you and your team make trade-offs between cost optimization efforts and your other priorities. + + Cost Insights features: + + Daily cost graph by team or billing account + Cost comparison against configurable business metrics + Insights panels for configurable cloud products your company uses + Cost alerts and recommendations + Selectable time periods for month over month, or quarter over quarter cost comparison + Conversion of cost growth into average engineer cost (configurable) to help optimization trade-off decisions + + ![plugin-cost-insights](https://user-images.githubusercontent.com/3030003/94430416-e166d380-0161-11eb-891c-9ce10187683e.gif) + + This PR adds the Cost Insights frontend React plugin with a defined CostInsightsApi. We include an example client with static data in the expected format. This API should talk with a cloud billing backend that aggregates billing data from your cloud provider. + + Fixes #688 💵 + +- bb48b9833: Added getLastCompleteBillingDate to the CostInsightsApi to reason about completeness of billing data +- 6a84cb072: Enable custom alert types in Cost Insights +- e7d4ac7ce: - getProjectDailyCost and getGroupDailyCost no longer accept a metric as a parameter + - getDailyMetricData added to API for fetching daily metric data for given interval + - dailyCost removed as configurable metric + - default field added to metric configuration for displaying comparison metric data in top panel + - Metric.kind can no longer be null + - MetricData type added +- 0e67c6b40: Remove product filters from query parameters + +### Patch Changes + +- 8d1360aa9: export test utilities for mocking context +- 0ee9e9f66: migrate type utilities out of type definition files +- 5c70f3d35: expose alerts utilities for export +- fd8384d7e: prefer named exports +- 26e69ab1a: Remove cost insights example client from demo app and export from plugin + Create cost insights dev plugin using example client + Make PluginConfig and dependent types public +- Updated dependencies [819a70229] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/core@0.2.0 + - @backstage/theme@0.2.0 + - @backstage/test-utils@0.1.2 diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index f978e13a8c..944dbf587f 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-cost-insights", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,9 +22,9 @@ }, "dependencies": { "@backstage/config": "^0.1.1", - "@backstage/core": "^0.1.1", - "@backstage/test-utils": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/core": "^0.2.0", + "@backstage/test-utils": "^0.1.2", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -46,9 +46,9 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/explore/CHANGELOG.md b/plugins/explore/CHANGELOG.md new file mode 100644 index 0000000000..dfa428b62a --- /dev/null +++ b/plugins/explore/CHANGELOG.md @@ -0,0 +1,48 @@ +# @backstage/plugin-explore + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI +- cab473771: This PR adds Spotify's Cost Insights Tool. Cost Insights explains costs from cloud services in an understandable way, using software terms familiar to your engineers. This tool helps you and your team make trade-offs between cost optimization efforts and your other priorities. + + Cost Insights features: + + Daily cost graph by team or billing account + Cost comparison against configurable business metrics + Insights panels for configurable cloud products your company uses + Cost alerts and recommendations + Selectable time periods for month over month, or quarter over quarter cost comparison + Conversion of cost growth into average engineer cost (configurable) to help optimization trade-off decisions + + ![plugin-cost-insights](https://user-images.githubusercontent.com/3030003/94430416-e166d380-0161-11eb-891c-9ce10187683e.gif) + + This PR adds the Cost Insights frontend React plugin with a defined CostInsightsApi. We include an example client with static data in the expected format. This API should talk with a cloud billing backend that aggregates billing data from your cloud provider. + + Fixes #688 💵 + +### Patch Changes + +- Updated dependencies [819a70229] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/core@0.2.0 + - @backstage/theme@0.2.0 diff --git a/plugins/explore/package.json b/plugins/explore/package.json index fa682e8dde..a1f71de6a2 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-explore", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/core": "^0.2.0", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -33,9 +33,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/gcp-projects/CHANGELOG.md b/plugins/gcp-projects/CHANGELOG.md new file mode 100644 index 0000000000..08e1962f3c --- /dev/null +++ b/plugins/gcp-projects/CHANGELOG.md @@ -0,0 +1,32 @@ +# @backstage/plugin-gcp-projects + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI + +### Patch Changes + +- Updated dependencies [819a70229] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/core@0.2.0 + - @backstage/theme@0.2.0 diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 86b525f1c9..812c3b1051 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gcp-projects", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/core": "^0.2.0", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -31,9 +31,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/github-actions/CHANGELOG.md b/plugins/github-actions/CHANGELOG.md new file mode 100644 index 0000000000..a4b69a07d4 --- /dev/null +++ b/plugins/github-actions/CHANGELOG.md @@ -0,0 +1,66 @@ +# @backstage/plugin-github-actions + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI +- d67c529ab: Adds a widget to show recent git workflow runs to the github actions plugin. The default setting is the last 5 runs across all branches but both branch and the number of runs are configurable. +- 6d97d2d6f: The InfoCard variant `'height100'` is deprecated. Use variant `'gridItem'` instead. + + When the InfoCard is displayed as a grid item within a grid, you may want items to have the same height for all items. + Set to the `'gridItem'` variant to display the InfoCard with full height suitable for Grid: + `...` + + Changed the InfoCards in '@backstage/plugin-github-actions', '@backstage/plugin-jenkins', '@backstage/plugin-lighthouse' + to pass an optional variant to the corresponding card of the plugin. + + As a result the overview content of the EntityPage shows cards with full height suitable for Grid. + +### Patch Changes + +- Updated dependencies [28edd7d29] +- Updated dependencies [819a70229] +- Updated dependencies [3a4236570] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [cbbd271c4] +- Updated dependencies [482b6313d] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [368fd8243] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [a768a07fb] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [5adfc005e] +- Updated dependencies [f0aa01bcc] +- Updated dependencies [0aecfded0] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [8b9c8196f] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [60d40892c] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [2ebcfac8d] +- Updated dependencies [fa56f4615] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [b3d57961c] +- Updated dependencies [0b956f21b] +- Updated dependencies [26e69ab1a] +- Updated dependencies [97c2cb19b] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [cbab5bbf8] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/plugin-catalog@0.2.0 + - @backstage/core-api@0.2.0 + - @backstage/core@0.2.0 + - @backstage/catalog-model@0.2.0 + - @backstage/theme@0.2.0 diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index ea5d8c4278..8d10760843 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-github-actions", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,11 +21,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1", - "@backstage/core": "^0.1.1", - "@backstage/core-api": "^0.1.1", - "@backstage/plugin-catalog": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/catalog-model": "^0.2.0", + "@backstage/core": "^0.2.0", + "@backstage/core-api": "^0.2.0", + "@backstage/plugin-catalog": "^0.2.0", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -40,9 +40,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/gitops-profiles/CHANGELOG.md b/plugins/gitops-profiles/CHANGELOG.md new file mode 100644 index 0000000000..a5d5249737 --- /dev/null +++ b/plugins/gitops-profiles/CHANGELOG.md @@ -0,0 +1,32 @@ +# @backstage/plugin-gitops-profiles + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI + +### Patch Changes + +- Updated dependencies [819a70229] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/core@0.2.0 + - @backstage/theme@0.2.0 diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index 714c63da20..b61c2f6620 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gitops-profiles", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/core": "^0.2.0", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -32,9 +32,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/graphiql/CHANGELOG.md b/plugins/graphiql/CHANGELOG.md new file mode 100644 index 0000000000..db59e7d369 --- /dev/null +++ b/plugins/graphiql/CHANGELOG.md @@ -0,0 +1,33 @@ +# @backstage/plugin-graphiql + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI + +### Patch Changes + +- 782f3b354: add test case for Progress component +- Updated dependencies [819a70229] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/core@0.2.0 + - @backstage/theme@0.2.0 diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index 0c23b5e6d0..e346133e3d 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphiql", "description": "Backstage plugin for browsing GraphQL APIs", - "version": "0.1.1", + "version": "0.2.0", "private": false, "publishConfig": { "access": "public", @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/core": "^0.2.0", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -43,9 +43,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/graphql/CHANGELOG.md b/plugins/graphql/CHANGELOG.md new file mode 100644 index 0000000000..f9a626d543 --- /dev/null +++ b/plugins/graphql/CHANGELOG.md @@ -0,0 +1,18 @@ +# @backstage/plugin-graphql-backend + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [28edd7d29] +- Updated dependencies [5249594c5] +- Updated dependencies [56e4eb589] +- Updated dependencies [e37c0a005] +- Updated dependencies [f00ca3cb8] +- Updated dependencies [6579769df] +- Updated dependencies [8c2b76e45] +- Updated dependencies [440a17b39] +- Updated dependencies [8afce088a] +- Updated dependencies [7bbeb049f] + - @backstage/plugin-catalog-graphql@0.2.0 + - @backstage/backend-common@0.2.0 diff --git a/plugins/graphql/package.json b/plugins/graphql/package.json index 16c3572251..f62fecc648 100644 --- a/plugins/graphql/package.json +++ b/plugins/graphql/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-graphql-backend", - "version": "0.1.1", + "version": "0.1.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -19,9 +19,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1", + "@backstage/backend-common": "^0.2.0", "@backstage/config": "^0.1.1", - "@backstage/plugin-catalog-graphql": "^0.1.1", + "@backstage/plugin-catalog-graphql": "^0.2.0", "@graphql-modules/core": "^0.7.17", "@types/express": "^4.17.6", "apollo-server": "^2.16.1", @@ -35,7 +35,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1", + "@backstage/cli": "^0.2.0", "@types/supertest": "^2.0.8", "eslint-plugin-graphql": "^4.0.0", "msw": "^0.20.5", diff --git a/plugins/jenkins/CHANGELOG.md b/plugins/jenkins/CHANGELOG.md new file mode 100644 index 0000000000..010471d9b2 --- /dev/null +++ b/plugins/jenkins/CHANGELOG.md @@ -0,0 +1,62 @@ +# @backstage/plugin-jenkins + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI +- 6d97d2d6f: The InfoCard variant `'height100'` is deprecated. Use variant `'gridItem'` instead. + + When the InfoCard is displayed as a grid item within a grid, you may want items to have the same height for all items. + Set to the `'gridItem'` variant to display the InfoCard with full height suitable for Grid: + `...` + + Changed the InfoCards in '@backstage/plugin-github-actions', '@backstage/plugin-jenkins', '@backstage/plugin-lighthouse' + to pass an optional variant to the corresponding card of the plugin. + + As a result the overview content of the EntityPage shows cards with full height suitable for Grid. + +### Patch Changes + +- 1297dcb3a: Refactor to use DiscoveryApi +- Updated dependencies [28edd7d29] +- Updated dependencies [819a70229] +- Updated dependencies [3a4236570] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [368fd8243] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [a768a07fb] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [5adfc005e] +- Updated dependencies [f0aa01bcc] +- Updated dependencies [0aecfded0] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [8b9c8196f] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [60d40892c] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [2ebcfac8d] +- Updated dependencies [fa56f4615] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [b3d57961c] +- Updated dependencies [0b956f21b] +- Updated dependencies [97c2cb19b] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/plugin-catalog@0.2.0 + - @backstage/core@0.2.0 + - @backstage/catalog-model@0.2.0 + - @backstage/theme@0.2.0 diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index cbf9791809..d0304376fd 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-jenkins", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1", - "@backstage/core": "^0.1.1", - "@backstage/plugin-catalog": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/catalog-model": "^0.2.0", + "@backstage/core": "^0.2.0", + "@backstage/plugin-catalog": "^0.2.0", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -36,9 +36,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md new file mode 100644 index 0000000000..2834082657 --- /dev/null +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -0,0 +1,16 @@ +# @backstage/plugin-kubernetes-backend + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [5249594c5] +- Updated dependencies [56e4eb589] +- Updated dependencies [e37c0a005] +- Updated dependencies [f00ca3cb8] +- Updated dependencies [6579769df] +- Updated dependencies [8c2b76e45] +- Updated dependencies [440a17b39] +- Updated dependencies [8afce088a] +- Updated dependencies [7bbeb049f] + - @backstage/backend-common@0.2.0 diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index c66c364871..7ac5f99f4c 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-backend", - "version": "0.1.1", + "version": "0.1.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1", + "@backstage/backend-common": "^0.2.0", "@backstage/config": "^0.1.1", "@kubernetes/client-node": "^0.12.1", "@types/express": "^4.17.6", @@ -37,7 +37,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1", + "@backstage/cli": "^0.2.0", "supertest": "^4.0.2" }, "files": [ diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md new file mode 100644 index 0000000000..0ec2c5dbaf --- /dev/null +++ b/plugins/kubernetes/CHANGELOG.md @@ -0,0 +1,42 @@ +# @backstage/plugin-kubernetes + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI + +### Patch Changes + +- Updated dependencies [819a70229] +- Updated dependencies [3a4236570] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [a768a07fb] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [5adfc005e] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [fa56f4615] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [b3d57961c] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/core@0.2.0 + - @backstage/catalog-model@0.2.0 + - @backstage/theme@0.2.0 + - @backstage/plugin-kubernetes-backend@0.1.2 diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 850dd4cada..29193e27ed 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,11 +20,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1", + "@backstage/catalog-model": "^0.2.0", "@backstage/config": "^0.1.1", - "@backstage/core": "^0.1.1", - "@backstage/plugin-kubernetes-backend": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/core": "^0.2.0", + "@backstage/plugin-kubernetes-backend": "^0.1.2", + "@backstage/theme": "^0.2.0", "@kubernetes/client-node": "^0.12.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -35,9 +35,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/lighthouse/CHANGELOG.md b/plugins/lighthouse/CHANGELOG.md new file mode 100644 index 0000000000..9a68b78a92 --- /dev/null +++ b/plugins/lighthouse/CHANGELOG.md @@ -0,0 +1,65 @@ +# @backstage/plugin-lighthouse + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI +- 6d97d2d6f: The InfoCard variant `'height100'` is deprecated. Use variant `'gridItem'` instead. + + When the InfoCard is displayed as a grid item within a grid, you may want items to have the same height for all items. + Set to the `'gridItem'` variant to display the InfoCard with full height suitable for Grid: + `...` + + Changed the InfoCards in '@backstage/plugin-github-actions', '@backstage/plugin-jenkins', '@backstage/plugin-lighthouse' + to pass an optional variant to the corresponding card of the plugin. + + As a result the overview content of the EntityPage shows cards with full height suitable for Grid. + +### Patch Changes + +- Updated dependencies [28edd7d29] +- Updated dependencies [819a70229] +- Updated dependencies [3a4236570] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [cbbd271c4] +- Updated dependencies [482b6313d] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [368fd8243] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [a768a07fb] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [5adfc005e] +- Updated dependencies [f0aa01bcc] +- Updated dependencies [0aecfded0] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [8b9c8196f] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [60d40892c] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [2ebcfac8d] +- Updated dependencies [fa56f4615] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [b3d57961c] +- Updated dependencies [0b956f21b] +- Updated dependencies [26e69ab1a] +- Updated dependencies [97c2cb19b] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [cbab5bbf8] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/plugin-catalog@0.2.0 + - @backstage/core-api@0.2.0 + - @backstage/core@0.2.0 + - @backstage/catalog-model@0.2.0 + - @backstage/theme@0.2.0 diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index 587d826d9d..27b6a21066 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-lighthouse", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,12 +21,12 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1", + "@backstage/catalog-model": "^0.2.0", "@backstage/config": "^0.1.1", - "@backstage/core": "^0.1.1", - "@backstage/core-api": "^0.1.1", - "@backstage/plugin-catalog": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/core": "^0.2.0", + "@backstage/core-api": "^0.2.0", + "@backstage/plugin-catalog": "^0.2.0", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -39,9 +39,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/newrelic/CHANGELOG.md b/plugins/newrelic/CHANGELOG.md new file mode 100644 index 0000000000..d007328180 --- /dev/null +++ b/plugins/newrelic/CHANGELOG.md @@ -0,0 +1,53 @@ +# @backstage/plugin-newrelic + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI +- 4512b9967: The New Relic plugin now uses the Backstage proxy to communicate with New Relic's API. + + Please update your `app-config.yaml` as follows: + + ```yaml + # Old Config + newrelic: + api: + baseUrl: 'https://api.newrelic.com/v2' + key: NEW_RELIC_REST_API_KEY + ``` + + ```yaml + # New Config + proxy: + '/newrelic/apm/api': + target: https://api.newrelic.com/v2 + headers: + X-Api-Key: + $env: NEW_RELIC_REST_API_KEY + ``` + +### Patch Changes + +- Updated dependencies [819a70229] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/core@0.2.0 + - @backstage/theme@0.2.0 diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index 0b52220239..e7d000abc5 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-newrelic", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/core": "^0.2.0", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -31,9 +31,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/proxy-backend/CHANGELOG.md b/plugins/proxy-backend/CHANGELOG.md new file mode 100644 index 0000000000..dcbe7eb7e8 --- /dev/null +++ b/plugins/proxy-backend/CHANGELOG.md @@ -0,0 +1,42 @@ +# @backstage/plugin-proxy-backend + +## 0.2.0 + +### Minor Changes + +- 5249594c5: Add service discovery interface and implement for single host deployments + + Fixes #1847, #2596 + + Went with an interface similar to the frontend DiscoveryApi, since it's dead simple but still provides a lot of flexibility in the implementation. + + Also ended up with two different methods, one for internal endpoint discovery and one for external. The two use-cases are explained a bit more in the docs, but basically it's service-to-service vs callback URLs. + + This did get me thinking about uniqueness and that we're heading towards a global namespace for backend plugin IDs. That's probably fine, but if we're happy with that we should leverage it a bit more to simplify the backend setup. For example we'd have each plugin provide its own ID and not manually mount on paths in the backend. + + Draft until we're happy with the implementation, then I can add more docs and changelog entry. Also didn't go on a thorough hunt for places where discovery can be used, but I don't think there are many since it's been pretty awkward to do service-to-service communication. + +- 9226c2aaa: Limit the http headers that are forwarded from the request to a safe set of defaults. + A user can configure additional headers that should be forwarded if the specific applications needs that. + + ```yaml + proxy: + '/my-api': + target: 'https://my-api.com/get' + allowedHeaders: + # We need to forward the Authorization header that was provided by the caller + - Authorization + ``` + +### Patch Changes + +- Updated dependencies [5249594c5] +- Updated dependencies [56e4eb589] +- Updated dependencies [e37c0a005] +- Updated dependencies [f00ca3cb8] +- Updated dependencies [6579769df] +- Updated dependencies [8c2b76e45] +- Updated dependencies [440a17b39] +- Updated dependencies [8afce088a] +- Updated dependencies [7bbeb049f] + - @backstage/backend-common@0.2.0 diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index 57594cfe3f..f147e400e4 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-proxy-backend", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -19,7 +19,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1", + "@backstage/backend-common": "^0.2.0", "@backstage/config": "^0.1.1", "@types/express": "^4.17.6", "express": "^4.17.1", @@ -33,7 +33,7 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", + "@backstage/cli": "^0.2.0", "@types/http-proxy-middleware": "^0.19.3", "@types/supertest": "^2.0.8", "@types/uuid": "^8.0.0", diff --git a/plugins/register-component/CHANGELOG.md b/plugins/register-component/CHANGELOG.md new file mode 100644 index 0000000000..cb1a24d131 --- /dev/null +++ b/plugins/register-component/CHANGELOG.md @@ -0,0 +1,55 @@ +# @backstage/plugin-register-component + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI +- 2ebcfac8d: Add a validate button to the register-component page + + This allows the user to validate his location before adding it. + +### Patch Changes + +- 8b9c8196f: Locations registered through the catalog client now default to the 'url' type. The type selection dropdown in the register-component form has been removed. +- Updated dependencies [28edd7d29] +- Updated dependencies [819a70229] +- Updated dependencies [3a4236570] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [368fd8243] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [a768a07fb] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [5adfc005e] +- Updated dependencies [f0aa01bcc] +- Updated dependencies [0aecfded0] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [8b9c8196f] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [60d40892c] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [2ebcfac8d] +- Updated dependencies [fa56f4615] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [b3d57961c] +- Updated dependencies [0b956f21b] +- Updated dependencies [97c2cb19b] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/plugin-catalog@0.2.0 + - @backstage/core@0.2.0 + - @backstage/catalog-model@0.2.0 + - @backstage/theme@0.2.0 diff --git a/plugins/register-component/package.json b/plugins/register-component/package.json index 58d3b122ca..095469f6b4 100644 --- a/plugins/register-component/package.json +++ b/plugins/register-component/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-register-component", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1", - "@backstage/core": "^0.1.1", - "@backstage/plugin-catalog": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/catalog-model": "^0.2.0", + "@backstage/core": "^0.2.0", + "@backstage/plugin-catalog": "^0.2.0", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -36,9 +36,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/rollbar-backend/CHANGELOG.md b/plugins/rollbar-backend/CHANGELOG.md new file mode 100644 index 0000000000..21f9ce8090 --- /dev/null +++ b/plugins/rollbar-backend/CHANGELOG.md @@ -0,0 +1,16 @@ +# @backstage/plugin-rollbar-backend + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [5249594c5] +- Updated dependencies [56e4eb589] +- Updated dependencies [e37c0a005] +- Updated dependencies [f00ca3cb8] +- Updated dependencies [6579769df] +- Updated dependencies [8c2b76e45] +- Updated dependencies [440a17b39] +- Updated dependencies [8afce088a] +- Updated dependencies [7bbeb049f] + - @backstage/backend-common@0.2.0 diff --git a/plugins/rollbar-backend/package.json b/plugins/rollbar-backend/package.json index 91e0000d6c..b1764f238f 100644 --- a/plugins/rollbar-backend/package.json +++ b/plugins/rollbar-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-rollbar-backend", - "version": "0.1.1", + "version": "0.1.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1", + "@backstage/backend-common": "^0.2.0", "@backstage/config": "^0.1.1", "@types/express": "^4.17.6", "axios": "^0.20.0", @@ -37,7 +37,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1", + "@backstage/cli": "^0.2.0", "@types/supertest": "^2.0.8", "supertest": "^4.0.2" }, diff --git a/plugins/rollbar/CHANGELOG.md b/plugins/rollbar/CHANGELOG.md new file mode 100644 index 0000000000..ee25fb10d9 --- /dev/null +++ b/plugins/rollbar/CHANGELOG.md @@ -0,0 +1,51 @@ +# @backstage/plugin-rollbar + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI + +### Patch Changes + +- Updated dependencies [28edd7d29] +- Updated dependencies [819a70229] +- Updated dependencies [3a4236570] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [368fd8243] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [a768a07fb] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [5adfc005e] +- Updated dependencies [f0aa01bcc] +- Updated dependencies [0aecfded0] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [8b9c8196f] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [60d40892c] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [2ebcfac8d] +- Updated dependencies [fa56f4615] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [b3d57961c] +- Updated dependencies [0b956f21b] +- Updated dependencies [97c2cb19b] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/plugin-catalog@0.2.0 + - @backstage/core@0.2.0 + - @backstage/catalog-model@0.2.0 + - @backstage/theme@0.2.0 diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index a3b11ace55..03e393aa1b 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-rollbar", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1", - "@backstage/core": "^0.1.1", - "@backstage/plugin-catalog": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/catalog-model": "^0.2.0", + "@backstage/core": "^0.2.0", + "@backstage/plugin-catalog": "^0.2.0", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -37,9 +37,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/react-hooks": "^3.3.0", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md new file mode 100644 index 0000000000..78a6d9527b --- /dev/null +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -0,0 +1,46 @@ +# @backstage/plugin-scaffolder-backend + +## 0.2.0 + +### Minor Changes + +- 3e254503d: Add Azure DevOps support to the scaffolder backend + + This adds support for Azure DevOps to the scaffolder (preparer & publisher). I thought I should get this in there now since #2426 has been merged. I had a previous PR with only the preparer but I closed that in favor of this one. + + I stayed with the 'azure/api' structure but I guess we should try and go the same way as with GitHub here #2501 + +### Patch Changes + +- 0c370c979: Update SSR template to pass CI +- 991a950e0: Added .fromConfig static factories for Preparers and Publishers + read integrations config to support url location types +- c926765a2: Allow templates to be located on non-default branch +- 6840a68df: Add authentication token to Scaffolder GitHub Preparer +- 1c8c43756: The new `scaffolder.github.baseUrl` config property allows to specify a custom base url for GitHub enterprise instances +- 5e4551e3a: Added support for configuring the working directory of the Scaffolder: + + ```yaml + backend: + workingDirectory: /some-dir # Use this to configure a working directory for the scaffolder, defaults to the OS temp-dir + ``` + +- e3d063ffa: Introduce PreparerOptions for PreparerBase +- Updated dependencies [3a4236570] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [5249594c5] +- Updated dependencies [56e4eb589] +- Updated dependencies [e37c0a005] +- Updated dependencies [a768a07fb] +- Updated dependencies [f00ca3cb8] +- Updated dependencies [6579769df] +- Updated dependencies [5adfc005e] +- Updated dependencies [8c2b76e45] +- Updated dependencies [440a17b39] +- Updated dependencies [fa56f4615] +- Updated dependencies [8afce088a] +- Updated dependencies [b3d57961c] +- Updated dependencies [7bbeb049f] + - @backstage/catalog-model@0.2.0 + - @backstage/backend-common@0.2.0 diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 676ddc9b8a..09cacb28eb 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1", - "@backstage/catalog-model": "^0.1.1", + "@backstage/backend-common": "^0.2.0", + "@backstage/catalog-model": "^0.2.0", "@backstage/config": "^0.1.1", "@gitbeaker/core": "^23.5.0", "@gitbeaker/node": "^23.5.0", @@ -47,7 +47,7 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1", + "@backstage/cli": "^0.2.0", "@octokit/types": "^5.4.1", "@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 new file mode 100644 index 0000000000..9c09366174 --- /dev/null +++ b/plugins/scaffolder/CHANGELOG.md @@ -0,0 +1,67 @@ +# @backstage/plugin-scaffolder + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI + +### Patch Changes + +- fb74f1db6: Make title meaningful after component creation + + Fixes #2458. + + After the change, the UX should look like this: + + ### If the component creation was successful: + + ![successfully-created-component](https://user-images.githubusercontent.com/33940798/94339294-8bd1e000-0016-11eb-885b-7936fcc23b63.gif) + + ### If the component creation failed: + + ![failed-to-create-component](https://user-images.githubusercontent.com/33940798/94339296-90969400-0016-11eb-9a74-ce16b3dd8d88.gif) + +- c5ef12926: fix the accordion details design when job stage fail +- 1c8c43756: The new `scaffolder.github.baseUrl` config property allows to specify a custom base url for GitHub enterprise instances +- Updated dependencies [28edd7d29] +- Updated dependencies [819a70229] +- Updated dependencies [3a4236570] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [368fd8243] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [a768a07fb] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [5adfc005e] +- Updated dependencies [f0aa01bcc] +- Updated dependencies [0aecfded0] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [8b9c8196f] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [60d40892c] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [2ebcfac8d] +- Updated dependencies [fa56f4615] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [b3d57961c] +- Updated dependencies [0b956f21b] +- Updated dependencies [97c2cb19b] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/plugin-catalog@0.2.0 + - @backstage/core@0.2.0 + - @backstage/catalog-model@0.2.0 + - @backstage/theme@0.2.0 diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 4f7dbdae20..738734ceb7 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1", - "@backstage/core": "^0.1.1", - "@backstage/plugin-catalog": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/catalog-model": "^0.2.0", + "@backstage/core": "^0.2.0", + "@backstage/plugin-catalog": "^0.2.0", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -41,9 +41,9 @@ "swr": "^0.3.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/sentry-backend/CHANGELOG.md b/plugins/sentry-backend/CHANGELOG.md new file mode 100644 index 0000000000..93361e05ff --- /dev/null +++ b/plugins/sentry-backend/CHANGELOG.md @@ -0,0 +1,16 @@ +# @backstage/plugin-sentry-backend + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [5249594c5] +- Updated dependencies [56e4eb589] +- Updated dependencies [e37c0a005] +- Updated dependencies [f00ca3cb8] +- Updated dependencies [6579769df] +- Updated dependencies [8c2b76e45] +- Updated dependencies [440a17b39] +- Updated dependencies [8afce088a] +- Updated dependencies [7bbeb049f] + - @backstage/backend-common@0.2.0 diff --git a/plugins/sentry-backend/package.json b/plugins/sentry-backend/package.json index cf9bf7886f..a1e1531b8b 100644 --- a/plugins/sentry-backend/package.json +++ b/plugins/sentry-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sentry-backend", - "version": "0.1.1", + "version": "0.1.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1", + "@backstage/backend-common": "^0.2.0", "@types/express": "^4.17.6", "axios": "^0.20.0", "compression": "^1.7.4", @@ -34,7 +34,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1" + "@backstage/cli": "^0.2.0" }, "files": [ "dist" diff --git a/plugins/sentry/CHANGELOG.md b/plugins/sentry/CHANGELOG.md new file mode 100644 index 0000000000..06380c39d9 --- /dev/null +++ b/plugins/sentry/CHANGELOG.md @@ -0,0 +1,41 @@ +# @backstage/plugin-sentry + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI + +### Patch Changes + +- Updated dependencies [819a70229] +- Updated dependencies [3a4236570] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [a768a07fb] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [5adfc005e] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [fa56f4615] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [b3d57961c] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/core@0.2.0 + - @backstage/catalog-model@0.2.0 + - @backstage/theme@0.2.0 diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 69c6f130eb..2ede8fd709 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sentry", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1", - "@backstage/core": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/catalog-model": "^0.2.0", + "@backstage/core": "^0.2.0", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -36,9 +36,9 @@ "timeago.js": "^4.0.2" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/sonarqube/CHANGELOG.md b/plugins/sonarqube/CHANGELOG.md new file mode 100644 index 0000000000..62f88596e7 --- /dev/null +++ b/plugins/sonarqube/CHANGELOG.md @@ -0,0 +1,37 @@ +# @backstage/plugin-sonarqube + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [819a70229] +- Updated dependencies [3a4236570] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [a768a07fb] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [5adfc005e] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [fa56f4615] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [b3d57961c] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/core@0.2.0 + - @backstage/catalog-model@0.2.0 + - @backstage/theme@0.2.0 diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index b3d90b2ca7..bc41213ad8 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sonarqube", - "version": "0.1.1", + "version": "0.1.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1", - "@backstage/core": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/catalog-model": "^0.2.0", + "@backstage/core": "^0.2.0", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -35,9 +35,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/tech-radar/CHANGELOG.md b/plugins/tech-radar/CHANGELOG.md new file mode 100644 index 0000000000..7f9f11a702 --- /dev/null +++ b/plugins/tech-radar/CHANGELOG.md @@ -0,0 +1,35 @@ +# @backstage/plugin-tech-radar + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI + +### Patch Changes + +- 782f3b354: add test case for Progress component +- 02c60b5f8: fix the horizontal scrolling issue in the RadarPage component +- Updated dependencies [819a70229] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/core@0.2.0 + - @backstage/theme@0.2.0 + - @backstage/test-utils@0.1.2 diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index c7163cc918..875d6d161b 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-radar", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.1.1", - "@backstage/test-utils": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/core": "^0.2.0", + "@backstage/test-utils": "^0.1.2", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -35,9 +35,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md new file mode 100644 index 0000000000..4191361a33 --- /dev/null +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -0,0 +1,94 @@ +# @backstage/plugin-techdocs-backend + +## 0.2.0 + +### Minor Changes + +- 6d29605db: Change the default backend plugin mount point to /api +- 5249594c5: Add service discovery interface and implement for single host deployments + + Fixes #1847, #2596 + + Went with an interface similar to the frontend DiscoveryApi, since it's dead simple but still provides a lot of flexibility in the implementation. + + Also ended up with two different methods, one for internal endpoint discovery and one for external. The two use-cases are explained a bit more in the docs, but basically it's service-to-service vs callback URLs. + + This did get me thinking about uniqueness and that we're heading towards a global namespace for backend plugin IDs. That's probably fine, but if we're happy with that we should leverage it a bit more to simplify the backend setup. For example we'd have each plugin provide its own ID and not manually mount on paths in the backend. + + Draft until we're happy with the implementation, then I can add more docs and changelog entry. Also didn't go on a thorough hunt for places where discovery can be used, but I don't think there are many since it's been pretty awkward to do service-to-service communication. + +- 5a920c6e4: Updated naming of environment variables. New pattern [NAME]\_TOKEN for Github, Gitlab, Azure & Github enterprise access tokens. + + ### Detail: + + - Previously we have to export same token for both, catalog & scaffolder + + ```bash + export GITHUB_ACCESS_TOKEN=foo + export GITHUB_PRIVATE_TOKEN=foo + ``` + + with latest changes, only single export is sufficient. + + ```bash + export GITHUB_TOKEN=foo + export GITLAB_TOKEN=foo + export GHE_TOKEN=foo + export AZURE_TOKEN=foo + ``` + + ### list: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Old nameNew name
GITHUB_ACCESS_TOKENGITHUB_TOKEN
GITHUB_PRIVATE_TOKENGITHUB_TOKEN
GITLAB_ACCESS_TOKENGITLAB_TOKEN
GITLAB_PRIVATE_TOKENGITLAB_TOKEN
AZURE_PRIVATE_TOKENAZURE_TOKEN
GHE_PRIVATE_TOKENGHE_TOKEN
+ +### Patch Changes + +- 22ff8fba5: Replacing the hard coded `baseApiUrl` by reading the value from configuration to enable private GitHub setup for TechDocs. +- Updated dependencies [3a4236570] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [5249594c5] +- Updated dependencies [56e4eb589] +- Updated dependencies [e37c0a005] +- Updated dependencies [a768a07fb] +- Updated dependencies [f00ca3cb8] +- Updated dependencies [6579769df] +- Updated dependencies [5adfc005e] +- Updated dependencies [8c2b76e45] +- Updated dependencies [440a17b39] +- Updated dependencies [fa56f4615] +- Updated dependencies [8afce088a] +- Updated dependencies [b3d57961c] +- Updated dependencies [7bbeb049f] + - @backstage/catalog-model@0.2.0 + - @backstage/backend-common@0.2.0 diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 3c99ee227a..c1d1f6ad27 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1", - "@backstage/catalog-model": "^0.1.1", + "@backstage/backend-common": "^0.2.0", + "@backstage/catalog-model": "^0.2.0", "@backstage/config": "^0.1.1", "@types/dockerode": "^2.5.34", "@types/express": "^4.17.6", @@ -37,7 +37,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1", + "@backstage/cli": "^0.2.0", "supertest": "^4.0.2" }, "files": [ diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md new file mode 100644 index 0000000000..387e4a8d90 --- /dev/null +++ b/plugins/techdocs/CHANGELOG.md @@ -0,0 +1,65 @@ +# @backstage/plugin-techdocs + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI +- 8351ad79b: Add a message if techdocs takes long time to load + + Fixes #2416. + + The UI after the change should look like this: + + ![techdocs-progress-bar](https://user-images.githubusercontent.com/33940798/94189286-296ac980-fec8-11ea-9051-1b3db938d12f.gif) + +### Patch Changes + +- 782f3b354: add test case for Progress component +- 57b54c8ed: While techdocs fetches site name and metadata for the component, the page title was displayed as '[object Object] | Backstage'. This has now been fixed to display the component ID if site name is not present or being fetched. +- Updated dependencies [28edd7d29] +- Updated dependencies [819a70229] +- Updated dependencies [3a4236570] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [cbbd271c4] +- Updated dependencies [482b6313d] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [368fd8243] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [a768a07fb] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [5adfc005e] +- Updated dependencies [f0aa01bcc] +- Updated dependencies [0aecfded0] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [8b9c8196f] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [60d40892c] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [2ebcfac8d] +- Updated dependencies [fa56f4615] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [b3d57961c] +- Updated dependencies [0b956f21b] +- Updated dependencies [26e69ab1a] +- Updated dependencies [97c2cb19b] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [cbab5bbf8] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/plugin-catalog@0.2.0 + - @backstage/core-api@0.2.0 + - @backstage/core@0.2.0 + - @backstage/catalog-model@0.2.0 + - @backstage/theme@0.2.0 + - @backstage/test-utils@0.1.2 diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 551932369c..ce0700c1a8 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,12 +22,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1", - "@backstage/core": "^0.1.1", - "@backstage/core-api": "^0.1.1", - "@backstage/plugin-catalog": "^0.1.1", - "@backstage/test-utils": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/catalog-model": "^0.2.0", + "@backstage/core": "^0.2.0", + "@backstage/core-api": "^0.2.0", + "@backstage/plugin-catalog": "^0.2.0", + "@backstage/test-utils": "^0.1.2", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -40,9 +40,9 @@ "sanitize-html": "^1.27.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/user-settings/CHANGELOG.md b/plugins/user-settings/CHANGELOG.md new file mode 100644 index 0000000000..2522a3af03 --- /dev/null +++ b/plugins/user-settings/CHANGELOG.md @@ -0,0 +1,32 @@ +# @backstage/plugin-user-settings + +## 0.2.0 + +### Minor Changes + +- 4fc1d440e: Add settings button to sidebar + +### Patch Changes + +- Updated dependencies [819a70229] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/core@0.2.0 + - @backstage/theme@0.2.0 diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 3e67c8cae6..2f098eae2e 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/core": "^0.2.0", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -32,9 +32,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/welcome/CHANGELOG.md b/plugins/welcome/CHANGELOG.md new file mode 100644 index 0000000000..2932d41d75 --- /dev/null +++ b/plugins/welcome/CHANGELOG.md @@ -0,0 +1,32 @@ +# @backstage/plugin-welcome + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI + +### Patch Changes + +- Updated dependencies [819a70229] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [482b6313d] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/core@0.2.0 + - @backstage/theme@0.2.0 diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index 846388eaec..148b5734ee 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-welcome", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -21,8 +21,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.1.1", - "@backstage/theme": "^0.1.1", + "@backstage/core": "^0.2.0", + "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -32,9 +32,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1", - "@backstage/dev-utils": "^0.1.1", - "@backstage/test-utils": "^0.1.1", + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.2", + "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", From 4dcdb8badf688a73b07e1ca72b1128c59b91510e Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Tue, 3 Nov 2020 22:02:32 -0500 Subject: [PATCH 081/252] Fix k8s contrib folder typo --- .../README.md | 2 +- .../deployment.yaml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) rename contrib/kubernetes/{plain_single_backend_deplyoment => plain_single_backend_deployment}/README.md (97%) rename contrib/kubernetes/{plain_single_backend_deplyoment => plain_single_backend_deployment}/deployment.yaml (84%) diff --git a/contrib/kubernetes/plain_single_backend_deplyoment/README.md b/contrib/kubernetes/plain_single_backend_deployment/README.md similarity index 97% rename from contrib/kubernetes/plain_single_backend_deplyoment/README.md rename to contrib/kubernetes/plain_single_backend_deployment/README.md index 685bed92a5..48c3490f81 100644 --- a/contrib/kubernetes/plain_single_backend_deplyoment/README.md +++ b/contrib/kubernetes/plain_single_backend_deployment/README.md @@ -13,7 +13,7 @@ yarn install yarn docker-build -kubectl apply -f contrib/kubernetes/plain_single_backend_deplyoment/deployment.yaml +kubectl apply -f contrib/kubernetes/plain_single_backend_deployment/deployment.yaml ``` You can use the following commands to monitor the deployment: diff --git a/contrib/kubernetes/plain_single_backend_deplyoment/deployment.yaml b/contrib/kubernetes/plain_single_backend_deployment/deployment.yaml similarity index 84% rename from contrib/kubernetes/plain_single_backend_deplyoment/deployment.yaml rename to contrib/kubernetes/plain_single_backend_deployment/deployment.yaml index a20c47229d..25c9207438 100644 --- a/contrib/kubernetes/plain_single_backend_deplyoment/deployment.yaml +++ b/contrib/kubernetes/plain_single_backend_deployment/deployment.yaml @@ -38,12 +38,12 @@ spec: - name: NODE_ENV value: development - # This makes it possible for the app to reach the backend when serving through `kubectl proxy` - # If you expose the service using for example an ingress controller, you should - # switch this out or remove it. - # - # Note that we're not setting app.baseUrl here, as setting the base path is not working at the moment. - # Further work is needed around the routing in the frontend or react-router before we can support that. + # This makes it possible for the app to reach the backend when serving through `kubectl proxy` + # If you expose the service using for example an ingress controller, you should + # switch this out or remove it. + # + # Note that we're not setting app.baseUrl here, as setting the base path is not working at the moment. + # Further work is needed around the routing in the frontend or react-router before we can support that. - name: APP_CONFIG_backend_baseUrl value: http://localhost:8001/api/v1/namespaces/backstage/services/backstage-backend:http/proxy From 1fddc733226eeab479cc972f0756882feda45e5c Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Tue, 3 Nov 2020 22:03:53 -0500 Subject: [PATCH 082/252] Additional folder rename fix --- docs/getting-started/deployment-k8s.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/deployment-k8s.md b/docs/getting-started/deployment-k8s.md index 5b7ce80051..bc24894ed1 100644 --- a/docs/getting-started/deployment-k8s.md +++ b/docs/getting-started/deployment-k8s.md @@ -9,5 +9,5 @@ Beyond that point we do not have an opinionated way to deploy Backstage within Kubernetes, as each cluster has its own unique set of tooling and patterns. We do provide examples to help you get started though. Check out -[this example](https://github.com/backstage/backstage/tree/master/contrib/kubernetes/plain_single_backend_deplyoment/) +[this example](https://github.com/backstage/backstage/tree/master/contrib/kubernetes/plain_single_backend_deployment/) for a basic single-deployment setup. From b061bfd75620457812ecbde32b5d417e0dad4383 Mon Sep 17 00:00:00 2001 From: Marek Calus Date: Wed, 4 Nov 2020 08:44:48 +0100 Subject: [PATCH 083/252] Bump yarn.lock --- yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index 38a37b8ab3..da531fe135 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3703,10 +3703,10 @@ react-router "6.0.0-beta.0" react-use "^15.3.3" -"@roadiehq/backstage-plugin-travis-ci@^0.2.5": - version "0.2.5" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-travis-ci/-/backstage-plugin-travis-ci-0.2.5.tgz#588cd8b4a4dd4bd426c0012f8d35eae4504b55f4" - integrity sha512-4aJizmvIeunpl8D/YDvzagNzikPU51DSZFz1tN0XCwRR+buTPknV141qsKP+yxUbs6nx3Xuh+T/vNkb9lk6FlA== +"@roadiehq/backstage-plugin-travis-ci@^0.2.6": + version "0.2.6" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-travis-ci/-/backstage-plugin-travis-ci-0.2.6.tgz#c253d4009b7150faa285786df7104f04cf99d4c2" + integrity sha512-Zs7mPmJifz08vNsS4zDzupexCEKAxPtqT4Vh8DhFJrphsiYwSIdPjW5JWif/XQIx0025Ew/RwuyMtwz2V/iWZg== dependencies: "@backstage/catalog-model" "^0.1.1-alpha.24" "@backstage/core" "^0.1.1-alpha.24" From 60a0e7d020f678b64db84e5534c4b5b474a72620 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 3 Nov 2020 20:33:13 +0100 Subject: [PATCH 084/252] package.json: temporary override of @roadiehq dependencies --- package.json | 7 +++ yarn.lock | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+) diff --git a/package.json b/package.json index 505ee0caad..da1d4de154 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,13 @@ "plugins/*" ] }, + "resolutions": { + "**/@roadiehq/backstage-plugin-*/@backstage/catalog-model": "0.2.0", + "**/@roadiehq/backstage-plugin-*/@backstage/core": "0.2.0", + "**/@roadiehq/backstage-plugin-*/@backstage/core-api": "0.2.0", + "**/@roadiehq/backstage-plugin-*/@backstage/plugin-catalog": "0.2.0", + "**/@roadiehq/backstage-plugin-*/@backstage/theme": "0.2.0" + }, "version": "1.0.0", "devDependencies": { "@changesets/cli": "^2.11.0", diff --git a/yarn.lock b/yarn.lock index 90d2657f0f..5db78343b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1297,6 +1297,153 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@backstage/catalog-model@^0.1.1-alpha.24": + version "0.2.0" + dependencies: + "@backstage/config" "^0.1.1" + "@types/json-schema" "^7.0.5" + "@types/yup" "^0.29.8" + json-schema "^0.2.5" + lodash "^4.17.15" + uuid "^8.0.0" + yup "^0.29.3" + +"@backstage/catalog-model@^0.1.1-alpha.25": + version "0.2.0" + dependencies: + "@backstage/config" "^0.1.1" + "@types/json-schema" "^7.0.5" + "@types/yup" "^0.29.8" + json-schema "^0.2.5" + lodash "^4.17.15" + uuid "^8.0.0" + yup "^0.29.3" + +"@backstage/core-api@^0.1.1-alpha.24": + version "0.2.0" + dependencies: + "@backstage/config" "^0.1.1" + "@backstage/test-utils" "^0.1.2" + "@backstage/theme" "^0.2.0" + "@material-ui/core" "^4.11.0" + "@material-ui/icons" "^4.9.1" + "@types/react" "^16.9" + prop-types "^15.7.2" + react "^16.12.0" + react-router-dom "6.0.0-beta.0" + react-use "^15.3.3" + zen-observable "^0.8.15" + +"@backstage/core@^0.1.1-alpha.24": + version "0.2.0" + dependencies: + "@backstage/config" "^0.1.1" + "@backstage/core-api" "^0.2.0" + "@backstage/theme" "^0.2.0" + "@material-ui/core" "^4.11.0" + "@material-ui/icons" "^4.9.1" + "@material-ui/lab" "4.0.0-alpha.45" + "@types/react" "^16.9" + "@types/react-sparklines" "^1.7.0" + classnames "^2.2.6" + clsx "^1.1.0" + immer "^7.0.9" + lodash "^4.17.15" + material-table "^1.69.1" + prop-types "^15.7.2" + rc-progress "^3.0.0" + react "^16.12.0" + react-dom "^16.12.0" + react-helmet "6.1.0" + react-hook-form "^6.6.0" + react-router "6.0.0-beta.0" + react-router-dom "6.0.0-beta.0" + react-sparklines "^1.7.0" + react-syntax-highlighter "^13.5.1" + react-use "^15.3.3" + +"@backstage/core@^0.1.1-alpha.25": + version "0.2.0" + dependencies: + "@backstage/config" "^0.1.1" + "@backstage/core-api" "^0.2.0" + "@backstage/theme" "^0.2.0" + "@material-ui/core" "^4.11.0" + "@material-ui/icons" "^4.9.1" + "@material-ui/lab" "4.0.0-alpha.45" + "@types/react" "^16.9" + "@types/react-sparklines" "^1.7.0" + classnames "^2.2.6" + clsx "^1.1.0" + immer "^7.0.9" + lodash "^4.17.15" + material-table "^1.69.1" + prop-types "^15.7.2" + rc-progress "^3.0.0" + react "^16.12.0" + react-dom "^16.12.0" + react-helmet "6.1.0" + react-hook-form "^6.6.0" + react-router "6.0.0-beta.0" + react-router-dom "6.0.0-beta.0" + react-sparklines "^1.7.0" + react-syntax-highlighter "^13.5.1" + react-use "^15.3.3" + +"@backstage/plugin-catalog@^0.1.1-alpha.24": + version "0.2.0" + dependencies: + "@backstage/catalog-model" "^0.2.0" + "@backstage/core" "^0.2.0" + "@backstage/plugin-scaffolder" "^0.2.0" + "@backstage/plugin-techdocs" "^0.2.0" + "@backstage/theme" "^0.2.0" + "@material-ui/core" "^4.11.0" + "@material-ui/icons" "^4.9.1" + "@material-ui/lab" "4.0.0-alpha.45" + "@types/react" "^16.9" + classnames "^2.2.6" + moment "^2.26.0" + react "^16.13.1" + react-dom "^16.13.1" + react-helmet "6.1.0" + react-router "6.0.0-beta.0" + react-router-dom "6.0.0-beta.0" + react-use "^15.3.3" + swr "^0.3.0" + +"@backstage/plugin-catalog@^0.1.1-alpha.25": + version "0.2.0" + dependencies: + "@backstage/catalog-model" "^0.2.0" + "@backstage/core" "^0.2.0" + "@backstage/plugin-scaffolder" "^0.2.0" + "@backstage/plugin-techdocs" "^0.2.0" + "@backstage/theme" "^0.2.0" + "@material-ui/core" "^4.11.0" + "@material-ui/icons" "^4.9.1" + "@material-ui/lab" "4.0.0-alpha.45" + "@types/react" "^16.9" + classnames "^2.2.6" + moment "^2.26.0" + react "^16.13.1" + react-dom "^16.13.1" + react-helmet "6.1.0" + react-router "6.0.0-beta.0" + react-router-dom "6.0.0-beta.0" + react-use "^15.3.3" + swr "^0.3.0" + +"@backstage/theme@^0.1.1-alpha.24": + version "0.2.0" + dependencies: + "@material-ui/core" "^4.11.0" + +"@backstage/theme@^0.1.1-alpha.25": + version "0.2.0" + dependencies: + "@material-ui/core" "^4.11.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" From c5bab94aba8e5161622b9f06c2c1315b4c6f58df Mon Sep 17 00:00:00 2001 From: Jesko Steinberg Date: Mon, 12 Oct 2020 15:26:31 +0200 Subject: [PATCH 085/252] refactor: use OAuthApiCreateOptions --- .changeset/2863.md | 29 +++++++++++++++++++ .../implementations/auth/auth0/Auth0Auth.ts | 17 ++--------- .../implementations/auth/github/GithubAuth.ts | 17 ++--------- .../implementations/auth/gitlab/GitlabAuth.ts | 17 ++--------- .../implementations/auth/google/GoogleAuth.ts | 17 ++--------- .../auth/microsoft/MicrosoftAuth.ts | 18 ++---------- .../implementations/auth/oauth2/OAuth2.ts | 14 ++------- .../implementations/auth/okta/OktaAuth.ts | 17 ++--------- .../implementations/auth/saml/SamlAuth.ts | 10 ++----- .../src/apis/implementations/auth/types.ts | 28 ++++++++++++++++++ 10 files changed, 73 insertions(+), 111 deletions(-) create mode 100644 .changeset/2863.md create mode 100644 packages/core-api/src/apis/implementations/auth/types.ts diff --git a/.changeset/2863.md b/.changeset/2863.md new file mode 100644 index 0000000000..729e3aab7b --- /dev/null +++ b/.changeset/2863.md @@ -0,0 +1,29 @@ +--- +'@backstage/core-api': patch +--- + +Updated the AuthApi `.create` methods to configure the default scope of the corresponding Auth Api. As a result the +default scope is configurable when overwriting the Core Api in the app. + +``` +GithubAuth.create({ + discoveryApi, + oauthRequestApi, + defaultScopes: ['read:user', 'repo'], +}), +``` + +Replaced redundant CreateOptions of each Auth Api with the OAuthApiCreateOptions type. + +``` +export type OAuthApiCreateOptions = AuthApiCreateOptions & { + oauthRequestApi: OAuthRequestApi; + defaultScopes?: string[]; +}; + +export type AuthApiCreateOptions = { + discoveryApi: DiscoveryApi; + environment?: string; + provider?: AuthProvider & { id: string }; +}; +``` diff --git a/packages/core-api/src/apis/implementations/auth/auth0/Auth0Auth.ts b/packages/core-api/src/apis/implementations/auth/auth0/Auth0Auth.ts index 793d9f90d3..b41e705726 100644 --- a/packages/core-api/src/apis/implementations/auth/auth0/Auth0Auth.ts +++ b/packages/core-api/src/apis/implementations/auth/auth0/Auth0Auth.ts @@ -16,21 +16,8 @@ import Auth0Icon from '@material-ui/icons/AcUnit'; import { auth0AuthApiRef } from '../../../definitions/auth'; -import { - OAuthRequestApi, - AuthProvider, - DiscoveryApi, -} from '../../../definitions'; import { OAuth2 } from '../oauth2'; - -type CreateOptions = { - discoveryApi: DiscoveryApi; - oauthRequestApi: OAuthRequestApi; - - defaultScopes?: string[]; - environment?: string; - provider?: AuthProvider & { id: string }; -}; +import { OAuthApiCreateOptions } from '../types'; const DEFAULT_PROVIDER = { id: 'auth0', @@ -45,7 +32,7 @@ class Auth0Auth { provider = DEFAULT_PROVIDER, oauthRequestApi, defaultScopes = ['openid', `email`, `profile`], - }: CreateOptions): typeof auth0AuthApiRef.T { + }: OAuthApiCreateOptions): typeof auth0AuthApiRef.T { return OAuth2.create({ discoveryApi, oauthRequestApi, diff --git a/packages/core-api/src/apis/implementations/auth/github/GithubAuth.ts b/packages/core-api/src/apis/implementations/auth/github/GithubAuth.ts index eb2f375ab0..532bff3480 100644 --- a/packages/core-api/src/apis/implementations/auth/github/GithubAuth.ts +++ b/packages/core-api/src/apis/implementations/auth/github/GithubAuth.ts @@ -25,26 +25,13 @@ import { BackstageIdentity, AuthRequestOptions, } from '../../../definitions/auth'; -import { - OAuthRequestApi, - AuthProvider, - DiscoveryApi, -} from '../../../definitions'; import { SessionManager } from '../../../../lib/AuthSessionManager/types'; import { AuthSessionStore, StaticAuthSessionManager, } from '../../../../lib/AuthSessionManager'; import { Observable } from '../../../../types'; - -type CreateOptions = { - discoveryApi: DiscoveryApi; - oauthRequestApi: OAuthRequestApi; - - defaultScopes?: string[]; - environment?: string; - provider?: AuthProvider & { id: string }; -}; +import { OAuthApiCreateOptions } from '../types'; export type GithubAuthResponse = { providerInfo: { @@ -69,7 +56,7 @@ class GithubAuth implements OAuthApi, SessionApi { provider = DEFAULT_PROVIDER, oauthRequestApi, defaultScopes = ['read:user'], - }: CreateOptions) { + }: OAuthApiCreateOptions) { const connector = new DefaultAuthConnector({ discoveryApi, environment, diff --git a/packages/core-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts b/packages/core-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts index b6a2757143..8669dff022 100644 --- a/packages/core-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts +++ b/packages/core-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts @@ -16,21 +16,8 @@ import GitlabIcon from '@material-ui/icons/AcUnit'; import { gitlabAuthApiRef } from '../../../definitions/auth'; -import { - OAuthRequestApi, - AuthProvider, - DiscoveryApi, -} from '../../../definitions'; import { OAuth2 } from '../oauth2'; - -type CreateOptions = { - discoveryApi: DiscoveryApi; - oauthRequestApi: OAuthRequestApi; - - defaultScopes?: string[]; - environment?: string; - provider?: AuthProvider & { id: string }; -}; +import { OAuthApiCreateOptions } from '../types'; const DEFAULT_PROVIDER = { id: 'gitlab', @@ -45,7 +32,7 @@ class GitlabAuth { provider = DEFAULT_PROVIDER, oauthRequestApi, defaultScopes = ['read_user'], - }: CreateOptions): typeof gitlabAuthApiRef.T { + }: OAuthApiCreateOptions): typeof gitlabAuthApiRef.T { return OAuth2.create({ discoveryApi, oauthRequestApi, diff --git a/packages/core-api/src/apis/implementations/auth/google/GoogleAuth.ts b/packages/core-api/src/apis/implementations/auth/google/GoogleAuth.ts index 2d2ff3ede7..fbc902cdd8 100644 --- a/packages/core-api/src/apis/implementations/auth/google/GoogleAuth.ts +++ b/packages/core-api/src/apis/implementations/auth/google/GoogleAuth.ts @@ -16,21 +16,8 @@ import GoogleIcon from '@material-ui/icons/AcUnit'; import { googleAuthApiRef } from '../../../definitions/auth'; -import { - OAuthRequestApi, - AuthProvider, - DiscoveryApi, -} from '../../../definitions'; import { OAuth2 } from '../oauth2'; - -type CreateOptions = { - discoveryApi: DiscoveryApi; - oauthRequestApi: OAuthRequestApi; - - defaultScopes?: string[]; - environment?: string; - provider?: AuthProvider & { id: string }; -}; +import { OAuthApiCreateOptions } from '../types'; const DEFAULT_PROVIDER = { id: 'google', @@ -51,7 +38,7 @@ class GoogleAuth { `${SCOPE_PREFIX}userinfo.email`, `${SCOPE_PREFIX}userinfo.profile`, ], - }: CreateOptions): typeof googleAuthApiRef.T { + }: OAuthApiCreateOptions): typeof googleAuthApiRef.T { return OAuth2.create({ discoveryApi, oauthRequestApi, diff --git a/packages/core-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts b/packages/core-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts index db9180b31c..3e2711db3b 100644 --- a/packages/core-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts +++ b/packages/core-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts @@ -16,22 +16,8 @@ import MicrosoftIcon from '@material-ui/icons/AcUnit'; import { microsoftAuthApiRef } from '../../../definitions/auth'; - -import { - OAuthRequestApi, - AuthProvider, - DiscoveryApi, -} from '../../../definitions'; import { OAuth2 } from '../oauth2'; - -type CreateOptions = { - discoveryApi: DiscoveryApi; - oauthRequestApi: OAuthRequestApi; - - defaultScopes?: string[]; - environment?: string; - provider?: AuthProvider & { id: string }; -}; +import { OAuthApiCreateOptions } from '../types'; const DEFAULT_PROVIDER = { id: 'microsoft', @@ -52,7 +38,7 @@ class MicrosoftAuth { 'email', 'User.Read', ], - }: CreateOptions): typeof microsoftAuthApiRef.T { + }: OAuthApiCreateOptions): typeof microsoftAuthApiRef.T { return OAuth2.create({ discoveryApi, oauthRequestApi, diff --git a/packages/core-api/src/apis/implementations/auth/oauth2/OAuth2.ts b/packages/core-api/src/apis/implementations/auth/oauth2/OAuth2.ts index d088ca9798..73ad3dcf14 100644 --- a/packages/core-api/src/apis/implementations/auth/oauth2/OAuth2.ts +++ b/packages/core-api/src/apis/implementations/auth/oauth2/OAuth2.ts @@ -19,11 +19,6 @@ import { DefaultAuthConnector } from '../../../../lib/AuthConnector'; import { RefreshingAuthSessionManager } from '../../../../lib/AuthSessionManager'; import { SessionManager } from '../../../../lib/AuthSessionManager/types'; import { Observable } from '../../../../types'; -import { - AuthProvider, - OAuthRequestApi, - DiscoveryApi, -} from '../../../definitions'; import { AuthRequestOptions, BackstageIdentity, @@ -36,19 +31,14 @@ import { BackstageIdentityApi, } from '../../../definitions/auth'; import { OAuth2Session } from './types'; +import { OAuthApiCreateOptions } from '../types'; type Options = { sessionManager: SessionManager; scopeTransform: (scopes: string[]) => string[]; }; -type CreateOptions = { - discoveryApi: DiscoveryApi; - oauthRequestApi: OAuthRequestApi; - - environment?: string; - provider?: AuthProvider & { id: string }; - defaultScopes?: string[]; +type CreateOptions = OAuthApiCreateOptions & { scopeTransform?: (scopes: string[]) => string[]; }; diff --git a/packages/core-api/src/apis/implementations/auth/okta/OktaAuth.ts b/packages/core-api/src/apis/implementations/auth/okta/OktaAuth.ts index 7382d24d62..2df908e9fb 100644 --- a/packages/core-api/src/apis/implementations/auth/okta/OktaAuth.ts +++ b/packages/core-api/src/apis/implementations/auth/okta/OktaAuth.ts @@ -16,21 +16,8 @@ import OktaIcon from '@material-ui/icons/AcUnit'; import { oktaAuthApiRef } from '../../../definitions/auth'; -import { - OAuthRequestApi, - AuthProvider, - DiscoveryApi, -} from '../../../definitions'; import { OAuth2 } from '../oauth2'; - -type CreateOptions = { - discoveryApi: DiscoveryApi; - oauthRequestApi: OAuthRequestApi; - - defaultScopes?: string[]; - environment?: string; - provider?: AuthProvider & { id: string }; -}; +import { OAuthApiCreateOptions } from '../types'; const DEFAULT_PROVIDER = { id: 'okta', @@ -57,7 +44,7 @@ class OktaAuth { provider = DEFAULT_PROVIDER, oauthRequestApi, defaultScopes = ['openid', 'email', 'profile', 'offline_access'], - }: CreateOptions): typeof oktaAuthApiRef.T { + }: OAuthApiCreateOptions): typeof oktaAuthApiRef.T { return OAuth2.create({ discoveryApi, oauthRequestApi, diff --git a/packages/core-api/src/apis/implementations/auth/saml/SamlAuth.ts b/packages/core-api/src/apis/implementations/auth/saml/SamlAuth.ts index 973b402756..da5f082c9b 100644 --- a/packages/core-api/src/apis/implementations/auth/saml/SamlAuth.ts +++ b/packages/core-api/src/apis/implementations/auth/saml/SamlAuth.ts @@ -27,18 +27,12 @@ import { BackstageIdentityApi, SessionApi, } from '../../../definitions/auth'; -import { AuthProvider, DiscoveryApi } from '../../../definitions'; import { SamlSession } from './types'; import { AuthSessionStore, StaticAuthSessionManager, } from '../../../../lib/AuthSessionManager'; - -type CreateOptions = { - discoveryApi: DiscoveryApi; - environment?: string; - provider?: AuthProvider & { id: string }; -}; +import { AuthApiCreateOptions } from '../types'; export type SamlAuthResponse = { profile: ProfileInfo; @@ -56,7 +50,7 @@ class SamlAuth implements ProfileInfoApi, BackstageIdentityApi, SessionApi { discoveryApi, environment = 'development', provider = DEFAULT_PROVIDER, - }: CreateOptions) { + }: AuthApiCreateOptions) { const connector = new DirectAuthConnector({ discoveryApi, environment, diff --git a/packages/core-api/src/apis/implementations/auth/types.ts b/packages/core-api/src/apis/implementations/auth/types.ts new file mode 100644 index 0000000000..db3b718d25 --- /dev/null +++ b/packages/core-api/src/apis/implementations/auth/types.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AuthProvider, DiscoveryApi, OAuthRequestApi } from '../../definitions'; + +export type OAuthApiCreateOptions = AuthApiCreateOptions & { + oauthRequestApi: OAuthRequestApi; + defaultScopes?: string[]; +}; + +export type AuthApiCreateOptions = { + discoveryApi: DiscoveryApi; + environment?: string; + provider?: AuthProvider & { id: string }; +}; From 42b0dbddcb1a3fbe6f2e9eb8bb6ec325b2925feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 4 Nov 2020 13:18:03 +0100 Subject: [PATCH 086/252] feat(catalog-client): create a basic catalog client (#3166) --- app-config.yaml | 2 +- packages/catalog-client/.eslintrc.js | 3 ++ packages/catalog-client/README.md | 17 +++++++++ packages/catalog-client/package.json | 35 +++++++++++++++++++ .../catalog-client/src}/CatalogClient.test.ts | 15 +++++--- .../catalog-client/src}/CatalogClient.ts | 9 +++-- packages/catalog-client/src/index.ts | 18 ++++++++++ packages/catalog-client/src/setupTests.ts | 17 +++++++++ .../catalog-client/src}/types.ts | 26 +++++--------- plugins/catalog/package.json | 1 + .../CatalogFilter/AllServicesCount.tsx | 8 ++--- .../CatalogFilter/CatalogFilter.test.tsx | 3 +- .../CatalogPage/CatalogPage.test.tsx | 6 ++-- .../components/CatalogPage/CatalogPage.tsx | 2 +- .../ResultsFilter/ResultsFilter.test.tsx | 3 +- .../UnregisterEntityDialog.tsx | 4 +-- .../src/filter/EntityFilterGroupsProvider.tsx | 2 +- .../src/filter/useEntityFilterGroup.test.tsx | 6 ++-- plugins/catalog/src/hooks/useEntity.ts | 2 +- plugins/catalog/src/index.ts | 13 ++++--- plugins/catalog/src/plugin.ts | 12 +++++-- .../reader/components/TechDocsHome.test.tsx | 12 +++---- 22 files changed, 155 insertions(+), 61 deletions(-) create mode 100644 packages/catalog-client/.eslintrc.js create mode 100644 packages/catalog-client/README.md create mode 100644 packages/catalog-client/package.json rename {plugins/catalog/src/api => packages/catalog-client/src}/CatalogClient.test.ts (87%) rename {plugins/catalog/src/api => packages/catalog-client/src}/CatalogClient.ts (96%) create mode 100644 packages/catalog-client/src/index.ts create mode 100644 packages/catalog-client/src/setupTests.ts rename {plugins/catalog/src/api => packages/catalog-client/src}/types.ts (70%) diff --git a/app-config.yaml b/app-config.yaml index bc25b7d71e..bec048ce33 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -16,7 +16,7 @@ backend: credentials: true csp: connect-src: ["'self'", 'http:', 'https:'] - # workingDirectory: /tmp # Use this to configure a working direcotry for the scaffolder, defaults to the OS temp-dir + # workingDirectory: /tmp # Use this to configure a working directory for the scaffolder, defaults to the OS temp-dir # See README.md in the proxy-backend plugin for information on the configuration format proxy: diff --git a/packages/catalog-client/.eslintrc.js b/packages/catalog-client/.eslintrc.js new file mode 100644 index 0000000000..13573efa9c --- /dev/null +++ b/packages/catalog-client/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint')], +}; diff --git a/packages/catalog-client/README.md b/packages/catalog-client/README.md new file mode 100644 index 0000000000..574e23cf62 --- /dev/null +++ b/packages/catalog-client/README.md @@ -0,0 +1,17 @@ +# Catalog Client + +Contains a frontend and backend compatible client for communicating with the +Backstage Catalog. + +Backend code may import and use this package directly. + +However, frontend code will not want to import this package directly - use the +`@backstage/plugin-catalog` package instead, which re-exports all of the types +and classes from this package. Thereby, you will also gain access to its +`catalogApiRef`. + +## Links + +- [Default frontend part of the catalog](https://github.com/spotify/backstage/tree/master/plugins/catalog) +- [Default backend part of the catalog](https://github.com/spotify/backstage/tree/master/plugins/catalog-backend) +- [The Backstage homepage](https://backstage.io) diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json new file mode 100644 index 0000000000..c4aa632ca9 --- /dev/null +++ b/packages/catalog-client/package.json @@ -0,0 +1,35 @@ +{ + "name": "@backstage/catalog-client", + "version": "0.2.0", + "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" + }, + "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" + }, + "dependencies": { + "@backstage/catalog-model": "^0.2.0", + "@backstage/config": "^0.1.1", + "cross-fetch": "^3.0.6" + }, + "devDependencies": { + "@backstage/cli": "^0.2.0", + "@types/jest": "^26.0.7", + "msw": "^0.21.2" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/catalog/src/api/CatalogClient.test.ts b/packages/catalog-client/src/CatalogClient.test.ts similarity index 87% rename from plugins/catalog/src/api/CatalogClient.test.ts rename to packages/catalog-client/src/CatalogClient.test.ts index 927e8dd52c..65625c11b8 100644 --- a/plugins/catalog/src/api/CatalogClient.test.ts +++ b/packages/catalog-client/src/CatalogClient.test.ts @@ -18,17 +18,22 @@ import { rest } from 'msw'; import { setupServer } from 'msw/node'; import { CatalogClient } from './CatalogClient'; import { Entity } from '@backstage/catalog-model'; -import { UrlPatternDiscovery } from '@backstage/core'; -import { msw } from '@backstage/test-utils'; +import { DiscoveryApi } from './types'; const server = setupServer(); const mockBaseUrl = 'http://backstage:9191/i-am-a-mock-base'; -const discoveryApi = UrlPatternDiscovery.compile(mockBaseUrl); +const discoveryApi: DiscoveryApi = { + async getBaseUrl(_pluginId) { + return mockBaseUrl; + }, +}; describe('CatalogClient', () => { - let client = new CatalogClient({ discoveryApi }); + let client: CatalogClient; - msw.setupDefaultHandlers(server); + beforeAll(() => server.listen({ onUnhandledRequest: 'error' })); + afterAll(() => server.close()); + afterEach(() => server.resetHandlers()); beforeEach(() => { client = new CatalogClient({ discoveryApi }); diff --git a/plugins/catalog/src/api/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts similarity index 96% rename from plugins/catalog/src/api/CatalogClient.ts rename to packages/catalog-client/src/CatalogClient.ts index 85fd44cf01..e39759a88d 100644 --- a/plugins/catalog/src/api/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -16,16 +16,17 @@ import { Entity, + EntityName, Location, LOCATION_ANNOTATION, } from '@backstage/catalog-model'; +import fetch from 'cross-fetch'; import { AddLocationRequest, AddLocationResponse, CatalogApi, - EntityCompoundName, + DiscoveryApi, } from './types'; -import { DiscoveryApi } from '@backstage/core'; export class CatalogClient implements CatalogApi { private readonly discoveryApi: DiscoveryApi; @@ -85,9 +86,7 @@ export class CatalogClient implements CatalogApi { return await this.getRequired(path); } - async getEntityByName( - compoundName: EntityCompoundName, - ): Promise { + async getEntityByName(compoundName: EntityName): Promise { const { kind, namespace = 'default', name } = compoundName; return this.getOptional(`/entities/by-name/${kind}/${namespace}/${name}`); } diff --git a/packages/catalog-client/src/index.ts b/packages/catalog-client/src/index.ts new file mode 100644 index 0000000000..9bd13f9b7c --- /dev/null +++ b/packages/catalog-client/src/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { CatalogClient } from './CatalogClient'; +export type { CatalogApi } from './types'; diff --git a/packages/catalog-client/src/setupTests.ts b/packages/catalog-client/src/setupTests.ts new file mode 100644 index 0000000000..ba33cf996b --- /dev/null +++ b/packages/catalog-client/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export {}; diff --git a/plugins/catalog/src/api/types.ts b/packages/catalog-client/src/types.ts similarity index 70% rename from plugins/catalog/src/api/types.ts rename to packages/catalog-client/src/types.ts index 06030e6ac5..575b8d6ca2 100644 --- a/plugins/catalog/src/api/types.ts +++ b/packages/catalog-client/src/types.ts @@ -14,26 +14,11 @@ * limitations under the License. */ -import { createApiRef } from '@backstage/core'; -import { Entity, Location } from '@backstage/catalog-model'; - -export const catalogApiRef = createApiRef({ - id: 'plugin.catalog.service', - description: - 'Used by the Catalog plugin to make requests to accompanying backend', -}); - -export type EntityCompoundName = { - kind: string; - namespace?: string; - name: string; -}; +import { Entity, EntityName, Location } from '@backstage/catalog-model'; export interface CatalogApi { getLocationById(id: String): Promise; - getEntityByName( - compoundName: EntityCompoundName, - ): Promise; + getEntityByName(name: EntityName): Promise; getEntities(filter?: Record): Promise; addLocation(location: AddLocationRequest): Promise; getLocationByEntity(entity: Entity): Promise; @@ -50,3 +35,10 @@ export type AddLocationResponse = { location: Location; entities: Entity[]; }; + +/** + * This is a copy of the core DiscoveryApi, to avoid importing core. + */ +export type DiscoveryApi = { + getBaseUrl(pluginId: string): Promise; +}; diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 66f7a681c5..6d52c4722f 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -21,6 +21,7 @@ "clean": "backstage-cli clean" }, "dependencies": { + "@backstage/catalog-client": "^0.2.0", "@backstage/catalog-model": "^0.2.0", "@backstage/core": "^0.2.0", "@backstage/plugin-scaffolder": "^0.2.0", diff --git a/plugins/catalog/src/components/CatalogFilter/AllServicesCount.tsx b/plugins/catalog/src/components/CatalogFilter/AllServicesCount.tsx index b4d90c249a..e78cedbe2d 100644 --- a/plugins/catalog/src/components/CatalogFilter/AllServicesCount.tsx +++ b/plugins/catalog/src/components/CatalogFilter/AllServicesCount.tsx @@ -14,13 +14,13 @@ * limitations under the License. */ -import React, { FC } from 'react'; import { useApi } from '@backstage/core'; -import { catalogApiRef } from '../../api/types'; -import { useAsync } from 'react-use'; import { CircularProgress, useTheme } from '@material-ui/core'; +import React from 'react'; +import { useAsync } from 'react-use'; +import { catalogApiRef } from '../../plugin'; -export const AllServicesCount: FC<{}> = () => { +export const AllServicesCount = () => { const theme = useTheme(); const catalogApi = useApi(catalogApiRef); const { value, loading } = useAsync(() => catalogApi.getEntities()); diff --git a/plugins/catalog/src/components/CatalogFilter/CatalogFilter.test.tsx b/plugins/catalog/src/components/CatalogFilter/CatalogFilter.test.tsx index bc23d61d9c..a4ede7c2f2 100644 --- a/plugins/catalog/src/components/CatalogFilter/CatalogFilter.test.tsx +++ b/plugins/catalog/src/components/CatalogFilter/CatalogFilter.test.tsx @@ -14,6 +14,7 @@ * limitations under the License. */ +import { CatalogApi } from '@backstage/catalog-client'; import { Entity } from '@backstage/catalog-model'; import { ApiProvider, @@ -25,8 +26,8 @@ import { import { MockStorageApi, wrapInTestApp } from '@backstage/test-utils'; import { fireEvent, render, waitFor } from '@testing-library/react'; import React from 'react'; -import { CatalogApi, catalogApiRef } from '../../api/types'; import { EntityFilterGroupsProvider } from '../../filter'; +import { catalogApiRef } from '../../plugin'; import { ButtonGroup, CatalogFilter } from './CatalogFilter'; describe('Catalog Filter', () => { diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx index 5752e58215..0d645f0f35 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx @@ -14,21 +14,21 @@ * limitations under the License. */ +import { CatalogApi } from '@backstage/catalog-client'; import { Entity } from '@backstage/catalog-model'; import { ApiProvider, ApiRegistry, IdentityApi, identityApiRef, - storageApiRef, ProfileInfo, + storageApiRef, } from '@backstage/core'; import { MockStorageApi, wrapInTestApp } from '@backstage/test-utils'; import { fireEvent, render } from '@testing-library/react'; import React from 'react'; -import { catalogApiRef } from '../..'; -import { CatalogApi } from '../../api/types'; import { EntityFilterGroupsProvider } from '../../filter'; +import { catalogApiRef } from '../../plugin'; import { CatalogPage } from './CatalogPage'; describe('CatalogPage', () => { diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx index 1a9a723f16..e73a789380 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx @@ -29,9 +29,9 @@ import SettingsIcon from '@material-ui/icons/Settings'; import StarIcon from '@material-ui/icons/Star'; import React, { useCallback, useMemo, useState } from 'react'; import { Link as RouterLink } from 'react-router-dom'; -import { catalogApiRef } from '../../api/types'; import { EntityFilterGroupsProvider, useFilteredEntities } from '../../filter'; import { useStarredEntities } from '../../hooks/useStarredEntities'; +import { catalogApiRef } from '../../plugin'; import { ButtonGroup, CatalogFilter } from '../CatalogFilter/CatalogFilter'; import { CatalogTable } from '../CatalogTable/CatalogTable'; import { ResultsFilter } from '../ResultsFilter/ResultsFilter'; diff --git a/plugins/catalog/src/components/ResultsFilter/ResultsFilter.test.tsx b/plugins/catalog/src/components/ResultsFilter/ResultsFilter.test.tsx index 6f294c54ff..39837d2ca1 100644 --- a/plugins/catalog/src/components/ResultsFilter/ResultsFilter.test.tsx +++ b/plugins/catalog/src/components/ResultsFilter/ResultsFilter.test.tsx @@ -14,6 +14,7 @@ * limitations under the License. */ +import { CatalogApi } from '@backstage/catalog-client'; import { Entity } from '@backstage/catalog-model'; import { ApiProvider, @@ -25,8 +26,8 @@ import { import { MockStorageApi, wrapInTestApp } from '@backstage/test-utils'; import { render } from '@testing-library/react'; import React from 'react'; -import { CatalogApi, catalogApiRef } from '../../api/types'; import { EntityFilterGroupsProvider } from '../../filter'; +import { catalogApiRef } from '../../plugin'; import { ResultsFilter } from './ResultsFilter'; describe('Results Filter', () => { diff --git a/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx b/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx index d860ba1ea4..218cc45e78 100644 --- a/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx +++ b/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx @@ -15,7 +15,7 @@ */ import { Entity, LOCATION_ANNOTATION } from '@backstage/catalog-model'; -import { Progress, useApi, alertApiRef } from '@backstage/core'; +import { alertApiRef, Progress, useApi } from '@backstage/core'; import { Button, Dialog, @@ -31,7 +31,7 @@ import Alert from '@material-ui/lab/Alert'; import React, { FC } from 'react'; import { useAsync } from 'react-use'; import { AsyncState } from 'react-use/lib/useAsync'; -import { catalogApiRef } from '../../api/types'; +import { catalogApiRef } from '../../plugin'; type Props = { open: boolean; diff --git a/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx b/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx index ceecc17953..36cc0c56df 100644 --- a/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx +++ b/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx @@ -18,7 +18,7 @@ import { Entity } from '@backstage/catalog-model'; import { useApi } from '@backstage/core'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useAsyncFn } from 'react-use'; -import { catalogApiRef } from '../api/types'; +import { catalogApiRef } from '../plugin'; import { filterGroupsContext, FilterGroupsContext } from './context'; import { EntityFilterFn, diff --git a/plugins/catalog/src/filter/useEntityFilterGroup.test.tsx b/plugins/catalog/src/filter/useEntityFilterGroup.test.tsx index 3d11ffe7a9..6b573e3721 100644 --- a/plugins/catalog/src/filter/useEntityFilterGroup.test.tsx +++ b/plugins/catalog/src/filter/useEntityFilterGroup.test.tsx @@ -15,13 +15,13 @@ */ import { ApiProvider, ApiRegistry, storageApiRef } from '@backstage/core'; +import { MockStorageApi } from '@backstage/test-utils'; import { act, renderHook } from '@testing-library/react-hooks'; import React from 'react'; -import { catalogApiRef } from '../api/types'; +import { catalogApiRef } from '../plugin'; import { EntityFilterGroupsProvider } from './EntityFilterGroupsProvider'; -import { FilterGroupStatesReady, FilterGroup } from './types'; +import { FilterGroup, FilterGroupStatesReady } from './types'; import { useEntityFilterGroup } from './useEntityFilterGroup'; -import { MockStorageApi } from '@backstage/test-utils'; describe('useEntityFilterGroup', () => { let catalogApi: jest.Mocked; diff --git a/plugins/catalog/src/hooks/useEntity.ts b/plugins/catalog/src/hooks/useEntity.ts index 1c5b94b8c1..29ca63904d 100644 --- a/plugins/catalog/src/hooks/useEntity.ts +++ b/plugins/catalog/src/hooks/useEntity.ts @@ -18,8 +18,8 @@ import { errorApiRef, useApi } from '@backstage/core'; import { createContext, useContext, useEffect } from 'react'; import { useNavigate } from 'react-router'; import { useAsync } from 'react-use'; -import { catalogApiRef } from '../api/types'; import { useEntityCompoundName } from '../components/useEntityCompoundName'; +import { catalogApiRef } from '../plugin'; type EntityLoadingStatus = { entity?: Entity; diff --git a/plugins/catalog/src/index.ts b/plugins/catalog/src/index.ts index 762f4924c0..606ad9028d 100644 --- a/plugins/catalog/src/index.ts +++ b/plugins/catalog/src/index.ts @@ -14,12 +14,11 @@ * limitations under the License. */ -export { plugin } from './plugin'; -export * from './api/CatalogClient'; -export * from './api/types'; -export * from './routes'; -export { useEntityCompoundName } from './components/useEntityCompoundName'; -export { Router } from './components/Router'; -export { useEntity, EntityContext } from './hooks/useEntity'; +export * from '@backstage/catalog-client'; export { AboutCard } from './components/AboutCard'; export { EntityPageLayout } from './components/EntityPageLayout'; +export { Router } from './components/Router'; +export { useEntityCompoundName } from './components/useEntityCompoundName'; +export { EntityContext, useEntity } from './hooks/useEntity'; +export { catalogApiRef, plugin } from './plugin'; +export * from './routes'; diff --git a/plugins/catalog/src/plugin.ts b/plugins/catalog/src/plugin.ts index dfe12fd500..5f737ec12d 100644 --- a/plugins/catalog/src/plugin.ts +++ b/plugins/catalog/src/plugin.ts @@ -14,13 +14,19 @@ * limitations under the License. */ +import { CatalogApi, CatalogClient } from '@backstage/catalog-client'; import { - createPlugin, createApiFactory, + createApiRef, + createPlugin, discoveryApiRef, } from '@backstage/core'; -import { catalogApiRef } from './api/types'; -import { CatalogClient } from './api/CatalogClient'; + +export const catalogApiRef = createApiRef({ + id: 'plugin.catalog.service', + description: + 'Used by the Catalog plugin to make requests to accompanying backend', +}); export const plugin = createPlugin({ id: 'catalog', diff --git a/plugins/techdocs/src/reader/components/TechDocsHome.test.tsx b/plugins/techdocs/src/reader/components/TechDocsHome.test.tsx index 7754f227fc..d5d0e19e08 100644 --- a/plugins/techdocs/src/reader/components/TechDocsHome.test.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsHome.test.tsx @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { TechDocsHome } from './TechDocsHome'; -import React from 'react'; -import { render } from '@testing-library/react'; -import { wrapInTestApp } from '@backstage/test-utils'; -import { ApiRegistry, ApiProvider } from '@backstage/core-api'; -import { catalogApiRef, CatalogApi } from '@backstage/plugin-catalog'; import { Entity } from '@backstage/catalog-model'; +import { ApiProvider, ApiRegistry } from '@backstage/core-api'; +import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog'; +import { wrapInTestApp } from '@backstage/test-utils'; +import { render } from '@testing-library/react'; +import React from 'react'; +import { TechDocsHome } from './TechDocsHome'; describe('TechDocs Home', () => { const catalogApi: Partial = { From 9a294574c873baa5915b8d2daaf60a808009cf5b Mon Sep 17 00:00:00 2001 From: Tim Hansen Date: Wed, 4 Nov 2020 10:54:41 -0700 Subject: [PATCH 087/252] Fix styling issue on Cost Insights product panels with no cost --- .changeset/cost-insights-quick-suits-brake.md | 5 +++ app-config.yaml | 2 +- .../ProductInsightsCard.tsx | 43 ++++++++++--------- 3 files changed, 29 insertions(+), 21 deletions(-) create mode 100644 .changeset/cost-insights-quick-suits-brake.md diff --git a/.changeset/cost-insights-quick-suits-brake.md b/.changeset/cost-insights-quick-suits-brake.md new file mode 100644 index 0000000000..0fa4414c6b --- /dev/null +++ b/.changeset/cost-insights-quick-suits-brake.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-cost-insights': patch +--- + +Fix styling issue on Cost Insights product panels with no cost diff --git a/app-config.yaml b/app-config.yaml index bec048ce33..148627dc5a 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -253,7 +253,7 @@ costInsights: name: Cloud Storage icon: storage bigQuery: - name: Big Query + name: BigQuery icon: search metrics: DAU: diff --git a/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.tsx b/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.tsx index 17b1624c05..b78053ab5b 100644 --- a/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.tsx +++ b/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.tsx @@ -16,7 +16,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { InfoCard, useApi } from '@backstage/core'; -import { Box } from '@material-ui/core'; +import { Box, Typography } from '@material-ui/core'; import Alert from '@material-ui/lab/Alert'; import { costInsightsApiRef } from '../../api'; import { PeriodSelect } from '../PeriodSelect'; @@ -58,7 +58,7 @@ export const ProductInsightsCard = ({ product }: ProductInsightsCardProps) => { const dispatchLoadingProduct = useCallback(dispatchLoading, [product.kind]); const amount = resource?.entities?.length || 0; - const hasCostsWithinTimeframe = resource?.change && !!amount; + const hasCostsWithinTimeframe = !!(resource?.change && amount); const previousName = formatPeriod( productFilter.duration, @@ -71,9 +71,9 @@ export const ProductInsightsCard = ({ product }: ProductInsightsCardProps) => { true, ); - const subheader = amount + const subheader = hasCostsWithinTimeframe ? `${amount} ${pluralOf(amount, 'entity', 'entities')}, sorted by cost` - : `There are no ${product.name} costs within this timeframe for your team's projects.`; + : null; const costStart = resource?.aggregation[0] || 0; const costEnd = resource?.aggregation[1] || 0; @@ -144,26 +144,29 @@ export const ProductInsightsCard = ({ product }: ProductInsightsCardProps) => { return ( - {hasCostsWithinTimeframe && ( - <> - - - - - + + - + + + ) : ( + + There are no {product.name} costs within this timeframe for your + team's projects. + )} ); From 596a3b8d1488caa90e5346b0b337cc1fdc5012f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 4 Nov 2020 20:01:03 +0100 Subject: [PATCH 088/252] docs: features/software-catalog/external-integrations (#3231) --- .../software-catalog/external-integrations.md | 160 +++++++++++++++++- .../src/ingestion/processors/index.ts | 2 + 2 files changed, 157 insertions(+), 5 deletions(-) diff --git a/docs/features/software-catalog/external-integrations.md b/docs/features/software-catalog/external-integrations.md index 00a9d95fc2..325bed5e4a 100644 --- a/docs/features/software-catalog/external-integrations.md +++ b/docs/features/software-catalog/external-integrations.md @@ -5,9 +5,159 @@ description: Documentation on External integrations to integrate systems with Backstage --- -Backstage natively supports storing software components in -[metadata YAML files](descriptor-format.md). However, companies that already -have an existing system for keeping track of software and its owners can -integrate such systems with Backstage. +Backstage natively supports importing catalog data through the use of +[entity descriptor YAML files](descriptor-format.md). However, companies that +already have an existing system for keeping track of software and its owners can +leverage those systems by integrating them with Backstage. This article shows +the most common way of doing that integration: by adding a custom catalog +_processor_. -TODO: Describe the API contract. +## Background + +The catalog has a frontend plugin part, that communicates via a service API to +the backend plugin part. The backend has a processing loop that repeatedly +ingests data from the sources you specify, to store them in its database. + +As a Backstage adopter, you would be able to customize or extend the catalog in +several ways - by replacing the entire backend API, or by replacing entire +implementation classes at certain points in the backend, or by leveraging the +ingestion process to fetch data from your own authoritative source. Each method +has benefits and drawbacks, but this article will focus on the last one of the +above. It is the one that is the most straight forward and future proof, and +leverages a lot of benefits that come with the builtin catalog. + +## Processors and the Ingestion Loop + +The catalog holds a number of registered locations, that were added either by +site admins or by individual Backstage users. Their purpose is to reference some +sort of data that the catalog shall keep itself up to date with. Each location +has a `type`, and a `target` that are both strings. + +```yaml +# Example location +type: url +target: https://github.com/backstage/backstage/blob/master/catalog-info.yaml +``` + +The builtin catalog backend has an ingestion loop that periodically goes through +all of these registered locations, and pushes them and their resulting output +through the list of _processors_. + +Processors are classes that the site admin has registered with the catalog at +startup. They are at the heart of all catalog logic, and have the ability to +read the contents of locations, modify in-flight entities that were read out of +a location, perform validation, and more. The catalog comes with a set of +builtin processors, that have the ability to read from a list of well known +location types, to perform the basic processing needs, etc, but more can be +added by the organization that adopts Backstage. + +We will now show the process of creating a new processor and location type, +which enables the ingestion of catalog data from an existing external API. + +## Deciding on the New Locations + +The first step is to decide how we want to point at the system that holds our +data. Let's assume that it is internally named System-X and can be reached +through HTTP REST calls to its API. + +Let's decide that our locations shall take the following form: + +```yaml +type: system-x +target: http://systemx.services.example.net/api/v2 +``` + +It got its own made-up `type`, and the `target` conveniently points to the +actual API endpoint to talk to. + +So now we have to make the catalog aware of such a location so that it can start +feeding it into the ingestion loop. For this kind of an integration, you'd +typically want to add it to the list of statically always-available locations in +the config. + +```yaml +# In app-config.yaml +catalog: + locations: + - type: system-x + target: http://systemx.services.example.net/api/v2 +``` + +If you start up the backend now, it will start to periodically say that it could +not find a processor that supports that location. So let's make a processor that +does so! + +## Creating a Catalog Data Reader Processor + +The recommended way of instantiating the catalog backend classes is to use the +[`CatalogBuilder`](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend/src/service/CatalogBuilder.ts), +as illustrated in the +[example backend here](https://github.com/backstage/backstage/blob/master/packages/backend/src/plugins/catalog.ts). +We will create a new +[`CatalogProcessor`](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend/src/ingestion/types.ts) +subclass that can be added to this catalog builder. + +It is up to you where you put the code for this new processor class. For quick +experimentation you could place it in your backend package, but we recommend +putting all extensions like this in a backend plugin package of their own in the +`plugins` folder of your Backstage repo. + +The class will have this basic structure: + +```ts +import { UrlReader } from '@backstage/backend-common'; +import { LocationSpec } from '@backstage/catalog-model'; +import { + results, + CatalogProcessor, + CatalogProcessorEmit, +} from '@backstage/plugin-catalog-backend'; + +// A processor that reads from the fictional System-X +export class SystemXReaderProcessor implements CatalogProcessor { + constructor(private readonly reader: UrlReader) {} + + async readLocation( + location: LocationSpec, + _optional: boolean, + emit: CatalogProcessorEmit, + ): Promise { + // Pick a custom location type string. A location will be + // registered later with this type. + if (location.type !== 'system-x') { + return false; + } + + try { + // Use the builtin reader facility to grab data from the + // API. If you prefer, you can just use plain fetch here + // (from the cross-fetch package), or any other method of + // your choosing. + const data = await this.reader.read(location.target); + const json = JSON.parse(data.toString()); + // Repeatedly call emit(results.entity(location, )) + } catch (error) { + const message = `Unable to read ${location.type}, ${error}`; + emit(results.generalError(location, message)); + } + + return true; + } +} +``` + +The key points to note are: + +- Make a class that implements `CatalogProcessor` +- Only act on location types that you care about, and leave the rest alone by + returning `false` +- Read the data from the external system in any way you see fit. Use the + location `target` field if you designed it as mentioned above +- Call `emit` any number of times with the results of that process +- Finally return `true` + +You should now be able to instantiate this class in your backend, and add it to +the `CatalogBuilder` using the `addProcessors` method. + +Start up the backend - it should now start reading from the previously +registered location and you'll see your entities start to appear in Backstage. diff --git a/plugins/catalog-backend/src/ingestion/processors/index.ts b/plugins/catalog-backend/src/ingestion/processors/index.ts index d654f37dc7..ef9c6332cd 100644 --- a/plugins/catalog-backend/src/ingestion/processors/index.ts +++ b/plugins/catalog-backend/src/ingestion/processors/index.ts @@ -19,6 +19,8 @@ import * as results from './results'; export { results }; export * from './types'; +export { parseEntityYaml } from './util/parse'; + export { AnnotateLocationEntityProcessor } from './AnnotateLocationEntityProcessor'; export { CodeOwnersProcessor } from './CodeOwnersProcessor'; export { FileReaderProcessor } from './FileReaderProcessor'; From 803527bd3266d04910715e80d28290df1f14e09c Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Mon, 2 Nov 2020 16:40:29 +0100 Subject: [PATCH 089/252] Upgrade @kyma-project/asyncapi-react to 0.14.2 There is an intersting fix for displaying async messages in the docs. --- .changeset/spicy-bananas-help.md | 5 ++ plugins/api-docs/package.json | 2 +- yarn.lock | 148 +++++++++---------------------- 3 files changed, 48 insertions(+), 107 deletions(-) create mode 100644 .changeset/spicy-bananas-help.md diff --git a/.changeset/spicy-bananas-help.md b/.changeset/spicy-bananas-help.md new file mode 100644 index 0000000000..6d847f4bc3 --- /dev/null +++ b/.changeset/spicy-bananas-help.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-api-docs': patch +--- + +Upgrade @kyma-project/asyncapi-react to 0.14.2 diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 3e949bd5cc..308a2abc27 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -24,7 +24,7 @@ "@backstage/core": "^0.2.0", "@backstage/plugin-catalog": "^0.2.0", "@backstage/theme": "^0.2.0", - "@kyma-project/asyncapi-react": "^0.13.1", + "@kyma-project/asyncapi-react": "^0.14.2", "@material-icons/font": "^1.0.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", diff --git a/yarn.lock b/yarn.lock index 123b9967be..ddfccf7fd6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@apidevtools/json-schema-ref-parser@9.0.6", "@apidevtools/json-schema-ref-parser@^9.0.6": +"@apidevtools/json-schema-ref-parser@^9.0.6": version "9.0.6" resolved "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.6.tgz#5d9000a3ac1fd25404da886da6b266adcd99cf1c" integrity sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg== @@ -56,10 +56,10 @@ dependencies: tslib "~2.0.1" -"@asyncapi/avro-schema-parser@^0.1.2": - version "0.1.2" - resolved "https://registry.npmjs.org/@asyncapi/avro-schema-parser/-/avro-schema-parser-0.1.2.tgz#f6c340ccaa24bc36399d3a0f1a6e02790a948453" - integrity sha512-K4GlakiE42J9AWwAu3BWn3Qbf+N8C6vE4eEm5LEx7HqAhHG+FA0U7/3vvXoj31rnS/8sgPQpc2msLWQlmCtZyw== +"@asyncapi/avro-schema-parser@^0.2.0": + version "0.2.0" + resolved "https://registry.npmjs.org/@asyncapi/avro-schema-parser/-/avro-schema-parser-0.2.0.tgz#c9da2bb2858aca5a3b9e9a0e600084afd3765551" + integrity sha512-/oPDPudF82RGFXz5uhF77PSDZkAR+yHpdxUEbJ5jwk/X3RB74VRk8dqSgTqhUO+pLh+/Hut3x3VFacA8C9L2QA== "@asyncapi/openapi-schema-parser@^2.0.0": version "2.0.0" @@ -68,13 +68,13 @@ dependencies: "@openapi-contrib/openapi-schema-to-json-schema" "^3.0.0" -"@asyncapi/parser@^1.0.0-rc.1": - version "1.0.0-rc.2" - resolved "https://registry.npmjs.org/@asyncapi/parser/-/parser-1.0.0-rc.2.tgz#87d2e83c1d390e21d53f868e6209cbc584d35449" - integrity sha512-nZYJLnMiq48q7YHa+AI9ZaDc5UqoqKR7MDhTiIKsIJj6X5fF6hpwBrTP0bQn76Pc1oGBXRB69PcD/Y+Et1qFyw== +"@asyncapi/parser@^1.0.1": + version "1.1.0" + resolved "https://registry.npmjs.org/@asyncapi/parser/-/parser-1.1.0.tgz#b366c85a6322e182e040d60f36400a0b5c26393c" + integrity sha512-0+NeTiW5sPNYaFf4P2VCcy7Z7MLMP7H969cgOp3gvVTKroI5idkYnWo/C16tKAxs+2oOQ9WnPolsNgc+jb/qtg== dependencies: "@apidevtools/json-schema-ref-parser" "^9.0.6" - "@asyncapi/specs" "^2.7.4" + "@asyncapi/specs" "^2.7.5" "@fmvilas/pseudo-yaml-ast" "^0.3.1" ajv "^6.10.1" js-yaml "^3.13.1" @@ -82,18 +82,10 @@ node-fetch "^2.6.0" tiny-merge-patch "^0.1.2" -"@asyncapi/raml-dt-schema-parser@^2.0.0": - version "2.0.0" - resolved "https://registry.npmjs.org/@asyncapi/raml-dt-schema-parser/-/raml-dt-schema-parser-2.0.0.tgz#6e9eff89032aa7b82a963d8e72e320e493fc3835" - integrity sha512-ve41LIvbqDU8s0ZeDrWy+dyQZ/XSb7vKiJUHNgy8xcgxsz4YuttbEJbyGJvarw3qf2y+0y6cA1KUFiXoE+AIUg== - dependencies: - js-yaml "^3.13.1" - ramldt2jsonschema "^1.1.0" - -"@asyncapi/specs@^2.7.4": - version "2.7.4" - resolved "https://registry.npmjs.org/@asyncapi/specs/-/specs-2.7.4.tgz#5db4390b68aeb40d70c1723d8f46b0a091110afc" - integrity sha512-3Np9ip1Qn5AgnxTrbI0CMW2F/WUorpiAKz+GUfEy4a6GPk0eTSI6CIcbx2/jej5P91nhEyny9+D3oIzn2MreTA== +"@asyncapi/specs@^2.7.5": + version "2.7.5" + resolved "https://registry.npmjs.org/@asyncapi/specs/-/specs-2.7.5.tgz#3a516d198fc41a1103695bd889fdd4fbbebe7f5d" + integrity sha512-T1Ham9sqZKCtSowXRPaBCRy2oz3KHglqqrKiaO7lEudpP6lwH5SwXaq4qliyKzWaqd22srJHE4szdsorbFZKlw== "@babel/code-frame@7.0.0": version "7.0.0" @@ -2647,19 +2639,17 @@ dependencies: stream "^0.0.2" -"@kyma-project/asyncapi-react@^0.13.1": - version "0.13.1" - resolved "https://registry.npmjs.org/@kyma-project/asyncapi-react/-/asyncapi-react-0.13.1.tgz#8a92ee2df996540240c0afa17adfa566e4cee18d" - integrity sha512-d+52fzfBHpcC32WvrfB6sSyQFgzda/lHrKSn5klXP6tEiOU5uaLEIQzvZLCL8kXtuHiknozHO3h/BfviqEkwww== +"@kyma-project/asyncapi-react@^0.14.2": + version "0.14.2" + resolved "https://registry.npmjs.org/@kyma-project/asyncapi-react/-/asyncapi-react-0.14.2.tgz#6d99ef878f0481b05db0f3be7c94f4534176675f" + integrity sha512-lf3zcIuCTaVEIkCpF7Vwulrucgdfp1zXUIvkqjYPteOJjOuT6BAvnYwgvFUYibitmD/ADkvK8ybIeKdNJcyaPw== dependencies: - "@asyncapi/avro-schema-parser" "^0.1.2" + "@asyncapi/avro-schema-parser" "^0.2.0" "@asyncapi/openapi-schema-parser" "^2.0.0" - "@asyncapi/parser" "^1.0.0-rc.1" - "@asyncapi/raml-dt-schema-parser" "^2.0.0" + "@asyncapi/parser" "^1.0.1" constate "^1.2.0" - dompurify "^1.0.11" - json-schema-ref-parser "^9.0.6" - markdown-it "^9.1.0" + dompurify "^2.1.1" + markdown-it "^11.0.1" merge "^1.2.1" openapi-sampler "^1.0.0-beta.15" react-use "^12.2.0" @@ -6599,26 +6589,6 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@6.5.2: - version "6.5.2" - resolved "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz#678495f9b82f7cca6be248dd92f59bff5e1f4360" - integrity sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA== - dependencies: - fast-deep-equal "^2.0.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.1" - -ajv@^5.0.0: - version "5.5.2" - resolved "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" - integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU= - dependencies: - co "^4.6.0" - fast-deep-equal "^1.0.0" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.3.0" - ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.1, ajv@^6.10.2, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.5.5, ajv@^6.7.0: version "6.12.5" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da" @@ -10511,11 +10481,6 @@ domhandler@^3.0, domhandler@^3.0.0: dependencies: domelementtype "^2.0.1" -dompurify@^1.0.11: - version "1.0.11" - resolved "https://registry.npmjs.org/dompurify/-/dompurify-1.0.11.tgz#fe0f4a40d147f7cebbe31a50a1357539cfc1eb4d" - integrity sha512-XywCTXZtc/qCX3iprD1pIklRVk/uhl8BKpkTxr+ZyMVUzSUg7wkQXRBp/euJ5J5moa1QvfpvaPQVP71z1O59dQ== - dompurify@^2.0.12: version "2.1.1" resolved "https://registry.npmjs.org/dompurify/-/dompurify-2.1.1.tgz#b5aa988676b093a9c836d8b855680a8598af25fe" @@ -10526,6 +10491,11 @@ dompurify@^2.0.7: resolved "https://registry.npmjs.org/dompurify/-/dompurify-2.0.12.tgz#284a2b041e1c60b8e72d7b4d2fadad36141254ae" integrity sha512-Fl8KseK1imyhErHypFPA8qpq9gPzlsJ/EukA6yk9o0gX23p1TzC+rh9LqNg1qvErRTc0UNMYlKxEGSfSh43NDg== +dompurify@^2.1.1: + version "2.2.0" + resolved "https://registry.npmjs.org/dompurify/-/dompurify-2.2.0.tgz#51d34e76faa38b5d6b4e83a0678530f27fe3965c" + integrity sha512-bqFOQ7XRmmozp0VsKdIEe8UwZYxj0yttz7l80GBtBqdVRY48cOpXH2J/CVO7AEkV51qY0EBVXfilec18mdmQ/w== + domutils@1.5.1: version "1.5.1" resolved "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" @@ -10781,7 +10751,7 @@ enquirer@^2.3.0, enquirer@^2.3.5: dependencies: ansi-colors "^4.1.1" -entities@^1.1.1, entities@^1.1.2, entities@~1.1.1: +entities@^1.1.1, 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== @@ -11589,11 +11559,6 @@ fast-deep-equal@2.0.1, fast-deep-equal@^2.0.1: resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= -fast-deep-equal@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" - integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= - fast-deep-equal@^3.0.0, fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -15027,25 +14992,6 @@ json-schema-merge-allof@^0.6.0: json-schema-compare "^0.2.2" lodash "^4.17.4" -json-schema-migrate@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/json-schema-migrate/-/json-schema-migrate-0.2.0.tgz#ba47a5b0072fc72396460e1bd60b44d52178bbc6" - integrity sha1-ukelsAcvxyOWRg4b1gtE1SF4u8Y= - dependencies: - ajv "^5.0.0" - -json-schema-ref-parser@^9.0.6: - version "9.0.6" - resolved "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-9.0.6.tgz#fc89a5e6b853f2abe8c0af30d3874196526adb60" - integrity sha512-z0JGv7rRD3CnJbZY/qCpscyArdtLJhr/wRBmFUdoZ8xMjsFyNdILSprG2degqRLjBjyhZHAEBpGOxniO9rKTxA== - dependencies: - "@apidevtools/json-schema-ref-parser" "9.0.6" - -json-schema-traverse@^0.3.0: - version "0.3.1" - resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" - integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A= - json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -15541,6 +15487,13 @@ linkify-it@^2.0.0: 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" + integrity sha512-gDBO4aHNZS6coiZCKVhSNh43F9ioIL4JwRjLZPkoLIY4yZFwg264Y5lu2x6rb1Js42Gh6Yqm2f6L2AJcnkzinQ== + dependencies: + uc.micro "^1.0.1" + lint-staged@^10.1.0: version "10.2.11" resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-10.2.11.tgz#713c80877f2dc8b609b05bc59020234e766c9720" @@ -16160,14 +16113,14 @@ markdown-it@^10.0.0: mdurl "^1.0.1" uc.micro "^1.0.5" -markdown-it@^9.1.0: - version "9.1.0" - resolved "https://registry.npmjs.org/markdown-it/-/markdown-it-9.1.0.tgz#df9601c168568704d554b1fff9af0c5b561168d9" - integrity sha512-xHKG4C8iPriyfu/jc2hsCC045fKrMQ0VexX2F1FGYiRxDxqMB2aAhF8WauJ3fltn2kb90moGBkiiEdooGIg55w== +markdown-it@^11.0.1: + version "11.0.1" + resolved "https://registry.npmjs.org/markdown-it/-/markdown-it-11.0.1.tgz#b54f15ec2a2193efa66dda1eb4173baea08993d6" + integrity sha512-aU1TzmBKcWNNYvH9pjq6u92BML+Hz3h5S/QpfTFwiQF852pLT+9qHsrhM9JYipkOXZxGn+sGH8oyJE9FD9WezQ== dependencies: argparse "^1.0.7" - entities "~1.1.1" - linkify-it "^2.0.0" + entities "~2.0.0" + linkify-it "^3.0.1" mdurl "^1.0.1" uc.micro "^1.0.5" @@ -19485,16 +19438,6 @@ ramda@^0.26, ramda@~0.26.1: resolved "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06" integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ== -ramldt2jsonschema@^1.1.0: - version "1.2.3" - resolved "https://registry.npmjs.org/ramldt2jsonschema/-/ramldt2jsonschema-1.2.3.tgz#8d45a7f306a1169a3bde91cab725d1d6d0ff7d55" - integrity sha512-+wLDAV2NNv9NkfEUOYStaDu/6RYgYXeC1zLtXE+dMU/jDfjpN4iJnBGycDwFTFaIQGosOQhxph7fEX6Mpwxdug== - dependencies: - commander "^5.0.0" - js-yaml "^3.14.0" - json-schema-migrate "^0.2.0" - webapi-parser "^0.5.0" - randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -23602,7 +23545,7 @@ upper-case@2.0.1, upper-case@^2.0.1: dependencies: tslib "^1.10.0" -uri-js@^4.2.1, uri-js@^4.2.2: +uri-js@^4.2.2: version "4.2.2" resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== @@ -23988,13 +23931,6 @@ wcwidth@^1.0.0, wcwidth@^1.0.1: dependencies: defaults "^1.0.3" -webapi-parser@^0.5.0: - version "0.5.0" - resolved "https://registry.npmjs.org/webapi-parser/-/webapi-parser-0.5.0.tgz#2632185c5d8f3e6addb2520857af89ea9844dc14" - integrity sha512-fPt6XuMqLSvBz8exwX4QE1UT+pROLHa00EMDCdO0ybICduwQ1V4f7AWX4pNOpCp+x+0FjczEsOxtQU0d8L3QKw== - dependencies: - ajv "6.5.2" - webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" From 24b3c310002e72601f6ecfe5c40143e03d3238b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 5 Nov 2020 11:13:21 +0100 Subject: [PATCH 090/252] catalog: make the table denser (#3224) --- .../src/components/CatalogTable/CatalogTable.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index 4d97ea4334..731a8dedc9 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -68,7 +68,13 @@ const columns: TableColumn[] = [ <> {entity.metadata.tags && entity.metadata.tags.map(t => ( - + ))} ), @@ -150,10 +156,12 @@ export const CatalogTable = ({ columns={columns} options={{ paging: true, - pageSize: 10, + pageSize: 20, actionsColumnIndex: -1, loadingType: 'linear', showEmptyDataSourceMessage: !loading, + padding: 'dense', + pageSizeOptions: [20, 50, 100], }} title={`${titlePreamble} (${(entities && entities.length) || 0})`} data={entities} From 6627b626f8a3051a42c3fee5d513947d0d48d56b Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 5 Nov 2020 04:11:00 -0700 Subject: [PATCH 091/252] Fix double divider on InfoCard (#3240) Changeset Co-authored-by: Tim Hansen --- .changeset/silent-dodos-repeat.md | 5 +++ .../core/src/layout/InfoCard/InfoCard.tsx | 33 +++++++++---------- 2 files changed, 20 insertions(+), 18 deletions(-) create mode 100644 .changeset/silent-dodos-repeat.md diff --git a/.changeset/silent-dodos-repeat.md b/.changeset/silent-dodos-repeat.md new file mode 100644 index 0000000000..04978406f3 --- /dev/null +++ b/.changeset/silent-dodos-repeat.md @@ -0,0 +1,5 @@ +--- +'@backstage/core': patch +--- + +Fix divider prop not respected on InfoCard diff --git a/packages/core/src/layout/InfoCard/InfoCard.tsx b/packages/core/src/layout/InfoCard/InfoCard.tsx index d13a94375f..5d77f4f34a 100644 --- a/packages/core/src/layout/InfoCard/InfoCard.tsx +++ b/packages/core/src/layout/InfoCard/InfoCard.tsx @@ -143,7 +143,7 @@ type Props = { export const InfoCard = ({ title, subheader, - divider, + divider = true, deepLink, slackChannel = '#backstage', variant, @@ -190,23 +190,20 @@ export const InfoCard = ({ {title && ( - <> - - - + )} {actionsTopRight && ( {actionsTopRight} From b0cb451f5db14681812fdf80a530b19307beb1df Mon Sep 17 00:00:00 2001 From: Iain Billett Date: Thu, 5 Nov 2020 12:03:13 +0000 Subject: [PATCH 092/252] Chart updates (#3241) * Allow deployments to use private images The docker credentials secret can be created by: kubectl create secret docker-registry # args * Update db password secret on update too Support changing DB password on upgrade. * Update Readme * How to use private docker repos * Clear up some confusion around the ca configmap name --- contrib/chart/backstage/README.md | 24 ++++++++++++++++--- .../templates/backend-deployment.yaml | 4 ++++ .../templates/frontend-deployment.yaml | 4 ++++ .../templates/lighthouse-deployment.yaml | 4 ++++ .../templates/postgresql-password-secret.yaml | 4 ++-- 5 files changed, 35 insertions(+), 5 deletions(-) diff --git a/contrib/chart/backstage/README.md b/contrib/chart/backstage/README.md index 8b922a6b56..7b60062265 100644 --- a/contrib/chart/backstage/README.md +++ b/contrib/chart/backstage/README.md @@ -125,12 +125,14 @@ lighthouse: ``` -For the CA, create a `configMap` named `-postgres-ca` with a file called `ca.crt`: +For the CA, create a `configMap` named `--postgres-ca` with a file called `ca.crt`: ``` -kubectl create configmap my-backstage --from-file=ca.crt" +kubectl create configmap my-company-backstage-postgres-ca --from-file=ca.crt" ``` +> Where the release name contains the chart name "backstage" then only the release name will be used. + Now install the helm chart: ``` @@ -155,10 +157,26 @@ backend: lighthouse: image: - repository: tag: ``` +### Use a private docker repo + +Create a docker-registry secret + +``` +kubectl create secret docker-registry # args +``` + +> For private images on docker hub --docker-server can be set to docker.io + +Reference the secret in your chart values + +```yaml +dockerRegistrySecretName: +``` + ### Different namespace To install the charts a specific namespace use `--namespace `: diff --git a/contrib/chart/backstage/templates/backend-deployment.yaml b/contrib/chart/backstage/templates/backend-deployment.yaml index c57764db8e..99ec955242 100644 --- a/contrib/chart/backstage/templates/backend-deployment.yaml +++ b/contrib/chart/backstage/templates/backend-deployment.yaml @@ -20,6 +20,10 @@ spec: component: backend spec: + {{- if .Values.dockerRegistrySecretName }} + imagePullSecrets: + - name: {{ .Values.dockerRegistrySecretName }} + {{- end}} containers: - name: {{ .Chart.Name }}-backend image: {{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag }} diff --git a/contrib/chart/backstage/templates/frontend-deployment.yaml b/contrib/chart/backstage/templates/frontend-deployment.yaml index a5d84be2b2..5430ef041f 100644 --- a/contrib/chart/backstage/templates/frontend-deployment.yaml +++ b/contrib/chart/backstage/templates/frontend-deployment.yaml @@ -20,6 +20,10 @@ spec: component: frontend spec: + {{- if .Values.dockerRegistrySecretName }} + imagePullSecrets: + - name: {{ .Values.dockerRegistrySecretName }} + {{- end}} containers: - name: {{ .Chart.Name }}-frontend image: {{ .Values.frontend.image.repository }}:{{ .Values.frontend.image.tag }} diff --git a/contrib/chart/backstage/templates/lighthouse-deployment.yaml b/contrib/chart/backstage/templates/lighthouse-deployment.yaml index 70341a2a22..13bb8eadbb 100644 --- a/contrib/chart/backstage/templates/lighthouse-deployment.yaml +++ b/contrib/chart/backstage/templates/lighthouse-deployment.yaml @@ -21,6 +21,10 @@ spec: component: lighthouse-audit-service spec: + {{- if .Values.dockerRegistrySecretName }} + imagePullSecrets: + - name: {{ .Values.dockerRegistrySecretName }} + {{- end}} containers: - name: lighthouse-audit-service image: {{ .Values.lighthouse.image.repository }}:{{ .Values.lighthouse.image.tag }} diff --git a/contrib/chart/backstage/templates/postgresql-password-secret.yaml b/contrib/chart/backstage/templates/postgresql-password-secret.yaml index 9685130a03..e53369adb9 100644 --- a/contrib/chart/backstage/templates/postgresql-password-secret.yaml +++ b/contrib/chart/backstage/templates/postgresql-password-secret.yaml @@ -8,7 +8,7 @@ metadata: labels: release: {{ .Release.Name }} annotations: - "helm.sh/hook": "pre-install" + "helm.sh/hook": "pre-install,pre-upgrade" "helm.sh/hook-delete-policy": "before-hook-creation" data: postgresql-password: {{ .Values.appConfig.backend.database.connection.password | b64enc }} @@ -23,7 +23,7 @@ metadata: labels: release: {{ .Release.Name }} annotations: - "helm.sh/hook": "pre-install" + "helm.sh/hook": "pre-install,pre-upgrade" "helm.sh/hook-delete-policy": "before-hook-creation" data: postgresql-password: {{ .Values.lighthouse.database.connection.password | b64enc }} From 8683b1af3808f48e8402653cd826dcff7de2e7ad Mon Sep 17 00:00:00 2001 From: Abhishek Jakhar Date: Thu, 5 Nov 2020 17:34:42 +0530 Subject: [PATCH 093/252] fix: tutorials markdown (#3244) --- docs/tutorials/journey.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/tutorials/journey.md b/docs/tutorials/journey.md index 7627920a75..664d4b77d3 100644 --- a/docs/tutorials/journey.md +++ b/docs/tutorials/journey.md @@ -264,14 +264,9 @@ release of Sam's plugin specifies a dependency on Backstage with a minimum version set to the same release as the one were the annotation was added to the core schema. -

- - # 9. Revenge of the Sam Sam, now in full control of all theme tunes in Backstage, releases v2.0.1, which switches all tracks to 4uLU6hMCjMI75M1A2tKUQC. Sam wanted to do something more nefarious, but since Backstage sandboxes sensitive actions and is mostly read-only with strict CSP, Sam's hands were tied. - -
From 3f05616bf563e432586e3449d9e130b1eb47fcf0 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Thu, 5 Nov 2020 13:05:05 +0100 Subject: [PATCH 094/252] fix text color of tech-radar footer (#3245) --- .changeset/smooth-hairs-breathe.md | 5 +++++ .../tech-radar/src/components/RadarFooter/RadarFooter.tsx | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 .changeset/smooth-hairs-breathe.md diff --git a/.changeset/smooth-hairs-breathe.md b/.changeset/smooth-hairs-breathe.md new file mode 100644 index 0000000000..2849a1d4b7 --- /dev/null +++ b/.changeset/smooth-hairs-breathe.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-tech-radar': patch +--- + +Make the footer color of the tech-radar work in both light and dark theme. diff --git a/plugins/tech-radar/src/components/RadarFooter/RadarFooter.tsx b/plugins/tech-radar/src/components/RadarFooter/RadarFooter.tsx index ced1ab8054..219db714af 100644 --- a/plugins/tech-radar/src/components/RadarFooter/RadarFooter.tsx +++ b/plugins/tech-radar/src/components/RadarFooter/RadarFooter.tsx @@ -15,19 +15,19 @@ */ import React from 'react'; -import { makeStyles, Theme } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core'; export type Props = { x: number; y: number; }; -const useStyles = makeStyles(() => ({ +const useStyles = makeStyles(theme => ({ text: { pointerEvents: 'none', userSelect: 'none', fontSize: '10px', - fill: '#000', + fill: theme.palette.text.secondary, }, })); From e24e46b0b92703226d33a0d079e8d8a10688b370 Mon Sep 17 00:00:00 2001 From: Mateusz Lewtak Date: Thu, 5 Nov 2020 13:40:25 +0100 Subject: [PATCH 095/252] Feat: update plugins to new versions --- package.json | 7 -- packages/app/package.json | 6 +- yarn.lock | 195 +++++--------------------------------- 3 files changed, 27 insertions(+), 181 deletions(-) diff --git a/package.json b/package.json index da1d4de154..505ee0caad 100644 --- a/package.json +++ b/package.json @@ -36,13 +36,6 @@ "plugins/*" ] }, - "resolutions": { - "**/@roadiehq/backstage-plugin-*/@backstage/catalog-model": "0.2.0", - "**/@roadiehq/backstage-plugin-*/@backstage/core": "0.2.0", - "**/@roadiehq/backstage-plugin-*/@backstage/core-api": "0.2.0", - "**/@roadiehq/backstage-plugin-*/@backstage/plugin-catalog": "0.2.0", - "**/@roadiehq/backstage-plugin-*/@backstage/theme": "0.2.0" - }, "version": "1.0.0", "devDependencies": { "@changesets/cli": "^2.11.0", diff --git a/packages/app/package.json b/packages/app/package.json index 514f8867c9..5cf74b0fbb 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -34,9 +34,9 @@ "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@octokit/rest": "^18.0.0", - "@roadiehq/backstage-plugin-github-insights": "^0.2.10", - "@roadiehq/backstage-plugin-github-pull-requests": "^0.6.0", - "@roadiehq/backstage-plugin-travis-ci": "^0.2.6", + "@roadiehq/backstage-plugin-github-insights": "^0.2.12", + "@roadiehq/backstage-plugin-github-pull-requests": "^0.6.1", + "@roadiehq/backstage-plugin-travis-ci": "^0.2.7", "history": "^5.0.0", "prop-types": "^15.7.2", "react": "^16.12.0", diff --git a/yarn.lock b/yarn.lock index ddfccf7fd6..0f16311d80 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1289,153 +1289,6 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" -"@backstage/catalog-model@^0.1.1-alpha.24": - version "0.2.0" - dependencies: - "@backstage/config" "^0.1.1" - "@types/json-schema" "^7.0.5" - "@types/yup" "^0.29.8" - json-schema "^0.2.5" - lodash "^4.17.15" - uuid "^8.0.0" - yup "^0.29.3" - -"@backstage/catalog-model@^0.1.1-alpha.25": - version "0.2.0" - dependencies: - "@backstage/config" "^0.1.1" - "@types/json-schema" "^7.0.5" - "@types/yup" "^0.29.8" - json-schema "^0.2.5" - lodash "^4.17.15" - uuid "^8.0.0" - yup "^0.29.3" - -"@backstage/core-api@^0.1.1-alpha.24": - version "0.2.0" - dependencies: - "@backstage/config" "^0.1.1" - "@backstage/test-utils" "^0.1.2" - "@backstage/theme" "^0.2.0" - "@material-ui/core" "^4.11.0" - "@material-ui/icons" "^4.9.1" - "@types/react" "^16.9" - prop-types "^15.7.2" - react "^16.12.0" - react-router-dom "6.0.0-beta.0" - react-use "^15.3.3" - zen-observable "^0.8.15" - -"@backstage/core@^0.1.1-alpha.24": - version "0.2.0" - dependencies: - "@backstage/config" "^0.1.1" - "@backstage/core-api" "^0.2.0" - "@backstage/theme" "^0.2.0" - "@material-ui/core" "^4.11.0" - "@material-ui/icons" "^4.9.1" - "@material-ui/lab" "4.0.0-alpha.45" - "@types/react" "^16.9" - "@types/react-sparklines" "^1.7.0" - classnames "^2.2.6" - clsx "^1.1.0" - immer "^7.0.9" - lodash "^4.17.15" - material-table "^1.69.1" - prop-types "^15.7.2" - rc-progress "^3.0.0" - react "^16.12.0" - react-dom "^16.12.0" - react-helmet "6.1.0" - react-hook-form "^6.6.0" - react-router "6.0.0-beta.0" - react-router-dom "6.0.0-beta.0" - react-sparklines "^1.7.0" - react-syntax-highlighter "^13.5.1" - react-use "^15.3.3" - -"@backstage/core@^0.1.1-alpha.25": - version "0.2.0" - dependencies: - "@backstage/config" "^0.1.1" - "@backstage/core-api" "^0.2.0" - "@backstage/theme" "^0.2.0" - "@material-ui/core" "^4.11.0" - "@material-ui/icons" "^4.9.1" - "@material-ui/lab" "4.0.0-alpha.45" - "@types/react" "^16.9" - "@types/react-sparklines" "^1.7.0" - classnames "^2.2.6" - clsx "^1.1.0" - immer "^7.0.9" - lodash "^4.17.15" - material-table "^1.69.1" - prop-types "^15.7.2" - rc-progress "^3.0.0" - react "^16.12.0" - react-dom "^16.12.0" - react-helmet "6.1.0" - react-hook-form "^6.6.0" - react-router "6.0.0-beta.0" - react-router-dom "6.0.0-beta.0" - react-sparklines "^1.7.0" - react-syntax-highlighter "^13.5.1" - react-use "^15.3.3" - -"@backstage/plugin-catalog@^0.1.1-alpha.24": - version "0.2.0" - dependencies: - "@backstage/catalog-model" "^0.2.0" - "@backstage/core" "^0.2.0" - "@backstage/plugin-scaffolder" "^0.2.0" - "@backstage/plugin-techdocs" "^0.2.0" - "@backstage/theme" "^0.2.0" - "@material-ui/core" "^4.11.0" - "@material-ui/icons" "^4.9.1" - "@material-ui/lab" "4.0.0-alpha.45" - "@types/react" "^16.9" - classnames "^2.2.6" - moment "^2.26.0" - react "^16.13.1" - react-dom "^16.13.1" - react-helmet "6.1.0" - react-router "6.0.0-beta.0" - react-router-dom "6.0.0-beta.0" - react-use "^15.3.3" - swr "^0.3.0" - -"@backstage/plugin-catalog@^0.1.1-alpha.25": - version "0.2.0" - dependencies: - "@backstage/catalog-model" "^0.2.0" - "@backstage/core" "^0.2.0" - "@backstage/plugin-scaffolder" "^0.2.0" - "@backstage/plugin-techdocs" "^0.2.0" - "@backstage/theme" "^0.2.0" - "@material-ui/core" "^4.11.0" - "@material-ui/icons" "^4.9.1" - "@material-ui/lab" "4.0.0-alpha.45" - "@types/react" "^16.9" - classnames "^2.2.6" - moment "^2.26.0" - react "^16.13.1" - react-dom "^16.13.1" - react-helmet "6.1.0" - react-router "6.0.0-beta.0" - react-router-dom "6.0.0-beta.0" - react-use "^15.3.3" - swr "^0.3.0" - -"@backstage/theme@^0.1.1-alpha.24": - version "0.2.0" - dependencies: - "@material-ui/core" "^4.11.0" - -"@backstage/theme@^0.1.1-alpha.25": - version "0.2.0" - dependencies: - "@material-ui/core" "^4.11.0" - "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -3797,14 +3650,14 @@ resolved "https://registry.npmjs.org/@rjsf/material-ui/-/material-ui-2.4.0.tgz#1b5859298bf3f61137d7b05084f058a775d6fd73" integrity sha512-U8F/suzg4MuV+8mK1/ufs0Y6c3O8hc1wnuD2IKoOVJvegGfz5JCafyoyGAW6iyuT1DZBMPzVWEqfiuYPmoE7pw== -"@roadiehq/backstage-plugin-github-insights@^0.2.10": - version "0.2.10" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-insights/-/backstage-plugin-github-insights-0.2.10.tgz#f58298486183e2af36b2c292d6145f2a35eb6fdb" - integrity sha512-vmIbjehJpoXjuLNX5cYbZTG6U0tHVQWrGUA5fYWQtAIVC0cmwbkuvW8WoNymAietGmlshm+0rV6MK0/vd2iu4A== +"@roadiehq/backstage-plugin-github-insights@^0.2.12": + version "0.2.12" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-insights/-/backstage-plugin-github-insights-0.2.12.tgz#aeda6306769f376cf6b5b9d74268b060a0f4e764" + integrity sha512-f9g5ajVWQkywoYo0zDUZV3g0SPxOTi3MvMuhUvgaZ/XKJr9mBZTe5qHnEYBSHOCB88l0Y2g0qHNdbjXhbFpokQ== dependencies: - "@backstage/catalog-model" "^0.1.1-alpha.25" - "@backstage/core" "^0.1.1-alpha.25" - "@backstage/theme" "^0.1.1-alpha.25" + "@backstage/catalog-model" "^0.2.0" + "@backstage/core" "^0.2.0" + "@backstage/theme" "^0.2.0" "@material-ui/core" "^4.11.0" "@material-ui/icons" "^4.9.1" "@material-ui/lab" "4.0.0-alpha.45" @@ -3818,15 +3671,15 @@ react-use "^15.3.3" remark-gfm "^1.0.0" -"@roadiehq/backstage-plugin-github-pull-requests@^0.6.0": - version "0.6.0" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-pull-requests/-/backstage-plugin-github-pull-requests-0.6.0.tgz#5d39eb237aa39b76bb7e5e867355abeccac7e5ff" - integrity sha512-cwTofvhSzb3FyyJ/HRixPkn9Ayg3x4tv6FlHSswlMEDx8EJv0hmfNXVps2QAG/82JgACTxmXKNQRnPQ0+TLiOw== +"@roadiehq/backstage-plugin-github-pull-requests@^0.6.1": + version "0.6.1" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-pull-requests/-/backstage-plugin-github-pull-requests-0.6.1.tgz#bce3ce93af7b2f6c38d4665c8a0d8a6b2eaa4212" + integrity sha512-3r0BBV6kRlbrlfVA/G7Tc0xbw9A+9roihspCaV/yhhqXgk/ik5v6ftEautlx45+gKaFMp+jgmfKHqFgOdSyhZw== dependencies: - "@backstage/catalog-model" "^0.1.1-alpha.25" - "@backstage/core" "^0.1.1-alpha.25" - "@backstage/plugin-catalog" "^0.1.1-alpha.25" - "@backstage/theme" "^0.1.1-alpha.25" + "@backstage/catalog-model" "^0.2.0" + "@backstage/core" "^0.2.0" + "@backstage/plugin-catalog" "^0.2.0" + "@backstage/theme" "^0.2.0" "@material-ui/core" "^4.11.0" "@material-ui/icons" "^4.9.1" "@material-ui/lab" "^4.0.0-alpha.56" @@ -3840,16 +3693,16 @@ react-router "6.0.0-beta.0" react-use "^15.3.3" -"@roadiehq/backstage-plugin-travis-ci@^0.2.6": - version "0.2.6" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-travis-ci/-/backstage-plugin-travis-ci-0.2.6.tgz#c253d4009b7150faa285786df7104f04cf99d4c2" - integrity sha512-Zs7mPmJifz08vNsS4zDzupexCEKAxPtqT4Vh8DhFJrphsiYwSIdPjW5JWif/XQIx0025Ew/RwuyMtwz2V/iWZg== +"@roadiehq/backstage-plugin-travis-ci@^0.2.7": + version "0.2.7" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-travis-ci/-/backstage-plugin-travis-ci-0.2.7.tgz#bc7968b461016b2710794d10766266de9ab4f759" + integrity sha512-uXF5t2uZqd9TNGFSMYlyk6NAweVb5KLlM4GMiltattzxRWiqbCp5VumubrLTTsA8dvsWCrXWfhe95MStcUnl+A== dependencies: - "@backstage/catalog-model" "^0.1.1-alpha.24" - "@backstage/core" "^0.1.1-alpha.24" - "@backstage/core-api" "^0.1.1-alpha.24" - "@backstage/plugin-catalog" "^0.1.1-alpha.24" - "@backstage/theme" "^0.1.1-alpha.24" + "@backstage/catalog-model" "^0.2.0" + "@backstage/core" "^0.2.0" + "@backstage/core-api" "^0.2.0" + "@backstage/plugin-catalog" "^0.2.0" + "@backstage/theme" "^0.2.0" "@material-ui/core" "^4.9.1" "@material-ui/icons" "^4.9.1" "@material-ui/lab" "4.0.0-alpha.45" From 47a611f1155d2d994559513f15f23185cb5bd3f8 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Thu, 5 Nov 2020 14:58:48 +0100 Subject: [PATCH 096/252] TechDocs: Remove techdocs-container package from mono-repo (#3230) * Move techdocs-container package to backstage/techdocs-container * Clean up codeowners * Remove unnecessary GitHub CI workflow * Modify path in docs for techdocs-container * Fix techdocs-cli URL * Remove techdocs packages from docs/support/packages --- .github/CODEOWNERS | 1 - .github/workflows/techdocs.yml | 33 ------- docs/features/techdocs/FAQ.md | 5 +- docs/features/techdocs/README.md | 10 +- docs/features/techdocs/concepts.md | 2 +- docs/support/project-structure.md | 6 -- .../blog/2020-09-08-announcing-tech-docs.md | 2 +- packages/techdocs-container/Dockerfile | 28 ------ packages/techdocs-container/README.md | 31 ------ .../techdocs-container/bin/scripts/plantuml | 0 .../techdocs-container/mock-docs/.gitignore | 1 - .../mock-docs/docs/index.md | 99 ------------------- .../techdocs-container/mock-docs/mkdocs.yml | 9 -- .../mock-docs/sub-docs/docs/index.md | 1 - .../mock-docs/sub-docs/mkdocs.yml | 4 - 15 files changed, 8 insertions(+), 224 deletions(-) delete mode 100644 .github/workflows/techdocs.yml delete mode 100644 packages/techdocs-container/Dockerfile delete mode 100644 packages/techdocs-container/README.md delete mode 100644 packages/techdocs-container/bin/scripts/plantuml delete mode 100644 packages/techdocs-container/mock-docs/.gitignore delete mode 100644 packages/techdocs-container/mock-docs/docs/index.md delete mode 100644 packages/techdocs-container/mock-docs/mkdocs.yml delete mode 100644 packages/techdocs-container/mock-docs/sub-docs/docs/index.md delete mode 100644 packages/techdocs-container/mock-docs/sub-docs/mkdocs.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index cf28c55cb3..7cef5afce0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -11,6 +11,5 @@ /plugins/techdocs @backstage/techdocs-core /plugins/techdocs-backend @backstage/techdocs-core /packages/techdocs-cli @backstage/techdocs-core -/packages/techdocs-container @backstage/techdocs-core /.github/workflows/techdocs.yml @backstage/techdocs-core /.changeset/cost-insights-* @backstage/silver-lining diff --git a/.github/workflows/techdocs.yml b/.github/workflows/techdocs.yml deleted file mode 100644 index 9b5ea50217..0000000000 --- a/.github/workflows/techdocs.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: TechDocs - -on: - pull_request: - paths: - - '.github/workflows/techdocs.yml' - - 'packages/techdocs-container/**' - - 'packages/techdocs-cli/**' - - 'plugins/techdocs/**' - - 'plugins/techdocs-backend/**' - -jobs: - build: - runs-on: ${{ matrix.os }} - - strategy: - matrix: - os: [ubuntu-latest] - python-version: [3.7] - - env: - TECHDOCS_CORE_PATH: ./packages/techdocs-container/techdocs-core - - name: Python ${{ matrix.node-version }} on ${{ matrix.os }} - steps: - - uses: actions/checkout@v2 - - # Build Docker Image - - name: Build Docker image - uses: docker/build-push-action@v1.1.0 - with: - path: packages/techdocs-container - push: false diff --git a/docs/features/techdocs/FAQ.md b/docs/features/techdocs/FAQ.md index 257d6e6f7a..de424778cb 100644 --- a/docs/features/techdocs/FAQ.md +++ b/docs/features/techdocs/FAQ.md @@ -17,9 +17,8 @@ This page answers frequently asked questions about [TechDocs](README.md). TechDocs is using [MkDocs](https://www.mkdocs.org/) to build project documentation under the hood. Documentation built with the -[techdocs-container](https://github.com/backstage/backstage/blob/master/packages/techdocs-container/README.md) -is using the MkDocs -[Material Theme](https://github.com/squidfunk/mkdocs-material). +[techdocs-container](https://github.com/backstage/techdocs-container) is using +the MkDocs [Material Theme](https://github.com/squidfunk/mkdocs-material). #### What is the mkdocs-techdocs-core plugin? diff --git a/docs/features/techdocs/README.md b/docs/features/techdocs/README.md index a16776868a..f1cf637997 100644 --- a/docs/features/techdocs/README.md +++ b/docs/features/techdocs/README.md @@ -109,8 +109,8 @@ migrate Spotify's existing TechDocs features to open source. | ------------------------------------------- | -------------------------------------------------------- | | Frontend | [`@backstage/plugin-techdocs`][techdocs/frontend] | | Backend | [`@backstage/plugin-techdocs-backend`][techdocs/backend] | -| Docker Container (for generating doc sites) | [`packages/techdocs-container`][techdocs/container] | -| CLI (for local development) | [`packages/techdocs-cli`][techdocs/cli] | +| Docker Container (for generating doc sites) | [`techdocs-container`][techdocs/container] | +| CLI (for local development) | [`@techdocs/cli`][techdocs/cli] | [getting started]: getting-started.md [concepts]: concepts.md @@ -120,7 +120,5 @@ migrate Spotify's existing TechDocs features to open source. https://github.com/backstage/backstage/blob/master/plugins/techdocs [techdocs/backend]: https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend -[techdocs/container]: - https://github.com/backstage/backstage/blob/master/packages/techdocs-container -[techdocs/cli]: - https://github.com/backstage/backstage/blob/master/packages/techdocs-cli +[techdocs/container]: https://github.com/backstage/techdocs-container +[techdocs/cli]: https://github.com/backstage/techdocs-cli diff --git a/docs/features/techdocs/concepts.md b/docs/features/techdocs/concepts.md index ed1fc2220c..c614965ab9 100644 --- a/docs/features/techdocs/concepts.md +++ b/docs/features/techdocs/concepts.md @@ -23,7 +23,7 @@ The TechDocs container is a Docker container available at pages, including stylesheets and scripts from Python flavored Markdown, through MkDocs. -[TechDocs Container](https://github.com/backstage/backstage/blob/master/packages/techdocs-container/README.md) +[TechDocs Container](https://github.com/backstage/techdocs-container) ### TechDocs publisher diff --git a/docs/support/project-structure.md b/docs/support/project-structure.md index 9bf143e0f2..034fba4cb8 100644 --- a/docs/support/project-structure.md +++ b/docs/support/project-structure.md @@ -168,12 +168,6 @@ are separated out into their own folder, see further down. package. The Backstage Storybook is found [here](https://backstage.io/storybook) -- [`techdocs-cli/`](https://github.com/backstage/backstage/tree/master/packages/techdocs-cli) - - Used for verifying TechDocs locally. - -- [`techdocs-container/`](https://github.com/backstage/backstage/tree/master/packages/techdocs-container) - - Used by the `techdocs-cli` - - [`test-utils-core/`](https://github.com/backstage/backstage/tree/master/packages/test-utils-core) - [`test-utils/`](https://github.com/backstage/backstage/tree/master/packages/test-utils) diff --git a/microsite/blog/2020-09-08-announcing-tech-docs.md b/microsite/blog/2020-09-08-announcing-tech-docs.md index ecf678a5bd..ceab17d7be 100644 --- a/microsite/blog/2020-09-08-announcing-tech-docs.md +++ b/microsite/blog/2020-09-08-announcing-tech-docs.md @@ -23,7 +23,7 @@ But this is just one way to do it. Today we’re most excited for what the open If you go to [GitHub](https://github.com/backstage/backstage/tree/master/plugins) now, you’ll find everything you need to start collaborating with us to build out the docs-like-code Backstage plugin — we’ll call it TechDocs in the open as well. -You’ll find the code in [techdocs](https://github.com/backstage/backstage/tree/master/plugins/techdocs) (frontend) and [techdocs-backend](https://github.com/backstage/backstage/tree/master/plugins/techdocs-backend). (There are also two separate packages [techdocs-cli](https://github.com/backstage/backstage/tree/master/packages/techdocs-cli) and [techdocs-container](https://github.com/backstage/backstage/tree/master/packages/techdocs-container).) +You’ll find the code in [techdocs](https://github.com/backstage/backstage/tree/master/plugins/techdocs) (frontend) and [techdocs-backend](https://github.com/backstage/backstage/tree/master/plugins/techdocs-backend). (There are also two separate packages [techdocs-cli](https://github.com/backstage/techdocs-cli) and [techdocs-container](https://github.com/backstage/techdocs-container).) You’ll find issues to work on in the [issues queue](https://github.com/backstage/backstage/issues?q=is%3Aissue+is%3Aopen+label%3A%22docs-like-code%22+label%3A%22help+wanted%22), typically starting with TechDocs: and labeled with docs-like-code, some labeled good first issue. Feel free to add your own issues, of course. diff --git a/packages/techdocs-container/Dockerfile b/packages/techdocs-container/Dockerfile deleted file mode 100644 index c30db043cd..0000000000 --- a/packages/techdocs-container/Dockerfile +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2020 Spotify AB -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM python:3.8-alpine - - -RUN apk update && apk --no-cache add gcc musl-dev openjdk11-jdk curl graphviz ttf-dejavu fontconfig - -RUN curl -o plantuml.jar -L http://sourceforge.net/projects/plantuml/files/plantuml.1.2020.16.jar/download && echo "c789ace48347c43073232b1458badc5810c01fe8 plantuml.jar" | sha1sum -c - && mv plantuml.jar /opt/plantuml.jar -RUN pip install --upgrade pip && pip install mkdocs-techdocs-core==0.0.11 - -# Create script to call plantuml.jar from a location in path - -RUN echo $'#!/bin/sh\n\njava -jar '/opt/plantuml.jar' ${@}' >> /usr/local/bin/plantuml -RUN chmod 755 /usr/local/bin/plantuml - -ENTRYPOINT [ "mkdocs" ] diff --git a/packages/techdocs-container/README.md b/packages/techdocs-container/README.md deleted file mode 100644 index 53a518fa97..0000000000 --- a/packages/techdocs-container/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# techdocs-container - -This is the Docker container that powers the creation of static documentation sites that are supported by [TechDocs](https://github.com/backstage/backstage/blob/master/plugins/techdocs). - -## Getting Started - -Using the TechDocs CLI, we can invoke the latest version of `techdocs-container` via Docker Hub: - -```bash -npx techdocs-cli serve -``` - -## Local Development - -```bash -docker build . -t mkdocs:local-dev - -docker run -w /content -v $(pwd)/mock-docs:/content -p 8000:8000 -it mkdocs:local-dev serve -a 0.0.0.0:8000 -``` - -Then open up `http://localhost:8000` on your local machine. - -## Publishing - -This container is published on DockerHub - https://hub.docker.com/r/spotify/techdocs - -The publishing is configured by [Automated Builds](https://hub.docker.com/repository/docker/spotify/techdocs/builds/edit) feature on Docker Hub which is triggered from GitHub (on new commits and releases). @backstage/techdocs-core team has access to the settings. - -The `latest` tag on Docker Hub points to the recent commits in the `master` branch. The [version tags](https://hub.docker.com/r/spotify/techdocs/tags) (e.g. v0.1.1-alpha.24) point to the GitHub tags created from [releases](https://github.com/backstage/backstage/releases) of this GitHub repository. - -Note: We recommend using a specific version of the container instead of `latest` release for stability and avoiding unexpected changes. diff --git a/packages/techdocs-container/bin/scripts/plantuml b/packages/techdocs-container/bin/scripts/plantuml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/techdocs-container/mock-docs/.gitignore b/packages/techdocs-container/mock-docs/.gitignore deleted file mode 100644 index 45ddf0ae39..0000000000 --- a/packages/techdocs-container/mock-docs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -site/ diff --git a/packages/techdocs-container/mock-docs/docs/index.md b/packages/techdocs-container/mock-docs/docs/index.md deleted file mode 100644 index 94a53acfa3..0000000000 --- a/packages/techdocs-container/mock-docs/docs/index.md +++ /dev/null @@ -1,99 +0,0 @@ -## hello mock docs - -!!! test -Testing something - -Abbreviations: -Some text about MOCDOC - -This is a paragraph. -{: #test_id .test_class } - -Apple -: Pomaceous fruit of plants of the genus Malus in -the family Rosaceae. - -```javascript -import { test } from 'something'; - -const addThingToThing = (a, b) a + b; -``` - -- [abc](#abc) -- [xyz](#xyz) - -## abc - -This is a b c. - -## xyz - -This is x y z. - -# The attack plan - -{% dot attack_plan.svg - digraph G { - rankdir=LR - Earth [peripheries=2] - Mars - Earth -> Mars - } -%} - -```graphviz dot attack_plan.svg -digraph G { - rankdir=LR - Earth [peripheries=2] - Mars - Earth -> Mars -} -``` - -# PlantUML Samples - -```plantuml classes="uml myDiagram" alt="Diagram placeholder" title="My diagram" -@startuml - Goofy -> MickeyMouse: calls - Goofy <-- MickeyMouse: responds -@enduml -``` - -:bulb: - -=== "JavaScript" - - ```javascript - import { test } from 'something'; - - const addThingToThing = (a, b) a + b; - ``` - -=== "Java" - - ```java - public void function() { - test(); - } - ``` - -```java tab="java" - public void function() { - test(); - } -``` - -```java tab="java 2" - public void function() { - test(); - } -``` - -```javascript -import { test } from 'something'; - -const addThingToThing = (a, b) a + b; -``` - - -*[MOCDOC]: Mock Documentation diff --git a/packages/techdocs-container/mock-docs/mkdocs.yml b/packages/techdocs-container/mock-docs/mkdocs.yml deleted file mode 100644 index d769fddfcf..0000000000 --- a/packages/techdocs-container/mock-docs/mkdocs.yml +++ /dev/null @@ -1,9 +0,0 @@ -site_name: 'mock-docs' -site_description: 'mock-docs site description' - -nav: - - Home: index.md - - SubDocs: '!include ./sub-docs/mkdocs.yml' - -plugins: - - techdocs-core diff --git a/packages/techdocs-container/mock-docs/sub-docs/docs/index.md b/packages/techdocs-container/mock-docs/sub-docs/docs/index.md deleted file mode 100644 index 65c6644ef8..0000000000 --- a/packages/techdocs-container/mock-docs/sub-docs/docs/index.md +++ /dev/null @@ -1 +0,0 @@ -### This is an md file in another docs folder using the [MkDocs Monorepo Plugin](https://github.com/spotify/mkdocs-monorepo-plugin) diff --git a/packages/techdocs-container/mock-docs/sub-docs/mkdocs.yml b/packages/techdocs-container/mock-docs/sub-docs/mkdocs.yml deleted file mode 100644 index 4490ddbefa..0000000000 --- a/packages/techdocs-container/mock-docs/sub-docs/mkdocs.yml +++ /dev/null @@ -1,4 +0,0 @@ -site_name: subdocs - -nav: - - Home 2: 'index.md' From 728f772d15fb93baac034af2032f3c1a4150ea34 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 5 Nov 2020 15:05:29 +0100 Subject: [PATCH 097/252] CLI: Bypass build error to complete plugin creation (#3242) * Bypass build error to complete plugin creation * Stop building and linting plugin if error occures --- .../src/commands/create-plugin/createPlugin.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index c9f8b3a81d..a2005c5580 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -140,15 +140,21 @@ async function buildPlugin(pluginFolder: string) { 'yarn build', ]; for (const command of commands) { - await Task.forItem('executing', command, async () => { - process.chdir(pluginFolder); - - await exec(command).catch(error => { + try { + await Task.forItem('executing', command, async () => { + process.chdir(pluginFolder); + await exec(command); + }).catch(error => { process.stdout.write(error.stderr); process.stdout.write(error.stdout); - throw new Error(`Could not execute command ${chalk.cyan(command)}`); + throw new Error( + `Warning: Could not execute command ${chalk.cyan(command)}`, + ); }); - }); + } catch (error) { + Task.error(error.message); + break; + } } } From e677dbd3c07ca40e8f676f4cf55c49679d8f85d9 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Wed, 4 Nov 2020 11:17:46 +0100 Subject: [PATCH 098/252] Move techdocs-cli to backstage/techdocs-cli repository --- packages/techdocs-cli/.eslintrc.js | 6 - packages/techdocs-cli/CHANGELOG.md | 17 --- packages/techdocs-cli/README.md | 48 -------- packages/techdocs-cli/bin/build.sh | 33 ----- packages/techdocs-cli/bin/techdocs-cli | 36 ------ packages/techdocs-cli/package.json | 50 -------- packages/techdocs-cli/src/index.ts | 126 -------------------- packages/techdocs-cli/src/lib/httpServer.ts | 79 ------------ 8 files changed, 395 deletions(-) delete mode 100644 packages/techdocs-cli/.eslintrc.js delete mode 100644 packages/techdocs-cli/CHANGELOG.md delete mode 100644 packages/techdocs-cli/README.md delete mode 100755 packages/techdocs-cli/bin/build.sh delete mode 100755 packages/techdocs-cli/bin/techdocs-cli delete mode 100644 packages/techdocs-cli/package.json delete mode 100644 packages/techdocs-cli/src/index.ts delete mode 100644 packages/techdocs-cli/src/lib/httpServer.ts diff --git a/packages/techdocs-cli/.eslintrc.js b/packages/techdocs-cli/.eslintrc.js deleted file mode 100644 index 503c048748..0000000000 --- a/packages/techdocs-cli/.eslintrc.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - extends: [require.resolve('@backstage/cli/config/eslint.backend')], - rules: { - 'no-console': 0, - }, -}; diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md deleted file mode 100644 index 9add3bb173..0000000000 --- a/packages/techdocs-cli/CHANGELOG.md +++ /dev/null @@ -1,17 +0,0 @@ -# @techdocs/cli - -## 0.1.2 - -### Patch Changes - -- Updated dependencies [28edd7d29] -- Updated dependencies [3472c8be7] -- Updated dependencies [1d0aec70f] -- Updated dependencies [a3840bed2] -- Updated dependencies [72f6cda35] -- Updated dependencies [8c2b76e45] -- Updated dependencies [cba4e4d97] -- Updated dependencies [8afce088a] -- Updated dependencies [9a3b3dbf1] -- Updated dependencies [7bbeb049f] - - @backstage/cli@0.2.0 diff --git a/packages/techdocs-cli/README.md b/packages/techdocs-cli/README.md deleted file mode 100644 index 031f26fd93..0000000000 --- a/packages/techdocs-cli/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# TechDocs CLI - -Check out the [TechDocs README](https://github.com/backstage/backstage/blob/master/plugins/techdocs/README.md) to learn more. - -**WIP: This cli is a work in progress. It is not ready for use yet. Follow our progress on [the Backstage Discord](https://discord.gg/MUpMjP2) under #docs-like-code or on [our GitHub Milestone](https://github.com/backstage/backstage/milestone/15).** - -## Prerequisities - -Run the following command from the project root: - -```bash -yarn install -``` - -## Run TechDocs CLI - -You'll need Docker installed and running to use this. - -```bash -cd packages/techdocs-container/mock-docs - -# To get a view of your docs in Backstage, use: -npx techdocs-cli serve - -# To view the raw mkdocs site (without Backstage), use: -npx techdocs-cli serve:mkdocs -``` - -If you run `npx techdocs-cli serve` you should have a `localhost:3000` serving TechDocs in Backstage, as well as `localhost:8000` serving Mkdocs (which won't open up and be exposed to the user). - -If running `npx techdocs-cli serve:mkdocs` you will have `localhost:8000` exposed, serving Mkdocs. - -Happy hacking! - -## Deploying a new version - -Deploying the Node packages to NPM happens automatically on merge to `master` through GitHub Actions. The deployment happens through Lerna which determines which packages throughout the Backstage project have changed. In our case, the package is called `techdocs-cli` in the repository but `@techdocs/cli` in the NPM registry. - -> Note: Once a package is published under a version, any subsequent changes will not override that version. You will need to bump up the version across the entire Backstage repository, which can be done through Lerna (see the command below). - -In order to bump up all packages, go to the root of the Backstage repository. To see the current version see the `lerna.json` under the `version` key. To then update all the versions (locally on your machine), run the following: - -```bash -git checkout -b bump-up-version -yarn lerna version --no-push --allow-branch --yes -``` - -Upon being merged to master, Lerna will then automatically publish these packages as configured by the Backstage core team. diff --git a/packages/techdocs-cli/bin/build.sh b/packages/techdocs-cli/bin/build.sh deleted file mode 100755 index 0a3dfeb96d..0000000000 --- a/packages/techdocs-cli/bin/build.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -# Copyright 2020 Spotify AB -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -e - -TECHDOCS_PREVIEW_SOURCE=../../plugins/techdocs/dist -TECHDOCS_PREVIEW_DEST=../../packages/techdocs-cli/dist/techdocs-preview-bundle - -# Build the CLI -yarn run backstage-cli -- build --outputs cjs - -# Create export of the TechDocs plugin -APP_CONFIG_techdocs_storageUrl='"http://localhost:3000/api"' yarn workspace @backstage/plugin-techdocs export - -# Copy over export to techdocs-cli dist/ folder -cp -r $TECHDOCS_PREVIEW_SOURCE $TECHDOCS_PREVIEW_DEST - -# Write to console -echo "[techdocs-cli]: Built the dist/ folder" -echo "[techdocs-cli]: Imported @backstage/plugin-techdocs dist/ folder into techdocs-preview-bundle/" diff --git a/packages/techdocs-cli/bin/techdocs-cli b/packages/techdocs-cli/bin/techdocs-cli deleted file mode 100755 index 9818edf417..0000000000 --- a/packages/techdocs-cli/bin/techdocs-cli +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env node -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* eslint-disable no-restricted-syntax */ -const path = require('path'); - -// Figure out whether we're running inside the backstage repo or as an installed dependency -const isLocal = require('fs').existsSync(path.resolve(__dirname, '../src')); - -if (!isLocal) { - require('..'); -} else { - require('ts-node').register({ - transpileOnly: true, - project: path.resolve(__dirname, '../../../tsconfig.json'), - compilerOptions: { - module: 'CommonJS', - }, - }); - - require('../src'); -} diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json deleted file mode 100644 index ee3785b0cc..0000000000 --- a/packages/techdocs-cli/package.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "name": "@techdocs/cli", - "description": "CLI for running TechDocs locally.", - "version": "0.1.2", - "private": false, - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "https://github.com/backstage/backstage", - "directory": "packages/techdocs-cli" - }, - "keywords": [ - "backstage", - "techdocs" - ], - "license": "Apache-2.0", - "main": "dist/index.cjs.js", - "scripts": { - "build": "./bin/build.sh", - "lint": "backstage-cli lint", - "test": "backstage-cli test --passWithNoTests", - "clean": "backstage-cli clean", - "start": "nodemon --" - }, - "bin": { - "techdocs-cli": "bin/techdocs-cli" - }, - "devDependencies": { - "@types/serve-handler": "^6.1.0" - }, - "files": [ - "bin", - "dist" - ], - "nodemonConfig": { - "watch": "./src", - "exec": "bin/build && bin/techdocs-cli", - "ext": "ts" - }, - "dependencies": { - "@backstage/cli": "^0.2.0", - "commander": "^6.1.0", - "fs-extra": "^9.0.1", - "http-proxy": "^1.18.1", - "react-dev-utils": "^10.2.1", - "serve-handler": "^6.1.3" - } -} diff --git a/packages/techdocs-cli/src/index.ts b/packages/techdocs-cli/src/index.ts deleted file mode 100644 index 824bdb84fc..0000000000 --- a/packages/techdocs-cli/src/index.ts +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { spawn, ChildProcess } from 'child_process'; -import program from 'commander'; -import { version } from '../package.json'; -import path from 'path'; -import HTTPServer from './lib/httpServer'; -import openBrowser from 'react-dev-utils/openBrowser'; - -const run = (name: string, args: string[] = []): ChildProcess => { - const [stdin, stdout, stderr] = [ - 'inherit' as const, - 'pipe' as const, - 'inherit' as const, - ]; - - const childProcess = spawn(name, args, { - stdio: [stdin, stdout, stderr], - shell: true, - env: { - ...process.env, - FORCE_COLOR: 'true', - }, - }); - - childProcess.once('error', error => { - console.error(error); - childProcess.kill(); - }); - - childProcess.once('exit', () => { - process.exit(0); - }); - - return childProcess; -}; - -const runMkdocsServer = (options?: { - devAddr: string; -}): Promise => { - const devAddr = options?.devAddr ?? '0.0.0.0:8000'; - - return new Promise(resolve => { - const childProcess = run('docker', [ - 'run', - '-it', - '-w', - '/content', - '-v', - `${process.cwd()}:/content`, - '-p', - '8000:8000', - 'spotify/techdocs', - 'serve', - '-a', - devAddr, - ]); - - childProcess.stdout?.on('data', rawData => { - const data = rawData.toString().split('\n')[0]; - console.log('[mkdocs] ', data); - - if (data.includes(`Serving on http://${devAddr}`)) { - resolve(childProcess); - } - }); - }); -}; - -const main = (argv: string[]) => { - program.name('techdocs-cli').version(version); - - program - .command('serve:mkdocs') - .description('Serve a documentation project locally') - .action(() => { - runMkdocsServer().then(() => { - openBrowser('http://localhost:8000'); - }); - }); - - program - .command('serve') - .description('Serve a documentation project locally') - .action(() => { - // Mkdocs server - const mkdocsServer = runMkdocsServer(); - - // Local Backstage Preview - const techdocsPreviewBundlePath = path.join( - /* eslint-disable-next-line no-restricted-syntax */ - __dirname, - '..', - 'dist', - 'techdocs-preview-bundle', - ); - - const httpServer = new HTTPServer(techdocsPreviewBundlePath, 3000) - .serve() - .catch(err => { - console.error(err); - mkdocsServer.then(childProcess => childProcess.kill()); - }); - - Promise.all([mkdocsServer, httpServer]).then(() => { - openBrowser('http://localhost:3000/docs/local-dev/'); - }); - }); - - program.parse(argv); -}; - -main(process.argv); diff --git a/packages/techdocs-cli/src/lib/httpServer.ts b/packages/techdocs-cli/src/lib/httpServer.ts deleted file mode 100644 index 6e6578840e..0000000000 --- a/packages/techdocs-cli/src/lib/httpServer.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import serveHandler from 'serve-handler'; -import http from 'http'; -import httpProxy from 'http-proxy'; - -export default class HTTPServer { - proxyEndpoint: string; - - constructor(public dir: string, public port: number) { - this.proxyEndpoint = '/api/'; - } - - private createProxy() { - const proxy = httpProxy.createProxyServer({ - target: 'http://localhost:8000', - }); - - return (request: http.IncomingMessage): [httpProxy, string] => { - const [, ...pathChunks] = - request.url?.substring(this.proxyEndpoint.length).split('/') ?? []; - const forwardPath = pathChunks.join('/'); - - return [proxy, forwardPath]; - }; - } - - public async serve(): Promise { - return new Promise((resolve, reject) => { - const proxyHandler = this.createProxy(); - - const server = http.createServer( - (request: http.IncomingMessage, response: http.ServerResponse) => { - if (request.url?.startsWith(this.proxyEndpoint)) { - const [proxy, forwardPath] = proxyHandler(request); - - proxy.on('error', (error: Error) => { - reject(error); - }); - - request.url = forwardPath; - return proxy.web(request, response); - } - - return serveHandler(request, response, { - public: this.dir, - trailingSlash: true, - rewrites: [{ source: '**', destination: 'index.html' }], - }); - }, - ); - - server.listen(this.port, () => { - console.log( - '[techdocs-preview-bundle] Running local version of Backstage at http://localhost:3000', - ); - resolve(server); - }); - - server.on('error', (error: Error) => { - reject(error); - }); - }); - } -} From db9c13ff394e29a6bdff7f42182355f3eb09ec02 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Wed, 4 Nov 2020 11:19:22 +0100 Subject: [PATCH 099/252] Clean up codeowners --- .github/CODEOWNERS | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7cef5afce0..d818c0d6b5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -10,6 +10,4 @@ /plugins/cloudbuild @trivago/ebarrios /plugins/techdocs @backstage/techdocs-core /plugins/techdocs-backend @backstage/techdocs-core -/packages/techdocs-cli @backstage/techdocs-core -/.github/workflows/techdocs.yml @backstage/techdocs-core /.changeset/cost-insights-* @backstage/silver-lining From 5dd4a338dbc83eb25a6ef2baf380fcc68a4d416e Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Wed, 4 Nov 2020 11:23:02 +0100 Subject: [PATCH 100/252] Docs update of techdocs-cli URL --- docs/features/techdocs/concepts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/techdocs/concepts.md b/docs/features/techdocs/concepts.md index c614965ab9..1675c64288 100644 --- a/docs/features/techdocs/concepts.md +++ b/docs/features/techdocs/concepts.md @@ -41,7 +41,7 @@ documentation for publishing. Currently it mostly acts as a wrapper around the TechDocs container and provides an easy-to-use interface for our docker container. -[TechDocs CLI](https://github.com/backstage/backstage/blob/master/packages/techdocs-cli/README.md) +[TechDocs CLI](https://github.com/backstage/techdocs-cli) ### TechDocs Reader From 4aca74e089be29fc3b8495f1c8168daa092f3760 Mon Sep 17 00:00:00 2001 From: Iain Billett Date: Thu, 5 Nov 2020 15:05:06 +0000 Subject: [PATCH 101/252] Load config from window in default config loader (#3238) Support setting config through the window object. The config should be set in window.__APP_CONFIG__ which should be an object. --- .changeset/3194.md | 7 +++++++ .../core/src/api-wrappers/createApp.test.tsx | 18 ++++++++++++++++++ packages/core/src/api-wrappers/createApp.tsx | 7 +++++++ 3 files changed, 32 insertions(+) create mode 100644 .changeset/3194.md diff --git a/.changeset/3194.md b/.changeset/3194.md new file mode 100644 index 0000000000..2c15d27eed --- /dev/null +++ b/.changeset/3194.md @@ -0,0 +1,7 @@ +--- +'@backstage/core': patch +--- + +Extend default config loader to read config from the window object. + +Config will be read from `window.__APP_CONFIG__` which should be an object. diff --git a/packages/core/src/api-wrappers/createApp.test.tsx b/packages/core/src/api-wrappers/createApp.test.tsx index a1655b88dd..31f584b68c 100644 --- a/packages/core/src/api-wrappers/createApp.test.tsx +++ b/packages/core/src/api-wrappers/createApp.test.tsx @@ -18,10 +18,12 @@ import { defaultConfigLoader } 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 () => { @@ -73,4 +75,20 @@ describe('defaultConfigLoader', () => { '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 }, + ]); + }); }); diff --git a/packages/core/src/api-wrappers/createApp.tsx b/packages/core/src/api-wrappers/createApp.tsx index c7d0f5ce01..592a2417ca 100644 --- a/packages/core/src/api-wrappers/createApp.tsx +++ b/packages/core/src/api-wrappers/createApp.tsx @@ -67,6 +67,13 @@ export const defaultConfigLoader: AppConfigLoader = async ( } } + const windowAppConfig = (window as any).__APP_CONFIG__; + if (windowAppConfig) { + configs.push({ + context: 'window', + data: windowAppConfig, + }); + } return configs; }; From 4577e377bef3be699027a8df68b2bd8e82dc9eb7 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Thu, 5 Nov 2020 18:41:49 +0100 Subject: [PATCH 102/252] Improve styling of outlined chips in dark mode (#3252) --- .changeset/strong-moose-cough.md | 5 +++++ packages/core/src/components/Chip/Chip.stories.tsx | 2 ++ packages/theme/src/baseTheme.ts | 5 ++++- 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 .changeset/strong-moose-cough.md diff --git a/.changeset/strong-moose-cough.md b/.changeset/strong-moose-cough.md new file mode 100644 index 0000000000..41a0da7cca --- /dev/null +++ b/.changeset/strong-moose-cough.md @@ -0,0 +1,5 @@ +--- +'@backstage/theme': patch +--- + +Improve styling of outlined chips in dark mode. diff --git a/packages/core/src/components/Chip/Chip.stories.tsx b/packages/core/src/components/Chip/Chip.stories.tsx index a605666482..87d904ad0e 100644 --- a/packages/core/src/components/Chip/Chip.stories.tsx +++ b/packages/core/src/components/Chip/Chip.stories.tsx @@ -39,3 +39,5 @@ export const SmallDeletable = () => ( export const SmallNotDeletable = () => ( ); + +export const Outline = () => ; diff --git a/packages/theme/src/baseTheme.ts b/packages/theme/src/baseTheme.ts index b0797c6506..193f6961d6 100644 --- a/packages/theme/src/baseTheme.ts +++ b/packages/theme/src/baseTheme.ts @@ -209,9 +209,12 @@ export function createThemeOverrides(theme: BackstageTheme): Overrides { // By default there's no margin, but it's usually wanted, so we add some trailing margin marginRight: theme.spacing(1), marginBottom: theme.spacing(1), + color: theme.palette.grey[900], + }, + outlined: { + color: theme.palette.text.primary, }, label: { - color: theme.palette.grey[900], lineHeight: `${theme.spacing(2.5)}px`, fontWeight: theme.typography.fontWeightMedium, fontSize: `${theme.spacing(1.75)}px`, From a16780d8332274e0d8a76ea78cf9abc7cd62d758 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Nov 2020 18:54:26 +0100 Subject: [PATCH 103/252] chore(deps): bump @gitbeaker/core from 23.5.0 to 25.2.0 (#3210) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(deps): bump @gitbeaker/core from 23.5.0 to 25.2.0 Bumps [@gitbeaker/core](https://github.com/jdalrymple/gitbeaker) from 23.5.0 to 25.2.0. - [Release notes](https://github.com/jdalrymple/gitbeaker/releases) - [Changelog](https://github.com/jdalrymple/gitbeaker/blob/master/CHANGELOG.md) - [Commits](https://github.com/jdalrymple/gitbeaker/compare/23.5.0...25.2.0) Signed-off-by: dependabot[bot] * bump the other package too Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Fredrik Adelöw --- packages/backend/package.json | 2 +- .../packages/backend/package.json.hbs | 2 +- plugins/scaffolder-backend/package.json | 4 +- yarn.lock | 141 ++++++------------ 4 files changed, 50 insertions(+), 99 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index 67b2a42fee..051b83a146 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -31,7 +31,7 @@ "@backstage/plugin-scaffolder-backend": "^0.2.0", "@backstage/plugin-sentry-backend": "^0.1.2", "@backstage/plugin-techdocs-backend": "^0.2.0", - "@gitbeaker/node": "^23.5.0", + "@gitbeaker/node": "^25.2.0", "@octokit/rest": "^18.0.0", "azure-devops-node-api": "^10.1.1", "dockerode": "^3.2.0", diff --git a/packages/create-app/templates/default-app/packages/backend/package.json.hbs b/packages/create-app/templates/default-app/packages/backend/package.json.hbs index bf887dc014..df9ac28739 100644 --- a/packages/create-app/templates/default-app/packages/backend/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/backend/package.json.hbs @@ -27,7 +27,7 @@ "@backstage/plugin-scaffolder-backend": "^{{version '@backstage/plugin-scaffolder-backend'}}", "@backstage/plugin-techdocs-backend": "^{{version '@backstage/plugin-techdocs-backend'}}", "@octokit/rest": "^18.0.0", - "@gitbeaker/node": "^23.5.0", + "@gitbeaker/node": "^25.2.0", "dockerode": "^3.2.0", "express": "^4.17.1", "express-promise-router": "^3.0.3", diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index a245d2ed24..ac30e23828 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -23,8 +23,8 @@ "@backstage/backend-common": "^0.2.0", "@backstage/catalog-model": "^0.2.0", "@backstage/config": "^0.1.1", - "@gitbeaker/core": "^23.5.0", - "@gitbeaker/node": "^23.5.0", + "@gitbeaker/core": "^25.2.0", + "@gitbeaker/node": "^25.2.0", "@octokit/rest": "^18.0.0", "@types/dockerode": "^2.5.32", "@types/express": "^4.17.6", diff --git a/yarn.lock b/yarn.lock index 0f16311d80..8ee1728b30 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1762,32 +1762,33 @@ dependencies: yaml-ast-parser "0.0.43" -"@gitbeaker/core@^23.5.0": - version "23.5.0" - resolved "https://registry.npmjs.org/@gitbeaker/core/-/core-23.5.0.tgz#e0be44eecd0d7bf5418c161997c36d45b0c1ba81" - integrity sha512-5geLk7SxAttABBJZxfuSp72lSYWRyRId583vGCpKB6ISkoDhP+vJxdE6ypmtOJPV4CaSRZhluAGAZd968pi9ng== +"@gitbeaker/core@^25.2.0": + version "25.2.0" + resolved "https://registry.npmjs.org/@gitbeaker/core/-/core-25.2.0.tgz#c2f46b65ed88aebfa69afd2e58dbf4ebe2efb2c7" + integrity sha512-dhCvZItI8FIzHtJ9EySQ43GmNg3j39MxDGMCpDHn+Qb1o54QS+J6XPVQrMRmJioZIyj+WZQPX/IP+/mRkx8vhg== dependencies: - "@gitbeaker/requester-utils" "^23.5.0" + "@gitbeaker/requester-utils" "^25.2.0" form-data "^3.0.0" li "^1.3.0" xcase "^2.0.1" -"@gitbeaker/node@^23.5.0": - version "23.5.0" - resolved "https://registry.npmjs.org/@gitbeaker/node/-/node-23.5.0.tgz#0243be78aad148e7d6b5d79b7acc340748b5fa94" - integrity sha512-GEyhcrF1Lm8YmsmwntfzuhXnq00TrG14wNZP2Hg+DGgexG25eBbbcyFXuFZlBFSaGMQlsc8aPeuEyfyGmgpwnw== +"@gitbeaker/node@^25.2.0": + version "25.2.0" + resolved "https://registry.npmjs.org/@gitbeaker/node/-/node-25.2.0.tgz#cc91e83328ec32de0b1a0dac23accd2385734a66" + integrity sha512-FWchXYJ5agn0ptAQxtkkSKSg1ObbP2xfMzHLECxINFRBHYhg0ms8Fp8Qb+71pxJz7IMlvajyEtZaPfHBmyuh9Q== dependencies: - "@gitbeaker/core" "^23.5.0" - "@gitbeaker/requester-utils" "^23.5.0" - got "^11.1.4" + "@gitbeaker/core" "^25.2.0" + "@gitbeaker/requester-utils" "^25.2.0" + got "^11.7.0" xcase "^2.0.1" -"@gitbeaker/requester-utils@^23.5.0": - version "23.5.0" - resolved "https://registry.npmjs.org/@gitbeaker/requester-utils/-/requester-utils-23.5.0.tgz#e6f5d0216cb978be95e6bf40adf606f23d426f14" - integrity sha512-MdmInOO4unkApvtbv6PnIpDYXosgZgClSOqbxF5S4aJRCZVTJ6oPjMoNP8luhyT9xQeknpKxn9Iv8psEh7IC1Q== +"@gitbeaker/requester-utils@^25.2.0": + version "25.2.0" + resolved "https://registry.npmjs.org/@gitbeaker/requester-utils/-/requester-utils-25.2.0.tgz#f71c44e0073617877f9875dd00c4ae0d74897b09" + integrity sha512-pjuFIVlbxSTPdN+zFT/LBP4ym8k0OBYwUpc5WkzoOtvdTGuDX05r8ufnV07kibLDJkwDmjwnH4Hsc66yevSQTw== dependencies: - query-string "^6.12.1" + form-data "^3.0.0" + query-string "^6.13.3" xcase "^2.0.1" "@graphql-codegen/cli@^1.17.7": @@ -3788,6 +3789,11 @@ resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-3.1.2.tgz#548650de521b344e3781fbdb0ece4aa6f729afb8" integrity sha512-JiX9vxoKMmu8Y3Zr2RVathBL1Cdu4Nt4MuNWemt1Nc06A0RAin9c5FArkhGsyMBWfCu4zj+9b+GxtjAnE4qqLQ== +"@sindresorhus/is@^4.0.0": + version "4.0.0" + resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.0.tgz#2ff674e9611b45b528896d820d3d7a812de2f0e4" + integrity sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ== + "@sinonjs/commons@^1.7.0": version "1.7.1" resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.1.tgz#da5fd19a5f71177a53778073978873964f49acf1" @@ -5724,13 +5730,6 @@ resolved "https://registry.npmjs.org/@types/semver/-/semver-6.2.1.tgz#a236185670a7860f1597cf73bea2e16d001461ba" integrity sha512-+beqKQOh9PYxuHvijhVl+tIHvT6tuwOrE9m14zd+MT2A38KoKZhh7pYJ0SNleLtwDsiIxHDsIk9bv01oOxvSvA== -"@types/serve-handler@^6.1.0": - version "6.1.0" - resolved "https://registry.npmjs.org/@types/serve-handler/-/serve-handler-6.1.0.tgz#6952aaf864e542297ce8a2480b3e0d9ea59de7f6" - integrity sha512-F9BpWotLZrQvq1CdE3oCnVmQUb6dWq20HSB/qjF4mG7WkZjPQ6fye8veznMKoBPhFJve4yz9C1NOITdTcoLngQ== - dependencies: - "@types/node" "*" - "@types/serve-static@*": version "1.13.3" resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz#eb7e1c41c4468272557e897e9171ded5e2ded9d1" @@ -8954,11 +8953,6 @@ contains-path@^0.1.0: resolved "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= -content-disposition@0.5.2: - version "0.5.2" - resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" - integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= - content-disposition@0.5.3: version "0.5.3" resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" @@ -11473,13 +11467,6 @@ fast-shallow-equal@^1.0.0: resolved "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz#d4dcaf6472440dcefa6f88b98e3251e27f25628b" integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw== -fast-url-parser@1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" - integrity sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0= - dependencies: - punycode "^1.3.2" - fastest-stable-stringify@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-1.0.1.tgz#9122d406d4c9d98bea644a6b6853d5874b87b028" @@ -12487,23 +12474,6 @@ got@^10.7.0: to-readable-stream "^2.0.0" type-fest "^0.10.0" -got@^11.1.4: - version "11.6.2" - resolved "https://registry.npmjs.org/got/-/got-11.6.2.tgz#79d7bb8c11df212b97f25565407a1f4ae73210ec" - integrity sha512-/21qgUePCeus29Jk7MEti8cgQUNXFSWfIevNIk4H7u1wmXNDrGPKPY6YsPY+o9CIT/a2DjCjRz0x1nM9FtS2/A== - dependencies: - "@sindresorhus/is" "^3.1.1" - "@szmarczak/http-timer" "^4.0.5" - "@types/cacheable-request" "^6.0.1" - "@types/responselike" "^1.0.0" - cacheable-lookup "^5.0.3" - cacheable-request "^7.0.1" - decompress-response "^6.0.0" - http2-wrapper "^1.0.0-beta.5.2" - lowercase-keys "^2.0.0" - p-cancelable "^2.0.0" - responselike "^2.0.0" - got@^11.5.2: version "11.6.0" resolved "https://registry.npmjs.org/got/-/got-11.6.0.tgz#4978c78f3cbc3a45ee95381f8bb6efd1db1f4638" @@ -12538,6 +12508,23 @@ got@^11.6.2: p-cancelable "^2.0.0" responselike "^2.0.0" +got@^11.7.0: + version "11.8.0" + resolved "https://registry.npmjs.org/got/-/got-11.8.0.tgz#be0920c3586b07fd94add3b5b27cb28f49e6545f" + integrity sha512-k9noyoIIY9EejuhaBNLyZ31D5328LeqnyPNXJQb2XlJZcKakLqN5m6O/ikhq/0lw56kUYS54fVm+D1x57YC9oQ== + dependencies: + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.1" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + got@^9.6.0: version "9.6.0" resolved "https://registry.npmjs.org/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -16343,18 +16330,6 @@ mime-db@1.44.0, "mime-db@>= 1.43.0 < 2": resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== -mime-db@~1.33.0: - version "1.33.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" - integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== - -mime-types@2.1.18: - version "2.1.18" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" - integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== - dependencies: - mime-db "~1.33.0" - mime-types@^2.1.12, mime-types@^2.1.26, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.27" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" @@ -18086,7 +18061,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-is-inside@1.0.2, path-is-inside@^1.0.2: +path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= @@ -18123,11 +18098,6 @@ path-to-regexp@0.1.7: resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= -path-to-regexp@2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45" - integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== - path-to-regexp@^1.7.0: version "1.8.0" resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" @@ -19175,7 +19145,7 @@ punycode@1.3.2: resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= -punycode@^1.2.4, punycode@^1.3.2: +punycode@^1.2.4: version "1.4.1" resolved "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= @@ -19220,10 +19190,10 @@ query-string@^4.1.0: object-assign "^4.1.0" strict-uri-encode "^1.0.0" -query-string@^6.12.1: - version "6.13.1" - resolved "https://registry.npmjs.org/query-string/-/query-string-6.13.1.tgz#d913ccfce3b4b3a713989fe6d39466d92e71ccad" - integrity sha512-RfoButmcK+yCta1+FuU8REvisx1oEzhMKwhLUNcepQTPGcNMp1sIqjnfCtfnvGSQZQEhaBHvccujtWoUV3TTbA== +query-string@^6.13.3: + version "6.13.6" + resolved "https://registry.npmjs.org/query-string/-/query-string-6.13.6.tgz#e5ac7c74f2a5da43fbca0b883b4f0bafba439966" + integrity sha512-/WWZ7d9na6s2wMEGdVCVgKWE9Rt7nYyNIf7k8xmHXcesPMlEzicWo3lbYwHyA4wBktI2KrXxxZeACLbE84hvSQ== dependencies: decode-uri-component "^0.2.0" split-on-first "^1.0.0" @@ -19306,11 +19276,6 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" -range-parser@1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" - integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= - range-parser@^1.2.1, range-parser@~1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" @@ -21045,20 +21010,6 @@ serve-favicon@^2.5.0: parseurl "~1.3.2" safe-buffer "5.1.1" -serve-handler@^6.1.3: - version "6.1.3" - resolved "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz#1bf8c5ae138712af55c758477533b9117f6435e8" - integrity sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w== - dependencies: - bytes "3.0.0" - content-disposition "0.5.2" - fast-url-parser "1.1.3" - mime-types "2.1.18" - minimatch "3.0.4" - path-is-inside "1.0.2" - path-to-regexp "2.2.1" - range-parser "1.2.0" - serve-index@^1.9.1: version "1.9.1" resolved "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" From e1a92d0b5d119d659b4ce54147e4e01d7c961e2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 5 Nov 2020 22:41:55 +0100 Subject: [PATCH 104/252] docs: clarify current state of databases (#3250) --- docs/overview/architecture-overview.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/overview/architecture-overview.md b/docs/overview/architecture-overview.md index 2d65e9e430..d8069c8665 100644 --- a/docs/overview/architecture-overview.md +++ b/docs/overview/architecture-overview.md @@ -148,7 +148,7 @@ The CircleCI plugin is an example of a third-party backed plugin. CircleCI is a SaaS service which can be used without any knowledge of Backstage. It has an API which a Backstage plugin consumes to display content. -Requests which go to CircleCI from the users browser are passed through a proxy +Requests going to CircleCI from the user's browser are passed through a proxy service that Backstage provides. Without this, the requests would be blocked by Cross Origin Resource Sharing policies which prevent a browser page served at [https://example.com](https://example.com) from serving resources hosted at @@ -161,16 +161,18 @@ https://circleci.com. As we have seen, both the `lighthouse-audit-service` and `catalog-backend` require a database to work with. -At the time of writing, the `lighthouse-audit-service` requires PostgreSQL to -work with. The service catalog backend uses an in-memory Sqlite3 instance. This -is a development-oriented setup and there are plans to support other databases -in the future. +The Backstage backend and its builtin plugins are based on the +[Knex](http://knexjs.org/) library, and set up a separate logical database per +plugin. This gives great isolation and lets them perform migrations and evolve +separate from each other. -To learn more about the future of databases and Backstage, see the following -GitHub issues. - -- [Knex + Plugins (Multiple vs Single Database) · Issue #1598 · backstage/backstage](https://github.com/backstage/backstage/issues/1598) -- [Update migrations to support postgres by dariddler · Pull Request #1527 · backstage/backstage](https://github.com/backstage/backstage/pull/1527#discussion_r450374145) +The Knex library supports a multitude of databases, but Backstage is at the time +of writing tested primarily against two of them: SQLite, which is mainly used as +an in-memory mock/test database, and PostgreSQL, which is the preferred +production database. Other databases such as the MySQL variants are reported to +work but +[aren't tested as fully](https://github.com/backstage/backstage/issues/2460) +yet. ## Containerization From 85379551e03ea82c7a0e872efe6e420d9742552a Mon Sep 17 00:00:00 2001 From: David Tuite Date: Thu, 5 Nov 2020 21:48:28 +0000 Subject: [PATCH 105/252] Add CircleCI to the Well Known Annotations (#3082) --- .../well-known-annotations.md | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/features/software-catalog/well-known-annotations.md b/docs/features/software-catalog/well-known-annotations.md index 25489b89d9..5cb6837340 100644 --- a/docs/features/software-catalog/well-known-annotations.md +++ b/docs/features/software-catalog/well-known-annotations.md @@ -158,6 +158,27 @@ fallback (`rollbar.organization` followed by `organization.name`). Specifying this annotation may enable Rollbar related features in Backstage for that entity. +### circleci.com/project-slug + +```yaml +# Example: +metadata: + annotations: + circleci.com/project-slug: github/spotify/pump-station +``` + +The value of this annotation is the so-called slug (or alternatively, the ID) of +a [CircleCI](https://circleci.com/) project within your organization. The value +can be the format of `[source-control-manager]/[organization]/[project-slug]` or +just `[organization]/[project-slug]`. When the `[source-control-manager]` slug +is omitted, `bitbucket` will be used as a fallback. + +Specifying this annotation will cause the CI/CD features in Backstage to display +data from CircleCI for that entity. + +Providing both the `github.com/project-slug` and `circleci.com/project-slug` +annotations can cause problems as both may be used for CI/CD features. + ### backstage.io/ldap-rdn, backstage.io/ldap-uuid, backstage.io/ldap-dn ```yaml From 3b02444c127a423de1163b868533d1088621792b Mon Sep 17 00:00:00 2001 From: David Tuite Date: Thu, 5 Nov 2020 22:10:45 +0000 Subject: [PATCH 106/252] Fix reference to Catalog path in overview docs (#3255) While you do end up on the catalog if you visit `/`, the browser URL is actually `/catalog`. --- docs/features/software-catalog/index.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/features/software-catalog/index.md b/docs/features/software-catalog/index.md index a7c80d45cc..6076068504 100644 --- a/docs/features/software-catalog/index.md +++ b/docs/features/software-catalog/index.md @@ -33,10 +33,10 @@ More specifically, the Service Catalog enables two main use-cases: ## Getting Started -The Software Catalog is available to browse on the start page at `/`. If you've -followed [Installing in your Backstage App](./installation.md) in your separate -App or [Getting Started with Backstage](../../getting-started) for this repo, -you should be able to browse the catalog at `http://localhost:3000`. +The Software Catalog is available to browse at `/catalog`. If you've followed +[Installing in your Backstage App](./installation.md) in your separate App or +[Getting Started with Backstage](../../getting-started) for this repo, you +should be able to browse the catalog at `http://localhost:3000`. ![](../../assets/software-catalog/service-catalog-home.png) From eecf08e3f7cce128a24d6caaaa1389690d796fb8 Mon Sep 17 00:00:00 2001 From: David Tuite Date: Thu, 5 Nov 2020 23:32:13 +0000 Subject: [PATCH 107/252] Update Node LTS in Getting Started docs (#3254) I've tested and the v14 [Fermium LTS](https://nodejs.org/en/about/releases/) works fine. --- docs/getting-started/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index eb199f4bc8..7fc80b625c 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -24,7 +24,7 @@ Requests towards this repo. Backstage provides the `@backstage/create-app` package to scaffold standalone instances of Backstage. You will need to have [Node.js](https://nodejs.org/en/download/) Active LTS Release installed -(currently v12), [Yarn](https://classic.yarnpkg.com/en/docs/install) and +(currently v14), [Yarn](https://classic.yarnpkg.com/en/docs/install) and [Python](https://www.python.org/downloads/) (although you likely have it already). You will also need to have [Docker](https://docs.docker.com/engine/install/) installed to use some features From c56e28375fbf934dc1f0fcf3e3216a66579f7a24 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 6 Nov 2020 00:33:10 +0100 Subject: [PATCH 108/252] create-app: add missing @backstage/plugin-api-docs entry to plugins.ts (#3257) --- .changeset/lucky-lemons-return.md | 5 +++++ .../templates/default-app/packages/app/src/plugins.ts | 1 + 2 files changed, 6 insertions(+) create mode 100644 .changeset/lucky-lemons-return.md diff --git a/.changeset/lucky-lemons-return.md b/.changeset/lucky-lemons-return.md new file mode 100644 index 0000000000..49125d31d4 --- /dev/null +++ b/.changeset/lucky-lemons-return.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Fix missing api-docs plugin registration in app template diff --git a/packages/create-app/templates/default-app/packages/app/src/plugins.ts b/packages/create-app/templates/default-app/packages/app/src/plugins.ts index c787ac2166..b719594a0b 100644 --- a/packages/create-app/templates/default-app/packages/app/src/plugins.ts +++ b/packages/create-app/templates/default-app/packages/app/src/plugins.ts @@ -1,3 +1,4 @@ +export { plugin as ApiDocs } from '@backstage/plugin-api-docs'; export { plugin as CatalogPlugin } from '@backstage/plugin-catalog'; export { plugin as RegisterComponent } from '@backstage/plugin-register-component'; export { plugin as ScaffolderPlugin } from '@backstage/plugin-scaffolder'; From 199237d2fb9d62263dffdc6325db1fd2905f34a3 Mon Sep 17 00:00:00 2001 From: Mikaela Grundin <9350337+zatine@users.noreply.github.com> Date: Fri, 6 Nov 2020 09:27:34 +0100 Subject: [PATCH 109/252] core: DependencyGraph component (#3236) * add dependency graph files * add stories for DependencyGraph * add changeset * add margin and border to storybook examples * support dark theme in default label * add transition to nodes, edges and labels * move @types to devDependencies * clean up DependencyGraph test * don't use default exports * change padding format * change marginX/Y to paddingX/Y * fix types export * fix storybook * move @types/dagre from devDependencies to dependencies --- .changeset/shaggy-bobcats-warn.md | 5 + packages/core/package.json | 8 + .../DependencyGraph/DefaultLabel.tsx | 35 ++ .../DependencyGraph/DefaultNode.tsx | 79 +++++ .../DependencyGraph.stories.tsx | 178 ++++++++++ .../DependencyGraph/DependencyGraph.test.tsx | 106 ++++++ .../DependencyGraph/DependencyGraph.tsx | 324 ++++++++++++++++++ .../components/DependencyGraph/Edge.test.tsx | 100 ++++++ .../src/components/DependencyGraph/Edge.tsx | 122 +++++++ .../components/DependencyGraph/Node.test.tsx | 79 +++++ .../src/components/DependencyGraph/Node.tsx | 76 ++++ .../components/DependencyGraph/constants.ts | 21 ++ .../src/components/DependencyGraph/index.ts | 20 ++ .../src/components/DependencyGraph/types.ts | 88 +++++ packages/core/src/components/index.ts | 1 + yarn.lock | 121 +++++++ 16 files changed, 1363 insertions(+) create mode 100644 .changeset/shaggy-bobcats-warn.md create mode 100644 packages/core/src/components/DependencyGraph/DefaultLabel.tsx create mode 100644 packages/core/src/components/DependencyGraph/DefaultNode.tsx create mode 100644 packages/core/src/components/DependencyGraph/DependencyGraph.stories.tsx create mode 100644 packages/core/src/components/DependencyGraph/DependencyGraph.test.tsx create mode 100644 packages/core/src/components/DependencyGraph/DependencyGraph.tsx create mode 100644 packages/core/src/components/DependencyGraph/Edge.test.tsx create mode 100644 packages/core/src/components/DependencyGraph/Edge.tsx create mode 100644 packages/core/src/components/DependencyGraph/Node.test.tsx create mode 100644 packages/core/src/components/DependencyGraph/Node.tsx create mode 100644 packages/core/src/components/DependencyGraph/constants.ts create mode 100644 packages/core/src/components/DependencyGraph/index.ts create mode 100644 packages/core/src/components/DependencyGraph/types.ts diff --git a/.changeset/shaggy-bobcats-warn.md b/.changeset/shaggy-bobcats-warn.md new file mode 100644 index 0000000000..048e1a0ff6 --- /dev/null +++ b/.changeset/shaggy-bobcats-warn.md @@ -0,0 +1,5 @@ +--- +'@backstage/core': minor +--- + +New DependencyGraph component added to core package. diff --git a/packages/core/package.json b/packages/core/package.json index 43f48e0ece..0ddc77f1e4 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -35,10 +35,15 @@ "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", + "@types/dagre": "^0.7.44", "@types/react": "^16.9", "@types/react-sparklines": "^1.7.0", "classnames": "^2.2.6", "clsx": "^1.1.0", + "d3-selection": "^2.0.0", + "d3-shape": "^2.0.0", + "d3-zoom": "^2.0.0", + "dagre": "^0.8.5", "immer": "^7.0.9", "lodash": "^4.17.15", "material-table": "^1.69.1", @@ -63,6 +68,9 @@ "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", "@types/classnames": "^2.2.9", + "@types/d3-selection": "^2.0.0", + "@types/d3-shape": "^2.0.0", + "@types/d3-zoom": "^2.0.0", "@types/google-protobuf": "^3.7.2", "@types/jest": "^26.0.7", "@types/node": "^12.0.0", diff --git a/packages/core/src/components/DependencyGraph/DefaultLabel.tsx b/packages/core/src/components/DependencyGraph/DefaultLabel.tsx new file mode 100644 index 0000000000..0679d1dae0 --- /dev/null +++ b/packages/core/src/components/DependencyGraph/DefaultLabel.tsx @@ -0,0 +1,35 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import makeStyles from '@material-ui/core/styles/makeStyles'; +import { BackstageTheme } from '@backstage/theme'; +import { RenderLabelProps } from './types'; + +const useStyles = makeStyles((theme: BackstageTheme) => ({ + text: { + fill: theme.palette.textContrast, + }, +})); + +export function DefaultLabel({ edge: { label } }: RenderLabelProps) { + const classes = useStyles(); + return ( + + {label} + + ); +} diff --git a/packages/core/src/components/DependencyGraph/DefaultNode.tsx b/packages/core/src/components/DependencyGraph/DefaultNode.tsx new file mode 100644 index 0000000000..9656c860ae --- /dev/null +++ b/packages/core/src/components/DependencyGraph/DefaultNode.tsx @@ -0,0 +1,79 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import { BackstageTheme } from '@backstage/theme'; +import { RenderNodeProps } from './types'; + +const useStyles = makeStyles((theme: BackstageTheme) => ({ + node: { + fill: theme.palette.background.paper, + stroke: theme.palette.border, + }, + text: { + fill: theme.palette.textContrast, + }, +})); + +export function DefaultNode({ node: { id } }: RenderNodeProps) { + const classes = useStyles(); + const [width, setWidth] = React.useState(0); + const [height, setHeight] = React.useState(0); + const idRef = React.useRef(null); + + React.useLayoutEffect(() => { + // set the width to the length of the ID + if (idRef.current) { + let { + height: renderedHeight, + width: renderedWidth, + } = idRef.current.getBBox(); + renderedHeight = Math.round(renderedHeight); + renderedWidth = Math.round(renderedWidth); + + if (renderedHeight !== height || renderedWidth !== width) { + setWidth(renderedWidth); + setHeight(renderedHeight); + } + } + }, [width, height]); + + const padding = 10; + const paddedWidth = width + padding * 2; + const paddedHeight = height + padding * 2; + + return ( + + + + {id} + + + ); +} diff --git a/packages/core/src/components/DependencyGraph/DependencyGraph.stories.tsx b/packages/core/src/components/DependencyGraph/DependencyGraph.stories.tsx new file mode 100644 index 0000000000..e39ecfc5da --- /dev/null +++ b/packages/core/src/components/DependencyGraph/DependencyGraph.stories.tsx @@ -0,0 +1,178 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { DependencyGraph } from './DependencyGraph'; +import { Direction, LabelPosition } from './types'; + +export default { + title: 'Data Display/DependencyGraph', + component: DependencyGraph, +}; + +const containerStyle = { width: '100%' }; +const graphStyle = { border: '1px solid grey' }; + +const exampleNodes = [ + { id: 'source' }, + { id: 'downstream' }, + { id: 'second-downstream' }, + { id: 'third-downstream' }, +]; + +const exampleEdges = [ + { from: 'source', to: 'downstream' }, + { from: 'downstream', to: 'second-downstream' }, + { from: 'downstream', to: 'third-downstream' }, +]; + +export const Default = () => ( +
+ +
+); + +export const BottomToTop = () => ( +
+ +
+); + +export const LeftToRight = () => ( +
+ +
+); + +export const RightToLeft = () => ( +
+ +
+); + +export const WithLabels = () => { + const edges = exampleEdges.map(edge => ({ ...edge, label: 'label' })); + return ( +
+ +
+ ); +}; + +export const CustomNodes = () => { + const colors = ['pink', 'coral', 'yellowgreen', 'aquamarine']; + const nodes = exampleNodes.map((node, index) => ({ + ...node, + description: 'Description text', + color: colors[index], + })); + return ( +
+ ( + + + + {props.node.id} + + + {props.node.description} + + + )} + /> +
+ ); +}; + +export const CustomLabels = () => { + const colors = ['pink', 'coral', 'aqua']; + const edges = exampleEdges.map((edge, index) => ({ + ...edge, + label: colors[index], + color: colors[index], + })); + return ( +
+ ( + + + + {props.edge.label} + + + )} + /> +
+ ); +}; diff --git a/packages/core/src/components/DependencyGraph/DependencyGraph.test.tsx b/packages/core/src/components/DependencyGraph/DependencyGraph.test.tsx new file mode 100644 index 0000000000..526dc6d7dd --- /dev/null +++ b/packages/core/src/components/DependencyGraph/DependencyGraph.test.tsx @@ -0,0 +1,106 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { DependencyGraph } from './DependencyGraph'; +import { RenderLabelProps, RenderNodeProps } from './types'; +import { EDGE_TEST_ID, LABEL_TEST_ID, NODE_TEST_ID } from './constants'; + +describe('', () => { + beforeAll(() => { + Object.defineProperty(window.SVGElement.prototype, 'getBBox', { + value: () => ({ width: 100, height: 100 }), + configurable: true, + }); + }); + + const nodes = [{ id: 'a' }, { id: 'b' }, { id: 'c' }]; + const edges = [ + { from: nodes[0].id, to: nodes[1].id }, + { from: nodes[1].id, to: nodes[2].id }, + ]; + + const CUSTOM_TEST_ID = 'custom-test-id'; + + it('renders each node and edge supplied', async () => { + const { getByText, queryAllByTestId, findAllByTestId } = render( + , + ); + const renderedNodes = await findAllByTestId(NODE_TEST_ID); + expect(renderedNodes).toHaveLength(3); + expect(getByText(nodes[0].id)).toBeInTheDocument(); + expect(getByText(nodes[1].id)).toBeInTheDocument(); + expect(getByText(nodes[2].id)).toBeInTheDocument(); + expect(queryAllByTestId(EDGE_TEST_ID)).toHaveLength(2); + expect(queryAllByTestId(LABEL_TEST_ID)).toHaveLength(0); + }); + + it('renders edge labels if present', async () => { + const labeledEdges = [ + { ...edges[0], label: 'first' }, + { ...edges[1], label: 'second' }, + ]; + const { getByText, getAllByTestId, findAllByTestId } = render( + , + ); + const renderedEdges = await findAllByTestId(EDGE_TEST_ID); + expect(renderedEdges).toHaveLength(2); + expect(getAllByTestId(LABEL_TEST_ID)).toHaveLength(2); + expect(getByText(labeledEdges[0].label)).toBeInTheDocument(); + expect(getByText(labeledEdges[1].label)).toBeInTheDocument(); + }); + + it('renders nodes according to renderNode prop', async () => { + const singleNode = [nodes[0]]; + + const renderNode = (props: RenderNodeProps) => ( + + {props.node.id} + + + ); + const { getByText, findByTestId, container } = render( + , + ); + const node = await findByTestId(CUSTOM_TEST_ID); + expect(node).toBeInTheDocument(); + expect(container.querySelector('circle')).toBeInTheDocument(); + expect(getByText(singleNode[0].id)).toBeInTheDocument(); + }); + + it('renders labels according to renderLabel prop', async () => { + const labeledEdge = [{ ...edges[0], label: 'label' }]; + + const renderLabel = (props: RenderLabelProps) => ( + + {props.edge.label} + + + ); + const { getByText, findByTestId, container } = render( + , + ); + const node = await findByTestId(CUSTOM_TEST_ID); + expect(node).toBeInTheDocument(); + expect(container.querySelector('circle')).toBeInTheDocument(); + expect(getByText(labeledEdge[0].label)).toBeInTheDocument(); + }); +}); diff --git a/packages/core/src/components/DependencyGraph/DependencyGraph.tsx b/packages/core/src/components/DependencyGraph/DependencyGraph.tsx new file mode 100644 index 0000000000..ced0bdf276 --- /dev/null +++ b/packages/core/src/components/DependencyGraph/DependencyGraph.tsx @@ -0,0 +1,324 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import * as d3Zoom from 'd3-zoom'; +import * as d3Selection from 'd3-selection'; +import useTheme from '@material-ui/core/styles/useTheme'; +import dagre from 'dagre'; +import debounce from 'lodash/debounce'; +import { BackstageTheme } from '@backstage/theme'; +import { + DependencyEdge, + DependencyNode, + Direction, + Alignment, + Ranker, + RenderNodeFunction, + RenderLabelFunction, + GraphEdge, + GraphNode, + LabelPosition, +} from './types'; +import { Node } from './Node'; +import { Edge } from './Edge'; +import { ARROW_MARKER_ID } from './constants'; + +export type DependencyGraphProps = React.SVGProps & { + edges: DependencyEdge[]; + nodes: DependencyNode[]; + direction?: Direction; + align?: Alignment; + nodeMargin?: number; + edgeMargin?: number; + rankMargin?: number; + paddingX?: number; + paddingY?: number; + acyclicer?: 'greedy'; + ranker?: Ranker; + labelPosition?: LabelPosition; + labelOffset?: number; + edgeRanks?: number; + edgeWeight?: number; + renderNode?: RenderNodeFunction; + renderLabel?: RenderLabelFunction; + defs?: SVGDefsElement | SVGDefsElement[]; +}; + +const WORKSPACE_ID = 'workspace'; + +export function DependencyGraph({ + edges, + nodes, + renderNode, + direction = Direction.TOP_BOTTOM, + align, + nodeMargin = 50, + edgeMargin = 10, + rankMargin = 50, + paddingX = 0, + paddingY = 0, + acyclicer, + ranker = Ranker.NETWORK_SIMPLEX, + labelPosition = LabelPosition.RIGHT, + labelOffset = 10, + edgeRanks = 1, + edgeWeight = 1, + renderLabel, + defs, + ...svgProps +}: DependencyGraphProps) { + const theme: BackstageTheme = useTheme(); + const [containerWidth, setContainerWidth] = React.useState(100); + const [containerHeight, setContainerHeight] = React.useState(100); + + const graph = React.useRef>( + new dagre.graphlib.Graph(), + ); + const [graphWidth, setGraphWidth] = React.useState( + graph.current.graph()?.width || 0, + ); + const [graphHeight, setGraphHeight] = React.useState( + graph.current.graph()?.height || 0, + ); + const [graphNodes, setGraphNodes] = React.useState([]); + const [graphEdges, setGraphEdges] = React.useState([]); + + const maxWidth = Math.max(graphWidth, containerWidth); + const maxHeight = Math.max(graphHeight, containerHeight); + + const containerRef = React.useMemo( + () => + debounce((node: SVGSVGElement) => { + if (!node) { + return; + } + // Set up zooming + panning + const container = d3Selection.select(node); + const workspace = d3Selection.select(node.getElementById(WORKSPACE_ID)); + const zoom = d3Zoom + .zoom() + .scaleExtent([1, 10]) + .on('zoom', event => { + event.transform.x = Math.min( + 0, + Math.max( + event.transform.x, + maxWidth - maxWidth * event.transform.k, + ), + ); + event.transform.y = Math.min( + 0, + Math.max( + event.transform.y, + maxHeight - maxHeight * event.transform.k, + ), + ); + workspace.attr('transform', event.transform); + }); + + container.call(zoom); + + const { + width: newContainerWidth, + height: newContainerHeight, + } = node.getBoundingClientRect(); + if (containerWidth !== newContainerWidth) { + setContainerWidth(newContainerWidth); + } + if (containerHeight !== newContainerHeight) { + setContainerHeight(newContainerHeight); + } + }, 100), + [containerHeight, containerWidth, maxWidth, maxHeight], + ); + + const setNodesAndEdges = React.useCallback(() => { + // Cleaning up lingering nodes and edges + const currentGraphNodes = graph.current.nodes(); + const currentGraphEdges = graph.current.edges(); + + currentGraphNodes.forEach(nodeId => { + const remainingNode = nodes.some(node => node.id === nodeId); + if (!remainingNode) { + graph.current.removeNode(nodeId); + } + }); + + currentGraphEdges.forEach(e => { + const remainingEdge = edges.some( + edge => edge.from === e.v && edge.to === e.w, + ); + if (!remainingEdge) { + graph.current.removeEdge(e.v, e.w); + } + }); + + // Adding/updating nodes and edges + nodes.forEach(node => { + const existingNode = graph.current + .nodes() + .find(nodeId => node.id === nodeId); + + if (existingNode) { + const { width, height, x, y } = graph.current.node(existingNode); + graph.current.setNode(existingNode, { ...node, width, height, x, y }); + } else { + graph.current.setNode(node.id, { ...node, width: 0, height: 0 }); + } + }); + + edges.forEach(e => { + graph.current.setEdge(e.from, e.to, { + ...e, + label: e.label, + width: 0, + height: 0, + labelpos: labelPosition, + labeloffset: labelOffset, + weight: edgeWeight, + minlen: edgeRanks, + }); + }); + }, [edges, nodes, labelPosition, labelOffset, edgeWeight, edgeRanks]); + + const updateGraph = React.useMemo( + () => + debounce( + () => { + dagre.layout(graph.current); + const { height, width } = graph.current.graph(); + const newHeight = Math.max(0, height || 0); + const newWidth = Math.max(0, width || 0); + setGraphWidth(newWidth); + setGraphHeight(newHeight); + + setGraphNodes(graph.current.nodes()); + setGraphEdges(graph.current.edges()); + }, + 250, + { leading: true }, + ), + [], + ); + + React.useEffect(() => { + graph.current.setGraph({ + rankdir: direction, + align, + nodesep: nodeMargin, + edgesep: edgeMargin, + ranksep: rankMargin, + marginx: paddingX, + marginy: paddingY, + acyclicer, + ranker, + }); + + setNodesAndEdges(); + updateGraph(); + + return updateGraph.cancel; + }, [ + acyclicer, + align, + direction, + edgeMargin, + paddingX, + paddingY, + nodeMargin, + rankMargin, + ranker, + setNodesAndEdges, + updateGraph, + ]); + + function setNode(id: string, node: DependencyNode) { + graph.current.setNode(id, node); + updateGraph(); + return graph.current; + } + + function setEdge(id: dagre.Edge, edge: DependencyEdge) { + graph.current.setEdge(id, edge); + updateGraph(); + return graph.current; + } + + return ( + + + + + + {defs} + + + + {graphEdges.map(e => { + const edge = graph.current.edge(e) as GraphEdge; + if (!edge) return null; + return ( + + ); + })} + {graphNodes.map((id: string) => { + const node = graph.current.node(id) as GraphNode; + if (!node) return null; + return ( + + ); + })} + + + + ); +} diff --git a/packages/core/src/components/DependencyGraph/Edge.test.tsx b/packages/core/src/components/DependencyGraph/Edge.test.tsx new file mode 100644 index 0000000000..b651c17bf0 --- /dev/null +++ b/packages/core/src/components/DependencyGraph/Edge.test.tsx @@ -0,0 +1,100 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { Edge } from './Edge'; +import { RenderLabelProps } from './types'; + +const fromNode = 'node'; +const toNode = 'other-node'; + +const edge = { + from: fromNode, + to: toNode, +}; + +const id = { + v: fromNode, + w: toNode, +}; + +const setEdge = jest.fn(); +const renderElement = jest.fn((props: RenderLabelProps) => ( + {props.edge.label} +)); + +const minProps = { + points: [ + { x: 10, y: 20 }, + { x: 20, y: 20 }, + ], + id, + setEdge, + renderElement, + edge, +}; + +const label = 'label'; +const edgeWithLabel = { ...edge, label }; + +describe('', () => { + beforeEach(() => { + // jsdom does not support SVG elements so we have to fall back to HTMLUnknownElement + Object.defineProperty(window.HTMLUnknownElement.prototype, 'getBBox', { + value: () => ({ width: 100, height: 100 }), + configurable: true, + }); + }); + + afterEach(jest.clearAllMocks); + + it('does not render the supplied label element if label is missing', () => { + const { container } = render(); + expect(container.getElementsByTagName('g')).toHaveLength(0); + }); + + it('renders the supplied label element if label is present', () => { + const { getByText } = render(); + expect(getByText(label)).toBeInTheDocument(); + }); + + it('passes down edge properties to the render method if label is present', () => { + const edgeWithRandomProp = { ...edge, label, randomProp: true }; + render( + , + ); + + expect(renderElement).toHaveBeenCalledWith({ edge: edgeWithRandomProp }); + }); + + it('calls setEdge with edge ID and actual label size after rendering', () => { + const { getByText } = render(); + expect(getByText(label)).toBeInTheDocument(); + + // Updates the edge in the graph + expect(setEdge).toHaveBeenCalledWith(id, { + height: 100, + width: 100, + ...edgeWithLabel, + }); + + // Does not pass down width/height to label + expect(renderElement).not.toHaveBeenCalledWith( + expect.objectContaining({ height: 100, width: 100 }), + ); + }); +}); diff --git a/packages/core/src/components/DependencyGraph/Edge.tsx b/packages/core/src/components/DependencyGraph/Edge.tsx new file mode 100644 index 0000000000..5e67aca63f --- /dev/null +++ b/packages/core/src/components/DependencyGraph/Edge.tsx @@ -0,0 +1,122 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import * as d3Shape from 'd3-shape'; +import isFinite from 'lodash/isFinite'; +import makeStyles from '@material-ui/core/styles/makeStyles'; +import { BackstageTheme } from '@backstage/theme'; +import { + GraphEdge, + RenderLabelProps, + RenderLabelFunction, + DependencyEdge, +} from './types'; +import { ARROW_MARKER_ID, EDGE_TEST_ID, LABEL_TEST_ID } from './constants'; +import { DefaultLabel } from './DefaultLabel'; + +const useStyles = makeStyles((theme: BackstageTheme) => ({ + path: { + strokeWidth: 2, + stroke: theme.palette.textSubtle, + fill: 'none', + transition: `${theme.transitions.duration.shortest}ms`, + }, + label: { + transition: `${theme.transitions.duration.shortest}ms`, + }, +})); + +type EdgePoint = dagre.GraphEdge['points'][0]; + +export type EdgeComponentProps = { + id: dagre.Edge; + edge: GraphEdge; + render?: RenderLabelFunction; + setEdge: (id: dagre.Edge, edge: DependencyEdge) => dagre.graphlib.Graph<{}>; +}; + +const renderDefault = (props: RenderLabelProps) => ; + +const createPath = d3Shape + .line() + .x(d => d.x) + .y(d => d.y) + .curve(d3Shape.curveMonotoneX); + +export function Edge({ + render = renderDefault, + setEdge, + id, + edge, +}: EdgeComponentProps) { + const { x = 0, y = 0, width, height, points, ...labelProps } = edge; + const classes = useStyles(); + + const labelRef = React.useRef(null); + + React.useLayoutEffect(() => { + // set the label width to the actual rendered width to properly layout graph + if (labelRef.current) { + let { + height: renderedHeight, + width: renderedWidth, + } = labelRef.current.getBBox(); + renderedHeight = Math.round(renderedHeight); + renderedWidth = Math.round(renderedWidth); + + if (renderedHeight !== height || renderedWidth !== width) { + setEdge(id, { + ...edge, + height: renderedHeight, + width: renderedWidth, + }); + } + } + }, [edge, height, width, setEdge, id]); + + let path: string = ''; + + if (points) { + const finitePoints = points.filter( + (point: EdgePoint) => isFinite(point.x) && isFinite(point.y), + ); + path = createPath(finitePoints) || ''; + } + + return ( + <> + {path && ( + + )} + {labelProps.label ? ( + + {render({ edge: labelProps })} + + ) : null} + + ); +} diff --git a/packages/core/src/components/DependencyGraph/Node.test.tsx b/packages/core/src/components/DependencyGraph/Node.test.tsx new file mode 100644 index 0000000000..9f9f7c693a --- /dev/null +++ b/packages/core/src/components/DependencyGraph/Node.test.tsx @@ -0,0 +1,79 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import dagre from 'dagre'; +import { render } from '@testing-library/react'; +import { Node } from './Node'; +import { RenderNodeProps } from './types'; + +const node = { id: 'abc' }; +const setNode = jest.fn(() => new dagre.graphlib.Graph()); +const renderElement = jest.fn((props: RenderNodeProps) => ( + {props.node.id} +)); + +const minProps = { + id: node.id, + node, + setNode, + render: renderElement, + x: 0, + y: 0, + width: 0, + height: 0, +}; + +describe('', () => { + beforeEach(() => { + // jsdom does not support SVG elements so we have to fall back to HTMLUnknownElement + Object.defineProperty(window.HTMLUnknownElement.prototype, 'getBBox', { + value: () => ({ width: 100, height: 100 }), + configurable: true, + }); + }); + + afterEach(jest.clearAllMocks); + + it('renders the supplied element', () => { + const { getByText } = render(); + expect(getByText(minProps.id)).toBeInTheDocument(); + }); + + it('passes down node properties to the render method', () => { + const nodeWithRandomProp = { ...node, randomProp: true }; + render(); + + expect(renderElement).toHaveBeenCalledWith({ node: nodeWithRandomProp }); + }); + + it('calls setNode with node ID and actual size after rendering', () => { + const { getByText } = render(); + expect(getByText(minProps.id)).toBeInTheDocument(); + + // Updates the node in the graph + expect(setNode).toHaveBeenCalledWith(node.id, { + height: 100, + width: 100, + ...node, + }); + + // Does not pass down width/height to node + expect(renderElement).not.toHaveBeenCalledWith( + expect.objectContaining({ height: 100, width: 100 }), + ); + }); +}); diff --git a/packages/core/src/components/DependencyGraph/Node.tsx b/packages/core/src/components/DependencyGraph/Node.tsx new file mode 100644 index 0000000000..4d64e31335 --- /dev/null +++ b/packages/core/src/components/DependencyGraph/Node.tsx @@ -0,0 +1,76 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import makeStyles from '@material-ui/core/styles/makeStyles'; +import { DefaultNode } from './DefaultNode'; +import { RenderNodeFunction, RenderNodeProps, GraphNode } from './types'; +import { NODE_TEST_ID } from './constants'; + +const useStyles = makeStyles(theme => ({ + node: { + transition: `${theme.transitions.duration.shortest}ms`, + }, +})); + +export type NodeComponentProps = { + node: GraphNode; + render?: RenderNodeFunction; + setNode: dagre.graphlib.Graph['setNode']; +}; + +const renderDefault = (props: RenderNodeProps) => ; + +export function Node({ + render = renderDefault, + setNode, + node, +}: NodeComponentProps) { + const { width, height, x = 0, y = 0, ...nodeProps } = node; + const classes = useStyles(); + const nodeRef = React.useRef(null); + + React.useLayoutEffect(() => { + // set the node width to the actual rendered width to properly layout graph + if (nodeRef.current) { + let { + height: renderedHeight, + width: renderedWidth, + } = nodeRef.current.getBBox(); + renderedHeight = Math.round(renderedHeight); + renderedWidth = Math.round(renderedWidth); + + if (renderedHeight !== height || renderedWidth !== width) { + setNode(node.id, { + ...node, + height: renderedHeight, + width: renderedWidth, + }); + } + } + }, [node, width, height, setNode]); + + return ( + + {render({ node: nodeProps })} + + ); +} diff --git a/packages/core/src/components/DependencyGraph/constants.ts b/packages/core/src/components/DependencyGraph/constants.ts new file mode 100644 index 0000000000..412a677f87 --- /dev/null +++ b/packages/core/src/components/DependencyGraph/constants.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const ARROW_MARKER_ID = 'arrow-marker'; + +export const NODE_TEST_ID = 'node'; +export const EDGE_TEST_ID = 'edge'; +export const LABEL_TEST_ID = 'label'; diff --git a/packages/core/src/components/DependencyGraph/index.ts b/packages/core/src/components/DependencyGraph/index.ts new file mode 100644 index 0000000000..9a4f0071e3 --- /dev/null +++ b/packages/core/src/components/DependencyGraph/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as DependencyGraphTypes from './types'; + +export { DependencyGraph } from './DependencyGraph'; +export { DependencyGraphTypes }; diff --git a/packages/core/src/components/DependencyGraph/types.ts b/packages/core/src/components/DependencyGraph/types.ts new file mode 100644 index 0000000000..19173db4b3 --- /dev/null +++ b/packages/core/src/components/DependencyGraph/types.ts @@ -0,0 +1,88 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import dagre from 'dagre'; + +type CustomType = { [customKey: string]: any }; + +/* Edges */ +export type DependencyEdge = T & { + from: string; + to: string; + label?: string; +}; + +export type GraphEdge = DependencyEdge & + dagre.GraphEdge & + EdgeProperties; + +export type RenderLabelProps = { edge: DependencyEdge }; + +export type RenderLabelFunction = ( + props: RenderLabelProps, +) => React.ReactNode; + +/* Nodes */ +export type DependencyNode = T & { + id: string; +}; + +export type GraphNode = dagre.Node>; + +export type RenderNodeProps = { node: DependencyNode }; + +export type RenderNodeFunction = ( + props: RenderNodeProps, +) => React.ReactNode; + +/* Based on: https://github.com/dagrejs/dagre/wiki#configuring-the-layout */ + +export type EdgeProperties = { + label?: string; + width?: number; + height?: number; + labeloffset?: number; + labelpos?: LabelPosition; + minlen?: number; + weight?: number; + [customKey: string]: any; +}; + +export enum Direction { + TOP_BOTTOM = 'TB', + BOTTOM_TOP = 'BT', + LEFT_RIGHT = 'LR', + RIGHT_LEFT = 'RL', +} + +export enum Alignment { + UP_LEFT = 'UL', + UP_RIGHT = 'UR', + DOWN_LEFT = 'DL', + DOWN_RIGHT = 'DR', +} + +export enum Ranker { + NETWORK_SIMPLEX = 'network-simplex', + TIGHT_TREE = 'tight-tree', + LONGEST_PATH = 'longest-path', +} + +export enum LabelPosition { + LEFT = 'l', + RIGHT = 'r', + CENTER = 'c', +} diff --git a/packages/core/src/components/index.ts b/packages/core/src/components/index.ts index 40c4ef0c11..bcc56debea 100644 --- a/packages/core/src/components/index.ts +++ b/packages/core/src/components/index.ts @@ -18,6 +18,7 @@ export * from './AlertDisplay'; export * from './Button'; export * from './CodeSnippet'; export * from './CopyTextButton'; +export * from './DependencyGraph'; export * from './DismissableBanner'; export * from './FeatureDiscovery'; export * from './HorizontalScrollGrid'; diff --git a/yarn.lock b/yarn.lock index 8ee1728b30..c265aecd6a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4982,16 +4982,38 @@ dependencies: postcss "5 - 7" +"@types/d3-color@*": + version "2.0.0" + resolved "https://registry.npmjs.org/@types/d3-color/-/d3-color-2.0.0.tgz#febdfadade56e215a4c3f612fe3000d92999f5d5" + integrity sha512-Bs0maTeU47rdZT+n42iQ0C4gnbnJlIDJkqHFtIsDx2tPPITDeoSdIrm+00UYXzegzArYC2GsG80eHNMwz08IAw== + "@types/d3-force@^1.2.1": version "1.2.1" resolved "https://registry.npmjs.org/@types/d3-force/-/d3-force-1.2.1.tgz#c28803ea36fe29788db69efa0ad6c2dc09544e83" integrity sha512-jqK+I36uz4kTBjyk39meed5y31Ab+tXYN/x1dn3nZEus9yOHCLc+VrcIYLc/aSQ0Y7tMPRlIhLetulME76EiiA== +"@types/d3-interpolate@*": + version "2.0.0" + resolved "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-2.0.0.tgz#325029216dc722c1c68c33ccda759f1209d35823" + integrity sha512-Wt1v2zTlEN8dSx8hhx6MoOhWQgTkz0Ukj7owAEIOF2QtI0e219paFX9rf/SLOr/UExWb1TcUzatU8zWwFby6gg== + dependencies: + "@types/d3-color" "*" + "@types/d3-path@*": version "1.0.8" resolved "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.8.tgz#48e6945a8ff43ee0a1ce85c8cfa2337de85c7c79" integrity sha512-AZGHWslq/oApTAHu9+yH/Bnk63y9oFOMROtqPAtxl5uB6qm1x2lueWdVEjsjjV3Qc2+QfuzKIwIR5MvVBakfzA== +"@types/d3-path@^1": + version "1.0.9" + resolved "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.9.tgz#73526b150d14cd96e701597cbf346cfd1fd4a58c" + integrity sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ== + +"@types/d3-selection@*", "@types/d3-selection@^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-2.0.0.tgz#59df94a8e47ed1050a337d4ffb4d4d213aa590a8" + integrity sha512-EF0lWZ4tg7oDFg4YQFlbOU3936e3a9UmoQ2IXlBy1+cv2c2Pv7knhKUzGlH5Hq2sF/KeDTH1amiRPey2rrLMQA== + "@types/d3-shape@*": version "1.3.2" resolved "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.2.tgz#a41d9d6b10d02e221696b240caf0b5d0f5a588ec" @@ -4999,6 +5021,26 @@ dependencies: "@types/d3-path" "*" +"@types/d3-shape@^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-2.0.0.tgz#61aa065726f3c2641aedc59c3603475ab11aeb2f" + integrity sha512-NLzD02m5PiD1KLEDjLN+MtqEcFYn4ZL9+Rqc9ZwARK1cpKZXd91zBETbe6wpBB6Ia0D0VZbpmbW3+BsGPGnCpA== + dependencies: + "@types/d3-path" "^1" + +"@types/d3-zoom@^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-2.0.0.tgz#ef8b87464e8ebc7c66b70f6383d1ae841e78e7fc" + integrity sha512-daL0PJm4yT0ISTGa7p2lHX0kvv9FO/IR1ooWbHR/7H4jpbaKiLux5FslyS/OvISPiJ5SXb4sOqYhO6fMB6hKRw== + dependencies: + "@types/d3-interpolate" "*" + "@types/d3-selection" "*" + +"@types/dagre@^0.7.44": + version "0.7.44" + resolved "https://registry.npmjs.org/@types/dagre/-/dagre-0.7.44.tgz#8f4b796b118ca29c132da7068fbc0d0351ee5851" + integrity sha512-N6HD+79w77ZVAaVO7JJDW5yJ9LAxM62FpgNGO9xEde+KVYjDRyhIMzfiErXpr1g0JPon9kwlBzoBK6s4fOww9Q== + "@types/diff@^4.0.2": version "4.0.2" resolved "https://registry.npmjs.org/@types/diff/-/diff-4.0.2.tgz#2e9bb89f9acc3ab0108f0f3dc4dbdcf2fff8a99c" @@ -9645,11 +9687,29 @@ d3-color@1: resolved "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz#c52002bf8846ada4424d55d97982fef26eb3bc8a" integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q== +"d3-color@1 - 2": + version "2.0.0" + resolved "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz#8d625cab42ed9b8f601a1760a389f7ea9189d62e" + integrity sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ== + "d3-dispatch@1 - 2": version "2.0.0" resolved "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-2.0.0.tgz#8a18e16f76dd3fcaef42163c97b926aa9b55e7cf" integrity sha512-S/m2VsXI7gAti2pBoLClFFTMOO1HTtT0j99AuXLoGFKO6deHDdnv6ZGTxSTTUTgO1zVcv82fCOtDjYK4EECmWA== +d3-drag@2: + version "2.0.0" + resolved "https://registry.npmjs.org/d3-drag/-/d3-drag-2.0.0.tgz#9eaf046ce9ed1c25c88661911c1d5a4d8eb7ea6d" + integrity sha512-g9y9WbMnF5uqB9qKqwIIa/921RYWzlUDv9Jl1/yONQwxbOfszAWTCm8u7HOTgJgRDXiRZN56cHT9pd24dmXs8w== + dependencies: + d3-dispatch "1 - 2" + d3-selection "2" + +"d3-ease@1 - 2": + version "2.0.0" + resolved "https://registry.npmjs.org/d3-ease/-/d3-ease-2.0.0.tgz#fd1762bfca00dae4bacea504b1d628ff290ac563" + integrity sha512-68/n9JWarxXkOWMshcT5IcjbB+agblQUaIsbnXmrzejn2O82n3p2A9R2zEB9HIEFWKFwPAEDDN8gR0VdSAyyAQ== + d3-force@^2.0.1: version "2.1.1" resolved "https://registry.npmjs.org/d3-force/-/d3-force-2.1.1.tgz#f20ccbf1e6c9e80add1926f09b51f686a8bc0937" @@ -9671,11 +9731,23 @@ d3-interpolate@1, d3-interpolate@^1.3.0: dependencies: d3-color "1" +"d3-interpolate@1 - 2": + version "2.0.1" + resolved "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz#98be499cfb8a3b94d4ff616900501a64abc91163" + integrity sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ== + dependencies: + d3-color "1 - 2" + d3-path@1: version "1.0.9" resolved "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf" integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg== +"d3-path@1 - 2": + version "2.0.0" + resolved "https://registry.npmjs.org/d3-path/-/d3-path-2.0.0.tgz#55d86ac131a0548adae241eebfb56b4582dd09d8" + integrity sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA== + "d3-quadtree@1 - 2": version "2.0.0" resolved "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-2.0.0.tgz#edbad045cef88701f6fee3aee8e93fb332d30f9d" @@ -9693,6 +9765,11 @@ d3-scale@^2.1.0: d3-time "1" d3-time-format "2" +d3-selection@2, d3-selection@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/d3-selection/-/d3-selection-2.0.0.tgz#94a11638ea2141b7565f883780dabc7ef6a61066" + integrity sha512-XoGGqhLUN/W14NmaqcO/bb1nqjDAw5WtSYb2X8wiuQWvSZUsUVYsOSkOybUrNvcBjaywBdYPy03eXHMXjk9nZA== + d3-shape@^1.2.0: version "1.3.7" resolved "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7" @@ -9700,6 +9777,13 @@ d3-shape@^1.2.0: dependencies: d3-path "1" +d3-shape@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/d3-shape/-/d3-shape-2.0.0.tgz#2331b62fa784a2a1daac47a7233cfd69301381fd" + integrity sha512-djpGlA779ua+rImicYyyjnOjeubyhql1Jyn1HK0bTyawuH76UQRWXd+pftr67H6Fa8hSwetkgb/0id3agKWykw== + dependencies: + d3-path "1 - 2" + d3-time-format@2: version "2.3.0" resolved "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz#107bdc028667788a8924ba040faf1fbccd5a7850" @@ -9717,6 +9801,28 @@ d3-time@1: resolved "https://registry.npmjs.org/d3-timer/-/d3-timer-2.0.0.tgz#055edb1d170cfe31ab2da8968deee940b56623e6" integrity sha512-TO4VLh0/420Y/9dO3+f9abDEFYeCUr2WZRlxJvbp4HPTQcSylXNiL6yZa9FIUvV1yRiFufl1bszTCLDqv9PWNA== +d3-transition@2: + version "2.0.0" + resolved "https://registry.npmjs.org/d3-transition/-/d3-transition-2.0.0.tgz#366ef70c22ef88d1e34105f507516991a291c94c" + integrity sha512-42ltAGgJesfQE3u9LuuBHNbGrI/AJjNL2OAUdclE70UE6Vy239GCBEYD38uBPoLeNsOhFStGpPI0BAOV+HMxog== + dependencies: + d3-color "1 - 2" + d3-dispatch "1 - 2" + d3-ease "1 - 2" + d3-interpolate "1 - 2" + d3-timer "1 - 2" + +d3-zoom@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/d3-zoom/-/d3-zoom-2.0.0.tgz#f04d0afd05518becce879d04709c47ecd93fba54" + integrity sha512-fFg7aoaEm9/jf+qfstak0IYpnesZLiMX6GZvXtUSdv8RH2o4E2qeelgdU09eKS6wGuiGMfcnMI0nTIqWzRHGpw== + dependencies: + d3-dispatch "1 - 2" + d3-drag "2" + d3-interpolate "1 - 2" + d3-selection "2" + d3-transition "2" + d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" @@ -9725,6 +9831,14 @@ d@1, d@^1.0.1: es5-ext "^0.10.50" type "^1.0.1" +dagre@^0.8.5: + version "0.8.5" + resolved "https://registry.npmjs.org/dagre/-/dagre-0.8.5.tgz#ba30b0055dac12b6c1fcc247817442777d06afee" + integrity sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw== + dependencies: + graphlib "^2.1.8" + lodash "^4.17.15" + damerau-levenshtein@^1.0.4: version "1.0.6" resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz#143c1641cb3d85c60c32329e26899adea8701791" @@ -12568,6 +12682,13 @@ graphiql@^1.0.0-alpha.10: regenerator-runtime "^0.13.5" theme-ui "^0.3.1" +graphlib@^2.1.8: + version "2.1.8" + resolved "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da" + integrity sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A== + dependencies: + lodash "^4.17.15" + graphql-config@^3.0.2: version "3.0.3" resolved "https://registry.npmjs.org/graphql-config/-/graphql-config-3.0.3.tgz#58907c65ed7d6e04132321450b60e57863ea9a5f" From 5a2705de23f111491c73eb4a1ccbcf3b3618547c Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Fri, 6 Nov 2020 11:43:58 +0100 Subject: [PATCH 110/252] Export `AuthProviders`, `DefaultProviderSettings` and `ProviderSettingsItem` (#3249) `Settings` allows to pass `providerSettings`, but right now there isn't a way to constuct them out of the existing components as they aren't exported. --- .changeset/moody-pugs-smoke.md | 5 +++++ .../src/components/AuthProviders/index.ts | 1 + plugins/user-settings/src/components/index.ts | 18 ++++++++++++++++++ plugins/user-settings/src/index.ts | 3 +-- 4 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 .changeset/moody-pugs-smoke.md create mode 100644 plugins/user-settings/src/components/index.ts diff --git a/.changeset/moody-pugs-smoke.md b/.changeset/moody-pugs-smoke.md new file mode 100644 index 0000000000..3092ec4bfe --- /dev/null +++ b/.changeset/moody-pugs-smoke.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-user-settings': patch +--- + +Export `AuthProviders`, `DefaultProviderSettings` and `ProviderSettingsItem`. diff --git a/plugins/user-settings/src/components/AuthProviders/index.ts b/plugins/user-settings/src/components/AuthProviders/index.ts index 2f6a2b5ab1..dd7fbcf79b 100644 --- a/plugins/user-settings/src/components/AuthProviders/index.ts +++ b/plugins/user-settings/src/components/AuthProviders/index.ts @@ -16,3 +16,4 @@ export { AuthProviders } from './AuthProviders'; export { DefaultProviderSettings } from './DefaultProviderSettings'; +export { ProviderSettingsItem } from './ProviderSettingsItem'; diff --git a/plugins/user-settings/src/components/index.ts b/plugins/user-settings/src/components/index.ts new file mode 100644 index 0000000000..fe92b14adb --- /dev/null +++ b/plugins/user-settings/src/components/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { Settings } from './Settings'; +export { SettingsPage as Router } from './SettingsPage'; +export * from './AuthProviders'; diff --git a/plugins/user-settings/src/index.ts b/plugins/user-settings/src/index.ts index 113930902a..9b0ce4262c 100644 --- a/plugins/user-settings/src/index.ts +++ b/plugins/user-settings/src/index.ts @@ -14,5 +14,4 @@ * limitations under the License. */ export { plugin } from './plugin'; -export { Settings } from './components/Settings'; -export { SettingsPage as Router } from './components/SettingsPage'; +export * from './components/'; From e8f69ba9367c4c700df0990460753f71aa32b667 Mon Sep 17 00:00:00 2001 From: Jesko Steinberg <61695677+jeskosda@users.noreply.github.com> Date: Fri, 6 Nov 2020 20:17:17 +0100 Subject: [PATCH 111/252] feat: add internal routing to BottomLink (#3258) * feat(core): Link component detect whether it's an external link or not * fix: make BottomLink using backstage/core Link component --- .changeset/dry-pillows-carry.md | 6 ++++++ packages/core/src/components/Link/Link.tsx | 13 ++++++++++--- .../core/src/layout/BottomLink/BottomLink.test.tsx | 1 - packages/core/src/layout/BottomLink/BottomLink.tsx | 4 ++-- 4 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 .changeset/dry-pillows-carry.md diff --git a/.changeset/dry-pillows-carry.md b/.changeset/dry-pillows-carry.md new file mode 100644 index 0000000000..5352e26d2d --- /dev/null +++ b/.changeset/dry-pillows-carry.md @@ -0,0 +1,6 @@ +--- +'@backstage/core': patch +--- + +- The BottomLink is now able to handle with internal routes. +- @backstage/core Link component detect whether it's an external link or not, and render accordingly diff --git a/packages/core/src/components/Link/Link.tsx b/packages/core/src/components/Link/Link.tsx index 8e7e3f179e..59a9604fa7 100644 --- a/packages/core/src/components/Link/Link.tsx +++ b/packages/core/src/components/Link/Link.tsx @@ -25,6 +25,13 @@ type Props = ComponentProps & * Thin wrapper on top of material-ui's Link component * Makes the Link to utilise react-router */ -export const Link = React.forwardRef((props, ref) => ( - -)); +export const Link = React.forwardRef((props, ref) => { + const to = String(props.to); + return /^https?:\/\//.test(to) ? ( + // External links + + ) : ( + // Interact with React Router for internal links + + ); +}); diff --git a/packages/core/src/layout/BottomLink/BottomLink.test.tsx b/packages/core/src/layout/BottomLink/BottomLink.test.tsx index 92ae2ecb3b..8bea00e35d 100644 --- a/packages/core/src/layout/BottomLink/BottomLink.test.tsx +++ b/packages/core/src/layout/BottomLink/BottomLink.test.tsx @@ -22,7 +22,6 @@ const minProps = { title: 'A deepLink title', link: '/mocked', }; - describe('', () => { it('renders without exploding', async () => { const rendered = await renderInTestApp(); diff --git a/packages/core/src/layout/BottomLink/BottomLink.tsx b/packages/core/src/layout/BottomLink/BottomLink.tsx index b3f7b4aa11..e768ffd9e4 100644 --- a/packages/core/src/layout/BottomLink/BottomLink.tsx +++ b/packages/core/src/layout/BottomLink/BottomLink.tsx @@ -16,7 +16,6 @@ import React, { FC } from 'react'; import { - Link, ListItem, ListItemIcon, Divider, @@ -26,6 +25,7 @@ import { import ArrowIcon from '@material-ui/icons/ArrowForward'; import { BackstageTheme } from '@backstage/theme'; import Box from '@material-ui/core/Box'; +import { Link } from '../../components/Link'; const useStyles = makeStyles(theme => ({ root: { @@ -53,7 +53,7 @@ export const BottomLink: FC = ({ link, title, onClick }) => { return (
- + From 33b7300ebefedc35cf7a83b8e6a3938a171aeb39 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Fri, 6 Nov 2020 20:21:36 +0100 Subject: [PATCH 112/252] Capture plugin name under the /api/ prefix for http metrics (#3259) --- .changeset/weak-rice-beam.md | 5 +++ .../src/service/lib/metrics.test.ts | 37 +++++++++++++++++++ .../backend-common/src/service/lib/metrics.ts | 18 ++++++++- 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 .changeset/weak-rice-beam.md create mode 100644 packages/backend-common/src/service/lib/metrics.test.ts diff --git a/.changeset/weak-rice-beam.md b/.changeset/weak-rice-beam.md new file mode 100644 index 0000000000..c507e350a9 --- /dev/null +++ b/.changeset/weak-rice-beam.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +Capture plugin name under the /api/ prefix for http metrics diff --git a/packages/backend-common/src/service/lib/metrics.test.ts b/packages/backend-common/src/service/lib/metrics.test.ts new file mode 100644 index 0000000000..9126423b7e --- /dev/null +++ b/packages/backend-common/src/service/lib/metrics.test.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { normalizePath } from './metrics'; + +describe('normalizePath', () => { + it('should normalize /path to /path', async () => { + const path = normalizePath({ url: 'http://server/path' }); + + expect(path).toBe('/path'); + }); + + it('should normalize /path/test to /path', async () => { + const path = normalizePath({ url: 'http://server/path/test' }); + + expect(path).toBe('/path'); + }); + + it('should normalize /api/plugin-name/test to /api/plugin-name', async () => { + const path = normalizePath({ url: 'http://server/api/plugin-name/test' }); + + expect(path).toBe('/api/plugin-name'); + }); +}); diff --git a/packages/backend-common/src/service/lib/metrics.ts b/packages/backend-common/src/service/lib/metrics.ts index d7b544765b..37d441f53c 100644 --- a/packages/backend-common/src/service/lib/metrics.ts +++ b/packages/backend-common/src/service/lib/metrics.ts @@ -16,6 +16,22 @@ import prom from 'prom-client'; import promBundle from 'express-prom-bundle'; import { RequestHandler } from 'express'; +import * as url from 'url'; + +const rootRegEx = new RegExp('^/([^/]*)/.*'); +const apiRegEx = new RegExp('^/api/([^/]*)/.*'); + +export function normalizePath(req: any): string { + const path = url.parse(req.originalUrl || req.url).pathname || '/'; + + // Capture /api/ and the plugin name + if (apiRegEx.test(path)) { + return path.replace(apiRegEx, '/api/$1'); + } + + // Only the first path segment at root level + return path.replace(rootRegEx, '/$1'); +} /** * Adds a /metrics endpoint, register default runtime metrics and instrument the router. @@ -31,7 +47,7 @@ export function metricsHandler(): RequestHandler { // cardinality (e.g. path params). Instead we would have to template them. However, this // is difficult, as every backend plugin might use different routes. Instead we only take // the first directory of the path, to have at least an idea how each plugin performs: - normalizePath: [['^/([^/]*)/.*', '/$1']], + normalizePath, promClient: { collectDefaultMetrics: {} }, }); } From e1292f417ce7a0c4c065fdb9b3b4a6d8bbc46219 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Sat, 7 Nov 2020 01:47:08 -0500 Subject: [PATCH 113/252] Fix #3329: Prevent nightly build on forks (#3261) * Prevent nightly build on forks * Fix indentation * Fix prettier! --- .github/workflows/nightly.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 335bd80109..3f59b815b3 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -6,6 +6,8 @@ on: jobs: build: + if: github.repository == 'backstage/backstage' # prevent running on forks + runs-on: ubuntu-latest strategy: From 00b7b66bd8107cc006e0dbb9dbe086bd75764d9f Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Sat, 7 Nov 2020 06:30:16 -0500 Subject: [PATCH 114/252] Fix typo in support button example (#3262) --- packages/core/src/layout/Page/Page.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/layout/Page/Page.stories.tsx b/packages/core/src/layout/Page/Page.stories.tsx index 3b1dd07c7a..1d3aaea412 100644 --- a/packages/core/src/layout/Page/Page.stories.tsx +++ b/packages/core/src/layout/Page/Page.stories.tsx @@ -185,7 +185,7 @@ const ExampleContentHeader = ({ selectedTab }: { selectedTab?: number }) => ( title={selectedTab !== undefined ? tabs[selectedTab].label : 'Header'} > - This Plugin is an example. This text could provide usefull information for + This Plugin is an example. This text could provide useful information for the user. From a41730c6ead2ebf297b13be794af0240d986ad03 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Sat, 7 Nov 2020 06:31:11 -0500 Subject: [PATCH 115/252] [plugin-jenkins] Add tooltip to Jenkins build rerun (#3251) * Add tooltop to Jenkins build rerun * Add tooltip to spelling --- .changeset/calm-emus-push.md | 5 +++++ .github/styles/vocab.txt | 1 + .../src/components/BuildsPage/lib/CITable/CITable.tsx | 10 ++++++---- 3 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 .changeset/calm-emus-push.md diff --git a/.changeset/calm-emus-push.md b/.changeset/calm-emus-push.md new file mode 100644 index 0000000000..b83f442c8a --- /dev/null +++ b/.changeset/calm-emus-push.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-jenkins': minor +--- + +Add tooltip for Jenkins rerun button diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt index ecb8fdf3c1..3c5fa34adc 100644 --- a/.github/styles/vocab.txt +++ b/.github/styles/vocab.txt @@ -191,6 +191,7 @@ toc tolerations Tolerations toolsets +tooltip touchpoints ui upvote diff --git a/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx b/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx index 23f9af445c..5d5d7d9fcd 100644 --- a/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx +++ b/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import React, { FC } from 'react'; -import { Box, IconButton, Link, Typography } from '@material-ui/core'; +import { Box, IconButton, Link, Typography, Tooltip } from '@material-ui/core'; import RetryIcon from '@material-ui/icons/Replay'; import GitHubIcon from '@material-ui/icons/GitHub'; import { generatePath, Link as RouterLink } from 'react-router-dom'; @@ -179,9 +179,11 @@ const generatedColumns: TableColumn[] = [ title: 'Actions', sorting: false, render: (row: Partial) => ( - - - + + + + + ), width: '10%', }, From 63834ee3383a8ab548c6fe566bb30bf2ddae1d52 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 6 Nov 2020 15:45:16 +0100 Subject: [PATCH 116/252] microsite/static: add confugration meta-schema based on JSONSchema draft-07 --- microsite/static/schema/config-v1 | 170 ++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 microsite/static/schema/config-v1 diff --git a/microsite/static/schema/config-v1 b/microsite/static/schema/config-v1 new file mode 100644 index 0000000000..f85f2844cc --- /dev/null +++ b/microsite/static/schema/config-v1 @@ -0,0 +1,170 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://backstage.io/schema/config-v1", + "title": "Backstage Configuration Meta-Schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [ + { "$ref": "#/definitions/nonNegativeInteger" }, + { "default": 0 } + ] + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + }, + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "$comment": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "readOnly": { + "type": "boolean", + "default": false + }, + "writeOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, + "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { "$ref": "#" }, + "items": { + "anyOf": [{ "$ref": "#" }, { "$ref": "#/definitions/schemaArray" }], + "default": true + }, + "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, + "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": { "$ref": "#" }, + "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, + "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "required": { "$ref": "#/definitions/stringArray" }, + "additionalProperties": { "$ref": "#" }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "propertyNames": { "format": "regex" }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [{ "$ref": "#" }, { "$ref": "#/definitions/stringArray" }] + } + }, + "propertyNames": { "$ref": "#" }, + "const": true, + "enum": { + "type": "array", + "items": true, + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { "type": "string" }, + "contentMediaType": { "type": "string" }, + "contentEncoding": { "type": "string" }, + "if": { "$ref": "#" }, + "then": { "$ref": "#" }, + "else": { "$ref": "#" }, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" }, + "visibility": { + "type": "string", + "enum": ["frontend", "backend", "secret"] + } + }, + "default": true +} From 8a39c5bd1c14951af5e3d3ed15108cbf16f660fd Mon Sep 17 00:00:00 2001 From: Ryan Vazquez Date: Mon, 9 Nov 2020 09:33:42 -0500 Subject: [PATCH 117/252] Big Query -> BigQuery --- app-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-config.yaml b/app-config.yaml index 4746e822da..6f82af7772 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -245,7 +245,7 @@ costInsights: name: Cloud Storage icon: storage bigQuery: - name: Big Query + name: BigQuery icon: search metrics: DAU: From c8b24b5bb36675f3337efd52bb16e010af36ae87 Mon Sep 17 00:00:00 2001 From: Ryan Vazquez Date: Mon, 9 Nov 2020 09:34:11 -0500 Subject: [PATCH 118/252] custom tooltip; export custom bar chart components --- .../cost-insights/src/api/CostInsightsApi.ts | 4 +- plugins/cost-insights/src/client.ts | 109 ++++++++- .../src/components/BarChart/BarChart.test.tsx | 28 +-- .../src/components/BarChart/BarChart.tsx | 77 +++--- .../src/components/BarChart/BarChartLabel.tsx | 11 +- .../BarChartLegend.test.tsx} | 17 +- .../BarChartLegend.tsx} | 51 ++-- .../src/components/BarChart/BarChartTick.tsx | 10 +- .../BarChartTooltip.test.tsx} | 21 +- .../components/BarChart/BarChartTooltip.tsx | 79 ++++++ .../BarChartTooltipItem.tsx} | 19 +- .../src/components/BarChart/helpers.tsx | 42 ++++ .../src/components/BarChart/index.ts | 9 + .../src/components/CostGrowth/CostGrowth.tsx | 2 +- .../CostGrowth/CostGrowthIndicator.test.tsx | 59 +++++ .../CostGrowth/CostGrowthIndicator.tsx | 68 ++++++ .../src/components/CostGrowth/index.ts | 2 + .../CostOverviewCard/CostOverviewChart.tsx | 70 +++--- .../CostOverviewCard/CostOverviewTooltip.tsx | 38 --- .../ProductInsights/ProductInsights.tsx | 1 + .../ProductEntityDialog.tsx | 188 +++++++++++++++ .../ProductInsightsCard.test.tsx | 38 +-- .../ProductInsightsCard.tsx | 66 ++--- .../ProductInsightsChart.tsx | 225 ++++++++++++++++++ .../components/ProductInsightsCard/index.ts | 1 + .../ProjectGrowthAlertCard.tsx | 31 +-- .../ProjectGrowthAlertChart.tsx | 62 +++++ .../ProjectGrowthInstructionsPage.tsx | 79 +++--- .../ResourceGrowthBarChart.test.tsx | 41 ---- .../ResourceGrowthBarChart.tsx | 87 ------- .../ResourceGrowthBarChart/index.ts | 17 -- .../ResourceGrowthBarChartLegend.test.tsx | 59 ----- .../ResourceGrowthBarChartLegend/index.ts | 17 -- .../src/components/Tooltip/Tooltip.tsx | 52 ---- .../src/components/Tooltip/index.ts | 20 -- .../UnlabeledDataflowAlertCard.tsx | 42 ++-- .../UnlabeledDataflowBarChart.tsx | 71 ------ .../UnlabeledDataflowBarChartLegend.tsx | 57 ----- plugins/cost-insights/src/components/index.ts | 1 - plugins/cost-insights/src/types/Alert.ts | 5 +- plugins/cost-insights/src/types/Entity.ts | 65 ++++- plugins/cost-insights/src/types/Product.ts | 10 - plugins/cost-insights/src/types/Theme.ts | 1 + .../cost-insights/src/utils/change.test.ts | 62 +++++ plugins/cost-insights/src/utils/change.ts | 20 +- plugins/cost-insights/src/utils/graphs.ts | 79 ++++++ plugins/cost-insights/src/utils/graphs.tsx | 36 --- plugins/cost-insights/src/utils/mockData.ts | 19 +- plugins/cost-insights/src/utils/sort.ts | 1 + plugins/cost-insights/src/utils/styles.ts | 99 +++++++- 50 files changed, 1425 insertions(+), 843 deletions(-) rename plugins/cost-insights/src/components/{UnlabeledDataflowAlertCard/UnlabeledDataflowBarChartLegend.test.tsx => BarChart/BarChartLegend.test.tsx} (56%) rename plugins/cost-insights/src/components/{ResourceGrowthBarChartLegend/ResourceGrowthBarChartLegend.tsx => BarChart/BarChartLegend.tsx} (55%) rename plugins/cost-insights/src/components/{Tooltip/Tooltip.test.tsx => BarChart/BarChartTooltip.test.tsx} (74%) create mode 100644 plugins/cost-insights/src/components/BarChart/BarChartTooltip.tsx rename plugins/cost-insights/src/components/{Tooltip/TooltipItem.tsx => BarChart/BarChartTooltipItem.tsx} (76%) create mode 100644 plugins/cost-insights/src/components/BarChart/helpers.tsx create mode 100644 plugins/cost-insights/src/components/CostGrowth/CostGrowthIndicator.test.tsx create mode 100644 plugins/cost-insights/src/components/CostGrowth/CostGrowthIndicator.tsx delete mode 100644 plugins/cost-insights/src/components/CostOverviewCard/CostOverviewTooltip.tsx create mode 100644 plugins/cost-insights/src/components/ProductInsightsCard/ProductEntityDialog.tsx create mode 100644 plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsChart.tsx create mode 100644 plugins/cost-insights/src/components/ProjectGrowthAlertCard/ProjectGrowthAlertChart.tsx delete mode 100644 plugins/cost-insights/src/components/ResourceGrowthBarChart/ResourceGrowthBarChart.test.tsx delete mode 100644 plugins/cost-insights/src/components/ResourceGrowthBarChart/ResourceGrowthBarChart.tsx delete mode 100644 plugins/cost-insights/src/components/ResourceGrowthBarChart/index.ts delete mode 100644 plugins/cost-insights/src/components/ResourceGrowthBarChartLegend/ResourceGrowthBarChartLegend.test.tsx delete mode 100644 plugins/cost-insights/src/components/ResourceGrowthBarChartLegend/index.ts delete mode 100644 plugins/cost-insights/src/components/Tooltip/Tooltip.tsx delete mode 100644 plugins/cost-insights/src/components/Tooltip/index.ts delete mode 100644 plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowBarChart.tsx delete mode 100644 plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowBarChartLegend.tsx create mode 100644 plugins/cost-insights/src/utils/change.test.ts create mode 100644 plugins/cost-insights/src/utils/graphs.ts delete mode 100644 plugins/cost-insights/src/utils/graphs.tsx diff --git a/plugins/cost-insights/src/api/CostInsightsApi.ts b/plugins/cost-insights/src/api/CostInsightsApi.ts index be5cd54fdb..5493130f78 100644 --- a/plugins/cost-insights/src/api/CostInsightsApi.ts +++ b/plugins/cost-insights/src/api/CostInsightsApi.ts @@ -19,9 +19,9 @@ import { Alert, Cost, Duration, + Entity, Group, Project, - ProductCost, Maybe, MetricData, } from '../types'; @@ -119,7 +119,7 @@ export type CostInsightsApi = { group: string, duration: Duration, project: Maybe, - ): Promise; + ): Promise; /** * Get current cost alerts for a given group. These show up as Action Items for the group on the diff --git a/plugins/cost-insights/src/client.ts b/plugins/cost-insights/src/client.ts index f7671bcce0..afdd9aff74 100644 --- a/plugins/cost-insights/src/client.ts +++ b/plugins/cost-insights/src/client.ts @@ -25,10 +25,10 @@ import { DateAggregation, DEFAULT_DATE_FORMAT, Duration, + Entity, Group, Maybe, MetricData, - ProductCost, Project, ProjectGrowthData, Trendline, @@ -198,10 +198,11 @@ export class ExampleCostInsightsClient implements CostInsightsApi { group: string, duration: Duration, project: Maybe, - ): Promise { + ): Promise { const projectProductInsights = await this.request( { product, group, duration, project }, { + id: 'project-product', aggregation: [80_000, 110_000], change: { ratio: 0.375, @@ -211,25 +212,47 @@ export class ExampleCostInsightsClient implements CostInsightsApi { { id: null, // entities with null ids will be appear as "Unlabeled" in product panels aggregation: [45_000, 50_000], + change: { + ratio: 0.111, + amount: 5_000, + }, + entities: [], }, { id: 'entity-a', aggregation: [15_000, 20_000], + change: { + ratio: 0.333, + amount: 5_000, + }, + entities: [], }, { id: 'entity-b', aggregation: [20_000, 30_000], + change: { + ratio: 0.5, + amount: 10_000, + }, + entities: [], }, { - id: 'entity-e', + id: 'entity-c', aggregation: [0, 10_000], + change: { + ratio: 10_000, + amount: 10_000, + }, + entities: [], }, ], }, ); - const productInsights: ProductCost = await this.request( + + const productInsights: Entity = await this.request( { product, group, duration, project }, { + id: 'product', aggregation: [200_000, 250_000], change: { ratio: 0.2, @@ -238,39 +261,103 @@ export class ExampleCostInsightsClient implements CostInsightsApi { entities: [ { id: null, // entities with null ids will be appear as "Unlabeled" in product panels - aggregation: [15_000, 30_000], + aggregation: [36_000, 42_000], + change: { + ratio: 0.1666, + amount: 6_000, + }, + entities: [], }, { id: 'entity-a', aggregation: [15_000, 20_000], + change: { + ratio: -0.33333333, + amount: 5_000, + }, + entities: [], }, { id: 'entity-b', aggregation: [20_000, 30_000], + change: { + ratio: 0.5, + amount: 10_000, + }, + entities: [], }, { id: 'entity-c', aggregation: [18_000, 25_000], + change: { + ratio: 0.38, + amount: 7_000, + }, + entities: [], }, { id: 'entity-d', - aggregation: [36_000, 42_000], + aggregation: [15_000, 30_000], + change: { + ratio: 1, + amount: 15_000, + }, + entities: [], }, { id: 'entity-e', aggregation: [0, 10_000], + entities: [], + change: { + ratio: 10_000, + amount: 10_000, + }, }, { id: 'entity-f', aggregation: [17_000, 19_000], + change: { + ratio: 0.118, + amount: 2_000, + }, + entities: [], }, { id: 'entity-g', - aggregation: [49_000, 30_000], - }, - { - id: 'entity-h', - aggregation: [0, 34_000], + aggregation: [80_000, 60_000], + change: { + ratio: -0.25, + amount: -20_000, + }, + entities: [ + { + id: 'vCPU Time Batch Belgium', + aggregation: [15_000, 15_000], + change: { + ratio: 0, + amount: 0, + }, + entities: [], + }, + { + id: 'RAM Time Belgium', + aggregation: [15_000, 30_000], + change: { + ratio: 1, + amount: 15_000, + }, + entities: [], + }, + { + id: 'Local Disk Time PD Standard Belgium', + aggregation: [50_000, 15_000], + change: { + ratio: -0.7, + amount: -35_000, + }, + entities: [], + }, + ], }, ], }, diff --git a/plugins/cost-insights/src/components/BarChart/BarChart.test.tsx b/plugins/cost-insights/src/components/BarChart/BarChart.test.tsx index a095324e6e..4e40a5ddda 100644 --- a/plugins/cost-insights/src/components/BarChart/BarChart.test.tsx +++ b/plugins/cost-insights/src/components/BarChart/BarChart.test.tsx @@ -15,56 +15,40 @@ */ import React from 'react'; -import { TooltipPayload } from 'recharts'; import { fireEvent } from '@testing-library/react'; import { BarChart, BarChartProps } from './BarChart'; -import { BarChartData, ResourceData } from '../../types'; +import { ResourceData } from '../../types'; import { createMockEntity } from '../../utils/mockData'; import { resourceSort } from '../../utils/sort'; import { renderInTestApp } from '@backstage/test-utils'; -import { TooltipItemProps } from '../Tooltip'; -import { costInsightsLightTheme } from '../../utils/styles'; + +// Disable responsive container console warnings generated by recharts; doesn't disable React warnings. +jest.spyOn(console, 'warn').mockImplementation(() => {}); const MockEntities = [...Array(10)].map((_, index) => - createMockEntity(() => ({ + createMockEntity(mock => ({ + ...mock, id: `test-id-${index + 1}`, // grow resource costs linearly for testing aggregation: [index * 1000, (index + 1) * 1000], })), ); -const MockBarChartData: BarChartData = { - previousFill: costInsightsLightTheme.palette.yellow, - currentFill: costInsightsLightTheme.palette.darkBlue, - previousName: 'Last Period', - currentName: 'Current Period', -}; - const MockResources: ResourceData[] = MockEntities.map(entity => ({ name: entity.id, previous: entity.aggregation[0], current: entity.aggregation[1], })); -const MockTooltipItem = (payload: TooltipPayload): TooltipItemProps => ({ - label: payload.name, - value: payload.value as string, - fill: payload.fill as string, -}); - const renderWithProps = ({ responsive = false, displayAmount = 6, - barChartData = MockBarChartData, - getTooltipItem = MockTooltipItem, resources = MockResources, }: BarChartProps) => { return renderInTestApp( , ); diff --git a/plugins/cost-insights/src/components/BarChart/BarChart.tsx b/plugins/cost-insights/src/components/BarChart/BarChart.tsx index aa1fd5c601..30e8ad71f8 100644 --- a/plugins/cost-insights/src/components/BarChart/BarChart.tsx +++ b/plugins/cost-insights/src/components/BarChart/BarChart.tsx @@ -20,51 +20,59 @@ import { BarChart as RechartsBarChart, CartesianGrid, ContentRenderer, - TooltipProps, + TooltipProps as RechartsTooltipProps, + RechartsFunction, ResponsiveContainer, Tooltip as RechartsTooltip, XAxis, YAxis, - TooltipPayload, } from 'recharts'; import { Box, useTheme } from '@material-ui/core'; import { BarChartTick } from './BarChartTick'; import { BarChartStepper } from './BarChartStepper'; -import { Tooltip, TooltipItemProps } from '../Tooltip'; - import { currencyFormatter } from '../../utils/formatters'; import { BarChartData, - Maybe, ResourceData, DataKey, CostInsightsTheme, } from '../../types'; -import { notEmpty } from '../../utils/assert'; import { useBarChartStyles } from '../../utils/styles'; import { resourceSort } from '../../utils/sort'; export type BarChartProps = { + resources: ResourceData[]; responsive?: boolean; displayAmount?: number; - barChartData: BarChartData; - getTooltipItem: (payload: TooltipPayload) => Maybe; - resources: ResourceData[]; + options?: Partial; + tooltip?: ContentRenderer; + onClick?: RechartsFunction; + onMouseMove?: RechartsFunction; }; export const BarChart = ({ + resources, responsive = true, displayAmount = 6, - barChartData, - getTooltipItem, - resources, + options = {}, + tooltip, + onClick, + onMouseMove, }: BarChartProps) => { const theme = useTheme(); const styles = useBarChartStyles(theme); const [activeChart, setActiveChart] = useState(false); const [stepWindow, setStepWindow] = useState(() => [0, displayAmount]); - const { previousFill, currentFill, previousName, currentName } = barChartData; + const data = Object.assign( + { + previousFill: theme.palette.lightBlue, + currentFill: theme.palette.darkBlue, + previousName: 'Previous', + currentName: 'Current', + }, + options, + ); const [stepStart, stepEnd] = stepWindow; const steps = Math.ceil(resources.length / displayAmount); @@ -95,24 +103,11 @@ export const BarChart = ({ [setStepWindow, resources, displayAmount], ); - const handleChartEnter = () => setActiveChart(true); - - const handleChartLeave = () => setActiveChart(false); - - const renderTooltipContent: ContentRenderer = ({ - label, - payload, - }) => { - if (!(payload && typeof label === 'string')) return [null, null]; - const items = payload.map(getTooltipItem).filter(notEmpty); - return ; - }; - return ( setActiveChart(false)} + onMouseEnter={() => setActiveChart(true)} data-testid="bar-chart-wrapper" > {/* Setting fixed values for height and width generates a console warning in testing but enables ResponsiveContainer to render its children. */} @@ -121,16 +116,22 @@ export const BarChart = ({ width={responsive ? '100%' : styles.container.width} > - + {tooltip && ( + + )} 0, globalResourcesMax]} - tick={{ fill: styles.axis.fill }} + tick={styles.axis} /> diff --git a/plugins/cost-insights/src/components/BarChart/BarChartLabel.tsx b/plugins/cost-insights/src/components/BarChart/BarChartLabel.tsx index 0a21f1ed46..c4402893f3 100644 --- a/plugins/cost-insights/src/components/BarChart/BarChartLabel.tsx +++ b/plugins/cost-insights/src/components/BarChart/BarChartLabel.tsx @@ -15,7 +15,7 @@ */ import React, { PropsWithChildren } from 'react'; -import { Box } from '@material-ui/core'; +import { Box, Typography } from '@material-ui/core'; import { useBarChartLabelStyles } from '../../utils/styles'; type BarChartLabel = { @@ -23,6 +23,7 @@ type BarChartLabel = { y: number; height: number; width: number; + details?: JSX.Element; }; export const BarChartLabel = ({ @@ -30,11 +31,11 @@ export const BarChartLabel = ({ y, height, width, + details, children, }: PropsWithChildren) => { const classes = useBarChartLabelStyles(); const translateX = width * -0.5; - const childArray = React.Children.toArray(children); return ( - {childArray[0]} - {childArray.slice(1)} + + {children} + + {details} ); diff --git a/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowBarChartLegend.test.tsx b/plugins/cost-insights/src/components/BarChart/BarChartLegend.test.tsx similarity index 56% rename from plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowBarChartLegend.test.tsx rename to plugins/cost-insights/src/components/BarChart/BarChartLegend.test.tsx index ab38ba8d44..3074af716e 100644 --- a/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowBarChartLegend.test.tsx +++ b/plugins/cost-insights/src/components/BarChart/BarChartLegend.test.tsx @@ -15,20 +15,15 @@ */ import React from 'react'; -import { UnlabeledDataflowBarChartLegend } from './UnlabeledDataflowBarChartLegend'; import { renderInTestApp } from '@backstage/test-utils'; +import { BarChartLegend } from './BarChartLegend'; -describe('', () => { - it('Displays the correct text', async () => { +describe('', () => { + it(`Should display the correct cost start and end`, async () => { const rendered = await renderInTestApp( - , + , ); - expect(rendered.getByText('Total Unlabeled Cost')).toBeInTheDocument(); - expect(rendered.getByText('Total Labeled Cost')).toBeInTheDocument(); - expect(rendered.getByText('$9,843')).toBeInTheDocument(); - expect(rendered.getByText('$2,309')).toBeInTheDocument(); + expect(rendered.getByText(/\$1,000/)).toBeInTheDocument(); + expect(rendered.queryByText(/\$5,000/)).toBeInTheDocument(); }); }); diff --git a/plugins/cost-insights/src/components/ResourceGrowthBarChartLegend/ResourceGrowthBarChartLegend.tsx b/plugins/cost-insights/src/components/BarChart/BarChartLegend.tsx similarity index 55% rename from plugins/cost-insights/src/components/ResourceGrowthBarChartLegend/ResourceGrowthBarChartLegend.tsx rename to plugins/cost-insights/src/components/BarChart/BarChartLegend.tsx index 6b5ce17c95..6db0e13572 100644 --- a/plugins/cost-insights/src/components/ResourceGrowthBarChartLegend/ResourceGrowthBarChartLegend.tsx +++ b/plugins/cost-insights/src/components/BarChart/BarChartLegend.tsx @@ -14,47 +14,58 @@ * limitations under the License. */ -import React from 'react'; +import React, { PropsWithChildren } from 'react'; import { Box, useTheme } from '@material-ui/core'; import { LegendItem } from '../LegendItem'; -import { CostGrowth } from '../CostGrowth'; import { currencyFormatter } from '../../utils/formatters'; -import { ChangeStatistic, CostInsightsTheme, Duration } from '../../types'; +import { CostInsightsTheme } from '../../types'; +import { useBarChartLayoutStyles as useStyles } from '../../utils/styles'; -export type ResourceGrowthBarChartLegendProps = { - change: ChangeStatistic; - duration: Duration; +type BarChartLegendOptions = { previousName: string; + previousFill: string; currentName: string; - costStart: number; - costEnd: number; + currentFill: string; }; -export const ResourceGrowthBarChartLegend = ({ - change, - duration, - previousName, - currentName, +export type BarChartLegendProps = { + costStart: number; + costEnd: number; + options?: Partial; +}; + +export const BarChartLegend = ({ costStart, costEnd, -}: ResourceGrowthBarChartLegendProps) => { + options = {}, + children, +}: PropsWithChildren) => { const theme = useTheme(); + const classes = useStyles(); + + const data = Object.assign( + { + previousName: 'Previous', + previousFill: theme.palette.lightBlue, + currentName: 'Current', + currentFill: theme.palette.darkBlue, + }, + options, + ); return ( - + - + {currencyFormatter.format(costStart)} - + {currencyFormatter.format(costEnd)} - - - + {children} ); }; diff --git a/plugins/cost-insights/src/components/BarChart/BarChartTick.tsx b/plugins/cost-insights/src/components/BarChart/BarChartTick.tsx index 005753fce6..145e5f805b 100644 --- a/plugins/cost-insights/src/components/BarChart/BarChartTick.tsx +++ b/plugins/cost-insights/src/components/BarChart/BarChartTick.tsx @@ -26,6 +26,7 @@ type BarChartTickProps = { value: any; }; visibleTicksCount: number; + details?: JSX.Element; }; export const BarChartTick = ({ @@ -35,11 +36,18 @@ export const BarChartTick = ({ width, payload, visibleTicksCount, + details, }: BarChartTickProps) => { const gutterWidth = 5; const labelWidth = width / visibleTicksCount - gutterWidth * 2; return ( - + {!payload.value ? 'Unlabeled' : payload.value} ); diff --git a/plugins/cost-insights/src/components/Tooltip/Tooltip.test.tsx b/plugins/cost-insights/src/components/BarChart/BarChartTooltip.test.tsx similarity index 74% rename from plugins/cost-insights/src/components/Tooltip/Tooltip.test.tsx rename to plugins/cost-insights/src/components/BarChart/BarChartTooltip.test.tsx index 2f1f8fe7e1..b30714ca58 100644 --- a/plugins/cost-insights/src/components/Tooltip/Tooltip.test.tsx +++ b/plugins/cost-insights/src/components/BarChart/BarChartTooltip.test.tsx @@ -16,10 +16,11 @@ import React from 'react'; import { renderInTestApp } from '@backstage/test-utils'; -import { Tooltip } from './Tooltip'; +import { BarChartTooltip } from './BarChartTooltip'; +import { BarChartTooltipItem } from './BarChartTooltipItem'; import { CostInsightsThemeProvider } from '../CostInsightsPage/CostInsightsThemeProvider'; -const mockTooltipItems = [ +const items = [ { label: 'Cost', value: '$1,000,000', @@ -32,22 +33,14 @@ const mockTooltipItems = [ }, ]; -describe('', () => { - it('renders without exploding', async () => { - const rendered = await renderInTestApp( - - - , - ); - expect( - rendered.container.querySelector('.tooltip-content'), - ).toBeInTheDocument(); - }); +const tooltipItems = () => + items.map(item => ); +describe('', () => { it('formats label and tooltip item text correctly', async () => { const rendered = await renderInTestApp( - + {tooltipItems} , ); expect(rendered.getByText('05/16/2020')).toBeInTheDocument(); diff --git a/plugins/cost-insights/src/components/BarChart/BarChartTooltip.tsx b/plugins/cost-insights/src/components/BarChart/BarChartTooltip.tsx new file mode 100644 index 0000000000..2cb09b724e --- /dev/null +++ b/plugins/cost-insights/src/components/BarChart/BarChartTooltip.tsx @@ -0,0 +1,79 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { ReactNode, PropsWithChildren } from 'react'; +import { Box, Divider, Typography } from '@material-ui/core'; +import { useTooltipStyles as useStyles } from '../../utils/styles'; + +export type BarChartTooltipProps = { + title: string; + content?: ReactNode | string; + subtitle?: ReactNode; + topRight?: ReactNode; + actions?: ReactNode; +}; + +export const BarChartTooltip = ({ + title, + content, + subtitle, + topRight, + actions, + children, +}: PropsWithChildren) => { + const classes = useStyles(); + + return ( + + + + {title} + {subtitle && ( + + {subtitle} + + )} + + {topRight} + + {content && ( + + + {content} + + + )} + + {children} + + {actions && ( + <> + + + {actions} + + + )} + + ); +}; diff --git a/plugins/cost-insights/src/components/Tooltip/TooltipItem.tsx b/plugins/cost-insights/src/components/BarChart/BarChartTooltipItem.tsx similarity index 76% rename from plugins/cost-insights/src/components/Tooltip/TooltipItem.tsx rename to plugins/cost-insights/src/components/BarChart/BarChartTooltipItem.tsx index a084a0fd1d..153182560f 100644 --- a/plugins/cost-insights/src/components/Tooltip/TooltipItem.tsx +++ b/plugins/cost-insights/src/components/BarChart/BarChartTooltipItem.tsx @@ -19,15 +19,18 @@ import { Box, Typography } from '@material-ui/core'; import LensIcon from '@material-ui/icons/Lens'; import { useTooltipStyles as useStyles } from '../../utils/styles'; -export type TooltipItemProps = { - value: string; - label: string; +export type TooltipItem = { fill: string; + label: string; + value: string; }; -export const TooltipItem = ({ fill, label, value }: TooltipItemProps) => { +export type BarChartTooltipItemProps = { + item: TooltipItem; +}; + +export const BarChartTooltipItem = ({ item }: BarChartTooltipItemProps) => { const classes = useStyles(); - const style = { fill: fill }; return ( { > - + - {label} + {item.label} - {value} + {item.value} ); }; diff --git a/plugins/cost-insights/src/components/BarChart/helpers.tsx b/plugins/cost-insights/src/components/BarChart/helpers.tsx new file mode 100644 index 0000000000..66389af771 --- /dev/null +++ b/plugins/cost-insights/src/components/BarChart/helpers.tsx @@ -0,0 +1,42 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { + ContentRenderer, + TooltipProps as RechartsTooltipProps, +} from 'recharts'; +import { BarChartTooltip } from './BarChartTooltip'; +import { BarChartTooltipItem } from './BarChartTooltipItem'; +import { notEmpty } from '../../utils/assert'; +import { isInvalid, titleOf, tooltipItemOf } from '../../utils/graphs'; + +export const renderDefaultTooltip: ContentRenderer = ({ + label, + payload = [], +}) => { + if (isInvalid({ label, payload })) return null; + + const title = titleOf(label); + const items = payload.map(tooltipItemOf).filter(notEmpty); + return ( + + {items.map((item, index) => ( + + ))} + + ); +}; diff --git a/plugins/cost-insights/src/components/BarChart/index.ts b/plugins/cost-insights/src/components/BarChart/index.ts index 4525bd24f3..76f859636a 100644 --- a/plugins/cost-insights/src/components/BarChart/index.ts +++ b/plugins/cost-insights/src/components/BarChart/index.ts @@ -16,3 +16,12 @@ export { BarChart } from './BarChart'; export type { BarChartProps } from './BarChart'; +export { BarChartLegend } from './BarChartLegend'; +export type { BarChartLegendProps } from './BarChartLegend'; +export { BarChartTooltip } from './BarChartTooltip'; +export type { BarChartTooltipProps } from './BarChartTooltip'; +export { BarChartTooltipItem } from './BarChartTooltipItem'; +export type { + TooltipItem, + BarChartTooltipItemProps, +} from './BarChartTooltipItem'; diff --git a/plugins/cost-insights/src/components/CostGrowth/CostGrowth.tsx b/plugins/cost-insights/src/components/CostGrowth/CostGrowth.tsx index 89e44ee185..01a0874636 100644 --- a/plugins/cost-insights/src/components/CostGrowth/CostGrowth.tsx +++ b/plugins/cost-insights/src/components/CostGrowth/CostGrowth.tsx @@ -49,7 +49,7 @@ export const CostGrowth = ({ change, duration }: CostGrowthProps) => { const converted = amount / (currency.rate ?? rate); // Determine if growth is significant enough to highlight - const growth = growthOf(engineers, change.ratio); + const growth = growthOf(change.ratio, engineers); const classes = classnames({ [styles.excess]: growth === GrowthType.Excess, [styles.savings]: growth === GrowthType.Savings, diff --git a/plugins/cost-insights/src/components/CostGrowth/CostGrowthIndicator.test.tsx b/plugins/cost-insights/src/components/CostGrowth/CostGrowthIndicator.test.tsx new file mode 100644 index 0000000000..5aabb09c7d --- /dev/null +++ b/plugins/cost-insights/src/components/CostGrowth/CostGrowthIndicator.test.tsx @@ -0,0 +1,59 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { renderInTestApp } from '@backstage/test-utils'; +import { CostGrowthIndicator } from './CostGrowthIndicator'; +import { ChangeThreshold, EngineerThreshold } from '../../types'; + +describe.each` + ratio | amount | ariaLabel + ${-0.1} | ${undefined} | ${'savings'} + ${0.01} | ${undefined} | ${'excess'} + ${ChangeThreshold.lower} | ${EngineerThreshold} | ${'savings'} + ${ChangeThreshold.lower - 0.01} | ${EngineerThreshold} | ${'savings'} + ${ChangeThreshold.lower - 0.01} | ${EngineerThreshold + 0.1} | ${'savings'} + ${ChangeThreshold.upper} | ${EngineerThreshold} | ${'excess'} + ${ChangeThreshold.upper + 0.01} | ${EngineerThreshold} | ${'excess'} + ${ChangeThreshold.upper + 0.01} | ${EngineerThreshold + 0.1} | ${'excess'} +`('growthOf', ({ ratio, amount, ariaLabel }) => { + it(`should display the correct indicator for ${ + ariaLabel ? ariaLabel : 'negligible' + }`, async () => { + const { getByLabelText } = await renderInTestApp( + , + ); + expect(getByLabelText(ariaLabel)).toBeInTheDocument(); + }); +}); + +describe.each` + ratio | amount + ${0} | ${undefined} + ${ChangeThreshold.lower} | ${0} + ${ChangeThreshold.lower + 0.01} | ${EngineerThreshold} + ${ChangeThreshold.lower + 0.01} | ${EngineerThreshold + 0.1} + ${ChangeThreshold.lower - 0.01} | ${EngineerThreshold - 0.1} + ${ChangeThreshold.upper + 0.01} | ${EngineerThreshold - 0.1} +`('growthOf', ({ ratio, amount }) => { + it('should display the correct indicator for negligible growth', async () => { + const { queryByLabelText } = await renderInTestApp( + , + ); + expect(queryByLabelText('savings')).not.toBeInTheDocument(); + expect(queryByLabelText('excess')).not.toBeInTheDocument(); + }); +}); diff --git a/plugins/cost-insights/src/components/CostGrowth/CostGrowthIndicator.tsx b/plugins/cost-insights/src/components/CostGrowth/CostGrowthIndicator.tsx new file mode 100644 index 0000000000..e220fdb8e7 --- /dev/null +++ b/plugins/cost-insights/src/components/CostGrowth/CostGrowthIndicator.tsx @@ -0,0 +1,68 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import classnames from 'classnames'; +import { Typography, TypographyProps } from '@material-ui/core'; +import { default as ArrowDropUp } from '@material-ui/icons/ArrowDropUp'; +import { default as ArrowDropDown } from '@material-ui/icons/ArrowDropDown'; +import { growthOf } from '../../utils/change'; +import { GrowthType } from '../../types'; +import { useCostGrowthStyles as useStyles } from '../../utils/styles'; + +export type CostGrowthIndicatorProps = TypographyProps & { + ratio: number; + amount?: number; + formatter?: (amount: number) => string; +}; + +export const CostGrowthIndicator = ({ + ratio, + amount, + formatter, + className, + ...props +}: CostGrowthIndicatorProps) => { + const classes = useStyles(); + const growth = growthOf(ratio, amount); + + const classNames = classnames(classes.indicator, className, { + [classes.savings]: growth === GrowthType.Savings, + [classes.excess]: growth === GrowthType.Excess, + }); + + // Display cost as a factor of engineer cost growth and percentage growth + if (typeof amount === 'number') { + return ( + + {formatter ? formatter(amount) : amount} + {growth === GrowthType.Savings && ( + + )} + {growth === GrowthType.Excess && } + + ); + } + + // Display cost as a factor of percent change + return ( + + {formatter ? formatter(ratio) : ratio} + {ratio < 0 && } + {ratio > 0 && } + + ); +}; diff --git a/plugins/cost-insights/src/components/CostGrowth/index.ts b/plugins/cost-insights/src/components/CostGrowth/index.ts index da17ae8709..569c343380 100644 --- a/plugins/cost-insights/src/components/CostGrowth/index.ts +++ b/plugins/cost-insights/src/components/CostGrowth/index.ts @@ -16,3 +16,5 @@ export { CostGrowth } from './CostGrowth'; export type { CostGrowthProps } from './CostGrowth'; +export { CostGrowthIndicator } from './CostGrowthIndicator'; +export type { CostGrowthIndicatorProps } from './CostGrowthIndicator'; diff --git a/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewChart.tsx b/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewChart.tsx index e442b9a64a..1bd945241a 100644 --- a/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewChart.tsx +++ b/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewChart.tsx @@ -14,32 +14,35 @@ * limitations under the License. */ import React from 'react'; +import moment from 'moment'; import { useTheme } from '@material-ui/core'; import { ComposedChart, + ContentRenderer, + TooltipProps, XAxis, YAxis, - Tooltip, + Tooltip as RechartsTooltip, CartesianGrid, Area, Line, ResponsiveContainer, - TooltipPayload, } from 'recharts'; import { ChartData, Cost, + DEFAULT_DATE_FORMAT, Maybe, Metric, MetricData, CostInsightsTheme, } from '../../types'; +import { BarChartTooltip, BarChartTooltipItem } from '../BarChart'; import { overviewGraphTickFormatter, formatGraphValue, + isInvalid, } from '../../utils/graphs'; -import { CostOverviewTooltip } from './CostOverviewTooltip'; -import { TooltipItemProps } from '../Tooltip'; import { useCostOverviewStyles as useStyles } from '../../utils/styles'; import { groupByDate, toDataMax, trendFrom } from '../../utils/charts'; import { aggregationSort } from '../../utils/sort'; @@ -91,22 +94,39 @@ export const CostOverviewChart = ({ : {}), })); - function tooltipFormatter(payload: TooltipPayload): TooltipItemProps { - return { - label: - payload.dataKey === data.dailyCost.dataKey - ? data.dailyCost.name - : data.metric.name, - value: - payload.dataKey === data.dailyCost.dataKey - ? formatGraphValue(payload.value as number, data.dailyCost.format) - : formatGraphValue(payload.value as number, data.metric.format), - fill: - payload.dataKey === data.dailyCost.dataKey - ? theme.palette.blue - : theme.palette.magenta, - }; - } + const tooltipRenderer: ContentRenderer = ({ + label, + payload = [], + }) => { + if (isInvalid({ label, payload })) return null; + + const dataKeys = [data.dailyCost.dataKey, data.metric.dataKey]; + const title = moment(label).format(DEFAULT_DATE_FORMAT); + const items = payload + .filter(p => dataKeys.includes(p.dataKey as string)) + .map(p => ({ + label: + p.dataKey === data.dailyCost.dataKey + ? data.dailyCost.name + : data.metric.name, + value: + p.dataKey === data.dailyCost.dataKey + ? formatGraphValue(p.value as number, data.dailyCost.format) + : formatGraphValue(p.value as number, data.metric.format), + fill: + p.dataKey === data.dailyCost.dataKey + ? theme.palette.blue + : theme.palette.magenta, + })); + + return ( + + {items.map((item, index) => ( + + ))} + + ); + }; return ( )} - - } - animationDuration={100} - /> + ); diff --git a/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewTooltip.tsx b/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewTooltip.tsx deleted file mode 100644 index e0a9b9a9ea..0000000000 --- a/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewTooltip.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import React from 'react'; -import moment from 'moment'; -import { TooltipPayload, TooltipProps } from 'recharts'; -import { Tooltip, TooltipItemProps } from '../../components/Tooltip'; -import { DEFAULT_DATE_FORMAT } from '../../types'; - -export type CostOverviewTooltipProps = TooltipProps & { - dataKeys: Array; - format: (payload: TooltipPayload) => TooltipItemProps; -}; - -export const CostOverviewTooltip = ({ - label, - payload, - dataKeys, - format, -}: CostOverviewTooltipProps) => { - const tooltipLabel = moment(label).format(DEFAULT_DATE_FORMAT); - const items = payload - ?.filter((p: TooltipPayload) => dataKeys.includes(p.dataKey as string)) - .map(p => format(p)); - return ; -}; diff --git a/plugins/cost-insights/src/components/ProductInsights/ProductInsights.tsx b/plugins/cost-insights/src/components/ProductInsights/ProductInsights.tsx index 16146cb045..c5d39a5139 100644 --- a/plugins/cost-insights/src/components/ProductInsights/ProductInsights.tsx +++ b/plugins/cost-insights/src/components/ProductInsights/ProductInsights.tsx @@ -21,6 +21,7 @@ import { useConfig } from '../../hooks'; export const ProductInsights = ({}) => { const config = useConfig(); + return ( <> diff --git a/plugins/cost-insights/src/components/ProductInsightsCard/ProductEntityDialog.tsx b/plugins/cost-insights/src/components/ProductInsightsCard/ProductEntityDialog.tsx new file mode 100644 index 0000000000..764a18d305 --- /dev/null +++ b/plugins/cost-insights/src/components/ProductInsightsCard/ProductEntityDialog.tsx @@ -0,0 +1,188 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import classnames from 'classnames'; +import { Table, TableColumn } from '@backstage/core'; +import { Dialog, IconButton, Typography } from '@material-ui/core'; +import { default as CloseButton } from '@material-ui/icons/Close'; +import { CostGrowthIndicator } from '../CostGrowth'; +import { currencyFormatter, formatPercent } from '../../utils/formatters'; +import { useEntityDialogStyles as useStyles } from '../../utils/styles'; +import { BarChartOptions, Entity } from '../../types'; + +function createRenderer(col: keyof RowData, classes: Record) { + return function render(rowData: {}): JSX.Element { + const row = rowData as RowData; + const rowStyles = classnames(classes.row, { + [classes.rowTotal]: row.id === 'total', + [classes.colFirst]: col === 'sku', + [classes.colLast]: col === 'ratio', + }); + + switch (col) { + case 'previous': + case 'current': + return ( + + {currencyFormatter.format(row[col])} + + ); + case 'ratio': + return ( + formatPercent(Math.abs(amount))} + /> + ); + default: + return {row.sku}; + } + }; +} + +// material-table does not support fixed rows. Override the sorting algorithm +// to force Total row to bottom by default or when a user sort toggles a column. +function createSorter(field?: keyof Omit) { + return function rowSort(data1: {}, data2: {}): number { + const a = data1 as RowData; + const b = data2 as RowData; + if (a.id === 'total') return 1; + if (b.id === 'total') return 1; + if (field === 'sku') return a.sku.localeCompare(b.sku); + + return field + ? a[field] - b[field] + : b.previous + b.current - (a.previous - a.current); + }; +} + +const defaultEntity: Entity = { + id: null, + aggregation: [0, 0], + change: { ratio: 0, amount: 0 }, + entities: [], +}; + +type RowData = { + id: string; + sku: string; + previous: number; + current: number; + ratio: number; +}; + +type ProductEntityDialogOptions = Partial< + Pick +>; + +type ProductEntityDialogProps = { + open: boolean; + entity?: Entity; + options?: ProductEntityDialogOptions; + onClose: () => void; +}; + +export const ProductEntityDialog = ({ + open, + entity = defaultEntity, + options = {}, + onClose, +}: ProductEntityDialogProps) => { + const classes = useStyles(); + + const data = Object.assign( + { + previousName: 'Previous', + currentName: 'Current', + }, + options, + ); + + const firstColClasses = classnames(classes.column, classes.colFirst); + const lastColClasses = classnames(classes.column, classes.colLast); + + const columns: TableColumn[] = [ + { + field: 'sku', + title: SKU, + render: createRenderer('sku', classes), + customSort: createSorter('sku'), + }, + { + field: 'previous', + title: ( + {data.previousName} + ), + align: 'right', + render: createRenderer('previous', classes), + customSort: createSorter('previous'), + }, + { + field: 'current', + title: ( + {data.currentName} + ), + align: 'right', + render: createRenderer('current', classes), + customSort: createSorter('current'), + }, + { + field: 'ratio', + title: M/M, + align: 'right', + render: createRenderer('ratio', classes), + customSort: createSorter('ratio'), + }, + ]; + + const rowData: RowData[] = entity.entities + .map(e => ({ + id: e.id || 'Unknown', + sku: e.id || 'Unknown', + previous: e.aggregation[0], + current: e.aggregation[1], + ratio: e.change.ratio, + })) + .concat({ + id: 'total', + sku: 'Total', + previous: entity.aggregation[0], + current: entity.aggregation[1], + ratio: entity.change.ratio, + }) + .sort(createSorter()); + + return ( + + + + + + + ); +}; diff --git a/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.test.tsx b/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.test.tsx index 9d1610e298..cdbbb79806 100644 --- a/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.test.tsx +++ b/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.test.tsx @@ -20,7 +20,6 @@ import { ProductInsightsCard } from './ProductInsightsCard'; import { CostInsightsApi } from '../../api'; import { createMockEntity, - createMockProductCost, mockDefaultLoadingState, MockComputeEngine, MockProductFilters, @@ -35,16 +34,14 @@ import { MockScrollProvider, MockLoadingProvider, } from '../../utils/tests'; -import { Duration, Product, ProductCost, ProductPeriod } from '../../types'; +import { Duration, Entity, Product, ProductPeriod } from '../../types'; -const costInsightsApi = ( - productCost: ProductCost, -): Partial => ({ - getProductInsights: () => - Promise.resolve(productCost) as Promise, +const costInsightsApi = (entity: Entity): Partial => ({ + getProductInsights: () => Promise.resolve(entity), }); -const mockProductCost = createMockProductCost(() => ({ +const mockProductCost = createMockEntity(() => ({ + id: 'test-id', entities: [], aggregation: [3000, 4000], change: { @@ -54,12 +51,12 @@ const mockProductCost = createMockProductCost(() => ({ })); const renderProductInsightsCardInTestApp = async ( - productCost: ProductCost, + entity: Entity, product: Product, duration: Duration, ) => await renderInTestApp( - + @@ -96,27 +93,30 @@ describe('', () => { }); it('Should render the right subheader for products with cost data', async () => { - const productCost = { + const entity = { ...mockProductCost, entities: [...Array(1000)].map(createMockEntity), }; const rendered = await renderProductInsightsCardInTestApp( - productCost, + entity, MockComputeEngine, Duration.P1M, ); const subheader = 'entities, sorted by cost'; - const subheaderRgx = new RegExp( - `${productCost.entities.length} ${subheader}`, - ); + const subheaderRgx = new RegExp(`${entity.entities.length} ${subheader}`); expect(rendered.getByText(subheaderRgx)).toBeInTheDocument(); }); it('Should render the right subheader if there is no cost data or change data', async () => { - const productCost = { entities: [], aggregation: [0, 0] } as ProductCost; + const entity: Entity = { + id: 'test-id', + entities: [], + aggregation: [0, 0], + change: { ratio: 0, amount: 0 }, + }; const subheader = `There are no ${MockComputeEngine.name} costs within this timeframe for your team's projects.`; const rendered = await renderProductInsightsCardInTestApp( - productCost, + entity, MockComputeEngine, Duration.P1M, ); @@ -138,12 +138,12 @@ describe('', () => { 'Should display the correct relative time', ({ duration, periodStartText, periodEndText }) => { it(`Should display the correct relative time for ${duration}`, async () => { - const productCost = { + const entity = { ...mockProductCost, entities: [...Array(3)].map(createMockEntity), }; const rendered = await renderProductInsightsCardInTestApp( - productCost, + entity, MockComputeEngine, duration, ); diff --git a/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.tsx b/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.tsx index 840c99522a..da304d42a2 100644 --- a/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.tsx +++ b/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.tsx @@ -16,23 +16,15 @@ import React, { useCallback, useEffect, useState } from 'react'; import { InfoCard, useApi } from '@backstage/core'; -import { Box } from '@material-ui/core'; import Alert from '@material-ui/lab/Alert'; import { costInsightsApiRef } from '../../api'; +import { ProductInsightsChart } from './ProductInsightsChart'; import { PeriodSelect } from '../PeriodSelect'; -import { ResourceGrowthBarChart } from '../ResourceGrowthBarChart'; -import { ResourceGrowthBarChartLegend } from '../ResourceGrowthBarChartLegend'; -import { - useFilters, - useLastCompleteBillingDate, - useLoading, - useScroll, -} from '../../hooks'; +import { useFilters, useLoading, useScroll } from '../../hooks'; import { useProductInsightsCardStyles as useStyles } from '../../utils/styles'; import { mapFiltersToProps, mapLoadingToProps } from './selector'; -import { Duration, Maybe, Product, ProductCost } from '../../types'; +import { Duration, Maybe, Product, Entity } from '../../types'; import { pluralOf } from '../../utils/grammar'; -import { formatPeriod } from '../../utils/formatters'; type ProductInsightsCardProps = { product: Product; @@ -42,8 +34,7 @@ export const ProductInsightsCard = ({ product }: ProductInsightsCardProps) => { const client = useApi(costInsightsApiRef); const classes = useStyles(); const { ScrollAnchor } = useScroll(product.kind); - const lastCompleteBillingDate = useLastCompleteBillingDate(); - const [resource, setResource] = useState>(null); + const [entity, setEntity] = useState>(null); const [error, setError] = useState>(null); const { group, product: productFilter, setProduct, project } = useFilters( @@ -57,38 +48,24 @@ export const ProductInsightsCard = ({ product }: ProductInsightsCardProps) => { // eslint-disable-next-line react-hooks/exhaustive-deps const dispatchLoadingProduct = useCallback(dispatchLoading, [product.kind]); - const amount = resource?.entities?.length || 0; - const hasCostsWithinTimeframe = resource?.change && !!amount; - - const previousName = formatPeriod( - productFilter.duration, - lastCompleteBillingDate, - false, - ); - const currentName = formatPeriod( - productFilter.duration, - lastCompleteBillingDate, - true, - ); + const amount = entity?.entities?.length || 0; + const hasCostsWithinTimeframe = entity?.change && !!amount; const subheader = amount ? `${amount} ${pluralOf(amount, 'entity', 'entities')}, sorted by cost` : `There are no ${product.name} costs within this timeframe for your team's projects.`; - const costStart = resource?.aggregation[0] || 0; - const costEnd = resource?.aggregation[1] || 0; - useEffect(() => { async function load() { if (loadingProduct) { try { - const p: ProductCost = await client.getProductInsights( + const e: Entity = await client.getProductInsights( product.kind, group!, productFilter!.duration, project, ); - setResource(p); + setEntity(e); } catch (e) { setError(e); } finally { @@ -100,7 +77,7 @@ export const ProductInsightsCard = ({ product }: ProductInsightsCardProps) => { }, [ client, product, - setResource, + setEntity, loadingProduct, dispatchLoadingProduct, productFilter, @@ -135,7 +112,7 @@ export const ProductInsightsCard = ({ product }: ProductInsightsCardProps) => { ); } - if (!resource) { + if (!entity) { return null; } @@ -143,25 +120,10 @@ export const ProductInsightsCard = ({ product }: ProductInsightsCardProps) => { {hasCostsWithinTimeframe && ( - <> - - - - - - - + )} ); diff --git a/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsChart.tsx b/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsChart.tsx new file mode 100644 index 0000000000..507be28500 --- /dev/null +++ b/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsChart.tsx @@ -0,0 +1,225 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { useEffect, useState } from 'react'; +import { + ContentRenderer, + TooltipProps as RechartsTooltipProps, + RechartsFunction, +} from 'recharts'; +import { Box, Typography } from '@material-ui/core'; +import { default as FullScreenIcon } from '@material-ui/icons/Fullscreen'; +import { useLastCompleteBillingDate } from '../../hooks'; +import { LegendItem } from '../LegendItem'; +import { ProductEntityDialog } from './ProductEntityDialog'; +import { CostGrowth, CostGrowthIndicator } from '../CostGrowth'; +import { + BarChart, + BarChartLegend, + BarChartTooltip, + BarChartTooltipItem, +} from '../BarChart'; +import { pluralOf } from '../../utils/grammar'; +import { findAlways, notEmpty } from '../../utils/assert'; +import { formatPeriod, formatPercent } from '../../utils/formatters'; +import { + titleOf, + tooltipItemOf, + resourceOf, + isInvalid, + isActiveLabel, +} from '../../utils/graphs'; +import { + useProductInsightsChartStyles as useStyles, + useBarChartLayoutStyles as useLayoutStyles, +} from '../../utils/styles'; +import { BarChartOptions, Duration, Entity, Maybe } from '../../types'; + +export type ProductInsightsChartProps = { + entity: Entity; + duration: Duration; +}; + +export const ProductInsightsChart = ({ + entity, + duration, +}: ProductInsightsChartProps) => { + const classes = useStyles(); + const layoutClasses = useLayoutStyles(); + const lastCompleteBillingDate = useLastCompleteBillingDate(); + const [isOpen, setOpen] = useState(false); + const [isClickable, setClickable] = useState(true); + const [selectLabel, setSelected] = useState>(null); + const [activeLabel, setActive] = useState>(null); + + const legendTitle = `Cost ${entity.change.ratio <= 0 ? 'Savings' : 'Growth'}`; + const costStart = entity.aggregation[0]; + const costEnd = entity.aggregation[1]; + const resources = entity.entities.map(resourceOf); + + const options: Partial = { + previousName: formatPeriod(duration, lastCompleteBillingDate, false), + currentName: formatPeriod(duration, lastCompleteBillingDate, true), + }; + + useEffect(() => { + function toggleModal() { + if (selectLabel) { + setOpen(true); + } else { + setOpen(false); + } + } + + toggleModal(); + }, [selectLabel]); + + useEffect(() => { + // disable click if an entity is unlabeled or if it does not have any skus + function toggleClickableEntity() { + if (activeLabel) { + const hasSkus = entity.entities.find(e => e.id === activeLabel) + ?.entities.length; + if (hasSkus) { + setClickable(true); + } else { + setClickable(false); + } + } else { + setClickable(false); + } + } + + toggleClickableEntity(); + }, [activeLabel, entity]); + + const onMouseMove: RechartsFunction = ( + data: Record<'activeLabel', string | undefined>, + ) => { + if (isActiveLabel(data)) { + setActive(data.activeLabel!); + } else { + setActive(null); + } + }; + + const onClick: RechartsFunction = (data: Record<'activeLabel', string>) => { + if (isActiveLabel(data)) { + setSelected(data.activeLabel); + } else { + setSelected(null); + } + }; + + const renderProductInsightsTooltip: ContentRenderer = ({ + label, + payload = [], + }) => { + /* Labels and payloads may be undefined or empty */ + if (isInvalid({ label, payload })) return null; + + /* + * recharts coerces null values to strings + * entity -> resource -> payload + * { id: null } -> { name: null } -> { label: '' } + */ + const id = label === '' ? null : label; + + const title = titleOf(label); + const items = payload.map(tooltipItemOf).filter(notEmpty); + + const activeEntity = findAlways(entity.entities, e => e.id === id); + const ratio = activeEntity.change.ratio; + const skus = activeEntity.entities; + const subtitle = `${skus.length} ${pluralOf(skus.length, 'SKU', 'SKUs')}`; + + if (skus.length) { + return ( + + } + actions={ + + + Click for breakdown + + } + > + {items.map((item, index) => ( + + ))} + + ); + } + + // If an entity doesn't have any skus, there aren't any costs to break down. + return ( + + } + content={ + id + ? null + : `This service is generating costs that can't be attributed to any known entity.` + } + > + {items.map((item, index) => ( + + ))} + + ); + }; + + const barChartProps = isClickable ? { onClick } : {}; + + return ( + + + + + + + + {selectLabel && entity.entities.length && ( + setSelected(null)} + entity={entity.entities.find(e => e.id === selectLabel)} + options={options} + /> + )} + + ); +}; diff --git a/plugins/cost-insights/src/components/ProductInsightsCard/index.ts b/plugins/cost-insights/src/components/ProductInsightsCard/index.ts index 61222f9466..33a402234c 100644 --- a/plugins/cost-insights/src/components/ProductInsightsCard/index.ts +++ b/plugins/cost-insights/src/components/ProductInsightsCard/index.ts @@ -15,3 +15,4 @@ */ export { ProductInsightsCard } from './ProductInsightsCard'; +export { ProductInsightsChart } from './ProductInsightsChart'; diff --git a/plugins/cost-insights/src/components/ProjectGrowthAlertCard/ProjectGrowthAlertCard.tsx b/plugins/cost-insights/src/components/ProjectGrowthAlertCard/ProjectGrowthAlertCard.tsx index 4311bcc1bf..bcda21e0af 100644 --- a/plugins/cost-insights/src/components/ProjectGrowthAlertCard/ProjectGrowthAlertCard.tsx +++ b/plugins/cost-insights/src/components/ProjectGrowthAlertCard/ProjectGrowthAlertCard.tsx @@ -15,12 +15,9 @@ */ import React from 'react'; -import moment from 'moment'; -import { Box } from '@material-ui/core'; import { InfoCard } from '@backstage/core'; -import { ResourceGrowthBarChart } from '../ResourceGrowthBarChart'; -import { ResourceGrowthBarChartLegend } from '../ResourceGrowthBarChartLegend'; -import { Duration, ProjectGrowthData } from '../../types'; +import { ProjectGrowthAlertChart } from './ProjectGrowthAlertChart'; +import { ProjectGrowthData } from '../../types'; import { pluralOf } from '../../utils/grammar'; type ProjectGrowthAlertProps = { @@ -28,39 +25,17 @@ type ProjectGrowthAlertProps = { }; export const ProjectGrowthAlertCard = ({ alert }: ProjectGrowthAlertProps) => { - const [costStart, costEnd] = alert.aggregation; - const subheader = ` ${alert.products.length} ${pluralOf(alert.products.length, 'product')}${ alert.products.length > 1 ? ', sorted by cost' : '' }`; - const previousName = moment(alert.periodStart, 'YYYY-[Q]Q').format( - '[Q]Q YYYY', - ); - const currentName = moment(alert.periodEnd, 'YYYY-[Q]Q').format('[Q]Q YYYY'); return ( - - - - - - + ); }; diff --git a/plugins/cost-insights/src/components/ProjectGrowthAlertCard/ProjectGrowthAlertChart.tsx b/plugins/cost-insights/src/components/ProjectGrowthAlertCard/ProjectGrowthAlertChart.tsx new file mode 100644 index 0000000000..dffed24992 --- /dev/null +++ b/plugins/cost-insights/src/components/ProjectGrowthAlertCard/ProjectGrowthAlertChart.tsx @@ -0,0 +1,62 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import moment from 'moment'; +import { Box } from '@material-ui/core'; +import { BarChart, BarChartLegend } from '../BarChart'; +import { LegendItem } from '../LegendItem'; +import { CostGrowth } from '../CostGrowth'; +import { renderDefaultTooltip } from '../BarChart/helpers'; +import { BarChartOptions, Duration, ProjectGrowthData } from '../../types'; +import { useBarChartLayoutStyles as useStyles } from '../../utils/styles'; +import { resourceOf } from '../../utils/graphs'; + +type ProjectGrowthAlertChartProps = { + alert: ProjectGrowthData; +}; + +export const ProjectGrowthAlertChart = ({ + alert, +}: ProjectGrowthAlertChartProps) => { + const classes = useStyles(); + + const costStart = alert.aggregation[0]; + const costEnd = alert.aggregation[1]; + const resourceData = alert.products.map(resourceOf); + + const options: Partial = { + previousName: moment(alert.periodStart, 'YYYY-[Q]Q').format('[Q]Q YYYY'), + currentName: moment(alert.periodEnd, 'YYYY-[Q]Q').format('[Q]Q YYYY'), + }; + + return ( + + + + + + + + + ); +}; diff --git a/plugins/cost-insights/src/components/ProjectGrowthInstructionsPage/ProjectGrowthInstructionsPage.tsx b/plugins/cost-insights/src/components/ProjectGrowthInstructionsPage/ProjectGrowthInstructionsPage.tsx index f2083388ae..ab5d7f3163 100644 --- a/plugins/cost-insights/src/components/ProjectGrowthInstructionsPage/ProjectGrowthInstructionsPage.tsx +++ b/plugins/cost-insights/src/components/ProjectGrowthInstructionsPage/ProjectGrowthInstructionsPage.tsx @@ -15,19 +15,23 @@ */ import React from 'react'; +import moment from 'moment'; import { Box, Typography } from '@material-ui/core'; import { InfoCard } from '@backstage/core'; import { AlertInstructionsLayout } from '../AlertInstructionsLayout'; +import { ProductInsightsChart } from '../ProductInsightsCard'; +import { MockBillingDateProvider } from '../../utils/tests'; import { Alert, + DEFAULT_DATE_FORMAT, Duration, Entity, Product, ProjectGrowthData, } from '../../types'; import { ProjectGrowthAlert } from '../../utils/alerts'; -import { ResourceGrowthBarChartLegend } from '../ResourceGrowthBarChartLegend'; -import { ResourceGrowthBarChart } from '../ResourceGrowthBarChart'; + +const today = moment().format(DEFAULT_DATE_FORMAT); export const ProjectGrowthInstructionsPage = () => { const alertData: ProjectGrowthData = { @@ -54,6 +58,7 @@ export const ProjectGrowthInstructionsPage = () => { }, ], }; + const projectGrowthAlert: Alert = new ProjectGrowthAlert(alertData); const product: Product = { @@ -61,20 +66,34 @@ export const ProjectGrowthInstructionsPage = () => { name: 'Compute Engine', }; - const entities: Entity[] = [ - { - id: 'service-one', - aggregation: [18200, 58500], + const entity: Entity = { + id: 'example-id', + aggregation: [20_000, 60_000], + change: { + ratio: 3, + amount: 40_000, }, - { - id: 'service-two', - aggregation: [1200, 1300], - }, - { - id: 'service-three', - aggregation: [600, 200], - }, - ]; + entities: [ + { + id: 'service-one', + aggregation: [18_200, 58_500], + entities: [], + change: { ratio: 2.21, amount: 40_300 }, + }, + { + id: 'service-two', + aggregation: [1200, 1300], + entities: [], + change: { ratio: 0.083, amount: 100 }, + }, + { + id: 'service-three', + aggregation: [600, 200], + entities: [], + change: { ratio: -0.666, amount: -400 }, + }, + ], + }; return ( @@ -153,28 +172,14 @@ export const ProjectGrowthInstructionsPage = () => { ) that has grown in cost: - {/* ProductInsightsCard without API query / PeriodSelect */} - - - - - - - - - - + + + + + From here, you can dig into commit history or deployment logs to find diff --git a/plugins/cost-insights/src/components/ResourceGrowthBarChart/ResourceGrowthBarChart.test.tsx b/plugins/cost-insights/src/components/ResourceGrowthBarChart/ResourceGrowthBarChart.test.tsx deleted file mode 100644 index e3d9d61d1b..0000000000 --- a/plugins/cost-insights/src/components/ResourceGrowthBarChart/ResourceGrowthBarChart.test.tsx +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React from 'react'; -import { ResourceGrowthBarChart } from './ResourceGrowthBarChart'; -import { renderInTestApp } from '@backstage/test-utils'; -import { createMockEntity } from '../../utils/mockData'; - -const MockResources = [...Array(10)].map((_, index) => - createMockEntity(() => ({ - id: `test-id-${index + 1}`, - // grow resource costs linearly for testing - aggregation: [index * 1000, (index + 1) * 1000], - })), -); - -describe('', () => { - it('Pre-renders without exploding', async () => { - const rendered = await renderInTestApp( - , - ); - expect(rendered.queryByTestId('bar-chart-wrapper')).toBeInTheDocument(); - }); -}); diff --git a/plugins/cost-insights/src/components/ResourceGrowthBarChart/ResourceGrowthBarChart.tsx b/plugins/cost-insights/src/components/ResourceGrowthBarChart/ResourceGrowthBarChart.tsx deleted file mode 100644 index b4014bf00f..0000000000 --- a/plugins/cost-insights/src/components/ResourceGrowthBarChart/ResourceGrowthBarChart.tsx +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React from 'react'; -import { TooltipPayload } from 'recharts'; -import { currencyFormatter } from '../../utils/formatters'; -import { - AlertCost, - BarChartData, - CostInsightsTheme, - DataKey, - Entity, - Maybe, - ResourceData, -} from '../../types'; -import { BarChart } from '../BarChart'; -import { TooltipItemProps } from '../Tooltip'; -import { useTheme } from '@material-ui/core'; - -export type ResourceGrowthBarChartProps = { - resources: Array; - previousName: string; - currentName: string; -}; - -export const ResourceGrowthBarChart = ({ - resources, - previousName, - currentName, -}: ResourceGrowthBarChartProps) => { - const theme = useTheme(); - const getTooltipItem = (payload: TooltipPayload): Maybe => { - const value = - typeof payload.value === 'number' - ? currencyFormatter.format(payload.value) - : (payload.value as string); - const fill = payload.fill as string; - - switch (payload.dataKey) { - case DataKey.Current: - case DataKey.Previous: - return { - label: payload.name, - value: value, - fill: fill, - }; - default: - return null; - } - }; - - const barChartData: BarChartData = { - previousFill: theme.palette.lightBlue, - currentFill: theme.palette.darkBlue, - previousName: previousName, - currentName: currentName, - }; - - const resourceData: ResourceData[] = resources.map(resource => { - return { - name: resource.id, - previous: resource.aggregation[0], - current: resource.aggregation[1], - }; - }); - - return ( - - ); -}; diff --git a/plugins/cost-insights/src/components/ResourceGrowthBarChart/index.ts b/plugins/cost-insights/src/components/ResourceGrowthBarChart/index.ts deleted file mode 100644 index ef649a17af..0000000000 --- a/plugins/cost-insights/src/components/ResourceGrowthBarChart/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export { ResourceGrowthBarChart } from './ResourceGrowthBarChart'; diff --git a/plugins/cost-insights/src/components/ResourceGrowthBarChartLegend/ResourceGrowthBarChartLegend.test.tsx b/plugins/cost-insights/src/components/ResourceGrowthBarChartLegend/ResourceGrowthBarChartLegend.test.tsx deleted file mode 100644 index 47285a2363..0000000000 --- a/plugins/cost-insights/src/components/ResourceGrowthBarChartLegend/ResourceGrowthBarChartLegend.test.tsx +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React, { PropsWithChildren } from 'react'; -import { renderInTestApp } from '@backstage/test-utils'; -import { ResourceGrowthBarChartLegend } from './ResourceGrowthBarChartLegend'; -import { defaultCurrencies } from '../../utils/currency'; -import { findAlways } from '../../utils/assert'; -import { MockConfigProvider, MockCurrencyProvider } from '../../utils/tests'; -import { Duration } from '../../types'; - -const engineers = findAlways(defaultCurrencies, c => c.kind === null); - -const MockContext = ({ children }: PropsWithChildren<{}>) => ( - - {children} - -); - -describe('', () => { - describe.each` - ratio | amount | costText | engineerTest - ${2.5} | ${300_000} | ${'Cost Growth'} | ${/\~6 engineers/} - ${-2.5} | ${-120_000} | ${'Cost Savings'} | ${/\~2 engineers/} - `( - 'Should display the cost text', - ({ ratio, amount, costText, engineerTest }) => { - it(`Should display the correct cost and engineer text for ${ratio} percent change`, async () => { - const rendered = await renderInTestApp( - - - , - ); - expect(rendered.getByText(costText)).toBeInTheDocument(); - expect(rendered.queryByText(engineerTest)).toBeInTheDocument(); - }); - }, - ); -}); diff --git a/plugins/cost-insights/src/components/ResourceGrowthBarChartLegend/index.ts b/plugins/cost-insights/src/components/ResourceGrowthBarChartLegend/index.ts deleted file mode 100644 index ee77a483e6..0000000000 --- a/plugins/cost-insights/src/components/ResourceGrowthBarChartLegend/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export { ResourceGrowthBarChartLegend } from './ResourceGrowthBarChartLegend'; diff --git a/plugins/cost-insights/src/components/Tooltip/Tooltip.tsx b/plugins/cost-insights/src/components/Tooltip/Tooltip.tsx deleted file mode 100644 index e0ee93c669..0000000000 --- a/plugins/cost-insights/src/components/Tooltip/Tooltip.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React from 'react'; -import { Box, Typography } from '@material-ui/core'; -import { TooltipItem, TooltipItemProps } from './TooltipItem'; -import { useTooltipStyles } from '../../utils/styles'; - -export type TooltipProps = { - label?: string; - items?: Array; -}; - -export const Tooltip = ({ label, items }: TooltipProps) => { - const classes = useTooltipStyles(); - return ( - - {label && ( - - {label} - - )} - {items && - items.map((item, index) => ( - - ))} - - ); -}; diff --git a/plugins/cost-insights/src/components/Tooltip/index.ts b/plugins/cost-insights/src/components/Tooltip/index.ts deleted file mode 100644 index b26409480e..0000000000 --- a/plugins/cost-insights/src/components/Tooltip/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export { Tooltip } from './Tooltip'; -export type { TooltipProps } from './Tooltip'; -export { TooltipItem } from './TooltipItem'; -export type { TooltipItemProps } from './TooltipItem'; diff --git a/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowAlertCard.tsx b/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowAlertCard.tsx index 8bc5ebfb6e..5384db566c 100644 --- a/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowAlertCard.tsx +++ b/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowAlertCard.tsx @@ -15,12 +15,13 @@ */ import React from 'react'; -import { Box } from '@material-ui/core'; import { InfoCard } from '@backstage/core'; -import { UnlabeledDataflowBarChart } from './UnlabeledDataflowBarChart'; -import { UnlabeledDataflowBarChartLegend } from './UnlabeledDataflowBarChartLegend'; -import { UnlabeledDataflowData } from '../../types'; +import { Box } from '@material-ui/core'; +import { BarChart, BarChartLegend } from '../BarChart'; +import { renderDefaultTooltip } from '../BarChart/helpers'; +import { UnlabeledDataflowData, ResourceData } from '../../types'; import { pluralOf } from '../../utils/grammar'; +import { useBarChartLayoutStyles as useStyles } from '../../utils/styles'; type UnlabeledDataflowAlertProps = { alert: UnlabeledDataflowData; @@ -29,22 +30,35 @@ type UnlabeledDataflowAlertProps = { export const UnlabeledDataflowAlertCard = ({ alert, }: UnlabeledDataflowAlertProps) => { + const classes = useStyles(); const projects = pluralOf(alert.projects.length, 'project'); const subheader = ` Showing costs from ${alert.projects.length} ${projects} with unlabeled Dataflow jobs in the last 30 days. `; + const options = { + previousName: 'Unlabeled Cost', + currentName: 'Labeled Cost', + }; + + const resources: ResourceData[] = alert.projects.map(project => ({ + name: project.id, + previous: project.unlabeledCost, + current: project.labeledCost, + })); + return ( - - - - - - - + + + ); diff --git a/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowBarChart.tsx b/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowBarChart.tsx deleted file mode 100644 index faf27ba04f..0000000000 --- a/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowBarChart.tsx +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React from 'react'; -import { TooltipPayload } from 'recharts'; -import { BarChart } from '../BarChart'; -import { TooltipItemProps } from '../Tooltip'; -import { - BarChartData, - CostInsightsTheme, - ResourceData, - UnlabeledDataflowAlertProject, -} from '../../types'; -import { currencyFormatter } from '../../utils/formatters'; -import { useTheme } from '@material-ui/core'; - -type UnlabeledDataflowBarChartProps = { - projects: Array; -}; - -export const UnlabeledDataflowBarChart = ({ - projects, -}: UnlabeledDataflowBarChartProps) => { - const theme = useTheme(); - const barChartData: BarChartData = { - previousFill: theme.palette.lightBlue, - currentFill: theme.palette.darkBlue, - previousName: 'Unlabeled Cost', - currentName: 'Labeled Cost', - }; - - const getTooltipItem = (payload: TooltipPayload): TooltipItemProps => { - return { - label: payload.name, - value: - typeof payload.value === 'number' - ? currencyFormatter.format(payload.value) - : (payload.value as string), - fill: payload.fill as string, - }; - }; - - const resources: ResourceData[] = projects.map(project => { - return { - name: project.id, - previous: project.unlabeledCost || 0, - current: project.labeledCost || 0, - }; - }); - - return ( - - ); -}; diff --git a/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowBarChartLegend.tsx b/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowBarChartLegend.tsx deleted file mode 100644 index aac37c799d..0000000000 --- a/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowBarChartLegend.tsx +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React from 'react'; -import { Box, useTheme } from '@material-ui/core'; -import { LegendItem } from '../LegendItem'; -import { currencyFormatter } from '../../utils/formatters'; -import { CostInsightsTheme } from '../../types'; - -type UnlabeledDataflowBarChartLegendProps = { - labeledCost: number; - unlabeledCost: number; -}; - -export const UnlabeledDataflowBarChartLegend = ({ - unlabeledCost, - labeledCost, -}: UnlabeledDataflowBarChartLegendProps) => { - const theme = useTheme(); - return ( - - - - {currencyFormatter.format(unlabeledCost)} - - - - - {currencyFormatter.format(labeledCost)} - - - - ); -}; diff --git a/plugins/cost-insights/src/components/index.ts b/plugins/cost-insights/src/components/index.ts index 5b918144d9..c390666419 100644 --- a/plugins/cost-insights/src/components/index.ts +++ b/plugins/cost-insights/src/components/index.ts @@ -17,4 +17,3 @@ export * from './BarChart'; export * from './CostGrowth'; export * from './LegendItem'; -export * from './Tooltip'; diff --git a/plugins/cost-insights/src/types/Alert.ts b/plugins/cost-insights/src/types/Alert.ts index d6046e3241..c5c0463d99 100644 --- a/plugins/cost-insights/src/types/Alert.ts +++ b/plugins/cost-insights/src/types/Alert.ts @@ -41,13 +41,16 @@ export interface ResourceData { name: Maybe; } -export interface BarChartData { +export interface BarChartOptions { previousFill: string; currentFill: string; previousName: string; currentName: string; } +/** deprecated use BarChartOptions instead */ +export interface BarChartData extends BarChartOptions {} + export enum DataKey { Previous = 'previous', Current = 'current', diff --git a/plugins/cost-insights/src/types/Entity.ts b/plugins/cost-insights/src/types/Entity.ts index f5ac620289..83347f05c3 100644 --- a/plugins/cost-insights/src/types/Entity.ts +++ b/plugins/cost-insights/src/types/Entity.ts @@ -20,5 +20,68 @@ import { Maybe } from './Maybe'; export interface Entity { id: Maybe; aggregation: [number, number]; - change?: Maybe; + entities: Entity[]; + change: ChangeStatistic; } + +/* + An entity is a tree-like structure that represents any unique + product or service that generates cost over a fixed period of time. + An entity could be atomic or composite. An atomic entity is indivisible + and cannot be broken into sub-entities. + + A composite entity can be broken down recursively into sub-entities + that generate cost **over the same time period**. All costs must sum to the root cost. + + Entities with null ids are considered "unlabeled" - costs without attribution. + If an entity is a composite, it may only have one (1) null child but may have any number of + null grandchildren. + + { + id: 'product', + aggregation: [0, 200], + change: { + ratio: 2000, + amount: 200 + }, + entities: [ + { + id: 'service-a', + aggregation: [0, 100], + change: { + ratio: 100, + amount: 100 + }, + entities: [] + }, + { + id: 'service-b', + aggregation: [0, 100], + change: { + ratio: 100, + amount: 100 + }, + entities: [ + { + id: 'service-b-sku-a', + aggregation: [0, 25], + change: { + ratio: 25, + amount: 25 + }, + entities: [] + }, + { + id: null, // Unlabeled cost for service-b + aggregation: [0, 75], + change: { + ratio: 75, + amount: 75 + }, + entities: [] + }, + ] + }, + ] + } +*/ diff --git a/plugins/cost-insights/src/types/Product.ts b/plugins/cost-insights/src/types/Product.ts index d2087644cb..77df579a11 100644 --- a/plugins/cost-insights/src/types/Product.ts +++ b/plugins/cost-insights/src/types/Product.ts @@ -14,17 +14,7 @@ * limitations under the License. */ -import { Entity } from './Entity'; -import { ChangeStatistic } from './ChangeStatistic'; -import { Maybe } from './Maybe'; - export interface Product { kind: string; name: string; } - -export interface ProductCost { - entities?: Array; - aggregation: [number, number]; - change?: Maybe; -} diff --git a/plugins/cost-insights/src/types/Theme.ts b/plugins/cost-insights/src/types/Theme.ts index 9053283351..fecaa17abb 100644 --- a/plugins/cost-insights/src/types/Theme.ts +++ b/plugins/cost-insights/src/types/Theme.ts @@ -30,6 +30,7 @@ type CostInsightsPaletteAdditions = { tooltip: CostInsightsTooltipOptions; navigationText: string; alertBackground: string; + textSecondary: string; }; export type CostInsightsPalette = BackstagePalette & diff --git a/plugins/cost-insights/src/utils/change.test.ts b/plugins/cost-insights/src/utils/change.test.ts new file mode 100644 index 0000000000..daf4538f2c --- /dev/null +++ b/plugins/cost-insights/src/utils/change.test.ts @@ -0,0 +1,62 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { growthOf } from './change'; +import { GrowthType, ChangeThreshold, EngineerThreshold } from '../types'; + +const GrowthMap = { + [GrowthType.Negligible]: 'negligible growth', + [GrowthType.Savings]: 'cost savings', + [GrowthType.Excess]: 'cost excess', +}; + +describe.each` + ratio | amount | expected + ${0.0} | ${undefined} | ${GrowthType.Negligible} + ${0.0} | ${EngineerThreshold} | ${GrowthType.Negligible} + ${ChangeThreshold.lower} | ${0} | ${GrowthType.Negligible} + ${ChangeThreshold.lower + 0.01} | ${undefined} | ${GrowthType.Negligible} + ${ChangeThreshold.lower + 0.01} | ${EngineerThreshold} | ${GrowthType.Negligible} + ${ChangeThreshold.lower + 0.01} | ${EngineerThreshold + 0.1} | ${GrowthType.Negligible} + ${ChangeThreshold.lower - 0.01} | ${EngineerThreshold - 0.1} | ${GrowthType.Negligible} + ${ChangeThreshold.upper - 0.01} | ${undefined} | ${GrowthType.Negligible} + ${ChangeThreshold.upper + 0.01} | ${EngineerThreshold - 0.1} | ${GrowthType.Negligible} + ${ChangeThreshold.lower} | ${undefined} | ${GrowthType.Savings} + ${ChangeThreshold.lower} | ${EngineerThreshold} | ${GrowthType.Savings} + ${ChangeThreshold.lower - 0.01} | ${undefined} | ${GrowthType.Savings} + ${ChangeThreshold.lower - 0.01} | ${EngineerThreshold} | ${GrowthType.Savings} + ${ChangeThreshold.lower - 0.01} | ${EngineerThreshold + 0.1} | ${GrowthType.Savings} + ${ChangeThreshold.upper} | ${undefined} | ${GrowthType.Excess} + ${ChangeThreshold.upper} | ${EngineerThreshold} | ${GrowthType.Excess} + ${ChangeThreshold.upper + 0.01} | ${undefined} | ${GrowthType.Excess} + ${ChangeThreshold.upper + 0.01} | ${EngineerThreshold} | ${GrowthType.Excess} + ${ChangeThreshold.upper + 0.01} | ${EngineerThreshold + 0.1} | ${GrowthType.Excess} +`( + 'growthOf', + ({ + ratio, + amount, + expected, + }: { + ratio: number; + amount: number; + expected: GrowthType; + }) => { + it(`should display ${GrowthMap[expected]}`, () => { + expect(growthOf(ratio, amount)).toBe(expected); + }); + }, +); diff --git a/plugins/cost-insights/src/utils/change.ts b/plugins/cost-insights/src/utils/change.ts index 1fe3662537..cc797bb3b0 100644 --- a/plugins/cost-insights/src/utils/change.ts +++ b/plugins/cost-insights/src/utils/change.ts @@ -24,14 +24,18 @@ import { } from '../types'; import { aggregationSort } from '../utils/sort'; -// Used by for displaying status colors -export function growthOf(amount: number, ratio: number) { - if (amount >= EngineerThreshold && ratio >= ChangeThreshold.upper) { - return GrowthType.Excess; - } - - if (amount >= EngineerThreshold && ratio <= ChangeThreshold.lower) { - return GrowthType.Savings; +// Used for displaying status colors +export function growthOf(ratio: number, amount?: number) { + if (typeof amount === 'number') { + if (amount >= EngineerThreshold && ratio >= ChangeThreshold.upper) { + return GrowthType.Excess; + } + if (amount >= EngineerThreshold && ratio <= ChangeThreshold.lower) { + return GrowthType.Savings; + } + } else { + if (ratio >= ChangeThreshold.upper) return GrowthType.Excess; + if (ratio <= ChangeThreshold.lower) return GrowthType.Savings; } return GrowthType.Negligible; diff --git a/plugins/cost-insights/src/utils/graphs.ts b/plugins/cost-insights/src/utils/graphs.ts new file mode 100644 index 0000000000..ac06e20587 --- /dev/null +++ b/plugins/cost-insights/src/utils/graphs.ts @@ -0,0 +1,79 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TooltipPayload, TooltipProps } from 'recharts'; +import { AlertCost, DataKey, Entity, ResourceData } from '../types'; +import { + currencyFormatter, + dateFormatter, + lengthyCurrencyFormatter, +} from './formatters'; + +export function formatGraphValue(value: number, format?: string) { + if (format === 'number') { + return value.toLocaleString(); + } + + if (value < 1) { + return lengthyCurrencyFormatter.format(value); + } + + return currencyFormatter.format(value); +} + +export const overviewGraphTickFormatter = (millis: string | number) => + typeof millis === 'number' ? dateFormatter.format(millis) : millis; + +export const tooltipItemOf = (payload: TooltipPayload) => { + const value = + typeof payload.value === 'number' + ? currencyFormatter.format(payload.value) + : (payload.value as string); + const fill = payload.fill as string; + + switch (payload.dataKey) { + case DataKey.Current: + case DataKey.Previous: + return { + label: payload.name, + value: value, + fill: fill, + }; + default: + return null; + } +}; + +export const resourceOf = (entity: Entity | AlertCost): ResourceData => ({ + name: entity.id, + previous: entity.aggregation[0], + current: entity.aggregation[1], +}); + +export const titleOf = (label?: string | number) => { + return label ? String(label) : 'Unlabeled'; +}; + +export const isInvalid = ({ label, payload }: TooltipProps) => { + // null labels are empty strings, which are valid + return label === undefined || !payload || !payload.length; +}; + +export const isActiveLabel = ( + data?: Record<'activeLabel', string | undefined>, +) => { + return data?.activeLabel && data.activeLabel !== ''; +}; diff --git a/plugins/cost-insights/src/utils/graphs.tsx b/plugins/cost-insights/src/utils/graphs.tsx deleted file mode 100644 index 167132cb36..0000000000 --- a/plugins/cost-insights/src/utils/graphs.tsx +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - currencyFormatter, - dateFormatter, - lengthyCurrencyFormatter, -} from './formatters'; - -export function formatGraphValue(value: number, format?: string) { - if (format === 'number') { - return value.toLocaleString(); - } - - if (value < 1) { - return lengthyCurrencyFormatter.format(value); - } - - return currencyFormatter.format(value); -} - -export const overviewGraphTickFormatter = (millis: string | number) => - typeof millis === 'number' ? dateFormatter.format(millis) : millis; diff --git a/plugins/cost-insights/src/utils/mockData.ts b/plugins/cost-insights/src/utils/mockData.ts index 58ba4a4251..ddeb4636b9 100644 --- a/plugins/cost-insights/src/utils/mockData.ts +++ b/plugins/cost-insights/src/utils/mockData.ts @@ -20,7 +20,6 @@ import { Duration, Entity, Product, - ProductCost, ProductFilters, ProjectGrowthData, UnlabeledDataflowAlertProject, @@ -41,6 +40,11 @@ export const createMockEntity = ( const defaultEntity: Entity = { id: 'test-entity', aggregation: [100, 200], + entities: [], + change: { + ratio: 0, + amount: 0, + }, }; if (typeof callback === 'function') { @@ -62,19 +66,6 @@ export const createMockProduct = ( return { ...defaultProduct }; }; -export const createMockProductCost = ( - callback?: mockEntityRenderer, -): ProductCost => { - const defaultProduct: ProductCost = { - entities: [], - aggregation: [0, 0], - }; - if (typeof callback === 'function') { - return callback({ ...defaultProduct }); - } - return { ...defaultProduct }; -}; - export const createMockProjectGrowthData = ( callback?: mockAlertRenderer, ): ProjectGrowthData => { diff --git a/plugins/cost-insights/src/utils/sort.ts b/plugins/cost-insights/src/utils/sort.ts index 944f5fc806..f4fc1987d1 100644 --- a/plugins/cost-insights/src/utils/sort.ts +++ b/plugins/cost-insights/src/utils/sort.ts @@ -19,5 +19,6 @@ export const aggregationSort = ( a: DateAggregation, b: DateAggregation, ): number => a.date.localeCompare(b.date); + export const resourceSort = (a: ResourceData, b: ResourceData) => b.previous + b.current - (a.previous + a.current); diff --git a/plugins/cost-insights/src/utils/styles.ts b/plugins/cost-insights/src/utils/styles.ts index b20c601276..4f8ac7fa98 100644 --- a/plugins/cost-insights/src/utils/styles.ts +++ b/plugins/cost-insights/src/utils/styles.ts @@ -116,6 +116,18 @@ export const useBarChartStyles = (theme: CostInsightsTheme) => ({ }, }); +export const useBarChartLayoutStyles = makeStyles(theme => + createStyles({ + wrapper: { + display: 'flex', + flexDirection: 'column', + }, + legend: { + paddingBottom: theme.spacing(2), + }, + }), +); + export const useBarChartStepperButtonStyles = makeStyles( (theme: CostInsightsTheme) => createStyles({ @@ -146,12 +158,13 @@ export const useBarChartStepperButtonStyles = makeStyles( }), ); -export const useBarChartLabelStyles = makeStyles(() => +export const useBarChartLabelStyles = makeStyles(theme => createStyles({ foreignObject: { textAlign: 'center', }, label: { + fontWeight: theme.typography.fontWeightBold, display: 'block', textDecoration: 'none', overflow: 'hidden', @@ -162,6 +175,11 @@ export const useBarChartLabelStyles = makeStyles(() => marginLeft: 2, fontSize: '1.25em', }, + button: { + textTransform: 'none', + fontWeight: theme.typography.fontWeightBold, + fontSize: theme.typography.fontSize, + }, }), ); @@ -244,6 +262,12 @@ export const useCostGrowthStyles = makeStyles( savings: { color: theme.palette.status.ok, }, + indicator: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'flex-end', + }, }), ); @@ -356,16 +380,33 @@ export const useTooltipStyles = makeStyles( (theme: CostInsightsTheme) => createStyles({ tooltip: { - padding: theme.spacing(1.5), backgroundColor: theme.palette.tooltip.background, borderRadius: theme.shape.borderRadius, boxShadow: theme.shadows[1], color: theme.palette.tooltip.color, fontSize: theme.typography.fontSize, + width: 250, + }, + actions: { + padding: theme.spacing(2), + }, + header: { + padding: theme.spacing(2), + }, + content: { + padding: theme.spacing(2), }, lensIcon: { fontSize: `.75rem`, }, + divider: { + /* TODO: tooltip divider color should be the inverse of theme. + Dark mode should appear dark, light mode should appear light */ + backgroundColor: darken(theme.palette.divider, 1), + }, + subtitle: { + fontStyle: 'italic', + }, }), ); @@ -446,6 +487,21 @@ export const useProductInsightsCardStyles = makeStyles( }), ); +export const useProductInsightsChartStyles = makeStyles( + (theme: BackstageTheme) => + createStyles({ + indicator: { + fontWeight: theme.typography.fontWeightBold, + fontSize: '1.25rem', + }, + actions: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + }, + }), +); + export const useBackdropStyles = makeStyles( (theme: BackstageTheme) => createStyles({ @@ -463,3 +519,42 @@ export const useSubtleTypographyStyles = makeStyles( }, }), ); + +export const useEntityDialogStyles = makeStyles(theme => + createStyles({ + dialogContent: { + padding: 0, + }, + dialogTitle: { + padding: 0, + }, + closeButton: { + position: 'absolute', + right: theme.spacing(1), + top: theme.spacing(1), + color: theme.palette.grey[500], + zIndex: 100, + }, + row: { + fontSize: theme.typography.fontSize * 1.25, + }, + rowTotal: { + fontWeight: theme.typography.fontWeightBold, + }, + colFirst: { + paddingLeft: theme.spacing(2), + }, + colLast: { + paddingRight: theme.spacing(2), + }, + column: { + fontWeight: theme.typography.fontWeightBold, + }, + growth: { + display: 'flex', + flexDirection: 'row', + alignContent: 'center', + justifyContent: 'flex-end', + }, + }), +); From 9e336a57a585b49cf3028fa366fb3b359e13ef7a Mon Sep 17 00:00:00 2001 From: Mateusz Lewtak <36006588+lewtakm@users.noreply.github.com> Date: Mon, 9 Nov 2020 15:48:56 +0100 Subject: [PATCH 119/252] Feat: update GitHub Pull Request plugin version (#3265) --- packages/app/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index 5cf74b0fbb..107b8c1cda 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -35,7 +35,7 @@ "@material-ui/icons": "^4.9.1", "@octokit/rest": "^18.0.0", "@roadiehq/backstage-plugin-github-insights": "^0.2.12", - "@roadiehq/backstage-plugin-github-pull-requests": "^0.6.1", + "@roadiehq/backstage-plugin-github-pull-requests": "^0.6.2", "@roadiehq/backstage-plugin-travis-ci": "^0.2.7", "history": "^5.0.0", "prop-types": "^15.7.2", diff --git a/yarn.lock b/yarn.lock index c265aecd6a..83b96cebdb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3672,10 +3672,10 @@ react-use "^15.3.3" remark-gfm "^1.0.0" -"@roadiehq/backstage-plugin-github-pull-requests@^0.6.1": - version "0.6.1" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-pull-requests/-/backstage-plugin-github-pull-requests-0.6.1.tgz#bce3ce93af7b2f6c38d4665c8a0d8a6b2eaa4212" - integrity sha512-3r0BBV6kRlbrlfVA/G7Tc0xbw9A+9roihspCaV/yhhqXgk/ik5v6ftEautlx45+gKaFMp+jgmfKHqFgOdSyhZw== +"@roadiehq/backstage-plugin-github-pull-requests@^0.6.2": + version "0.6.2" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-pull-requests/-/backstage-plugin-github-pull-requests-0.6.2.tgz#02f4a7a03e1a7dc24342ec695130017ce7d3ad8d" + integrity sha512-OdBWO6NdiBKlol1WlbFQGeHxV2C8wl8EiI2NgA15lozsqFA22kWis6Z16cAHe4ZdRCjihDmqINtAIJzeZZ1gOg== dependencies: "@backstage/catalog-model" "^0.2.0" "@backstage/core" "^0.2.0" From f12b26274c875659fd7ffc57226c1cebcfc05918 Mon Sep 17 00:00:00 2001 From: Ryan Vazquez Date: Mon, 9 Nov 2020 10:08:24 -0500 Subject: [PATCH 120/252] remove unused import --- .../src/components/ProductInsightsCard/ProductInsightsChart.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsChart.tsx b/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsChart.tsx index 489c852f26..1392b05b34 100644 --- a/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsChart.tsx +++ b/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsChart.tsx @@ -22,7 +22,6 @@ import { } from 'recharts'; import { Box, Typography } from '@material-ui/core'; import { default as FullScreenIcon } from '@material-ui/icons/Fullscreen'; -import { useLastCompleteBillingDate } from '../../hooks'; import { LegendItem } from '../LegendItem'; import { ProductEntityDialog } from './ProductEntityDialog'; import { CostGrowth, CostGrowthIndicator } from '../CostGrowth'; From f059eaf7550e642c8ca9e5ab1d7e1a5617ec9608 Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Mon, 9 Nov 2020 16:28:51 +0100 Subject: [PATCH 121/252] readTree method on UrlReader (#2925) * Basic implementation of readTree on github and use it in techdocs * Added a UrlPreparer and cleaned up some minor things * Download entire archive and filter out the wanted files in readTree * Added test for dir output of tree reader * Fixed formatting * Fixed formatting * Some cleanups and fixes * Fix typing issues * Fix prettier * Fix prettier * Removed unused dependency * Fixed comments on PR * Prettier * Moved @types/fs-extra to devDeps * Fixed another PR comment * Added a test for getDocFilesFromRepository * Prettier --- packages/backend-common/package.json | 10 ++ .../src/reading/GithubUrlReader.test.ts | 80 ++++++++++++ .../src/reading/GithubUrlReader.ts | 93 +++++++++++++- .../src/reading/UrlReaderPredicateMux.ts | 29 ++++- .../src/reading/__fixtures__/repo.tar.gz | Bin 0 -> 232 bytes packages/backend-common/src/reading/index.ts | 2 +- packages/backend-common/src/reading/types.ts | 16 +++ packages/backend/src/plugins/techdocs.ts | 8 +- plugins/techdocs-backend/src/helpers.test.ts | 117 ++++++++++++++++++ plugins/techdocs-backend/src/helpers.ts | 34 ++++- .../techdocs-backend/src/service/helpers.ts | 2 +- .../src/techdocs/stages/prepare/index.ts | 1 + .../src/techdocs/stages/prepare/types.ts | 8 +- .../src/techdocs/stages/prepare/url.ts | 42 +++++++ yarn.lock | 16 ++- 15 files changed, 450 insertions(+), 8 deletions(-) create mode 100644 packages/backend-common/src/reading/__fixtures__/repo.tar.gz create mode 100644 plugins/techdocs-backend/src/helpers.test.ts create mode 100644 plugins/techdocs-backend/src/techdocs/stages/prepare/url.ts diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index c004eaac28..f76cb1757a 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -36,11 +36,13 @@ "@types/cors": "^2.8.6", "@types/express": "^4.17.6", "compression": "^1.7.4", + "concat-stream": "^2.0.0", "cors": "^2.8.5", "cross-fetch": "^3.0.6", "express": "^4.17.1", "express-prom-bundle": "^6.1.0", "express-promise-router": "^3.0.3", + "fs-extra": "^9.0.1", "git-url-parse": "^11.4.0", "helmet": "^4.0.0", "knex": "^0.21.6", @@ -51,6 +53,7 @@ "prom-client": "^12.0.0", "selfsigned": "^1.10.7", "stoppable": "^1.1.0", + "tar": "^6.0.5", "winston": "^3.2.1" }, "peerDependencies": { @@ -64,17 +67,24 @@ "devDependencies": { "@backstage/cli": "^0.2.0", "@types/compression": "^1.7.0", + "@types/concat-stream": "^1.6.0", + "@types/fs-extra": "^9.0.3", "@types/http-errors": "^1.6.3", "@types/minimist": "^1.2.0", + "@types/mock-fs": "^4.13.0", "@types/morgan": "^1.9.0", + "@types/recursive-readdir": "^2.2.0", "@types/stoppable": "^1.1.0", "@types/supertest": "^2.0.8", + "@types/tar": "^4.0.3", "@types/webpack-env": "^1.15.2", "@types/yaml": "^1.9.7", "get-port": "^5.1.1", "http-errors": "^1.7.3", "jest": "^26.0.1", + "mock-fs": "^4.13.0", "msw": "^0.21.2", + "recursive-readdir": "^2.2.2", "supertest": "^4.0.2" }, "files": [ diff --git a/packages/backend-common/src/reading/GithubUrlReader.test.ts b/packages/backend-common/src/reading/GithubUrlReader.test.ts index 8df464db9f..1e7326af2b 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.test.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.test.ts @@ -15,6 +15,9 @@ */ import { ConfigReader } from '@backstage/config'; +import { setupServer } from 'msw/node'; +import { msw } from '@backstage/test-utils'; +import { rest } from 'msw'; import { getApiRequestOptions, getApiUrl, @@ -24,6 +27,10 @@ import { ProviderConfig, readConfig, } from './GithubUrlReader'; +import fs from 'fs'; +import path from 'path'; +import mockfs from 'mock-fs'; +import recursive from 'recursive-readdir'; describe('GithubUrlReader', () => { describe('getApiRequestOptions', () => { @@ -230,4 +237,77 @@ describe('GithubUrlReader', () => { ); }); }); + + describe('readTree', () => { + const worker = setupServer(); + + msw.setupDefaultHandlers(worker); + + const repoBuffer = fs.readFileSync( + path.resolve('src', 'reading', '__fixtures__', 'repo.tar.gz'), + ); + + beforeEach(() => { + worker.use( + rest.get( + 'https://github.com/spotify/mock/archive/repo.tar.gz', + (_, res, ctx) => + res( + ctx.status(200), + ctx.set('Content-Type', 'application/x-gzip'), + ctx.body(repoBuffer), + ), + ), + ); + }); + + it('returns the wanted files from an archive', async () => { + const processor = new GithubUrlReader({ + host: 'github.com', + }); + + const response = await processor.readTree( + 'https://github.com/spotify/mock', + 'repo', + ['mkdocs.yml', 'docs'], + ); + + const files = response.files(); + + const mkDocsFile = await files[0].content(); + const indexMarkdownFile = await files[1].content(); + + expect(mkDocsFile.toString()).toBe('site_name: Test\n'); + expect(indexMarkdownFile.toString()).toBe('# Test\n'); + }); + + it('returns a folder path from an archive', async () => { + const processor = new GithubUrlReader({ + host: 'github.com', + }); + + const response = await processor.readTree( + 'https://github.com/spotify/mock', + 'repo', + ['mkdocs.yml', 'docs'], + ); + + mockfs(); + const directory = await response.dir('/tmp/fs'); + + const writtenToDirectory = fs.existsSync(directory); + const paths = await recursive(directory); + mockfs.restore(); + + expect(writtenToDirectory).toBe(true); + expect(paths.sort()).toEqual( + [ + '/tmp/fs/mock-repo/docs/index.md', + '/tmp/fs/mock-repo/mkdocs.yml', + ].sort(), + ); + + worker.resetHandlers(); + }); + }); }); diff --git a/packages/backend-common/src/reading/GithubUrlReader.ts b/packages/backend-common/src/reading/GithubUrlReader.ts index d12344ec91..906076a3c2 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.ts @@ -18,7 +18,12 @@ import { Config } from '@backstage/config'; import parseGitUri from 'git-url-parse'; import fetch from 'cross-fetch'; import { NotFoundError } from '../errors'; -import { ReaderFactory, UrlReader } from './types'; +import { ReaderFactory, ReadTreeResponse, UrlReader, File } from './types'; +import tar from 'tar'; +import fs from 'fs-extra'; +import concatStream from 'concat-stream'; +import path from 'path'; +import os from 'os'; /** * The configuration parameters for a single GitHub API provider. @@ -229,6 +234,92 @@ export class GithubUrlReader implements UrlReader { throw new Error(message); } + private async getRepositoryArchive( + repoUrl: string, + branchName: string, + ): Promise { + return fetch(new URL(`${repoUrl}/archive/${branchName}.tar.gz`).toString()); + } + + private async writeBufferToFile( + filePath: string, + content: Buffer, + ): Promise { + await fs.outputFile(filePath, content.toString()); + } + + async readTree( + repoUrl: string, + branchName: string, + paths: Array, + ): Promise { + const { name: repoName } = parseGitUri(repoUrl); + + const repoArchive = await this.getRepositoryArchive(repoUrl, branchName); + + const files: File[] = []; + return new Promise(resolve => { + const parser = new (tar.Parse as any)({ + filter: (path: string) => + !!paths.filter(file => { + return path.startsWith(`${repoName}-${branchName}/${file}`); + }).length, + onentry: (entry: tar.ReadEntry) => { + if (entry.type === 'Directory') { + entry.resume(); + return; + } + + const contentPromise: Promise = new Promise(res => { + entry.pipe(concatStream(res)); + }); + + files.push({ + path: entry.path, + content: () => contentPromise, + }); + + entry.resume(); + }, + }); + + // @ts-ignore Typescript doesn't consider .pipe a method on ReadableStream. Don't know why. + repoArchive.body?.pipe(parser).on('finish', () => { + resolve({ + files: () => { + return files; + }, + archive: () => { + return new Promise(resolve => + resolve(Buffer.from('Archive is not yet implemented')), + ); + }, + dir: (outDir: string | undefined) => { + const targetDirectory = + outDir || fs.mkdtempSync(path.join(os.tmpdir(), 'backstage-')); + + return new Promise((res, rej) => { + Promise.all( + files.map(async file => { + return this.writeBufferToFile( + `${targetDirectory}/${file.path}`, + await file.content(), + ); + }), + ) + .then(() => { + res(`${targetDirectory}/${repoName}-${branchName}`); + }) + .catch(err => { + rej(err); + }); + }); + }, + }); + }); + }); + } + toString() { const { host, token } = this.config; return `github{host=${host},authed=${Boolean(token)}}`; diff --git a/packages/backend-common/src/reading/UrlReaderPredicateMux.ts b/packages/backend-common/src/reading/UrlReaderPredicateMux.ts index 7654cc8aac..74f94f0295 100644 --- a/packages/backend-common/src/reading/UrlReaderPredicateMux.ts +++ b/packages/backend-common/src/reading/UrlReaderPredicateMux.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { UrlReader, UrlReaderPredicateTuple } from './types'; +import { ReadTreeResponse, UrlReader, UrlReaderPredicateTuple } from './types'; type Options = { // UrlReader to fall back to if no other reader is matched @@ -53,6 +53,33 @@ export class UrlReaderPredicateMux implements UrlReader { throw new Error(`No reader found that could handle '${url}'`); } + readTree( + repoUrl: string, + branchName: string, + paths: Array, + ): Promise { + const parsed = new URL(repoUrl); + + for (const { predicate, reader } of this.readers) { + if (predicate(parsed)) { + if (reader.readTree) return reader.readTree(repoUrl, branchName, paths); + throw new Error( + `Trying to call readTree on UrlReader which does not support the feature.`, + ); + } + } + + if (this.fallback) { + if (this.fallback.readTree) + return this.fallback.readTree(repoUrl, branchName, paths); + throw new Error( + `Trying to call readTree on UrlReader which does not support the feature.`, + ); + } + + throw new Error(`No reader found that could handle '${repoUrl}'`); + } + toString() { return `predicateMux{readers=${this.readers .map(t => t.reader) diff --git a/packages/backend-common/src/reading/__fixtures__/repo.tar.gz b/packages/backend-common/src/reading/__fixtures__/repo.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..7a8e9902a24232a5f7130637a05304134abc3fec GIT binary patch literal 232 zcmb2|=3oE==C@Zbay1!XSu&B+beL(LRxe2^Idk!eV446aOJ+o+S-o>~??20b^`}#YbLB3%Cll?@@AuK( zf9m={{U?{EUwv`;^MMVwzTe;Tuda?M|Lgvh`?I~>7XNQAm|j0i{htM|=koGBZ|a}E f`_yk@`Q^XBu@$utzd*@``2}nqTB%DJG#D5FAn; + readTree?( + repoUrl: string, + branchName: string, + paths: Array, + ): Promise; }; export type UrlReaderPredicateTuple = { @@ -37,3 +42,14 @@ export type ReaderFactory = (options: { config: Config; logger: Logger; }) => UrlReaderPredicateTuple[]; + +export type File = { + path: string; + content(): Promise; +}; + +export type ReadTreeResponse = { + files(): File[]; + archive(): Promise; + dir(outDir?: string): Promise; +}; diff --git a/packages/backend/src/plugins/techdocs.ts b/packages/backend/src/plugins/techdocs.ts index dea31c763b..a9d6293c33 100644 --- a/packages/backend/src/plugins/techdocs.ts +++ b/packages/backend/src/plugins/techdocs.ts @@ -22,6 +22,7 @@ import { LocalPublish, TechdocsGenerator, CommonGitPreparer, + UrlPreparer, } from '@backstage/plugin-techdocs-backend'; import { PluginEnvironment } from '../types'; import Docker from 'dockerode'; @@ -30,20 +31,25 @@ export default async function createPlugin({ logger, config, discovery, + reader, }: PluginEnvironment) { const generators = new Generators(); const techdocsGenerator = new TechdocsGenerator(logger, config); generators.register('techdocs', techdocsGenerator); const preparers = new Preparers(); - const commonGitPreparer = new CommonGitPreparer(logger); const directoryPreparer = new DirectoryPreparer(logger); preparers.register('dir', directoryPreparer); + + const commonGitPreparer = new CommonGitPreparer(logger); preparers.register('github', commonGitPreparer); preparers.register('gitlab', commonGitPreparer); preparers.register('azure/api', commonGitPreparer); + const urlPreparer = new UrlPreparer(reader, logger); + preparers.register('url', urlPreparer); + const publisher = new LocalPublish(logger); const dockerClient = new Docker(); diff --git a/plugins/techdocs-backend/src/helpers.test.ts b/plugins/techdocs-backend/src/helpers.test.ts new file mode 100644 index 0000000000..8d4d69ba26 --- /dev/null +++ b/plugins/techdocs-backend/src/helpers.test.ts @@ -0,0 +1,117 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getDocFilesFromRepository } from './helpers'; +import { UrlReader, ReadTreeResponse } from '@backstage/backend-common'; +import { Entity } from '@backstage/catalog-model'; + +describe('getDocFilesFromRepository', () => { + it('should take the directory from UrlReader.readTree and add the docs path when mkdocs.yml is in root', async () => { + class MockUrlReader implements UrlReader { + async read() { + return Buffer.from('mock'); + } + + async readTree(): Promise { + return { + dir: async () => { + return '/tmp/testfolder'; + }, + files: () => { + return []; + }, + archive: async () => { + return Buffer.from(''); + }, + }; + } + } + + const mockEntity: Entity = { + metadata: { + namespace: 'default', + annotations: { + 'backstage.io/techdocs-ref': + 'url:https://github.com/backstage/backstage/blob/master/mkdocs.yml', + }, + name: 'mytestcomponent', + description: 'A component for testing', + }, + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + spec: { + type: 'documentation', + lifecycle: 'experimental', + owner: 'testuser', + }, + }; + + const output = await getDocFilesFromRepository( + new MockUrlReader(), + mockEntity, + ); + + expect(output).toBe('/tmp/testfolder/.'); + }); + + it('should take the directory from UrlReader.readTree and add the docs path when mkdocs.yml is in a subfolder', async () => { + class MockUrlReader implements UrlReader { + async read() { + return Buffer.from('mock'); + } + + async readTree(): Promise { + return { + dir: async () => { + return '/tmp/testfolder'; + }, + files: () => { + return []; + }, + archive: async () => { + return Buffer.from(''); + }, + }; + } + } + + const mockEntity: Entity = { + metadata: { + namespace: 'default', + annotations: { + 'backstage.io/techdocs-ref': + 'url:https://github.com/backstage/backstage/blob/master/subfolder/mkdocs.yml', + }, + name: 'mytestcomponent', + description: 'A component for testing', + }, + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + spec: { + type: 'documentation', + lifecycle: 'experimental', + owner: 'testuser', + }, + }; + + const output = await getDocFilesFromRepository( + new MockUrlReader(), + mockEntity, + ); + + expect(output).toBe('/tmp/testfolder/subfolder'); + }); +}); diff --git a/plugins/techdocs-backend/src/helpers.ts b/plugins/techdocs-backend/src/helpers.ts index 4cbf46516f..f861d4375e 100644 --- a/plugins/techdocs-backend/src/helpers.ts +++ b/plugins/techdocs-backend/src/helpers.ts @@ -22,7 +22,7 @@ import fs from 'fs-extra'; import { getDefaultBranch } from './default-branch'; import { getGitRepoType, getTokenForGitRepo } from './git-auth'; import { Entity } from '@backstage/catalog-model'; -import { InputError } from '@backstage/backend-common'; +import { InputError, UrlReader } from '@backstage/backend-common'; import { RemoteProtocol } from './techdocs/stages/prepare/types'; import { Logger } from 'winston'; @@ -78,6 +78,7 @@ export const getLocationForEntity = ( case 'github': case 'gitlab': case 'azure/api': + case 'url': return { type, target }; case 'dir': if (path.isAbsolute(target)) return { type, target }; @@ -168,3 +169,34 @@ export const getLastCommitTimestamp = async ( return commit.date().getTime(); }; + +export const getDocFilesFromRepository = async ( + reader: UrlReader, + entity: Entity, +): Promise => { + const { target } = parseReferenceAnnotation( + 'backstage.io/techdocs-ref', + entity, + ); + + const { ref, filepath: mkdocsPath } = parseGitUrl(target); + + const docsRootPath = path.dirname(mkdocsPath); + const docsFolderPath = path.join(docsRootPath, 'docs'); + + if (reader.readTree) { + const readTreeResponse = await reader.readTree( + parseGitUrl(target).toString(), + ref, + [mkdocsPath, docsFolderPath], + ); + + const tmpDir = await readTreeResponse.dir(); + + return `${tmpDir}/${docsRootPath}`; + } + + throw new Error( + `No readTree method available on the UrlReader for ${target}`, + ); +}; diff --git a/plugins/techdocs-backend/src/service/helpers.ts b/plugins/techdocs-backend/src/service/helpers.ts index b308dc28af..3017a68461 100644 --- a/plugins/techdocs-backend/src/service/helpers.ts +++ b/plugins/techdocs-backend/src/service/helpers.ts @@ -103,7 +103,7 @@ export class DocsBuilder { const { type, target } = getLocationForEntity(this.entity); // Unless docs are stored locally - const nonAgeCheckTypes = ['dir', 'file']; + const nonAgeCheckTypes = ['dir', 'file', 'url']; if (!nonAgeCheckTypes.includes(type)) { const lastCommit = await getLastCommitTimestamp(target, this.logger); const storageTimeStamp = buildMetadataStorage.getTimestamp(); diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/index.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/index.ts index 46e1d84c69..6c73725a3a 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/index.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/index.ts @@ -15,5 +15,6 @@ */ export { DirectoryPreparer } from './dir'; export { CommonGitPreparer } from './commonGit'; +export { UrlPreparer } from './url'; export { Preparers } from './preparers'; export type { PreparerBuilder, PreparerBase } from './types'; diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/types.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/types.ts index 1dd091e107..97e75306c3 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/types.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/types.ts @@ -30,4 +30,10 @@ export type PreparerBuilder = { get(entity: Entity): PreparerBase; }; -export type RemoteProtocol = 'dir' | 'github' | 'gitlab' | 'file' | 'azure/api'; +export type RemoteProtocol = + | 'dir' + | 'github' + | 'gitlab' + | 'file' + | 'azure/api' + | 'url'; diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/url.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/url.ts new file mode 100644 index 0000000000..330db05aa4 --- /dev/null +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/url.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Entity } from '@backstage/catalog-model'; +import { PreparerBase } from './types'; +import { getDocFilesFromRepository } from '../../../helpers'; + +import { Logger } from 'winston'; +import { UrlReader } from '@backstage/backend-common'; + +export class UrlPreparer implements PreparerBase { + private readonly logger: Logger; + private readonly reader: UrlReader; + + constructor(reader: UrlReader, logger: Logger) { + this.logger = logger; + this.reader = reader; + } + + async prepare(entity: Entity): Promise { + try { + return getDocFilesFromRepository(this.reader, entity); + } catch (error) { + this.logger.debug( + `Unable to fetch files for building docs ${error.message}`, + ); + throw error; + } + } +} diff --git a/yarn.lock b/yarn.lock index 83b96cebdb..391b71d8c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4916,6 +4916,13 @@ dependencies: "@types/express" "*" +"@types/concat-stream@^1.6.0": + version "1.6.0" + resolved "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.0.tgz#394dbe0bb5fee46b38d896735e8b68ef2390d00d" + integrity sha1-OU2+C7X+5Gs42JZzXoto7yOQ0A0= + dependencies: + "@types/node" "*" + "@types/connect-history-api-fallback@*": version "1.3.3" resolved "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.3.tgz#4772b79b8b53185f0f4c9deab09236baf76ee3b4" @@ -5109,6 +5116,13 @@ dependencies: "@types/node" "*" +"@types/fs-extra@^9.0.3": + version "9.0.3" + resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.3.tgz#9996e5cce993508c32325380b429f04a1327523e" + integrity sha512-NKdGoXLTFTRED3ENcfCsH8+ekV4gbsysanx2OPbstXVV6fZMgUCqTxubs6I9r7pbOJbFgVq1rpFtLURjKCZWUw== + dependencies: + "@types/node" "*" + "@types/git-url-parse@^9.0.0": version "9.0.0" resolved "https://registry.npmjs.org/@types/git-url-parse/-/git-url-parse-9.0.0.tgz#aac1315a44fa4ed5a52c3820f6c3c2fb79cbd12d" @@ -22456,7 +22470,7 @@ tar@^4, tar@^4.4.10, tar@^4.4.12, tar@^4.4.8: safe-buffer "^5.1.2" yallist "^3.0.3" -tar@^6.0.1, tar@^6.0.2: +tar@^6.0.1, tar@^6.0.2, tar@^6.0.5: version "6.0.5" resolved "https://registry.npmjs.org/tar/-/tar-6.0.5.tgz#bde815086e10b39f1dcd298e89d596e1535e200f" integrity sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg== From 2d0bd1be700bfdb172879c62b03391a6f6c0dc34 Mon Sep 17 00:00:00 2001 From: Chad Wilson Date: Mon, 9 Nov 2020 10:52:00 -0500 Subject: [PATCH 122/252] About card edit improvements (#2950) * Update to Edit link in Card component. * prettier * new line * Added changeset * Actually run prettier. . . * Added functionality to determine source of url for icon choice. Fixed failed tests. * Added test case for new edithref attribute * Prettier * forgot a file * correct url substring sanitation * corrected url substring sanitation * Added handling of Bitbucket Cloud and Gitlab. * Added tests for each GitLab and BitBucket edits. * Update to leverage git-url-parse * update test to reflect owner change --- .changeset/thick-lobsters-swim.md | 5 ++ plugins/catalog/package.json | 1 + .../components/AboutCard/AboutCard.test.tsx | 68 +++++++++++++++- .../src/components/AboutCard/AboutCard.tsx | 33 +++++--- .../components/CatalogTable/CatalogTable.tsx | 11 +-- .../catalog/src/components/createEditLink.ts | 77 +++++++++++++++++++ 6 files changed, 175 insertions(+), 20 deletions(-) create mode 100644 .changeset/thick-lobsters-swim.md create mode 100644 plugins/catalog/src/components/createEditLink.ts diff --git a/.changeset/thick-lobsters-swim.md b/.changeset/thick-lobsters-swim.md new file mode 100644 index 0000000000..3a377b7535 --- /dev/null +++ b/.changeset/thick-lobsters-swim.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Improved the edit link to open the component yaml in edit mode in corresponding SCM. Broke out logic for createEditLink to be reused. diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 6d52c4722f..045230d3f9 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -32,6 +32,7 @@ "@material-ui/lab": "4.0.0-alpha.45", "@types/react": "^16.9", "classnames": "^2.2.6", + "git-url-parse": "^11.4.0", "moment": "^2.26.0", "react": "^16.13.1", "react-dom": "^16.13.1", diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx index 2854af2f26..3038fc3c5c 100644 --- a/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx @@ -18,7 +18,7 @@ import React from 'react'; import { render } from '@testing-library/react'; import { AboutCard } from './AboutCard'; -describe('', () => { +describe(' GitHub', () => { it('renders info and "view source" link', () => { const entity = { apiVersion: 'v1', @@ -42,5 +42,71 @@ describe('', () => { 'href', 'https://github.com/backstage/backstage/blob/master/software.yaml', ); + expect(getByText('View Source').closest('a')).toHaveAttribute( + 'edithref', + 'https://github.com/backstage/backstage/edit/master/software.yaml', + ); + }); +}); + +describe(' GitLab', () => { + it('renders info and "view source" link', () => { + const entity = { + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'software', + annotations: { + 'backstage.io/managed-by-location': + 'gitlab:https://gitlab.com/backstage/backstage/-/blob/master/software.yaml', + }, + }, + spec: { + owner: 'guest', + type: 'service', + lifecycle: 'production', + }, + }; + const { getByText } = render(); + expect(getByText('service')).toBeInTheDocument(); + expect(getByText('View Source').closest('a')).toHaveAttribute( + 'href', + 'https://gitlab.com/backstage/backstage/-/blob/master/software.yaml', + ); + expect(getByText('View Source').closest('a')).toHaveAttribute( + 'edithref', + 'https://gitlab.com/backstage/backstage/-/edit/master/software.yaml', + ); + }); +}); + +describe(' BitBucket', () => { + it('renders info and "view source" link', () => { + const entity = { + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'software', + annotations: { + 'backstage.io/managed-by-location': + 'bitbucket:https://bitbucket.org/backstage/backstage/src/master/software.yaml', + }, + }, + spec: { + owner: 'guest', + type: 'service', + lifecycle: 'production', + }, + }; + const { getByText } = render(); + expect(getByText('service')).toBeInTheDocument(); + expect(getByText('View Source').closest('a')).toHaveAttribute( + 'href', + 'https://bitbucket.org/backstage/backstage/src/master/software.yaml', + ); + expect(getByText('View Source').closest('a')).toHaveAttribute( + 'edithref', + 'https://bitbucket.org/backstage/backstage/src/master/software.yaml?mode=edit&spa=0&at=master', + ); }); }); diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.tsx index 74877b5a65..8da67d7493 100644 --- a/plugins/catalog/src/components/AboutCard/AboutCard.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutCard.tsx @@ -37,6 +37,8 @@ import EditIcon from '@material-ui/icons/Edit'; import GitHubIcon from '@material-ui/icons/GitHub'; import React from 'react'; import { IconLinkVertical } from './IconLinkVertical'; +import { findLocationForEntityMeta } from '../../data/utils'; +import { createEditLink, determineUrlType } from '../createEditLink'; const useStyles = makeStyles(theme => ({ links: { @@ -79,18 +81,24 @@ const iconMap: Record = { github: , }; -type CodeLinkInfo = { icon?: React.ReactNode; href?: string }; +type CodeLinkInfo = { + icon?: React.ReactNode; + edithref?: string; + href?: string; +}; function getCodeLinkInfo(entity: Entity): CodeLinkInfo { - const location = - entity?.metadata?.annotations?.['backstage.io/managed-by-location']; - + const location = findLocationForEntityMeta(entity?.metadata); if (location) { - // split by first `:` - // e.g. "github:https://github.com/backstage/backstage/blob/master/software.yaml" - const [type, target] = location.split(/:(.+)/); - - return { icon: iconMap[type], href: target }; + const type = + location.type === 'url' + ? determineUrlType(location.target) + : location.type; + return { + icon: iconMap[type], + edithref: createEditLink(location), + href: location.target, + }; } return {}; } @@ -109,7 +117,12 @@ export function AboutCard({ entity, variant }: AboutCardProps) { + { + window.open(codeLink.edithref || '#', '_blank'); + }} + > } diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index 731a8dedc9..15a220e839 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Entity, LocationSpec } from '@backstage/catalog-model'; +import { Entity } from '@backstage/catalog-model'; import { Table, TableColumn, TableProps } from '@backstage/core'; import { Chip, Link } from '@material-ui/core'; import Edit from '@material-ui/icons/Edit'; @@ -22,6 +22,7 @@ import { Alert } from '@material-ui/lab'; import React from 'react'; import { generatePath, Link as RouterLink } from 'react-router-dom'; import { findLocationForEntityMeta } from '../../data/utils'; +import { createEditLink } from '../createEditLink'; import { useStarredEntities } from '../../hooks/useStarredEntities'; import { entityRoute, entityRouteParams } from '../../routes'; import { @@ -120,14 +121,6 @@ export const CatalogTable = ({ }; }, (rowData: Entity) => { - const createEditLink = (location: LocationSpec): string => { - switch (location.type) { - case 'github': - return location.target.replace('/blob/', '/edit/'); - default: - return location.target; - } - }; const location = findLocationForEntityMeta(rowData.metadata); return { icon: () => , diff --git a/plugins/catalog/src/components/createEditLink.ts b/plugins/catalog/src/components/createEditLink.ts new file mode 100644 index 0000000000..cc04d605f1 --- /dev/null +++ b/plugins/catalog/src/components/createEditLink.ts @@ -0,0 +1,77 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { LocationSpec } from '@backstage/catalog-model'; +import gitUrlParse from 'git-url-parse'; + +/** + * Creates the edit link for components yaml file + * @see LocationSpec + * @param location The LocationSpec being used to determine entity SCM location + * @returns string representing the edit location based on SCM path + */ + +export const createEditLink = (location: LocationSpec): string | undefined => { + try { + const urlData = gitUrlParse(location.target); + const url = new URL(location.target); + switch (location.type) { + case 'github': + case 'gitlab': + return location.target.replace('/blob/', '/edit/'); + case 'bitbucket': + url.searchParams.set('mode', 'edit'); + url.searchParams.set('spa', '0'); + url.searchParams.set('at', urlData.ref); + return url.toString(); + case 'url': + if ( + urlData.source === 'github.com' || + urlData.source === 'gitlab.com/' + ) { + return location.target.replace('/blob/', '/edit/'); + } else if (urlData.source === 'bitbucket.org') { + url.searchParams.set('mode', 'edit'); + url.searchParams.set('spa', '0'); + url.searchParams.set('at', urlData.ref); + return url.toString(); + } + return location.target; + default: + return location.target; + } + } catch { + return undefined; + } +}; + +/** + * Determines type based on passed in url. This is used to set the icon associated with the type of entity + * @param url + * @returns string representing type of icon to be used + */ +export const determineUrlType = (url: string): string => { + const urlData = gitUrlParse(url); + + if (urlData.source === 'github.com') { + return 'github'; + } else if (urlData.source === 'bitbucket.org') { + return 'bitbucket'; + } else if (urlData.source === 'gitlab.com') { + return 'gitlab'; + } + return 'url'; +}; From 091765a32ed6d05eed29d6ef396050ed33bd46db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 9 Nov 2020 17:55:32 +0100 Subject: [PATCH 123/252] Update help-im-behind-a-corporate-proxy.md (#3266) --- contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md b/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md index 4f8351ac15..f5288cf144 100644 --- a/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md +++ b/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md @@ -12,7 +12,7 @@ There are however some ways to get this to work without too much effort. It's mo ### Using `global-agent` -1. Install `global-agent` using `yarn install global-agent` +1. Install `global-agent` using `yarn add global-agent` 2. Go to the entry file for the backend (`src/index.ts`) 3. At the top of the file paste the following: @@ -33,7 +33,7 @@ More information and more options for configuring `global-agent` including just `proxy-agent` is a library that you can use to override the `globalAgents` of `node` land with a tunnel to use for each request. -1. Install `proxy-agent` using `yarn install proxy-agent` +1. Install `proxy-agent` using `yarn add proxy-agent` 2. Go to the entry file for the backend (`src/index.ts`) 3. At the top of the file paste the following: From 0c0798f082ae6f443798196d835d727837046477 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Mon, 9 Nov 2020 18:19:47 +0100 Subject: [PATCH 124/252] Persist Table Filters in the API Explorer (#2936) * fix: allow changing the categories of a checkbox tree * feat: allow setting the current selection in the checkbox tree * feat: allow setting the current selection in the select * feat: add a way to access the tables internal state (filters, search, ...) * feat: add useQueryParams hook * feat: persist the table state of the api explorer in the url * Use react-use instead of writing own hooks * Resolve review comments * Rename selectedChilds to selecetedChildren * Add changesets * Support passing a separate state name to useQueryParamState This allows to use the useQueryParamState hook multiple time per route and have a separate state. * refactor: fix typo... --- .changeset/fluffy-dragons-cry.md | 5 + .changeset/odd-parents-push.md | 5 + .changeset/seven-monkeys-run.md | 5 + packages/core/package.json | 1 + .../CheckboxTree/CheckboxTree.stories.tsx | 34 +++- .../components/CheckboxTree/CheckboxTree.tsx | 90 ++++++++-- .../core/src/components/Select/Select.tsx | 38 ++-- .../core/src/components/Table/Filters.tsx | 36 ++-- packages/core/src/components/Table/Table.tsx | 168 ++++++++++++++---- packages/core/src/components/Table/index.ts | 2 +- packages/core/src/hooks/index.ts | 17 ++ packages/core/src/hooks/useQueryParamState.ts | 84 +++++++++ packages/core/src/index.ts | 1 + .../ApiExplorerTable/ApiExplorerTable.tsx | 15 +- 14 files changed, 427 insertions(+), 74 deletions(-) create mode 100644 .changeset/fluffy-dragons-cry.md create mode 100644 .changeset/odd-parents-push.md create mode 100644 .changeset/seven-monkeys-run.md create mode 100644 packages/core/src/hooks/index.ts create mode 100644 packages/core/src/hooks/useQueryParamState.ts diff --git a/.changeset/fluffy-dragons-cry.md b/.changeset/fluffy-dragons-cry.md new file mode 100644 index 0000000000..32a202b417 --- /dev/null +++ b/.changeset/fluffy-dragons-cry.md @@ -0,0 +1,5 @@ +--- +'@backstage/core': patch +--- + +Extend the table to share its current filter state. The filter state can be used together with the new `useQueryParamState` hook to store the current filter state to the browser history and restore it after navigating to other routes. diff --git a/.changeset/odd-parents-push.md b/.changeset/odd-parents-push.md new file mode 100644 index 0000000000..5d997b7a45 --- /dev/null +++ b/.changeset/odd-parents-push.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-api-docs': patch +--- + +Persist table state of the API Explorer to the browser history. This allows to navigate between pages and come back to the previous filter state. diff --git a/.changeset/seven-monkeys-run.md b/.changeset/seven-monkeys-run.md new file mode 100644 index 0000000000..7e66daa86f --- /dev/null +++ b/.changeset/seven-monkeys-run.md @@ -0,0 +1,5 @@ +--- +'@backstage/core': patch +--- + +Make the selected state of Select and CheckboxTree controllable from outside. diff --git a/packages/core/package.json b/packages/core/package.json index 0ddc77f1e4..aaedf2ca6f 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -44,6 +44,7 @@ "d3-shape": "^2.0.0", "d3-zoom": "^2.0.0", "dagre": "^0.8.5", + "qs": "^6.9.4", "immer": "^7.0.9", "lodash": "^4.17.15", "material-table": "^1.69.1", diff --git a/packages/core/src/components/CheckboxTree/CheckboxTree.stories.tsx b/packages/core/src/components/CheckboxTree/CheckboxTree.stories.tsx index d5a33f0f9b..db590e6524 100644 --- a/packages/core/src/components/CheckboxTree/CheckboxTree.stories.tsx +++ b/packages/core/src/components/CheckboxTree/CheckboxTree.stories.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React from 'react'; +import React, { useState } from 'react'; import { CheckboxTree } from '.'; const CHECKBOX_TREE_ITEMS = [ @@ -71,3 +71,35 @@ export const Default = () => ( subCategories={CHECKBOX_TREE_ITEMS} /> ); + +export const DynamicTree = () => { + function generateTree(showMore: boolean = false) { + const t = [ + { + label: 'Show more', + options: [], + }, + ]; + + if (showMore) { + t.push({ + label: 'More', + options: [], + }); + } + + return t; + } + + const [tree, setTree] = useState(generateTree()); + + return ( + { + setTree(generateTree(state.some(c => c.category === 'Show more'))); + }} + label="default" + subCategories={tree} + /> + ); +}; diff --git a/packages/core/src/components/CheckboxTree/CheckboxTree.tsx b/packages/core/src/components/CheckboxTree/CheckboxTree.tsx index 6825704971..d6d3410913 100644 --- a/packages/core/src/components/CheckboxTree/CheckboxTree.tsx +++ b/packages/core/src/components/CheckboxTree/CheckboxTree.tsx @@ -14,20 +14,22 @@ * limitations under the License. */ /* eslint-disable guard-for-in */ -import React, { useEffect, useReducer } from 'react'; -import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; import { + Checkbox, + Collapse, List, ListItem, ListItemIcon, - Checkbox, ListItemText, - Collapse, Typography, } from '@material-ui/core'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; import ExpandLess from '@material-ui/icons/ExpandLess'; import ExpandMore from '@material-ui/icons/ExpandMore'; import produce from 'immer'; +import { isEqual } from 'lodash'; +import React, { useEffect, useReducer } from 'react'; +import { usePrevious } from 'react-use'; type IndexedObject = { [key: string]: T; @@ -96,11 +98,14 @@ type Option = { isChecked?: boolean; }; +type Selection = { category?: string; selectedChildren?: string[] }[]; + export type CheckboxTreeProps = { subCategories: SubCategory[]; label: string; triggerReset?: boolean; - onChange: (arg: any) => any; + selected?: Selection; + onChange: (arg: Selection) => any; }; /* REDUCER */ @@ -114,6 +119,11 @@ type Action = | { type: 'checkOption'; payload: checkOptionPayload } | { type: 'checkCategory'; payload: string } | { type: 'toggleCategory'; payload: string } + | { + type: 'updateCategories'; + payload: IndexedObject; + } + | { type: 'updateSelected'; payload: Selection } | { type: 'triggerReset' }; const reducer = ( @@ -157,6 +167,38 @@ const reducer = ( } }); } + case 'updateCategories': { + return produce(state, newState => { + for (const category in newState) { + delete newState[category]; + } + + for (const category in action.payload) { + newState[category] = action.payload[category]; + + if (state[category]) { + newState[category].isChecked = state[category].isChecked; + newState[category].isOpen = state[category].isOpen; + } + } + }); + } + case 'updateSelected': { + return produce(state, newState => { + for (const category in newState) { + const selection = action.payload.find(s => s.category === category); + + if (selection) { + newState[category].isChecked = true; + + for (const option in newState[category].options) { + newState[category].options[option].isChecked = + selection.selectedChildren?.includes(option) || false; + } + } + } + }); + } default: return state; } @@ -183,23 +225,30 @@ const indexer = ( }; }, {}); -export const CheckboxTree = (props: CheckboxTreeProps) => { - const { onChange } = props; +export const CheckboxTree = ({ + subCategories, + label, + selected, + onChange, + triggerReset, +}: CheckboxTreeProps) => { const classes = useStyles(); - const [state, dispatch] = useReducer(reducer, indexer(props.subCategories)); + const [state, dispatch] = useReducer(reducer, indexer(subCategories)); const handleOpen = (event: any, value: any) => { event.stopPropagation(); dispatch({ type: 'toggleCategory', payload: value }); }; + const previousSubCategories = usePrevious(subCategories); + useEffect(() => { const values = Object.values(state).map(category => ({ - category: category.isChecked ? category.label : null, - selectedChilds: Object.values(category.options) + category: category.isChecked ? category.label : undefined, + selectedChildren: Object.values(category.options) .filter(option => option.isChecked) - .map(option => option.value), + .map(option => option.label), })); onChange(values); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -207,11 +256,26 @@ export const CheckboxTree = (props: CheckboxTreeProps) => { useEffect(() => { dispatch({ type: 'triggerReset' }); - }, [props.triggerReset]); + }, [triggerReset]); + + useEffect(() => { + if (selected) { + dispatch({ type: 'updateSelected', payload: selected }); + } + }, [selected]); + + useEffect(() => { + if (!isEqual(subCategories, previousSubCategories)) { + dispatch({ + type: 'updateCategories', + payload: indexer(subCategories), + }); + } + }, [subCategories, previousSubCategories]); return (
- {props.label} + {label} {Object.values(state).map(item => (
diff --git a/packages/core/src/components/Select/Select.tsx b/packages/core/src/components/Select/Select.tsx index 1c691340b2..1e6dba76e0 100644 --- a/packages/core/src/components/Select/Select.tsx +++ b/packages/core/src/components/Select/Select.tsx @@ -94,30 +94,46 @@ type Item = { value: string | number; }; +type Selection = string | string[] | number | number[]; + export type SelectProps = { multiple?: boolean; items: Item[]; label: string; placeholder?: string; - onChange: (arg: any) => any; + selected?: Selection; + onChange: (arg: Selection) => void; triggerReset?: boolean; }; -export const SelectComponent = (props: SelectProps) => { - const { multiple, items, label, placeholder, onChange } = props; +export const SelectComponent = ({ + multiple, + items, + label, + placeholder, + selected, + onChange, + triggerReset, +}: SelectProps) => { const classes = useStyles(); - const [value, setValue] = useState( - multiple ? [] : '', + const [value, setValue] = useState( + selected || (multiple ? [] : ''), ); const [isOpen, setOpen] = useState(false); useEffect(() => { setValue(multiple ? [] : ''); - }, [props.triggerReset, multiple]); + }, [triggerReset, multiple]); + + useEffect(() => { + if (selected !== undefined) { + setValue(selected); + } + }, [selected]); const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => { - setValue(event.target.value as any); - onChange(event.target.value); + setValue(event.target.value as Selection); + onChange(event.target.value as Selection); }; const handleClick = (event: React.ChangeEvent) => { @@ -153,10 +169,10 @@ export const SelectComponent = (props: SelectProps) => { onClick={handleClick} open={isOpen} input={} - renderValue={selected => + renderValue={s => multiple && (value as any[]).length !== 0 ? (
- {(selected as string[]).map(selectedValue => ( + {(s as string[]).map(selectedValue => ( el.value === selectedValue)?.value} label={ @@ -172,7 +188,7 @@ export const SelectComponent = (props: SelectProps) => { {(value as any[]).length === 0 ? placeholder || '' - : items.find(el => el.value === selected)?.label} + : items.find(el => el.value === s)?.label} ) } diff --git a/packages/core/src/components/Table/Filters.tsx b/packages/core/src/components/Table/Filters.tsx index fdff04e478..138fec4736 100644 --- a/packages/core/src/components/Table/Filters.tsx +++ b/packages/core/src/components/Table/Filters.tsx @@ -65,6 +65,7 @@ export type SelectedFilters = { type Props = { filters: Filter[]; + selectedFilters?: SelectedFilters; onChangeFilters: (arg: any) => any; }; @@ -73,21 +74,20 @@ export const Filters = (props: Props) => { const { onChangeFilters } = props; - const [filters, setFilters] = useState(props.filters); - const [selectedFilters, setSelectedFilters] = useState({}); + const [selectedFilters, setSelectedFilters] = useState({ + ...props.selectedFilters, + }); const [reset, triggerReset] = useState(false); // Trigger re-rendering const handleClick = () => { setSelectedFilters({}); - setFilters([...props.filters]); triggerReset(el => !el); }; useEffect(() => { onChangeFilters(selectedFilters); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedFilters]); + }, [selectedFilters, onChangeFilters]); // As material table doesn't provide a way to add a column filter tab we will make our own filter logic return ( @@ -99,29 +99,38 @@ export const Filters = (props: Props) => {
- {filters?.length && - filters.map(filter => + {props.filters?.length && + props.filters.map(filter => filter.type === 'checkbox-tree' ? ( ({ + category: s, + }), + ) + : undefined + } onChange={el => setSelectedFilters({ ...selectedFilters, [filter.element.label]: el .filter( (checkboxFilter: any) => - checkboxFilter.category !== null || - checkboxFilter.selectedChilds.length, + checkboxFilter.category || + checkboxFilter.selectedChildren.length, ) .map((checkboxFilter: any) => - checkboxFilter.category !== null + checkboxFilter.category ? [ - ...checkboxFilter.selectedChilds, + ...checkboxFilter.selectedChildren, checkboxFilter.category, ] - : checkboxFilter.selectedChilds, + : checkboxFilter.selectedChildren, ) .flat(), }) @@ -132,10 +141,11 @@ export const Filters = (props: Props) => { triggerReset={reset} key={filter.element.label} {...(filter.element as SelectProps)} + selected={selectedFilters[filter.element.label]} onChange={el => setSelectedFilters({ ...selectedFilters, - [filter.element.label]: el, + [filter.element.label]: el as any, }) } /> diff --git a/packages/core/src/components/Table/Table.tsx b/packages/core/src/components/Table/Table.tsx index 87ad50e58f..852f4a1547 100644 --- a/packages/core/src/components/Table/Table.tsx +++ b/packages/core/src/components/Table/Table.tsx @@ -16,10 +16,10 @@ import { BackstageTheme } from '@backstage/theme'; import { + IconButton, makeStyles, Typography, useTheme, - IconButton, } from '@material-ui/core'; // Material-table is not using the standard icons available in in material-ui. https://github.com/mbrn/material-table/issues/51 import AddBox from '@material-ui/icons/AddBox'; @@ -37,6 +37,7 @@ import Remove from '@material-ui/icons/Remove'; import SaveAlt from '@material-ui/icons/SaveAlt'; import Search from '@material-ui/icons/Search'; import ViewColumn from '@material-ui/icons/ViewColumn'; +import { isEqual, transform } from 'lodash'; import MTable, { Column, MaterialTableProps, @@ -44,7 +45,13 @@ import MTable, { MTableToolbar, Options, } from 'material-table'; -import React, { forwardRef, useCallback, useEffect, useState } from 'react'; +import React, { + forwardRef, + useCallback, + useEffect, + useRef, + useState, +} from 'react'; import { CheckboxTreeProps } from '../CheckboxTree/CheckboxTree'; import { SelectProps } from '../Select/Select'; import { Filter, Filters, SelectedFilters, Without } from './Filters'; @@ -188,6 +195,20 @@ function convertColumns( }); } +function removeDefaultValues(state: any, defaultState: any): any { + return transform(state, (result, value, key) => { + if (!isEqual(value, defaultState[key])) { + result[key] = value; + } + }); +} + +const defaultInitialState = { + search: '', + filtersOpen: false, + filters: {}, +}; + export interface TableColumn extends Column { highlight?: boolean; width?: string; @@ -198,11 +219,19 @@ export type TableFilter = { type: 'select' | 'multiple-select' | 'checkbox-tree'; }; +export type TableState = { + search?: string; + filtersOpen?: boolean; + filters?: SelectedFilters; +}; + export interface TableProps extends MaterialTableProps { columns: TableColumn[]; subtitle?: string; filters?: TableFilter[]; + initialState?: TableState; + onStateChange?: (state: TableState) => any; } export function Table({ @@ -211,6 +240,8 @@ export function Table({ title, subtitle, filters, + initialState, + onStateChange, ...props }: TableProps) { const headerClasses = useHeaderStyles(); @@ -222,13 +253,43 @@ export function Table({ const theme = useTheme(); - const [filtersOpen, toggleFilters] = useState(false); + const calculatedInitialState = { ...defaultInitialState, ...initialState }; + + const [filtersOpen, toggleFilters] = useState( + calculatedInitialState.filtersOpen, + ); const [selectedFiltersLength, setSelectedFiltersLength] = useState(0); const [tableData, setTableData] = useState(data as any[]); - const [selectedFilters, setSelectedFilters] = useState(); + const [selectedFilters, setSelectedFilters] = useState( + calculatedInitialState.filters, + ); const MTColumns = convertColumns(columns, theme); + const [search, setSearch] = useState(calculatedInitialState.search); + const toolbarRef = useRef(); + + useEffect(() => { + if (toolbarRef.current) { + toolbarRef.current.onSearchChange(search); + } + }, [search, toolbarRef]); + + useEffect(() => { + if (onStateChange) { + const state = removeDefaultValues( + { + search, + filtersOpen, + filters: selectedFilters, + }, + defaultInitialState, + ); + + onStateChange(state); + } + }, [search, filtersOpen, selectedFilters, onStateChange]); + const defaultOptions: Options = { headerStyle: { textTransform: 'uppercase', @@ -248,7 +309,7 @@ export function Table({ } const selectedFiltersArray = Object.values(selectedFilters); - if (selectedFiltersArray.flat().length) { + if (data && selectedFiltersArray.flat().length) { const newData = (data as any[]).filter( el => !!Object.entries(selectedFilters) @@ -279,7 +340,7 @@ export function Table({ const constructFilters = ( filterConfig: TableFilter[], - dataValue: any[], + dataValue: any[] | undefined, ): Filter[] => { const extractDistinctValues = (field: string | keyof T): Set => { const distinctValues = new Set(); @@ -289,15 +350,20 @@ export function Table({ } }; - dataValue.forEach(el => { - const value = extractValueByField(el, getFieldByTitle(field) as string); + if (dataValue) { + dataValue.forEach(el => { + const value = extractValueByField( + el, + getFieldByTitle(field) as string, + ); - if (Array.isArray(value)) { - (value as []).forEach(addValue); - } else { - addValue(value); - } - }); + if (Array.isArray(value)) { + (value as []).forEach(addValue); + } else { + addValue(value); + } + }); + } return distinctValues; }; @@ -335,11 +401,63 @@ export function Table({ })); }; + const Toolbar = useCallback( + toolbarProps => { + const onSearchChanged = (searchText: string) => { + toolbarProps.onSearchChanged(searchText); + setSearch(searchText); + }; + + if (filters?.length) { + return ( +
+
+ toggleFilters(el => !el)} + aria-label="filter list" + > + + + + Filters ({selectedFiltersLength}) + +
+ +
+ ); + } + + return ( + + ); + }, + [ + filters?.length, + selectedFiltersLength, + toggleFilters, + toolbarClasses, + filtersClasses, + setSearch, + toolbarRef, + ], + ); + return (
- {filtersOpen && filters?.length && ( + {filtersOpen && data && filters?.length && ( )} @@ -348,25 +466,7 @@ export function Table({ Header: headerProps => ( ), - Toolbar: toolbarProps => - filters?.length ? ( -
-
- toggleFilters(el => !el)} - aria-label="filter list" - > - - - - Filters ({selectedFiltersLength}) - -
- -
- ) : ( - - ), + Toolbar, }} options={{ ...defaultOptions, ...options }} columns={MTColumns} diff --git a/packages/core/src/components/Table/index.ts b/packages/core/src/components/Table/index.ts index 9cb8478688..83432ffc70 100644 --- a/packages/core/src/components/Table/index.ts +++ b/packages/core/src/components/Table/index.ts @@ -16,4 +16,4 @@ export { SubvalueCell } from './SubvalueCell'; export { Table } from './Table'; -export type { TableColumn, TableFilter, TableProps } from './Table'; +export type { TableColumn, TableFilter, TableProps, TableState } from './Table'; diff --git a/packages/core/src/hooks/index.ts b/packages/core/src/hooks/index.ts new file mode 100644 index 0000000000..a77a0eb8a4 --- /dev/null +++ b/packages/core/src/hooks/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { useQueryParamState } from './useQueryParamState'; diff --git a/packages/core/src/hooks/useQueryParamState.ts b/packages/core/src/hooks/useQueryParamState.ts new file mode 100644 index 0000000000..9317dcd3e1 --- /dev/null +++ b/packages/core/src/hooks/useQueryParamState.ts @@ -0,0 +1,84 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import qs from 'qs'; +import { useState } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { useDebounce } from 'react-use'; + +function stringify(queryParams: any): string { + // Even though these setting don't look nice (e.g. escaped brackets), we should keep + // them this way. The current syntax handles all cases, including variable types with + // arrays or strings. + return qs.stringify(queryParams, { + strictNullHandling: true, + }); +} + +function parse(queryString: string): any { + return qs.parse(queryString, { + ignoreQueryPrefix: true, + strictNullHandling: true, + }); +} + +function extractState(queryString: string, stateName: string): any | undefined { + const queryParams = parse(queryString); + + return queryParams[stateName]; +} + +function joinQueryString( + queryString: string, + stateName: string, + state: any, +): string { + const queryParams = { + ...parse(queryString), + [stateName]: state, + }; + return stringify(queryParams); +} + +type SetQueryParams = (params: T) => void; + +export function useQueryParamState( + stateName: string, +): [T | undefined, SetQueryParams] { + const navigate = useNavigate(); + const location = useLocation(); + const [queryParamState, setQueryParamState] = useState( + extractState(location.search, stateName), + ); + + useDebounce( + () => { + const queryString = joinQueryString( + location.search, + stateName, + queryParamState, + ); + + if (location.search !== queryString) { + navigate({ ...location, search: `?${queryString}` }, { replace: true }); + } + }, + 100, + [queryParamState], + ); + + return [queryParamState, setQueryParamState]; +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 4c0d42bbb2..43168635cb 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -19,3 +19,4 @@ export * from '@backstage/core-api'; export * from './api-wrappers'; export * from './components'; export * from './layout'; +export * from './hooks'; diff --git a/plugins/api-docs/src/components/ApiExplorerTable/ApiExplorerTable.tsx b/plugins/api-docs/src/components/ApiExplorerTable/ApiExplorerTable.tsx index f3be9f664b..e12d172e41 100644 --- a/plugins/api-docs/src/components/ApiExplorerTable/ApiExplorerTable.tsx +++ b/plugins/api-docs/src/components/ApiExplorerTable/ApiExplorerTable.tsx @@ -15,7 +15,14 @@ */ import { ApiEntityV1alpha1, Entity } from '@backstage/catalog-model'; -import { Table, TableFilter, TableColumn, useApi } from '@backstage/core'; +import { + Table, + TableColumn, + TableFilter, + TableState, + useApi, + useQueryParamState, +} from '@backstage/core'; import { Chip, Link } from '@material-ui/core'; import { Alert } from '@material-ui/lab'; import React from 'react'; @@ -120,6 +127,10 @@ export const ApiExplorerTable = ({ loading, error, }: ExplorerTableProps) => { + const [queryParamState, setQueryParamState] = useQueryParamState( + 'apiTable', + ); + if (error) { return (
@@ -142,6 +153,8 @@ export const ApiExplorerTable = ({ }} data={entities} filters={filters} + initialState={queryParamState} + onStateChange={setQueryParamState} /> ); }; From 82eaf047064523efc40072ab9e0c6c1093442723 Mon Sep 17 00:00:00 2001 From: Ryan Vazquez Date: Mon, 9 Nov 2020 14:38:02 -0500 Subject: [PATCH 125/252] bugfix: display select for 1 or more metrics --- .../src/components/CostOverviewCard/CostOverviewCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.tsx b/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.tsx index 7d55026be8..8f52a0070b 100644 --- a/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.tsx +++ b/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.tsx @@ -98,7 +98,7 @@ export const CostOverviewCard = ({ /> - {config.metrics.length > 1 && ( + {config.metrics.length && ( Date: Tue, 10 Nov 2020 10:22:48 +0100 Subject: [PATCH 126/252] Move TechDocs project board: Fix workflow --- .github/workflows/techdocs-project-board.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/techdocs-project-board.yml b/.github/workflows/techdocs-project-board.yml index a0691f2a79..d1b6dc3fd5 100644 --- a/.github/workflows/techdocs-project-board.yml +++ b/.github/workflows/techdocs-project-board.yml @@ -1,6 +1,7 @@ name: Automatically add new TechDocs Issues and PRs to the GitHub project board -# Development of TechDocs in Backstage is managed by this Kanban board - https://github.com/backstage/backstage/projects/5 -# New issues with TechDocs in their title or docs-like-code label will be added to the board. +# Development of TechDocs in Backstage is managed by this Kanban board - https://github.com/orgs/backstage/projects/1 +# New issues and PRs with TechDocs in their title or docs-like-code label will be added to the board. +# Caveat: New PRs created from forks will not be added since GitHub actions don't share credentials with forks. on: issues: @@ -23,7 +24,7 @@ jobs: contains(github.event.issue.title, 'techdocs') || contains(github.event.issue.title, 'Techdocs') with: - project: 'https://github.com/backstage/backstage/projects/5' + project: 'https://github.com/orgs/backstage/projects/1' column_name: 'Incoming' - name: Assign new issue to Incoming based on its label. @@ -31,7 +32,7 @@ jobs: if: | contains(github.event.issue.labels.*.name, 'docs-like-code') with: - project: 'https://github.com/backstage/backstage/projects/5' + project: 'https://github.com/orgs/backstage/projects/1' column_name: 'Incoming' - name: Assign new PR to Incoming based on its title. @@ -41,7 +42,7 @@ jobs: contains(github.event.pull_request.title, 'techdocs') || contains(github.event.pull_request.title, 'Techdocs') with: - project: 'https://github.com/backstage/backstage/projects/5' + project: 'https://github.com/orgs/backstage/projects/1' column_name: 'Incoming' - name: Assign new PR to Incoming based on its label. @@ -49,5 +50,5 @@ jobs: if: | contains(github.event.pull_request.labels.*.name, 'docs-like-code') with: - project: 'https://github.com/backstage/backstage/projects/5' + project: 'https://github.com/orgs/backstage/projects/1' column_name: 'Incoming' From 39eef8134942449d1a926c6fd405a5ebaed06891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Torres?= Date: Tue, 10 Nov 2020 11:12:17 +0000 Subject: [PATCH 127/252] fix: link to examples in Catalog docs (#3271) Fix reference to artist-lookup-component.yaml path in Catalog docs, the new folder structure is missing --- docs/features/software-catalog/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/features/software-catalog/index.md b/docs/features/software-catalog/index.md index 6076068504..08e6260e80 100644 --- a/docs/features/software-catalog/index.md +++ b/docs/features/software-catalog/index.md @@ -62,7 +62,7 @@ Users can register new components by going to `/create` and clicking the Backstage expects the full URL to the YAML in your source control. Example: ```bash -https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml +https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/artist-lookup-component.yaml ``` _More examples can be found @@ -91,7 +91,7 @@ above example can be added using the following configuration: catalog: locations: - type: url - target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/artist-lookup-component.yaml ``` More information about catalog configuration can be found From 8dd60357f128f5793a07a368ad706172ddfad26a Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 10 Nov 2020 15:20:20 +0100 Subject: [PATCH 128/252] Add MY_GITHUB_TOKEN for the project board automation workflow --- .github/workflows/techdocs-project-board.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/techdocs-project-board.yml b/.github/workflows/techdocs-project-board.yml index d1b6dc3fd5..b81be4d79c 100644 --- a/.github/workflows/techdocs-project-board.yml +++ b/.github/workflows/techdocs-project-board.yml @@ -11,6 +11,7 @@ on: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + MY_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: assign_issue_or_pr_to_project: From 99c35b909a364cfd9afcbd7d4e878f643ff89bba Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 10 Nov 2020 15:29:28 +0100 Subject: [PATCH 129/252] =?UTF-8?q?Emojis=20are=20nice=20-=20Fix=20column?= =?UTF-8?q?=20name=20Incoming=20=F0=9F=93=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/techdocs-project-board.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/techdocs-project-board.yml b/.github/workflows/techdocs-project-board.yml index b81be4d79c..c6beb9567c 100644 --- a/.github/workflows/techdocs-project-board.yml +++ b/.github/workflows/techdocs-project-board.yml @@ -10,7 +10,6 @@ on: types: [opened, reopened, labeled, edited] env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} MY_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: @@ -26,7 +25,7 @@ jobs: contains(github.event.issue.title, 'Techdocs') with: project: 'https://github.com/orgs/backstage/projects/1' - column_name: 'Incoming' + column_name: 'Incoming 📨' - name: Assign new issue to Incoming based on its label. uses: srggrs/assign-one-project-github-action@1.2.0 @@ -34,7 +33,7 @@ jobs: contains(github.event.issue.labels.*.name, 'docs-like-code') with: project: 'https://github.com/orgs/backstage/projects/1' - column_name: 'Incoming' + column_name: 'Incoming 📨' - name: Assign new PR to Incoming based on its title. uses: srggrs/assign-one-project-github-action@1.2.0 @@ -44,7 +43,7 @@ jobs: contains(github.event.pull_request.title, 'Techdocs') with: project: 'https://github.com/orgs/backstage/projects/1' - column_name: 'Incoming' + column_name: 'Incoming 📨' - name: Assign new PR to Incoming based on its label. uses: srggrs/assign-one-project-github-action@1.2.0 @@ -52,4 +51,4 @@ jobs: contains(github.event.pull_request.labels.*.name, 'docs-like-code') with: project: 'https://github.com/orgs/backstage/projects/1' - column_name: 'Incoming' + column_name: 'Incoming 📨' From b4204066f0352e3fa1e976895cde598693e3de29 Mon Sep 17 00:00:00 2001 From: Mateusz Lewtak <36006588+lewtakm@users.noreply.github.com> Date: Tue, 10 Nov 2020 16:02:13 +0100 Subject: [PATCH 130/252] Feat: Add BuildKite CI/CD Plugin to sample app (#3248) * Feat: Add BuildKite CI/CD Plugin to sample app * Feat: Add EntityPage test, bump plugin version * Feat: update package.json * feat: run prettier * Feat: update test code after CR --- app-config.yaml | 6 ++ packages/app/package.json | 1 + .../components/catalog/EntityPage.test.tsx | 62 +++++++++++++++++++ .../app/src/components/catalog/EntityPage.tsx | 8 ++- packages/app/src/plugins.ts | 1 + yarn.lock | 19 ++++++ 6 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 packages/app/src/components/catalog/EntityPage.test.tsx diff --git a/app-config.yaml b/app-config.yaml index 148627dc5a..3d0d093536 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -46,6 +46,12 @@ proxy: X-Api-Key: $env: NEW_RELIC_REST_API_KEY + '/buildkite/api': + target: https://api.buildkite.com/v2/ + headers: + Authorization: + $env: BUILDKITE_TOKEN + organization: name: My Company diff --git a/packages/app/package.json b/packages/app/package.json index 107b8c1cda..54f0dd2879 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -37,6 +37,7 @@ "@roadiehq/backstage-plugin-github-insights": "^0.2.12", "@roadiehq/backstage-plugin-github-pull-requests": "^0.6.2", "@roadiehq/backstage-plugin-travis-ci": "^0.2.7", + "@roadiehq/backstage-plugin-buildkite": "^0.1.2", "history": "^5.0.0", "prop-types": "^15.7.2", "react": "^16.12.0", diff --git a/packages/app/src/components/catalog/EntityPage.test.tsx b/packages/app/src/components/catalog/EntityPage.test.tsx new file mode 100644 index 0000000000..f4dfafc7d3 --- /dev/null +++ b/packages/app/src/components/catalog/EntityPage.test.tsx @@ -0,0 +1,62 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { CICDSwitcher } from './EntityPage'; +import { UrlPatternDiscovery, ApiProvider, ApiRegistry } from '@backstage/core'; +import { + buildKiteApiRef, + BuildKiteApi, +} from '@roadiehq/backstage-plugin-buildkite'; +import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; + +describe('EntityPage Test', () => { + const entity = { + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'ExampleComponent', + annotations: { + 'buildkite.com/project-slug': 'exampleProject/examplePipeline', + }, + }, + spec: { + owner: 'guest', + type: 'service', + lifecycle: 'production', + }, + }; + + const discoveryApi = UrlPatternDiscovery.compile('http://exampleapi.com'); + + const apis = ApiRegistry.from([ + [buildKiteApiRef, new BuildKiteApi({ discoveryApi })], + ]); + + describe('CICDSwitcher Test', () => { + it('Should render BuildKite View', async () => { + const renderedComponent = await renderWithEffects( + wrapInTestApp( + + + , + ), + ); + expect( + renderedComponent.getByText(/exampleProject\/examplePipeline/), + ).toBeInTheDocument(); + }); + }); +}); diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index 0a3007237e..73d84ef1ca 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -66,13 +66,19 @@ import { isPluginApplicableToEntity as isPullRequestsAvailable, PullRequestsStatsCard, } from '@roadiehq/backstage-plugin-github-pull-requests'; +import { + Router as BuildKiteRouter, + isPluginApplicableToEntity as isBuildKiteAvailable, +} from '@roadiehq/backstage-plugin-buildkite'; -const CICDSwitcher = ({ entity }: { entity: Entity }) => { +export const CICDSwitcher = ({ entity }: { entity: Entity }) => { // This component is just an example of how you can implement your company's logic in entity page. // You can for example enforce that all components of type 'service' should use GitHubActions switch (true) { case isJenkinsAvailable(entity): return ; + case isBuildKiteAvailable(entity): + return ; case isGitHubActionsAvailable(entity): return ; case isCircleCIAvailable(entity): diff --git a/packages/app/src/plugins.ts b/packages/app/src/plugins.ts index bdc3851318..c61a16e59c 100644 --- a/packages/app/src/plugins.ts +++ b/packages/app/src/plugins.ts @@ -38,3 +38,4 @@ export { plugin as Cloudbuild } from '@backstage/plugin-cloudbuild'; export { plugin as CostInsights } from '@backstage/plugin-cost-insights'; export { plugin as GitHubInsights } from '@roadiehq/backstage-plugin-github-insights'; export { plugin as UserSettings } from '@backstage/plugin-user-settings'; +export { plugin as BuildKite } from '@roadiehq/backstage-plugin-buildkite'; diff --git a/yarn.lock b/yarn.lock index 391b71d8c9..ceb0234e6b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3651,6 +3651,25 @@ resolved "https://registry.npmjs.org/@rjsf/material-ui/-/material-ui-2.4.0.tgz#1b5859298bf3f61137d7b05084f058a775d6fd73" integrity sha512-U8F/suzg4MuV+8mK1/ufs0Y6c3O8hc1wnuD2IKoOVJvegGfz5JCafyoyGAW6iyuT1DZBMPzVWEqfiuYPmoE7pw== +"@roadiehq/backstage-plugin-buildkite@^0.1.2": + version "0.1.2" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-buildkite/-/backstage-plugin-buildkite-0.1.2.tgz#2f2b414acc18ed7820abc6fb0c042f00c31d7c9a" + integrity sha512-NO1ogAK6lfh/YUmftqbYn6pkUmMJXk9cfda0YDnG23XlcKIst1d3xO5FggozVtU5IZiJDxxwxOOo4ylhBWrGeQ== + dependencies: + "@backstage/catalog-model" "^0.2.0" + "@backstage/core" "^0.2.0" + "@backstage/plugin-catalog" "^0.2.0" + "@material-ui/core" "^4.11.0" + "@material-ui/icons" "^4.9.1" + "@material-ui/lab" "4.0.0-alpha.45" + moment "^2.27.0" + react "^16.13.1" + react-dom "^16.13.1" + react-lazylog "^4.5.2" + react-router "6.0.0-beta.0" + react-router-dom "6.0.0-beta.0" + react-use "^15.3.3" + "@roadiehq/backstage-plugin-github-insights@^0.2.12": version "0.2.12" resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-insights/-/backstage-plugin-github-insights-0.2.12.tgz#aeda6306769f376cf6b5b9d74268b060a0f4e764" From a906f20e7b8070bc58f7807e26e9f9e68753cf31 Mon Sep 17 00:00:00 2001 From: o-farooq Date: Wed, 11 Nov 2020 04:29:45 +1300 Subject: [PATCH 131/252] feat(techradar-plugin) add techradar blip history backend support (#3191) * add techradar entry history data structure * updated names and added changeset * fix docs Co-authored-by: Omer Farooq <17722640+o-farooq@users.noreply.github.com> --- .changeset/wicked-impalas-tan.md | 5 + plugins/tech-radar/src/api.ts | 12 ++- .../src/components/RadarComponent.tsx | 26 ++++- plugins/tech-radar/src/sampleData.ts | 101 +++++++++++++----- plugins/tech-radar/src/utils/types.ts | 8 ++ 5 files changed, 121 insertions(+), 31 deletions(-) create mode 100644 .changeset/wicked-impalas-tan.md diff --git a/.changeset/wicked-impalas-tan.md b/.changeset/wicked-impalas-tan.md new file mode 100644 index 0000000000..8cfe919f5c --- /dev/null +++ b/.changeset/wicked-impalas-tan.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-tech-radar': minor +--- + +Added tech radar blip history backend support and normalized the data structure diff --git a/plugins/tech-radar/src/api.ts b/plugins/tech-radar/src/api.ts index 7a6e790136..0c30c9374c 100644 --- a/plugins/tech-radar/src/api.ts +++ b/plugins/tech-radar/src/api.ts @@ -34,11 +34,17 @@ export interface RadarQuadrant { export interface RadarEntry { key: string; // react key id: string; - moved: MovedState; - quadrant: RadarQuadrant; - ring: RadarRing; + quadrant: string; title: string; url: string; + timeline: Array; +} + +export interface RadarEntrySnapshot { + date: Date; + ringId: string; + description?: string; + moved?: MovedState; } /** diff --git a/plugins/tech-radar/src/components/RadarComponent.tsx b/plugins/tech-radar/src/components/RadarComponent.tsx index 99190202b9..583a7c91e0 100644 --- a/plugins/tech-radar/src/components/RadarComponent.tsx +++ b/plugins/tech-radar/src/components/RadarComponent.tsx @@ -20,6 +20,7 @@ import { useAsync } from 'react-use'; import Radar from '../components/Radar'; import { TechRadarComponentProps, TechRadarLoaderResponse } from '../api'; import getSampleData from '../sampleData'; +import { Entry } from '../utils/types'; const useTechRadarLoader = (props: TechRadarComponentProps) => { const errorApi = useApi(errorApiRef); @@ -47,6 +48,29 @@ const useTechRadarLoader = (props: TechRadarComponentProps) => { const RadarComponent = (props: TechRadarComponentProps): JSX.Element => { const { loading, error, value: data } = useTechRadarLoader(props); + const mapToEntries = ( + loaderResponse: TechRadarLoaderResponse | undefined, + ): Array => { + return loaderResponse!.entries.map(entry => { + return { + id: entry.key, + quadrant: loaderResponse!.quadrants.find(q => q.id === entry.quadrant)!, + title: entry.title, + ring: loaderResponse!.rings.find( + r => r.id === entry.timeline[0].ringId, + )!, + history: entry.timeline.map(e => { + return { + date: e.date, + ring: loaderResponse!.rings.find(a => a.id === e.ringId)!, + description: e.description, + moved: e.moved, + }; + }), + }; + }); + }; + return ( <> {loading && } @@ -55,7 +79,7 @@ const RadarComponent = (props: TechRadarComponentProps): JSX.Element => { {...props} rings={data!.rings} quadrants={data!.quadrants} - entries={data!.entries} + entries={mapToEntries(data)} /> )} diff --git a/plugins/tech-radar/src/sampleData.ts b/plugins/tech-radar/src/sampleData.ts index a9fa7efd7b..914bc30931 100644 --- a/plugins/tech-radar/src/sampleData.ts +++ b/plugins/tech-radar/src/sampleData.ts @@ -35,85 +35,132 @@ quadrants.push({ id: 'process', name: 'Process' }); const entries = new Array(); entries.push({ - moved: 0, - ring: { id: 'use', name: 'USE', color: '#93c47d' }, + timeline: [ + { + moved: 0, + ringId: 'use', + date: new Date('2020-08-06'), + description: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua', + }, + ], url: '#', key: 'javascript', id: 'javascript', title: 'JavaScript', - quadrant: { id: 'languages', name: 'Languages' }, + quadrant: 'languages', }); entries.push({ - moved: 0, - ring: { id: 'use', name: 'USE', color: '#93c47d' }, + timeline: [ + { + moved: 0, + ringId: 'use', + date: new Date('2020-08-06'), + description: + 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat', + }, + ], url: '#', key: 'typescript', id: 'typescript', title: 'TypeScript', - quadrant: { id: 'languages', name: 'Languages' }, + quadrant: 'languages', }); entries.push({ - moved: 0, - ring: { id: 'use', name: 'USE', color: '#93c47d' }, + timeline: [ + { + moved: 0, + ringId: 'use', + date: new Date('2020-08-06'), + }, + ], url: '#', key: 'webpack', id: 'webpack', title: 'Webpack', - quadrant: { id: 'frameworks', name: 'Frameworks' }, + quadrant: 'frameworks', }); entries.push({ - moved: 0, - ring: { id: 'use', name: 'USE', color: '#93c47d' }, + timeline: [ + { + moved: 0, + ringId: 'use', + date: new Date('2020-08-06'), + }, + ], url: '#', key: 'react', id: 'react', title: 'React', - quadrant: { id: 'frameworks', name: 'Frameworks' }, + quadrant: 'frameworks', }); entries.push({ - moved: 0, - ring: { id: 'use', name: 'USE', color: '#93c47d' }, + timeline: [ + { + moved: 0, + ringId: 'use', + date: new Date('2020-08-06'), + }, + ], url: '#', key: 'code-reviews', id: 'code-reviews', title: 'Code Reviews', - quadrant: { id: 'process', name: 'Process' }, + quadrant: 'process', }); entries.push({ - moved: 0, + timeline: [ + { + moved: 0, + ringId: 'assess', + date: new Date('2020-08-06'), + }, + ], url: '#', key: 'mob-programming', id: 'mob-programming', title: 'Mob Programming', - quadrant: { id: 'process', name: 'Process' }, - ring: { id: 'assess', name: 'ASSESS', color: '#fbdb84' }, + quadrant: 'process', }); entries.push({ - moved: 0, + timeline: [ + { + moved: 0, + ringId: 'use', + date: new Date('2020-08-06'), + }, + ], url: '#', key: 'docs-like-code', id: 'docs-like-code', title: 'Docs-like-code', - quadrant: { id: 'process', name: 'Process' }, - ring: { id: 'use', name: 'USE', color: '#93c47d' }, + quadrant: 'process', }); entries.push({ - moved: 0, + timeline: [ + { + ringId: 'hold', + date: new Date('2020-08-06'), + }, + ], url: '#', key: 'force-push', id: 'force-push', title: 'Force push to master', - quadrant: { id: 'process', name: 'Process' }, - ring: { id: 'hold', name: 'HOLD', color: '#93c47d' }, + quadrant: 'process', }); entries.push({ - moved: 0, - ring: { id: 'use', name: 'USE', color: '#93c47d' }, + timeline: [ + { + ringId: 'use', + date: new Date('2020-08-06'), + }, + ], url: '#', key: 'github-actions', id: 'github-actions', title: 'GitHub Actions', - quadrant: { id: 'infrastructure', name: 'Infrastructure' }, + quadrant: 'infrastructure', }); export default function getSampleData(): Promise { diff --git a/plugins/tech-radar/src/utils/types.ts b/plugins/tech-radar/src/utils/types.ts index f3933ee584..3322a67dcb 100644 --- a/plugins/tech-radar/src/utils/types.ts +++ b/plugins/tech-radar/src/utils/types.ts @@ -69,6 +69,14 @@ export type Entry = { // How this entry has recently moved; -1 for "down", +1 for "up", 0 for not moved moved?: MovedState; active?: boolean; + timeline?: Array; +}; + +export type EntrySnapshot = { + date: Date; + ring: Ring; + description?: string; + moved?: MovedState; }; // The same as Entry except quadrant/ring are declared by their string ID instead of being the actual objects From 2fcc82a95cc179c40d702e5e7cf396b295e5cbfa Mon Sep 17 00:00:00 2001 From: Ryan Vazquez Date: Tue, 10 Nov 2020 15:11:35 -0500 Subject: [PATCH 132/252] emphasize divider background use fixed title for project growth alert map product option to entity id in client remove conditional from test name update tooltip verbiage for unlabeled costs add default tooltip tooltip cleanup remove unused palette property --- plugins/cost-insights/src/client.ts | 4 +- .../src/components/BarChart/BarChart.tsx | 23 +++++++++- .../src/components/BarChart/helpers.tsx | 42 ------------------- .../CostGrowth/CostGrowthIndicator.test.tsx | 4 +- .../ProductInsightsChart.tsx | 6 +-- .../ProjectGrowthAlertChart.tsx | 11 +---- .../UnlabeledDataflowAlertCard.tsx | 7 +--- plugins/cost-insights/src/types/Theme.ts | 1 - plugins/cost-insights/src/utils/styles.ts | 5 +-- 9 files changed, 33 insertions(+), 70 deletions(-) delete mode 100644 plugins/cost-insights/src/components/BarChart/helpers.tsx diff --git a/plugins/cost-insights/src/client.ts b/plugins/cost-insights/src/client.ts index 50e925bc28..740a44fcbd 100644 --- a/plugins/cost-insights/src/client.ts +++ b/plugins/cost-insights/src/client.ts @@ -196,7 +196,7 @@ export class ExampleCostInsightsClient implements CostInsightsApi { productInsightsOptions: ProductInsightsOptions, ): Promise { const projectProductInsights = await this.request(productInsightsOptions, { - id: 'project-product', + id: productInsightsOptions.product, aggregation: [80_000, 110_000], change: { ratio: 0.375, @@ -243,7 +243,7 @@ export class ExampleCostInsightsClient implements CostInsightsApi { }); const productInsights: Entity = await this.request(productInsightsOptions, { - id: 'product', + id: productInsightsOptions.product, aggregation: [200_000, 250_000], change: { ratio: 0.2, diff --git a/plugins/cost-insights/src/components/BarChart/BarChart.tsx b/plugins/cost-insights/src/components/BarChart/BarChart.tsx index 30e8ad71f8..93f738a38b 100644 --- a/plugins/cost-insights/src/components/BarChart/BarChart.tsx +++ b/plugins/cost-insights/src/components/BarChart/BarChart.tsx @@ -30,6 +30,8 @@ import { import { Box, useTheme } from '@material-ui/core'; import { BarChartTick } from './BarChartTick'; import { BarChartStepper } from './BarChartStepper'; +import { BarChartTooltip } from './BarChartTooltip'; +import { BarChartTooltipItem } from './BarChartTooltipItem'; import { currencyFormatter } from '../../utils/formatters'; import { BarChartData, @@ -37,8 +39,27 @@ import { DataKey, CostInsightsTheme, } from '../../types'; +import { notEmpty } from '../../utils/assert'; import { useBarChartStyles } from '../../utils/styles'; import { resourceSort } from '../../utils/sort'; +import { isInvalid, titleOf, tooltipItemOf } from '../../utils/graphs'; + +export const defaultTooltip: ContentRenderer = ({ + label, + payload = [], +}) => { + if (isInvalid({ label, payload })) return null; + + const title = titleOf(label); + const items = payload.map(tooltipItemOf).filter(notEmpty); + return ( + + {items.map((item, index) => ( + + ))} + + ); +}; export type BarChartProps = { resources: ResourceData[]; @@ -55,7 +76,7 @@ export const BarChart = ({ responsive = true, displayAmount = 6, options = {}, - tooltip, + tooltip = defaultTooltip, onClick, onMouseMove, }: BarChartProps) => { diff --git a/plugins/cost-insights/src/components/BarChart/helpers.tsx b/plugins/cost-insights/src/components/BarChart/helpers.tsx deleted file mode 100644 index 66389af771..0000000000 --- a/plugins/cost-insights/src/components/BarChart/helpers.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React from 'react'; -import { - ContentRenderer, - TooltipProps as RechartsTooltipProps, -} from 'recharts'; -import { BarChartTooltip } from './BarChartTooltip'; -import { BarChartTooltipItem } from './BarChartTooltipItem'; -import { notEmpty } from '../../utils/assert'; -import { isInvalid, titleOf, tooltipItemOf } from '../../utils/graphs'; - -export const renderDefaultTooltip: ContentRenderer = ({ - label, - payload = [], -}) => { - if (isInvalid({ label, payload })) return null; - - const title = titleOf(label); - const items = payload.map(tooltipItemOf).filter(notEmpty); - return ( - - {items.map((item, index) => ( - - ))} - - ); -}; diff --git a/plugins/cost-insights/src/components/CostGrowth/CostGrowthIndicator.test.tsx b/plugins/cost-insights/src/components/CostGrowth/CostGrowthIndicator.test.tsx index 5aabb09c7d..8be5e238cc 100644 --- a/plugins/cost-insights/src/components/CostGrowth/CostGrowthIndicator.test.tsx +++ b/plugins/cost-insights/src/components/CostGrowth/CostGrowthIndicator.test.tsx @@ -30,9 +30,7 @@ describe.each` ${ChangeThreshold.upper + 0.01} | ${EngineerThreshold} | ${'excess'} ${ChangeThreshold.upper + 0.01} | ${EngineerThreshold + 0.1} | ${'excess'} `('growthOf', ({ ratio, amount, ariaLabel }) => { - it(`should display the correct indicator for ${ - ariaLabel ? ariaLabel : 'negligible' - }`, async () => { + it(`should display the correct indicator for ${ariaLabel}`, async () => { const { getByLabelText } = await renderInTestApp( , ); diff --git a/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsChart.tsx b/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsChart.tsx index 1392b05b34..6050dae9c7 100644 --- a/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsChart.tsx +++ b/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsChart.tsx @@ -144,7 +144,7 @@ export const ProductInsightsChart = ({ const activeEntity = findAlways(entity.entities, e => e.id === id); const ratio = activeEntity.change.ratio; const skus = activeEntity.entities; - const subtitle = `${skus.length} ${pluralOf(skus.length, 'SKU', 'SKUs')}`; + const subtitle = `${skus.length} ${pluralOf(skus.length, 'SKU')}`; if (skus.length) { return ( @@ -179,14 +179,14 @@ export const ProductInsightsChart = ({ topRight={ } content={ id ? null - : `This service is generating costs that can't be attributed to any known entity.` + : "This product has costs that are not labeled and therefore can't be attributed to a specific entity." } > {items.map((item, index) => ( diff --git a/plugins/cost-insights/src/components/ProjectGrowthAlertCard/ProjectGrowthAlertChart.tsx b/plugins/cost-insights/src/components/ProjectGrowthAlertCard/ProjectGrowthAlertChart.tsx index dffed24992..53e52f18e8 100644 --- a/plugins/cost-insights/src/components/ProjectGrowthAlertCard/ProjectGrowthAlertChart.tsx +++ b/plugins/cost-insights/src/components/ProjectGrowthAlertCard/ProjectGrowthAlertChart.tsx @@ -20,7 +20,6 @@ import { Box } from '@material-ui/core'; import { BarChart, BarChartLegend } from '../BarChart'; import { LegendItem } from '../LegendItem'; import { CostGrowth } from '../CostGrowth'; -import { renderDefaultTooltip } from '../BarChart/helpers'; import { BarChartOptions, Duration, ProjectGrowthData } from '../../types'; import { useBarChartLayoutStyles as useStyles } from '../../utils/styles'; import { resourceOf } from '../../utils/graphs'; @@ -46,17 +45,11 @@ export const ProjectGrowthAlertChart = ({ return ( - + - + ); }; diff --git a/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowAlertCard.tsx b/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowAlertCard.tsx index 5384db566c..e56b3b044e 100644 --- a/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowAlertCard.tsx +++ b/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowAlertCard.tsx @@ -18,7 +18,6 @@ import React from 'react'; import { InfoCard } from '@backstage/core'; import { Box } from '@material-ui/core'; import { BarChart, BarChartLegend } from '../BarChart'; -import { renderDefaultTooltip } from '../BarChart/helpers'; import { UnlabeledDataflowData, ResourceData } from '../../types'; import { pluralOf } from '../../utils/grammar'; import { useBarChartLayoutStyles as useStyles } from '../../utils/styles'; @@ -54,11 +53,7 @@ export const UnlabeledDataflowAlertCard = ({ costEnd={alert.unlabeledCost} options={options} /> - + ); diff --git a/plugins/cost-insights/src/types/Theme.ts b/plugins/cost-insights/src/types/Theme.ts index fecaa17abb..9053283351 100644 --- a/plugins/cost-insights/src/types/Theme.ts +++ b/plugins/cost-insights/src/types/Theme.ts @@ -30,7 +30,6 @@ type CostInsightsPaletteAdditions = { tooltip: CostInsightsTooltipOptions; navigationText: string; alertBackground: string; - textSecondary: string; }; export type CostInsightsPalette = BackstagePalette & diff --git a/plugins/cost-insights/src/utils/styles.ts b/plugins/cost-insights/src/utils/styles.ts index 4f8ac7fa98..de743d8459 100644 --- a/plugins/cost-insights/src/utils/styles.ts +++ b/plugins/cost-insights/src/utils/styles.ts @@ -15,6 +15,7 @@ */ import { createStyles, + emphasize, darken, getLuminance, lighten, @@ -400,9 +401,7 @@ export const useTooltipStyles = makeStyles( fontSize: `.75rem`, }, divider: { - /* TODO: tooltip divider color should be the inverse of theme. - Dark mode should appear dark, light mode should appear light */ - backgroundColor: darken(theme.palette.divider, 1), + backgroundColor: emphasize(theme.palette.divider, 1), }, subtitle: { fontStyle: 'italic', From 0703edee09888cb484f84c81a9b5ba2150a4c901 Mon Sep 17 00:00:00 2001 From: Ryan Vazquez Date: Tue, 10 Nov 2020 16:48:27 -0500 Subject: [PATCH 133/252] changeset --- .changeset/cost-insights-sharp-actors-mix.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .changeset/cost-insights-sharp-actors-mix.md diff --git a/.changeset/cost-insights-sharp-actors-mix.md b/.changeset/cost-insights-sharp-actors-mix.md new file mode 100644 index 0000000000..5ae1035612 --- /dev/null +++ b/.changeset/cost-insights-sharp-actors-mix.md @@ -0,0 +1,10 @@ +--- +'@backstage/plugin-cost-insights': minor +--- + +rename: Tooltip -> BarChartTooltip +rename: TooltipItem -> BarChartTooltipItem +Deprecate BarChartData in favor of BarChartOptions +Export BarChartLegend component +Update BarChart props to accept options prop +Deprecate ProductCost type in favor of Entity. Update CostInsightsApi From 9badb8f73731521bb0b1e9eeec43516d6abd1973 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Wed, 11 Nov 2020 09:53:26 +0100 Subject: [PATCH 134/252] TechDocs project: try without having an emoji in the name --- .github/workflows/techdocs-project-board.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/techdocs-project-board.yml b/.github/workflows/techdocs-project-board.yml index c6beb9567c..a2fb980259 100644 --- a/.github/workflows/techdocs-project-board.yml +++ b/.github/workflows/techdocs-project-board.yml @@ -25,7 +25,7 @@ jobs: contains(github.event.issue.title, 'Techdocs') with: project: 'https://github.com/orgs/backstage/projects/1' - column_name: 'Incoming 📨' + column_name: 'Incoming' - name: Assign new issue to Incoming based on its label. uses: srggrs/assign-one-project-github-action@1.2.0 @@ -33,7 +33,7 @@ jobs: contains(github.event.issue.labels.*.name, 'docs-like-code') with: project: 'https://github.com/orgs/backstage/projects/1' - column_name: 'Incoming 📨' + column_name: 'Incoming' - name: Assign new PR to Incoming based on its title. uses: srggrs/assign-one-project-github-action@1.2.0 @@ -43,7 +43,7 @@ jobs: contains(github.event.pull_request.title, 'Techdocs') with: project: 'https://github.com/orgs/backstage/projects/1' - column_name: 'Incoming 📨' + column_name: 'Incoming' - name: Assign new PR to Incoming based on its label. uses: srggrs/assign-one-project-github-action@1.2.0 @@ -51,4 +51,4 @@ jobs: contains(github.event.pull_request.labels.*.name, 'docs-like-code') with: project: 'https://github.com/orgs/backstage/projects/1' - column_name: 'Incoming 📨' + column_name: 'Incoming' From c3e8426a91ef8c413e0116490bd193eb36385176 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Wed, 11 Nov 2020 13:03:19 +0100 Subject: [PATCH 135/252] Improve sonarqube readme The wrong name here leads to confusion. --- plugins/sonarqube/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/sonarqube/README.md b/plugins/sonarqube/README.md index af18017359..1c8d5a6a0a 100644 --- a/plugins/sonarqube/README.md +++ b/plugins/sonarqube/README.md @@ -77,7 +77,7 @@ sonarQube: 5. Get and provide `SONARQUBE_AUTH_HEADER` as env variable (https://sonarcloud.io/account/security or https://docs.sonarqube.org/latest/user-guide/user-token/) -6. Add the `sonarqube.org/project-key` annotation to your component-info.yaml file: +6. Add the `sonarqube.org/project-key` annotation to your catalog-info.yaml file: ```yaml apiVersion: backstage.io/v1alpha1 From 4a9d666a329056eea954455215bea93511b1f565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 11 Nov 2020 13:31:08 +0100 Subject: [PATCH 136/252] catalog: more info about refresh errors (#3272) We sometimes get errors like `Cannot read property 'toLowerCase' of undefined` which really doesn't help debugging. So log the error stack instead of just the message, and also make the rules enforcer (which is the very first code that's hit by newly read data) more defensive. The latter is needed because no validation has been performed on the entity data yet, so if the user mistakenly inserted bad data, the code will crash instead of just rejecting the data as invalid. --- plugins/catalog-backend/src/ingestion/CatalogRules.ts | 6 +++--- .../catalog-backend/src/ingestion/HigherOrderOperations.ts | 4 ++-- plugins/catalog-backend/src/ingestion/LocationReaders.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/catalog-backend/src/ingestion/CatalogRules.ts b/plugins/catalog-backend/src/ingestion/CatalogRules.ts index e82699f526..b112e32ef5 100644 --- a/plugins/catalog-backend/src/ingestion/CatalogRules.ts +++ b/plugins/catalog-backend/src/ingestion/CatalogRules.ts @@ -147,10 +147,10 @@ export class CatalogRulesEnforcer { } for (const matcher of matchers) { - if (matcher.type !== location.type) { + if (matcher.type !== location?.type) { continue; } - if (matcher.target && matcher.target !== location.target) { + if (matcher.target && matcher.target !== location?.target) { continue; } return true; @@ -165,7 +165,7 @@ export class CatalogRulesEnforcer { } for (const matcher of matchers) { - if (entity.kind.toLowerCase() !== matcher.kind.toLowerCase()) { + if (entity?.kind?.toLowerCase() !== matcher.kind.toLowerCase()) { continue; } diff --git a/plugins/catalog-backend/src/ingestion/HigherOrderOperations.ts b/plugins/catalog-backend/src/ingestion/HigherOrderOperations.ts index f352e8d3fd..bc277e1767 100644 --- a/plugins/catalog-backend/src/ingestion/HigherOrderOperations.ts +++ b/plugins/catalog-backend/src/ingestion/HigherOrderOperations.ts @@ -130,7 +130,7 @@ export class HigherOrderOperations implements HigherOrderOperation { await this.locationsCatalog.logUpdateSuccess(location.id, undefined); } catch (e) { this.logger.warn( - `Failed to refresh location ${location.type}:${location.target}, ${e}`, + `Failed to refresh location ${location.type}:${location.target}, ${e.stack}`, ); await this.locationsCatalog.logUpdateFailure(location.id, e); } @@ -152,7 +152,7 @@ export class HigherOrderOperations implements HigherOrderOperation { for (const item of readerOutput.errors) { this.logger.warn( - `Failed item in location ${item.location.type}:${item.location.target}, ${item.error}`, + `Failed item in location ${item.location.type}:${item.location.target}, ${item.error.stack}`, ); } diff --git a/plugins/catalog-backend/src/ingestion/LocationReaders.ts b/plugins/catalog-backend/src/ingestion/LocationReaders.ts index 8031b8ff0d..a9248bb3fb 100644 --- a/plugins/catalog-backend/src/ingestion/LocationReaders.ts +++ b/plugins/catalog-backend/src/ingestion/LocationReaders.ts @@ -97,7 +97,7 @@ export class LocationReaders implements LocationReader { output.errors.push({ location: item.location, error: new Error( - `Entity of kind ${item.entity.kind} is not allowed from location ${item.location.target}:${item.location.type}`, + `Entity of kind ${item.entity.kind} is not allowed from location ${item.location.type} ${item.location.target}`, ), }); } @@ -117,7 +117,7 @@ export class LocationReaders implements LocationReader { items = newItems; } - const message = `Max recursion depth ${MAX_DEPTH} reached for ${location.type} ${location.target}`; + const message = `Max recursion depth ${MAX_DEPTH} reached for location ${location.type} ${location.target}`; logger.warn(message); output.errors.push({ location, error: new Error(message) }); return output; From 84b654d5de3b15c2c521bea723ed679979012599 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Wed, 11 Nov 2020 13:34:40 +0100 Subject: [PATCH 137/252] Use dense table style and outlined chips in the API Explorer (#3276) This matches the styling of the catalog. --- .changeset/perfect-boats-attack.md | 5 +++++ .../src/components/ApiExplorerTable/ApiExplorerTable.tsx | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 .changeset/perfect-boats-attack.md diff --git a/.changeset/perfect-boats-attack.md b/.changeset/perfect-boats-attack.md new file mode 100644 index 0000000000..51c327127a --- /dev/null +++ b/.changeset/perfect-boats-attack.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-api-docs': patch +--- + +Use dense table style and outlined chips in the API Explorer. diff --git a/plugins/api-docs/src/components/ApiExplorerTable/ApiExplorerTable.tsx b/plugins/api-docs/src/components/ApiExplorerTable/ApiExplorerTable.tsx index e12d172e41..9222623aed 100644 --- a/plugins/api-docs/src/components/ApiExplorerTable/ApiExplorerTable.tsx +++ b/plugins/api-docs/src/components/ApiExplorerTable/ApiExplorerTable.tsx @@ -90,7 +90,13 @@ const columns: TableColumn[] = [ <> {entity.metadata.tags && entity.metadata.tags.map(t => ( - + ))} ), @@ -149,6 +155,7 @@ export const ApiExplorerTable = ({ paging: false, actionsColumnIndex: -1, loadingType: 'linear', + padding: 'dense', showEmptyDataSourceMessage: !loading, }} data={entities} From 9090739a53b6e60f3474271b57b46ae2720e0a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 11 Nov 2020 14:42:08 +0100 Subject: [PATCH 138/252] storybook: fix broken stories that lack a router parent (#3273) --- .../ProgressBars/GaugeCard.stories.tsx | 23 ++++-- .../src/layout/InfoCard/InfoCard.stories.tsx | 17 +++-- .../core/src/layout/Page/Page.stories.tsx | 53 +++++++------- .../layout/TabbedCard/TabbedCard.stories.tsx | 71 +++++++++++-------- 4 files changed, 94 insertions(+), 70 deletions(-) diff --git a/packages/core/src/components/ProgressBars/GaugeCard.stories.tsx b/packages/core/src/components/ProgressBars/GaugeCard.stories.tsx index 45de49cd2d..982999f8d6 100644 --- a/packages/core/src/components/ProgressBars/GaugeCard.stories.tsx +++ b/packages/core/src/components/ProgressBars/GaugeCard.stories.tsx @@ -14,19 +14,28 @@ * limitations under the License. */ -import React from 'react'; +import React, { PropsWithChildren } from 'react'; import { GaugeCard } from './GaugeCard'; import { Grid } from '@material-ui/core'; +import { MemoryRouter } from 'react-router'; const linkInfo = { title: 'Go to XYZ Location', link: '#' }; +const Wrapper = ({ children }: PropsWithChildren<{}>) => ( + + + {children} + + +); + export default { title: 'Data Display/Progress Card', component: GaugeCard, }; export const Default = () => ( - + @@ -36,11 +45,11 @@ export const Default = () => ( - + ); export const Subhead = () => ( - + @@ -58,11 +67,11 @@ export const Subhead = () => ( progress={0.89} /> - + ); export const LinkInFooter = () => ( - + @@ -72,5 +81,5 @@ export const LinkInFooter = () => ( - + ); diff --git a/packages/core/src/layout/InfoCard/InfoCard.stories.tsx b/packages/core/src/layout/InfoCard/InfoCard.stories.tsx index 4886a38b0f..0c3c523ba0 100644 --- a/packages/core/src/layout/InfoCard/InfoCard.stories.tsx +++ b/packages/core/src/layout/InfoCard/InfoCard.stories.tsx @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, { FC } from 'react'; -import { InfoCard } from '.'; import { Grid, Typography } from '@material-ui/core'; +import React, { PropsWithChildren } from 'react'; +import { MemoryRouter } from 'react-router'; +import { InfoCard } from '.'; const linkInfo = { title: 'Go to XYZ Location', link: '#' }; @@ -35,12 +36,14 @@ const text = ( ); -const Wrapper: FC<{}> = ({ children }) => ( - - - {children} +const Wrapper = ({ children }: PropsWithChildren<{}>) => ( + + + + {children} + - + ); export const Default = () => ( diff --git a/packages/core/src/layout/Page/Page.stories.tsx b/packages/core/src/layout/Page/Page.stories.tsx index 1d3aaea412..1fdaf8eced 100644 --- a/packages/core/src/layout/Page/Page.stories.tsx +++ b/packages/core/src/layout/Page/Page.stories.tsx @@ -14,25 +14,26 @@ * limitations under the License. */ +import { Box, Chip, Grid, Link, Typography } from '@material-ui/core'; import React, { useState } from 'react'; +import { MemoryRouter } from 'react-router'; import { - Header, - Page, - HeaderLabel, - ContentHeader, Content, - InfoCard, + ContentHeader, + Header, + HeaderLabel, HeaderTabs, + InfoCard, + Page, } from '../'; import { + GaugeCard, + StatusOK, SupportButton, Table, - StatusOK, TableColumn, - GaugeCard, TrendLine, } from '../../components'; -import { Box, Typography, Link, Chip, Grid } from '@material-ui/core'; export default { title: 'Plugins/Examples', @@ -194,23 +195,25 @@ const ExampleContentHeader = ({ selectedTab }: { selectedTab?: number }) => ( export const PluginWithData = () => { const [selectedTab, setSelectedTab] = useState(2); return ( -
- - - setSelectedTab(index)} - tabs={tabs.map(({ label }, index) => ({ - id: index.toString(), - label, - }))} - /> - - - - - -
+ +
+ + + setSelectedTab(index)} + tabs={tabs.map(({ label }, index) => ({ + id: index.toString(), + label, + }))} + /> + + + + + +
+
); }; diff --git a/packages/core/src/layout/TabbedCard/TabbedCard.stories.tsx b/packages/core/src/layout/TabbedCard/TabbedCard.stories.tsx index f4af6dcf9f..65c73b190a 100644 --- a/packages/core/src/layout/TabbedCard/TabbedCard.stories.tsx +++ b/packages/core/src/layout/TabbedCard/TabbedCard.stories.tsx @@ -13,12 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, { useState } from 'react'; +import React, { useState, PropsWithChildren } from 'react'; import { TabbedCard, CardTab } from '.'; import { Grid } from '@material-ui/core'; +import { MemoryRouter } from 'react-router'; const cardContentStyle = { height: 200, width: 500 }; +const Wrapper = ({ children }: PropsWithChildren<{}>) => ( + {children} +); + export default { title: 'Layout/Tabbed Card', component: TabbedCard, @@ -33,20 +38,22 @@ export default { export const Default = () => { return ( - - -
Some content
-
- -
Some content 2
-
- -
Some content 3
-
- -
Some content 4
-
-
+ + + +
Some content
+
+ +
Some content 2
+
+ +
Some content 3
+
+ +
Some content 4
+
+
+
); }; @@ -54,20 +61,22 @@ const linkInfo = { title: 'Go to XYZ Location', link: '#' }; export const WithFooterLink = () => { return ( - - -
Some content
-
- -
Some content 2
-
- -
Some content 3
-
- -
Some content 4
-
-
+ + + +
Some content
+
+ +
Some content 2
+
+ +
Some content 3
+
+ +
Some content 4
+
+
+
); }; @@ -78,7 +87,7 @@ export const WithControlledTabValue = () => { setSelectedTab(newSelectedTab); return ( - <> + Selected tab is {selectedTab} {
Some content 4
- +
); }; From c250dfe2936f1b2f9b608ff7a7c00c91eb98930a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 11 Nov 2020 18:27:27 +0100 Subject: [PATCH 139/252] Update README.md (#3278) --- plugins/sonarqube/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/sonarqube/README.md b/plugins/sonarqube/README.md index 1c8d5a6a0a..28bf196ea5 100644 --- a/plugins/sonarqube/README.md +++ b/plugins/sonarqube/README.md @@ -9,6 +9,8 @@ The SonarQube Plugin displays code statistics from [SonarCloud](https://sonarclo 1. Install the SonarQube Plugin: ```bash +# packages/app + yarn add @backstage/plugin-sonarqube ``` @@ -43,7 +45,7 @@ const OverviewContent = ({ entity }: { entity: Entity }) => ( **SonarCloud** ```yaml -// app-config.yaml +# app-config.yaml proxy: '/sonarqube': @@ -59,7 +61,7 @@ proxy: **SonarQube** ```yaml -// app-config.yaml +# app-config.yaml proxy: '/sonarqube': From 46287639982a046aeeec3e610de73265540fb5ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 12 Nov 2020 11:43:35 +0100 Subject: [PATCH 140/252] auth-backend: Encode the OAuth state param using URL safe chars (#3281) --- .changeset/tidy-bobcats-prove.md | 5 +++++ plugins/auth-backend/src/lib/oauth/helpers.ts | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/tidy-bobcats-prove.md diff --git a/.changeset/tidy-bobcats-prove.md b/.changeset/tidy-bobcats-prove.md new file mode 100644 index 0000000000..ad73d547e0 --- /dev/null +++ b/.changeset/tidy-bobcats-prove.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Encode the OAuth state parameter using URL safe chars only, so that providers have an easier time forming the callback URL. diff --git a/plugins/auth-backend/src/lib/oauth/helpers.ts b/plugins/auth-backend/src/lib/oauth/helpers.ts index 9f250a0285..bc6fcf1004 100644 --- a/plugins/auth-backend/src/lib/oauth/helpers.ts +++ b/plugins/auth-backend/src/lib/oauth/helpers.ts @@ -19,7 +19,7 @@ import { OAuthState } from './types'; export const readState = (stateString: string): OAuthState => { const state = Object.fromEntries( - new URLSearchParams(decodeURIComponent(stateString)), + new URLSearchParams(Buffer.from(stateString, 'hex').toString('utf-8')), ); if ( !state.nonce || @@ -40,7 +40,7 @@ export const encodeState = (state: OAuthState): string => { searchParams.append('nonce', state.nonce); searchParams.append('env', state.env); - return encodeURIComponent(searchParams.toString()); + return Buffer.from(searchParams.toString(), 'utf-8').toString('hex'); }; export const verifyNonce = (req: express.Request, providerId: string) => { From 752808090be59a99ee835463dc69c5e81df5e931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 12 Nov 2020 11:48:46 +0100 Subject: [PATCH 141/252] auth-backend: Handle non-7-bit safe characters in the posted message data (#3280) --- .changeset/modern-islands-joke.md | 5 ++ .../src/lib/flow/authFlowHelpers.test.ts | 58 +++++++++++++++---- .../src/lib/flow/authFlowHelpers.ts | 33 ++++++----- 3 files changed, 72 insertions(+), 24 deletions(-) create mode 100644 .changeset/modern-islands-joke.md diff --git a/.changeset/modern-islands-joke.md b/.changeset/modern-islands-joke.md new file mode 100644 index 0000000000..5737c4f762 --- /dev/null +++ b/.changeset/modern-islands-joke.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Handle non-7-bit safe characters in the posted message data diff --git a/plugins/auth-backend/src/lib/flow/authFlowHelpers.test.ts b/plugins/auth-backend/src/lib/flow/authFlowHelpers.test.ts index f42e4c4270..6c49eaf351 100644 --- a/plugins/auth-backend/src/lib/flow/authFlowHelpers.test.ts +++ b/plugins/auth-backend/src/lib/flow/authFlowHelpers.test.ts @@ -15,10 +15,20 @@ */ import express from 'express'; -import { ensuresXRequestedWith, postMessageResponse } from './authFlowHelpers'; +import { + safelyEncodeURIComponent, + ensuresXRequestedWith, + postMessageResponse, +} from './authFlowHelpers'; import { WebMessageResponse } from './types'; describe('oauth helpers', () => { + describe('safelyEncodeURIComponent', () => { + it('encodes all occurrences of single quotes', () => { + expect(safelyEncodeURIComponent("a'ö'b")).toBe('a%27%C3%B6%27b'); + }); + }); + describe('postMessageResponse', () => { const appOrigin = 'http://localhost:3000'; it('should post a message back with payload success', () => { @@ -45,15 +55,12 @@ describe('oauth helpers', () => { }, }, }; - const jsonData = JSON.stringify(data); - const base64Data = Buffer.from(jsonData, 'utf8').toString('base64'); + const encoded = safelyEncodeURIComponent(JSON.stringify(data)); postMessageResponse(mockResponse, appOrigin, data); expect(mockResponse.setHeader).toBeCalledTimes(3); expect(mockResponse.end).toBeCalledTimes(1); - expect(mockResponse.end).toBeCalledWith( - expect.stringContaining(base64Data), - ); + expect(mockResponse.end).toBeCalledWith(expect.stringContaining(encoded)); }); it('should post a message back with payload error', () => { @@ -64,16 +71,47 @@ describe('oauth helpers', () => { const data: WebMessageResponse = { type: 'authorization_response', - error: new Error('Unknown error occured'), + error: new Error('Unknown error occurred'), + }; + const encoded = safelyEncodeURIComponent(JSON.stringify(data)); + + postMessageResponse(mockResponse, appOrigin, data); + expect(mockResponse.setHeader).toBeCalledTimes(3); + expect(mockResponse.end).toBeCalledTimes(1); + expect(mockResponse.end).toBeCalledWith(expect.stringContaining(encoded)); + }); + + it('handles single quotes and unicode chars safely', () => { + const mockResponse = ({ + end: jest.fn().mockReturnThis(), + setHeader: jest.fn().mockReturnThis(), + } as unknown) as express.Response; + + const data: WebMessageResponse = { + type: 'authorization_response', + response: { + providerInfo: { + accessToken: 'ACCESS_TOKEN', + idToken: 'ID_TOKEN', + expiresInSeconds: 10, + scope: 'email', + }, + profile: { + email: 'foo@bar.com', + displayName: "Adam l'Hôpital", + }, + backstageIdentity: { + id: 'a', + idToken: 'a.b.c', + }, + }, }; - const jsonData = JSON.stringify(data); - const base64Data = Buffer.from(jsonData, 'utf8').toString('base64'); postMessageResponse(mockResponse, appOrigin, data); expect(mockResponse.setHeader).toBeCalledTimes(3); expect(mockResponse.end).toBeCalledTimes(1); expect(mockResponse.end).toBeCalledWith( - expect.stringContaining(base64Data), + expect.stringContaining('Adam%20l%27H%C3%B4pital'), ); }); }); diff --git a/plugins/auth-backend/src/lib/flow/authFlowHelpers.ts b/plugins/auth-backend/src/lib/flow/authFlowHelpers.ts index 63d7c28b50..d2a9557a28 100644 --- a/plugins/auth-backend/src/lib/flow/authFlowHelpers.ts +++ b/plugins/auth-backend/src/lib/flow/authFlowHelpers.ts @@ -18,37 +18,42 @@ import express from 'express'; import crypto from 'crypto'; import { WebMessageResponse } from './types'; +export const safelyEncodeURIComponent = (value: string) => { + // Note the g at the end of the regex; all occurrences of single quotes must + // be replaced, which encodeURIComponent does not do itself by default + return encodeURIComponent(value).replace(/'/g, '%27'); +}; + export const postMessageResponse = ( res: express.Response, appOrigin: string, response: WebMessageResponse, ) => { const jsonData = JSON.stringify(response); - const base64Data = Buffer.from(jsonData, 'utf8').toString('base64'); + const base64Data = safelyEncodeURIComponent(jsonData); + const base64Origin = safelyEncodeURIComponent(appOrigin); - res.setHeader('Content-Type', 'text/html'); - res.setHeader('X-Frame-Options', 'sameorigin'); + // NOTE: It is absolutely imperative that we use the safe encoder above, to + // be sure that the js code below does not allow the injection of malicious + // data. // TODO: Make target app origin configurable globally const script = ` - (window.opener || window.parent).postMessage(JSON.parse(atob('${base64Data}')), '${appOrigin}') - window.close() + var json = decodeURIComponent('${base64Data}'); + var origin = decodeURIComponent('${base64Origin}'); + (window.opener || window.parent).postMessage(JSON.parse(json), origin); + window.close(); `; const hash = crypto.createHash('sha256').update(script).digest('base64'); - res.setHeader('Content-Security-Policy', `script-src 'sha256-${hash}'`); - res.end(` - - - - - - `); + res.setHeader('Content-Type', 'text/html'); + res.setHeader('X-Frame-Options', 'sameorigin'); + res.setHeader('Content-Security-Policy', `script-src 'sha256-${hash}'`); + res.end(``); }; export const ensuresXRequestedWith = (req: express.Request) => { const requiredHeader = req.header('X-Requested-With'); - if (!requiredHeader || requiredHeader !== 'XMLHttpRequest') { return false; } From 76a8099029c79ec43dddff05258ff7289370fc4f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 12 Nov 2020 10:52:23 +0000 Subject: [PATCH 142/252] Version Packages --- .changeset/2863.md | 29 ----------- .changeset/3157.md | 16 ------ .changeset/3194.md | 7 --- .changeset/calm-emus-push.md | 5 -- .changeset/cost-insights-quick-suits-brake.md | 5 -- .changeset/cost-insights-sharp-actors-mix.md | 10 ---- .changeset/dry-pillows-carry.md | 6 --- .changeset/fluffy-dragons-cry.md | 5 -- .changeset/lucky-lemons-return.md | 5 -- .changeset/modern-islands-joke.md | 5 -- .changeset/moody-pugs-smoke.md | 5 -- .changeset/odd-parents-push.md | 5 -- .changeset/perfect-boats-attack.md | 5 -- .changeset/seven-monkeys-run.md | 5 -- .changeset/shaggy-bobcats-warn.md | 5 -- .changeset/silent-dodos-repeat.md | 5 -- .changeset/smooth-hairs-breathe.md | 5 -- .changeset/spicy-bananas-help.md | 5 -- .changeset/strong-moose-cough.md | 5 -- .changeset/thick-lobsters-swim.md | 5 -- .changeset/tidy-bobcats-prove.md | 5 -- .changeset/weak-otters-love.md | 16 ------ .changeset/weak-rice-beam.md | 5 -- .changeset/wicked-impalas-tan.md | 5 -- packages/app/CHANGELOG.md | 48 ++++++++++++++++++ packages/app/package.json | 50 +++++++++---------- packages/backend-common/CHANGELOG.md | 6 +++ packages/backend-common/package.json | 2 +- packages/backend/CHANGELOG.md | 13 +++++ packages/backend/package.json | 10 ++-- packages/cli/package.json | 8 +-- packages/core-api/CHANGELOG.md | 33 ++++++++++++ packages/core-api/package.json | 4 +- packages/core/CHANGELOG.md | 35 +++++++++++++ packages/core/package.json | 6 +-- packages/create-app/CHANGELOG.md | 6 +++ packages/create-app/package.json | 34 ++++++------- packages/dev-utils/CHANGELOG.md | 15 ++++++ packages/dev-utils/package.json | 6 +-- packages/theme/CHANGELOG.md | 6 +++ packages/theme/package.json | 2 +- plugins/api-docs/CHANGELOG.md | 20 ++++++++ plugins/api-docs/package.json | 10 ++-- plugins/auth-backend/CHANGELOG.md | 9 ++++ plugins/auth-backend/package.json | 4 +- plugins/catalog/CHANGELOG.md | 19 +++++++ plugins/catalog/package.json | 12 ++--- plugins/circleci/CHANGELOG.md | 17 +++++++ plugins/circleci/package.json | 10 ++-- plugins/cloudbuild/CHANGELOG.md | 17 +++++++ plugins/cloudbuild/package.json | 10 ++-- plugins/cost-insights/CHANGELOG.md | 25 ++++++++++ plugins/cost-insights/package.json | 8 +-- plugins/explore/CHANGELOG.md | 15 ++++++ plugins/explore/package.json | 8 +-- plugins/gcp-projects/CHANGELOG.md | 15 ++++++ plugins/gcp-projects/package.json | 8 +-- plugins/github-actions/CHANGELOG.md | 19 +++++++ plugins/github-actions/package.json | 12 ++--- plugins/gitops-profiles/CHANGELOG.md | 15 ++++++ plugins/gitops-profiles/package.json | 8 +-- plugins/graphiql/CHANGELOG.md | 15 ++++++ plugins/graphiql/package.json | 8 +-- plugins/jenkins/CHANGELOG.md | 21 ++++++++ plugins/jenkins/package.json | 10 ++-- plugins/kubernetes/CHANGELOG.md | 15 ++++++ plugins/kubernetes/package.json | 8 +-- plugins/lighthouse/CHANGELOG.md | 19 +++++++ plugins/lighthouse/package.json | 12 ++--- plugins/newrelic/CHANGELOG.md | 15 ++++++ plugins/newrelic/package.json | 8 +-- plugins/register-component/CHANGELOG.md | 17 +++++++ plugins/register-component/package.json | 10 ++-- plugins/rollbar/CHANGELOG.md | 17 +++++++ plugins/rollbar/package.json | 10 ++-- plugins/scaffolder-backend/CHANGELOG.md | 21 ++++++++ plugins/scaffolder-backend/package.json | 4 +- plugins/scaffolder/CHANGELOG.md | 31 ++++++++++++ plugins/scaffolder/package.json | 10 ++-- plugins/sentry/CHANGELOG.md | 15 ++++++ plugins/sentry/package.json | 8 +-- plugins/sonarqube/CHANGELOG.md | 15 ++++++ plugins/sonarqube/package.json | 8 +-- plugins/tech-radar/CHANGELOG.md | 20 ++++++++ plugins/tech-radar/package.json | 8 +-- plugins/techdocs/CHANGELOG.md | 19 +++++++ plugins/techdocs/package.json | 12 ++--- plugins/user-settings/CHANGELOG.md | 16 ++++++ plugins/user-settings/package.json | 8 +-- plugins/welcome/CHANGELOG.md | 15 ++++++ plugins/welcome/package.json | 8 +-- 91 files changed, 776 insertions(+), 346 deletions(-) delete mode 100644 .changeset/2863.md delete mode 100644 .changeset/3157.md delete mode 100644 .changeset/3194.md delete mode 100644 .changeset/calm-emus-push.md delete mode 100644 .changeset/cost-insights-quick-suits-brake.md delete mode 100644 .changeset/cost-insights-sharp-actors-mix.md delete mode 100644 .changeset/dry-pillows-carry.md delete mode 100644 .changeset/fluffy-dragons-cry.md delete mode 100644 .changeset/lucky-lemons-return.md delete mode 100644 .changeset/modern-islands-joke.md delete mode 100644 .changeset/moody-pugs-smoke.md delete mode 100644 .changeset/odd-parents-push.md delete mode 100644 .changeset/perfect-boats-attack.md delete mode 100644 .changeset/seven-monkeys-run.md delete mode 100644 .changeset/shaggy-bobcats-warn.md delete mode 100644 .changeset/silent-dodos-repeat.md delete mode 100644 .changeset/smooth-hairs-breathe.md delete mode 100644 .changeset/spicy-bananas-help.md delete mode 100644 .changeset/strong-moose-cough.md delete mode 100644 .changeset/thick-lobsters-swim.md delete mode 100644 .changeset/tidy-bobcats-prove.md delete mode 100644 .changeset/weak-otters-love.md delete mode 100644 .changeset/weak-rice-beam.md delete mode 100644 .changeset/wicked-impalas-tan.md diff --git a/.changeset/2863.md b/.changeset/2863.md deleted file mode 100644 index 729e3aab7b..0000000000 --- a/.changeset/2863.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -'@backstage/core-api': patch ---- - -Updated the AuthApi `.create` methods to configure the default scope of the corresponding Auth Api. As a result the -default scope is configurable when overwriting the Core Api in the app. - -``` -GithubAuth.create({ - discoveryApi, - oauthRequestApi, - defaultScopes: ['read:user', 'repo'], -}), -``` - -Replaced redundant CreateOptions of each Auth Api with the OAuthApiCreateOptions type. - -``` -export type OAuthApiCreateOptions = AuthApiCreateOptions & { - oauthRequestApi: OAuthRequestApi; - defaultScopes?: string[]; -}; - -export type AuthApiCreateOptions = { - discoveryApi: DiscoveryApi; - environment?: string; - provider?: AuthProvider & { id: string }; -}; -``` diff --git a/.changeset/3157.md b/.changeset/3157.md deleted file mode 100644 index f15d05c46a..0000000000 --- a/.changeset/3157.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -'@backstage/core': patch ---- - -Adds the MarkdownContent component to render and display Markdown content with the default -[GFM](https://github.github.com/gfm/) (Github flavored Markdown) dialect. - -``` - -``` - -To render the Markdown content with plain [CommonMark](https://commonmark.org/), set the dialect to `common-mark` - -``` - BarChartTooltip -rename: TooltipItem -> BarChartTooltipItem -Deprecate BarChartData in favor of BarChartOptions -Export BarChartLegend component -Update BarChart props to accept options prop -Deprecate ProductCost type in favor of Entity. Update CostInsightsApi diff --git a/.changeset/dry-pillows-carry.md b/.changeset/dry-pillows-carry.md deleted file mode 100644 index 5352e26d2d..0000000000 --- a/.changeset/dry-pillows-carry.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/core': patch ---- - -- The BottomLink is now able to handle with internal routes. -- @backstage/core Link component detect whether it's an external link or not, and render accordingly diff --git a/.changeset/fluffy-dragons-cry.md b/.changeset/fluffy-dragons-cry.md deleted file mode 100644 index 32a202b417..0000000000 --- a/.changeset/fluffy-dragons-cry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': patch ---- - -Extend the table to share its current filter state. The filter state can be used together with the new `useQueryParamState` hook to store the current filter state to the browser history and restore it after navigating to other routes. diff --git a/.changeset/lucky-lemons-return.md b/.changeset/lucky-lemons-return.md deleted file mode 100644 index 49125d31d4..0000000000 --- a/.changeset/lucky-lemons-return.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Fix missing api-docs plugin registration in app template diff --git a/.changeset/modern-islands-joke.md b/.changeset/modern-islands-joke.md deleted file mode 100644 index 5737c4f762..0000000000 --- a/.changeset/modern-islands-joke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': patch ---- - -Handle non-7-bit safe characters in the posted message data diff --git a/.changeset/moody-pugs-smoke.md b/.changeset/moody-pugs-smoke.md deleted file mode 100644 index 3092ec4bfe..0000000000 --- a/.changeset/moody-pugs-smoke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-user-settings': patch ---- - -Export `AuthProviders`, `DefaultProviderSettings` and `ProviderSettingsItem`. diff --git a/.changeset/odd-parents-push.md b/.changeset/odd-parents-push.md deleted file mode 100644 index 5d997b7a45..0000000000 --- a/.changeset/odd-parents-push.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-api-docs': patch ---- - -Persist table state of the API Explorer to the browser history. This allows to navigate between pages and come back to the previous filter state. diff --git a/.changeset/perfect-boats-attack.md b/.changeset/perfect-boats-attack.md deleted file mode 100644 index 51c327127a..0000000000 --- a/.changeset/perfect-boats-attack.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-api-docs': patch ---- - -Use dense table style and outlined chips in the API Explorer. diff --git a/.changeset/seven-monkeys-run.md b/.changeset/seven-monkeys-run.md deleted file mode 100644 index 7e66daa86f..0000000000 --- a/.changeset/seven-monkeys-run.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': patch ---- - -Make the selected state of Select and CheckboxTree controllable from outside. diff --git a/.changeset/shaggy-bobcats-warn.md b/.changeset/shaggy-bobcats-warn.md deleted file mode 100644 index 048e1a0ff6..0000000000 --- a/.changeset/shaggy-bobcats-warn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': minor ---- - -New DependencyGraph component added to core package. diff --git a/.changeset/silent-dodos-repeat.md b/.changeset/silent-dodos-repeat.md deleted file mode 100644 index 04978406f3..0000000000 --- a/.changeset/silent-dodos-repeat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': patch ---- - -Fix divider prop not respected on InfoCard diff --git a/.changeset/smooth-hairs-breathe.md b/.changeset/smooth-hairs-breathe.md deleted file mode 100644 index 2849a1d4b7..0000000000 --- a/.changeset/smooth-hairs-breathe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-tech-radar': patch ---- - -Make the footer color of the tech-radar work in both light and dark theme. diff --git a/.changeset/spicy-bananas-help.md b/.changeset/spicy-bananas-help.md deleted file mode 100644 index 6d847f4bc3..0000000000 --- a/.changeset/spicy-bananas-help.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-api-docs': patch ---- - -Upgrade @kyma-project/asyncapi-react to 0.14.2 diff --git a/.changeset/strong-moose-cough.md b/.changeset/strong-moose-cough.md deleted file mode 100644 index 41a0da7cca..0000000000 --- a/.changeset/strong-moose-cough.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/theme': patch ---- - -Improve styling of outlined chips in dark mode. diff --git a/.changeset/thick-lobsters-swim.md b/.changeset/thick-lobsters-swim.md deleted file mode 100644 index 3a377b7535..0000000000 --- a/.changeset/thick-lobsters-swim.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog': patch ---- - -Improved the edit link to open the component yaml in edit mode in corresponding SCM. Broke out logic for createEditLink to be reused. diff --git a/.changeset/tidy-bobcats-prove.md b/.changeset/tidy-bobcats-prove.md deleted file mode 100644 index ad73d547e0..0000000000 --- a/.changeset/tidy-bobcats-prove.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': patch ---- - -Encode the OAuth state parameter using URL safe chars only, so that providers have an easier time forming the callback URL. diff --git a/.changeset/weak-otters-love.md b/.changeset/weak-otters-love.md deleted file mode 100644 index 2c273e067b..0000000000 --- a/.changeset/weak-otters-love.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -'@backstage/plugin-scaffolder': minor -'@backstage/plugin-scaffolder-backend': minor ---- - -`createRouter` of scaffolder backend will now require additional option as `entityClient` which could be generated by `CatalogEntityClient` in `plugin-scaffolder-backend` package. Here is example to generate `entityClient`. - -```js -import { CatalogEntityClient } from '@backstage/plugin-scaffolder-backend'; -import { SingleHostDiscovery } from '@backstage/backend-common'; - -const discovery = SingleHostDiscovery.fromConfig(config); -const entityClient = new CatalogEntityClient({ discovery }); -``` - -- Scaffolder's API `/v1/jobs` will accept `templateName` instead of `template` Entity. diff --git a/.changeset/weak-rice-beam.md b/.changeset/weak-rice-beam.md deleted file mode 100644 index c507e350a9..0000000000 --- a/.changeset/weak-rice-beam.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Capture plugin name under the /api/ prefix for http metrics diff --git a/.changeset/wicked-impalas-tan.md b/.changeset/wicked-impalas-tan.md deleted file mode 100644 index 8cfe919f5c..0000000000 --- a/.changeset/wicked-impalas-tan.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-tech-radar': minor ---- - -Added tech radar blip history backend support and normalized the data structure diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 458f5a5e5c..e24e7393ec 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,53 @@ # example-app +## 0.2.1 + +### Patch Changes + +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [a41730c6e] +- Updated dependencies [9a294574c] +- Updated dependencies [0703edee0] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [5a2705de2] +- Updated dependencies [0c0798f08] +- Updated dependencies [84b654d5d] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [3f05616bf] +- Updated dependencies [803527bd3] +- Updated dependencies [4577e377b] +- Updated dependencies [2d0bd1be7] +- Updated dependencies [59166e5ec] +- Updated dependencies [a906f20e7] + - @backstage/core@0.3.0 + - @backstage/plugin-jenkins@0.3.0 + - @backstage/plugin-cost-insights@0.3.0 + - @backstage/plugin-user-settings@0.2.1 + - @backstage/plugin-api-docs@0.2.1 + - @backstage/plugin-tech-radar@0.3.0 + - @backstage/theme@0.2.1 + - @backstage/plugin-catalog@0.2.1 + - @backstage/plugin-scaffolder@0.3.0 + - @backstage/plugin-circleci@0.2.1 + - @backstage/plugin-cloudbuild@0.2.1 + - @backstage/plugin-explore@0.2.1 + - @backstage/plugin-gcp-projects@0.2.1 + - @backstage/plugin-github-actions@0.2.1 + - @backstage/plugin-gitops-profiles@0.2.1 + - @backstage/plugin-graphiql@0.2.1 + - @backstage/plugin-kubernetes@0.2.1 + - @backstage/plugin-lighthouse@0.2.1 + - @backstage/plugin-newrelic@0.2.1 + - @backstage/plugin-register-component@0.2.1 + - @backstage/plugin-rollbar@0.2.1 + - @backstage/plugin-sentry@0.2.1 + - @backstage/plugin-techdocs@0.2.1 + - @backstage/plugin-welcome@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/packages/app/package.json b/packages/app/package.json index 54f0dd2879..f013200fbb 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,36 +1,36 @@ { "name": "example-app", - "version": "0.2.0", + "version": "0.2.1", "private": true, "bundled": true, "dependencies": { "@backstage/catalog-model": "^0.2.0", "@backstage/cli": "^0.2.0", - "@backstage/core": "^0.2.0", - "@backstage/plugin-api-docs": "^0.2.0", - "@backstage/plugin-catalog": "^0.2.0", - "@backstage/plugin-circleci": "^0.2.0", - "@backstage/plugin-cloudbuild": "^0.2.0", - "@backstage/plugin-cost-insights": "^0.2.0", - "@backstage/plugin-explore": "^0.2.0", - "@backstage/plugin-gcp-projects": "^0.2.0", - "@backstage/plugin-github-actions": "^0.2.0", - "@backstage/plugin-gitops-profiles": "^0.2.0", - "@backstage/plugin-graphiql": "^0.2.0", - "@backstage/plugin-jenkins": "^0.2.0", - "@backstage/plugin-kubernetes": "^0.2.0", - "@backstage/plugin-lighthouse": "^0.2.0", - "@backstage/plugin-newrelic": "^0.2.0", - "@backstage/plugin-register-component": "^0.2.0", - "@backstage/plugin-rollbar": "^0.2.0", - "@backstage/plugin-scaffolder": "^0.2.0", - "@backstage/plugin-sentry": "^0.2.0", - "@backstage/plugin-tech-radar": "^0.2.0", - "@backstage/plugin-techdocs": "^0.2.0", - "@backstage/plugin-user-settings": "^0.2.0", - "@backstage/plugin-welcome": "^0.2.0", + "@backstage/core": "^0.3.0", + "@backstage/plugin-api-docs": "^0.2.1", + "@backstage/plugin-catalog": "^0.2.1", + "@backstage/plugin-circleci": "^0.2.1", + "@backstage/plugin-cloudbuild": "^0.2.1", + "@backstage/plugin-cost-insights": "^0.3.0", + "@backstage/plugin-explore": "^0.2.1", + "@backstage/plugin-gcp-projects": "^0.2.1", + "@backstage/plugin-github-actions": "^0.2.1", + "@backstage/plugin-gitops-profiles": "^0.2.1", + "@backstage/plugin-graphiql": "^0.2.1", + "@backstage/plugin-jenkins": "^0.3.0", + "@backstage/plugin-kubernetes": "^0.2.1", + "@backstage/plugin-lighthouse": "^0.2.1", + "@backstage/plugin-newrelic": "^0.2.1", + "@backstage/plugin-register-component": "^0.2.1", + "@backstage/plugin-rollbar": "^0.2.1", + "@backstage/plugin-scaffolder": "^0.3.0", + "@backstage/plugin-sentry": "^0.2.1", + "@backstage/plugin-tech-radar": "^0.3.0", + "@backstage/plugin-techdocs": "^0.2.1", + "@backstage/plugin-user-settings": "^0.2.1", + "@backstage/plugin-welcome": "^0.2.1", "@backstage/test-utils": "^0.1.2", - "@backstage/theme": "^0.2.0", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@octokit/rest": "^18.0.0", diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index 2b8b38b9b0..0f18908932 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/backend-common +## 0.2.1 + +### Patch Changes + +- 33b7300eb: Capture plugin name under the /api/ prefix for http metrics + ## 0.2.0 ### Minor Changes diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index f76cb1757a..67e4ffe11f 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.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "private": false, diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index a3e5535e40..2fe72cb101 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,18 @@ # example-backend +## 0.2.1 + +### Patch Changes + +- Updated dependencies [752808090] +- Updated dependencies [462876399] +- Updated dependencies [59166e5ec] +- Updated dependencies [33b7300eb] + - @backstage/plugin-auth-backend@0.2.1 + - @backstage/plugin-scaffolder-backend@0.3.0 + - @backstage/backend-common@0.2.1 + - example-app@0.2.1 + ## 0.2.0 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 051b83a146..e67cadb44e 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.2.0", + "version": "0.2.1", "main": "dist/index.cjs.js", "types": "src/index.ts", "private": true, @@ -18,24 +18,24 @@ "migrate:create": "knex migrate:make -x ts" }, "dependencies": { - "@backstage/backend-common": "^0.2.0", + "@backstage/backend-common": "^0.2.1", "@backstage/catalog-model": "^0.2.0", "@backstage/config": "^0.1.1", "@backstage/plugin-app-backend": "^0.2.0", - "@backstage/plugin-auth-backend": "^0.2.0", + "@backstage/plugin-auth-backend": "^0.2.1", "@backstage/plugin-catalog-backend": "^0.2.0", "@backstage/plugin-graphql-backend": "^0.1.2", "@backstage/plugin-kubernetes-backend": "^0.1.2", "@backstage/plugin-proxy-backend": "^0.2.0", "@backstage/plugin-rollbar-backend": "^0.1.2", - "@backstage/plugin-scaffolder-backend": "^0.2.0", + "@backstage/plugin-scaffolder-backend": "^0.3.0", "@backstage/plugin-sentry-backend": "^0.1.2", "@backstage/plugin-techdocs-backend": "^0.2.0", "@gitbeaker/node": "^25.2.0", "@octokit/rest": "^18.0.0", "azure-devops-node-api": "^10.1.1", "dockerode": "^3.2.0", - "example-app": "^0.2.0", + "example-app": "^0.2.1", "express": "^4.17.1", "express-promise-router": "^3.0.3", "knex": "^0.21.6", diff --git a/packages/cli/package.json b/packages/cli/package.json index 7ae244866d..317e78ca16 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -108,12 +108,12 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-common": "^0.2.0", + "@backstage/backend-common": "^0.2.1", "@backstage/config": "^0.1.1", - "@backstage/core": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/core": "^0.3.0", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", - "@backstage/theme": "^0.2.0", + "@backstage/theme": "^0.2.1", "@types/diff": "^4.0.2", "@types/fs-extra": "^9.0.1", "@types/html-webpack-plugin": "^3.2.2", diff --git a/packages/core-api/CHANGELOG.md b/packages/core-api/CHANGELOG.md index 0e84d14aea..b936715c70 100644 --- a/packages/core-api/CHANGELOG.md +++ b/packages/core-api/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/core-api +## 0.2.1 + +### Patch Changes + +- c5bab94ab: Updated the AuthApi `.create` methods to configure the default scope of the corresponding Auth Api. As a result the + default scope is configurable when overwriting the Core Api in the app. + + ``` + GithubAuth.create({ + discoveryApi, + oauthRequestApi, + defaultScopes: ['read:user', 'repo'], + }), + ``` + + Replaced redundant CreateOptions of each Auth Api with the OAuthApiCreateOptions type. + + ``` + export type OAuthApiCreateOptions = AuthApiCreateOptions & { + oauthRequestApi: OAuthRequestApi; + defaultScopes?: string[]; + }; + + export type AuthApiCreateOptions = { + discoveryApi: DiscoveryApi; + environment?: string; + provider?: AuthProvider & { id: string }; + }; + ``` + +- Updated dependencies [4577e377b] + - @backstage/theme@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/packages/core-api/package.json b/packages/core-api/package.json index 42ba1c2080..98eaa0fa61 100644 --- a/packages/core-api/package.json +++ b/packages/core-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-api", "description": "Internal Core API used by Backstage plugins and apps", - "version": "0.2.0", + "version": "0.2.1", "private": false, "publishConfig": { "access": "public", @@ -31,7 +31,7 @@ "dependencies": { "@backstage/config": "^0.1.1", "@backstage/test-utils": "^0.1.2", - "@backstage/theme": "^0.2.0", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@types/react": "^16.9", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 10110a7b24..435b344a6a 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,40 @@ # @backstage/core +## 0.3.0 + +### Minor Changes + +- 199237d2f: New DependencyGraph component added to core package. + +### Patch Changes + +- 7b37d65fd: Adds the MarkdownContent component to render and display Markdown content with the default + [GFM](https://github.github.com/gfm/) (Github flavored Markdown) dialect. + + ``` + + ``` + + To render the Markdown content with plain [CommonMark](https://commonmark.org/), set the dialect to `common-mark` + + ``` + BarChartTooltip + rename: TooltipItem -> BarChartTooltipItem + Deprecate BarChartData in favor of BarChartOptions + Export BarChartLegend component + Update BarChart props to accept options prop + Deprecate ProductCost type in favor of Entity. Update CostInsightsApi + +### Patch Changes + +- 9a294574c: Fix styling issue on Cost Insights product panels with no cost +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index 944dbf587f..39159c03c4 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-cost-insights", - "version": "0.2.0", + "version": "0.3.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,9 +22,9 @@ }, "dependencies": { "@backstage/config": "^0.1.1", - "@backstage/core": "^0.2.0", + "@backstage/core": "^0.3.0", "@backstage/test-utils": "^0.1.2", - "@backstage/theme": "^0.2.0", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -47,7 +47,7 @@ }, "devDependencies": { "@backstage/cli": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/plugins/explore/CHANGELOG.md b/plugins/explore/CHANGELOG.md index dfa428b62a..e4072fd0f2 100644 --- a/plugins/explore/CHANGELOG.md +++ b/plugins/explore/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-explore +## 0.2.1 + +### Patch Changes + +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/explore/package.json b/plugins/explore/package.json index a1f71de6a2..7770341ffb 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-explore", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.2.0", - "@backstage/theme": "^0.2.0", + "@backstage/core": "^0.3.0", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -34,7 +34,7 @@ }, "devDependencies": { "@backstage/cli": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/plugins/gcp-projects/CHANGELOG.md b/plugins/gcp-projects/CHANGELOG.md index 08e1962f3c..6884ecceee 100644 --- a/plugins/gcp-projects/CHANGELOG.md +++ b/plugins/gcp-projects/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-gcp-projects +## 0.2.1 + +### Patch Changes + +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 812c3b1051..e4af2bd4a3 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gcp-projects", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.2.0", - "@backstage/theme": "^0.2.0", + "@backstage/core": "^0.3.0", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -32,7 +32,7 @@ }, "devDependencies": { "@backstage/cli": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/plugins/github-actions/CHANGELOG.md b/plugins/github-actions/CHANGELOG.md index a4b69a07d4..b679d6844b 100644 --- a/plugins/github-actions/CHANGELOG.md +++ b/plugins/github-actions/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-github-actions +## 0.2.1 + +### Patch Changes + +- Updated dependencies [c5bab94ab] +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] +- Updated dependencies [2d0bd1be7] + - @backstage/core-api@0.2.1 + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + - @backstage/plugin-catalog@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 8d10760843..e0db90453a 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-github-actions", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,10 +22,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.2.0", - "@backstage/core": "^0.2.0", - "@backstage/core-api": "^0.2.0", - "@backstage/plugin-catalog": "^0.2.0", - "@backstage/theme": "^0.2.0", + "@backstage/core": "^0.3.0", + "@backstage/core-api": "^0.2.1", + "@backstage/plugin-catalog": "^0.2.1", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -41,7 +41,7 @@ }, "devDependencies": { "@backstage/cli": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/plugins/gitops-profiles/CHANGELOG.md b/plugins/gitops-profiles/CHANGELOG.md index a5d5249737..7aa4253a2e 100644 --- a/plugins/gitops-profiles/CHANGELOG.md +++ b/plugins/gitops-profiles/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-gitops-profiles +## 0.2.1 + +### Patch Changes + +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index b61c2f6620..167970f830 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gitops-profiles", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.2.0", - "@backstage/theme": "^0.2.0", + "@backstage/core": "^0.3.0", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -33,7 +33,7 @@ }, "devDependencies": { "@backstage/cli": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/plugins/graphiql/CHANGELOG.md b/plugins/graphiql/CHANGELOG.md index db59e7d369..19c9e95988 100644 --- a/plugins/graphiql/CHANGELOG.md +++ b/plugins/graphiql/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-graphiql +## 0.2.1 + +### Patch Changes + +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index e346133e3d..be20cfb635 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphiql", "description": "Backstage plugin for browsing GraphQL APIs", - "version": "0.2.0", + "version": "0.2.1", "private": false, "publishConfig": { "access": "public", @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.2.0", - "@backstage/theme": "^0.2.0", + "@backstage/core": "^0.3.0", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -44,7 +44,7 @@ }, "devDependencies": { "@backstage/cli": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/plugins/jenkins/CHANGELOG.md b/plugins/jenkins/CHANGELOG.md index 010471d9b2..44febbc63d 100644 --- a/plugins/jenkins/CHANGELOG.md +++ b/plugins/jenkins/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-jenkins +## 0.3.0 + +### Minor Changes + +- a41730c6e: Add tooltip for Jenkins rerun button + +### Patch Changes + +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] +- Updated dependencies [2d0bd1be7] + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + - @backstage/plugin-catalog@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index d0304376fd..b4f1d25199 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-jenkins", - "version": "0.2.0", + "version": "0.3.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,9 +22,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.2.0", - "@backstage/core": "^0.2.0", - "@backstage/plugin-catalog": "^0.2.0", - "@backstage/theme": "^0.2.0", + "@backstage/core": "^0.3.0", + "@backstage/plugin-catalog": "^0.2.1", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -37,7 +37,7 @@ }, "devDependencies": { "@backstage/cli": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index 0ec2c5dbaf..e5611e4953 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-kubernetes +## 0.2.1 + +### Patch Changes + +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 29193e27ed..c1457944de 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,9 +22,9 @@ "dependencies": { "@backstage/catalog-model": "^0.2.0", "@backstage/config": "^0.1.1", - "@backstage/core": "^0.2.0", + "@backstage/core": "^0.3.0", "@backstage/plugin-kubernetes-backend": "^0.1.2", - "@backstage/theme": "^0.2.0", + "@backstage/theme": "^0.2.1", "@kubernetes/client-node": "^0.12.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -36,7 +36,7 @@ }, "devDependencies": { "@backstage/cli": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/plugins/lighthouse/CHANGELOG.md b/plugins/lighthouse/CHANGELOG.md index 9a68b78a92..3c5e94c476 100644 --- a/plugins/lighthouse/CHANGELOG.md +++ b/plugins/lighthouse/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-lighthouse +## 0.2.1 + +### Patch Changes + +- Updated dependencies [c5bab94ab] +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] +- Updated dependencies [2d0bd1be7] + - @backstage/core-api@0.2.1 + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + - @backstage/plugin-catalog@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index 27b6a21066..9389c84856 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-lighthouse", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,10 +23,10 @@ "dependencies": { "@backstage/catalog-model": "^0.2.0", "@backstage/config": "^0.1.1", - "@backstage/core": "^0.2.0", - "@backstage/core-api": "^0.2.0", - "@backstage/plugin-catalog": "^0.2.0", - "@backstage/theme": "^0.2.0", + "@backstage/core": "^0.3.0", + "@backstage/core-api": "^0.2.1", + "@backstage/plugin-catalog": "^0.2.1", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -40,7 +40,7 @@ }, "devDependencies": { "@backstage/cli": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/plugins/newrelic/CHANGELOG.md b/plugins/newrelic/CHANGELOG.md index d007328180..86b71d497e 100644 --- a/plugins/newrelic/CHANGELOG.md +++ b/plugins/newrelic/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-newrelic +## 0.2.1 + +### Patch Changes + +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index e7d000abc5..475f19d651 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-newrelic", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.2.0", - "@backstage/theme": "^0.2.0", + "@backstage/core": "^0.3.0", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -32,7 +32,7 @@ }, "devDependencies": { "@backstage/cli": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/plugins/register-component/CHANGELOG.md b/plugins/register-component/CHANGELOG.md index cb1a24d131..3e966ef0d0 100644 --- a/plugins/register-component/CHANGELOG.md +++ b/plugins/register-component/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-register-component +## 0.2.1 + +### Patch Changes + +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] +- Updated dependencies [2d0bd1be7] + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + - @backstage/plugin-catalog@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/register-component/package.json b/plugins/register-component/package.json index 095469f6b4..9c87f33e29 100644 --- a/plugins/register-component/package.json +++ b/plugins/register-component/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-register-component", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,9 +22,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.2.0", - "@backstage/core": "^0.2.0", - "@backstage/plugin-catalog": "^0.2.0", - "@backstage/theme": "^0.2.0", + "@backstage/core": "^0.3.0", + "@backstage/plugin-catalog": "^0.2.1", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -37,7 +37,7 @@ }, "devDependencies": { "@backstage/cli": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/plugins/rollbar/CHANGELOG.md b/plugins/rollbar/CHANGELOG.md index ee25fb10d9..3d53c1bf0d 100644 --- a/plugins/rollbar/CHANGELOG.md +++ b/plugins/rollbar/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-rollbar +## 0.2.1 + +### Patch Changes + +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] +- Updated dependencies [2d0bd1be7] + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + - @backstage/plugin-catalog@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 03e393aa1b..722fe35fb6 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-rollbar", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,9 +22,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.2.0", - "@backstage/core": "^0.2.0", - "@backstage/plugin-catalog": "^0.2.0", - "@backstage/theme": "^0.2.0", + "@backstage/core": "^0.3.0", + "@backstage/plugin-catalog": "^0.2.1", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -38,7 +38,7 @@ }, "devDependencies": { "@backstage/cli": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index 78a6d9527b..96f4ef75f7 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-scaffolder-backend +## 0.3.0 + +### Minor Changes + +- 59166e5ec: `createRouter` of scaffolder backend will now require additional option as `entityClient` which could be generated by `CatalogEntityClient` in `plugin-scaffolder-backend` package. Here is example to generate `entityClient`. + + ```js + import { CatalogEntityClient } from '@backstage/plugin-scaffolder-backend'; + import { SingleHostDiscovery } from '@backstage/backend-common'; + + const discovery = SingleHostDiscovery.fromConfig(config); + const entityClient = new CatalogEntityClient({ discovery }); + ``` + + - Scaffolder's API `/v1/jobs` will accept `templateName` instead of `template` Entity. + +### Patch Changes + +- Updated dependencies [33b7300eb] + - @backstage/backend-common@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index ac30e23828..4bed7ea60c 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "0.2.0", + "version": "0.3.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.2.0", + "@backstage/backend-common": "^0.2.1", "@backstage/catalog-model": "^0.2.0", "@backstage/config": "^0.1.1", "@gitbeaker/core": "^25.2.0", diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index 9c09366174..9eac595997 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,36 @@ # @backstage/plugin-scaffolder +## 0.3.0 + +### Minor Changes + +- 59166e5ec: `createRouter` of scaffolder backend will now require additional option as `entityClient` which could be generated by `CatalogEntityClient` in `plugin-scaffolder-backend` package. Here is example to generate `entityClient`. + + ```js + import { CatalogEntityClient } from '@backstage/plugin-scaffolder-backend'; + import { SingleHostDiscovery } from '@backstage/backend-common'; + + const discovery = SingleHostDiscovery.fromConfig(config); + const entityClient = new CatalogEntityClient({ discovery }); + ``` + + - Scaffolder's API `/v1/jobs` will accept `templateName` instead of `template` Entity. + +### Patch Changes + +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] +- Updated dependencies [2d0bd1be7] + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + - @backstage/plugin-catalog@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 738734ceb7..a423c64aa3 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "0.2.0", + "version": "0.3.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,9 +22,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.2.0", - "@backstage/core": "^0.2.0", - "@backstage/plugin-catalog": "^0.2.0", - "@backstage/theme": "^0.2.0", + "@backstage/core": "^0.3.0", + "@backstage/plugin-catalog": "^0.2.1", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -42,7 +42,7 @@ }, "devDependencies": { "@backstage/cli": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/plugins/sentry/CHANGELOG.md b/plugins/sentry/CHANGELOG.md index 06380c39d9..88adfe43a1 100644 --- a/plugins/sentry/CHANGELOG.md +++ b/plugins/sentry/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-sentry +## 0.2.1 + +### Patch Changes + +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 2ede8fd709..7941df5dfd 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sentry", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,8 +22,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.2.0", - "@backstage/core": "^0.2.0", - "@backstage/theme": "^0.2.0", + "@backstage/core": "^0.3.0", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -37,7 +37,7 @@ }, "devDependencies": { "@backstage/cli": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/plugins/sonarqube/CHANGELOG.md b/plugins/sonarqube/CHANGELOG.md index 62f88596e7..0296bd0416 100644 --- a/plugins/sonarqube/CHANGELOG.md +++ b/plugins/sonarqube/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-sonarqube +## 0.1.3 + +### Patch Changes + +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + ## 0.1.2 ### Patch Changes diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index bc41213ad8..d7240aabde 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sonarqube", - "version": "0.1.2", + "version": "0.1.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,8 +22,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.2.0", - "@backstage/core": "^0.2.0", - "@backstage/theme": "^0.2.0", + "@backstage/core": "^0.3.0", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -36,7 +36,7 @@ }, "devDependencies": { "@backstage/cli": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/plugins/tech-radar/CHANGELOG.md b/plugins/tech-radar/CHANGELOG.md index 7f9f11a702..cf6f360fe6 100644 --- a/plugins/tech-radar/CHANGELOG.md +++ b/plugins/tech-radar/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-tech-radar +## 0.3.0 + +### Minor Changes + +- a906f20e7: Added tech radar blip history backend support and normalized the data structure + +### Patch Changes + +- 3f05616bf: Make the footer color of the tech-radar work in both light and dark theme. +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index 875d6d161b..431fd3d515 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-radar", - "version": "0.2.0", + "version": "0.3.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.2.0", + "@backstage/core": "^0.3.0", "@backstage/test-utils": "^0.1.2", - "@backstage/theme": "^0.2.0", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -36,7 +36,7 @@ }, "devDependencies": { "@backstage/cli": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index 387e4a8d90..1c1dcb5c8a 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-techdocs +## 0.2.1 + +### Patch Changes + +- Updated dependencies [c5bab94ab] +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] +- Updated dependencies [2d0bd1be7] + - @backstage/core-api@0.2.1 + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + - @backstage/plugin-catalog@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index ce0700c1a8..6a664a83fb 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,11 +23,11 @@ }, "dependencies": { "@backstage/catalog-model": "^0.2.0", - "@backstage/core": "^0.2.0", - "@backstage/core-api": "^0.2.0", - "@backstage/plugin-catalog": "^0.2.0", + "@backstage/core": "^0.3.0", + "@backstage/core-api": "^0.2.1", + "@backstage/plugin-catalog": "^0.2.1", "@backstage/test-utils": "^0.1.2", - "@backstage/theme": "^0.2.0", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -41,7 +41,7 @@ }, "devDependencies": { "@backstage/cli": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/plugins/user-settings/CHANGELOG.md b/plugins/user-settings/CHANGELOG.md index 2522a3af03..729d2a669a 100644 --- a/plugins/user-settings/CHANGELOG.md +++ b/plugins/user-settings/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-user-settings +## 0.2.1 + +### Patch Changes + +- 5a2705de2: Export `AuthProviders`, `DefaultProviderSettings` and `ProviderSettingsItem`. +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 2f098eae2e..a48c99cbc6 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.2.0", - "@backstage/theme": "^0.2.0", + "@backstage/core": "^0.3.0", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -33,7 +33,7 @@ }, "devDependencies": { "@backstage/cli": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/plugins/welcome/CHANGELOG.md b/plugins/welcome/CHANGELOG.md index 2932d41d75..e6165ef4f9 100644 --- a/plugins/welcome/CHANGELOG.md +++ b/plugins/welcome/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-welcome +## 0.2.1 + +### Patch Changes + +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index 148b5734ee..fcfbed2af7 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-welcome", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -21,8 +21,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.2.0", - "@backstage/theme": "^0.2.0", + "@backstage/core": "^0.3.0", + "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -33,7 +33,7 @@ }, "devDependencies": { "@backstage/cli": "^0.2.0", - "@backstage/dev-utils": "^0.1.2", + "@backstage/dev-utils": "^0.1.3", "@backstage/test-utils": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", From 1ef651471d7efa991adf90c68edba2c0d35a60d9 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 12 Nov 2020 12:25:39 +0100 Subject: [PATCH 143/252] package: add override of core for roadie plugins --- package.json | 3 +++ yarn.lock | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/package.json b/package.json index 505ee0caad..b97462114c 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,9 @@ "plugins/*" ] }, + "resolutions": { + "**/@roadiehq/backstage-plugin-*/@backstage/core": "0.3.0" + }, "version": "1.0.0", "devDependencies": { "@changesets/cli": "^2.11.0", diff --git a/yarn.lock b/yarn.lock index ceb0234e6b..10e70f52fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1289,6 +1289,42 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@backstage/core@^0.2.0": + version "0.3.0" + dependencies: + "@backstage/config" "^0.1.1" + "@backstage/core-api" "^0.2.1" + "@backstage/theme" "^0.2.1" + "@material-ui/core" "^4.11.0" + "@material-ui/icons" "^4.9.1" + "@material-ui/lab" "4.0.0-alpha.45" + "@types/dagre" "^0.7.44" + "@types/react" "^16.9" + "@types/react-sparklines" "^1.7.0" + classnames "^2.2.6" + clsx "^1.1.0" + d3-selection "^2.0.0" + d3-shape "^2.0.0" + d3-zoom "^2.0.0" + dagre "^0.8.5" + immer "^7.0.9" + lodash "^4.17.15" + material-table "^1.69.1" + prop-types "^15.7.2" + qs "^6.9.4" + rc-progress "^3.0.0" + react "^16.12.0" + react-dom "^16.12.0" + react-helmet "6.1.0" + react-hook-form "^6.6.0" + react-markdown "^5.0.2" + react-router "6.0.0-beta.0" + react-router-dom "6.0.0-beta.0" + react-sparklines "^1.7.0" + react-syntax-highlighter "^13.5.1" + react-use "^15.3.3" + remark-gfm "^1.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" From e74422b4d844b43172b76216fa5e79b7be528dd8 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 12 Nov 2020 10:37:04 +0100 Subject: [PATCH 144/252] backend-common: make ReadTreeResponse.files async --- packages/backend-common/src/reading/GithubUrlReader.test.ts | 2 +- packages/backend-common/src/reading/GithubUrlReader.ts | 2 +- packages/backend-common/src/reading/types.ts | 2 +- plugins/techdocs-backend/src/helpers.test.ts | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/backend-common/src/reading/GithubUrlReader.test.ts b/packages/backend-common/src/reading/GithubUrlReader.test.ts index 1e7326af2b..7552547924 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.test.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.test.ts @@ -272,7 +272,7 @@ describe('GithubUrlReader', () => { ['mkdocs.yml', 'docs'], ); - const files = response.files(); + const files = await response.files(); const mkDocsFile = await files[0].content(); const indexMarkdownFile = await files[1].content(); diff --git a/packages/backend-common/src/reading/GithubUrlReader.ts b/packages/backend-common/src/reading/GithubUrlReader.ts index 906076a3c2..41db541f7f 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.ts @@ -286,7 +286,7 @@ export class GithubUrlReader implements UrlReader { // @ts-ignore Typescript doesn't consider .pipe a method on ReadableStream. Don't know why. repoArchive.body?.pipe(parser).on('finish', () => { resolve({ - files: () => { + files: async () => { return files; }, archive: () => { diff --git a/packages/backend-common/src/reading/types.ts b/packages/backend-common/src/reading/types.ts index 26ac4af45e..d9fa5d839e 100644 --- a/packages/backend-common/src/reading/types.ts +++ b/packages/backend-common/src/reading/types.ts @@ -49,7 +49,7 @@ export type File = { }; export type ReadTreeResponse = { - files(): File[]; + files(): Promise; archive(): Promise; dir(outDir?: string): Promise; }; diff --git a/plugins/techdocs-backend/src/helpers.test.ts b/plugins/techdocs-backend/src/helpers.test.ts index 8d4d69ba26..c3f2261637 100644 --- a/plugins/techdocs-backend/src/helpers.test.ts +++ b/plugins/techdocs-backend/src/helpers.test.ts @@ -30,7 +30,7 @@ describe('getDocFilesFromRepository', () => { dir: async () => { return '/tmp/testfolder'; }, - files: () => { + files: async () => { return []; }, archive: async () => { @@ -78,7 +78,7 @@ describe('getDocFilesFromRepository', () => { dir: async () => { return '/tmp/testfolder'; }, - files: () => { + files: async () => { return []; }, archive: async () => { From 1b632b5aa71f695a3d265be6ec6327c4158babb5 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 10 Nov 2020 10:47:24 +0100 Subject: [PATCH 145/252] backend-common: add archive tree reading utilities --- .../src/reading/tree/ArchiveResponse.test.ts | 93 +++++++++++++ .../src/reading/tree/ArchiveResponse.ts | 124 ++++++++++++++++++ .../reading/tree/ReadTreeResponseFactory.ts | 47 +++++++ .../backend-common/src/reading/tree/index.ts | 17 +++ 4 files changed, 281 insertions(+) create mode 100644 packages/backend-common/src/reading/tree/ArchiveResponse.test.ts create mode 100644 packages/backend-common/src/reading/tree/ArchiveResponse.ts create mode 100644 packages/backend-common/src/reading/tree/ReadTreeResponseFactory.ts create mode 100644 packages/backend-common/src/reading/tree/index.ts diff --git a/packages/backend-common/src/reading/tree/ArchiveResponse.test.ts b/packages/backend-common/src/reading/tree/ArchiveResponse.test.ts new file mode 100644 index 0000000000..57a490650c --- /dev/null +++ b/packages/backend-common/src/reading/tree/ArchiveResponse.test.ts @@ -0,0 +1,93 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs-extra'; +import mockFs from 'mock-fs'; +import { Readable } from 'stream'; +import { resolve as resolvePath } from 'path'; +import { ArchiveResponse } from './ArchiveResponse'; + +const archiveData = fs.readFileSync( + resolvePath(__filename, '../../__fixtures__/repo.tar.gz'), +); + +describe('ArchiveResponse', () => { + beforeEach(() => { + mockFs({ + '/test-archive.tar.gz': archiveData, + '/tmp': mockFs.directory(), + }); + }); + + afterEach(() => { + mockFs.restore(); + }); + + it('should read as archive and files', async () => { + const stream = fs.createReadStream('/test-archive.tar.gz'); + + const res = new ArchiveResponse(stream, 'mock-repo', '/tmp'); + const buffer = await res.archive(); + + await expect(res.archive()).rejects.toThrow( + 'Response has already been read', + ); + + const res2 = new ArchiveResponse(Readable.from(buffer), '', '/tmp'); + const files = await res2.files(); + + expect(files).toEqual([ + { + path: './mkdocs.yml', + content: expect.any(Function), + }, + { + path: './docs/index.md', + content: expect.any(Function), + }, + ]); + const contents = await Promise.all(files.map(f => f.content())); + expect(contents.map(c => c.toString('utf8').trim())).toEqual([ + 'site_name: Test', + '# Test', + ]); + }); + + it('should extract entire archive into directory', async () => { + const stream = fs.createReadStream('/test-archive.tar.gz'); + + const res = new ArchiveResponse(stream, '', '/tmp'); + const dir = await res.dir(); + + await expect( + fs.readFile(resolvePath(dir, 'mock-repo/mkdocs.yml'), 'utf8'), + ).resolves.toBe('site_name: Test\n'); + await expect( + fs.readFile(resolvePath(dir, 'mock-repo/docs/index.md'), 'utf8'), + ).resolves.toBe('# Test\n'); + }); + + it('should extract archive into directory with a subpath', async () => { + const stream = fs.createReadStream('/test-archive.tar.gz'); + + const res = new ArchiveResponse(stream, 'mock-repo/docs', '/tmp'); + const dir = await res.dir(); + + await expect( + fs.readFile(resolvePath(dir, 'index.md'), 'utf8'), + ).resolves.toBe('# Test\n'); + }); +}); diff --git a/packages/backend-common/src/reading/tree/ArchiveResponse.ts b/packages/backend-common/src/reading/tree/ArchiveResponse.ts new file mode 100644 index 0000000000..65ad8c18fe --- /dev/null +++ b/packages/backend-common/src/reading/tree/ArchiveResponse.ts @@ -0,0 +1,124 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import os from 'os'; +import tar, { Parse, ParseStream, ReadEntry } from 'tar'; +import path from 'path'; +import fs from 'fs-extra'; +import { Readable, pipeline as pipelineCb } from 'stream'; +import { promisify } from 'util'; +import concatStream from 'concat-stream'; +import { ReadTreeResponse, File } from '../types'; + +// Tar types for `Parse` is not a proper constructor, but it should be +const TarParseStream = (Parse as unknown) as { new (): ParseStream }; + +const pipeline = promisify(pipelineCb); + +/** + * Wraps a tar archive stream into a tree response reader. + */ +export class ArchiveResponse implements ReadTreeResponse { + private read = false; + + constructor( + private readonly stream: Readable, + private readonly subPath: string, + private readonly workDir: string = os.tmpdir(), + ) {} + + // Make sure the input stream is only read once + private onlyOnce() { + if (this.read) { + throw new Error('Response has already been read'); + } + this.read = true; + } + + async files(): Promise { + this.onlyOnce(); + + const files: File[] = []; + const parser = new TarParseStream(); + + parser.on('entry', (entry: ReadEntry & Readable) => { + if (entry.type === 'Directory') { + entry.resume(); + return; + } + + const contentPromise = new Promise(async resolve => { + await pipeline(entry, concatStream(resolve)); + }); + + files.push({ + path: entry.path, + content: () => contentPromise, + }); + + entry.resume(); + }); + + await pipeline(this.stream, parser); + + return files; + } + + async archive(): Promise { + if (!this.subPath) { + this.onlyOnce(); + + return new Promise(resolve => + pipeline(this.stream, concatStream(resolve)), + ); + } + + // TODO(Rugvip): method for repacking a tar with a subpath is to simply extract into a + // tmp dir and recreate the archive. Would be nicer to stream things instead. + const tmpDir = await this.dir(); + + try { + return await new Promise(async resolve => { + await pipeline( + tar.create({ cwd: tmpDir }, ['.']), + concatStream(resolve), + ); + }); + } finally { + await fs.remove(tmpDir); + } + } + + async dir(outDir?: string): Promise { + this.onlyOnce(); + + const dir = + outDir ?? (await fs.mkdtemp(path.join(this.workDir, 'backstage-'))); + + const strip = this.subPath ? this.subPath.split('/').length : 0; + + await pipeline( + this.stream, + tar.extract({ + strip, + cwd: dir, + filter: path => path.startsWith(this.subPath), + }), + ); + + return dir; + } +} diff --git a/packages/backend-common/src/reading/tree/ReadTreeResponseFactory.ts b/packages/backend-common/src/reading/tree/ReadTreeResponseFactory.ts new file mode 100644 index 0000000000..0cdf926720 --- /dev/null +++ b/packages/backend-common/src/reading/tree/ReadTreeResponseFactory.ts @@ -0,0 +1,47 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import os from 'os'; +import { Readable } from 'stream'; +import { Config } from '@backstage/config'; +import { ReadTreeResponse } from '../types'; +import { ArchiveResponse } from './ArchiveResponse'; + +type FromArchiveOptions = { + // A binary stream of a tar archive. + stream: Readable; + // If set, root of the tree will be set to the given path. Should not have a trailing `/`. + path?: string; +}; + +export class ReadTreeResponseFactory { + static create(options: { config: Config }): ReadTreeResponseFactory { + return new ReadTreeResponseFactory( + options.config.getOptionalString('backend.workingDirectory') ?? + os.tmpdir(), + ); + } + + constructor(private readonly workDir: string) {} + + async fromArchive(options: FromArchiveOptions): Promise { + return new ArchiveResponse( + options.stream, + options.path ?? '', + this.workDir, + ); + } +} diff --git a/packages/backend-common/src/reading/tree/index.ts b/packages/backend-common/src/reading/tree/index.ts new file mode 100644 index 0000000000..858cf15877 --- /dev/null +++ b/packages/backend-common/src/reading/tree/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { ReadTreeResponseFactory } from './ReadTreeResponseFactory'; From 73231427e138a7aa983a22a5d84e725805500d10 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 12 Nov 2020 12:20:07 +0100 Subject: [PATCH 146/252] backend-common: add top-level archive filtering to ArchiveResponse + fix files() --- .../src/reading/tree/ArchiveResponse.test.ts | 67 +++++++++++++++++-- .../src/reading/tree/ArchiveResponse.ts | 58 ++++++++++++---- .../reading/tree/ReadTreeResponseFactory.ts | 3 + 3 files changed, 112 insertions(+), 16 deletions(-) diff --git a/packages/backend-common/src/reading/tree/ArchiveResponse.test.ts b/packages/backend-common/src/reading/tree/ArchiveResponse.test.ts index 57a490650c..6b8f29de8c 100644 --- a/packages/backend-common/src/reading/tree/ArchiveResponse.test.ts +++ b/packages/backend-common/src/reading/tree/ArchiveResponse.test.ts @@ -36,10 +36,51 @@ describe('ArchiveResponse', () => { mockFs.restore(); }); + it('should read files', async () => { + const stream = fs.createReadStream('/test-archive.tar.gz'); + + const res = new ArchiveResponse(stream, 'mock-repo/', '/tmp'); + const files = await res.files(); + + expect(files).toEqual([ + { + path: 'mkdocs.yml', + content: expect.any(Function), + }, + { + path: 'docs/index.md', + content: expect.any(Function), + }, + ]); + const contents = await Promise.all(files.map(f => f.content())); + expect(contents.map(c => c.toString('utf8').trim())).toEqual([ + 'site_name: Test', + '# Test', + ]); + }); + + it('should read files with filter', async () => { + const stream = fs.createReadStream('/test-archive.tar.gz'); + + const res = new ArchiveResponse(stream, 'mock-repo/', '/tmp', path => + path.endsWith('.yml'), + ); + const files = await res.files(); + + expect(files).toEqual([ + { + path: 'mkdocs.yml', + content: expect.any(Function), + }, + ]); + const content = await files[0].content(); + expect(content.toString('utf8').trim()).toEqual('site_name: Test'); + }); + it('should read as archive and files', async () => { const stream = fs.createReadStream('/test-archive.tar.gz'); - const res = new ArchiveResponse(stream, 'mock-repo', '/tmp'); + const res = new ArchiveResponse(stream, 'mock-repo/', '/tmp'); const buffer = await res.archive(); await expect(res.archive()).rejects.toThrow( @@ -51,11 +92,11 @@ describe('ArchiveResponse', () => { expect(files).toEqual([ { - path: './mkdocs.yml', + path: 'mkdocs.yml', content: expect.any(Function), }, { - path: './docs/index.md', + path: 'docs/index.md', content: expect.any(Function), }, ]); @@ -83,11 +124,29 @@ describe('ArchiveResponse', () => { it('should extract archive into directory with a subpath', async () => { const stream = fs.createReadStream('/test-archive.tar.gz'); - const res = new ArchiveResponse(stream, 'mock-repo/docs', '/tmp'); + const res = new ArchiveResponse(stream, 'mock-repo/docs/', '/tmp'); const dir = await res.dir(); + expect(dir).toMatch(/^\/tmp\/.*$/); await expect( fs.readFile(resolvePath(dir, 'index.md'), 'utf8'), ).resolves.toBe('# Test\n'); }); + + it('should extract archive into directory with a subpath and filter', async () => { + const stream = fs.createReadStream('/test-archive.tar.gz'); + + const res = new ArchiveResponse(stream, 'mock-repo/', '/tmp', path => + path.endsWith('.yml'), + ); + const dir = await res.dir({ targetDir: '/tmp' }); + + expect(dir).toBe('/tmp'); + await expect(fs.pathExists(resolvePath(dir, 'mkdocs.yml'))).resolves.toBe( + true, + ); + await expect( + fs.pathExists(resolvePath(dir, 'docs/index.md')), + ).resolves.toBe(false); + }); }); diff --git a/packages/backend-common/src/reading/tree/ArchiveResponse.ts b/packages/backend-common/src/reading/tree/ArchiveResponse.ts index 65ad8c18fe..83f6f6a8e5 100644 --- a/packages/backend-common/src/reading/tree/ArchiveResponse.ts +++ b/packages/backend-common/src/reading/tree/ArchiveResponse.ts @@ -21,7 +21,7 @@ import fs from 'fs-extra'; import { Readable, pipeline as pipelineCb } from 'stream'; import { promisify } from 'util'; import concatStream from 'concat-stream'; -import { ReadTreeResponse, File } from '../types'; +import { ReadTreeResponse, File, ReadTreeResponseDirOptions } from '../types'; // Tar types for `Parse` is not a proper constructor, but it should be const TarParseStream = (Parse as unknown) as { new (): ParseStream }; @@ -38,7 +38,17 @@ export class ArchiveResponse implements ReadTreeResponse { private readonly stream: Readable, private readonly subPath: string, private readonly workDir: string = os.tmpdir(), - ) {} + private readonly filter?: (path: string) => boolean, + ) { + if (subPath) { + if (!subPath.endsWith('/')) { + throw new TypeError('ArchiveResponse subPath must end with a /'); + } + if (subPath.startsWith('/')) { + throw new TypeError('ArchiveResponse subPath must not start with a /'); + } + } + } // Make sure the input stream is only read once private onlyOnce() { @@ -60,14 +70,28 @@ export class ArchiveResponse implements ReadTreeResponse { return; } - const contentPromise = new Promise(async resolve => { + if (this.subPath) { + if (!entry.path.startsWith(this.subPath)) { + entry.resume(); + return; + } + } + + const path = this.subPath + ? entry.path.replace(this.subPath, '') + : entry.path; + if (this.filter) { + if (!this.filter(path)) { + entry.resume(); + return; + } + } + + const content = new Promise(async resolve => { await pipeline(entry, concatStream(resolve)); }); - files.push({ - path: entry.path, - content: () => contentPromise, - }); + files.push({ path, content: () => content }); entry.resume(); }); @@ -93,7 +117,7 @@ export class ArchiveResponse implements ReadTreeResponse { try { return await new Promise(async resolve => { await pipeline( - tar.create({ cwd: tmpDir }, ['.']), + tar.create({ cwd: tmpDir }, ['']), concatStream(resolve), ); }); @@ -102,20 +126,30 @@ export class ArchiveResponse implements ReadTreeResponse { } } - async dir(outDir?: string): Promise { + async dir(options?: ReadTreeResponseDirOptions): Promise { this.onlyOnce(); const dir = - outDir ?? (await fs.mkdtemp(path.join(this.workDir, 'backstage-'))); + options?.targetDir ?? + (await fs.mkdtemp(path.join(this.workDir, 'backstage-'))); - const strip = this.subPath ? this.subPath.split('/').length : 0; + const strip = this.subPath ? this.subPath.split('/').length - 1 : 0; await pipeline( this.stream, tar.extract({ strip, cwd: dir, - filter: path => path.startsWith(this.subPath), + filter: path => { + if (this.subPath && !path.startsWith(this.subPath)) { + return false; + } + if (this.filter) { + const innerPath = path.split('/').slice(strip).join('/'); + return this.filter(innerPath); + } + return true; + }, }), ); diff --git a/packages/backend-common/src/reading/tree/ReadTreeResponseFactory.ts b/packages/backend-common/src/reading/tree/ReadTreeResponseFactory.ts index 0cdf926720..5044cdabdc 100644 --- a/packages/backend-common/src/reading/tree/ReadTreeResponseFactory.ts +++ b/packages/backend-common/src/reading/tree/ReadTreeResponseFactory.ts @@ -25,6 +25,8 @@ type FromArchiveOptions = { stream: Readable; // If set, root of the tree will be set to the given path. Should not have a trailing `/`. path?: string; + // Filter passed on from the ReadTreeOptions + filter?: (path: string) => boolean; }; export class ReadTreeResponseFactory { @@ -42,6 +44,7 @@ export class ReadTreeResponseFactory { options.stream, options.path ?? '', this.workDir, + options.filter, ); } } From 5bc30d262f928955776763a1f217da2811858aee Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 12 Nov 2020 12:31:23 +0100 Subject: [PATCH 147/252] backend-common: make ReadTreeResponse.dir() accept an options object instead --- .../src/reading/GithubUrlReader.test.ts | 2 +- .../backend-common/src/reading/GithubUrlReader.ts | 13 ++++++++++--- packages/backend-common/src/reading/types.ts | 12 +++++++++++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/packages/backend-common/src/reading/GithubUrlReader.test.ts b/packages/backend-common/src/reading/GithubUrlReader.test.ts index 7552547924..e165745790 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.test.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.test.ts @@ -293,7 +293,7 @@ describe('GithubUrlReader', () => { ); mockfs(); - const directory = await response.dir('/tmp/fs'); + const directory = await response.dir({ targetDir: '/tmp/fs' }); const writtenToDirectory = fs.existsSync(directory); const paths = await recursive(directory); diff --git a/packages/backend-common/src/reading/GithubUrlReader.ts b/packages/backend-common/src/reading/GithubUrlReader.ts index 41db541f7f..e3a47ee420 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.ts @@ -18,7 +18,13 @@ import { Config } from '@backstage/config'; import parseGitUri from 'git-url-parse'; import fetch from 'cross-fetch'; import { NotFoundError } from '../errors'; -import { ReaderFactory, ReadTreeResponse, UrlReader, File } from './types'; +import { + ReaderFactory, + ReadTreeResponse, + UrlReader, + File, + ReadTreeResponseDirOptions, +} from './types'; import tar from 'tar'; import fs from 'fs-extra'; import concatStream from 'concat-stream'; @@ -294,9 +300,10 @@ export class GithubUrlReader implements UrlReader { resolve(Buffer.from('Archive is not yet implemented')), ); }, - dir: (outDir: string | undefined) => { + dir: (options?: ReadTreeResponseDirOptions) => { const targetDirectory = - outDir || fs.mkdtempSync(path.join(os.tmpdir(), 'backstage-')); + options?.targetDir || + fs.mkdtempSync(path.join(os.tmpdir(), 'backstage-')); return new Promise((res, rej) => { Promise.all( diff --git a/packages/backend-common/src/reading/types.ts b/packages/backend-common/src/reading/types.ts index d9fa5d839e..8a78e9d65f 100644 --- a/packages/backend-common/src/reading/types.ts +++ b/packages/backend-common/src/reading/types.ts @@ -17,6 +17,11 @@ import { Logger } from 'winston'; import { Config } from '@backstage/config'; +export type ReadTreeOptions = { + /** A filter that can be used to select which files should be extracted. By default all files are extracted */ + filter?(path: string): boolean; +}; + /** * A generic interface for fetching plain data from URLs. */ @@ -48,8 +53,13 @@ export type File = { content(): Promise; }; +export type ReadTreeResponseDirOptions = { + /** The directory to write files to. Defaults to the OS tmpdir or `backend.workingDirectory` if set in config */ + targetDir?: string; +}; + export type ReadTreeResponse = { files(): Promise; archive(): Promise; - dir(outDir?: string): Promise; + dir(options?: ReadTreeResponseDirOptions): Promise; }; From e1057d9a506d7763ffdad4d5aaf1c84c4c6414eb Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 12 Nov 2020 12:33:58 +0100 Subject: [PATCH 148/252] backend-common: provide a ReadTreeResponseFactory instance to all ReaderFactories --- .../src/reading/AzureUrlReader.test.ts | 17 +++++++++++++++-- .../src/reading/GitlabUrlReader.test.ts | 17 +++++++++++++++-- .../backend-common/src/reading/UrlReaders.ts | 4 +++- packages/backend-common/src/reading/types.ts | 2 ++ 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/packages/backend-common/src/reading/AzureUrlReader.test.ts b/packages/backend-common/src/reading/AzureUrlReader.test.ts index 24b435006a..6efc7641b8 100644 --- a/packages/backend-common/src/reading/AzureUrlReader.test.ts +++ b/packages/backend-common/src/reading/AzureUrlReader.test.ts @@ -20,9 +20,14 @@ import { ConfigReader } from '@backstage/config'; import { getVoidLogger } from '../logging'; import { AzureUrlReader } from './AzureUrlReader'; import { msw } from '@backstage/test-utils'; +import { ReadTreeResponseFactory } from './tree'; const logger = getVoidLogger(); +const treeResponseFactory = ReadTreeResponseFactory.create({ + config: new ConfigReader({}), +}); + describe('AzureUrlReader', () => { const worker = setupServer(); msw.setupDefaultHandlers(worker); @@ -87,7 +92,11 @@ describe('AzureUrlReader', () => { }), }, ])('should handle happy path %#', async ({ url, config, response }) => { - const [{ reader }] = AzureUrlReader.factory({ config, logger }); + const [{ reader }] = AzureUrlReader.factory({ + config, + logger, + treeResponseFactory, + }); const data = await reader.read(url); const res = await JSON.parse(data.toString('utf-8')); @@ -115,7 +124,11 @@ describe('AzureUrlReader', () => { }, ])('should handle error path %#', async ({ url, config, error }) => { await expect(async () => { - const [{ reader }] = AzureUrlReader.factory({ config, logger }); + const [{ reader }] = AzureUrlReader.factory({ + config, + logger, + treeResponseFactory, + }); await reader.read(url); }).rejects.toThrow(error); }); diff --git a/packages/backend-common/src/reading/GitlabUrlReader.test.ts b/packages/backend-common/src/reading/GitlabUrlReader.test.ts index 4b3aa471c3..3a76c0631b 100644 --- a/packages/backend-common/src/reading/GitlabUrlReader.test.ts +++ b/packages/backend-common/src/reading/GitlabUrlReader.test.ts @@ -20,9 +20,14 @@ import { ConfigReader } from '@backstage/config'; import { getVoidLogger } from '../logging'; import { GitlabUrlReader } from './GitlabUrlReader'; import { msw } from '@backstage/test-utils'; +import { ReadTreeResponseFactory } from './tree'; const logger = getVoidLogger(); +const treeResponseFactory = ReadTreeResponseFactory.create({ + config: new ConfigReader({}), +}); + describe('GitlabUrlReader', () => { const worker = setupServer(); @@ -98,7 +103,11 @@ describe('GitlabUrlReader', () => { }), }, ])('should handle happy path %#', async ({ url, config, response }) => { - const [{ reader }] = GitlabUrlReader.factory({ config, logger }); + const [{ reader }] = GitlabUrlReader.factory({ + config, + logger, + treeResponseFactory, + }); const data = await reader.read(url); const res = await JSON.parse(data.toString('utf-8')); @@ -114,7 +123,11 @@ describe('GitlabUrlReader', () => { }, ])('should handle error path %#', async ({ url, config, error }) => { await expect(async () => { - const [{ reader }] = GitlabUrlReader.factory({ config, logger }); + const [{ reader }] = GitlabUrlReader.factory({ + config, + logger, + treeResponseFactory, + }); await reader.read(url); }).rejects.toThrow(error); }); diff --git a/packages/backend-common/src/reading/UrlReaders.ts b/packages/backend-common/src/reading/UrlReaders.ts index e1d99a2c49..2bb5617907 100644 --- a/packages/backend-common/src/reading/UrlReaders.ts +++ b/packages/backend-common/src/reading/UrlReaders.ts @@ -23,6 +23,7 @@ import { BitbucketUrlReader } from './BitbucketUrlReader'; import { GithubUrlReader } from './GithubUrlReader'; import { GitlabUrlReader } from './GitlabUrlReader'; import { FetchUrlReader } from './FetchUrlReader'; +import { ReadTreeResponseFactory } from './tree'; type CreateOptions = { /** Root config object */ @@ -49,9 +50,10 @@ export class UrlReaders { fallback, }: CreateOptions): UrlReader { const mux = new UrlReaderPredicateMux({ fallback: fallback }); + const treeResponseFactory = ReadTreeResponseFactory.create({ config }); for (const factory of factories ?? []) { - const tuples = factory({ config, logger: logger }); + const tuples = factory({ config, logger: logger, treeResponseFactory }); for (const tuple of tuples) { mux.register(tuple); diff --git a/packages/backend-common/src/reading/types.ts b/packages/backend-common/src/reading/types.ts index 8a78e9d65f..bf53e98ef2 100644 --- a/packages/backend-common/src/reading/types.ts +++ b/packages/backend-common/src/reading/types.ts @@ -16,6 +16,7 @@ import { Logger } from 'winston'; import { Config } from '@backstage/config'; +import { ReadTreeResponseFactory } from './tree'; export type ReadTreeOptions = { /** A filter that can be used to select which files should be extracted. By default all files are extracted */ @@ -46,6 +47,7 @@ export type UrlReaderPredicateTuple = { export type ReaderFactory = (options: { config: Config; logger: Logger; + treeResponseFactory: ReadTreeResponseFactory; }) => UrlReaderPredicateTuple[]; export type File = { From 6417d966bb4117093883f6f64d15d7c6f339ec08 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 12 Nov 2020 13:53:22 +0100 Subject: [PATCH 149/252] backend-common,techdocs-backend: refactor .readTree to accept options and implement GitHub reader with response utils --- .../src/reading/GithubUrlReader.test.ts | 82 ++++++----- .../src/reading/GithubUrlReader.ts | 139 ++++++------------ .../src/reading/UrlReaderPredicateMux.ts | 14 +- .../src/reading/tree/ArchiveResponse.ts | 6 +- packages/backend-common/src/reading/types.ts | 6 +- plugins/techdocs-backend/src/helpers.test.ts | 54 +------ plugins/techdocs-backend/src/helpers.ts | 15 +- 7 files changed, 113 insertions(+), 203 deletions(-) diff --git a/packages/backend-common/src/reading/GithubUrlReader.test.ts b/packages/backend-common/src/reading/GithubUrlReader.test.ts index e165745790..658d908f84 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.test.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.test.ts @@ -29,8 +29,11 @@ import { } from './GithubUrlReader'; import fs from 'fs'; import path from 'path'; -import mockfs from 'mock-fs'; -import recursive from 'recursive-readdir'; +import { ReadTreeResponseFactory } from './tree'; + +const treeResponseFactory = ReadTreeResponseFactory.create({ + config: new ConfigReader({}), +}); describe('GithubUrlReader', () => { describe('getApiRequestOptions', () => { @@ -226,10 +229,13 @@ describe('GithubUrlReader', () => { describe('implementation', () => { it('rejects unknown targets', async () => { - const processor = new GithubUrlReader({ - host: 'github.com', - apiBaseUrl: 'https://api.github.com', - }); + const processor = new GithubUrlReader( + { + host: 'github.com', + apiBaseUrl: 'https://api.github.com', + }, + { treeResponseFactory }, + ); await expect( processor.read('https://not.github.com/apa'), ).rejects.toThrow( @@ -250,7 +256,7 @@ describe('GithubUrlReader', () => { beforeEach(() => { worker.use( rest.get( - 'https://github.com/spotify/mock/archive/repo.tar.gz', + 'https://github.com/backstage/mock/archive/repo.tar.gz', (_, res, ctx) => res( ctx.status(200), @@ -262,18 +268,20 @@ describe('GithubUrlReader', () => { }); it('returns the wanted files from an archive', async () => { - const processor = new GithubUrlReader({ - host: 'github.com', - }); + const processor = new GithubUrlReader( + { + host: 'github.com', + }, + { treeResponseFactory }, + ); const response = await processor.readTree( - 'https://github.com/spotify/mock', - 'repo', - ['mkdocs.yml', 'docs'], + 'https://github.com/backstage/mock/tree/repo', ); const files = await response.files(); + expect(files.length).toBe(2); const mkDocsFile = await files[0].content(); const indexMarkdownFile = await files[1].content(); @@ -281,33 +289,39 @@ describe('GithubUrlReader', () => { expect(indexMarkdownFile.toString()).toBe('# Test\n'); }); - it('returns a folder path from an archive', async () => { - const processor = new GithubUrlReader({ - host: 'github.com', - }); + it('must specify a branch', async () => { + const processor = new GithubUrlReader( + { + host: 'github.com', + }, + { treeResponseFactory }, + ); + + await expect( + processor.readTree('https://github.com/backstage/mock'), + ).rejects.toThrow( + 'GitHub URL must contain branch to be able to fetch tree', + ); + }); + + it('returns the wanted files from an archive with a subpath', async () => { + const processor = new GithubUrlReader( + { + host: 'github.com', + }, + { treeResponseFactory }, + ); const response = await processor.readTree( - 'https://github.com/spotify/mock', - 'repo', - ['mkdocs.yml', 'docs'], + 'https://github.com/backstage/mock/tree/repo/docs', ); - mockfs(); - const directory = await response.dir({ targetDir: '/tmp/fs' }); + const files = await response.files(); - const writtenToDirectory = fs.existsSync(directory); - const paths = await recursive(directory); - mockfs.restore(); + expect(files.length).toBe(1); + const indexMarkdownFile = await files[0].content(); - expect(writtenToDirectory).toBe(true); - expect(paths.sort()).toEqual( - [ - '/tmp/fs/mock-repo/docs/index.md', - '/tmp/fs/mock-repo/mkdocs.yml', - ].sort(), - ); - - worker.resetHandlers(); + expect(indexMarkdownFile.toString()).toBe('# Test\n'); }); }); }); diff --git a/packages/backend-common/src/reading/GithubUrlReader.ts b/packages/backend-common/src/reading/GithubUrlReader.ts index e3a47ee420..ca67a6cfcb 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.ts @@ -17,19 +17,15 @@ import { Config } from '@backstage/config'; import parseGitUri from 'git-url-parse'; import fetch from 'cross-fetch'; -import { NotFoundError } from '../errors'; +import { Readable } from 'stream'; +import { InputError, NotFoundError } from '../errors'; import { ReaderFactory, ReadTreeResponse, UrlReader, - File, - ReadTreeResponseDirOptions, + ReadTreeOptions, } from './types'; -import tar from 'tar'; -import fs from 'fs-extra'; -import concatStream from 'concat-stream'; -import path from 'path'; -import os from 'os'; +import { ReadTreeResponseFactory } from './tree'; /** * The configuration parameters for a single GitHub API provider. @@ -198,19 +194,18 @@ export function readConfig(config: Config): ProviderConfig[] { * the one exposed by GitHub itself. */ export class GithubUrlReader implements UrlReader { - private config: ProviderConfig; - - static factory: ReaderFactory = ({ config }) => { + static factory: ReaderFactory = ({ config, treeResponseFactory }) => { return readConfig(config).map(provider => { - const reader = new GithubUrlReader(provider); + const reader = new GithubUrlReader(provider, { treeResponseFactory }); const predicate = (url: URL) => url.host === provider.host; return { reader, predicate }; }); }; - constructor(config: ProviderConfig) { - this.config = config; - } + constructor( + private readonly config: ProviderConfig, + private readonly deps: { treeResponseFactory: ReadTreeResponseFactory }, + ) {} async read(url: string): Promise { const useApi = @@ -240,90 +235,48 @@ export class GithubUrlReader implements UrlReader { throw new Error(message); } - private async getRepositoryArchive( - repoUrl: string, - branchName: string, - ): Promise { - return fetch(new URL(`${repoUrl}/archive/${branchName}.tar.gz`).toString()); - } - - private async writeBufferToFile( - filePath: string, - content: Buffer, - ): Promise { - await fs.outputFile(filePath, content.toString()); - } - async readTree( - repoUrl: string, - branchName: string, - paths: Array, + url: string, + options?: ReadTreeOptions, ): Promise { - const { name: repoName } = parseGitUri(repoUrl); + const { + name: repoName, + ref, + protocol, + source, + full_name, + filepath, + } = parseGitUri(url); - const repoArchive = await this.getRepositoryArchive(repoUrl, branchName); + if (!ref) { + // TODO(Rugvip): We should add support for defaulting to the default branch + throw new InputError( + 'GitHub URL must contain branch to be able to fetch tree', + ); + } - const files: File[] = []; - return new Promise(resolve => { - const parser = new (tar.Parse as any)({ - filter: (path: string) => - !!paths.filter(file => { - return path.startsWith(`${repoName}-${branchName}/${file}`); - }).length, - onentry: (entry: tar.ReadEntry) => { - if (entry.type === 'Directory') { - entry.resume(); - return; - } + // TODO(Rugvip): use API to fetch URL instead + const response = await fetch( + new URL( + `${protocol}://${source}/${full_name}/archive/${ref}.tar.gz`, + ).toString(), + ); + if (!response.ok) { + const message = `Failed to read tree from ${url}, ${response.status} ${response.statusText}`; + if (response.status === 404) { + throw new NotFoundError(message); + } + throw new Error(message); + } - const contentPromise: Promise = new Promise(res => { - entry.pipe(concatStream(res)); - }); + const path = `${repoName}-${ref}/${filepath}`; - files.push({ - path: entry.path, - content: () => contentPromise, - }); - - entry.resume(); - }, - }); - - // @ts-ignore Typescript doesn't consider .pipe a method on ReadableStream. Don't know why. - repoArchive.body?.pipe(parser).on('finish', () => { - resolve({ - files: async () => { - return files; - }, - archive: () => { - return new Promise(resolve => - resolve(Buffer.from('Archive is not yet implemented')), - ); - }, - dir: (options?: ReadTreeResponseDirOptions) => { - const targetDirectory = - options?.targetDir || - fs.mkdtempSync(path.join(os.tmpdir(), 'backstage-')); - - return new Promise((res, rej) => { - Promise.all( - files.map(async file => { - return this.writeBufferToFile( - `${targetDirectory}/${file.path}`, - await file.content(), - ); - }), - ) - .then(() => { - res(`${targetDirectory}/${repoName}-${branchName}`); - }) - .catch(err => { - rej(err); - }); - }); - }, - }); - }); + return this.deps.treeResponseFactory.fromArchive({ + // TODO(Rugvip): Underlying implementation of fetch will be node-fetch, we probably want + // to stick to using that in exclusively backend code. + stream: (response.body as unknown) as Readable, + path, + filter: options?.filter, }); } diff --git a/packages/backend-common/src/reading/UrlReaderPredicateMux.ts b/packages/backend-common/src/reading/UrlReaderPredicateMux.ts index 74f94f0295..9b0566106a 100644 --- a/packages/backend-common/src/reading/UrlReaderPredicateMux.ts +++ b/packages/backend-common/src/reading/UrlReaderPredicateMux.ts @@ -14,7 +14,12 @@ * limitations under the License. */ -import { ReadTreeResponse, UrlReader, UrlReaderPredicateTuple } from './types'; +import { + ReadTreeOptions, + ReadTreeResponse, + UrlReader, + UrlReaderPredicateTuple, +} from './types'; type Options = { // UrlReader to fall back to if no other reader is matched @@ -55,14 +60,13 @@ export class UrlReaderPredicateMux implements UrlReader { readTree( repoUrl: string, - branchName: string, - paths: Array, + options?: ReadTreeOptions, ): Promise { const parsed = new URL(repoUrl); for (const { predicate, reader } of this.readers) { if (predicate(parsed)) { - if (reader.readTree) return reader.readTree(repoUrl, branchName, paths); + if (reader.readTree) return reader.readTree(repoUrl, options); throw new Error( `Trying to call readTree on UrlReader which does not support the feature.`, ); @@ -71,7 +75,7 @@ export class UrlReaderPredicateMux implements UrlReader { if (this.fallback) { if (this.fallback.readTree) - return this.fallback.readTree(repoUrl, branchName, paths); + return this.fallback.readTree(repoUrl, options); throw new Error( `Trying to call readTree on UrlReader which does not support the feature.`, ); diff --git a/packages/backend-common/src/reading/tree/ArchiveResponse.ts b/packages/backend-common/src/reading/tree/ArchiveResponse.ts index 83f6f6a8e5..0e06ff3804 100644 --- a/packages/backend-common/src/reading/tree/ArchiveResponse.ts +++ b/packages/backend-common/src/reading/tree/ArchiveResponse.ts @@ -42,10 +42,12 @@ export class ArchiveResponse implements ReadTreeResponse { ) { if (subPath) { if (!subPath.endsWith('/')) { - throw new TypeError('ArchiveResponse subPath must end with a /'); + this.subPath += '/'; } if (subPath.startsWith('/')) { - throw new TypeError('ArchiveResponse subPath must not start with a /'); + throw new TypeError( + `ArchiveResponse subPath must not start with a /, got '${subPath}'`, + ); } } } diff --git a/packages/backend-common/src/reading/types.ts b/packages/backend-common/src/reading/types.ts index bf53e98ef2..c7e5915401 100644 --- a/packages/backend-common/src/reading/types.ts +++ b/packages/backend-common/src/reading/types.ts @@ -28,11 +28,7 @@ export type ReadTreeOptions = { */ export type UrlReader = { read(url: string): Promise; - readTree?( - repoUrl: string, - branchName: string, - paths: Array, - ): Promise; + readTree?(url: string, options?: ReadTreeOptions): Promise; }; export type UrlReaderPredicateTuple = { diff --git a/plugins/techdocs-backend/src/helpers.test.ts b/plugins/techdocs-backend/src/helpers.test.ts index c3f2261637..5e98ab51c8 100644 --- a/plugins/techdocs-backend/src/helpers.test.ts +++ b/plugins/techdocs-backend/src/helpers.test.ts @@ -19,7 +19,7 @@ import { UrlReader, ReadTreeResponse } from '@backstage/backend-common'; import { Entity } from '@backstage/catalog-model'; describe('getDocFilesFromRepository', () => { - it('should take the directory from UrlReader.readTree and add the docs path when mkdocs.yml is in root', async () => { + it('should read a remote directory using UrlReader.readTree', async () => { class MockUrlReader implements UrlReader { async read() { return Buffer.from('mock'); @@ -45,7 +45,7 @@ describe('getDocFilesFromRepository', () => { namespace: 'default', annotations: { 'backstage.io/techdocs-ref': - 'url:https://github.com/backstage/backstage/blob/master/mkdocs.yml', + 'url:https://github.com/backstage/backstage/blob/master/subfolder/', }, name: 'mytestcomponent', description: 'A component for testing', @@ -64,54 +64,6 @@ describe('getDocFilesFromRepository', () => { mockEntity, ); - expect(output).toBe('/tmp/testfolder/.'); - }); - - it('should take the directory from UrlReader.readTree and add the docs path when mkdocs.yml is in a subfolder', async () => { - class MockUrlReader implements UrlReader { - async read() { - return Buffer.from('mock'); - } - - async readTree(): Promise { - return { - dir: async () => { - return '/tmp/testfolder'; - }, - files: async () => { - return []; - }, - archive: async () => { - return Buffer.from(''); - }, - }; - } - } - - const mockEntity: Entity = { - metadata: { - namespace: 'default', - annotations: { - 'backstage.io/techdocs-ref': - 'url:https://github.com/backstage/backstage/blob/master/subfolder/mkdocs.yml', - }, - name: 'mytestcomponent', - description: 'A component for testing', - }, - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - spec: { - type: 'documentation', - lifecycle: 'experimental', - owner: 'testuser', - }, - }; - - const output = await getDocFilesFromRepository( - new MockUrlReader(), - mockEntity, - ); - - expect(output).toBe('/tmp/testfolder/subfolder'); + expect(output).toBe('/tmp/testfolder'); }); }); diff --git a/plugins/techdocs-backend/src/helpers.ts b/plugins/techdocs-backend/src/helpers.ts index f861d4375e..97b087558f 100644 --- a/plugins/techdocs-backend/src/helpers.ts +++ b/plugins/techdocs-backend/src/helpers.ts @@ -179,21 +179,10 @@ export const getDocFilesFromRepository = async ( entity, ); - const { ref, filepath: mkdocsPath } = parseGitUrl(target); - - const docsRootPath = path.dirname(mkdocsPath); - const docsFolderPath = path.join(docsRootPath, 'docs'); - if (reader.readTree) { - const readTreeResponse = await reader.readTree( - parseGitUrl(target).toString(), - ref, - [mkdocsPath, docsFolderPath], - ); + const response = await reader.readTree(target); - const tmpDir = await readTreeResponse.dir(); - - return `${tmpDir}/${docsRootPath}`; + return response.dir(); } throw new Error( From b672492e2164a58b0224cfbdbd26f3e29479d7d1 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 12 Nov 2020 13:59:10 +0100 Subject: [PATCH 150/252] backend-common: make .readTree required --- .../backend-common/src/reading/AzureUrlReader.ts | 6 +++++- .../backend-common/src/reading/BitbucketUrlReader.ts | 6 +++++- .../backend-common/src/reading/FetchUrlReader.ts | 6 +++++- .../backend-common/src/reading/GitlabUrlReader.ts | 6 +++++- .../src/reading/UrlReaderPredicateMux.ts | 11 ++--------- packages/backend-common/src/reading/types.ts | 2 +- .../ingestion/processors/CodeOwnersProcessor.test.ts | 12 ++++++------ .../processors/PlaceholderProcessor.test.ts | 2 +- .../src/service/CatalogBuilder.test.ts | 5 ++++- plugins/techdocs-backend/src/helpers.ts | 10 ++-------- 10 files changed, 36 insertions(+), 30 deletions(-) diff --git a/packages/backend-common/src/reading/AzureUrlReader.ts b/packages/backend-common/src/reading/AzureUrlReader.ts index 8e6e6a75df..79058c79a9 100644 --- a/packages/backend-common/src/reading/AzureUrlReader.ts +++ b/packages/backend-common/src/reading/AzureUrlReader.ts @@ -17,7 +17,7 @@ import fetch from 'cross-fetch'; import { Config } from '@backstage/config'; import { NotFoundError } from '../errors'; -import { ReaderFactory, UrlReader } from './types'; +import { ReaderFactory, ReadTreeResponse, UrlReader } from './types'; type Options = { // TODO: added here for future support, but we only allow dev.azure.com for now @@ -86,6 +86,10 @@ export class AzureUrlReader implements UrlReader { throw new Error(message); } + readTree(): Promise { + throw new Error('AzureUrlReader does not implement readTree'); + } + // Converts // from: https://dev.azure.com/{organization}/{project}/_git/reponame?path={path}&version=GB{commitOrBranch}&_a=contents // to: https://dev.azure.com/{organization}/{project}/_apis/git/repositories/reponame/items?path={path}&version={commitOrBranch} diff --git a/packages/backend-common/src/reading/BitbucketUrlReader.ts b/packages/backend-common/src/reading/BitbucketUrlReader.ts index 348426fd34..b2fb9fa309 100644 --- a/packages/backend-common/src/reading/BitbucketUrlReader.ts +++ b/packages/backend-common/src/reading/BitbucketUrlReader.ts @@ -18,7 +18,7 @@ import { Config } from '@backstage/config'; import parseGitUri from 'git-url-parse'; import fetch from 'cross-fetch'; import { NotFoundError } from '../errors'; -import { ReaderFactory, UrlReader } from './types'; +import { ReaderFactory, ReadTreeResponse, UrlReader } from './types'; const DEFAULT_BASE_URL = 'https://api.bitbucket.org/2.0'; @@ -209,6 +209,10 @@ export class BitbucketUrlReader implements UrlReader { throw new Error(message); } + readTree(): Promise { + throw new Error('BitbucketUrlReader does not implement readTree'); + } + toString() { const { host, token, username, appPassword } = this.config; let authed = Boolean(token); diff --git a/packages/backend-common/src/reading/FetchUrlReader.ts b/packages/backend-common/src/reading/FetchUrlReader.ts index d2e5c45620..1d1784590c 100644 --- a/packages/backend-common/src/reading/FetchUrlReader.ts +++ b/packages/backend-common/src/reading/FetchUrlReader.ts @@ -16,7 +16,7 @@ import fetch from 'cross-fetch'; import { NotFoundError } from '../errors'; -import { UrlReader } from './types'; +import { ReadTreeResponse, UrlReader } from './types'; /** * A UrlReader that does a plain fetch of the URL. @@ -41,6 +41,10 @@ export class FetchUrlReader implements UrlReader { throw new Error(message); } + readTree(): Promise { + throw new Error('FetchUrlReader does not implement readTree'); + } + toString() { return 'fetch{}'; } diff --git a/packages/backend-common/src/reading/GitlabUrlReader.ts b/packages/backend-common/src/reading/GitlabUrlReader.ts index 378e76fb61..c621e41338 100644 --- a/packages/backend-common/src/reading/GitlabUrlReader.ts +++ b/packages/backend-common/src/reading/GitlabUrlReader.ts @@ -17,7 +17,7 @@ import fetch from 'cross-fetch'; import { Config } from '@backstage/config'; import { NotFoundError } from '../errors'; -import { ReaderFactory, UrlReader } from './types'; +import { ReaderFactory, ReadTreeResponse, UrlReader } from './types'; type Options = { host: string; @@ -87,6 +87,10 @@ export class GitlabUrlReader implements UrlReader { throw new Error(message); } + readTree(): Promise { + throw new Error('GitlabUrlReader does not implement readTree'); + } + // Converts // from: https://gitlab.example.com/a/b/blob/master/c.yaml // to: https://gitlab.example.com/a/b/raw/master/c.yaml diff --git a/packages/backend-common/src/reading/UrlReaderPredicateMux.ts b/packages/backend-common/src/reading/UrlReaderPredicateMux.ts index 9b0566106a..9a81fdc9ab 100644 --- a/packages/backend-common/src/reading/UrlReaderPredicateMux.ts +++ b/packages/backend-common/src/reading/UrlReaderPredicateMux.ts @@ -66,19 +66,12 @@ export class UrlReaderPredicateMux implements UrlReader { for (const { predicate, reader } of this.readers) { if (predicate(parsed)) { - if (reader.readTree) return reader.readTree(repoUrl, options); - throw new Error( - `Trying to call readTree on UrlReader which does not support the feature.`, - ); + return reader.readTree(repoUrl, options); } } if (this.fallback) { - if (this.fallback.readTree) - return this.fallback.readTree(repoUrl, options); - throw new Error( - `Trying to call readTree on UrlReader which does not support the feature.`, - ); + return this.fallback.readTree(repoUrl, options); } throw new Error(`No reader found that could handle '${repoUrl}'`); diff --git a/packages/backend-common/src/reading/types.ts b/packages/backend-common/src/reading/types.ts index c7e5915401..101502fed7 100644 --- a/packages/backend-common/src/reading/types.ts +++ b/packages/backend-common/src/reading/types.ts @@ -28,7 +28,7 @@ export type ReadTreeOptions = { */ export type UrlReader = { read(url: string): Promise; - readTree?(url: string, options?: ReadTreeOptions): Promise; + readTree(url: string, options?: ReadTreeOptions): Promise; }; export type UrlReaderPredicateTuple = { diff --git a/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts index e4ddb249d6..dfb470fa21 100644 --- a/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts @@ -157,14 +157,14 @@ describe('CodeOwnersProcessor', () => { const read = jest .fn() .mockResolvedValue(mockReadResult({ data: ownersText })); - const reader = { read }; + const reader = { read, readTree: jest.fn() }; const result = await findRawCodeOwners(mockLocation(), reader); expect(result).toEqual(ownersText); }); it('should raise error when no codeowner', async () => { const read = jest.fn().mockRejectedValue(mockReadResult()); - const reader = { read }; + const reader = { read, readTree: jest.fn() }; await expect( findRawCodeOwners(mockLocation(), reader), @@ -178,7 +178,7 @@ describe('CodeOwnersProcessor', () => { .mockImplementationOnce(() => mockReadResult({ error: 'foo' })) .mockImplementationOnce(() => mockReadResult({ error: 'bar' })) .mockResolvedValue(mockReadResult({ data: ownersText })); - const reader = { read }; + const reader = { read, readTree: jest.fn() }; const result = await findRawCodeOwners(mockLocation(), reader); @@ -197,7 +197,7 @@ describe('CodeOwnersProcessor', () => { const read = jest .fn() .mockResolvedValue(mockReadResult({ data: mockCodeOwnersText() })); - const reader = { read }; + const reader = { read, readTree: jest.fn() }; const owner = await resolveCodeOwner(mockLocation(), reader); expect(owner).toBe('backstage-core'); @@ -207,7 +207,7 @@ describe('CodeOwnersProcessor', () => { const read = jest .fn() .mockImplementation(() => mockReadResult({ error: 'error: foo' })); - const reader = { read }; + const reader = { read, readTree: jest.fn() }; await expect( resolveCodeOwner(mockLocation(), reader), @@ -221,7 +221,7 @@ describe('CodeOwnersProcessor', () => { const read = jest .fn() .mockResolvedValue(mockReadResult({ data: mockCodeOwnersText() })); - const reader = { read }; + const reader = { read, readTree: jest.fn() }; const processor = new CodeOwnersProcessor({ reader }); return { entity, processor, read }; diff --git a/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts index 48cd2dae01..5a3511af6d 100644 --- a/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts @@ -27,7 +27,7 @@ import { describe('PlaceholderProcessor', () => { const read: jest.MockedFunction = jest.fn(); - const reader: UrlReader = { read }; + const reader: UrlReader = { read, readTree: jest.fn() }; beforeEach(() => { jest.resetAllMocks(); diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.test.ts b/plugins/catalog-backend/src/service/CatalogBuilder.test.ts index 45d92e6555..ee7c6f2246 100644 --- a/plugins/catalog-backend/src/service/CatalogBuilder.test.ts +++ b/plugins/catalog-backend/src/service/CatalogBuilder.test.ts @@ -40,7 +40,10 @@ const dummyEntityYaml = yaml.stringify(dummyEntity); describe('CatalogBuilder', () => { let db: Knex; - const reader: jest.Mocked = { read: jest.fn() }; + const reader: jest.Mocked = { + read: jest.fn(), + readTree: jest.fn(), + }; const env: CatalogEnvironment = { logger: getVoidLogger(), database: { getClient: async () => db }, diff --git a/plugins/techdocs-backend/src/helpers.ts b/plugins/techdocs-backend/src/helpers.ts index 97b087558f..901aea533a 100644 --- a/plugins/techdocs-backend/src/helpers.ts +++ b/plugins/techdocs-backend/src/helpers.ts @@ -179,13 +179,7 @@ export const getDocFilesFromRepository = async ( entity, ); - if (reader.readTree) { - const response = await reader.readTree(target); + const response = await reader.readTree(target); - return response.dir(); - } - - throw new Error( - `No readTree method available on the UrlReader for ${target}`, - ); + return await response.dir(); }; From 8e2effb531a862c0415c6adf4dfb352421ccc07c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 12 Nov 2020 14:01:32 +0100 Subject: [PATCH 151/252] changesets: added changeset for UrlReader readTree refactoring --- .changeset/odd-camels-begin.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/odd-camels-begin.md diff --git a/.changeset/odd-camels-begin.md b/.changeset/odd-camels-begin.md new file mode 100644 index 0000000000..d28d729609 --- /dev/null +++ b/.changeset/odd-camels-begin.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': minor +--- + +Refactored UrlReader.readTree to be required and accept (url, options) From bd1cef1847bb2f2c75d7eda147a68a868f06b31e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 13 Nov 2020 00:43:20 +0100 Subject: [PATCH 152/252] backend-common: review comments for readTree + switch archive return type to a stream --- .../src/reading/UrlReaderPredicateMux.ts | 13 ++++------ .../src/reading/tree/ArchiveResponse.test.ts | 3 +-- .../src/reading/tree/ArchiveResponse.ts | 26 +++++++++---------- .../reading/tree/ReadTreeResponseFactory.ts | 2 +- packages/backend-common/src/reading/types.ts | 19 +++++++++++--- plugins/techdocs-backend/src/helpers.test.ts | 3 ++- 6 files changed, 37 insertions(+), 29 deletions(-) diff --git a/packages/backend-common/src/reading/UrlReaderPredicateMux.ts b/packages/backend-common/src/reading/UrlReaderPredicateMux.ts index 9a81fdc9ab..465c125fda 100644 --- a/packages/backend-common/src/reading/UrlReaderPredicateMux.ts +++ b/packages/backend-common/src/reading/UrlReaderPredicateMux.ts @@ -58,23 +58,20 @@ export class UrlReaderPredicateMux implements UrlReader { throw new Error(`No reader found that could handle '${url}'`); } - readTree( - repoUrl: string, - options?: ReadTreeOptions, - ): Promise { - const parsed = new URL(repoUrl); + readTree(url: string, options?: ReadTreeOptions): Promise { + const parsed = new URL(url); for (const { predicate, reader } of this.readers) { if (predicate(parsed)) { - return reader.readTree(repoUrl, options); + return reader.readTree(url, options); } } if (this.fallback) { - return this.fallback.readTree(repoUrl, options); + return this.fallback.readTree(url, options); } - throw new Error(`No reader found that could handle '${repoUrl}'`); + throw new Error(`No reader found that could handle '${url}'`); } toString() { diff --git a/packages/backend-common/src/reading/tree/ArchiveResponse.test.ts b/packages/backend-common/src/reading/tree/ArchiveResponse.test.ts index 6b8f29de8c..fd351863b2 100644 --- a/packages/backend-common/src/reading/tree/ArchiveResponse.test.ts +++ b/packages/backend-common/src/reading/tree/ArchiveResponse.test.ts @@ -16,7 +16,6 @@ import fs from 'fs-extra'; import mockFs from 'mock-fs'; -import { Readable } from 'stream'; import { resolve as resolvePath } from 'path'; import { ArchiveResponse } from './ArchiveResponse'; @@ -87,7 +86,7 @@ describe('ArchiveResponse', () => { 'Response has already been read', ); - const res2 = new ArchiveResponse(Readable.from(buffer), '', '/tmp'); + const res2 = new ArchiveResponse(buffer, '', '/tmp'); const files = await res2.files(); expect(files).toEqual([ diff --git a/packages/backend-common/src/reading/tree/ArchiveResponse.ts b/packages/backend-common/src/reading/tree/ArchiveResponse.ts index 0e06ff3804..e16be63127 100644 --- a/packages/backend-common/src/reading/tree/ArchiveResponse.ts +++ b/packages/backend-common/src/reading/tree/ArchiveResponse.ts @@ -14,14 +14,17 @@ * limitations under the License. */ -import os from 'os'; import tar, { Parse, ParseStream, ReadEntry } from 'tar'; import path from 'path'; import fs from 'fs-extra'; import { Readable, pipeline as pipelineCb } from 'stream'; import { promisify } from 'util'; import concatStream from 'concat-stream'; -import { ReadTreeResponse, File, ReadTreeResponseDirOptions } from '../types'; +import { + ReadTreeResponse, + ReadTreeResponseFile, + ReadTreeResponseDirOptions, +} from '../types'; // Tar types for `Parse` is not a proper constructor, but it should be const TarParseStream = (Parse as unknown) as { new (): ParseStream }; @@ -37,7 +40,7 @@ export class ArchiveResponse implements ReadTreeResponse { constructor( private readonly stream: Readable, private readonly subPath: string, - private readonly workDir: string = os.tmpdir(), + private readonly workDir: string, private readonly filter?: (path: string) => boolean, ) { if (subPath) { @@ -60,10 +63,10 @@ export class ArchiveResponse implements ReadTreeResponse { this.read = true; } - async files(): Promise { + async files(): Promise { this.onlyOnce(); - const files: File[] = []; + const files = Array(); const parser = new TarParseStream(); parser.on('entry', (entry: ReadEntry & Readable) => { @@ -79,9 +82,7 @@ export class ArchiveResponse implements ReadTreeResponse { } } - const path = this.subPath - ? entry.path.replace(this.subPath, '') - : entry.path; + const path = entry.path.slice(this.subPath.length); if (this.filter) { if (!this.filter(path)) { entry.resume(); @@ -103,13 +104,11 @@ export class ArchiveResponse implements ReadTreeResponse { return files; } - async archive(): Promise { + async archive(): Promise { if (!this.subPath) { this.onlyOnce(); - return new Promise(resolve => - pipeline(this.stream, concatStream(resolve)), - ); + return this.stream; } // TODO(Rugvip): method for repacking a tar with a subpath is to simply extract into a @@ -117,12 +116,13 @@ export class ArchiveResponse implements ReadTreeResponse { const tmpDir = await this.dir(); try { - return await new Promise(async resolve => { + const data = await new Promise(async resolve => { await pipeline( tar.create({ cwd: tmpDir }, ['']), concatStream(resolve), ); }); + return Readable.from(data); } finally { await fs.remove(tmpDir); } diff --git a/packages/backend-common/src/reading/tree/ReadTreeResponseFactory.ts b/packages/backend-common/src/reading/tree/ReadTreeResponseFactory.ts index 5044cdabdc..a134d185f3 100644 --- a/packages/backend-common/src/reading/tree/ReadTreeResponseFactory.ts +++ b/packages/backend-common/src/reading/tree/ReadTreeResponseFactory.ts @@ -23,7 +23,7 @@ import { ArchiveResponse } from './ArchiveResponse'; type FromArchiveOptions = { // A binary stream of a tar archive. stream: Readable; - // If set, root of the tree will be set to the given path. Should not have a trailing `/`. + // If set, the root of the tree will be set to the given directory path. path?: string; // Filter passed on from the ReadTreeOptions filter?: (path: string) => boolean; diff --git a/packages/backend-common/src/reading/types.ts b/packages/backend-common/src/reading/types.ts index 101502fed7..f9dca3e1d5 100644 --- a/packages/backend-common/src/reading/types.ts +++ b/packages/backend-common/src/reading/types.ts @@ -19,7 +19,18 @@ import { Config } from '@backstage/config'; import { ReadTreeResponseFactory } from './tree'; export type ReadTreeOptions = { - /** A filter that can be used to select which files should be extracted. By default all files are extracted */ + /** + * A filter that can be used to select which files should be included. + * + * The path passed to the filter function is the relative path from the URL + * that the file tree is fetched from, without any leading '/'. + * + * For example, given the URL https://github.com/my/repo/tree/master/my-dir, a file + * at https://github.com/my/repo/blob/master/my-dir/my-subdir/my-file.txt will + * be represented as my-subdir/my-file.txt + * + * If no filter is provided all files are extracted. + */ filter?(path: string): boolean; }; @@ -46,7 +57,7 @@ export type ReaderFactory = (options: { treeResponseFactory: ReadTreeResponseFactory; }) => UrlReaderPredicateTuple[]; -export type File = { +export type ReadTreeResponseFile = { path: string; content(): Promise; }; @@ -57,7 +68,7 @@ export type ReadTreeResponseDirOptions = { }; export type ReadTreeResponse = { - files(): Promise; - archive(): Promise; + files(): Promise; + archive(): Promise; dir(options?: ReadTreeResponseDirOptions): Promise; }; diff --git a/plugins/techdocs-backend/src/helpers.test.ts b/plugins/techdocs-backend/src/helpers.test.ts index 5e98ab51c8..10518df74f 100644 --- a/plugins/techdocs-backend/src/helpers.test.ts +++ b/plugins/techdocs-backend/src/helpers.test.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Readable } from 'stream'; import { getDocFilesFromRepository } from './helpers'; import { UrlReader, ReadTreeResponse } from '@backstage/backend-common'; import { Entity } from '@backstage/catalog-model'; @@ -34,7 +35,7 @@ describe('getDocFilesFromRepository', () => { return []; }, archive: async () => { - return Buffer.from(''); + return Readable.from(''); }, }; } From a51d475899f17083d3f266d084dbcbc67ce040ba Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Fri, 13 Nov 2020 02:51:11 -0500 Subject: [PATCH 153/252] Standarize code fence and wording tweaks (#3285) --- contrib/chart/backstage/README.md | 30 ++++++++++++++---------------- docs/api/utility-apis.md | 8 ++++---- docs/conf/index.md | 4 ++-- docs/conf/writing.md | 11 +++++++---- 4 files changed, 27 insertions(+), 26 deletions(-) diff --git a/contrib/chart/backstage/README.md b/contrib/chart/backstage/README.md index 7b60062265..96bfdb4351 100644 --- a/contrib/chart/backstage/README.md +++ b/contrib/chart/backstage/README.md @@ -7,7 +7,7 @@ This folder contains Helm charts that can easily create a Kubernetes deployment These charts depend on the `nginx-ingress` controller being present in the cluster. If it's not already installed you can run: -``` +```shell helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx helm install nginx-ingress ingress-nginx/ingress-nginx ``` @@ -16,7 +16,7 @@ helm install nginx-ingress ingress-nginx/ingress-nginx After choosing a DNS name where backstage will be hosted create a yaml file for your custom configuration. -``` +```yaml appConfig: app: baseUrl: https://backstage.mydomain.com @@ -30,12 +30,11 @@ appConfig: techdocs: storageUrl: https://backstage.mydomain.com/api/techdocs/static/docs requestUrl: https://backstage.mydomain.com/api/techdocs - ``` Then use it to run: -``` +```shell git clone https://github.com/backstage/backstage.git cd contrib/chart/backstage helm dependency update @@ -54,7 +53,7 @@ After a few minutes Backstage should be up and running in your cluster under the Make sure to create the appropriate DNS entry in your infrastructure. To find the public IP address run: -```bash +```shell $ kubectl get ingress NAME HOSTS ADDRESS PORTS AGE backstage-ingress * 123.1.2.3 80 17m @@ -74,10 +73,10 @@ These charts can install or reuse a `clusterIssuer` to generate certificates for To enable it you need to provide a valid email address in the chart's values: -``` +```yaml issuer: email: me@example.com - clusterIssuer: "letsencrypt-prod" + clusterIssuer: 'letsencrypt-prod' ``` By default, the charts use `letsencrypt-staging` so in the above example we instruct helm to use the production issuer @@ -92,7 +91,7 @@ Configuring a connection to an existing PostgreSQL instance is possible through First create a yaml file with the configuration you want to override, for example `backstage-prod.yaml`: -```bash +```yaml postgresql: enabled: false @@ -122,12 +121,11 @@ lighthouse: user: password: database: lighthouse_audit_service - ``` For the CA, create a `configMap` named `--postgres-ca` with a file called `ca.crt`: -``` +```shell kubectl create configmap my-company-backstage-postgres-ca --from-file=ca.crt" ``` @@ -135,7 +133,7 @@ kubectl create configmap my-company-backstage-postgres-ca --from-file=ca.crt" Now install the helm chart: -``` +```shell cd contrib/chart/backstage helm install -f backstage-prod.yaml my-backstage . ``` @@ -144,7 +142,7 @@ helm install -f backstage-prod.yaml my-backstage . The docker images used for the deployment can be configured through the charts values: -``` +```yaml frontend: image: repository: @@ -165,7 +163,7 @@ lighthouse: Create a docker-registry secret -``` +```shell kubectl create secret docker-registry # args ``` @@ -181,7 +179,7 @@ dockerRegistrySecretName: To install the charts a specific namespace use `--namespace `: -``` +```shell helm install -f my_values.yaml --namespace demos backstage . ``` @@ -189,7 +187,7 @@ helm install -f my_values.yaml --namespace demos backstage . To deploy backstage with the pre-loaded demo data disable `backend.demoData`: -``` +```shell helm install -f my_values.yaml --set backend.demoData=false backstage . ``` @@ -244,7 +242,7 @@ This error happens in the backend when it tries to connect to the configured Pos To uninstall Backstage simply run: -``` +```shell RELEASE_NAME= # use `helm list` to find out the name helm uninstall ${RELEASE_NAME} kubectl delete pvc data-${RELEASE_NAME}-postgresql-0 diff --git a/docs/api/utility-apis.md b/docs/api/utility-apis.md index a4d154b44b..2547db5c5c 100644 --- a/docs/api/utility-apis.md +++ b/docs/api/utility-apis.md @@ -11,7 +11,7 @@ possible residing within the plugin itself and its backend APIs. There will however always be a need for plugins to communicate outside of its boundaries, both with other plugins and the app itself. -Backstage provides two primary methods for plugins to communication across their +Backstage provides two primary methods for plugins to communicate across their boundaries in client-side code. The first one being the `createPlugin` API and the registration hooks passed to the `register` method, and the second one being Utility APIs. While the `createPlugin` API is focused on the initialization @@ -195,8 +195,8 @@ interface for the API, and create an `ApiRef` using `createApiRef` exported from `@backstage/core`. Also be sure to provide at least one implementation of the API, and to declare a default factory for the API in `createPlugin`. -Custom Utility APIs can be either public or private, which it is up to the -plugin to choose. Private APIs do not expose an external API surface, and it's +Custom Utility APIs can be either public or private, which is up to the plugin +to choose. Private APIs do not expose an external API surface, and it's therefore possible to make breaking changes to the API without affecting other users of the plugin. If an API is made public however, it opens up for other plugins to make use of the API, and it also makes it possible for users for your @@ -243,7 +243,7 @@ The figure below shows the relationship between The current method for connecting Utility API providers and consumers is via the React tree using an `ApiProvider`, which is added to the `AppProvider` of the `App`. In the future there may potentially be more ways to do this, in ways that -are not tied to react. A design goal of the Utility APIs was to not have them +are not tied to React. A design goal of the Utility APIs was to not have them directly tied to React. The indirection provided by Utility APIs also makes it straightforward to test diff --git a/docs/conf/index.md b/docs/conf/index.md index 2c3ee06ba6..5e4639cc99 100644 --- a/docs/conf/index.md +++ b/docs/conf/index.md @@ -28,8 +28,8 @@ development or small tweaks to be able to reuse deployment artifacts in different environments. The configuration is shared between the frontend and backend, meaning that -values that are common between the two only needs to be defined once. Such as -the `backend.baseUrl`. +values that are common between the two only need to be defined once. Such as the +`backend.baseUrl`. For more details, see [Writing Configuration](./writing.md). diff --git a/docs/conf/writing.md b/docs/conf/writing.md index 4eb92bb2ff..6b60c015ae 100644 --- a/docs/conf/writing.md +++ b/docs/conf/writing.md @@ -74,7 +74,7 @@ Note that if any config flags are provided, the default `app-config.yaml` files 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 ``` @@ -99,8 +99,8 @@ order: ## Secrets -Secrets are supported via a special secret keys that are prefixed with `$`, -which in turn provides a number of different ways to read in secrets. To load a +Secrets are supported via special secret keys that are prefixed with `$`, which +in turn provide a number of different ways to read in secrets. To load a configuration value as a secret, supply an object with one of the special secret keys, for example `$env` or `$file`. A full list of supported secret keys can be found below. For example, the following will read the config key @@ -155,8 +155,11 @@ used to point to a specific value inside the file. Supported file extensions are ```yaml $data: ./my-secrets.json#deployment.key +``` -# my-secrets.json +Example `my-secrets.json` file: + +```json { "deployment": { "key": "my-secret-key" From 1b11e7b70807114b9e01a3ec692891a744643760 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Fri, 13 Nov 2020 02:52:27 -0500 Subject: [PATCH 154/252] Fix broken link from Docs site to Code of Conduct (#3284) * Fix link * Add clarifications --- docs/support/support.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/support/support.md b/docs/support/support.md index 797aca041f..49075366af 100644 --- a/docs/support/support.md +++ b/docs/support/support.md @@ -5,15 +5,16 @@ description: Support and Community Details and Links --- - [Discord chatroom](https://discord.gg/MUpMjP2) - Get support or discuss the - project + project. - [Good First Issues](https://github.com/backstage/backstage/contribute) - Start - here if you want to contribute + here if you want to contribute. - [RFCs](https://github.com/backstage/backstage/labels/rfc) - Help shape the - technical direction -- [FAQ](../FAQ.md) - Frequently Asked Questions -- [Code of Conduct](../../CODE_OF_CONDUCT.md) - This is how we roll -- [Blog](https://backstage.io/blog/) - Announcements and updates + technical direction by reviewing _Request for Comments_ issues. +- [FAQ](../FAQ.md) - Frequently Asked Questions. +- [Code of Conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md) - + This is how we roll. +- [Blog](https://backstage.io/blog/) - Announcements and updates. - [Newsletter](https://mailchi.mp/spotify/backstage-community) - Subscribe to - our email newsletter + our email newsletter. - Give us a star ⭐️ - If you are using Backstage or think it is an interesting - project, we would love a star ❤️ + project, we would love a star! ❤️ From d21e4172b5d4a269244bae30ffd16bff9d2a3c9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 13 Nov 2020 10:04:33 +0100 Subject: [PATCH 155/252] auth-backend: change messaging around missing email (#3287) --- plugins/auth-backend/src/providers/auth0/provider.ts | 2 +- plugins/auth-backend/src/providers/oauth2/provider.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/auth-backend/src/providers/auth0/provider.ts b/plugins/auth-backend/src/providers/auth0/provider.ts index 668ab17ee1..f851fb1eb5 100644 --- a/plugins/auth-backend/src/providers/auth0/provider.ts +++ b/plugins/auth-backend/src/providers/auth0/provider.ts @@ -139,7 +139,7 @@ export class Auth0AuthProvider implements OAuthHandlers { const { profile } = response; if (!profile.email) { - throw new Error('Profile does not contain a profile'); + throw new Error('Profile does not contain an email'); } const id = profile.email.split('@')[0]; diff --git a/plugins/auth-backend/src/providers/oauth2/provider.ts b/plugins/auth-backend/src/providers/oauth2/provider.ts index a657ea2195..6342d556e1 100644 --- a/plugins/auth-backend/src/providers/oauth2/provider.ts +++ b/plugins/auth-backend/src/providers/oauth2/provider.ts @@ -148,7 +148,7 @@ export class OAuth2AuthProvider implements OAuthHandlers { const { profile } = response; if (!profile.email) { - throw new Error('Profile does not contain a profile'); + throw new Error('Profile does not contain an email'); } const id = profile.email.split('@')[0]; From f531d307c4b9c8fb98f0d4b1b3006dd6023a8961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 13 Nov 2020 11:16:33 +0100 Subject: [PATCH 156/252] catalog-backend: reject overwrite of entities from foreign locations (#3286) --- .changeset/hip-experts-dance.md | 5 +++++ .../catalog/DatabaseEntitiesCatalog.test.ts | 9 ++++++++- .../src/catalog/DatabaseEntitiesCatalog.ts | 20 +++++++++++++++---- 3 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 .changeset/hip-experts-dance.md diff --git a/.changeset/hip-experts-dance.md b/.changeset/hip-experts-dance.md new file mode 100644 index 0000000000..f88fe62cd4 --- /dev/null +++ b/.changeset/hip-experts-dance.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +An entity A, that exists in the catalog, can no longer be overwritten by registering a different location that also tries to supply an entity with the same kind+namespace+name. Writes of that new entity will instead be rejected with a log message similar to `Rejecting write of entity Component:default/artist-lookup from file:/Users/freben/dev/github/backstage/packages/catalog-model/examples/components/artist-lookup-component.yaml because entity existed from github:https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/artist-lookup-component.yaml` diff --git a/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.test.ts b/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.test.ts index 00898b33a6..8d2138b3e5 100644 --- a/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.test.ts @@ -15,7 +15,7 @@ */ import { getVoidLogger } from '@backstage/backend-common'; -import type { Entity } from '@backstage/catalog-model'; +import { Entity, LOCATION_ANNOTATION } from '@backstage/catalog-model'; import { Database, DatabaseManager, Transaction } from '../database'; import { EntityFilters } from '../service/EntityFilters'; import { DatabaseEntitiesCatalog } from './DatabaseEntitiesCatalog'; @@ -127,6 +127,9 @@ describe('DatabaseEntitiesCatalog', () => { metadata: { name: 'c', namespace: 'd', + annotations: { + [LOCATION_ANNOTATION]: 'mock', + }, }, }; const dbEntity: Entity = { @@ -135,7 +138,11 @@ describe('DatabaseEntitiesCatalog', () => { metadata: { name: 'c', namespace: 'd', + description: 'changes', uid: 'u', + annotations: { + [LOCATION_ANNOTATION]: 'mock', + }, }, }; db.entities.mockResolvedValue([ diff --git a/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.ts b/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.ts index 5c55a30a55..f40e327eaa 100644 --- a/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.ts @@ -205,9 +205,11 @@ export class DatabaseEntitiesCatalog implements EntitiesCatalog { // TODO(Rugvip): We currently always update relations, but we // likely want to figure out a way to avoid that for (const { entity, relations } of toIgnore) { - const entityId = entity.metadata.uid!; - await this.setRelations(entityId, relations, tx); - modifiedEntityIds.push({ entityId }); + const entityId = entity.metadata.uid; + if (entityId) { + await this.setRelations(entityId, relations, tx); + modifiedEntityIds.push({ entityId }); + } } break; @@ -300,12 +302,22 @@ export class DatabaseEntitiesCatalog implements EntitiesCatalog { for (const request of requests) { const newEntity = request.entity; const oldEntity = oldEntitiesByName.get(newEntity.metadata.name); + const newLocation = newEntity.metadata.annotations?.[LOCATION_ANNOTATION]; + const oldLocation = + oldEntity?.metadata.annotations?.[LOCATION_ANNOTATION]; if (!oldEntity) { toAdd.push(request); + } else if (oldLocation !== newLocation) { + this.logger.warn( + `Rejecting write of entity ${serializeEntityRef( + newEntity, + )} from ${newLocation} because entity existed from ${oldLocation}`, + ); + toIgnore.push(request); } else if (entityHasChanges(oldEntity, newEntity)) { // TODO(freben): This currently uses addOrUpdateEntity under the hood, // but should probably calculate the end result entity right here - // instead and call a dedicated batch update database method instead + // instead and call a dedicated batch update database method toUpdate.push(request); } else { toIgnore.push(request); From 7a46fa07d14decc197cca72806f68ea3571b43b6 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Fri, 13 Nov 2020 15:22:24 +0100 Subject: [PATCH 157/252] TechDocs project: Use org token GH_SERVICE_ACCOUNT_TOKEN --- .github/workflows/techdocs-project-board.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/techdocs-project-board.yml b/.github/workflows/techdocs-project-board.yml index a2fb980259..a8d476f713 100644 --- a/.github/workflows/techdocs-project-board.yml +++ b/.github/workflows/techdocs-project-board.yml @@ -10,7 +10,7 @@ on: types: [opened, reopened, labeled, edited] env: - MY_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + MY_GITHUB_TOKEN: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} jobs: assign_issue_or_pr_to_project: From 053fc56745b5cc4e6209222032934729b06da047 Mon Sep 17 00:00:00 2001 From: Brenda Sukh Date: Fri, 13 Nov 2020 10:15:32 -0500 Subject: [PATCH 158/252] Move out utility functions for mock data to utils --- plugins/cost-insights/src/client.ts | 39 +++------------------ plugins/cost-insights/src/utils/mockData.ts | 30 ++++++++++++++++ 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/plugins/cost-insights/src/client.ts b/plugins/cost-insights/src/client.ts index 740a44fcbd..74d4954ea4 100644 --- a/plugins/cost-insights/src/client.ts +++ b/plugins/cost-insights/src/client.ts @@ -16,11 +16,9 @@ /* eslint-disable no-restricted-imports */ import dayjs from 'dayjs'; -import regression, { DataPoint } from 'regression'; import { CostInsightsApi, ProductInsightsOptions } from '../src/api'; import { Alert, - ChangeStatistic, Cost, DateAggregation, DEFAULT_DATE_FORMAT, @@ -30,24 +28,21 @@ import { MetricData, Project, ProjectGrowthData, - Trendline, UnlabeledDataflowData, } from '../src/types'; import { ProjectGrowthAlert, UnlabeledDataflowAlert, } from '../src/utils/alerts'; -import { - exclusiveEndDateOf, - inclusiveStartDateOf, -} from '../src/utils/duration'; +import { inclusiveStartDateOf } from '../src/utils/duration'; +import { trendlineOf, changeOf } from './utils/mockData'; type IntervalFields = { duration: Duration; endDate: string; }; -function parseIntervals(intervals: string): IntervalFields { +export function parseIntervals(intervals: string): IntervalFields { const match = intervals.match( /\/(?P\d+[DM])\/(?\d{4}-\d{2}-\d{2})/, ); @@ -66,7 +61,7 @@ function aggregationFor( baseline: number, ): DateAggregation[] { const { duration, endDate } = parseIntervals(intervals); - const days = dayjs(exclusiveEndDateOf(duration, endDate)).diff( + const days = dayjs(endDate).diff( inclusiveStartDateOf(duration, endDate), 'day', ); @@ -86,32 +81,6 @@ function aggregationFor( ); } -function trendlineOf(aggregation: DateAggregation[]): Trendline { - const data: ReadonlyArray = aggregation.map(a => [ - Date.parse(a.date) / 1000, - a.amount, - ]); - const result = regression.linear(data, { precision: 5 }); - return { - slope: result.equation[0], - intercept: result.equation[1], - }; -} - -function changeOf(aggregation: DateAggregation[]): ChangeStatistic { - const half = Math.ceil(aggregation.length / 2); - const before = aggregation - .slice(0, half) - .reduce((sum, a) => sum + a.amount, 0); - const after = aggregation - .slice(half, aggregation.length) - .reduce((sum, a) => sum + a.amount, 0); - return { - ratio: (after - before) / before, - amount: after - before, - }; -} - export class ExampleCostInsightsClient implements CostInsightsApi { private request(_: any, res: any): Promise { return new Promise(resolve => setTimeout(resolve, 0, res)); diff --git a/plugins/cost-insights/src/utils/mockData.ts b/plugins/cost-insights/src/utils/mockData.ts index ddeb4636b9..f5ad21854a 100644 --- a/plugins/cost-insights/src/utils/mockData.ts +++ b/plugins/cost-insights/src/utils/mockData.ts @@ -14,16 +14,20 @@ * limitations under the License. */ +import regression, { DataPoint } from 'regression'; import { Config } from '@backstage/config'; import { ConfigApi } from '@backstage/core'; import { + ChangeStatistic, Duration, Entity, Product, ProductFilters, ProjectGrowthData, + Trendline, UnlabeledDataflowAlertProject, UnlabeledDataflowData, + DateAggregation, } from '../types'; import { DefaultLoadingAction, @@ -185,3 +189,29 @@ export const MockCostInsightsConfig: Partial = { getConfig: () => MockProductsConfig as Config, getOptionalConfig: () => MockMetricsConfig as Config, }; + +export function trendlineOf(aggregation: DateAggregation[]): Trendline { + const data: ReadonlyArray = aggregation.map(a => [ + Date.parse(a.date) / 1000, + a.amount, + ]); + const result = regression.linear(data, { precision: 5 }); + return { + slope: result.equation[0], + intercept: result.equation[1], + }; +} + +export function changeOf(aggregation: DateAggregation[]): ChangeStatistic { + const half = Math.ceil(aggregation.length / 2); + const before = aggregation + .slice(0, half) + .reduce((sum, a) => sum + a.amount, 0); + const after = aggregation + .slice(half, aggregation.length) + .reduce((sum, a) => sum + a.amount, 0); + return { + ratio: (after - before) / before, + amount: after - before, + }; +} From 3ec3fc48a7a7065e454309109a9f50b94c0ca4e2 Mon Sep 17 00:00:00 2001 From: Brenda Sukh Date: Fri, 13 Nov 2020 10:15:50 -0500 Subject: [PATCH 159/252] Fix compared change calculation --- plugins/cost-insights/src/utils/change.ts | 24 +++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/plugins/cost-insights/src/utils/change.ts b/plugins/cost-insights/src/utils/change.ts index cc797bb3b0..79f653158a 100644 --- a/plugins/cost-insights/src/utils/change.ts +++ b/plugins/cost-insights/src/utils/change.ts @@ -21,8 +21,10 @@ import { EngineerThreshold, GrowthType, MetricData, + Duration, } from '../types'; import { aggregationSort } from '../utils/sort'; +import moment from 'moment'; // Used for displaying status colors export function growthOf(ratio: number, amount?: number) { @@ -45,11 +47,29 @@ export function growthOf(ratio: number, amount?: number) { export function getComparedChange( dailyCost: Cost, metricData: MetricData, + duration: Duration, ): ChangeStatistic { const ratio = dailyCost.change.ratio - metricData.change.ratio; - const amount = dailyCost.aggregation.slice().sort(aggregationSort)[0].amount; + const previousPeriodTotal = getPreviousPeriodTotalCost(dailyCost, duration); return { ratio: ratio, - amount: amount * ratio, + amount: previousPeriodTotal * ratio, }; } + +export function getPreviousPeriodTotalCost( + dailyCost: Cost, + duration: Duration, +): number { + const costsByDate = dailyCost.aggregation.slice().sort(aggregationSort); + const nextPeriodStart = moment(costsByDate[0].date).add( + moment.duration(duration), + ); + + // Add up costs that incurred before the start of the next period. + return costsByDate.reduce((acc, costByDate) => { + return moment(costByDate.date).isBefore(nextPeriodStart) + ? acc + costByDate.amount + : acc; + }, 0); +} From 83e3df8a579020d8cfe17e4cac6453f557daa2b7 Mon Sep 17 00:00:00 2001 From: Brenda Sukh Date: Fri, 13 Nov 2020 10:16:38 -0500 Subject: [PATCH 160/252] Add mock daily cost data for test purposes --- .../CostOverviewCard/CostOverviewCard.tsx | 2 +- .../cost-insights/src/utils/change.test.ts | 25 +- plugins/cost-insights/src/utils/mockData.ts | 247 ++++++++++++++++++ 3 files changed, 271 insertions(+), 3 deletions(-) diff --git a/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.tsx b/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.tsx index 8f52a0070b..358c7a98aa 100644 --- a/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.tsx +++ b/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.tsx @@ -50,7 +50,7 @@ export const CostOverviewCard = ({ ? findAlways(config.metrics, m => m.kind === filters.metric) : null; const comparedChange = metricData - ? getComparedChange(dailyCostData, metricData) + ? getComparedChange(dailyCostData, metricData, filters.duration) : null; return ( diff --git a/plugins/cost-insights/src/utils/change.test.ts b/plugins/cost-insights/src/utils/change.test.ts index daf4538f2c..709f2dd882 100644 --- a/plugins/cost-insights/src/utils/change.test.ts +++ b/plugins/cost-insights/src/utils/change.test.ts @@ -14,8 +14,15 @@ * limitations under the License. */ -import { growthOf } from './change'; -import { GrowthType, ChangeThreshold, EngineerThreshold } from '../types'; +import { growthOf, getPreviousPeriodTotalCost } from './change'; +import { + GrowthType, + ChangeThreshold, + EngineerThreshold, + Duration, + Cost, +} from '../types'; +import { MockAggregatedDailyCosts, trendlineOf, changeOf } from './mockData'; const GrowthMap = { [GrowthType.Negligible]: 'negligible growth', @@ -60,3 +67,17 @@ describe.each` }); }, ); + +describe('getPreviousPeriodTotalCost', () => { + it('Correctly returns the total cost for the previous period given daily costs', () => { + const mockGroupDailyCost: Cost = { + id: 'test-group', + aggregation: MockAggregatedDailyCosts, + change: changeOf(MockAggregatedDailyCosts), + trendline: trendlineOf(MockAggregatedDailyCosts), + }; + expect( + getPreviousPeriodTotalCost(mockGroupDailyCost, Duration.P1M), + ).toEqual(100_000); + }); +}); diff --git a/plugins/cost-insights/src/utils/mockData.ts b/plugins/cost-insights/src/utils/mockData.ts index f5ad21854a..708ce733dd 100644 --- a/plugins/cost-insights/src/utils/mockData.ts +++ b/plugins/cost-insights/src/utils/mockData.ts @@ -215,3 +215,250 @@ export function changeOf(aggregation: DateAggregation[]): ChangeStatistic { amount: after - before, }; } + +export const MockAggregatedDailyCosts: DateAggregation[] = [ + { + date: '2020-08-07', + amount: 3500, + }, + { + date: '2020-08-06', + amount: 2500, + }, + { + date: '2020-08-05', + amount: 1400, + }, + { + date: '2020-08-04', + amount: 3800, + }, + { + date: '2020-08-09', + amount: 1900, + }, + { + date: '2020-08-08', + amount: 2400, + }, + { + date: '2020-08-03', + amount: 4000, + }, + { + date: '2020-08-02', + amount: 3700, + }, + { + date: '2020-08-01', + amount: 2500, + }, + { + date: '2020-08-18', + amount: 4300, + }, + { + date: '2020-08-17', + amount: 1500, + }, + { + date: '2020-08-16', + amount: 3600, + }, + { + date: '2020-08-15', + amount: 2200, + }, + { + date: '2020-08-19', + amount: 3900, + }, + { + date: '2020-08-10', + amount: 4100, + }, + { + date: '2020-08-14', + amount: 3600, + }, + { + date: '2020-08-13', + amount: 2900, + }, + { + date: '2020-08-12', + amount: 2700, + }, + { + date: '2020-08-11', + amount: 5100, + }, + { + date: '2020-09-19', + amount: 1200, + }, + { + date: '2020-09-18', + amount: 6500, + }, + { + date: '2020-09-17', + amount: 2500, + }, + { + date: '2020-09-16', + amount: 1400, + }, + { + date: '2020-09-11', + amount: 2300, + }, + { + date: '2020-09-10', + amount: 1900, + }, + { + date: '2020-09-15', + amount: 3100, + }, + { + date: '2020-09-14', + amount: 4500, + }, + { + date: '2020-09-13', + amount: 3300, + }, + { + date: '2020-09-12', + amount: 2800, + }, + { + date: '2020-09-29', + amount: 2600, + }, + { + date: '2020-09-28', + amount: 4100, + }, + { + date: '2020-09-27', + amount: 3800, + }, + { + date: '2020-09-22', + amount: 3700, + }, + { + date: '2020-09-21', + amount: 2700, + }, + { + date: '2020-09-20', + amount: 2200, + }, + { + date: '2020-09-26', + amount: 3300, + }, + { + date: '2020-09-25', + amount: 4000, + }, + { + date: '2020-09-24', + amount: 3800, + }, + { + date: '2020-09-23', + amount: 4100, + }, + { + date: '2020-08-29', + amount: 4400, + }, + { + date: '2020-08-28', + amount: 5000, + }, + { + date: '2020-08-27', + amount: 4900, + }, + { + date: '2020-08-26', + amount: 4100, + }, + { + date: '2020-08-21', + amount: 3700, + }, + { + date: '2020-08-20', + amount: 2200, + }, + { + date: '2020-08-25', + amount: 1700, + }, + { + date: '2020-08-24', + amount: 2100, + }, + { + date: '2020-08-23', + amount: 3100, + }, + { + date: '2020-08-22', + amount: 1500, + }, + { + date: '2020-09-08', + amount: 2900, + }, + { + date: '2020-09-07', + amount: 4100, + }, + { + date: '2020-09-06', + amount: 3600, + }, + { + date: '2020-09-05', + amount: 3300, + }, + { + date: '2020-09-09', + amount: 2800, + }, + { + date: '2020-08-31', + amount: 3400, + }, + { + date: '2020-08-30', + amount: 4300, + }, + { + date: '2020-09-04', + amount: 6100, + }, + { + date: '2020-09-03', + amount: 2500, + }, + { + date: '2020-09-02', + amount: 4900, + }, + { + date: '2020-09-01', + amount: 6100, + }, + { + date: '2020-09-30', + amount: 5500, + }, +]; From 43396127893cd1d6162b7090f6b27bec18bddc3c Mon Sep 17 00:00:00 2001 From: Brenda Sukh Date: Fri, 13 Nov 2020 15:15:58 -0500 Subject: [PATCH 161/252] Update compared change calculation to use last billing date --- plugins/cost-insights/src/utils/change.ts | 27 +++++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/plugins/cost-insights/src/utils/change.ts b/plugins/cost-insights/src/utils/change.ts index 79f653158a..a5a0a86f0a 100644 --- a/plugins/cost-insights/src/utils/change.ts +++ b/plugins/cost-insights/src/utils/change.ts @@ -25,6 +25,11 @@ import { } from '../types'; import { aggregationSort } from '../utils/sort'; import moment from 'moment'; +import dayjs, { OpUnitType } from 'dayjs'; +import duration, { DurationInputType } from 'dayjs/plugin/duration'; +import { inclusiveStartDateOf } from './duration'; + +dayjs.extend(duration); // Used for displaying status colors export function growthOf(ratio: number, amount?: number) { @@ -48,9 +53,14 @@ export function getComparedChange( dailyCost: Cost, metricData: MetricData, duration: Duration, + lastCompleteBillingDate: string, // YYYY-MM-DD, ): ChangeStatistic { const ratio = dailyCost.change.ratio - metricData.change.ratio; - const previousPeriodTotal = getPreviousPeriodTotalCost(dailyCost, duration); + const previousPeriodTotal = getPreviousPeriodTotalCost( + dailyCost, + duration, + lastCompleteBillingDate, + ); return { ratio: ratio, amount: previousPeriodTotal * ratio, @@ -60,15 +70,18 @@ export function getComparedChange( export function getPreviousPeriodTotalCost( dailyCost: Cost, duration: Duration, + endDate: string, ): number { - const costsByDate = dailyCost.aggregation.slice().sort(aggregationSort); - const nextPeriodStart = moment(costsByDate[0].date).add( - moment.duration(duration), - ); + const dayjsDuration = dayjs.duration(duration); + const startDate = inclusiveStartDateOf(duration, endDate); + const [amount, type]: [number, OpUnitType] = dayjsDuration.days() + ? [dayjsDuration.days(), 'days' as OpUnitType] + : [dayjsDuration.months(), 'months' as OpUnitType]; + const nextPeriodStart = dayjs(startDate).add(amount, type); // Add up costs that incurred before the start of the next period. - return costsByDate.reduce((acc, costByDate) => { - return moment(costByDate.date).isBefore(nextPeriodStart) + return dailyCost.aggregation.reduce((acc, costByDate) => { + return dayjs(costByDate.date).isBefore(nextPeriodStart) ? acc + costByDate.amount : acc; }, 0); From 3a86e69f8cf773a1306828e9ab41150105a8336d Mon Sep 17 00:00:00 2001 From: Brenda Sukh Date: Fri, 13 Nov 2020 15:16:15 -0500 Subject: [PATCH 162/252] Fix cost chart tooltip date label --- .../src/components/CostOverviewCard/CostOverviewChart.tsx | 7 +++++-- plugins/cost-insights/src/utils/change.test.ts | 7 ++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewChart.tsx b/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewChart.tsx index 6ed72923ff..1910013581 100644 --- a/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewChart.tsx +++ b/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewChart.tsx @@ -14,7 +14,8 @@ * limitations under the License. */ import React from 'react'; -import moment from 'moment'; +import dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc'; import { useTheme } from '@material-ui/core'; import { ComposedChart, @@ -50,6 +51,8 @@ import { useCostOverviewStyles as useStyles } from '../../utils/styles'; import { groupByDate, toDataMax, trendFrom } from '../../utils/charts'; import { aggregationSort } from '../../utils/sort'; +dayjs.extend(utc); + type CostOverviewChartProps = { metric: Maybe; metricData: Maybe; @@ -104,7 +107,7 @@ export const CostOverviewChart = ({ if (isInvalid({ label, payload })) return null; const dataKeys = [data.dailyCost.dataKey, data.metric.dataKey]; - const title = moment(label).format(DEFAULT_DATE_FORMAT); + const title = dayjs(label).utc().format(DEFAULT_DATE_FORMAT); const items = payload .filter(p => dataKeys.includes(p.dataKey as string)) .map(p => ({ diff --git a/plugins/cost-insights/src/utils/change.test.ts b/plugins/cost-insights/src/utils/change.test.ts index 709f2dd882..94adc25f64 100644 --- a/plugins/cost-insights/src/utils/change.test.ts +++ b/plugins/cost-insights/src/utils/change.test.ts @@ -76,8 +76,13 @@ describe('getPreviousPeriodTotalCost', () => { change: changeOf(MockAggregatedDailyCosts), trendline: trendlineOf(MockAggregatedDailyCosts), }; + const exclusiveEndDate = '2020-10-01'; expect( - getPreviousPeriodTotalCost(mockGroupDailyCost, Duration.P1M), + getPreviousPeriodTotalCost( + mockGroupDailyCost, + Duration.P1M, + exclusiveEndDate, + ), ).toEqual(100_000); }); }); From da883b1fb11b57ea883985f7452cc507c2202dc1 Mon Sep 17 00:00:00 2001 From: Brenda Sukh Date: Fri, 13 Nov 2020 15:16:32 -0500 Subject: [PATCH 163/252] Pass in last complete billing date to compared change calculation --- .../CostOverviewCard/CostOverviewCard.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.tsx b/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.tsx index 358c7a98aa..81dc118ea9 100644 --- a/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.tsx +++ b/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.tsx @@ -22,7 +22,12 @@ import { CostOverviewHeader } from './CostOverviewHeader'; import { LegendItem } from '../LegendItem'; import { MetricSelect } from '../MetricSelect'; import { PeriodSelect } from '../PeriodSelect'; -import { useScroll, useFilters, useConfig } from '../../hooks'; +import { + useScroll, + useFilters, + useConfig, + useLastCompleteBillingDate, +} from '../../hooks'; import { mapFiltersToProps } from './selector'; import { DefaultNavigation } from '../../utils/navigation'; import { formatPercent } from '../../utils/formatters'; @@ -41,6 +46,7 @@ export const CostOverviewCard = ({ }: CostOverviewCardProps) => { const theme = useTheme(); const config = useConfig(); + const lastCompleteBillingDate = useLastCompleteBillingDate(); const { ScrollAnchor } = useScroll(DefaultNavigation.CostOverviewCard); const { setDuration, setProject, setMetric, ...filters } = useFilters( mapFiltersToProps, @@ -50,7 +56,12 @@ export const CostOverviewCard = ({ ? findAlways(config.metrics, m => m.kind === filters.metric) : null; const comparedChange = metricData - ? getComparedChange(dailyCostData, metricData, filters.duration) + ? getComparedChange( + dailyCostData, + metricData, + filters.duration, + lastCompleteBillingDate, + ) : null; return ( From b13324a4de6910dc202e1ae17a55350cfbb8ad27 Mon Sep 17 00:00:00 2001 From: Brenda Sukh Date: Fri, 13 Nov 2020 15:18:50 -0500 Subject: [PATCH 164/252] Fix ts errors --- plugins/cost-insights/src/utils/change.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/cost-insights/src/utils/change.ts b/plugins/cost-insights/src/utils/change.ts index a5a0a86f0a..0fca672425 100644 --- a/plugins/cost-insights/src/utils/change.ts +++ b/plugins/cost-insights/src/utils/change.ts @@ -23,10 +23,8 @@ import { MetricData, Duration, } from '../types'; -import { aggregationSort } from '../utils/sort'; -import moment from 'moment'; import dayjs, { OpUnitType } from 'dayjs'; -import duration, { DurationInputType } from 'dayjs/plugin/duration'; +import duration from 'dayjs/plugin/duration'; import { inclusiveStartDateOf } from './duration'; dayjs.extend(duration); From 259d848eee9caf998ac102801f7189854d2f4c8d Mon Sep 17 00:00:00 2001 From: Brenda Sukh Date: Fri, 13 Nov 2020 15:21:24 -0500 Subject: [PATCH 165/252] Add changeset --- .changeset/cost-insights-wild-cars-wait.md | 5 +++++ plugins/cost-insights/src/client.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/cost-insights-wild-cars-wait.md diff --git a/.changeset/cost-insights-wild-cars-wait.md b/.changeset/cost-insights-wild-cars-wait.md new file mode 100644 index 0000000000..24f84dc56b --- /dev/null +++ b/.changeset/cost-insights-wild-cars-wait.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-cost-insights': patch +--- + +Fix savings/excess display calculation diff --git a/plugins/cost-insights/src/client.ts b/plugins/cost-insights/src/client.ts index 74d4954ea4..2180385424 100644 --- a/plugins/cost-insights/src/client.ts +++ b/plugins/cost-insights/src/client.ts @@ -42,7 +42,7 @@ type IntervalFields = { endDate: string; }; -export function parseIntervals(intervals: string): IntervalFields { +function parseIntervals(intervals: string): IntervalFields { const match = intervals.match( /\/(?P\d+[DM])\/(?\d{4}-\d{2}-\d{2})/, ); From 319b6b17def15a7b7ad8fd03cc9a36a6e8a3cae6 Mon Sep 17 00:00:00 2001 From: Brenda Sukh Date: Fri, 13 Nov 2020 15:51:25 -0500 Subject: [PATCH 166/252] Make exclusive end dates explicit --- .../cost-insights/src/utils/change.test.ts | 2 +- plugins/cost-insights/src/utils/change.ts | 13 +++++--- plugins/cost-insights/src/utils/duration.ts | 32 +++++++++++-------- plugins/cost-insights/src/utils/formatters.ts | 24 ++++++++------ 4 files changed, 44 insertions(+), 27 deletions(-) diff --git a/plugins/cost-insights/src/utils/change.test.ts b/plugins/cost-insights/src/utils/change.test.ts index 94adc25f64..6a0a8e793b 100644 --- a/plugins/cost-insights/src/utils/change.test.ts +++ b/plugins/cost-insights/src/utils/change.test.ts @@ -76,7 +76,7 @@ describe('getPreviousPeriodTotalCost', () => { change: changeOf(MockAggregatedDailyCosts), trendline: trendlineOf(MockAggregatedDailyCosts), }; - const exclusiveEndDate = '2020-10-01'; + const exclusiveEndDate = '2020-09-30'; expect( getPreviousPeriodTotalCost( mockGroupDailyCost, diff --git a/plugins/cost-insights/src/utils/change.ts b/plugins/cost-insights/src/utils/change.ts index 0fca672425..1718462948 100644 --- a/plugins/cost-insights/src/utils/change.ts +++ b/plugins/cost-insights/src/utils/change.ts @@ -22,6 +22,7 @@ import { GrowthType, MetricData, Duration, + DEFAULT_DATE_FORMAT, } from '../types'; import dayjs, { OpUnitType } from 'dayjs'; import duration from 'dayjs/plugin/duration'; @@ -68,13 +69,17 @@ export function getComparedChange( export function getPreviousPeriodTotalCost( dailyCost: Cost, duration: Duration, - endDate: string, + inclusiveEndDate: string, ): number { const dayjsDuration = dayjs.duration(duration); - const startDate = inclusiveStartDateOf(duration, endDate); + const startDate = inclusiveStartDateOf( + duration, + dayjs(inclusiveEndDate).add(1, 'day').format(DEFAULT_DATE_FORMAT), + ); + // dayjs doesn't allow adding an ISO 8601 period to dates. const [amount, type]: [number, OpUnitType] = dayjsDuration.days() - ? [dayjsDuration.days(), 'days' as OpUnitType] - : [dayjsDuration.months(), 'months' as OpUnitType]; + ? [dayjsDuration.days(), 'day'] + : [dayjsDuration.months(), 'month']; const nextPeriodStart = dayjs(startDate).add(amount, type); // Add up costs that incurred before the start of the next period. diff --git a/plugins/cost-insights/src/utils/duration.ts b/plugins/cost-insights/src/utils/duration.ts index 6a880b382c..ae8c255b91 100644 --- a/plugins/cost-insights/src/utils/duration.ts +++ b/plugins/cost-insights/src/utils/duration.ts @@ -22,27 +22,27 @@ import { assertNever } from './assert'; * Derive the start date of a given period, assuming two repeating intervals. * * @param duration see comment on Duration enum - * @param endDate from CostInsightsApi.getLastCompleteBillingDate + * @param endDate from CostInsightsApi.getLastCompleteBillingDate + 1 day */ export function inclusiveStartDateOf( duration: Duration, - endDate: string, + exclusiveEndDate: string, ): string { switch (duration) { case Duration.P30D: case Duration.P90D: - return moment(endDate) + return moment(exclusiveEndDate) .utc() .subtract(moment.duration(duration).add(moment.duration(duration))) .format(DEFAULT_DATE_FORMAT); case Duration.P1M: - return moment(endDate) + return moment(exclusiveEndDate) .utc() .startOf('month') .subtract(moment.duration(duration).add(moment.duration(duration))) .format(DEFAULT_DATE_FORMAT); case Duration.P3M: - return moment(endDate) + return moment(exclusiveEndDate) .utc() .startOf('quarter') .subtract(moment.duration(duration).add(moment.duration(duration))) @@ -54,16 +54,22 @@ export function inclusiveStartDateOf( export function exclusiveEndDateOf( duration: Duration, - endDate: string, + inclusiveEndDate: string, ): string { switch (duration) { case Duration.P30D: case Duration.P90D: - return moment(endDate).utc().add(1, 'day').format(DEFAULT_DATE_FORMAT); + return moment(inclusiveEndDate) + .utc() + .add(1, 'day') + .format(DEFAULT_DATE_FORMAT); case Duration.P1M: - return moment(endDate).utc().startOf('month').format(DEFAULT_DATE_FORMAT); + return moment(inclusiveEndDate) + .utc() + .startOf('month') + .format(DEFAULT_DATE_FORMAT); case Duration.P3M: - return moment(endDate) + return moment(inclusiveEndDate) .utc() .startOf('quarter') .format(DEFAULT_DATE_FORMAT); @@ -74,15 +80,15 @@ export function exclusiveEndDateOf( export function inclusiveEndDateOf( duration: Duration, - endDate: string, + inclusiveEndDate: string, ): string { - return moment(exclusiveEndDateOf(duration, endDate)) + return moment(exclusiveEndDateOf(duration, inclusiveEndDate)) .utc() .subtract(1, 'day') .format(DEFAULT_DATE_FORMAT); } // https://en.wikipedia.org/wiki/ISO_8601#Repeating_intervals -export function intervalsOf(duration: Duration, endDate: string) { - return `R2/${duration}/${exclusiveEndDateOf(duration, endDate)}`; +export function intervalsOf(duration: Duration, inclusiveEndDate: string) { + return `R2/${duration}/${exclusiveEndDateOf(duration, inclusiveEndDate)}`; } diff --git a/plugins/cost-insights/src/utils/formatters.ts b/plugins/cost-insights/src/utils/formatters.ts index bf8e26ef5f..e81fc1f7c9 100644 --- a/plugins/cost-insights/src/utils/formatters.ts +++ b/plugins/cost-insights/src/utils/formatters.ts @@ -15,7 +15,7 @@ */ import moment from 'moment'; -import { Duration } from '../types'; +import { Duration, DEFAULT_DATE_FORMAT } from '../types'; import { inclusiveEndDateOf, inclusiveStartDateOf } from '../utils/duration'; import { pluralOf } from '../utils/grammar'; @@ -84,21 +84,27 @@ export function formatPercent(n: number): string { return `${(n * 100).toFixed(0)}%`; } -export function formatLastTwoLookaheadQuarters(endDate: string) { - const start = moment(inclusiveStartDateOf(Duration.P3M, endDate)).format( - '[Q]Q YYYY', - ); - const end = moment(inclusiveEndDateOf(Duration.P3M, endDate)).format( +export function formatLastTwoLookaheadQuarters(inclusiveEndDate: string) { + const exclusiveEndDate = moment(inclusiveEndDate) + .add(1, 'day') + .format(DEFAULT_DATE_FORMAT); + const start = moment( + inclusiveStartDateOf(Duration.P3M, exclusiveEndDate), + ).format('[Q]Q YYYY'); + const end = moment(inclusiveEndDateOf(Duration.P3M, inclusiveEndDate)).format( '[Q]Q YYYY', ); return `${start} vs ${end}`; } -export function formatLastTwoMonths(endDate: string) { - const start = moment(inclusiveStartDateOf(Duration.P1M, endDate)) +export function formatLastTwoMonths(inclusiveEndDate: string) { + const exclusiveEndDate = moment(inclusiveEndDate) + .add(1, 'day') + .format(DEFAULT_DATE_FORMAT); + const start = moment(inclusiveStartDateOf(Duration.P1M, exclusiveEndDate)) .utc() .format('MMMM'); - const end = moment(inclusiveEndDateOf(Duration.P1M, endDate)) + const end = moment(inclusiveEndDateOf(Duration.P1M, inclusiveEndDate)) .utc() .format('MMMM'); return `${start} vs ${end}`; From e4cf4342c27d931be0b9010f290337ce5e43eb51 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 16 Nov 2020 00:57:26 +0100 Subject: [PATCH 167/252] cli: work around race condition while packing dependencies of the CLI --- packages/cli/src/lib/packager/index.ts | 113 +++++++++++++++---------- 1 file changed, 69 insertions(+), 44 deletions(-) diff --git a/packages/cli/src/lib/packager/index.ts b/packages/cli/src/lib/packager/index.ts index 56f1ee453c..8653e66d37 100644 --- a/packages/cli/src/lib/packager/index.ts +++ b/packages/cli/src/lib/packager/index.ts @@ -24,8 +24,16 @@ import { tmpdir } from 'os'; import tar, { CreateOptions } from 'tar'; import { paths } from '../paths'; import { run } from '../run'; +import { packageVersions } from '../version'; import { ParallelOption } from '../parallel'; +// These packages aren't safe to pack in parallel since the CLI depends on them +const UNSAFE_PACKAGES = [ + ...Object.keys(packageVersions), + '@backstage/cli-common', + '@backstage/config-loader', +]; + type LernaPackage = { name: string; private: boolean; @@ -135,51 +143,68 @@ async function moveToDistWorkspace( workspaceDir: string, localPackages: LernaPackage[], ): Promise { + async function pack(target: LernaPackage, archive: string) { + console.log(`Repacking ${target.name} into dist workspace`); + const archivePath = resolvePath(workspaceDir, archive); + + await run('yarn', ['pack', '--filename', archivePath], { + cwd: target.location, + }); + // TODO(Rugvip): yarn pack doesn't call postpack, once the bug is fixed this can be removed + if (target.scripts.postpack) { + await run('yarn', ['postpack'], { cwd: target.location }); + } + + const outputDir = relativePath(paths.targetRoot, target.location); + const absoluteOutputPath = resolvePath(workspaceDir, outputDir); + await fs.ensureDir(absoluteOutputPath); + + await tar.extract({ + file: archivePath, + cwd: absoluteOutputPath, + strip: 1, + }); + await fs.remove(archivePath); + + // We remove the dependencies from package.json of packages that are marked + // as bundled, so that yarn doesn't try to install them. + if (target.get('bundled')) { + const pkgJson = await fs.readJson( + resolvePath(absoluteOutputPath, 'package.json'), + ); + delete pkgJson.dependencies; + delete pkgJson.devDependencies; + delete pkgJson.peerDependencies; + delete pkgJson.optionalDependencies; + + await fs.writeJson( + resolvePath(absoluteOutputPath, 'package.json'), + pkgJson, + { + spaces: 2, + }, + ); + } + } + + const unsafePackages = localPackages.filter(p => + UNSAFE_PACKAGES.includes(p.name), + ); + const safePackages = localPackages.filter( + p => !UNSAFE_PACKAGES.includes(p.name), + ); + + // The unsafe package are packed first one by one in order to avoid race conditions + // where the CLI is being executed with broken dependencies. + for (const target of unsafePackages) { + await pack(target, `temp-package.tgz`); + } + + // Repacking in parallel is much faster and safe for all packages outside of the Backstage repo await Promise.all( - localPackages.map(async (target, index) => { - console.log(`Repacking ${target.name} into dist workspace`); - const archive = `temp-package-${index}.tgz`; - const archivePath = resolvePath(workspaceDir, archive); - - await run('yarn', ['pack', '--filename', archivePath], { - cwd: target.location, - }); - // TODO(Rugvip): yarn pack doesn't call postpack, once the bug is fixed this can be removed - if (target.scripts.postpack) { - await run('yarn', ['postpack'], { cwd: target.location }); - } - - const outputDir = relativePath(paths.targetRoot, target.location); - const absoluteOutputPath = resolvePath(workspaceDir, outputDir); - await fs.ensureDir(absoluteOutputPath); - - await tar.extract({ - file: archivePath, - cwd: absoluteOutputPath, - strip: 1, - }); - await fs.remove(archivePath); - - // We remove the dependencies from package.json of packages that are marked - // as bundled, so that yarn doesn't try to install them. - if (target.get('bundled')) { - const pkgJson = await fs.readJson( - resolvePath(absoluteOutputPath, 'package.json'), - ); - delete pkgJson.dependencies; - delete pkgJson.devDependencies; - delete pkgJson.peerDependencies; - delete pkgJson.optionalDependencies; - - await fs.writeJson( - resolvePath(absoluteOutputPath, 'package.json'), - pkgJson, - { - spaces: 2, - }, - ); - } - }), + safePackages.map(async (target, index) => + pack(target, `temp-package-${index}.tgz`), + ), ); } From 0b2ad2853265a30121e2a35f325701301b01d451 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 16 Nov 2020 10:59:00 +0100 Subject: [PATCH 168/252] microsite: new email --- docs/overview/roadmap.md | 2 +- docs/overview/vision.md | 4 ++-- microsite/blog/2020-03-16-announcing-backstage.md | 2 +- microsite/blog/2020-03-18-what-is-backstage.md | 2 +- microsite/blog/2020-06-22-backstage-service-catalog-alpha.md | 2 +- .../2020-08-05-announcing-backstage-software-templates.md | 2 +- microsite/blog/2020-09-23-backstage-cncf-sandbox.md | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index 5ff4d49a7c..71612cc5e4 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -39,7 +39,7 @@ If you have questions about the roadmap or want to provide feedback, we would love to hear from you! Please create an [Issue](https://github.com/backstage/backstage/issues/new/choose), ping us on [Discord](https://discord.gg/EBHEGzX) or reach out directly at -[alund@spotify.com](mailto:alund@spotify.com). +[backstage-interest@spotify.com](mailto:backstage-interest@spotify.com). Want to help out? Awesome ❤️ Head over to [CONTRIBUTING](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) diff --git a/docs/overview/vision.md b/docs/overview/vision.md index df75a4f0a6..c17e2b17ba 100644 --- a/docs/overview/vision.md +++ b/docs/overview/vision.md @@ -19,5 +19,5 @@ We are working on making Backstage the trusted standard toolbox (read: UX layer) for the open source infrastructure landscape. Think of it like Kubernetes for developer experience. We realize this is an ambitious goal. We can’t do it alone. If this sounds interesting or you'd like to help us shape our product -vision, we'd love to talk. You can email me directly: -[alund@spotify.com](mailto:alund@spotify.com). +vision, we'd love to talk. You can email us directly: +[backstage-interest@spotify.com](mailto:backstage-interest@spotify.com). diff --git a/microsite/blog/2020-03-16-announcing-backstage.md b/microsite/blog/2020-03-16-announcing-backstage.md index 3911debf08..5824b76a57 100644 --- a/microsite/blog/2020-03-16-announcing-backstage.md +++ b/microsite/blog/2020-03-16-announcing-backstage.md @@ -41,4 +41,4 @@ We are envisioning three phases of the project and we have already begun work on - **Phase 3:** Ecosystem (later) - Everyone's infrastructure stack is different. By fostering a vibrant community of contributors we hope to provide an ecosystem of Open Source plugins/integrations that allows you to pick the tools that match your stack. -Our vision for Backstage is for it to become the trusted standard toolbox (read: UI layer) for the open source infrastructure landscape. Think of it like Kubernetes for developer experience. We realize this is an ambitious goal. We can’t do it alone. If this sounds interesting or you'd like to help us shape our product vision, we'd love to talk. You can email me directly: [alund@spotify.com](mailto:alund@spotify.com). +Our vision for Backstage is for it to become the trusted standard toolbox (read: UI layer) for the open source infrastructure landscape. Think of it like Kubernetes for developer experience. We realize this is an ambitious goal. We can’t do it alone. If this sounds interesting or you'd like to help us shape our product vision, we'd love to talk. You can email us directly: [backstage-interest@spotify.com](mailto:backstage-interest@spotify.com). diff --git a/microsite/blog/2020-03-18-what-is-backstage.md b/microsite/blog/2020-03-18-what-is-backstage.md index 0a0ff9e0b1..f4f62e2cf7 100644 --- a/microsite/blog/2020-03-18-what-is-backstage.md +++ b/microsite/blog/2020-03-18-what-is-backstage.md @@ -85,4 +85,4 @@ We are envisioning [three phases](https://github.com/backstage/backstage/milesto https://github.com/backstage/backstage/milestones -Want to discuss the project or need support? Join us on [Discord](https://discord.gg/MUpMjP2) or reach out on [alund@spotify.com](mailto:alund@spotify.com). +Want to discuss the project or need support? Join us on [Discord](https://discord.gg/MUpMjP2) or reach out on [backstage-interest@spotify.com](mailto:backstage-interest@spotify.com). diff --git a/microsite/blog/2020-06-22-backstage-service-catalog-alpha.md b/microsite/blog/2020-06-22-backstage-service-catalog-alpha.md index eeed43b713..e50d7a6d47 100644 --- a/microsite/blog/2020-06-22-backstage-service-catalog-alpha.md +++ b/microsite/blog/2020-06-22-backstage-service-catalog-alpha.md @@ -49,6 +49,6 @@ Alpha is our shorthand for "we don’t yet think Backstage is ready for producti As with most alpha releases, you should expect things to change quite a lot until we reach the beta stage (we’re targeting the end of summer). There are obviously many things missing as well, but we wanted to start collecting feedback early and make it easier to see the end-to-end flow. -If you have feedback or questions, please open a [GitHub issue](https://github.com/backstage/backstage/issues), ping us on [Discord chat](https://discord.gg/EBHEGzX) or send me an email at [alund@spotify.com](mailto:alund@spotify.com) 🙏 +If you have feedback or questions, please open a [GitHub issue](https://github.com/backstage/backstage/issues), ping us on [Discord chat](https://discord.gg/EBHEGzX) or send us an email at [backstage-interest@spotify.com](mailto:backstage-interest@spotify.com) 🙏 To get regular product updates and news about the Backstage community, sign up for the [Backstage newsletter](https://mailchi.mp/spotify/backstage-community). diff --git a/microsite/blog/2020-08-05-announcing-backstage-software-templates.md b/microsite/blog/2020-08-05-announcing-backstage-software-templates.md index c216d7e398..bd6ae8eeeb 100644 --- a/microsite/blog/2020-08-05-announcing-backstage-software-templates.md +++ b/microsite/blog/2020-08-05-announcing-backstage-software-templates.md @@ -73,6 +73,6 @@ Backstage ships with four example templates, but since these are likely not the We have learned that one of the keys to getting these standards adopted is to keep an open process. Templates are code. By making it clear to your engineers that you are open to pull requests, and that teams with different needs can add their own templates, you are on the path of striking a good balance between autonomy and standardization. -If you have feedback or questions, please open a [GitHub issue](https://github.com/backstage/backstage/issues), ping us on [Discord chat](https://discord.gg/EBHEGzX) or send me an email at [alund@spotify.com](mailto:alund@spotify.com) 🙏 +If you have feedback or questions, please open a [GitHub issue](https://github.com/backstage/backstage/issues), ping us on [Discord chat](https://discord.gg/EBHEGzX) or send us an email at [backstage-interest@spotify.com](mailto:backstage-interest@spotify.com) 🙏 To get regular product updates and news about the Backstage community, sign up for the [Backstage newsletter](https://mailchi.mp/spotify/backstage-community). diff --git a/microsite/blog/2020-09-23-backstage-cncf-sandbox.md b/microsite/blog/2020-09-23-backstage-cncf-sandbox.md index fbf47a39c3..8fc459cb62 100644 --- a/microsite/blog/2020-09-23-backstage-cncf-sandbox.md +++ b/microsite/blog/2020-09-23-backstage-cncf-sandbox.md @@ -18,4 +18,4 @@ The Backstage community is healthy and growing quickly. Over [130 people](https: We’re excited to embark on this journey with the CNCF community. There’s so much great tech being built here, and it’s about time we share it to build even greater products, together. Entering into the CNCF Sandbox is just the first step. We are committed to working with the community to bring Backstage through the Incubation step, and finally all the way to becoming a Graduated, top-level project. -Thanks to everyone for your support so far. We hope you [join us](https://mailchi.mp/spotify/backstage-community) in this next chapter of Backstage's journey. If you have questions or feedback, feel free to [email](mailto:alund@spotify.com) me directly. +Thanks to everyone for your support so far. We hope you [join us](https://mailchi.mp/spotify/backstage-community) in this next chapter of Backstage's journey. If you have questions or feedback, feel free to [email](mailto:backstage-interest@spotify.com) me directly. From 05ae2a4e5238d29eb461dfc713b753341fef3461 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 16 Nov 2020 13:17:35 +0100 Subject: [PATCH 169/252] cli: remove plugin:export --- packages/cli/src/commands/index.ts | 7 ------ packages/cli/src/commands/plugin/export.ts | 27 ---------------------- plugins/techdocs/package.json | 1 - 3 files changed, 35 deletions(-) delete mode 100644 packages/cli/src/commands/plugin/export.ts diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 7f028919f7..5b0dae6244 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -103,13 +103,6 @@ export function registerCommands(program: CommanderStatic) { .option(...configOption) .action(lazy(() => import('./plugin/serve').then(m => m.default))); - program - .command('plugin:export') - .description('Exports the dev/ folder of a plugin') - .option('--stats', 'Write bundle stats to output directory') - .option(...configOption) - .action(lazy(() => import('./plugin/export').then(m => m.default))); - program .command('plugin:diff') .option('--check', 'Fail if changes are required') diff --git a/packages/cli/src/commands/plugin/export.ts b/packages/cli/src/commands/plugin/export.ts deleted file mode 100644 index 69955a2e5f..0000000000 --- a/packages/cli/src/commands/plugin/export.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Command } from 'commander'; -import { buildBundle } from '../../lib/bundler'; -import { loadCliConfig } from '../../lib/config'; - -export default async (cmd: Command) => { - await buildBundle({ - entry: 'dev/index', - statsJsonEnabled: cmd.stats, - ...(await loadCliConfig(cmd.config)), - }); -}; diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 6a664a83fb..88a38549ad 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -12,7 +12,6 @@ }, "scripts": { "build": "backstage-cli plugin:build", - "export": "backstage-cli plugin:export", "start": "backstage-cli plugin:serve", "lint": "backstage-cli lint", "test": "backstage-cli test", From 2f8a074dd2741a8079ae28ccaec8b63424724b61 Mon Sep 17 00:00:00 2001 From: Ryan Vazquez Date: Mon, 16 Nov 2020 09:41:01 -0500 Subject: [PATCH 170/252] remove excessive header margin from cost overview banner --- .../src/components/CostInsightsPage/CostInsightsPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsPage.tsx b/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsPage.tsx index 3234d1aa7f..50429e8f13 100644 --- a/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsPage.tsx +++ b/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsPage.tsx @@ -191,7 +191,7 @@ export const CostInsightsPage = () => { const CostOverviewBanner = () => ( Date: Mon, 16 Nov 2020 09:45:59 -0500 Subject: [PATCH 171/252] changeset --- .changeset/cost-insights-strange-rings-smile.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/cost-insights-strange-rings-smile.md diff --git a/.changeset/cost-insights-strange-rings-smile.md b/.changeset/cost-insights-strange-rings-smile.md new file mode 100644 index 0000000000..c5f4ff9b73 --- /dev/null +++ b/.changeset/cost-insights-strange-rings-smile.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-cost-insights': patch +--- + +remove excessive margin from cost overview banner From 746e861d0bc5b2e47e1a2a6ef3eeeaf83adebd9a Mon Sep 17 00:00:00 2001 From: Brenda Sukh Date: Mon, 16 Nov 2020 09:56:21 -0500 Subject: [PATCH 172/252] Remove currency feature flag --- .../CostInsightsPage/CostInsightsPage.tsx | 17 ++++++----------- .../CurrencySelect/CurrencySelect.tsx | 1 + 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsPage.tsx b/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsPage.tsx index 3234d1aa7f..80464a56cb 100644 --- a/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsPage.tsx +++ b/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsPage.tsx @@ -16,7 +16,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { Box, Container, Divider, Grid, Typography } from '@material-ui/core'; -import { featureFlagsApiRef, Progress, useApi } from '@backstage/core'; +import { Progress, useApi } from '@backstage/core'; import { default as MaterialAlert } from '@material-ui/lab/Alert'; import { costInsightsApiRef } from '../../api'; import { AlertActionCardList } from '../AlertActionCardList'; @@ -49,7 +49,6 @@ import { useSubtleTypographyStyles } from '../../utils/styles'; export const CostInsightsPage = () => { const classes = useSubtleTypographyStyles(); - const featureFlags = useApi(featureFlagsApiRef); const client = useApi(costInsightsApiRef); const config = useConfig(); const groups = useGroups(); @@ -203,15 +202,11 @@ export const CostInsightsPage = () => { - {featureFlags.isActive('cost-insights-currencies') && ( - - - - )} + {currencies.map((c: Currency) => ( Date: Mon, 16 Nov 2020 10:07:56 -0500 Subject: [PATCH 173/252] Add changeset --- .changeset/cost-insights-ten-carpets-return.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/cost-insights-ten-carpets-return.md diff --git a/.changeset/cost-insights-ten-carpets-return.md b/.changeset/cost-insights-ten-carpets-return.md new file mode 100644 index 0000000000..f378a20eab --- /dev/null +++ b/.changeset/cost-insights-ten-carpets-return.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-cost-insights': minor +--- + +remove cost insights currency feature flag From 7b37e68348d5b389f46bf1d0b26ae10bc8115676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 16 Nov 2020 16:15:09 +0100 Subject: [PATCH 174/252] integrations: move common integration concerns to a separate package (#3295) --- .changeset/ninety-gifts-guess.md | 6 + packages/backend-common/package.json | 1 + .../src/reading/AzureUrlReader.ts | 40 ++---- .../src/reading/BitbucketUrlReader.test.ts | 78 ++-------- .../src/reading/BitbucketUrlReader.ts | 135 +++++------------- .../src/reading/GithubUrlReader.test.ts | 88 +++--------- .../src/reading/GithubUrlReader.ts | 130 +++++------------ .../src/reading/GitlabUrlReader.ts | 43 ++---- packages/integration/.eslintrc.js | 3 + packages/integration/README.md | 9 ++ packages/integration/package.json | 33 +++++ packages/integration/src/azure/config.test.ts | 91 ++++++++++++ packages/integration/src/azure/config.ts | 72 ++++++++++ packages/integration/src/azure/index.ts | 21 +++ .../integration/src/bitbucket/config.test.ts | 133 +++++++++++++++++ packages/integration/src/bitbucket/config.ts | 115 +++++++++++++++ packages/integration/src/bitbucket/index.ts | 21 +++ .../integration/src/github/config.test.ts | 117 +++++++++++++++ packages/integration/src/github/config.ts | 113 +++++++++++++++ packages/integration/src/github/index.ts | 21 +++ .../integration/src/gitlab/config.test.ts | 91 ++++++++++++ packages/integration/src/gitlab/config.ts | 70 +++++++++ packages/integration/src/gitlab/index.ts | 21 +++ packages/integration/src/index.ts | 20 +++ packages/integration/src/setupTests.ts | 17 +++ 25 files changed, 1086 insertions(+), 403 deletions(-) create mode 100644 .changeset/ninety-gifts-guess.md create mode 100644 packages/integration/.eslintrc.js create mode 100644 packages/integration/README.md create mode 100644 packages/integration/package.json create mode 100644 packages/integration/src/azure/config.test.ts create mode 100644 packages/integration/src/azure/config.ts create mode 100644 packages/integration/src/azure/index.ts create mode 100644 packages/integration/src/bitbucket/config.test.ts create mode 100644 packages/integration/src/bitbucket/config.ts create mode 100644 packages/integration/src/bitbucket/index.ts create mode 100644 packages/integration/src/github/config.test.ts create mode 100644 packages/integration/src/github/config.ts create mode 100644 packages/integration/src/github/index.ts create mode 100644 packages/integration/src/gitlab/config.test.ts create mode 100644 packages/integration/src/gitlab/config.ts create mode 100644 packages/integration/src/gitlab/index.ts create mode 100644 packages/integration/src/index.ts create mode 100644 packages/integration/src/setupTests.ts diff --git a/.changeset/ninety-gifts-guess.md b/.changeset/ninety-gifts-guess.md new file mode 100644 index 0000000000..bdd20322ec --- /dev/null +++ b/.changeset/ninety-gifts-guess.md @@ -0,0 +1,6 @@ +--- +'@backstage/backend-common': patch +'@backstage/integration': patch +--- + +Added the integration package diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 67e4ffe11f..ea84d1a383 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -32,6 +32,7 @@ "@backstage/cli-common": "^0.1.1", "@backstage/config": "^0.1.1", "@backstage/config-loader": "^0.2.0", + "@backstage/integration": "^0.1.0", "@backstage/test-utils": "^0.1.2", "@types/cors": "^2.8.6", "@types/express": "^4.17.6", diff --git a/packages/backend-common/src/reading/AzureUrlReader.ts b/packages/backend-common/src/reading/AzureUrlReader.ts index 79058c79a9..db8b738667 100644 --- a/packages/backend-common/src/reading/AzureUrlReader.ts +++ b/packages/backend-common/src/reading/AzureUrlReader.ts @@ -14,49 +14,27 @@ * limitations under the License. */ +import { + AzureIntegrationConfig, + readAzureIntegrationConfigs, +} from '@backstage/integration'; import fetch from 'cross-fetch'; -import { Config } from '@backstage/config'; import { NotFoundError } from '../errors'; import { ReaderFactory, ReadTreeResponse, UrlReader } from './types'; -type Options = { - // TODO: added here for future support, but we only allow dev.azure.com for now - host: string; - token?: string; -}; - -function readConfig(config: Config): Options[] { - const optionsArr = Array(); - - const providerConfigs = - config.getOptionalConfigArray('integrations.azure') ?? []; - - for (const providerConfig of providerConfigs) { - const host = providerConfig.getOptionalString('host') ?? 'dev.azure.com'; - const token = providerConfig.getOptionalString('token'); - - optionsArr.push({ host, token }); - } - - // As a convenience we always make sure there's at least an unauthenticated - // reader for public azure repos. - if (!optionsArr.some(p => p.host === 'dev.azure.com')) { - optionsArr.push({ host: 'dev.azure.com' }); - } - - return optionsArr; -} - export class AzureUrlReader implements UrlReader { static factory: ReaderFactory = ({ config }) => { - return readConfig(config).map(options => { + const configs = readAzureIntegrationConfigs( + config.getOptionalConfigArray('integrations.azure') ?? [], + ); + return configs.map(options => { const reader = new AzureUrlReader(options); const predicate = (url: URL) => url.host === options.host; return { reader, predicate }; }); }; - constructor(private readonly options: Options) { + constructor(private readonly options: AzureIntegrationConfig) { if (options.host !== 'dev.azure.com') { throw Error( `Azure integration currently only supports 'dev.azure.com', tried to use host '${options.host}'`, diff --git a/packages/backend-common/src/reading/BitbucketUrlReader.test.ts b/packages/backend-common/src/reading/BitbucketUrlReader.test.ts index 210ee16873..01744db28a 100644 --- a/packages/backend-common/src/reading/BitbucketUrlReader.test.ts +++ b/packages/backend-common/src/reading/BitbucketUrlReader.test.ts @@ -14,24 +14,22 @@ * limitations under the License. */ -import { ConfigReader } from '@backstage/config'; +import { BitbucketIntegrationConfig } from '@backstage/integration'; import { BitbucketUrlReader, getApiRequestOptions, getApiUrl, - ProviderConfig, - readConfig, } from './BitbucketUrlReader'; describe('BitbucketUrlReader', () => { describe('getApiRequestOptions', () => { it('inserts a token when needed', () => { - const withToken: ProviderConfig = { + const withToken: BitbucketIntegrationConfig = { host: '', apiBaseUrl: '', token: 'A', }; - const withoutToken: ProviderConfig = { + const withoutToken: BitbucketIntegrationConfig = { host: '', apiBaseUrl: '', }; @@ -44,13 +42,13 @@ describe('BitbucketUrlReader', () => { }); it('insert basic auth when needed', () => { - const withUsernameAndPassword: ProviderConfig = { + const withUsernameAndPassword: BitbucketIntegrationConfig = { host: '', apiBaseUrl: '', username: 'some-user', appPassword: 'my-secret', }; - const withoutUsernameAndPassword: ProviderConfig = { + const withoutUsernameAndPassword: BitbucketIntegrationConfig = { host: '', apiBaseUrl: '', }; @@ -67,11 +65,11 @@ describe('BitbucketUrlReader', () => { describe('getApiUrl', () => { it('rejects targets that do not look like URLs', () => { - const config: ProviderConfig = { host: '', apiBaseUrl: '' }; + const config: BitbucketIntegrationConfig = { host: '', apiBaseUrl: '' }; expect(() => getApiUrl('a/b', config)).toThrow(/Incorrect URL: a\/b/); }); it('happy path for Bitbucket Cloud', () => { - const config: ProviderConfig = { + const config: BitbucketIntegrationConfig = { host: 'bitbucket.org', apiBaseUrl: 'https://api.bitbucket.org/2.0', }; @@ -87,7 +85,7 @@ describe('BitbucketUrlReader', () => { ); }); it('happy path for Bitbucket Server', () => { - const config: ProviderConfig = { + const config: BitbucketIntegrationConfig = { host: 'bitbucket.mycompany.net', apiBaseUrl: 'https://bitbucket.mycompany.net/rest/api/1.0', }; @@ -104,66 +102,6 @@ describe('BitbucketUrlReader', () => { }); }); - describe('readConfig', () => { - function config( - providers: { - host: string; - apiBaseUrl?: string; - token?: string; - username?: string; - password?: string; - }[], - ) { - return ConfigReader.fromConfigs([ - { - context: '', - data: { - integrations: { bitbucket: providers }, - }, - }, - ]); - } - - it('adds a default Bitbucket Cloud entry when missing', () => { - const output = readConfig(config([])); - expect(output).toEqual([ - { - host: 'bitbucket.org', - apiBaseUrl: 'https://api.bitbucket.org/2.0', - }, - ]); - }); - - it('injects the correct Bitbucket Cloud API base URL when missing', () => { - const output = readConfig(config([{ host: 'bitbucket.org' }])); - expect(output).toEqual([ - { - host: 'bitbucket.org', - apiBaseUrl: 'https://api.bitbucket.org/2.0', - }, - ]); - }); - - it('rejects custom targets with no base URLs', () => { - expect(() => - readConfig(config([{ host: 'bitbucket.mycompany.net' }])), - ).toThrow( - "Bitbucket integration for 'bitbucket.mycompany.net' must configure an explicit apiBaseUrl", - ); - }); - - it('rejects funky configs', () => { - expect(() => readConfig(config([{ host: 7 } as any]))).toThrow(/host/); - expect(() => readConfig(config([{ token: 7 } as any]))).toThrow(/token/); - expect(() => - readConfig(config([{ host: 'bitbucket.org', apiBaseUrl: 7 } as any])), - ).toThrow(/apiBaseUrl/); - expect(() => - readConfig(config([{ host: 'bitbucket.org', token: 7 } as any])), - ).toThrow(/token/); - }); - }); - describe('implementation', () => { it('rejects unknown targets', async () => { const processor = new BitbucketUrlReader({ diff --git a/packages/backend-common/src/reading/BitbucketUrlReader.ts b/packages/backend-common/src/reading/BitbucketUrlReader.ts index b2fb9fa309..9694c1d987 100644 --- a/packages/backend-common/src/reading/BitbucketUrlReader.ts +++ b/packages/backend-common/src/reading/BitbucketUrlReader.ts @@ -14,57 +14,18 @@ * limitations under the License. */ -import { Config } from '@backstage/config'; -import parseGitUri from 'git-url-parse'; +import { + BitbucketIntegrationConfig, + readBitbucketIntegrationConfigs, +} from '@backstage/integration'; import fetch from 'cross-fetch'; +import parseGitUri from 'git-url-parse'; import { NotFoundError } from '../errors'; import { ReaderFactory, ReadTreeResponse, UrlReader } from './types'; -const DEFAULT_BASE_URL = 'https://api.bitbucket.org/2.0'; - -/** - * The configuration parameters for a single Bitbucket API provider. - */ -export type ProviderConfig = { - /** - * The host of the target that this matches on, e.g. "bitbucket.com" - */ - host: string; - - /** - * The base URL of the API of this provider, e.g. "https://api.bitbucket.org/2.0", - * with no trailing slash. - * - * May be omitted specifically for Bitbucket Cloud; then it will be deduced. - * - * The API will always be preferred if both its base URL and a token are - * present. - */ - apiBaseUrl?: string; - - /** - * The authorization token to use for requests to a Bitbucket Server provider. - * - * See https://confluence.atlassian.com/bitbucketserver/personal-access-tokens-939515499.html - * - * If no token is specified, anonymous access is used. - */ - token?: string; - - /** - * The username to use for requests to Bitbucket Cloud (bitbucket.org). - */ - username?: string; - - /** - * Authentication with Bitbucket Cloud (bitbucket.org) is done using app passwords. - * - * See https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/ - */ - appPassword?: string; -}; - -export function getApiRequestOptions(provider: ProviderConfig): RequestInit { +export function getApiRequestOptions( + provider: BitbucketIntegrationConfig, +): RequestInit { const headers: HeadersInit = {}; if (provider.token) { @@ -84,7 +45,10 @@ export function getApiRequestOptions(provider: ProviderConfig): RequestInit { // Converts for example // from: https://bitbucket.org/orgname/reponame/src/master/file.yaml // to: https://api.bitbucket.org/2.0/repositories/orgname/reponame/src/master/file.yaml -export function getApiUrl(target: string, provider: ProviderConfig): URL { +export function getApiUrl( + target: string, + provider: BitbucketIntegrationConfig, +): URL { try { const { owner, name, ref, filepathtype, filepath } = parseGitUri(target); if ( @@ -115,74 +79,39 @@ export function getApiUrl(target: string, provider: ProviderConfig): URL { } } -export function readConfig(config: Config): ProviderConfig[] { - const providers: ProviderConfig[] = []; - - const providerConfigs = - config.getOptionalConfigArray('integrations.bitbucket') ?? []; - - // First read all the explicit providers - for (const providerConfig of providerConfigs) { - const host = providerConfig.getOptionalString('host') ?? 'bitbucket.org'; - let apiBaseUrl = providerConfig.getOptionalString('apiBaseUrl'); - const token = providerConfig.getOptionalString('token'); - const username = providerConfig.getOptionalString('username'); - const appPassword = providerConfig.getOptionalString('appPassword'); - - if (apiBaseUrl) { - apiBaseUrl = apiBaseUrl.replace(/\/+$/, ''); - } else if (host === 'bitbucket.org') { - apiBaseUrl = DEFAULT_BASE_URL; - } - - if (!apiBaseUrl) { - throw new Error( - `Bitbucket integration for '${host}' must configure an explicit apiBaseUrl`, - ); - } - if (!token && username && !appPassword) { - throw new Error( - `Bitbucket integration for '${host}' has configured a username but is missing a required appPassword.`, - ); - } - - providers.push({ - host, - apiBaseUrl, - token, - username, - appPassword, - }); - } - - // If no explicit bitbucket.org provider was added, put one in the list as - // a convenience - if (!providers.some(p => p.host === 'bitbucket.org')) { - providers.push({ - host: 'bitbucket.org', - apiBaseUrl: DEFAULT_BASE_URL, - }); - } - - return providers; -} - /** * A processor that adds the ability to read files from Bitbucket v1 and v2 APIs, such as * the one exposed by Bitbucket Cloud itself. */ export class BitbucketUrlReader implements UrlReader { - private config: ProviderConfig; + private readonly config: BitbucketIntegrationConfig; static factory: ReaderFactory = ({ config }) => { - return readConfig(config).map(provider => { + const configs = readBitbucketIntegrationConfigs( + config.getOptionalConfigArray('integrations.bitbucket') ?? [], + ); + return configs.map(provider => { const reader = new BitbucketUrlReader(provider); const predicate = (url: URL) => url.host === provider.host; return { reader, predicate }; }); }; - constructor(config: ProviderConfig) { + constructor(config: BitbucketIntegrationConfig) { + const { host, apiBaseUrl, token, username, appPassword } = config; + + if (!apiBaseUrl) { + throw new Error( + `Bitbucket integration for '${host}' must configure an explicit apiBaseUrl`, + ); + } + + if (!token && username && !appPassword) { + throw new Error( + `Bitbucket integration for '${host}' has configured a username but is missing a required appPassword.`, + ); + } + this.config = config; } diff --git a/packages/backend-common/src/reading/GithubUrlReader.test.ts b/packages/backend-common/src/reading/GithubUrlReader.test.ts index 658d908f84..eb87d339ea 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.test.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.test.ts @@ -15,20 +15,19 @@ */ import { ConfigReader } from '@backstage/config'; -import { setupServer } from 'msw/node'; +import { GitHubIntegrationConfig } from '@backstage/integration'; import { msw } from '@backstage/test-utils'; +import fs from 'fs'; import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import path from 'path'; import { getApiRequestOptions, getApiUrl, getRawRequestOptions, getRawUrl, GithubUrlReader, - ProviderConfig, - readConfig, } from './GithubUrlReader'; -import fs from 'fs'; -import path from 'path'; import { ReadTreeResponseFactory } from './tree'; const treeResponseFactory = ReadTreeResponseFactory.create({ @@ -38,19 +37,19 @@ const treeResponseFactory = ReadTreeResponseFactory.create({ describe('GithubUrlReader', () => { describe('getApiRequestOptions', () => { it('sets the correct API version', () => { - const config: ProviderConfig = { host: '', apiBaseUrl: '' }; + const config: GitHubIntegrationConfig = { host: '', apiBaseUrl: '' }; expect((getApiRequestOptions(config).headers as any).Accept).toEqual( 'application/vnd.github.v3.raw', ); }); it('inserts a token when needed', () => { - const withToken: ProviderConfig = { + const withToken: GitHubIntegrationConfig = { host: '', apiBaseUrl: '', token: 'A', }; - const withoutToken: ProviderConfig = { + const withoutToken: GitHubIntegrationConfig = { host: '', apiBaseUrl: '', }; @@ -65,12 +64,12 @@ describe('GithubUrlReader', () => { describe('getRawRequestOptions', () => { it('inserts a token when needed', () => { - const withToken: ProviderConfig = { + const withToken: GitHubIntegrationConfig = { host: '', rawBaseUrl: '', token: 'A', }; - const withoutToken: ProviderConfig = { + const withoutToken: GitHubIntegrationConfig = { host: '', rawBaseUrl: '', }; @@ -85,12 +84,12 @@ describe('GithubUrlReader', () => { describe('getApiUrl', () => { it('rejects targets that do not look like URLs', () => { - const config: ProviderConfig = { host: '', apiBaseUrl: '' }; + const config: GitHubIntegrationConfig = { host: '', apiBaseUrl: '' }; expect(() => getApiUrl('a/b', config)).toThrow(/Incorrect URL: a\/b/); }); it('happy path for github', () => { - const config: ProviderConfig = { + const config: GitHubIntegrationConfig = { host: 'github.com', apiBaseUrl: 'https://api.github.com', }; @@ -117,7 +116,7 @@ describe('GithubUrlReader', () => { }); it('happy path for ghe', () => { - const config: ProviderConfig = { + const config: GitHubIntegrationConfig = { host: 'ghe.mycompany.net', apiBaseUrl: 'https://ghe.mycompany.net/api/v3', }; @@ -136,12 +135,12 @@ describe('GithubUrlReader', () => { describe('getRawUrl', () => { it('rejects targets that do not look like URLs', () => { - const config: ProviderConfig = { host: '', apiBaseUrl: '' }; + const config: GitHubIntegrationConfig = { host: '', apiBaseUrl: '' }; expect(() => getRawUrl('a/b', config)).toThrow(/Incorrect URL: a\/b/); }); it('happy path for github', () => { - const config: ProviderConfig = { + const config: GitHubIntegrationConfig = { host: 'github.com', rawBaseUrl: 'https://raw.githubusercontent.com', }; @@ -158,7 +157,7 @@ describe('GithubUrlReader', () => { }); it('happy path for ghe', () => { - const config: ProviderConfig = { + const config: GitHubIntegrationConfig = { host: 'ghe.mycompany.net', rawBaseUrl: 'https://ghe.mycompany.net/raw', }; @@ -173,60 +172,6 @@ describe('GithubUrlReader', () => { }); }); - describe('readConfig', () => { - function config( - providers: { host: string; apiBaseUrl?: string; token?: string }[], - ) { - return ConfigReader.fromConfigs([ - { - context: '', - data: { - integrations: { github: providers }, - }, - }, - ]); - } - - it('adds a default GitHub entry when missing', () => { - const output = readConfig(config([])); - expect(output).toEqual([ - { - host: 'github.com', - apiBaseUrl: 'https://api.github.com', - rawBaseUrl: 'https://raw.githubusercontent.com', - }, - ]); - }); - - it('injects the correct GitHub API base URL when missing', () => { - const output = readConfig(config([{ host: 'github.com' }])); - expect(output).toEqual([ - { - host: 'github.com', - apiBaseUrl: 'https://api.github.com', - rawBaseUrl: 'https://raw.githubusercontent.com', - }, - ]); - }); - - it('rejects custom targets with no base URLs', () => { - expect(() => readConfig(config([{ host: 'ghe.company.com' }]))).toThrow( - "GitHub integration for 'ghe.company.com' must configure an explicit apiBaseUrl and rawBaseUrl", - ); - }); - - it('rejects funky configs', () => { - expect(() => readConfig(config([{ host: 7 } as any]))).toThrow(/host/); - expect(() => readConfig(config([{ token: 7 } as any]))).toThrow(/token/); - expect(() => - readConfig(config([{ host: 'github.com', apiBaseUrl: 7 } as any])), - ).toThrow(/apiBaseUrl/); - expect(() => - readConfig(config([{ host: 'github.com', token: 7 } as any])), - ).toThrow(/token/); - }); - }); - describe('implementation', () => { it('rejects unknown targets', async () => { const processor = new GithubUrlReader( @@ -271,6 +216,7 @@ describe('GithubUrlReader', () => { const processor = new GithubUrlReader( { host: 'github.com', + apiBaseUrl: 'https://api.github.com', }, { treeResponseFactory }, ); @@ -293,6 +239,7 @@ describe('GithubUrlReader', () => { const processor = new GithubUrlReader( { host: 'github.com', + apiBaseUrl: 'https://api.github.com', }, { treeResponseFactory }, ); @@ -308,6 +255,7 @@ describe('GithubUrlReader', () => { const processor = new GithubUrlReader( { host: 'github.com', + apiBaseUrl: 'https://api.github.com', }, { treeResponseFactory }, ); diff --git a/packages/backend-common/src/reading/GithubUrlReader.ts b/packages/backend-common/src/reading/GithubUrlReader.ts index ca67a6cfcb..2fbaa0b32b 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.ts @@ -14,59 +14,25 @@ * limitations under the License. */ -import { Config } from '@backstage/config'; -import parseGitUri from 'git-url-parse'; +import { + GitHubIntegrationConfig, + readGitHubIntegrationConfigs, +} from '@backstage/integration'; import fetch from 'cross-fetch'; +import parseGitUri from 'git-url-parse'; import { Readable } from 'stream'; import { InputError, NotFoundError } from '../errors'; +import { ReadTreeResponseFactory } from './tree'; import { ReaderFactory, + ReadTreeOptions, ReadTreeResponse, UrlReader, - ReadTreeOptions, } from './types'; -import { ReadTreeResponseFactory } from './tree'; -/** - * The configuration parameters for a single GitHub API provider. - */ -export type ProviderConfig = { - /** - * The host of the target that this matches on, e.g. "github.com" - */ - host: string; - - /** - * The base URL of the API of this provider, e.g. "https://api.github.com", - * with no trailing slash. - * - * May be omitted specifically for GitHub; then it will be deduced. - * - * The API will always be preferred if both its base URL and a token are - * present. - */ - apiBaseUrl?: string; - - /** - * The base URL of the raw fetch endpoint of this provider, e.g. - * "https://raw.githubusercontent.com", with no trailing slash. - * - * May be omitted specifically for GitHub; then it will be deduced. - * - * The API will always be preferred if both its base URL and a token are - * present. - */ - rawBaseUrl?: string; - - /** - * The authorization token to use for requests to this provider. - * - * If no token is specified, anonymous access is used. - */ - token?: string; -}; - -export function getApiRequestOptions(provider: ProviderConfig): RequestInit { +export function getApiRequestOptions( + provider: GitHubIntegrationConfig, +): RequestInit { const headers: HeadersInit = { Accept: 'application/vnd.github.v3.raw', }; @@ -80,7 +46,9 @@ export function getApiRequestOptions(provider: ProviderConfig): RequestInit { }; } -export function getRawRequestOptions(provider: ProviderConfig): RequestInit { +export function getRawRequestOptions( + provider: GitHubIntegrationConfig, +): RequestInit { const headers: HeadersInit = {}; if (provider.token) { @@ -95,7 +63,10 @@ export function getRawRequestOptions(provider: ProviderConfig): RequestInit { // Converts for example // from: https://github.com/a/b/blob/branchname/path/to/c.yaml // to: https://api.github.com/repos/a/b/contents/path/to/c.yaml?ref=branchname -export function getApiUrl(target: string, provider: ProviderConfig): URL { +export function getApiUrl( + target: string, + provider: GitHubIntegrationConfig, +): URL { try { const { owner, name, ref, filepathtype, filepath } = parseGitUri(target); @@ -120,7 +91,10 @@ export function getApiUrl(target: string, provider: ProviderConfig): URL { // Converts for example // from: https://github.com/a/b/blob/branchname/c.yaml // to: https://raw.githubusercontent.com/a/b/branchname/c.yaml -export function getRawUrl(target: string, provider: ProviderConfig): URL { +export function getRawUrl( + target: string, + provider: GitHubIntegrationConfig, +): URL { try { const { owner, name, ref, filepathtype, filepath } = parseGitUri(target); @@ -142,60 +116,16 @@ export function getRawUrl(target: string, provider: ProviderConfig): URL { } } -export function readConfig(config: Config): ProviderConfig[] { - const providers: ProviderConfig[] = []; - - const providerConfigs = - config.getOptionalConfigArray('integrations.github') ?? []; - - // First read all the explicit providers - for (const providerConfig of providerConfigs) { - const host = providerConfig.getOptionalString('host') ?? 'github.com'; - let apiBaseUrl = providerConfig.getOptionalString('apiBaseUrl'); - let rawBaseUrl = providerConfig.getOptionalString('rawBaseUrl'); - const token = providerConfig.getOptionalString('token'); - - if (apiBaseUrl) { - apiBaseUrl = apiBaseUrl.replace(/\/+$/, ''); - } else if (host === 'github.com') { - apiBaseUrl = 'https://api.github.com'; - } - - if (rawBaseUrl) { - rawBaseUrl = rawBaseUrl.replace(/\/+$/, ''); - } else if (host === 'github.com') { - rawBaseUrl = 'https://raw.githubusercontent.com'; - } - - if (!apiBaseUrl && !rawBaseUrl) { - throw new Error( - `GitHub integration for '${host}' must configure an explicit apiBaseUrl and rawBaseUrl`, - ); - } - - providers.push({ host, apiBaseUrl, rawBaseUrl, token }); - } - - // If no explicit github.com provider was added, put one in the list as - // a convenience - if (!providers.some(p => p.host === 'github.com')) { - providers.push({ - host: 'github.com', - apiBaseUrl: 'https://api.github.com', - rawBaseUrl: 'https://raw.githubusercontent.com', - }); - } - - return providers; -} - /** * A processor that adds the ability to read files from GitHub v3 APIs, such as * the one exposed by GitHub itself. */ export class GithubUrlReader implements UrlReader { static factory: ReaderFactory = ({ config, treeResponseFactory }) => { - return readConfig(config).map(provider => { + const configs = readGitHubIntegrationConfigs( + config.getOptionalConfigArray('integrations.github') ?? [], + ); + return configs.map(provider => { const reader = new GithubUrlReader(provider, { treeResponseFactory }); const predicate = (url: URL) => url.host === provider.host; return { reader, predicate }; @@ -203,9 +133,15 @@ export class GithubUrlReader implements UrlReader { }; constructor( - private readonly config: ProviderConfig, + private readonly config: GitHubIntegrationConfig, private readonly deps: { treeResponseFactory: ReadTreeResponseFactory }, - ) {} + ) { + if (!config.apiBaseUrl && !config.rawBaseUrl) { + throw new Error( + `GitHub integration for '${config.host}' must configure an explicit apiBaseUrl and rawBaseUrl`, + ); + } + } async read(url: string): Promise { const useApi = diff --git a/packages/backend-common/src/reading/GitlabUrlReader.ts b/packages/backend-common/src/reading/GitlabUrlReader.ts index c621e41338..e2d3edfea2 100644 --- a/packages/backend-common/src/reading/GitlabUrlReader.ts +++ b/packages/backend-common/src/reading/GitlabUrlReader.ts @@ -14,48 +14,27 @@ * limitations under the License. */ +import { + GitLabIntegrationConfig, + readGitLabIntegrationConfigs, +} from '@backstage/integration'; import fetch from 'cross-fetch'; -import { Config } from '@backstage/config'; import { NotFoundError } from '../errors'; import { ReaderFactory, ReadTreeResponse, UrlReader } from './types'; -type Options = { - host: string; - token?: string; -}; - -function readConfig(config: Config): Options[] { - const optionsArr = Array(); - - const providerConfigs = - config.getOptionalConfigArray('integrations.gitlab') ?? []; - - for (const providerConfig of providerConfigs) { - const host = providerConfig.getOptionalString('host') ?? 'gitlab.com'; - const token = providerConfig.getOptionalString('token'); - - optionsArr.push({ host, token }); - } - - // As a convenience we always make sure there's at least an unauthenticated - // reader for public gitlab repos. - if (!optionsArr.some(p => p.host === 'gitlab.com')) { - optionsArr.push({ host: 'gitlab.com' }); - } - - return optionsArr; -} - export class GitlabUrlReader implements UrlReader { static factory: ReaderFactory = ({ config }) => { - return readConfig(config).map(options => { + const configs = readGitLabIntegrationConfigs( + config.getOptionalConfigArray('integrations.gitlab') ?? [], + ); + return configs.map(options => { const reader = new GitlabUrlReader(options); const predicate = (url: URL) => url.host === options.host; return { reader, predicate }; }); }; - constructor(private readonly options: Options) {} + constructor(private readonly options: GitLabIntegrationConfig) {} async read(url: string): Promise { // TODO(Rugvip): merged the old GitlabReaderProcessor in here and used @@ -133,9 +112,9 @@ export class GitlabUrlReader implements UrlReader { try { const url = new URL(target); - const branchAndfilePath = url.pathname.split('/-/blob/')[1]; + const branchAndFilePath = url.pathname.split('/-/blob/')[1]; - const [branch, ...filePath] = branchAndfilePath.split('/'); + const [branch, ...filePath] = branchAndFilePath.split('/'); url.pathname = [ '/api/v4/projects', diff --git a/packages/integration/.eslintrc.js b/packages/integration/.eslintrc.js new file mode 100644 index 0000000000..13573efa9c --- /dev/null +++ b/packages/integration/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint')], +}; diff --git a/packages/integration/README.md b/packages/integration/README.md new file mode 100644 index 0000000000..c1b66f0104 --- /dev/null +++ b/packages/integration/README.md @@ -0,0 +1,9 @@ +# Integrations common functionality + +Contains some common functionality of integrations. + +This package will be imported both by the frontend and backend. + +## Links + +- [The Backstage homepage](https://backstage.io) diff --git a/packages/integration/package.json b/packages/integration/package.json new file mode 100644 index 0000000000..cfbdcbe57c --- /dev/null +++ b/packages/integration/package.json @@ -0,0 +1,33 @@ +{ + "name": "@backstage/integration", + "version": "0.1.0", + "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" + }, + "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" + }, + "dependencies": { + "@backstage/config": "^0.1.1", + "git-url-parse": "^11.4.0" + }, + "devDependencies": { + "@backstage/cli": "^0.2.0", + "@types/jest": "^26.0.7" + }, + "files": [ + "dist" + ] +} diff --git a/packages/integration/src/azure/config.test.ts b/packages/integration/src/azure/config.test.ts new file mode 100644 index 0000000000..0b943f2081 --- /dev/null +++ b/packages/integration/src/azure/config.test.ts @@ -0,0 +1,91 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Config, ConfigReader } from '@backstage/config'; +import { + AzureIntegrationConfig, + readAzureIntegrationConfig, + readAzureIntegrationConfigs, +} from './config'; + +describe('readAzureIntegrationConfig', () => { + function buildConfig(data: Partial): Config { + return ConfigReader.fromConfigs([{ context: '', data }]); + } + + it('reads all values', () => { + const output = readAzureIntegrationConfig( + buildConfig({ + host: 'a.com', + token: 't', + }), + ); + expect(output).toEqual({ + host: 'a.com', + token: 't', + }); + }); + + it('inserts the defaults if missing', () => { + const output = readAzureIntegrationConfig(buildConfig({})); + expect(output).toEqual({ host: 'dev.azure.com' }); + }); + + it('rejects funky configs', () => { + const valid: any = { + host: 'a.com', + token: 't', + }; + expect(() => + readAzureIntegrationConfig(buildConfig({ ...valid, host: 7 })), + ).toThrow(/host/); + expect(() => + readAzureIntegrationConfig(buildConfig({ ...valid, token: 7 })), + ).toThrow(/token/); + }); +}); + +describe('readAzureIntegrationConfigs', () => { + function buildConfig(data: Partial[]): Config[] { + return data.map(item => + ConfigReader.fromConfigs([{ context: '', data: item }]), + ); + } + + it('reads all values', () => { + const output = readAzureIntegrationConfigs( + buildConfig([ + { + host: 'a.com', + token: 't', + }, + ]), + ); + expect(output).toContainEqual({ + host: 'a.com', + token: 't', + }); + }); + + it('adds a default entry when missing', () => { + const output = readAzureIntegrationConfigs(buildConfig([])); + expect(output).toEqual([ + { + host: 'dev.azure.com', + }, + ]); + }); +}); diff --git a/packages/integration/src/azure/config.ts b/packages/integration/src/azure/config.ts new file mode 100644 index 0000000000..27b73ed707 --- /dev/null +++ b/packages/integration/src/azure/config.ts @@ -0,0 +1,72 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Config } from '@backstage/config'; + +const AZURE_HOST = 'dev.azure.com'; + +/** + * The configuration parameters for a single Azure provider. + */ +export type AzureIntegrationConfig = { + /** + * The host of the target that this matches on, e.g. "dev.azure.com". + * + * Currently only "dev.azure.com" is supported. + */ + host: string; + + /** + * The authorization token to use for requests. + * + * If no token is specified, anonymous access is used. + */ + token?: string; +}; + +/** + * Reads a single Azure integration config. + * + * @param config The config object of a single integration + */ +export function readAzureIntegrationConfig( + config: Config, +): AzureIntegrationConfig { + const host = config.getOptionalString('host') ?? AZURE_HOST; + const token = config.getOptionalString('token'); + return { host, token }; +} + +/** + * Reads a set of Azure integration configs, and inserts some defaults for + * public Azure if not specified. + * + * @param configs All of the integration config objects + */ +export function readAzureIntegrationConfigs( + configs: Config[], +): AzureIntegrationConfig[] { + // First read all the explicit integrations + const result = configs.map(readAzureIntegrationConfig); + + // If no explicit dev.azure.com integration was added, put one in the list as + // a convenience + if (!result.some(c => c.host === AZURE_HOST)) { + result.push({ host: AZURE_HOST }); + } + + return result; +} diff --git a/packages/integration/src/azure/index.ts b/packages/integration/src/azure/index.ts new file mode 100644 index 0000000000..ede0c88a81 --- /dev/null +++ b/packages/integration/src/azure/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { + readAzureIntegrationConfig, + readAzureIntegrationConfigs, +} from './config'; +export type { AzureIntegrationConfig } from './config'; diff --git a/packages/integration/src/bitbucket/config.test.ts b/packages/integration/src/bitbucket/config.test.ts new file mode 100644 index 0000000000..775a8b7d2d --- /dev/null +++ b/packages/integration/src/bitbucket/config.test.ts @@ -0,0 +1,133 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Config, ConfigReader } from '@backstage/config'; +import { + BitbucketIntegrationConfig, + readBitbucketIntegrationConfig, + readBitbucketIntegrationConfigs, +} from './config'; + +describe('readBitbucketIntegrationConfig', () => { + function buildConfig(data: Partial): Config { + return ConfigReader.fromConfigs([{ context: '', data }]); + } + + it('reads all values', () => { + const output = readBitbucketIntegrationConfig( + buildConfig({ + host: 'a.com', + apiBaseUrl: 'https://a.com/api', + token: 't', + username: 'u', + appPassword: 'p', + }), + ); + expect(output).toEqual({ + host: 'a.com', + apiBaseUrl: 'https://a.com/api', + token: 't', + username: 'u', + appPassword: 'p', + }); + }); + + it('inserts the defaults if missing', () => { + const output = readBitbucketIntegrationConfig(buildConfig({})); + expect(output).toEqual( + expect.objectContaining({ + host: 'bitbucket.org', + apiBaseUrl: 'https://api.bitbucket.org/2.0', + }), + ); + }); + + it('rejects funky configs', () => { + const valid: any = { + host: 'a.com', + apiBaseUrl: 'https://a.com/api', + token: 't', + username: 'u', + appPassword: 'p', + }; + expect(() => + readBitbucketIntegrationConfig(buildConfig({ ...valid, host: 7 })), + ).toThrow(/host/); + expect(() => + readBitbucketIntegrationConfig(buildConfig({ ...valid, apiBaseUrl: 7 })), + ).toThrow(/apiBaseUrl/); + expect(() => + readBitbucketIntegrationConfig(buildConfig({ ...valid, token: 7 })), + ).toThrow(/token/); + expect(() => + readBitbucketIntegrationConfig(buildConfig({ ...valid, username: 7 })), + ).toThrow(/username/); + expect(() => + readBitbucketIntegrationConfig(buildConfig({ ...valid, appPassword: 7 })), + ).toThrow(/appPassword/); + }); +}); + +describe('readBitbucketIntegrationConfigs', () => { + function buildConfig(data: Partial[]): Config[] { + return data.map(item => + ConfigReader.fromConfigs([{ context: '', data: item }]), + ); + } + + it('reads all values', () => { + const output = readBitbucketIntegrationConfigs( + buildConfig([ + { + host: 'a.com', + apiBaseUrl: 'https://a.com/api', + token: 't', + username: 'u', + appPassword: 'p', + }, + ]), + ); + expect(output).toContainEqual({ + host: 'a.com', + apiBaseUrl: 'https://a.com/api', + token: 't', + username: 'u', + appPassword: 'p', + }); + }); + + it('adds a default Bitbucket Cloud entry when missing', () => { + const output = readBitbucketIntegrationConfigs(buildConfig([])); + expect(output).toEqual([ + { + host: 'bitbucket.org', + apiBaseUrl: 'https://api.bitbucket.org/2.0', + }, + ]); + }); + + it('injects the correct Bitbucket Cloud API base URL when missing', () => { + const output = readBitbucketIntegrationConfigs( + buildConfig([{ host: 'bitbucket.org' }]), + ); + expect(output).toEqual([ + { + host: 'bitbucket.org', + apiBaseUrl: 'https://api.bitbucket.org/2.0', + }, + ]); + }); +}); diff --git a/packages/integration/src/bitbucket/config.ts b/packages/integration/src/bitbucket/config.ts new file mode 100644 index 0000000000..1997a5597d --- /dev/null +++ b/packages/integration/src/bitbucket/config.ts @@ -0,0 +1,115 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Config } from '@backstage/config'; + +const BITBUCKET_HOST = 'bitbucket.org'; +const BITBUCKET_API_BASE_URL = 'https://api.bitbucket.org/2.0'; + +/** + * The configuration parameters for a single Bitbucket API provider. + */ +export type BitbucketIntegrationConfig = { + /** + * The host of the target that this matches on, e.g. "bitbucket.org" + */ + host: string; + + /** + * The base URL of the API of this provider, e.g. "https://api.bitbucket.org/2.0", + * with no trailing slash. + * + * May be omitted specifically for Bitbucket Cloud; then it will be deduced. + * + * The API will always be preferred if both its base URL and a token are + * present. + */ + apiBaseUrl?: string; + + /** + * The authorization token to use for requests to a Bitbucket Server provider. + * + * See https://confluence.atlassian.com/bitbucketserver/personal-access-tokens-939515499.html + * + * If no token is specified, anonymous access is used. + */ + token?: string; + + /** + * The username to use for requests to Bitbucket Cloud (bitbucket.org). + */ + username?: string; + + /** + * Authentication with Bitbucket Cloud (bitbucket.org) is done using app passwords. + * + * See https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/ + */ + appPassword?: string; +}; + +/** + * Reads a single Bitbucket integration config. + * + * @param config The config object of a single integration + */ +export function readBitbucketIntegrationConfig( + config: Config, +): BitbucketIntegrationConfig { + const host = config.getOptionalString('host') ?? BITBUCKET_HOST; + let apiBaseUrl = config.getOptionalString('apiBaseUrl'); + const token = config.getOptionalString('token'); + const username = config.getOptionalString('username'); + const appPassword = config.getOptionalString('appPassword'); + + if (apiBaseUrl) { + apiBaseUrl = apiBaseUrl.replace(/\/+$/, ''); + } else if (host === BITBUCKET_HOST) { + apiBaseUrl = BITBUCKET_API_BASE_URL; + } + + return { + host, + apiBaseUrl, + token, + username, + appPassword, + }; +} + +/** + * Reads a set of Bitbucket integration configs, and inserts some defaults for + * public Bitbucket if not specified. + * + * @param configs All of the integration config objects + */ +export function readBitbucketIntegrationConfigs( + configs: Config[], +): BitbucketIntegrationConfig[] { + // First read all the explicit integrations + const result = configs.map(readBitbucketIntegrationConfig); + + // If no explicit bitbucket.org integration was added, put one in the list as + // a convenience + if (!result.some(c => c.host === BITBUCKET_HOST)) { + result.push({ + host: BITBUCKET_HOST, + apiBaseUrl: BITBUCKET_API_BASE_URL, + }); + } + + return result; +} diff --git a/packages/integration/src/bitbucket/index.ts b/packages/integration/src/bitbucket/index.ts new file mode 100644 index 0000000000..897c00d160 --- /dev/null +++ b/packages/integration/src/bitbucket/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { + readBitbucketIntegrationConfig, + readBitbucketIntegrationConfigs, +} from './config'; +export type { BitbucketIntegrationConfig } from './config'; diff --git a/packages/integration/src/github/config.test.ts b/packages/integration/src/github/config.test.ts new file mode 100644 index 0000000000..d33cffb7be --- /dev/null +++ b/packages/integration/src/github/config.test.ts @@ -0,0 +1,117 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Config, ConfigReader } from '@backstage/config'; +import { + GitHubIntegrationConfig, + readGitHubIntegrationConfig, + readGitHubIntegrationConfigs, +} from './config'; + +describe('readGitHubIntegrationConfig', () => { + function buildConfig(provider: Partial) { + return ConfigReader.fromConfigs([{ context: '', data: provider }]); + } + + it('reads all values', () => { + const output = readGitHubIntegrationConfig( + buildConfig({ + host: 'a.com', + apiBaseUrl: 'https://a.com/api', + rawBaseUrl: 'https://a.com/raw', + token: 't', + }), + ); + expect(output).toEqual({ + host: 'a.com', + apiBaseUrl: 'https://a.com/api', + rawBaseUrl: 'https://a.com/raw', + token: 't', + }); + }); + + it('injects the correct GitHub API base URL when missing', () => { + const output = readGitHubIntegrationConfig( + buildConfig({ host: 'github.com' }), + ); + expect(output).toEqual({ + host: 'github.com', + apiBaseUrl: 'https://api.github.com', + rawBaseUrl: 'https://raw.githubusercontent.com', + }); + }); + + it('rejects funky configs', () => { + const valid: any = { + host: 'a.com', + apiBaseUrl: 'https://a.com/api', + rawBaseUrl: 'https://a.com/raw', + token: 't', + }; + expect(() => + readGitHubIntegrationConfig(buildConfig({ ...valid, host: 7 })), + ).toThrow(/host/); + expect(() => + readGitHubIntegrationConfig(buildConfig({ ...valid, apiBaseUrl: 7 })), + ).toThrow(/apiBaseUrl/); + expect(() => + readGitHubIntegrationConfig(buildConfig({ ...valid, rawBaseUrl: 7 })), + ).toThrow(/rawBaseUrl/); + expect(() => + readGitHubIntegrationConfig(buildConfig({ ...valid, token: 7 })), + ).toThrow(/token/); + }); +}); + +describe('readGitHubIntegrationConfigs', () => { + function buildConfig( + providers: Partial[], + ): Config[] { + return providers.map(provider => + ConfigReader.fromConfigs([{ context: '', data: provider }]), + ); + } + + it('reads all values', () => { + const output = readGitHubIntegrationConfigs( + buildConfig([ + { + host: 'a.com', + apiBaseUrl: 'https://a.com/api', + rawBaseUrl: 'https://a.com/raw', + token: 't', + }, + ]), + ); + expect(output).toContainEqual({ + host: 'a.com', + apiBaseUrl: 'https://a.com/api', + rawBaseUrl: 'https://a.com/raw', + token: 't', + }); + }); + + it('adds a default GitHub entry when missing', () => { + const output = readGitHubIntegrationConfigs(buildConfig([])); + expect(output).toEqual([ + { + host: 'github.com', + apiBaseUrl: 'https://api.github.com', + rawBaseUrl: 'https://raw.githubusercontent.com', + }, + ]); + }); +}); diff --git a/packages/integration/src/github/config.ts b/packages/integration/src/github/config.ts new file mode 100644 index 0000000000..f646acc53b --- /dev/null +++ b/packages/integration/src/github/config.ts @@ -0,0 +1,113 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Config } from '@backstage/config'; + +const GITHUB_HOST = 'github.com'; +const GITHUB_API_BASE_URL = 'https://api.github.com'; +const GITHUB_RAW_BASE_URL = 'https://raw.githubusercontent.com'; + +/** + * The configuration parameters for a single GitHub integration. + */ +export type GitHubIntegrationConfig = { + /** + * The host of the target that this matches on, e.g. "github.com" + */ + host: string; + + /** + * The base URL of the API of this provider, e.g. "https://api.github.com", + * with no trailing slash. + * + * May be omitted specifically for GitHub; then it will be deduced. + * + * The API will always be preferred if both its base URL and a token are + * present. + */ + apiBaseUrl?: string; + + /** + * The base URL of the raw fetch endpoint of this provider, e.g. + * "https://raw.githubusercontent.com", with no trailing slash. + * + * May be omitted specifically for GitHub; then it will be deduced. + * + * The API will always be preferred if both its base URL and a token are + * present. + */ + rawBaseUrl?: string; + + /** + * The authorization token to use for requests to this provider. + * + * If no token is specified, anonymous access is used. + */ + token?: string; +}; + +/** + * Reads a single GitHub integration config. + * + * @param config The config object of a single integration + */ +export function readGitHubIntegrationConfig( + config: Config, +): GitHubIntegrationConfig { + const host = config.getOptionalString('host') ?? GITHUB_HOST; + let apiBaseUrl = config.getOptionalString('apiBaseUrl'); + let rawBaseUrl = config.getOptionalString('rawBaseUrl'); + const token = config.getOptionalString('token'); + + if (apiBaseUrl) { + apiBaseUrl = apiBaseUrl.replace(/\/+$/, ''); + } else if (host === GITHUB_HOST) { + apiBaseUrl = GITHUB_API_BASE_URL; + } + + if (rawBaseUrl) { + rawBaseUrl = rawBaseUrl.replace(/\/+$/, ''); + } else if (host === GITHUB_HOST) { + rawBaseUrl = GITHUB_RAW_BASE_URL; + } + + return { host, apiBaseUrl, rawBaseUrl, token }; +} + +/** + * Reads a set of GitHub integration configs, and inserts some defaults for + * public GitHub if not specified. + * + * @param configs All of the integration config objects + */ +export function readGitHubIntegrationConfigs( + configs: Config[], +): GitHubIntegrationConfig[] { + // First read all the explicit integrations + const result = configs.map(readGitHubIntegrationConfig); + + // If no explicit github.com integration was added, put one in the list as + // a convenience + if (!result.some(c => c.host === GITHUB_HOST)) { + result.push({ + host: GITHUB_HOST, + apiBaseUrl: GITHUB_API_BASE_URL, + rawBaseUrl: GITHUB_RAW_BASE_URL, + }); + } + + return result; +} diff --git a/packages/integration/src/github/index.ts b/packages/integration/src/github/index.ts new file mode 100644 index 0000000000..2099dd42e3 --- /dev/null +++ b/packages/integration/src/github/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { + readGitHubIntegrationConfig, + readGitHubIntegrationConfigs, +} from './config'; +export type { GitHubIntegrationConfig } from './config'; diff --git a/packages/integration/src/gitlab/config.test.ts b/packages/integration/src/gitlab/config.test.ts new file mode 100644 index 0000000000..9998a530c6 --- /dev/null +++ b/packages/integration/src/gitlab/config.test.ts @@ -0,0 +1,91 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Config, ConfigReader } from '@backstage/config'; +import { + GitLabIntegrationConfig, + readGitLabIntegrationConfig, + readGitLabIntegrationConfigs, +} from './config'; + +describe('readGitLabIntegrationConfig', () => { + function buildConfig(data: Partial): Config { + return ConfigReader.fromConfigs([{ context: '', data }]); + } + + it('reads all values', () => { + const output = readGitLabIntegrationConfig( + buildConfig({ + host: 'a.com', + token: 't', + }), + ); + expect(output).toEqual({ + host: 'a.com', + token: 't', + }); + }); + + it('inserts the defaults if missing', () => { + const output = readGitLabIntegrationConfig(buildConfig({})); + expect(output).toEqual({ host: 'gitlab.com' }); + }); + + it('rejects funky configs', () => { + const valid: any = { + host: 'a.com', + token: 't', + }; + expect(() => + readGitLabIntegrationConfig(buildConfig({ ...valid, host: 7 })), + ).toThrow(/host/); + expect(() => + readGitLabIntegrationConfig(buildConfig({ ...valid, token: 7 })), + ).toThrow(/token/); + }); +}); + +describe('readGitLabIntegrationConfigs', () => { + function buildConfig(data: Partial[]): Config[] { + return data.map(item => + ConfigReader.fromConfigs([{ context: '', data: item }]), + ); + } + + it('reads all values', () => { + const output = readGitLabIntegrationConfigs( + buildConfig([ + { + host: 'a.com', + token: 't', + }, + ]), + ); + expect(output).toContainEqual({ + host: 'a.com', + token: 't', + }); + }); + + it('adds a default entry when missing', () => { + const output = readGitLabIntegrationConfigs(buildConfig([])); + expect(output).toEqual([ + { + host: 'gitlab.com', + }, + ]); + }); +}); diff --git a/packages/integration/src/gitlab/config.ts b/packages/integration/src/gitlab/config.ts new file mode 100644 index 0000000000..97c948c999 --- /dev/null +++ b/packages/integration/src/gitlab/config.ts @@ -0,0 +1,70 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Config } from '@backstage/config'; + +const GITLAB_HOST = 'gitlab.com'; + +/** + * The configuration parameters for a single GitLab integration. + */ +export type GitLabIntegrationConfig = { + /** + * The host of the target that this matches on, e.g. "gitlab.com" + */ + host: string; + + /** + * The authorization token to use for requests this provider. + * + * If no token is specified, anonymous access is used. + */ + token?: string; +}; + +/** + * Reads a single GitLab integration config. + * + * @param config The config object of a single integration + */ +export function readGitLabIntegrationConfig( + config: Config, +): GitLabIntegrationConfig { + const host = config.getOptionalString('host') ?? GITLAB_HOST; + const token = config.getOptionalString('token'); + return { host, token }; +} + +/** + * Reads a set of GitLab integration configs, and inserts some defaults for + * public GitLab if not specified. + * + * @param configs All of the integration config objects + */ +export function readGitLabIntegrationConfigs( + configs: Config[], +): GitLabIntegrationConfig[] { + // First read all the explicit integrations + const result = configs.map(readGitLabIntegrationConfig); + + // As a convenience we always make sure there's at least an unauthenticated + // reader for public gitlab repos. + if (!result.some(c => c.host === GITLAB_HOST)) { + result.push({ host: GITLAB_HOST }); + } + + return result; +} diff --git a/packages/integration/src/gitlab/index.ts b/packages/integration/src/gitlab/index.ts new file mode 100644 index 0000000000..0801914fd4 --- /dev/null +++ b/packages/integration/src/gitlab/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { + readGitLabIntegrationConfig, + readGitLabIntegrationConfigs, +} from './config'; +export type { GitLabIntegrationConfig } from './config'; diff --git a/packages/integration/src/index.ts b/packages/integration/src/index.ts new file mode 100644 index 0000000000..bfed81824f --- /dev/null +++ b/packages/integration/src/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './azure'; +export * from './bitbucket'; +export * from './github'; +export * from './gitlab'; diff --git a/packages/integration/src/setupTests.ts b/packages/integration/src/setupTests.ts new file mode 100644 index 0000000000..ba33cf996b --- /dev/null +++ b/packages/integration/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export {}; From 3ad6320029670c37dbe25f564ba93c57094bc902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 16 Nov 2020 16:45:46 +0100 Subject: [PATCH 175/252] docs: catalog - extending the model, and relations (#3291) * docs: catalog - extending the model, and relations * Address comments --- .../software-catalog/descriptor-format.md | 71 ++++++++++++++++ .../software-catalog/extending-the-model.md | 63 ++++++++++++-- .../well-known-annotations.md | 4 +- .../software-catalog/well-known-relations.md | 83 +++++++++++++++++++ microsite/sidebars.json | 1 + 5 files changed, 213 insertions(+), 9 deletions(-) create mode 100644 docs/features/software-catalog/well-known-relations.md diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index a330ef196a..9081009385 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -22,11 +22,15 @@ we recommend that you name them `catalog-info.yaml`. - [Overall Shape Of An Entity](#overall-shape-of-an-entity) - [Common to All Kinds: The Envelope](#common-to-all-kinds-the-envelope) - [Common to All Kinds: The Metadata](#common-to-all-kinds-the-metadata) +- [Common to All Kinds: Relations](#common-to-all-kinds-relations) - [Kind: Component](#kind-component) - [Kind: Template](#kind-template) - [Kind: API](#kind-api) - [Kind: Group](#kind-group) - [Kind: User](#kind-user) +- [Kind: Resource](#kind-resource) +- [Kind: System](#kind-system) +- [Kind: Domain](#kind-domain) ## Overall Shape Of An Entity @@ -259,6 +263,61 @@ This field is optional, and currently has no special semantics. Each tag must be sequences of `[a-z0-9]` separated by `-`, at most 63 characters in total. +## Common to All Kinds: Relations + +The `relations` root field is a read-only list of relations, between the current +entity and other entities, described in the +[well-known relations section](well-known-relations.md). Relations are commonly +two-way, so that there's a pair of relation types each describing one direction +of the relation. + +A relation as part of a single entity that's read out of the API may look as +follows. + +```js +{ + // ... + "relations": [ + { + "target": { + "kind": "group", + "namespace": "default", + "name": "dev.infra" + }, + "type": "ownedBy" + } + ], + "spec": { + "owner": "dev.infra", + // ... + } +} +``` + +The fields of a relation are: + +| Field | Type | Description | +| ---------- | ------ | -------------------------------------------------------------------------------- | +| `target` | Object | A complete [compound reference](references.md) to the other end of the relation. | +| `type` | String | The type of relation FROM a source entity TO the target entity. | +| `metadata` | Object | Reserved for future use. | + +Entity descriptor YAML files are not supposed to contain this field. Instead, +catalog processors analyze the entity descriptor data and its surroundings, and +deduce relations that are then attached onto the entity as read from the +catalog. + +Where relations are produced, they are to be considered the authoritative source +for that piece of data. In the example above, a plugin would do better to +consume the relation rather than `spec.owner` for deducing the owner of the +entity, because it may even be the case that the owner isn't taken from the YAML +at all - it could be taken from a CODEOWNERS file nearby instead for example. +Also, the `spec.owner` is on a shortened form and may have semantics associated +with it (such as the default kind being `Group` if not specified). + +See the [well-known relations section](well-known-relations.md) for a list of +well-known / common relations and their semantics. + ## Kind: Component Describes the following entity kind: @@ -740,3 +799,15 @@ with the default kind `Group` and the default namespace equal to the same namespace as the user. Only `Group` entities may be referenced. Most commonly, these entries point to groups in the same namespace, so in those cases it is sufficient to enter only the `metadata.name` field of those groups. + +## Kind: Resource + +This kind is not yet defined, but is reserved [for future use](system-model.md). + +## Kind: System + +This kind is not yet defined, but is reserved [for future use](system-model.md). + +## Kind: Domain + +This kind is not yet defined, but is reserved [for future use](system-model.md). diff --git a/docs/features/software-catalog/extending-the-model.md b/docs/features/software-catalog/extending-the-model.md index 72170fb576..cbacce40c7 100644 --- a/docs/features/software-catalog/extending-the-model.md +++ b/docs/features/software-catalog/extending-the-model.md @@ -4,6 +4,36 @@ title: Extending the model description: Documentation on Extending the model --- +The Backstage catalog [entity data model](descriptor-format.md) is based on the +[Kubernetes objects format](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/), +and borrows a lot of its semantics as well. This page describes those semantics +at a higher level and how to extend them to fit your organization. + +Backstage comes with a number of catalog concepts out of the box: + +- There are a number of builtin versioned _kinds_, such as `Component`, `User` + etc. These encapsulate the high level concept of an entity, and define the + schema for its entity definition data. +- An entity has both a _metadata_ object and a _spec_ object at the root. +- Each kind may or may not have a _type_. For example, there are several well + known types of component, such as `service` and `website`. These clarify the + more detailed nature of the entity, and may affect what features are exposed + in the interface. +- Entities may have a number of _[annotations](well-known-annotations.md)_ on + them. These can be added either by humans into the descriptor files, or added + by automated processes when the entity is ingested into the catalog. +- Entities may have a number of _labels_ on them. +- Entities may have a number of _relations_, expressing how they relate to each + other in different ways. + +We'll list different possibilities for extending this below. + +## Adding a New Kind + +> TODO: Fill in + +## Adding a New Type of an Existing Kind + Backstage natively supports tracking of the following component [`type`](descriptor-format.md)'s: @@ -20,13 +50,6 @@ track in Backstage, it is possible to add your own software types that fit your organization's data model. Inside Spotify our model has grown significantly over the years, and now includes ML models, Apps, data pipelines and many more. -## Adding a new type - -TODO: Describe what changes are needed to add a new type that shows up in the -catalog. - -## The Other type - It might be tempting to put software that doesn't fit into any of the existing types into Other. There are a few reasons why we advise against this; firstly, we have found that it is preferred to match the conceptual model that your @@ -39,3 +62,29 @@ For example, the [Lighthouse plugin](https://github.com/backstage/backstage/tree/master/plugins/lighthouse) only makes sense for Websites. The more specific you can be in how you model your software, the easier it is to provide plugins that are contextual. + +> TODO: Fill in + +## Changing the Validation Rules for Core Entity Fields + +> TODO: Fill in + +## Adding New Fields to the Metadata Object + +> TODO: Fill in + +## Adding New Fields to the Spec Object of an Existing Kind + +> TODO: Fill in + +## Adding a New Annotation + +> TODO: Fill in + +## Adding a New Label + +> TODO: Fill in + +## Adding a New Relation Type + +> TODO: Fill in diff --git a/docs/features/software-catalog/well-known-annotations.md b/docs/features/software-catalog/well-known-annotations.md index 5cb6837340..1f5e851064 100644 --- a/docs/features/software-catalog/well-known-annotations.md +++ b/docs/features/software-catalog/well-known-annotations.md @@ -2,9 +2,9 @@ id: well-known-annotations title: Well-known Annotations on Catalog Entities sidebar_label: Well-known Annotations -description: Documentation on lists a number of well known Annotations, that +description: Documentation that lists a number of well known Annotations, that have defined semantics. They can be attached to catalog entities and consumed -by plugins as needed +by plugins as needed. --- This section lists a number of well known diff --git a/docs/features/software-catalog/well-known-relations.md b/docs/features/software-catalog/well-known-relations.md new file mode 100644 index 0000000000..fe1c32b02f --- /dev/null +++ b/docs/features/software-catalog/well-known-relations.md @@ -0,0 +1,83 @@ +--- +id: well-known-relations +title: Well-known Relations between Catalog Entities +sidebar_label: Well-known Relations +description: Documentation that lists a number of well known Relations, that +have defined semantics. They can be attached to catalog entities and consumed +by plugins as needed. +--- + +This section lists a number of well known +[entity relation types](descriptor-format.md#common-to-all-kinds-relations), +that have defined semantics. They can be attached to catalog entities and +consumed by plugins as needed. + +If you are looking to extend the set of relations, see +[Extending the model](extending-the-model.md). + +## Relations + +This is a (non-exhaustive) list of relations that are known to be in active use. + +Each relation has a _source_ (implicitly: the entity that holds the relation), a +_target_ (the entity to which the source has a relation), and a _type_ that +tells what relation the source has with the target. The relation is directional; +there are commonly pairs of relation types and the entity at the other end will +have the opposite relation in the opposite direction (e.g. when querying for +`A`, you will see `A.ownedBy.B`, and when querying `B`, you will see +`B.ownerOf.A`). + +### `ownedBy` and `ownerOf` + +An ownership relation where the owner is usually an organizational entity +([User](descriptor-format.md#kind-user) or +[Group](descriptor-format.md#kind-group)), and the other entity can be anything. + +In Backstage, the owner of an entity is the singular entity (commonly a team) +that bears ultimate responsibility for the entity, and has the authority and +capability to develop and maintain it. They will be the point of contact if +something goes wrong, or if features are to be requested. The main purpose of +this relation is for display purposes in Backstage, so that people looking at +catalog entities can get an understanding of to whom this entity belongs. It is +not to be used by automated processes to for example assign authorization in +runtime systems. There may be others that also develop or otherwise touch the +entity, but there will always be one ultimate owner. + +This relation is commonly generated based on `spec.owner` of the owned entity, +where present. + +### `consumesApi` and `providesApi` + +A relation with an [API](descriptor-format.md#kind-api) entity, typically from a +[Component](descriptor-format.md#kind-component) or +[System](descriptor-format.md#kind-system). + +These relations express that a component or system either exposes an API - +meaning that it hosts callable endpoints from which you can consume that API - +or that they are dependent on being able to consume that API. + +This relation is commonly generated based on `spec.implementsApis` of the +component or system in question. + +### `dependsOn` and `dependencyOf` + +A relation denoting a dependency on another entity. + +This relation is a general expression of being in need of that other entity for +an entity to function. It can for example be used to express that a website +component needs a library component as part of its build, or that a service +component uses a persistent storage resource. + +### `parentOf` and `childOf` + +A parent/child relation to build up a tree, used for example to describe the +organizational structure between [Groups](descriptor-format.md#kind-group). + +This relation is commonly based on `spec.parent` and/or `spec.children`. + +### `memberOf` and `hasMember` + +A membership relation, typically for [Users](descriptor-format.md#kind-user) in +[Groups](descriptor-format.md#kind-group). + +This relation is commonly based on `spec.memberOf`. diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 64fdd2f420..cb7cfcd6d5 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -45,6 +45,7 @@ "features/software-catalog/descriptor-format", "features/software-catalog/references", "features/software-catalog/well-known-annotations", + "features/software-catalog/well-known-relations", "features/software-catalog/extending-the-model", "features/software-catalog/external-integrations", "features/software-catalog/software-catalog-api" From 885526474eb7d62599c38e5f5a11d653a4c17175 Mon Sep 17 00:00:00 2001 From: Brenda Sukh Date: Mon, 16 Nov 2020 16:14:19 -0500 Subject: [PATCH 176/252] Fix styling on dropdowns --- .../CostInsightsPage/CostInsightsPage.tsx | 19 +++++++++++-------- .../CurrencySelect/CurrencySelect.tsx | 1 - 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsPage.tsx b/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsPage.tsx index 80464a56cb..8a9400d97a 100644 --- a/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsPage.tsx +++ b/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsPage.tsx @@ -192,21 +192,24 @@ export const CostInsightsPage = () => { px={3} marginTop={10} display="flex" - flexDirection="row" justifyContent="space-between" + alignItems="center" + minHeight={40} > - + Cost Overview Billing data as of {lastCompleteBillingDate} - - + + + + {currencies.map((c: Currency) => ( Date: Mon, 16 Nov 2020 17:34:35 -0500 Subject: [PATCH 177/252] Add label to currency select --- .../CurrencySelect/CurrencySelect.tsx | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/plugins/cost-insights/src/components/CurrencySelect/CurrencySelect.tsx b/plugins/cost-insights/src/components/CurrencySelect/CurrencySelect.tsx index 1b5920ffcb..2db1b8ee38 100644 --- a/plugins/cost-insights/src/components/CurrencySelect/CurrencySelect.tsx +++ b/plugins/cost-insights/src/components/CurrencySelect/CurrencySelect.tsx @@ -15,7 +15,13 @@ */ import React from 'react'; -import { MenuItem, Select, SelectProps } from '@material-ui/core'; +import { + InputLabel, + FormControl, + MenuItem, + Select, + SelectProps, +} from '@material-ui/core'; import { Currency, CurrencyType } from '../../types'; import { findAlways } from '../../utils/assert'; import { useSelectStyles as useStyles } from '../../utils/styles'; @@ -51,24 +57,28 @@ export const CurrencySelect = ({ }; return ( - + + Convert to: + + ); }; From d33f5157c797afbe9358515510012dafc624d95c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Frinnstr=C3=B6m?= Date: Tue, 17 Nov 2020 10:36:12 +0100 Subject: [PATCH 178/252] Extracted pushToRemote in scaffolder-backend --- .changeset/silent-suns-bathe.md | 5 + .../scaffolder/stages/publish/azure.test.ts | 125 +----------- .../src/scaffolder/stages/publish/azure.ts | 30 +-- .../scaffolder/stages/publish/github.test.ts | 181 +++++------------- .../src/scaffolder/stages/publish/github.ts | 35 +--- .../scaffolder/stages/publish/gitlab.test.ts | 138 ++----------- .../src/scaffolder/stages/publish/gitlab.ts | 29 +-- .../scaffolder/stages/publish/helpers.test.ts | 112 +++++++++++ .../src/scaffolder/stages/publish/helpers.ts | 55 ++++++ 9 files changed, 259 insertions(+), 451 deletions(-) create mode 100644 .changeset/silent-suns-bathe.md create mode 100644 plugins/scaffolder-backend/src/scaffolder/stages/publish/helpers.test.ts create mode 100644 plugins/scaffolder-backend/src/scaffolder/stages/publish/helpers.ts diff --git a/.changeset/silent-suns-bathe.md b/.changeset/silent-suns-bathe.md new file mode 100644 index 0000000000..96e32ea125 --- /dev/null +++ b/.changeset/silent-suns-bathe.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Extracted pushToRemote function for reuse between publishers diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/publish/azure.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/publish/azure.test.ts index 8b84eb0edf..7ab6f81ae5 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/publish/azure.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/publish/azure.test.ts @@ -17,10 +17,13 @@ jest.mock('nodegit'); jest.mock('azure-devops-node-api/GitApi'); jest.mock('azure-devops-node-api/interfaces/GitInterfaces'); +jest.mock('./helpers', () => ({ + pushToRemoteUserPass: jest.fn(), +})); import { AzurePublisher } from './azure'; import { GitApi } from 'azure-devops-node-api/GitApi'; -import * as NodeGit from 'nodegit'; +import { pushToRemoteUserPass } from './helpers'; const { mockGitApi } = require('azure-devops-node-api/GitApi') as { mockGitApi: { @@ -28,25 +31,6 @@ const { mockGitApi } = require('azure-devops-node-api/GitApi') as { }; }; -const { - Repository, - mockRepo, - mockIndex, - Signature, - Remote, - mockRemote, - Cred, -} = require('nodegit') as { - Repository: jest.Mocked<{ init: any }>; - Signature: jest.Mocked<{ now: any }>; - Cred: jest.Mocked<{ userpassPlaintextNew: any }>; - Remote: jest.Mocked<{ create: any }>; - - mockIndex: jest.Mocked; - mockRepo: jest.Mocked; - mockRemote: jest.Mocked; -}; - describe('Azure Publisher', () => { const publisher = new AzurePublisher(new GitApi('', []), 'fake-token'); @@ -60,7 +44,7 @@ describe('Azure Publisher', () => { remoteUrl: 'mockclone', } as { remoteUrl: string }); - await publisher.publish({ + const result = await publisher.publish({ values: { storePath: 'project/repo', owner: 'bob', @@ -68,109 +52,16 @@ describe('Azure Publisher', () => { directory: '/tmp/test', }); + expect(result).toEqual({ remoteUrl: 'mockclone' }); expect(mockGitApi.createRepository).toHaveBeenCalledWith( { name: 'repo', }, 'project', ); - }); - }); - - describe('publish: createGitDirectory', () => { - const values = { - isOrg: true, - storePath: 'blam/test', - owner: 'lols', - }; - - const mockDir = '/tmp/test/dir'; - - mockGitApi.createRepository.mockResolvedValue({ - remoteUrl: 'mockclone', - } as { remoteUrl: string }); - - it('should call init on the repo with the directory', async () => { - await publisher.publish({ - values, - directory: mockDir, - }); - - expect(Repository.init).toHaveBeenCalledWith(mockDir, 0); - }); - - it('should call refresh index on the index and write the new files', async () => { - await publisher.publish({ - values, - directory: mockDir, - }); - - expect(mockRepo.refreshIndex).toHaveBeenCalled(); - }); - - it('should call add all files and write', async () => { - await publisher.publish({ - values, - directory: mockDir, - }); - - expect(mockIndex.addAll).toHaveBeenCalled(); - expect(mockIndex.write).toHaveBeenCalled(); - expect(mockIndex.writeTree).toHaveBeenCalled(); - }); - - it('should create a commit with on head with the right name and commiter', async () => { - const mockSignature = { mockSignature: 'bloblly' }; - Signature.now.mockReturnValue(mockSignature); - - await publisher.publish({ - values, - directory: mockDir, - }); - - expect(Signature.now).toHaveBeenCalledTimes(2); - expect(Signature.now).toHaveBeenCalledWith( - 'Scaffolder', - 'scaffolder@backstage.io', - ); - - expect(mockRepo.createCommit).toHaveBeenCalledWith( - 'HEAD', - mockSignature, - mockSignature, - 'initial commit', - 'mockoid', - [], - ); - }); - - it('creates a remote with the repo and remote', async () => { - await publisher.publish({ - values, - directory: mockDir, - }); - - expect(Remote.create).toHaveBeenCalledWith( - mockRepo, - 'origin', + expect(pushToRemoteUserPass).toHaveBeenCalledWith( + '/tmp/test', 'mockclone', - ); - }); - - it('shoud push to the remote repo', async () => { - await publisher.publish({ - values, - directory: mockDir, - }); - - const [remotes, { callbacks }] = mockRemote.push.mock - .calls[0] as NodeGit.PushOptions[]; - - expect(remotes).toEqual(['refs/heads/master:refs/heads/master']); - - callbacks?.credentials?.(); - - expect(Cred.userpassPlaintextNew).toHaveBeenCalledWith( 'notempty', 'fake-token', ); diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/publish/azure.ts b/plugins/scaffolder-backend/src/scaffolder/stages/publish/azure.ts index 9dfc7b5b66..33f7f89199 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/publish/azure.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/publish/azure.ts @@ -17,10 +17,9 @@ import { PublisherBase } from './types'; import { GitApi } from 'azure-devops-node-api/GitApi'; import { GitRepositoryCreateOptions } from 'azure-devops-node-api/interfaces/GitInterfaces'; - +import { pushToRemoteUserPass } from './helpers'; import { JsonValue } from '@backstage/config'; import { RequiredTemplateValues } from '../templater'; -import { Repository, Remote, Signature, Cred } from 'nodegit'; export class AzurePublisher implements PublisherBase { private readonly client: GitApi; @@ -39,7 +38,7 @@ export class AzurePublisher implements PublisherBase { directory: string; }): Promise<{ remoteUrl: string }> { const remoteUrl = await this.createRemote(values); - await this.pushToRemote(directory, remoteUrl); + await pushToRemoteUserPass(directory, remoteUrl, 'notempty', this.token); return { remoteUrl }; } @@ -54,29 +53,4 @@ export class AzurePublisher implements PublisherBase { return repo.remoteUrl || ''; } - - private async pushToRemote(directory: string, remote: string): Promise { - const repo = await Repository.init(directory, 0); - const index = await repo.refreshIndex(); - await index.addAll(); - await index.write(); - const oid = await index.writeTree(); - await repo.createCommit( - 'HEAD', - Signature.now('Scaffolder', 'scaffolder@backstage.io'), - Signature.now('Scaffolder', 'scaffolder@backstage.io'), - 'initial commit', - oid, - [], - ); - - const remoteRepo = await Remote.create(repo, 'origin', remote); - - await remoteRepo.push(['refs/heads/master:refs/heads/master'], { - callbacks: { - // Username can anything but the empty string according to: https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=preview-page#use-a-pat - credentials: () => Cred.userpassPlaintextNew('notempty', this.token), - }, - }); - } } diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts index e68b0d0447..c85b4acba3 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts @@ -16,15 +16,18 @@ jest.mock('@octokit/rest'); jest.mock('nodegit'); +jest.mock('./helpers', () => ({ + pushToRemoteUserPass: jest.fn(), +})); import { Octokit } from '@octokit/rest'; -import * as NodeGit from 'nodegit'; import { OctokitResponse, ReposCreateInOrgResponseData, UsersGetByUsernameResponseData, } from '@octokit/types'; import { GithubPublisher } from './github'; +import { pushToRemoteUserPass } from './helpers'; const { mockGithubClient } = require('@octokit/rest') as { mockGithubClient: { @@ -34,25 +37,6 @@ const { mockGithubClient } = require('@octokit/rest') as { }; }; -const { - Repository, - mockRepo, - mockIndex, - Signature, - Remote, - mockRemote, - Cred, -} = require('nodegit') as { - Repository: jest.Mocked<{ init: any }>; - Signature: jest.Mocked<{ now: any }>; - Cred: jest.Mocked<{ userpassPlaintextNew: any }>; - Remote: jest.Mocked<{ create: any }>; - - mockIndex: jest.Mocked; - mockRepo: jest.Mocked; - mockRemote: jest.Mocked; -}; - describe('GitHub Publisher', () => { beforeEach(() => { jest.clearAllMocks(); @@ -72,8 +56,13 @@ describe('GitHub Publisher', () => { clone_url: 'mockclone', }, } as OctokitResponse); + mockGithubClient.users.getByUsername.mockResolvedValue({ + data: { + type: 'Organization', + }, + } as OctokitResponse); - await publisher.publish({ + const result = await publisher.publish({ values: { storePath: 'blam/test', owner: 'bob', @@ -82,6 +71,7 @@ describe('GitHub Publisher', () => { directory: '/tmp/test', }); + expect(result).toEqual({ remoteUrl: 'mockclone' }); expect(mockGithubClient.repos.createInOrg).toHaveBeenCalledWith({ org: 'blam', name: 'test', @@ -97,6 +87,12 @@ describe('GitHub Publisher', () => { repo: 'test', permission: 'admin', }); + expect(pushToRemoteUserPass).toHaveBeenCalledWith( + '/tmp/test', + 'mockclone', + 'abc', + 'x-oauth-basic', + ); }); it('should use octokit to create a repo in the authed user if the organisation property is not set', async () => { @@ -111,7 +107,7 @@ describe('GitHub Publisher', () => { }, } as OctokitResponse); - await publisher.publish({ + const result = await publisher.publish({ values: { storePath: 'blam/test', owner: 'bob', @@ -120,6 +116,7 @@ describe('GitHub Publisher', () => { directory: '/tmp/test', }); + expect(result).toEqual({ remoteUrl: 'mockclone' }); expect( mockGithubClient.repos.createForAuthenticatedUser, ).toHaveBeenCalledWith({ @@ -127,6 +124,12 @@ describe('GitHub Publisher', () => { private: false, }); expect(mockGithubClient.repos.addCollaborator).not.toHaveBeenCalled(); + expect(pushToRemoteUserPass).toHaveBeenCalledWith( + '/tmp/test', + 'mockclone', + 'abc', + 'x-oauth-basic', + ); }); }); @@ -142,7 +145,7 @@ describe('GitHub Publisher', () => { }, } as OctokitResponse); - await publisher.publish({ + const result = await publisher.publish({ values: { storePath: 'blam/test', owner: 'bob', @@ -152,6 +155,7 @@ describe('GitHub Publisher', () => { directory: '/tmp/test', }); + expect(result).toEqual({ remoteUrl: 'mockclone' }); expect( mockGithubClient.repos.createForAuthenticatedUser, ).toHaveBeenCalledWith({ @@ -165,113 +169,12 @@ describe('GitHub Publisher', () => { username: 'bob', permission: 'admin', }); - }); - - describe('publish: createGitDirectory', () => { - const values = { - storePath: 'blam/test', - owner: 'lols', - access: 'lols', - }; - - const mockDir = '/tmp/test/dir'; - - mockGithubClient.repos.createInOrg.mockResolvedValue({ - data: { - clone_url: 'mockclone', - }, - } as OctokitResponse); - mockGithubClient.users.getByUsername.mockResolvedValue({ - data: { - type: 'Organization', - }, - } as OctokitResponse); - - it('should call init on the repo with the directory', async () => { - await publisher.publish({ - values, - directory: mockDir, - }); - - expect(Repository.init).toHaveBeenCalledWith(mockDir, 0); - }); - - it('should call refresh index on the index and write the new files', async () => { - await publisher.publish({ - values, - directory: mockDir, - }); - - expect(mockRepo.refreshIndex).toHaveBeenCalled(); - }); - - it('should call add all files and write', async () => { - await publisher.publish({ - values, - directory: mockDir, - }); - - expect(mockIndex.addAll).toHaveBeenCalled(); - expect(mockIndex.write).toHaveBeenCalled(); - expect(mockIndex.writeTree).toHaveBeenCalled(); - }); - - it('should create a commit with on head with the right name and commiter', async () => { - const mockSignature = { mockSignature: 'bloblly' }; - Signature.now.mockReturnValue(mockSignature); - - await publisher.publish({ - values, - directory: mockDir, - }); - - expect(Signature.now).toHaveBeenCalledTimes(2); - expect(Signature.now).toHaveBeenCalledWith( - 'Scaffolder', - 'scaffolder@backstage.io', - ); - - expect(mockRepo.createCommit).toHaveBeenCalledWith( - 'HEAD', - mockSignature, - mockSignature, - 'initial commit', - 'mockoid', - [], - ); - }); - - it('creates a remote with the repo and remote', async () => { - await publisher.publish({ - values, - directory: mockDir, - }); - - expect(Remote.create).toHaveBeenCalledWith( - mockRepo, - 'origin', - 'mockclone', - ); - }); - - it('shoud push to the remote repo', async () => { - await publisher.publish({ - values, - directory: mockDir, - }); - - const [remotes, { callbacks }] = mockRemote.push.mock - .calls[0] as NodeGit.PushOptions[]; - - expect(remotes).toEqual(['refs/heads/master:refs/heads/master']); - - callbacks?.credentials?.(); - - expect(Cred.userpassPlaintextNew).toHaveBeenCalledWith( - 'abc', - 'x-oauth-basic', - ); - }); + expect(pushToRemoteUserPass).toHaveBeenCalledWith( + '/tmp/test', + 'mockclone', + 'abc', + 'x-oauth-basic', + ); }); }); @@ -294,7 +197,7 @@ describe('GitHub Publisher', () => { }, } as OctokitResponse); - await publisher.publish({ + const result = await publisher.publish({ values: { isOrg: true, storePath: 'blam/test', @@ -303,12 +206,19 @@ describe('GitHub Publisher', () => { directory: '/tmp/test', }); + expect(result).toEqual({ remoteUrl: 'mockclone' }); expect(mockGithubClient.repos.createInOrg).toHaveBeenCalledWith({ org: 'blam', name: 'test', private: true, visibility: 'internal', }); + expect(pushToRemoteUserPass).toHaveBeenCalledWith( + '/tmp/test', + 'mockclone', + 'abc', + 'x-oauth-basic', + ); }); }); @@ -331,7 +241,7 @@ describe('GitHub Publisher', () => { }, } as OctokitResponse); - await publisher.publish({ + const result = await publisher.publish({ values: { storePath: 'blam/test', owner: 'bob', @@ -339,12 +249,19 @@ describe('GitHub Publisher', () => { directory: '/tmp/test', }); + expect(result).toEqual({ remoteUrl: 'mockclone' }); expect( mockGithubClient.repos.createForAuthenticatedUser, ).toHaveBeenCalledWith({ name: 'test', private: true, }); + expect(pushToRemoteUserPass).toHaveBeenCalledWith( + '/tmp/test', + 'mockclone', + 'abc', + 'x-oauth-basic', + ); }); }); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.ts b/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.ts index 60ed86daa1..d5542e8800 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.ts @@ -16,10 +16,9 @@ import { PublisherBase } from './types'; import { Octokit } from '@octokit/rest'; - +import { pushToRemoteUserPass } from './helpers'; import { JsonValue } from '@backstage/config'; import { RequiredTemplateValues } from '../templater'; -import { Repository, Remote, Signature, Cred } from 'nodegit'; export type RepoVisibilityOptions = 'private' | 'internal' | 'public'; @@ -52,7 +51,12 @@ export class GithubPublisher implements PublisherBase { directory: string; }): Promise<{ remoteUrl: string }> { const remoteUrl = await this.createRemote(values); - await this.pushToRemote(directory, remoteUrl); + await pushToRemoteUserPass( + directory, + remoteUrl, + this.token, + 'x-oauth-basic', + ); return { remoteUrl }; } @@ -104,29 +108,4 @@ export class GithubPublisher implements PublisherBase { return data?.clone_url; } - - private async pushToRemote(directory: string, remote: string): Promise { - const repo = await Repository.init(directory, 0); - const index = await repo.refreshIndex(); - await index.addAll(); - await index.write(); - const oid = await index.writeTree(); - await repo.createCommit( - 'HEAD', - Signature.now('Scaffolder', 'scaffolder@backstage.io'), - Signature.now('Scaffolder', 'scaffolder@backstage.io'), - 'initial commit', - oid, - [], - ); - - const remoteRepo = await Remote.create(repo, 'origin', remote); - await remoteRepo.push(['refs/heads/master:refs/heads/master'], { - callbacks: { - credentials: () => { - return Cred.userpassPlaintextNew(this.token, 'x-oauth-basic'); - }, - }, - }); - } } diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/publish/gitlab.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/publish/gitlab.test.ts index 43b19859de..a87fa6c6d9 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/publish/gitlab.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/publish/gitlab.test.ts @@ -16,11 +16,14 @@ jest.mock('nodegit'); jest.mock('@gitbeaker/node'); +jest.mock('./helpers', () => ({ + pushToRemoteUserPass: jest.fn(), +})); import { GitlabPublisher } from './gitlab'; import { Gitlab as GitlabAPI } from '@gitbeaker/core'; import { Gitlab } from '@gitbeaker/node'; -import * as NodeGit from 'nodegit'; +import { pushToRemoteUserPass } from './helpers'; const { mockGitlabClient } = require('@gitbeaker/node') as { mockGitlabClient: { @@ -30,25 +33,6 @@ const { mockGitlabClient } = require('@gitbeaker/node') as { }; }; -const { - Repository, - mockRepo, - mockIndex, - Signature, - Remote, - mockRemote, - Cred, -} = require('nodegit') as { - Repository: jest.Mocked<{ init: any }>; - Signature: jest.Mocked<{ now: any }>; - Cred: jest.Mocked<{ userpassPlaintextNew: any }>; - Remote: jest.Mocked<{ create: any }>; - - mockIndex: jest.Mocked; - mockRepo: jest.Mocked; - mockRemote: jest.Mocked; -}; - describe('GitLab Publisher', () => { const publisher = new GitlabPublisher(new Gitlab({}), 'fake-token'); @@ -61,8 +45,11 @@ describe('GitLab Publisher', () => { mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 42, } as { id: number }); + mockGitlabClient.Projects.create.mockResolvedValue({ + http_url_to_repo: 'mockclone', + } as { http_url_to_repo: string }); - await publisher.publish({ + const result = await publisher.publish({ values: { isOrg: true, storePath: 'blam/test', @@ -71,10 +58,17 @@ describe('GitLab Publisher', () => { directory: '/tmp/test', }); + expect(result).toEqual({ remoteUrl: 'mockclone' }); expect(mockGitlabClient.Projects.create).toHaveBeenCalledWith({ namespace_id: 42, name: 'test', }); + expect(pushToRemoteUserPass).toHaveBeenCalledWith( + '/tmp/test', + 'mockclone', + 'oauth2', + 'fake-token', + ); }); it('should use gitbeaker to create a repo in the authed user if the namespace property is not set', async () => { @@ -86,7 +80,7 @@ describe('GitLab Publisher', () => { http_url_to_repo: 'mockclone', } as { http_url_to_repo: string }); - await publisher.publish({ + const result = await publisher.publish({ values: { storePath: 'blam/test', owner: 'bob', @@ -94,109 +88,15 @@ describe('GitLab Publisher', () => { directory: '/tmp/test', }); + expect(result).toEqual({ remoteUrl: 'mockclone' }); expect(mockGitlabClient.Users.current).toHaveBeenCalled(); - expect(mockGitlabClient.Projects.create).toHaveBeenCalledWith({ namespace_id: 21, name: 'test', }); - }); - }); - - describe('publish: createGitDirectory', () => { - const values = { - isOrg: true, - storePath: 'blam/test', - owner: 'lols', - }; - - const mockDir = '/tmp/test/dir'; - - mockGitlabClient.Projects.create.mockResolvedValue({ - http_url_to_repo: 'mockclone', - } as { http_url_to_repo: string }); - - it('should call init on the repo with the directory', async () => { - await publisher.publish({ - values, - directory: mockDir, - }); - - expect(Repository.init).toHaveBeenCalledWith(mockDir, 0); - }); - - it('should call refresh index on the index and write the new files', async () => { - await publisher.publish({ - values, - directory: mockDir, - }); - - expect(mockRepo.refreshIndex).toHaveBeenCalled(); - }); - - it('should call add all files and write', async () => { - await publisher.publish({ - values, - directory: mockDir, - }); - - expect(mockIndex.addAll).toHaveBeenCalled(); - expect(mockIndex.write).toHaveBeenCalled(); - expect(mockIndex.writeTree).toHaveBeenCalled(); - }); - - it('should create a commit with on head with the right name and commiter', async () => { - const mockSignature = { mockSignature: 'bloblly' }; - Signature.now.mockReturnValue(mockSignature); - - await publisher.publish({ - values, - directory: mockDir, - }); - - expect(Signature.now).toHaveBeenCalledTimes(2); - expect(Signature.now).toHaveBeenCalledWith( - 'Scaffolder', - 'scaffolder@backstage.io', - ); - - expect(mockRepo.createCommit).toHaveBeenCalledWith( - 'HEAD', - mockSignature, - mockSignature, - 'initial commit', - 'mockoid', - [], - ); - }); - - it('creates a remote with the repo and remote', async () => { - await publisher.publish({ - values, - directory: mockDir, - }); - - expect(Remote.create).toHaveBeenCalledWith( - mockRepo, - 'origin', + expect(pushToRemoteUserPass).toHaveBeenCalledWith( + '/tmp/test', 'mockclone', - ); - }); - - it('shoud push to the remote repo', async () => { - await publisher.publish({ - values, - directory: mockDir, - }); - - const [remotes, { callbacks }] = mockRemote.push.mock - .calls[0] as NodeGit.PushOptions[]; - - expect(remotes).toEqual(['refs/heads/master:refs/heads/master']); - - callbacks?.credentials?.(); - - expect(Cred.userpassPlaintextNew).toHaveBeenCalledWith( 'oauth2', 'fake-token', ); diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/publish/gitlab.ts b/plugins/scaffolder-backend/src/scaffolder/stages/publish/gitlab.ts index a755a138b5..e748dc53dc 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/publish/gitlab.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/publish/gitlab.ts @@ -16,10 +16,9 @@ import { PublisherBase } from './types'; import { Gitlab } from '@gitbeaker/core'; - +import { pushToRemoteUserPass } from './helpers'; import { JsonValue } from '@backstage/config'; import { RequiredTemplateValues } from '../templater'; -import { Repository, Remote, Signature, Cred } from 'nodegit'; export class GitlabPublisher implements PublisherBase { private readonly client: Gitlab; @@ -38,7 +37,7 @@ export class GitlabPublisher implements PublisherBase { directory: string; }): Promise<{ remoteUrl: string }> { const remoteUrl = await this.createRemote(values); - await this.pushToRemote(directory, remoteUrl); + await pushToRemoteUserPass(directory, remoteUrl, 'oauth2', this.token); return { remoteUrl }; } @@ -63,28 +62,4 @@ export class GitlabPublisher implements PublisherBase { return project?.http_url_to_repo; } - - private async pushToRemote(directory: string, remote: string): Promise { - const repo = await Repository.init(directory, 0); - const index = await repo.refreshIndex(); - await index.addAll(); - await index.write(); - const oid = await index.writeTree(); - await repo.createCommit( - 'HEAD', - Signature.now('Scaffolder', 'scaffolder@backstage.io'), - Signature.now('Scaffolder', 'scaffolder@backstage.io'), - 'initial commit', - oid, - [], - ); - - const remoteRepo = await Remote.create(repo, 'origin', remote); - - await remoteRepo.push(['refs/heads/master:refs/heads/master'], { - callbacks: { - credentials: () => Cred.userpassPlaintextNew('oauth2', this.token), - }, - }); - } } diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/publish/helpers.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/publish/helpers.test.ts new file mode 100644 index 0000000000..3a317adacd --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/stages/publish/helpers.test.ts @@ -0,0 +1,112 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +jest.mock('nodegit'); +import * as NodeGit from 'nodegit'; +import { pushToRemoteCred } from './helpers'; + +const { + Repository, + mockRepo, + mockIndex, + Signature, + Remote, + mockRemote, + Cred, +} = require('nodegit') as { + Repository: jest.Mocked<{ init: any }>; + Signature: jest.Mocked<{ now: any }>; + Cred: jest.Mocked<{ userpassPlaintextNew: any }>; + Remote: jest.Mocked<{ create: any }>; + + mockIndex: jest.Mocked; + mockRepo: jest.Mocked; + mockRemote: jest.Mocked; +}; + +describe('pushToRemoteCred', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + const directory = '/tmp/test/dir'; + const remote = 'mockclone'; + const credentialsProvider = () => + NodeGit.Cred.userpassPlaintextNew('username', 'password'); + + it('should call init on the repo with the directory', async () => { + await pushToRemoteCred(directory, remote, credentialsProvider); + + expect(Repository.init).toHaveBeenCalledWith(directory, 0); + }); + + it('should call refresh index on the index and write the new files', async () => { + await pushToRemoteCred(directory, remote, credentialsProvider); + + expect(mockRepo.refreshIndex).toHaveBeenCalled(); + }); + + it('should call add all files and write', async () => { + await pushToRemoteCred(directory, remote, credentialsProvider); + + expect(mockIndex.addAll).toHaveBeenCalled(); + expect(mockIndex.write).toHaveBeenCalled(); + expect(mockIndex.writeTree).toHaveBeenCalled(); + }); + + it('should create a commit with on head with the right name and commiter', async () => { + const mockSignature = { mockSignature: 'bloblly' }; + Signature.now.mockReturnValue(mockSignature); + + await pushToRemoteCred(directory, remote, credentialsProvider); + + expect(Signature.now).toHaveBeenCalledTimes(2); + expect(Signature.now).toHaveBeenCalledWith( + 'Scaffolder', + 'scaffolder@backstage.io', + ); + + expect(mockRepo.createCommit).toHaveBeenCalledWith( + 'HEAD', + mockSignature, + mockSignature, + 'initial commit', + 'mockoid', + [], + ); + }); + + it('creates a remote with the repo and remote', async () => { + await pushToRemoteCred(directory, remote, credentialsProvider); + + expect(Remote.create).toHaveBeenCalledWith(mockRepo, 'origin', 'mockclone'); + }); + + it('shoud push to the remote repo', async () => { + await pushToRemoteCred(directory, remote, credentialsProvider); + + const [remotes, { callbacks }] = mockRemote.push.mock + .calls[0] as NodeGit.PushOptions[]; + + expect(remotes).toEqual(['refs/heads/master:refs/heads/master']); + + callbacks?.credentials?.(); + + expect(Cred.userpassPlaintextNew).toHaveBeenCalledWith( + 'username', + 'password', + ); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/publish/helpers.ts b/plugins/scaffolder-backend/src/scaffolder/stages/publish/helpers.ts new file mode 100644 index 0000000000..51f5a4c854 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/stages/publish/helpers.ts @@ -0,0 +1,55 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Repository, Remote, Signature, Cred } from 'nodegit'; + +export async function pushToRemoteCred( + directory: string, + remote: string, + credentialsProvider: () => Cred, +): Promise { + const repo = await Repository.init(directory, 0); + const index = await repo.refreshIndex(); + await index.addAll(); + await index.write(); + const oid = await index.writeTree(); + await repo.createCommit( + 'HEAD', + Signature.now('Scaffolder', 'scaffolder@backstage.io'), + Signature.now('Scaffolder', 'scaffolder@backstage.io'), + 'initial commit', + oid, + [], + ); + + const remoteRepo = await Remote.create(repo, 'origin', remote); + + await remoteRepo.push(['refs/heads/master:refs/heads/master'], { + callbacks: { + credentials: credentialsProvider, + }, + }); +} + +export async function pushToRemoteUserPass( + directory: string, + remote: string, + username: string, + password: string, +): Promise { + return pushToRemoteCred(directory, remote, () => + Cred.userpassPlaintextNew(username, password), + ); +} From 53b1a29968f3845c554e5a5b5d2db485f8986606 Mon Sep 17 00:00:00 2001 From: Mateusz Lewtak Date: Tue, 17 Nov 2020 12:20:27 +0100 Subject: [PATCH 179/252] Feat: bump GitHub Insights Plugin version --- packages/app/package.json | 2 +- yarn.lock | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index f013200fbb..8dda24fdf1 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -34,7 +34,7 @@ "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@octokit/rest": "^18.0.0", - "@roadiehq/backstage-plugin-github-insights": "^0.2.12", + "@roadiehq/backstage-plugin-github-insights": "^0.2.14", "@roadiehq/backstage-plugin-github-pull-requests": "^0.6.2", "@roadiehq/backstage-plugin-travis-ci": "^0.2.7", "@roadiehq/backstage-plugin-buildkite": "^0.1.2", diff --git a/yarn.lock b/yarn.lock index 10e70f52fb..947b33e89a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3706,10 +3706,10 @@ react-router-dom "6.0.0-beta.0" react-use "^15.3.3" -"@roadiehq/backstage-plugin-github-insights@^0.2.12": - version "0.2.12" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-insights/-/backstage-plugin-github-insights-0.2.12.tgz#aeda6306769f376cf6b5b9d74268b060a0f4e764" - integrity sha512-f9g5ajVWQkywoYo0zDUZV3g0SPxOTi3MvMuhUvgaZ/XKJr9mBZTe5qHnEYBSHOCB88l0Y2g0qHNdbjXhbFpokQ== +"@roadiehq/backstage-plugin-github-insights@^0.2.14": + version "0.2.14" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-insights/-/backstage-plugin-github-insights-0.2.14.tgz#2e4bf61495e650bb2b7a1711f97c9c4995215588" + integrity sha512-xhzHAmmTu7op1V7K3ytomGlmzHMs9jnb2Lc2YTrU3ame4WZcrrMCrqi8X9v8B8VHfwMBrwa55tyxRYObXrPr7A== dependencies: "@backstage/catalog-model" "^0.2.0" "@backstage/core" "^0.2.0" @@ -3722,10 +3722,8 @@ history "^5.0.0" react "^16.13.1" react-dom "^16.13.1" - react-markdown "^5.0.0" react-router "^6.0.0-beta.0" react-use "^15.3.3" - remark-gfm "^1.0.0" "@roadiehq/backstage-plugin-github-pull-requests@^0.6.2": version "0.6.2" @@ -19788,7 +19786,7 @@ react-markdown@^4.3.1: unist-util-visit "^1.3.0" xtend "^4.0.1" -react-markdown@^5.0.0, react-markdown@^5.0.2: +react-markdown@^5.0.2: version "5.0.2" resolved "https://registry.npmjs.org/react-markdown/-/react-markdown-5.0.2.tgz#d15a8beb37b4ec34fc23dd892e7755eb7040b8db" integrity sha512-kmkB4JbV7LqkDAjvaKRKtodB3n3Id76/DalaDun1U8FuLB0SenPfvH+jAQ5Pcpo54cACRQc1LB1yXmuuuIVecw== From d1bfe7b71fff1b2483729735a19a7b8f58909cfa Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 17 Nov 2020 13:19:28 +0100 Subject: [PATCH 180/252] Docs: Add TechDocs survey on docs site --- docs/features/techdocs/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/features/techdocs/README.md b/docs/features/techdocs/README.md index f1cf637997..16131a1ded 100644 --- a/docs/features/techdocs/README.md +++ b/docs/features/techdocs/README.md @@ -122,3 +122,12 @@ migrate Spotify's existing TechDocs features to open source. https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend [techdocs/container]: https://github.com/backstage/techdocs-container [techdocs/cli]: https://github.com/backstage/techdocs-cli + +## Feedback + +We have created a sweet and short TechDocs user survey - +https://docs.google.com/forms/d/e/1FAIpQLSdn5Vn3MQhCdyYRuW8cMzZkMQF0bFxXYN168gZRvESLfJWVVg/viewform + +This is to gather inputs from you (the Backstage community) which will help us +best serve TechDocs adopters and existing users. Your inputs will shape our +roadmap and we will share it in the open. From 22f7755d298833639046711a5ecb770adaea3e01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 17 Nov 2020 13:26:24 +0100 Subject: [PATCH 181/252] docs: skip prettier for long metadata lines (#3311) --- docs/features/software-catalog/descriptor-format.md | 4 ++-- docs/features/software-catalog/external-integrations.md | 4 ++-- docs/features/software-catalog/index.md | 4 ++-- docs/features/software-catalog/well-known-annotations.md | 5 ++--- docs/features/software-catalog/well-known-relations.md | 5 ++--- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index 9081009385..5d7a5b9af0 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -2,8 +2,8 @@ id: descriptor-format title: Descriptor Format of Catalog Entities sidebar_label: YAML File Format -description: Documentation on Descriptor Format of Catalog Entities which -describes the default data shape and semantics of catalog entities +# prettier-ignore +description: Documentation on Descriptor Format of Catalog Entities which describes the default data shape and semantics of catalog entities --- This section describes the default data shape and semantics of catalog entities. diff --git a/docs/features/software-catalog/external-integrations.md b/docs/features/software-catalog/external-integrations.md index 325bed5e4a..e7d253f11e 100644 --- a/docs/features/software-catalog/external-integrations.md +++ b/docs/features/software-catalog/external-integrations.md @@ -1,8 +1,8 @@ --- id: external-integrations title: External integrations -description: Documentation on External integrations to integrate systems -with Backstage +# prettier-ignore +description: Documentation on External integrations to integrate systems with Backstage --- Backstage natively supports importing catalog data through the use of diff --git a/docs/features/software-catalog/index.md b/docs/features/software-catalog/index.md index 08e6260e80..70541b85db 100644 --- a/docs/features/software-catalog/index.md +++ b/docs/features/software-catalog/index.md @@ -2,8 +2,8 @@ id: software-catalog-overview title: Backstage Service Catalog (alpha) sidebar_label: Overview -description: The Backstage Service Catalog — actually, a software catalog, since -it includes more than just services +# prettier-ignore +description: The Backstage Service Catalog — actually, a software catalog, since it includes more than just services --- ## What is a Service Catalog? diff --git a/docs/features/software-catalog/well-known-annotations.md b/docs/features/software-catalog/well-known-annotations.md index 1f5e851064..4d227ea568 100644 --- a/docs/features/software-catalog/well-known-annotations.md +++ b/docs/features/software-catalog/well-known-annotations.md @@ -2,9 +2,8 @@ id: well-known-annotations title: Well-known Annotations on Catalog Entities sidebar_label: Well-known Annotations -description: Documentation that lists a number of well known Annotations, that -have defined semantics. They can be attached to catalog entities and consumed -by plugins as needed. +# prettier-ignore +description: Documentation that lists a number of well known Annotations, that have defined semantics. They can be attached to catalog entities and consumed by plugins as needed. --- This section lists a number of well known diff --git a/docs/features/software-catalog/well-known-relations.md b/docs/features/software-catalog/well-known-relations.md index fe1c32b02f..07a4fc0a5c 100644 --- a/docs/features/software-catalog/well-known-relations.md +++ b/docs/features/software-catalog/well-known-relations.md @@ -2,9 +2,8 @@ id: well-known-relations title: Well-known Relations between Catalog Entities sidebar_label: Well-known Relations -description: Documentation that lists a number of well known Relations, that -have defined semantics. They can be attached to catalog entities and consumed -by plugins as needed. +# prettier-ignore +description: Documentation that lists a number of well known Relations, that have defined semantics. They can be attached to catalog entities and consumed by plugins as needed. --- This section lists a number of well known From ea0fe99f82d3fe504cd58bc0cd32db138fe87710 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Tue, 17 Nov 2020 12:40:52 -0500 Subject: [PATCH 182/252] Add tooltips --- plugins/catalog/src/components/AboutCard/AboutCard.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.tsx index 8da67d7493..89630d6f10 100644 --- a/plugins/catalog/src/components/AboutCard/AboutCard.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutCard.tsx @@ -119,6 +119,7 @@ export function AboutCard({ entity, variant }: AboutCardProps) { action={ { window.open(codeLink.edithref || '#', '_blank'); }} @@ -133,7 +134,12 @@ export function AboutCard({ entity, variant }: AboutCardProps) { disabled={ !entity.metadata.annotations?.['backstage.io/techdocs-ref'] } - label="View Techdocs" + label="View TechDocs" + title={ + !entity.metadata.annotations?.['backstage.io/techdocs-ref'] + ? 'No TechDocs available' + : '' + } icon={} href={`/docs/${ entity.metadata.namespace || ENTITY_DEFAULT_NAMESPACE @@ -142,6 +148,7 @@ export function AboutCard({ entity, variant }: AboutCardProps) { } href="api" /> From 8b7737d0b8f1dbe2e1f52ba30d9777241243b253 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Tue, 17 Nov 2020 12:44:13 -0500 Subject: [PATCH 183/252] Add changeset --- .changeset/friendly-dodos-remember.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/friendly-dodos-remember.md diff --git a/.changeset/friendly-dodos-remember.md b/.changeset/friendly-dodos-remember.md new file mode 100644 index 0000000000..463d71f806 --- /dev/null +++ b/.changeset/friendly-dodos-remember.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Add About Card tooltips From 5efc00c6a6c2b90c9c102c7b9ca93306893d9cbc Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Tue, 17 Nov 2020 13:45:11 -0500 Subject: [PATCH 184/252] Add typescript support for title --- .../components/AboutCard/IconLinkVertical/IconLinkVertical.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/catalog/src/components/AboutCard/IconLinkVertical/IconLinkVertical.tsx b/plugins/catalog/src/components/AboutCard/IconLinkVertical/IconLinkVertical.tsx index c054bc0d4e..dd267dde35 100644 --- a/plugins/catalog/src/components/AboutCard/IconLinkVertical/IconLinkVertical.tsx +++ b/plugins/catalog/src/components/AboutCard/IconLinkVertical/IconLinkVertical.tsx @@ -23,6 +23,7 @@ export type IconLinkVerticalProps = { icon?: React.ReactNode; href?: string; disabled?: boolean; + title?: string; label: string; }; @@ -57,6 +58,7 @@ export function IconLinkVertical({ {icon} From b3a5e3b150f85517b6127959ddcedfd28794741c Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Tue, 17 Nov 2020 13:50:50 -0500 Subject: [PATCH 185/252] Add tooltips plural --- .github/styles/vocab.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt index 3c5fa34adc..043b83a51f 100644 --- a/.github/styles/vocab.txt +++ b/.github/styles/vocab.txt @@ -192,6 +192,7 @@ tolerations Tolerations toolsets tooltip +tooltips touchpoints ui upvote From 65edcdee22f4f17184652542d44b5c2dd693b50b Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 17 Nov 2020 20:03:44 +0100 Subject: [PATCH 186/252] TechDocs: Remove hardcoded backend URL from techdocs-backend --- packages/backend/src/plugins/techdocs.ts | 2 +- .../techdocs-backend/src/techdocs/stages/publish/local.ts | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/plugins/techdocs.ts b/packages/backend/src/plugins/techdocs.ts index a9d6293c33..ecc67b528f 100644 --- a/packages/backend/src/plugins/techdocs.ts +++ b/packages/backend/src/plugins/techdocs.ts @@ -50,7 +50,7 @@ export default async function createPlugin({ const urlPreparer = new UrlPreparer(reader, logger); preparers.register('url', urlPreparer); - const publisher = new LocalPublish(logger); + const publisher = new LocalPublish(logger, config); const dockerClient = new Docker(); diff --git a/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts b/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts index 6d655d4632..221b0d48e7 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts @@ -18,12 +18,15 @@ import { Logger } from 'winston'; import { Entity } from '@backstage/catalog-model'; import { PublisherBase } from './types'; import { resolvePackagePath } from '@backstage/backend-common'; +import { Config } from '@backstage/config'; export class LocalPublish implements PublisherBase { private readonly logger: Logger; + private readonly config: Config; - constructor(logger: Logger) { + constructor(logger: Logger, config: Config) { this.logger = logger; + this.config = config; } publish({ @@ -63,8 +66,9 @@ export class LocalPublish implements PublisherBase { reject(err); } + const backendBaseUrl = this.config.getString('backend.baseUrl'); resolve({ - remoteUrl: `http://localhost:7000/api/techdocs/static/docs/${entity.metadata.name}`, + remoteUrl: `${backendBaseUrl}/api/techdocs/static/docs/${entity.metadata.name}`, }); }); }); From c01bf361a44e2e1c93b7602ea7ebd24fc7cbd983 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 17 Nov 2020 20:19:30 +0100 Subject: [PATCH 187/252] Modify tests to include new config param in LocalPublish --- plugins/techdocs-backend/src/service/standaloneServer.ts | 2 +- .../src/techdocs/stages/publish/local.test.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/techdocs-backend/src/service/standaloneServer.ts b/plugins/techdocs-backend/src/service/standaloneServer.ts index 79e8b0b945..7dda1b833b 100644 --- a/plugins/techdocs-backend/src/service/standaloneServer.ts +++ b/plugins/techdocs-backend/src/service/standaloneServer.ts @@ -53,7 +53,7 @@ export async function startStandaloneServer( const techdocsGenerator = new TechdocsGenerator(logger, config); generators.register('techdocs', techdocsGenerator); - const publisher = new LocalPublish(logger); + const publisher = new LocalPublish(logger, config); const dockerClient = new Docker(); diff --git a/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts b/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts index d97a4014f6..199e6346e8 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts @@ -18,6 +18,7 @@ import fs from 'fs-extra'; import path from 'path'; import { getVoidLogger } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; import { LocalPublish } from './local'; const createMockEntity = (annotations = {}) => { @@ -37,7 +38,8 @@ const logger = getVoidLogger(); describe('local publisher', () => { it('should publish generated documentation dir', async () => { - const publisher = new LocalPublish(logger); + const testConfig = ConfigReader.fromConfigs([{ context: '', data: {} }]); + const publisher = new LocalPublish(logger, testConfig); const mockEntity = createMockEntity(); From 3b314807c63e7b922b7d3538b4aaac3af5832b71 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 17 Nov 2020 21:03:56 +0100 Subject: [PATCH 188/252] TechDocs: Use discovery instead of config to read base url --- packages/backend/src/plugins/techdocs.ts | 2 +- plugins/techdocs-backend/package.json | 1 + .../src/service/standaloneServer.ts | 2 +- .../src/techdocs/stages/publish/local.test.ts | 19 ++++++++++++++++--- .../src/techdocs/stages/publish/local.ts | 15 ++++++++------- yarn.lock | 16 +++++----------- 6 files changed, 32 insertions(+), 23 deletions(-) diff --git a/packages/backend/src/plugins/techdocs.ts b/packages/backend/src/plugins/techdocs.ts index ecc67b528f..de48280e64 100644 --- a/packages/backend/src/plugins/techdocs.ts +++ b/packages/backend/src/plugins/techdocs.ts @@ -50,7 +50,7 @@ export default async function createPlugin({ const urlPreparer = new UrlPreparer(reader, logger); preparers.register('url', urlPreparer); - const publisher = new LocalPublish(logger, config); + const publisher = new LocalPublish(logger, discovery); const dockerClient = new Docker(); diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index c1d1f6ad27..236f53b9d1 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -23,6 +23,7 @@ "@backstage/backend-common": "^0.2.0", "@backstage/catalog-model": "^0.2.0", "@backstage/config": "^0.1.1", + "@backstage/core-api": "^0.2.1", "@types/dockerode": "^2.5.34", "@types/express": "^4.17.6", "command-exists-promise": "^2.0.2", diff --git a/plugins/techdocs-backend/src/service/standaloneServer.ts b/plugins/techdocs-backend/src/service/standaloneServer.ts index 7dda1b833b..4082a36172 100644 --- a/plugins/techdocs-backend/src/service/standaloneServer.ts +++ b/plugins/techdocs-backend/src/service/standaloneServer.ts @@ -53,7 +53,7 @@ export async function startStandaloneServer( const techdocsGenerator = new TechdocsGenerator(logger, config); generators.register('techdocs', techdocsGenerator); - const publisher = new LocalPublish(logger, config); + const publisher = new LocalPublish(logger, discovery); const dockerClient = new Docker(); diff --git a/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts b/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts index 199e6346e8..4b6503f620 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts @@ -17,7 +17,7 @@ /* eslint-disable no-restricted-syntax */ import fs from 'fs-extra'; import path from 'path'; -import { getVoidLogger } from '@backstage/backend-common'; +import { getVoidLogger, SingleHostDiscovery } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { LocalPublish } from './local'; @@ -38,8 +38,21 @@ const logger = getVoidLogger(); describe('local publisher', () => { it('should publish generated documentation dir', async () => { - const testConfig = ConfigReader.fromConfigs([{ context: '', data: {} }]); - const publisher = new LocalPublish(logger, testConfig); + const testConfig = ConfigReader.fromConfigs([ + { + context: '', + data: { + backend: { + baseUrl: 'http://localhost:7000', + listen: { + port: 7000, + }, + }, + }, + }, + ]); + const testDiscovery = SingleHostDiscovery.fromConfig(testConfig); + const publisher = new LocalPublish(logger, testDiscovery); const mockEntity = createMockEntity(); diff --git a/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts b/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts index 221b0d48e7..9f066a919c 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts @@ -18,15 +18,15 @@ import { Logger } from 'winston'; import { Entity } from '@backstage/catalog-model'; import { PublisherBase } from './types'; import { resolvePackagePath } from '@backstage/backend-common'; -import { Config } from '@backstage/config'; +import { DiscoveryApi } from '@backstage/core-api'; export class LocalPublish implements PublisherBase { private readonly logger: Logger; - private readonly config: Config; + private readonly discoveryApi: DiscoveryApi; - constructor(logger: Logger, config: Config) { + constructor(logger: Logger, discoveryApi: DiscoveryApi) { this.logger = logger; - this.config = config; + this.discoveryApi = discoveryApi; } publish({ @@ -66,9 +66,10 @@ export class LocalPublish implements PublisherBase { reject(err); } - const backendBaseUrl = this.config.getString('backend.baseUrl'); - resolve({ - remoteUrl: `${backendBaseUrl}/api/techdocs/static/docs/${entity.metadata.name}`, + this.discoveryApi.getBaseUrl('techdocs').then(techdocsApiUrl => { + resolve({ + remoteUrl: `${techdocsApiUrl}/static/docs/${entity.metadata.name}`, + }); }); }); }); diff --git a/yarn.lock b/yarn.lock index 10e70f52fb..9d4984e152 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1290,40 +1290,34 @@ to-fast-properties "^2.0.0" "@backstage/core@^0.2.0": - version "0.3.0" + version "0.2.0" + resolved "https://registry.npmjs.org/@backstage/core/-/core-0.2.0.tgz#543246b2d87563c9aa4d9fb96e40fdfc7e827520" + integrity sha512-75m2u3FoUngBOvt9l65xZcYTzzB+49OXpY1A9VNFUR1+jMs3cL/0HDfByQV2H0xXaHzMngQ8C5u/sWhkQsij1w== dependencies: "@backstage/config" "^0.1.1" - "@backstage/core-api" "^0.2.1" - "@backstage/theme" "^0.2.1" + "@backstage/core-api" "^0.2.0" + "@backstage/theme" "^0.2.0" "@material-ui/core" "^4.11.0" "@material-ui/icons" "^4.9.1" "@material-ui/lab" "4.0.0-alpha.45" - "@types/dagre" "^0.7.44" "@types/react" "^16.9" "@types/react-sparklines" "^1.7.0" classnames "^2.2.6" clsx "^1.1.0" - d3-selection "^2.0.0" - d3-shape "^2.0.0" - d3-zoom "^2.0.0" - dagre "^0.8.5" immer "^7.0.9" lodash "^4.17.15" material-table "^1.69.1" prop-types "^15.7.2" - qs "^6.9.4" rc-progress "^3.0.0" react "^16.12.0" react-dom "^16.12.0" react-helmet "6.1.0" react-hook-form "^6.6.0" - react-markdown "^5.0.2" react-router "6.0.0-beta.0" react-router-dom "6.0.0-beta.0" react-sparklines "^1.7.0" react-syntax-highlighter "^13.5.1" react-use "^15.3.3" - remark-gfm "^1.0.0" "@bcoe/v8-coverage@^0.2.3": version "0.2.3" From 2723a607915021dcfd71222187aa17c01fe4edee Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 17 Nov 2020 21:17:19 +0100 Subject: [PATCH 189/252] Update template app to use new API params for LocalPublish --- .../default-app/packages/backend/src/plugins/techdocs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts b/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts index 5506228962..ac4d81a8e8 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts @@ -28,7 +28,7 @@ export default async function createPlugin({ preparers.register('github', commonGitPreparer); preparers.register('gitlab', commonGitPreparer); - const publisher = new LocalPublish(logger); + const publisher = new LocalPublish(logger, discovery); const dockerClient = new Docker(); From a6a61f192c89c65c1293b07ac3398237d2a6a957 Mon Sep 17 00:00:00 2001 From: Ryan Vazquez Date: Tue, 17 Nov 2020 17:28:30 -0500 Subject: [PATCH 190/252] fix tooltip for long period separated titles --- .../src/components/BarChart/BarChartTooltip.tsx | 7 ++++--- plugins/cost-insights/src/utils/styles.ts | 8 +++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/plugins/cost-insights/src/components/BarChart/BarChartTooltip.tsx b/plugins/cost-insights/src/components/BarChart/BarChartTooltip.tsx index 2cb09b724e..8ab787a443 100644 --- a/plugins/cost-insights/src/components/BarChart/BarChartTooltip.tsx +++ b/plugins/cost-insights/src/components/BarChart/BarChartTooltip.tsx @@ -35,7 +35,6 @@ export const BarChartTooltip = ({ children, }: PropsWithChildren) => { const classes = useStyles(); - return ( - {title} + + {title} + {subtitle && ( {subtitle} )} - {topRight} + {topRight && {topRight}} {content && ( diff --git a/plugins/cost-insights/src/utils/styles.ts b/plugins/cost-insights/src/utils/styles.ts index de743d8459..1e8fd50899 100644 --- a/plugins/cost-insights/src/utils/styles.ts +++ b/plugins/cost-insights/src/utils/styles.ts @@ -386,7 +386,7 @@ export const useTooltipStyles = makeStyles( boxShadow: theme.shadows[1], color: theme.palette.tooltip.color, fontSize: theme.typography.fontSize, - width: 250, + maxWidth: 300, }, actions: { padding: theme.spacing(2), @@ -403,6 +403,12 @@ export const useTooltipStyles = makeStyles( divider: { backgroundColor: emphasize(theme.palette.divider, 1), }, + truncate: { + maxWidth: 200, + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + }, subtitle: { fontStyle: 'italic', }, From 74b9561a2bf77d9e462125a8b68033c6f4db663c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 18 Nov 2020 01:44:17 +0100 Subject: [PATCH 191/252] github/workdflows: bump setup-msbuild to fix windows builds (#3321) --- .github/workflows/e2e-win.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-win.yml b/.github/workflows/e2e-win.yml index 570d857c48..1844700cea 100644 --- a/.github/workflows/e2e-win.yml +++ b/.github/workflows/e2e-win.yml @@ -32,7 +32,7 @@ jobs: with: node-version: ${{ matrix.node-version }} - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.0.1 + uses: microsoft/setup-msbuild@v1.0.2 - name: yarn install run: yarn install --frozen-lockfile From 9023404510fc9579a8f755482d695cf49a67a5c9 Mon Sep 17 00:00:00 2001 From: Joel Low Date: Wed, 18 Nov 2020 14:11:15 +0800 Subject: [PATCH 192/252] feat(cli): Support specifying listen host/port for frontend --- .changeset/modern-boats-knock.md | 5 +++++ packages/cli/src/lib/bundler/server.ts | 9 +++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 .changeset/modern-boats-knock.md diff --git a/.changeset/modern-boats-knock.md b/.changeset/modern-boats-knock.md new file mode 100644 index 0000000000..16f80fe041 --- /dev/null +++ b/.changeset/modern-boats-knock.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Support specifying listen host/port for frontend diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index e1fb560e22..2710145d83 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -25,7 +25,12 @@ import { resolveBundlingPaths } from './paths'; export async function serveBundle(options: ServeOptions) { const url = resolveBaseUrl(options.config); - const port = Number(url.port) || (url.protocol === 'https:' ? 443 : 80); + const host = + options.config.getOptionalString('app.listen.host') || url.hostname; + const port = + options.config.getOptionalNumber('app.listen.port') || + Number(url.port) || + (url.protocol === 'https:' ? 443 : 80); const paths = resolveBundlingPaths(options); const pkgPath = paths.targetPackageJson; @@ -50,7 +55,7 @@ export async function serveBundle(options: ServeOptions) { clientLogLevel: 'warning', stats: 'errors-warnings', https: url.protocol === 'https:', - host: url.hostname, + host, port, proxy: pkg.proxy, }); From 7d680c075e9fd75122a4a3ac3dc8cb50e7f7bcdc Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Wed, 18 Nov 2020 11:20:37 +0100 Subject: [PATCH 193/252] [Search] Search frontend plugin (#3086) * feat(search) search plugin * feat(search): search page components * feat(search): api * feat(search): filters wip * feat(search): wip filters * fix(search): delete unused useParams hook * fix(search): update docs * fix(search): use latest versions of dependencies * fix(search): change version of catalog plugin * fix(search api): pass instance of catalog api to search api * fix(filters): rename component from FilterButton to FiltersButton * fixup * fix(filters): use list of checkboxes to match style of catalog page filters * fix(styles): use theme spacing for margins and paddings, delete unused styles * fix(search): change terminology of search input to be more consistent and clear * fix(search): restructure component exports according to ADR * fix(search): replace sm with xs on Grid components to support smaller screens * fix(search): add types * fixup * fix(search): bump backstage core * change versions of backstage theme and dev-utils --- .github/CODEOWNERS | 1 + packages/app/package.json | 1 + packages/app/src/plugins.ts | 1 + plugins/search/.eslintrc.js | 3 + plugins/search/README.md | 9 + plugins/search/dev/index.tsx | 19 ++ plugins/search/package.json | 49 ++++ plugins/search/src/apis.ts | 52 ++++ .../search/src/components/Filters/Filters.tsx | 132 ++++++++++ .../src/components/Filters/FiltersButton.tsx | 56 +++++ .../search/src/components/Filters/index.tsx | 19 ++ .../src/components/SearchBar/SearchBar.tsx | 69 ++++++ .../search/src/components/SearchBar/index.tsx | 17 ++ .../src/components/SearchPage/SearchPage.tsx | 55 +++++ .../src/components/SearchPage/index.tsx | 17 ++ .../components/SearchResult/SearchResult.tsx | 227 ++++++++++++++++++ .../src/components/SearchResult/index.tsx | 17 ++ plugins/search/src/components/index.tsx | 20 ++ plugins/search/src/index.ts | 16 ++ plugins/search/src/plugin.test.ts | 22 ++ plugins/search/src/plugin.ts | 29 +++ plugins/search/src/setupTests.ts | 17 ++ 22 files changed, 848 insertions(+) create mode 100644 plugins/search/.eslintrc.js create mode 100644 plugins/search/README.md create mode 100644 plugins/search/dev/index.tsx create mode 100644 plugins/search/package.json create mode 100644 plugins/search/src/apis.ts create mode 100644 plugins/search/src/components/Filters/Filters.tsx create mode 100644 plugins/search/src/components/Filters/FiltersButton.tsx create mode 100644 plugins/search/src/components/Filters/index.tsx create mode 100644 plugins/search/src/components/SearchBar/SearchBar.tsx create mode 100644 plugins/search/src/components/SearchBar/index.tsx create mode 100644 plugins/search/src/components/SearchPage/SearchPage.tsx create mode 100644 plugins/search/src/components/SearchPage/index.tsx create mode 100644 plugins/search/src/components/SearchResult/SearchResult.tsx create mode 100644 plugins/search/src/components/SearchResult/index.tsx create mode 100644 plugins/search/src/components/index.tsx create mode 100644 plugins/search/src/index.ts create mode 100644 plugins/search/src/plugin.test.ts create mode 100644 plugins/search/src/plugin.ts create mode 100644 plugins/search/src/setupTests.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d818c0d6b5..b3c9bc653d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -9,5 +9,6 @@ /plugins/cost-insights @backstage/silver-lining /plugins/cloudbuild @trivago/ebarrios /plugins/techdocs @backstage/techdocs-core +/plugins/search @backstage/techdocs-core /plugins/techdocs-backend @backstage/techdocs-core /.changeset/cost-insights-* @backstage/silver-lining diff --git a/packages/app/package.json b/packages/app/package.json index f013200fbb..dea63e3a4c 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -25,6 +25,7 @@ "@backstage/plugin-rollbar": "^0.2.1", "@backstage/plugin-scaffolder": "^0.3.0", "@backstage/plugin-sentry": "^0.2.1", + "@backstage/plugin-search": "^0.2.0", "@backstage/plugin-tech-radar": "^0.3.0", "@backstage/plugin-techdocs": "^0.2.1", "@backstage/plugin-user-settings": "^0.2.1", diff --git a/packages/app/src/plugins.ts b/packages/app/src/plugins.ts index c61a16e59c..d6577b4ce4 100644 --- a/packages/app/src/plugins.ts +++ b/packages/app/src/plugins.ts @@ -39,3 +39,4 @@ export { plugin as CostInsights } from '@backstage/plugin-cost-insights'; export { plugin as GitHubInsights } from '@roadiehq/backstage-plugin-github-insights'; export { plugin as UserSettings } from '@backstage/plugin-user-settings'; export { plugin as BuildKite } from '@roadiehq/backstage-plugin-buildkite'; +export { plugin as Search } from '@backstage/plugin-search'; diff --git a/plugins/search/.eslintrc.js b/plugins/search/.eslintrc.js new file mode 100644 index 0000000000..13573efa9c --- /dev/null +++ b/plugins/search/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint')], +}; diff --git a/plugins/search/README.md b/plugins/search/README.md new file mode 100644 index 0000000000..fb4b047811 --- /dev/null +++ b/plugins/search/README.md @@ -0,0 +1,9 @@ +# Backstage Search + +**This plugin is still under development.** + +You can follow the progress under the Global search in Backstage [milestone](https://github.com/backstage/backstage/milestone/21) or reach out to us in the #search Discord channel. + +## Getting started + +Run `yarn start` in the root directory, and then navigate to [/search](http://localhost:3000/search)to check out the plugin. diff --git a/plugins/search/dev/index.tsx b/plugins/search/dev/index.tsx new file mode 100644 index 0000000000..264d6f801f --- /dev/null +++ b/plugins/search/dev/index.tsx @@ -0,0 +1,19 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { createDevApp } from '@backstage/dev-utils'; +import { plugin } from '../src/plugin'; + +createDevApp().registerPlugin(plugin).render(); diff --git a/plugins/search/package.json b/plugins/search/package.json new file mode 100644 index 0000000000..6f356d1bf4 --- /dev/null +++ b/plugins/search/package.json @@ -0,0 +1,49 @@ +{ + "name": "@backstage/plugin-search", + "version": "0.2.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "scripts": { + "build": "backstage-cli plugin:build", + "start": "backstage-cli plugin:serve", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "diff": "backstage-cli plugin:diff", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "dependencies": { + "@backstage/core": "^0.3.0", + "@backstage/theme": "^0.2.1", + "@material-ui/core": "^4.11.0", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "4.0.0-alpha.45", + "@backstage/plugin-catalog": "^0.2.0", + "react-router-dom": "6.0.0-beta.0", + "react": "^16.13.1", + "react-dom": "^16.13.1", + "react-use": "^15.3.3" + }, + "devDependencies": { + "@backstage/cli": "^0.2.0", + "@backstage/dev-utils": "^0.1.3", + "@backstage/test-utils": "^0.1.2", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^10.4.1", + "@testing-library/user-event": "^12.0.7", + "@types/jest": "^26.0.7", + "@types/node": "^12.0.0", + "msw": "^0.21.2", + "cross-fetch": "^3.0.6" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/search/src/apis.ts b/plugins/search/src/apis.ts new file mode 100644 index 0000000000..1783b91d2a --- /dev/null +++ b/plugins/search/src/apis.ts @@ -0,0 +1,52 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CatalogApi } from '@backstage/plugin-catalog'; + +export type Result = { + name: string; + description: string; + owner: string; + kind: string; + lifecycle: string; +}; + +export type SearchResults = Array; + +class SearchApi { + private catalogApi: CatalogApi; + + constructor(catalogApi: CatalogApi) { + this.catalogApi = catalogApi; + } + + private async entities() { + const entities = await this.catalogApi.getEntities(); + return entities.map((result: any) => ({ + name: result.metadata.name, + description: result.metadata.description, + owner: result.spec.owner, + kind: result.kind, + lifecycle: result.spec.lifecycle, + })); + } + + public getSearchResult(): Promise { + return this.entities(); + } +} + +export default SearchApi; diff --git a/plugins/search/src/components/Filters/Filters.tsx b/plugins/search/src/components/Filters/Filters.tsx new file mode 100644 index 0000000000..19d4e53d72 --- /dev/null +++ b/plugins/search/src/components/Filters/Filters.tsx @@ -0,0 +1,132 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { + makeStyles, + Typography, + Divider, + Card, + CardHeader, + Button, + CardContent, + Select, + Checkbox, + List, + ListItem, + ListItemText, + MenuItem, +} from '@material-ui/core'; + +const useStyles = makeStyles(theme => ({ + filters: { + background: 'transparent', + boxShadow: '0px 0px 0px 0px', + }, + checkbox: { + padding: theme.spacing(0, 1, 0, 1), + }, + dropdown: { + width: '100%', + }, +})); + +export type FiltersState = { + selected: string; + checked: Array; +}; + +type FiltersProps = { + filters: FiltersState; + resetFilters: () => void; + updateSelected: (filter: string) => void; + updateChecked: (filter: string) => void; +}; + +export const Filters = ({ + filters, + resetFilters, + updateSelected, + updateChecked, +}: FiltersProps) => { + const classes = useStyles(); + + // TODO: move mocked filters out of filters component to make it more generic + const filter1 = ['All', 'API', 'Component', 'Location', 'Template']; + const filter2 = ['deprecated', 'recommended', 'experimental', 'production']; + + return ( + + Filters} + action={ + + } + /> + + + Kind + + + + Lifecycle + + {filter2.map(filter => ( + updateChecked(filter)} + > + + + + ))} + + + + ); +}; diff --git a/plugins/search/src/components/Filters/FiltersButton.tsx b/plugins/search/src/components/Filters/FiltersButton.tsx new file mode 100644 index 0000000000..4775e9d8b8 --- /dev/null +++ b/plugins/search/src/components/Filters/FiltersButton.tsx @@ -0,0 +1,56 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import FilterListIcon from '@material-ui/icons/FilterList'; +import { makeStyles, IconButton, Typography } from '@material-ui/core'; + +const useStyles = makeStyles(theme => ({ + filters: { + width: '250px', + display: 'flex', + }, + icon: { + margin: theme.spacing(-1, 0, 0, 0), + }, +})); + +type FiltersButtonProps = { + numberOfSelectedFilters: number; + handleToggleFilters: () => void; +}; + +export const FiltersButton = ({ + numberOfSelectedFilters, + handleToggleFilters, +}: FiltersButtonProps) => { + const classes = useStyles(); + + return ( +
+ + + + + Filters ({numberOfSelectedFilters ? numberOfSelectedFilters : 0}) + +
+ ); +}; diff --git a/plugins/search/src/components/Filters/index.tsx b/plugins/search/src/components/Filters/index.tsx new file mode 100644 index 0000000000..ea431a3c01 --- /dev/null +++ b/plugins/search/src/components/Filters/index.tsx @@ -0,0 +1,19 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { FiltersButton } from './FiltersButton'; +export { Filters } from './Filters'; +export type { FiltersState } from './Filters'; diff --git a/plugins/search/src/components/SearchBar/SearchBar.tsx b/plugins/search/src/components/SearchBar/SearchBar.tsx new file mode 100644 index 0000000000..9aa48e7284 --- /dev/null +++ b/plugins/search/src/components/SearchBar/SearchBar.tsx @@ -0,0 +1,69 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import { Paper } from '@material-ui/core'; +import InputBase from '@material-ui/core/InputBase'; +import IconButton from '@material-ui/core/IconButton'; +import SearchIcon from '@material-ui/icons/Search'; +import ClearButton from '@material-ui/icons/Clear'; + +const useStyles = makeStyles(() => ({ + root: { + display: 'flex', + alignItems: 'center', + }, + input: { + flex: 1, + }, +})); + +type SearchBarProps = { + searchQuery: string; + handleSearch: any; + handleClearSearchBar: any; +}; + +export const SearchBar = ({ + searchQuery, + handleSearch, + handleClearSearchBar, +}: SearchBarProps) => { + const classes = useStyles(); + + return ( + handleSearch(e)} + className={classes.root} + > + + + + handleSearch(e)} + inputProps={{ 'aria-label': 'search backstage' }} + /> + handleClearSearchBar()}> + + + + ); +}; diff --git a/plugins/search/src/components/SearchBar/index.tsx b/plugins/search/src/components/SearchBar/index.tsx new file mode 100644 index 0000000000..e2b8af1946 --- /dev/null +++ b/plugins/search/src/components/SearchBar/index.tsx @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { SearchBar } from './SearchBar'; diff --git a/plugins/search/src/components/SearchPage/SearchPage.tsx b/plugins/search/src/components/SearchPage/SearchPage.tsx new file mode 100644 index 0000000000..b5779a3d6e --- /dev/null +++ b/plugins/search/src/components/SearchPage/SearchPage.tsx @@ -0,0 +1,55 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { useState } from 'react'; + +import { Header, Content, Page } from '@backstage/core'; +import { Grid } from '@material-ui/core'; + +import { SearchBar } from '../SearchBar'; +import { SearchResult } from '../SearchResult'; + +export const SearchPage = () => { + const [searchQuery, setSearchQuery] = useState(''); + + const handleSearch = (event: React.ChangeEvent) => { + event.preventDefault(); + setSearchQuery(event.target.value); + }; + + const handleClearSearchBar = () => { + setSearchQuery(''); + }; + + return ( + +
+ + + + + + + + + + + + ); +}; diff --git a/plugins/search/src/components/SearchPage/index.tsx b/plugins/search/src/components/SearchPage/index.tsx new file mode 100644 index 0000000000..acdc0967ab --- /dev/null +++ b/plugins/search/src/components/SearchPage/index.tsx @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { SearchPage } from './SearchPage'; diff --git a/plugins/search/src/components/SearchResult/SearchResult.tsx b/plugins/search/src/components/SearchResult/SearchResult.tsx new file mode 100644 index 0000000000..a4985c20fb --- /dev/null +++ b/plugins/search/src/components/SearchResult/SearchResult.tsx @@ -0,0 +1,227 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { useState, useEffect } from 'react'; +import { useAsync } from 'react-use'; + +import { makeStyles, Typography, Grid, Divider } from '@material-ui/core'; +import { Table, TableColumn, useApi } from '@backstage/core'; +import { catalogApiRef } from '@backstage/plugin-catalog'; + +import { FiltersButton, Filters, FiltersState } from '../Filters'; +import SearchApi, { Result, SearchResults } from '../../apis'; + +const useStyles = makeStyles(theme => ({ + searchTerm: { + background: '#eee', + borderRadius: '10%', + }, + tableHeader: { + margin: theme.spacing(1, 0, 0, 0), + display: 'flex', + }, + divider: { + width: '1px', + margin: theme.spacing(0, 2), + padding: theme.spacing(2, 0), + }, +})); + +type SearchResultProps = { + searchQuery?: string; +}; + +type TableHeaderProps = { + searchQuery?: string; + numberOfSelectedFilters: number; + numberOfResults: number; + handleToggleFilters: () => void; +}; + +type Filters = { + selected: string; + checked: Array; +}; + +// TODO: move out column to make the search result component more generic +const columns: TableColumn[] = [ + { + title: 'Component Id', + field: 'name', + highlight: true, + }, + { + title: 'Description', + field: 'description', + }, + { + title: 'Owner', + field: 'owner', + }, + { + title: 'Kind', + field: 'kind', + }, + { + title: 'LifeCycle', + field: 'lifecycle', + }, +]; + +const TableHeader = ({ + searchQuery, + numberOfSelectedFilters, + numberOfResults, + handleToggleFilters, +}: TableHeaderProps) => { + const classes = useStyles(); + + return ( +
+ + + + {searchQuery ? ( + + {`${numberOfResults} `} + {numberOfResults > 1 ? `results for ` : `result for `} + "{searchQuery}"{' '} + + ) : ( + {`${numberOfResults} results`} + )} + +
+ ); +}; + +export const SearchResult = ({ searchQuery }: SearchResultProps) => { + const catalogApi = useApi(catalogApiRef); + + const [showFilters, toggleFilters] = useState(false); + const [filters, setFilters] = useState({ + selected: 'All', + checked: [], + }); + + const [filteredResults, setFilteredResults] = useState([]); + + const searchApi = new SearchApi(catalogApi); + + const { loading, error, value: results } = useAsync(() => { + return searchApi.getSearchResult(); + }, []); + + useEffect(() => { + if (results) { + let withFilters = results; + + // apply filters + + // filter on selected + if (filters.selected !== 'All') { + withFilters = results.filter((result: Result) => + filters.selected.includes(result.kind), + ); + } + + // filter on checked + if (filters.checked.length > 0) { + withFilters = withFilters.filter((result: Result) => + filters.checked.includes(result.lifecycle), + ); + } + + // filter on searchQuery + if (searchQuery) { + withFilters = withFilters.filter( + (result: Result) => + result.name?.toLowerCase().includes(searchQuery) || + result.description?.toLowerCase().includes(searchQuery), + ); + } + + setFilteredResults(withFilters); + } + }, [filters, searchQuery, results]); + + if (loading || error || !results) return null; + + const resetFilters = () => { + setFilters({ + selected: 'All', + checked: [], + }); + }; + + const updateSelected = (filter: string) => { + setFilters(prevState => ({ + ...prevState, + selected: filter, + })); + }; + + const updateChecked = (filter: string) => { + if (filters.checked.includes(filter)) { + setFilters(prevState => ({ + ...prevState, + checked: prevState.checked.filter(item => item !== filter), + })); + return; + } + + setFilters(prevState => ({ + ...prevState, + checked: [...prevState.checked, filter], + })); + }; + + return ( + <> + + {showFilters && ( + + + + )} + +
toggleFilters(!showFilters)} + /> + } + /> + + + + ); +}; diff --git a/plugins/search/src/components/SearchResult/index.tsx b/plugins/search/src/components/SearchResult/index.tsx new file mode 100644 index 0000000000..cf10135fd0 --- /dev/null +++ b/plugins/search/src/components/SearchResult/index.tsx @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { SearchResult } from './SearchResult'; diff --git a/plugins/search/src/components/index.tsx b/plugins/search/src/components/index.tsx new file mode 100644 index 0000000000..f8e6a5a09e --- /dev/null +++ b/plugins/search/src/components/index.tsx @@ -0,0 +1,20 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './Filters'; +export * from './SearchBar'; +export * from './SearchPage'; +export * from './SearchResult'; diff --git a/plugins/search/src/index.ts b/plugins/search/src/index.ts new file mode 100644 index 0000000000..224e293890 --- /dev/null +++ b/plugins/search/src/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { plugin } from './plugin'; diff --git a/plugins/search/src/plugin.test.ts b/plugins/search/src/plugin.test.ts new file mode 100644 index 0000000000..92b8d5bcbf --- /dev/null +++ b/plugins/search/src/plugin.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { plugin } from './plugin'; + +describe('search', () => { + it('should export plugin', () => { + expect(plugin).toBeDefined(); + }); +}); diff --git a/plugins/search/src/plugin.ts b/plugins/search/src/plugin.ts new file mode 100644 index 0000000000..44c7bcb042 --- /dev/null +++ b/plugins/search/src/plugin.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { createPlugin, createRouteRef } from '@backstage/core'; +import { SearchPage } from './components/SearchPage'; + +export const rootRouteRef = createRouteRef({ + path: '/search', + title: 'search', +}); + +export const plugin = createPlugin({ + id: 'search', + register({ router }) { + router.addRoute(rootRouteRef, SearchPage); + }, +}); diff --git a/plugins/search/src/setupTests.ts b/plugins/search/src/setupTests.ts new file mode 100644 index 0000000000..43b8421558 --- /dev/null +++ b/plugins/search/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import '@testing-library/jest-dom'; +import 'cross-fetch/polyfill'; From ddb5a38f7782089da0f0875bbe0339daa6adfb4a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 7 Nov 2020 17:51:16 +0100 Subject: [PATCH 194/252] backend-common,core,auth-backend: added initial config schemas --- packages/backend-common/config-schema.json | 80 ++++++++++++++++++++++ packages/backend-common/package.json | 3 +- packages/core/package.json | 51 +++++++++++++- plugins/auth-backend/package.json | 35 +++++++++- 4 files changed, 166 insertions(+), 3 deletions(-) create mode 100644 packages/backend-common/config-schema.json diff --git a/packages/backend-common/config-schema.json b/packages/backend-common/config-schema.json new file mode 100644 index 0000000000..2012447a11 --- /dev/null +++ b/packages/backend-common/config-schema.json @@ -0,0 +1,80 @@ +{ + "$schema": "https://backstage.io/schema/config-v1", + "title": "@backstage/backend-common", + "type": "object", + "properties": { + "integrations": { + "type": "object", + "additionalProperties": false, + "properties": { + "github": { + "type": "array", + "items": { + "type": "object", + "required": ["host"], + "properties": { + "host": { + "type": "string" + }, + "token": { + "type": "string", + "visibility": "secret" + } + } + } + }, + "gitlab": { + "type": "array", + "items": { + "type": "object", + "required": ["host"], + "properties": { + "host": { + "type": "string" + }, + "token": { + "type": "string", + "visibility": "secret" + } + } + } + }, + "bitbucket": { + "type": "array", + "items": { + "type": "object", + "required": ["host"], + "properties": { + "host": { + "type": "string" + }, + "username": { + "type": "string" + }, + "appPassword": { + "type": "string", + "visibility": "secret" + } + } + } + }, + "azure": { + "type": "array", + "items": { + "type": "object", + "required": ["host"], + "properties": { + "host": { + "type": "string" + }, + "token": { + "type": "string", + "visibility": "secret" + } + } + } + } + } + } + } +} diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index ea84d1a383..2f58bd0536 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -90,5 +90,6 @@ }, "files": [ "dist" - ] + ], + "configSchema": "config-schema.json" } diff --git a/packages/core/package.json b/packages/core/package.json index 848f0847bd..3d4b53d35b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -80,5 +80,54 @@ }, "files": [ "dist" - ] + ], + "configSchema": { + "$schema": "https://backstage.io/schema/config-v1", + "title": "@backstage/core", + "type": "object", + "properties": { + "app": { + "type": "object", + "properties": { + "title": { + "type": "string", + "visibility": "frontend" + }, + "baseUrl": { + "type": "string", + "visibility": "frontend" + } + }, + "required": [ + "title", + "baseUrl" + ] + }, + "backend": { + "type": "object", + "properties": { + "baseUrl": { + "type": "string", + "visibility": "frontend" + } + }, + "required": [ + "baseUrl" + ] + }, + "organization": { + "type": "object", + "properties": { + "name": { + "type": "string", + "visibility": "frontend" + } + } + } + }, + "required": [ + "app", + "backend" + ] + } } diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index af3d056b28..2bc00f56a2 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -66,5 +66,38 @@ "files": [ "dist", "migrations" - ] + ], + "configSchema": { + "$schema": "https://backstage.io/schema/config-v1", + "title": "@backstage/plugin-auth-backend", + "type": "object", + "properties": { + "auth": { + "type": "object", + "properties": { + "providers": { + "type": "object", + "properties": { + "google": { + "type": "object", + "visibility": "frontend", + "additionalProperties": { + "type": "object", + "properties": { + "clientId": { + "type": "string" + }, + "clientSecret": { + "type": "string", + "visibility": "secret" + } + } + } + } + } + } + } + } + } + } } From 6fec95079a18d2da1c8036d16809dac0d67388dc Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 7 Nov 2020 18:08:19 +0100 Subject: [PATCH 195/252] config-loader: initial incomplete config schema implementation --- packages/config-loader/package.json | 6 + packages/config-loader/src/lib/index.ts | 1 + packages/config-loader/src/lib/schema.ts | 216 +++++++++++++++++++++++ packages/config-loader/src/loader.ts | 10 +- yarn.lock | 32 +++- 5 files changed, 261 insertions(+), 4 deletions(-) create mode 100644 packages/config-loader/src/lib/schema.ts diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 2d3474f3af..39735ff2fd 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -30,8 +30,14 @@ "clean": "backstage-cli clean" }, "dependencies": { + "@backstage/cli-common": "^0.1.1", "@backstage/config": "^0.1.1", + "@types/json-schema": "^7.0.6", + "@types/json-schema-merge-allof": "^0.6.0", + "ajv": "^7.0.0-beta.2", "fs-extra": "^9.0.0", + "json-schema": "^0.2.5", + "json-schema-merge-allof": "^0.7.0", "yaml": "^1.9.2", "yup": "^0.29.3" }, diff --git a/packages/config-loader/src/lib/index.ts b/packages/config-loader/src/lib/index.ts index 1b5ad2ef50..29812053bc 100644 --- a/packages/config-loader/src/lib/index.ts +++ b/packages/config-loader/src/lib/index.ts @@ -17,3 +17,4 @@ export { readConfigFile } from './reader'; export { readEnvConfig } from './env'; export { readSecret } from './secrets'; +export { loadSchema } from './schema'; diff --git a/packages/config-loader/src/lib/schema.ts b/packages/config-loader/src/lib/schema.ts new file mode 100644 index 0000000000..c3441bce38 --- /dev/null +++ b/packages/config-loader/src/lib/schema.ts @@ -0,0 +1,216 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs-extra'; +import { resolve as resolvePath, dirname } from 'path'; +import Ajv from 'ajv'; +import { JSONSchema7 as JSONSchema } from 'json-schema'; +import mergeAllOf, { Resolvers } from 'json-schema-merge-allof'; +import { AppConfig, ConfigReader } from '@backstage/config'; + +const CONFIG_VISIBILITIES = ['frontend', 'backend', 'secret'] as const; + +type ConfigVisibility = typeof CONFIG_VISIBILITIES[number]; + +type ConfigSchema = { + load(appConfigs: AppConfig[]): AppConfig[]; +}; + +type ConfigSchemaPackageEntry = { + value: JSONSchema; + path: string; +}; + +type Options = { + dependencies: string[]; +}; + +type ValidationError = string; + +type ValidationResult = { + errors?: ValidationError[]; + visibilities: Map; +}; + +type ValidationFunc = (configs: AppConfig[]) => ValidationResult; + +export async function loadSchema(options: Options): Promise { + const start = process.hrtime(); + + const schemas = await collectConfigSchemas(options.dependencies[0]); + + const [durS, durNs] = process.hrtime(start); + const dur = (durS + durNs / 10 ** 9).toFixed(3); + console.log(`DEBUG: collected config schemas in ${dur}s`); + console.log('DEBUG: schemas =', schemas); + + const validate = compileConfigSchemas(schemas); + + return { + load(configs: AppConfig[]): AppConfig[] { + const result = validate(configs); + console.log('DEBUG: result =', result); + if (result.errors) { + throw new Error( + `Config validation failed, ${result.errors.join('; ')}`, + ); + } + + return configs; + }, + }; +} + +function compileConfigSchemas( + schemas: ConfigSchemaPackageEntry[], +): ValidationFunc { + const visibilities = new Map(); + + const ajv = new Ajv({ + strict: true, + allErrors: true, + defaultMeta: 'http://json-schema.org/draft-07/schema#', + schemas: { + 'https://backstage.io/schema/config-v1': true, + }, + keywords: [ + { + keyword: 'visibility', + schemaType: 'string', + metaSchema: { + type: 'string', + enum: CONFIG_VISIBILITIES, + }, + compile(visibility: ConfigVisibility) { + return (_data, ctx) => { + if (!ctx) { + return false; + } + if (visibility === 'frontend') { + visibilities.set(ctx.dataPath, visibility); + } + return true; + }; + }, + }, + ], + }); + + const merged = mergeAllOf( + { allOf: schemas.map(_ => _.value) }, + { + ignoreAdditionalProperties: true, + resolvers: { + visibility(values: string[], path: string[]) { + const hasApp = values.some(_ => _ === 'frontend'); + const hasSecret = values.some(_ => _ === 'secret'); + if (hasApp && hasSecret) { + throw new Error( + `Config schema visibility is both 'frontend' and 'secret' for ${path.join( + '/', + )}`, + ); + } else if (hasApp) { + return 'frontend'; + } else if (hasSecret) { + return 'secret'; + } + + return 'backend'; + }, + } as Partial>, + }, + ); + + const validate = ajv.compile(merged); + + return configs => { + const config = ConfigReader.fromConfigs(configs).get(); + + visibilities.clear(); + const valid = validate(config); + if (!valid) { + const errors = ajv.errorsText(validate.errors); + return { + errors: [errors], + visibilities: new Map(), + }; + } + + return { + visibilities: new Map(visibilities), + }; + }; +} + +const req = + typeof __non_webpack_require__ === 'undefined' + ? require + : __non_webpack_require__; + +async function collectConfigSchemas( + name: string, + opts: unknown = {}, + visited: Set = new Set(), + schemas: ConfigSchemaPackageEntry[] = [], +): Promise { + const pkgPath = req.resolve(`${name}/package.json`, opts); + if (visited.has(pkgPath)) { + return schemas; + } + visited.add(pkgPath); + const pkg = await fs.readJson(pkgPath); + const depNames = [ + ...Object.keys(pkg.dependencies ?? {}), + ...Object.keys(pkg.peerDependencies ?? {}), + ]; + + // TODO(Rugvip): Trying this out to avoid having to traverse the full dependency graph, + // since that's pretty slow. We probably need a better way to determine when + // we've left the Backstage ecosystem, but this will do for now. + const hasSchema = 'configSchema' in pkg; + const hasBackstageDep = depNames.some(_ => _.startsWith('@backstage/')); + if (!hasSchema && !hasBackstageDep) { + return schemas; + } + if (hasSchema) { + if (typeof pkg.configSchema === 'string') { + if (!pkg.configSchema.endsWith('.json')) { + throw new Error( + `Config schema files must be .json, got ${pkg.configSchema}`, + ); + } + const value = await fs.readJson( + resolvePath(dirname(pkgPath), pkg.configSchema), + ); + schemas.push({ + value, + path: pkgPath, + }); + } else { + schemas.push({ + value: pkg.configSchema, + path: pkgPath, + }); + } + } + + for (const depName of depNames) { + await collectConfigSchemas(depName, { paths: [pkgPath] }, visited, schemas); + } + + return schemas; +} diff --git a/packages/config-loader/src/loader.ts b/packages/config-loader/src/loader.ts index e7eb2a38fa..318eaa7915 100644 --- a/packages/config-loader/src/loader.ts +++ b/packages/config-loader/src/loader.ts @@ -17,7 +17,7 @@ import fs from 'fs-extra'; import { resolve as resolvePath, dirname, isAbsolute } from 'path'; import { AppConfig, JsonObject } from '@backstage/config'; -import { readConfigFile, readEnvConfig, readSecret } from './lib'; +import { readConfigFile, readEnvConfig, readSecret, loadSchema } from './lib'; export type LoadConfigOptions = { // The root directory of the config loading context. Used to find default configs. @@ -71,13 +71,17 @@ class Context { } } +type LoadedConfig = AppConfig[]; + export async function loadConfig( options: LoadConfigOptions, -): Promise { +): Promise { const configs = []; const { configRoot } = options; const configPaths = options.configPaths.slice(); + const schema = await loadSchema({ dependencies: ['example-backend'] }); + // If no paths are provided, we default to reading // `app-config.yaml` and, if it exists, `app-config.local.yaml` if (configPaths.length === 0) { @@ -126,5 +130,5 @@ export async function loadConfig( configs.push(...readEnvConfig(process.env)); - return configs; + return schema.load(configs); } diff --git a/yarn.lock b/yarn.lock index 10e70f52fb..98e1b2476f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5369,7 +5369,14 @@ resolved "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.5.tgz#136d5e6a57a931e1cce6f9d8126aa98a9c92a6bb" integrity sha512-JCcp6J0GV66Y4ZMDAQCXot4xprYB+Zfd3meK9+INSJeVZwJmHAW30BBEEkPzXswMXuiyReUGOP3GxrADc9wPww== -"@types/json-schema@*", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5": +"@types/json-schema-merge-allof@^0.6.0": + version "0.6.0" + resolved "https://registry.npmjs.org/@types/json-schema-merge-allof/-/json-schema-merge-allof-0.6.0.tgz#0f587d8a3bcb41a55ef2e91d3ba96430c9bc1813" + integrity sha512-v6iCEk4Sxy1twlCTtrRxMqSHX0vuLJ7Ql4hiIUZRMOswxtlUeybIfYaZIj7pX747RBczG2YtBm4Fn6sqKzeY2Q== + dependencies: + "@types/json-schema" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6": version "7.0.6" resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== @@ -6562,6 +6569,15 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.1, ajv@^6.10.2, ajv@^6.12.4, ajv@^6.12.5, ajv json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^7.0.0-beta.2: + version "7.0.0-beta.3" + resolved "https://registry.npmjs.org/ajv/-/ajv-7.0.0-beta.3.tgz#d34861ccfbdebb55bf9f49a08b29f76bf656fc5c" + integrity sha512-gUGVvM4NmyqrFvCNAQnP4P7FC0RjxMQyRnrXpozNglBkDJnTysVbvycyOZUy5n6yLKSqVDUqWZBXj7dXINrSqw== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^0.5.0" + uri-js "^4.2.2" + alphanum-sort@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -15022,11 +15038,25 @@ json-schema-merge-allof@^0.6.0: json-schema-compare "^0.2.2" lodash "^4.17.4" +json-schema-merge-allof@^0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/json-schema-merge-allof/-/json-schema-merge-allof-0.7.0.tgz#84d3e8c3e03d3060014286958eb8834fa9d76304" + integrity sha512-kvsuSVnl1n5xnNEu5ed4o8r8ujSA4/IgRtHmpgfMfa7FOMIRAzN4F9qbuklouTn5J8bi83y6MQ11n+ERMMTXZg== + dependencies: + compute-lcm "^1.1.0" + json-schema-compare "^0.2.2" + lodash "^4.17.4" + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.5.0.tgz#1069a6097f9e9a567bfc6cc215e85f606ebf6480" + integrity sha512-x+TRJIQFskrNnFKE2Viz9FCSjK1vIh+H/uaBiOYszh/IcZmAFneQ35H4osWDJp1NPXccuV2I0RMXmi2ZS6Kqcg== + json-schema@0.2.3: version "0.2.3" resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" From 47952780392b522827707c6ffbcf5a58771c89c2 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 8 Nov 2020 14:05:56 +0100 Subject: [PATCH 196/252] config-loader: add config filtering by visibility --- packages/config-loader/src/lib/schema.ts | 97 +++++++++++++++++++++--- packages/config-loader/src/loader.ts | 2 +- 2 files changed, 86 insertions(+), 13 deletions(-) diff --git a/packages/config-loader/src/lib/schema.ts b/packages/config-loader/src/lib/schema.ts index c3441bce38..0d820ab173 100644 --- a/packages/config-loader/src/lib/schema.ts +++ b/packages/config-loader/src/lib/schema.ts @@ -19,14 +19,24 @@ import { resolve as resolvePath, dirname } from 'path'; import Ajv from 'ajv'; import { JSONSchema7 as JSONSchema } from 'json-schema'; import mergeAllOf, { Resolvers } from 'json-schema-merge-allof'; -import { AppConfig, ConfigReader } from '@backstage/config'; +import { + AppConfig, + ConfigReader, + JsonObject, + JsonValue, +} from '@backstage/config'; const CONFIG_VISIBILITIES = ['frontend', 'backend', 'secret'] as const; type ConfigVisibility = typeof CONFIG_VISIBILITIES[number]; +const DEFAULT_CONFIG_VISIBILITY: ConfigVisibility = 'backend'; + type ConfigSchema = { - load(appConfigs: AppConfig[]): AppConfig[]; + process( + appConfigs: AppConfig[], + options?: ConfigProcessingOptions, + ): AppConfig[]; }; type ConfigSchemaPackageEntry = { @@ -42,11 +52,61 @@ type ValidationError = string; type ValidationResult = { errors?: ValidationError[]; - visibilities: Map; + visibilityByPath: Map; }; type ValidationFunc = (configs: AppConfig[]) => ValidationResult; +type ConfigProcessingOptions = { + visibilities?: ConfigVisibility[]; +}; + +export function filterByVisibility( + data: JsonObject, + includeVisibilities: ConfigVisibility[], + visibilityByPath: Map, +): JsonObject { + function transform(jsonVal: JsonValue, path: string): JsonValue | undefined { + if (typeof jsonVal !== 'object') { + const visibility = + visibilityByPath.get(path) ?? DEFAULT_CONFIG_VISIBILITY; + if (includeVisibilities.includes(visibility)) { + return jsonVal; + } + return undefined; + } else if (jsonVal === null) { + return undefined; + } else if (Array.isArray(jsonVal)) { + const arr = new Array(); + + for (const [index, value] of jsonVal.entries()) { + const out = transform(value, `${path}/${index}`); + if (out !== undefined) { + arr.push(out); + } + } + + return arr.length === 0 ? undefined : arr; + } + + const outObj: JsonObject = {}; + + for (const [key, value] of Object.entries(jsonVal)) { + if (value === undefined) { + continue; + } + const out = transform(value, `${path}/${key}`); + if (out !== undefined) { + outObj[key] = out; + } + } + + return Object.keys(outObj).length === 0 ? undefined : outObj; + } + + return (transform(data, '') as JsonObject) ?? {}; +} + export async function loadSchema(options: Options): Promise { const start = process.hrtime(); @@ -60,16 +120,28 @@ export async function loadSchema(options: Options): Promise { const validate = compileConfigSchemas(schemas); return { - load(configs: AppConfig[]): AppConfig[] { + process( + configs: AppConfig[], + { visibilities }: ConfigProcessingOptions = {}, + ): AppConfig[] { const result = validate(configs); - console.log('DEBUG: result =', result); if (result.errors) { throw new Error( `Config validation failed, ${result.errors.join('; ')}`, ); } - return configs; + let processedConfigs = configs; + + if (visibilities) { + processedConfigs = processedConfigs.map(({ data, context }) => ({ + context, + data: filterByVisibility(data, visibilities, result.visibilityByPath), + })); + } + console.log('DEBUG: result.visibilityByPath =', result.visibilityByPath); + + return processedConfigs; }, }; } @@ -77,7 +149,7 @@ export async function loadSchema(options: Options): Promise { function compileConfigSchemas( schemas: ConfigSchemaPackageEntry[], ): ValidationFunc { - const visibilities = new Map(); + const visibilityByPath = new Map(); const ajv = new Ajv({ strict: true, @@ -99,8 +171,8 @@ function compileConfigSchemas( if (!ctx) { return false; } - if (visibility === 'frontend') { - visibilities.set(ctx.dataPath, visibility); + if (visibility) { + visibilityByPath.set(ctx.dataPath, visibility); } return true; }; @@ -140,18 +212,19 @@ function compileConfigSchemas( return configs => { const config = ConfigReader.fromConfigs(configs).get(); - visibilities.clear(); + visibilityByPath.clear(); + const valid = validate(config); if (!valid) { const errors = ajv.errorsText(validate.errors); return { errors: [errors], - visibilities: new Map(), + visibilityByPath: new Map(), }; } return { - visibilities: new Map(visibilities), + visibilityByPath: new Map(visibilityByPath), }; }; } diff --git a/packages/config-loader/src/loader.ts b/packages/config-loader/src/loader.ts index 318eaa7915..2d3c2afd9c 100644 --- a/packages/config-loader/src/loader.ts +++ b/packages/config-loader/src/loader.ts @@ -130,5 +130,5 @@ export async function loadConfig( configs.push(...readEnvConfig(process.env)); - return schema.load(configs); + return schema.process(configs, { visibilities: ['frontend', 'secret'] }); } From 8c65cec932d2818914b0ef124059e832016952a9 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 8 Nov 2020 14:18:58 +0100 Subject: [PATCH 197/252] config-loader: split up config schema implementation and move into separate lib --- packages/config-loader/src/index.ts | 2 +- packages/config-loader/src/lib/index.ts | 2 +- packages/config-loader/src/lib/schema.ts | 289 ------------------ .../config-loader/src/lib/schema/collect.ts | 78 +++++ .../config-loader/src/lib/schema/compile.ts | 109 +++++++ .../config-loader/src/lib/schema/filtering.ts | 64 ++++ .../config-loader/src/lib/schema/index.ts | 17 ++ packages/config-loader/src/lib/schema/load.ts | 63 ++++ .../config-loader/src/lib/schema/types.ts | 49 +++ packages/config-loader/src/loader.ts | 6 +- 10 files changed, 384 insertions(+), 295 deletions(-) delete mode 100644 packages/config-loader/src/lib/schema.ts create mode 100644 packages/config-loader/src/lib/schema/collect.ts create mode 100644 packages/config-loader/src/lib/schema/compile.ts create mode 100644 packages/config-loader/src/lib/schema/filtering.ts create mode 100644 packages/config-loader/src/lib/schema/index.ts create mode 100644 packages/config-loader/src/lib/schema/load.ts create mode 100644 packages/config-loader/src/lib/schema/types.ts diff --git a/packages/config-loader/src/index.ts b/packages/config-loader/src/index.ts index 02db134fe5..415b6d4022 100644 --- a/packages/config-loader/src/index.ts +++ b/packages/config-loader/src/index.ts @@ -14,6 +14,6 @@ * limitations under the License. */ -export { readEnvConfig } from './lib'; +export { readEnvConfig, loadConfigSchema } from './lib'; export { loadConfig } from './loader'; export type { LoadConfigOptions } from './loader'; diff --git a/packages/config-loader/src/lib/index.ts b/packages/config-loader/src/lib/index.ts index 29812053bc..4da0c97788 100644 --- a/packages/config-loader/src/lib/index.ts +++ b/packages/config-loader/src/lib/index.ts @@ -17,4 +17,4 @@ export { readConfigFile } from './reader'; export { readEnvConfig } from './env'; export { readSecret } from './secrets'; -export { loadSchema } from './schema'; +export { loadConfigSchema } from './schema'; diff --git a/packages/config-loader/src/lib/schema.ts b/packages/config-loader/src/lib/schema.ts deleted file mode 100644 index 0d820ab173..0000000000 --- a/packages/config-loader/src/lib/schema.ts +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs-extra'; -import { resolve as resolvePath, dirname } from 'path'; -import Ajv from 'ajv'; -import { JSONSchema7 as JSONSchema } from 'json-schema'; -import mergeAllOf, { Resolvers } from 'json-schema-merge-allof'; -import { - AppConfig, - ConfigReader, - JsonObject, - JsonValue, -} from '@backstage/config'; - -const CONFIG_VISIBILITIES = ['frontend', 'backend', 'secret'] as const; - -type ConfigVisibility = typeof CONFIG_VISIBILITIES[number]; - -const DEFAULT_CONFIG_VISIBILITY: ConfigVisibility = 'backend'; - -type ConfigSchema = { - process( - appConfigs: AppConfig[], - options?: ConfigProcessingOptions, - ): AppConfig[]; -}; - -type ConfigSchemaPackageEntry = { - value: JSONSchema; - path: string; -}; - -type Options = { - dependencies: string[]; -}; - -type ValidationError = string; - -type ValidationResult = { - errors?: ValidationError[]; - visibilityByPath: Map; -}; - -type ValidationFunc = (configs: AppConfig[]) => ValidationResult; - -type ConfigProcessingOptions = { - visibilities?: ConfigVisibility[]; -}; - -export function filterByVisibility( - data: JsonObject, - includeVisibilities: ConfigVisibility[], - visibilityByPath: Map, -): JsonObject { - function transform(jsonVal: JsonValue, path: string): JsonValue | undefined { - if (typeof jsonVal !== 'object') { - const visibility = - visibilityByPath.get(path) ?? DEFAULT_CONFIG_VISIBILITY; - if (includeVisibilities.includes(visibility)) { - return jsonVal; - } - return undefined; - } else if (jsonVal === null) { - return undefined; - } else if (Array.isArray(jsonVal)) { - const arr = new Array(); - - for (const [index, value] of jsonVal.entries()) { - const out = transform(value, `${path}/${index}`); - if (out !== undefined) { - arr.push(out); - } - } - - return arr.length === 0 ? undefined : arr; - } - - const outObj: JsonObject = {}; - - for (const [key, value] of Object.entries(jsonVal)) { - if (value === undefined) { - continue; - } - const out = transform(value, `${path}/${key}`); - if (out !== undefined) { - outObj[key] = out; - } - } - - return Object.keys(outObj).length === 0 ? undefined : outObj; - } - - return (transform(data, '') as JsonObject) ?? {}; -} - -export async function loadSchema(options: Options): Promise { - const start = process.hrtime(); - - const schemas = await collectConfigSchemas(options.dependencies[0]); - - const [durS, durNs] = process.hrtime(start); - const dur = (durS + durNs / 10 ** 9).toFixed(3); - console.log(`DEBUG: collected config schemas in ${dur}s`); - console.log('DEBUG: schemas =', schemas); - - const validate = compileConfigSchemas(schemas); - - return { - process( - configs: AppConfig[], - { visibilities }: ConfigProcessingOptions = {}, - ): AppConfig[] { - const result = validate(configs); - if (result.errors) { - throw new Error( - `Config validation failed, ${result.errors.join('; ')}`, - ); - } - - let processedConfigs = configs; - - if (visibilities) { - processedConfigs = processedConfigs.map(({ data, context }) => ({ - context, - data: filterByVisibility(data, visibilities, result.visibilityByPath), - })); - } - console.log('DEBUG: result.visibilityByPath =', result.visibilityByPath); - - return processedConfigs; - }, - }; -} - -function compileConfigSchemas( - schemas: ConfigSchemaPackageEntry[], -): ValidationFunc { - const visibilityByPath = new Map(); - - const ajv = new Ajv({ - strict: true, - allErrors: true, - defaultMeta: 'http://json-schema.org/draft-07/schema#', - schemas: { - 'https://backstage.io/schema/config-v1': true, - }, - keywords: [ - { - keyword: 'visibility', - schemaType: 'string', - metaSchema: { - type: 'string', - enum: CONFIG_VISIBILITIES, - }, - compile(visibility: ConfigVisibility) { - return (_data, ctx) => { - if (!ctx) { - return false; - } - if (visibility) { - visibilityByPath.set(ctx.dataPath, visibility); - } - return true; - }; - }, - }, - ], - }); - - const merged = mergeAllOf( - { allOf: schemas.map(_ => _.value) }, - { - ignoreAdditionalProperties: true, - resolvers: { - visibility(values: string[], path: string[]) { - const hasApp = values.some(_ => _ === 'frontend'); - const hasSecret = values.some(_ => _ === 'secret'); - if (hasApp && hasSecret) { - throw new Error( - `Config schema visibility is both 'frontend' and 'secret' for ${path.join( - '/', - )}`, - ); - } else if (hasApp) { - return 'frontend'; - } else if (hasSecret) { - return 'secret'; - } - - return 'backend'; - }, - } as Partial>, - }, - ); - - const validate = ajv.compile(merged); - - return configs => { - const config = ConfigReader.fromConfigs(configs).get(); - - visibilityByPath.clear(); - - const valid = validate(config); - if (!valid) { - const errors = ajv.errorsText(validate.errors); - return { - errors: [errors], - visibilityByPath: new Map(), - }; - } - - return { - visibilityByPath: new Map(visibilityByPath), - }; - }; -} - -const req = - typeof __non_webpack_require__ === 'undefined' - ? require - : __non_webpack_require__; - -async function collectConfigSchemas( - name: string, - opts: unknown = {}, - visited: Set = new Set(), - schemas: ConfigSchemaPackageEntry[] = [], -): Promise { - const pkgPath = req.resolve(`${name}/package.json`, opts); - if (visited.has(pkgPath)) { - return schemas; - } - visited.add(pkgPath); - const pkg = await fs.readJson(pkgPath); - const depNames = [ - ...Object.keys(pkg.dependencies ?? {}), - ...Object.keys(pkg.peerDependencies ?? {}), - ]; - - // TODO(Rugvip): Trying this out to avoid having to traverse the full dependency graph, - // since that's pretty slow. We probably need a better way to determine when - // we've left the Backstage ecosystem, but this will do for now. - const hasSchema = 'configSchema' in pkg; - const hasBackstageDep = depNames.some(_ => _.startsWith('@backstage/')); - if (!hasSchema && !hasBackstageDep) { - return schemas; - } - if (hasSchema) { - if (typeof pkg.configSchema === 'string') { - if (!pkg.configSchema.endsWith('.json')) { - throw new Error( - `Config schema files must be .json, got ${pkg.configSchema}`, - ); - } - const value = await fs.readJson( - resolvePath(dirname(pkgPath), pkg.configSchema), - ); - schemas.push({ - value, - path: pkgPath, - }); - } else { - schemas.push({ - value: pkg.configSchema, - path: pkgPath, - }); - } - } - - for (const depName of depNames) { - await collectConfigSchemas(depName, { paths: [pkgPath] }, visited, schemas); - } - - return schemas; -} diff --git a/packages/config-loader/src/lib/schema/collect.ts b/packages/config-loader/src/lib/schema/collect.ts new file mode 100644 index 0000000000..b6d0bffaef --- /dev/null +++ b/packages/config-loader/src/lib/schema/collect.ts @@ -0,0 +1,78 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs-extra'; +import { resolve as resolvePath, dirname } from 'path'; +import { ConfigSchemaPackageEntry } from './types'; + +const req = + typeof __non_webpack_require__ === 'undefined' + ? require + : __non_webpack_require__; + +export async function collectConfigSchemas( + name: string, + opts: unknown = {}, + visited: Set = new Set(), + schemas: ConfigSchemaPackageEntry[] = [], +): Promise { + const pkgPath = req.resolve(`${name}/package.json`, opts); + if (visited.has(pkgPath)) { + return schemas; + } + visited.add(pkgPath); + const pkg = await fs.readJson(pkgPath); + const depNames = [ + ...Object.keys(pkg.dependencies ?? {}), + ...Object.keys(pkg.peerDependencies ?? {}), + ]; + + // TODO(Rugvip): Trying this out to avoid having to traverse the full dependency graph, + // since that's pretty slow. We probably need a better way to determine when + // we've left the Backstage ecosystem, but this will do for now. + const hasSchema = 'configSchema' in pkg; + const hasBackstageDep = depNames.some(_ => _.startsWith('@backstage/')); + if (!hasSchema && !hasBackstageDep) { + return schemas; + } + if (hasSchema) { + if (typeof pkg.configSchema === 'string') { + if (!pkg.configSchema.endsWith('.json')) { + throw new Error( + `Config schema files must be .json, got ${pkg.configSchema}`, + ); + } + const value = await fs.readJson( + resolvePath(dirname(pkgPath), pkg.configSchema), + ); + schemas.push({ + value, + path: pkgPath, + }); + } else { + schemas.push({ + value: pkg.configSchema, + path: pkgPath, + }); + } + } + + for (const depName of depNames) { + await collectConfigSchemas(depName, { paths: [pkgPath] }, visited, schemas); + } + + return schemas; +} diff --git a/packages/config-loader/src/lib/schema/compile.ts b/packages/config-loader/src/lib/schema/compile.ts new file mode 100644 index 0000000000..9ab8627fce --- /dev/null +++ b/packages/config-loader/src/lib/schema/compile.ts @@ -0,0 +1,109 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Ajv from 'ajv'; +import { JSONSchema7 as JSONSchema } from 'json-schema'; +import mergeAllOf, { Resolvers } from 'json-schema-merge-allof'; +import { ConfigReader } from '@backstage/config'; +import { + ConfigSchemaPackageEntry, + ValidationFunc, + CONFIG_VISIBILITIES, + ConfigVisibility, +} from './types'; + +export function compileConfigSchemas( + schemas: ConfigSchemaPackageEntry[], +): ValidationFunc { + const visibilityByPath = new Map(); + + const ajv = new Ajv({ + strict: true, + allErrors: true, + defaultMeta: 'http://json-schema.org/draft-07/schema#', + schemas: { + 'https://backstage.io/schema/config-v1': true, + }, + keywords: [ + { + keyword: 'visibility', + schemaType: 'string', + metaSchema: { + type: 'string', + enum: CONFIG_VISIBILITIES, + }, + compile(visibility: ConfigVisibility) { + return (_data, ctx) => { + if (!ctx) { + return false; + } + if (visibility) { + visibilityByPath.set(ctx.dataPath, visibility); + } + return true; + }; + }, + }, + ], + }); + + const merged = mergeAllOf( + { allOf: schemas.map(_ => _.value) }, + { + ignoreAdditionalProperties: true, + resolvers: { + visibility(values: string[], path: string[]) { + const hasApp = values.some(_ => _ === 'frontend'); + const hasSecret = values.some(_ => _ === 'secret'); + if (hasApp && hasSecret) { + throw new Error( + `Config schema visibility is both 'frontend' and 'secret' for ${path.join( + '/', + )}`, + ); + } else if (hasApp) { + return 'frontend'; + } else if (hasSecret) { + return 'secret'; + } + + return 'backend'; + }, + } as Partial>, + }, + ); + + const validate = ajv.compile(merged); + + return configs => { + const config = ConfigReader.fromConfigs(configs).get(); + + visibilityByPath.clear(); + + const valid = validate(config); + if (!valid) { + const errors = ajv.errorsText(validate.errors); + return { + errors: [errors], + visibilityByPath: new Map(), + }; + } + + return { + visibilityByPath: new Map(visibilityByPath), + }; + }; +} diff --git a/packages/config-loader/src/lib/schema/filtering.ts b/packages/config-loader/src/lib/schema/filtering.ts new file mode 100644 index 0000000000..64ccfa5e24 --- /dev/null +++ b/packages/config-loader/src/lib/schema/filtering.ts @@ -0,0 +1,64 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { JsonObject, JsonValue } from '@backstage/config'; +import { ConfigVisibility, DEFAULT_CONFIG_VISIBILITY } from './types'; + +export function filterByVisibility( + data: JsonObject, + includeVisibilities: ConfigVisibility[], + visibilityByPath: Map, +): JsonObject { + function transform(jsonVal: JsonValue, path: string): JsonValue | undefined { + if (typeof jsonVal !== 'object') { + const visibility = + visibilityByPath.get(path) ?? DEFAULT_CONFIG_VISIBILITY; + if (includeVisibilities.includes(visibility)) { + return jsonVal; + } + return undefined; + } else if (jsonVal === null) { + return undefined; + } else if (Array.isArray(jsonVal)) { + const arr = new Array(); + + for (const [index, value] of jsonVal.entries()) { + const out = transform(value, `${path}/${index}`); + if (out !== undefined) { + arr.push(out); + } + } + + return arr.length === 0 ? undefined : arr; + } + + const outObj: JsonObject = {}; + + for (const [key, value] of Object.entries(jsonVal)) { + if (value === undefined) { + continue; + } + const out = transform(value, `${path}/${key}`); + if (out !== undefined) { + outObj[key] = out; + } + } + + return Object.keys(outObj).length === 0 ? undefined : outObj; + } + + return (transform(data, '') as JsonObject) ?? {}; +} diff --git a/packages/config-loader/src/lib/schema/index.ts b/packages/config-loader/src/lib/schema/index.ts new file mode 100644 index 0000000000..990c0dc182 --- /dev/null +++ b/packages/config-loader/src/lib/schema/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { loadConfigSchema } from './load'; diff --git a/packages/config-loader/src/lib/schema/load.ts b/packages/config-loader/src/lib/schema/load.ts new file mode 100644 index 0000000000..75fcb86ba7 --- /dev/null +++ b/packages/config-loader/src/lib/schema/load.ts @@ -0,0 +1,63 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AppConfig } from '@backstage/config'; +import { compileConfigSchemas } from './compile'; +import { collectConfigSchemas } from './collect'; +import { filterByVisibility } from './filtering'; +import { ConfigSchema } from './types'; + +type Options = { + dependencies: string[]; +}; + +export async function loadConfigSchema( + options: Options, +): Promise { + const start = process.hrtime(); + + const schemas = await collectConfigSchemas(options.dependencies[0]); + + const [durS, durNs] = process.hrtime(start); + const dur = (durS + durNs / 10 ** 9).toFixed(3); + console.log(`DEBUG: collected config schemas in ${dur}s`); + console.log('DEBUG: schemas =', schemas); + + const validate = compileConfigSchemas(schemas); + + return { + process(configs: AppConfig[], { visibilities } = {}): AppConfig[] { + const result = validate(configs); + if (result.errors) { + throw new Error( + `Config validation failed, ${result.errors.join('; ')}`, + ); + } + + let processedConfigs = configs; + + if (visibilities) { + processedConfigs = processedConfigs.map(({ data, context }) => ({ + context, + data: filterByVisibility(data, visibilities, result.visibilityByPath), + })); + } + console.log('DEBUG: result.visibilityByPath =', result.visibilityByPath); + + return processedConfigs; + }, + }; +} diff --git a/packages/config-loader/src/lib/schema/types.ts b/packages/config-loader/src/lib/schema/types.ts new file mode 100644 index 0000000000..9e5624ca6a --- /dev/null +++ b/packages/config-loader/src/lib/schema/types.ts @@ -0,0 +1,49 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AppConfig } from '@backstage/config'; +import { JSONSchema7 as JSONSchema } from 'json-schema'; + +export type ConfigSchemaPackageEntry = { + value: JSONSchema; + path: string; +}; + +export const CONFIG_VISIBILITIES = ['frontend', 'backend', 'secret'] as const; + +export type ConfigVisibility = typeof CONFIG_VISIBILITIES[number]; + +export const DEFAULT_CONFIG_VISIBILITY: ConfigVisibility = 'backend'; + +type ValidationError = string; + +type ValidationResult = { + errors?: ValidationError[]; + visibilityByPath: Map; +}; + +export type ValidationFunc = (configs: AppConfig[]) => ValidationResult; + +type ConfigProcessingOptions = { + visibilities?: ConfigVisibility[]; +}; + +export type ConfigSchema = { + process( + appConfigs: AppConfig[], + options?: ConfigProcessingOptions, + ): AppConfig[]; +}; diff --git a/packages/config-loader/src/loader.ts b/packages/config-loader/src/loader.ts index 2d3c2afd9c..00414f86eb 100644 --- a/packages/config-loader/src/loader.ts +++ b/packages/config-loader/src/loader.ts @@ -17,7 +17,7 @@ import fs from 'fs-extra'; import { resolve as resolvePath, dirname, isAbsolute } from 'path'; import { AppConfig, JsonObject } from '@backstage/config'; -import { readConfigFile, readEnvConfig, readSecret, loadSchema } from './lib'; +import { readConfigFile, readEnvConfig, readSecret } from './lib'; export type LoadConfigOptions = { // The root directory of the config loading context. Used to find default configs. @@ -80,8 +80,6 @@ export async function loadConfig( const { configRoot } = options; const configPaths = options.configPaths.slice(); - const schema = await loadSchema({ dependencies: ['example-backend'] }); - // If no paths are provided, we default to reading // `app-config.yaml` and, if it exists, `app-config.local.yaml` if (configPaths.length === 0) { @@ -130,5 +128,5 @@ export async function loadConfig( configs.push(...readEnvConfig(process.env)); - return schema.process(configs, { visibilities: ['frontend', 'secret'] }); + return configs; } From b652ebc4e7e276d963804f3d1a6e5c98c81e0a62 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 8 Nov 2020 14:39:07 +0100 Subject: [PATCH 198/252] config-loader: add visibility filtering tests + fix filtering logic --- .../src/lib/schema/filtering.test.ts | 105 ++++++++++++++++++ .../config-loader/src/lib/schema/filtering.ts | 23 ++-- 2 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 packages/config-loader/src/lib/schema/filtering.test.ts diff --git a/packages/config-loader/src/lib/schema/filtering.test.ts b/packages/config-loader/src/lib/schema/filtering.test.ts new file mode 100644 index 0000000000..8cc907417b --- /dev/null +++ b/packages/config-loader/src/lib/schema/filtering.test.ts @@ -0,0 +1,105 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { JsonObject } from '@backstage/config'; +import { ConfigVisibility } from './types'; +import { filterByVisibility } from './filtering'; + +const data = { + arr: ['f', 'b', 's'], + objArr: [ + { f: 1, b: 2, s: 3 }, + { f: 4, b: 5, s: 6 }, + ], + obj: { + f: 'a', + b: { + s: true, + }, + }, + arrF: [{ never: 'here' }], + arrB: [{ never: 'here' }], + arrS: [{ never: 'here' }], + objF: { never: 'here' }, + objB: { never: 'here' }, + objS: { never: 'here' }, +}; + +const visibilities = new Map( + Object.entries({ + '/arr/0': 'frontend', + '/arr/1': 'backend', + '/arr/2': 'secret', + '/obj/f': 'frontend', + '/obj/b': 'backend', + '/obj/b/s': 'secret', + '/objArr/0/f': 'frontend', + '/objArr/0/b': 'backend', + '/objArr/0/s': 'secret', + '/objArr/1/f': 'frontend', + '/objArr/1/b': 'backend', + '/objArr/1/s': 'secret', + '/arrF': 'frontend', + '/arrB': 'backend', + '/arrS': 'secret', + '/objF': 'frontend', + '/objB': 'backend', + '/objS': 'secret', + }), +); + +describe('filterByVisibility', () => { + test.each<[ConfigVisibility[], JsonObject]>([ + [[], {}], + [ + ['frontend'], + { + arr: ['f'], + objArr: [{ f: 1 }, { f: 4 }], + obj: { f: 'a' }, + arrF: [], + objF: {}, + }, + ], + [ + ['backend'], + { + arr: ['b'], + objArr: [{ b: 2 }, { b: 5 }], + obj: { b: {} }, + arrF: [{ never: 'here' }], + arrB: [{ never: 'here' }], + arrS: [{ never: 'here' }], + objF: { never: 'here' }, + objB: { never: 'here' }, + objS: { never: 'here' }, + }, + ], + [ + ['secret'], + { + arr: ['s'], + objArr: [{ s: 3 }, { s: 6 }], + obj: { b: { s: true } }, + arrS: [], + objS: {}, + }, + ], + [['frontend', 'backend', 'secret'], data], + ])('should filter correctly with %p', (filter, expected) => { + expect(filterByVisibility(data, filter, visibilities)).toEqual(expected); + }); +}); diff --git a/packages/config-loader/src/lib/schema/filtering.ts b/packages/config-loader/src/lib/schema/filtering.ts index 64ccfa5e24..3ed4c958b8 100644 --- a/packages/config-loader/src/lib/schema/filtering.ts +++ b/packages/config-loader/src/lib/schema/filtering.ts @@ -23,13 +23,12 @@ export function filterByVisibility( visibilityByPath: Map, ): JsonObject { function transform(jsonVal: JsonValue, path: string): JsonValue | undefined { + const isVisible = includeVisibilities.includes( + visibilityByPath.get(path) ?? DEFAULT_CONFIG_VISIBILITY, + ); + if (typeof jsonVal !== 'object') { - const visibility = - visibilityByPath.get(path) ?? DEFAULT_CONFIG_VISIBILITY; - if (includeVisibilities.includes(visibility)) { - return jsonVal; - } - return undefined; + return isVisible ? jsonVal : undefined; } else if (jsonVal === null) { return undefined; } else if (Array.isArray(jsonVal)) { @@ -42,10 +41,14 @@ export function filterByVisibility( } } - return arr.length === 0 ? undefined : arr; + if (arr.length > 0 || isVisible) { + return arr; + } + return undefined; } const outObj: JsonObject = {}; + let hasOutput = false; for (const [key, value] of Object.entries(jsonVal)) { if (value === undefined) { @@ -54,10 +57,14 @@ export function filterByVisibility( const out = transform(value, `${path}/${key}`); if (out !== undefined) { outObj[key] = out; + hasOutput = true; } } - return Object.keys(outObj).length === 0 ? undefined : outObj; + if (hasOutput || isVisible) { + return outObj; + } + return undefined; } return (transform(data, '') as JsonObject) ?? {}; From a533c15b0979745739a86456742f6ff7ba06aa2f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 8 Nov 2020 14:57:50 +0100 Subject: [PATCH 199/252] config-loader: document schema lib --- .../config-loader/src/lib/schema/collect.ts | 3 ++ .../config-loader/src/lib/schema/compile.ts | 16 +++++++ .../config-loader/src/lib/schema/filtering.ts | 4 ++ packages/config-loader/src/lib/schema/load.ts | 3 ++ .../config-loader/src/lib/schema/types.ts | 45 +++++++++++++++++++ 5 files changed, 71 insertions(+) diff --git a/packages/config-loader/src/lib/schema/collect.ts b/packages/config-loader/src/lib/schema/collect.ts index b6d0bffaef..25bdbe7f55 100644 --- a/packages/config-loader/src/lib/schema/collect.ts +++ b/packages/config-loader/src/lib/schema/collect.ts @@ -23,6 +23,9 @@ const req = ? require : __non_webpack_require__; +/** + * This collects all known config schemas across all dependencies of the app. + */ export async function collectConfigSchemas( name: string, opts: unknown = {}, diff --git a/packages/config-loader/src/lib/schema/compile.ts b/packages/config-loader/src/lib/schema/compile.ts index 9ab8627fce..76642690d5 100644 --- a/packages/config-loader/src/lib/schema/compile.ts +++ b/packages/config-loader/src/lib/schema/compile.ts @@ -25,9 +25,19 @@ import { ConfigVisibility, } from './types'; +/** + * This takes a collection of Backstage configuration schemas from various + * sources and compiles them down into a single schema validation function. + * + * It also handles the implementation of the custom "visibility" keyword used + * to specify the scope of different config paths. + */ export function compileConfigSchemas( schemas: ConfigSchemaPackageEntry[], ): ValidationFunc { + // The ajv instance below is stateful and doesn't really allow for additional + // output during validation. We work around this by having this extra piece + // of state that we reset before each validation. const visibilityByPath = new Map(); const ajv = new Ajv({ @@ -63,8 +73,13 @@ export function compileConfigSchemas( const merged = mergeAllOf( { allOf: schemas.map(_ => _.value) }, { + // JSONSchema is typically subtractive, as in it always reduces the set of allowed + // inputs through constraints. This changes the object property merging to be additive + // rather than subtractive. ignoreAdditionalProperties: true, resolvers: { + // This ensures that the visibilities across different schemas are sound, and + // selects the most specific visibility for each path. visibility(values: string[], path: string[]) { const hasApp = values.some(_ => _ === 'frontend'); const hasSecret = values.some(_ => _ === 'secret'); @@ -95,6 +110,7 @@ export function compileConfigSchemas( const valid = validate(config); if (!valid) { + // TODO(Rugvip): better messages here, with more context such as which file the error occurred in const errors = ajv.errorsText(validate.errors); return { errors: [errors], diff --git a/packages/config-loader/src/lib/schema/filtering.ts b/packages/config-loader/src/lib/schema/filtering.ts index 3ed4c958b8..d6dcaf2dd7 100644 --- a/packages/config-loader/src/lib/schema/filtering.ts +++ b/packages/config-loader/src/lib/schema/filtering.ts @@ -17,6 +17,10 @@ import { JsonObject, JsonValue } from '@backstage/config'; import { ConfigVisibility, DEFAULT_CONFIG_VISIBILITY } from './types'; +/** + * This filters data by visibility by discovering the visibility of each + * value, and then only keeping the ones that are specified in `includeVisibilities`. + */ export function filterByVisibility( data: JsonObject, includeVisibilities: ConfigVisibility[], diff --git a/packages/config-loader/src/lib/schema/load.ts b/packages/config-loader/src/lib/schema/load.ts index 75fcb86ba7..286b363b50 100644 --- a/packages/config-loader/src/lib/schema/load.ts +++ b/packages/config-loader/src/lib/schema/load.ts @@ -24,6 +24,9 @@ type Options = { dependencies: string[]; }; +/** + * Loads config schema for a Backstage instance. + */ export async function loadConfigSchema( options: Options, ): Promise { diff --git a/packages/config-loader/src/lib/schema/types.ts b/packages/config-loader/src/lib/schema/types.ts index 9e5624ca6a..7e831a7a1c 100644 --- a/packages/config-loader/src/lib/schema/types.ts +++ b/packages/config-loader/src/lib/schema/types.ts @@ -17,30 +17,75 @@ import { AppConfig } from '@backstage/config'; import { JSONSchema7 as JSONSchema } from 'json-schema'; +/** + * An sub-set of configuration schema. + */ export type ConfigSchemaPackageEntry = { + /** + * The configuration schema itself, as JSONSchema draft-07 + */ value: JSONSchema; + /** + * The path that the configuration schema was discovered at. + */ path: string; }; +/** + * A list of all possible configuration value visibilities. + */ export const CONFIG_VISIBILITIES = ['frontend', 'backend', 'secret'] as const; +/** + * A type representing the possible configuration value visibilities + */ export type ConfigVisibility = typeof CONFIG_VISIBILITIES[number]; +/** + * The default configuration visibility if no other values is given. + */ export const DEFAULT_CONFIG_VISIBILITY: ConfigVisibility = 'backend'; +/** + * An explanation of a configuration validation error. + */ type ValidationError = string; +/** + * The result of validating configuration data using a schema. + */ type ValidationResult = { + /** + * Errors that where emitted during validation, if any. + */ errors?: ValidationError[]; + /** + * The configuration visibilities the where discovered during validation. + * + * The path in the key uses the form `////` + */ visibilityByPath: Map; }; +/** + * A function used validate configuration data. + */ export type ValidationFunc = (configs: AppConfig[]) => ValidationResult; +/** + * Options used to process configuration data with a schema. + */ type ConfigProcessingOptions = { + /** + * The visibilities that should be included in the output data. + * If omitted, the data will not be filtered by visibility. + */ visibilities?: ConfigVisibility[]; }; +/** + * A loaded configuration schema that is ready to process configuration data. + */ export type ConfigSchema = { process( appConfigs: AppConfig[], From 4ccba1aa9620af569ec4c424806bafb510982570 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 8 Nov 2020 14:59:35 +0100 Subject: [PATCH 200/252] config-loader: remove debugging output in schema lib --- packages/config-loader/src/lib/schema/load.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/config-loader/src/lib/schema/load.ts b/packages/config-loader/src/lib/schema/load.ts index 286b363b50..46e1358ba9 100644 --- a/packages/config-loader/src/lib/schema/load.ts +++ b/packages/config-loader/src/lib/schema/load.ts @@ -30,15 +30,8 @@ type Options = { export async function loadConfigSchema( options: Options, ): Promise { - const start = process.hrtime(); - const schemas = await collectConfigSchemas(options.dependencies[0]); - const [durS, durNs] = process.hrtime(start); - const dur = (durS + durNs / 10 ** 9).toFixed(3); - console.log(`DEBUG: collected config schemas in ${dur}s`); - console.log('DEBUG: schemas =', schemas); - const validate = compileConfigSchemas(schemas); return { @@ -58,7 +51,6 @@ export async function loadConfigSchema( data: filterByVisibility(data, visibilities, result.visibilityByPath), })); } - console.log('DEBUG: result.visibilityByPath =', result.visibilityByPath); return processedConfigs; }, From f4af5d1dced13c29bf6fdb07fd024979453920d7 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 8 Nov 2020 15:22:28 +0100 Subject: [PATCH 201/252] config-loader: consider all deps when discovering schema and parallelize processing --- .../config-loader/src/lib/schema/collect.ts | 109 +++++++++++------- packages/config-loader/src/lib/schema/load.ts | 2 +- 2 files changed, 66 insertions(+), 45 deletions(-) diff --git a/packages/config-loader/src/lib/schema/collect.ts b/packages/config-loader/src/lib/schema/collect.ts index 25bdbe7f55..6e3755926c 100644 --- a/packages/config-loader/src/lib/schema/collect.ts +++ b/packages/config-loader/src/lib/schema/collect.ts @@ -18,6 +18,11 @@ import fs from 'fs-extra'; import { resolve as resolvePath, dirname } from 'path'; import { ConfigSchemaPackageEntry } from './types'; +type Item = { + name: string; + parentPath?: string; +}; + const req = typeof __non_webpack_require__ === 'undefined' ? require @@ -27,55 +32,71 @@ const req = * This collects all known config schemas across all dependencies of the app. */ export async function collectConfigSchemas( - name: string, - opts: unknown = {}, - visited: Set = new Set(), - schemas: ConfigSchemaPackageEntry[] = [], + packageNames: string[], ): Promise { - const pkgPath = req.resolve(`${name}/package.json`, opts); - if (visited.has(pkgPath)) { - return schemas; - } - visited.add(pkgPath); - const pkg = await fs.readJson(pkgPath); - const depNames = [ - ...Object.keys(pkg.dependencies ?? {}), - ...Object.keys(pkg.peerDependencies ?? {}), - ]; + const visitedPackages = new Set(); + const schemas = Array(); - // TODO(Rugvip): Trying this out to avoid having to traverse the full dependency graph, - // since that's pretty slow. We probably need a better way to determine when - // we've left the Backstage ecosystem, but this will do for now. - const hasSchema = 'configSchema' in pkg; - const hasBackstageDep = depNames.some(_ => _.startsWith('@backstage/')); - if (!hasSchema && !hasBackstageDep) { - return schemas; - } - if (hasSchema) { - if (typeof pkg.configSchema === 'string') { - if (!pkg.configSchema.endsWith('.json')) { - throw new Error( - `Config schema files must be .json, got ${pkg.configSchema}`, - ); - } - const value = await fs.readJson( - resolvePath(dirname(pkgPath), pkg.configSchema), - ); - schemas.push({ - value, - path: pkgPath, - }); - } else { - schemas.push({ - value: pkg.configSchema, - path: pkgPath, - }); + async function process({ name, parentPath }: Item) { + // Ensures that we only process each package once. We don't bother with + // loading in schemas from duplicates of different versions, as that's not + // supported by Backstage right now anyway. We may want to change that in + // the future though, if it for example becomes possible to load in two + // different versions of e.g. @backstage/core at once. + if (visitedPackages.has(name)) { + return; } + visitedPackages.add(name); + + const pkgPath = req.resolve( + `${name}/package.json`, + parentPath && { + paths: [parentPath], + }, + ); + + const pkg = await fs.readJson(pkgPath); + const depNames = [ + ...Object.keys(pkg.dependencies ?? {}), + ...Object.keys(pkg.peerDependencies ?? {}), + ]; + + // TODO(Rugvip): Trying this out to avoid having to traverse the full dependency graph, + // since that's pretty slow. We probably need a better way to determine when + // we've left the Backstage ecosystem, but this will do for now. + const hasSchema = 'configSchema' in pkg; + const hasBackstageDep = depNames.some(_ => _.startsWith('@backstage/')); + if (!hasSchema && !hasBackstageDep) { + return; + } + if (hasSchema) { + if (typeof pkg.configSchema === 'string') { + if (!pkg.configSchema.endsWith('.json')) { + throw new Error( + `Config schema files must be .json, got ${pkg.configSchema}`, + ); + } + const value = await fs.readJson( + resolvePath(dirname(pkgPath), pkg.configSchema), + ); + schemas.push({ + value, + path: pkgPath, + }); + } else { + schemas.push({ + value: pkg.configSchema, + path: pkgPath, + }); + } + } + + await Promise.all( + depNames.map(name => process({ name, parentPath: pkgPath })), + ); } - for (const depName of depNames) { - await collectConfigSchemas(depName, { paths: [pkgPath] }, visited, schemas); - } + await Promise.all(packageNames.map(name => process({ name }))); return schemas; } diff --git a/packages/config-loader/src/lib/schema/load.ts b/packages/config-loader/src/lib/schema/load.ts index 46e1358ba9..6352da65af 100644 --- a/packages/config-loader/src/lib/schema/load.ts +++ b/packages/config-loader/src/lib/schema/load.ts @@ -30,7 +30,7 @@ type Options = { export async function loadConfigSchema( options: Options, ): Promise { - const schemas = await collectConfigSchemas(options.dependencies[0]); + const schemas = await collectConfigSchemas(options.dependencies); const validate = compileConfigSchemas(schemas); From 7298f8b26576042b05b37ec38fac7e6a8ac1f2b6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 8 Nov 2020 15:30:03 +0100 Subject: [PATCH 202/252] config-loader: remove support for conditionally reading secrets --- packages/backend-common/src/config.ts | 1 - packages/cli/src/lib/config.ts | 6 +- packages/config-loader/src/lib/reader.test.ts | 19 ------ packages/config-loader/src/lib/reader.ts | 4 -- .../config-loader/src/lib/secrets.test.ts | 1 - packages/config-loader/src/lib/types.ts | 1 - packages/config-loader/src/loader.test.ts | 59 ++----------------- packages/config-loader/src/loader.ts | 27 +-------- 8 files changed, 7 insertions(+), 111 deletions(-) diff --git a/packages/backend-common/src/config.ts b/packages/backend-common/src/config.ts index 69e46bd5b4..86beb65805 100644 --- a/packages/backend-common/src/config.ts +++ b/packages/backend-common/src/config.ts @@ -40,7 +40,6 @@ export async function loadBackendConfig(options: Options): Promise { env: process.env.APP_ENV ?? process.env.NODE_ENV ?? 'development', configRoot: paths.targetRoot, configPaths: configOpts.map(opt => resolvePath(opt)), - shouldReadSecrets: true, }); options.logger.info( diff --git a/packages/cli/src/lib/config.ts b/packages/cli/src/lib/config.ts index 5801632ea5..0ce19c80f5 100644 --- a/packages/cli/src/lib/config.ts +++ b/packages/cli/src/lib/config.ts @@ -18,14 +18,10 @@ import { loadConfig } from '@backstage/config-loader'; import { ConfigReader } from '@backstage/config'; import { paths } from './paths'; -export async function loadCliConfig( - configArgs: string[], - shouldReadSecrets: boolean = false, -) { +export async function loadCliConfig(configArgs: string[]) { const configPaths = configArgs.map(arg => paths.resolveTarget(arg)); const appConfigs = await loadConfig({ - shouldReadSecrets, env: process.env.APP_ENV ?? process.env.NODE_ENV ?? 'production', configRoot: paths.targetRoot, configPaths, diff --git a/packages/config-loader/src/lib/reader.test.ts b/packages/config-loader/src/lib/reader.test.ts index 6ccceef60d..a0a8495714 100644 --- a/packages/config-loader/src/lib/reader.test.ts +++ b/packages/config-loader/src/lib/reader.test.ts @@ -28,7 +28,6 @@ function memoryFiles(files: { [path: string]: string }) { const mockContext: ReaderContext = { env: {}, - skip: () => false, readFile: jest.fn(), readSecret: jest.fn(), }; @@ -179,22 +178,4 @@ describe('readConfigFile', () => { await expect(config).rejects.toThrow('Invalid secret at .app: NOPE'); }); - - it('should omit skipped values', async () => { - const readFile = memoryFiles({ - './app-config.yaml': 'app: { title: skip, name: include }', - }); - - const config = readConfigFile('./app-config.yaml', { - ...mockContext, - readFile, - skip: (path: string) => path === '.app.title', - readSecret: jest.fn() as ReadSecretFunc, - }); - - await expect(config).resolves.toEqual({ - context: 'app-config.yaml', - data: { app: { name: 'include' } }, - }); - }); }); diff --git a/packages/config-loader/src/lib/reader.ts b/packages/config-loader/src/lib/reader.ts index 8eadae0fe0..9eba58be97 100644 --- a/packages/config-loader/src/lib/reader.ts +++ b/packages/config-loader/src/lib/reader.ts @@ -37,10 +37,6 @@ export async function readConfigFile( obj: JsonValue, path: string, ): Promise { - if (ctx.skip(path)) { - return undefined; - } - if (typeof obj !== 'object') { return obj; } else if (obj === null) { diff --git a/packages/config-loader/src/lib/secrets.test.ts b/packages/config-loader/src/lib/secrets.test.ts index cfc4150d68..d80ada193b 100644 --- a/packages/config-loader/src/lib/secrets.test.ts +++ b/packages/config-loader/src/lib/secrets.test.ts @@ -21,7 +21,6 @@ const ctx: ReaderContext = { env: { SECRET: 'my-secret', }, - skip: () => false, readSecret: jest.fn(), async readFile(path) { const content = ({ diff --git a/packages/config-loader/src/lib/types.ts b/packages/config-loader/src/lib/types.ts index 02b8a9d053..e189aef20d 100644 --- a/packages/config-loader/src/lib/types.ts +++ b/packages/config-loader/src/lib/types.ts @@ -28,7 +28,6 @@ export type SkipFunc = (path: string) => boolean; */ export type ReaderContext = { env: { [name in string]?: string }; - skip: SkipFunc; readFile: ReadFileFunc; readSecret: ReadSecretFunc; }; diff --git a/packages/config-loader/src/loader.test.ts b/packages/config-loader/src/loader.test.ts index 3b0d8e6e92..a9857a784f 100644 --- a/packages/config-loader/src/loader.test.ts +++ b/packages/config-loader/src/loader.test.ts @@ -44,47 +44,6 @@ describe('loadConfig', () => { configRoot: '/root', configPaths: [], env: 'production', - shouldReadSecrets: false, - }), - ).resolves.toEqual([ - { - context: 'app-config.yaml', - data: { - app: { - title: 'Example App', - }, - }, - }, - ]); - }); - - it('loads config without secrets', async () => { - await expect( - loadConfig({ - configRoot: '/root', - configPaths: ['/root/app-config.yaml'], - env: 'production', - shouldReadSecrets: false, - }), - ).resolves.toEqual([ - { - context: 'app-config.yaml', - data: { - app: { - title: 'Example App', - }, - }, - }, - ]); - }); - - it('loads config with secrets', async () => { - await expect( - loadConfig({ - configRoot: '/root', - configPaths: ['/root/app-config.yaml'], - env: 'production', - shouldReadSecrets: true, }), ).resolves.toEqual([ { @@ -99,16 +58,12 @@ describe('loadConfig', () => { ]); }); - it('loads development config without secrets', async () => { + it('loads config with secrets', async () => { await expect( loadConfig({ configRoot: '/root', - configPaths: [ - '/root/app-config.yaml', - '/root/app-config.development.yaml', - ], - env: 'development', - shouldReadSecrets: false, + configPaths: ['/root/app-config.yaml'], + env: 'production', }), ).resolves.toEqual([ { @@ -116,15 +71,10 @@ describe('loadConfig', () => { data: { app: { title: 'Example App', + sessionKey: 'abc123', }, }, }, - { - context: 'app-config.development.yaml', - data: { - app: {}, - }, - }, ]); }); @@ -137,7 +87,6 @@ describe('loadConfig', () => { '/root/app-config.development.yaml', ], env: 'development', - shouldReadSecrets: true, }), ).resolves.toEqual([ { diff --git a/packages/config-loader/src/loader.ts b/packages/config-loader/src/loader.ts index 00414f86eb..a647367469 100644 --- a/packages/config-loader/src/loader.ts +++ b/packages/config-loader/src/loader.ts @@ -28,18 +28,13 @@ export type LoadConfigOptions = { // TODO(Rugvip): This will be removed in the future, but for now we use it to warn about possible mistakes. env: string; - - // Whether to read secrets or omit them, defaults to false. - shouldReadSecrets?: boolean; }; class Context { constructor( private readonly options: { - secretPaths: Set; env: { [name in string]?: string }; rootPath: string; - shouldReadSecrets: boolean; }, ) {} @@ -47,35 +42,21 @@ class Context { return this.options.env; } - skip(path: string): boolean { - if (this.options.shouldReadSecrets) { - return false; - } - return this.options.secretPaths.has(path); - } - async readFile(path: string): Promise { return fs.readFile(resolvePath(this.options.rootPath, path), 'utf8'); } async readSecret( - path: string, + _path: string, desc: JsonObject, ): Promise { - this.options.secretPaths.add(path); - if (!this.options.shouldReadSecrets) { - return undefined; - } - return readSecret(desc, this); } } -type LoadedConfig = AppConfig[]; - export async function loadConfig( options: LoadConfigOptions, -): Promise { +): Promise { const configs = []; const { configRoot } = options; const configPaths = options.configPaths.slice(); @@ -102,8 +83,6 @@ export async function loadConfig( } try { - const secretPaths = new Set(); - for (const configPath of configPaths) { if (!isAbsolute(configPath)) { throw new Error(`Config load path is not absolute: '${configPath}'`); @@ -111,10 +90,8 @@ export async function loadConfig( const config = await readConfigFile( configPath, new Context({ - secretPaths, env: process.env, rootPath: dirname(configPath), - shouldReadSecrets: Boolean(options.shouldReadSecrets), }), ); From 564f438edd4a4c016e2ac73ff132c420ae371a69 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 8 Nov 2020 16:10:39 +0100 Subject: [PATCH 203/252] cli: load config schema and use to filter config + add --frontend flag to print --- packages/cli/package.json | 1 + packages/cli/src/commands/config/print.ts | 35 ++++++++++++++++++++--- packages/cli/src/commands/index.ts | 1 + packages/cli/src/lib/bundler/bundle.ts | 2 +- packages/cli/src/lib/bundler/config.ts | 10 +++---- packages/cli/src/lib/bundler/server.ts | 2 +- packages/cli/src/lib/bundler/types.ts | 12 ++++---- packages/cli/src/lib/config.ts | 20 +++++++++++-- 8 files changed, 64 insertions(+), 19 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 317e78ca16..66c56dd8cc 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -76,6 +76,7 @@ "jest": "^26.0.1", "jest-css-modules": "^2.1.0", "jest-esm-transformer": "^1.0.0", + "lodash": "^4.17.19", "mini-css-extract-plugin": "^0.9.0", "ora": "^4.0.3", "raw-loader": "^4.0.1", diff --git a/packages/cli/src/commands/config/print.ts b/packages/cli/src/commands/config/print.ts index eb814a5bee..ce0d9976b1 100644 --- a/packages/cli/src/commands/config/print.ts +++ b/packages/cli/src/commands/config/print.ts @@ -16,16 +16,43 @@ import { Command } from 'commander'; import { stringify as stringifyYaml } from 'yaml'; +import { ConfigReader } from '@backstage/config'; import { loadCliConfig } from '../../lib/config'; +import cloneDeepWith from 'lodash/cloneDeepWith'; export default async (cmd: Command) => { - const { config } = await loadCliConfig(cmd.config, cmd.withSecrets ?? false); + const { schema, appConfigs } = await loadCliConfig(cmd.config); + let data; - const flatConfig = config.get(); + if (cmd.frontend) { + const frontendConfigs = schema.process(appConfigs, { + visibilities: ['frontend'], + }); + data = ConfigReader.fromConfigs(frontendConfigs).get(); + } else if (cmd.withSecrets) { + data = ConfigReader.fromConfigs(appConfigs).get(); + } else { + let secretConfigs = schema.process(appConfigs, { + visibilities: ['secret'], + }); + + secretConfigs = secretConfigs.map(entry => ({ + context: entry.context, + data: cloneDeepWith(entry.data, value => { + console.log('DEBUG: value =', value); + if (typeof value !== 'object') { + return ''; + } + return undefined; + }), + })); + + data = ConfigReader.fromConfigs(appConfigs.concat(secretConfigs)).get(); + } if (cmd.format === 'json') { - process.stdout.write(`${JSON.stringify(flatConfig, null, 2)}\n`); + process.stdout.write(`${JSON.stringify(data, null, 2)}\n`); } else { - process.stdout.write(`${stringifyYaml(flatConfig)}\n`); + process.stdout.write(`${stringifyYaml(data)}\n`); } }; diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 5b0dae6244..bcbde20d84 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -136,6 +136,7 @@ export function registerCommands(program: CommanderStatic) { program .command('config:print') + .option('--frontend', 'Print only the frontend configuration') .option('--with-secrets', 'Include secrets in the printed configuration') .option( '--format ', diff --git a/packages/cli/src/lib/bundler/bundle.ts b/packages/cli/src/lib/bundler/bundle.ts index 8746c807e7..a5d581c60a 100644 --- a/packages/cli/src/lib/bundler/bundle.ts +++ b/packages/cli/src/lib/bundler/bundle.ts @@ -40,7 +40,7 @@ export async function buildBundle(options: BuildOptions) { ...options, checksEnabled: false, isDev: false, - baseUrl: resolveBaseUrl(options.config), + baseUrl: resolveBaseUrl(options.frontendConfig), }); const compiler = webpack(config); diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 34b9f498ad..9e150e0ccb 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -74,11 +74,11 @@ export async function createConfig( paths: BundlingPaths, options: BundlingOptions, ): Promise { - const { checksEnabled, isDev } = options; + const { checksEnabled, isDev, frontendConfig } = options; const { plugins, loaders } = transforms(options); - const baseUrl = options.config.getString('app.baseUrl'); + const baseUrl = frontendConfig.getString('app.baseUrl'); const validBaseUrl = new URL(baseUrl); if (checksEnabled) { @@ -99,7 +99,7 @@ export async function createConfig( plugins.push( new webpack.EnvironmentPlugin({ - APP_CONFIG: options.appConfigs, + APP_CONFIG: options.frontendAppConfigs, }), ); @@ -109,9 +109,9 @@ export async function createConfig( templateParameters: { publicPath: validBaseUrl.pathname.replace(/\/$/, ''), app: { - title: options.config.getString('app.title'), + title: frontendConfig.getString('app.title'), baseUrl: validBaseUrl.href, - googleAnalyticsTrackingId: options.config.getOptionalString( + googleAnalyticsTrackingId: frontendConfig.getOptionalString( 'app.googleAnalyticsTrackingId', ), }, diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index 2710145d83..f540e01084 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -23,7 +23,7 @@ import { ServeOptions } from './types'; import { resolveBundlingPaths } from './paths'; export async function serveBundle(options: ServeOptions) { - const url = resolveBaseUrl(options.config); + const url = resolveBaseUrl(options.frontendConfig); const host = options.config.getOptionalString('app.listen.host') || url.hostname; diff --git a/packages/cli/src/lib/bundler/types.ts b/packages/cli/src/lib/bundler/types.ts index 00f1895725..d0af0a9937 100644 --- a/packages/cli/src/lib/bundler/types.ts +++ b/packages/cli/src/lib/bundler/types.ts @@ -21,23 +21,23 @@ import { ParallelOption } from '../parallel'; export type BundlingOptions = { checksEnabled: boolean; isDev: boolean; - config: Config; - appConfigs: AppConfig[]; + frontendConfig: Config; + frontendAppConfigs: AppConfig[]; baseUrl: URL; parallel?: ParallelOption; }; export type ServeOptions = BundlingPathsOptions & { checksEnabled: boolean; - config: Config; - appConfigs: AppConfig[]; + frontendConfig: Config; + frontendAppConfigs: AppConfig[]; }; export type BuildOptions = BundlingPathsOptions & { statsJsonEnabled: boolean; parallel?: ParallelOption; - config: Config; - appConfigs: AppConfig[]; + frontendConfig: Config; + frontendAppConfigs: AppConfig[]; }; export type BackendBundlingOptions = { diff --git a/packages/cli/src/lib/config.ts b/packages/cli/src/lib/config.ts index 0ce19c80f5..a282bbf88c 100644 --- a/packages/cli/src/lib/config.ts +++ b/packages/cli/src/lib/config.ts @@ -14,13 +14,22 @@ * limitations under the License. */ -import { loadConfig } from '@backstage/config-loader'; +import { loadConfig, loadConfigSchema } from '@backstage/config-loader'; import { ConfigReader } from '@backstage/config'; import { paths } from './paths'; export async function loadCliConfig(configArgs: string[]) { const configPaths = configArgs.map(arg => paths.resolveTarget(arg)); + // Consider all packages in the monorepo when loading in config + const LernaProject = require('@lerna/project'); + const project = new LernaProject(paths.targetDir); + const packages = await project.getPackages(); + const localPackageNames = packages.map((p: any) => p.name); + const schema = await loadConfigSchema({ + dependencies: localPackageNames, + }); + const appConfigs = await loadConfig({ env: process.env.APP_ENV ?? process.env.NODE_ENV ?? 'production', configRoot: paths.targetRoot, @@ -31,8 +40,15 @@ export async function loadCliConfig(configArgs: string[]) { `Loaded config from ${appConfigs.map(c => c.context).join(', ')}`, ); + const frontendAppConfigs = schema.process(appConfigs, { + visibilities: ['frontend'], + }); + const frontendConfig = ConfigReader.fromConfigs(frontendAppConfigs); + return { + schema, appConfigs, - config: ConfigReader.fromConfigs(appConfigs), + frontendConfig, + frontendAppConfigs, }; } From 3849e24112fefdea9a9a28d56176472b7c80a4e6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 9 Nov 2020 00:19:30 +0100 Subject: [PATCH 204/252] config-loader: add support for serializing and deserializing schema --- packages/config-loader/src/index.ts | 1 + packages/config-loader/src/lib/index.ts | 2 +- .../config-loader/src/lib/schema/collect.ts | 17 ++++++---- .../config-loader/src/lib/schema/index.ts | 1 + packages/config-loader/src/lib/schema/load.ts | 34 +++++++++++++++---- .../config-loader/src/lib/schema/types.ts | 7 ++-- 6 files changed, 46 insertions(+), 16 deletions(-) diff --git a/packages/config-loader/src/index.ts b/packages/config-loader/src/index.ts index 415b6d4022..b08380d8ce 100644 --- a/packages/config-loader/src/index.ts +++ b/packages/config-loader/src/index.ts @@ -15,5 +15,6 @@ */ export { readEnvConfig, loadConfigSchema } from './lib'; +export type { ConfigSchema } from './lib'; export { loadConfig } from './loader'; export type { LoadConfigOptions } from './loader'; diff --git a/packages/config-loader/src/lib/index.ts b/packages/config-loader/src/lib/index.ts index 4da0c97788..ceb7c34222 100644 --- a/packages/config-loader/src/lib/index.ts +++ b/packages/config-loader/src/lib/index.ts @@ -17,4 +17,4 @@ export { readConfigFile } from './reader'; export { readEnvConfig } from './env'; export { readSecret } from './secrets'; -export { loadConfigSchema } from './schema'; +export * from './schema'; diff --git a/packages/config-loader/src/lib/schema/collect.ts b/packages/config-loader/src/lib/schema/collect.ts index 6e3755926c..f7fed94748 100644 --- a/packages/config-loader/src/lib/schema/collect.ts +++ b/packages/config-loader/src/lib/schema/collect.ts @@ -15,7 +15,11 @@ */ import fs from 'fs-extra'; -import { resolve as resolvePath, dirname } from 'path'; +import { + resolve as resolvePath, + relative as relativePath, + dirname, +} from 'path'; import { ConfigSchemaPackageEntry } from './types'; type Item = { @@ -36,8 +40,9 @@ export async function collectConfigSchemas( ): Promise { const visitedPackages = new Set(); const schemas = Array(); + const currentDir = process.cwd(); - async function process({ name, parentPath }: Item) { + async function processItem({ name, parentPath }: Item) { // Ensures that we only process each package once. We don't bother with // loading in schemas from duplicates of different versions, as that's not // supported by Backstage right now anyway. We may want to change that in @@ -81,22 +86,22 @@ export async function collectConfigSchemas( ); schemas.push({ value, - path: pkgPath, + path: relativePath(currentDir, pkgPath), }); } else { schemas.push({ value: pkg.configSchema, - path: pkgPath, + path: relativePath(currentDir, pkgPath), }); } } await Promise.all( - depNames.map(name => process({ name, parentPath: pkgPath })), + depNames.map(name => processItem({ name, parentPath: pkgPath })), ); } - await Promise.all(packageNames.map(name => process({ name }))); + await Promise.all(packageNames.map(name => processItem({ name }))); return schemas; } diff --git a/packages/config-loader/src/lib/schema/index.ts b/packages/config-loader/src/lib/schema/index.ts index 990c0dc182..bc3c7d8ebc 100644 --- a/packages/config-loader/src/lib/schema/index.ts +++ b/packages/config-loader/src/lib/schema/index.ts @@ -15,3 +15,4 @@ */ export { loadConfigSchema } from './load'; +export type { ConfigSchema } from './types'; diff --git a/packages/config-loader/src/lib/schema/load.ts b/packages/config-loader/src/lib/schema/load.ts index 6352da65af..cdfc8f9549 100644 --- a/packages/config-loader/src/lib/schema/load.ts +++ b/packages/config-loader/src/lib/schema/load.ts @@ -14,15 +14,19 @@ * limitations under the License. */ -import { AppConfig } from '@backstage/config'; +import { AppConfig, JsonObject } from '@backstage/config'; import { compileConfigSchemas } from './compile'; import { collectConfigSchemas } from './collect'; import { filterByVisibility } from './filtering'; -import { ConfigSchema } from './types'; +import { ConfigSchema, ConfigSchemaPackageEntry } from './types'; -type Options = { - dependencies: string[]; -}; +type Options = + | { + dependencies: string[]; + } + | { + serialized: JsonObject; + }; /** * Loads config schema for a Backstage instance. @@ -30,7 +34,19 @@ type Options = { export async function loadConfigSchema( options: Options, ): Promise { - const schemas = await collectConfigSchemas(options.dependencies); + let schemas: ConfigSchemaPackageEntry[]; + + if ('dependencies' in options) { + schemas = await collectConfigSchemas(options.dependencies); + } else { + const { serialized } = options; + if (serialized?.backstageConfigSchemaVersion !== 1) { + throw new Error( + 'Serialized configuration schema is invalid or has an invalid version number', + ); + } + schemas = serialized.schemas as ConfigSchemaPackageEntry[]; + } const validate = compileConfigSchemas(schemas); @@ -54,5 +70,11 @@ export async function loadConfigSchema( return processedConfigs; }, + serialize(): JsonObject { + return { + schemas, + backstageConfigSchemaVersion: 1, + }; + }, }; } diff --git a/packages/config-loader/src/lib/schema/types.ts b/packages/config-loader/src/lib/schema/types.ts index 7e831a7a1c..cec2da0160 100644 --- a/packages/config-loader/src/lib/schema/types.ts +++ b/packages/config-loader/src/lib/schema/types.ts @@ -14,8 +14,7 @@ * limitations under the License. */ -import { AppConfig } from '@backstage/config'; -import { JSONSchema7 as JSONSchema } from 'json-schema'; +import { AppConfig, JsonObject } from '@backstage/config'; /** * An sub-set of configuration schema. @@ -24,7 +23,7 @@ export type ConfigSchemaPackageEntry = { /** * The configuration schema itself, as JSONSchema draft-07 */ - value: JSONSchema; + value: JsonObject; /** * The path that the configuration schema was discovered at. */ @@ -91,4 +90,6 @@ export type ConfigSchema = { appConfigs: AppConfig[], options?: ConfigProcessingOptions, ): AppConfig[]; + + serialize(): JsonObject; }; From e43aa0dc60012aa11c68a8dfd3d40c27381bdfcd Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 9 Nov 2020 00:27:24 +0100 Subject: [PATCH 205/252] cli: write serialized config schema to app output --- packages/cli/src/lib/bundler/bundle.ts | 10 +++++++++- packages/cli/src/lib/bundler/types.ts | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/lib/bundler/bundle.ts b/packages/cli/src/lib/bundler/bundle.ts index a5d581c60a..30fef2e8b7 100644 --- a/packages/cli/src/lib/bundler/bundle.ts +++ b/packages/cli/src/lib/bundler/bundle.ts @@ -33,7 +33,7 @@ const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024; const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024; export async function buildBundle(options: BuildOptions) { - const { statsJsonEnabled } = options; + const { statsJsonEnabled, schema: configSchema } = options; const paths = resolveBundlingPaths(options); const config = await createConfig(paths, { @@ -56,6 +56,14 @@ export async function buildBundle(options: BuildOptions) { }); } + if (configSchema) { + await fs.writeJson( + resolvePath(paths.targetDist, '.config-schema.json'), + configSchema.serialize(), + { spaces: 2 }, + ); + } + const { stats } = await build(compiler, isCi).catch(error => { console.log(chalk.red('Failed to compile.\n')); throw new Error(`Failed to compile.\n${error.message || error}`); diff --git a/packages/cli/src/lib/bundler/types.ts b/packages/cli/src/lib/bundler/types.ts index d0af0a9937..71343a1761 100644 --- a/packages/cli/src/lib/bundler/types.ts +++ b/packages/cli/src/lib/bundler/types.ts @@ -17,6 +17,7 @@ import { AppConfig, Config } from '@backstage/config'; import { BundlingPathsOptions } from './paths'; import { ParallelOption } from '../parallel'; +import { ConfigSchema } from '@backstage/config-loader'; export type BundlingOptions = { checksEnabled: boolean; @@ -36,6 +37,7 @@ export type ServeOptions = BundlingPathsOptions & { export type BuildOptions = BundlingPathsOptions & { statsJsonEnabled: boolean; parallel?: ParallelOption; + schema?: ConfigSchema; frontendConfig: Config; frontendAppConfigs: AppConfig[]; }; From 2e4de993383b72be6ee93bdf6ce60aa30070ce7a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 9 Nov 2020 10:11:50 +0100 Subject: [PATCH 206/252] config-loader: switch to ajv v6 --- packages/config-loader/package.json | 2 +- .../config-loader/src/lib/schema/compile.ts | 40 ++++++++----------- .../src/lib/schema/filtering.test.ts | 36 ++++++++--------- .../config-loader/src/lib/schema/filtering.ts | 4 +- yarn.lock | 14 ------- 5 files changed, 38 insertions(+), 58 deletions(-) diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 39735ff2fd..32776d71b7 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -34,7 +34,7 @@ "@backstage/config": "^0.1.1", "@types/json-schema": "^7.0.6", "@types/json-schema-merge-allof": "^0.6.0", - "ajv": "^7.0.0-beta.2", + "ajv": "^6.12.5", "fs-extra": "^9.0.0", "json-schema": "^0.2.5", "json-schema-merge-allof": "^0.7.0", diff --git a/packages/config-loader/src/lib/schema/compile.ts b/packages/config-loader/src/lib/schema/compile.ts index 76642690d5..f63dc3e636 100644 --- a/packages/config-loader/src/lib/schema/compile.ts +++ b/packages/config-loader/src/lib/schema/compile.ts @@ -41,33 +41,27 @@ export function compileConfigSchemas( const visibilityByPath = new Map(); const ajv = new Ajv({ - strict: true, allErrors: true, - defaultMeta: 'http://json-schema.org/draft-07/schema#', schemas: { 'https://backstage.io/schema/config-v1': true, }, - keywords: [ - { - keyword: 'visibility', - schemaType: 'string', - metaSchema: { - type: 'string', - enum: CONFIG_VISIBILITIES, - }, - compile(visibility: ConfigVisibility) { - return (_data, ctx) => { - if (!ctx) { - return false; - } - if (visibility) { - visibilityByPath.set(ctx.dataPath, visibility); - } - return true; - }; - }, - }, - ], + }).addKeyword('visibility', { + type: 'string', + metaSchema: { + type: 'string', + enum: CONFIG_VISIBILITIES, + }, + compile(visibility: ConfigVisibility) { + return (_data, dataPath) => { + if (!dataPath) { + return false; + } + if (visibility) { + visibilityByPath.set(dataPath, visibility); + } + return true; + }; + }, }); const merged = mergeAllOf( diff --git a/packages/config-loader/src/lib/schema/filtering.test.ts b/packages/config-loader/src/lib/schema/filtering.test.ts index 8cc907417b..191edf4824 100644 --- a/packages/config-loader/src/lib/schema/filtering.test.ts +++ b/packages/config-loader/src/lib/schema/filtering.test.ts @@ -40,24 +40,24 @@ const data = { const visibilities = new Map( Object.entries({ - '/arr/0': 'frontend', - '/arr/1': 'backend', - '/arr/2': 'secret', - '/obj/f': 'frontend', - '/obj/b': 'backend', - '/obj/b/s': 'secret', - '/objArr/0/f': 'frontend', - '/objArr/0/b': 'backend', - '/objArr/0/s': 'secret', - '/objArr/1/f': 'frontend', - '/objArr/1/b': 'backend', - '/objArr/1/s': 'secret', - '/arrF': 'frontend', - '/arrB': 'backend', - '/arrS': 'secret', - '/objF': 'frontend', - '/objB': 'backend', - '/objS': 'secret', + '.arr[0]': 'frontend', + '.arr[1]': 'backend', + '.arr[2]': 'secret', + '.obj.f': 'frontend', + '.obj.b': 'backend', + '.obj.b.s': 'secret', + '.objArr[0].f': 'frontend', + '.objArr[0].b': 'backend', + '.objArr[0].s': 'secret', + '.objArr[1].f': 'frontend', + '.objArr[1].b': 'backend', + '.objArr[1].s': 'secret', + '.arrF': 'frontend', + '.arrB': 'backend', + '.arrS': 'secret', + '.objF': 'frontend', + '.objB': 'backend', + '.objS': 'secret', }), ); diff --git a/packages/config-loader/src/lib/schema/filtering.ts b/packages/config-loader/src/lib/schema/filtering.ts index d6dcaf2dd7..908e9e4739 100644 --- a/packages/config-loader/src/lib/schema/filtering.ts +++ b/packages/config-loader/src/lib/schema/filtering.ts @@ -39,7 +39,7 @@ export function filterByVisibility( const arr = new Array(); for (const [index, value] of jsonVal.entries()) { - const out = transform(value, `${path}/${index}`); + const out = transform(value, `${path}[${index}]`); if (out !== undefined) { arr.push(out); } @@ -58,7 +58,7 @@ export function filterByVisibility( if (value === undefined) { continue; } - const out = transform(value, `${path}/${key}`); + const out = transform(value, `${path}.${key}`); if (out !== undefined) { outObj[key] = out; hasOutput = true; diff --git a/yarn.lock b/yarn.lock index 98e1b2476f..a85e24210f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6569,15 +6569,6 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.1, ajv@^6.10.2, ajv@^6.12.4, ajv@^6.12.5, ajv json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^7.0.0-beta.2: - version "7.0.0-beta.3" - resolved "https://registry.npmjs.org/ajv/-/ajv-7.0.0-beta.3.tgz#d34861ccfbdebb55bf9f49a08b29f76bf656fc5c" - integrity sha512-gUGVvM4NmyqrFvCNAQnP4P7FC0RjxMQyRnrXpozNglBkDJnTysVbvycyOZUy5n6yLKSqVDUqWZBXj7dXINrSqw== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^0.5.0" - uri-js "^4.2.2" - alphanum-sort@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -15052,11 +15043,6 @@ json-schema-traverse@^0.4.1: resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema-traverse@^0.5.0: - version "0.5.0" - resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.5.0.tgz#1069a6097f9e9a567bfc6cc215e85f606ebf6480" - integrity sha512-x+TRJIQFskrNnFKE2Viz9FCSjK1vIh+H/uaBiOYszh/IcZmAFneQ35H4osWDJp1NPXccuV2I0RMXmi2ZS6Kqcg== - json-schema@0.2.3: version "0.2.3" resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" From 7f95e40fbb8e43cd75a9e61f62ac6fda728037a6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 9 Nov 2020 10:28:11 +0100 Subject: [PATCH 207/252] app-backend,cli: add support for injecting full config based on serialized schema --- packages/backend/src/plugins/app.ts | 6 ++- packages/core/src/api-wrappers/createApp.tsx | 6 ++- plugins/app-backend/package.json | 1 + plugins/app-backend/src/lib/config.test.ts | 26 +++++------ plugins/app-backend/src/lib/config.ts | 46 +++++++++++++------ .../app-backend/src/service/router.test.ts | 4 +- plugins/app-backend/src/service/router.ts | 25 ++++++---- .../src/service/standaloneServer.ts | 5 +- 8 files changed, 79 insertions(+), 40 deletions(-) diff --git a/packages/backend/src/plugins/app.ts b/packages/backend/src/plugins/app.ts index c9f7c0622a..637af80974 100644 --- a/packages/backend/src/plugins/app.ts +++ b/packages/backend/src/plugins/app.ts @@ -17,9 +17,13 @@ import { createRouter } from '@backstage/plugin-app-backend'; import { PluginEnvironment } from '../types'; -export default async function createPlugin({ logger }: PluginEnvironment) { +export default async function createPlugin({ + logger, + config, +}: PluginEnvironment) { return await createRouter({ logger, + config, appPackageName: 'example-app', }); } diff --git a/packages/core/src/api-wrappers/createApp.tsx b/packages/core/src/api-wrappers/createApp.tsx index 592a2417ca..4a1e58db42 100644 --- a/packages/core/src/api-wrappers/createApp.tsx +++ b/packages/core/src/api-wrappers/createApp.tsx @@ -61,7 +61,11 @@ export const defaultConfigLoader: AppConfigLoader = async ( if (runtimeConfigJson !== '__app_injected_runtime_config__'.toUpperCase()) { try { const data = JSON.parse(runtimeConfigJson) as JsonObject; - configs.push({ data, context: 'env' }); + if (Array.isArray(data)) { + configs.push(...data); + } else { + configs.push({ data, context: 'env' }); + } } catch (error) { throw new Error(`Failed to load runtime configuration, ${error}`); } diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index c9b82cf6f1..08266acb68 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -22,6 +22,7 @@ "dependencies": { "@backstage/backend-common": "^0.2.0", "@backstage/config-loader": "^0.2.0", + "@backstage/config": "^0.1.1", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^3.0.3", diff --git a/plugins/app-backend/src/lib/config.test.ts b/plugins/app-backend/src/lib/config.test.ts index 16c5600b1d..475f72f3b1 100644 --- a/plugins/app-backend/src/lib/config.test.ts +++ b/plugins/app-backend/src/lib/config.test.ts @@ -17,7 +17,7 @@ import fs from 'fs-extra'; import { resolve as resolvePath } from 'path'; import { getVoidLogger } from '@backstage/backend-common'; -import { injectEnvConfig } from './config'; +import { injectConfig } from './config'; jest.mock('fs-extra'); @@ -29,12 +29,12 @@ const readFileMock = (fsMock.readFile as unknown) as jest.MockedFunction< const MOCK_DIR = 'mock-dir'; const baseOptions = { - env: {}, + appConfigs: [], staticDir: MOCK_DIR, logger: getVoidLogger(), }; -describe('injectEnvConfig', () => { +describe('injectConfig', () => { beforeEach(() => { fsMock.readdir.mockResolvedValue(['main.js']); }); @@ -44,7 +44,7 @@ describe('injectEnvConfig', () => { }); it('should not inject without config', async () => { - await injectEnvConfig(baseOptions); + await injectConfig(baseOptions); expect(fsMock.readdir).toHaveBeenCalledTimes(0); expect(fsMock.readFile).toHaveBeenCalledTimes(0); expect(fsMock.writeFile).toHaveBeenCalledTimes(0); @@ -64,7 +64,10 @@ describe('injectEnvConfig', () => { return 'NO_PLACEHOLDER_HERE'; }); - await injectEnvConfig({ ...baseOptions, env: { APP_CONFIG_x: '0' } }); + await injectConfig({ + ...baseOptions, + appConfigs: [{ data: { x: 0 }, context: 'test' }], + }); expect(fsMock.readFile).toHaveBeenCalledTimes(2); expect(fsMock.readFile).toHaveBeenNthCalledWith( 1, @@ -96,11 +99,9 @@ describe('injectEnvConfig', () => { 'JSON.parse("__APP_INJECTED_RUNTIME_CONFIG__")', ); - await injectEnvConfig({ + await injectConfig({ ...baseOptions, - env: { - APP_CONFIG_x: '0', - }, + appConfigs: [{ data: { x: 0 }, context: 'test' }], }); expect(fsMock.writeFile).toHaveBeenCalledTimes(1); @@ -115,12 +116,9 @@ describe('injectEnvConfig', () => { readFileMock.mockResolvedValue(fsMock.writeFile.mock.calls[0][1]); - await injectEnvConfig({ + await injectConfig({ ...baseOptions, - env: { - APP_CONFIG_x: '1', - APP_CONFIG_y: '2', - }, + appConfigs: [{ data: { x: 1, y: 2 }, context: 'test' }], }); expect(fsMock.writeFile).toHaveBeenCalledTimes(2); diff --git a/plugins/app-backend/src/lib/config.ts b/plugins/app-backend/src/lib/config.ts index 0ed56ef722..60b5d28806 100644 --- a/plugins/app-backend/src/lib/config.ts +++ b/plugins/app-backend/src/lib/config.ts @@ -16,12 +16,12 @@ import fs from 'fs-extra'; import { resolve as resolvePath } from 'path'; -import { readEnvConfig } from '@backstage/config-loader'; import { Logger } from 'winston'; +import { AppConfig, Config, JsonObject } from '@backstage/config'; +import { loadConfigSchema, readEnvConfig } from '@backstage/config-loader'; -type Options = { - // Environment to read config from - env: { [name: string]: string | undefined }; +type InjectOptions = { + appConfigs: AppConfig[]; // Directory of the static JS files to search for file to inject staticDir: string; logger: Logger; @@ -31,19 +31,13 @@ type Options = { * Injects config from APP_CONFIG_ env vars, replacing existing * injected config if it has already been injected. */ -export async function injectEnvConfig(options: Options) { - const { env, staticDir, logger } = options; - - const envConfig = readEnvConfig(env); - if (envConfig.length === 0) { - return; - } +export async function injectConfig(options: InjectOptions) { + const { staticDir, logger, appConfigs } = options; const files = await fs.readdir(staticDir); const jsFiles = files.filter(file => file.endsWith('.js')); - const [{ data }] = envConfig; - const escapedData = JSON.stringify(data).replace(/("|'|\\)/g, '\\$1'); + const escapedData = JSON.stringify(appConfigs).replace(/("|'|\\)/g, '\\$1'); const injected = `/*__APP_INJECTED_CONFIG_MARKER__*/"${escapedData}"/*__INJECTED_END__*/`; for (const jsFile of jsFiles) { @@ -72,3 +66,29 @@ export async function injectEnvConfig(options: Options) { } logger.info('Env config not injected'); } + +type ReadOptions = { + env: { [name: string]: string | undefined }; + appDistDir: string; + config: Config; +}; + +export async function readConfigs(options: ReadOptions): Promise { + const { env, appDistDir, config } = options; + + const appConfigs = readEnvConfig(env); + + const schemaPath = resolvePath(appDistDir, '.config-schema.json'); + if (await fs.pathExists(schemaPath)) { + const serializedSchema = await fs.readJson(schemaPath); + const schema = await loadConfigSchema({ serialized: serializedSchema }); + + const frontendConfigs = await schema.process( + [{ data: config.get() as JsonObject, context: 'app' }], + { visibilities: ['frontend'] }, + ); + appConfigs.push(...frontendConfigs); + } + + return appConfigs; +} diff --git a/plugins/app-backend/src/service/router.test.ts b/plugins/app-backend/src/service/router.test.ts index aed4280ca5..b0cc3c59f7 100644 --- a/plugins/app-backend/src/service/router.test.ts +++ b/plugins/app-backend/src/service/router.test.ts @@ -16,10 +16,10 @@ import { resolve as resolvePath } from 'path'; import { getVoidLogger } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; import express from 'express'; import Router from 'express-promise-router'; import request from 'supertest'; - import { createRouter } from './router'; jest.mock('../lib/config', () => ({ injectEnvConfig: jest.fn() })); @@ -35,6 +35,7 @@ describe('createRouter', () => { beforeAll(async () => { const router = await createRouter({ logger: getVoidLogger(), + config: new ConfigReader({}), appPackageName: 'example-app', }); app = express().use(router); @@ -76,6 +77,7 @@ describe('createRouter with static fallback handler', () => { const router = await createRouter({ logger: getVoidLogger(), + config: new ConfigReader({}), appPackageName: 'example-app', staticFallbackHandler, }); diff --git a/plugins/app-backend/src/service/router.ts b/plugins/app-backend/src/service/router.ts index c9a06e4dda..b435ec15f1 100644 --- a/plugins/app-backend/src/service/router.ts +++ b/plugins/app-backend/src/service/router.ts @@ -15,13 +15,15 @@ */ import { resolve as resolvePath } from 'path'; -import { notFoundHandler, resolvePackagePath } from '@backstage/backend-common'; import express from 'express'; import Router from 'express-promise-router'; import { Logger } from 'winston'; -import { injectEnvConfig } from '../lib/config'; +import { notFoundHandler, resolvePackagePath } from '@backstage/backend-common'; +import { Config } from '@backstage/config'; +import { injectConfig, readConfigs } from '../lib/config'; export interface RouterOptions { + config: Config; logger: Logger; appPackageName: string; staticFallbackHandler?: express.Handler; @@ -30,22 +32,27 @@ export interface RouterOptions { export async function createRouter( options: RouterOptions, ): Promise { - const appDistDir = resolvePackagePath(options.appPackageName, 'dist'); - options.logger.info(`Serving static app content from ${appDistDir}`); + const { config, logger, appPackageName, staticFallbackHandler } = options; - await injectEnvConfig({ + const appDistDir = resolvePackagePath(appPackageName, 'dist'); + logger.info(`Serving static app content from ${appDistDir}`); + const staticDir = resolvePath(appDistDir, 'static'); + + const appConfigs = await readConfigs({ + config, + appDistDir, env: process.env, - logger: options.logger, - staticDir: resolvePath(appDistDir, 'static'), }); + await injectConfig({ appConfigs, logger, staticDir }); + const router = Router(); // Use a separate router for static content so that a fallback can be provided by backend const staticRouter = Router(); staticRouter.use(express.static(resolvePath(appDistDir, 'static'))); - if (options.staticFallbackHandler) { - staticRouter.use(options.staticFallbackHandler); + if (staticFallbackHandler) { + staticRouter.use(staticFallbackHandler); } staticRouter.use(notFoundHandler()); diff --git a/plugins/app-backend/src/service/standaloneServer.ts b/plugins/app-backend/src/service/standaloneServer.ts index 8abf3b81f2..005d80027b 100644 --- a/plugins/app-backend/src/service/standaloneServer.ts +++ b/plugins/app-backend/src/service/standaloneServer.ts @@ -14,14 +14,16 @@ * limitations under the License. */ -import { createServiceBuilder } from '@backstage/backend-common'; import { Server } from 'http'; import { Logger } from 'winston'; +import { createServiceBuilder } from '@backstage/backend-common'; +import { Config } from '@backstage/config'; import { createRouter } from './router'; export interface ServerOptions { port: number; enableCors: boolean; + config: Config; logger: Logger; } @@ -32,6 +34,7 @@ export async function startStandaloneServer( logger.debug('Starting application server...'); const router = await createRouter({ logger, + config: options.config, appPackageName: 'example-app', }); From eff7b6e3ba05901a44cc99f836535669f222b857 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 9 Nov 2020 10:32:25 +0100 Subject: [PATCH 208/252] cli: remove debug print --- packages/cli/src/commands/config/print.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/cli/src/commands/config/print.ts b/packages/cli/src/commands/config/print.ts index ce0d9976b1..66b709e601 100644 --- a/packages/cli/src/commands/config/print.ts +++ b/packages/cli/src/commands/config/print.ts @@ -35,11 +35,9 @@ export default async (cmd: Command) => { let secretConfigs = schema.process(appConfigs, { visibilities: ['secret'], }); - secretConfigs = secretConfigs.map(entry => ({ context: entry.context, data: cloneDeepWith(entry.data, value => { - console.log('DEBUG: value =', value); if (typeof value !== 'object') { return ''; } From c18769cc00dd85017886cd03ca22a58f3ddbb9b6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 14 Nov 2020 14:21:53 +0100 Subject: [PATCH 209/252] config-loader: visibilities -> visiblity --- packages/cli/src/commands/config/print.ts | 4 ++-- packages/cli/src/lib/config.ts | 2 +- packages/config-loader/src/lib/schema/filtering.test.ts | 4 ++-- packages/config-loader/src/lib/schema/load.ts | 6 +++--- packages/config-loader/src/lib/schema/types.ts | 2 +- plugins/app-backend/src/lib/config.ts | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/cli/src/commands/config/print.ts b/packages/cli/src/commands/config/print.ts index 66b709e601..246847b610 100644 --- a/packages/cli/src/commands/config/print.ts +++ b/packages/cli/src/commands/config/print.ts @@ -26,14 +26,14 @@ export default async (cmd: Command) => { if (cmd.frontend) { const frontendConfigs = schema.process(appConfigs, { - visibilities: ['frontend'], + visiblity: ['frontend'], }); data = ConfigReader.fromConfigs(frontendConfigs).get(); } else if (cmd.withSecrets) { data = ConfigReader.fromConfigs(appConfigs).get(); } else { let secretConfigs = schema.process(appConfigs, { - visibilities: ['secret'], + visiblity: ['secret'], }); secretConfigs = secretConfigs.map(entry => ({ context: entry.context, diff --git a/packages/cli/src/lib/config.ts b/packages/cli/src/lib/config.ts index a282bbf88c..63e3958a6f 100644 --- a/packages/cli/src/lib/config.ts +++ b/packages/cli/src/lib/config.ts @@ -41,7 +41,7 @@ export async function loadCliConfig(configArgs: string[]) { ); const frontendAppConfigs = schema.process(appConfigs, { - visibilities: ['frontend'], + visiblity: ['frontend'], }); const frontendConfig = ConfigReader.fromConfigs(frontendAppConfigs); diff --git a/packages/config-loader/src/lib/schema/filtering.test.ts b/packages/config-loader/src/lib/schema/filtering.test.ts index 191edf4824..8e3fb61ae5 100644 --- a/packages/config-loader/src/lib/schema/filtering.test.ts +++ b/packages/config-loader/src/lib/schema/filtering.test.ts @@ -38,7 +38,7 @@ const data = { objS: { never: 'here' }, }; -const visibilities = new Map( +const visiblity = new Map( Object.entries({ '.arr[0]': 'frontend', '.arr[1]': 'backend', @@ -100,6 +100,6 @@ describe('filterByVisibility', () => { ], [['frontend', 'backend', 'secret'], data], ])('should filter correctly with %p', (filter, expected) => { - expect(filterByVisibility(data, filter, visibilities)).toEqual(expected); + expect(filterByVisibility(data, filter, visiblity)).toEqual(expected); }); }); diff --git a/packages/config-loader/src/lib/schema/load.ts b/packages/config-loader/src/lib/schema/load.ts index cdfc8f9549..d886926647 100644 --- a/packages/config-loader/src/lib/schema/load.ts +++ b/packages/config-loader/src/lib/schema/load.ts @@ -51,7 +51,7 @@ export async function loadConfigSchema( const validate = compileConfigSchemas(schemas); return { - process(configs: AppConfig[], { visibilities } = {}): AppConfig[] { + process(configs: AppConfig[], { visiblity } = {}): AppConfig[] { const result = validate(configs); if (result.errors) { throw new Error( @@ -61,10 +61,10 @@ export async function loadConfigSchema( let processedConfigs = configs; - if (visibilities) { + if (visiblity) { processedConfigs = processedConfigs.map(({ data, context }) => ({ context, - data: filterByVisibility(data, visibilities, result.visibilityByPath), + data: filterByVisibility(data, visiblity, result.visibilityByPath), })); } diff --git a/packages/config-loader/src/lib/schema/types.ts b/packages/config-loader/src/lib/schema/types.ts index cec2da0160..6ab0602fb3 100644 --- a/packages/config-loader/src/lib/schema/types.ts +++ b/packages/config-loader/src/lib/schema/types.ts @@ -79,7 +79,7 @@ type ConfigProcessingOptions = { * The visibilities that should be included in the output data. * If omitted, the data will not be filtered by visibility. */ - visibilities?: ConfigVisibility[]; + visiblity?: ConfigVisibility[]; }; /** diff --git a/plugins/app-backend/src/lib/config.ts b/plugins/app-backend/src/lib/config.ts index 60b5d28806..82b431c33f 100644 --- a/plugins/app-backend/src/lib/config.ts +++ b/plugins/app-backend/src/lib/config.ts @@ -85,7 +85,7 @@ export async function readConfigs(options: ReadOptions): Promise { const frontendConfigs = await schema.process( [{ data: config.get() as JsonObject, context: 'app' }], - { visibilities: ['frontend'] }, + { visiblity: ['frontend'] }, ); appConfigs.push(...frontendConfigs); } From 051e0fd17e7b58cbfc8a0f548f8b891139f5859e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 14 Nov 2020 14:28:48 +0100 Subject: [PATCH 210/252] config-loader: hasApp -> hasFrontend --- packages/config-loader/src/lib/schema/compile.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/config-loader/src/lib/schema/compile.ts b/packages/config-loader/src/lib/schema/compile.ts index f63dc3e636..7dc65be910 100644 --- a/packages/config-loader/src/lib/schema/compile.ts +++ b/packages/config-loader/src/lib/schema/compile.ts @@ -75,15 +75,15 @@ export function compileConfigSchemas( // This ensures that the visibilities across different schemas are sound, and // selects the most specific visibility for each path. visibility(values: string[], path: string[]) { - const hasApp = values.some(_ => _ === 'frontend'); + const hasFrontend = values.some(_ => _ === 'frontend'); const hasSecret = values.some(_ => _ === 'secret'); - if (hasApp && hasSecret) { + if (hasFrontend && hasSecret) { throw new Error( `Config schema visibility is both 'frontend' and 'secret' for ${path.join( '/', )}`, ); - } else if (hasApp) { + } else if (hasFrontend) { return 'frontend'; } else if (hasSecret) { return 'secret'; From 49ab961b8bb659336aead47f786740a9959b6fb6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 14 Nov 2020 15:48:02 +0100 Subject: [PATCH 211/252] config-loader: add support for transforming values during schema processing --- packages/config-loader/src/index.ts | 2 +- .../config-loader/src/lib/schema/filtering.ts | 20 +++++++++---- .../config-loader/src/lib/schema/index.ts | 2 +- packages/config-loader/src/lib/schema/load.ts | 28 +++++++++++++++++-- .../config-loader/src/lib/schema/types.ts | 16 +++++++++++ 5 files changed, 58 insertions(+), 10 deletions(-) diff --git a/packages/config-loader/src/index.ts b/packages/config-loader/src/index.ts index b08380d8ce..9ad54c5f18 100644 --- a/packages/config-loader/src/index.ts +++ b/packages/config-loader/src/index.ts @@ -15,6 +15,6 @@ */ export { readEnvConfig, loadConfigSchema } from './lib'; -export type { ConfigSchema } from './lib'; +export type { ConfigSchema, ConfigVisibility } from './lib'; export { loadConfig } from './loader'; export type { LoadConfigOptions } from './loader'; diff --git a/packages/config-loader/src/lib/schema/filtering.ts b/packages/config-loader/src/lib/schema/filtering.ts index 908e9e4739..74e75f465d 100644 --- a/packages/config-loader/src/lib/schema/filtering.ts +++ b/packages/config-loader/src/lib/schema/filtering.ts @@ -15,7 +15,11 @@ */ import { JsonObject, JsonValue } from '@backstage/config'; -import { ConfigVisibility, DEFAULT_CONFIG_VISIBILITY } from './types'; +import { + ConfigVisibility, + DEFAULT_CONFIG_VISIBILITY, + TransformFunc, +} from './types'; /** * This filters data by visibility by discovering the visibility of each @@ -25,14 +29,20 @@ export function filterByVisibility( data: JsonObject, includeVisibilities: ConfigVisibility[], visibilityByPath: Map, + transformFunc?: TransformFunc, ): JsonObject { function transform(jsonVal: JsonValue, path: string): JsonValue | undefined { - const isVisible = includeVisibilities.includes( - visibilityByPath.get(path) ?? DEFAULT_CONFIG_VISIBILITY, - ); + const visibility = visibilityByPath.get(path) ?? DEFAULT_CONFIG_VISIBILITY; + const isVisible = includeVisibilities.includes(visibility); if (typeof jsonVal !== 'object') { - return isVisible ? jsonVal : undefined; + if (isVisible) { + if (transformFunc) { + return transformFunc(jsonVal, { visibility }); + } + return jsonVal; + } + return undefined; } else if (jsonVal === null) { return undefined; } else if (Array.isArray(jsonVal)) { diff --git a/packages/config-loader/src/lib/schema/index.ts b/packages/config-loader/src/lib/schema/index.ts index bc3c7d8ebc..8cefb93b3c 100644 --- a/packages/config-loader/src/lib/schema/index.ts +++ b/packages/config-loader/src/lib/schema/index.ts @@ -15,4 +15,4 @@ */ export { loadConfigSchema } from './load'; -export type { ConfigSchema } from './types'; +export type { ConfigSchema, ConfigVisibility } from './types'; diff --git a/packages/config-loader/src/lib/schema/load.ts b/packages/config-loader/src/lib/schema/load.ts index d886926647..010317849c 100644 --- a/packages/config-loader/src/lib/schema/load.ts +++ b/packages/config-loader/src/lib/schema/load.ts @@ -18,7 +18,11 @@ import { AppConfig, JsonObject } from '@backstage/config'; import { compileConfigSchemas } from './compile'; import { collectConfigSchemas } from './collect'; import { filterByVisibility } from './filtering'; -import { ConfigSchema, ConfigSchemaPackageEntry } from './types'; +import { + ConfigSchema, + ConfigSchemaPackageEntry, + CONFIG_VISIBILITIES, +} from './types'; type Options = | { @@ -51,7 +55,10 @@ export async function loadConfigSchema( const validate = compileConfigSchemas(schemas); return { - process(configs: AppConfig[], { visiblity } = {}): AppConfig[] { + process( + configs: AppConfig[], + { visiblity, valueTransform } = {}, + ): AppConfig[] { const result = validate(configs); if (result.errors) { throw new Error( @@ -64,7 +71,22 @@ export async function loadConfigSchema( if (visiblity) { processedConfigs = processedConfigs.map(({ data, context }) => ({ context, - data: filterByVisibility(data, visiblity, result.visibilityByPath), + data: filterByVisibility( + data, + visiblity, + result.visibilityByPath, + valueTransform, + ), + })); + } else if (valueTransform) { + processedConfigs = processedConfigs.map(({ data, context }) => ({ + context, + data: filterByVisibility( + data, + Array.from(CONFIG_VISIBILITIES), + result.visibilityByPath, + valueTransform, + ), })); } diff --git a/packages/config-loader/src/lib/schema/types.ts b/packages/config-loader/src/lib/schema/types.ts index 6ab0602fb3..c82036bbf4 100644 --- a/packages/config-loader/src/lib/schema/types.ts +++ b/packages/config-loader/src/lib/schema/types.ts @@ -71,6 +71,14 @@ type ValidationResult = { */ export type ValidationFunc = (configs: AppConfig[]) => ValidationResult; +/** + * A function used to transform primitive configuration values. + */ +export type TransformFunc = ( + value: T, + context: { visibility: ConfigVisibility }, +) => T | undefined; + /** * Options used to process configuration data with a schema. */ @@ -80,6 +88,14 @@ type ConfigProcessingOptions = { * If omitted, the data will not be filtered by visibility. */ visiblity?: ConfigVisibility[]; + + /** + * A transform function that can be used to transform primitive configuration values + * during validation. The value returned from the transform function will be used + * instead of the original value. If the transform returns `undefined`, the value + * will be omitted. + */ + valueTransform?: TransformFunc; }; /** From b6e59e6368ccc62c713a6d253b331e3c603ad737 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 14 Nov 2020 15:50:41 +0100 Subject: [PATCH 212/252] cli: refactor and fix config:print to properly filter secrets --- packages/cli/src/commands/config/print.ts | 65 +++++++++++++---------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/packages/cli/src/commands/config/print.ts b/packages/cli/src/commands/config/print.ts index 246847b610..9649c70ef3 100644 --- a/packages/cli/src/commands/config/print.ts +++ b/packages/cli/src/commands/config/print.ts @@ -16,37 +16,14 @@ import { Command } from 'commander'; import { stringify as stringifyYaml } from 'yaml'; -import { ConfigReader } from '@backstage/config'; +import { AppConfig, ConfigReader } from '@backstage/config'; import { loadCliConfig } from '../../lib/config'; -import cloneDeepWith from 'lodash/cloneDeepWith'; +import { ConfigSchema, ConfigVisibility } from '@backstage/config-loader'; export default async (cmd: Command) => { const { schema, appConfigs } = await loadCliConfig(cmd.config); - let data; - - if (cmd.frontend) { - const frontendConfigs = schema.process(appConfigs, { - visiblity: ['frontend'], - }); - data = ConfigReader.fromConfigs(frontendConfigs).get(); - } else if (cmd.withSecrets) { - data = ConfigReader.fromConfigs(appConfigs).get(); - } else { - let secretConfigs = schema.process(appConfigs, { - visiblity: ['secret'], - }); - secretConfigs = secretConfigs.map(entry => ({ - context: entry.context, - data: cloneDeepWith(entry.data, value => { - if (typeof value !== 'object') { - return ''; - } - return undefined; - }), - })); - - data = ConfigReader.fromConfigs(appConfigs.concat(secretConfigs)).get(); - } + const visibility = getVisiblityOption(cmd); + const data = serializeConfigData(appConfigs, schema, visibility); if (cmd.format === 'json') { process.stdout.write(`${JSON.stringify(data, null, 2)}\n`); @@ -54,3 +31,37 @@ export default async (cmd: Command) => { process.stdout.write(`${stringifyYaml(data)}\n`); } }; + +function getVisiblityOption(cmd: Command): ConfigVisibility { + if (cmd.frontend && cmd.withSecrets) { + throw new Error('Not allowed to combine frontend and secret config'); + } + if (cmd.frontend) { + return 'frontend'; + } else if (cmd.withSecrets) { + return 'secret'; + } + return 'backend'; +} + +function serializeConfigData( + appConfigs: AppConfig[], + schema: ConfigSchema, + visiblity: ConfigVisibility, +) { + if (visiblity === 'frontend') { + const frontendConfigs = schema.process(appConfigs, { + visiblity: ['frontend'], + }); + return ConfigReader.fromConfigs(frontendConfigs).get(); + } else if (visiblity === 'secret') { + return ConfigReader.fromConfigs(appConfigs).get(); + } + + const sanitizedConfigs = schema.process(appConfigs, { + valueTransform: (value, { visibility }) => + visibility === 'secret' ? '' : value, + }); + + return ConfigReader.fromConfigs(sanitizedConfigs).get(); +} From eb9e293fcef38a29cb75020f0afaa92e02581c84 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 14 Nov 2020 18:03:10 +0100 Subject: [PATCH 213/252] config-loader: skip schema collection from packages that don't export package.json --- .../config-loader/src/lib/schema/collect.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/config-loader/src/lib/schema/collect.ts b/packages/config-loader/src/lib/schema/collect.ts index f7fed94748..6f441a2735 100644 --- a/packages/config-loader/src/lib/schema/collect.ts +++ b/packages/config-loader/src/lib/schema/collect.ts @@ -53,12 +53,19 @@ export async function collectConfigSchemas( } visitedPackages.add(name); - const pkgPath = req.resolve( - `${name}/package.json`, - parentPath && { - paths: [parentPath], - }, - ); + let pkgPath: string; + try { + pkgPath = req.resolve( + `${name}/package.json`, + parentPath && { + paths: [parentPath], + }, + ); + } catch { + // We can somewhat safely ignore packages that don't export package.json, + // as they are likely not part of the Backstage ecosystem anyway. + return; + } const pkg = await fs.readJson(pkgPath); const depNames = [ From d02d79e39276c1c61e695d3b3995685d0e643d9e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 15 Nov 2020 13:07:13 +0100 Subject: [PATCH 214/252] config-loader,cli: better presentation of config schema validation errors --- packages/cli/src/lib/config.ts | 29 ++++++++++++------- .../config-loader/src/lib/schema/compile.ts | 10 +++++-- packages/config-loader/src/lib/schema/load.ts | 4 ++- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/packages/cli/src/lib/config.ts b/packages/cli/src/lib/config.ts index 63e3958a6f..08a8193818 100644 --- a/packages/cli/src/lib/config.ts +++ b/packages/cli/src/lib/config.ts @@ -40,15 +40,24 @@ export async function loadCliConfig(configArgs: string[]) { `Loaded config from ${appConfigs.map(c => c.context).join(', ')}`, ); - const frontendAppConfigs = schema.process(appConfigs, { - visiblity: ['frontend'], - }); - const frontendConfig = ConfigReader.fromConfigs(frontendAppConfigs); + try { + const frontendAppConfigs = schema.process(appConfigs, { + visiblity: ['frontend'], + }); + const frontendConfig = ConfigReader.fromConfigs(frontendAppConfigs); - return { - schema, - appConfigs, - frontendConfig, - frontendAppConfigs, - }; + return { + schema, + appConfigs, + frontendConfig, + frontendAppConfigs, + }; + } catch (error) { + const maybeSchemaError = error as Error & { messages?: string[] }; + if (maybeSchemaError.messages) { + const messages = maybeSchemaError.messages.join('\n '); + throw new Error(`Configuration does not match schema\n\n ${messages}`); + } + throw error; + } } diff --git a/packages/config-loader/src/lib/schema/compile.ts b/packages/config-loader/src/lib/schema/compile.ts index 7dc65be910..6ee24f8d21 100644 --- a/packages/config-loader/src/lib/schema/compile.ts +++ b/packages/config-loader/src/lib/schema/compile.ts @@ -104,10 +104,14 @@ export function compileConfigSchemas( const valid = validate(config); if (!valid) { - // TODO(Rugvip): better messages here, with more context such as which file the error occurred in - const errors = ajv.errorsText(validate.errors); + const errors = validate.errors ?? []; return { - errors: [errors], + errors: errors.map(({ dataPath, message, params }) => { + const paramStr = Object.entries(params) + .map(([name, value]) => `${name}=${value}`) + .join(' '); + return `Config ${message || ''} { ${paramStr} } at ${dataPath}`; + }), visibilityByPath: new Map(), }; } diff --git a/packages/config-loader/src/lib/schema/load.ts b/packages/config-loader/src/lib/schema/load.ts index 010317849c..01a9499983 100644 --- a/packages/config-loader/src/lib/schema/load.ts +++ b/packages/config-loader/src/lib/schema/load.ts @@ -61,9 +61,11 @@ export async function loadConfigSchema( ): AppConfig[] { const result = validate(configs); if (result.errors) { - throw new Error( + const error = new Error( `Config validation failed, ${result.errors.join('; ')}`, ); + (error as any).messages = result.errors; + throw error; } let processedConfigs = configs; From 28a49c2ffa49a2316c97a738f7e13da2c13477e3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 15 Nov 2020 20:09:07 +0100 Subject: [PATCH 215/252] config-loader: make visibility schema keyword work for all types --- packages/config-loader/src/lib/schema/compile.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/config-loader/src/lib/schema/compile.ts b/packages/config-loader/src/lib/schema/compile.ts index 6ee24f8d21..5009dccce7 100644 --- a/packages/config-loader/src/lib/schema/compile.ts +++ b/packages/config-loader/src/lib/schema/compile.ts @@ -46,7 +46,6 @@ export function compileConfigSchemas( 'https://backstage.io/schema/config-v1': true, }, }).addKeyword('visibility', { - type: 'string', metaSchema: { type: 'string', enum: CONFIG_VISIBILITIES, From 06205426d8b285709c068b1fad98e62cd2f172f2 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 15 Nov 2020 20:13:42 +0100 Subject: [PATCH 216/252] config-loader: add support for defining config schema using .d.ts files --- packages/config-loader/package.json | 1 + .../config-loader/src/lib/schema/collect.ts | 72 ++++++++++++++++--- yarn.lock | 21 ++++-- 3 files changed, 79 insertions(+), 15 deletions(-) diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 32776d71b7..9abd3c754e 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -38,6 +38,7 @@ "fs-extra": "^9.0.0", "json-schema": "^0.2.5", "json-schema-merge-allof": "^0.7.0", + "typescript-json-schema": "^0.43.0", "yaml": "^1.9.2", "yup": "^0.29.3" }, diff --git a/packages/config-loader/src/lib/schema/collect.ts b/packages/config-loader/src/lib/schema/collect.ts index 6f441a2735..dd0776213c 100644 --- a/packages/config-loader/src/lib/schema/collect.ts +++ b/packages/config-loader/src/lib/schema/collect.ts @@ -21,6 +21,8 @@ import { dirname, } from 'path'; import { ConfigSchemaPackageEntry } from './types'; +import { getProgramFromFiles, generateSchema } from 'typescript-json-schema'; +import { JsonObject } from '@backstage/config'; type Item = { name: string; @@ -40,6 +42,7 @@ export async function collectConfigSchemas( ): Promise { const visitedPackages = new Set(); const schemas = Array(); + const tsSchemaPaths = Array(); const currentDir = process.cwd(); async function processItem({ name, parentPath }: Item) { @@ -83,18 +86,23 @@ export async function collectConfigSchemas( } if (hasSchema) { if (typeof pkg.configSchema === 'string') { - if (!pkg.configSchema.endsWith('.json')) { + const isJson = pkg.configSchema.endsWith('.json'); + const isDts = pkg.configSchema.endsWith('.d.ts'); + if (!isJson && !isDts) { throw new Error( - `Config schema files must be .json, got ${pkg.configSchema}`, + `Config schema files must be .json or .d.ts, got ${pkg.configSchema}`, ); } - const value = await fs.readJson( - resolvePath(dirname(pkgPath), pkg.configSchema), - ); - schemas.push({ - value, - path: relativePath(currentDir, pkgPath), - }); + if (isDts) { + tsSchemaPaths.push(resolvePath(dirname(pkgPath), pkg.configSchema)); + } else { + const path = resolvePath(dirname(pkgPath), pkg.configSchema); + const value = await fs.readJson(path); + schemas.push({ + value, + path: relativePath(currentDir, path), + }); + } } else { schemas.push({ value: pkg.configSchema, @@ -110,5 +118,49 @@ export async function collectConfigSchemas( await Promise.all(packageNames.map(name => processItem({ name }))); - return schemas; + const tsSchemas = compileTsSchemas(currentDir, tsSchemaPaths); + + return schemas.concat(tsSchemas); +} + +// This handles the support of TypeScript .d.ts config schema declarations. +// We collect all typescript schema definition and compile them all in one go. +// This is much faster than compiling them separately. +function compileTsSchemas(currentDir: string, paths: string[]) { + if (paths.length === 0) { + return []; + } + + const program = getProgramFromFiles(paths, { + incremental: false, + isolatedModules: true, + lib: ['ES5'], // Skipping most libs speeds processing up a lot, we just need the primitive types anyway + noEmit: true, + noResolve: true, + skipLibCheck: true, // Skipping lib checks speeds things up + skipDefaultLibCheck: true, + strict: true, + typeRoots: [], // Do not include any additional types + types: [], + }); + + const tsSchemas = paths.map(schemaFile => { + const value = generateSchema( + program, + // All schemas should export a `Config` symbol + 'Config', + // This enables usage of @visibility is doc comments + { validationKeywords: ['visibility'] }, + [schemaFile], + ) as JsonObject | null; + + const path = relativePath(currentDir, schemaFile); + + if (!value) { + throw new Error(`Invalid schema in ${path}, missing Config export`); + } + return { path, value }; + }); + + return tsSchemas; } diff --git a/yarn.lock b/yarn.lock index a85e24210f..104aa4a174 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12478,7 +12478,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@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.6: version "7.1.6" resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -23239,10 +23239,21 @@ typedarray@^0.0.6: resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz#153bbd468ef07725c1df9c77e8b453f8d36abba5" - integrity sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg== +typescript-json-schema@^0.43.0: + version "0.43.0" + resolved "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.43.0.tgz#8bd9c832f1f15f006ff933907ce192222fdfd92f" + integrity sha512-4c9IMlIlHYJiQtzL1gh2nIPJEjBgJjDUs50gsnnc+GFyDSK1oFM3uQIBSVosiuA/4t6LSAXDS9vTdqbQC6EcgA== + dependencies: + "@types/json-schema" "^7.0.5" + glob "~7.1.6" + json-stable-stringify "^1.0.1" + typescript "~4.0.2" + yargs "^15.4.1" + +typescript@^4.0.3, typescript@~4.0.2: + version "4.0.5" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz#ae9dddfd1069f1cb5beb3ef3b2170dd7c1332389" + integrity sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ== ua-parser-js@^0.7.18: version "0.7.21" From 567ce7804a79ce669b5767d0f954e74fdf9d7f2d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 15 Nov 2020 20:47:04 +0100 Subject: [PATCH 217/252] backend-common: port config schema to d.ts and fill out --- packages/backend-common/config-schema.json | 80 ----------- packages/backend-common/config.d.ts | 148 +++++++++++++++++++++ packages/backend-common/package.json | 5 +- 3 files changed, 151 insertions(+), 82 deletions(-) delete mode 100644 packages/backend-common/config-schema.json create mode 100644 packages/backend-common/config.d.ts diff --git a/packages/backend-common/config-schema.json b/packages/backend-common/config-schema.json deleted file mode 100644 index 2012447a11..0000000000 --- a/packages/backend-common/config-schema.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "$schema": "https://backstage.io/schema/config-v1", - "title": "@backstage/backend-common", - "type": "object", - "properties": { - "integrations": { - "type": "object", - "additionalProperties": false, - "properties": { - "github": { - "type": "array", - "items": { - "type": "object", - "required": ["host"], - "properties": { - "host": { - "type": "string" - }, - "token": { - "type": "string", - "visibility": "secret" - } - } - } - }, - "gitlab": { - "type": "array", - "items": { - "type": "object", - "required": ["host"], - "properties": { - "host": { - "type": "string" - }, - "token": { - "type": "string", - "visibility": "secret" - } - } - } - }, - "bitbucket": { - "type": "array", - "items": { - "type": "object", - "required": ["host"], - "properties": { - "host": { - "type": "string" - }, - "username": { - "type": "string" - }, - "appPassword": { - "type": "string", - "visibility": "secret" - } - } - } - }, - "azure": { - "type": "array", - "items": { - "type": "object", - "required": ["host"], - "properties": { - "host": { - "type": "string" - }, - "token": { - "type": "string", - "visibility": "secret" - } - } - } - } - } - } - } -} diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts new file mode 100644 index 0000000000..64e883aa26 --- /dev/null +++ b/packages/backend-common/config.d.ts @@ -0,0 +1,148 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface Config { + frontend: { + baseUrl: string; // defined in core, but repeated here without doc + }; + + backend: { + baseUrl: string; // defined in core, but repeated here without doc + /** Address that the backend should listen to. */ + + listen: + | string + | { + /** Address of the interface that the backend should bind to. */ + address?: string; + /** Port that the backend should listen to. */ + port?: number; + }; + + /** HTTPS configuration for the backend. If omitted the backend will serve HTTP */ + https?: { + /** Certificate configuration or parameters for generating a self-signed certificate */ + certificate?: + | { + /** Algorithm to use to generate a self-signed certificate */ + algorithm: string; + keySize?: number; + days?: number; + } + | { + /** PEM encoded certificate. Use $file to load in a file */ + cert: string; + /** + * PEM encoded certificate key. Use $file to load in a file. + * @visibility secret + */ + key: string; + }; + }; + + /** Database connection configuration, select database type using the `client` field */ + database: + | { + client: 'sqlite3'; + connection: ':memory:' | string; + } + | { + client: 'pg'; + /** + * PostgreSQL connection string or knex configuration object. + * @secret + */ + connection: string | object; + }; + + cors?: { + origin?: string | string[]; + methods?: string | string[]; + allowedHeaders?: string | string[]; + exposedHeaders?: string | string[]; + credentials?: boolean; + maxAge?: number; + preflightContinue?: boolean; + optionsSuccessStatus?: number; + }; + + /** */ + csp?: object; + }; + + /** Configuration for integrations towards various external repository provider systems */ + integrations: { + /** Integration configuration for Azure */ + azure: { + /** The hostname of the given Azure instance */ + host: string; + /** + * Token used to authenticate requests. + * @visibility secret + */ + token?: string; + }[]; + + /** Integration configuration for BitBucket */ + bitbucket: { + /** The hostname of the given Bitbucket instance */ + host: string; + /** + * Token used to authenticate requests. + * @visibility secret + */ + token?: string; + /** The base url for the BitBucket API, for example https://api.bitbucket.org/2.0 */ + apiBaseUrl?: string; + /** + * The username to use for authenticated requests. + * @visibility secret + */ + username?: string; + /** + * BitBucket app password used to authenticate requests. + * @visibility secret + */ + appPassword?: string; + }[]; + + /** Integration configuration for GitHub */ + github: { + /** The hostname of the given GitHub instance */ + host: string; + /** + * Token used to authenticate requests. + * @visibility secret + */ + token?: string; + /** The base url for the GitHub API, for example https://api.github.com */ + apiBaseUrl?: string; + /** The base url for GitHub raw resources, for example https://raw.githubusercontent.com */ + rawBaseUrl?: string; + }[]; + + /** Integration configuration for GitLab */ + gitlab: { + /** The hostname of the given GitLab instance */ + host: string; + /** + * Token used to authenticate requests. + * @visibility secret + */ + token?: string; + }[]; + }; +} diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 2f58bd0536..737e9664bf 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -89,7 +89,8 @@ "supertest": "^4.0.2" }, "files": [ - "dist" + "dist", + "config.d.ts" ], - "configSchema": "config-schema.json" + "configSchema": "config.d.ts" } From 8782781f16f87a7bbbc2093b44af70cabef49887 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 15 Nov 2020 20:53:00 +0100 Subject: [PATCH 218/252] core: ported config schema to d.t.s --- packages/core/config.d.ts | 65 ++++++++++++++++++++++++++++++++++++++ packages/core/package.json | 53 ++----------------------------- 2 files changed, 68 insertions(+), 50 deletions(-) create mode 100644 packages/core/config.d.ts diff --git a/packages/core/config.d.ts b/packages/core/config.d.ts new file mode 100644 index 0000000000..014e4ac934 --- /dev/null +++ b/packages/core/config.d.ts @@ -0,0 +1,65 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface Config { + /** + * Generic frontend configuration. + */ + app: { + /** + * The public absolute root URL that the frontend. + * @visibility frontend + */ + baseUrl: string; + + /** + * The title of the app. + * @visibility frontend + */ + title?: string; + }; + + /** + * Generic backend configuration. + */ + backend: { + /** + * The public absolute root URL that the backend is reachable at. + * @visibility frontend + */ + baseUrl: string; + }; + + /** + * Configuration that provides information about the organization that the app is for. + */ + organization?: { + /** + * The name of the organization that the app belongs to. + * @visibility frontend + */ + name?: string; + }; + + homepage?: { + clocks?: { + /** @visibility frontend */ + label: string; + /** @visibility frontend */ + timezone: string; + }[]; + }; +} diff --git a/packages/core/package.json b/packages/core/package.json index 3d4b53d35b..d0b623ea83 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -79,55 +79,8 @@ "@types/zen-observable": "^0.8.0" }, "files": [ - "dist" + "dist", + "config.d.ts" ], - "configSchema": { - "$schema": "https://backstage.io/schema/config-v1", - "title": "@backstage/core", - "type": "object", - "properties": { - "app": { - "type": "object", - "properties": { - "title": { - "type": "string", - "visibility": "frontend" - }, - "baseUrl": { - "type": "string", - "visibility": "frontend" - } - }, - "required": [ - "title", - "baseUrl" - ] - }, - "backend": { - "type": "object", - "properties": { - "baseUrl": { - "type": "string", - "visibility": "frontend" - } - }, - "required": [ - "baseUrl" - ] - }, - "organization": { - "type": "object", - "properties": { - "name": { - "type": "string", - "visibility": "frontend" - } - } - } - }, - "required": [ - "app", - "backend" - ] - } + "configSchema": "config.d.ts" } From cb1634e818847e629002f5b5cb91b2029264491a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 15 Nov 2020 21:11:47 +0100 Subject: [PATCH 219/252] cli: added config schema --- packages/cli/package.json | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/cli/package.json b/packages/cli/package.json index 66c56dd8cc..23c5c5d6af 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -147,5 +147,31 @@ "watch": "./src", "exec": "bin/backstage-cli", "ext": "ts" + }, + "configSchema": { + "$schema": "https://backstage.io/schema/config-v1", + "title": "@backstage/cli", + "type": "object", + "properties": { + "app": { + "type": "object", + "properties": { + "baseUrl": { + "type": "string", + "visibility": "frontend" + }, + "title": { + "type": "string", + "visibility": "frontend" + }, + "googleAnalyticsTrackingId": { + "type": "string", + "visibility": "frontend", + "description": "Tracking ID for Google Analytics", + "example": "UA-000000-0" + } + } + } + } } } From f6ed7d5d0d966c14bb48895e094741abde9be7f3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 15 Nov 2020 21:29:30 +0100 Subject: [PATCH 220/252] config-loader: normalize data paths to ensure consistent behavior --- .../config-loader/src/lib/schema/compile.ts | 6 +++++- .../src/lib/schema/filtering.test.ts | 18 +++++++++--------- .../config-loader/src/lib/schema/filtering.ts | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/config-loader/src/lib/schema/compile.ts b/packages/config-loader/src/lib/schema/compile.ts index 5009dccce7..93ab8e978f 100644 --- a/packages/config-loader/src/lib/schema/compile.ts +++ b/packages/config-loader/src/lib/schema/compile.ts @@ -56,7 +56,11 @@ export function compileConfigSchemas( return false; } if (visibility) { - visibilityByPath.set(dataPath, visibility); + const normalizedPath = dataPath.replace( + /\['?(.*?)'?\]/g, + (_, segment) => `.${segment}`, + ); + visibilityByPath.set(normalizedPath, visibility); } return true; }; diff --git a/packages/config-loader/src/lib/schema/filtering.test.ts b/packages/config-loader/src/lib/schema/filtering.test.ts index 8e3fb61ae5..bfad2ca95a 100644 --- a/packages/config-loader/src/lib/schema/filtering.test.ts +++ b/packages/config-loader/src/lib/schema/filtering.test.ts @@ -40,18 +40,18 @@ const data = { const visiblity = new Map( Object.entries({ - '.arr[0]': 'frontend', - '.arr[1]': 'backend', - '.arr[2]': 'secret', + '.arr.0': 'frontend', + '.arr.1': 'backend', + '.arr.2': 'secret', '.obj.f': 'frontend', '.obj.b': 'backend', '.obj.b.s': 'secret', - '.objArr[0].f': 'frontend', - '.objArr[0].b': 'backend', - '.objArr[0].s': 'secret', - '.objArr[1].f': 'frontend', - '.objArr[1].b': 'backend', - '.objArr[1].s': 'secret', + '.objArr.0.f': 'frontend', + '.objArr.0.b': 'backend', + '.objArr.0.s': 'secret', + '.objArr.1.f': 'frontend', + '.objArr.1.b': 'backend', + '.objArr.1.s': 'secret', '.arrF': 'frontend', '.arrB': 'backend', '.arrS': 'secret', diff --git a/packages/config-loader/src/lib/schema/filtering.ts b/packages/config-loader/src/lib/schema/filtering.ts index 74e75f465d..10a97f9a7f 100644 --- a/packages/config-loader/src/lib/schema/filtering.ts +++ b/packages/config-loader/src/lib/schema/filtering.ts @@ -49,7 +49,7 @@ export function filterByVisibility( const arr = new Array(); for (const [index, value] of jsonVal.entries()) { - const out = transform(value, `${path}[${index}]`); + const out = transform(value, `${path}.${index}`); if (out !== undefined) { arr.push(out); } From feb9905a377a5d441c7b7568010b618b8b5e5a37 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 15 Nov 2020 21:30:49 +0100 Subject: [PATCH 221/252] plugins: add config schema needed by frontend plugins --- plugins/auth-backend/package.json | 35 +------------------- plugins/cost-insights/config.d.ts | 52 ++++++++++++++++++++++++++++++ plugins/cost-insights/package.json | 6 ++-- plugins/lighthouse/package.json | 18 ++++++++++- plugins/rollbar/package.json | 18 ++++++++++- plugins/sentry/package.json | 18 ++++++++++- plugins/techdocs/package.json | 18 ++++++++++- plugins/user-settings/package.json | 21 +++++++++++- 8 files changed, 145 insertions(+), 41 deletions(-) create mode 100644 plugins/cost-insights/config.d.ts diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 2bc00f56a2..af3d056b28 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -66,38 +66,5 @@ "files": [ "dist", "migrations" - ], - "configSchema": { - "$schema": "https://backstage.io/schema/config-v1", - "title": "@backstage/plugin-auth-backend", - "type": "object", - "properties": { - "auth": { - "type": "object", - "properties": { - "providers": { - "type": "object", - "properties": { - "google": { - "type": "object", - "visibility": "frontend", - "additionalProperties": { - "type": "object", - "properties": { - "clientId": { - "type": "string" - }, - "clientSecret": { - "type": "string", - "visibility": "secret" - } - } - } - } - } - } - } - } - } - } + ] } diff --git a/plugins/cost-insights/config.d.ts b/plugins/cost-insights/config.d.ts new file mode 100644 index 0000000000..0f147c2cfe --- /dev/null +++ b/plugins/cost-insights/config.d.ts @@ -0,0 +1,52 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +interface Config { + costInsights: { + /** + * @visibility frontend + */ + engineerCost: number; + + products: { + [kind: string]: { + /** + * @visibility frontend + */ + name: string; + + /** + * @visibility frontend + */ + icon: 'compute' | 'data' | 'database' | 'storage' | 'search' | 'ml'; + }; + }; + + metrics?: { + [kind: string]: { + /** + * @visibility frontend + */ + name: string; + + /** + * @visibility frontend + */ + default?: boolean; + }; + }; + }; +} diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index 39159c03c4..1d7850482c 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -61,6 +61,8 @@ "msw": "^0.21.2" }, "files": [ - "dist" - ] + "dist", + "config.d.ts" + ], + "configSchema": "config.d.ts" } diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index 9389c84856..53bfc0e78b 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -52,5 +52,21 @@ }, "files": [ "dist" - ] + ], + "configSchema": { + "$schema": "https://backstage.io/schema/config-v1", + "title": "@backstage/lighthouse", + "type": "object", + "properties": { + "lighthouse": { + "type": "object", + "properties": { + "baseUrl": { + "type": "string", + "visibility": "frontend" + } + } + } + } + } } diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 722fe35fb6..18e4de3024 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -52,5 +52,21 @@ }, "files": [ "dist" - ] + ], + "configSchema": { + "$schema": "https://backstage.io/schema/config-v1", + "title": "@backstage/rollbar", + "type": "object", + "properties": { + "rollbar": { + "type": "object", + "properties": { + "organization": { + "type": "string", + "visibility": "frontend" + } + } + } + } + } } diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 7941df5dfd..2757c02db9 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -49,5 +49,21 @@ }, "files": [ "dist" - ] + ], + "configSchema": { + "$schema": "https://backstage.io/schema/config-v1", + "title": "@backstage/sentry", + "type": "object", + "properties": { + "sentry": { + "type": "object", + "properties": { + "organization": { + "type": "string", + "visibility": "frontend" + } + } + } + } + } } diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 88a38549ad..a513b7aa8d 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -53,5 +53,21 @@ }, "files": [ "dist" - ] + ], + "configSchema": { + "$schema": "https://backstage.io/schema/config-v1", + "title": "@backstage/techdocs", + "type": "object", + "properties": { + "techdocs": { + "type": "object", + "properties": { + "requestUrl": { + "type": "string", + "visibility": "frontend" + } + } + } + } + } } diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index a48c99cbc6..f82a6b2b6f 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -45,5 +45,24 @@ }, "files": [ "dist" - ] + ], + "configSchema": { + "$schema": "https://backstage.io/schema/config-v1", + "title": "@backstage/user-settings", + "type": "object", + "properties": { + "auth": { + "type": "object", + "properties": { + "providers": { + "type": "object", + "additionalProperties": { + "type": "object", + "visibility": "frontend" + } + } + } + } + } + } } From b8eba8219ede8797aa0a75214be0b8f8b3182788 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 15 Nov 2020 21:40:50 +0100 Subject: [PATCH 222/252] config-loader: enable validation of required props in d.ts definitions + fix issues --- app-config.yaml | 2 +- packages/backend-common/config.d.ts | 2 +- packages/config-loader/src/lib/schema/collect.ts | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app-config.yaml b/app-config.yaml index 3d0d093536..a331d72d7c 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -270,7 +270,7 @@ costInsights: homepage: clocks: - label: UTC - timzone: UTC + timezone: UTC - label: NYC timezone: 'America/New_York' - label: STO diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts index 64e883aa26..b6beff12f7 100644 --- a/packages/backend-common/config.d.ts +++ b/packages/backend-common/config.d.ts @@ -15,7 +15,7 @@ */ export interface Config { - frontend: { + app: { baseUrl: string; // defined in core, but repeated here without doc }; diff --git a/packages/config-loader/src/lib/schema/collect.ts b/packages/config-loader/src/lib/schema/collect.ts index dd0776213c..11a7ce4ec2 100644 --- a/packages/config-loader/src/lib/schema/collect.ts +++ b/packages/config-loader/src/lib/schema/collect.ts @@ -150,7 +150,10 @@ function compileTsSchemas(currentDir: string, paths: string[]) { // All schemas should export a `Config` symbol 'Config', // This enables usage of @visibility is doc comments - { validationKeywords: ['visibility'] }, + { + required: true, + validationKeywords: ['visibility'], + }, [schemaFile], ) as JsonObject | null; From 13c999c513d1ff4a32841b36f94077473ba4d175 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 15 Nov 2020 21:41:32 +0100 Subject: [PATCH 223/252] cli: added config:validate --- packages/cli/src/commands/config/validate.ts | 22 ++++++++++++++++++++ packages/cli/src/commands/index.ts | 8 +++++++ 2 files changed, 30 insertions(+) create mode 100644 packages/cli/src/commands/config/validate.ts diff --git a/packages/cli/src/commands/config/validate.ts b/packages/cli/src/commands/config/validate.ts new file mode 100644 index 0000000000..229f9e07e9 --- /dev/null +++ b/packages/cli/src/commands/config/validate.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Command } from 'commander'; +import { loadCliConfig } from '../../lib/config'; + +export default async (cmd: Command) => { + await loadCliConfig(cmd.config); +}; diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index bcbde20d84..304b5bae8f 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -146,6 +146,14 @@ export function registerCommands(program: CommanderStatic) { .description('Print the app configuration for the current package') .action(lazy(() => import('./config/print').then(m => m.default))); + program + .command('config:validate') + .option(...configOption) + .description( + 'Validate that the given configuration loads and matches schema', + ) + .action(lazy(() => import('./config/validate').then(m => m.default))); + program .command('prepack') .description('Prepares a package for packaging before publishing') From acb2f2c32e7adafefe0cfb66a983f6984c22936c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 15 Nov 2020 21:42:17 +0100 Subject: [PATCH 224/252] github/workflows: validate config in CI --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75c6a13cd2..00ed9ce714 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,6 +72,9 @@ jobs: - name: prettier run: yarn prettier:check + - name: validate config + run: yarn backstage-cli config:validate + - name: lint run: yarn lerna -- run lint --since origin/master From 1722cb53c281001e5aa96f631b14a6c0582b4c4f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 15 Nov 2020 21:56:40 +0100 Subject: [PATCH 225/252] changesets: added config schema changesets --- .changeset/add-config-schema-declarations.md | 13 ++++++++++ .changeset/add-config-schema-support.md | 27 ++++++++++++++++++++ .changeset/app-backend-config.md | 7 +++++ 3 files changed, 47 insertions(+) create mode 100644 .changeset/add-config-schema-declarations.md create mode 100644 .changeset/add-config-schema-support.md create mode 100644 .changeset/app-backend-config.md diff --git a/.changeset/add-config-schema-declarations.md b/.changeset/add-config-schema-declarations.md new file mode 100644 index 0000000000..2f52a2ad3f --- /dev/null +++ b/.changeset/add-config-schema-declarations.md @@ -0,0 +1,13 @@ +--- +'@backstage/backend-common': patch +'@backstage/cli': patch +'@backstage/core': patch +'@backstage/plugin-cost-insights': patch +'@backstage/plugin-lighthouse': patch +'@backstage/plugin-rollbar': patch +'@backstage/plugin-sentry': patch +'@backstage/plugin-techdocs': patch +'@backstage/plugin-user-settings': patch +--- + +Added configuration schema diff --git a/.changeset/add-config-schema-support.md b/.changeset/add-config-schema-support.md new file mode 100644 index 0000000000..fd20426b30 --- /dev/null +++ b/.changeset/add-config-schema-support.md @@ -0,0 +1,27 @@ +--- +'@backstage/backend-common': minor +'@backstage/cli': minor +'@backstage/config-loader': minor +--- + +Added support for loading and validating configuration schema, as well as declaring config visibility through schema. + +The new `loadConfigSchema` function exported by `@backstage/config-loader` allows for the collection and merging of configuration schemas from all nearby dependencies of the project. + +Configuration schema is declared using the following JSONSchema meta schema, which is based on draft07: https://backstage.io/schema/config-v1. The only difference to the draft07 schema is the custom `visibility` keyword, which is used to indicate whether the given config value should be visible in the frontend or not. The possible values are `frontend`, `backend`, `secret`, where `backend` is the default. A visibility of `secret` has the same scope at runtime, but it will be treated with more care in certain contexts, and defining both `frontend` and `secret` for the same value in two different schemas will result in an error during schema merging. + +Packages that wish to contribute configuration schema should declare it in a root "configSchema" field in `package.json`. The field can either contain an inlined JSON schema, or a relative path to a schema file. Schema files can be declared in either `.json` or `.d.ts`. + +TypeScript configuration schema files should export a single `Config` type, for example: + +```ts +export interface Config { + app: { + /** + * Frontend root URL + * @visibility frontend + */ + baseUrl: string; + }; +} +``` diff --git a/.changeset/app-backend-config.md b/.changeset/app-backend-config.md new file mode 100644 index 0000000000..9df47ef1b8 --- /dev/null +++ b/.changeset/app-backend-config.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-app-backend': minor +--- + +Use new config schema support to automatically inject config with frontend visibility, in addition to the existing env schema injection. + +This removes the confusing behavior where configuration was only injected into the app att build time. Any runtime configuration (except for environment config) in the backend used to only apply to the backend itself, and not be injected into the frontend. From 12f601f830da5d49396b916a0f9baf49d5a2919c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 15 Nov 2020 21:58:12 +0100 Subject: [PATCH 226/252] config-loader: fix types dependencies --- packages/config-loader/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 9abd3c754e..98583cf833 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -32,8 +32,6 @@ "dependencies": { "@backstage/cli-common": "^0.1.1", "@backstage/config": "^0.1.1", - "@types/json-schema": "^7.0.6", - "@types/json-schema-merge-allof": "^0.6.0", "ajv": "^6.12.5", "fs-extra": "^9.0.0", "json-schema": "^0.2.5", @@ -44,6 +42,8 @@ }, "devDependencies": { "@types/jest": "^26.0.7", + "@types/json-schema": "^7.0.6", + "@types/json-schema-merge-allof": "^0.6.0", "@types/mock-fs": "^4.10.0", "@types/node": "^12.0.0", "@types/yup": "^0.29.8", From 9f6f39ce5f4c4688e0dd0ab7fd90a09f1dbb870d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 15 Nov 2020 23:02:59 +0100 Subject: [PATCH 227/252] backend-common: integration configs aren't required --- packages/backend-common/config.d.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts index b6beff12f7..9e8fb7ee35 100644 --- a/packages/backend-common/config.d.ts +++ b/packages/backend-common/config.d.ts @@ -84,9 +84,9 @@ export interface Config { }; /** Configuration for integrations towards various external repository provider systems */ - integrations: { + integrations?: { /** Integration configuration for Azure */ - azure: { + azure?: { /** The hostname of the given Azure instance */ host: string; /** @@ -97,7 +97,7 @@ export interface Config { }[]; /** Integration configuration for BitBucket */ - bitbucket: { + bitbucket?: { /** The hostname of the given Bitbucket instance */ host: string; /** @@ -120,7 +120,7 @@ export interface Config { }[]; /** Integration configuration for GitHub */ - github: { + github?: { /** The hostname of the given GitHub instance */ host: string; /** @@ -135,7 +135,7 @@ export interface Config { }[]; /** Integration configuration for GitLab */ - gitlab: { + gitlab?: { /** The hostname of the given GitLab instance */ host: string; /** From 82043bfe8bcaedd4717b42767eef35367078a69d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 15 Nov 2020 23:59:15 +0100 Subject: [PATCH 228/252] docs: add docs for configuration schema --- .github/styles/vocab.txt | 1 + docs/conf/defining.md | 103 +++++++++++++++++++++++++++++++++++++-- docs/conf/index.md | 18 ++++++- docs/conf/writing.md | 10 ++-- 4 files changed, 118 insertions(+), 14 deletions(-) diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt index 3c5fa34adc..08e9883560 100644 --- a/.github/styles/vocab.txt +++ b/.github/styles/vocab.txt @@ -76,6 +76,7 @@ http https img incentivised +inlined inlinehilite interop javascript diff --git a/docs/conf/defining.md b/docs/conf/defining.md index bea03e4e44..83df8c3297 100644 --- a/docs/conf/defining.md +++ b/docs/conf/defining.md @@ -4,16 +4,109 @@ title: Defining Configuration for your Plugin description: Documentation on Defining Configuration for your Plugin --- -There is currently no tooling support or helpers for defining plugin -configuration. But it's on the roadmap. +Configuration in Backstage is organized via a configuration schema, which in +turn is defined using a super set of +[JSON Schema Draft-07](https://json-schema.org/specification-links.html#draft-7). +Each plugin or package within a Backstage app can contribute to the schema, +which during validation is stitched together into a single schema. -Meanwhile, document the config values that you are reading in your plugin -README. +## Schema Collection and Definition -## Format +Schemas are collected from all packages and dependencies in each repo that are a +part of the Backstage ecosystem, including transitive dependencies. The current +definition of "part of the ecosystem" is that a package has at least one +dependency in the `@backstage` namespace, but this is subject to change. + +Each package is search for schema at a single point of entry, a top-level +`"configSchema"` field in `package.json`. The field can either contain an +inlined JSON schema, or a relative path to a schema file. Supported schema file +formats are `.json` or `.d.ts`. + +> When using a schema file, be sure to include the file in your `package.json` > +> `"files"` field as well! + +TypeScript configuration schema files should export a single `Config` type, for +example: + +```ts +export interface Config { + app: { + /** + * Frontend root URL + * @visibility frontend + */ + baseUrl: string; + }; +} +``` + +Separate `.json` schema files can use a top-level +`"$schema": "https://backstage.io/schema/config-v1"` declaration in order to +receive schema validation and autocompletion. For example: + +```json +{ + "$schema": "https://backstage.io/schema/config-v1", + "type": "object", + "properties": { + "app": { + "type": "object", + "properties": { + "baseUrl": { + "type": "string", + "description": "Frontend root URL", + "visibility": "frontend" + } + }, + "required": ["baseUrl"] + }, + "required": ["app"] + } +} +``` + +## Visibility + +The `https://backstage.io/schema/config-v1` meta schema is a super set of JSON +Schema Draft 07. The single addition is a custom `visibility` keyword, which is +used to indicate whether the given config value should be visible in the +frontend or not. The possible values are `frontend`, `backend`, `secret`, where +`backend` is the default. A visibility of `secret` has the same scope at +runtime, but it will be treated with more care in certain contexts, and defining +both `frontend` and `secret` for the same value in two different schemas will +result in an error during schema merging. + +The visibility only applies to the direct parent of where the keyword is placed +in the schema. For example, if you set the visibility to `frontend` for a subset +of the schema with `type: "object"`, but none of the descendants, only an empty +object will be available in the frontend. The full ancestry does not need to +have correctly defined visibilities however, so it is enough to only for example +declare the visibility of a leaf node of `type: "string"`. + +## Validation + +Schema can be validated using the `backstage-cli config:validate` command. If +you want to validate anything else than the default `app-config.yaml`, be sure +to pass in all of the configuration files as `--config ` options as well. + +To validate and examine the frontend configuration, use can use the +`backstage-cli config:print --frontend` command. Just like for validation you +may need to pass in all files using one or multiple `--config ` options. + +## Guidelines + +> Limit static configuration. The first question to ask is whether a particular +> option actually needs to be static configuration, or if it might just as well +> be a TypeScript API. In general options that you want to be able to change for +> different deployment environments should be static configuration, while it +> should otherwise be avoided. When defining configuration for your plugin, keep keys camelCased and stick to existing casing conventions such as `baseUrl`. It is also usually best to prefer objects over arrays, as it makes it possible to override individual values using separate files or environment variables. + +Avoid creating new top-level fields as much as possible. Either place your +configuration within an existing known top-level block, or create a single new +one using e.g. the name of the product that the plugin integrates. diff --git a/docs/conf/index.md b/docs/conf/index.md index 5e4639cc99..96fb40087d 100644 --- a/docs/conf/index.md +++ b/docs/conf/index.md @@ -33,6 +33,20 @@ values that are common between the two only need to be defined once. Such as the For more details, see [Writing Configuration](./writing.md). +## Configuration Schema + +The configuration is validated using a JSON Schema definitions. Each plugin and +package can provide pieces of the configuration schema, which is stitched +together to form a complete schema during validation. The configuration schema +is also used to select what configuration is available in the frontend using a +custom `visibility` keyword, as configuration is by default only available in +the backend. + +You can validate your configuration against the schema using +`backstage-cli config:validate`, and define schema for your own plugin either +using JSON Schema or TypeScript. For more information, see +[Defining Configuration](./defining.md). + ## Reading Configuration As a plugin developer, you likely end up wanting to define configuration that @@ -49,5 +63,5 @@ More details are provided in dedicated sections of the documentation. plugin. - [Writing Configuration](./writing.md): How to provide configuration for your Backstage deployment. -- [Defining Configuration](./defining.md): How to define configuration for users - of your plugin. +- [Defining Configuration](./defining.md): How to define configuration schema + for users of your plugin or package. diff --git a/docs/conf/writing.md b/docs/conf/writing.md index 6b60c015ae..f28dd5b2d3 100644 --- a/docs/conf/writing.md +++ b/docs/conf/writing.md @@ -97,10 +97,10 @@ order: - If no config flags are provided, `app-config.local.yaml` has higher priority than `app-config.yaml`. -## Secrets +## Secrets and Dynamic Data -Secrets are supported via special secret keys that are prefixed with `$`, which -in turn provide a number of different ways to read in secrets. To load a +Secrets are supported via special data loading keys that are prefixed with `$`, +which in turn provide a number of different ways to read in secrets. To load a configuration value as a secret, supply an object with one of the special secret keys, for example `$env` or `$file`. A full list of supported secret keys can be found below. For example, the following will read the config key @@ -117,10 +117,6 @@ will return the value of the environment variable `MY_SECRET_KEY` when the backend started up. All secrets are loaded at startup, so changing the contents of secret files or environment variables will not be reflected at runtime. -Note that secrets will never be included in the frontend bundle or development -builds. When loading configuration you have to explicitly enable reading of -secrets, which is only done for the backend configuration. - As hinted at, secrets can be loaded from a bunch of different sources, and can be extended with more. Below is a list of the currently supported methods for loading secrets. From e2fc08cb15a3e449563d6777a08e033c468fa8ce Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 16 Nov 2020 00:35:09 +0100 Subject: [PATCH 229/252] app-backend: fix tests --- plugins/app-backend/src/lib/config.test.ts | 45 ++++++++++++++----- plugins/app-backend/src/lib/config.ts | 7 ++- .../app-backend/src/service/router.test.ts | 5 ++- 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/plugins/app-backend/src/lib/config.test.ts b/plugins/app-backend/src/lib/config.test.ts index 475f72f3b1..95b34c8422 100644 --- a/plugins/app-backend/src/lib/config.test.ts +++ b/plugins/app-backend/src/lib/config.test.ts @@ -43,11 +43,23 @@ describe('injectConfig', () => { jest.resetAllMocks(); }); - it('should not inject without config', async () => { + it('should inject without config', async () => { + fsMock.readdir.mockResolvedValue(['main.js']); + readFileMock.mockImplementation( + async () => '"__APP_INJECTED_RUNTIME_CONFIG__"', + ); await injectConfig(baseOptions); - expect(fsMock.readdir).toHaveBeenCalledTimes(0); - expect(fsMock.readFile).toHaveBeenCalledTimes(0); - expect(fsMock.writeFile).toHaveBeenCalledTimes(0); + expect(fsMock.readdir).toHaveBeenCalledTimes(1); + expect(fsMock.readFile).toHaveBeenCalledTimes(1); + expect(fsMock.writeFile).toHaveBeenCalledTimes(1); + expect(fsMock.writeFile).toHaveBeenCalledWith( + resolvePath(MOCK_DIR, 'main.js'), + '/*__APP_INJECTED_CONFIG_MARKER__*/"[]"/*__INJECTED_END__*/', + 'utf8', + ); + + // eslint-disable-next-line no-eval + expect(JSON.parse(eval(fsMock.writeFile.mock.calls[0][1]))).toEqual([]); }); it('should find the correct file to inject', async () => { @@ -83,14 +95,19 @@ describe('injectConfig', () => { expect(fsMock.writeFile).toHaveBeenCalledTimes(1); expect(fsMock.writeFile).toHaveBeenCalledWith( resolvePath(MOCK_DIR, 'main.js'), - '/*__APP_INJECTED_CONFIG_MARKER__*/"{\\"x\\":0}"/*__INJECTED_END__*/', + '/*__APP_INJECTED_CONFIG_MARKER__*/"[{\\"data\\":{\\"x\\":0},\\"context\\":\\"test\\"}]"/*__INJECTED_END__*/', 'utf8', ); // eslint-disable-next-line no-eval - expect(JSON.parse(eval(fsMock.writeFile.mock.calls[0][1]))).toEqual({ - x: 0, - }); + expect(JSON.parse(eval(fsMock.writeFile.mock.calls[0][1]))).toEqual([ + { + data: { + x: 0, + }, + context: 'test', + }, + ]); }); it('should re-inject config', async () => { @@ -107,12 +124,14 @@ describe('injectConfig', () => { expect(fsMock.writeFile).toHaveBeenCalledTimes(1); expect(fsMock.writeFile).toHaveBeenCalledWith( resolvePath(MOCK_DIR, 'main.js'), - 'JSON.parse(/*__APP_INJECTED_CONFIG_MARKER__*/"{\\"x\\":0}"/*__INJECTED_END__*/)', + 'JSON.parse(/*__APP_INJECTED_CONFIG_MARKER__*/"[{\\"data\\":{\\"x\\":0},\\"context\\":\\"test\\"}]"/*__INJECTED_END__*/)', 'utf8', ); // eslint-disable-next-line no-eval - expect(eval(fsMock.writeFile.mock.calls[0][1])).toEqual({ x: 0 }); + expect(eval(fsMock.writeFile.mock.calls[0][1])).toEqual([ + { data: { x: 0 }, context: 'test' }, + ]); readFileMock.mockResolvedValue(fsMock.writeFile.mock.calls[0][1]); @@ -124,11 +143,13 @@ describe('injectConfig', () => { expect(fsMock.writeFile).toHaveBeenCalledTimes(2); expect(fsMock.writeFile).toHaveBeenLastCalledWith( resolvePath(MOCK_DIR, 'main.js'), - 'JSON.parse(/*__APP_INJECTED_CONFIG_MARKER__*/"{\\"x\\":1,\\"y\\":2}"/*__INJECTED_END__*/)', + 'JSON.parse(/*__APP_INJECTED_CONFIG_MARKER__*/"[{\\"data\\":{\\"x\\":1,\\"y\\":2},\\"context\\":\\"test\\"}]"/*__INJECTED_END__*/)', 'utf8', ); // eslint-disable-next-line no-eval - expect(eval(fsMock.writeFile.mock.calls[1][1])).toEqual({ x: 1, y: 2 }); + expect(eval(fsMock.writeFile.mock.calls[1][1])).toEqual([ + { data: { x: 1, y: 2 }, context: 'test' }, + ]); }); }); diff --git a/plugins/app-backend/src/lib/config.ts b/plugins/app-backend/src/lib/config.ts index 82b431c33f..f076967280 100644 --- a/plugins/app-backend/src/lib/config.ts +++ b/plugins/app-backend/src/lib/config.ts @@ -28,8 +28,7 @@ type InjectOptions = { }; /** - * Injects config from APP_CONFIG_ env vars, replacing existing - * injected config if it has already been injected. + * Injects configs into the app bundle, replacing any existing injected config. */ export async function injectConfig(options: InjectOptions) { const { staticDir, logger, appConfigs } = options; @@ -73,6 +72,10 @@ type ReadOptions = { config: Config; }; +/** + * Read config from environment and process the backend config using the + * schema that is embedded in the frontend build. + */ export async function readConfigs(options: ReadOptions): Promise { const { env, appDistDir, config } = options; diff --git a/plugins/app-backend/src/service/router.test.ts b/plugins/app-backend/src/service/router.test.ts index b0cc3c59f7..35dd9287a6 100644 --- a/plugins/app-backend/src/service/router.test.ts +++ b/plugins/app-backend/src/service/router.test.ts @@ -22,7 +22,10 @@ import Router from 'express-promise-router'; import request from 'supertest'; import { createRouter } from './router'; -jest.mock('../lib/config', () => ({ injectEnvConfig: jest.fn() })); +jest.mock('../lib/config', () => ({ + injectConfig: jest.fn(), + readConfigs: jest.fn(), +})); global.__non_webpack_require__ = { /* eslint-disable-next-line no-restricted-syntax */ From 90fb5c81d06779e9a63bdaa1e59da96f76e6c75a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 16 Nov 2020 01:05:54 +0100 Subject: [PATCH 230/252] cli: update diff to handle package with configSchema files --- packages/cli/src/lib/diff/handlers.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/lib/diff/handlers.ts b/packages/cli/src/lib/diff/handlers.ts index 40e3d4c587..a210986b2a 100644 --- a/packages/cli/src/lib/diff/handlers.ts +++ b/packages/cli/src/lib/diff/handlers.ts @@ -61,7 +61,7 @@ class PackageJsonHandler { await this.syncField('main:src'); } await this.syncField('types'); - await this.syncField('files'); + await this.syncFiles(); await this.syncScripts(); await this.syncPublishConfig(); await this.syncDependencies('dependencies'); @@ -105,6 +105,15 @@ class PackageJsonHandler { } } + private async syncFiles() { + if (typeof this.targetPkg.configSchema === 'string') { + const files = [...this.pkg.files, this.targetPkg.configSchema]; + await this.syncField('files', { files }); + } else { + await this.syncField('files'); + } + } + private async syncScripts() { const pkgScripts = this.pkg.scripts; const targetScripts = (this.targetPkg.scripts = From 9621ef3a9617277cdbfe97ebd06245d9b51975ef Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 16 Nov 2020 10:28:23 +0100 Subject: [PATCH 231/252] techdocs: add storageUrl to config schema Co-authored-by: Sebastian Qvarfordt --- plugins/techdocs/package.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index a513b7aa8d..a2b1349d53 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -65,8 +65,14 @@ "requestUrl": { "type": "string", "visibility": "frontend" + }, + "storageUrl": { + "type": "string" } - } + }, + "required": [ + "requestUrl" + ] } } } From 5357723ea456332b1dfb32f6b2adbe445511bd9e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 16 Nov 2020 14:56:08 +0100 Subject: [PATCH 232/252] config-loader: fix .d.ts schema path handling on Windows --- .../config-loader/src/lib/schema/collect.ts | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/config-loader/src/lib/schema/collect.ts b/packages/config-loader/src/lib/schema/collect.ts index 11a7ce4ec2..9d8e6e73f9 100644 --- a/packages/config-loader/src/lib/schema/collect.ts +++ b/packages/config-loader/src/lib/schema/collect.ts @@ -19,6 +19,7 @@ import { resolve as resolvePath, relative as relativePath, dirname, + sep, } from 'path'; import { ConfigSchemaPackageEntry } from './types'; import { getProgramFromFiles, generateSchema } from 'typescript-json-schema'; @@ -43,7 +44,7 @@ export async function collectConfigSchemas( const visitedPackages = new Set(); const schemas = Array(); const tsSchemaPaths = Array(); - const currentDir = process.cwd(); + const currentDir = await fs.realpath(process.cwd()); async function processItem({ name, parentPath }: Item) { // Ensures that we only process each package once. We don't bother with @@ -94,7 +95,12 @@ export async function collectConfigSchemas( ); } if (isDts) { - tsSchemaPaths.push(resolvePath(dirname(pkgPath), pkg.configSchema)); + tsSchemaPaths.push( + relativePath( + currentDir, + resolvePath(dirname(pkgPath), pkg.configSchema), + ), + ); } else { const path = resolvePath(dirname(pkgPath), pkg.configSchema); const value = await fs.readJson(path); @@ -118,7 +124,7 @@ export async function collectConfigSchemas( await Promise.all(packageNames.map(name => processItem({ name }))); - const tsSchemas = compileTsSchemas(currentDir, tsSchemaPaths); + const tsSchemas = compileTsSchemas(tsSchemaPaths); return schemas.concat(tsSchemas); } @@ -126,7 +132,7 @@ export async function collectConfigSchemas( // This handles the support of TypeScript .d.ts config schema declarations. // We collect all typescript schema definition and compile them all in one go. // This is much faster than compiling them separately. -function compileTsSchemas(currentDir: string, paths: string[]) { +function compileTsSchemas(paths: string[]) { if (paths.length === 0) { return []; } @@ -144,7 +150,7 @@ function compileTsSchemas(currentDir: string, paths: string[]) { types: [], }); - const tsSchemas = paths.map(schemaFile => { + const tsSchemas = paths.map(path => { const value = generateSchema( program, // All schemas should export a `Config` symbol @@ -154,11 +160,9 @@ function compileTsSchemas(currentDir: string, paths: string[]) { required: true, validationKeywords: ['visibility'], }, - [schemaFile], + [path.split(sep).join('/')], // Unix paths are expected for all OSes here ) as JsonObject | null; - const path = relativePath(currentDir, schemaFile); - if (!value) { throw new Error(`Invalid schema in ${path}, missing Config export`); } From 72116243287db45f35f3b94cd655632a0902fe35 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 16 Nov 2020 19:20:16 +0100 Subject: [PATCH 233/252] config-loader: added tests for schema collection --- .../src/lib/schema/collect.test.ts | 229 ++++++++++++++++++ .../config-loader/src/lib/schema/collect.ts | 29 ++- 2 files changed, 247 insertions(+), 11 deletions(-) create mode 100644 packages/config-loader/src/lib/schema/collect.test.ts diff --git a/packages/config-loader/src/lib/schema/collect.test.ts b/packages/config-loader/src/lib/schema/collect.test.ts new file mode 100644 index 0000000000..7aca4f7c0c --- /dev/null +++ b/packages/config-loader/src/lib/schema/collect.test.ts @@ -0,0 +1,229 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import mockFs from 'mock-fs'; +import { collectConfigSchemas } from './collect'; + +const mockSchema = { + type: 'object', + properties: { + key: { + type: 'string', + visibility: 'frontend', + }, + }, +}; + +describe('collectConfigSchemas', () => { + afterEach(() => { + mockFs.restore(); + }); + + it('should not find any schemas without packages', async () => { + mockFs({ + 'lerna.json': JSON.stringify({ + packages: ['packages/*'], + }), + }); + + await expect(collectConfigSchemas([])).resolves.toEqual([]); + }); + + it('should find schema in a local package', async () => { + mockFs({ + node_modules: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + configSchema: mockSchema, + }), + }, + }, + }); + + await expect(collectConfigSchemas(['a'])).resolves.toEqual([ + { + path: 'node_modules/a/package.json', + value: mockSchema, + }, + ]); + }); + + it('should find schema in transitive dependencies', async () => { + mockFs({ + node_modules: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + dependencies: { b: '0.0.0', '@backstage/mock': '0.0.0' }, + }), + }, + b: { + 'package.json': JSON.stringify({ + name: 'b', + dependencies: { + c1: '0.0.0', + c2: '0.0.0', + '@backstage/mock': '0.0.0', + }, + configSchema: { ...mockSchema, title: 'b' }, + }), + }, + c1: { + 'package.json': JSON.stringify({ + name: 'c1', + dependencies: { d1: '0.0.0' }, + configSchema: { ...mockSchema, title: 'c1' }, + }), + }, + c2: { + 'package.json': JSON.stringify({ + name: 'c2', + dependencies: { d2: '0.0.0' }, + }), + }, + d1: { + 'package.json': JSON.stringify({ + name: 'd1', + dependencies: {}, + configSchema: { ...mockSchema, title: 'd1' }, + }), + }, + d2: { + 'package.json': JSON.stringify({ + name: 'd2', + dependencies: {}, + configSchema: { ...mockSchema, title: 'd2' }, + }), + }, + }, + }); + + await expect(collectConfigSchemas(['a'])).resolves.toEqual([ + { + path: 'node_modules/b/package.json', + value: { ...mockSchema, title: 'b' }, + }, + { + path: 'node_modules/c1/package.json', + value: { ...mockSchema, title: 'c1' }, + }, + { + path: 'node_modules/d1/package.json', + value: { ...mockSchema, title: 'd1' }, + }, + ]); + }); + + it('should schema of different types', async () => { + mockFs({ + node_modules: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + configSchema: { ...mockSchema, title: 'inline' }, + }), + }, + b: { + 'package.json': JSON.stringify({ + name: 'b', + configSchema: 'schema.json', + }), + 'schema.json': JSON.stringify({ ...mockSchema, title: 'external' }), + }, + c: { + 'package.json': JSON.stringify({ + name: 'c', + configSchema: 'schema.d.ts', + }), + 'schema.d.ts': `export interface Config { + /** @visibility secret */ + tsKey: string + }`, + }, + }, + // TypeScript compilation needs to load some real files inside the typescript dir + '../../node_modules/typescript': (mockFs as any).load( + '../../node_modules/typescript', + ), + }); + + await expect(collectConfigSchemas(['a', 'b', 'c'])).resolves.toEqual([ + { + path: 'node_modules/a/package.json', + value: { ...mockSchema, title: 'inline' }, + }, + { + path: 'node_modules/b/schema.json', + value: { ...mockSchema, title: 'external' }, + }, + { + path: 'node_modules/c/schema.d.ts', + value: { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + properties: { + tsKey: { + type: 'string', + visibility: 'secret', + }, + }, + required: ['tsKey'], + }, + }, + ]); + }); + + it('should not allow unknown schema file types', async () => { + mockFs({ + node_modules: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + configSchema: 'schema.yaml', + }), + 'schema.yaml': mockSchema, + }, + }, + }); + + await expect(collectConfigSchemas(['a'])).rejects.toThrow( + 'Config schema files must be .json or .d.ts, got schema.yaml', + ); + }); + + it('should reject typescript config declaration without a Config type', async () => { + mockFs({ + node_modules: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + configSchema: 'schema.d.ts', + }), + 'schema.d.ts': `export interface NotConfig {}`, + }, + }, + // TypeScript compilation needs to load some real files inside the typescript dir + '../../node_modules/typescript': (mockFs as any).load( + '../../node_modules/typescript', + ), + }); + + await expect(collectConfigSchemas(['a'])).rejects.toThrow( + 'Invalid schema in node_modules/a/schema.d.ts, missing Config export', + ); + }); +}); diff --git a/packages/config-loader/src/lib/schema/collect.ts b/packages/config-loader/src/lib/schema/collect.ts index 9d8e6e73f9..0e53562875 100644 --- a/packages/config-loader/src/lib/schema/collect.ts +++ b/packages/config-loader/src/lib/schema/collect.ts @@ -151,17 +151,24 @@ function compileTsSchemas(paths: string[]) { }); const tsSchemas = paths.map(path => { - const value = generateSchema( - program, - // All schemas should export a `Config` symbol - 'Config', - // This enables usage of @visibility is doc comments - { - required: true, - validationKeywords: ['visibility'], - }, - [path.split(sep).join('/')], // Unix paths are expected for all OSes here - ) as JsonObject | null; + let value; + try { + value = generateSchema( + program, + // All schemas should export a `Config` symbol + 'Config', + // This enables usage of @visibility is doc comments + { + required: true, + validationKeywords: ['visibility'], + }, + [path.split(sep).join('/')], // Unix paths are expected for all OSes here + ) as JsonObject | null; + } catch (error) { + if (error.message !== 'type Config not found') { + throw error; + } + } if (!value) { throw new Error(`Invalid schema in ${path}, missing Config export`); From eda9f91515b6024a79e273d24044f2f643ee8d40 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 16 Nov 2020 19:32:25 +0100 Subject: [PATCH 234/252] config-loader: added test for config schema load --- .../config-loader/src/lib/schema/load.test.ts | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 packages/config-loader/src/lib/schema/load.test.ts diff --git a/packages/config-loader/src/lib/schema/load.test.ts b/packages/config-loader/src/lib/schema/load.test.ts new file mode 100644 index 0000000000..a13f36be20 --- /dev/null +++ b/packages/config-loader/src/lib/schema/load.test.ts @@ -0,0 +1,99 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import mockFs from 'mock-fs'; +import { loadConfigSchema } from './load'; + +describe('loadConfigSchema', () => { + afterEach(() => { + mockFs.restore(); + }); + + it('should load schema from packages or data', async () => { + mockFs({ + node_modules: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + configSchema: { + type: 'object', + properties: { + key1: { type: 'string', visibility: 'frontend' }, + }, + }, + }), + }, + b: { + 'package.json': JSON.stringify({ + name: 'b', + configSchema: 'schema.json', + }), + 'schema.json': JSON.stringify({ + name: 'a', + configSchema: { + type: 'object', + properties: { + key2: { type: 'number' }, + }, + }, + }), + }, + }, + }); + + const schema = await loadConfigSchema({ + dependencies: ['a'], + }); + + const configs = [{ data: { key1: 'a', key2: 2 }, context: 'test' }]; + + expect(schema.process(configs)).toEqual(configs); + expect(schema.process(configs, { visiblity: ['frontend'] })).toEqual([ + { data: { key1: 'a' }, context: 'test' }, + ]); + expect( + schema.process(configs, { + visiblity: ['frontend'], + valueTransform: () => 'X', + }), + ).toEqual([{ data: { key1: 'X' }, context: 'test' }]); + expect( + schema.process(configs, { + valueTransform: () => 'X', + }), + ).toEqual([{ data: { key1: 'X', key2: 'X' }, context: 'test' }]); + + const serialized = schema.serialize(); + + const schema2 = await loadConfigSchema({ serialized }); + expect(schema2.process(configs, { visiblity: ['frontend'] })).toEqual([ + { data: { key1: 'a' }, context: 'test' }, + ]); + expect(() => + schema2.process([...configs, { data: { key1: 3 }, context: 'test2' }]), + ).toThrow( + 'Config validation failed, Config should be string { type=string } at .key1', + ); + + await expect( + loadConfigSchema({ + serialized: { ...serialized, backstageConfigSchemaVersion: 2 }, + }), + ).rejects.toThrow( + 'Serialized configuration schema is invalid or has an invalid version number', + ); + }); +}); From 39591369bacda81c315a6e3f01ce07af004f4692 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 16 Nov 2020 19:42:52 +0100 Subject: [PATCH 235/252] config-loader: add tests for schema compile and skip adding backend visibility paths --- .../src/lib/schema/compile.test.ts | 114 ++++++++++++++++++ .../config-loader/src/lib/schema/compile.ts | 2 +- 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 packages/config-loader/src/lib/schema/compile.test.ts diff --git a/packages/config-loader/src/lib/schema/compile.test.ts b/packages/config-loader/src/lib/schema/compile.test.ts new file mode 100644 index 0000000000..91e7aa687e --- /dev/null +++ b/packages/config-loader/src/lib/schema/compile.test.ts @@ -0,0 +1,114 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { compileConfigSchemas } from './compile'; + +describe('compileConfigSchemas', () => { + it('should merge schemas', () => { + const validate = compileConfigSchemas([ + { + path: 'a', + value: { type: 'object', properties: { a: { type: 'string' } } }, + }, + { + path: 'b', + value: { type: 'object', properties: { b: { type: 'number' } } }, + }, + ]); + expect(validate([{ data: { a: 1 }, context: 'test' }])).toEqual({ + errors: ['Config should be string { type=string } at .a'], + visibilityByPath: new Map(), + }); + expect(validate([{ data: { b: 'b' }, context: 'test' }])).toEqual({ + errors: ['Config should be number { type=number } at .b'], + visibilityByPath: new Map(), + }); + }); + + it('should discover visibilities', () => { + const validate = compileConfigSchemas([ + { + path: 'a1', + value: { + type: 'object', + properties: { + a: { type: 'string', visibility: 'frontend' }, + b: { type: 'string', visibility: 'backend' }, + c: { type: 'string' }, + d: { + type: 'array', + visibility: 'secret', + items: { type: 'string', visibility: 'frontend' }, + }, + }, + }, + }, + { + path: 'a2', + value: { + type: 'object', + properties: { + a: { type: 'string' }, + b: { type: 'string', visibility: 'secret' }, + c: { type: 'string', visibility: 'backend' }, + d: { + type: 'array', + visibility: 'secret', + items: { type: 'string' }, + }, + }, + }, + }, + ]); + expect( + validate([ + { data: { a: 'a', b: 'b', c: 'c', d: ['d'] }, context: 'test' }, + ]), + ).toEqual({ + visibilityByPath: new Map( + Object.entries({ + '.a': 'frontend', + '.b': 'secret', + '.d': 'secret', + '.d.0': 'frontend', + }), + ), + }); + }); + + it('should reject visiblity conflicts', () => { + expect(() => + compileConfigSchemas([ + { + path: 'a1', + value: { + type: 'object', + properties: { a: { type: 'string', visibility: 'frontend' } }, + }, + }, + { + path: 'a2', + value: { + type: 'object', + properties: { a: { type: 'string', visibility: 'secret' } }, + }, + }, + ]), + ).toThrow( + "Config schema visibility is both 'frontend' and 'secret' for properties/a/visibility", + ); + }); +}); diff --git a/packages/config-loader/src/lib/schema/compile.ts b/packages/config-loader/src/lib/schema/compile.ts index 93ab8e978f..f01a4f640a 100644 --- a/packages/config-loader/src/lib/schema/compile.ts +++ b/packages/config-loader/src/lib/schema/compile.ts @@ -55,7 +55,7 @@ export function compileConfigSchemas( if (!dataPath) { return false; } - if (visibility) { + if (visibility && visibility !== 'backend') { const normalizedPath = dataPath.replace( /\['?(.*?)'?\]/g, (_, segment) => `.${segment}`, From 0b97a89f55b9bc9174ab1475456593400a21f300 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 17 Nov 2020 18:25:55 +0100 Subject: [PATCH 236/252] address config schema review comments --- .changeset/add-config-schema-support.md | 6 ++-- .changeset/app-backend-config.md | 2 +- .github/styles/vocab.txt | 1 + .github/workflows/master.yml | 3 ++ docs/conf/defining.md | 30 +++++++++---------- docs/conf/index.md | 8 ++--- packages/backend-common/config.d.ts | 18 +++++------ .../config-loader/src/lib/schema/types.ts | 6 ++-- 8 files changed, 39 insertions(+), 35 deletions(-) diff --git a/.changeset/add-config-schema-support.md b/.changeset/add-config-schema-support.md index fd20426b30..300c75f651 100644 --- a/.changeset/add-config-schema-support.md +++ b/.changeset/add-config-schema-support.md @@ -4,13 +4,13 @@ '@backstage/config-loader': minor --- -Added support for loading and validating configuration schema, as well as declaring config visibility through schema. +Added support for loading and validating configuration schemas, as well as declaring config visibility through schemas. The new `loadConfigSchema` function exported by `@backstage/config-loader` allows for the collection and merging of configuration schemas from all nearby dependencies of the project. -Configuration schema is declared using the following JSONSchema meta schema, which is based on draft07: https://backstage.io/schema/config-v1. The only difference to the draft07 schema is the custom `visibility` keyword, which is used to indicate whether the given config value should be visible in the frontend or not. The possible values are `frontend`, `backend`, `secret`, where `backend` is the default. A visibility of `secret` has the same scope at runtime, but it will be treated with more care in certain contexts, and defining both `frontend` and `secret` for the same value in two different schemas will result in an error during schema merging. +A configuration schema is declared using the `https://backstage.io/schema/config-v1` JSON Schema meta schema, which is based on draft07. The only difference to the draft07 schema is the custom `visibility` keyword, which is used to indicate whether the given config value should be visible in the frontend or not. The possible values are `frontend`, `backend`, and `secret`, where `backend` is the default. A visibility of `secret` has the same scope at runtime, but it will be treated with more care in certain contexts, and defining both `frontend` and `secret` for the same value in two different schemas will result in an error during schema merging. -Packages that wish to contribute configuration schema should declare it in a root "configSchema" field in `package.json`. The field can either contain an inlined JSON schema, or a relative path to a schema file. Schema files can be declared in either `.json` or `.d.ts`. +Packages that wish to contribute configuration schema should declare it in a root `"configSchema"` field in `package.json`. The field can either contain an inlined JSON schema, or a relative path to a schema file. Schema files can be in either `.json` or `.d.ts` format. TypeScript configuration schema files should export a single `Config` type, for example: diff --git a/.changeset/app-backend-config.md b/.changeset/app-backend-config.md index 9df47ef1b8..39b0b956ad 100644 --- a/.changeset/app-backend-config.md +++ b/.changeset/app-backend-config.md @@ -4,4 +4,4 @@ Use new config schema support to automatically inject config with frontend visibility, in addition to the existing env schema injection. -This removes the confusing behavior where configuration was only injected into the app att build time. Any runtime configuration (except for environment config) in the backend used to only apply to the backend itself, and not be injected into the frontend. +This removes the confusing behavior where configuration was only injected into the app at build time. Any runtime configuration (except for environment config) in the backend used to only apply to the backend itself, and not be injected into the frontend. diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt index 08e9883560..740cf5b07f 100644 --- a/.github/styles/vocab.txt +++ b/.github/styles/vocab.txt @@ -177,6 +177,7 @@ src subkey superfences Superfences +superset talkdesk Talkdesk tasklist diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index f5029bf836..4c20194f32 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -47,6 +47,9 @@ jobs: run: yarn install --frozen-lockfile # End of yarn setup + - name: validate config + run: yarn backstage-cli config:validate + - name: lint run: yarn lerna -- run lint diff --git a/docs/conf/defining.md b/docs/conf/defining.md index 83df8c3297..d231320e93 100644 --- a/docs/conf/defining.md +++ b/docs/conf/defining.md @@ -5,7 +5,7 @@ description: Documentation on Defining Configuration for your Plugin --- Configuration in Backstage is organized via a configuration schema, which in -turn is defined using a super set of +turn is defined using a superset of [JSON Schema Draft-07](https://json-schema.org/specification-links.html#draft-7). Each plugin or package within a Backstage app can contribute to the schema, which during validation is stitched together into a single schema. @@ -17,13 +17,13 @@ part of the Backstage ecosystem, including transitive dependencies. The current definition of "part of the ecosystem" is that a package has at least one dependency in the `@backstage` namespace, but this is subject to change. -Each package is search for schema at a single point of entry, a top-level +Each package is searched for a schema at a single point of entry, a top-level `"configSchema"` field in `package.json`. The field can either contain an inlined JSON schema, or a relative path to a schema file. Supported schema file formats are `.json` or `.d.ts`. -> When using a schema file, be sure to include the file in your `package.json` > -> `"files"` field as well! +> When defining a schema file, be sure to include the file in your +> `package.json` > `"files"` field as well! TypeScript configuration schema files should export a single `Config` type, for example: @@ -67,11 +67,11 @@ receive schema validation and autocompletion. For example: ## Visibility -The `https://backstage.io/schema/config-v1` meta schema is a super set of JSON +The `https://backstage.io/schema/config-v1` meta schema is a superset of JSON Schema Draft 07. The single addition is a custom `visibility` keyword, which is used to indicate whether the given config value should be visible in the -frontend or not. The possible values are `frontend`, `backend`, `secret`, where -`backend` is the default. A visibility of `secret` has the same scope at +frontend or not. The possible values are `frontend`, `backend`, and `secret`, +where `backend` is the default. A visibility of `secret` has the same scope at runtime, but it will be treated with more care in certain contexts, and defining both `frontend` and `secret` for the same value in two different schemas will result in an error during schema merging. @@ -85,24 +85,24 @@ declare the visibility of a leaf node of `type: "string"`. ## Validation -Schema can be validated using the `backstage-cli config:validate` command. If +Schemas can be validated using the `backstage-cli config:validate` command. If you want to validate anything else than the default `app-config.yaml`, be sure to pass in all of the configuration files as `--config ` options as well. -To validate and examine the frontend configuration, use can use the +To validate and examine the frontend configuration, use the `backstage-cli config:print --frontend` command. Just like for validation you may need to pass in all files using one or multiple `--config ` options. ## Guidelines -> Limit static configuration. The first question to ask is whether a particular -> option actually needs to be static configuration, or if it might just as well -> be a TypeScript API. In general options that you want to be able to change for -> different deployment environments should be static configuration, while it -> should otherwise be avoided. +> Make limited use of static configuration. The first question to ask is whether +> a particular option actually needs to be static configuration, or if it might +> just as well be a TypeScript API. In general, options that you want to be able +> to change for different deployment environments should be static +> configuration, while it should otherwise be avoided. When defining configuration for your plugin, keep keys camelCased and stick to -existing casing conventions such as `baseUrl`. +existing casing conventions such as `baseUrl` rather than `baseURL`. It is also usually best to prefer objects over arrays, as it makes it possible to override individual values using separate files or environment variables. diff --git a/docs/conf/index.md b/docs/conf/index.md index 96fb40087d..48da4eb74e 100644 --- a/docs/conf/index.md +++ b/docs/conf/index.md @@ -35,15 +35,15 @@ For more details, see [Writing Configuration](./writing.md). ## Configuration Schema -The configuration is validated using a JSON Schema definitions. Each plugin and -package can provide pieces of the configuration schema, which is stitched +The configuration is validated using JSON Schema definitions. Each plugin and +package can provide pieces of the configuration schema, which are stitched together to form a complete schema during validation. The configuration schema is also used to select what configuration is available in the frontend using a custom `visibility` keyword, as configuration is by default only available in the backend. You can validate your configuration against the schema using -`backstage-cli config:validate`, and define schema for your own plugin either +`backstage-cli config:validate`, and define a schema for your own plugin either using JSON Schema or TypeScript. For more information, see [Defining Configuration](./defining.md). @@ -63,5 +63,5 @@ More details are provided in dedicated sections of the documentation. plugin. - [Writing Configuration](./writing.md): How to provide configuration for your Backstage deployment. -- [Defining Configuration](./defining.md): How to define configuration schema +- [Defining Configuration](./defining.md): How to define a configuration schema for users of your plugin or package. diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts index 9e8fb7ee35..7ebcd01e8c 100644 --- a/packages/backend-common/config.d.ts +++ b/packages/backend-common/config.d.ts @@ -21,8 +21,8 @@ export interface Config { backend: { baseUrl: string; // defined in core, but repeated here without doc - /** Address that the backend should listen to. */ + /** Address that the backend should listen to. */ listen: | string | { @@ -86,7 +86,7 @@ export interface Config { /** Configuration for integrations towards various external repository provider systems */ integrations?: { /** Integration configuration for Azure */ - azure?: { + azure?: Array<{ /** The hostname of the given Azure instance */ host: string; /** @@ -94,10 +94,10 @@ export interface Config { * @visibility secret */ token?: string; - }[]; + }>; /** Integration configuration for BitBucket */ - bitbucket?: { + bitbucket?: Array<{ /** The hostname of the given Bitbucket instance */ host: string; /** @@ -117,10 +117,10 @@ export interface Config { * @visibility secret */ appPassword?: string; - }[]; + }>; /** Integration configuration for GitHub */ - github?: { + github?: Array<{ /** The hostname of the given GitHub instance */ host: string; /** @@ -132,10 +132,10 @@ export interface Config { apiBaseUrl?: string; /** The base url for GitHub raw resources, for example https://raw.githubusercontent.com */ rawBaseUrl?: string; - }[]; + }>; /** Integration configuration for GitLab */ - gitlab?: { + gitlab?: Array<{ /** The hostname of the given GitLab instance */ host: string; /** @@ -143,6 +143,6 @@ export interface Config { * @visibility secret */ token?: string; - }[]; + }>; }; } diff --git a/packages/config-loader/src/lib/schema/types.ts b/packages/config-loader/src/lib/schema/types.ts index c82036bbf4..7705242b31 100644 --- a/packages/config-loader/src/lib/schema/types.ts +++ b/packages/config-loader/src/lib/schema/types.ts @@ -21,11 +21,11 @@ import { AppConfig, JsonObject } from '@backstage/config'; */ export type ConfigSchemaPackageEntry = { /** - * The configuration schema itself, as JSONSchema draft-07 + * The configuration schema itself. */ value: JsonObject; /** - * The path that the configuration schema was discovered at. + * The relative path that the configuration schema was discovered at. */ path: string; }; @@ -59,7 +59,7 @@ type ValidationResult = { */ errors?: ValidationError[]; /** - * The configuration visibilities the where discovered during validation. + * The configuration visibilities that were discovered during validation. * * The path in the key uses the form `////` */ From 80bf8c7f4fb97f2af0dbefce0fabb42980de50c8 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 17 Nov 2020 18:26:40 +0100 Subject: [PATCH 237/252] cli: rename config:validate to config:check --- .github/workflows/ci.yml | 2 +- .github/workflows/master.yml | 2 +- docs/conf/defining.md | 6 +++--- docs/conf/index.md | 2 +- packages/cli/src/commands/index.ts | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00ed9ce714..c35e2a88ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,7 +73,7 @@ jobs: run: yarn prettier:check - name: validate config - run: yarn backstage-cli config:validate + run: yarn backstage-cli config:check - name: lint run: yarn lerna -- run lint --since origin/master diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 4c20194f32..ca63e627e9 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -48,7 +48,7 @@ jobs: # End of yarn setup - name: validate config - run: yarn backstage-cli config:validate + run: yarn backstage-cli config:check - name: lint run: yarn lerna -- run lint diff --git a/docs/conf/defining.md b/docs/conf/defining.md index d231320e93..ead5c4ff67 100644 --- a/docs/conf/defining.md +++ b/docs/conf/defining.md @@ -85,9 +85,9 @@ declare the visibility of a leaf node of `type: "string"`. ## Validation -Schemas can be validated using the `backstage-cli config:validate` command. If -you want to validate anything else than the default `app-config.yaml`, be sure -to pass in all of the configuration files as `--config ` options as well. +Schemas can be validated using the `backstage-cli config:check` command. If you +want to validate anything else than the default `app-config.yaml`, be sure to +pass in all of the configuration files as `--config ` options as well. To validate and examine the frontend configuration, use the `backstage-cli config:print --frontend` command. Just like for validation you diff --git a/docs/conf/index.md b/docs/conf/index.md index 48da4eb74e..a6f1d1f6f7 100644 --- a/docs/conf/index.md +++ b/docs/conf/index.md @@ -43,7 +43,7 @@ custom `visibility` keyword, as configuration is by default only available in the backend. You can validate your configuration against the schema using -`backstage-cli config:validate`, and define a schema for your own plugin either +`backstage-cli config:check`, and define a schema for your own plugin either using JSON Schema or TypeScript. For more information, see [Defining Configuration](./defining.md). diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 304b5bae8f..1ebe518cac 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -147,7 +147,7 @@ export function registerCommands(program: CommanderStatic) { .action(lazy(() => import('./config/print').then(m => m.default))); program - .command('config:validate') + .command('config:check') .option(...configOption) .description( 'Validate that the given configuration loads and matches schema', From c7d6fefe9c9751d667208ae3bc6c9c879f22773c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 18 Nov 2020 10:18:10 +0100 Subject: [PATCH 238/252] cli: add config schema for frontend listening config + fix usage --- packages/cli/package.json | 16 ++++++++++++++++ packages/cli/src/lib/bundler/server.ts | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 23c5c5d6af..516e354012 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -169,6 +169,22 @@ "visibility": "frontend", "description": "Tracking ID for Google Analytics", "example": "UA-000000-0" + }, + "listen": { + "type": "object", + "description": "Listening configuration for local development", + "properties": { + "host": { + "type": "number", + "visibility": "frontend", + "description": "The host that the frontend should be bound to. Only used for local development." + }, + "post": { + "type": "number", + "visibility": "frontend", + "description": "The port that the frontend should be bound to. Only used for local development." + } + } } } } diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index f540e01084..d198f5507c 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -26,9 +26,9 @@ export async function serveBundle(options: ServeOptions) { const url = resolveBaseUrl(options.frontendConfig); const host = - options.config.getOptionalString('app.listen.host') || url.hostname; + options.frontendConfig.getOptionalString('app.listen.host') || url.hostname; const port = - options.config.getOptionalNumber('app.listen.port') || + options.frontendConfig.getOptionalNumber('app.listen.port') || Number(url.port) || (url.protocol === 'https:' ? 443 : 80); From 717e43de146b7c18798bcef4ebd0d5bd740578f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 18 Nov 2020 19:58:36 +0100 Subject: [PATCH 239/252] catalog-client: change entities interface, add fields support (#3296) --- .changeset/shaggy-turkeys-warn.md | 5 + .../catalog-client/src/CatalogClient.test.ts | 45 ++++++--- packages/catalog-client/src/CatalogClient.ts | 96 +++++++++++-------- packages/catalog-client/src/types.ts | 13 ++- .../ApiExplorerPage/ApiExplorerPage.test.tsx | 34 +++---- .../ApiExplorerPage/ApiExplorerPage.tsx | 6 +- .../CatalogFilter/CatalogFilter.test.tsx | 46 ++++----- .../CatalogPage/CatalogPage.test.tsx | 50 +++++----- .../ResultsFilter/ResultsFilter.test.tsx | 74 +++++++------- .../UnregisterEntityDialog.tsx | 10 +- .../src/filter/EntityFilterGroupsProvider.tsx | 9 +- .../src/filter/useEntityFilterGroup.test.tsx | 18 ++-- .../RollbarHome/RollbarHome.test.tsx | 2 +- .../rollbar/src/hooks/useRollbarEntities.ts | 4 +- .../ScaffolderPage/ScaffolderPage.tsx | 10 +- .../TemplatePage/TemplatePage.test.tsx | 60 ++++++------ .../components/TemplatePage/TemplatePage.tsx | 30 +++--- .../reader/components/TechDocsHome.test.tsx | 3 +- .../src/reader/components/TechDocsHome.tsx | 18 ++-- 19 files changed, 298 insertions(+), 235 deletions(-) create mode 100644 .changeset/shaggy-turkeys-warn.md diff --git a/.changeset/shaggy-turkeys-warn.md b/.changeset/shaggy-turkeys-warn.md new file mode 100644 index 0000000000..442cc788f2 --- /dev/null +++ b/.changeset/shaggy-turkeys-warn.md @@ -0,0 +1,5 @@ +--- +'@backstage/catalog-client': minor +--- + +Changed the getEntities interface to (1) nest parameters in an object, (2) support field selection, and (3) return an object with an items field for future extension diff --git a/packages/catalog-client/src/CatalogClient.test.ts b/packages/catalog-client/src/CatalogClient.test.ts index 65625c11b8..6369f95b76 100644 --- a/packages/catalog-client/src/CatalogClient.test.ts +++ b/packages/catalog-client/src/CatalogClient.test.ts @@ -14,11 +14,11 @@ * limitations under the License. */ +import { Entity } from '@backstage/catalog-model'; import { rest } from 'msw'; import { setupServer } from 'msw/node'; import { CatalogClient } from './CatalogClient'; -import { Entity } from '@backstage/catalog-model'; -import { DiscoveryApi } from './types'; +import { CatalogListResponse, DiscoveryApi } from './types'; const server = setupServer(); const mockBaseUrl = 'http://backstage:9191/i-am-a-mock-base'; @@ -40,7 +40,7 @@ describe('CatalogClient', () => { }); describe('getEntities', () => { - const defaultResponse: Entity[] = [ + const defaultServiceResponse: Entity[] = [ { apiVersion: '1', kind: 'Component', @@ -58,22 +58,26 @@ describe('CatalogClient', () => { }, }, ]; + const defaultResponse: CatalogListResponse = { + items: defaultServiceResponse, + }; beforeEach(() => { server.use( rest.get(`${mockBaseUrl}/entities`, (_, res, ctx) => { - return res(ctx.json(defaultResponse)); + return res(ctx.json(defaultServiceResponse)); }), ); }); it('should entities from correct endpoint', async () => { - const entities = await client.getEntities(); - expect(entities).toEqual(defaultResponse); + const response = await client.getEntities(); + expect(response).toEqual(defaultResponse); }); it('builds entity search filters properly', async () => { expect.assertions(2); + server.use( rest.get(`${mockBaseUrl}/entities`, (req, res, ctx) => { expect(req.url.search).toBe('?filter=a=1,b=2,b=3,%C3%B6=%3D'); @@ -81,13 +85,32 @@ describe('CatalogClient', () => { }), ); - const entities = await client.getEntities({ - a: '1', - b: ['2', '3'], - ö: '=', + const response = await client.getEntities({ + filter: { + a: '1', + b: ['2', '3'], + ö: '=', + }, }); - expect(entities).toEqual([]); + expect(response.items).toEqual([]); + }); + + it('builds entity field selectors properly', async () => { + expect.assertions(2); + + server.use( + rest.get(`${mockBaseUrl}/entities`, (req, res, ctx) => { + expect(req.url.search).toBe('?fields=a.b,%C3%B6'); + return res(ctx.json([])); + }), + ); + + const response = await client.getEntities({ + fields: ['a.b', 'ö'], + }); + + expect(response.items).toEqual([]); }); }); }); diff --git a/packages/catalog-client/src/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts index e39759a88d..362b1e71da 100644 --- a/packages/catalog-client/src/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -25,6 +25,8 @@ import { AddLocationRequest, AddLocationResponse, CatalogApi, + CatalogEntitiesRequest, + CatalogListResponse, DiscoveryApi, } from './types'; @@ -35,55 +37,33 @@ export class CatalogClient implements CatalogApi { this.discoveryApi = options.discoveryApi; } - private async getRequired(path: string): Promise { - const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`; - const response = await fetch(url); - - if (!response.ok) { - const payload = await response.text(); - const message = `Request failed with ${response.status} ${response.statusText}, ${payload}`; - throw new Error(message); - } - - return await response.json(); - } - - private async getOptional(path: string): Promise { - const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`; - const response = await fetch(url); - - if (!response.ok) { - if (response.status === 404) { - return undefined; - } - - const payload = await response.text(); - const message = `Request failed with ${response.status} ${response.statusText}, ${payload}`; - throw new Error(message); - } - - return await response.json(); - } - async getLocationById(id: String): Promise { return await this.getOptional(`/locations/${id}`); } async getEntities( - filter?: Record, - ): Promise { - let path = `/entities`; - if (filter) { - const parts: string[] = []; - for (const [key, value] of Object.entries(filter)) { - for (const v of [value].flat()) { - parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`); - } + request?: CatalogEntitiesRequest, + ): Promise> { + const { filter = {}, fields = [] } = request ?? {}; + const params: string[] = []; + + const filterParts: string[] = []; + for (const [key, value] of Object.entries(filter)) { + for (const v of [value].flat()) { + filterParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`); } - path += `?filter=${parts.join(',')}`; + } + if (filterParts.length) { + params.push(`filter=${filterParts.join(',')}`); } - return await this.getRequired(path); + if (fields.length) { + params.push(`fields=${fields.map(encodeURIComponent).join(',')}`); + } + + const query = params.length ? `?${params.join('&')}` : ''; + const entities: Entity[] = await this.getRequired(`/entities${query}`); + return { items: entities }; } async getEntityByName(compoundName: EntityName): Promise { @@ -153,4 +133,38 @@ export class CatalogClient implements CatalogApi { } return undefined; } + + // + // Private methods + // + + private async getRequired(path: string): Promise { + const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`; + const response = await fetch(url); + + if (!response.ok) { + const payload = await response.text(); + const message = `Request failed with ${response.status} ${response.statusText}, ${payload}`; + throw new Error(message); + } + + return await response.json(); + } + + private async getOptional(path: string): Promise { + const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`; + const response = await fetch(url); + + if (!response.ok) { + if (response.status === 404) { + return undefined; + } + + const payload = await response.text(); + const message = `Request failed with ${response.status} ${response.statusText}, ${payload}`; + throw new Error(message); + } + + return await response.json(); + } } diff --git a/packages/catalog-client/src/types.ts b/packages/catalog-client/src/types.ts index 575b8d6ca2..e72317d444 100644 --- a/packages/catalog-client/src/types.ts +++ b/packages/catalog-client/src/types.ts @@ -16,10 +16,21 @@ import { Entity, EntityName, Location } from '@backstage/catalog-model'; +export type CatalogEntitiesRequest = { + filter?: Record | undefined; + fields?: string[] | undefined; +}; + +export type CatalogListResponse = { + items: T[]; +}; + export interface CatalogApi { getLocationById(id: String): Promise; getEntityByName(name: EntityName): Promise; - getEntities(filter?: Record): Promise; + getEntities( + request?: CatalogEntitiesRequest, + ): Promise>; addLocation(location: AddLocationRequest): Promise; getLocationByEntity(entity: Entity): Promise; removeEntityByUid(uid: string): Promise; diff --git a/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.test.tsx b/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.test.tsx index 46cf014134..10495bb957 100644 --- a/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.test.tsx +++ b/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.test.tsx @@ -26,24 +26,26 @@ import { ApiExplorerPage } from './ApiExplorerPage'; describe('ApiCatalogPage', () => { const catalogApi: Partial = { getEntities: () => - Promise.resolve([ - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'API', - metadata: { - name: 'Entity1', + Promise.resolve({ + items: [ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'API', + metadata: { + name: 'Entity1', + }, + spec: { type: 'openapi' }, }, - spec: { type: 'openapi' }, - }, - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'API', - metadata: { - name: 'Entity2', + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'API', + metadata: { + name: 'Entity2', + }, + spec: { type: 'openapi' }, }, - spec: { type: 'openapi' }, - }, - ] as Entity[]), + ] as Entity[], + }), getLocationByEntity: () => Promise.resolve({ id: 'id', type: 'github', target: 'url' }), }; diff --git a/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.tsx b/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.tsx index c6a8d62b58..398268caf2 100644 --- a/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.tsx +++ b/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.tsx @@ -25,8 +25,8 @@ import { ApiExplorerLayout } from './ApiExplorerLayout'; export const ApiExplorerPage = () => { const catalogApi = useApi(catalogApiRef); - const { loading, error, value: matchingEntities } = useAsync(() => { - return catalogApi.getEntities({ kind: 'API' }); + const { loading, error, value: catalogResponse } = useAsync(() => { + return catalogApi.getEntities({ filter: { kind: 'API' } }); }, [catalogApi]); return ( @@ -44,7 +44,7 @@ export const ApiExplorerPage = () => { All your APIs diff --git a/plugins/catalog/src/components/CatalogFilter/CatalogFilter.test.tsx b/plugins/catalog/src/components/CatalogFilter/CatalogFilter.test.tsx index a4ede7c2f2..b92fc3d4bc 100644 --- a/plugins/catalog/src/components/CatalogFilter/CatalogFilter.test.tsx +++ b/plugins/catalog/src/components/CatalogFilter/CatalogFilter.test.tsx @@ -33,30 +33,32 @@ import { ButtonGroup, CatalogFilter } from './CatalogFilter'; describe('Catalog Filter', () => { const catalogApi: Partial = { getEntities: () => - Promise.resolve([ - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { - name: 'Entity1', + Promise.resolve({ + items: [ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'Entity1', + }, + spec: { + owner: 'tools@example.com', + type: 'service', + }, }, - spec: { - owner: 'tools@example.com', - type: 'service', + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'Entity2', + }, + spec: { + owner: 'not-tools@example.com', + type: 'service', + }, }, - }, - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { - name: 'Entity2', - }, - spec: { - owner: 'not-tools@example.com', - type: 'service', - }, - }, - ] as Entity[]), + ] as Entity[], + }), }; const identityApi: Partial = { diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx index 0d645f0f35..83bf4a6f6e 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx @@ -34,37 +34,39 @@ import { CatalogPage } from './CatalogPage'; describe('CatalogPage', () => { const catalogApi: Partial = { getEntities: () => - Promise.resolve([ - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { - name: 'Entity1', + Promise.resolve({ + items: [ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'Entity1', + }, + spec: { + owner: 'tools@example.com', + type: 'service', + }, }, - spec: { - owner: 'tools@example.com', - type: 'service', + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'Entity2', + }, + spec: { + owner: 'not-tools@example.com', + type: 'service', + }, }, - }, - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { - name: 'Entity2', - }, - spec: { - owner: 'not-tools@example.com', - type: 'service', - }, - }, - ] as Entity[]), + ] as Entity[], + }), getLocationByEntity: () => Promise.resolve({ id: 'id', type: 'github', target: 'url' }), }; const testProfile: Partial = { displayName: 'Display Name', }; - const indentityApi: Partial = { + const identityApi: Partial = { getUserId: () => 'tools@example.com', getProfile: () => testProfile, }; @@ -75,7 +77,7 @@ describe('CatalogPage', () => { diff --git a/plugins/catalog/src/components/ResultsFilter/ResultsFilter.test.tsx b/plugins/catalog/src/components/ResultsFilter/ResultsFilter.test.tsx index 39837d2ca1..d105979600 100644 --- a/plugins/catalog/src/components/ResultsFilter/ResultsFilter.test.tsx +++ b/plugins/catalog/src/components/ResultsFilter/ResultsFilter.test.tsx @@ -33,46 +33,48 @@ import { ResultsFilter } from './ResultsFilter'; describe('Results Filter', () => { const catalogApi: Partial = { getEntities: () => - Promise.resolve([ - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { - name: 'Entity1', - tags: ['java'], + Promise.resolve({ + items: [ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'Entity1', + tags: ['java'], + }, + spec: { + owner: 'tools@example.com', + type: 'service', + }, }, - spec: { - owner: 'tools@example.com', - type: 'service', + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'Entity2', + }, + spec: { + owner: 'not-tools@example.com', + type: 'service', + }, }, - }, - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { - name: 'Entity2', + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'Entity3', + tags: ['java', 'test'], + }, + spec: { + owner: 'tools@example.com', + type: 'service', + }, }, - spec: { - owner: 'not-tools@example.com', - type: 'service', - }, - }, - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { - name: 'Entity3', - tags: ['java', 'test'], - }, - spec: { - owner: 'tools@example.com', - type: 'service', - }, - }, - ] as Entity[]), + ] as Entity[], + }), }; - const indentityApi: Partial = { + const identityApi: Partial = { getUserId: () => 'tools@example.com', }; @@ -82,7 +84,7 @@ describe('Results Filter', () => { diff --git a/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx b/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx index 218cc45e78..96b6880bb9 100644 --- a/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx +++ b/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx @@ -44,9 +44,13 @@ function useColocatedEntities(entity: Entity): AsyncState { const catalogApi = useApi(catalogApiRef); return useAsync(async () => { const myLocation = entity.metadata.annotations?.[LOCATION_ANNOTATION]; - return myLocation - ? await catalogApi.getEntities({ [LOCATION_ANNOTATION]: myLocation }) - : []; + if (!myLocation) { + return []; + } + const response = await catalogApi.getEntities({ + filter: { [LOCATION_ANNOTATION]: myLocation }, + }); + return response.items; }, [catalogApi, entity]); } diff --git a/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx b/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx index 36cc0c56df..edda8eecb5 100644 --- a/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx +++ b/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx @@ -46,9 +46,12 @@ export const EntityFilterGroupsProvider = ({ // The hook that implements the actual context building function useProvideEntityFilters(): FilterGroupsContext { const catalogApi = useApi(catalogApiRef); - const [{ value: entities, error }, doReload] = useAsyncFn(() => - catalogApi.getEntities({ kind: 'Component' }), - ); + const [{ value: entities, error }, doReload] = useAsyncFn(async () => { + const response = await catalogApi.getEntities({ + filter: { kind: 'Component' }, + }); + return response.items; + }); const filterGroups = useRef<{ [filterGroupId: string]: FilterGroup; diff --git a/plugins/catalog/src/filter/useEntityFilterGroup.test.tsx b/plugins/catalog/src/filter/useEntityFilterGroup.test.tsx index 6b573e3721..e9af063160 100644 --- a/plugins/catalog/src/filter/useEntityFilterGroup.test.tsx +++ b/plugins/catalog/src/filter/useEntityFilterGroup.test.tsx @@ -49,7 +49,7 @@ describe('useEntityFilterGroup', () => { }); it('works for an empty set of filters', async () => { - catalogApi.getEntities.mockResolvedValue([]); + catalogApi.getEntities.mockResolvedValue({ items: [] }); const group: FilterGroup = { filters: {} }; const { result, waitFor } = renderHook( () => useEntityFilterGroup('g1', group), @@ -60,13 +60,15 @@ describe('useEntityFilterGroup', () => { }); it('works for a single group', async () => { - catalogApi.getEntities.mockResolvedValue([ - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { name: 'n' }, - }, - ]); + catalogApi.getEntities.mockResolvedValue({ + items: [ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { name: 'n' }, + }, + ], + }); const group: FilterGroup = { filters: { f1: e => e.metadata.name === 'n', diff --git a/plugins/rollbar/src/components/RollbarHome/RollbarHome.test.tsx b/plugins/rollbar/src/components/RollbarHome/RollbarHome.test.tsx index c6ee052c0e..ff3e909bf7 100644 --- a/plugins/rollbar/src/components/RollbarHome/RollbarHome.test.tsx +++ b/plugins/rollbar/src/components/RollbarHome/RollbarHome.test.tsx @@ -52,7 +52,7 @@ describe('RollbarHome component', () => { catalogApiRef, ({ async getEntities() { - return []; + return { items: [] }; }, } as Partial) as CatalogApi, ], diff --git a/plugins/rollbar/src/hooks/useRollbarEntities.ts b/plugins/rollbar/src/hooks/useRollbarEntities.ts index 3edf34d1f3..5979c5832a 100644 --- a/plugins/rollbar/src/hooks/useRollbarEntities.ts +++ b/plugins/rollbar/src/hooks/useRollbarEntities.ts @@ -28,8 +28,8 @@ export function useRollbarEntities() { configApi.getString('organization.name'); const { value, loading, error } = useAsync(async () => { - const entities = await catalogApi.getEntities(); - return entities.filter(entity => { + const response = await catalogApi.getEntities(); + return response.items.filter(entity => { return !!entity.metadata.annotations?.[ROLLBAR_ANNOTATION]; }); }, [catalogApi]); diff --git a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx index d0cb25f254..abdb411ad9 100644 --- a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx +++ b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx @@ -53,10 +53,12 @@ export const ScaffolderPage = () => { const { data: templates, isValidating, error } = useStaleWhileRevalidate( 'templates/all', - async () => - catalogApi.getEntities({ kind: 'Template' }) as Promise< - TemplateEntityV1alpha1[] - >, + async () => { + const response = await catalogApi.getEntities({ + filter: { kind: 'Template' }, + }); + return response.items as TemplateEntityV1alpha1[]; + }, ); useEffect(() => { diff --git a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.test.tsx b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.test.tsx index 6229a88fc3..62963b58b8 100644 --- a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.test.tsx +++ b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.test.tsx @@ -13,18 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React from 'react'; -import { TemplatePage } from './TemplatePage'; -import { wrapInTestApp, renderWithEffects } from '@backstage/test-utils'; -import { ApiRegistry, errorApiRef, ApiProvider } from '@backstage/core'; -import { scaffolderApiRef, ScaffolderApi } from '../../api'; -import { catalogApiRef, CatalogApi } from '@backstage/plugin-catalog'; -import { mutate } from 'swr'; -import { act } from 'react-dom/test-utils'; -import { Route, MemoryRouter } from 'react-router'; -import { rootRoute } from '../../routes'; -import { ThemeProvider } from '@material-ui/core'; +import { ApiProvider, ApiRegistry, errorApiRef } from '@backstage/core'; +import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog'; +import { renderInTestApp, renderWithEffects } from '@backstage/test-utils'; import { lightTheme } from '@backstage/theme'; +import { ThemeProvider } from '@material-ui/core'; +import React from 'react'; +import { act } from 'react-dom/test-utils'; +import { MemoryRouter, Route } from 'react-router'; +import { ScaffolderApi, scaffolderApiRef } from '../../api'; +import { rootRoute } from '../../routes'; +import { TemplatePage } from './TemplatePage'; const templateMock = { apiVersion: 'backstage.io/v1alpha1', @@ -90,48 +89,43 @@ const apis = ApiRegistry.from([ ]); describe('TemplatePage', () => { - afterEach(async () => { - // Cleaning up swr's cache - await act(async () => { - await mutate('templates/test'); - }); - }); + beforeEach(() => jest.resetAllMocks()); + it('renders correctly', async () => { - catalogApiMock.getEntities.mockResolvedValueOnce([templateMock]); - const rendered = await renderWithEffects( - wrapInTestApp( - - - , - ), + catalogApiMock.getEntities.mockResolvedValueOnce({ items: [templateMock] }); + const rendered = await renderInTestApp( + + + , ); expect(rendered.queryByText('Create a new component')).toBeInTheDocument(); expect(rendered.queryByText('React SSR Template')).toBeInTheDocument(); // await act(async () => await mutate('templates/test')); }); + it('renders spinner while loading', async () => { let resolve: Function; const promise = new Promise(res => { resolve = res; }); - catalogApiMock.getEntities.mockResolvedValueOnce(promise); - const rendered = await renderWithEffects( - wrapInTestApp( - - - , - ), + catalogApiMock.getEntities.mockReturnValueOnce(promise); + const rendered = await renderInTestApp( + + + , ); expect(rendered.queryByText('Create a new component')).toBeInTheDocument(); expect(rendered.queryByTestId('loading-progress')).toBeInTheDocument(); // Need to cleanup the promise or will timeout - resolve!(); + act(() => { + resolve!({ items: [] }); + }); }); it('navigates away if no template was loaded', async () => { - catalogApiMock.getEntities.mockResolvedValueOnce([]); + catalogApiMock.getEntities.mockResolvedValueOnce({ items: [] }); const rendered = await renderWithEffects( diff --git a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx index cb6b38903e..4836cdab56 100644 --- a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx +++ b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx @@ -27,28 +27,26 @@ import { catalogApiRef } from '@backstage/plugin-catalog'; import { LinearProgress } from '@material-ui/core'; import { IChangeEvent } from '@rjsf/core'; import React, { useState } from 'react'; -import { useParams } from 'react-router-dom'; -import useStaleWhileRevalidate from 'swr'; -import { scaffolderApiRef } from '../../api'; -import { JobStatusModal } from '../JobStatusModal'; -import { Job } from '../../types'; -import { MultistepJsonForm } from '../MultistepJsonForm'; import { Navigate } from 'react-router'; +import { useParams } from 'react-router-dom'; +import { useAsync } from 'react-use'; +import { scaffolderApiRef } from '../../api'; import { rootRoute } from '../../routes'; +import { Job } from '../../types'; +import { JobStatusModal } from '../JobStatusModal'; +import { MultistepJsonForm } from '../MultistepJsonForm'; const useTemplate = ( templateName: string, catalogApi: typeof catalogApiRef.T, ) => { - const { data, error } = useStaleWhileRevalidate( - `templates/${templateName}`, - async () => - catalogApi.getEntities({ - kind: 'Template', - 'metadata.name': templateName, - }) as Promise, - ); - return { template: data?.[0], loading: !error && !data, error }; + const { value, loading, error } = useAsync(async () => { + const response = await catalogApi.getEntities({ + filter: { kind: 'Template', 'metadata.name': templateName }, + }); + return response.items as TemplateEntityV1alpha1[]; + }); + return { template: value?.[0], loading, error }; }; const OWNER_REPO_SCHEMA = { @@ -110,7 +108,7 @@ export const TemplatePage = () => { const handleCreateComplete = async (job: Job) => { const target = job.metadata.remoteUrl?.replace( /\.git$/, - // TODO(Rugvip): This is not the location we want. As part of scaffodler v2 we + // TODO(Rugvip): This is not the location we want. As part of scaffolder v2 we // want this to be more flexible, but before that we might want // to update all templates to use catalog-info.yaml instead. '/blob/master/component-info.yaml', diff --git a/plugins/techdocs/src/reader/components/TechDocsHome.test.tsx b/plugins/techdocs/src/reader/components/TechDocsHome.test.tsx index d5d0e19e08..0924b90fc1 100644 --- a/plugins/techdocs/src/reader/components/TechDocsHome.test.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsHome.test.tsx @@ -14,7 +14,6 @@ * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; import { ApiProvider, ApiRegistry } from '@backstage/core-api'; import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog'; import { wrapInTestApp } from '@backstage/test-utils'; @@ -24,7 +23,7 @@ import { TechDocsHome } from './TechDocsHome'; describe('TechDocs Home', () => { const catalogApi: Partial = { - getEntities: () => Promise.resolve([] as Entity[]), + getEntities: () => Promise.resolve({ items: [] }), }; const apiRegistry = ApiRegistry.from([[catalogApiRef, catalogApi]]); diff --git a/plugins/techdocs/src/reader/components/TechDocsHome.tsx b/plugins/techdocs/src/reader/components/TechDocsHome.tsx index 0408a540ca..9f569dd167 100644 --- a/plugins/techdocs/src/reader/components/TechDocsHome.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsHome.tsx @@ -14,19 +14,19 @@ * limitations under the License. */ -import React from 'react'; -import { useAsync } from 'react-use'; -import { useNavigate, generatePath } from 'react-router-dom'; -import { Grid } from '@material-ui/core'; import { + Content, + Header, ItemCard, + Page, Progress, useApi, - Content, - Page, - Header, } from '@backstage/core'; import { catalogApiRef } from '@backstage/plugin-catalog'; +import { Grid } from '@material-ui/core'; +import React from 'react'; +import { generatePath, useNavigate } from 'react-router-dom'; +import { useAsync } from 'react-use'; import { rootDocsRouteRef } from '../../plugin'; export const TechDocsHome = () => { @@ -34,8 +34,8 @@ export const TechDocsHome = () => { const navigate = useNavigate(); const { value, loading, error } = useAsync(async () => { - const entities = await catalogApi.getEntities(); - return entities.filter(entity => { + const response = await catalogApi.getEntities(); + return response.items.filter(entity => { return !!entity.metadata.annotations?.['backstage.io/techdocs-ref']; }); }); From f8cb1a976d82e8e54cd70dd31dc0957506f092b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 18 Nov 2020 20:09:38 +0100 Subject: [PATCH 240/252] fix: unbreak search after merging catalog client change --- plugins/search/src/apis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/search/src/apis.ts b/plugins/search/src/apis.ts index 1783b91d2a..f3a050b2a2 100644 --- a/plugins/search/src/apis.ts +++ b/plugins/search/src/apis.ts @@ -35,7 +35,7 @@ class SearchApi { private async entities() { const entities = await this.catalogApi.getEntities(); - return entities.map((result: any) => ({ + return entities.items.map((result: any) => ({ name: result.metadata.name, description: result.metadata.description, owner: result.spec.owner, From 66daa47c6a1265e8ff052d9dbcf17f9bb0893650 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Wed, 18 Nov 2020 20:22:09 +0100 Subject: [PATCH 241/252] 1. Remove DiscoveryApi (frontend) and use PluginEndpointDiscovery(SingleHostDiscovery) (backend) 2. Mock class for testing --- plugins/techdocs-backend/package.json | 1 - .../src/techdocs/stages/publish/local.test.ts | 25 +++++++------------ .../src/techdocs/stages/publish/local.ts | 14 ++++++----- yarn.lock | 16 ++++++++---- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 236f53b9d1..c1d1f6ad27 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -23,7 +23,6 @@ "@backstage/backend-common": "^0.2.0", "@backstage/catalog-model": "^0.2.0", "@backstage/config": "^0.1.1", - "@backstage/core-api": "^0.2.1", "@types/dockerode": "^2.5.34", "@types/express": "^4.17.6", "command-exists-promise": "^2.0.2", diff --git a/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts b/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts index 4b6503f620..18344489e1 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts @@ -17,8 +17,10 @@ /* eslint-disable no-restricted-syntax */ import fs from 'fs-extra'; import path from 'path'; -import { getVoidLogger, SingleHostDiscovery } from '@backstage/backend-common'; -import { ConfigReader } from '@backstage/config'; +import { + getVoidLogger, + PluginEndpointDiscovery, +} from '@backstage/backend-common'; import { LocalPublish } from './local'; const createMockEntity = (annotations = {}) => { @@ -38,20 +40,11 @@ const logger = getVoidLogger(); describe('local publisher', () => { it('should publish generated documentation dir', async () => { - const testConfig = ConfigReader.fromConfigs([ - { - context: '', - data: { - backend: { - baseUrl: 'http://localhost:7000', - listen: { - port: 7000, - }, - }, - }, - }, - ]); - const testDiscovery = SingleHostDiscovery.fromConfig(testConfig); + const testDiscovery: jest.Mocked = { + getBaseUrl: jest.fn().mockResolvedValueOnce('http://localhost:7000'), + getExternalBaseUrl: jest.fn(), + }; + const publisher = new LocalPublish(logger, testDiscovery); const mockEntity = createMockEntity(); diff --git a/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts b/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts index 9f066a919c..e13c66b7f0 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts @@ -17,16 +17,18 @@ import fs from 'fs-extra'; import { Logger } from 'winston'; import { Entity } from '@backstage/catalog-model'; import { PublisherBase } from './types'; -import { resolvePackagePath } from '@backstage/backend-common'; -import { DiscoveryApi } from '@backstage/core-api'; +import { + resolvePackagePath, + PluginEndpointDiscovery, +} from '@backstage/backend-common'; export class LocalPublish implements PublisherBase { private readonly logger: Logger; - private readonly discoveryApi: DiscoveryApi; + private readonly discovery: PluginEndpointDiscovery; - constructor(logger: Logger, discoveryApi: DiscoveryApi) { + constructor(logger: Logger, discovery: PluginEndpointDiscovery) { this.logger = logger; - this.discoveryApi = discoveryApi; + this.discovery = discovery; } publish({ @@ -66,7 +68,7 @@ export class LocalPublish implements PublisherBase { reject(err); } - this.discoveryApi.getBaseUrl('techdocs').then(techdocsApiUrl => { + this.discovery.getBaseUrl('techdocs').then(techdocsApiUrl => { resolve({ remoteUrl: `${techdocsApiUrl}/static/docs/${entity.metadata.name}`, }); diff --git a/yarn.lock b/yarn.lock index 9d4984e152..10e70f52fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1290,34 +1290,40 @@ to-fast-properties "^2.0.0" "@backstage/core@^0.2.0": - version "0.2.0" - resolved "https://registry.npmjs.org/@backstage/core/-/core-0.2.0.tgz#543246b2d87563c9aa4d9fb96e40fdfc7e827520" - integrity sha512-75m2u3FoUngBOvt9l65xZcYTzzB+49OXpY1A9VNFUR1+jMs3cL/0HDfByQV2H0xXaHzMngQ8C5u/sWhkQsij1w== + version "0.3.0" dependencies: "@backstage/config" "^0.1.1" - "@backstage/core-api" "^0.2.0" - "@backstage/theme" "^0.2.0" + "@backstage/core-api" "^0.2.1" + "@backstage/theme" "^0.2.1" "@material-ui/core" "^4.11.0" "@material-ui/icons" "^4.9.1" "@material-ui/lab" "4.0.0-alpha.45" + "@types/dagre" "^0.7.44" "@types/react" "^16.9" "@types/react-sparklines" "^1.7.0" classnames "^2.2.6" clsx "^1.1.0" + d3-selection "^2.0.0" + d3-shape "^2.0.0" + d3-zoom "^2.0.0" + dagre "^0.8.5" immer "^7.0.9" lodash "^4.17.15" material-table "^1.69.1" prop-types "^15.7.2" + qs "^6.9.4" rc-progress "^3.0.0" react "^16.12.0" react-dom "^16.12.0" react-helmet "6.1.0" react-hook-form "^6.6.0" + react-markdown "^5.0.2" react-router "6.0.0-beta.0" react-router-dom "6.0.0-beta.0" react-sparklines "^1.7.0" react-syntax-highlighter "^13.5.1" react-use "^15.3.3" + remark-gfm "^1.0.0" "@bcoe/v8-coverage@^0.2.3": version "0.2.3" From f8877545c54cf3852f5f414fffe687c9fbe464fd Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Wed, 18 Nov 2020 20:35:22 +0100 Subject: [PATCH 242/252] TechDocs: Reject promise if call to getBaseUrl fails --- .../src/techdocs/stages/publish/local.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts b/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts index e13c66b7f0..07cef2f4d2 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts @@ -68,11 +68,16 @@ export class LocalPublish implements PublisherBase { reject(err); } - this.discovery.getBaseUrl('techdocs').then(techdocsApiUrl => { - resolve({ - remoteUrl: `${techdocsApiUrl}/static/docs/${entity.metadata.name}`, + this.discovery + .getBaseUrl('techdocs') + .then(techdocsApiUrl => { + resolve({ + remoteUrl: `${techdocsApiUrl}/static/docs/${entity.metadata.name}`, + }); + }) + .catch(reason => { + reject(reason); }); - }); }); }); } From 4a25ee5cf546525dbbc4646b0aec78b5cd15349e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 18 Nov 2020 21:10:04 +0100 Subject: [PATCH 243/252] auth-backend: use the catalog client (#3298) --- plugins/auth-backend/package.json | 1 + .../lib/catalog/CatalogIdentityClient.test.ts | 48 +++++++++++++++++++ .../src/lib/catalog/CatalogIdentityClient.ts | 42 +++++----------- .../src/providers/google/provider.ts | 34 ++++++------- plugins/auth-backend/src/providers/types.ts | 6 ++- plugins/auth-backend/src/service/router.ts | 19 ++++---- .../src/service/standaloneServer.ts | 10 ++-- 7 files changed, 99 insertions(+), 61 deletions(-) create mode 100644 plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.test.ts diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index af3d056b28..28ac51034e 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -21,6 +21,7 @@ }, "dependencies": { "@backstage/backend-common": "^0.2.1", + "@backstage/catalog-client": "^0.2.0", "@backstage/catalog-model": "^0.2.0", "@backstage/config": "^0.1.1", "@types/express": "^4.17.6", diff --git a/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.test.ts b/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.test.ts new file mode 100644 index 0000000000..ec6aa1b6ba --- /dev/null +++ b/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.test.ts @@ -0,0 +1,48 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CatalogApi } from '@backstage/catalog-client'; +import { UserEntity } from '@backstage/catalog-model'; +import { CatalogIdentityClient } from './CatalogIdentityClient'; + +describe('CatalogIdentityClient', () => { + const catalogApi: jest.Mocked = { + getLocationById: jest.fn(), + getEntityByName: jest.fn(), + getEntities: jest.fn(), + addLocation: jest.fn(), + getLocationByEntity: jest.fn(), + removeEntityByUid: jest.fn(), + }; + + afterEach(() => jest.resetAllMocks()); + + it('passes through the correct search params', async () => { + catalogApi.getEntities.mockResolvedValueOnce({ items: [{} as UserEntity] }); + const client = new CatalogIdentityClient({ + catalogApi: catalogApi as CatalogApi, + }); + + client.findUser({ annotations: { key: 'value' } }); + + expect(catalogApi.getEntities).toBeCalledWith({ + filter: { + kind: 'user', + 'metadata.annotations.key': 'value', + }, + }); + }); +}); diff --git a/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts b/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts index e56763f656..5bc4e5de21 100644 --- a/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts +++ b/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts @@ -14,13 +14,9 @@ * limitations under the License. */ -import { - ConflictError, - NotFoundError, - PluginEndpointDiscovery, -} from '@backstage/backend-common'; +import { ConflictError, NotFoundError } from '@backstage/backend-common'; +import { CatalogApi } from '@backstage/catalog-client'; import { UserEntity } from '@backstage/catalog-model'; -import fetch from 'cross-fetch'; type UserQuery = { annotations: Record; @@ -30,10 +26,10 @@ type UserQuery = { * A catalog client tailored for reading out identity data from the catalog. */ export class CatalogIdentityClient { - private readonly discovery: PluginEndpointDiscovery; + private readonly catalogApi: CatalogApi; - constructor(options: { discovery: PluginEndpointDiscovery }) { - this.discovery = options.discovery; + constructor(options: { catalogApi: CatalogApi }) { + this.catalogApi = options.catalogApi; } /** @@ -42,35 +38,23 @@ export class CatalogIdentityClient { * Throws a NotFoundError or ConflictError if 0 or multiple users are found. */ async findUser(query: UserQuery): Promise { - const conditions = ['kind=user']; + const filter: Record = { + kind: 'user', + }; for (const [key, value] of Object.entries(query.annotations)) { - const uk = encodeURIComponent(key); - const uv = encodeURIComponent(value); - conditions.push(`metadata.annotations.${uk}=${uv}`); + filter[`metadata.annotations.${key}`] = value; } - const baseUrl = await this.discovery.getBaseUrl('catalog'); - const response = await fetch( - `${baseUrl}/entities?filter=${conditions.join(',')}`, - ); + const { items } = await this.catalogApi.getEntities({ filter }); - if (!response.ok) { - const text = await response.text(); - throw new Error( - `Request failed with ${response.status} ${response.statusText}, ${text}`, - ); - } - - const users: UserEntity[] = await response.json(); - - if (users.length !== 1) { - if (users.length > 1) { + if (items.length !== 1) { + if (items.length > 1) { throw new ConflictError('User lookup resulted in multiple matches'); } else { throw new NotFoundError('User not found'); } } - return users[0]; + return items[0] as UserEntity; } } diff --git a/plugins/auth-backend/src/providers/google/provider.ts b/plugins/auth-backend/src/providers/google/provider.ts index 80143ca201..dd5b51eb01 100644 --- a/plugins/auth-backend/src/providers/google/provider.ts +++ b/plugins/auth-backend/src/providers/google/provider.ts @@ -15,29 +15,29 @@ */ import express from 'express'; -import { Logger } from 'winston'; +import passport from 'passport'; import { Strategy as GoogleStrategy } from 'passport-google-oauth20'; +import { Logger } from 'winston'; +import { CatalogIdentityClient } from '../../lib/catalog'; import { + encodeState, + OAuthAdapter, + OAuthEnvironmentHandler, + OAuthHandlers, + OAuthProviderOptions, + OAuthRefreshRequest, + OAuthResponse, + OAuthStartRequest, +} from '../../lib/oauth'; +import { + executeFetchUserProfileStrategy, executeFrameHandlerStrategy, executeRedirectStrategy, executeRefreshTokenStrategy, makeProfileInfo, - executeFetchUserProfileStrategy, PassportDoneCallback, } from '../../lib/passport'; -import { RedirectInfo, AuthProviderFactory } from '../types'; -import { - OAuthAdapter, - OAuthHandlers, - OAuthProviderOptions, - OAuthResponse, - OAuthEnvironmentHandler, - OAuthStartRequest, - encodeState, - OAuthRefreshRequest, -} from '../../lib/oauth'; -import passport from 'passport'; -import { CatalogIdentityClient } from '../../lib/catalog'; +import { AuthProviderFactory, RedirectInfo } from '../types'; type PrivateInfo = { refreshToken: string; @@ -179,7 +179,7 @@ export const createGoogleProvider: AuthProviderFactory = ({ config, logger, tokenIssuer, - discovery, + catalogApi, }) => OAuthEnvironmentHandler.mapConfig(config, envConfig => { const providerId = 'google'; @@ -192,7 +192,7 @@ export const createGoogleProvider: AuthProviderFactory = ({ clientSecret, callbackUrl, logger, - identityClient: new CatalogIdentityClient({ discovery }), + identityClient: new CatalogIdentityClient({ catalogApi }), }); return OAuthAdapter.fromConfig(globalConfig, provider, { diff --git a/plugins/auth-backend/src/providers/types.ts b/plugins/auth-backend/src/providers/types.ts index 6776095f99..0e500e027e 100644 --- a/plugins/auth-backend/src/providers/types.ts +++ b/plugins/auth-backend/src/providers/types.ts @@ -14,11 +14,12 @@ * limitations under the License. */ +import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { CatalogApi } from '@backstage/catalog-client'; +import { Config } from '@backstage/config'; import express from 'express'; import { Logger } from 'winston'; import { TokenIssuer } from '../identity'; -import { Config } from '@backstage/config'; -import { PluginEndpointDiscovery } from '@backstage/backend-common'; export type AuthProviderConfig = { /** @@ -117,6 +118,7 @@ export type AuthProviderFactoryOptions = { logger: Logger; tokenIssuer: TokenIssuer; discovery: PluginEndpointDiscovery; + catalogApi: CatalogApi; }; export type AuthProviderFactory = ( diff --git a/plugins/auth-backend/src/service/router.ts b/plugins/auth-backend/src/service/router.ts index 6bb100de6b..0d19e67fc4 100644 --- a/plugins/auth-backend/src/service/router.ts +++ b/plugins/auth-backend/src/service/router.ts @@ -14,18 +14,19 @@ * limitations under the License. */ -import express from 'express'; -import Router from 'express-promise-router'; -import cookieParser from 'cookie-parser'; -import { Logger } from 'winston'; -import { createAuthProvider } from '../providers'; -import { Config } from '@backstage/config'; -import { DatabaseKeyStore, TokenFactory, createOidcRouter } from '../identity'; import { NotFoundError, - PluginEndpointDiscovery, PluginDatabaseManager, + PluginEndpointDiscovery, } from '@backstage/backend-common'; +import { CatalogClient } from '@backstage/catalog-client'; +import { Config } from '@backstage/config'; +import cookieParser from 'cookie-parser'; +import express from 'express'; +import Router from 'express-promise-router'; +import { Logger } from 'winston'; +import { createOidcRouter, DatabaseKeyStore, TokenFactory } from '../identity'; +import { createAuthProvider } from '../providers'; export interface RouterOptions { logger: Logger; @@ -56,6 +57,7 @@ export async function createRouter({ keyDurationSeconds, logger: logger.child({ component: 'token-factory' }), }); + const catalogApi = new CatalogClient({ discoveryApi: discovery }); router.use(cookieParser()); router.use(express.urlencoded({ extended: false })); @@ -73,6 +75,7 @@ export async function createRouter({ logger, tokenIssuer, discovery, + catalogApi, }); const r = Router(); diff --git a/plugins/auth-backend/src/service/standaloneServer.ts b/plugins/auth-backend/src/service/standaloneServer.ts index a4f2377507..15a3347d5f 100644 --- a/plugins/auth-backend/src/service/standaloneServer.ts +++ b/plugins/auth-backend/src/service/standaloneServer.ts @@ -14,16 +14,16 @@ * limitations under the License. */ -import Knex from 'knex'; -import { Server } from 'http'; -import { Logger } from 'winston'; -import { createRouter } from './router'; import { createServiceBuilder, - useHotMemoize, loadBackendConfig, SingleHostDiscovery, + useHotMemoize, } from '@backstage/backend-common'; +import { Server } from 'http'; +import Knex from 'knex'; +import { Logger } from 'winston'; +import { createRouter } from './router'; export interface ServerOptions { logger: Logger; From 86017bc2fbc5d34f540b6530a864ba699a897ab9 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Wed, 18 Nov 2020 21:19:06 +0100 Subject: [PATCH 244/252] docs: Add catalog-client to project structure --- docs/support/project-structure.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/support/project-structure.md b/docs/support/project-structure.md index 034fba4cb8..deb8c47777 100644 --- a/docs/support/project-structure.md +++ b/docs/support/project-structure.md @@ -89,6 +89,10 @@ are separated out into their own folder, see further down. There are no "core" packages in the backend. Instead we have `backend-common` which contains helper middleware and other utils. +- [`catalog-client`](https://github.com/backstage/backstage/tree/master/packages/catalog-client) - + A client to communicate with Backstage software catalog. Recommended for backend plugins, compatible + but not recommended for frontend plugins (use `@backstage/plugin-catalog` package instead). + - [`catalog-model/`](https://github.com/backstage/backstage/tree/master/packages/catalog-model) - You can consider this to be a library for working with the catalog of sorts. It contains the definition of an From 4787cd04deaadcc531ce27b7ce686b3940bb4b47 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Wed, 18 Nov 2020 22:53:19 +0100 Subject: [PATCH 245/252] docs: Rephrase catalog-client and run prettier --- docs/support/project-structure.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/support/project-structure.md b/docs/support/project-structure.md index deb8c47777..c25e087e57 100644 --- a/docs/support/project-structure.md +++ b/docs/support/project-structure.md @@ -90,8 +90,10 @@ are separated out into their own folder, see further down. which contains helper middleware and other utils. - [`catalog-client`](https://github.com/backstage/backstage/tree/master/packages/catalog-client) - - A client to communicate with Backstage software catalog. Recommended for backend plugins, compatible - but not recommended for frontend plugins (use `@backstage/plugin-catalog` package instead). + An isomorphic client to interact with the Software Catalog. Backend plugins + can use the package directly. Frontend plugins can use the client by using + `@backstage/plugin-catalog` in combination with `useApi` and the + `catalogApiRef`. - [`catalog-model/`](https://github.com/backstage/backstage/tree/master/packages/catalog-model) - You can consider this to be a library for working with the catalog of sorts. From 7d7abd50c8befe18f86dab1ba5cb6982539ca5aa Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 18 Nov 2020 19:45:32 +0100 Subject: [PATCH 246/252] create-app: add app-backend plugin to template --- .changeset/add-app-backend.md | 104 ++++++++++++++++++ packages/create-app/package.json | 1 + packages/create-app/src/lib/versions.ts | 2 + .../default-app/app-config.production.yaml | 8 ++ .../templates/default-app/package.json.hbs | 1 + .../default-app/packages/backend/Dockerfile | 2 +- .../packages/backend/package.json.hbs | 4 +- .../default-app/packages/backend/src/index.ts | 5 +- .../packages/backend/src/plugins/app.ts | 13 +++ 9 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 .changeset/add-app-backend.md create mode 100644 packages/create-app/templates/default-app/app-config.production.yaml create mode 100644 packages/create-app/templates/default-app/packages/backend/src/plugins/app.ts diff --git a/.changeset/add-app-backend.md b/.changeset/add-app-backend.md new file mode 100644 index 0000000000..e44a56c885 --- /dev/null +++ b/.changeset/add-app-backend.md @@ -0,0 +1,104 @@ +--- +'@backstage/create-app': patch +--- + +Add `app-backend` as a backend plugin, and make a single docker build of the backend the default way to deploy backstage. + +Note that the `app-backend` currently only is a solution for deployments of the app, it's not a dev server and is not intended for local development. + +## Template changes + +As a part of installing the `app-backend` plugin, the below changes where made. The changes are grouped into two steps, installing the plugin, and updating the Docker build and configuration. + +### Installing the `app-backend` plugin in the backend + +First, install the `@backstage/plugin-app-backend` plugin package in your backend. These changes where made for `v0.3.0` of the plugin, and the installation process might change in the future. Run the following from the root of the repo: + +```bash +cd packages/backend +yarn add @backstage/plugin-app-backend +``` + +For the `app-backend` to get access to the static content in the frontend we also need to add the local `app` package as a dependency. Add the following to your `"dependencies"` in `packages/backend/package.json`, assuming your app package is still named `app` and on version `0.0.0`: + +```json +"app": "0.0.0", +``` + +Don't worry, this will not cause your entire frontend dependency tree to be added to the app, just double check that `packages/app/package.json` has a `"bundled": true` field at top-level. This signals to the backend build process that the package is bundled and that no transitive dependencies should be included. + +Next, create `packages/backend/src/plugins/app.ts` with the following: + +```ts +import { createRouter } from '@backstage/plugin-app-backend'; +import { PluginEnvironment } from '../types'; + +export default async function createPlugin({ + logger, + config, +}: PluginEnvironment) { + return await createRouter({ + logger, + config, + appPackageName: 'app', + }); +} +``` + +In `packages/backend/src/index.ts`, make the following changes: + +Add an import for the newly created plugin setup file: + +```ts +import app from './plugins/app'; +``` + +Setup the following plugin env. + +```ts +const appEnv = useHotMemoize(module, () => createEnv('app')); +``` + +Change service builder setup to include the `app` plugin as follows. Note that the `app` plugin is not installed on the `/api` route with most other plugins. + +```ts +const service = createServiceBuilder(module) + .loadConfig(config) + .addRouter('/api', apiRouter) + .addRouter('', await app(appEnv)); +``` + +You should now have the `app-backend` plugin installed in your backend, ready to serve the frontend bundle! + +### Docker build setup + +Since the backend image is now the only one needed for a simple Backstage deployment, the image tag name in the `build-image` script inside `packages/backend/package.json` was changed to the following: + +```json +"build-image": "backstage-cli backend:build-image --build --tag backstage", +``` + +For convenience, a `build-image` script was also added to the root `package.json` with the following: + +```json +"build-image": "yarn workspace backend build-image", +``` + +In the root of the repo, a new `app-config.production.yaml` file was added. This is used to set the appropriate `app.baseUrl` now that the frontend is served directly by the backend in the production deployment. It has the following contents: + +```yaml +app: + # Should be the same as backend.baseUrl when using the `app-backend` plugin + baseUrl: http://localhost:7000 + +backend: + baseUrl: http://localhost:7000 + listen: + port: 7000 +``` + +In order to load in the new configuration at runtime, the command in the `Dockerfile` at the repo root was changed to the following: + +```dockerfile +CMD ["node", "packages/backend", "--config", "app-config.yaml", "--config", "app-config.production.yaml"] +``` diff --git a/packages/create-app/package.json b/packages/create-app/package.json index bd1d8d1bd1..55cdcb1a2a 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -43,6 +43,7 @@ "@backstage/config": "^0.1.1", "@backstage/core": "^0.3.0", "@backstage/plugin-api-docs": "^0.2.1", + "@backstage/plugin-app-backend": "^0.2.0", "@backstage/plugin-auth-backend": "^0.2.1", "@backstage/plugin-catalog": "^0.2.1", "@backstage/plugin-catalog-backend": "^0.2.0", diff --git a/packages/create-app/src/lib/versions.ts b/packages/create-app/src/lib/versions.ts index 196e0004c1..84f015509c 100644 --- a/packages/create-app/src/lib/versions.ts +++ b/packages/create-app/src/lib/versions.ts @@ -35,6 +35,7 @@ import { version as cli } from '@backstage/cli/package.json'; import { version as config } from '@backstage/config/package.json'; import { version as core } from '@backstage/core/package.json'; import { version as pluginApiDocs } from '@backstage/plugin-api-docs/package.json'; +import { version as pluginAppBackend } from '@backstage/plugin-app-backend/package.json'; import { version as pluginAuthBackend } from '@backstage/plugin-auth-backend/package.json'; import { version as pluginCatalog } from '@backstage/plugin-catalog/package.json'; import { version as pluginCatalogBackend } from '@backstage/plugin-catalog-backend/package.json'; @@ -61,6 +62,7 @@ export const packageVersions = { '@backstage/config': config, '@backstage/core': core, '@backstage/plugin-api-docs': pluginApiDocs, + '@backstage/plugin-app-backend': pluginAppBackend, '@backstage/plugin-auth-backend': pluginAuthBackend, '@backstage/plugin-catalog': pluginCatalog, '@backstage/plugin-catalog-backend': pluginCatalogBackend, diff --git a/packages/create-app/templates/default-app/app-config.production.yaml b/packages/create-app/templates/default-app/app-config.production.yaml new file mode 100644 index 0000000000..92f4574fd1 --- /dev/null +++ b/packages/create-app/templates/default-app/app-config.production.yaml @@ -0,0 +1,8 @@ +app: + # Should be the same as backend.baseUrl when using the `app-backend` plugin + baseUrl: http://localhost:7000 + +backend: + baseUrl: http://localhost:7000 + listen: + port: 7000 diff --git a/packages/create-app/templates/default-app/package.json.hbs b/packages/create-app/templates/default-app/package.json.hbs index b9fb0f3744..0116287a46 100644 --- a/packages/create-app/templates/default-app/package.json.hbs +++ b/packages/create-app/templates/default-app/package.json.hbs @@ -8,6 +8,7 @@ "scripts": { "start": "yarn workspace app start", "build": "lerna run build", + "build-image": "yarn workspace backend build-image", "tsc": "tsc", "tsc:full": "tsc --skipLibCheck false --incremental false", "clean": "backstage-cli clean && lerna run clean", diff --git a/packages/create-app/templates/default-app/packages/backend/Dockerfile b/packages/create-app/templates/default-app/packages/backend/Dockerfile index 93929ceb86..0fcd23f0e1 100644 --- a/packages/create-app/templates/default-app/packages/backend/Dockerfile +++ b/packages/create-app/templates/default-app/packages/backend/Dockerfile @@ -13,4 +13,4 @@ RUN yarn install --frozen-lockfile --production # Do not use this Dockerfile outside of that command, as it will copy in the source code instead. COPY . . -CMD ["node", "packages/backend"] +CMD ["node", "packages/backend", "--config", "app-config.yaml", "--config", "app-config.production.yaml"] diff --git a/packages/create-app/templates/default-app/packages/backend/package.json.hbs b/packages/create-app/templates/default-app/packages/backend/package.json.hbs index df9ac28739..179d604670 100644 --- a/packages/create-app/templates/default-app/packages/backend/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/backend/package.json.hbs @@ -9,7 +9,7 @@ }, "scripts": { "build": "backstage-cli backend:build", - "build-image": "backstage-cli backend:build-image --build --tag example-backend", + "build-image": "backstage-cli backend:build-image --build --tag backstage", "start": "backstage-cli backend:dev", "lint": "backstage-cli lint", "test": "backstage-cli test", @@ -17,9 +17,11 @@ "migrate:create": "knex migrate:make -x ts" }, "dependencies": { + "app": "0.0.0", "@backstage/backend-common": "^{{version '@backstage/backend-common'}}", "@backstage/catalog-model": "^{{version '@backstage/catalog-model'}}", "@backstage/config": "^{{version '@backstage/config'}}", + "@backstage/plugin-app-backend": "^{{version '@backstage/plugin-app-backend'}}", "@backstage/plugin-auth-backend": "^{{version '@backstage/plugin-auth-backend'}}", "@backstage/plugin-catalog-backend": "^{{version '@backstage/plugin-catalog-backend'}}", "@backstage/plugin-proxy-backend": "^{{version '@backstage/plugin-proxy-backend'}}", diff --git a/packages/create-app/templates/default-app/packages/backend/src/index.ts b/packages/create-app/templates/default-app/packages/backend/src/index.ts index 51de1ad6b3..80dc6230a6 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/index.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/index.ts @@ -18,6 +18,7 @@ import { UrlReaders, } from '@backstage/backend-common'; import { Config } from '@backstage/config'; +import app from './plugins/app'; import auth from './plugins/auth'; import catalog from './plugins/catalog'; import scaffolder from './plugins/scaffolder'; @@ -53,6 +54,7 @@ async function main() { const authEnv = useHotMemoize(module, () => createEnv('auth')); const proxyEnv = useHotMemoize(module, () => createEnv('proxy')); const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs')); + const appEnv = useHotMemoize(module, () => createEnv('app')); const apiRouter = Router(); apiRouter.use('/catalog', await catalog(catalogEnv)); @@ -64,7 +66,8 @@ async function main() { const service = createServiceBuilder(module) .loadConfig(config) - .addRouter('/api', apiRouter); + .addRouter('/api', apiRouter) + .addRouter('', await app(appEnv)); await service.start().catch(err => { console.log(err); diff --git a/packages/create-app/templates/default-app/packages/backend/src/plugins/app.ts b/packages/create-app/templates/default-app/packages/backend/src/plugins/app.ts new file mode 100644 index 0000000000..51316949cb --- /dev/null +++ b/packages/create-app/templates/default-app/packages/backend/src/plugins/app.ts @@ -0,0 +1,13 @@ +import { createRouter } from '@backstage/plugin-app-backend'; +import { PluginEnvironment } from '../types'; + +export default async function createPlugin({ + logger, + config, +}: PluginEnvironment) { + return await createRouter({ + logger, + config, + appPackageName: 'app', + }); +} From 3525e6f687bd5fc9948e58fb2066a3864b39176a Mon Sep 17 00:00:00 2001 From: Stefano Vuerich Date: Thu, 19 Nov 2020 08:40:14 +0100 Subject: [PATCH 247/252] fix: conditional getuid and getgid for Windows environment --- .../stages/templater/helpers.test.ts | 15 +++++++------- .../scaffolder/stages/templater/helpers.ts | 20 ++++++++++++++----- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.test.ts index a4441ca23f..ca838fbfaa 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.test.ts @@ -17,15 +17,9 @@ import Stream, { PassThrough } from 'stream'; import os from 'os'; import fs from 'fs'; import Docker from 'dockerode'; -import { runDockerContainer } from './helpers'; +import { UserOptions, runDockerContainer} from './helpers'; describe('helpers', () => { - if (process.platform === 'win32') { - // eslint-disable-next-line jest/no-focused-tests - it.only('should skip tests on windows', () => { - expect('test').not.toBe('run'); - }); - } const mockDocker = new Docker() as jest.Mocked; @@ -142,12 +136,17 @@ describe('helpers', () => { dockerClient: mockDocker, }); + const userOptions: UserOptions = {}; + if (process.getuid && process.getgid) { + userOptions.User = `${process.getuid()}:${process.getgid()}`; + } + expect(mockDocker.run).toHaveBeenCalledWith( imageName, args, expect.any(Stream), expect.objectContaining({ - User: `${process.getuid()}:${process.getgid()}`, + ...userOptions, Env: ['HOME=/tmp'], }), ); diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.ts b/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.ts index e71b63bfb0..42bdbc8060 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.ts @@ -36,6 +36,10 @@ export type RunCommandOptions = { logStream?: Writable; }; +export type UserOptions = { + User?: string; +} + /** * Gets the templater key to use for templating from the entity * @param entity Template entity @@ -115,6 +119,16 @@ export const runDockerContainer = async ({ }); }); + const userOptions: UserOptions = {}; + if (process.getuid && process.getgid) { + // Files that are created inside the Docker container will be owned by + // root on the host system on non Mac systems, because of reasons. Mainly the fact that + // volume sharing is done using NFS on Mac and actual mounts in Linux world. + // So we set the user in the container as the same user and group id as the host. + // On Windows we don't have process.getuid nor process.getgid + userOptions.User = `${process.getuid()}:${process.getgid()}`; + } + const [{ Error: error, StatusCode: statusCode }] = await dockerClient.run( imageName, args, @@ -129,11 +143,7 @@ export const runDockerContainer = async ({ `${await fs.promises.realpath(templateDir)}:/template`, ], }, - // Files that are created inside the Docker container will be owned by - // root on the host system on non Mac systems, because of reasons. Mainly the fact that - // volume sharing is done using NFS on Mac and actual mounts in Linux world. - // So we set the user in the container as the same user and group id as the host. - User: `${process.getuid()}:${process.getgid()}`, + ...userOptions, // Set the home directory inside the container as something that applications can // write to, otherwise they will just flop and fail trying to write to / Env: ['HOME=/tmp'], From 00b60af58ae758b0e3d0ba7f4c625a413dea7a12 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Thu, 19 Nov 2020 09:26:31 +0100 Subject: [PATCH 248/252] docs: Correct URL of techdocs-container --- docs/features/techdocs/getting-started.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/features/techdocs/getting-started.md b/docs/features/techdocs/getting-started.md index cbbbc1e6cd..e51c6a122e 100644 --- a/docs/features/techdocs/getting-started.md +++ b/docs/features/techdocs/getting-started.md @@ -95,7 +95,7 @@ environment is compatible with techdocs. You will have to install the `mkdocs` and `mkdocs-techdocs-core` package from pip, as well as `graphviz` and `plantuml` from your OS package manager (e.g. apt). See our -[Dockerfile](https://github.com/spotify/backstage/blob/master/packages/techdocs-container/Dockerfile) +[Dockerfile](https://github.com/backstage/techdocs-container/blob/main/Dockerfile) for the latest requirements. You should be trying to match your Dockerfile with this one. @@ -104,7 +104,7 @@ Note: We recommend Python version 3.7 or higher. Caveat: Please install the `mkdocs-techdocs-core` package after all other Python packages. The order is important to make sure we get correct version of some of the dependencies. For example, we want `Markdown` version to be -[3.2.2](https://github.com/spotify/backstage/blob/f9f70c225548017b6a14daea75b00fbd399c11eb/packages/techdocs-container/techdocs-core/requirements.txt#L11). +[3.2.2](https://github.com/backstage/backstage/blob/f9f70c225548017b6a14daea75b00fbd399c11eb/packages/techdocs-container/techdocs-core/requirements.txt#L11). You can also explicitly install `Markdown==3.2.2` after installing all other Python packages. From 49554d43e37084979c6e3f56f7a3e3f2b9c0c995 Mon Sep 17 00:00:00 2001 From: Mateusz Lewtak <36006588+lewtakm@users.noreply.github.com> Date: Thu, 19 Nov 2020 10:30:34 +0100 Subject: [PATCH 249/252] Feat: update Lighthouse plugin to use new MarkdownComponent (#3328) --- plugins/lighthouse/package.json | 1 - .../lighthouse/src/components/Intro/index.tsx | 7 +- yarn.lock | 156 +----------------- 3 files changed, 7 insertions(+), 157 deletions(-) diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index 53bfc0e78b..8172847120 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -34,7 +34,6 @@ "@types/react": "^16.9", "react": "^16.13.1", "react-dom": "^16.13.1", - "react-markdown": "^4.3.1", "react-router-dom": "6.0.0-beta.0", "react-use": "^15.3.3" }, diff --git a/plugins/lighthouse/src/components/Intro/index.tsx b/plugins/lighthouse/src/components/Intro/index.tsx index dc2a8ea283..3e969a3c74 100644 --- a/plugins/lighthouse/src/components/Intro/index.tsx +++ b/plugins/lighthouse/src/components/Intro/index.tsx @@ -15,8 +15,7 @@ */ import React, { useState } from 'react'; import { useLocalStorage } from 'react-use'; -import Markdown from 'react-markdown'; -import { ContentHeader, InfoCard } from '@backstage/core'; +import { ContentHeader, InfoCard, MarkdownContent } from '@backstage/core'; import { makeStyles, Button, Grid, Tabs, Tab } from '@material-ui/core'; import CloseIcon from '@material-ui/icons/Close'; @@ -116,8 +115,8 @@ function GettingStartedCard() { } > - {value === 0 && } - {value === 1 && } + {value === 0 && } + {value === 1 && } ); } diff --git a/yarn.lock b/yarn.lock index feacd32d97..bb59924b80 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8750,11 +8750,6 @@ codeowners-utils@^1.0.2: ignore "^5.1.4" locate-path "^5.0.0" -collapse-white-space@^1.0.2: - version "1.0.6" - resolved "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" - integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== - collect-v8-coverage@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.0.tgz#150ee634ac3650b71d9c985eb7f608942334feb1" @@ -13624,7 +13619,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -13851,7 +13846,7 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-buffer@^1.0.2, is-buffer@^1.1.4, is-buffer@^1.1.5: +is-buffer@^1.0.2, is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== @@ -14263,11 +14258,6 @@ is-utf8@^0.2.0: resolved "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= -is-whitespace-character@^1.0.0: - version "1.0.4" - resolved "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" - integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== - is-window@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/is-window/-/is-window-1.0.2.tgz#2c896ca53db97de45d3c33133a65d8c9f563480d" @@ -14278,11 +14268,6 @@ is-windows@^1.0.0, is-windows@^1.0.1, is-windows@^1.0.2: resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== -is-word-character@^1.0.0: - version "1.0.4" - resolved "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz#ce0e73216f98599060592f62ff31354ddbeb0230" - integrity sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA== - is-wsl@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" @@ -16134,11 +16119,6 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -markdown-escapes@^1.0.0: - version "1.0.4" - 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" @@ -17996,7 +17976,7 @@ parse-asn1@^5.0.0: pbkdf2 "^3.0.3" safe-buffer "^5.1.1" -parse-entities@^1.1.0, parse-entities@^1.1.2: +parse-entities@^1.1.2: version "1.2.2" resolved "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.2.tgz#c31bf0f653b6661354f8973559cb86dd1d5edf50" integrity sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg== @@ -19781,20 +19761,6 @@ react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== -react-markdown@^4.3.1: - version "4.3.1" - resolved "https://registry.npmjs.org/react-markdown/-/react-markdown-4.3.1.tgz#39f0633b94a027445b86c9811142d05381300f2f" - integrity sha512-HQlWFTbDxTtNY6bjgp3C3uv1h2xcjCSi1zAEzfBW9OwJJvENSYiLXWNXN5hHLsoqai7RnZiiHzcnWdXk2Splzw== - dependencies: - html-to-react "^1.3.4" - mdast-add-list-metadata "1.0.1" - prop-types "^15.7.2" - react-is "^16.8.6" - remark-parse "^5.0.0" - unified "^6.1.5" - unist-util-visit "^1.3.0" - xtend "^4.0.1" - react-markdown@^5.0.0, react-markdown@^5.0.2: version "5.0.2" resolved "https://registry.npmjs.org/react-markdown/-/react-markdown-5.0.2.tgz#d15a8beb37b4ec34fc23dd892e7755eb7040b8db" @@ -20529,27 +20495,6 @@ remark-gfm@^1.0.0: mdast-util-gfm "^0.1.0" micromark-extension-gfm "^0.3.0" -remark-parse@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/remark-parse/-/remark-parse-5.0.0.tgz#4c077f9e499044d1d5c13f80d7a98cf7b9285d95" - integrity sha512-b3iXszZLH1TLoyUzrATcTQUZrwNl1rE70rVdSruJFlDaJ9z5aMkhrG43Pp68OgfHndL/ADz6V69Zow8cTQu+JA== - dependencies: - collapse-white-space "^1.0.2" - is-alphabetical "^1.0.0" - is-decimal "^1.0.0" - is-whitespace-character "^1.0.0" - is-word-character "^1.0.0" - markdown-escapes "^1.0.0" - parse-entities "^1.1.0" - repeat-string "^1.5.4" - state-toggle "^1.0.0" - trim "0.0.1" - trim-trailing-lines "^1.0.0" - unherit "^1.0.4" - unist-util-remove-position "^1.0.0" - vfile-location "^2.0.0" - xtend "^4.0.1" - remark-parse@^9.0.0: version "9.0.0" resolved "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz#4d20a299665880e4f4af5d90b7c7b8a935853640" @@ -20596,7 +20541,7 @@ repeat-element@^1.1.2: resolved "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== -repeat-string@^1.0.0, repeat-string@^1.5.2, repeat-string@^1.5.4, repeat-string@^1.6.1: +repeat-string@^1.0.0, repeat-string@^1.5.2, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= @@ -21853,11 +21798,6 @@ start-server-webpack-plugin@^2.2.5: resolved "https://registry.npmjs.org/start-server-webpack-plugin/-/start-server-webpack-plugin-2.2.5.tgz#4a2838759b0f36acd11b0b2f5f196f289ae29d31" integrity sha512-DRCkciwCJoCFZ+wt3wWMkR1M2mpVhJbUKFXqhK3FWyIUKYb42NnocH5sMwqgo+nPNHupqNwK/v8lgfBbr2NKdg== -state-toggle@^1.0.0: - version "1.0.3" - resolved "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe" - integrity sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ== - static-extend@^0.1.1: version "0.1.2" resolved "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -22969,16 +22909,6 @@ trim-off-newlines@^1.0.0: resolved "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= -trim-trailing-lines@^1.0.0: - version "1.1.3" - resolved "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.3.tgz#7f0739881ff76657b7776e10874128004b625a94" - integrity sha512-4ku0mmjXifQcTVfYDfR5lpgV7zVqPg6zV9rdZmwOPqq0+Zq19xDqEgagqVbc4pOOShbncuAOIs59R3+3gcF3ZA== - -trim@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" - integrity sha1-WFhUf2spB1fulczMZm+1AITEYN0= - triple-beam@^1.2.0, triple-beam@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" @@ -23306,14 +23236,6 @@ unfetch@^4.1.0: resolved "https://registry.npmjs.org/unfetch/-/unfetch-4.1.0.tgz#6ec2dd0de887e58a4dee83a050ded80ffc4137db" integrity sha512-crP/n3eAPUJxZXM9T80/yv0YhkTEx2K1D3h7D1AJM6fzsWZrxdyRuLN0JH/dkZh1LNH8LxCnBzoPFCPbb2iGpg== -unherit@^1.0.4: - version "1.1.3" - resolved "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz#6c9b503f2b41b262330c80e91c8614abdaa69c22" - integrity sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ== - dependencies: - inherits "^2.0.0" - xtend "^4.0.0" - unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" @@ -23337,18 +23259,6 @@ unicode-property-aliases-ecmascript@^1.0.4: resolved "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== -unified@^6.1.5: - version "6.2.0" - resolved "https://registry.npmjs.org/unified/-/unified-6.2.0.tgz#7fbd630f719126d67d40c644b7e3f617035f6dba" - integrity sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA== - dependencies: - bail "^1.0.0" - extend "^3.0.0" - is-plain-obj "^1.1.0" - trough "^1.0.0" - vfile "^2.0.0" - x-is-string "^0.1.0" - unified@^9.0.0: version "9.2.0" resolved "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz#67a62c627c40589edebbf60f53edfd4d822027f8" @@ -23402,28 +23312,11 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" -unist-util-is@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz#d9e84381c2468e82629e4a5be9d7d05a2dd324cd" - integrity sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A== - unist-util-is@^4.0.0: version "4.0.3" resolved "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.3.tgz#e8b44db55fc20c43752b3346c116344d45d7c91d" integrity sha512-bTofCFVx0iQM8Jqb1TBDVRIQW03YkD3p66JOd/aCWuqzlLyUtx1ZAGw/u+Zw+SttKvSVcvTiKYbfrtLoLefykw== -unist-util-remove-position@^1.0.0: - version "1.1.4" - resolved "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz#ec037348b6102c897703eee6d0294ca4755a2020" - integrity sha512-tLqd653ArxJIPnKII6LMZwH+mb5q+n/GtXQZo6S6csPRs5zB0u79Yw8ouR3wTw8wxvdJFhpP6Y7jorWdCgLO0A== - dependencies: - unist-util-visit "^1.1.0" - -unist-util-stringify-position@^1.0.0, unist-util-stringify-position@^1.1.1: - version "1.1.2" - resolved "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz#3f37fcf351279dcbca7480ab5889bb8a832ee1c6" - integrity sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ== - unist-util-stringify-position@^2.0.0: version "2.0.3" resolved "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da" @@ -23436,13 +23329,6 @@ unist-util-visit-parents@1.1.2: resolved "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-1.1.2.tgz#f6e3afee8bdbf961c0e6f028ea3c0480028c3d06" integrity sha512-yvo+MMLjEwdc3RhhPYSximset7rwjMrdt9E41Smmvg25UQIenzrN83cRnF1JMzoMi9zZOQeYXHSDf7p+IQkW3Q== -unist-util-visit-parents@^2.0.0: - version "2.1.2" - resolved "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz#25e43e55312166f3348cae6743588781d112c1e9" - integrity sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g== - dependencies: - unist-util-is "^3.0.0" - unist-util-visit-parents@^3.0.0: version "3.1.1" resolved "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" @@ -23451,13 +23337,6 @@ unist-util-visit-parents@^3.0.0: "@types/unist" "^2.0.0" unist-util-is "^4.0.0" -unist-util-visit@^1.1.0, unist-util-visit@^1.3.0: - version "1.4.1" - resolved "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz#4724aaa8486e6ee6e26d7ff3c8685960d560b1e3" - integrity sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw== - dependencies: - unist-util-visit-parents "^2.0.0" - unist-util-visit@^2.0.0: version "2.0.3" resolved "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" @@ -23813,18 +23692,6 @@ verror@1.10.0, verror@^1.8.1: core-util-is "1.0.2" extsprintf "^1.2.0" -vfile-location@^2.0.0: - version "2.0.6" - resolved "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.6.tgz#8a274f39411b8719ea5728802e10d9e0dff1519e" - integrity sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA== - -vfile-message@^1.0.0: - version "1.1.1" - resolved "https://registry.npmjs.org/vfile-message/-/vfile-message-1.1.1.tgz#5833ae078a1dfa2d96e9647886cd32993ab313e1" - integrity sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA== - dependencies: - unist-util-stringify-position "^1.1.1" - vfile-message@^2.0.0: version "2.0.4" resolved "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" @@ -23833,16 +23700,6 @@ vfile-message@^2.0.0: "@types/unist" "^2.0.0" unist-util-stringify-position "^2.0.0" -vfile@^2.0.0: - version "2.3.0" - resolved "https://registry.npmjs.org/vfile/-/vfile-2.3.0.tgz#e62d8e72b20e83c324bc6c67278ee272488bf84a" - integrity sha512-ASt4mBUHcTpMKD/l5Q+WJXNtshlWxOogYyGYYrg4lt/vuRjC1EFQtlAofL5VmtVNIZJzWYFJjzGWZ0Gw8pzW1w== - dependencies: - is-buffer "^1.1.4" - replace-ext "1.0.0" - unist-util-stringify-position "^1.0.0" - vfile-message "^1.0.0" - vfile@^4.0.0: version "4.2.0" resolved "https://registry.npmjs.org/vfile/-/vfile-4.2.0.tgz#26c78ac92eb70816b01d4565e003b7e65a2a0e01" @@ -24419,11 +24276,6 @@ ws@^7.3.1: resolved "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA== -x-is-string@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82" - integrity sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI= - xcase@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/xcase/-/xcase-2.0.1.tgz#c7fa72caa0f440db78fd5673432038ac984450b9" From ef8dff2cbe4e6388c661678bde23cd11cbdc7eff Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Thu, 19 Nov 2020 11:38:36 +0100 Subject: [PATCH 250/252] Replace broken icon for the api-docs plugin Closes #3330 I would guess that https://github.com/vscode-icons/vscode-icons is a more stable location for an icon. --- microsite/data/plugins/api-docs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/api-docs.yaml b/microsite/data/plugins/api-docs.yaml index 3343a46693..03ec818445 100644 --- a/microsite/data/plugins/api-docs.yaml +++ b/microsite/data/plugins/api-docs.yaml @@ -5,5 +5,5 @@ authorUrl: https://sda.se/ category: Discovery description: Components to discover and display API entities as an extension to the catalog plugin. documentation: https://github.com/backstage/backstage/blob/master/plugins/api-docs/README.md -iconUrl: https://thecoders.io/wp-content/uploads/2019/11/tech-swagger.svg +iconUrl: https://raw.githubusercontent.com/vscode-icons/vscode-icons/master/icons/file_type_swagger.svg npmPackageName: '@backstage/plugin-api-docs' From 09d90484ea25b34b28b3f61eef95139385458e83 Mon Sep 17 00:00:00 2001 From: Stefano Vuerich Date: Thu, 19 Nov 2020 11:46:34 +0100 Subject: [PATCH 251/252] fix: runned prettier --- .../src/scaffolder/stages/templater/helpers.test.ts | 3 +-- .../src/scaffolder/stages/templater/helpers.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.test.ts index ca838fbfaa..57e47f4c27 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.test.ts @@ -17,10 +17,9 @@ import Stream, { PassThrough } from 'stream'; import os from 'os'; import fs from 'fs'; import Docker from 'dockerode'; -import { UserOptions, runDockerContainer} from './helpers'; +import { UserOptions, runDockerContainer } from './helpers'; describe('helpers', () => { - const mockDocker = new Docker() as jest.Mocked; beforeEach(() => { diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.ts b/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.ts index 42bdbc8060..e79f2cb066 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.ts @@ -38,7 +38,7 @@ export type RunCommandOptions = { export type UserOptions = { User?: string; -} +}; /** * Gets the templater key to use for templating from the entity From 019c67a02280e39e25f07c92745c36bf849846ea Mon Sep 17 00:00:00 2001 From: Askar Date: Thu, 19 Nov 2020 13:39:22 +0100 Subject: [PATCH 252/252] fix(search): return EmptyState/Progress bar/Alert depending on result of api call (#3350) * fix(search): return EmptyState/Progress bar/Alert depending on result of api request * Update plugins/search/src/components/SearchResult/SearchResult.tsx Co-authored-by: Emma Indal Co-authored-by: Emma Indal --- .../components/SearchResult/SearchResult.tsx | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/plugins/search/src/components/SearchResult/SearchResult.tsx b/plugins/search/src/components/SearchResult/SearchResult.tsx index a4985c20fb..e4163c0de7 100644 --- a/plugins/search/src/components/SearchResult/SearchResult.tsx +++ b/plugins/search/src/components/SearchResult/SearchResult.tsx @@ -17,7 +17,14 @@ import React, { useState, useEffect } from 'react'; import { useAsync } from 'react-use'; import { makeStyles, Typography, Grid, Divider } from '@material-ui/core'; -import { Table, TableColumn, useApi } from '@backstage/core'; +import { Alert } from '@material-ui/lab'; +import { + EmptyState, + Progress, + Table, + TableColumn, + useApi, +} from '@backstage/core'; import { catalogApiRef } from '@backstage/plugin-catalog'; import { FiltersButton, Filters, FiltersState } from '../Filters'; @@ -159,8 +166,19 @@ export const SearchResult = ({ searchQuery }: SearchResultProps) => { setFilteredResults(withFilters); } }, [filters, searchQuery, results]); - - if (loading || error || !results) return null; + if (loading) { + return ; + } + if (error) { + return ( + + Error encountered while fetching search results. {error.toString()} + + ); + } + if (!results || results.length === 0) { + return ; + } const resetFilters = () => { setFilters({