From 0d40a302ff0410c84bff96a9d8136db8e6058bdb Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 31 Aug 2020 17:30:47 +0200 Subject: [PATCH 01/11] cli: define BUILD_INFO in builds --- packages/cli/src/lib/bundler/bundle.ts | 2 +- packages/cli/src/lib/bundler/config.ts | 45 ++++++++++++++++++++++++-- packages/cli/src/lib/bundler/server.ts | 2 +- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/lib/bundler/bundle.ts b/packages/cli/src/lib/bundler/bundle.ts index 1193ed505e..8746c807e7 100644 --- a/packages/cli/src/lib/bundler/bundle.ts +++ b/packages/cli/src/lib/bundler/bundle.ts @@ -36,7 +36,7 @@ export async function buildBundle(options: BuildOptions) { const { statsJsonEnabled } = options; const paths = resolveBundlingPaths(options); - const config = createConfig(paths, { + const config = await createConfig(paths, { ...options, checksEnabled: false, isDev: false, diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index d432d6b64e..61f4448dd9 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import fs from 'fs-extra'; import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; import HtmlWebpackPlugin from 'html-webpack-plugin'; import ModuleScopePlugin from 'react-dev-utils/ModuleScopePlugin'; @@ -25,6 +26,9 @@ import { Config } from '@backstage/config'; import { BundlingPaths } from './paths'; import { transforms } from './transforms'; import { BundlingOptions, BackendBundlingOptions } from './types'; +import { version } from '../../lib/version'; +import { paths as cliPaths } from '../../lib/paths'; +import { runPlain } from '../run'; export function resolveBaseUrl(config: Config): URL { const baseUrl = config.getString('app.baseUrl'); @@ -35,10 +39,40 @@ export function resolveBaseUrl(config: Config): URL { } } -export function createConfig( +async function readBuildInfo() { + const timestamp = Date.now(); + + let commit = 'unknown'; + try { + commit = await runPlain('git', 'rev-parse', 'HEAD'); + } catch (error) { + console.warn(`WARNING: Failed to read git commit, ${error}`); + } + + let gitVersion = 'unknown'; + try { + gitVersion = await runPlain('git', 'describe', '--always'); + } catch (error) { + console.warn(`WARNING: Failed to describe git version, ${error}`); + } + + const { version: packageVersion } = await fs.readJson( + cliPaths.resolveTarget('package.json'), + ); + + return { + cliVersion: version, + gitVersion, + packageVersion, + timestamp, + commit, + }; +} + +export async function createConfig( paths: BundlingPaths, options: BundlingOptions, -): webpack.Configuration { +): Promise { const { checksEnabled, isDev } = options; const { plugins, loaders } = transforms(options); @@ -81,6 +115,13 @@ export function createConfig( }), ); + const buildInfo = await readBuildInfo(); + plugins.push( + new webpack.DefinePlugin({ + 'process.env.BUILD_INFO': JSON.stringify(buildInfo), + }), + ); + return { mode: isDev ? 'development' : 'production', profile: false, diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index c7bacc0c7d..4c4a424d08 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -30,7 +30,7 @@ export async function serveBundle(options: ServeOptions) { const paths = resolveBundlingPaths(options); const pkgPath = paths.targetPackageJson; const pkg = await fs.readJson(pkgPath); - const config = createConfig(paths, { + const config = await createConfig(paths, { ...options, isDev: true, baseUrl: url, From 0ff78005cd23e9d54e313b95548c0b7138b0dd5c Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Mon, 31 Aug 2020 23:07:16 +0200 Subject: [PATCH 02/11] fix(docs): code block background --- microsite/static/css/custom.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/static/css/custom.css b/microsite/static/css/custom.css index 3c0184a07b..5e078582cc 100644 --- a/microsite/static/css/custom.css +++ b/microsite/static/css/custom.css @@ -216,7 +216,7 @@ td { code { font-family: IBM Plex Mono, Menlo, Monaco, Consolas, Courier New, monospace; font-weight: 500; - background-color: #0e0e0e; + background-color: #272822; } /* .stripe { From 8a67a8df844d234fc1bcb007c2fa1b21b0674257 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Tue, 1 Sep 2020 00:21:25 +0200 Subject: [PATCH 03/11] fix(docs): add i18n to gitignore to avoid lerna errors in master --- .gitignore | 1 + microsite/i18n/en.json | 3 +++ 2 files changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 743561c3b6..aee80f4c10 100644 --- a/.gitignore +++ b/.gitignore @@ -92,6 +92,7 @@ dist # Microsite build output microsite/build +microsite/i18n # Gatsby files .cache/ diff --git a/microsite/i18n/en.json b/microsite/i18n/en.json index 96ffb780dc..e9f63d5737 100644 --- a/microsite/i18n/en.json +++ b/microsite/i18n/en.json @@ -92,6 +92,9 @@ "features/software-catalog/software-catalog-api": { "title": "API" }, + "features/software-catalog/software-catalog-configuration": { + "title": "Catalog Configuration" + }, "features/software-catalog/descriptor-format": { "title": "Descriptor Format of Catalog Entities", "sidebar_label": "YAML File Format" From 329b663c93e28003b999271b4b35a044eddc2a01 Mon Sep 17 00:00:00 2001 From: Andrew Thauer <6507159+andrewthauer@users.noreply.github.com> Date: Sat, 29 Aug 2020 16:59:14 -0400 Subject: [PATCH 04/11] refactor: cleanup & renames to common db code --- .../src/database/connection.test.ts | 16 +- .../backend-common/src/database/connection.ts | 26 ++- .../src/database/postgres.test.ts | 176 ++++++++++-------- .../backend-common/src/database/postgres.ts | 45 +++-- .../src/database/sqlite3.test.ts | 17 +- .../backend-common/src/database/sqlite3.ts | 12 +- packages/backend/src/index.ts | 13 +- .../default-app/packages/backend/src/index.ts | 13 +- 8 files changed, 184 insertions(+), 134 deletions(-) diff --git a/packages/backend-common/src/database/connection.test.ts b/packages/backend-common/src/database/connection.test.ts index 814503fda1..2cc3a54e06 100644 --- a/packages/backend-common/src/database/connection.test.ts +++ b/packages/backend-common/src/database/connection.test.ts @@ -15,7 +15,7 @@ */ import { ConfigReader } from '@backstage/config'; -import { createDatabase } from './connection'; +import { createDatabaseClient } from './connection'; describe('database connection', () => { const createConfig = (data: any) => @@ -26,10 +26,10 @@ describe('database connection', () => { }, ]); - describe(createDatabase, () => { + describe(createDatabaseClient, () => { it('returns a postgres connection', () => { expect( - createDatabase( + createDatabaseClient( createConfig({ client: 'pg', connection: { @@ -45,7 +45,7 @@ describe('database connection', () => { it('returns an sqlite connection', () => { expect( - createDatabase( + createDatabaseClient( createConfig({ client: 'sqlite3', connection: ':memory:', @@ -56,7 +56,7 @@ describe('database connection', () => { it('tries to create a mysql connection as a passthrough', () => { expect(() => - createDatabase( + createDatabaseClient( createConfig({ client: 'mysql', connection: { @@ -72,7 +72,7 @@ describe('database connection', () => { it('accepts overrides', () => { expect( - createDatabase( + createDatabaseClient( createConfig({ client: 'pg', connection: { @@ -93,7 +93,7 @@ describe('database connection', () => { it('throws an error without a client', () => { expect(() => - createDatabase( + createDatabaseClient( createConfig({ connection: '', }), @@ -103,7 +103,7 @@ describe('database connection', () => { it('throws an error without a connection', () => { expect(() => - createDatabase( + createDatabaseClient( createConfig({ client: 'pg', }), diff --git a/packages/backend-common/src/database/connection.ts b/packages/backend-common/src/database/connection.ts index d968275482..009a85239f 100644 --- a/packages/backend-common/src/database/connection.ts +++ b/packages/backend-common/src/database/connection.ts @@ -15,30 +15,36 @@ */ import knex from 'knex'; -import { ConfigReader } from '@backstage/config'; +import { Config } from '@backstage/config'; import { mergeDatabaseConfig } from './config'; -import { createPgDatabase } from './postgres'; -import { createSqlite3Database } from './sqlite3'; +import { createPgDatabaseClient } from './postgres'; +import { createSqliteDatabaseClient } from './sqlite3'; type DatabaseClient = 'pg' | 'sqlite3' | string; /** * Creates a knex database connection * - * @param config The database config + * @param dbConfig The database config * @param overrides Additional options to merge with the config */ -export function createDatabase( - config: ConfigReader, +export function createDatabaseClient( + dbConfig: Config, overrides?: Partial, ) { - const client: DatabaseClient = config.getString('client'); + const client: DatabaseClient = dbConfig.getString('client'); if (client === 'pg') { - return createPgDatabase(config, overrides); + return createPgDatabaseClient(dbConfig, overrides); } else if (client === 'sqlite3') { - return createSqlite3Database(config); + return createSqliteDatabaseClient(dbConfig); } - return knex(mergeDatabaseConfig(config.get(), overrides)); + return knex(mergeDatabaseConfig(dbConfig.get(), overrides)); } + +/** + * Alias for createDatabaseClient + * @deprecated Use createDatabaseClient instead + */ +export const createDatabase = createDatabaseClient; diff --git a/packages/backend-common/src/database/postgres.test.ts b/packages/backend-common/src/database/postgres.test.ts index 8edfc2c15d..82161dc53c 100644 --- a/packages/backend-common/src/database/postgres.test.ts +++ b/packages/backend-common/src/database/postgres.test.ts @@ -14,15 +14,26 @@ * limitations under the License. */ -import { ConfigReader } from '@backstage/config'; +import { Config, ConfigReader } from '@backstage/config'; import { - parsePgConnectionString, + createPgDatabaseClient, buildPgDatabaseConfig, - createPgDatabase, + getPgConnectionConfig, + parsePgConnectionString, } from './postgres'; describe('postgres', () => { - const createConfig = (connection: any) => + const createMockConnection = () => ({ + host: 'acme', + user: 'foo', + password: 'bar', + database: 'foodb', + }); + + const createMockConnectionString = () => + 'postgresql://foo:bar@acme:5432/foodb'; + + const createConfig = (connection: any): Config => ConfigReader.fromConfigs([ { context: '', @@ -35,86 +46,58 @@ describe('postgres', () => { describe(buildPgDatabaseConfig, () => { it('builds a postgres config', () => { - expect( - buildPgDatabaseConfig( - createConfig({ - host: 'acme', - user: 'foo', - password: 'bar', - port: '5432', - database: 'foodb', - }), - ), - ).toEqual({ + const mockConnection = createMockConnection(); + + expect(buildPgDatabaseConfig(createConfig(mockConnection))).toEqual({ client: 'pg', - connection: { - host: 'acme', - user: 'foo', - password: 'bar', - port: '5432', - database: 'foodb', - }, + connection: mockConnection, useNullAsDefault: true, }); }); it('builds a connection string config', () => { - expect( - buildPgDatabaseConfig( - createConfig('postgresql://foo:bar@acme:5432/foodb'), - ), - ).toEqual({ - client: 'pg', - connection: 'postgresql://foo:bar@acme:5432/foodb', - useNullAsDefault: true, - }); + const mockConnectionString = createMockConnectionString(); + + expect(buildPgDatabaseConfig(createConfig(mockConnectionString))).toEqual( + { + client: 'pg', + connection: mockConnectionString, + useNullAsDefault: true, + }, + ); }); it('overrides the database name', () => { + const mockConnection = createMockConnection(); + expect( - buildPgDatabaseConfig( - createConfig({ - host: 'somehost', - user: 'postgres', - password: 'pass', - database: 'foo', - }), - { connection: { database: 'foodb' } }, - ), + buildPgDatabaseConfig(createConfig(mockConnection), { + connection: { database: 'other_db' }, + }), ).toEqual({ client: 'pg', connection: { - host: 'somehost', - user: 'postgres', - password: 'pass', - database: 'foodb', + ...mockConnection, + database: 'other_db', }, useNullAsDefault: true, }); }); it('adds additional config settings', () => { + const mockConnection = createMockConnection(); + expect( - buildPgDatabaseConfig( - createConfig({ - host: 'somehost', - user: 'postgres', - password: 'pass', - database: 'foo', - }), - { - connection: { database: 'foodb' }, - pool: { min: 0, max: 7 }, - debug: true, - }, - ), + buildPgDatabaseConfig(createConfig(mockConnection), { + connection: { database: 'other_db' }, + pool: { min: 0, max: 7 }, + debug: true, + }), ).toEqual({ client: 'pg', connection: { - host: 'somehost', - user: 'postgres', - password: 'pass', - database: 'foodb', + ...mockConnection, + database: 'other_db', }, useNullAsDefault: true, pool: { min: 0, max: 7 }, @@ -123,37 +106,72 @@ describe('postgres', () => { }); it('overrides the database from connection string', () => { + const mockConnectionString = createMockConnectionString(); + const mockConnection = createMockConnection(); + expect( - buildPgDatabaseConfig( - createConfig('postgresql://postgres:pass@localhost:5432/dbname'), - { connection: { database: 'foodb' } }, - ), + buildPgDatabaseConfig(createConfig(mockConnectionString), { + connection: { database: 'other_db' }, + }), ).toEqual({ client: 'pg', connection: { - host: 'localhost', - user: 'postgres', - password: 'pass', + ...mockConnection, port: '5432', - database: 'foodb', + database: 'other_db', }, useNullAsDefault: true, }); }); }); - describe(createPgDatabase, () => { + describe(getPgConnectionConfig, () => { + it('returns the connection object back', () => { + const mockConnection = createMockConnection(); + const config = createConfig(mockConnection); + + expect(getPgConnectionConfig(config)).toEqual(mockConnection); + }); + + it('does not parse the connection string', () => { + const mockConnection = createMockConnection(); + const config = createConfig(mockConnection); + + expect(getPgConnectionConfig(config, true)).toEqual(mockConnection); + }); + + it('automatically parses the connection string', () => { + const mockConnection = createMockConnection(); + const mockConnectionString = createMockConnectionString(); + const config = createConfig(mockConnectionString); + + expect(getPgConnectionConfig(config)).toEqual({ + ...mockConnection, + port: '5432', + }); + }); + + it('parses the connection string', () => { + const mockConnection = createMockConnection(); + const mockConnectionString = createMockConnectionString(); + const config = createConfig(mockConnectionString); + + expect(getPgConnectionConfig(config, true)).toEqual({ + ...mockConnection, + port: '5432', + }); + }); + }); + + describe(createPgDatabaseClient, () => { it('creates a postgres knex instance', () => { expect( - createPgDatabase( + createPgDatabaseClient( createConfig({ - client: 'pg', - connection: { - host: 'acme', - user: 'foo', - password: 'bar', - database: 'foodb', - }, + host: 'acme', + user: 'foo', + password: 'bar', + database: 'foodb', }), ), ).toBeTruthy(); @@ -161,7 +179,7 @@ describe('postgres', () => { it('attempts to read an ssl cert', () => { expect(() => - createPgDatabase( + createPgDatabaseClient( createConfig( 'postgresql://postgres:pass@localhost:5432/dbname?sslrootcert=/path/to/file', ), diff --git a/packages/backend-common/src/database/postgres.ts b/packages/backend-common/src/database/postgres.ts index 09b6d0653b..03a74156c7 100644 --- a/packages/backend-common/src/database/postgres.ts +++ b/packages/backend-common/src/database/postgres.ts @@ -14,18 +14,18 @@ * limitations under the License. */ -import knex from 'knex'; -import { ConfigReader } from '@backstage/config'; +import knex, { PgConnectionConfig } from 'knex'; +import { Config } from '@backstage/config'; import { mergeDatabaseConfig } from './config'; /** - * Creates a knex sqlite3 database connection + * Creates a knex postgres database connection * * @param dbConfig The database config * @param overrides Additional options to merge with the config */ -export function createPgDatabase( - dbConfig: ConfigReader, +export function createPgDatabaseClient( + dbConfig: Config, overrides?: knex.Config, ) { const knexConfig = buildPgDatabaseConfig(dbConfig, overrides); @@ -40,26 +40,43 @@ export function createPgDatabase( * @param overrides Additional options to merge with the config */ export function buildPgDatabaseConfig( - dbConfig: ConfigReader, + dbConfig: Config, overrides?: knex.Config, ) { - const connection = dbConfig.get('connection') as any; - return mergeDatabaseConfig( dbConfig.get(), { - // Only parse the connection string when overrides are provided - connection: - overrides && - (typeof connection === 'string' || connection instanceof String) - ? parsePgConnectionString(connection as string) - : connection, + connection: getPgConnectionConfig(dbConfig, !!overrides), useNullAsDefault: true, }, overrides, ); } +/** + * Gets the postgres connection config + * + * @param dbConfig The database config + * @param parseConnectionString Flag to explictly control connection string parsing + */ +export function getPgConnectionConfig( + dbConfig: Config, + parseConnectionString?: boolean, +): PgConnectionConfig | string { + const connection = dbConfig.get('connection') as any; + const isConnectionString = + typeof connection === 'string' || connection instanceof String; + const autoParse = typeof parseConnectionString !== 'boolean'; + + const shouldParseConnectionString = autoParse + ? isConnectionString + : parseConnectionString && isConnectionString; + + return shouldParseConnectionString + ? parsePgConnectionString(connection as string) + : connection; +} + /** * Parses a connection string using pg-connection-string * diff --git a/packages/backend-common/src/database/sqlite3.test.ts b/packages/backend-common/src/database/sqlite3.test.ts index a25720b81c..dbff5ee354 100644 --- a/packages/backend-common/src/database/sqlite3.test.ts +++ b/packages/backend-common/src/database/sqlite3.test.ts @@ -15,7 +15,10 @@ */ import { ConfigReader } from '@backstage/config'; -import { buildSqlite3DatabaseConfig, createSqlite3Database } from './sqlite3'; +import { + buildSqliteDatabaseConfig, + createSqliteDatabaseClient, +} from './sqlite3'; describe('sqlite3', () => { const createConfig = (connection: any) => @@ -29,9 +32,9 @@ describe('sqlite3', () => { }, ]); - describe(buildSqlite3DatabaseConfig, () => { + describe(buildSqliteDatabaseConfig, () => { it('buidls a string connection', () => { - expect(buildSqlite3DatabaseConfig(createConfig(':memory:'))).toEqual({ + expect(buildSqliteDatabaseConfig(createConfig(':memory:'))).toEqual({ client: 'sqlite3', connection: ':memory:', useNullAsDefault: true, @@ -40,7 +43,7 @@ describe('sqlite3', () => { it('builds a filename connection', () => { expect( - buildSqlite3DatabaseConfig( + buildSqliteDatabaseConfig( createConfig({ filename: '/path/to/foo', }), @@ -56,7 +59,7 @@ describe('sqlite3', () => { it('replaces the connection with an override', () => { expect( - buildSqlite3DatabaseConfig(createConfig(':memory:'), { + buildSqliteDatabaseConfig(createConfig(':memory:'), { connection: { filename: '/path/to/foo' }, }), ).toEqual({ @@ -69,10 +72,10 @@ describe('sqlite3', () => { }); }); - describe(createSqlite3Database, () => { + describe(createSqliteDatabaseClient, () => { it('creates an in memory knex instance', () => { expect( - createSqlite3Database( + createSqliteDatabaseClient( createConfig({ client: 'sqlite3', connection: ':memory:', diff --git a/packages/backend-common/src/database/sqlite3.ts b/packages/backend-common/src/database/sqlite3.ts index 6249394d05..7bdc4380a1 100644 --- a/packages/backend-common/src/database/sqlite3.ts +++ b/packages/backend-common/src/database/sqlite3.ts @@ -15,7 +15,7 @@ */ import knex from 'knex'; -import { ConfigReader } from '@backstage/config'; +import { Config } from '@backstage/config'; import { mergeDatabaseConfig } from './config'; /** @@ -24,11 +24,11 @@ import { mergeDatabaseConfig } from './config'; * @param dbConfig The database config * @param overrides Additional options to merge with the config */ -export function createSqlite3Database( - dbConfig: ConfigReader, +export function createSqliteDatabaseClient( + dbConfig: Config, overrides?: knex.Config, ) { - const knexConfig = buildSqlite3DatabaseConfig(dbConfig, overrides); + const knexConfig = buildSqliteDatabaseConfig(dbConfig, overrides); const database = knex(knexConfig); database.client.pool.on('createSuccess', (_eventId: any, resource: any) => { @@ -44,8 +44,8 @@ export function createSqlite3Database( * @param dbConfig The database config * @param overrides Additional options to merge with the config */ -export function buildSqlite3DatabaseConfig( - dbConfig: ConfigReader, +export function buildSqliteDatabaseConfig( + dbConfig: Config, overrides?: knex.Config, ) { return mergeDatabaseConfig( diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 6df593182d..0423047cf7 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -23,7 +23,7 @@ */ import { - createDatabase, + createDatabaseClient, createServiceBuilder, loadBackendConfig, getRootLogger, @@ -48,11 +48,14 @@ function makeCreateEnv(loadedConfigs: AppConfig[]) { return (plugin: string): PluginEnvironment => { const logger = getRootLogger().child({ type: 'plugin', plugin }); - const database = createDatabase(config.getConfig('backend.database'), { - connection: { - database: `backstage_plugin_${plugin}`, + const database = createDatabaseClient( + config.getConfig('backend.database'), + { + connection: { + database: `backstage_plugin_${plugin}`, + }, }, - }); + ); return { logger, database, config }; }; } diff --git a/packages/create-app/templates/default-app/packages/backend/src/index.ts b/packages/create-app/templates/default-app/packages/backend/src/index.ts index d6fba2478a..6a014727e9 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/index.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/index.ts @@ -7,7 +7,7 @@ */ import { - createDatabase, + createDatabaseClient, createServiceBuilder, loadBackendConfig, getRootLogger, @@ -27,11 +27,14 @@ function makeCreateEnv(loadedConfigs: AppConfig[]) { return (plugin: string): PluginEnvironment => { const logger = getRootLogger().child({ type: 'plugin', plugin }); - const database = createDatabase(config.getConfig('backend.database'), { - connection: { - database: `backstage_plugin_${plugin}`, + const database = createDatabaseClient( + config.getConfig('backend.database'), + { + connection: { + database: `backstage_plugin_${plugin}`, + }, }, - }); + ); return { logger, database, config }; }; } From 055b8baa6de8c5c5208f6d016510934b823c068f Mon Sep 17 00:00:00 2001 From: Iain Billett Date: Tue, 1 Sep 2020 07:55:31 +0100 Subject: [PATCH 05/11] Add Pull Request Plugin to the marketplace (#2190) * Add Pull Request Plugin to the marketplace * Add Pull Request Plugin to the marketplace * typo --- microsite/data/plugins/github-pull-requests.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 microsite/data/plugins/github-pull-requests.yaml diff --git a/microsite/data/plugins/github-pull-requests.yaml b/microsite/data/plugins/github-pull-requests.yaml new file mode 100644 index 0000000000..917198a221 --- /dev/null +++ b/microsite/data/plugins/github-pull-requests.yaml @@ -0,0 +1,10 @@ +--- +title: GitHub Pull Requests +author: roadie.io +authorUrl: https://roadie.io/ +category: CI +description: View GitHub pull requests for your service in Backstage. +documentation: https://roadie.io/backstage/plugins/github-pull-requests +iconUrl: https://roadie.io/static/7f13bb8d861d8dedc5112fb939d215f9/351f2/GitHub-Mark-Light-120px-plus.png +npmPackageName: '@roadiehq/backstage-plugin-github-pull-requests' + From 94da78133f16b1a5a346be445426633e07492870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 1 Sep 2020 09:03:44 +0200 Subject: [PATCH 06/11] fix(deps): yarn.lock update again --- yarn.lock | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index d90056f47d..aa85370465 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2724,7 +2724,7 @@ dependencies: "@types/node" ">= 8" -"@open-draft/until@^1.0.3": +"@open-draft/until@^1.0.0", "@open-draft/until@^1.0.3": version "1.0.3" resolved "https://registry.npmjs.org/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" integrity sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q== @@ -4106,6 +4106,11 @@ dependencies: "@types/express" "*" +"@types/cookie@^0.3.3": + version "0.3.3" + resolved "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803" + integrity sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow== + "@types/cookie@^0.4.0": version "0.4.0" resolved "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz#14f854c0f93d326e39da6e3b6f34f7d37513d108" @@ -11650,7 +11655,7 @@ graphql-upload@^8.0.2: http-errors "^1.7.3" object-path "^0.11.4" -graphql@15.3.0, graphql@^15.3.0: +graphql@15.3.0, graphql@^15.0.0, graphql@^15.3.0: version "15.3.0" resolved "https://registry.npmjs.org/graphql/-/graphql-15.3.0.tgz#3ad2b0caab0d110e3be4a5a9b2aa281e362b5278" integrity sha512-GTCJtzJmkFLWRfFJuoo9RWWa/FfamUHgiFosxi/X1Ani4AVWbeyBenZTNX6dM+7WSbbFfTo/25eh0LLkwHMw2w== @@ -11858,7 +11863,7 @@ he@^1.2.0: resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -headers-utils@^1.2.0: +headers-utils@^1.1.9, headers-utils@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/headers-utils/-/headers-utils-1.2.0.tgz#5e10d1bc9d2bccf789547afca5b991a3167241e8" integrity sha512-4/BMXcWrJErw7JpM87gF8MNEXcIMLzepYZjNRv/P9ctgupl2Ywa3u1PgHtNhSRq84bHH9Ndlkdy7bSi+bZ9I9A== @@ -15561,6 +15566,22 @@ ms@^2.0.0, ms@^2.1.1: resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +msw@^0.19.5: + version "0.19.5" + resolved "https://registry.npmjs.org/msw/-/msw-0.19.5.tgz#7d2a1a852ccf1644d3db6735d69fff6777aac33f" + integrity sha512-J5eQ++gDVZoHPC8gVXtWcakLjgmPipvFj/sEnlRV/WViXuiq2CamSqO3Wbh6H8bAmj+k2vUWCfcVT1HjMdKB2Q== + dependencies: + "@open-draft/until" "^1.0.0" + "@types/cookie" "^0.3.3" + chalk "^4.0.0" + cookie "^0.4.1" + graphql "^15.0.0" + headers-utils "^1.1.9" + node-match-path "^0.4.2" + node-request-interceptor "^0.2.5" + statuses "^2.0.0" + yargs "^15.3.1" + msw@^0.20.5: version "0.20.5" resolved "https://registry.npmjs.org/msw/-/msw-0.20.5.tgz#b6141080c0d8b17c451d9ca36c28cc47b4ac487a" @@ -15807,7 +15828,7 @@ node-libs-browser@^2.2.1: util "^0.11.0" vm-browserify "^1.0.1" -node-match-path@^0.4.4: +node-match-path@^0.4.2, node-match-path@^0.4.4: version "0.4.4" resolved "https://registry.npmjs.org/node-match-path/-/node-match-path-0.4.4.tgz#516a10926093c0cc6f237d020685b593b19baebb" integrity sha512-pBq9gp7TG0r0VXuy/oeZmQsjBSnYQo7G886Ly/B3azRwZuEtHCY155dzmfoKWcDPGgyfIGD8WKVC7h3+6y7yTg== @@ -15866,6 +15887,14 @@ node-releases@^1.1.29, node-releases@^1.1.52, node-releases@^1.1.58: resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz#6948bdfce8286f0b5d0e5a88e8384e954dfe7084" integrity sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA== +node-request-interceptor@^0.2.5: + version "0.2.6" + resolved "https://registry.npmjs.org/node-request-interceptor/-/node-request-interceptor-0.2.6.tgz#541278d7033bb6a8befb5dd793f83428cf6446a2" + integrity sha512-aJW1tPSM7nzuZFRe+C/KSz22GJO3CVFMxHHmMGX8Z+tjP7TCIVbzeckLFVfJG68BdVgrdOOP7Ejc57ag820eyA== + dependencies: + debug "^4.1.1" + headers-utils "^1.2.0" + node-request-interceptor@^0.3.5: version "0.3.5" resolved "https://registry.npmjs.org/node-request-interceptor/-/node-request-interceptor-0.3.5.tgz#4b26159617829c9a70643012c0fdc3ae4c78ae43" From 0627c246373f9073883a6808817e262e98fbaad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Tue, 1 Sep 2020 09:26:21 +0200 Subject: [PATCH 07/11] Change templates to use Home theme (#2202) * Change templates to use Home theme * Update docs URL --- .../src/components/ScaffolderPage/ScaffolderPage.tsx | 4 ++-- .../scaffolder/src/components/TemplatePage/TemplatePage.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx index 12b3e303dd..6f8bf0d550 100644 --- a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx +++ b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx @@ -64,7 +64,7 @@ export const ScaffolderPage: React.FC<{}> = () => { }, [error, errorApi]); return ( - +
= () => { Shoot! Looks like you don't have any templates. Check out the documentation{' '} - + here! diff --git a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx index 05cc249e41..3c22e8d50c 100644 --- a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx +++ b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx @@ -136,7 +136,7 @@ export const TemplatePage = () => { } return ( - +
Date: Tue, 1 Sep 2020 09:44:22 +0200 Subject: [PATCH 08/11] chore: remove gitignored files --- microsite/i18n/en.json | 328 ----------------------------------------- 1 file changed, 328 deletions(-) delete mode 100644 microsite/i18n/en.json diff --git a/microsite/i18n/en.json b/microsite/i18n/en.json deleted file mode 100644 index e9f63d5737..0000000000 --- a/microsite/i18n/en.json +++ /dev/null @@ -1,328 +0,0 @@ -{ - "_comment": "This file is auto-generated by write-translations.js", - "localized-strings": { - "next": "Next", - "previous": "Previous", - "tagline": "An open platform for building developer portals", - "docs": { - "api/backend": { - "title": "Backend" - }, - "api/utility-apis": { - "title": "Utility APIs" - }, - "architecture-decisions/adrs-adr001": { - "title": "ADR001: Architecture Decision Record (ADR) log", - "sidebar_label": "ADR001" - }, - "architecture-decisions/adrs-adr002": { - "title": "ADR002: Default Software Catalog File Format", - "sidebar_label": "ADR002" - }, - "architecture-decisions/adrs-adr003": { - "title": "ADR003: Avoid Default Exports and Prefer Named Exports", - "sidebar_label": "ADR003" - }, - "architecture-decisions/adrs-adr004": { - "title": "ADR004: Module Export Structure", - "sidebar_label": "ADR004" - }, - "architecture-decisions/adrs-adr005": { - "title": "ADR005: Catalog Core Entities", - "sidebar_label": "ADR005" - }, - "architecture-decisions/adrs-adr006": { - "title": "ADR006: Avoid React.FC and React.SFC", - "sidebar_label": "ADR006" - }, - "architecture-decisions/adrs-adr007": { - "title": "ADR007: Use MSW to mock http requests", - "sidebar_label": "ADR007" - }, - "architecture-decisions/adrs-adr008": { - "title": "ADR008: Default Catalog File Name", - "sidebar_label": "ADR008" - }, - "architecture-decisions/adrs-overview": { - "title": "Architecture Decision Records (ADR)", - "sidebar_label": "Overview" - }, - "auth/add-auth-provider": { - "title": "Adding authentication providers" - }, - "auth/auth-backend-classes": { - "title": "Auth backend classes" - }, - "auth/auth-backend": { - "title": "Auth backend" - }, - "auth/glossary": { - "title": "Glossary" - }, - "auth/index": { - "title": "User Authentication and Authorization in Backstage" - }, - "auth/oauth": { - "title": "OAuth and OpenID Connect" - }, - "conf/defining": { - "title": "Defining Configuration for your Plugin" - }, - "conf/index": { - "title": "Static Configuration in Backstage" - }, - "conf/reading": { - "title": "Reading Backstage Configuration" - }, - "conf/writing": { - "title": "Writing Backstage Configuration Files" - }, - "dls/contributing-to-storybook": { - "title": "Contributing to Storybook" - }, - "dls/design": { - "title": "Design" - }, - "dls/figma": { - "title": "Figma" - }, - "FAQ": { - "title": "FAQ" - }, - "features/software-catalog/software-catalog-api": { - "title": "API" - }, - "features/software-catalog/software-catalog-configuration": { - "title": "Catalog Configuration" - }, - "features/software-catalog/descriptor-format": { - "title": "Descriptor Format of Catalog Entities", - "sidebar_label": "YAML File Format" - }, - "features/software-catalog/extending-the-model": { - "title": "Extending the model" - }, - "features/software-catalog/external-integrations": { - "title": "External integrations" - }, - "features/software-catalog/software-catalog-overview": { - "title": "Backstage Service Catalog (alpha)", - "sidebar_label": "Backstage Service Catalog" - }, - "features/software-catalog/installation": { - "title": "features/software-catalog/installation" - }, - "features/software-catalog/system-model": { - "title": "System Model" - }, - "features/software-templates/adding-templates": { - "title": "Adding your own Templates" - }, - "features/software-templates/extending/extending-preparer": { - "title": "Create your own Preparer" - }, - "features/software-templates/extending/extending-publisher": { - "title": "Create your own Publisher" - }, - "features/software-templates/extending/extending-templater": { - "title": "Creating your own Templater" - }, - "features/software-templates/extending/extending-index": { - "title": "Extending the Scaffolder" - }, - "features/software-templates/software-templates-index": { - "title": "Software Templates" - }, - "features/software-templates/installation": { - "title": "features/software-templates/installation" - }, - "features/techdocs/concepts": { - "title": "Concepts" - }, - "features/techdocs/creating-and-publishing": { - "title": "Creating and publishing your docs", - "sidebar_label": "Creating and Publishing Documentation" - }, - "features/techdocs/faqs": { - "title": "TechDocs FAQ", - "sidebar_label": "FAQ" - }, - "features/techdocs/getting-started": { - "title": "Getting Started" - }, - "features/techdocs/techdocs-overview": { - "title": "TechDocs Documentation", - "sidebar_label": "Overview" - }, - "getting-started/app-custom-theme": { - "title": "Customize the look-and-feel of your App" - }, - "getting-started/configure-app-with-plugins": { - "title": "Configuring App with plugins" - }, - "getting-started/create-an-app": { - "title": "Create an App" - }, - "getting-started/deployment-k8s": { - "title": "Kubernetes" - }, - "getting-started/deployment-other": { - "title": "Other" - }, - "getting-started/development-environment": { - "title": "Development Environment" - }, - "getting-started/index": { - "title": "Running Backstage Locally" - }, - "getting-started/installation": { - "title": "Installation" - }, - "overview/adopting": { - "title": "Strategies for adopting" - }, - "overview/architecture-overview": { - "title": "Architecture overview" - }, - "overview/architecture-terminology": { - "title": "Architecture terminology" - }, - "overview/background": { - "title": "The Spotify Story" - }, - "overview/roadmap": { - "title": "Project roadmap" - }, - "overview/support": { - "title": "Support and community" - }, - "overview/vision": { - "title": "Vision" - }, - "overview/what-is-backstage": { - "title": "What is Backstage?" - }, - "plugins/add-to-marketplace": { - "title": "Add to Marketplace" - }, - "plugins/backend-plugin": { - "title": "Backend plugin" - }, - "plugins/call-existing-api": { - "title": "Call Existing API" - }, - "plugins/create-a-plugin": { - "title": "Create a Backstage Plugin" - }, - "plugins/existing-plugins": { - "title": "Existing plugins" - }, - "plugins/index": { - "title": "Intro to plugins" - }, - "plugins/plugin-development": { - "title": "Plugin Development" - }, - "plugins/proxying": { - "title": "Proxying" - }, - "plugins/publish-private": { - "title": "Publish private" - }, - "plugins/publishing": { - "title": "Publishing" - }, - "plugins/structure-of-a-plugin": { - "title": "Structure of a Plugin" - }, - "plugins/testing": { - "title": "Testing with Jest" - }, - "README": { - "title": "README" - }, - "reference/createPlugin-feature-flags": { - "title": "createPlugin - feature flags" - }, - "reference/createPlugin-router": { - "title": "createPlugin - router" - }, - "reference/createPlugin": { - "title": "createPlugin" - }, - "reference/utility-apis/AlertApi": { - "title": "reference/utility-apis/AlertApi" - }, - "reference/utility-apis/AppThemeApi": { - "title": "reference/utility-apis/AppThemeApi" - }, - "reference/utility-apis/BackstageIdentityApi": { - "title": "reference/utility-apis/BackstageIdentityApi" - }, - "reference/utility-apis/Config": { - "title": "reference/utility-apis/Config" - }, - "reference/utility-apis/ErrorApi": { - "title": "reference/utility-apis/ErrorApi" - }, - "reference/utility-apis/FeatureFlagsApi": { - "title": "reference/utility-apis/FeatureFlagsApi" - }, - "reference/utility-apis/IdentityApi": { - "title": "reference/utility-apis/IdentityApi" - }, - "reference/utility-apis/OAuthApi": { - "title": "reference/utility-apis/OAuthApi" - }, - "reference/utility-apis/OAuthRequestApi": { - "title": "reference/utility-apis/OAuthRequestApi" - }, - "reference/utility-apis/OpenIdConnectApi": { - "title": "reference/utility-apis/OpenIdConnectApi" - }, - "reference/utility-apis/ProfileInfoApi": { - "title": "reference/utility-apis/ProfileInfoApi" - }, - "reference/utility-apis/README": { - "title": "Utility API References" - }, - "reference/utility-apis/SessionStateApi": { - "title": "reference/utility-apis/SessionStateApi" - }, - "reference/utility-apis/StorageApi": { - "title": "reference/utility-apis/StorageApi" - }, - "tutorials/journey": { - "title": "Future developer journey" - } - }, - "links": { - "GitHub": "GitHub", - "Docs": "Docs", - "Plugins": "Plugins", - "Blog": "Blog", - "Demos": "Demos", - "Newsletter": "Newsletter" - }, - "categories": { - "Overview": "Overview", - "Getting Started": "Getting Started", - "Features": "Features", - "Plugins": "Plugins", - "Configuration": "Configuration", - "Auth and identity": "Auth and identity", - "Designing for Backstage": "Designing for Backstage", - "API references": "API references", - "Tutorials": "Tutorials", - "Architecture Decision Records (ADRs)": "Architecture Decision Records (ADRs)", - "Contribute": "Contribute", - "Support": "Support", - "FAQ": "FAQ" - } - }, - "pages-strings": { - "Help Translate|recruit community translators for your project": "Help Translate", - "Edit this Doc|recruitment message asking to edit the doc source": "Edit", - "Translate this Doc|recruitment message asking to translate the docs": "Translate" - } -} From bcbeb0b657945b55965260c67524621c17fb823e Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Tue, 1 Sep 2020 09:46:46 +0200 Subject: [PATCH 09/11] Add api-docs to marketplace (#2204) * docs: improve documentation about adding plugins to the marketplace The relative path is misleading as one has to come from root. * docs: add api-docs plugin to marketplace --- docs/plugins/add-to-marketplace.md | 4 +++- microsite/data/plugins/api-docs.yaml | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 microsite/data/plugins/api-docs.yaml diff --git a/docs/plugins/add-to-marketplace.md b/docs/plugins/add-to-marketplace.md index 23cfecd5d0..d85807a226 100644 --- a/docs/plugins/add-to-marketplace.md +++ b/docs/plugins/add-to-marketplace.md @@ -6,7 +6,9 @@ title: Add to Marketplace ## Adding a Plugin to the Marketplace To add a new plugin to the [plugin marketplace](https://backstage.io/plugins) -create a file in `data/plugins` with your plugin's information. Example: +create a file in +[`microsite/data/plugins`](https://github.com/spotify/backstage/tree/master/microsite/data/plugins) +with your plugin's information. Example: ```yaml --- diff --git a/microsite/data/plugins/api-docs.yaml b/microsite/data/plugins/api-docs.yaml new file mode 100644 index 0000000000..b7f85b62b3 --- /dev/null +++ b/microsite/data/plugins/api-docs.yaml @@ -0,0 +1,9 @@ +--- +title: API Docs +author: SDA SE +authorUrl: https://sda.se/ +category: Discovery +description: Components to discover and display API entities as an extension to the catalog plugin. +documentation: https://github.com/spotify/backstage/blob/master/plugins/api-docs/README.md +iconUrl: https://github.com/spotify/backstage/raw/master/plugins/api-docs/docs/entity_tab_api.png +npmPackageName: '@backstage/plugin-api-docs' From b9ed0d3088a6c3634d16a08d7bce4c992b4477dd Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Tue, 1 Sep 2020 09:51:00 +0200 Subject: [PATCH 10/11] feat(catalog): change the tags in the entity list to be gray (#2205) --- .../catalog/src/components/CatalogTable/CatalogTable.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index c68eb10e66..cd4df13775 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -75,12 +75,7 @@ const columns: TableColumn[] = [ <> {entity.metadata.tags && entity.metadata.tags.map(t => ( - + ))} ), From ff5da01280cc79a7792b58a18de5bd604db431e1 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Tue, 1 Sep 2020 09:54:05 +0200 Subject: [PATCH 11/11] TechDocs: techdocs backend tests (#2196) * add tests for generators class * add tests for helpers * renaming * fixup --- .../stages/generate/generators.test.ts | 47 ++++++++ .../techdocs/stages/generate/generators.ts | 53 +++++---- .../techdocs/stages/generate/helpers.test.ts | 103 ++++++++++++++++++ 3 files changed, 175 insertions(+), 28 deletions(-) create mode 100644 plugins/techdocs-backend/src/techdocs/stages/generate/generators.test.ts create mode 100644 plugins/techdocs-backend/src/techdocs/stages/generate/helpers.test.ts diff --git a/plugins/techdocs-backend/src/techdocs/stages/generate/generators.test.ts b/plugins/techdocs-backend/src/techdocs/stages/generate/generators.test.ts new file mode 100644 index 0000000000..b339aef9f8 --- /dev/null +++ b/plugins/techdocs-backend/src/techdocs/stages/generate/generators.test.ts @@ -0,0 +1,47 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Generators, TechdocsGenerator } from './'; +import { getVoidLogger } from '@backstage/backend-common'; + +const logger = getVoidLogger(); + +const mockEntity = { + apiVersion: 'version', + kind: 'TestKind', + metadata: { + name: 'testName', + }, +}; + +describe('generators', () => { + it('should return error if no generator is registered', async () => { + const generators = new Generators(); + + expect(() => generators.get(mockEntity)).toThrowError( + 'No generator registered for entity: "techdocs"', + ); + }); + + it('should return correct registered generator', async () => { + const generators = new Generators(); + const techdocs = new TechdocsGenerator(logger); + + generators.register('techdocs', techdocs); + + expect(generators.get(mockEntity)).toBe(techdocs); + }); +}); diff --git a/plugins/techdocs-backend/src/techdocs/stages/generate/generators.ts b/plugins/techdocs-backend/src/techdocs/stages/generate/generators.ts index da9d3c418b..f28a169942 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/generate/generators.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/generate/generators.ts @@ -15,32 +15,29 @@ */ import { - GeneratorBase, - SupportedGeneratorKey, - GeneratorBuilder, - } from './types'; - - import { Entity } from '@backstage/catalog-model'; - import { getGeneratorKey } from './helpers'; - - export class Generators implements GeneratorBuilder { - private generatorMap = new Map(); - - register(templaterKey: SupportedGeneratorKey, templater: GeneratorBase) { - this.generatorMap.set(templaterKey, templater); - } - - get(entity: Entity): GeneratorBase { - const generatorKey = getGeneratorKey(entity); - const generator = this.generatorMap.get(generatorKey); - - if (!generator) { - throw new Error( - `No generator registered for entity: "${generatorKey}"`, - ); - } - - return generator; - } + GeneratorBase, + SupportedGeneratorKey, + GeneratorBuilder, +} from './types'; + +import { Entity } from '@backstage/catalog-model'; +import { getGeneratorKey } from './helpers'; + +export class Generators implements GeneratorBuilder { + private generatorMap = new Map(); + + register(generatorKey: SupportedGeneratorKey, generator: GeneratorBase) { + this.generatorMap.set(generatorKey, generator); } - \ No newline at end of file + + get(entity: Entity): GeneratorBase { + const generatorKey = getGeneratorKey(entity); + const generator = this.generatorMap.get(generatorKey); + + if (!generator) { + throw new Error(`No generator registered for entity: "${generatorKey}"`); + } + + return generator; + } +} diff --git a/plugins/techdocs-backend/src/techdocs/stages/generate/helpers.test.ts b/plugins/techdocs-backend/src/techdocs/stages/generate/helpers.test.ts new file mode 100644 index 0000000000..799e79ef67 --- /dev/null +++ b/plugins/techdocs-backend/src/techdocs/stages/generate/helpers.test.ts @@ -0,0 +1,103 @@ +/* + * 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 Stream, { PassThrough } from 'stream'; +import os from 'os'; +import Docker from 'dockerode'; +import { runDockerContainer, getGeneratorKey } from './helpers'; + +const mockEntity = { + apiVersion: 'version', + kind: 'TestKind', + metadata: { + name: 'testName', + }, +}; + +const mockDocker = new Docker() as jest.Mocked; + +describe('helpers', () => { + describe('getGeneratorKey', () => { + it('should return techdocs as the only generator key', () => { + const key = getGeneratorKey(mockEntity); + expect(key).toBe('techdocs'); + }); + }); + + describe('runDockerContainer', () => { + beforeEach(() => { + jest.spyOn(mockDocker, 'pull').mockImplementation((async ( + _image: string, + _something: any, + handler: (err: Error | undefined, stream: PassThrough) => void, + ) => { + const mockStream = new PassThrough(); + handler(undefined, mockStream); + mockStream.end(); + }) as any); + + jest + .spyOn(mockDocker, 'run') + .mockResolvedValue([{ Error: null, StatusCode: 0 }]); + }); + + const imageName = 'spotify/techdocs'; + const args = ['build', '-d', '/result']; + const docsDir = os.tmpdir(); + const resultDir = os.tmpdir(); + + it('should pull the techdocs docker container', async () => { + await runDockerContainer({ + imageName, + args, + docsDir, + resultDir, + dockerClient: mockDocker, + }); + + expect(mockDocker.pull).toHaveBeenCalledWith( + imageName, + {}, + expect.any(Function), + ); + }); + + it('should run the techdocs docker container', async () => { + await runDockerContainer({ + imageName, + args, + docsDir, + resultDir, + dockerClient: mockDocker, + }); + + expect(mockDocker.run).toHaveBeenCalledWith( + imageName, + args, + expect.any(Stream), + { + Volumes: { + '/content': {}, + '/result': {}, + }, + WorkingDir: '/content', + HostConfig: { + Binds: [`${docsDir}:/content`, `${resultDir}:/result`], + }, + }, + ); + }); + }); +});