From bb243dca25cf3057297dbfb6a3cbe52289f215c0 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 13 Oct 2020 11:46:21 +0200 Subject: [PATCH 01/44] cli: add support for ts4 --- packages/cli/package.json | 4 ++-- packages/docgen/package.json | 2 +- yarn.lock | 16 ++++++++-------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 084254d7ff..ace0639cce 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -84,12 +84,12 @@ "rollup-pluginutils": "^2.8.2", "start-server-webpack-plugin": "^2.2.5", "style-loader": "^1.2.1", - "sucrase": "^3.14.1", + "sucrase": "^3.16.0", "tar": "^6.0.1", "terser-webpack-plugin": "^1.4.3", "ts-jest": "^26.0.0", "ts-loader": "^7.0.4", - "typescript": "^3.9.3", + "typescript": "^4.0.3", "url-loader": "^4.1.0", "webpack": "^4.41.6", "webpack-dev-server": "^3.11.0", diff --git a/packages/docgen/package.json b/packages/docgen/package.json index 61efc0bbd4..7c0bf846bd 100644 --- a/packages/docgen/package.json +++ b/packages/docgen/package.json @@ -31,7 +31,7 @@ "fs-extra": "^9.0.0", "github-slugger": "^1.3.0", "ts-node": "^8.6.2", - "typescript": "^3.9.3" + "typescript": "^4.0.3" }, "devDependencies": { "@types/fs-extra": "^9.0.1", diff --git a/yarn.lock b/yarn.lock index 4d28072207..9a5bd93ffb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21732,10 +21732,10 @@ subscriptions-transport-ws@^0.9.11, subscriptions-transport-ws@^0.9.16: symbol-observable "^1.0.4" ws "^5.2.0" -sucrase@^3.14.1: - version "3.15.0" - resolved "https://registry.npmjs.org/sucrase/-/sucrase-3.15.0.tgz#78596a78be7264a65b52ed8d873883413ef0220c" - integrity sha512-05TJOUfMgckH7wKqfk/1p4G6q16nIeW/GHQwD44vkT0mQMqqzgfHCwkX3whNmwyOo7nVF0jDLwVu/qOBTtsscw== +sucrase@^3.16.0: + version "3.16.0" + resolved "https://registry.npmjs.org/sucrase/-/sucrase-3.16.0.tgz#19b5b886ccca270dd5ca12ff060eeaf0b599735f" + integrity sha512-ovVuswxV5TayCPXfTk8bgBgk6uNRvsinIkEpq0J6zS1xXCx5N/LLGcbsKdRhqn/ToZylMX6+yXaR1LSn1I42Pg== dependencies: commander "^4.0.0" glob "7.1.6" @@ -22702,10 +22702,10 @@ typedarray@^0.0.6: resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^3.9.3: - version "3.9.7" - resolved "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" - integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== +typescript@^4.0.3: + version "4.0.3" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz#153bbd468ef07725c1df9c77e8b453f8d36abba5" + integrity sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg== ua-parser-js@^0.7.18: version "0.7.21" From 9f3ddfacdeed708755b98a0964c936eed6f23b09 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 17 Oct 2020 14:44:02 +0200 Subject: [PATCH 02/44] cli: split and bump eslint dependencies + fix issues --- .../src/database/config.test.ts | 2 +- .../src/database/connection.test.ts | 2 +- .../src/database/postgres.test.ts | 10 +- .../src/database/sqlite3.test.ts | 4 +- packages/backend-common/src/paths.ts | 2 - packages/cli/package.json | 11 +- packages/cli/src/lib/parallel.test.ts | 4 +- packages/techdocs-cli/package.json | 6 +- .../processors/CodeOwnersProcessor.test.ts | 18 +- .../src/components/BarChart/BarChart.test.tsx | 14 +- .../ProjectSelect/ProjectSelect.test.tsx | 7 +- .../src/components/AuditView/index.test.tsx | 4 +- .../src/hooks/useWebsiteForEntity.test.tsx | 2 +- .../RegisterComponentForm.test.tsx | 2 +- yarn.lock | 190 ++++++++++-------- 15 files changed, 157 insertions(+), 121 deletions(-) diff --git a/packages/backend-common/src/database/config.test.ts b/packages/backend-common/src/database/config.test.ts index c9a4107686..eb26013b4b 100644 --- a/packages/backend-common/src/database/config.test.ts +++ b/packages/backend-common/src/database/config.test.ts @@ -17,7 +17,7 @@ import { mergeDatabaseConfig } from './config'; describe('config', () => { - describe(mergeDatabaseConfig, () => { + describe('mergeDatabaseConfig', () => { it('does not mutate the input object', () => { const input = { original: 'key', diff --git a/packages/backend-common/src/database/connection.test.ts b/packages/backend-common/src/database/connection.test.ts index 2cc3a54e06..de0b0e40dc 100644 --- a/packages/backend-common/src/database/connection.test.ts +++ b/packages/backend-common/src/database/connection.test.ts @@ -26,7 +26,7 @@ describe('database connection', () => { }, ]); - describe(createDatabaseClient, () => { + describe('createDatabaseClient', () => { it('returns a postgres connection', () => { expect( createDatabaseClient( diff --git a/packages/backend-common/src/database/postgres.test.ts b/packages/backend-common/src/database/postgres.test.ts index 82161dc53c..8d139e4481 100644 --- a/packages/backend-common/src/database/postgres.test.ts +++ b/packages/backend-common/src/database/postgres.test.ts @@ -44,7 +44,7 @@ describe('postgres', () => { }, ]); - describe(buildPgDatabaseConfig, () => { + describe('buildPgDatabaseConfig', () => { it('builds a postgres config', () => { const mockConnection = createMockConnection(); @@ -125,7 +125,7 @@ describe('postgres', () => { }); }); - describe(getPgConnectionConfig, () => { + describe('getPgConnectionConfig', () => { it('returns the connection object back', () => { const mockConnection = createMockConnection(); const config = createConfig(mockConnection); @@ -163,7 +163,7 @@ describe('postgres', () => { }); }); - describe(createPgDatabaseClient, () => { + describe('createPgDatabaseClient', () => { it('creates a postgres knex instance', () => { expect( createPgDatabaseClient( @@ -188,8 +188,8 @@ describe('postgres', () => { }); }); - describe(parsePgConnectionString, () => { - it('parses a connection string uri ', () => { + describe('parsePgConnectionString', () => { + it('parses a connection string uri', () => { expect( parsePgConnectionString( 'postgresql://postgres:pass@foobar:5432/dbname?ssl=true', diff --git a/packages/backend-common/src/database/sqlite3.test.ts b/packages/backend-common/src/database/sqlite3.test.ts index dbff5ee354..a3ab331c2d 100644 --- a/packages/backend-common/src/database/sqlite3.test.ts +++ b/packages/backend-common/src/database/sqlite3.test.ts @@ -32,7 +32,7 @@ describe('sqlite3', () => { }, ]); - describe(buildSqliteDatabaseConfig, () => { + describe('buildSqliteDatabaseConfig', () => { it('buidls a string connection', () => { expect(buildSqliteDatabaseConfig(createConfig(':memory:'))).toEqual({ client: 'sqlite3', @@ -72,7 +72,7 @@ describe('sqlite3', () => { }); }); - describe(createSqliteDatabaseClient, () => { + describe('createSqliteDatabaseClient', () => { it('creates an in memory knex instance', () => { expect( createSqliteDatabaseClient( diff --git a/packages/backend-common/src/paths.ts b/packages/backend-common/src/paths.ts index 402c0e2252..262be366f6 100644 --- a/packages/backend-common/src/paths.ts +++ b/packages/backend-common/src/paths.ts @@ -14,8 +14,6 @@ * limitations under the License. */ -/* eslint-disable @typescript-eslint/camelcase */ - import { resolve as resolvePath } from 'path'; /** diff --git a/packages/cli/package.json b/packages/cli/package.json index ace0639cce..74cbbfff6a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -38,7 +38,9 @@ "@rollup/plugin-json": "^4.0.2", "@rollup/plugin-node-resolve": "^8.1.0", "@rollup/plugin-yaml": "^2.1.1", - "@spotify/eslint-config": "^7.0.1", + "@spotify/eslint-config-base": "^8.0.0", + "@spotify/eslint-config-react": "^8.0.0", + "@spotify/eslint-config-typescript": "^8.0.0", "@sucrase/webpack-loader": "^2.0.0", "@svgr/plugin-jsx": "5.4.x", "@svgr/plugin-svgo": "5.4.x", @@ -47,6 +49,8 @@ "@types/start-server-webpack-plugin": "^2.2.0", "@types/webpack-env": "^1.15.2", "@types/webpack-node-externals": "^2.5.0", + "@typescript-eslint/eslint-plugin": "^v3.10.1", + "@typescript-eslint/parser": "^v3.10.1", "bfj": "^7.0.2", "chalk": "^4.0.0", "chokidar": "^3.3.1", @@ -56,9 +60,14 @@ "diff": "^4.0.2", "esbuild": "^0.7.7", "eslint": "^7.1.0", + "eslint-config-prettier": "^6.0.0", "eslint-formatter-friendly": "^7.0.0", "eslint-plugin-import": "^2.20.2", + "eslint-plugin-jest": "^24.1.0", + "eslint-plugin-jsx-a11y": "^6.2.1", "eslint-plugin-monorepo": "^0.2.1", + "eslint-plugin-react": "^7.12.4", + "eslint-plugin-react-hooks": "^4.0.0", "fork-ts-checker-webpack-plugin": "^4.0.5", "fs-extra": "^9.0.0", "handlebars": "^4.7.3", diff --git a/packages/cli/src/lib/parallel.test.ts b/packages/cli/src/lib/parallel.test.ts index c8173603d9..8047774a6e 100644 --- a/packages/cli/src/lib/parallel.test.ts +++ b/packages/cli/src/lib/parallel.test.ts @@ -17,7 +17,7 @@ import { isParallelDefault, parseParallel } from './parallel'; describe('parallel', () => { - describe(parseParallel, () => { + describe('parseParallel', () => { it('coerces "false" string to boolean', () => { expect(parseParallel('false')).toBeFalsy(); }); @@ -44,7 +44,7 @@ describe('parallel', () => { }); }); - describe(isParallelDefault, () => { + describe('isParallelDefault', () => { it('returns true if default value', () => { expect(isParallelDefault(undefined)).toBeTruthy(); expect(isParallelDefault(true)).toBeTruthy(); diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index adce3dd101..30ca6e4538 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -28,11 +28,7 @@ "techdocs-cli": "bin/techdocs-cli" }, "devDependencies": { - "@spotify/eslint-config": "^7.0.0", - "@spotify/prettier-config": "^7.0.0", - "@types/serve-handler": "^6.1.0", - "eslint": "^7.1.0", - "eslint-plugin-import": "^2.22.0" + "@types/serve-handler": "^6.1.0" }, "files": [ "bin", diff --git a/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts index c1c83b7c6c..e1bd5bcb46 100644 --- a/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts @@ -27,7 +27,7 @@ import { resolveCodeOwner, } from './CodeOwnersProcessor'; -describe(CodeOwnersProcessor, () => { +describe('CodeOwnersProcessor', () => { const mockUrl = ({ basePath = '' } = {}): string => `https://github.com/spotify/backstage/blob/master/${basePath}catalog-info.yaml`; const mockLocation = ({ @@ -79,7 +79,7 @@ describe(CodeOwnersProcessor, () => { return data; }; - describe(buildUrl, () => { + describe('buildUrl', () => { it.each([['azure.com'], ['dev.azure.com']])( 'should throw not implemented error', source => { @@ -99,7 +99,7 @@ describe(CodeOwnersProcessor, () => { }); }); - describe(buildCodeOwnerUrl, () => { + describe('buildCodeOwnerUrl', () => { it('should build a location spec to the codeowners', () => { expect(buildCodeOwnerUrl(mockUrl(), '/docs/CODEOWNERS')).toEqual( 'https://github.com/spotify/backstage/blob/master/docs/CODEOWNERS', @@ -116,13 +116,13 @@ describe(CodeOwnersProcessor, () => { }); }); - describe(parseCodeOwners, () => { + describe('parseCodeOwners', () => { it('should parse the codeowners file', () => { expect(parseCodeOwners(mockCodeOwnersText())).toEqual(mockCodeOwners()); }); }); - describe(normalizeCodeOwner, () => { + describe('normalizeCodeOwner', () => { it('should remove org from org/team format', () => { expect(normalizeCodeOwner('@acme/foo')).toBe('foo'); }); @@ -139,13 +139,13 @@ describe(CodeOwnersProcessor, () => { ); }); - describe(findPrimaryCodeOwner, () => { + describe('findPrimaryCodeOwner', () => { it('should return the primary owner', () => { expect(findPrimaryCodeOwner(mockCodeOwners())).toBe('backstage-core'); }); }); - describe(findRawCodeOwners, () => { + describe('findRawCodeOwners', () => { it('should return found codeowner', async () => { const ownersText = mockCodeOwnersText(); const read = jest @@ -184,7 +184,7 @@ describe(CodeOwnersProcessor, () => { }); }); - describe(resolveCodeOwner, () => { + describe('resolveCodeOwner', () => { it('should return found codeowner', async () => { const read = jest .fn() @@ -207,7 +207,7 @@ describe(CodeOwnersProcessor, () => { }); }); - describe(CodeOwnersProcessor, () => { + describe('CodeOwnersProcessor', () => { const setupTest = ({ kind = 'Component', spec = {} } = {}) => { const entity = { kind, spec }; const read = jest diff --git a/plugins/cost-insights/src/components/BarChart/BarChart.test.tsx b/plugins/cost-insights/src/components/BarChart/BarChart.test.tsx index a39c5bafc2..737b86018c 100644 --- a/plugins/cost-insights/src/components/BarChart/BarChart.test.tsx +++ b/plugins/cost-insights/src/components/BarChart/BarChart.test.tsx @@ -79,12 +79,14 @@ describe('', () => { it('Should display only 6 resources by default, sorted by cost', async () => { const rendered = await renderWithProps({} as BarChartProps); - MockResources.sort(resourceSort).forEach((resource, index) => { - if (index < 6) { - expect(rendered.getByText(resource.name!)).toBeInTheDocument(); - } else { - expect(rendered.queryByText(resource.name!)).not.toBeInTheDocument(); - } + const sorted = MockResources.sort(resourceSort); + + expect(sorted.length).toBe(10); + sorted.slice(0, 6).forEach(resource => { + expect(rendered.getByText(resource.name!)).toBeInTheDocument(); + }); + sorted.slice(6).forEach(resource => { + expect(rendered.queryByText(resource.name!)).not.toBeInTheDocument(); }); }); diff --git a/plugins/cost-insights/src/components/ProjectSelect/ProjectSelect.test.tsx b/plugins/cost-insights/src/components/ProjectSelect/ProjectSelect.test.tsx index 92de819fe0..a74c79f3b8 100644 --- a/plugins/cost-insights/src/components/ProjectSelect/ProjectSelect.test.tsx +++ b/plugins/cost-insights/src/components/ProjectSelect/ProjectSelect.test.tsx @@ -59,10 +59,9 @@ describe('', () => { const button = getByRole(projectSelectContainer, 'button'); UserEvent.click(button); await waitFor(() => rendered.getByTestId('option-all')); - mockProjects.forEach( - project => - project.id && - expect(rendered.getByText(project.id)).toBeInTheDocument(), + + mockProjects.forEach(project => + expect(rendered.getByText(project.id)).toBeInTheDocument(), ); }); }); diff --git a/plugins/lighthouse/src/components/AuditView/index.test.tsx b/plugins/lighthouse/src/components/AuditView/index.test.tsx index 742be67fed..ca292a133d 100644 --- a/plugins/lighthouse/src/components/AuditView/index.test.tsx +++ b/plugins/lighthouse/src/components/AuditView/index.test.tsx @@ -163,7 +163,7 @@ describe('AuditView', () => { }); describe('when the request for the website by id is pending', () => { - it('it shows the loading', async () => { + it('shows the loading', async () => { mockFetch.mockImplementationOnce(() => new Promise(() => {})); const rendered = render( wrapInTestApp( @@ -177,7 +177,7 @@ describe('AuditView', () => { }); describe('when the request for the website by id fails', () => { - it('it shows an error', async () => { + it('shows an error', async () => { mockFetch.mockRejectOnce(new Error('failed to fetch')); const rendered = render( wrapInTestApp( diff --git a/plugins/lighthouse/src/hooks/useWebsiteForEntity.test.tsx b/plugins/lighthouse/src/hooks/useWebsiteForEntity.test.tsx index 868e26f04c..580162f182 100644 --- a/plugins/lighthouse/src/hooks/useWebsiteForEntity.test.tsx +++ b/plugins/lighthouse/src/hooks/useWebsiteForEntity.test.tsx @@ -75,7 +75,7 @@ describe('useWebsiteForEntity', () => { (mockLighthouseApi.getWebsiteByUrl as jest.Mock).mockResolvedValue(website); }); - it('returns the lighthouse information for the website url in annotations ', async () => { + it('returns the lighthouse information for the website url in annotations', async () => { const { result, waitForNextUpdate } = subject(); await waitForNextUpdate(); expect(result.current?.value).toBe(website); diff --git a/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx b/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx index 10c3d9fcd1..875b1f8660 100644 --- a/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx +++ b/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx @@ -30,7 +30,7 @@ describe('RegisterComponentForm', () => { expect(screen.getByText('Submit').closest('button')).toBeDisabled(); }); - it('should enable a submit button when the target url is set ', async () => { + it('should enable a submit button when the target url is set', async () => { render(); await act(async () => { diff --git a/yarn.lock b/yarn.lock index 9a5bd93ffb..168da27a6f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3802,10 +3802,10 @@ dependencies: "@sinonjs/commons" "^1.7.0" -"@spotify/eslint-config-base@^7.0.0": - version "7.0.0" - resolved "https://registry.npmjs.org/@spotify/eslint-config-base/-/eslint-config-base-7.0.0.tgz#36804ae09ec938f1aa5f9464ea993f3f151cfaa8" - integrity sha512-XRTrTRyRRYBxPYINKNHw4B8QWlA3p4I+id/Po2sMdejtXH3LZgDgIjdschUZSdQ6J6dVYDdaVykdizRM9I+G6Q== +"@spotify/eslint-config-base@^8.0.0": + version "8.0.0" + resolved "https://registry.npmjs.org/@spotify/eslint-config-base/-/eslint-config-base-8.0.0.tgz#c3d10d8a05ad9129d579952cb80063e4c4fa216e" + integrity sha512-07Fo+KoTMl4AaZJPD6deFnYmG634wzgeqfFcWdFlIrOLAasvWSH8Lp6ZE9cJBKuffOAy8kuEE1SwFo34+5X8bQ== "@spotify/eslint-config-oss@^1.0.1": version "1.0.2" @@ -3814,51 +3814,21 @@ dependencies: eslint-plugin-notice "^0.9.10" -"@spotify/eslint-config-react@^7.0.1": - version "7.0.1" - resolved "https://registry.npmjs.org/@spotify/eslint-config-react/-/eslint-config-react-7.0.1.tgz#2e70de9d7911ea9aeaa55a83a332220a28f6c431" - integrity sha512-HNDHvm19EaBXiDgsg52mjMgKWZTeA0hO2Q75ACNwb8UtjIKkANqyyuzyDGo8jiGMbWIm6wJjShlRtT95emNlqQ== +"@spotify/eslint-config-react@^8.0.0": + version "8.0.2" + resolved "https://registry.npmjs.org/@spotify/eslint-config-react/-/eslint-config-react-8.0.2.tgz#91af3c58b22d49a96795336ed66927e722cda430" + integrity sha512-V0gzO/jWmjhODLINAfeunHj+OBdj/cQeVDw+92ZUlkonAg6BZLjX6Vy8jeIUFjfgmVOtI9eKbcApxaurj1jN6g== -"@spotify/eslint-config-typescript@^7.0.0": - version "7.0.0" - resolved "https://registry.npmjs.org/@spotify/eslint-config-typescript/-/eslint-config-typescript-7.0.0.tgz#fc5227e3344f74b41ac3a530df24a95ac13254e4" - integrity sha512-28I/SAf68NKbWZ5IY0WYMa0D18PxWdC9DP9gRbOTlZufmsS8jEgqf3zBUWmP6XOf1nihpKWcqvbFUG5H7/JYXA== - -"@spotify/eslint-config@^7.0.0", "@spotify/eslint-config@^7.0.1": - version "7.0.1" - resolved "https://registry.npmjs.org/@spotify/eslint-config/-/eslint-config-7.0.1.tgz#07a21cfd7fce89cfc2c6dd5ea5d747e741201b66" - integrity sha512-8GI/TZGUhS4pr7oipT2MjrZFRgXcKzk9YImEusUdD2f5vlCniRFIBQNrvTMkyjfdQqvIVqJPLcdVPXeAgprsMw== - dependencies: - "@spotify/eslint-config-base" "^7.0.0" - "@spotify/eslint-config-react" "^7.0.1" - "@spotify/eslint-config-typescript" "^7.0.0" - "@spotify/web-scripts-utils" "^7.0.0" - "@typescript-eslint/eslint-plugin" "^2.14.0" - "@typescript-eslint/parser" "^2.14.0" - eslint-config-prettier "^6.0.0" - eslint-plugin-jest "^23.6.0" - eslint-plugin-jsx-a11y "^6.2.1" - eslint-plugin-react "^7.12.4" - eslint-plugin-react-hooks "^4.0.0" - -"@spotify/prettier-config@^7.0.0": - version "7.0.0" - resolved "https://registry.npmjs.org/@spotify/prettier-config/-/prettier-config-7.0.0.tgz#47750979d1282197295108b6958360660a955c16" - integrity sha512-lIMcx/2oDqTtW84iHKkRJe+8U6HK6GPwWH5sJp9UEHcDpdXomOQYvwcGXy2I2zwPQQ14gYYE6nEJuSnnYqsYRw== +"@spotify/eslint-config-typescript@^8.0.0": + version "8.1.1" + resolved "https://registry.npmjs.org/@spotify/eslint-config-typescript/-/eslint-config-typescript-8.1.1.tgz#a8c6f74ef9445fe740008ae3a26003a2eef78f93" + integrity sha512-0NkE6ZiSRf9IBWwCxzn/l7UdwcL/KZsbzjjOSweafyYueajEn+eIpKjOgDGUL/ebxSrOtjEp+YeFc4lSPkP0qg== "@spotify/prettier-config@^8.0.0": version "8.0.0" resolved "https://registry.npmjs.org/@spotify/prettier-config/-/prettier-config-8.0.0.tgz#8b6c2bd579ddc54887155a0721fe04e96c89f7f2" integrity sha512-so8w32ZV42CHWxOEXcBtbNO/hLXFrQNXVmhfzhUI6dVB9cq2xjRaiqu8GjFj8LvKbWpPj+S+KwTIS4aDVWqrFQ== -"@spotify/web-scripts-utils@^7.0.0": - version "7.0.0" - resolved "https://registry.npmjs.org/@spotify/web-scripts-utils/-/web-scripts-utils-7.0.0.tgz#8c6b8039fc645a36ac48629eb9ba06600f4d828a" - integrity sha512-McMy0j60lxOHjgDjegthZqEWN/PabphiM30A/mI/Y7xh9+JFnYWBTSm/wgn6EW2BsPpV631xSYYkk6B1Ph14Fw== - dependencies: - glob "^7.1.4" - read-pkg-up "^7.0.1" - "@storybook/addon-actions@^6.0.21": version "6.0.21" resolved "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-6.0.21.tgz#0de1d109d4b1eb99f644bbe84e74c25cfd2b1b6b" @@ -5830,49 +5800,113 @@ resolved "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d" integrity sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg== -"@typescript-eslint/eslint-plugin@^2.14.0": - version "2.24.0" - resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.24.0.tgz#a86cf618c965a462cddf3601f594544b134d6d68" - integrity sha512-wJRBeaMeT7RLQ27UQkDFOu25MqFOBus8PtOa9KaT5ZuxC1kAsd7JEHqWt4YXuY9eancX0GK9C68i5OROnlIzBA== +"@typescript-eslint/eslint-plugin@^v3.10.1": + version "3.10.1" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.10.1.tgz#7e061338a1383f59edc204c605899f93dc2e2c8f" + integrity sha512-PQg0emRtzZFWq6PxBcdxRH3QIQiyFO3WCVpRL3fgj5oQS3CDs3AeAKfv4DxNhzn8ITdNJGJ4D3Qw8eAJf3lXeQ== dependencies: - "@typescript-eslint/experimental-utils" "2.24.0" - eslint-utils "^1.4.3" + "@typescript-eslint/experimental-utils" "3.10.1" + debug "^4.1.1" functional-red-black-tree "^1.0.1" regexpp "^3.0.0" + semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@2.24.0", "@typescript-eslint/experimental-utils@^2.5.0": - version "2.24.0" - resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.24.0.tgz#a5cb2ed89fedf8b59638dc83484eb0c8c35e1143" - integrity sha512-DXrwuXTdVh3ycNCMYmWhUzn/gfqu9N0VzNnahjiDJvcyhfBy4gb59ncVZVxdp5XzBC77dCncu0daQgOkbvPwBw== +"@typescript-eslint/experimental-utils@3.10.1": + version "3.10.1" + resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz#e179ffc81a80ebcae2ea04e0332f8b251345a686" + integrity sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.24.0" + "@typescript-eslint/types" "3.10.1" + "@typescript-eslint/typescript-estree" "3.10.1" eslint-scope "^5.0.0" + eslint-utils "^2.0.0" -"@typescript-eslint/parser@^2.14.0": - version "2.24.0" - resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.24.0.tgz#2cf0eae6e6dd44d162486ad949c126b887f11eb8" - integrity sha512-H2Y7uacwSSg8IbVxdYExSI3T7uM1DzmOn2COGtCahCC3g8YtM1xYAPi2MAHyfPs61VKxP/J/UiSctcRgw4G8aw== +"@typescript-eslint/experimental-utils@^4.0.1": + version "4.4.1" + resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.4.1.tgz#40613b9757fa0170de3e0043254dbb077cafac0c" + integrity sha512-Nt4EVlb1mqExW9cWhpV6pd1a3DkUbX9DeyYsdoeziKOpIJ04S2KMVDO+SEidsXRH/XHDpbzXykKcMTLdTXH6cQ== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/scope-manager" "4.4.1" + "@typescript-eslint/types" "4.4.1" + "@typescript-eslint/typescript-estree" "4.4.1" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" + +"@typescript-eslint/parser@^v3.10.1": + version "3.10.1" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.10.1.tgz#1883858e83e8b442627e1ac6f408925211155467" + integrity sha512-Ug1RcWcrJP02hmtaXVS3axPPTTPnZjupqhgj+NnZ6BCkwSImWk/283347+x9wN+lqOdK9Eo3vsyiyDHgsmiEJw== dependencies: "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "2.24.0" - "@typescript-eslint/typescript-estree" "2.24.0" + "@typescript-eslint/experimental-utils" "3.10.1" + "@typescript-eslint/types" "3.10.1" + "@typescript-eslint/typescript-estree" "3.10.1" eslint-visitor-keys "^1.1.0" -"@typescript-eslint/typescript-estree@2.24.0": - version "2.24.0" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.24.0.tgz#38bbc8bb479790d2f324797ffbcdb346d897c62a" - integrity sha512-RJ0yMe5owMSix55qX7Mi9V6z2FDuuDpN6eR5fzRJrp+8in9UF41IGNQHbg5aMK4/PjVaEQksLvz0IA8n+Mr/FA== +"@typescript-eslint/scope-manager@4.4.1": + version "4.4.1" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.4.1.tgz#d19447e60db2ce9c425898d62fa03b2cce8ea3f9" + integrity sha512-2oD/ZqD4Gj41UdFeWZxegH3cVEEH/Z6Bhr/XvwTtGv66737XkR4C9IqEkebCuqArqBJQSj4AgNHHiN1okzD/wQ== dependencies: + "@typescript-eslint/types" "4.4.1" + "@typescript-eslint/visitor-keys" "4.4.1" + +"@typescript-eslint/types@3.10.1": + version "3.10.1" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.1.tgz#1d7463fa7c32d8a23ab508a803ca2fe26e758727" + integrity sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ== + +"@typescript-eslint/types@4.4.1": + version "4.4.1" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.4.1.tgz#c507b35cf523bc7ba00aae5f75ee9b810cdabbc1" + integrity sha512-KNDfH2bCyax5db+KKIZT4rfA8rEk5N0EJ8P0T5AJjo5xrV26UAzaiqoJCxeaibqc0c/IvZxp7v2g3difn2Pn3w== + +"@typescript-eslint/typescript-estree@3.10.1": + version "3.10.1" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz#fd0061cc38add4fad45136d654408569f365b853" + integrity sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w== + dependencies: + "@typescript-eslint/types" "3.10.1" + "@typescript-eslint/visitor-keys" "3.10.1" debug "^4.1.1" - eslint-visitor-keys "^1.1.0" glob "^7.1.6" is-glob "^4.0.1" lodash "^4.17.15" - semver "^6.3.0" + semver "^7.3.2" tsutils "^3.17.1" +"@typescript-eslint/typescript-estree@4.4.1": + version "4.4.1" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.4.1.tgz#598f6de488106c2587d47ca2462c60f6e2797cb8" + integrity sha512-wP/V7ScKzgSdtcY1a0pZYBoCxrCstLrgRQ2O9MmCUZDtmgxCO/TCqOTGRVwpP4/2hVfqMz/Vw1ZYrG8cVxvN3g== + dependencies: + "@typescript-eslint/types" "4.4.1" + "@typescript-eslint/visitor-keys" "4.4.1" + debug "^4.1.1" + globby "^11.0.1" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" + +"@typescript-eslint/visitor-keys@3.10.1": + version "3.10.1" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz#cd4274773e3eb63b2e870ac602274487ecd1e931" + integrity sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ== + dependencies: + eslint-visitor-keys "^1.1.0" + +"@typescript-eslint/visitor-keys@4.4.1": + version "4.4.1" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.4.1.tgz#1769dc7a9e2d7d2cfd3318b77ed8249187aed5c3" + integrity sha512-H2JMWhLaJNeaylSnMSQFEhT/S/FsJbebQALmoJxMPMxLtlVAMy2uJP/Z543n9IizhjRayLSqoInehCeNW9rWcw== + dependencies: + "@typescript-eslint/types" "4.4.1" + eslint-visitor-keys "^2.0.0" + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -10677,7 +10711,7 @@ eslint-plugin-graphql@^4.0.0: lodash.flatten "^4.4.0" lodash.without "^4.4.0" -eslint-plugin-import@^2.20.2, eslint-plugin-import@^2.22.0: +eslint-plugin-import@^2.20.2: version "2.22.0" resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz#92f7736fe1fde3e2de77623c838dd992ff5ffb7e" integrity sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg== @@ -10696,12 +10730,12 @@ eslint-plugin-import@^2.20.2, eslint-plugin-import@^2.22.0: resolve "^1.17.0" tsconfig-paths "^3.9.0" -eslint-plugin-jest@^23.6.0: - version "23.8.2" - resolved "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-23.8.2.tgz#6f28b41c67ef635f803ebd9e168f6b73858eb8d4" - integrity sha512-xwbnvOsotSV27MtAe7s8uGWOori0nUsrXh2f1EnpmXua8sDfY6VZhHAhHg2sqK7HBNycRQExF074XSZ7DvfoFg== +eslint-plugin-jest@^24.1.0: + version "24.1.0" + resolved "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.1.0.tgz#6708037d7602e5288ce877fd0103f329dc978361" + integrity sha512-827YJ+E8B9PvXu/0eiVSNFfxxndbKv+qE/3GSMhdorCaeaOehtqHGX2YDW9B85TEOre9n/zscledkFW/KbnyGg== dependencies: - "@typescript-eslint/experimental-utils" "^2.5.0" + "@typescript-eslint/experimental-utils" "^4.0.1" eslint-plugin-jsx-a11y@^6.2.1: version "6.2.3" @@ -10779,13 +10813,6 @@ eslint-scope@^5.0.0, eslint-scope@^5.1.0: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-utils@^1.4.3: - version "1.4.3" - resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" - integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== - dependencies: - eslint-visitor-keys "^1.1.0" - eslint-utils@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd" @@ -10798,6 +10825,11 @@ eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.2.0: resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.2.0.tgz#74415ac884874495f78ec2a97349525344c981fa" integrity sha512-WFb4ihckKil6hu3Dp798xdzSfddwKKU3+nGniKF6HfeW6OLd2OUDEPP7TcHtB5+QXOKg2s6B2DaMPE1Nn/kxKQ== +eslint-visitor-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" + integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== + eslint@^7.1.0: version "7.4.0" resolved "https://registry.npmjs.org/eslint/-/eslint-7.4.0.tgz#4e35a2697e6c1972f9d6ef2b690ad319f80f206f" @@ -12189,7 +12221,7 @@ globalthis@^1.0.0: dependencies: define-properties "^1.1.3" -globby@11.0.1, globby@^11.0.0: +globby@11.0.1, globby@^11.0.0, globby@^11.0.1: version "11.0.1" resolved "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ== From e4d0aa4609a9bd3ce62044a28efec9707499f840 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 17 Oct 2020 15:11:28 +0200 Subject: [PATCH 03/44] docgen: fix duplicate help text --- packages/docgen/src/index.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/docgen/src/index.ts b/packages/docgen/src/index.ts index f41629e7fd..4b69fdbe01 100644 --- a/packages/docgen/src/index.ts +++ b/packages/docgen/src/index.ts @@ -49,10 +49,6 @@ const main = (argv: string[]) => { process.exit(1); }); - if (!process.argv.slice(2).length) { - program.outputHelp(chalk.yellow); - } - program.parse(argv); }; From c6ff101a99194be011225283c07088136543a25c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Sat, 17 Oct 2020 20:38:40 +0200 Subject: [PATCH 04/44] UX improvements to Settings --- .../components/AuthProviders/AuthProviders.tsx | 2 +- .../AuthProviders/ProviderSettingsItem.tsx | 16 +++++++--------- .../src/components/FeatureFlags/FeatureFlags.tsx | 2 +- .../components/FeatureFlags/FeatureFlagsItem.tsx | 15 ++++++--------- .../src/components/General/General.tsx | 8 ++++---- 5 files changed, 19 insertions(+), 24 deletions(-) diff --git a/plugins/user-settings/src/components/AuthProviders/AuthProviders.tsx b/plugins/user-settings/src/components/AuthProviders/AuthProviders.tsx index 563bf44150..bc2a68578d 100644 --- a/plugins/user-settings/src/components/AuthProviders/AuthProviders.tsx +++ b/plugins/user-settings/src/components/AuthProviders/AuthProviders.tsx @@ -37,7 +37,7 @@ export const AuthProviders = ({ providerSettings }: Props) => { } return ( - + {providers} ); diff --git a/plugins/user-settings/src/components/AuthProviders/ProviderSettingsItem.tsx b/plugins/user-settings/src/components/AuthProviders/ProviderSettingsItem.tsx index 7213ee2b83..4bae3dbfbb 100644 --- a/plugins/user-settings/src/components/AuthProviders/ProviderSettingsItem.tsx +++ b/plugins/user-settings/src/components/AuthProviders/ProviderSettingsItem.tsx @@ -22,14 +22,13 @@ import { SessionState, } from '@backstage/core'; import { + Button, ListItem, ListItemIcon, ListItemSecondaryAction, ListItemText, Tooltip, } from '@material-ui/core'; -import PowerButton from '@material-ui/icons/PowerSettingsNew'; -import { ToggleButton } from '@material-ui/lab'; type Props = { title: string; @@ -84,14 +83,13 @@ export const ProviderSettingsItem = ({ arrow title={signedIn ? `Sign out from ${title}` : `Sign in to ${title}`} > - (signedIn ? api.signOut() : api.signIn())} + diff --git a/plugins/user-settings/src/components/FeatureFlags/FeatureFlags.tsx b/plugins/user-settings/src/components/FeatureFlags/FeatureFlags.tsx index 71f59c52b8..e0f4d6f40e 100644 --- a/plugins/user-settings/src/components/FeatureFlags/FeatureFlags.tsx +++ b/plugins/user-settings/src/components/FeatureFlags/FeatureFlags.tsx @@ -62,7 +62,7 @@ export const FeatureFlags = () => { } return ( - + {featureFlags.map(featureFlag => { const enabled = Boolean(state[featureFlag.name]); diff --git a/plugins/user-settings/src/components/FeatureFlags/FeatureFlagsItem.tsx b/plugins/user-settings/src/components/FeatureFlags/FeatureFlagsItem.tsx index 92065f2ed6..460a15f56b 100644 --- a/plugins/user-settings/src/components/FeatureFlags/FeatureFlagsItem.tsx +++ b/plugins/user-settings/src/components/FeatureFlags/FeatureFlagsItem.tsx @@ -19,10 +19,9 @@ import { ListItem, ListItemSecondaryAction, ListItemText, + Switch, Tooltip, } from '@material-ui/core'; -import CheckIcon from '@material-ui/icons/CheckCircle'; -import { ToggleButton } from '@material-ui/lab'; import { FeatureFlagsRegistryItem } from '@backstage/core'; type Props = { @@ -39,14 +38,12 @@ export const FlagItem = ({ flag, enabled, toggleHandler }: Props) => ( /> - toggleHandler(flag.name)} - > - - + name={flag.name} + /> diff --git a/plugins/user-settings/src/components/General/General.tsx b/plugins/user-settings/src/components/General/General.tsx index 00ac17dfe1..424f352502 100644 --- a/plugins/user-settings/src/components/General/General.tsx +++ b/plugins/user-settings/src/components/General/General.tsx @@ -21,12 +21,12 @@ import { Profile } from './Profile'; import { ThemeToggle } from './ThemeToggle'; export const General = () => ( - - + + - - + + From 7a2f8b04cd1e2bae5db4071f62cfa6172155f3f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Sat, 17 Oct 2020 21:19:53 +0200 Subject: [PATCH 05/44] Review comments --- .../src/components/General/General.tsx | 4 +-- .../src/components/General/PinButton.tsx | 25 ++++++------------- .../src/components/General/ThemeToggle.tsx | 12 +++++++-- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/plugins/user-settings/src/components/General/General.tsx b/plugins/user-settings/src/components/General/General.tsx index 424f352502..b6d30d0103 100644 --- a/plugins/user-settings/src/components/General/General.tsx +++ b/plugins/user-settings/src/components/General/General.tsx @@ -22,10 +22,10 @@ import { ThemeToggle } from './ThemeToggle'; export const General = () => ( - + - + diff --git a/plugins/user-settings/src/components/General/PinButton.tsx b/plugins/user-settings/src/components/General/PinButton.tsx index e36fc365f8..181574fccf 100644 --- a/plugins/user-settings/src/components/General/PinButton.tsx +++ b/plugins/user-settings/src/components/General/PinButton.tsx @@ -19,18 +19,11 @@ import { ListItem, ListItemSecondaryAction, ListItemText, + Switch, Tooltip, } from '@material-ui/core'; -import LockIcon from '@material-ui/icons/Lock'; -import LockOpenIcon from '@material-ui/icons/LockOpen'; -import { ToggleButton } from '@material-ui/lab'; import { SidebarPinStateContext } from '@backstage/core'; -type PinIconProps = { isPinned: boolean }; - -const PinIcon = ({ isPinned }: PinIconProps) => - isPinned ? : ; - export const PinButton = () => { const { isPinned, toggleSidebarPinState } = useContext( SidebarPinStateContext, @@ -48,16 +41,12 @@ export const PinButton = () => { arrow title={`${isPinned ? 'Unpin' : 'Pin'} Sidebar`} > - { - toggleSidebarPinState(); - }} - > - - + toggleSidebarPinState()} + name="pin" + /> diff --git a/plugins/user-settings/src/components/General/ThemeToggle.tsx b/plugins/user-settings/src/components/General/ThemeToggle.tsx index 111bd838a8..dc24a61f8f 100644 --- a/plugins/user-settings/src/components/General/ThemeToggle.tsx +++ b/plugins/user-settings/src/components/General/ThemeToggle.tsx @@ -102,12 +102,20 @@ export const ThemeToggle = () => { title={`Select ${theme.title}`} value={theme.variant} > - + <> + {theme.variant}  + + ); })} - + + Auto  From 014bc250b379ae94fa263a27f86c544583d027ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sat, 10 Oct 2020 15:12:49 +0200 Subject: [PATCH 06/44] Fix API references parsing Allows API entities to use a different namespace than the default one. --- .../src/components/useComponentApiEntities.ts | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/plugins/api-docs/src/components/useComponentApiEntities.ts b/plugins/api-docs/src/components/useComponentApiEntities.ts index 11b5de988f..9e5cbd968e 100644 --- a/plugins/api-docs/src/components/useComponentApiEntities.ts +++ b/plugins/api-docs/src/components/useComponentApiEntities.ts @@ -16,7 +16,11 @@ import { useAsyncRetry } from 'react-use'; import { errorApiRef, useApi } from '@backstage/core'; -import { ApiEntity, ComponentEntity } from '@backstage/catalog-model'; +import { + ApiEntity, + ComponentEntity, + parseEntityName, +} from '@backstage/catalog-model'; import { catalogApiRef } from '@backstage/plugin-catalog'; import { useComponentApiNames } from './useComponentApiNames'; @@ -43,10 +47,20 @@ export function useComponentApiEntities({ await Promise.all( apiNames.map(async name => { try { - const api = (await catalogApi.getEntityByName({ - kind: 'API', - name, - })) as ApiEntity | undefined; + const apiEntityName = parseEntityName(name, { + defaultNamespace: entity.metadata.namespace, + defaultKind: 'API', + }); + + if (apiEntityName.kind !== 'API') { + throw new Error( + `Referenced entity of kind "${apiEntityName.kind}" as an API`, + ); + } + + const api = (await catalogApi.getEntityByName(apiEntityName)) as + | ApiEntity + | undefined; if (api) { resultMap.set(api.metadata.name, api); From d90a8109c276423c386fde4e35182042d4e8e043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Sun, 18 Oct 2020 07:53:22 +0200 Subject: [PATCH 07/44] Fix test? --- .../user-settings/src/components/General/PinButton.test.tsx | 3 +-- plugins/user-settings/src/components/General/PinButton.tsx | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/user-settings/src/components/General/PinButton.test.tsx b/plugins/user-settings/src/components/General/PinButton.test.tsx index 76af8ce8d4..88f636d918 100644 --- a/plugins/user-settings/src/components/General/PinButton.test.tsx +++ b/plugins/user-settings/src/components/General/PinButton.test.tsx @@ -32,10 +32,9 @@ describe('', () => { , ), ); - expect(rendered.getByText('Pin Sidebar')).toBeInTheDocument(); - const pinButton = rendered.getByTitle('Pin Sidebar'); + const pinButton = rendered.getByTestId('pin'); fireEvent.click(pinButton); expect(mockToggleFn).toHaveBeenCalled(); }); diff --git a/plugins/user-settings/src/components/General/PinButton.tsx b/plugins/user-settings/src/components/General/PinButton.tsx index 181574fccf..bfcdb0c7ff 100644 --- a/plugins/user-settings/src/components/General/PinButton.tsx +++ b/plugins/user-settings/src/components/General/PinButton.tsx @@ -46,6 +46,7 @@ export const PinButton = () => { checked={isPinned} onChange={() => toggleSidebarPinState()} name="pin" + data-test-id="pin" /> From c3113d1d39ddc6dd0b3a44ecad5e56d235232a40 Mon Sep 17 00:00:00 2001 From: Eduardo Apolinario Date: Sat, 17 Oct 2020 23:13:06 -0700 Subject: [PATCH 08/44] Fix Core Features configuration id (#2948) The current configuration id is pointing to an inexistent place. This PR fixes the name of the configuration node in sidebars.json. --- docs/features/software-catalog/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/software-catalog/configuration.md b/docs/features/software-catalog/configuration.md index 46de2d3d67..dd19d4cd51 100644 --- a/docs/features/software-catalog/configuration.md +++ b/docs/features/software-catalog/configuration.md @@ -1,5 +1,5 @@ --- -id: software-catalog-configuration +id: configuration title: Catalog Configuration description: Documentation on Software Catalog Configuration --- From 763ec1cd2f9c62fd4a7fb62fe0f41c1dfaaac832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Sun, 18 Oct 2020 08:20:06 +0200 Subject: [PATCH 09/44] Another try --- plugins/user-settings/src/components/General/PinButton.test.tsx | 2 +- plugins/user-settings/src/components/General/PinButton.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/user-settings/src/components/General/PinButton.test.tsx b/plugins/user-settings/src/components/General/PinButton.test.tsx index 88f636d918..d85fa3ddd9 100644 --- a/plugins/user-settings/src/components/General/PinButton.test.tsx +++ b/plugins/user-settings/src/components/General/PinButton.test.tsx @@ -35,7 +35,7 @@ describe('', () => { expect(rendered.getByText('Pin Sidebar')).toBeInTheDocument(); const pinButton = rendered.getByTestId('pin'); - fireEvent.click(pinButton); + fireEvent.change(pinButton); expect(mockToggleFn).toHaveBeenCalled(); }); }); diff --git a/plugins/user-settings/src/components/General/PinButton.tsx b/plugins/user-settings/src/components/General/PinButton.tsx index bfcdb0c7ff..a812079133 100644 --- a/plugins/user-settings/src/components/General/PinButton.tsx +++ b/plugins/user-settings/src/components/General/PinButton.tsx @@ -47,6 +47,7 @@ export const PinButton = () => { onChange={() => toggleSidebarPinState()} name="pin" data-test-id="pin" + inputProps={{ 'aria-label': 'Pin Sidebar Switch' }} /> From edeb5e45a304bb4c1fe5a2322bead66ec101aab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matic=20Ples=CC=8Cec?= Date: Sun, 18 Oct 2020 18:19:09 +0200 Subject: [PATCH 10/44] Update docs regarding npm config ignore-scripts flag --- docs/getting-started/create-an-app.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/getting-started/create-an-app.md b/docs/getting-started/create-an-app.md index 9be0e24452..670c7fbf98 100644 --- a/docs/getting-started/create-an-app.md +++ b/docs/getting-started/create-an-app.md @@ -124,3 +124,21 @@ the root directory: ```bash yarn workspace backend start ``` + +### Troubleshooting + +#### Cannot find module + +You may encounter an error similar to below: + +``` +internal/modules/cjs/loader.js:968 + throw err; + ^ + +Error: Cannot find module '../build/Debug/nodegit.node' +``` + +This can occur if an npm dependency is not completely installed. Because some +dependencies run a post-install script, ensure that both your npm and yarn +configs have the `ignore-scripts` flag set to `false`. From 4036ff59d01de97f08eff7f7b03e2ab0e6aead92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Sun, 18 Oct 2020 21:16:35 +0200 Subject: [PATCH 11/44] feat(catalog-backend): First steps of refactoring - split processEntity --- .changeset/carpal-tunnel-driver.md | 13 + .../src/ingestion/LocationReaders.ts | 72 +++- .../AnnotateLocationEntityProcessor.ts | 5 +- .../processors/CodeOwnersProcessor.test.ts | 8 +- .../processors/CodeOwnersProcessor.ts | 5 +- .../processors/EntityPolicyProcessor.ts | 34 -- .../processors/LocationEntityProcessor.ts | 2 +- .../processors/PlaceholderProcessor.test.ts | 20 +- .../processors/PlaceholderProcessor.ts | 5 +- .../src/ingestion/processors/index.ts | 1 - .../src/ingestion/processors/types.ts | 26 +- .../src/service/CatalogBuilder.test.ts | 91 ++--- .../src/service/CatalogBuilder.ts | 316 +++++------------- 13 files changed, 257 insertions(+), 341 deletions(-) create mode 100644 .changeset/carpal-tunnel-driver.md delete mode 100644 plugins/catalog-backend/src/ingestion/processors/EntityPolicyProcessor.ts diff --git a/.changeset/carpal-tunnel-driver.md b/.changeset/carpal-tunnel-driver.md new file mode 100644 index 0000000000..2e6a814533 --- /dev/null +++ b/.changeset/carpal-tunnel-driver.md @@ -0,0 +1,13 @@ +--- +'@backstage/plugin-catalog-backend': minor +--- + +- The `CatalogProcessor` API was updated to have `preProcessEntity` and + `postProcessEntity` methods, instead of just one `processEntity`. This makes + it easier to make processors that have several stages in one, and to make + different processors more position independent in the list of processors. +- The `EntityPolicy` is now given directly to the `LocationReaders`, instead of + being enforced inside a policy. We have decided to separate out the act of + validating an entity to be outside of the processing flow, to make it + possible to apply more liberally and to evolve it as a separate concept. +- Because of the above, the `EntityPolicyProcessor` has been removed. diff --git a/plugins/catalog-backend/src/ingestion/LocationReaders.ts b/plugins/catalog-backend/src/ingestion/LocationReaders.ts index c36b3d2b05..e23a4524b6 100644 --- a/plugins/catalog-backend/src/ingestion/LocationReaders.ts +++ b/plugins/catalog-backend/src/ingestion/LocationReaders.ts @@ -17,6 +17,7 @@ import { UrlReader } from '@backstage/backend-common'; import { Entity, + EntityPolicy, ENTITY_DEFAULT_NAMESPACE, LocationSpec, } from '@backstage/catalog-model'; @@ -44,6 +45,7 @@ type Options = { config: Config; processors: CatalogProcessor[]; rulesEnforcer: CatalogRulesEnforcer; + policy: EntityPolicy; }; /** @@ -74,10 +76,12 @@ export class LocationReaders implements LocationReader { } else if (item.type === 'entity') { if (rulesEnforcer.isAllowed(item.entity, item.location)) { const entity = await this.handleEntity(item, emit); - output.entities.push({ - entity, - location: item.location, - }); + if (entity) { + output.entities.push({ + entity, + location: item.location, + }); + } } else { output.errors.push({ location: item.location, @@ -162,25 +166,65 @@ export class LocationReaders implements LocationReader { private async handleEntity( item: CatalogProcessorEntityResult, emit: CatalogProcessorEmit, - ): Promise { + ): Promise { const { processors, logger } = this.options; let current = item.entity; + // Construct the name carefully, this happens before validation below + // so we do not want to crash here due to missing metadata or so + const kind = current.kind || ''; + const namespace = !current.metadata + ? '' + : current.metadata.namespace ?? ENTITY_DEFAULT_NAMESPACE; + const name = !current.metadata ? '' : current.metadata.name; + for (const processor of processors) { - if (processor.processEntity) { + if (processor.preProcessEntity) { try { - current = await processor.processEntity(current, item.location, emit); + current = await processor.preProcessEntity( + current, + item.location, + emit, + ); } catch (e) { - // Construct the name carefully, if we got validation errors we do - // not want to crash here due to missing metadata or so - const namespace = !current.metadata - ? '' - : current.metadata.namespace ?? ENTITY_DEFAULT_NAMESPACE; - const name = !current.metadata ? '' : current.metadata.name; - const message = `Processor ${processor.constructor.name} threw an error while processing entity ${current.kind}:${namespace}/${name} at ${item.location.type} ${item.location.target}, ${e}`; + const message = `Processor ${processor.constructor.name} threw an error while preprocessing entity ${kind}:${namespace}/${name} at ${item.location.type} ${item.location.target}, ${e}`; emit(result.generalError(item.location, message)); logger.warn(message); + return undefined; + } + } + } + + try { + const next = await this.options.policy.enforce(current); + if (!next) { + const message = `Policy unexpectedly returned no data while analyzing entity ${kind}:${namespace}/${name} at ${item.location.type} ${item.location.target}`; + emit(result.generalError(item.location, message)); + logger.warn(message); + return undefined; + } + current = next; + } catch (e) { + const message = `Policy check failed while analyzing entity ${kind}:${namespace}/${name} at ${item.location.type} ${item.location.target}, ${e}`; + emit(result.inputError(item.location, message)); + logger.warn(message); + return undefined; + } + + for (const processor of processors) { + if (processor.postProcessEntity) { + try { + current = await processor.postProcessEntity( + current, + item.location, + emit, + ); + } catch (e) { + const message = `Processor ${processor.constructor.name} threw an error while postprocessing entity ${kind}:${namespace}/${name} at ${item.location.type} ${item.location.target}, ${e}`; + emit(result.generalError(item.location, message)); + logger.warn(message); + return undefined; } } } diff --git a/plugins/catalog-backend/src/ingestion/processors/AnnotateLocationEntityProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/AnnotateLocationEntityProcessor.ts index fea70b6c5e..ea8afcddc5 100644 --- a/plugins/catalog-backend/src/ingestion/processors/AnnotateLocationEntityProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/AnnotateLocationEntityProcessor.ts @@ -19,7 +19,10 @@ import lodash from 'lodash'; import { CatalogProcessor } from './types'; export class AnnotateLocationEntityProcessor implements CatalogProcessor { - async processEntity(entity: Entity, location: LocationSpec): Promise { + async preProcessEntity( + entity: Entity, + location: LocationSpec, + ): Promise { return lodash.merge( { metadata: { diff --git a/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts index c1c83b7c6c..247020a547 100644 --- a/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.test.ts @@ -224,7 +224,7 @@ describe(CodeOwnersProcessor, () => { spec: { owner: '@acme/foo-team' }, }); - const result = await processor.processEntity( + const result = await processor.preProcessEntity( entity as any, mockLocation(), ); @@ -235,7 +235,7 @@ describe(CodeOwnersProcessor, () => { it('should ignore url locations', async () => { const { entity, processor } = setupTest(); - const result = await processor.processEntity( + const result = await processor.preProcessEntity( entity as any, mockLocation({ type: 'url' }), ); @@ -246,7 +246,7 @@ describe(CodeOwnersProcessor, () => { it('should ignore invalid kinds', async () => { const { entity, processor } = setupTest({ kind: 'Group' }); - const result = await processor.processEntity( + const result = await processor.preProcessEntity( entity as any, mockLocation(), ); @@ -257,7 +257,7 @@ describe(CodeOwnersProcessor, () => { it('should set owner from codeowner', async () => { const { entity, processor } = setupTest(); - const result = await processor.processEntity( + const result = await processor.preProcessEntity( entity as any, mockLocation(), ); diff --git a/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.ts index 1802e01d99..bc78b6e34a 100644 --- a/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.ts @@ -40,7 +40,10 @@ type Options = { export class CodeOwnersProcessor implements CatalogProcessor { constructor(private readonly options: Options) {} - async processEntity(entity: Entity, location: LocationSpec): Promise { + async preProcessEntity( + entity: Entity, + location: LocationSpec, + ): Promise { // Only continue if the owner is not set if ( !entity || diff --git a/plugins/catalog-backend/src/ingestion/processors/EntityPolicyProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/EntityPolicyProcessor.ts deleted file mode 100644 index 87543d91f4..0000000000 --- a/plugins/catalog-backend/src/ingestion/processors/EntityPolicyProcessor.ts +++ /dev/null @@ -1,34 +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 { Entity, EntityPolicy } from '@backstage/catalog-model'; -import { CatalogProcessor } from './types'; - -export class EntityPolicyProcessor implements CatalogProcessor { - private readonly policy: EntityPolicy; - - constructor(policy: EntityPolicy) { - this.policy = policy; - } - - async processEntity(entity: Entity): Promise { - const output = await this.policy.enforce(entity); - if (!output) { - throw new Error(`Entity did not match any known schema`); - } - return output; - } -} diff --git a/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.ts index 1917f58fbd..86e17f07f2 100644 --- a/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.ts @@ -19,7 +19,7 @@ import * as result from './results'; import { CatalogProcessor, CatalogProcessorEmit } from './types'; export class LocationRefProcessor implements CatalogProcessor { - async processEntity( + async postProcessEntity( entity: Entity, _location: LocationSpec, emit: CatalogProcessorEmit, diff --git a/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts index 294cb10f29..44f3163a97 100644 --- a/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts @@ -46,7 +46,7 @@ describe('PlaceholderProcessor', () => { reader, }); await expect( - processor.processEntity(input, { type: 't', target: 'l' }), + processor.preProcessEntity(input, { type: 't', target: 'l' }), ).resolves.toBe(input); }); @@ -62,7 +62,7 @@ describe('PlaceholderProcessor', () => { }); await expect( - processor.processEntity( + processor.preProcessEntity( { apiVersion: 'a', kind: 'k', @@ -98,7 +98,7 @@ describe('PlaceholderProcessor', () => { }); await expect( - processor.processEntity( + processor.preProcessEntity( { apiVersion: 'a', kind: 'k', @@ -122,7 +122,7 @@ describe('PlaceholderProcessor', () => { }); await expect( - processor.processEntity( + processor.preProcessEntity( { apiVersion: 'a', kind: 'k', @@ -143,7 +143,7 @@ describe('PlaceholderProcessor', () => { }); await expect( - processor.processEntity( + processor.preProcessEntity( { apiVersion: 'a', kind: 'k', @@ -177,7 +177,7 @@ describe('PlaceholderProcessor', () => { }); await expect( - processor.processEntity( + processor.preProcessEntity( { apiVersion: 'a', kind: 'k', @@ -209,7 +209,7 @@ describe('PlaceholderProcessor', () => { }); await expect( - processor.processEntity( + processor.preProcessEntity( { apiVersion: 'a', kind: 'k', @@ -241,7 +241,7 @@ describe('PlaceholderProcessor', () => { }); await expect( - processor.processEntity( + processor.preProcessEntity( { apiVersion: 'a', kind: 'k', @@ -277,7 +277,7 @@ describe('PlaceholderProcessor', () => { }); await expect( - processor.processEntity( + processor.preProcessEntity( { apiVersion: 'a', kind: 'k', @@ -316,7 +316,7 @@ describe('PlaceholderProcessor', () => { }); await expect( - processor.processEntity( + processor.preProcessEntity( { apiVersion: 'a', kind: 'k', diff --git a/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.ts index 3941a2bedc..0e23d51f61 100644 --- a/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.ts @@ -45,7 +45,10 @@ type Options = { export class PlaceholderProcessor implements CatalogProcessor { constructor(private readonly options: Options) {} - async processEntity(entity: Entity, location: LocationSpec): Promise { + async preProcessEntity( + entity: Entity, + location: LocationSpec, + ): Promise { const process = async (data: any): Promise<[any, boolean]> => { if (!data || !(data instanceof Object)) { // Scalars can't have placeholders diff --git a/plugins/catalog-backend/src/ingestion/processors/index.ts b/plugins/catalog-backend/src/ingestion/processors/index.ts index f966ef3348..59a87af555 100644 --- a/plugins/catalog-backend/src/ingestion/processors/index.ts +++ b/plugins/catalog-backend/src/ingestion/processors/index.ts @@ -23,7 +23,6 @@ export { AnnotateLocationEntityProcessor } from './AnnotateLocationEntityProcess export { AzureApiReaderProcessor } from './AzureApiReaderProcessor'; export { BitbucketApiReaderProcessor } from './BitbucketApiReaderProcessor'; export { CodeOwnersProcessor } from './CodeOwnersProcessor'; -export { EntityPolicyProcessor } from './EntityPolicyProcessor'; export { FileReaderProcessor } from './FileReaderProcessor'; export { GithubOrgReaderProcessor } from './GithubOrgReaderProcessor'; export { GithubReaderProcessor } from './GithubReaderProcessor'; diff --git a/plugins/catalog-backend/src/ingestion/processors/types.ts b/plugins/catalog-backend/src/ingestion/processors/types.ts index 0e6b1491a6..c9adf07eff 100644 --- a/plugins/catalog-backend/src/ingestion/processors/types.ts +++ b/plugins/catalog-backend/src/ingestion/processors/types.ts @@ -46,15 +46,33 @@ export type CatalogProcessor = { ): Promise; /** - * Processes an emitted entity, e.g. by validating or modifying it. + * Pre-processes an emitted entity, after it has been emitted but before it + * has been validated. * - * @param entity The entity to process + * This type of processing usually involves enriching the entity with + * additional data, and the input entity may actually still be incomplete + * when the processor is invoked. + * + * @param entity The (possibly partial) entity to process * @param location The location that the entity came from - * @param read Reads the contents of a location * @param emit A sink for auxiliary items resulting from the processing * @returns The same entity or a modified version of it */ - processEntity?( + preProcessEntity?( + entity: Entity, + location: LocationSpec, + emit: CatalogProcessorEmit, + ): Promise; + + /** + * Post-processes an emitted entity, after it has been validated. + * + * @param entity The entity to process + * @param location The location that the entity came from + * @param emit A sink for auxiliary items resulting from the processing + * @returns The same entity or a modified version of it + */ + postProcessEntity?( entity: Entity, location: LocationSpec, emit: CatalogProcessorEmit, diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.test.ts b/plugins/catalog-backend/src/service/CatalogBuilder.test.ts index 66e7dba682..3f7c613c00 100644 --- a/plugins/catalog-backend/src/service/CatalogBuilder.test.ts +++ b/plugins/catalog-backend/src/service/CatalogBuilder.test.ts @@ -49,49 +49,6 @@ describe('CatalogBuilder', () => { reader.read.mockResolvedValue(Buffer.from('junk')); const builder = new CatalogBuilder(env) - .replaceReaderProcessors([ - { - async readLocation( - location: LocationSpec, - _optional: boolean, - emit: CatalogProcessorEmit, - ) { - expect(location.type).toBe('test'); - emit(result.data(location, await reader.read('ignored'))); - return true; - }, - }, - ]) - .replaceParserProcessors([ - { - async parseData( - data: Buffer, - location: LocationSpec, - emit: CatalogProcessorEmit, - ) { - expect(data.toString()).toEqual('junk'); - emit( - result.entity(location, { - apiVersion: 'av', - kind: 'Component', - metadata: { name: 'n' }, - }), - ); - return true; - }, - }, - ]) - .replacePreProcessors([ - { - async processEntity(entity: Entity) { - expect(entity.apiVersion).toBe('av'); - return { - ...entity, - metadata: { ...entity.metadata, namespace: 'ns' }, - }; - }, - }, - ]) .replaceEntityPolicies([ { async enforce(entity: Entity) { @@ -108,9 +65,51 @@ describe('CatalogBuilder', () => { }, }, ]) - .replacePostProcessors([ + .setPlaceholderResolver('t', async ({ value }) => { + expect(value).toBe('tt'); + return 'tt2'; + }) + .setFieldFormatValidators({ + isValidEntityName: n => { + expect(n).toBe('n'); + return true; + }, + }) + .replaceProcessors([ { - async processEntity(entity: Entity) { + async readLocation( + location: LocationSpec, + _optional: boolean, + emit: CatalogProcessorEmit, + ) { + expect(location.type).toBe('test'); + emit(result.data(location, await reader.read('ignored'))); + return true; + }, + async parseData( + data: Buffer, + location: LocationSpec, + emit: CatalogProcessorEmit, + ) { + expect(data.toString()).toEqual('junk'); + emit( + result.entity(location, { + apiVersion: 'av', + kind: 'Component', + metadata: { name: 'n', replaced: { $t: 'tt' } }, + }), + ); + return true; + }, + async preProcessEntity(entity: Entity) { + expect(entity.apiVersion).toBe('av'); + return { + ...entity, + metadata: { ...entity.metadata, namespace: 'ns' }, + }; + }, + async postProcessEntity(entity: Entity) { + expect(entity.metadata.namespace).toBe('ns'); return { ...entity, metadata: { ...entity.metadata, post: 'p' }, @@ -124,6 +123,7 @@ describe('CatalogBuilder', () => { type: 'test', target: '', }); + expect.assertions(8); expect(added.entities).toEqual([ { apiVersion: 'av', @@ -132,6 +132,7 @@ describe('CatalogBuilder', () => { name: 'n', namespace: 'ns', post: 'p', + replaced: 'tt2', }), }, ]); diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.ts b/plugins/catalog-backend/src/service/CatalogBuilder.ts index aa4f97258d..01a4cd2435 100644 --- a/plugins/catalog-backend/src/service/CatalogBuilder.ts +++ b/plugins/catalog-backend/src/service/CatalogBuilder.ts @@ -48,7 +48,6 @@ import { BitbucketApiReaderProcessor, CatalogProcessor, CodeOwnersProcessor, - EntityPolicyProcessor, FileReaderProcessor, GithubOrgReaderProcessor, GithubReaderProcessor, @@ -84,36 +83,24 @@ export type CatalogEnvironment = { * * The touch points where you can replace or extend behavior are as follows: * - * - Reader processors can be added or replaced. These implement the - * functionality of reading raw data from a location, in the form of an - * entity definition file. - * - Parser processors can be added or replaced. These accept the raw data as - * read by the previous processors and parse it into raw structured data - * (for example, from a binary buffer containing yaml text, to a JS object - * structure). + * - Entity policies can be added or replaced. These are automatically run + * after the processors' pre-processing steps. All policies are given the + * chance to inspect the entity, and all of them have to pass in order for + * the entity to be considered valid from an overall point of view. + * - Entity kinds can be added or replaced. These are the second line of + * validation that is applied after the entity policies, which adds + * additional kind-specific validation (usually based on a schema). Only one + * of the entity kinds has to accept the entity, but if none of them do, the + * entity is rejected as a whole. * - Placeholder resolvers can be replaced or added. These run on the raw * structured data between the parsing and pre-processing steps, to replace * dollar-prefixed entries with their actual values (like $file). - * - Pre-processors can be added or replaced. These take the raw unvalidated - * data from the parser processors and can enrich or extend it before - * validation. This is the place where for example codeowners data can be - * injected into partial entity definitions. - * - Entity policies can be added or replaced. These are the first line of - * validation from the output of the pre-processing step. All policies are - * given the chance to inspect the entity, and all of them have to pass in - * order for the entity to be considered valid from an overall point of - * view. * - Field format validators can be replaced. These check the format of - * individual core fields such as metadata.name, such that they adhere to - * certain rules. - * - Entity kinds can be added or replaced. These are the second line of - * validation that is applied after the entity policies, which add additional - * kind-specific validation (usually based on a schema). Only one of the - * entity kinds has to accept the entity, but if none of them do, the - * entity is rejected as a whole. - * - Post-processors can be added or replaced. These take the validated - * entities out of the validation step and can perform additional actions on - * them. + * individual core fields such as metadata.name, to ensure that they adhere + * to certain rules. + * - Processors can be added or replaced. These implement the functionality of + * reading, parsing and processing the entity data before it is persisted in + * the catalog. */ export class CatalogBuilder { private readonly env: CatalogEnvironment; @@ -121,16 +108,10 @@ export class CatalogBuilder { private entityPoliciesReplace: boolean; private entityKinds: EntityPolicy[]; private entityKindsReplace: boolean; - private readerProcessors: CatalogProcessor[]; - private readerProcessorsReplace: boolean; - private parserProcessors: CatalogProcessor[]; - private parserProcessorsReplace: boolean; - private preProcessors: CatalogProcessor[]; - private preProcessorsReplace: boolean; - private postProcessors: CatalogProcessor[]; - private postProcessorsReplace: boolean; private placeholderResolvers: Record; private fieldFormatValidators: Partial; + private processors: CatalogProcessor[]; + private processorsReplace: boolean; constructor(env: CatalogEnvironment) { this.env = env; @@ -138,16 +119,10 @@ export class CatalogBuilder { this.entityPoliciesReplace = false; this.entityKinds = []; this.entityKindsReplace = false; - this.readerProcessors = []; - this.readerProcessorsReplace = false; - this.parserProcessors = []; - this.parserProcessorsReplace = false; - this.preProcessors = []; - this.preProcessorsReplace = false; - this.postProcessors = []; - this.postProcessorsReplace = false; this.placeholderResolvers = {}; this.fieldFormatValidators = {}; + this.processors = []; + this.processorsReplace = false; } /** @@ -212,114 +187,6 @@ export class CatalogBuilder { return this; } - /** - * Adds processors that support reading of definition files. These are run - * before the entities are parsed, pre-processed, validated and post- - * processed. - * - * @param processors One or more processors - */ - addReaderProcessor(...processors: CatalogProcessor[]): CatalogBuilder { - this.readerProcessors.push(...processors); - return this; - } - - /** - * Sets what processors to use for the reading of definition files. These are - * run before the entities are parsed, pre-processed, validated and post- - * processed. - * - * This function replaces the default set of processors in this stage; use - * with care. - * - * @param processors One or more processors - */ - replaceReaderProcessors(processors: CatalogProcessor[]): CatalogBuilder { - this.readerProcessors = [...processors]; - this.readerProcessorsReplace = true; - return this; - } - - /** - * Adds processors that run after each definition file has been read, in - * order to parse the raw data. These are run before the entities are - * pre-processed, validated and post-processed. - * - * @param processors One or more processors - */ - addParserProcessor(...processors: CatalogProcessor[]): CatalogBuilder { - this.parserProcessors.push(...processors); - return this; - } - - /** - * Sets what processors to run after each definition file has been read, in - * order to parse the raw data. These are run before the entities are - * pre-processed, validated and post-processed. - * - * This function replaces the default set of processors in this stage; use - * with care. - * - * @param processors One or more processors - */ - replaceParserProcessors(processors: CatalogProcessor[]): CatalogBuilder { - this.parserProcessors = [...processors]; - this.parserProcessorsReplace = true; - return this; - } - - /** - * Adds processors that run after each entity has been read and parsed, - * but before being validated and post-processed. - * - * @param processors One or more processors - */ - addPreProcessor(...processors: CatalogProcessor[]): CatalogBuilder { - this.preProcessors.push(...processors); - return this; - } - - /** - * Sets what processors to run after each entity has been read and parsed, - * but before being validated and post-processed. - * - * This function replaces the default set of processors in this stage; use - * with care. - * - * @param processors One or more processors - */ - replacePreProcessors(processors: CatalogProcessor[]): CatalogBuilder { - this.preProcessors = [...processors]; - this.preProcessorsReplace = true; - return this; - } - - /** - * Adds processors that run after each entity has been read, parsed, - * run through the pre-processors, and validated. - * - * @param processors One or more processors - */ - addPostProcessor(...processors: CatalogProcessor[]): CatalogBuilder { - this.postProcessors.push(...processors); - return this; - } - - /** - * Sets what processors to run after each entity has been read, parsed, - * run through the pre-processors, and validated. - * - * This function replaces the default set of processors in this stage; use - * with care. - * - * @param processors One or more processors - */ - replacePostProcessors(processors: CatalogProcessor[]): CatalogBuilder { - this.postProcessors = [...processors]; - this.postProcessorsReplace = true; - return this; - } - /** * Adds, or overwrites, a handler for placeholders (e.g. $file) in entity * definition files. @@ -327,8 +194,12 @@ export class CatalogBuilder { * @param key The key that identifies the placeholder, e.g. "file" * @param resolver The resolver that gets values for this placeholder */ - setPlaceholderResolver(key: string, resolver: PlaceholderResolver) { + setPlaceholderResolver( + key: string, + resolver: PlaceholderResolver, + ): CatalogBuilder { this.placeholderResolvers[key] = resolver; + return this; } /** @@ -341,8 +212,34 @@ export class CatalogBuilder { * * @param validators The (subset of) validators to set */ - setFieldFormatValidators(validators: Partial) { + setFieldFormatValidators(validators: Partial): CatalogBuilder { lodash.merge(this.fieldFormatValidators, validators); + return this; + } + + /** + * Adds entity processors. These are responsible for reading, parsing, and + * processing entities before they are persisted in the catalog. + * + * @param processors One or more processors + */ + addProcessor(...processors: CatalogProcessor[]): CatalogBuilder { + this.processors.push(...processors); + return this; + } + + /** + * Sets what entity processors to use. These are responsible for reading, + * parsing, and processing entities before they are persisted in the catalog. + * + * This function replaces the default set of processors; use with care. + * + * @param processors One or more processors + */ + replaceProcessors(processors: CatalogProcessor[]): CatalogBuilder { + this.processors = [...processors]; + this.processorsReplace = true; + return this; } /** @@ -355,14 +252,15 @@ export class CatalogBuilder { }> { const { config, database, logger } = this.env; - const entityPolicy = this.buildEntityPolicy(); - const processors = this.buildProcessors(entityPolicy); + const policy = this.buildEntityPolicy(); + const processors = this.buildProcessors(); const rulesEnforcer = CatalogRulesEnforcer.fromConfig(config); const locationReader = new LocationReaders({ ...this.env, processors, rulesEnforcer, + policy, }); const db = await DatabaseManager.createDatabase( @@ -418,106 +316,74 @@ export class CatalogBuilder { ]); } - private buildProcessors(entityPolicy: EntityPolicy): CatalogProcessor[] { - const { config, reader } = this.env; + private buildProcessors(): CatalogProcessor[] { + const { config, logger, reader } = this.env; - const placeholderResolvers = lodash.merge( - { - json: jsonPlaceholderResolver, - yaml: yamlPlaceholderResolver, - text: textPlaceholderResolver, - }, - this.placeholderResolvers, - ); + const placeholderResolvers: Record = { + json: jsonPlaceholderResolver, + yaml: yamlPlaceholderResolver, + text: textPlaceholderResolver, + ...this.placeholderResolvers, + }; + + const processors = this.processorsReplace + ? this.processors + : [ + new FileReaderProcessor(), + GithubOrgReaderProcessor.fromConfig(config, { logger }), + LdapOrgReaderProcessor.fromConfig(config, { logger }), + new UrlReaderProcessor({ reader, logger }), + new YamlProcessor(), + new CodeOwnersProcessor({ reader }), + new LocationRefProcessor(), + new AnnotateLocationEntityProcessor(), + ]; return [ StaticLocationProcessor.fromConfig(config), - ...this.buildReaderProcessors(), - ...this.buildParserProcessors(), new PlaceholderProcessor({ resolvers: placeholderResolvers, reader }), - ...this.buildPreProcessors(), - new EntityPolicyProcessor(entityPolicy), - ...this.buildPostProcessors(), + ...this.buildDeprecatedReaderProcessors(), + ...processors, ]; } - private buildReaderProcessors(): CatalogProcessor[] { - const { config, logger, reader } = this.env; + // TODO(Rugvip): These are added for backwards compatibility if config exists + // The idea is to have everyone migrate from using the old processors to + // the new integration config driven UrlReaders. In an upcoming release we + // can then completely remove support for the old processors, but still + // keep handling the deprecated location types for a while, but with a + // warning. + private buildDeprecatedReaderProcessors(): CatalogProcessor[] { + const { config, logger } = this.env; - if (this.readerProcessorsReplace) { - return this.readerProcessors; - } - - // TODO(Rugvip): These are added for backwards compatibility if config exists - // The idea is to have everyone migrate from using the old processors to the new - // integration config driven UrlReaders. In an upcoming release we can then completely - // remove support for the old processors, but still keep handling the deprecated location - // types for a while, but with a warning. - const oldProcessors = []; + const result = []; const pc = config.getOptionalConfig('catalog.processors'); if (pc?.has('github')) { logger.warn( `Using deprecated configuration for catalog.processors.github, move to using integrations.github instead`, ); - oldProcessors.push(GithubReaderProcessor.fromConfig(config, logger)); + result.push(GithubReaderProcessor.fromConfig(config, logger)); } if (pc?.has('gitlabApi')) { logger.warn( `Using deprecated configuration for catalog.processors.gitlabApi, move to using integrations.gitlab instead`, ); - oldProcessors.push(new GitlabApiReaderProcessor(config)); - oldProcessors.push(new GitlabReaderProcessor()); + result.push(new GitlabApiReaderProcessor(config)); + result.push(new GitlabReaderProcessor()); } if (pc?.has('bitbucketApi')) { logger.warn( `Using deprecated configuration for catalog.processors.bitbucketApi, move to using integrations.bitbucket instead`, ); - oldProcessors.push(new BitbucketApiReaderProcessor(config)); + result.push(new BitbucketApiReaderProcessor(config)); } if (pc?.has('azureApi')) { logger.warn( `Using deprecated configuration for catalog.processors.azureApi, move to using integrations.azure instead`, ); - oldProcessors.push(new AzureApiReaderProcessor(config)); + result.push(new AzureApiReaderProcessor(config)); } - return [ - new FileReaderProcessor(), - ...oldProcessors, - GithubOrgReaderProcessor.fromConfig(config, { logger }), - LdapOrgReaderProcessor.fromConfig(config, { logger }), - new UrlReaderProcessor({ reader, logger }), - ...this.readerProcessors, - ]; - } - - private buildParserProcessors(): CatalogProcessor[] { - if (this.parserProcessorsReplace) { - return this.parserProcessors; - } - - return [new YamlProcessor(), ...this.parserProcessors]; - } - - private buildPreProcessors(): CatalogProcessor[] { - const { reader } = this.env; - - if (this.preProcessorsReplace) { - return this.preProcessors; - } - - return [new CodeOwnersProcessor({ reader }), ...this.preProcessors]; - } - - private buildPostProcessors(): CatalogProcessor[] { - if (this.postProcessorsReplace) { - return this.postProcessors; - } - - return [ - new LocationRefProcessor(), - new AnnotateLocationEntityProcessor(), - ...this.postProcessors, - ]; + return result; } } From f2819b2a9f1d8f25ff4061eb53b9daefaa384d76 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 19 Oct 2020 01:32:35 +0200 Subject: [PATCH 12/44] core-api: move utility api system implementation into apis/system --- .../core-api/src/apis/definitions/AlertApi.ts | 2 +- .../src/apis/definitions/AppThemeApi.ts | 2 +- .../src/apis/definitions/ConfigApi.ts | 2 +- .../src/apis/definitions/DiscoveryApi.ts | 2 +- .../core-api/src/apis/definitions/ErrorApi.ts | 2 +- .../src/apis/definitions/FeatureFlagsApi.ts | 2 +- .../src/apis/definitions/IdentityApi.ts | 2 +- .../src/apis/definitions/OAuthRequestApi.ts | 2 +- .../src/apis/definitions/StorageApi.ts | 2 +- .../core-api/src/apis/definitions/auth.ts | 4 ++-- packages/core-api/src/apis/index.ts | 6 +---- .../apis/{ => system}/ApiAggregator.test.ts | 0 .../src/apis/{ => system}/ApiAggregator.ts | 3 +-- .../{ => system}/ApiFactoryRegistry.test.ts | 0 .../apis/{ => system}/ApiFactoryRegistry.ts | 2 +- .../apis/{ => system}/ApiProvider.test.tsx | 0 .../src/apis/{ => system}/ApiProvider.tsx | 3 +-- .../src/apis/{ => system}/ApiRef.test.ts | 0 .../core-api/src/apis/{ => system}/ApiRef.ts | 8 ++----- .../src/apis/{ => system}/ApiRegistry.test.ts | 0 .../src/apis/{ => system}/ApiRegistry.ts | 3 +-- .../src/apis/{ => system}/ApiResolver.test.ts | 0 .../src/apis/{ => system}/ApiResolver.ts | 2 +- .../core-api/src/apis/{ => system}/helpers.ts | 3 +-- packages/core-api/src/apis/system/index.ts | 23 +++++++++++++++++++ .../core-api/src/apis/{ => system}/types.ts | 6 ++++- packages/core-api/src/app/App.tsx | 3 +-- packages/core-api/src/plugin/types.ts | 2 +- 28 files changed, 50 insertions(+), 36 deletions(-) rename packages/core-api/src/apis/{ => system}/ApiAggregator.test.ts (100%) rename packages/core-api/src/apis/{ => system}/ApiAggregator.ts (93%) rename packages/core-api/src/apis/{ => system}/ApiFactoryRegistry.test.ts (100%) rename packages/core-api/src/apis/{ => system}/ApiFactoryRegistry.ts (98%) rename packages/core-api/src/apis/{ => system}/ApiProvider.test.tsx (100%) rename packages/core-api/src/apis/{ => system}/ApiProvider.tsx (96%) rename packages/core-api/src/apis/{ => system}/ApiRef.test.ts (100%) rename packages/core-api/src/apis/{ => system}/ApiRef.ts (95%) rename packages/core-api/src/apis/{ => system}/ApiRegistry.test.ts (100%) rename packages/core-api/src/apis/{ => system}/ApiRegistry.ts (96%) rename packages/core-api/src/apis/{ => system}/ApiResolver.test.ts (100%) rename packages/core-api/src/apis/{ => system}/ApiResolver.ts (98%) rename packages/core-api/src/apis/{ => system}/helpers.ts (93%) create mode 100644 packages/core-api/src/apis/system/index.ts rename packages/core-api/src/apis/{ => system}/types.ts (94%) diff --git a/packages/core-api/src/apis/definitions/AlertApi.ts b/packages/core-api/src/apis/definitions/AlertApi.ts index 123cd47651..d149940103 100644 --- a/packages/core-api/src/apis/definitions/AlertApi.ts +++ b/packages/core-api/src/apis/definitions/AlertApi.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { createApiRef } from '../ApiRef'; +import { createApiRef } from '../system'; import { Observable } from '../../types'; export type AlertMessage = { diff --git a/packages/core-api/src/apis/definitions/AppThemeApi.ts b/packages/core-api/src/apis/definitions/AppThemeApi.ts index 3f580450f0..019aa2f496 100644 --- a/packages/core-api/src/apis/definitions/AppThemeApi.ts +++ b/packages/core-api/src/apis/definitions/AppThemeApi.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createApiRef } from '../ApiRef'; +import { createApiRef } from '../system'; import { BackstageTheme } from '@backstage/theme'; import { Observable } from '../../types'; import { SvgIconProps } from '@material-ui/core'; diff --git a/packages/core-api/src/apis/definitions/ConfigApi.ts b/packages/core-api/src/apis/definitions/ConfigApi.ts index 1fa6e70d1f..6d34055f05 100644 --- a/packages/core-api/src/apis/definitions/ConfigApi.ts +++ b/packages/core-api/src/apis/definitions/ConfigApi.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { createApiRef } from '../ApiRef'; +import { createApiRef } from '../system'; import { Config } from '@backstage/config'; // Using interface to make the ConfigApi name show up in docs diff --git a/packages/core-api/src/apis/definitions/DiscoveryApi.ts b/packages/core-api/src/apis/definitions/DiscoveryApi.ts index b0773086c7..13ead7b929 100644 --- a/packages/core-api/src/apis/definitions/DiscoveryApi.ts +++ b/packages/core-api/src/apis/definitions/DiscoveryApi.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { createApiRef } from '../ApiRef'; +import { createApiRef } from '../system'; /** * The discovery API is used to provide a mechanism for plugins to diff --git a/packages/core-api/src/apis/definitions/ErrorApi.ts b/packages/core-api/src/apis/definitions/ErrorApi.ts index 7f9676ea5a..365f59f4a3 100644 --- a/packages/core-api/src/apis/definitions/ErrorApi.ts +++ b/packages/core-api/src/apis/definitions/ErrorApi.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createApiRef } from '../ApiRef'; +import { createApiRef } from '../system'; import { Observable } from '../../types'; /** diff --git a/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts b/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts index dd9657d7d8..0b7cddfe15 100644 --- a/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts +++ b/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createApiRef } from '../ApiRef'; +import { createApiRef } from '../system'; import { UserFlags, FeatureFlagsRegistry } from '../../app/FeatureFlags'; import { FeatureFlagName } from '../../plugin'; diff --git a/packages/core-api/src/apis/definitions/IdentityApi.ts b/packages/core-api/src/apis/definitions/IdentityApi.ts index 2684422b1e..87dc4ca989 100644 --- a/packages/core-api/src/apis/definitions/IdentityApi.ts +++ b/packages/core-api/src/apis/definitions/IdentityApi.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { createApiRef } from '../ApiRef'; +import { createApiRef } from '../system'; import { ProfileInfo } from './auth'; /** diff --git a/packages/core-api/src/apis/definitions/OAuthRequestApi.ts b/packages/core-api/src/apis/definitions/OAuthRequestApi.ts index 6fefbd1b9c..7bbebf90c1 100644 --- a/packages/core-api/src/apis/definitions/OAuthRequestApi.ts +++ b/packages/core-api/src/apis/definitions/OAuthRequestApi.ts @@ -16,7 +16,7 @@ import { IconComponent } from '../../icons'; import { Observable } from '../../types'; -import { createApiRef } from '../ApiRef'; +import { createApiRef } from '../system'; /** * Information about the auth provider that we're requesting a login towards. diff --git a/packages/core-api/src/apis/definitions/StorageApi.ts b/packages/core-api/src/apis/definitions/StorageApi.ts index 920ee56811..923633318b 100644 --- a/packages/core-api/src/apis/definitions/StorageApi.ts +++ b/packages/core-api/src/apis/definitions/StorageApi.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createApiRef } from '../ApiRef'; +import { createApiRef } from '../system'; import { Observable } from '../../types'; import { ErrorApi } from './ErrorApi'; diff --git a/packages/core-api/src/apis/definitions/auth.ts b/packages/core-api/src/apis/definitions/auth.ts index 109f15b26e..5ffe312591 100644 --- a/packages/core-api/src/apis/definitions/auth.ts +++ b/packages/core-api/src/apis/definitions/auth.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { createApiRef } from '../ApiRef'; -import { Observable } from '../..'; +import { createApiRef } from '../system'; +import { Observable } from '../../types'; /** * This file contains declarations for common interfaces of auth-related APIs. diff --git a/packages/core-api/src/apis/index.ts b/packages/core-api/src/apis/index.ts index c856a13668..03569f9570 100644 --- a/packages/core-api/src/apis/index.ts +++ b/packages/core-api/src/apis/index.ts @@ -14,10 +14,6 @@ * limitations under the License. */ -export { ApiProvider, useApi, useApiHolder } from './ApiProvider'; -export { ApiRegistry } from './ApiRegistry'; -export * from './ApiRef'; -export * from './types'; -export * from './helpers'; +export * from './system'; export * from './definitions'; export * from './implementations'; diff --git a/packages/core-api/src/apis/ApiAggregator.test.ts b/packages/core-api/src/apis/system/ApiAggregator.test.ts similarity index 100% rename from packages/core-api/src/apis/ApiAggregator.test.ts rename to packages/core-api/src/apis/system/ApiAggregator.test.ts diff --git a/packages/core-api/src/apis/ApiAggregator.ts b/packages/core-api/src/apis/system/ApiAggregator.ts similarity index 93% rename from packages/core-api/src/apis/ApiAggregator.ts rename to packages/core-api/src/apis/system/ApiAggregator.ts index da49ee6488..1587a1d10b 100644 --- a/packages/core-api/src/apis/ApiAggregator.ts +++ b/packages/core-api/src/apis/system/ApiAggregator.ts @@ -14,8 +14,7 @@ * limitations under the License. */ -import { ApiRef } from './ApiRef'; -import { ApiHolder } from './types'; +import { ApiRef, ApiHolder } from './types'; /** * An ApiHolder that queries multiple other holders from for diff --git a/packages/core-api/src/apis/ApiFactoryRegistry.test.ts b/packages/core-api/src/apis/system/ApiFactoryRegistry.test.ts similarity index 100% rename from packages/core-api/src/apis/ApiFactoryRegistry.test.ts rename to packages/core-api/src/apis/system/ApiFactoryRegistry.test.ts diff --git a/packages/core-api/src/apis/ApiFactoryRegistry.ts b/packages/core-api/src/apis/system/ApiFactoryRegistry.ts similarity index 98% rename from packages/core-api/src/apis/ApiFactoryRegistry.ts rename to packages/core-api/src/apis/system/ApiFactoryRegistry.ts index 0d36e6c550..556f63589c 100644 --- a/packages/core-api/src/apis/ApiFactoryRegistry.ts +++ b/packages/core-api/src/apis/system/ApiFactoryRegistry.ts @@ -15,12 +15,12 @@ */ import { + ApiRef, ApiFactoryHolder, ApiFactory, AnyApiRef, AnyApiFactory, } from './types'; -import { ApiRef } from './ApiRef'; type ApiFactoryScope = | 'default' // Default factories registered by core and plugins diff --git a/packages/core-api/src/apis/ApiProvider.test.tsx b/packages/core-api/src/apis/system/ApiProvider.test.tsx similarity index 100% rename from packages/core-api/src/apis/ApiProvider.test.tsx rename to packages/core-api/src/apis/system/ApiProvider.test.tsx diff --git a/packages/core-api/src/apis/ApiProvider.tsx b/packages/core-api/src/apis/system/ApiProvider.tsx similarity index 96% rename from packages/core-api/src/apis/ApiProvider.tsx rename to packages/core-api/src/apis/system/ApiProvider.tsx index 24610a1372..f2aa70244e 100644 --- a/packages/core-api/src/apis/ApiProvider.tsx +++ b/packages/core-api/src/apis/system/ApiProvider.tsx @@ -16,8 +16,7 @@ import React, { FC, createContext, useContext, ReactNode } from 'react'; import PropTypes from 'prop-types'; -import { ApiRef } from './ApiRef'; -import { ApiHolder, TypesToApiRefs } from './types'; +import { ApiRef, ApiHolder, TypesToApiRefs } from './types'; import { ApiAggregator } from './ApiAggregator'; type ApiProviderProps = { diff --git a/packages/core-api/src/apis/ApiRef.test.ts b/packages/core-api/src/apis/system/ApiRef.test.ts similarity index 100% rename from packages/core-api/src/apis/ApiRef.test.ts rename to packages/core-api/src/apis/system/ApiRef.test.ts diff --git a/packages/core-api/src/apis/ApiRef.ts b/packages/core-api/src/apis/system/ApiRef.ts similarity index 95% rename from packages/core-api/src/apis/ApiRef.ts rename to packages/core-api/src/apis/system/ApiRef.ts index 793c1e5386..0e1eb177c8 100644 --- a/packages/core-api/src/apis/ApiRef.ts +++ b/packages/core-api/src/apis/system/ApiRef.ts @@ -14,17 +14,13 @@ * limitations under the License. */ +import type { ApiRef } from './types'; + export type ApiRefConfig = { id: string; description: string; }; -export type ApiRef = { - id: string; - description: string; - T: T; -}; - class ApiRefImpl implements ApiRef { constructor(private readonly config: ApiRefConfig) { const valid = config.id diff --git a/packages/core-api/src/apis/ApiRegistry.test.ts b/packages/core-api/src/apis/system/ApiRegistry.test.ts similarity index 100% rename from packages/core-api/src/apis/ApiRegistry.test.ts rename to packages/core-api/src/apis/system/ApiRegistry.test.ts diff --git a/packages/core-api/src/apis/ApiRegistry.ts b/packages/core-api/src/apis/system/ApiRegistry.ts similarity index 96% rename from packages/core-api/src/apis/ApiRegistry.ts rename to packages/core-api/src/apis/system/ApiRegistry.ts index 64d97ba234..14f58268b4 100644 --- a/packages/core-api/src/apis/ApiRegistry.ts +++ b/packages/core-api/src/apis/system/ApiRegistry.ts @@ -14,8 +14,7 @@ * limitations under the License. */ -import { ApiRef } from './ApiRef'; -import { ApiHolder } from './types'; +import { ApiRef, ApiHolder } from './types'; type ApiImpl = readonly [ApiRef, T]; diff --git a/packages/core-api/src/apis/ApiResolver.test.ts b/packages/core-api/src/apis/system/ApiResolver.test.ts similarity index 100% rename from packages/core-api/src/apis/ApiResolver.test.ts rename to packages/core-api/src/apis/system/ApiResolver.test.ts diff --git a/packages/core-api/src/apis/ApiResolver.ts b/packages/core-api/src/apis/system/ApiResolver.ts similarity index 98% rename from packages/core-api/src/apis/ApiResolver.ts rename to packages/core-api/src/apis/system/ApiResolver.ts index cb65186a37..0dfeaeedd0 100644 --- a/packages/core-api/src/apis/ApiResolver.ts +++ b/packages/core-api/src/apis/system/ApiResolver.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { ApiRef } from './ApiRef'; import { + ApiRef, ApiHolder, ApiFactoryHolder, AnyApiRef, diff --git a/packages/core-api/src/apis/helpers.ts b/packages/core-api/src/apis/system/helpers.ts similarity index 93% rename from packages/core-api/src/apis/helpers.ts rename to packages/core-api/src/apis/system/helpers.ts index 7d616acd01..0ccd0cdb80 100644 --- a/packages/core-api/src/apis/helpers.ts +++ b/packages/core-api/src/apis/system/helpers.ts @@ -14,8 +14,7 @@ * limitations under the License. */ -import { ApiFactory, TypesToApiRefs } from './types'; -import { ApiRef } from './ApiRef'; +import { ApiRef, ApiFactory, TypesToApiRefs } from './types'; /** * Used to infer types for a standalone ApiFactory that isn't immediately passed diff --git a/packages/core-api/src/apis/system/index.ts b/packages/core-api/src/apis/system/index.ts new file mode 100644 index 0000000000..10b2e0f084 --- /dev/null +++ b/packages/core-api/src/apis/system/index.ts @@ -0,0 +1,23 @@ +/* + * 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 { ApiProvider, useApi, useApiHolder } from './ApiProvider'; +export { ApiRegistry } from './ApiRegistry'; +export { ApiResolver } from './ApiResolver'; +export { ApiFactoryRegistry } from './ApiFactoryRegistry'; +export { createApiRef } from './ApiRef'; +export * from './types'; +export * from './helpers'; diff --git a/packages/core-api/src/apis/types.ts b/packages/core-api/src/apis/system/types.ts similarity index 94% rename from packages/core-api/src/apis/types.ts rename to packages/core-api/src/apis/system/types.ts index 61c229b18e..b0c19e551c 100644 --- a/packages/core-api/src/apis/types.ts +++ b/packages/core-api/src/apis/system/types.ts @@ -14,7 +14,11 @@ * limitations under the License. */ -import { ApiRef } from './ApiRef'; +export type ApiRef = { + id: string; + description: string; + T: T; +}; export type AnyApiRef = ApiRef; diff --git a/packages/core-api/src/app/App.tsx b/packages/core-api/src/app/App.tsx index c222c3c420..0588cd819c 100644 --- a/packages/core-api/src/app/App.tsx +++ b/packages/core-api/src/app/App.tsx @@ -54,8 +54,7 @@ import { } from '../apis'; import { useAsync } from 'react-use'; import { AppIdentity } from './AppIdentity'; -import { ApiFactoryRegistry } from '../apis/ApiFactoryRegistry'; -import { ApiResolver } from '../apis/ApiResolver'; +import { ApiResolver, ApiFactoryRegistry } from '../apis/system'; type FullAppOptions = { apis: Iterable; diff --git a/packages/core-api/src/plugin/types.ts b/packages/core-api/src/plugin/types.ts index 12992f3620..dacb3668bc 100644 --- a/packages/core-api/src/plugin/types.ts +++ b/packages/core-api/src/plugin/types.ts @@ -16,7 +16,7 @@ import { ComponentType } from 'react'; import { RouteRef } from '../routing'; -import { AnyApiFactory } from '../apis'; +import { AnyApiFactory } from '../apis/system'; export type RouteOptions = { // Whether the route path must match exactly, defaults to true. From 1bf3ff96a7f52e05510d461dccad7175a74a3b04 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 19 Oct 2020 01:37:00 +0200 Subject: [PATCH 13/44] core-api: work around issue with ApiRef export const declarations --- .../core-api/src/apis/definitions/AlertApi.ts | 4 +-- .../src/apis/definitions/AppThemeApi.ts | 4 +-- .../src/apis/definitions/ConfigApi.ts | 4 +-- .../src/apis/definitions/DiscoveryApi.ts | 4 +-- .../core-api/src/apis/definitions/ErrorApi.ts | 4 +-- .../src/apis/definitions/FeatureFlagsApi.ts | 4 +-- .../src/apis/definitions/IdentityApi.ts | 4 +-- .../src/apis/definitions/OAuthRequestApi.ts | 4 +-- .../src/apis/definitions/StorageApi.ts | 4 +-- .../core-api/src/apis/definitions/auth.ts | 34 +++++++++---------- 10 files changed, 35 insertions(+), 35 deletions(-) diff --git a/packages/core-api/src/apis/definitions/AlertApi.ts b/packages/core-api/src/apis/definitions/AlertApi.ts index d149940103..91641ac35b 100644 --- a/packages/core-api/src/apis/definitions/AlertApi.ts +++ b/packages/core-api/src/apis/definitions/AlertApi.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { createApiRef } from '../system'; +import { createApiRef, ApiRef } from '../system'; import { Observable } from '../../types'; export type AlertMessage = { @@ -38,7 +38,7 @@ export type AlertApi = { alert$(): Observable; }; -export const alertApiRef = createApiRef({ +export const alertApiRef: ApiRef = createApiRef({ id: 'core.alert', description: 'Used to report alerts and forward them to the app', }); diff --git a/packages/core-api/src/apis/definitions/AppThemeApi.ts b/packages/core-api/src/apis/definitions/AppThemeApi.ts index 019aa2f496..515e8df082 100644 --- a/packages/core-api/src/apis/definitions/AppThemeApi.ts +++ b/packages/core-api/src/apis/definitions/AppThemeApi.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createApiRef } from '../system'; +import { ApiRef, createApiRef } from '../system'; import { BackstageTheme } from '@backstage/theme'; import { Observable } from '../../types'; import { SvgIconProps } from '@material-ui/core'; @@ -77,7 +77,7 @@ export type AppThemeApi = { setActiveThemeId(themeId?: string): void; }; -export const appThemeApiRef = createApiRef({ +export const appThemeApiRef: ApiRef = createApiRef({ id: 'core.apptheme', description: 'API Used to configure the app theme, and enumerate options', }); diff --git a/packages/core-api/src/apis/definitions/ConfigApi.ts b/packages/core-api/src/apis/definitions/ConfigApi.ts index 6d34055f05..2ce972af0e 100644 --- a/packages/core-api/src/apis/definitions/ConfigApi.ts +++ b/packages/core-api/src/apis/definitions/ConfigApi.ts @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { createApiRef } from '../system'; +import { ApiRef, createApiRef } from '../system'; import { Config } from '@backstage/config'; // Using interface to make the ConfigApi name show up in docs export type ConfigApi = Config; -export const configApiRef = createApiRef({ +export const configApiRef: ApiRef = createApiRef({ id: 'core.config', description: 'Used to access runtime configuration', }); diff --git a/packages/core-api/src/apis/definitions/DiscoveryApi.ts b/packages/core-api/src/apis/definitions/DiscoveryApi.ts index 13ead7b929..9777835ec8 100644 --- a/packages/core-api/src/apis/definitions/DiscoveryApi.ts +++ b/packages/core-api/src/apis/definitions/DiscoveryApi.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { createApiRef } from '../system'; +import { ApiRef, createApiRef } from '../system'; /** * The discovery API is used to provide a mechanism for plugins to @@ -41,7 +41,7 @@ export type DiscoveryApi = { getBaseUrl(pluginId: string): Promise; }; -export const discoveryApiRef = createApiRef({ +export const discoveryApiRef: ApiRef = createApiRef({ id: 'core.discovery', description: 'Provides service discovery of backend plugins', }); diff --git a/packages/core-api/src/apis/definitions/ErrorApi.ts b/packages/core-api/src/apis/definitions/ErrorApi.ts index 365f59f4a3..edd6966968 100644 --- a/packages/core-api/src/apis/definitions/ErrorApi.ts +++ b/packages/core-api/src/apis/definitions/ErrorApi.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createApiRef } from '../system'; +import { ApiRef, createApiRef } from '../system'; import { Observable } from '../../types'; /** @@ -62,7 +62,7 @@ export type ErrorApi = { error$(): Observable<{ error: Error; context?: ErrorContext }>; }; -export const errorApiRef = createApiRef({ +export const errorApiRef: ApiRef = createApiRef({ id: 'core.error', description: 'Used to report errors and forward them to the app', }); diff --git a/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts b/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts index 0b7cddfe15..6e8c8cd8fc 100644 --- a/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts +++ b/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createApiRef } from '../system'; +import { ApiRef, createApiRef } from '../system'; import { UserFlags, FeatureFlagsRegistry } from '../../app/FeatureFlags'; import { FeatureFlagName } from '../../plugin'; @@ -57,7 +57,7 @@ export interface FeatureFlagsRegistryItem { name: FeatureFlagName; } -export const featureFlagsApiRef = createApiRef({ +export const featureFlagsApiRef: ApiRef = createApiRef({ id: 'core.featureflags', description: 'Used to toggle functionality in features across Backstage', }); diff --git a/packages/core-api/src/apis/definitions/IdentityApi.ts b/packages/core-api/src/apis/definitions/IdentityApi.ts index 87dc4ca989..5cfc4723e0 100644 --- a/packages/core-api/src/apis/definitions/IdentityApi.ts +++ b/packages/core-api/src/apis/definitions/IdentityApi.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { createApiRef } from '../system'; +import { ApiRef, createApiRef } from '../system'; import { ProfileInfo } from './auth'; /** @@ -51,7 +51,7 @@ export type IdentityApi = { signOut(): Promise; }; -export const identityApiRef = createApiRef({ +export const identityApiRef: ApiRef = createApiRef({ id: 'core.identity', description: 'Provides access to the identity of the signed in user', }); diff --git a/packages/core-api/src/apis/definitions/OAuthRequestApi.ts b/packages/core-api/src/apis/definitions/OAuthRequestApi.ts index 7bbebf90c1..7514a88dc1 100644 --- a/packages/core-api/src/apis/definitions/OAuthRequestApi.ts +++ b/packages/core-api/src/apis/definitions/OAuthRequestApi.ts @@ -16,7 +16,7 @@ import { IconComponent } from '../../icons'; import { Observable } from '../../types'; -import { createApiRef } from '../system'; +import { ApiRef, createApiRef } from '../system'; /** * Information about the auth provider that we're requesting a login towards. @@ -127,7 +127,7 @@ export type OAuthRequestApi = { authRequest$(): Observable; }; -export const oauthRequestApiRef = createApiRef({ +export const oauthRequestApiRef: ApiRef = createApiRef({ id: 'core.oauthrequest', description: 'An API for implementing unified OAuth flows in Backstage', }); diff --git a/packages/core-api/src/apis/definitions/StorageApi.ts b/packages/core-api/src/apis/definitions/StorageApi.ts index 923633318b..d0f71ea3a4 100644 --- a/packages/core-api/src/apis/definitions/StorageApi.ts +++ b/packages/core-api/src/apis/definitions/StorageApi.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createApiRef } from '../system'; +import { ApiRef, createApiRef } from '../system'; import { Observable } from '../../types'; import { ErrorApi } from './ErrorApi'; @@ -65,7 +65,7 @@ export interface StorageApi { observe$(key: string): Observable>; } -export const storageApiRef = createApiRef({ +export const storageApiRef: ApiRef = createApiRef({ id: 'core.storage', description: 'Provides the ability to store data which is unique to the user', }); diff --git a/packages/core-api/src/apis/definitions/auth.ts b/packages/core-api/src/apis/definitions/auth.ts index 5ffe312591..946baeabd9 100644 --- a/packages/core-api/src/apis/definitions/auth.ts +++ b/packages/core-api/src/apis/definitions/auth.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createApiRef } from '../system'; +import { ApiRef, createApiRef } from '../system'; import { Observable } from '../../types'; /** @@ -212,13 +212,13 @@ export type SessionApi = { * Note that the ID token payload is only guaranteed to contain the user's numerical Google ID, * email and expiration information. Do not rely on any other fields, as they might not be present. */ -export const googleAuthApiRef = createApiRef< +export const googleAuthApiRef: ApiRef< OAuthApi & OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi ->({ +> = createApiRef({ id: 'core.auth.google', description: 'Provides authentication towards Google APIs and identities', }); @@ -229,9 +229,9 @@ export const googleAuthApiRef = createApiRef< * See https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/ * for a full list of supported scopes. */ -export const githubAuthApiRef = createApiRef< +export const githubAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi ->({ +> = createApiRef({ id: 'core.auth.github', description: 'Provides authentication towards GitHub APIs', }); @@ -242,13 +242,13 @@ export const githubAuthApiRef = createApiRef< * See https://developer.okta.com/docs/guides/implement-oauth-for-okta/scopes/ * for a full list of supported scopes. */ -export const oktaAuthApiRef = createApiRef< +export const oktaAuthApiRef: ApiRef< OAuthApi & OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi ->({ +> = createApiRef({ id: 'core.auth.okta', description: 'Provides authentication towards Okta APIs', }); @@ -259,9 +259,9 @@ export const oktaAuthApiRef = createApiRef< * See https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#limiting-scopes-of-a-personal-access-token * for a full list of supported scopes. */ -export const gitlabAuthApiRef = createApiRef< +export const gitlabAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi ->({ +> = createApiRef({ id: 'core.auth.gitlab', description: 'Provides authentication towards GitLab APIs', }); @@ -272,9 +272,9 @@ export const gitlabAuthApiRef = createApiRef< * See https://auth0.com/docs/scopes/current/oidc-scopes * for a full list of supported scopes. */ -export const auth0AuthApiRef = createApiRef< +export const auth0AuthApiRef: ApiRef< OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi ->({ +> = createApiRef({ id: 'core.auth.auth0', description: 'Provides authentication towards Auth0 APIs', }); @@ -286,13 +286,13 @@ export const auth0AuthApiRef = createApiRef< * - https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent * - https://docs.microsoft.com/en-us/graph/permissions-reference */ -export const microsoftAuthApiRef = createApiRef< +export const microsoftAuthApiRef: ApiRef< OAuthApi & OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi ->({ +> = createApiRef({ id: 'core.auth.microsoft', description: 'Provides authentication towards Microsoft APIs and identities', }); @@ -300,13 +300,13 @@ export const microsoftAuthApiRef = createApiRef< /** * Provides authentication for custom identity providers. */ -export const oauth2ApiRef = createApiRef< +export const oauth2ApiRef: ApiRef< OAuthApi & OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi ->({ +> = createApiRef({ id: 'core.auth.oauth2', description: 'Example of how to use oauth2 custom provider', }); @@ -314,9 +314,9 @@ export const oauth2ApiRef = createApiRef< /** * Provides authentication for saml based identity providers */ -export const samlAuthApiRef = createApiRef< +export const samlAuthApiRef: ApiRef< ProfileInfoApi & BackstageIdentityApi & SessionApi ->({ +> = createApiRef({ id: 'core.auth.saml', description: 'Example of how to use SAML custom provider', }); From feeceb96dc906ae12626e11c233039a92f64ddf8 Mon Sep 17 00:00:00 2001 From: Abhishek Jakhar Date: Mon, 19 Oct 2020 07:05:34 +0530 Subject: [PATCH 14/44] handle the case where no entities are available to show --- .../UnregisterEntityDialog/UnregisterEntityDialog.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx b/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx index e4eec3c21f..258cc38d15 100644 --- a/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx +++ b/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx @@ -85,7 +85,7 @@ export const UnregisterEntityDialog: FC = ({ {error.toString()} ) : null} - {entities ? ( + {entities?.length ? ( <> This action will unregister the following entities: @@ -107,11 +107,11 @@ export const UnregisterEntityDialog: FC = ({ - - To undo, just re-register the entity in Backstage. - ) : null} + + To undo, just re-register the entity in Backstage. +